@clawos-dev/clawd 0.2.256 → 0.2.258
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +796 -786
- package/dist/dispatch/mcp-server.cjs +36 -17
- package/dist/inbox/mcp-server.cjs +9 -7
- package/dist/peer-ops/mcp-server.cjs +7 -5
- package/dist/persona-defaults/persona-app-builder/CLAUDE.md +36 -56
- package/dist/persona-defaults/persona-clawd-butler/.claude/skills/clawd-orchestration/SKILL.md +2 -2
- package/dist/persona-defaults/persona-clawd-butler/CLAUDE.md +2 -2
- package/dist/persona-defaults/persona-dataclaw-builder/CLAUDE.md +9 -20
- package/dist/rpc-tool/mcp-server.cjs +21209 -0
- package/dist/share-md-viewer-error.html +1 -1
- package/dist/share-md-viewer.html +1 -1
- package/dist/share-ui/assets/{guest-kK-Ir45s.css → guest-DTAqwGDc.css} +1 -1
- package/dist/share-ui/assets/{guest-hgeG05IO.js → guest-SFe_5tKr.js} +65 -65
- package/dist/share-ui/guest.html +2 -2
- package/dist/shift/mcp-server.cjs +15 -16
- package/dist/ticket/mcp-server.cjs +10 -5
- package/package.json +1 -1
|
@@ -21185,7 +21185,12 @@ var AttachmentGroupAddArgs = external_exports.object({
|
|
|
21185
21185
|
sessionId: external_exports.string().min(1),
|
|
21186
21186
|
relPath: external_exports.string().min(1),
|
|
21187
21187
|
/** owner 手动加入时可选备注;agent 自动入清单不走此 RPC */
|
|
21188
|
-
label: external_exports.string().optional()
|
|
21188
|
+
label: external_exports.string().optional(),
|
|
21189
|
+
/**
|
|
21190
|
+
* 统一 RPC 鉴权(spec 2026-07-28 §8 决策 B):agent 经 clawd-rpc 调用持 owner token,
|
|
21191
|
+
* ctx 已推不出"这是 agent";body 显式标 origin:'agent' 保留群清单 from 的展示语义。
|
|
21192
|
+
*/
|
|
21193
|
+
origin: external_exports.literal("agent").optional()
|
|
21189
21194
|
});
|
|
21190
21195
|
var AttachmentGroupAddResponseSchema = external_exports.object({
|
|
21191
21196
|
entry: GroupFileEntrySchema
|
|
@@ -22776,6 +22781,14 @@ var DispatchListArgsSchema = external_exports.object({
|
|
|
22776
22781
|
var DispatchListResultSchema = external_exports.object({
|
|
22777
22782
|
records: external_exports.array(DispatchRecordSchema)
|
|
22778
22783
|
});
|
|
22784
|
+
var DispatchGetArgsSchema = external_exports.object({
|
|
22785
|
+
dispatchId: external_exports.string().min(1)
|
|
22786
|
+
});
|
|
22787
|
+
var DispatchGetOkSchema = external_exports.object({
|
|
22788
|
+
type: external_exports.literal("personaDispatch:get:ok"),
|
|
22789
|
+
status: DispatchStatusSchema,
|
|
22790
|
+
outcome: DispatchOutcomeSchema.optional()
|
|
22791
|
+
});
|
|
22779
22792
|
|
|
22780
22793
|
// ../protocol/src/log.ts
|
|
22781
22794
|
var LOG_SOURCES = ["daemon", "main", "renderer"];
|
|
@@ -23543,7 +23556,7 @@ async function handlePersonaDispatchToolCall(input, ctx) {
|
|
|
23543
23556
|
isError: true
|
|
23544
23557
|
};
|
|
23545
23558
|
}
|
|
23546
|
-
const url = `${ctx.daemonUrl}/
|
|
23559
|
+
const url = `${ctx.daemonUrl}/rpc/personaDispatch:run`;
|
|
23547
23560
|
log("personaDispatch pre-fetch", { url });
|
|
23548
23561
|
let res;
|
|
23549
23562
|
try {
|
|
@@ -23551,9 +23564,11 @@ async function handlePersonaDispatchToolCall(input, ctx) {
|
|
|
23551
23564
|
method: "POST",
|
|
23552
23565
|
headers: {
|
|
23553
23566
|
"content-type": "application/json",
|
|
23554
|
-
|
|
23567
|
+
authorization: `Bearer ${ctx.daemonToken}`
|
|
23555
23568
|
},
|
|
23556
23569
|
body: JSON.stringify({
|
|
23570
|
+
// sessionId 是普通 body 参数(run handler 用作 sourceSessionId 归因),鉴权只看 Bearer
|
|
23571
|
+
sessionId: ctx.sessionId,
|
|
23557
23572
|
// 续派时可省 targetPersona(daemon 从台账取)
|
|
23558
23573
|
...input.targetPersona ? { targetPersona: input.targetPersona } : {},
|
|
23559
23574
|
prompt: input.prompt,
|
|
@@ -23609,10 +23624,12 @@ async function handlePersonaDispatchToolCall(input, ctx) {
|
|
|
23609
23624
|
async function handlePersonaDispatchListToolCall(input, ctx) {
|
|
23610
23625
|
const f = ctx.fetch ?? fetch;
|
|
23611
23626
|
const filter = input.sourceSessionId ?? ctx.sessionId;
|
|
23612
|
-
const url = `${ctx.daemonUrl}/
|
|
23627
|
+
const url = `${ctx.daemonUrl}/rpc/personaDispatch:list`;
|
|
23613
23628
|
log("personaDispatchList pre-fetch", { url, filter });
|
|
23614
|
-
const headers = {
|
|
23615
|
-
|
|
23629
|
+
const headers = {
|
|
23630
|
+
"content-type": "application/json",
|
|
23631
|
+
authorization: `Bearer ${ctx.daemonToken}`
|
|
23632
|
+
};
|
|
23616
23633
|
let res;
|
|
23617
23634
|
try {
|
|
23618
23635
|
res = await f(url, {
|
|
@@ -23676,10 +23693,12 @@ async function handlePersonaDispatchCompleteToolCall(input, ctx) {
|
|
|
23676
23693
|
text: input.text ?? "",
|
|
23677
23694
|
filePaths: input.filePaths
|
|
23678
23695
|
};
|
|
23679
|
-
const url = `${ctx.daemonUrl}/
|
|
23696
|
+
const url = `${ctx.daemonUrl}/rpc/personaDispatch:complete`;
|
|
23680
23697
|
log("personaDispatchComplete pre-fetch", { url, hasSessionId: Boolean(ctx.sessionId) });
|
|
23681
|
-
const headers = {
|
|
23682
|
-
|
|
23698
|
+
const headers = {
|
|
23699
|
+
"content-type": "application/json",
|
|
23700
|
+
authorization: `Bearer ${ctx.daemonToken}`
|
|
23701
|
+
};
|
|
23683
23702
|
try {
|
|
23684
23703
|
const res = await f(url, {
|
|
23685
23704
|
method: "POST",
|
|
@@ -23707,6 +23726,7 @@ async function handlePersonaDispatchCompleteToolCall(input, ctx) {
|
|
|
23707
23726
|
}
|
|
23708
23727
|
async function main() {
|
|
23709
23728
|
const daemonUrl = process.env.CLAWD_DAEMON_URL;
|
|
23729
|
+
const daemonToken = process.env.CLAWD_DAEMON_TOKEN;
|
|
23710
23730
|
const sessionId = process.env.CLAWD_SESSION_ID;
|
|
23711
23731
|
const dispatchId = process.env.CLAWD_DISPATCH_ID;
|
|
23712
23732
|
log("boot", {
|
|
@@ -23716,10 +23736,10 @@ async function main() {
|
|
|
23716
23736
|
hasDispatchId: Boolean(dispatchId),
|
|
23717
23737
|
logPath: DISPATCH_LOG ?? "(stderr)"
|
|
23718
23738
|
});
|
|
23719
|
-
if (!daemonUrl) {
|
|
23720
|
-
log("boot refusing \u2014 CLAWD_DAEMON_URL not set");
|
|
23739
|
+
if (!daemonUrl || !daemonToken) {
|
|
23740
|
+
log("boot refusing \u2014 CLAWD_DAEMON_URL/TOKEN not set");
|
|
23721
23741
|
process.stderr.write(
|
|
23722
|
-
"dispatch-mcp-server: CLAWD_DAEMON_URL not set; refusing to boot\n"
|
|
23742
|
+
"dispatch-mcp-server: CLAWD_DAEMON_URL/CLAWD_DAEMON_TOKEN not set; refusing to boot\n"
|
|
23723
23743
|
);
|
|
23724
23744
|
process.exit(1);
|
|
23725
23745
|
}
|
|
@@ -23757,7 +23777,7 @@ async function main() {
|
|
|
23757
23777
|
)
|
|
23758
23778
|
}
|
|
23759
23779
|
},
|
|
23760
|
-
async (input) => handlePersonaDispatchToolCall(input, { daemonUrl, sessionId })
|
|
23780
|
+
async (input) => handlePersonaDispatchToolCall(input, { daemonUrl, daemonToken, sessionId })
|
|
23761
23781
|
);
|
|
23762
23782
|
server.registerTool(
|
|
23763
23783
|
"personaDispatchComplete",
|
|
@@ -23771,9 +23791,8 @@ async function main() {
|
|
|
23771
23791
|
reason: external_exports.string().optional().describe("failure reason (required when isFailure=true)")
|
|
23772
23792
|
}
|
|
23773
23793
|
},
|
|
23774
|
-
// sessionId
|
|
23775
|
-
|
|
23776
|
-
async (input) => handlePersonaDispatchCompleteToolCall(input, { daemonUrl, sessionId, dispatchId })
|
|
23794
|
+
// sessionId 只是普通参数(complete schema 会 strip),鉴权走 Bearer。
|
|
23795
|
+
async (input) => handlePersonaDispatchCompleteToolCall(input, { daemonUrl, daemonToken, sessionId, dispatchId })
|
|
23777
23796
|
);
|
|
23778
23797
|
server.registerTool(
|
|
23779
23798
|
"personaDispatchList",
|
|
@@ -23784,7 +23803,7 @@ async function main() {
|
|
|
23784
23803
|
sourceSessionId: external_exports.string().optional().describe("filter by source session id (omit = current session)")
|
|
23785
23804
|
}
|
|
23786
23805
|
},
|
|
23787
|
-
async (input) => handlePersonaDispatchListToolCall(input, { daemonUrl, sessionId })
|
|
23806
|
+
async (input) => handlePersonaDispatchListToolCall(input, { daemonUrl, daemonToken, sessionId })
|
|
23788
23807
|
);
|
|
23789
23808
|
await server.connect(new StdioServerTransport());
|
|
23790
23809
|
}
|
|
@@ -21126,10 +21126,11 @@ function log(msg, info) {
|
|
|
21126
21126
|
}
|
|
21127
21127
|
async function handleSendDmCall(params, ctx = {}) {
|
|
21128
21128
|
const daemonUrl = process.env.CLAWD_DAEMON_URL;
|
|
21129
|
+
const daemonToken = process.env.CLAWD_DAEMON_TOKEN;
|
|
21129
21130
|
const sessionId = process.env.CLAWD_SESSION_ID;
|
|
21130
|
-
if (!daemonUrl) {
|
|
21131
|
-
log("sendDm CLAWD_DAEMON_URL missing");
|
|
21132
|
-
return { content: [{ type: "text", text: "Error: CLAWD_DAEMON_URL not set; cannot call daemon." }], isError: true };
|
|
21131
|
+
if (!daemonUrl || !daemonToken) {
|
|
21132
|
+
log("sendDm CLAWD_DAEMON_URL/TOKEN missing");
|
|
21133
|
+
return { content: [{ type: "text", text: "Error: CLAWD_DAEMON_URL/CLAWD_DAEMON_TOKEN not set; cannot call daemon." }], isError: true };
|
|
21133
21134
|
}
|
|
21134
21135
|
if (!sessionId) {
|
|
21135
21136
|
log("sendDm CLAWD_SESSION_ID missing");
|
|
@@ -21142,10 +21143,11 @@ async function handleSendDmCall(params, ctx = {}) {
|
|
|
21142
21143
|
log("sendDm pre-fetch", { hasPeerDeviceId: Boolean(params.peerDeviceId), textLen: params.text.length });
|
|
21143
21144
|
let res;
|
|
21144
21145
|
try {
|
|
21145
|
-
res = await f(`${daemonUrl}/
|
|
21146
|
+
res = await f(`${daemonUrl}/rpc/inbox:sendDm`, {
|
|
21146
21147
|
method: "POST",
|
|
21147
|
-
headers: { "content-type": "application/json",
|
|
21148
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${daemonToken}` },
|
|
21148
21149
|
body: JSON.stringify({
|
|
21150
|
+
sessionId,
|
|
21149
21151
|
text: params.text,
|
|
21150
21152
|
...params.peerDeviceId ? { peerDeviceId: params.peerDeviceId } : {}
|
|
21151
21153
|
})
|
|
@@ -21171,8 +21173,8 @@ async function handleSendDmCall(params, ctx = {}) {
|
|
|
21171
21173
|
return { content: [{ type: "text", text: JSON.stringify(json.result) }] };
|
|
21172
21174
|
}
|
|
21173
21175
|
async function main() {
|
|
21174
|
-
if (!process.env.CLAWD_DAEMON_URL) {
|
|
21175
|
-
process.stderr.write("inbox-mcp-server: CLAWD_DAEMON_URL not set; refusing to boot\n");
|
|
21176
|
+
if (!process.env.CLAWD_DAEMON_URL || !process.env.CLAWD_DAEMON_TOKEN) {
|
|
21177
|
+
process.stderr.write("inbox-mcp-server: CLAWD_DAEMON_URL/CLAWD_DAEMON_TOKEN not set; refusing to boot\n");
|
|
21176
21178
|
process.exit(1);
|
|
21177
21179
|
}
|
|
21178
21180
|
const server = new McpServer({ name: "clawd-inbox", version: "0.1.0" });
|
|
@@ -39530,10 +39530,11 @@ async function handlePeerExecToolCall(input, ctx) {
|
|
|
39530
39530
|
const f = ctx.fetch ?? fetch;
|
|
39531
39531
|
let res;
|
|
39532
39532
|
try {
|
|
39533
|
-
res = await f(`${ctx.daemonUrl}/
|
|
39533
|
+
res = await f(`${ctx.daemonUrl}/rpc/peerExec:run`, {
|
|
39534
39534
|
method: "POST",
|
|
39535
|
-
headers: { "content-type": "application/json",
|
|
39535
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${ctx.daemonToken}` },
|
|
39536
39536
|
body: JSON.stringify({
|
|
39537
|
+
sessionId: ctx.sessionId,
|
|
39537
39538
|
peerDeviceId: input.peerDeviceId,
|
|
39538
39539
|
command: input.command,
|
|
39539
39540
|
...input.timeoutMs ? { timeoutMs: input.timeoutMs } : {}
|
|
@@ -39564,6 +39565,7 @@ ${r.stderr}`].join("\n");
|
|
|
39564
39565
|
async function main() {
|
|
39565
39566
|
configureFetchForLongWait();
|
|
39566
39567
|
const daemonUrl = process.env.CLAWD_DAEMON_URL;
|
|
39568
|
+
const daemonToken = process.env.CLAWD_DAEMON_TOKEN;
|
|
39567
39569
|
const sessionId = process.env.CLAWD_SESSION_ID;
|
|
39568
39570
|
const server = new McpServer({ name: "peer-ops", version: "0.1.0" });
|
|
39569
39571
|
server.registerTool(
|
|
@@ -39577,14 +39579,14 @@ async function main() {
|
|
|
39577
39579
|
timeoutMs: external_exports.number().int().positive().optional().describe("timeout in ms (default 30000, capped at 300000)")
|
|
39578
39580
|
}
|
|
39579
39581
|
},
|
|
39580
|
-
async (input) => handlePeerExecToolCall(input, { daemonUrl, sessionId })
|
|
39582
|
+
async (input) => handlePeerExecToolCall(input, { daemonUrl, daemonToken, sessionId })
|
|
39581
39583
|
);
|
|
39582
39584
|
await server.connect(new StdioServerTransport());
|
|
39583
39585
|
}
|
|
39584
39586
|
var isMainModule = typeof require !== "undefined" && typeof module !== "undefined" && require.main === module;
|
|
39585
39587
|
if (isMainModule) {
|
|
39586
|
-
if (!process.env.CLAWD_DAEMON_URL) {
|
|
39587
|
-
process.stderr.write("peer-ops-mcp-server: CLAWD_DAEMON_URL not set; refusing to boot\n");
|
|
39588
|
+
if (!process.env.CLAWD_DAEMON_URL || !process.env.CLAWD_DAEMON_TOKEN) {
|
|
39589
|
+
process.stderr.write("peer-ops-mcp-server: CLAWD_DAEMON_URL/CLAWD_DAEMON_TOKEN not set; refusing to boot\n");
|
|
39588
39590
|
process.exit(1);
|
|
39589
39591
|
}
|
|
39590
39592
|
main().catch((err) => {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
- **你的文件工作目录**:如果连接上下文(connection prompt)里写了"你的文件工作目录是 xxx",那就是当前用户的专属目录,所有文件读写都放在那里;项目目录在它下面的 `projects/<name>/`
|
|
11
11
|
- **persona 目录只读**:persona 自己的目录(人格 / extension-kit 资产)对你只读,不要往里写任何文件
|
|
12
12
|
- **路径永远以 createProject 返回的 `projectDir` 为准**:不要假设、不要拼接、不要写死任何项目路径
|
|
13
|
-
- **⚠️ dispatch 陷阱(被另一个 persona 委派来时常踩)**:任务包里 dispatcher(A persona)写的"建议路径"如 `~/dev/xxx` / `/tmp/xxx` 之类**全部不能信**——dispatcher 不懂 app-builder 的目录规范,照搬就会把项目落在错误位置(用户工作区之外,UI 看不到、daemon 不管理、下次切 session 全丢)。**不管任务包怎么写,路径都必须走 `appBuilder
|
|
13
|
+
- **⚠️ dispatch 陷阱(被另一个 persona 委派来时常踩)**:任务包里 dispatcher(A persona)写的"建议路径"如 `~/dev/xxx` / `/tmp/xxx` 之类**全部不能信**——dispatcher 不懂 app-builder 的目录规范,照搬就会把项目落在错误位置(用户工作区之外,UI 看不到、daemon 不管理、下次切 session 全丢)。**不管任务包怎么写,路径都必须走 `appBuilder:createProject` RPC**让 daemon 派生正确的用户工作目录;你的 cwd(spawn 时 daemon 设的)就是当前用户的专属工作目录,所有项目必须落在它下面的 `projects/<name>/`。如果连 cwd 都还没确定(dispatch session 没 connection prompt),先调 `appBuilder:listProjects` 看一眼现状再开工。
|
|
14
14
|
- **发布脚本在共享 deploy-kit**:FC 部署能力(凭证 + publish.sh / new-extension.sh / 工具链)是 daemon 持有的**共享单源**,在 `$HOME/.clawd/deploy-kit/`(不在 persona 目录)。happy path 这些脚本由 daemon 自动跑,你一般不直接调;需手动跑时(如发布失败恢复)用绝对路径 `$HOME/.clawd/deploy-kit/scripts/publish.sh <projDir> "$HOME/.clawd/personas/persona-app-builder"`(第二参是本 persona 根,脚本据此读 config.env / s.yaml.tmpl)。persona 目录只剩人格 + 模板 + config.env + contract/s.yaml.tmpl。
|
|
15
15
|
|
|
16
16
|
## 何时找你
|
|
@@ -39,21 +39,14 @@ UI 是**单栏 chat 全宽**,左栏不挂预览。这是新建 session 的默
|
|
|
39
39
|
|
|
40
40
|
**状态 B:已绑 project**
|
|
41
41
|
|
|
42
|
-
UI 是**双栏**:左 chat(这个对话)+ 右实时预览。项目目录是 createProject 当时返回的 `projectDir`(用户工作目录下的 `projects/<name>/`),dev server 已自动起。继续聊需求、scaffold 业务代码、写功能。忘了路径就调 `appBuilder
|
|
42
|
+
UI 是**双栏**:左 chat(这个对话)+ 右实时预览。项目目录是 createProject 当时返回的 `projectDir`(用户工作目录下的 `projects/<name>/`),dev server 已自动起。继续聊需求、scaffold 业务代码、写功能。忘了路径就调 `appBuilder:listProjects` 再看一眼。
|
|
43
43
|
|
|
44
|
-
### 触发 createProject
|
|
44
|
+
### 触发 createProject(拿到名字后调 clawd-rpc)
|
|
45
45
|
|
|
46
|
-
daemon
|
|
47
|
-
- `CLAWD_DAEMON_URL` = `http://127.0.0.1:<port>`(daemon HTTP RPC base)
|
|
48
|
-
- `CLAWD_SESSION_ID` = 当前 session id
|
|
46
|
+
所有 daemon RPC 都走 `clawd-rpc` MCP 的 `call` tool(鉴权由 tool 内部处理,sessionId 自动补):
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
curl -sS -X POST "$CLAWD_DAEMON_URL/api/rpc/appBuilder.createProject" \
|
|
54
|
-
-H "Content-Type: application/json" \
|
|
55
|
-
-H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" \
|
|
56
|
-
-d '{"name":"<老板给的名字>"}'
|
|
48
|
+
```
|
|
49
|
+
call({ method: "appBuilder:createProject", args: { name: "<老板给的名字>" } })
|
|
57
50
|
```
|
|
58
51
|
|
|
59
52
|
返回:
|
|
@@ -73,11 +66,8 @@ createProject 成功后 daemon 自动:
|
|
|
73
66
|
|
|
74
67
|
1. **install 阶段**:
|
|
75
68
|
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
-H "Content-Type: application/json" \
|
|
79
|
-
-H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" \
|
|
80
|
-
-d '{"stage":"installing"}'
|
|
69
|
+
```
|
|
70
|
+
call({ method: "appBuilder:reportStage", args: { stage: "installing" } })
|
|
81
71
|
```
|
|
82
72
|
(spinner 切到 "安装依赖中… (2/3)")
|
|
83
73
|
|
|
@@ -85,21 +75,15 @@ createProject 成功后 daemon 自动:
|
|
|
85
75
|
|
|
86
76
|
2. **启动 dev server**:
|
|
87
77
|
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
-H "Content-Type: application/json" \
|
|
91
|
-
-H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" \
|
|
92
|
-
-d '{}'
|
|
78
|
+
```
|
|
79
|
+
call({ method: "appBuilder:startDevServer", args: {} })
|
|
93
80
|
```
|
|
94
81
|
daemon supervisor spawn 子进程跑 `devCommand`,自动推 stage='starting-dev-server' → 'running',UI 切 iframe。返回 `{"ok":true,"result":{"project":{...}}}` 表示 spawn 成功。
|
|
95
82
|
|
|
96
83
|
3. **任一阶段失败**(install / dev server 起不来):调 reportStage 上报 failed:
|
|
97
84
|
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
-H "Content-Type: application/json" \
|
|
101
|
-
-H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" \
|
|
102
|
-
-d '{"stage":"failed","reason":"pnpm install crashed: <一句话原因>"}'
|
|
85
|
+
```
|
|
86
|
+
call({ method: "appBuilder:reportStage", args: { stage: "failed", reason: "pnpm install crashed: <一句话原因>" } })
|
|
103
87
|
```
|
|
104
88
|
UI 右栏切红色错误 + reason。然后跟老板讨论修法。修好再继续。
|
|
105
89
|
|
|
@@ -114,46 +98,42 @@ createProject 成功后 daemon 自动:
|
|
|
114
98
|
|
|
115
99
|
## 可用 RPC 完整清单
|
|
116
100
|
|
|
117
|
-
**只有以下 6 个 method 可用**(assistant
|
|
101
|
+
**只有以下 6 个 method 可用**(assistant 调用面)。统一经 `clawd-rpc` MCP 的 `call` tool 调用(method 用 canonical 名,鉴权/sessionId 由 tool 处理)。
|
|
118
102
|
|
|
119
103
|
| method | 入参 | 用途 |
|
|
120
104
|
|---|---|---|
|
|
121
|
-
| `appBuilder
|
|
122
|
-
| `appBuilder
|
|
123
|
-
| `appBuilder
|
|
124
|
-
| `appBuilder
|
|
125
|
-
| `appBuilder
|
|
126
|
-
| `appBuilder
|
|
127
|
-
| `appBuilder.setActiveSession` | `{sessionId: string \| null}` | UI 切 session 时自动调,daemon 联动停老 / 恢复新 dev server。你不主动调 |
|
|
105
|
+
| `appBuilder:createProject` | `{name}` | 创建 project(含 scaffold) + 自动绑当前 session |
|
|
106
|
+
| `appBuilder:listProjects` | `{}` | 列所有 project(看 port / stage / 重名校验) |
|
|
107
|
+
| `appBuilder:reportStage` | `{stage, reason?}` | 上报阶段(仅 `installing` / `failed`) |
|
|
108
|
+
| `appBuilder:startDevServer` | `{force?: boolean}` | 起 dev server(默认幂等:isRunning=true → no-op;`force:true` 强制 stop+spawn,UI 刷新按钮专用) |
|
|
109
|
+
| `appBuilder:setProdUrl` | `{name, url}` | 发布上线后写公网 URL(UI 切线上预览) |
|
|
110
|
+
| `appBuilder:updateProjectPort` | `{name, newPort}` | 改端口(一般 UI 项目设置 menu 触发,你不主动调) |
|
|
128
111
|
|
|
129
112
|
### 红线:不在此列就不要瞎试
|
|
130
113
|
|
|
131
|
-
**这是完整清单**。不存在以下 method(试了会被 daemon 返 `
|
|
132
|
-
- ❌ `appBuilder
|
|
133
|
-
- ❌ `appBuilder
|
|
134
|
-
- ❌ `appBuilder
|
|
135
|
-
- ❌ `appBuilder
|
|
114
|
+
**这是完整清单**。不存在以下 method(试了会被 daemon 返 `METHOD_NOT_IMPLEMENTED`):
|
|
115
|
+
- ❌ `appBuilder:restartDevServer` —— 用 `startDevServer({force: true})` 强制重启(一般你不需要,UI 刷新按钮场景;assistant 走默认幂等就够)
|
|
116
|
+
- ❌ `appBuilder:deleteProject` —— 删 project = 老板在 clawd 删 session(自动联动)
|
|
117
|
+
- ❌ `appBuilder:status` / `appBuilder:listMethods` / 任何枚举类 —— 看本文档
|
|
118
|
+
- ❌ `appBuilder:installDeps` / `appBuilder:scaffold` —— install 是你跑的 shell,scaffold 是 daemon 自动跑
|
|
136
119
|
|
|
137
120
|
遇到清单外的能力需求 → 跟老板说,让他评估要不要加,不要自己发明 method 名瞎试。
|
|
138
121
|
|
|
139
|
-
### 其它可用 RPC(同
|
|
122
|
+
### 其它可用 RPC(同 call 套路)
|
|
140
123
|
|
|
141
|
-
- `appBuilder
|
|
142
|
-
- `appBuilder
|
|
143
|
-
- `appBuilder
|
|
124
|
+
- `appBuilder:listProjects` — 列所有 project(判 name 重名 / 查 port 用)
|
|
125
|
+
- `appBuilder:updateProjectPort` — 改 project 端口(一般 UI 项目设置抽屉做,你不主动调)
|
|
126
|
+
- `appBuilder:setProdUrl` — **publish 完成后调用**,把公网 URL 绑到 project:
|
|
144
127
|
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
-H "Content-Type: application/json" \
|
|
148
|
-
-H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" \
|
|
149
|
-
-d '{"name":"<project-name>","url":"https://<公网地址>"}'
|
|
128
|
+
```
|
|
129
|
+
call({ method: "appBuilder:setProdUrl", args: { name: "<project-name>", url: "https://<公网地址>" } })
|
|
150
130
|
```
|
|
151
131
|
|
|
152
132
|
daemon 写入 `.clawd-project.json.prodUrl`,UI 右栏预览自动切到线上 URL(取代旧 `<builder-preview url=... label="prod" />` marker 机制)。
|
|
153
133
|
|
|
154
134
|
### 删 project
|
|
155
135
|
|
|
156
|
-
不要用 RPC 删(`appBuilder
|
|
136
|
+
不要用 RPC 删(`appBuilder:deleteProject` 故意不暴露给你)。老板在 clawd 删 session 时 daemon 会自动联动停 dev server + 删 project 目录 —— 这是唯一删除路径。
|
|
157
137
|
|
|
158
138
|
## 发布流水线(daemon 自动跑 + 共享 deploy-kit 脚本)
|
|
159
139
|
|
|
@@ -210,7 +190,7 @@ set +a
|
|
|
210
190
|
|
|
211
191
|
一个 project 大致走这四段,按需推进、不必僵化:
|
|
212
192
|
|
|
213
|
-
1. **创建 project**(fresh session 进来时)—— 问老板想做啥 app + 反问名字 + 调 `appBuilder
|
|
193
|
+
1. **创建 project**(fresh session 进来时)—— 问老板想做啥 app + 反问名字 + 调 `appBuilder:createProject` RPC(详见上文「触发 createProject」)。已绑 session 跳过这一步
|
|
214
194
|
2. **开发前后端** —— scaffold 已由 daemon 在 createProject 时自动跑(你摸不到模板源);你直接在 projectDir 里写业务。复杂任务走 superpowers 节奏,简单任务 TodoWrite 跟踪(分级见下)
|
|
215
195
|
3. **接 Supabase** —— 数据/认证/存储走 supabase MCP,schema 以线上为准,新表加 `${APP_NAME}_${SLUG}_` 前缀(从 ext.conf 读)
|
|
216
196
|
4. **部署** —— 老板点「发布上线」按钮,daemon 自动跑 `deploy-kit/scripts/publish.sh`(build + 发 FC + 绑域名 + 验证 + 写 prodUrl),happy path 跳过你。仅发布失败时你接管手动重跑(见下文「发布上线」)。
|
|
@@ -263,20 +243,20 @@ await app.listen(Number(process.env.CLAWD_PREVIEW_PORT));
|
|
|
263
243
|
|
|
264
244
|
**告别 marker**:dev / prod 预览都不再需要你输出 `<builder-preview ...>` 标记(单栏 refactor 2026-06-02 §5.6.1 砍掉了所有 marker 路径)。
|
|
265
245
|
- **dev 预览**:UI 通过 session ↔ project ↔ port 数据流自动加载,daemon state 驱动
|
|
266
|
-
- **prod 预览**:跑完 `publish.sh` 后调 `appBuilder
|
|
246
|
+
- **prod 预览**:跑完 `publish.sh` 后调 `appBuilder:setProdUrl` RPC(见上文),daemon 写入 `.clawd-project.json.prodUrl` 后 UI 自动切线上 URL
|
|
267
247
|
|
|
268
248
|
### 发布上线(脚手架化 2026-06-03)
|
|
269
249
|
|
|
270
|
-
老板点 PreviewPane「发布上线」按钮走 **daemon RPC 脚手架**(`appBuilder
|
|
250
|
+
老板点 PreviewPane「发布上线」按钮走 **daemon RPC 脚手架**(`appBuilder:publish`),happy path 完全跳过你 —— daemon 自己 spawn `publish.sh`、解析 stage marker、调 `appBuilder:setProdUrl` 写 prodUrl,你不会收到任何 chat 消息。
|
|
271
251
|
|
|
272
252
|
**只有发布失败时**,daemon 会以普通消息发来一条以 `发布失败:[<stage>]` 开头的文本(含 stderr 错误摘要 + `.publish.log` 路径)。**收到这种消息时,你接管完成本次发布**:
|
|
273
253
|
|
|
274
254
|
1. 读项目目录下的 `.publish.log` 完整日志
|
|
275
255
|
2. 定位并修复问题(改源码 / 改 `ext.conf` / 检查 `.secrets/aliyun.env` 等)
|
|
276
256
|
3. 重跑 `bash "$HOME/.clawd/deploy-kit/scripts/publish.sh" <projDir> "$HOME/.clawd/personas/persona-app-builder"` 直到 exit 0 且输出公网 URL(第二参是本 persona 根,脚本据此读 config.env / s.yaml.tmpl)
|
|
277
|
-
4. 调 `appBuilder
|
|
257
|
+
4. 调 `appBuilder:setProdUrl` 把 URL 写入项目元数据(同上文)
|
|
278
258
|
|
|
279
|
-
**注意**:重跑期间不要自己调 `appBuilder
|
|
259
|
+
**注意**:重跑期间不要自己调 `appBuilder:publish` RPC(那是给 UI 按钮用的,会被 daemon 当作"已 in-flight"拒绝),手动跑脚本即可。失败回灌的消息开头固定是 `发布失败:[`,识别此前缀即触发接管流程。
|
|
280
260
|
|
|
281
261
|
**dev server 起不来怎么排查**:daemon 把 supervisor 输出(`dev-server.start` / `.stdout` / `.stderr` / `.spawn-failed` / `.exit`)打进 `~/.clawd/clawd.log`,老板可以 `grep dev-server ~/.clawd/clawd.log` 看真凶。常见原因:
|
|
282
262
|
- 端口 6XXX 被系统其他进程占了 → 右栏 header 齿轮菜单 → "Update Port..." 换段内另一个
|
package/dist/persona-defaults/persona-clawd-butler/.claude/skills/clawd-orchestration/SKILL.md
CHANGED
|
@@ -31,8 +31,8 @@ description: 老板要 dispatch 任务给别的 persona、给联系人发 DM、
|
|
|
31
31
|
|
|
32
32
|
不是所有活都自己干。有些老板要的活本机已经有对口 persona,转派更靠谱:
|
|
33
33
|
|
|
34
|
-
```
|
|
35
|
-
|
|
34
|
+
```
|
|
35
|
+
call({ method: "persona:list" }) # clawd-rpc MCP tool
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
看清单里各 persona 的定位,判断该派给谁。特别是:
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
**深任务转派**:管家做浅的、日常的、跨面协调。深的(改 clawd 源码、跨会话长期跟进、走规范流程)转派——先看当前本机有哪些 persona,按定位挑对应转派对象:
|
|
10
10
|
|
|
11
|
-
```
|
|
12
|
-
|
|
11
|
+
```
|
|
12
|
+
call({ method: "persona:list" }) # clawd-rpc MCP tool
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
**回答风格**:默认对"用户"讲人话,避开 daemon / WebSocket / protocol / frp 之类实现词。只有老板明确问底层才用技术语言。
|
|
@@ -6,31 +6,23 @@
|
|
|
6
6
|
|
|
7
7
|
你复用的是 daemon 里那条 `appBuilder.*` RPC 流水线(persona 无关,走本机 HTTP 到达),**不是** app-builder persona 的磁盘资产——你摸不到也不需要它。下面是你要做的全部,自包含照做即可。
|
|
8
8
|
|
|
9
|
-
daemon
|
|
10
|
-
- `CLAWD_DAEMON_URL` = `http://127.0.0.1:<port>`(RPC base)
|
|
11
|
-
- `CLAWD_SESSION_ID` = 当前 session id
|
|
12
|
-
|
|
13
|
-
所有调用都是 `POST $CLAWD_DAEMON_URL/api/rpc/<method>` + JSON body + `X-Clawd-Session-Id` header。
|
|
9
|
+
所有 daemon RPC 都走 `clawd-rpc` MCP 的 `call` tool(method 用 canonical 名;鉴权 / sessionId 由 tool 内部处理)。
|
|
14
10
|
|
|
15
11
|
**状态 A:未绑 project(fresh session)** —— 起手三步:
|
|
16
12
|
1. 主动问「老板想看什么数据的页?」(一句话,别长 prompt)。顺带提醒:稍后需要老板给一把 dataclaw apikey(向刘浩索取;只要冒号后的 secret 段)。
|
|
17
13
|
2. 老板描述需求 → **反问「项目叫什么名字」**(不擅自命名)
|
|
18
14
|
3. 拿到名字后建 project:
|
|
19
15
|
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
-H "Content-Type: application/json" -H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" \
|
|
23
|
-
-d '{"name":"<老板给的名字>"}'
|
|
16
|
+
```
|
|
17
|
+
call({ method: "appBuilder:createProject", args: { name: "<老板给的名字>" } })
|
|
24
18
|
```
|
|
25
19
|
返回 `result.projectDir` 就是项目目录绝对路径,**记住它**,后续 cd / 写文件都用它。重名报 `VALIDATION_ERROR` → 让老板换名。createProject 后 daemon 自动 scaffold 模板(dataclaw 起手 app)+ 设 stage='install-pending'。
|
|
26
20
|
|
|
27
21
|
**状态 B:已绑 project** —— UI 双栏(左 chat + 右实时预览)。接着按顺序做:
|
|
28
22
|
|
|
29
23
|
1. 上报安装 + 装依赖:
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
-H "Content-Type: application/json" -H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" \
|
|
33
|
-
-d '{"stage":"installing"}'
|
|
24
|
+
```
|
|
25
|
+
call({ method: "appBuilder:reportStage", args: { stage: "installing" } })
|
|
34
26
|
```
|
|
35
27
|
然后 `cd <projectDir>` → `pnpm install`(模板有 server/ 和 web/ 两套 deps)。
|
|
36
28
|
|
|
@@ -45,16 +37,13 @@ EOF
|
|
|
45
37
|
key 只存这里:dev 由 dotenv 加载、发布 FC 时 pre-deploy 自动从这读出注入 FC(`.env` 被 `.fcignore` 排除、不进部署包)。老板要换 key → 重写本文件后重启 dev server。
|
|
46
38
|
|
|
47
39
|
3. 起 dev server(双栏预览):
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
-H "Content-Type: application/json" -H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" -d '{}'
|
|
40
|
+
```
|
|
41
|
+
call({ method: "appBuilder:startDevServer", args: {} })
|
|
51
42
|
```
|
|
52
43
|
|
|
53
44
|
4. 任一步失败上报 failed:
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
-H "Content-Type: application/json" -H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" \
|
|
57
|
-
-d '{"stage":"failed","reason":"<一句话原因>"}'
|
|
45
|
+
```
|
|
46
|
+
call({ method: "appBuilder:reportStage", args: { stage: "failed", reason: "<一句话原因>" } })
|
|
58
47
|
```
|
|
59
48
|
|
|
60
49
|
**stage 规则**:你只能上报 `installing` / `failed`;`install-pending` / `starting-dev-server` / `running` / `paused` 由 daemon 自动设(防谎报)。**scaffold 不是你的步骤**(daemon 自动跑,你摸不到模板源)。
|