@clawos-dev/clawd 0.2.231 → 0.2.233
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 +99 -145
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -5556,11 +5556,12 @@ var init_schemas = __esm({
|
|
|
5556
5556
|
});
|
|
5557
5557
|
|
|
5558
5558
|
// ../protocol/src/frames.ts
|
|
5559
|
-
var PROTOCOL_VERSION;
|
|
5559
|
+
var PROTOCOL_VERSION, DISPATCH_MCP_ID;
|
|
5560
5560
|
var init_frames = __esm({
|
|
5561
5561
|
"../protocol/src/frames.ts"() {
|
|
5562
5562
|
"use strict";
|
|
5563
5563
|
PROTOCOL_VERSION = 1;
|
|
5564
|
+
DISPATCH_MCP_ID = "dispatch";
|
|
5564
5565
|
}
|
|
5565
5566
|
});
|
|
5566
5567
|
|
|
@@ -12148,17 +12149,8 @@ function buildSpawnArgs(ctx) {
|
|
|
12148
12149
|
if (ctx.addDirs) {
|
|
12149
12150
|
for (const dir of ctx.addDirs) args.push("--add-dir", dir);
|
|
12150
12151
|
}
|
|
12151
|
-
|
|
12152
|
-
args.push("--mcp-config",
|
|
12153
|
-
}
|
|
12154
|
-
if (ctx.ticketMcpConfigPath) {
|
|
12155
|
-
args.push("--mcp-config", ctx.ticketMcpConfigPath);
|
|
12156
|
-
}
|
|
12157
|
-
if (ctx.shiftMcpConfigPath) {
|
|
12158
|
-
args.push("--mcp-config", ctx.shiftMcpConfigPath);
|
|
12159
|
-
}
|
|
12160
|
-
if (ctx.inboxMcpConfigPath) {
|
|
12161
|
-
args.push("--mcp-config", ctx.inboxMcpConfigPath);
|
|
12152
|
+
for (const mcp of ctx.mcpConfigs ?? []) {
|
|
12153
|
+
args.push("--mcp-config", mcp.path);
|
|
12162
12154
|
}
|
|
12163
12155
|
args.push("--disallowed-tools", "CronCreate,CronDelete,CronList,ScheduleWakeup,RemoteTrigger");
|
|
12164
12156
|
if (ctx.effort) args.push("--effort", ctx.effort);
|
|
@@ -41818,6 +41810,7 @@ var import_node_os4 = __toESM(require("os"), 1);
|
|
|
41818
41810
|
var import_node_path7 = __toESM(require("path"), 1);
|
|
41819
41811
|
|
|
41820
41812
|
// src/session/reducer.ts
|
|
41813
|
+
init_protocol();
|
|
41821
41814
|
init_runtime();
|
|
41822
41815
|
|
|
41823
41816
|
// src/permission/rules.ts
|
|
@@ -41953,10 +41946,10 @@ var CONTACT_SSH_SYSTEM_PROMPT_HINT = `## \u8DE8\u8BBE\u5907\u6587\u4EF6\u8BBF\u9
|
|
|
41953
41946
|
# execPath+cliPath\uFF0C\u4FDD\u8BC1 relay \u8DDF daemon \u7248\u672C\u540C\u6B65\u3002\u4E0D\u8981\u7528\u88F8 \`clawd\`\uFF08\u8D70 $PATH \u4F1A\u6F02\uFF09\u3002
|
|
41954
41947
|
\`\`\`
|
|
41955
41948
|
|
|
41956
|
-
**A \
|
|
41949
|
+
**A \u5EFA\u8BAE\u5728\u54EA\u4E9B\u76EE\u5F55\u5E72\u6D3B**\uFF1AA \u4FA7 jail \u8FDB shell \u524D\u4F1A\u628A\u5EFA\u8BAE\u76EE\u5F55\u5217\u8868\u5199\u5230 stderr\uFF08\u683C\u5F0F \`[clawd-ssh-jail] Suggested working directories for this contact: ...\`\uFF09\u4F5C\u4E3A\u53C2\u8003\u3002\u6CE8\u610F\uFF1A**\u6CA1\u6709\u76EE\u5F55\u7EA7\u5F3A\u5236\u9650\u5236**\u2014\u2014\u4F60\u8BBF\u95EE\u7684\u662F A \u673A\u5668\u4E0A\u8BE5 SSH \u7528\u6237\u6743\u9650\u5185\u7684\u6587\u4EF6\uFF0C\u8BF7\u53EA\u5728 A \u6388\u6743/\u9700\u8981\u7684\u8303\u56F4\u5185\u64CD\u4F5C\uFF0C\u4E0D\u8981\u8D8A\u754C\u7FFB\u770B\u65E0\u5173\u76EE\u5F55\u3002
|
|
41957
41950
|
|
|
41958
41951
|
**\u6CE8\u610F**\uFF1A
|
|
41959
|
-
- \
|
|
41952
|
+
- \u5EFA\u8BAE\u76EE\u5F55\u6570\u636E\u6E90\u5B9E\u65F6\uFF08jail \u6BCF\u6B21\u8BFB A \u4FA7\u6700\u65B0\u914D\u7F6E\uFF09\uFF0CA \u6539\u4E86 exposedDirs \u540E\u4F60\u4E0B\u4E00\u6B21\u62E8\u53F7\u7ACB\u5373\u611F\u77E5
|
|
41960
41953
|
- \u6392\u969C\uFF1A\u672C\u673A debug \u65E5\u5FD7\u5728 \`~/.clawd/log/contact-ssh.log\`\uFF08\u7528\u6237\u53EF \`tail -f\` \u770B\u6BCF\u4E00\u6B65\uFF09\uFF0C\u4F60\u51FA\u9519\u65F6\u544A\u8BC9\u7528\u6237\u67E5\u8FD9\u4E2A\u6587\u4EF6
|
|
41961
41954
|
- \u5982\u679C \`ls ~/.clawd/contact-ssh-keys/\` \u4E3A\u7A7A\uFF0C\u8BF4\u660E\u8FD8\u6CA1\u6709\u5BF9\u7AEF\u6388\u6743\u4F60\u6216 key \u8FD8\u6CA1\u62C9\u5230\uFF08B \u4FA7 daemon \u6BCF 60s \u4E00\u6B21\u81EA\u52A8\u62C9\uFF09\uFF0C\u5982\u5B9E\u544A\u8BC9\u7528\u6237
|
|
41962
41955
|
`;
|
|
@@ -42045,10 +42038,7 @@ function buildSpawnContext(state, deps) {
|
|
|
42045
42038
|
if (dispatchId) env.CLAWD_DISPATCH_ID = dispatchId;
|
|
42046
42039
|
const personaId = file.ownerPersonaId;
|
|
42047
42040
|
if (personaId) env.CLAWD_PERSONA_ID = personaId;
|
|
42048
|
-
const
|
|
42049
|
-
const ticketMcpConfigPath2 = deps.getTicketMcpConfigPath?.() ?? null;
|
|
42050
|
-
const shiftMcpConfigPath2 = deps.getShiftMcpConfigPath?.() ?? null;
|
|
42051
|
-
const inboxMcpConfigPath2 = deps.getInboxMcpConfigPath?.() ?? null;
|
|
42041
|
+
const mcpConfigs = deps.getMcpConfigs?.() ?? [];
|
|
42052
42042
|
const ctx = {
|
|
42053
42043
|
cwd: file.cwd,
|
|
42054
42044
|
toolSessionId: file.toolSessionId,
|
|
@@ -42059,12 +42049,7 @@ function buildSpawnContext(state, deps) {
|
|
|
42059
42049
|
// Phase 2 capability platform (plan §3): personaRoot 透传给 buildSpawnArgs;
|
|
42060
42050
|
// 内部 shouldSandbox(cwd, personaRoot) 决定是否注入 --settings sandbox-settings.
|
|
42061
42051
|
personaRoot: deps.personaRoot,
|
|
42062
|
-
|
|
42063
|
-
// 都能看到 personaDispatch / personaDispatchComplete tool;按 env 区分实际行为。
|
|
42064
|
-
dispatchMcpConfigPath: dispatchMcpConfigPath2 ?? void 0,
|
|
42065
|
-
ticketMcpConfigPath: ticketMcpConfigPath2 ?? void 0,
|
|
42066
|
-
shiftMcpConfigPath: shiftMcpConfigPath2 ?? void 0,
|
|
42067
|
-
inboxMcpConfigPath: inboxMcpConfigPath2 ?? void 0,
|
|
42052
|
+
...mcpConfigs.length > 0 ? { mcpConfigs } : {},
|
|
42068
42053
|
// shift v1:把 SessionFile.ownerPersonaId 透传到 SpawnContext;buildSpawnArgs 等下游
|
|
42069
42054
|
// 不直接读这个字段(只读 env),但保留显式字段方便 daemon 内部诊断/日志。
|
|
42070
42055
|
personaId
|
|
@@ -42092,7 +42077,7 @@ function buildSpawnContext(state, deps) {
|
|
|
42092
42077
|
}
|
|
42093
42078
|
ctx.extraSystemPrompt = buildConnectionPrompt(promptArgs);
|
|
42094
42079
|
}
|
|
42095
|
-
if (
|
|
42080
|
+
if (mcpConfigs.some((c) => c.id === DISPATCH_MCP_ID)) {
|
|
42096
42081
|
ctx.extraSystemPrompt = (ctx.extraSystemPrompt ? ctx.extraSystemPrompt + "\n\n" : "") + DISPATCH_SYSTEM_PROMPT_HINT;
|
|
42097
42082
|
}
|
|
42098
42083
|
if (daemonUrl) {
|
|
@@ -42875,13 +42860,9 @@ var SessionRunner = class {
|
|
|
42875
42860
|
// 单栏 refactor (spec 2026-06-02 §5.1): cc 子进程 env 注入 CLAWD_DAEMON_URL,让 assistant
|
|
42876
42861
|
// curl daemon HTTP RPC adapter (/api/rpc/<method>) 触发管理操作。null = HTTP adapter 未启。
|
|
42877
42862
|
getDaemonUrl: this.hooks.getDaemonUrl,
|
|
42878
|
-
//
|
|
42879
|
-
//
|
|
42880
|
-
|
|
42881
|
-
getShiftMcpConfigPath: this.hooks.getShiftMcpConfigPath,
|
|
42882
|
-
getInboxMcpConfigPath: this.hooks.getInboxMcpConfigPath,
|
|
42883
|
-
// Ticket MCP:透传 ticket.mcp.json 路径闭包(所有 persona session 都挂,无 gating)
|
|
42884
|
-
getTicketMcpConfigPath: this.hooks.getTicketMcpConfigPath,
|
|
42863
|
+
// daemon 级 MCP server 列表闭包透传;见 reducer.buildSpawnContext 派生
|
|
42864
|
+
// SpawnContext.mcpConfigs + 按 DISPATCH_MCP_ID 判断 dispatch 教学注入
|
|
42865
|
+
getMcpConfigs: this.hooks.getMcpConfigs,
|
|
42885
42866
|
lookupDispatchByBSessionId: this.hooks.lookupDispatchByBSessionId,
|
|
42886
42867
|
// ReadyGate v2:透传 mode 让 reducer send / ready-detected 分支决定走暂存队列还是直写
|
|
42887
42868
|
mode: this.hooks.mode,
|
|
@@ -43711,12 +43692,9 @@ var SessionManager = class {
|
|
|
43711
43692
|
// 单栏 refactor (spec 2026-06-02 §5.1): 透传 daemon HTTP RPC base URL 闭包,
|
|
43712
43693
|
// reducer 把它注入 cc 子进程 env CLAWD_DAEMON_URL.
|
|
43713
43694
|
getDaemonUrl: this.deps.getDaemonUrl,
|
|
43714
|
-
//
|
|
43715
|
-
//
|
|
43716
|
-
|
|
43717
|
-
getTicketMcpConfigPath: this.deps.ticketMcpConfigPath ? () => this.deps.ticketMcpConfigPath ?? null : void 0,
|
|
43718
|
-
getShiftMcpConfigPath: this.deps.shiftMcpConfigPath ? () => this.deps.shiftMcpConfigPath ?? null : void 0,
|
|
43719
|
-
getInboxMcpConfigPath: this.deps.inboxMcpConfigPath ? () => this.deps.inboxMcpConfigPath ?? null : void 0,
|
|
43695
|
+
// daemon 级 MCP server 列表闭包透传给 reducer,cc spawn 逐项加 --mcp-config flag;
|
|
43696
|
+
// B session 的 CLAWD_DISPATCH_ID env 由下方 lookupDispatchByBSessionId 反查闭包负责。
|
|
43697
|
+
getMcpConfigs: () => this.deps.mcpConfigs ?? [],
|
|
43720
43698
|
lookupDispatchByBSessionId: this.deps.personaDispatchManager ? (bSid) => this.deps.personaDispatchManager?.lookupDispatchByBSessionId(bSid) : void 0,
|
|
43721
43699
|
// file-sharing (spec §6 PR 3):闭包 scope + sessionId,runner 只暴露 tool/relPath/cwd
|
|
43722
43700
|
onFileEdit: attachmentGroup ? (input) => attachmentGroup.onFileEdit({
|
|
@@ -48169,17 +48147,8 @@ function buildTuiSpawnArgs(ctx, isResume = false) {
|
|
|
48169
48147
|
if (ctx.addDirs) {
|
|
48170
48148
|
for (const dir of ctx.addDirs) args.push("--add-dir", dir);
|
|
48171
48149
|
}
|
|
48172
|
-
|
|
48173
|
-
args.push("--mcp-config",
|
|
48174
|
-
}
|
|
48175
|
-
if (ctx.ticketMcpConfigPath) {
|
|
48176
|
-
args.push("--mcp-config", ctx.ticketMcpConfigPath);
|
|
48177
|
-
}
|
|
48178
|
-
if (ctx.shiftMcpConfigPath) {
|
|
48179
|
-
args.push("--mcp-config", ctx.shiftMcpConfigPath);
|
|
48180
|
-
}
|
|
48181
|
-
if (ctx.inboxMcpConfigPath) {
|
|
48182
|
-
args.push("--mcp-config", ctx.inboxMcpConfigPath);
|
|
48150
|
+
for (const mcp of ctx.mcpConfigs ?? []) {
|
|
48151
|
+
args.push("--mcp-config", mcp.path);
|
|
48183
48152
|
}
|
|
48184
48153
|
args.push("--disallowed-tools", "CronCreate,CronDelete,CronList,ScheduleWakeup,RemoteTrigger");
|
|
48185
48154
|
if (ctx.effort) args.push("--effort", ctx.effort);
|
|
@@ -53495,9 +53464,8 @@ var CLAWD_SSH_JAIL_SCRIPT = String.raw`#!/usr/bin/env bash
|
|
|
53495
53464
|
# 真实请求(interactive shell 时为空)。
|
|
53496
53465
|
#
|
|
53497
53466
|
# 职责:
|
|
53498
|
-
# 1. 读 ~/.clawd/contacts.json
|
|
53499
|
-
# 2.
|
|
53500
|
-
# 3. exec 沙箱 shell
|
|
53467
|
+
# 1. 读 ~/.clawd/contacts.json 校验 contact.sshAllowed,读 exposedDirs 作为提示
|
|
53468
|
+
# 2. 直接 exec shell(去沙箱后无目录级限制;exposedDirs 仅 echo 给对端参考)
|
|
53501
53469
|
|
|
53502
53470
|
set -euo pipefail
|
|
53503
53471
|
|
|
@@ -53544,33 +53512,19 @@ for c in data.get('contacts', []):
|
|
|
53544
53512
|
sys.exit(3)
|
|
53545
53513
|
")
|
|
53546
53514
|
|
|
53547
|
-
|
|
53548
|
-
|
|
53549
|
-
|
|
53550
|
-
|
|
53515
|
+
# exposedDirs 可能为空(sshAllowed=true 但未配目录)——去沙箱后不再因空目录拒绝,直接放行。
|
|
53516
|
+
if [ -n "$EXPOSED_JSON" ]; then
|
|
53517
|
+
log_line INFO jail.entered "exposedDirs 提示已加载,放行 exec shell"
|
|
53518
|
+
# 告知 SSH client 建议目录(发到 stderr)。去沙箱后这些只是建议,不做目录级强制限制;
|
|
53519
|
+
# exposedDirs 仅供对端参考,运行时读,A 改后下次拨号即感知。
|
|
53520
|
+
echo "[clawd-ssh-jail] Suggested working directories for this contact:" >&2
|
|
53521
|
+
while IFS= read -r d; do
|
|
53522
|
+
echo " - $d" >&2
|
|
53523
|
+
done <<< "$EXPOSED_JSON"
|
|
53524
|
+
echo "[clawd-ssh-jail] Note: no directory-level restriction is enforced." >&2
|
|
53525
|
+
else
|
|
53526
|
+
log_line INFO jail.entered "sshAllowed=true 且未配 exposedDirs,直接放行 exec shell"
|
|
53551
53527
|
fi
|
|
53552
|
-
log_line INFO jail.entered "exposedDirs 白名单已加载,进沙箱 exec shell"
|
|
53553
|
-
|
|
53554
|
-
# 进沙箱前告知 SSH client 授权目录列表(发到 stderr)—— CC 或其他 SSH client 试错一次就能
|
|
53555
|
-
# 学到 exposedDirs 具体值,无需 A 侧向 B 侧同步(授权数据实时的 single source of truth)。
|
|
53556
|
-
# 每次拨号运行时读,A 改 exposedDirs 后 B 侧下一次拨号立即感知。
|
|
53557
|
-
echo "[clawd-ssh-jail] Access allowed to these directories:" >&2
|
|
53558
|
-
while IFS= read -r d; do
|
|
53559
|
-
echo " - $d" >&2
|
|
53560
|
-
done <<< "$EXPOSED_JSON"
|
|
53561
|
-
echo "[clawd-ssh-jail] Anything outside these paths will be sandbox-denied." >&2
|
|
53562
|
-
|
|
53563
|
-
# 校验路径安全(bash 侧二次防御)
|
|
53564
|
-
while IFS= read -r line; do
|
|
53565
|
-
case "$line" in
|
|
53566
|
-
/*) : ;;
|
|
53567
|
-
*) echo "clawd-ssh-jail: bad path: $line" >&2; exit 1 ;;
|
|
53568
|
-
esac
|
|
53569
|
-
case "$line" in
|
|
53570
|
-
*[\"\'\`\$\;\|\&\(\)\{\}\[\]\<\>\*\?]*)
|
|
53571
|
-
echo "clawd-ssh-jail: unsafe path: $line" >&2; exit 1 ;;
|
|
53572
|
-
esac
|
|
53573
|
-
done <<< "$EXPOSED_JSON"
|
|
53574
53528
|
|
|
53575
53529
|
CMD="${"${SSH_ORIGINAL_COMMAND:-}"}"
|
|
53576
53530
|
if [ -z "$CMD" ]; then
|
|
@@ -53579,48 +53533,11 @@ else
|
|
|
53579
53533
|
SHELL_CMD=(bash -c "$CMD")
|
|
53580
53534
|
fi
|
|
53581
53535
|
|
|
53582
|
-
|
|
53583
|
-
|
|
53584
|
-
|
|
53585
|
-
|
|
53586
|
-
|
|
53587
|
-
(allow signal (target self))
|
|
53588
|
-
(allow sysctl-read)
|
|
53589
|
-
(allow mach-lookup)
|
|
53590
|
-
(allow file-read-metadata)
|
|
53591
|
-
(allow network*)
|
|
53592
|
-
(allow file-read* (subpath \"/usr\"))
|
|
53593
|
-
(allow file-read* (subpath \"/bin\"))
|
|
53594
|
-
(allow file-read* (subpath \"/sbin\"))
|
|
53595
|
-
(allow file-read* (subpath \"/System\"))
|
|
53596
|
-
(allow file-read* (subpath \"/Library\"))
|
|
53597
|
-
(allow file-read* (subpath \"/etc\"))
|
|
53598
|
-
(allow file-read* (subpath \"/private/etc\"))"
|
|
53599
|
-
while IFS= read -r d; do
|
|
53600
|
-
POLICY+="
|
|
53601
|
-
(allow file-read* file-write* (subpath \"$d\"))"
|
|
53602
|
-
done <<< "$EXPOSED_JSON"
|
|
53603
|
-
exec sandbox-exec -p "$POLICY" "${"${SHELL_CMD[@]}"}"
|
|
53604
|
-
;;
|
|
53605
|
-
Linux)
|
|
53606
|
-
BWRAP_ARGS=(
|
|
53607
|
-
--unshare-user --unshare-ipc --unshare-pid --unshare-uts
|
|
53608
|
-
--die-with-parent
|
|
53609
|
-
--proc /proc --dev /dev --tmpfs /tmp
|
|
53610
|
-
--ro-bind /usr /usr --ro-bind /bin /bin --ro-bind /sbin /sbin
|
|
53611
|
-
--ro-bind /lib /lib --ro-bind /etc /etc
|
|
53612
|
-
)
|
|
53613
|
-
if [ -d /lib64 ]; then BWRAP_ARGS+=(--ro-bind /lib64 /lib64); fi
|
|
53614
|
-
while IFS= read -r d; do
|
|
53615
|
-
BWRAP_ARGS+=(--bind "$d" "$d")
|
|
53616
|
-
done <<< "$EXPOSED_JSON"
|
|
53617
|
-
exec bwrap "${"${BWRAP_ARGS[@]}"}" "${"${SHELL_CMD[@]}"}"
|
|
53618
|
-
;;
|
|
53619
|
-
*)
|
|
53620
|
-
echo "clawd-ssh-jail: unsupported OS $(uname -s)" >&2
|
|
53621
|
-
exit 1
|
|
53622
|
-
;;
|
|
53623
|
-
esac
|
|
53536
|
+
# 去掉 OS 沙箱层(原本 macOS Seatbelt 策略 / Linux 命名空间隔离):SSH 进来即该用户
|
|
53537
|
+
# 权限内的 shell,直接 exec。沙箱只限制文件、拦不住网络/进程,隔离价值有限,且相关 API
|
|
53538
|
+
# 在 macOS 已 deprecated。授权即完全信任——sshAllowed 授权开关 + authorized_keys 即时
|
|
53539
|
+
# 踢行是唯一访问闸。要加回沙箱:从 git 历史(T-29 之前)恢复本段的 OS 分支即可。
|
|
53540
|
+
exec "${"${SHELL_CMD[@]}"}"
|
|
53624
53541
|
`;
|
|
53625
53542
|
function ensureJailScript(dataDir) {
|
|
53626
53543
|
const binDir = import_node_path37.default.join(dataDir, "bin");
|
|
@@ -53667,6 +53584,8 @@ var SshdManager = class {
|
|
|
53667
53584
|
stopping = false;
|
|
53668
53585
|
exitHookInstalled = false;
|
|
53669
53586
|
startupTimeoutMs;
|
|
53587
|
+
// sshd stderr 按行嗌探 pubkey 认证结果时的半行缓冲(防关键字被 chunk 边界切断)
|
|
53588
|
+
sniffBuf = "";
|
|
53670
53589
|
get port() {
|
|
53671
53590
|
return this.deps.port;
|
|
53672
53591
|
}
|
|
@@ -53714,7 +53633,11 @@ var SshdManager = class {
|
|
|
53714
53633
|
logStream.write(String(c));
|
|
53715
53634
|
};
|
|
53716
53635
|
proc.stdout?.on("data", tee);
|
|
53717
|
-
proc.stderr?.on("data",
|
|
53636
|
+
proc.stderr?.on("data", (c) => {
|
|
53637
|
+
const s = String(c);
|
|
53638
|
+
logStream.write(s);
|
|
53639
|
+
this.sniffSshdAuth(s);
|
|
53640
|
+
});
|
|
53718
53641
|
proc.once("exit", () => logStream.end());
|
|
53719
53642
|
const ready = await waitForSshdReady(proc, this.startupTimeoutMs);
|
|
53720
53643
|
if (!ready.ok) {
|
|
@@ -53801,7 +53724,36 @@ ${tail}` : ready.error;
|
|
|
53801
53724
|
} catch {
|
|
53802
53725
|
}
|
|
53803
53726
|
}
|
|
53727
|
+
// 把 sshd -e 输出里的 pubkey 认证结果转写一条到 contact-ssh.log,让 A 侧审计日志一处即可
|
|
53728
|
+
// 看到"隧道连上但 sshd 拒了 key"(否则只有 tunnel.connected + tunnel.disconnected,为什么
|
|
53729
|
+
// 被拒要翻另一个 sshd.log 对时间线)。按 \n 切行嗅探,半行留 sniffBuf 等下个 chunk 拼回。
|
|
53730
|
+
sniffSshdAuth(chunk) {
|
|
53731
|
+
const sshLog = this.deps.sshLog;
|
|
53732
|
+
if (!sshLog) return;
|
|
53733
|
+
this.sniffBuf += chunk;
|
|
53734
|
+
const parts = this.sniffBuf.split("\n");
|
|
53735
|
+
this.sniffBuf = parts.pop() ?? "";
|
|
53736
|
+
for (const line of parts) {
|
|
53737
|
+
if (/Accepted publickey/i.test(line)) {
|
|
53738
|
+
sshLog.info(
|
|
53739
|
+
"ssh.auth-ok",
|
|
53740
|
+
"sshd \u63A5\u53D7\u4E86 pubkey \u8BA4\u8BC1\uFF08\u5BF9\u7AEF key \u547D\u4E2D authorized_keys\uFF09",
|
|
53741
|
+
sshdAuthMeta(line)
|
|
53742
|
+
);
|
|
53743
|
+
} else if (/Failed publickey/i.test(line) || /Connection closed by (authenticating|invalid) user/i.test(line)) {
|
|
53744
|
+
sshLog.warn(
|
|
53745
|
+
"ssh.auth-rejected",
|
|
53746
|
+
"sshd \u62D2\u7EDD\u4E86 pubkey \u8BA4\u8BC1\uFF08\u5BF9\u7AEF key \u4E0D\u5728 authorized_keys / \u672A\u6388\u6743 / key \u672A\u62C9\u5230\uFF09",
|
|
53747
|
+
sshdAuthMeta(line)
|
|
53748
|
+
);
|
|
53749
|
+
}
|
|
53750
|
+
}
|
|
53751
|
+
}
|
|
53804
53752
|
};
|
|
53753
|
+
function sshdAuthMeta(line) {
|
|
53754
|
+
const m2 = /port (\d+)/.exec(line);
|
|
53755
|
+
return m2 ? { clientPort: Number(m2[1]) } : {};
|
|
53756
|
+
}
|
|
53805
53757
|
async function waitForSshdReady(proc, timeoutMs) {
|
|
53806
53758
|
return new Promise((resolve6) => {
|
|
53807
53759
|
let settled = false;
|
|
@@ -54192,6 +54144,11 @@ async function handleSshTunnelLocalUpgrade(req, socket, head, deps) {
|
|
|
54192
54144
|
url: outboundUrl
|
|
54193
54145
|
});
|
|
54194
54146
|
deps.wss.handleUpgrade(req, socket, head, (clientWs) => {
|
|
54147
|
+
sshLog.info(
|
|
54148
|
+
"relay.client-connected",
|
|
54149
|
+
"relay CLI \u5DF2\u8FDE\u4E0A\u672C\u5730 daemon\uFF08client WS \u5347\u7EA7\u6210\u529F\uFF09\uFF0C\u5F00\u59CB\u62E8 peer",
|
|
54150
|
+
{ peerDeviceId, peerDisplayName: contact.displayName }
|
|
54151
|
+
);
|
|
54195
54152
|
const makeOut = deps.makeOutboundWs ?? ((u, o) => new import_websocket.default(u, o));
|
|
54196
54153
|
const outbound = makeOut(outboundUrl, {
|
|
54197
54154
|
headers: { Authorization: `Bearer ${contact.connectToken}` }
|
|
@@ -59689,6 +59646,7 @@ async function startDaemon(config) {
|
|
|
59689
59646
|
const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
|
|
59690
59647
|
const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
|
|
59691
59648
|
const here = typeof __dirname === "string" ? __dirname : import_node_path64.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
|
|
59649
|
+
const mcpConfigs = [];
|
|
59692
59650
|
const dispatchServerCandidates = [
|
|
59693
59651
|
import_node_path64.default.join(here, "dispatch", "mcp-server.cjs"),
|
|
59694
59652
|
// 生产 dist/index → dist/dispatch/mcp-server.cjs
|
|
@@ -59696,16 +59654,16 @@ async function startDaemon(config) {
|
|
|
59696
59654
|
// dev tsx src/index → ../dist/dispatch/mcp-server.cjs
|
|
59697
59655
|
];
|
|
59698
59656
|
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
|
|
59699
|
-
let dispatchMcpConfigPath2;
|
|
59700
59657
|
if (dispatchServerScriptPath) {
|
|
59701
59658
|
const dispatchLogPath = import_node_path64.default.join(logDir, "dispatch-mcp-server.log");
|
|
59702
|
-
|
|
59659
|
+
const dispatchCfgPath = writeDispatchMcpConfig({
|
|
59703
59660
|
dataDir: config.dataDir,
|
|
59704
59661
|
serverScriptPath: dispatchServerScriptPath,
|
|
59705
59662
|
logPath: dispatchLogPath
|
|
59706
59663
|
});
|
|
59664
|
+
mcpConfigs.push({ id: DISPATCH_MCP_ID, path: dispatchCfgPath });
|
|
59707
59665
|
logger.info("dispatch.mcp.json written", {
|
|
59708
|
-
path:
|
|
59666
|
+
path: dispatchCfgPath,
|
|
59709
59667
|
server: dispatchServerScriptPath,
|
|
59710
59668
|
serverLog: dispatchLogPath
|
|
59711
59669
|
});
|
|
@@ -59719,18 +59677,18 @@ async function startDaemon(config) {
|
|
|
59719
59677
|
import_node_path64.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
|
|
59720
59678
|
];
|
|
59721
59679
|
const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
|
|
59722
|
-
let ticketMcpConfigPath2;
|
|
59723
59680
|
if (ticketServerScriptPath) {
|
|
59724
59681
|
const ticketLogPath = import_node_path64.default.join(logDir, "ticket-mcp-server.log");
|
|
59725
|
-
|
|
59682
|
+
const ticketCfgPath = writeTicketMcpConfig({
|
|
59726
59683
|
dataDir: config.dataDir,
|
|
59727
59684
|
serverScriptPath: ticketServerScriptPath,
|
|
59728
59685
|
apiBaseUrl: config.clawosApi ?? DEFAULT_CLAWOS_API,
|
|
59729
59686
|
apiKey: resolveClawdTicketsApiKey(),
|
|
59730
59687
|
logPath: ticketLogPath
|
|
59731
59688
|
});
|
|
59689
|
+
mcpConfigs.push({ id: "ticket", path: ticketCfgPath });
|
|
59732
59690
|
logger.info("ticket.mcp.json written", {
|
|
59733
|
-
path:
|
|
59691
|
+
path: ticketCfgPath,
|
|
59734
59692
|
server: ticketServerScriptPath,
|
|
59735
59693
|
serverLog: ticketLogPath
|
|
59736
59694
|
});
|
|
@@ -59744,16 +59702,16 @@ async function startDaemon(config) {
|
|
|
59744
59702
|
import_node_path64.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
|
|
59745
59703
|
];
|
|
59746
59704
|
const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
|
|
59747
|
-
let shiftMcpConfigPath2;
|
|
59748
59705
|
if (shiftServerScriptPath) {
|
|
59749
59706
|
const shiftLogPath = import_node_path64.default.join(logDir, "shift-mcp-server.log");
|
|
59750
|
-
|
|
59707
|
+
const shiftCfgPath = await writeShiftMcpConfig({
|
|
59751
59708
|
dataDir: config.dataDir,
|
|
59752
59709
|
serverScriptPath: shiftServerScriptPath,
|
|
59753
59710
|
logPath: shiftLogPath
|
|
59754
59711
|
});
|
|
59712
|
+
mcpConfigs.push({ id: "shift", path: shiftCfgPath });
|
|
59755
59713
|
logger.info("shift.mcp.json written", {
|
|
59756
|
-
path:
|
|
59714
|
+
path: shiftCfgPath,
|
|
59757
59715
|
server: shiftServerScriptPath,
|
|
59758
59716
|
serverLog: shiftLogPath
|
|
59759
59717
|
});
|
|
@@ -59768,16 +59726,16 @@ async function startDaemon(config) {
|
|
|
59768
59726
|
import_node_path64.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
|
|
59769
59727
|
];
|
|
59770
59728
|
const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
|
|
59771
|
-
let inboxMcpConfigPath2;
|
|
59772
59729
|
if (inboxServerScriptPath) {
|
|
59773
59730
|
const inboxLogPath = import_node_path64.default.join(logDir, "inbox-mcp-server.log");
|
|
59774
|
-
|
|
59731
|
+
const inboxCfgPath = await writeInboxMcpConfig({
|
|
59775
59732
|
dataDir: config.dataDir,
|
|
59776
59733
|
serverScriptPath: inboxServerScriptPath,
|
|
59777
59734
|
logPath: inboxLogPath
|
|
59778
59735
|
});
|
|
59736
|
+
mcpConfigs.push({ id: "inbox", path: inboxCfgPath });
|
|
59779
59737
|
logger.info("inbox.mcp.json written", {
|
|
59780
|
-
path:
|
|
59738
|
+
path: inboxCfgPath,
|
|
59781
59739
|
server: inboxServerScriptPath,
|
|
59782
59740
|
serverLog: inboxLogPath
|
|
59783
59741
|
});
|
|
@@ -59823,18 +59781,12 @@ async function startDaemon(config) {
|
|
|
59823
59781
|
// 127.0.0.1(不是 config.host)—— cc 跑在本机,loopback 最稳;外部访问限制 + http-router
|
|
59824
59782
|
// 的 isLoopback 兜底已确保安全。
|
|
59825
59783
|
getDaemonUrl: () => `http://127.0.0.1:${config.port}`,
|
|
59826
|
-
// Persona dispatch (Task 8): manager
|
|
59827
|
-
//
|
|
59828
|
-
// reducer 通过闭包反查 dispatchId 注 CLAWD_DISPATCH_ID env。
|
|
59829
|
-
// - dispatchMcpConfigPath: reducer 透传到 SpawnContext,cc spawn 加 --mcp-config flag。
|
|
59784
|
+
// Persona dispatch (Task 8): manager.createDispatchedSession 用 personaDispatchManager
|
|
59785
|
+
// registerBSession;reducer 通过闭包反查 dispatchId 注 CLAWD_DISPATCH_ID env。
|
|
59830
59786
|
personaDispatchManager,
|
|
59831
|
-
|
|
59832
|
-
|
|
59833
|
-
|
|
59834
|
-
// 缺省 = shift-mcp-server.cjs 没 build 出来,cc 看不到 shift tool 但 scheduler 仍跑。
|
|
59835
|
-
shiftMcpConfigPath: shiftMcpConfigPath2,
|
|
59836
|
-
// sendDm MCP tool:所有 persona session 都挂 inbox.mcp.json(无 gating)。
|
|
59837
|
-
inboxMcpConfigPath: inboxMcpConfigPath2,
|
|
59787
|
+
// daemon 级 MCP server 登记表(dispatch / ticket / shift / inbox / ...,上方各 writer
|
|
59788
|
+
// 块写盘成功即登记);reducer 透传到 SpawnContext.mcpConfigs,cc spawn 逐项 --mcp-config。
|
|
59789
|
+
mcpConfigs,
|
|
59838
59790
|
broadcastFrame: (frame, target) => {
|
|
59839
59791
|
if (target === "all") {
|
|
59840
59792
|
transport?.broadcastAll(frame);
|
|
@@ -60656,7 +60608,9 @@ ${bar}
|
|
|
60656
60608
|
port: config.sshdPort,
|
|
60657
60609
|
logger,
|
|
60658
60610
|
installProcessExitHandlers: true,
|
|
60659
|
-
onSshdExit: (info) => logger.warn("sshd exited unexpectedly", info)
|
|
60611
|
+
onSshdExit: (info) => logger.warn("sshd exited unexpectedly", info),
|
|
60612
|
+
// sshd 的 pubkey 认证结果转写进 contact-ssh.log(补 A 侧「隧道连上但 sshd 拒了 key」盲区)
|
|
60613
|
+
sshLog
|
|
60660
60614
|
});
|
|
60661
60615
|
try {
|
|
60662
60616
|
await sshdMgr.start();
|