Русский · English · Español · 中文 · Português

「Space Now」— 智能体 API

本页面面向 AI 智能体(及其使用者)。它说明智能体如何连接到用户浏览器中正在运行的「Space Now」页面 (known.html),以及能在其中做什么:界面能做的一切——打开任意天体、 飞行相机、拖动时间、切换图层和过滤器、宇航员模式、查找日食和行星连珠、配合语音和字幕讲故事、展示自己的图片、 录制视频、读取错误。渲染在用户的浏览器中完成(使用其 GPU 的 WebGL);智能体只发送命令,并获得回复和截图。

1. 配对

  1. 用户打开网站,点击圆形的 🤖 按钮(右下角,宇航员上方)——智能体栏展开;点击「开启智能体」会显示一个代码(20 个字符,例如 r4fxjbjsxdy6ex3pf4hd)。该代码就是通行证:持有者即可控制此标签页。🤖 只是隐藏/显示该栏,连接不会断开;栏中的「断开连接」会关闭连接。该栏始终有 📂(打开故事文件或保存当前显示的故事——没有智能体也可用)和「MCP ↗」(即本页)。
  2. 用户把代码交给智能体。智能体用该代码调用 connect。
  3. 永久连接(可选):在页面上——智能体栏中的 📌「记住」(代码保存在此浏览器中,网站每次打开时自动连接);在智能体端——使用 save: true 调用 connect(代码保存到 ~/.orbit-agent.json,MCP 启动时自动连接)。之后只需打开网站即可。要忘记:取消固定 📌 并调用 forget。
  4. 页面上的「断开连接」(或智能体离开)会结束会话;随后页面会把图层和界面恢复到智能体介入之前的状态,停止音乐、语音、字幕、图片和脚本,并移除着陆点(sites)、绘图和标记 (draw、mark、flybys)、媒体窗口和自建天体(spawn)——与 onstop 无关。相机、时间和 选中项也会恢复(默认如此;onstop {restore: false} — 保留智能体展示的内容)。例外:带 detach: true 的 play / story run — 演示会播放到结束,然后回滚。

声音(语音、音乐)只有在用户至少点击过一次页面后才会启动——这是浏览器的规定。语音默认关闭:用户通过智能体栏中的 🔊 开启(会被记住);字幕始终显示。

演示检查清单——务必照做

[
  {"cmd":"onstop","args":{"restore":true}},        // 第一步:结束、按 Esc 或 ✕ 时一切恢复原状
  {"cmd":"reset"},
  {"cmd":"view","args":{"on":["oplanets"]}},        // 只开需要的图层:卫星、航天器、轨道、星座一律关闭
  {"cmd":"hideUi","args":{"on":true,"labels":false}},
  {"cmd":"time","args":{"at":"<moment>","rate":0}}, // 自己设定时间;rate 0 — 光照不会漂移
  … 飞行 (fly) …
  … 演示 …
]

2. 传输

双方都连接到同一网站上的中继房间:wss://<site>/agent/<code>?r=agent(页面使用 ?r=page)。房间在两端之间转发消息;不保存任何内容。JSON 消息:

agent → page   { "id": 1, "cmd": "go", "args": { "search": "?o=Mars" } }
page  → agent  { "id": 1, "ok": true, "result": … }   |   { "id": 1, "ok": false, "error": "…" }
room  → both   { "sys": "peer", "on": true|false }      (对端加入 / 离开)
keepalive      每 30 秒发送一次 { "sys": "ping" },房间回复 { "sys": "pong" }

每个角色只允许一个连接(使用相同代码的新智能体会替换旧的,关闭码 4000 — 此时不要重连)。 消息最大 4 MB。站点:https://space.bl2.ru、https://space.bl2.net(同一网站,默认语言为 ru/en)。

3. MCP 服务器

现成的 MCP 服务器(stdio):orbit-proto 仓库中的 scripts/agent-mcp.mjs(Node ≥ 20,依赖 ws)。 下面的每个命令都是同名工具(hide_ui → 页面命令 hideUi),另外还有 connect、 disconnect、help(本页的文本形式;lang: en)。文件:image 接受 path,record 保存 到 path,screenshot 返回图片。

