mengrru/spherse/spherse-use-ui-sdk

Claude Code and agent skill by mengrru/spherse

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

在 Spherse 的 HTML 内容中使用注入的 window.spherse SDK 调用 App 能力(创建会话并获取会话 ID、静默后台发送消息、打开已有会话、打开文件、向会话发消息、读写数据、枚举数据、目录列表、文件元信息、订阅文件变化、只读查询项目信息、弹 toast 提示)

Install

npx vskill@latest install mengrru/spherse/spherse-use-ui-sdk

SKILL.md

UI SDK — window.spherse

Spherse 中的 HTML 内容(欢迎页、自定义侧边面板、Content Browser 预览、聊天 HtmlCard)都在 iframe 中展示。App 会自动向每个 HTML 注入一个零依赖的 SDK 脚本,暴露 window.spherse(另有别名 window.Spherse,其它大小写不可用)。你不需要手写 postMessage、不需要内联 Promise wrapper、不需要引入任何脚本标签 —— 直接用即可。

快速上手

html
<button onclick="spherse.openFile('world/characters.md')">打开角色档案</button>
<button onclick="startWriting()">开始写作</button>
<script>
function startWriting() {
spherse.createSession({ agentId: "writer", message: "开始新的写作会话" });
}
</script>

SDK 已由 App 注入,不要再自己写 <script> 加载它,也不要复制 spherseCall 之类的 wrapper —— 那些都已内置。

API 总览

window.spherse 提供以下能力:

类别方法说明
触发型(fire-and-forget)openSession / openFile / openExternalLink / floatSession / unfloatSession / floatContent / unfloatContent / emitAgentTriggerEvent / toast单向触发,无返回值
请求型(Promise)createSession(params) → Promise<{ sessionId }>创建会话,返回新会话 ID
请求型(Promise)sendMessage(params) → Promise等待发送结果
请求型(Promise)data.get / data.set / data.delete / data.keys / data.entries / data.mutatekey-value 持久化 + manifest 结构性变更
请求型(Promise)uploadFile(params) → Promise<{ path, bytes, renamed }>把 Blob/File 上传到项目目录
请求型(Promise)api.call(op, args) 及 api.* 命名方法只读查询项目信息(agents / sessions / content / fileTree)
事件型events.on("file:update", filter, handler)订阅指定项目文件的变化信号
事件型events.on("navigate", handler)感知宿主主视图跳转(订阅即回放当前页面)
运行时spherse.runtime(同步读)/ spherse.getRuntime()(Promise)获取当前会话上下文(仅 HtmlCard 有值)

所有请求型方法都返回 Promise,内部已处理 requestId 匹配与超时(默认 10 秒,uploadFile 为 60 秒),失败时 reject。

运行时上下文(聊天 HtmlCard 专属)

当 HTML 作为聊天 HtmlCard 渲染时,spherse.runtime 携带当前会话信息;Welcome Page、自定义侧边面板与 Content Browser 预览中为 null,且这些场景下 await spherse.getRuntime() 永远不会 resolve——不要在页面加载时调用它。

  • 交互式卡片(用户点击才触发):直接读 spherse.runtime
  • 加载即使用:用 await spherse.getRuntime()(内部已处理竞态,无需自己注册 message 监听)
javascript
const rt = await spherse.getRuntime();
// rt.sessionId / rt.agentId / rt.projectId
await spherse.sendMessage({ sessionId: rt.sessionId, message: "继续分析" });

触发型 Action

spherse.openFile(path) / spherse.openFile(params)

在 Content Browser 中打开项目文件。path 可传字符串或对象 { path, float }。

参数类型必填说明
pathstring是项目内文件路径
floatboolean否为 true 时在浮窗中打开(仅 desktop;web 端忽略并回退到主面板导航)
javascript
spherse.openFile("world/characters/主角设定.md");
// 直接以浮窗打开
spherse.openFile({ path: "world/characters/主角设定.md", float: true });
与 `floatContent` 的区别:openFile({ float: true }) 是「一步直接浮窗打开」;floatContent 用于把文件浮窗化(可多窗口并存)。两者可按场景选用。
spherse.openSession(sessionId)

