@clawos-dev/clawd 0.2.251 → 0.2.252
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 -360
- package/dist/dispatch/mcp-server.cjs +0 -6
- 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",
|
|
@@ -45880,17 +45861,9 @@ var PersonaManager = class {
|
|
|
45880
45861
|
}
|
|
45881
45862
|
/**
|
|
45882
45863
|
* 删除 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 矩阵后正式实现.
|
|
45864
|
+
* PersonaStore.remove(personaId) 级联删整个 <personaRoot>/<personaId>/ 目录
|
|
45865
|
+
* (含 .clawd/sessions/owner/ 和 .clawd/sessions/guests/<capId>/),rmSync
|
|
45866
|
+
* recursive force 一锅清。
|
|
45894
45867
|
*/
|
|
45895
45868
|
delete(personaId) {
|
|
45896
45869
|
this.deps.store.remove(personaId);
|
|
@@ -50678,9 +50651,9 @@ var LocalWsServer = class {
|
|
|
50678
50651
|
this.safeSend(c.ws, frame);
|
|
50679
50652
|
}
|
|
50680
50653
|
}
|
|
50681
|
-
//
|
|
50682
|
-
//
|
|
50683
|
-
// guest
|
|
50654
|
+
// 仅广播给 owner 连接(跳过 guest ws)。用于 contact:added / contact:removed /
|
|
50655
|
+
// contact:pinned / friend:reverseTokenOffered / appBuilder:project-updated 等
|
|
50656
|
+
// owner-only push frame——guest 无需消费。noAuth localhost ws 无 ctx,视作 owner。
|
|
50684
50657
|
broadcastToOwners(frame) {
|
|
50685
50658
|
const gate = this.opts.authGate;
|
|
50686
50659
|
for (const c of this.clients.values()) {
|
|
@@ -50754,10 +50727,8 @@ var LocalWsServer = class {
|
|
|
50754
50727
|
}
|
|
50755
50728
|
}
|
|
50756
50729
|
}
|
|
50757
|
-
//
|
|
50758
|
-
//
|
|
50759
|
-
// capability:delete 级联用 cap.id 调本方法——noAuth 旧 per-peer cap guest 的
|
|
50760
|
-
// principal.id ≡ cap.id,级联语义不变(Phase 3 决策 #14 正名)。
|
|
50730
|
+
// 关闭某 guest principal 的所有活跃 ws 连接。close code 4401 让客户端识别
|
|
50731
|
+
// 'TOKEN_REVOKED' 而非普通断连;调用方按需触发(如 contact:remove 后要断对方现有连接)。
|
|
50761
50732
|
closeConnectionsByGuestId(guestPrincipalId, code = 4401, reason = "TOKEN_REVOKED") {
|
|
50762
50733
|
const set = this.guestIdToClients.get(guestPrincipalId);
|
|
50763
50734
|
if (!set) return;
|
|
@@ -51231,191 +51202,38 @@ async function authenticate(token, deps) {
|
|
|
51231
51202
|
};
|
|
51232
51203
|
}
|
|
51233
51204
|
|
|
51234
|
-
// src/
|
|
51235
|
-
var fs33 = __toESM(require("fs"), 1);
|
|
51205
|
+
// src/legacy-cleanup.ts
|
|
51206
|
+
var fs33 = __toESM(require("fs/promises"), 1);
|
|
51236
51207
|
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) {
|
|
51208
|
+
var LEGACY_FILES = [
|
|
51209
|
+
"personal-capability.json",
|
|
51210
|
+
"received-capabilities.json",
|
|
51211
|
+
"capabilities.json"
|
|
51212
|
+
];
|
|
51213
|
+
async function cleanupLegacyCapabilityFiles(dataDir, logger) {
|
|
51398
51214
|
const removed = [];
|
|
51399
|
-
for (const
|
|
51400
|
-
|
|
51401
|
-
const dir = factory.vmGuestRoot(g2.resource.id, cap.id);
|
|
51215
|
+
for (const name of LEGACY_FILES) {
|
|
51216
|
+
const p2 = path34.join(dataDir, name);
|
|
51402
51217
|
try {
|
|
51403
|
-
|
|
51404
|
-
removed.push(
|
|
51405
|
-
|
|
51218
|
+
await fs33.unlink(p2);
|
|
51219
|
+
removed.push(name);
|
|
51220
|
+
logger.debug("legacy capability file removed", { file: name });
|
|
51221
|
+
} catch (err) {
|
|
51222
|
+
if (err?.code === "ENOENT") continue;
|
|
51223
|
+
throw err;
|
|
51406
51224
|
}
|
|
51407
51225
|
}
|
|
51408
51226
|
return { removed };
|
|
51409
51227
|
}
|
|
51410
51228
|
|
|
51411
51229
|
// src/inbox/inbox-store.ts
|
|
51412
|
-
var
|
|
51230
|
+
var fs34 = __toESM(require("fs"), 1);
|
|
51413
51231
|
var path35 = __toESM(require("path"), 1);
|
|
51414
51232
|
var INBOX_SUBDIR = "inbox";
|
|
51415
51233
|
var InboxStore = class {
|
|
51416
51234
|
constructor(dataDir) {
|
|
51417
51235
|
this.dataDir = dataDir;
|
|
51418
|
-
|
|
51236
|
+
fs34.mkdirSync(this.dirPath(), { recursive: true });
|
|
51419
51237
|
}
|
|
51420
51238
|
dataDir;
|
|
51421
51239
|
/**
|
|
@@ -51427,7 +51245,7 @@ var InboxStore = class {
|
|
|
51427
51245
|
const file = this.filePath(peerDeviceId);
|
|
51428
51246
|
let raw;
|
|
51429
51247
|
try {
|
|
51430
|
-
raw =
|
|
51248
|
+
raw = fs34.readFileSync(file, "utf8");
|
|
51431
51249
|
} catch (err) {
|
|
51432
51250
|
if (err?.code === "ENOENT") return [];
|
|
51433
51251
|
return [];
|
|
@@ -51443,7 +51261,7 @@ var InboxStore = class {
|
|
|
51443
51261
|
const dir = this.dirPath();
|
|
51444
51262
|
let entries;
|
|
51445
51263
|
try {
|
|
51446
|
-
entries =
|
|
51264
|
+
entries = fs34.readdirSync(dir);
|
|
51447
51265
|
} catch (err) {
|
|
51448
51266
|
if (err?.code === "ENOENT") return [];
|
|
51449
51267
|
return [];
|
|
@@ -51459,9 +51277,9 @@ var InboxStore = class {
|
|
|
51459
51277
|
if (existing.some((m2) => m2.id === message.id)) return;
|
|
51460
51278
|
const file = this.filePath(message.peerDeviceId);
|
|
51461
51279
|
const line = JSON.stringify(message) + "\n";
|
|
51462
|
-
|
|
51280
|
+
fs34.appendFileSync(file, line, { mode: 384 });
|
|
51463
51281
|
try {
|
|
51464
|
-
|
|
51282
|
+
fs34.chmodSync(file, 384);
|
|
51465
51283
|
} catch {
|
|
51466
51284
|
}
|
|
51467
51285
|
}
|
|
@@ -51491,7 +51309,7 @@ var InboxStore = class {
|
|
|
51491
51309
|
removeByPeerDeviceId(peerDeviceId) {
|
|
51492
51310
|
const file = this.filePath(peerDeviceId);
|
|
51493
51311
|
try {
|
|
51494
|
-
|
|
51312
|
+
fs34.unlinkSync(file);
|
|
51495
51313
|
} catch (err) {
|
|
51496
51314
|
if (err?.code === "ENOENT") return;
|
|
51497
51315
|
}
|
|
@@ -51500,10 +51318,10 @@ var InboxStore = class {
|
|
|
51500
51318
|
const file = this.filePath(peerDeviceId);
|
|
51501
51319
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
51502
51320
|
const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
|
|
51503
|
-
|
|
51504
|
-
|
|
51321
|
+
fs34.writeFileSync(tmp, content, { mode: 384 });
|
|
51322
|
+
fs34.renameSync(tmp, file);
|
|
51505
51323
|
try {
|
|
51506
|
-
|
|
51324
|
+
fs34.chmodSync(file, 384);
|
|
51507
51325
|
} catch {
|
|
51508
51326
|
}
|
|
51509
51327
|
}
|
|
@@ -51599,7 +51417,7 @@ var InboxManager = class {
|
|
|
51599
51417
|
};
|
|
51600
51418
|
|
|
51601
51419
|
// src/state/contact-store.ts
|
|
51602
|
-
var
|
|
51420
|
+
var fs35 = __toESM(require("fs"), 1);
|
|
51603
51421
|
var path36 = __toESM(require("path"), 1);
|
|
51604
51422
|
var FILE_NAME = "contacts.json";
|
|
51605
51423
|
var ContactStore = class {
|
|
@@ -51613,7 +51431,7 @@ var ContactStore = class {
|
|
|
51613
51431
|
const file = path36.join(this.dataDir, FILE_NAME);
|
|
51614
51432
|
let raw;
|
|
51615
51433
|
try {
|
|
51616
|
-
raw =
|
|
51434
|
+
raw = fs35.readFileSync(file, "utf8");
|
|
51617
51435
|
} catch (err) {
|
|
51618
51436
|
if (err?.code !== "ENOENT") this.renameBak(file);
|
|
51619
51437
|
return;
|
|
@@ -51689,20 +51507,20 @@ var ContactStore = class {
|
|
|
51689
51507
|
null,
|
|
51690
51508
|
2
|
|
51691
51509
|
);
|
|
51692
|
-
|
|
51693
|
-
|
|
51694
|
-
|
|
51510
|
+
fs35.mkdirSync(this.dataDir, { recursive: true });
|
|
51511
|
+
fs35.writeFileSync(tmp, content, { mode: 384 });
|
|
51512
|
+
fs35.renameSync(tmp, file);
|
|
51695
51513
|
}
|
|
51696
51514
|
renameBak(file) {
|
|
51697
51515
|
try {
|
|
51698
|
-
|
|
51516
|
+
fs35.renameSync(file, `${file}.bak`);
|
|
51699
51517
|
} catch {
|
|
51700
51518
|
}
|
|
51701
51519
|
}
|
|
51702
51520
|
};
|
|
51703
51521
|
|
|
51704
51522
|
// src/contact/connect-remote.ts
|
|
51705
|
-
var
|
|
51523
|
+
var crypto5 = __toESM(require("crypto"), 1);
|
|
51706
51524
|
var HANDSHAKE_TIMEOUT_MS = 5e3;
|
|
51707
51525
|
var RPC_TIMEOUT_MS = 5e3;
|
|
51708
51526
|
async function connectRemote(args) {
|
|
@@ -51766,7 +51584,7 @@ async function connectRemote(args) {
|
|
|
51766
51584
|
ws.once("close", onClose);
|
|
51767
51585
|
});
|
|
51768
51586
|
function call(method, payload) {
|
|
51769
|
-
const requestId =
|
|
51587
|
+
const requestId = crypto5.randomUUID();
|
|
51770
51588
|
return new Promise((resolve6, reject) => {
|
|
51771
51589
|
const onMessage = (raw) => {
|
|
51772
51590
|
let f;
|
|
@@ -51848,7 +51666,7 @@ async function autoReverseContact(args) {
|
|
|
51848
51666
|
}
|
|
51849
51667
|
|
|
51850
51668
|
// src/migrations/2026-05-20-flatten-sessions.ts
|
|
51851
|
-
var
|
|
51669
|
+
var fs36 = __toESM(require("fs"), 1);
|
|
51852
51670
|
var path37 = __toESM(require("path"), 1);
|
|
51853
51671
|
var MIGRATION_FLAG_NAME = ".migration.v1.done";
|
|
51854
51672
|
function migrateFlattenSessions(opts) {
|
|
@@ -51868,7 +51686,7 @@ function migrateFlattenSessions(opts) {
|
|
|
51868
51686
|
if (!entry.endsWith(".json")) continue;
|
|
51869
51687
|
const src = path37.join(defaultDir, entry);
|
|
51870
51688
|
const dst = path37.join(sessionsDir, entry);
|
|
51871
|
-
|
|
51689
|
+
fs36.renameSync(src, dst);
|
|
51872
51690
|
movedBare += 1;
|
|
51873
51691
|
}
|
|
51874
51692
|
rmdirIfEmpty(defaultDir);
|
|
@@ -51880,10 +51698,10 @@ function migrateFlattenSessions(opts) {
|
|
|
51880
51698
|
const ownerSrc = path37.join(personaDir, "owner");
|
|
51881
51699
|
if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
|
|
51882
51700
|
const ownerDst = path37.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
|
|
51883
|
-
|
|
51701
|
+
fs36.mkdirSync(ownerDst, { recursive: true });
|
|
51884
51702
|
for (const file of readdirSafe(ownerSrc)) {
|
|
51885
51703
|
if (!file.endsWith(".json")) continue;
|
|
51886
|
-
|
|
51704
|
+
fs36.renameSync(path37.join(ownerSrc, file), path37.join(ownerDst, file));
|
|
51887
51705
|
movedVmOwner += 1;
|
|
51888
51706
|
}
|
|
51889
51707
|
rmdirIfEmpty(ownerSrc);
|
|
@@ -51891,18 +51709,18 @@ function migrateFlattenSessions(opts) {
|
|
|
51891
51709
|
const listenerSrc = path37.join(personaDir, "listener");
|
|
51892
51710
|
if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
|
|
51893
51711
|
const archiveDst = path37.join(dataDir, ".legacy", `listener-${pid}`);
|
|
51894
|
-
|
|
51712
|
+
fs36.mkdirSync(archiveDst, { recursive: true });
|
|
51895
51713
|
for (const file of readdirSafe(listenerSrc)) {
|
|
51896
51714
|
if (!file.endsWith(".json")) continue;
|
|
51897
|
-
|
|
51715
|
+
fs36.renameSync(path37.join(listenerSrc, file), path37.join(archiveDst, file));
|
|
51898
51716
|
archivedListener += 1;
|
|
51899
51717
|
}
|
|
51900
51718
|
rmdirIfEmpty(listenerSrc);
|
|
51901
51719
|
}
|
|
51902
51720
|
rmdirIfEmpty(personaDir);
|
|
51903
51721
|
}
|
|
51904
|
-
|
|
51905
|
-
|
|
51722
|
+
fs36.mkdirSync(sessionsDir, { recursive: true });
|
|
51723
|
+
fs36.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
|
|
51906
51724
|
return {
|
|
51907
51725
|
skipped: false,
|
|
51908
51726
|
flagWritten: true,
|
|
@@ -51913,7 +51731,7 @@ function migrateFlattenSessions(opts) {
|
|
|
51913
51731
|
}
|
|
51914
51732
|
function existsSync3(p2) {
|
|
51915
51733
|
try {
|
|
51916
|
-
|
|
51734
|
+
fs36.statSync(p2);
|
|
51917
51735
|
return true;
|
|
51918
51736
|
} catch {
|
|
51919
51737
|
return false;
|
|
@@ -51921,21 +51739,21 @@ function existsSync3(p2) {
|
|
|
51921
51739
|
}
|
|
51922
51740
|
function isDir(p2) {
|
|
51923
51741
|
try {
|
|
51924
|
-
return
|
|
51742
|
+
return fs36.statSync(p2).isDirectory();
|
|
51925
51743
|
} catch {
|
|
51926
51744
|
return false;
|
|
51927
51745
|
}
|
|
51928
51746
|
}
|
|
51929
51747
|
function readdirSafe(p2) {
|
|
51930
51748
|
try {
|
|
51931
|
-
return
|
|
51749
|
+
return fs36.readdirSync(p2);
|
|
51932
51750
|
} catch {
|
|
51933
51751
|
return [];
|
|
51934
51752
|
}
|
|
51935
51753
|
}
|
|
51936
51754
|
function rmdirIfEmpty(p2) {
|
|
51937
51755
|
try {
|
|
51938
|
-
|
|
51756
|
+
fs36.rmdirSync(p2);
|
|
51939
51757
|
} catch {
|
|
51940
51758
|
}
|
|
51941
51759
|
}
|
|
@@ -54282,7 +54100,7 @@ async function upsertDeviceBinding(opts) {
|
|
|
54282
54100
|
}
|
|
54283
54101
|
|
|
54284
54102
|
// src/feishu-auth/verify-token.ts
|
|
54285
|
-
var
|
|
54103
|
+
var crypto13 = __toESM(require("crypto"), 1);
|
|
54286
54104
|
var CONNECT_TOKEN_PREFIX = "clawdtk1";
|
|
54287
54105
|
function verifyConnectToken(args) {
|
|
54288
54106
|
const now = args.nowSeconds ?? Math.floor(Date.now() / 1e3);
|
|
@@ -54294,7 +54112,7 @@ function verifyConnectToken(args) {
|
|
|
54294
54112
|
const data = `${prefix}.${payloadB64}`;
|
|
54295
54113
|
let signatureValid = false;
|
|
54296
54114
|
try {
|
|
54297
|
-
signatureValid =
|
|
54115
|
+
signatureValid = crypto13.verify(
|
|
54298
54116
|
null,
|
|
54299
54117
|
Buffer.from(data, "utf8"),
|
|
54300
54118
|
args.publicKeyPem,
|
|
@@ -54331,7 +54149,7 @@ function verifyConnectToken(args) {
|
|
|
54331
54149
|
}
|
|
54332
54150
|
|
|
54333
54151
|
// src/feishu-auth/server-key.ts
|
|
54334
|
-
var
|
|
54152
|
+
var fs50 = __toESM(require("fs"), 1);
|
|
54335
54153
|
var path53 = __toESM(require("path"), 1);
|
|
54336
54154
|
var FILE_NAME2 = "server-signing-key.json";
|
|
54337
54155
|
var ServerKeyStore = class {
|
|
@@ -54345,7 +54163,7 @@ var ServerKeyStore = class {
|
|
|
54345
54163
|
/** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
|
|
54346
54164
|
read() {
|
|
54347
54165
|
try {
|
|
54348
|
-
const raw =
|
|
54166
|
+
const raw = fs50.readFileSync(this.filePath(), "utf8");
|
|
54349
54167
|
const parsed = JSON.parse(raw);
|
|
54350
54168
|
if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
|
|
54351
54169
|
return parsed.publicKeyPem;
|
|
@@ -54360,12 +54178,12 @@ var ServerKeyStore = class {
|
|
|
54360
54178
|
publicKeyPem,
|
|
54361
54179
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
54362
54180
|
};
|
|
54363
|
-
|
|
54364
|
-
|
|
54181
|
+
fs50.mkdirSync(this.dataDir, { recursive: true });
|
|
54182
|
+
fs50.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
|
|
54365
54183
|
}
|
|
54366
54184
|
clear() {
|
|
54367
54185
|
try {
|
|
54368
|
-
|
|
54186
|
+
fs50.unlinkSync(this.filePath());
|
|
54369
54187
|
} catch {
|
|
54370
54188
|
}
|
|
54371
54189
|
}
|
|
@@ -55272,45 +55090,6 @@ function buildCapabilitiesHandlers(deps) {
|
|
|
55272
55090
|
};
|
|
55273
55091
|
}
|
|
55274
55092
|
|
|
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
55093
|
// src/handlers/inbox.ts
|
|
55315
55094
|
init_protocol();
|
|
55316
55095
|
function resolvePeerDeviceId(ctx, argsPeerDeviceId) {
|
|
@@ -58178,9 +57957,6 @@ function buildMethodHandlers(deps) {
|
|
|
58178
57957
|
personaManager: deps.personaManager,
|
|
58179
57958
|
personaRegistry: deps.personaRegistry
|
|
58180
57959
|
}),
|
|
58181
|
-
...buildCapabilityHandlers({
|
|
58182
|
-
manager: deps.capabilityManager
|
|
58183
|
-
}),
|
|
58184
57960
|
...buildInboxHandlers({
|
|
58185
57961
|
manager: deps.inboxManager,
|
|
58186
57962
|
sendDmDeps: {
|
|
@@ -58216,8 +57992,7 @@ function buildMethodHandlers(deps) {
|
|
|
58216
57992
|
ownerPrincipalId: deps.ownerPrincipalId,
|
|
58217
57993
|
ownerId: deps.ownerId,
|
|
58218
57994
|
ownerProvider: deps.ownerProvider,
|
|
58219
|
-
personaStore: deps.personaStore
|
|
58220
|
-
capabilityManager: deps.capabilityManager
|
|
57995
|
+
personaStore: deps.personaStore
|
|
58221
57996
|
}),
|
|
58222
57997
|
...buildFeishuAuthHandlers(deps.feishuAuth),
|
|
58223
57998
|
...buildDeviceHandlers(deps.feishuDevice),
|
|
@@ -58531,12 +58306,6 @@ var METHOD_GRANT_MAP = {
|
|
|
58531
58306
|
// handler 内部按 ctx.principal.kind 决定返回 "合成 owner self capability" 还是
|
|
58532
58307
|
// "查 capabilityManager 拿 caller 的 guest capability"。
|
|
58533
58308
|
"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
58309
|
// ---- inbox: 双投 P2P IM ----
|
|
58541
58310
|
// 三条都 'public' — 能连上 = 已通过本地验票. handler 内额外校验 guest 的
|
|
58542
58311
|
// args.peerDeviceId 与 ctx.principal.id(=deviceId) 一致, 防 guest 越权操作别的 DM 桶.
|
|
@@ -59196,32 +58965,13 @@ async function startDaemon(config) {
|
|
|
59196
58965
|
}
|
|
59197
58966
|
} : {}
|
|
59198
58967
|
});
|
|
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
|
-
});
|
|
58968
|
+
await cleanupLegacyCapabilityFiles(config.dataDir, logger);
|
|
59219
58969
|
migrateDropPersonsDir(config.dataDir);
|
|
59220
58970
|
let wsServer = null;
|
|
59221
58971
|
const authGate = authMode === "first-message" ? new AuthGate({
|
|
59222
58972
|
shouldEnforce: buildShouldEnforce({ tunnel: config.tunnel }),
|
|
59223
58973
|
// Task 1.7:authenticate 注入路径替代 expectedToken 单 token 比对。
|
|
59224
|
-
// owner 路径 constantTimeEqual 防侧信道;guest 路径走
|
|
58974
|
+
// owner 路径 constantTimeEqual 防侧信道;guest 路径走 verifyConnectToken (Ed25519).
|
|
59225
58975
|
expectedToken: resolvedAuthToken,
|
|
59226
58976
|
authenticate: async (t, selfUrl) => {
|
|
59227
58977
|
const result = await authenticate(t, buildConnectAuthDeps());
|
|
@@ -59745,8 +59495,6 @@ async function startDaemon(config) {
|
|
|
59745
59495
|
personaRoot: import_node_path61.default.join(config.dataDir, "personas"),
|
|
59746
59496
|
// v2 多人 persona 隔离:handler 派生 guest user-dir 放行
|
|
59747
59497
|
usersRoot,
|
|
59748
|
-
// capability:list / delete handler 依赖
|
|
59749
|
-
capabilityManager,
|
|
59750
59498
|
// v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
|
|
59751
59499
|
ownerDisplayName,
|
|
59752
59500
|
// 决策 #16: whoami owner.id = 本机 deviceId(路由主键)+ ownerId/provider 归属人
|
|
@@ -59756,8 +59504,6 @@ async function startDaemon(config) {
|
|
|
59756
59504
|
ownerId,
|
|
59757
59505
|
ownerProvider,
|
|
59758
59506
|
personaStore,
|
|
59759
|
-
// capability handler 也用 (capability:issue 走 registry / capability:list)
|
|
59760
|
-
capabilityRegistry,
|
|
59761
59507
|
// capability platform v3 inbox: handler 接 manager (post/list/markRead),
|
|
59762
59508
|
// cascade 接 store (list 统计 deletedInboxEvents).
|
|
59763
59509
|
inboxManager,
|
|
@@ -60092,18 +59838,11 @@ async function startDaemon(config) {
|
|
|
60092
59838
|
),
|
|
60093
59839
|
protocolVersion: PROTOCOL_VERSION,
|
|
60094
59840
|
authGate: authGate ?? void 0,
|
|
60095
|
-
//
|
|
60096
|
-
//
|
|
60097
|
-
//
|
|
60098
|
-
//
|
|
59841
|
+
// T-35: capability path retired;本 hook 只剩 persona web 分享 visitor token 分支:
|
|
59842
|
+
// noAuth localhost daemon 上访客 token 若命中 → attach guest ctx,否则 fallback owner
|
|
59843
|
+
// (非 persona-web 场景静默 return null;tunnel 模式走 AuthGate 不经此路)。参数名
|
|
59844
|
+
// tryVerifyCapabilityToken 不再准确,跨 local-ws-server 改名属 followup 清理。
|
|
60099
59845
|
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
59846
|
const vis = verifyVisitorToken(authFile.visitorTokenSecret, token, Math.floor(Date.now() / 1e3));
|
|
60108
59847
|
if (vis.ok) return visitorGuestContext(vis.payload);
|
|
60109
59848
|
return null;
|