{ "mcpServers": { "orbit-space": { "command": "node", "args": ["scripts/agent-mcp.mjs"] } } }

环境变量:AGENT_SITE — 默认站点,AGENT_CODE — 启动时连接。

不使用 MCP 服务器的连接方式(云端智能体)

// 「启动后立即返回」— 在 space.bl2.ru 标签页的控制台中(或 Node ≥ 22)
const code = '<code>', ws = new WebSocket(`wss://space.bl2.ru/agent/${code}?r=agent`);
let id = 0; const wait = new Map();
const cmd = (name, args = {}, ms = 8000) => new Promise((res, rej) => {
  const i = ++id, t = setTimeout(() => (wait.delete(i), rej(new Error('timeout ' + name))), ms);
  wait.set(i, (m) => (clearTimeout(t), m.ok ? res(m.result) : rej(new Error(m.error))));
  ws.send(JSON.stringify({ id: i, cmd: name, args }));
});
ws.onmessage = (e) => { const m = JSON.parse(e.data); if (!m.sys) wait.get(m.id)?.(m), wait.delete(m.id); };
ws.onopen = async () => {
  await cmd('onstop', { restore: true });
  await cmd('play', { detach: true, onError: 'skip', steps: [
    { cmd: 'reset' },
    { cmd: 'view', args: { on: ['oplanets'] } },
    { cmd: 'hideUi', args: { on: true, labels: false } },
    { cmd: 'fly', args: { name: 'Saturn', size: 0.5, lit: true }, wait: 6500 },
    { cmd: 'caption', args: { title: 'Saturn', sub: '1.4 billion km from the Sun' }, wait: 8000 },
  ] });
  ws.close(); // 演示由页面自行播放,结束后再把一切恢复原状
};

4. 命令

查看与状态

cmdargs说明
state时间、速率、选中天体、视图中心、相机(yaw、pitch、dist km、fov)、附近恒星、可见图层的键、画布尺寸(像素)
screenshotwidth, quality, full3D 场景的 JPEG。full: true — 带界面的整个标签页(用户需通过智能体栏中的「📷」一次性允许标签页捕获)
bodyname用于取景:半径 km、类型、母天体、卫星、相机角度、sunYaw/sunPitch(使天体被完全照亮的相机方向)、fov、distForSize {0.25, 0.5, 0.66, 0.8 → km}:圆盘占满屏幕高度该比例时的距离(= radius / tan(fov/2) / k)
wherename, at某一时刻的位置:距太阳和地球的 km 与 AU、从地球看的赤经/赤纬(J2000)
logslevel, last, clear页面控制台的错误/警告/信息(环形缓冲区 300 条)
snapshot完整状态(时间、速率、相机、中心、选中项、所有图层复选框、界面是否隐藏、宇航员链接)— 保存下来可用于复现 bug
restorestate返回到某个快照

视图、相机、时间、图层

