@clawos-dev/clawd 0.2.251 → 0.2.253
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 +113 -361
- package/dist/dispatch/mcp-server.cjs +0 -6
- package/dist/persona-defaults/persona-clawd-helper/.claude/skills/clawd-config-editor/SKILL.md +39 -0
- package/dist/persona-defaults/persona-clawd-helper/.claude/skills/clawd-introspection/SKILL.md +40 -0
- package/dist/persona-defaults/persona-clawd-helper/.claude/skills/clawd-orchestration/SKILL.md +53 -0
- package/dist/persona-defaults/persona-clawd-helper/.claude/skills/clawd-product/SKILL.md +31 -0
- package/dist/persona-defaults/persona-clawd-helper/.claude/skills/clawd-session-lens/SKILL.md +37 -0
- package/dist/persona-defaults/persona-clawd-helper/CLAUDE.md +21 -115
- package/dist/share-md-viewer-error.html +1 -1
- package/dist/share-md-viewer.html +1 -1
- package/dist/share-ui/assets/guest-53QJqRhe.css +32 -0
- package/dist/share-ui/assets/{guest-D6RAMr_5.js → guest-B7FHhw7e.js} +13 -13
- package/dist/share-ui/guest.html +2 -2
- package/package.json +1 -1
- package/dist/share-ui/assets/guest-k2RPEdy5.css +0 -32
package/dist/cli.cjs
CHANGED
|
@@ -148,12 +148,6 @@ var init_methods = __esm({
|
|
|
148
148
|
"attachment.groupAdd",
|
|
149
149
|
"attachment.groupRemove",
|
|
150
150
|
"attachment.groupList",
|
|
151
|
-
// ---- capability:* (capability platform 鉴权底座) ----
|
|
152
|
-
// 飞书统一身份 Phase 2 (2026-06-01, spec 决策 #12): personal token / 邀请码全链路删除——
|
|
153
|
-
// device:connect 已完整替代(中心 server 签发自包含 connect token)。capability:list / capability:delete
|
|
154
|
-
// 保留:调试 + whoami 反查 + 撤销级联 + 清旧 per-peer cap。personal-cap:get 已下线。
|
|
155
|
-
"capability:list",
|
|
156
|
-
"capability:delete",
|
|
157
151
|
// ---- inbox:* (capability platform Phase 3 跨用户通知 + Phase 4 DM) ----
|
|
158
152
|
// owner 接 guest 的 cross-principal 消息事件 + DM 双向私聊.
|
|
159
153
|
// inbox:list / markRead: admin-only (Phase 3); inbox:postMessage: DM 自带能力,
|
|
@@ -5643,11 +5637,7 @@ var init_persona_mode = __esm({
|
|
|
5643
5637
|
});
|
|
5644
5638
|
|
|
5645
5639
|
// ../protocol/src/capability.ts
|
|
5646
|
-
|
|
5647
|
-
const { secretHash: _hash, ...wire } = cap;
|
|
5648
|
-
return wire;
|
|
5649
|
-
}
|
|
5650
|
-
var ResourceSchema, ActionSchema, GrantSchema, CapabilitySchema, CapabilityWireSchema, CapabilityErrorCodeSchema, WhoamiResponseSchema, PERSONAL_CAP_GRANTS;
|
|
5640
|
+
var ResourceSchema, ActionSchema, GrantSchema, CapabilitySchema, CapabilityWireSchema, WhoamiResponseSchema, PERSONAL_CAP_GRANTS;
|
|
5651
5641
|
var init_capability = __esm({
|
|
5652
5642
|
"../protocol/src/capability.ts"() {
|
|
5653
5643
|
"use strict";
|
|
@@ -5688,12 +5678,6 @@ var init_capability = __esm({
|
|
|
5688
5678
|
peerDisplayName: external_exports.string().optional()
|
|
5689
5679
|
}).strict();
|
|
5690
5680
|
CapabilityWireSchema = CapabilitySchema.omit({ secretHash: true });
|
|
5691
|
-
CapabilityErrorCodeSchema = external_exports.enum([
|
|
5692
|
-
"TOKEN_INVALID",
|
|
5693
|
-
"TOKEN_REVOKED",
|
|
5694
|
-
"TOKEN_EXPIRED",
|
|
5695
|
-
"TOKEN_EXHAUSTED"
|
|
5696
|
-
]);
|
|
5697
5681
|
WhoamiResponseSchema = external_exports.object({
|
|
5698
5682
|
type: external_exports.literal("whoami:ok"),
|
|
5699
5683
|
owner: external_exports.object({
|
|
@@ -6099,9 +6083,6 @@ var init_feishu_auth = __esm({
|
|
|
6099
6083
|
"use strict";
|
|
6100
6084
|
init_zod();
|
|
6101
6085
|
FEISHU_GATED_METHODS = [
|
|
6102
|
-
// capability(调试 / 撤销,仍属远程身份体系)
|
|
6103
|
-
"capability:list",
|
|
6104
|
-
"capability:delete",
|
|
6105
6086
|
// 联系人列表(device:connect 落同一 store;list 读 / remove 删)
|
|
6106
6087
|
"contact:list",
|
|
6107
6088
|
"contact:remove",
|
|
@@ -7527,7 +7508,7 @@ var require_atomic_sleep = __commonJS({
|
|
|
7527
7508
|
var require_sonic_boom = __commonJS({
|
|
7528
7509
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
7529
7510
|
"use strict";
|
|
7530
|
-
var
|
|
7511
|
+
var fs65 = require("fs");
|
|
7531
7512
|
var EventEmitter3 = require("events");
|
|
7532
7513
|
var inherits = require("util").inherits;
|
|
7533
7514
|
var path74 = require("path");
|
|
@@ -7584,20 +7565,20 @@ var require_sonic_boom = __commonJS({
|
|
|
7584
7565
|
const mode = sonic.mode;
|
|
7585
7566
|
if (sonic.sync) {
|
|
7586
7567
|
try {
|
|
7587
|
-
if (sonic.mkdir)
|
|
7588
|
-
const fd =
|
|
7568
|
+
if (sonic.mkdir) fs65.mkdirSync(path74.dirname(file), { recursive: true });
|
|
7569
|
+
const fd = fs65.openSync(file, flags, mode);
|
|
7589
7570
|
fileOpened(null, fd);
|
|
7590
7571
|
} catch (err) {
|
|
7591
7572
|
fileOpened(err);
|
|
7592
7573
|
throw err;
|
|
7593
7574
|
}
|
|
7594
7575
|
} else if (sonic.mkdir) {
|
|
7595
|
-
|
|
7576
|
+
fs65.mkdir(path74.dirname(file), { recursive: true }, (err) => {
|
|
7596
7577
|
if (err) return fileOpened(err);
|
|
7597
|
-
|
|
7578
|
+
fs65.open(file, flags, mode, fileOpened);
|
|
7598
7579
|
});
|
|
7599
7580
|
} else {
|
|
7600
|
-
|
|
7581
|
+
fs65.open(file, flags, mode, fileOpened);
|
|
7601
7582
|
}
|
|
7602
7583
|
}
|
|
7603
7584
|
function SonicBoom(opts) {
|
|
@@ -7638,8 +7619,8 @@ var require_sonic_boom = __commonJS({
|
|
|
7638
7619
|
this.flush = flushBuffer;
|
|
7639
7620
|
this.flushSync = flushBufferSync;
|
|
7640
7621
|
this._actualWrite = actualWriteBuffer;
|
|
7641
|
-
fsWriteSync = () =>
|
|
7642
|
-
fsWrite = () =>
|
|
7622
|
+
fsWriteSync = () => fs65.writeSync(this.fd, this._writingBuf);
|
|
7623
|
+
fsWrite = () => fs65.write(this.fd, this._writingBuf, this.release);
|
|
7643
7624
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
7644
7625
|
this._writingBuf = "";
|
|
7645
7626
|
this.write = write;
|
|
@@ -7648,15 +7629,15 @@ var require_sonic_boom = __commonJS({
|
|
|
7648
7629
|
this._actualWrite = actualWrite;
|
|
7649
7630
|
fsWriteSync = () => {
|
|
7650
7631
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7651
|
-
return
|
|
7632
|
+
return fs65.writeSync(this.fd, this._writingBuf);
|
|
7652
7633
|
}
|
|
7653
|
-
return
|
|
7634
|
+
return fs65.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7654
7635
|
};
|
|
7655
7636
|
fsWrite = () => {
|
|
7656
7637
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7657
|
-
return
|
|
7638
|
+
return fs65.write(this.fd, this._writingBuf, this.release);
|
|
7658
7639
|
}
|
|
7659
|
-
return
|
|
7640
|
+
return fs65.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
7660
7641
|
};
|
|
7661
7642
|
} else {
|
|
7662
7643
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -7713,7 +7694,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7713
7694
|
}
|
|
7714
7695
|
}
|
|
7715
7696
|
if (this._fsync) {
|
|
7716
|
-
|
|
7697
|
+
fs65.fsyncSync(this.fd);
|
|
7717
7698
|
}
|
|
7718
7699
|
const len = this._len;
|
|
7719
7700
|
if (this._reopening) {
|
|
@@ -7827,7 +7808,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7827
7808
|
const onDrain = () => {
|
|
7828
7809
|
if (!this._fsync) {
|
|
7829
7810
|
try {
|
|
7830
|
-
|
|
7811
|
+
fs65.fsync(this.fd, (err) => {
|
|
7831
7812
|
this._flushPending = false;
|
|
7832
7813
|
cb(err);
|
|
7833
7814
|
});
|
|
@@ -7929,7 +7910,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7929
7910
|
const fd = this.fd;
|
|
7930
7911
|
this.once("ready", () => {
|
|
7931
7912
|
if (fd !== this.fd) {
|
|
7932
|
-
|
|
7913
|
+
fs65.close(fd, (err) => {
|
|
7933
7914
|
if (err) {
|
|
7934
7915
|
return this.emit("error", err);
|
|
7935
7916
|
}
|
|
@@ -7978,7 +7959,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7978
7959
|
buf = this._bufs[0];
|
|
7979
7960
|
}
|
|
7980
7961
|
try {
|
|
7981
|
-
const n = Buffer.isBuffer(buf) ?
|
|
7962
|
+
const n = Buffer.isBuffer(buf) ? fs65.writeSync(this.fd, buf) : fs65.writeSync(this.fd, buf, "utf8");
|
|
7982
7963
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
7983
7964
|
buf = releasedBufObj.writingBuf;
|
|
7984
7965
|
this._len = releasedBufObj.len;
|
|
@@ -7994,7 +7975,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7994
7975
|
}
|
|
7995
7976
|
}
|
|
7996
7977
|
try {
|
|
7997
|
-
|
|
7978
|
+
fs65.fsyncSync(this.fd);
|
|
7998
7979
|
} catch {
|
|
7999
7980
|
}
|
|
8000
7981
|
}
|
|
@@ -8015,7 +7996,7 @@ var require_sonic_boom = __commonJS({
|
|
|
8015
7996
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
8016
7997
|
}
|
|
8017
7998
|
try {
|
|
8018
|
-
const n =
|
|
7999
|
+
const n = fs65.writeSync(this.fd, buf);
|
|
8019
8000
|
buf = buf.subarray(n);
|
|
8020
8001
|
this._len = Math.max(this._len - n, 0);
|
|
8021
8002
|
if (buf.length <= 0) {
|
|
@@ -8043,13 +8024,13 @@ var require_sonic_boom = __commonJS({
|
|
|
8043
8024
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
8044
8025
|
if (this.sync) {
|
|
8045
8026
|
try {
|
|
8046
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
8027
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs65.writeSync(this.fd, this._writingBuf) : fs65.writeSync(this.fd, this._writingBuf, "utf8");
|
|
8047
8028
|
release(null, written);
|
|
8048
8029
|
} catch (err) {
|
|
8049
8030
|
release(err);
|
|
8050
8031
|
}
|
|
8051
8032
|
} else {
|
|
8052
|
-
|
|
8033
|
+
fs65.write(this.fd, this._writingBuf, release);
|
|
8053
8034
|
}
|
|
8054
8035
|
}
|
|
8055
8036
|
function actualWriteBuffer() {
|
|
@@ -8058,7 +8039,7 @@ var require_sonic_boom = __commonJS({
|
|
|
8058
8039
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
8059
8040
|
if (this.sync) {
|
|
8060
8041
|
try {
|
|
8061
|
-
const written =
|
|
8042
|
+
const written = fs65.writeSync(this.fd, this._writingBuf);
|
|
8062
8043
|
release(null, written);
|
|
8063
8044
|
} catch (err) {
|
|
8064
8045
|
release(err);
|
|
@@ -8067,7 +8048,7 @@ var require_sonic_boom = __commonJS({
|
|
|
8067
8048
|
if (kCopyBuffer) {
|
|
8068
8049
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
8069
8050
|
}
|
|
8070
|
-
|
|
8051
|
+
fs65.write(this.fd, this._writingBuf, release);
|
|
8071
8052
|
}
|
|
8072
8053
|
}
|
|
8073
8054
|
function actualClose(sonic) {
|
|
@@ -8083,12 +8064,12 @@ var require_sonic_boom = __commonJS({
|
|
|
8083
8064
|
sonic._lens = [];
|
|
8084
8065
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
8085
8066
|
try {
|
|
8086
|
-
|
|
8067
|
+
fs65.fsync(sonic.fd, closeWrapped);
|
|
8087
8068
|
} catch {
|
|
8088
8069
|
}
|
|
8089
8070
|
function closeWrapped() {
|
|
8090
8071
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
8091
|
-
|
|
8072
|
+
fs65.close(sonic.fd, done);
|
|
8092
8073
|
} else {
|
|
8093
8074
|
done();
|
|
8094
8075
|
}
|
|
@@ -26932,8 +26913,8 @@ var require_CronFileParser = __commonJS({
|
|
|
26932
26913
|
* @throws If file cannot be read
|
|
26933
26914
|
*/
|
|
26934
26915
|
static parseFileSync(filePath) {
|
|
26935
|
-
const { readFileSync:
|
|
26936
|
-
const data =
|
|
26916
|
+
const { readFileSync: readFileSync6 } = require("fs");
|
|
26917
|
+
const data = readFileSync6(filePath, "utf8");
|
|
26937
26918
|
return _CronFileParser.#parseContent(data);
|
|
26938
26919
|
}
|
|
26939
26920
|
/**
|
|
@@ -29234,7 +29215,7 @@ var require_websocket = __commonJS({
|
|
|
29234
29215
|
var http3 = require("http");
|
|
29235
29216
|
var net3 = require("net");
|
|
29236
29217
|
var tls = require("tls");
|
|
29237
|
-
var { randomBytes, createHash:
|
|
29218
|
+
var { randomBytes, createHash: createHash2 } = require("crypto");
|
|
29238
29219
|
var { Duplex, Readable: Readable3 } = require("stream");
|
|
29239
29220
|
var { URL: URL2 } = require("url");
|
|
29240
29221
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -29894,7 +29875,7 @@ var require_websocket = __commonJS({
|
|
|
29894
29875
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
29895
29876
|
return;
|
|
29896
29877
|
}
|
|
29897
|
-
const digest =
|
|
29878
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
29898
29879
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
29899
29880
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
29900
29881
|
return;
|
|
@@ -30261,7 +30242,7 @@ var require_websocket_server = __commonJS({
|
|
|
30261
30242
|
var EventEmitter3 = require("events");
|
|
30262
30243
|
var http3 = require("http");
|
|
30263
30244
|
var { Duplex } = require("stream");
|
|
30264
|
-
var { createHash:
|
|
30245
|
+
var { createHash: createHash2 } = require("crypto");
|
|
30265
30246
|
var extension2 = require_extension();
|
|
30266
30247
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
30267
30248
|
var subprotocol2 = require_subprotocol();
|
|
@@ -30562,7 +30543,7 @@ var require_websocket_server = __commonJS({
|
|
|
30562
30543
|
);
|
|
30563
30544
|
}
|
|
30564
30545
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
30565
|
-
const digest =
|
|
30546
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
30566
30547
|
const headers = [
|
|
30567
30548
|
"HTTP/1.1 101 Switching Protocols",
|
|
30568
30549
|
"Upgrade: websocket",
|
|
@@ -42057,6 +42038,8 @@ function buildSpawnContext(state, deps) {
|
|
|
42057
42038
|
env.CLAWD_SESSION_ID = file.sessionId;
|
|
42058
42039
|
const daemonUrl = deps.getDaemonUrl?.() ?? null;
|
|
42059
42040
|
if (daemonUrl) env.CLAWD_DAEMON_URL = daemonUrl;
|
|
42041
|
+
const clawosApi = deps.getClawosApi?.();
|
|
42042
|
+
if (clawosApi) env.CLAWOS_API = clawosApi;
|
|
42060
42043
|
const dispatchId = deps.lookupDispatchByBSessionId?.(file.sessionId);
|
|
42061
42044
|
if (dispatchId) env.CLAWD_DISPATCH_ID = dispatchId;
|
|
42062
42045
|
const personaId = file.ownerPersonaId;
|
|
@@ -42906,6 +42889,9 @@ var SessionRunner = class {
|
|
|
42906
42889
|
// 单栏 refactor (spec 2026-06-02 §5.1): cc 子进程 env 注入 CLAWD_DAEMON_URL,让 assistant
|
|
42907
42890
|
// curl daemon HTTP RPC adapter (/api/rpc/<method>) 触发管理操作。null = HTTP adapter 未启。
|
|
42908
42891
|
getDaemonUrl: this.hooks.getDaemonUrl,
|
|
42892
|
+
// clawd 管家 (spec 2026-07-22-clawd-butler-persona §3.1): cc 子进程 env 注入 CLAWOS_API,
|
|
42893
|
+
// 让 clawd 管家 skill fetch `${CLAWOS_API}/docs/**` 线上文档。
|
|
42894
|
+
getClawosApi: this.hooks.getClawosApi,
|
|
42909
42895
|
// daemon 级 MCP server 列表闭包透传;见 reducer.buildSpawnContext 派生
|
|
42910
42896
|
// SpawnContext.mcpConfigs + 按 DISPATCH_MCP_ID 判断 dispatch 教学注入
|
|
42911
42897
|
getMcpConfigs: this.hooks.getMcpConfigs,
|
|
@@ -43708,6 +43694,9 @@ var SessionManager = class {
|
|
|
43708
43694
|
// 单栏 refactor (spec 2026-06-02 §5.1): 透传 daemon HTTP RPC base URL 闭包,
|
|
43709
43695
|
// reducer 把它注入 cc 子进程 env CLAWD_DAEMON_URL.
|
|
43710
43696
|
getDaemonUrl: this.deps.getDaemonUrl,
|
|
43697
|
+
// clawd 管家 (spec 2026-07-22-clawd-butler-persona §3.1): 透传 clawos 云 api base URL
|
|
43698
|
+
// 闭包, reducer 把它注入 cc 子进程 env CLAWOS_API, skill fetch 线上 md 文档用。
|
|
43699
|
+
getClawosApi: this.deps.getClawosApi,
|
|
43711
43700
|
// daemon 级 MCP server 列表闭包透传给 reducer,cc spawn 逐项加 --mcp-config flag;
|
|
43712
43701
|
// B session 的 CLAWD_DISPATCH_ID env 由下方 lookupDispatchByBSessionId 反查闭包负责。
|
|
43713
43702
|
getMcpConfigs: () => this.deps.mcpConfigs ?? [],
|
|
@@ -45880,17 +45869,9 @@ var PersonaManager = class {
|
|
|
45880
45869
|
}
|
|
45881
45870
|
/**
|
|
45882
45871
|
* 删除 persona。
|
|
45883
|
-
* PersonaStore.remove(personaId)
|
|
45884
|
-
*
|
|
45885
|
-
*
|
|
45886
|
-
*
|
|
45887
|
-
* ⚠️ TODO (Phase 3+ design gap, reviewer P1 flagged):
|
|
45888
|
-
* ~/.clawd/capabilities.json 里若有 cap.grants 含本 personaId, grant 会变成
|
|
45889
|
-
* 悬空引用 (guest 仍能持 token 接入, 但调依赖该 persona 的 RPC 会 fail).
|
|
45890
|
-
* 选项: (A) 这里扫所有 cap 过滤掉该 personaId 的 grant; (B) revoke 所有 grant
|
|
45891
|
-
* 含该 personaId 的 cap. 当前因 cleanupGuestSessionsForCapability 用 rmSync
|
|
45892
|
-
* force=true 吞 ENOENT 不会 crash, 但 UI CapabilityManagerDrawer 会展示
|
|
45893
|
-
* "无效的 persona" grant. Phase 3 加 cross-reference 矩阵后正式实现.
|
|
45872
|
+
* PersonaStore.remove(personaId) 级联删整个 <personaRoot>/<personaId>/ 目录
|
|
45873
|
+
* (含 .clawd/sessions/owner/ 和 .clawd/sessions/guests/<capId>/),rmSync
|
|
45874
|
+
* recursive force 一锅清。
|
|
45894
45875
|
*/
|
|
45895
45876
|
delete(personaId) {
|
|
45896
45877
|
this.deps.store.remove(personaId);
|
|
@@ -45940,7 +45921,7 @@ var DEFAULT_PERSONAS = [
|
|
|
45940
45921
|
},
|
|
45941
45922
|
{
|
|
45942
45923
|
personaId: "persona-clawd-helper",
|
|
45943
|
-
label: "clawd\
|
|
45924
|
+
label: "clawd \u7BA1\u5BB6",
|
|
45944
45925
|
model: "opus",
|
|
45945
45926
|
iconKey: "assist",
|
|
45946
45927
|
public: false,
|
|
@@ -50678,9 +50659,9 @@ var LocalWsServer = class {
|
|
|
50678
50659
|
this.safeSend(c.ws, frame);
|
|
50679
50660
|
}
|
|
50680
50661
|
}
|
|
50681
|
-
//
|
|
50682
|
-
//
|
|
50683
|
-
// guest
|
|
50662
|
+
// 仅广播给 owner 连接(跳过 guest ws)。用于 contact:added / contact:removed /
|
|
50663
|
+
// contact:pinned / friend:reverseTokenOffered / appBuilder:project-updated 等
|
|
50664
|
+
// owner-only push frame——guest 无需消费。noAuth localhost ws 无 ctx,视作 owner。
|
|
50684
50665
|
broadcastToOwners(frame) {
|
|
50685
50666
|
const gate = this.opts.authGate;
|
|
50686
50667
|
for (const c of this.clients.values()) {
|
|
@@ -50754,10 +50735,8 @@ var LocalWsServer = class {
|
|
|
50754
50735
|
}
|
|
50755
50736
|
}
|
|
50756
50737
|
}
|
|
50757
|
-
//
|
|
50758
|
-
//
|
|
50759
|
-
// capability:delete 级联用 cap.id 调本方法——noAuth 旧 per-peer cap guest 的
|
|
50760
|
-
// principal.id ≡ cap.id,级联语义不变(Phase 3 决策 #14 正名)。
|
|
50738
|
+
// 关闭某 guest principal 的所有活跃 ws 连接。close code 4401 让客户端识别
|
|
50739
|
+
// 'TOKEN_REVOKED' 而非普通断连;调用方按需触发(如 contact:remove 后要断对方现有连接)。
|
|
50761
50740
|
closeConnectionsByGuestId(guestPrincipalId, code = 4401, reason = "TOKEN_REVOKED") {
|
|
50762
50741
|
const set = this.guestIdToClients.get(guestPrincipalId);
|
|
50763
50742
|
if (!set) return;
|
|
@@ -51231,191 +51210,38 @@ async function authenticate(token, deps) {
|
|
|
51231
51210
|
};
|
|
51232
51211
|
}
|
|
51233
51212
|
|
|
51234
|
-
// src/
|
|
51235
|
-
var fs33 = __toESM(require("fs"), 1);
|
|
51213
|
+
// src/legacy-cleanup.ts
|
|
51214
|
+
var fs33 = __toESM(require("fs/promises"), 1);
|
|
51236
51215
|
var path34 = __toESM(require("path"), 1);
|
|
51237
|
-
var
|
|
51238
|
-
|
|
51239
|
-
|
|
51240
|
-
|
|
51241
|
-
|
|
51242
|
-
|
|
51243
|
-
this.cache = this.readFromDisk();
|
|
51244
|
-
}
|
|
51245
|
-
dataDir;
|
|
51246
|
-
cache;
|
|
51247
|
-
list() {
|
|
51248
|
-
return [...this.cache];
|
|
51249
|
-
}
|
|
51250
|
-
upsert(cap) {
|
|
51251
|
-
const idx = this.cache.findIndex((c) => c.id === cap.id);
|
|
51252
|
-
if (idx >= 0) {
|
|
51253
|
-
this.cache[idx] = cap;
|
|
51254
|
-
} else {
|
|
51255
|
-
this.cache.push(cap);
|
|
51256
|
-
}
|
|
51257
|
-
this.flush();
|
|
51258
|
-
}
|
|
51259
|
-
remove(id) {
|
|
51260
|
-
const next = this.cache.filter((c) => c.id !== id);
|
|
51261
|
-
if (next.length === this.cache.length) return;
|
|
51262
|
-
this.cache = next;
|
|
51263
|
-
this.flush();
|
|
51264
|
-
}
|
|
51265
|
-
filePath() {
|
|
51266
|
-
return path34.join(this.dataDir, CAPABILITIES_FILE_NAME);
|
|
51267
|
-
}
|
|
51268
|
-
readFromDisk() {
|
|
51269
|
-
const file = this.filePath();
|
|
51270
|
-
let raw;
|
|
51271
|
-
try {
|
|
51272
|
-
raw = fs33.readFileSync(file, "utf8");
|
|
51273
|
-
} catch (err) {
|
|
51274
|
-
if (err?.code === "ENOENT") return [];
|
|
51275
|
-
return [];
|
|
51276
|
-
}
|
|
51277
|
-
if (!raw.trim()) return [];
|
|
51278
|
-
let parsed;
|
|
51279
|
-
try {
|
|
51280
|
-
parsed = JSON.parse(raw);
|
|
51281
|
-
} catch {
|
|
51282
|
-
return [];
|
|
51283
|
-
}
|
|
51284
|
-
if (!parsed || typeof parsed !== "object") return [];
|
|
51285
|
-
const arr = parsed.capabilities;
|
|
51286
|
-
if (!Array.isArray(arr)) return [];
|
|
51287
|
-
const out = [];
|
|
51288
|
-
for (const item of arr) {
|
|
51289
|
-
const r = CapabilitySchema.safeParse(item);
|
|
51290
|
-
if (r.success) out.push(r.data);
|
|
51291
|
-
}
|
|
51292
|
-
return out;
|
|
51293
|
-
}
|
|
51294
|
-
flush() {
|
|
51295
|
-
const content = { version: FILE_VERSION, capabilities: this.cache };
|
|
51296
|
-
this.atomicWrite(this.filePath(), JSON.stringify(content, null, 2));
|
|
51297
|
-
}
|
|
51298
|
-
atomicWrite(file, content) {
|
|
51299
|
-
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
51300
|
-
fs33.writeFileSync(tmp, content, { mode: 384 });
|
|
51301
|
-
fs33.renameSync(tmp, file);
|
|
51302
|
-
try {
|
|
51303
|
-
fs33.chmodSync(file, 384);
|
|
51304
|
-
} catch {
|
|
51305
|
-
}
|
|
51306
|
-
}
|
|
51307
|
-
};
|
|
51308
|
-
|
|
51309
|
-
// src/permission/capability-registry.ts
|
|
51310
|
-
var crypto5 = __toESM(require("crypto"), 1);
|
|
51311
|
-
var CapabilityRegistry = class {
|
|
51312
|
-
constructor(store, now = () => Date.now()) {
|
|
51313
|
-
this.store = store;
|
|
51314
|
-
this.now = now;
|
|
51315
|
-
for (const cap of store.list()) {
|
|
51316
|
-
this.bySecretHash.set(cap.secretHash, cap);
|
|
51317
|
-
}
|
|
51318
|
-
}
|
|
51319
|
-
store;
|
|
51320
|
-
now;
|
|
51321
|
-
// sha256(token) → Capability
|
|
51322
|
-
bySecretHash = /* @__PURE__ */ new Map();
|
|
51323
|
-
list() {
|
|
51324
|
-
return this.store.list();
|
|
51325
|
-
}
|
|
51326
|
-
verifyToken(token) {
|
|
51327
|
-
if (!token) return { ok: false, code: "TOKEN_INVALID" };
|
|
51328
|
-
const hash = sha256Hex(token);
|
|
51329
|
-
const cap = this.bySecretHash.get(hash);
|
|
51330
|
-
if (!cap) return { ok: false, code: "TOKEN_INVALID" };
|
|
51331
|
-
if (cap.expiresAt && this.now() >= cap.expiresAt) {
|
|
51332
|
-
return { ok: false, code: "TOKEN_EXPIRED" };
|
|
51333
|
-
}
|
|
51334
|
-
if (cap.maxUses && cap.usedCount >= cap.maxUses) {
|
|
51335
|
-
return { ok: false, code: "TOKEN_EXHAUSTED" };
|
|
51336
|
-
}
|
|
51337
|
-
const updated = { ...cap, usedCount: cap.usedCount + 1 };
|
|
51338
|
-
this.bySecretHash.set(hash, updated);
|
|
51339
|
-
this.store.upsert(updated);
|
|
51340
|
-
return { ok: true, capability: updated };
|
|
51341
|
-
}
|
|
51342
|
-
upsertCapability(cap) {
|
|
51343
|
-
this.bySecretHash.set(cap.secretHash, cap);
|
|
51344
|
-
this.store.upsert(cap);
|
|
51345
|
-
}
|
|
51346
|
-
/**
|
|
51347
|
-
* Hard delete:从 store 物理移除 + 从 Map 清掉 secretHash 索引。
|
|
51348
|
-
* 后续 verifyToken 该 token 走 'TOKEN_INVALID' 分支(secretHash 已不在 Map)。
|
|
51349
|
-
* idempotent:不存在 → null。
|
|
51350
|
-
*/
|
|
51351
|
-
delete(id) {
|
|
51352
|
-
const current = this.store.list().find((c) => c.id === id);
|
|
51353
|
-
if (!current) return null;
|
|
51354
|
-
this.bySecretHash.delete(current.secretHash);
|
|
51355
|
-
this.store.remove(id);
|
|
51356
|
-
return current;
|
|
51357
|
-
}
|
|
51358
|
-
findById(id) {
|
|
51359
|
-
return this.store.list().find((c) => c.id === id) ?? null;
|
|
51360
|
-
}
|
|
51361
|
-
};
|
|
51362
|
-
function sha256Hex(s) {
|
|
51363
|
-
return crypto5.createHash("sha256").update(s).digest("hex");
|
|
51364
|
-
}
|
|
51365
|
-
|
|
51366
|
-
// src/permission/capability-manager.ts
|
|
51367
|
-
var CapabilityManager = class {
|
|
51368
|
-
constructor(registry2, hooks = {}) {
|
|
51369
|
-
this.registry = registry2;
|
|
51370
|
-
this.hooks = hooks;
|
|
51371
|
-
}
|
|
51372
|
-
registry;
|
|
51373
|
-
hooks;
|
|
51374
|
-
list() {
|
|
51375
|
-
return this.registry.list();
|
|
51376
|
-
}
|
|
51377
|
-
// 调试 / 旧 per-peer cap 反查用(Phase 3 决策 #14 后无生产调用方,保留属三件套范围)。
|
|
51378
|
-
// 返回 Capability 持久化形态;调用方需 stripSecretHash 后再上 wire。
|
|
51379
|
-
findById(id) {
|
|
51380
|
-
return this.registry.findById(id);
|
|
51381
|
-
}
|
|
51382
|
-
/**
|
|
51383
|
-
* Hard delete:从 registry / store 物理移除 capability。
|
|
51384
|
-
* idempotent:不存在 → null(handler 翻译成 VALIDATION_ERROR)
|
|
51385
|
-
* onDeleted hook 在 daemon/index.ts wire 负责 close 连接 + cleanup 文件 + 广播
|
|
51386
|
-
*/
|
|
51387
|
-
delete(id) {
|
|
51388
|
-
const removed = this.registry.delete(id);
|
|
51389
|
-
if (!removed) return null;
|
|
51390
|
-
this.hooks.onDeleted?.(removed);
|
|
51391
|
-
return { capability: removed };
|
|
51392
|
-
}
|
|
51393
|
-
};
|
|
51394
|
-
|
|
51395
|
-
// src/permission/cleanup.ts
|
|
51396
|
-
var fs34 = __toESM(require("fs"), 1);
|
|
51397
|
-
function cleanupGuestSessionsForCapability(cap, factory) {
|
|
51216
|
+
var LEGACY_FILES = [
|
|
51217
|
+
"personal-capability.json",
|
|
51218
|
+
"received-capabilities.json",
|
|
51219
|
+
"capabilities.json"
|
|
51220
|
+
];
|
|
51221
|
+
async function cleanupLegacyCapabilityFiles(dataDir, logger) {
|
|
51398
51222
|
const removed = [];
|
|
51399
|
-
for (const
|
|
51400
|
-
|
|
51401
|
-
const dir = factory.vmGuestRoot(g2.resource.id, cap.id);
|
|
51223
|
+
for (const name of LEGACY_FILES) {
|
|
51224
|
+
const p2 = path34.join(dataDir, name);
|
|
51402
51225
|
try {
|
|
51403
|
-
|
|
51404
|
-
removed.push(
|
|
51405
|
-
|
|
51226
|
+
await fs33.unlink(p2);
|
|
51227
|
+
removed.push(name);
|
|
51228
|
+
logger.debug("legacy capability file removed", { file: name });
|
|
51229
|
+
} catch (err) {
|
|
51230
|
+
if (err?.code === "ENOENT") continue;
|
|
51231
|
+
throw err;
|
|
51406
51232
|
}
|
|
51407
51233
|
}
|
|
51408
51234
|
return { removed };
|
|
51409
51235
|
}
|
|
51410
51236
|
|
|
51411
51237
|
// src/inbox/inbox-store.ts
|
|
51412
|
-
var
|
|
51238
|
+
var fs34 = __toESM(require("fs"), 1);
|
|
51413
51239
|
var path35 = __toESM(require("path"), 1);
|
|
51414
51240
|
var INBOX_SUBDIR = "inbox";
|
|
51415
51241
|
var InboxStore = class {
|
|
51416
51242
|
constructor(dataDir) {
|
|
51417
51243
|
this.dataDir = dataDir;
|
|
51418
|
-
|
|
51244
|
+
fs34.mkdirSync(this.dirPath(), { recursive: true });
|
|
51419
51245
|
}
|
|
51420
51246
|
dataDir;
|
|
51421
51247
|
/**
|
|
@@ -51427,7 +51253,7 @@ var InboxStore = class {
|
|
|
51427
51253
|
const file = this.filePath(peerDeviceId);
|
|
51428
51254
|
let raw;
|
|
51429
51255
|
try {
|
|
51430
|
-
raw =
|
|
51256
|
+
raw = fs34.readFileSync(file, "utf8");
|
|
51431
51257
|
} catch (err) {
|
|
51432
51258
|
if (err?.code === "ENOENT") return [];
|
|
51433
51259
|
return [];
|
|
@@ -51443,7 +51269,7 @@ var InboxStore = class {
|
|
|
51443
51269
|
const dir = this.dirPath();
|
|
51444
51270
|
let entries;
|
|
51445
51271
|
try {
|
|
51446
|
-
entries =
|
|
51272
|
+
entries = fs34.readdirSync(dir);
|
|
51447
51273
|
} catch (err) {
|
|
51448
51274
|
if (err?.code === "ENOENT") return [];
|
|
51449
51275
|
return [];
|
|
@@ -51459,9 +51285,9 @@ var InboxStore = class {
|
|
|
51459
51285
|
if (existing.some((m2) => m2.id === message.id)) return;
|
|
51460
51286
|
const file = this.filePath(message.peerDeviceId);
|
|
51461
51287
|
const line = JSON.stringify(message) + "\n";
|
|
51462
|
-
|
|
51288
|
+
fs34.appendFileSync(file, line, { mode: 384 });
|
|
51463
51289
|
try {
|
|
51464
|
-
|
|
51290
|
+
fs34.chmodSync(file, 384);
|
|
51465
51291
|
} catch {
|
|
51466
51292
|
}
|
|
51467
51293
|
}
|
|
@@ -51491,7 +51317,7 @@ var InboxStore = class {
|
|
|
51491
51317
|
removeByPeerDeviceId(peerDeviceId) {
|
|
51492
51318
|
const file = this.filePath(peerDeviceId);
|
|
51493
51319
|
try {
|
|
51494
|
-
|
|
51320
|
+
fs34.unlinkSync(file);
|
|
51495
51321
|
} catch (err) {
|
|
51496
51322
|
if (err?.code === "ENOENT") return;
|
|
51497
51323
|
}
|
|
@@ -51500,10 +51326,10 @@ var InboxStore = class {
|
|
|
51500
51326
|
const file = this.filePath(peerDeviceId);
|
|
51501
51327
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
51502
51328
|
const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
|
|
51503
|
-
|
|
51504
|
-
|
|
51329
|
+
fs34.writeFileSync(tmp, content, { mode: 384 });
|
|
51330
|
+
fs34.renameSync(tmp, file);
|
|
51505
51331
|
try {
|
|
51506
|
-
|
|
51332
|
+
fs34.chmodSync(file, 384);
|
|
51507
51333
|
} catch {
|
|
51508
51334
|
}
|
|
51509
51335
|
}
|
|
@@ -51599,7 +51425,7 @@ var InboxManager = class {
|
|
|
51599
51425
|
};
|
|
51600
51426
|
|
|
51601
51427
|
// src/state/contact-store.ts
|
|
51602
|
-
var
|
|
51428
|
+
var fs35 = __toESM(require("fs"), 1);
|
|
51603
51429
|
var path36 = __toESM(require("path"), 1);
|
|
51604
51430
|
var FILE_NAME = "contacts.json";
|
|
51605
51431
|
var ContactStore = class {
|
|
@@ -51613,7 +51439,7 @@ var ContactStore = class {
|
|
|
51613
51439
|
const file = path36.join(this.dataDir, FILE_NAME);
|
|
51614
51440
|
let raw;
|
|
51615
51441
|
try {
|
|
51616
|
-
raw =
|
|
51442
|
+
raw = fs35.readFileSync(file, "utf8");
|
|
51617
51443
|
} catch (err) {
|
|
51618
51444
|
if (err?.code !== "ENOENT") this.renameBak(file);
|
|
51619
51445
|
return;
|
|
@@ -51689,20 +51515,20 @@ var ContactStore = class {
|
|
|
51689
51515
|
null,
|
|
51690
51516
|
2
|
|
51691
51517
|
);
|
|
51692
|
-
|
|
51693
|
-
|
|
51694
|
-
|
|
51518
|
+
fs35.mkdirSync(this.dataDir, { recursive: true });
|
|
51519
|
+
fs35.writeFileSync(tmp, content, { mode: 384 });
|
|
51520
|
+
fs35.renameSync(tmp, file);
|
|
51695
51521
|
}
|
|
51696
51522
|
renameBak(file) {
|
|
51697
51523
|
try {
|
|
51698
|
-
|
|
51524
|
+
fs35.renameSync(file, `${file}.bak`);
|
|
51699
51525
|
} catch {
|
|
51700
51526
|
}
|
|
51701
51527
|
}
|
|
51702
51528
|
};
|
|
51703
51529
|
|
|
51704
51530
|
// src/contact/connect-remote.ts
|
|
51705
|
-
var
|
|
51531
|
+
var crypto5 = __toESM(require("crypto"), 1);
|
|
51706
51532
|
var HANDSHAKE_TIMEOUT_MS = 5e3;
|
|
51707
51533
|
var RPC_TIMEOUT_MS = 5e3;
|
|
51708
51534
|
async function connectRemote(args) {
|
|
@@ -51766,7 +51592,7 @@ async function connectRemote(args) {
|
|
|
51766
51592
|
ws.once("close", onClose);
|
|
51767
51593
|
});
|
|
51768
51594
|
function call(method, payload) {
|
|
51769
|
-
const requestId =
|
|
51595
|
+
const requestId = crypto5.randomUUID();
|
|
51770
51596
|
return new Promise((resolve6, reject) => {
|
|
51771
51597
|
const onMessage = (raw) => {
|
|
51772
51598
|
let f;
|
|
@@ -51848,7 +51674,7 @@ async function autoReverseContact(args) {
|
|
|
51848
51674
|
}
|
|
51849
51675
|
|
|
51850
51676
|
// src/migrations/2026-05-20-flatten-sessions.ts
|
|
51851
|
-
var
|
|
51677
|
+
var fs36 = __toESM(require("fs"), 1);
|
|
51852
51678
|
var path37 = __toESM(require("path"), 1);
|
|
51853
51679
|
var MIGRATION_FLAG_NAME = ".migration.v1.done";
|
|
51854
51680
|
function migrateFlattenSessions(opts) {
|
|
@@ -51868,7 +51694,7 @@ function migrateFlattenSessions(opts) {
|
|
|
51868
51694
|
if (!entry.endsWith(".json")) continue;
|
|
51869
51695
|
const src = path37.join(defaultDir, entry);
|
|
51870
51696
|
const dst = path37.join(sessionsDir, entry);
|
|
51871
|
-
|
|
51697
|
+
fs36.renameSync(src, dst);
|
|
51872
51698
|
movedBare += 1;
|
|
51873
51699
|
}
|
|
51874
51700
|
rmdirIfEmpty(defaultDir);
|
|
@@ -51880,10 +51706,10 @@ function migrateFlattenSessions(opts) {
|
|
|
51880
51706
|
const ownerSrc = path37.join(personaDir, "owner");
|
|
51881
51707
|
if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
|
|
51882
51708
|
const ownerDst = path37.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
|
|
51883
|
-
|
|
51709
|
+
fs36.mkdirSync(ownerDst, { recursive: true });
|
|
51884
51710
|
for (const file of readdirSafe(ownerSrc)) {
|
|
51885
51711
|
if (!file.endsWith(".json")) continue;
|
|
51886
|
-
|
|
51712
|
+
fs36.renameSync(path37.join(ownerSrc, file), path37.join(ownerDst, file));
|
|
51887
51713
|
movedVmOwner += 1;
|
|
51888
51714
|
}
|
|
51889
51715
|
rmdirIfEmpty(ownerSrc);
|
|
@@ -51891,18 +51717,18 @@ function migrateFlattenSessions(opts) {
|
|
|
51891
51717
|
const listenerSrc = path37.join(personaDir, "listener");
|
|
51892
51718
|
if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
|
|
51893
51719
|
const archiveDst = path37.join(dataDir, ".legacy", `listener-${pid}`);
|
|
51894
|
-
|
|
51720
|
+
fs36.mkdirSync(archiveDst, { recursive: true });
|
|
51895
51721
|
for (const file of readdirSafe(listenerSrc)) {
|
|
51896
51722
|
if (!file.endsWith(".json")) continue;
|
|
51897
|
-
|
|
51723
|
+
fs36.renameSync(path37.join(listenerSrc, file), path37.join(archiveDst, file));
|
|
51898
51724
|
archivedListener += 1;
|
|
51899
51725
|
}
|
|
51900
51726
|
rmdirIfEmpty(listenerSrc);
|
|
51901
51727
|
}
|
|
51902
51728
|
rmdirIfEmpty(personaDir);
|
|
51903
51729
|
}
|
|
51904
|
-
|
|
51905
|
-
|
|
51730
|
+
fs36.mkdirSync(sessionsDir, { recursive: true });
|
|
51731
|
+
fs36.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
|
|
51906
51732
|
return {
|
|
51907
51733
|
skipped: false,
|
|
51908
51734
|
flagWritten: true,
|
|
@@ -51913,7 +51739,7 @@ function migrateFlattenSessions(opts) {
|
|
|
51913
51739
|
}
|
|
51914
51740
|
function existsSync3(p2) {
|
|
51915
51741
|
try {
|
|
51916
|
-
|
|
51742
|
+
fs36.statSync(p2);
|
|
51917
51743
|
return true;
|
|
51918
51744
|
} catch {
|
|
51919
51745
|
return false;
|
|
@@ -51921,21 +51747,21 @@ function existsSync3(p2) {
|
|
|
51921
51747
|
}
|
|
51922
51748
|
function isDir(p2) {
|
|
51923
51749
|
try {
|
|
51924
|
-
return
|
|
51750
|
+
return fs36.statSync(p2).isDirectory();
|
|
51925
51751
|
} catch {
|
|
51926
51752
|
return false;
|
|
51927
51753
|
}
|
|
51928
51754
|
}
|
|
51929
51755
|
function readdirSafe(p2) {
|
|
51930
51756
|
try {
|
|
51931
|
-
return
|
|
51757
|
+
return fs36.readdirSync(p2);
|
|
51932
51758
|
} catch {
|
|
51933
51759
|
return [];
|
|
51934
51760
|
}
|
|
51935
51761
|
}
|
|
51936
51762
|
function rmdirIfEmpty(p2) {
|
|
51937
51763
|
try {
|
|
51938
|
-
|
|
51764
|
+
fs36.rmdirSync(p2);
|
|
51939
51765
|
} catch {
|
|
51940
51766
|
}
|
|
51941
51767
|
}
|
|
@@ -54282,7 +54108,7 @@ async function upsertDeviceBinding(opts) {
|
|
|
54282
54108
|
}
|
|
54283
54109
|
|
|
54284
54110
|
// src/feishu-auth/verify-token.ts
|
|
54285
|
-
var
|
|
54111
|
+
var crypto13 = __toESM(require("crypto"), 1);
|
|
54286
54112
|
var CONNECT_TOKEN_PREFIX = "clawdtk1";
|
|
54287
54113
|
function verifyConnectToken(args) {
|
|
54288
54114
|
const now = args.nowSeconds ?? Math.floor(Date.now() / 1e3);
|
|
@@ -54294,7 +54120,7 @@ function verifyConnectToken(args) {
|
|
|
54294
54120
|
const data = `${prefix}.${payloadB64}`;
|
|
54295
54121
|
let signatureValid = false;
|
|
54296
54122
|
try {
|
|
54297
|
-
signatureValid =
|
|
54123
|
+
signatureValid = crypto13.verify(
|
|
54298
54124
|
null,
|
|
54299
54125
|
Buffer.from(data, "utf8"),
|
|
54300
54126
|
args.publicKeyPem,
|
|
@@ -54331,7 +54157,7 @@ function verifyConnectToken(args) {
|
|
|
54331
54157
|
}
|
|
54332
54158
|
|
|
54333
54159
|
// src/feishu-auth/server-key.ts
|
|
54334
|
-
var
|
|
54160
|
+
var fs50 = __toESM(require("fs"), 1);
|
|
54335
54161
|
var path53 = __toESM(require("path"), 1);
|
|
54336
54162
|
var FILE_NAME2 = "server-signing-key.json";
|
|
54337
54163
|
var ServerKeyStore = class {
|
|
@@ -54345,7 +54171,7 @@ var ServerKeyStore = class {
|
|
|
54345
54171
|
/** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
|
|
54346
54172
|
read() {
|
|
54347
54173
|
try {
|
|
54348
|
-
const raw =
|
|
54174
|
+
const raw = fs50.readFileSync(this.filePath(), "utf8");
|
|
54349
54175
|
const parsed = JSON.parse(raw);
|
|
54350
54176
|
if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
|
|
54351
54177
|
return parsed.publicKeyPem;
|
|
@@ -54360,12 +54186,12 @@ var ServerKeyStore = class {
|
|
|
54360
54186
|
publicKeyPem,
|
|
54361
54187
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
54362
54188
|
};
|
|
54363
|
-
|
|
54364
|
-
|
|
54189
|
+
fs50.mkdirSync(this.dataDir, { recursive: true });
|
|
54190
|
+
fs50.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
|
|
54365
54191
|
}
|
|
54366
54192
|
clear() {
|
|
54367
54193
|
try {
|
|
54368
|
-
|
|
54194
|
+
fs50.unlinkSync(this.filePath());
|
|
54369
54195
|
} catch {
|
|
54370
54196
|
}
|
|
54371
54197
|
}
|
|
@@ -55272,45 +55098,6 @@ function buildCapabilitiesHandlers(deps) {
|
|
|
55272
55098
|
};
|
|
55273
55099
|
}
|
|
55274
55100
|
|
|
55275
|
-
// src/handlers/capability.ts
|
|
55276
|
-
init_zod();
|
|
55277
|
-
init_protocol();
|
|
55278
|
-
var DeleteArgsSchema = external_exports.object({
|
|
55279
|
-
capabilityId: external_exports.string().min(1)
|
|
55280
|
-
}).strict();
|
|
55281
|
-
function buildCapabilityHandlers(deps) {
|
|
55282
|
-
const { manager } = deps;
|
|
55283
|
-
const list = async () => {
|
|
55284
|
-
return {
|
|
55285
|
-
response: {
|
|
55286
|
-
type: "capability:list",
|
|
55287
|
-
capabilities: manager.list().map(stripSecretHash)
|
|
55288
|
-
}
|
|
55289
|
-
};
|
|
55290
|
-
};
|
|
55291
|
-
const del = async (frame) => {
|
|
55292
|
-
const { type: _type, requestId: _requestId, ...rest } = frame;
|
|
55293
|
-
const args = DeleteArgsSchema.parse(rest);
|
|
55294
|
-
const result = manager.delete(args.capabilityId);
|
|
55295
|
-
if (!result) {
|
|
55296
|
-
throw new ClawdError(
|
|
55297
|
-
ERROR_CODES.VALIDATION_ERROR,
|
|
55298
|
-
`capability not found: ${args.capabilityId}`
|
|
55299
|
-
);
|
|
55300
|
-
}
|
|
55301
|
-
return {
|
|
55302
|
-
response: {
|
|
55303
|
-
type: "capability:deleted",
|
|
55304
|
-
capabilityId: args.capabilityId
|
|
55305
|
-
}
|
|
55306
|
-
};
|
|
55307
|
-
};
|
|
55308
|
-
return {
|
|
55309
|
-
"capability:list": list,
|
|
55310
|
-
"capability:delete": del
|
|
55311
|
-
};
|
|
55312
|
-
}
|
|
55313
|
-
|
|
55314
55101
|
// src/handlers/inbox.ts
|
|
55315
55102
|
init_protocol();
|
|
55316
55103
|
function resolvePeerDeviceId(ctx, argsPeerDeviceId) {
|
|
@@ -58178,9 +57965,6 @@ function buildMethodHandlers(deps) {
|
|
|
58178
57965
|
personaManager: deps.personaManager,
|
|
58179
57966
|
personaRegistry: deps.personaRegistry
|
|
58180
57967
|
}),
|
|
58181
|
-
...buildCapabilityHandlers({
|
|
58182
|
-
manager: deps.capabilityManager
|
|
58183
|
-
}),
|
|
58184
57968
|
...buildInboxHandlers({
|
|
58185
57969
|
manager: deps.inboxManager,
|
|
58186
57970
|
sendDmDeps: {
|
|
@@ -58216,8 +58000,7 @@ function buildMethodHandlers(deps) {
|
|
|
58216
58000
|
ownerPrincipalId: deps.ownerPrincipalId,
|
|
58217
58001
|
ownerId: deps.ownerId,
|
|
58218
58002
|
ownerProvider: deps.ownerProvider,
|
|
58219
|
-
personaStore: deps.personaStore
|
|
58220
|
-
capabilityManager: deps.capabilityManager
|
|
58003
|
+
personaStore: deps.personaStore
|
|
58221
58004
|
}),
|
|
58222
58005
|
...buildFeishuAuthHandlers(deps.feishuAuth),
|
|
58223
58006
|
...buildDeviceHandlers(deps.feishuDevice),
|
|
@@ -58531,12 +58314,6 @@ var METHOD_GRANT_MAP = {
|
|
|
58531
58314
|
// handler 内部按 ctx.principal.kind 决定返回 "合成 owner self capability" 还是
|
|
58532
58315
|
// "查 capabilityManager 拿 caller 的 guest capability"。
|
|
58533
58316
|
"whoami": { kind: "public" },
|
|
58534
|
-
// ---- capability platform(admin-only) ----
|
|
58535
|
-
// 飞书统一身份 Phase 2 (2026-06-01, spec 决策 #12): personal-cap:get / capability:issue /
|
|
58536
|
-
// bindPeer 已下线(personal token / 邀请码全链路删除)。capability:list / delete 保留供调试 +
|
|
58537
|
-
// 撤销级联 + 清旧 per-peer cap。
|
|
58538
|
-
"capability:list": ADMIN_ANY,
|
|
58539
|
-
"capability:delete": ADMIN_ANY,
|
|
58540
58317
|
// ---- inbox: 双投 P2P IM ----
|
|
58541
58318
|
// 三条都 'public' — 能连上 = 已通过本地验票. handler 内额外校验 guest 的
|
|
58542
58319
|
// args.peerDeviceId 与 ctx.principal.id(=deviceId) 一致, 防 guest 越权操作别的 DM 桶.
|
|
@@ -59196,32 +58973,13 @@ async function startDaemon(config) {
|
|
|
59196
58973
|
}
|
|
59197
58974
|
} : {}
|
|
59198
58975
|
});
|
|
59199
|
-
|
|
59200
|
-
const capabilityRegistry = new CapabilityRegistry(capabilityStore);
|
|
59201
|
-
const capabilityManager = new CapabilityManager(capabilityRegistry, {
|
|
59202
|
-
onDeleted: (cap) => {
|
|
59203
|
-
const deletedAt = Date.now();
|
|
59204
|
-
wsServer?.broadcastToOwners({
|
|
59205
|
-
type: "capability:tokenDeleted",
|
|
59206
|
-
capabilityId: cap.id,
|
|
59207
|
-
deletedAt
|
|
59208
|
-
});
|
|
59209
|
-
wsServer?.closeConnectionsByGuestId(cap.id);
|
|
59210
|
-
const cleanup = cleanupGuestSessionsForCapability(cap, sessionStoreFactory);
|
|
59211
|
-
if (cleanup.removed.length > 0) {
|
|
59212
|
-
logger.info("capability delete cascade: guest sessions removed", {
|
|
59213
|
-
capabilityId: cap.id,
|
|
59214
|
-
removedDirs: cleanup.removed
|
|
59215
|
-
});
|
|
59216
|
-
}
|
|
59217
|
-
}
|
|
59218
|
-
});
|
|
58976
|
+
await cleanupLegacyCapabilityFiles(config.dataDir, logger);
|
|
59219
58977
|
migrateDropPersonsDir(config.dataDir);
|
|
59220
58978
|
let wsServer = null;
|
|
59221
58979
|
const authGate = authMode === "first-message" ? new AuthGate({
|
|
59222
58980
|
shouldEnforce: buildShouldEnforce({ tunnel: config.tunnel }),
|
|
59223
58981
|
// Task 1.7:authenticate 注入路径替代 expectedToken 单 token 比对。
|
|
59224
|
-
// owner 路径 constantTimeEqual 防侧信道;guest 路径走
|
|
58982
|
+
// owner 路径 constantTimeEqual 防侧信道;guest 路径走 verifyConnectToken (Ed25519).
|
|
59225
58983
|
expectedToken: resolvedAuthToken,
|
|
59226
58984
|
authenticate: async (t, selfUrl) => {
|
|
59227
58985
|
const result = await authenticate(t, buildConnectAuthDeps());
|
|
@@ -59477,6 +59235,11 @@ async function startDaemon(config) {
|
|
|
59477
59235
|
// 127.0.0.1(不是 config.host)—— cc 跑在本机,loopback 最稳;外部访问限制 + http-router
|
|
59478
59236
|
// 的 isLoopback 兜底已确保安全。
|
|
59479
59237
|
getDaemonUrl: () => `http://127.0.0.1:${config.port}`,
|
|
59238
|
+
// clawd 管家 (spec 2026-07-22-clawd-butler-persona §3.1):cc 子进程 env CLAWOS_API 闭包来源。
|
|
59239
|
+
// clawd 管家 skill 用它 fetch 云端 md 文档(`${CLAWOS_API}/docs/**`)。非 tunnel 模式
|
|
59240
|
+
// config.clawosApi 为 null → fallback 到 DEFAULT_CLAWOS_API(api.clawos.chat),保证注入
|
|
59241
|
+
// 的 env 一定非空;skill 拿到但云端不可达时走 §3.4 明说降级。
|
|
59242
|
+
getClawosApi: () => config.clawosApi ?? DEFAULT_CLAWOS_API,
|
|
59480
59243
|
// Persona dispatch (Task 8): manager.createDispatchedSession 用 personaDispatchManager
|
|
59481
59244
|
// registerBSession;reducer 通过闭包反查 dispatchId 注 CLAWD_DISPATCH_ID env。
|
|
59482
59245
|
personaDispatchManager,
|
|
@@ -59745,8 +59508,6 @@ async function startDaemon(config) {
|
|
|
59745
59508
|
personaRoot: import_node_path61.default.join(config.dataDir, "personas"),
|
|
59746
59509
|
// v2 多人 persona 隔离:handler 派生 guest user-dir 放行
|
|
59747
59510
|
usersRoot,
|
|
59748
|
-
// capability:list / delete handler 依赖
|
|
59749
|
-
capabilityManager,
|
|
59750
59511
|
// v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
|
|
59751
59512
|
ownerDisplayName,
|
|
59752
59513
|
// 决策 #16: whoami owner.id = 本机 deviceId(路由主键)+ ownerId/provider 归属人
|
|
@@ -59756,8 +59517,6 @@ async function startDaemon(config) {
|
|
|
59756
59517
|
ownerId,
|
|
59757
59518
|
ownerProvider,
|
|
59758
59519
|
personaStore,
|
|
59759
|
-
// capability handler 也用 (capability:issue 走 registry / capability:list)
|
|
59760
|
-
capabilityRegistry,
|
|
59761
59520
|
// capability platform v3 inbox: handler 接 manager (post/list/markRead),
|
|
59762
59521
|
// cascade 接 store (list 统计 deletedInboxEvents).
|
|
59763
59522
|
inboxManager,
|
|
@@ -60092,18 +59851,11 @@ async function startDaemon(config) {
|
|
|
60092
59851
|
),
|
|
60093
59852
|
protocolVersion: PROTOCOL_VERSION,
|
|
60094
59853
|
authGate: authGate ?? void 0,
|
|
60095
|
-
//
|
|
60096
|
-
//
|
|
60097
|
-
//
|
|
60098
|
-
//
|
|
59854
|
+
// T-35: capability path retired;本 hook 只剩 persona web 分享 visitor token 分支:
|
|
59855
|
+
// noAuth localhost daemon 上访客 token 若命中 → attach guest ctx,否则 fallback owner
|
|
59856
|
+
// (非 persona-web 场景静默 return null;tunnel 模式走 AuthGate 不经此路)。参数名
|
|
59857
|
+
// tryVerifyCapabilityToken 不再准确,跨 local-ws-server 改名属 followup 清理。
|
|
60099
59858
|
tryVerifyCapabilityToken: (token) => {
|
|
60100
|
-
const v2 = capabilityRegistry.verifyToken(token);
|
|
60101
|
-
if (v2.ok) {
|
|
60102
|
-
return {
|
|
60103
|
-
principal: { id: v2.capability.id, kind: "guest", displayName: v2.capability.displayName },
|
|
60104
|
-
grants: v2.capability.grants
|
|
60105
|
-
};
|
|
60106
|
-
}
|
|
60107
59859
|
const vis = verifyVisitorToken(authFile.visitorTokenSecret, token, Math.floor(Date.now() / 1e3));
|
|
60108
59860
|
if (vis.ok) return visitorGuestContext(vis.payload);
|
|
60109
59861
|
return null;
|