打开已有会话并导航到聊天页面,不发消息。sessionId 可传字符串或 { sessionId, float }。float 为 true 时在浮窗中打开(desktop),否则在主面板打开。

这是「只跳转、不发消息」的正确方式。sendMessage 只用于发送消息。
javascript
const rt = await spherse.getRuntime();
spherse.openSession(rt.sessionId);
// 或浮窗打开
spherse.openSession({ sessionId: rt.sessionId, float: true });
spherse.openExternalLink(url)

在系统默认浏览器中打开外部链接(http/https/mailto/tel)。HTML 中的外部链接若用原生 <a href>,会在 iframe 内原地跳转,应改用本方法。url 可传字符串或 { url }。

javascript
spherse.openExternalLink("https://example.com/reference");
仅 http/https/mailto/tel 协议生效,其它协议会被静默忽略。
spherse.floatSession(sessionId) / spherse.unfloatSession()

将指定会话显示为浮窗 / 关闭当前浮窗。sessionId 可传字符串或 { sessionId }。一次只能有一个浮窗。

spherse.floatContent(path) / spherse.unfloatContent(path)

将项目内文件以浮窗形式打开 / 关闭。多个文件可同时浮窗。

spherse.emitAgentTriggerEvent(params)

触发自定义事件,激活配置了「事件触发器」的 agent。

参数类型必填说明
eventNamestring是自定义事件名(不能以 sp: 开头,该前缀为系统保留)
payloadstring否事件附带数据,会通过 {{payload}} 注入触发器消息模板
javascript
spherse.emitAgentTriggerEvent({ eventName: "daily-review", payload: "第3章" });
spherse.toast(params)

在 App 中弹出一条 toast 提示。message 必填,variant / description 可选。fire-and-forget,无返回值。

参数类型必填说明
messagestring是提示正文
variantstring否"default" \"success" \"error" \"warning" \"info",默认 default
descriptionstring否次要说明文字
javascript
spherse.toast({ variant: "success", message: "已保存", description: "world/game.html" });
spherse.toast({ variant: "error", message: "保存失败,请重试" });

请求型 Action — 创建会话

spherse.createSession(params) → Promise<{ sessionId }>

创建新会话,成功时 resolve { sessionId }(新会话的 ID),默认导航到聊天页面。

参数类型必填说明
agentIdstring否目标 agent 的 ID(UUID,与 agentSlug 二选一,同时提供时以 agentId 为准)
agentSlugstring否目标 agent 的 slug(即 agent 目录名,形如 writer-a1b2c3,可在 agent 右键菜单「复制 ID」获取)
messagestring否初始消息内容(open: false 时为排队语义:等会话被打开后才发出)
namestring否会话名称(作为会话标题显示在会话列表中;空白字符串会被忽略)
openboolean否为 false 时只创建会话、不跳转不浮窗;省略即默认打开
floatboolean否为 true 时在浮窗中打开(与 open: false 同给时以 open: false 为准)
  • 成功:resolve { sessionId: string },可继续用于 sendMessage / openSession / floatSession
  • 失败:reject(agent_not_found / create_failed)
javascript
const { sessionId } = await spherse.createSession({ agentId: "writer", message: "请帮我扩展这段设定" });
// 或用 slug
spherse.createSession({ agentSlug: "writer-a1b2c3" });
// 创建时命名(显示在会话列表)
spherse.createSession({ agentSlug: "writer-a1b2c3", name: "角色设定整理" });

后台会话 compose 模式(创建即静默执行,不打开任何 UI):

javascript
const { sessionId } = await spherse.createSession({ agentId: "writer", open: false });
await spherse.sendMessage({ sessionId, message: "后台整理设定集", open: false });

请求型 Action — 发送消息

spherse.sendMessage(params) → Promise<void>

向已有会话发送消息,默认导航到聊天页面。message 必填 —— 这是发消息动作,不能省略 message。如需只打开已有会话不发消息,请用 `spherse.openSession`。