cmdargs说明
searchq, limit像搜索框一样搜索天体库:天体、卫星、航天器、星云和星系(其图层开启时)、地球卫星(名称或 NORAD)、小行星(名称或编号;星表通过 load-asteroids 行加载)。返回带 n 的行
openn打开一个搜索结果:选中、卡片、飞行 — 如同点击该行
gosearch像分享链接一样打开视图:?o= 天体(Mars、Марс、ISS…),?a= 小行星(编号或名称),?s= 卫星 NORAD,?g= 星系,?n= 星云,?st= 星座中的恒星;附加参数 d= 相机 km、v=yaw,pitch、t= ISO 时间
flyname, size, lit, face, seconds平滑飞向某个天体(如同在树中点击)。size — 天体占屏幕高度的比例(0.66 — 三分之二),lit: true — 从受光面观察;飞行结束后取景还需约 2.5 秒(总计约 6 秒,请使用 wait ≥ 6500)。face: "spot" — 木星:时间回退,直到大红斑转到白天一侧
camerayaw, pitch, dist, ms围绕视图中心的相机(rad、km)。ms > 0 — 在 ms 毫秒内平滑移动。示例「占满屏幕 2/3,受光面」:body → camera {yaw: sunYaw+0.4, pitch: sunPitch, dist: distForSize[0.66], ms: 3000}
timeat, rate, live, from, tofrom, to, ms, ease — 从一个日期平滑拖动到另一个日期;at — Unix 秒或 ISO 时刻(公元前:-000500-01-01),速度(1 实时,3600 = 1 小时/秒,负数 = 倒退,0 暂停),live: true
viewon, set, orbitsview — 每个开关的标准键。无参数 — 列出 {key, on};on: [...] — 全部关闭,只开启列出的;set: {key: true|false} — 只切换这些;orbits: false — 一次关闭所有轨道线。键(每个组还有 <key>:orbits — 其轨道线):oplanets — 太阳、行星和卫星;odwarfs — 矮行星和最大的小行星;ohelio — 日球层顶;oshock — 终端激波;osandbox — 自建天体(spawn);oknown-small — 已知小天体(Apophis、Bennu…);ocomets — 彗星;ointerstellar — 星际天体(1I、2I、3I);okuiper — 柯伊伯带;ooort — 奥尔特云;a0…a9 — 按大小划分的小行星(a0 — 大于 100 km);ocraft、ocraft-orbit、ocraft-l — 飞行中的航天器、环绕天体轨道上的航天器、位于拉格朗日点的航天器;s0…s30 — 地球卫星组,sats — 全部;ogalaxy — 银河系模型;ouniverse — 星系;stars — 带名称的星座恒星;conlines — 星座连线图(与 stars 一起;不在 view {on} 的列表中 — 无连线);galmarks — 银心和奔赴点标签;galsun — 「太阳 — 你在这里」;scenelabels — 仅标注场景所需内容的标签(labels {only})。单独显示一条星际轨迹:关闭 ointerstellar:orbits,select {name: '3I/ATLAS'} — 其轨迹高亮。另有:earth:ph|cities|seas|mountains|volcanoes、weather、aurora(极光)、sw(空间天气)、sk:0…6、sk:neb。故事和幻灯片带有 on 字段(在步骤之前应用):只列出需要的——默认开启的新图层不会出现在旧故事中
layers所有图层组:key、name、visible、orbits、count
layerkey, visible, orbits切换图层。键:oplanets、odwarfs、ocomets、ocraft*、ogalaxy、ouniverse、a0…a9 按大小的小行星、s0… 卫星组…
galaxyfar银河系视图(far — 连同其他星系)。用于场景:dist(0 — 模型位于太阳处,> 0 — 向外飞出)、pitch、center: true — 绕银心轨道运行、off: true;银河时间 — galtime {rate}(年/秒)
galaxiescount, dwell按距离依次飞越 N 个著名星系,每个停留 dwell 秒
clear取消选中,关闭卡片

事件(由页面计算,无需自备公式)

cmdargs说明
findkind, from, to, total, count, min从任意日期起查找 lunar(total: true — 「血月」)、solar、parade(min 行星数 3…7);返回食甚时刻、是否全食、可见的纬度/经度/地区;lunar — contact(本影接触)
showkind, at, keepView像「📅 事件」那样展示事件:时间 — 食甚前半小时,速度 ×100(请设置 time {rate: 0} 或你自己的值),取消选中,视图中心和相机置于地球(日食:阴影点上方)或月球(月食);行星连珠 — 从地球出发的第一人称视角,仅显示行星。用 view 设置的图层会保留(keepView: false — 使用事件自己的图层)。之后:hideUi,必要时 camera

整个界面

