@clawos-dev/clawd 0.2.233 → 0.2.235
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 +125 -71
- package/dist/dispatch/mcp-server.cjs +15 -8
- package/dist/share-md-viewer-error.html +1 -1
- package/dist/share-md-viewer.html +1 -1
- package/dist/share-ui/assets/guest-BXzrtMaF.css +32 -0
- package/dist/share-ui/assets/{guest-DUG627MS.js → guest-DkkDu-6U.js} +121 -121
- package/dist/share-ui/guest.html +2 -2
- package/package.json +1 -1
- package/dist/share-ui/assets/guest-AOFHLDRS.css +0 -32
package/dist/cli.cjs
CHANGED
|
@@ -4820,6 +4820,14 @@ var init_schemas = __esm({
|
|
|
4820
4820
|
* 或 deps.ownerDisplayName(owner)。
|
|
4821
4821
|
*/
|
|
4822
4822
|
creatorDisplayName: external_exports.string().min(1).optional(),
|
|
4823
|
+
/**
|
|
4824
|
+
* 创建该 session 的连接者飞书 union_id(一次性 stamp,T-31 persona prompt injection)。
|
|
4825
|
+
* session:create / dispatch spawnB handler 从 ctx.principal.feishuUnionId(guest 场景来自
|
|
4826
|
+
* connect token subOwner)或 deps.ownerFeishuUnionId(owner 场景)传入;newRunner 时读这个
|
|
4827
|
+
* 字段派生 SubSessionMeta.callerFeishuUnionId 注入 cc system prompt「身份卡」的 union_id 行。
|
|
4828
|
+
* Optional:老 SessionFile / owner 未登飞书路径缺省,缺省时 prompt 不渲染该行(向后兼容)。
|
|
4829
|
+
*/
|
|
4830
|
+
creatorFeishuUnionId: external_exports.string().min(1).optional(),
|
|
4823
4831
|
/**
|
|
4824
4832
|
* Mirror peer sessions (2026-05-26): mirror 标识。**我在朋友 daemon 上创建的 session**,
|
|
4825
4833
|
* 远端是 source of truth,本机是持久化副本,方便 sidebar 跨设备聚合。
|
|
@@ -5737,8 +5745,10 @@ var init_contact = __esm({
|
|
|
5737
5745
|
*/
|
|
5738
5746
|
sshAllowed: external_exports.boolean().default(false),
|
|
5739
5747
|
/**
|
|
5740
|
-
*
|
|
5741
|
-
*
|
|
5748
|
+
* @deprecated T-29 去沙箱后废弃。原为授权访问的目录白名单(clawd-ssh-jail 生成 sbpl/bwrap
|
|
5749
|
+
* policy 用);去掉 OS 沙箱后不再有目录级限制,SSH 授权即 on/off(见 sshAllowed)。
|
|
5750
|
+
* 字段仅为兼容老 contacts.json 保留(default [] 保证 strict parse 不炸),不再有代码读写它,
|
|
5751
|
+
* 也不再进 setSshAccess RPC / ContactSettingsDrawer / jail。勿新增消费方。
|
|
5742
5752
|
*/
|
|
5743
5753
|
exposedDirs: external_exports.array(external_exports.string()).default([])
|
|
5744
5754
|
}).strict();
|
|
@@ -5787,14 +5797,12 @@ var init_contact_ssh = __esm({
|
|
|
5787
5797
|
init_zod();
|
|
5788
5798
|
ContactSetSshAccessArgsSchema = external_exports.object({
|
|
5789
5799
|
deviceId: external_exports.string().min(1),
|
|
5790
|
-
sshAllowed: external_exports.boolean()
|
|
5791
|
-
exposedDirs: external_exports.array(external_exports.string())
|
|
5800
|
+
sshAllowed: external_exports.boolean()
|
|
5792
5801
|
}).strict();
|
|
5793
5802
|
ContactSetSshAccessOkSchema = external_exports.object({
|
|
5794
5803
|
type: external_exports.literal("contact:setSshAccess:ok"),
|
|
5795
5804
|
deviceId: external_exports.string().min(1),
|
|
5796
|
-
sshAllowed: external_exports.boolean()
|
|
5797
|
-
exposedDirs: external_exports.array(external_exports.string())
|
|
5805
|
+
sshAllowed: external_exports.boolean()
|
|
5798
5806
|
}).strict();
|
|
5799
5807
|
ContactSshKeyIssueArgsSchema = external_exports.object({
|
|
5800
5808
|
deviceId: external_exports.string().min(1)
|
|
@@ -5807,8 +5815,7 @@ var init_contact_ssh = __esm({
|
|
|
5807
5815
|
ContactSshAccessUpdatedFrameSchema = external_exports.object({
|
|
5808
5816
|
type: external_exports.literal("contact:ssh-access-updated"),
|
|
5809
5817
|
deviceId: external_exports.string().min(1),
|
|
5810
|
-
sshAllowed: external_exports.boolean()
|
|
5811
|
-
exposedDirs: external_exports.array(external_exports.string())
|
|
5818
|
+
sshAllowed: external_exports.boolean()
|
|
5812
5819
|
}).strict();
|
|
5813
5820
|
}
|
|
5814
5821
|
});
|
|
@@ -41423,11 +41430,17 @@ function scopeSubPath(scope) {
|
|
|
41423
41430
|
if (scope.mode === "guest") return [scope.personaId, "guests", scope.capId];
|
|
41424
41431
|
return [scope.personaId, scope.mode];
|
|
41425
41432
|
}
|
|
41426
|
-
function metaFromScope(scope, personaRoot, usersRoot, ownerPrincipalId, callerDisplayName) {
|
|
41433
|
+
function metaFromScope(scope, personaRoot, usersRoot, ownerPrincipalId, callerDisplayName, callerFeishuUnionId) {
|
|
41427
41434
|
if (scope.kind === "default") return void 0;
|
|
41428
41435
|
if (scope.mode === "owner") {
|
|
41429
41436
|
if (!ownerPrincipalId || !usersRoot) {
|
|
41430
|
-
return {
|
|
41437
|
+
return {
|
|
41438
|
+
idleKillEnabled: false,
|
|
41439
|
+
personaMode: "owner",
|
|
41440
|
+
callerDisplayName,
|
|
41441
|
+
callerDeviceId: ownerPrincipalId,
|
|
41442
|
+
callerFeishuUnionId
|
|
41443
|
+
};
|
|
41431
41444
|
}
|
|
41432
41445
|
const userWorkDir2 = deriveUserWorkDir(ownerPrincipalId, usersRoot);
|
|
41433
41446
|
return {
|
|
@@ -41435,7 +41448,9 @@ function metaFromScope(scope, personaRoot, usersRoot, ownerPrincipalId, callerDi
|
|
|
41435
41448
|
personaMode: "owner",
|
|
41436
41449
|
userWorkDir: userWorkDir2,
|
|
41437
41450
|
addDirs: [userWorkDir2],
|
|
41438
|
-
callerDisplayName
|
|
41451
|
+
callerDisplayName,
|
|
41452
|
+
callerDeviceId: ownerPrincipalId,
|
|
41453
|
+
callerFeishuUnionId
|
|
41439
41454
|
};
|
|
41440
41455
|
}
|
|
41441
41456
|
const userWorkDir = deriveUserWorkDir(scope.capId, usersRoot);
|
|
@@ -41444,7 +41459,9 @@ function metaFromScope(scope, personaRoot, usersRoot, ownerPrincipalId, callerDi
|
|
|
41444
41459
|
personaMode: "guest",
|
|
41445
41460
|
userWorkDir,
|
|
41446
41461
|
addDirs: [userWorkDir],
|
|
41447
|
-
callerDisplayName
|
|
41462
|
+
callerDisplayName,
|
|
41463
|
+
callerDeviceId: scope.capId,
|
|
41464
|
+
callerFeishuUnionId
|
|
41448
41465
|
};
|
|
41449
41466
|
}
|
|
41450
41467
|
function scopeForFile(file, ownerPrincipalId) {
|
|
@@ -41844,30 +41861,61 @@ function buildRule(tool, input) {
|
|
|
41844
41861
|
}
|
|
41845
41862
|
|
|
41846
41863
|
// src/persona/connection-prompt.ts
|
|
41847
|
-
|
|
41848
|
-
|
|
41849
|
-
|
|
41850
|
-
|
|
41851
|
-
|
|
41852
|
-
|
|
41864
|
+
function renderIdentityLines(fields) {
|
|
41865
|
+
const lines = [`- \u663E\u793A\u540D\uFF1A${fields.label}`];
|
|
41866
|
+
if (fields.feishuUnionId) lines.push(`- \u98DE\u4E66 union_id\uFF1A${fields.feishuUnionId}`);
|
|
41867
|
+
if (fields.deviceId) lines.push(`- \u8BBE\u5907 id\uFF1A${fields.deviceId}`);
|
|
41868
|
+
if (fields.extraLastLine) lines.push(fields.extraLastLine);
|
|
41869
|
+
return lines.join("\n");
|
|
41870
|
+
}
|
|
41853
41871
|
function buildConnectionPrompt(args) {
|
|
41854
|
-
|
|
41855
|
-
if (args.mode === "
|
|
41856
|
-
|
|
41857
|
-
|
|
41858
|
-
|
|
41872
|
+
const parts = ["# \u8FDE\u63A5\u4E0A\u4E0B\u6587"];
|
|
41873
|
+
if (args.mode === "owner") {
|
|
41874
|
+
parts.push("\u5F53\u524D\u8FDE\u63A5\u65B9\uFF08\u5C31\u662F owner \u672C\u4EBA\uFF09\uFF1A");
|
|
41875
|
+
parts.push(
|
|
41876
|
+
renderIdentityLines({
|
|
41877
|
+
label: args.ownerLabel,
|
|
41878
|
+
feishuUnionId: args.ownerFeishuUnionId,
|
|
41879
|
+
deviceId: args.ownerDeviceId
|
|
41880
|
+
})
|
|
41859
41881
|
);
|
|
41860
41882
|
} else {
|
|
41861
|
-
|
|
41883
|
+
parts.push("\u5F53\u524D\u8FDE\u63A5\u65B9\uFF08\u4E0D\u662F owner \u672C\u4EBA\uFF09\uFF1A");
|
|
41884
|
+
parts.push(
|
|
41885
|
+
renderIdentityLines({
|
|
41886
|
+
label: args.guestLabel,
|
|
41887
|
+
feishuUnionId: args.guestFeishuUnionId,
|
|
41888
|
+
deviceId: args.guestDeviceId,
|
|
41889
|
+
extraLastLine: "- \u8EAB\u4EFD\uFF1AGuest\uFF08\u8FDC\u7A0B\u8FDE\u63A5\u8FDB\u6765\u7684\u8054\u7CFB\u4EBA\uFF09"
|
|
41890
|
+
})
|
|
41891
|
+
);
|
|
41892
|
+
parts.push("");
|
|
41893
|
+
parts.push("\u672C persona \u7684 owner\uFF1A");
|
|
41894
|
+
parts.push(
|
|
41895
|
+
renderIdentityLines({
|
|
41896
|
+
label: args.ownerLabel,
|
|
41897
|
+
feishuUnionId: args.ownerFeishuUnionId,
|
|
41898
|
+
deviceId: args.ownerDeviceId
|
|
41899
|
+
})
|
|
41900
|
+
);
|
|
41862
41901
|
}
|
|
41863
41902
|
if (args.workDir) {
|
|
41864
|
-
|
|
41865
|
-
|
|
41903
|
+
parts.push("");
|
|
41904
|
+
let line = `\u4F60\u7684\u6587\u4EF6\u5DE5\u4F5C\u76EE\u5F55\u662F ${args.workDir}\uFF0C\u8FD9\u4E2A\u76EE\u5F55\u5BF9\u4F60\u53EF\u8BFB\u53EF\u5199\uFF0C\u6587\u4EF6\u7684\u8BFB\u53D6/\u521B\u5EFA/\u4FEE\u6539\u90FD\u5728\u8FD9\u91CC\u8FDB\u884C\u3002`;
|
|
41866
41905
|
if (args.mode === "guest") {
|
|
41867
|
-
|
|
41906
|
+
line += "persona \u76EE\u5F55\u5BF9\u4F60\u53EA\u8BFB\uFF0C\u4E0D\u8981\u5F80\u91CC\u5199\u3002";
|
|
41868
41907
|
}
|
|
41908
|
+
parts.push(line);
|
|
41869
41909
|
}
|
|
41870
|
-
|
|
41910
|
+
parts.push("");
|
|
41911
|
+
if (args.mode === "owner") {
|
|
41912
|
+
parts.push("\u81EA\u6211\u4ECB\u7ECD\u65F6\u4F7F\u7528\u7B2C\u4E00/\u7B2C\u4E8C\u4EBA\u79F0\uFF0C\u4F8B\u5982\u300C\u6211\u662F\u60A8\u7684 xxx \u52A9\u624B\u300D\u3002");
|
|
41913
|
+
} else {
|
|
41914
|
+
parts.push(
|
|
41915
|
+
`\u81EA\u6211\u4ECB\u7ECD\u65F6\u7B2C\u4E09\u4EBA\u79F0\u6307\u4EE3 owner\u3001\u7B2C\u4E8C\u4EBA\u79F0\u79F0\u547C\u5BF9\u65B9\uFF0C\u4F8B\u5982\u300C${args.guestLabel}\uFF0C\u6211\u662F ${args.ownerLabel} \u7684 xxx \u52A9\u624B\uFF0C\u4F60\u53EF\u4EE5\u901A\u8FC7\u6211\u770B\u5230\u4ED6\u7684 xxx\u300D\u3002\u4E0D\u8981\u628A\u5BF9\u65B9\u5F53\u6210 owner\uFF0C\u4E0D\u8981\u66FF owner \u505A\u51FA\u627F\u8BFA\u3002`
|
|
41916
|
+
);
|
|
41917
|
+
}
|
|
41918
|
+
return parts.join("\n");
|
|
41871
41919
|
}
|
|
41872
41920
|
|
|
41873
41921
|
// src/persona/sharing-prompt.ts
|
|
@@ -41946,10 +41994,10 @@ var CONTACT_SSH_SYSTEM_PROMPT_HINT = `## \u8DE8\u8BBE\u5907\u6587\u4EF6\u8BBF\u9
|
|
|
41946
41994
|
# 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
|
|
41947
41995
|
\`\`\`
|
|
41948
41996
|
|
|
41949
|
-
|
|
41997
|
+
**\u4F60\u80FD\u8BBF\u95EE\u4EC0\u4E48**\uFF1A**\u6CA1\u6709\u76EE\u5F55\u7EA7\u9650\u5236**\u2014\u2014\u4F60\u4EE5 A \u673A\u5668\u4E0A\u8BE5 SSH \u7528\u6237\u7684\u6743\u9650\u8BBF\u95EE\u6587\u4EF6\uFF08\u7B49\u540C\u4E8E A \u7ED9\u4F60\u5F00\u4E86\u4E00\u4E2A shell\uFF09\u3002\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
|
|
41950
41998
|
|
|
41951
41999
|
**\u6CE8\u610F**\uFF1A
|
|
41952
|
-
- \
|
|
42000
|
+
- \u6388\u6743\u662F\u5B9E\u65F6\u7684\uFF1AA \u64A4\u9500 sshAllowed \u540E\u4F60\u4E0B\u4E00\u6B21\u62E8\u53F7\u7ACB\u5373\u5931\u6548\uFF08authorized_keys \u7ACB\u5373\u5237\uFF09
|
|
41953
42001
|
- \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
|
|
41954
42002
|
- \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
|
|
41955
42003
|
`;
|
|
@@ -42065,13 +42113,19 @@ function buildSpawnContext(state, deps) {
|
|
|
42065
42113
|
promptArgs = {
|
|
42066
42114
|
mode: "guest",
|
|
42067
42115
|
ownerLabel: deps.ownerDisplayName,
|
|
42116
|
+
ownerDeviceId: deps.ownerDeviceId,
|
|
42117
|
+
ownerFeishuUnionId: deps.ownerFeishuUnionId,
|
|
42068
42118
|
guestLabel: meta.callerDisplayName,
|
|
42119
|
+
guestDeviceId: meta.callerDeviceId,
|
|
42120
|
+
guestFeishuUnionId: meta.callerFeishuUnionId,
|
|
42069
42121
|
workDir: meta.userWorkDir
|
|
42070
42122
|
};
|
|
42071
42123
|
} else {
|
|
42072
42124
|
promptArgs = {
|
|
42073
42125
|
mode: "owner",
|
|
42074
42126
|
ownerLabel: meta.callerDisplayName ?? deps.ownerDisplayName,
|
|
42127
|
+
ownerDeviceId: meta.callerDeviceId ?? deps.ownerDeviceId,
|
|
42128
|
+
ownerFeishuUnionId: meta.callerFeishuUnionId ?? deps.ownerFeishuUnionId,
|
|
42075
42129
|
workDir: meta.userWorkDir
|
|
42076
42130
|
};
|
|
42077
42131
|
}
|
|
@@ -42856,6 +42910,8 @@ var SessionRunner = class {
|
|
|
42856
42910
|
resolveContextWindow: this.hooks.resolveContextWindow,
|
|
42857
42911
|
genUuid: this.hooks.genUuid ?? v4_default,
|
|
42858
42912
|
ownerDisplayName: this.hooks.ownerDisplayName,
|
|
42913
|
+
ownerDeviceId: this.hooks.ownerDeviceId,
|
|
42914
|
+
ownerFeishuUnionId: this.hooks.ownerFeishuUnionId,
|
|
42859
42915
|
personaRoot: this.hooks.personaRoot,
|
|
42860
42916
|
// 单栏 refactor (spec 2026-06-02 §5.1): cc 子进程 env 注入 CLAWD_DAEMON_URL,让 assistant
|
|
42861
42917
|
// curl daemon HTTP RPC adapter (/api/rpc/<method>) 触发管理操作。null = HTTP adapter 未启。
|
|
@@ -43634,12 +43690,14 @@ var SessionManager = class {
|
|
|
43634
43690
|
const adapter = this.deps.getAdapter(file.tool ?? "claude");
|
|
43635
43691
|
const store = this.storeFor(scope);
|
|
43636
43692
|
const callerDisplayName = file.creatorDisplayName ?? (scope.kind === "persona" ? scope.mode === "owner" ? this.deps.ownerDisplayName : this.deps.contactStore?.get(scope.capId)?.displayName : void 0);
|
|
43693
|
+
const callerFeishuUnionId = file.creatorFeishuUnionId ?? (scope.kind === "persona" && scope.mode === "owner" ? this.deps.ownerFeishuUnionId : void 0);
|
|
43637
43694
|
const subSessionMeta = metaFromScope(
|
|
43638
43695
|
scope,
|
|
43639
43696
|
this.deps.personaRoot ?? "",
|
|
43640
43697
|
this.deps.usersRoot ?? "",
|
|
43641
43698
|
this.deps.ownerPrincipalId,
|
|
43642
|
-
callerDisplayName
|
|
43699
|
+
callerDisplayName,
|
|
43700
|
+
callerFeishuUnionId
|
|
43643
43701
|
);
|
|
43644
43702
|
if (subSessionMeta?.userWorkDir) {
|
|
43645
43703
|
import_node_fs9.default.mkdirSync(subSessionMeta.userWorkDir, { recursive: true });
|
|
@@ -43686,6 +43744,11 @@ var SessionManager = class {
|
|
|
43686
43744
|
// personaStore 不再透给 runner hooks(in-place mention 注入路径已删,runner 不用);
|
|
43687
43745
|
// manager 自身的 sandbox 派生仍直接读 this.deps.personaStore。
|
|
43688
43746
|
ownerDisplayName: this.deps.ownerDisplayName,
|
|
43747
|
+
// T-31 persona prompt injection:owner 设备 id(= ownerPrincipalId)+ 飞书 union_id
|
|
43748
|
+
// 供 reducer 组 promptArgs 时作为「本 persona 的 owner」身份卡数据源(guest session 用)
|
|
43749
|
+
// 或「当前连接方」身份卡的兜底源(owner session 老路径未 stamp caller 时)。
|
|
43750
|
+
ownerDeviceId: this.deps.ownerPrincipalId,
|
|
43751
|
+
ownerFeishuUnionId: this.deps.ownerFeishuUnionId,
|
|
43689
43752
|
// Phase 2 capability platform (plan §3): 透传 personaRoot, buildSpawnArgs 据
|
|
43690
43753
|
// cwd 是否在 personaRoot 下自动决定是否注入 --settings sandbox-settings.json.
|
|
43691
43754
|
personaRoot: this.deps.personaRoot,
|
|
@@ -43801,7 +43864,7 @@ var SessionManager = class {
|
|
|
43801
43864
|
}
|
|
43802
43865
|
}
|
|
43803
43866
|
// ---- 命令方法:均返回 { response, broadcast[] },由 dispatcher 聚合 ----
|
|
43804
|
-
create(args, creatorPrincipalId, creatorDisplayName) {
|
|
43867
|
+
create(args, creatorPrincipalId, creatorDisplayName, creatorFeishuUnionId) {
|
|
43805
43868
|
let cwd;
|
|
43806
43869
|
if (args.ownerPersonaId) {
|
|
43807
43870
|
cwd = derivePersonaSpawnCwd(
|
|
@@ -43846,6 +43909,7 @@ var SessionManager = class {
|
|
|
43846
43909
|
ephemeral: args.ephemeral,
|
|
43847
43910
|
creatorPrincipalId,
|
|
43848
43911
|
creatorDisplayName,
|
|
43912
|
+
...creatorFeishuUnionId ? { creatorFeishuUnionId } : {},
|
|
43849
43913
|
createdAt: iso,
|
|
43850
43914
|
updatedAt: iso
|
|
43851
43915
|
};
|
|
@@ -44616,7 +44680,9 @@ var SessionManager = class {
|
|
|
44616
44680
|
// creatorDisplayName = A 的 displayName,供 guest meta callerDisplayName(reducer 要求)。
|
|
44617
44681
|
...args.guestPrincipalId ? {
|
|
44618
44682
|
creatorPrincipalId: args.guestPrincipalId,
|
|
44619
|
-
...args.guestDisplayName ? { creatorDisplayName: args.guestDisplayName } : {}
|
|
44683
|
+
...args.guestDisplayName ? { creatorDisplayName: args.guestDisplayName } : {},
|
|
44684
|
+
// T-31: A 的飞书 union_id stamp 到 B 的 SessionFile;guest meta 由 newRunner 派生
|
|
44685
|
+
...args.guestFeishuUnionId ? { creatorFeishuUnionId: args.guestFeishuUnionId } : {}
|
|
44620
44686
|
} : {},
|
|
44621
44687
|
// T-20:dispatch model override 落盘(reducer spawn 路径读 SessionFile.model 生成 --model flag)
|
|
44622
44688
|
...args.model ? { model: args.model } : {},
|
|
@@ -48890,6 +48956,8 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
48890
48956
|
prompt: args.prompt,
|
|
48891
48957
|
guestPrincipalId: ctx.principal.id,
|
|
48892
48958
|
guestDisplayName: ctx.principal.displayName,
|
|
48959
|
+
// T-31: 把 A 的飞书 union_id 透传给 B(connectGuestContext 已把它挂在 principal.feishuUnionId)
|
|
48960
|
+
...ctx.principal.feishuUnionId ? { guestFeishuUnionId: ctx.principal.feishuUnionId } : {},
|
|
48893
48961
|
...args.model ? { model: args.model } : {}
|
|
48894
48962
|
}).then(() => logger?.info("dispatch.spawnB.ok", { dispatchId: dispatchId2 })).catch((err) => {
|
|
48895
48963
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -50662,7 +50730,7 @@ function ownerContext(ownerPrincipalId, displayName, feishuUnionId) {
|
|
|
50662
50730
|
}
|
|
50663
50731
|
function connectGuestContext(subDevice, subOwner, provider, displayName) {
|
|
50664
50732
|
return {
|
|
50665
|
-
principal: { id: subDevice, kind: "guest", displayName },
|
|
50733
|
+
principal: { id: subDevice, kind: "guest", displayName, feishuUnionId: subOwner },
|
|
50666
50734
|
grants: PERSONAL_CAP_GRANTS.map((g2) => ({
|
|
50667
50735
|
resource: { ...g2.resource },
|
|
50668
50736
|
actions: [...g2.actions]
|
|
@@ -51144,9 +51212,8 @@ var ContactStore = class {
|
|
|
51144
51212
|
return true;
|
|
51145
51213
|
}
|
|
51146
51214
|
/**
|
|
51147
|
-
* 更新单条 contact 的 SSH
|
|
51148
|
-
*
|
|
51149
|
-
* 那是 handler / UI 的责任。数组语义是完全替换(不 append)。
|
|
51215
|
+
* 更新单条 contact 的 SSH 授权开关(对齐 setPin pattern)。T-29 去沙箱后只改 sshAllowed;
|
|
51216
|
+
* exposedDirs 已废弃,本方法不再触碰它(保留 contact 原值不动)。
|
|
51150
51217
|
* @returns 是否命中:deviceId 不存在返 false;命中即 flush.
|
|
51151
51218
|
*/
|
|
51152
51219
|
setSshAccess(deviceId, opts) {
|
|
@@ -51154,8 +51221,7 @@ var ContactStore = class {
|
|
|
51154
51221
|
if (!existing) return false;
|
|
51155
51222
|
this.contacts.set(deviceId, {
|
|
51156
51223
|
...existing,
|
|
51157
|
-
sshAllowed: opts.sshAllowed
|
|
51158
|
-
exposedDirs: opts.exposedDirs
|
|
51224
|
+
sshAllowed: opts.sshAllowed
|
|
51159
51225
|
});
|
|
51160
51226
|
this.flush();
|
|
51161
51227
|
return true;
|
|
@@ -53464,8 +53530,8 @@ var CLAWD_SSH_JAIL_SCRIPT = String.raw`#!/usr/bin/env bash
|
|
|
53464
53530
|
# 真实请求(interactive shell 时为空)。
|
|
53465
53531
|
#
|
|
53466
53532
|
# 职责:
|
|
53467
|
-
# 1. 读 ~/.clawd/contacts.json 校验 contact.sshAllowed
|
|
53468
|
-
# 2. 直接 exec shell
|
|
53533
|
+
# 1. 读 ~/.clawd/contacts.json 校验 contact.sshAllowed(不通过即拒)
|
|
53534
|
+
# 2. 直接 exec shell(去沙箱后无目录级限制)
|
|
53469
53535
|
|
|
53470
53536
|
set -euo pipefail
|
|
53471
53537
|
|
|
@@ -53492,13 +53558,15 @@ log_line INFO jail.entered "clawd-ssh-jail 起来 device=${"${DEVICE_ID}"} cmd=$
|
|
|
53492
53558
|
|
|
53493
53559
|
CONTACTS="${"${HOME}"}/.clawd/contacts.json"
|
|
53494
53560
|
if [ ! -f "$CONTACTS" ]; then
|
|
53495
|
-
log_line ERROR jail.entered "contacts.json
|
|
53561
|
+
log_line ERROR jail.entered "contacts.json 不存在,无法校验 sshAllowed"
|
|
53496
53562
|
echo "clawd-ssh-jail: contacts.json missing" >&2
|
|
53497
53563
|
exit 1
|
|
53498
53564
|
fi
|
|
53499
53565
|
|
|
53500
|
-
#
|
|
53501
|
-
|
|
53566
|
+
# 校验该 contact 的 sshAllowed(mac/linux 都自带 python3)。去沙箱后不再读目录白名单:
|
|
53567
|
+
# sshAllowed=false → DENIED exit 2;deviceId 未命中 → NOT_FOUND exit 3;命中且允许 → exit 0。
|
|
53568
|
+
# set -e 下 python 非 0 退出即终止脚本(不进 exec),只有 exit 0 才继续 exec shell。
|
|
53569
|
+
python3 -c "
|
|
53502
53570
|
import json, sys
|
|
53503
53571
|
with open('$CONTACTS') as f:
|
|
53504
53572
|
data = json.load(f)
|
|
@@ -53506,25 +53574,11 @@ for c in data.get('contacts', []):
|
|
|
53506
53574
|
if c.get('deviceId') == '$DEVICE_ID':
|
|
53507
53575
|
if not c.get('sshAllowed'):
|
|
53508
53576
|
print('DENIED', file=sys.stderr); sys.exit(2)
|
|
53509
|
-
for d in c.get('exposedDirs', []):
|
|
53510
|
-
print(d)
|
|
53511
53577
|
sys.exit(0)
|
|
53512
|
-
sys.exit(3)
|
|
53513
|
-
"
|
|
53514
|
-
|
|
53515
|
-
|
|
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"
|
|
53527
|
-
fi
|
|
53578
|
+
print('NOT_FOUND', file=sys.stderr); sys.exit(3)
|
|
53579
|
+
"
|
|
53580
|
+
|
|
53581
|
+
log_line INFO jail.entered "sshAllowed 校验通过,放行 exec shell"
|
|
53528
53582
|
|
|
53529
53583
|
CMD="${"${SSH_ORIGINAL_COMMAND:-}"}"
|
|
53530
53584
|
if [ -z "$CMD" ]; then
|
|
@@ -54920,10 +54974,12 @@ function buildSessionHandlers(deps) {
|
|
|
54920
54974
|
ensurePersonaAccess(ctx, args.ownerPersonaId, "send");
|
|
54921
54975
|
const creatorPrincipalId = ctx?.principal.id ?? ownerPrincipalId;
|
|
54922
54976
|
const creatorDisplayName = ctx?.principal.displayName ?? "Owner";
|
|
54977
|
+
const creatorFeishuUnionId = ctx?.principal.feishuUnionId;
|
|
54923
54978
|
const { response, broadcast } = manager.create(
|
|
54924
54979
|
{ ...args, tool },
|
|
54925
54980
|
creatorPrincipalId,
|
|
54926
|
-
creatorDisplayName
|
|
54981
|
+
creatorDisplayName,
|
|
54982
|
+
creatorFeishuUnionId
|
|
54927
54983
|
);
|
|
54928
54984
|
return { response: { type: "session:info", ...response }, broadcast };
|
|
54929
54985
|
};
|
|
@@ -55995,8 +56051,7 @@ function buildContactSshHandlers(deps) {
|
|
|
55995
56051
|
const { type: _t, requestId: _r, ...rest } = frame;
|
|
55996
56052
|
const args = ContactSetSshAccessArgsSchema.parse(rest);
|
|
55997
56053
|
const hit = deps.store.setSshAccess(args.deviceId, {
|
|
55998
|
-
sshAllowed: args.sshAllowed
|
|
55999
|
-
exposedDirs: args.exposedDirs
|
|
56054
|
+
sshAllowed: args.sshAllowed
|
|
56000
56055
|
});
|
|
56001
56056
|
if (!hit) {
|
|
56002
56057
|
sshLog.warn("authz.setSshAccess", "owner \u5C1D\u8BD5\u6539 SSH \u6388\u6743\u4F46 contact \u4E0D\u5728 store", {
|
|
@@ -56010,21 +56065,18 @@ function buildContactSshHandlers(deps) {
|
|
|
56010
56065
|
rebuildAuthorizedKeys(deps.store, deps.sshdDir);
|
|
56011
56066
|
sshLog.info("authz.setSshAccess", "owner \u6539\u4E86 SSH \u6388\u6743 \u2192 authorized_keys \u5DF2\u91CD\u5EFA", {
|
|
56012
56067
|
peerDeviceId: args.deviceId,
|
|
56013
|
-
sshAllowed: args.sshAllowed
|
|
56014
|
-
exposedDirs: args.exposedDirs
|
|
56068
|
+
sshAllowed: args.sshAllowed
|
|
56015
56069
|
});
|
|
56016
56070
|
deps.broadcast({
|
|
56017
56071
|
type: "contact:ssh-access-updated",
|
|
56018
56072
|
deviceId: args.deviceId,
|
|
56019
|
-
sshAllowed: args.sshAllowed
|
|
56020
|
-
exposedDirs: args.exposedDirs
|
|
56073
|
+
sshAllowed: args.sshAllowed
|
|
56021
56074
|
});
|
|
56022
56075
|
return {
|
|
56023
56076
|
response: {
|
|
56024
56077
|
type: "contact:setSshAccess:ok",
|
|
56025
56078
|
deviceId: args.deviceId,
|
|
56026
|
-
sshAllowed: args.sshAllowed
|
|
56027
|
-
exposedDirs: args.exposedDirs
|
|
56079
|
+
sshAllowed: args.sshAllowed
|
|
56028
56080
|
}
|
|
56029
56081
|
};
|
|
56030
56082
|
};
|
|
@@ -60227,6 +60279,8 @@ async function startDaemon(config) {
|
|
|
60227
60279
|
// B 角色(跨设备):非空 → guest scope + creatorPrincipalId/creatorDisplayName = A。
|
|
60228
60280
|
guestPrincipalId: args.guestPrincipalId,
|
|
60229
60281
|
guestDisplayName: args.guestDisplayName,
|
|
60282
|
+
// T-31: 把 A 的飞书 union_id stamp 到 B SessionFile,供 cc prompt 身份卡 union_id 行
|
|
60283
|
+
guestFeishuUnionId: args.guestFeishuUnionId,
|
|
60230
60284
|
// T-20: 可选 model override 透到 SessionFile.model
|
|
60231
60285
|
...args.model ? { model: args.model } : {}
|
|
60232
60286
|
});
|
|
@@ -39876,6 +39876,14 @@ var SessionFileSchema = external_exports.object({
|
|
|
39876
39876
|
* 或 deps.ownerDisplayName(owner)。
|
|
39877
39877
|
*/
|
|
39878
39878
|
creatorDisplayName: external_exports.string().min(1).optional(),
|
|
39879
|
+
/**
|
|
39880
|
+
* 创建该 session 的连接者飞书 union_id(一次性 stamp,T-31 persona prompt injection)。
|
|
39881
|
+
* session:create / dispatch spawnB handler 从 ctx.principal.feishuUnionId(guest 场景来自
|
|
39882
|
+
* connect token subOwner)或 deps.ownerFeishuUnionId(owner 场景)传入;newRunner 时读这个
|
|
39883
|
+
* 字段派生 SubSessionMeta.callerFeishuUnionId 注入 cc system prompt「身份卡」的 union_id 行。
|
|
39884
|
+
* Optional:老 SessionFile / owner 未登飞书路径缺省,缺省时 prompt 不渲染该行(向后兼容)。
|
|
39885
|
+
*/
|
|
39886
|
+
creatorFeishuUnionId: external_exports.string().min(1).optional(),
|
|
39879
39887
|
/**
|
|
39880
39888
|
* Mirror peer sessions (2026-05-26): mirror 标识。**我在朋友 daemon 上创建的 session**,
|
|
39881
39889
|
* 远端是 source of truth,本机是持久化副本,方便 sidebar 跨设备聚合。
|
|
@@ -40747,8 +40755,10 @@ var ContactSchema = external_exports.object({
|
|
|
40747
40755
|
*/
|
|
40748
40756
|
sshAllowed: external_exports.boolean().default(false),
|
|
40749
40757
|
/**
|
|
40750
|
-
*
|
|
40751
|
-
*
|
|
40758
|
+
* @deprecated T-29 去沙箱后废弃。原为授权访问的目录白名单(clawd-ssh-jail 生成 sbpl/bwrap
|
|
40759
|
+
* policy 用);去掉 OS 沙箱后不再有目录级限制,SSH 授权即 on/off(见 sshAllowed)。
|
|
40760
|
+
* 字段仅为兼容老 contacts.json 保留(default [] 保证 strict parse 不炸),不再有代码读写它,
|
|
40761
|
+
* 也不再进 setSshAccess RPC / ContactSettingsDrawer / jail。勿新增消费方。
|
|
40752
40762
|
*/
|
|
40753
40763
|
exposedDirs: external_exports.array(external_exports.string()).default([])
|
|
40754
40764
|
}).strict();
|
|
@@ -40790,14 +40800,12 @@ var ContactPinnedFrameSchema = external_exports.object({
|
|
|
40790
40800
|
// ../protocol/src/contact-ssh.ts
|
|
40791
40801
|
var ContactSetSshAccessArgsSchema = external_exports.object({
|
|
40792
40802
|
deviceId: external_exports.string().min(1),
|
|
40793
|
-
sshAllowed: external_exports.boolean()
|
|
40794
|
-
exposedDirs: external_exports.array(external_exports.string())
|
|
40803
|
+
sshAllowed: external_exports.boolean()
|
|
40795
40804
|
}).strict();
|
|
40796
40805
|
var ContactSetSshAccessOkSchema = external_exports.object({
|
|
40797
40806
|
type: external_exports.literal("contact:setSshAccess:ok"),
|
|
40798
40807
|
deviceId: external_exports.string().min(1),
|
|
40799
|
-
sshAllowed: external_exports.boolean()
|
|
40800
|
-
exposedDirs: external_exports.array(external_exports.string())
|
|
40808
|
+
sshAllowed: external_exports.boolean()
|
|
40801
40809
|
}).strict();
|
|
40802
40810
|
var ContactSshKeyIssueArgsSchema = external_exports.object({
|
|
40803
40811
|
deviceId: external_exports.string().min(1)
|
|
@@ -40810,8 +40818,7 @@ var ContactSshKeyIssueOkSchema = external_exports.object({
|
|
|
40810
40818
|
var ContactSshAccessUpdatedFrameSchema = external_exports.object({
|
|
40811
40819
|
type: external_exports.literal("contact:ssh-access-updated"),
|
|
40812
40820
|
deviceId: external_exports.string().min(1),
|
|
40813
|
-
sshAllowed: external_exports.boolean()
|
|
40814
|
-
exposedDirs: external_exports.array(external_exports.string())
|
|
40821
|
+
sshAllowed: external_exports.boolean()
|
|
40815
40822
|
}).strict();
|
|
40816
40823
|
|
|
40817
40824
|
// ../protocol/src/extension.ts
|