参数类型必填说明
sessionIdstring是目标会话 ID
messagestring是消息内容
openboolean否为 false 时静默发送:不跳转不浮窗,消息直接送达 server 立即执行(会话未打开也能发送)
floatboolean否为 true 时在浮窗中打开(与 open: false 同给时以 open: false 为准)

返回 Promise:

  • resolve:消息已发出(目标会话已打开时走实时通道,未打开时由 server 直接执行并持久化)
  • session_busy:目标会话正在生成中(含后台运行),消息不会被发送,应稍后重试
  • session_not_found / send_failed:会话不存在或发送失败
javascript
const rt = await spherse.getRuntime();
try {
await spherse.sendMessage({ sessionId: rt.sessionId, message: "继续" });
} catch (e) {
// 会话忙碌或发送失败
}

请求型 Action — key-value 数据

data.* 系列在项目内的 .data.json 文件中读写 key-value。通常将数据文件放在 HTML 同级并按页面命名;多个页面共用一份业务数据时也可以显式指定同一个文件。

需要设计页面与 Agent 共同读写的数据型应用时,加载 spherse-build-data-app skill;本节作为页面侧 API 参数与返回值参考。

spherse.data.get(params) → Promise<any>
参数类型必填说明
filestring是数据文件路径(如 world/game.data.json)
keystring是要读取的 key

返回值:对应的 value(任意 JSON 类型),key 不存在时返回 null。

javascript
const score = await spherse.data.get({ file: "world/game.data.json", key: "score" });
spherse.data.set(params) → Promise<any>
参数类型必填说明
filestring是数据文件路径
keystring是key 名
valueany是任意 JSON 可序列化值

返回写入后的 value。文件不存在时自动创建。

javascript
await spherse.data.set({ file: "world/game.data.json", key: "score", value: 100 });
await spherse.data.set({ file: "world/game.data.json", key: "player", value: { name: "Alice", hp: 80 } });
spherse.data.delete(params) → Promise<true>
javascript
await spherse.data.delete({ file: "world/game.data.json", key: "score" });
spherse.data.mutate(params) → Promise<object>

执行数据文件 $manifest 中声明的业务命名 mutation(与 agent 的 mutate_data 同一入口)。结构性写入(新增/修改/删除数组条目等)优先用此接口,不要用 data.set 传整个数组——避免与 agent 并发写同一集合时互相覆盖。append 返回新增条目(包含 auto 生成的 id、时间等字段),update 返回更新后的条目,remove 返回被删除的条目,set 返回更新后的目标对象。

参数类型必填说明
filestring是数据文件路径
namestring是mutation 入口名(见文件 $manifest,或让生成页面的 agent 告知)
argsobject否入口参数(必填字段见 manifest 声明;identity/match 字段按名传入)
idempotencyKeystring否幂等键:当前运行期间同一 key 重试返回首次结果
javascript
const todo = await spherse.data.mutate({ file: "board.data.json", name: "addTodo", args: { title: "买牛奶" }, idempotencyKey: "add-milk-1" });
console.log(todo.id); // manifest 中 auto.id 生成的 UUID
await spherse.data.mutate({ file: "board.data.json", name: "setTodoStatus", args: { id: "abc", status: "done" } });
spherse.data.keys(params) → Promise<string[]>

返回数据文件中所有顶层 key 列表。文件不存在时返回 []。

javascript
const keys = await spherse.data.keys({ file: "world/game.data.json" });
// → ["score", "name", "items"]
spherse.data.entries(params) → Promise<Record<string, any>>

返回数据文件中全部 key-value 对象。文件不存在时返回 {}。

javascript
const all = await spherse.data.entries({ file: "world/game.data.json" });
// → { score: 100, name: "Alice", items: [1, 2] }
数据文件命名规范
  • 文件名必须以 .data.json 结尾;推荐与 HTML 同级并按页面命名(world/game.html → world/game.data.json),共享业务数据也可由多个页面指定同一文件
  • 顶层 JSON object,仅支持顶层 key 操作(不支持 a.b.c 嵌套路径;key 中的点不会被解释为路径)
  • value 支持任意 JSON 可序列化类型
  • data.set / data.delete 是key 级原子操作:单次写入不会产生半截 JSON;同 key 多次写入按执行顺序生效,最终值以后一次为准
  • 写入粒度约定:data.set 适合单值/标量/低冲突数据;集合类(数组)的结构性增删改必须走 `data.mutate`——data.set 传整个数组会覆盖 agent 并发写入的条目(丢失更新)
  • 数据文件损坏(非法 JSON)时 data.* 返回错误(ok:false),不会静默把文件重置为空
  • 顶层 $ 前缀键为平台保留(如 $manifest):data.set 拒绝写入、data.keys / data.entries 不返回它们