cmdargs说明
uitext, within每个可见的按钮、复选框、滑块、输入框、列表/树行,附带 ref、文本、id、data 属性、状态。可按文本或 CSS 区域过滤
clickref | selector点击它
setref | selector, value复选框 true/false、滑块、输入框、select
keycode, ms, shift, ctrl按键(KeyW… 按住 ms 毫秒以飞行,Space 暂停,KeyH 隐藏面板,Escape…)
pointeraction, x, y, to, dy3D 场景上的鼠标:click(选中)、dblclick(飞向)、move(悬停)、drag(旋转)到 [x,y]、wheel(dy 缩放)
hideUion, labels隐藏一切:面板、角落按钮、智能体栏(labels: false — 连天体标签也隐藏);只保留场景、字幕、图片、媒体窗口、标记(mark)和智能体的绘图。on: false — 恢复
astroaction, name宇航员:enter(相机所在处)、goto name(飞过去,绕轨道运行)、land(站到地面上)、exit、link、state。行走和环顾 — key/pointer
evalcode仅限开发服务器(npm run dev):在页面中运行 JS 表达式;window.__known 保存状态。公共网站上不可用

讲故事

cmdargs说明
captiontitle, sub, text, seconds左下角的字幕,样式同屏保(title 为空 — 隐藏)
saytext, title, sub, audio, keep, caption以字幕形式显示文本;audio — mp3 配音(链接或电脑上的文件名;不使用浏览器语音 — 请事先制作 mp3,例如用 edge-tts);🔇 — 静音
musicon, theme, volume浏览器中生成的背景音乐:theme 0 «Nebula»、1 «Depth»、2 «Crystals»,null — 轮换
imageid, src | path, at, x, y, dx, dy, w, label, remove, clear覆盖在场景上的你自己的图片:附着在天体上(随之移动)或位于屏幕某点
playsteps | stop | pause | seek | step由页面播放的完整脚本:[{cmd, args, wait}],wait = 毫秒或 "speech"。如同播放器:pause、seek: n — 跳到第 n 步(之前的步骤立即执行,不等待、不播放语音)、step: ±1。用户在底部会看到播放器:⏮ ⏪ ▶/⏸ ⏹ ⏩ ⏭、「幻灯片 · 步骤」、在网站上保存 💾(密码)、下载 ⬇、删除 🗑、✕。断开连接时停止 — 带 detach: true 除外(播放到结束,然后回滚)。onError: 'skip'(默认 — 继续)| 'stop';失败的步骤会进入 logs、不带参数的 play(lastError)和播放器。演示会自动切到下一张幻灯片;✕ 或 Esc — 退出(界面恢复)
deckslides, start, go, close演示文稿:[{title, steps}];用户在底部看到「◀ n/N ▶」(以及 PageUp/PageDown),翻页会播放该幻灯片的步骤。在 hideUi 下仍保持可见
buttonsitems底部你自己的按钮:[{label, title, steps}],点击即播放这些步骤;[] 移除
storyaction, story, id, password, name故事 = 智能体编排的内容(飞行计划、解说、可视化 — 例如用于 NASA Space Apps):{title, summary, lang, slides:[{title, steps}] | steps}。run — 演示;publish — 用编辑密码保存到网站(password;不提供 — 页面会询问用户)→ 链接 known.html?story=<uuid>,任何人无需智能体即可观看(带「◀ n/N ▶」);最大 256 KB,最后一次打开后保留 30 天;update — 编辑(id + 相同密码);delete — 从网站删除(id + 相同密码);download — 给用户的文件(不限大小);open — 用户选择文件(或把 .json 拖到页面上;智能体栏中的「📂」菜单);load — 按 id 加载
steps当前在故事或演示文稿中的位置:幻灯片及幻灯片列表、步骤及步骤列表(n、label)、播放中、已暂停。跳转 — play {seek: n} / {step: ±1}、deck {go: n}。播放器中有 ☰ — 幻灯片和步骤列表,点击即可跳转
scenecode故事中的环境模式场景 — 其相机飞行、字幕和时间(约 20–30 秒,等待结束)。代码见 scenes:body:Сатурн、event:<id>、mission:…、astro:…、lapse:…、galaxy、pullback。环境模式(「∞」)本身就是通用播放器中的同一个场景生成器:⏮ ▶/⏸ ⏭、☰ 已播放的和所有场景、💾/⬇ — 把已播放内容保存为场景步骤构成的故事
scenes按分区列出所有环境场景及其代码
recordaction, fps, mbps / path场景视频:start、stop(MCP 把 WebM 保存到 path)

统一入口:WS、MCP、控制台、故事

无论命令来自哪里,命令和语言都是相同的:任意语言的 WebSocket、MCP、页面控制台(智能体栏中的 >_ 按钮)或故事。可以写好整个脚本交给 story,也可以逐条调用命令。新增:exec {steps} — 立即运行语言步骤(变量在多次调用之间保留),expr {e} — 求值表达式,js {code, timeout} — 沙箱中的 JavaScript(隔离的 iframe + Web Worker:没有 DOM、cookie 或网络;内部可用 await cmd(name, args)、await expr('…')、log()、sleep()、return)。

控制台:fly Mars size=0.6(一条命令,第一个词是名称)、JSON 命令、步骤 {…} / […]、表达式 = …、代码 js …。

基础脚本 — Node.js:

// node agent.mjs <code>   (Node ≥ 22 — 内置 WebSocket)
const code = process.argv[2], ws = new WebSocket(`wss://space.bl2.ru/agent/${code}?r=agent`);
let id = 0; const wait = new Map();
const cmd = (name, args = {}) => new Promise((res, rej) => {
  const i = ++id; wait.set(i, { res, rej }); ws.send(JSON.stringify({ id: i, cmd: name, args }));
});
ws.onmessage = (e) => {
  const m = JSON.parse(e.data);
  if (m.sys) return;                                   // peer / pong
  const w = wait.get(m.id); wait.delete(m.id);
  m.ok ? w.res(m.result) : w.rej(new Error(m.error));
};
setInterval(() => ws.send('{"sys":"ping"}'), 30000);
ws.onopen = async () => {
  await cmd('fly', { name: 'Mars', size: 0.6, lit: true });
  console.log(await cmd('expr', { e: "round(au(get('Mars','sun')),3)" }));
  await cmd('exec', { steps: [{ wait: "dist('camera','Mars') < 1e5 km" }, { cmd: 'caption', args: { title: 'Mars' } }] });
  process.exit(0);
};

Python:

# python agent.py <code>    (pip install websockets)
import asyncio, json, sys, websockets
async def main(code):
    async with websockets.connect(f"wss://space.bl2.ru/agent/{code}?r=agent") as ws:
        n = 0
        async def cmd(name, **args):
            nonlocal n; n += 1
            await ws.send(json.dumps({"id": n, "cmd": name, "args": args}))
            while True:
                m = json.loads(await ws.recv())
                if m.get("id") == n:
                    if not m["ok"]: raise RuntimeError(m["error"])
                    return m["result"]
        await cmd("fly", name="Saturn", size=0.6, lit=True)
        print(await cmd("expr", e="get('Saturn','moons')"))
asyncio.run(main(sys.argv[1]))

脚本语言 v2

版本:不带 v(或 v: 1)的故事 — 如上所述的步骤和幻灯片;v: 2 — 带条件的语言。新版本的处理器绝不会改变旧版本的行为。

{ "v": 2, "title": "…", "on": ["oplanets"], "vars": { "n": 0 }, "steps": [
  { "cmd": "fly", "args": { "name": "Mars", "size": 0.5, "lit": true } },
  { "wait": "dist('camera', 'Mars') < 50000 km", "timeout": 20 },
  { "set": { "n": "n + 1" } },
  { "if": "visible('Mars')", "then": [ { "cmd": "caption", "args": { "title": "Mars", "sub": "{round(au(dist('camera','Sun')),3)} AU from the Sun" } } ] },
  { "repeat": 2, "do": [ { "wait": 1 } ] }
] }