为数据文件声明 $manifest(结构性数据推荐)

数据会随使用增长、且希望 agent 后续能直接按业务语义读写(而不是整文件读改写)时,生成页面时应在数据文件根部内嵌 $manifest,声明业务命名的查询/变更入口。agent 将通过 query_data / mutate_data 工具按这些入口精准读写,schema 校验也保证不会写坏页面渲染假设。数据建模方法见 spherse-build-data-app skill,HTML 落地约束见 spherse-write-html skill。

事件订阅 — 文件变化

spherse.events.on("file:update", filter, handler) → unsubscribe

订阅指定项目文件的变化信号。filter.path 支持两种写法:

  • ./data.json、../shared/data.json:基于当前 HTML 的 document.baseURI 解析,SDK 自动转换为项目相对路径,适合与 fetch("./data.json") 共用路径。
  • world/data.json:直接作为项目根目录相对路径。

App 只发送变化信号,不发送文件内容。收到信号后由页面重新 fetch 或调用其它 SDK 读取方法。绝对 URL、越过 preview 项目根目录的相对路径会被拒绝。

javascript
async function render() {
const data = await fetch("./atlas.data.json").then((response) => response.json());
// 使用 data 更新页面
}
const unsubscribe = spherse.events.on(
"file:update",
{ path: "./atlas.data.json" },
render,
);
render();

返回的 unsubscribe() 可重复调用且只会取消一次。页面卸载时 SDK 也会自动清理订阅。

handler 收到的事件结构:

javascript
{
path: "world/atlas.data.json"
}

handler 中的 path 始终是归一化后的项目根目录相对路径,即使订阅时传入的是 ./atlas.data.json。

file:update 的语义是“该文件可能已经变化”。handler 应重新读取目标文件并处理读取失败;短时间内同一路径的连续变化会被合并。操作系统底层的文件事件类型不会暴露给用户 HTML,因为它无法可靠区分创建、删除和编辑器的原子替换。

事件订阅 — 宿主导航

spherse.events.on("navigate", handler) → unsubscribe

感知宿主 App 主视图的跳转。订阅成功时立即收到一条当前页面状态(无需等待下次跳转),之后每次主视图变化再推送;返回的 unsubscribe() 同样幂等。

handler 收到的 payload 按当前页面类型带不同字段:

当前主视图payload
欢迎页{ kind: "welcome" }
聊天{ kind: "chat", sessionId }
文件预览(Content Browser){ kind: "file", path }(path 为项目相对路径)
内置浏览器{ kind: "browser", url }
javascript
spherse.events.on("navigate", (e) => {
if (e.kind === "file" && e.path === "todo/事务簿.html") {
highlightTodoTab();
}
});

注意 kind: "file" 指文件预览视图,与 file:update(文件内容变化信号)语义不同。聊天内定位消息(?messageId=)、应用内浮窗、split pane 的开关与切换不构成导航,不会推送事件。

请求型 Action — 上传文件

spherse.uploadFile(params) → Promise<{ path, bytes, renamed }>

把页面内的 Blob / File 上传到项目目录(与文件面板拖拽上传同路径:单文件上限 100MB,同名自动重命名 name (1).ext)。

参数类型必填说明
data`Blob \File`是待上传内容;File 自带文件名时可省 name
namestringBlob 时必填最终落盘文件名(显式给出时优先于 File.name)
dirPathstring否目标目录(项目根相对路径),省略 = 项目根

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/mengrru/spherse/spherse-use-ui-sdk/badge)](https://verified-skill.com/skills/mengrru/spherse/spherse-use-ui-sdk)

Something wrong with this skill or its page? Tell us.