步骤:{cmd, args, wait}(wait — 毫秒或表达式)、{wait: expression | seconds, timeout}、{if, then, else}、{repeat: n | expression, do}、{set: {name: expression}}、{on: [...]}。表达式 — 不使用 eval:带单位的数字 km au ly m、字符串 '…'、变量、+ - * / %、< > <= >= == !=、&& || !;函数 dist(a, b)('camera' 或某个天体,km)、visible(body)、elapsed()、time()、camdist()、au(x)、ly(x)、round(x, d)、abs min max。即时属性:get(body | 'camera', 'prop') — 天体有 name、kind、radius、parent、moons、x y z、sun(距太阳 km)、earth、camera(距相机 km)、center、visible;相机有 dist、yaw、pitch、fov、x y z、sun、center;不带 prop — 整个对象。把命令结果存入变量:{cmd, args, as: 'r'},取字段 — field(r, 'rows.0.text'),大小 — count(x)。在 args 字符串中 — {expression}。错误进入 logs。后续计划:触发器(when/once/every)和沙箱(生成天体和 3D 模型)。自定义函数:{def: name, params, e} — 用于表达式,{def, params, steps} — 作为步骤 {call, args, as}、{local}、{return};在故事中 — funcs。更多函数:angle(a, b) — 从相机看去两个方向之间的角度(度),edge(a, b) — a 超出 b 边缘露出多少度,sin cos(度)、pow sqrt。命令上的 wait: true — 等到它完成。用于场景:fitdist(r[, margin]) — 半径 r km 的圆恰好放进屏幕(取窄边)的距离(天体还有 day — 自转周期,秒);satcount('all' | 'active' | 'debris' | 'rb' | 'dead') — 地球周围的星表天体;mstat(craft, 'launch' | 'km' | 'turns' | 'rmin' | 'rnow' | 'flybys') — 根据任务轨迹得出的任务数据,截至今天。航天器轨迹和飞掠来自导入脚本(scripts/fetch-missions.mjs,JPL Horizons,每日):库中新增的航天器会自动获得其飞掠和数据;flybys {craft} 在其路径上绘制飞掠点。场景中的媒体:media {url | file, title, x, y, w, h, volume, loop, start, id} — 带音频或视频的窗口:mp3/视频链接或 YouTube;file — 用户电脑上的文件(网站不存储文件:用户在窗口中选择,之后可离线播放);close: true 关闭窗口。状态:remember {name} — 保存相机、时间、视图和界面;recall {name} — 恢复;onstop {restore: true}(在故事文件中 — 字段 "onstop": "restore")— 结束、按 Esc 或 ✕ 时把一切恢复到开始前的样子。请告诉用户:故事和屏保可以用遥控器操控 — 演示翻页笔(PageDown/PageUp,「.」或 B — 黑屏)、键盘、耳机和电视盒子上的媒体键(下一个、上一个、暂停)、游戏手柄(方向键 ←/→,A — 暂停,B — 黑屏)、键盘方向键、Space、Esc。直达链接:?ambient — 屏保,?ambient=<scene code from ☰> — 从该场景开始(例如 ?ambient=landings:Moon:apollo),?story=<id>&step=N 或 &slide=N — 从该处开始播放已发布的故事;播放器中的 🔗 按钮会复制指向当前播放内容的链接。智能体可以用 ambient {play: code} 命令实现同样的效果;reset 在场景之前重置相机、时间和视图。

相机与运动

cmdargs说明
flyname, ms, ease, size, alt, lat, lon, rel, lit一气呵成地飞到取景位置:size — 圆盘占屏幕高度的比例,alt — 距表面的高度(km),lat/lon — 位于某个表面点上方,rel: {to, deg, elev} — 相对于另一个天体的方位(0° — 它在相机身后,180° — 在画面中天体的身后;elev — 抬高)。返回 ms。
cameradist, yaw, pitch, rel, ms, ease, target围绕视图中心的相机;rel 同 fly。target:{between: [a, b], k} — 看向天体之间的某一点;{sat: 25544} — 绕卫星(NORAD)运行;{sat, space: {tilt, spin, from, ms}} — 从太空视角(地球在卫星身后):tilt° 为偏离垂直方向的角度,spin °/s,在 ms 内从 from km 接近到 dist;null — 回到该天体。
mediaurl | file, kind, title, x, y, w, h, volume, loop, start, id, anchor, dx, dy, header, close, forgetDir带音频或视频的窗口:链接(mp3/视频/YouTube)或 file — 电脑上的文件(由用户选择)。anchor: {sat: NORAD} | {body: name} — 窗口跟随某个天体(dx, dy — 偏移,px);header: false — 无标题栏(附着时的默认值)。为 wait: true 返回 ms。close: true | id;forgetDir: true — 忘记媒体文件夹。故事开始前会出现「准备场景」窗口(文件、链接预加载)。
ambientplay从指定场景开始的屏保「∞」:代码同 ☰(hover|Mars、landings:Moon:apollo、event:eclSun、mission:Parker、satellites…);不带 play — 代码列表。
langset界面语言:不带 set — 返回当前语言和列表(ru、en、es、zh、pt);set — 切换。即时切换,无需重新加载:智能体保持连接。屏保会用新语言重新播放当前场景,播放器、卡片、图层树和窗口都会重绘。页面地址变为 /<语言>/(例如 /zh/;域名自身语言就是 /)——语言只由地址决定,没有 ?lang= 参数;指定语言的网站链接为 https://space.bl2.net/<语言>/(俄语为 https://space.bl2.ru/)。
reset—场景之前:停止环绕、视线和时间的渐变;关闭银河;移除标记、飞掠点和媒体窗口(屏保场景会自行完成这些)。
remember / recallname保存状态(相机、时间、视图、界面)/ 恢复。
onstoprestore默认开启:结束、按 Esc、✕ 或智能体离开时,把一切恢复到智能体第一条命令之前的样子:图层和视图开关、面板和被隐藏的界面、相机(角度、距离、中心、跟踪)、选中项、时间和速度(或「live」);音乐和语音停止。智能体自己放置的东西(sites、draw、mark、flybys、image、media、spawn)一律会被移除。restore: false(在故事文件中 — "onstop": "keep")— 保持相机、时间和选中项为智能体离开时的状态。exec 仅在显式 onstop {restore: true} 时于结束时回滚。
consoleopen打开 / 关闭 >_ 控制台。
sitesbody | code, group, ids, light, on着陆点:表面点上的卡片(在场景中)。可省略 body — 天体由 group 决定(apollo、luna、rovers、new、farside、first、china — 月球;mars、chryse — 火星;venera — 金星)或由 ids 决定。示例:sites {group: 'apollo', light: true} → {lat, lon, alt, at, ids};然后 time {at, rate: 0} 和 fly {name: 'Moon', lat, lon, alt}。如果不飞到 lat/lon,相机可能朝向背面 — 看不到卡片。light: true — 着陆点处于白天的时间(回复 at)。
flybyscraft, on航天器路径上的引力弹弓和入轨点(来自任务轨迹)。
orbitspeed (°/s), turns | ms, pitch, dist, end, blend, ease, stop绕天体运行。end: {to, deg, elev} — 恰好在该取景处结束(单向,不回头)。带速度结束 — 惯性滑行,下一个 fly 会接管该速度。
lookto, ms, ease, clear看向某个天体:视点不动(或继续环绕),视线平滑转向并跟踪它。clear: true — 回到中心。
select, shadow, camera targetname; name, by, center, ms, dist; target用于屏保场景:select {name} — 高亮天体的路径('' — 清除);shadow {name, by, center, ms, dist} — 相机对准食的阴影点;camera {target: {between: [a, b], k}, ms} — 天体之间的注视点(orbit 会绕它转),target: null — 返回;look {to: 'galcenter'};fly {frame: {path, from, to, with}, elev, deg} — 把航天器的整条路径纳入取景。
followname, ms, dist, ease跟随另一个天体:保持取景的平滑交接。

展示小天体(彗星、星际天体 — 在数千万 km 外看不见):follow {name} 或 fly {name},然后 mark {name, label: true} — 在该天体处显示闪烁的彩色标记(hideUi 下也可见;另有 color、speed),select {name} — 其路径高亮。移除:mark {remove: name} 或 mark {clear: true}、select {name: ''};onstop 回滚和智能体离开时会自动移除标记。

缓动 ease — 类似 CSS 时间函数:linear, ease, ease-in, ease-out, ease-in-out, smooth, cubic-bezier(x1,y1,x2,y2) 或关于 t(0…1)的公式:1-pow(1-t,2)、t*t*t*(t*(6*t-15)+10)。

重叠:{cmd, args, then: [steps], before_end: ms | after_begin: ms | when: "condition"} — 嵌套步骤与该命令同时运行。示例:绕地球运行,转向木卫一,飞向它:

[ {"cmd": "fly", "args": {"name": "Earth", "size": 0.4, "rel": {"to": "sun", "deg": 0, "elev": 10}, "ms": 3000, "ease": "ease-in-out"}, "wait": true},
  {"cmd": "orbit", "args": {"speed": 60, "ms": 6000, "ease": "ease-in"},
   "when": "edge('Io','Earth') < 15", "then": [{"cmd": "look", "args": {"to": "Io", "ms": 1500}, "wait": true}], "wait": true},
  {"cmd": "fly", "args": {"name": "Io", "size": 0.15, "rel": {"to": "Jupiter", "deg": 180, "elev": 15}, "ms": 8000, "ease": "ease-out"}, "wait": true} ]

沙箱、绘图、宇航员

cmdargs说明
spawnid, like, diameter, color, at: {near, dist, dir} | pos, vel你自己的天体:某个天体或岩石的副本;它会进入围绕主导天体的圆轨道(按物理规律,而非直线)
moveid, vel | impact: {target, at}新速度或「在某一时刻撞击某个天体」
despawn / objectsid | all移除 / 列出
drawitems | clear | remove太空中的图形,用于调试和教学:arrow(vel、sun、north、{sum})、axes(roll/pitch/yaw)、line、dash、path、angle(弧线和度数)、project(向天体投影、高度)、point、text;label — 沿线的文字
flybyscraft, on航天器路径上的引力弹弓点:「天体 · 日期」及弹弓带来的效果;on: false — 移除
sitesbody | code, group, ids, light, on着陆点(月球、火星、金星、土卫六):点和场景内的卡片 — 照片、名称、年份、描述;group — 组合视图(apollo、luna、rovers、new、farside、first、china、mars、chryse、venera;天体由 group 决定,body 可省略);light: true — 位于白天一侧的着陆点;返回供 fly 使用的 lat、lon、alt;on: false — 移除
astroaction: goto | view | shield | land | exit, name, tpd, yaw, pitch, on, text宇航员:锚定到任何对象(天体、ISS、NORAD 编号、小行星);view — 看向人物;shield — 移除/设置护盾、自己的消息

页面控制台 — 反引号键(俄文键盘布局上为 ё)。

工作方式

  1. 阅读本页(help),用代码 connect。
  2. 向页面索取数据:search / open(天体)、body(大小、受光面)、where(位置)、find(事件)、layers、state。
  3. 根据这些数据准备动作:一个 play 脚本,或让用户逐页翻看的幻灯片 deck,或 buttons。
  4. 运行;用 screenshot 和 logs 检查;调整。

5. 示例:「给我讲讲行星」

connect {code}
layers → layer {key, visible:false} 关闭除 oplanets 之外的所有图层
music {theme: 0, volume: 0.4};  hide_ui {on: true}
play {steps: [
  {cmd:"fly", args:{name:"Mars"}, wait: 3800},
  {cmd:"camera", args:{yaw: …, pitch: …, dist: …, ms: 3000}, wait: 3200},   // 数值取自 body {name:"Mars"}
  {cmd:"say", args:{title:"Mars", sub:"228 million km · light takes 13 min", text:"…", audio:"mars.mp3"}, wait: "speech"}, // 语音由浏览器中的 Piper 提供(ru ruslan,en amy);audio — 用现成的 mp3 代替
  …
]}

6. 资源

规则:页面只接受这些命令;不能跳转到其他网站,不涉及个人数据。请让用户始终保有控制权 — 他们可以随时点击「断开连接」。