@clawos-dev/clawd 0.2.239 → 0.2.240
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 +889 -639
- package/dist/dispatch/mcp-server.cjs +35 -15
- package/dist/peer-ops/mcp-server.cjs +39608 -0
- package/dist/share-ui/assets/{guest-x12quoXh.js → guest-DIEZ-f-B.js} +18 -18
- package/dist/share-ui/guest.html +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -115,6 +115,11 @@ var init_methods = __esm({
|
|
|
115
115
|
// METHOD_GRANT_MAP capability-scoped(handler 自检 persona.public + assertGrant(send))。
|
|
116
116
|
// personaDispatch:complete 仅 B 本地 cc 调(/api/rpc),不跨设备,故不列入 WS method。
|
|
117
117
|
"personaDispatch:run",
|
|
118
|
+
// ---- peerExec:run (跨设备 exec,spec 2026-07-16-cross-device-exec-mcp) ----
|
|
119
|
+
// A 侧本地 cc 经 /api/rpc(EXPOSED_RPC_METHODS)带 peerDeviceId → daemon forward;
|
|
120
|
+
// B 侧对端经 /rpc(Bearer connect token)无 peerDeviceId → 本地 spawn。METHOD_GRANT_MAP
|
|
121
|
+
// 设 public,handler 内判 remoteAccessAllowed(跟 inbox:postMessage 同款)。
|
|
122
|
+
"peerExec:run",
|
|
118
123
|
// ---- session:pty 双向透传(仅 CLAWD_CC_MODE=tui 才生效,对应 session:pty push 帧的上行) ----
|
|
119
124
|
// session:pty:input — UI 把用户键盘字节(base64 UTF-8)发给 daemon,daemon 直写 pty stdin。
|
|
120
125
|
// 高频帧(每次按键 1 帧),不入 reducer / 不广播。response 是 fire-and-forget 风格 ack。
|
|
@@ -161,10 +166,10 @@ var init_methods = __esm({
|
|
|
161
166
|
"contact:list",
|
|
162
167
|
"contact:pin",
|
|
163
168
|
"contact:remove",
|
|
164
|
-
// ---- contact:
|
|
165
|
-
// owner UI 授权 contact
|
|
166
|
-
//
|
|
167
|
-
"contact:
|
|
169
|
+
// ---- contact:setRemoteAccess (远程访问授权,spec 2026-07-16) ----
|
|
170
|
+
// owner UI 授权 contact 反向操作我的设备(exec + SSH 共用开关 remoteAccessAllowed)。
|
|
171
|
+
// 详见 protocol/src/contact-ssh.ts.
|
|
172
|
+
"contact:setRemoteAccess",
|
|
168
173
|
// ---- visitor:* (web 访客 · persona web 分享,spec 2026-06-24-persona-web-share-design) ----
|
|
169
174
|
// owner-only:列出登录访问过本机 public persona 的 web 访客(零安装、飞书登录、daemon 自签
|
|
170
175
|
// visitor token)。存储 ~/.clawd/visitors.json(VisitorStore),登录(exchange)即 upsert;
|
|
@@ -744,8 +749,8 @@ var init_parseUtil = __esm({
|
|
|
744
749
|
init_errors2();
|
|
745
750
|
init_en();
|
|
746
751
|
makeIssue = (params) => {
|
|
747
|
-
const { data, path:
|
|
748
|
-
const fullPath = [...
|
|
752
|
+
const { data, path: path77, errorMaps, issueData } = params;
|
|
753
|
+
const fullPath = [...path77, ...issueData.path || []];
|
|
749
754
|
const fullIssue = {
|
|
750
755
|
...issueData,
|
|
751
756
|
path: fullPath
|
|
@@ -1056,11 +1061,11 @@ var init_types = __esm({
|
|
|
1056
1061
|
init_parseUtil();
|
|
1057
1062
|
init_util();
|
|
1058
1063
|
ParseInputLazyPath = class {
|
|
1059
|
-
constructor(parent, value,
|
|
1064
|
+
constructor(parent, value, path77, key) {
|
|
1060
1065
|
this._cachedPath = [];
|
|
1061
1066
|
this.parent = parent;
|
|
1062
1067
|
this.data = value;
|
|
1063
|
-
this._path =
|
|
1068
|
+
this._path = path77;
|
|
1064
1069
|
this._key = key;
|
|
1065
1070
|
}
|
|
1066
1071
|
get path() {
|
|
@@ -5745,21 +5750,21 @@ var init_contact = __esm({
|
|
|
5745
5750
|
*/
|
|
5746
5751
|
pinnedAt: external_exports.number().int().nullable().default(null),
|
|
5747
5752
|
/**
|
|
5748
|
-
*
|
|
5749
|
-
*
|
|
5750
|
-
*
|
|
5751
|
-
*
|
|
5753
|
+
* 远程访问授权(spec 2026-07-16)。owner 视角:允许该 contact 反向操作我的设备——
|
|
5754
|
+
* 一个开关两种实现:跨设备 exec(peerExec:run handler 判它)+ SSH 反向访问
|
|
5755
|
+
* (rebuildAuthorizedKeys 读它)。default=false,owner 显式开。
|
|
5756
|
+
* 授权粒度 per-people (per-contact):借用别人 persona 本来就带信任对方本人的前提。
|
|
5752
5757
|
*/
|
|
5753
|
-
|
|
5758
|
+
remoteAccessAllowed: external_exports.boolean().default(false),
|
|
5754
5759
|
/** 对端设备 SSH identity pubkey(一行 `ssh-ed25519 AAAA... comment`);见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5755
5760
|
sshPublicKey: external_exports.string().optional(),
|
|
5756
5761
|
/** 对端 OS 登录账号名,SSH 拨号登录名(`<osUser>@127.0.0.1`);见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5757
5762
|
osUser: external_exports.string().optional(),
|
|
5758
5763
|
/**
|
|
5759
5764
|
* @deprecated T-29 去沙箱后废弃。原为授权访问的目录白名单(clawd-ssh-jail 生成 sbpl/bwrap
|
|
5760
|
-
* policy 用);去掉 OS
|
|
5765
|
+
* policy 用);去掉 OS 沙箱后不再有目录级限制,远程访问授权即 on/off(见 remoteAccessAllowed)。
|
|
5761
5766
|
* 字段仅为兼容老 contacts.json 保留(default [] 保证 strict parse 不炸),不再有代码读写它,
|
|
5762
|
-
* 也不再进
|
|
5767
|
+
* 也不再进 setRemoteAccess RPC / ContactSettingsDrawer / jail。勿新增消费方。
|
|
5763
5768
|
*/
|
|
5764
5769
|
exposedDirs: external_exports.array(external_exports.string()).default([])
|
|
5765
5770
|
}).strict();
|
|
@@ -5801,24 +5806,51 @@ var init_contact = __esm({
|
|
|
5801
5806
|
});
|
|
5802
5807
|
|
|
5803
5808
|
// ../protocol/src/contact-ssh.ts
|
|
5804
|
-
var
|
|
5809
|
+
var ContactSetRemoteAccessArgsSchema, ContactSetRemoteAccessOkSchema, ContactRemoteAccessUpdatedFrameSchema;
|
|
5805
5810
|
var init_contact_ssh = __esm({
|
|
5806
5811
|
"../protocol/src/contact-ssh.ts"() {
|
|
5807
5812
|
"use strict";
|
|
5808
5813
|
init_zod();
|
|
5809
|
-
|
|
5814
|
+
ContactSetRemoteAccessArgsSchema = external_exports.object({
|
|
5810
5815
|
deviceId: external_exports.string().min(1),
|
|
5811
|
-
|
|
5816
|
+
remoteAccessAllowed: external_exports.boolean()
|
|
5812
5817
|
}).strict();
|
|
5813
|
-
|
|
5814
|
-
type: external_exports.literal("contact:
|
|
5818
|
+
ContactSetRemoteAccessOkSchema = external_exports.object({
|
|
5819
|
+
type: external_exports.literal("contact:setRemoteAccess:ok"),
|
|
5815
5820
|
deviceId: external_exports.string().min(1),
|
|
5816
|
-
|
|
5821
|
+
remoteAccessAllowed: external_exports.boolean()
|
|
5817
5822
|
}).strict();
|
|
5818
|
-
|
|
5819
|
-
type: external_exports.literal("contact:
|
|
5823
|
+
ContactRemoteAccessUpdatedFrameSchema = external_exports.object({
|
|
5824
|
+
type: external_exports.literal("contact:remote-access-updated"),
|
|
5820
5825
|
deviceId: external_exports.string().min(1),
|
|
5821
|
-
|
|
5826
|
+
remoteAccessAllowed: external_exports.boolean()
|
|
5827
|
+
}).strict();
|
|
5828
|
+
}
|
|
5829
|
+
});
|
|
5830
|
+
|
|
5831
|
+
// ../protocol/src/peer-exec.ts
|
|
5832
|
+
var PeerExecRunArgsSchema, PeerExecRunOkSchema;
|
|
5833
|
+
var init_peer_exec = __esm({
|
|
5834
|
+
"../protocol/src/peer-exec.ts"() {
|
|
5835
|
+
"use strict";
|
|
5836
|
+
init_zod();
|
|
5837
|
+
PeerExecRunArgsSchema = external_exports.object({
|
|
5838
|
+
/** A 侧发起时带:目标设备 deviceId。peer-forward 转发前 strip 掉,B 侧本地执行时无此字段。 */
|
|
5839
|
+
peerDeviceId: external_exports.string().min(1).optional(),
|
|
5840
|
+
/** 交给对端 `bash -c` 的命令;cc 自拼完整路径(一发一收,无跨命令会话状态)。 */
|
|
5841
|
+
command: external_exports.string().min(1),
|
|
5842
|
+
/** 超时上限(ms);缺省用 handler 默认 30_000,clamp 300_000。 */
|
|
5843
|
+
timeoutMs: external_exports.number().int().positive().optional()
|
|
5844
|
+
}).strict();
|
|
5845
|
+
PeerExecRunOkSchema = external_exports.object({
|
|
5846
|
+
type: external_exports.literal("peerExec:run:ok"),
|
|
5847
|
+
stdout: external_exports.string(),
|
|
5848
|
+
stderr: external_exports.string(),
|
|
5849
|
+
/** 进程退出码;被超时 kill 时 null。 */
|
|
5850
|
+
exitCode: external_exports.number().int().nullable(),
|
|
5851
|
+
timedOut: external_exports.boolean(),
|
|
5852
|
+
stdoutTruncated: external_exports.boolean(),
|
|
5853
|
+
stderrTruncated: external_exports.boolean()
|
|
5822
5854
|
}).strict();
|
|
5823
5855
|
}
|
|
5824
5856
|
});
|
|
@@ -6230,6 +6262,7 @@ var init_runtime = __esm({
|
|
|
6230
6262
|
init_inbox();
|
|
6231
6263
|
init_contact();
|
|
6232
6264
|
init_contact_ssh();
|
|
6265
|
+
init_peer_exec();
|
|
6233
6266
|
init_extension();
|
|
6234
6267
|
init_feishu_auth();
|
|
6235
6268
|
init_dispatch();
|
|
@@ -6509,8 +6542,8 @@ var require_req = __commonJS({
|
|
|
6509
6542
|
if (req.originalUrl) {
|
|
6510
6543
|
_req.url = req.originalUrl;
|
|
6511
6544
|
} else {
|
|
6512
|
-
const
|
|
6513
|
-
_req.url = typeof
|
|
6545
|
+
const path77 = req.path;
|
|
6546
|
+
_req.url = typeof path77 === "string" ? path77 : req.url ? req.url.path || req.url : void 0;
|
|
6514
6547
|
}
|
|
6515
6548
|
if (req.query) {
|
|
6516
6549
|
_req.query = req.query;
|
|
@@ -6675,14 +6708,14 @@ var require_redact = __commonJS({
|
|
|
6675
6708
|
}
|
|
6676
6709
|
return obj;
|
|
6677
6710
|
}
|
|
6678
|
-
function parsePath(
|
|
6711
|
+
function parsePath(path77) {
|
|
6679
6712
|
const parts = [];
|
|
6680
6713
|
let current = "";
|
|
6681
6714
|
let inBrackets = false;
|
|
6682
6715
|
let inQuotes = false;
|
|
6683
6716
|
let quoteChar = "";
|
|
6684
|
-
for (let i = 0; i <
|
|
6685
|
-
const char =
|
|
6717
|
+
for (let i = 0; i < path77.length; i++) {
|
|
6718
|
+
const char = path77[i];
|
|
6686
6719
|
if (!inBrackets && char === ".") {
|
|
6687
6720
|
if (current) {
|
|
6688
6721
|
parts.push(current);
|
|
@@ -6813,10 +6846,10 @@ var require_redact = __commonJS({
|
|
|
6813
6846
|
return current;
|
|
6814
6847
|
}
|
|
6815
6848
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
6816
|
-
for (const
|
|
6817
|
-
const parts = parsePath(
|
|
6849
|
+
for (const path77 of paths) {
|
|
6850
|
+
const parts = parsePath(path77);
|
|
6818
6851
|
if (parts.includes("*")) {
|
|
6819
|
-
redactWildcardPath(obj, parts, censor,
|
|
6852
|
+
redactWildcardPath(obj, parts, censor, path77, remove);
|
|
6820
6853
|
} else {
|
|
6821
6854
|
if (remove) {
|
|
6822
6855
|
removeKey(obj, parts);
|
|
@@ -6901,8 +6934,8 @@ var require_redact = __commonJS({
|
|
|
6901
6934
|
}
|
|
6902
6935
|
} else {
|
|
6903
6936
|
if (afterWildcard.includes("*")) {
|
|
6904
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6905
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
6937
|
+
const wrappedCensor = typeof censor === "function" ? (value, path77) => {
|
|
6938
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path77];
|
|
6906
6939
|
return censor(value, fullPath);
|
|
6907
6940
|
} : censor;
|
|
6908
6941
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -6937,8 +6970,8 @@ var require_redact = __commonJS({
|
|
|
6937
6970
|
return null;
|
|
6938
6971
|
}
|
|
6939
6972
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
6940
|
-
for (const
|
|
6941
|
-
const parts = parsePath(
|
|
6973
|
+
for (const path77 of pathsToClone) {
|
|
6974
|
+
const parts = parsePath(path77);
|
|
6942
6975
|
let current = pathStructure;
|
|
6943
6976
|
for (let i = 0; i < parts.length; i++) {
|
|
6944
6977
|
const part = parts[i];
|
|
@@ -6990,24 +7023,24 @@ var require_redact = __commonJS({
|
|
|
6990
7023
|
}
|
|
6991
7024
|
return cloneSelectively(obj, pathStructure);
|
|
6992
7025
|
}
|
|
6993
|
-
function validatePath(
|
|
6994
|
-
if (typeof
|
|
7026
|
+
function validatePath(path77) {
|
|
7027
|
+
if (typeof path77 !== "string") {
|
|
6995
7028
|
throw new Error("Paths must be (non-empty) strings");
|
|
6996
7029
|
}
|
|
6997
|
-
if (
|
|
7030
|
+
if (path77 === "") {
|
|
6998
7031
|
throw new Error("Invalid redaction path ()");
|
|
6999
7032
|
}
|
|
7000
|
-
if (
|
|
7001
|
-
throw new Error(`Invalid redaction path (${
|
|
7033
|
+
if (path77.includes("..")) {
|
|
7034
|
+
throw new Error(`Invalid redaction path (${path77})`);
|
|
7002
7035
|
}
|
|
7003
|
-
if (
|
|
7004
|
-
throw new Error(`Invalid redaction path (${
|
|
7036
|
+
if (path77.includes(",")) {
|
|
7037
|
+
throw new Error(`Invalid redaction path (${path77})`);
|
|
7005
7038
|
}
|
|
7006
7039
|
let bracketCount = 0;
|
|
7007
7040
|
let inQuotes = false;
|
|
7008
7041
|
let quoteChar = "";
|
|
7009
|
-
for (let i = 0; i <
|
|
7010
|
-
const char =
|
|
7042
|
+
for (let i = 0; i < path77.length; i++) {
|
|
7043
|
+
const char = path77[i];
|
|
7011
7044
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
7012
7045
|
if (!inQuotes) {
|
|
7013
7046
|
inQuotes = true;
|
|
@@ -7021,20 +7054,20 @@ var require_redact = __commonJS({
|
|
|
7021
7054
|
} else if (char === "]" && !inQuotes) {
|
|
7022
7055
|
bracketCount--;
|
|
7023
7056
|
if (bracketCount < 0) {
|
|
7024
|
-
throw new Error(`Invalid redaction path (${
|
|
7057
|
+
throw new Error(`Invalid redaction path (${path77})`);
|
|
7025
7058
|
}
|
|
7026
7059
|
}
|
|
7027
7060
|
}
|
|
7028
7061
|
if (bracketCount !== 0) {
|
|
7029
|
-
throw new Error(`Invalid redaction path (${
|
|
7062
|
+
throw new Error(`Invalid redaction path (${path77})`);
|
|
7030
7063
|
}
|
|
7031
7064
|
}
|
|
7032
7065
|
function validatePaths(paths) {
|
|
7033
7066
|
if (!Array.isArray(paths)) {
|
|
7034
7067
|
throw new TypeError("paths must be an array");
|
|
7035
7068
|
}
|
|
7036
|
-
for (const
|
|
7037
|
-
validatePath(
|
|
7069
|
+
for (const path77 of paths) {
|
|
7070
|
+
validatePath(path77);
|
|
7038
7071
|
}
|
|
7039
7072
|
}
|
|
7040
7073
|
function slowRedact(options = {}) {
|
|
@@ -7202,8 +7235,8 @@ var require_redaction = __commonJS({
|
|
|
7202
7235
|
if (shape[k2] === null) {
|
|
7203
7236
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
7204
7237
|
} else {
|
|
7205
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
7206
|
-
return censor(value, [k2, ...
|
|
7238
|
+
const wrappedCensor = typeof censor === "function" ? (value, path77) => {
|
|
7239
|
+
return censor(value, [k2, ...path77]);
|
|
7207
7240
|
} : censor;
|
|
7208
7241
|
o[k2] = Redact({
|
|
7209
7242
|
paths: shape[k2],
|
|
@@ -7421,10 +7454,10 @@ var require_atomic_sleep = __commonJS({
|
|
|
7421
7454
|
var require_sonic_boom = __commonJS({
|
|
7422
7455
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
7423
7456
|
"use strict";
|
|
7424
|
-
var
|
|
7457
|
+
var fs71 = require("fs");
|
|
7425
7458
|
var EventEmitter3 = require("events");
|
|
7426
7459
|
var inherits = require("util").inherits;
|
|
7427
|
-
var
|
|
7460
|
+
var path77 = require("path");
|
|
7428
7461
|
var sleep2 = require_atomic_sleep();
|
|
7429
7462
|
var assert = require("assert");
|
|
7430
7463
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -7478,20 +7511,20 @@ var require_sonic_boom = __commonJS({
|
|
|
7478
7511
|
const mode = sonic.mode;
|
|
7479
7512
|
if (sonic.sync) {
|
|
7480
7513
|
try {
|
|
7481
|
-
if (sonic.mkdir)
|
|
7482
|
-
const fd =
|
|
7514
|
+
if (sonic.mkdir) fs71.mkdirSync(path77.dirname(file), { recursive: true });
|
|
7515
|
+
const fd = fs71.openSync(file, flags, mode);
|
|
7483
7516
|
fileOpened(null, fd);
|
|
7484
7517
|
} catch (err) {
|
|
7485
7518
|
fileOpened(err);
|
|
7486
7519
|
throw err;
|
|
7487
7520
|
}
|
|
7488
7521
|
} else if (sonic.mkdir) {
|
|
7489
|
-
|
|
7522
|
+
fs71.mkdir(path77.dirname(file), { recursive: true }, (err) => {
|
|
7490
7523
|
if (err) return fileOpened(err);
|
|
7491
|
-
|
|
7524
|
+
fs71.open(file, flags, mode, fileOpened);
|
|
7492
7525
|
});
|
|
7493
7526
|
} else {
|
|
7494
|
-
|
|
7527
|
+
fs71.open(file, flags, mode, fileOpened);
|
|
7495
7528
|
}
|
|
7496
7529
|
}
|
|
7497
7530
|
function SonicBoom(opts) {
|
|
@@ -7532,8 +7565,8 @@ var require_sonic_boom = __commonJS({
|
|
|
7532
7565
|
this.flush = flushBuffer;
|
|
7533
7566
|
this.flushSync = flushBufferSync;
|
|
7534
7567
|
this._actualWrite = actualWriteBuffer;
|
|
7535
|
-
fsWriteSync = () =>
|
|
7536
|
-
fsWrite = () =>
|
|
7568
|
+
fsWriteSync = () => fs71.writeSync(this.fd, this._writingBuf);
|
|
7569
|
+
fsWrite = () => fs71.write(this.fd, this._writingBuf, this.release);
|
|
7537
7570
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
7538
7571
|
this._writingBuf = "";
|
|
7539
7572
|
this.write = write;
|
|
@@ -7542,15 +7575,15 @@ var require_sonic_boom = __commonJS({
|
|
|
7542
7575
|
this._actualWrite = actualWrite;
|
|
7543
7576
|
fsWriteSync = () => {
|
|
7544
7577
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7545
|
-
return
|
|
7578
|
+
return fs71.writeSync(this.fd, this._writingBuf);
|
|
7546
7579
|
}
|
|
7547
|
-
return
|
|
7580
|
+
return fs71.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7548
7581
|
};
|
|
7549
7582
|
fsWrite = () => {
|
|
7550
7583
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7551
|
-
return
|
|
7584
|
+
return fs71.write(this.fd, this._writingBuf, this.release);
|
|
7552
7585
|
}
|
|
7553
|
-
return
|
|
7586
|
+
return fs71.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
7554
7587
|
};
|
|
7555
7588
|
} else {
|
|
7556
7589
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -7607,7 +7640,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7607
7640
|
}
|
|
7608
7641
|
}
|
|
7609
7642
|
if (this._fsync) {
|
|
7610
|
-
|
|
7643
|
+
fs71.fsyncSync(this.fd);
|
|
7611
7644
|
}
|
|
7612
7645
|
const len = this._len;
|
|
7613
7646
|
if (this._reopening) {
|
|
@@ -7721,7 +7754,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7721
7754
|
const onDrain = () => {
|
|
7722
7755
|
if (!this._fsync) {
|
|
7723
7756
|
try {
|
|
7724
|
-
|
|
7757
|
+
fs71.fsync(this.fd, (err) => {
|
|
7725
7758
|
this._flushPending = false;
|
|
7726
7759
|
cb(err);
|
|
7727
7760
|
});
|
|
@@ -7823,7 +7856,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7823
7856
|
const fd = this.fd;
|
|
7824
7857
|
this.once("ready", () => {
|
|
7825
7858
|
if (fd !== this.fd) {
|
|
7826
|
-
|
|
7859
|
+
fs71.close(fd, (err) => {
|
|
7827
7860
|
if (err) {
|
|
7828
7861
|
return this.emit("error", err);
|
|
7829
7862
|
}
|
|
@@ -7872,7 +7905,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7872
7905
|
buf = this._bufs[0];
|
|
7873
7906
|
}
|
|
7874
7907
|
try {
|
|
7875
|
-
const n = Buffer.isBuffer(buf) ?
|
|
7908
|
+
const n = Buffer.isBuffer(buf) ? fs71.writeSync(this.fd, buf) : fs71.writeSync(this.fd, buf, "utf8");
|
|
7876
7909
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
7877
7910
|
buf = releasedBufObj.writingBuf;
|
|
7878
7911
|
this._len = releasedBufObj.len;
|
|
@@ -7888,7 +7921,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7888
7921
|
}
|
|
7889
7922
|
}
|
|
7890
7923
|
try {
|
|
7891
|
-
|
|
7924
|
+
fs71.fsyncSync(this.fd);
|
|
7892
7925
|
} catch {
|
|
7893
7926
|
}
|
|
7894
7927
|
}
|
|
@@ -7909,7 +7942,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7909
7942
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
7910
7943
|
}
|
|
7911
7944
|
try {
|
|
7912
|
-
const n =
|
|
7945
|
+
const n = fs71.writeSync(this.fd, buf);
|
|
7913
7946
|
buf = buf.subarray(n);
|
|
7914
7947
|
this._len = Math.max(this._len - n, 0);
|
|
7915
7948
|
if (buf.length <= 0) {
|
|
@@ -7937,13 +7970,13 @@ var require_sonic_boom = __commonJS({
|
|
|
7937
7970
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
7938
7971
|
if (this.sync) {
|
|
7939
7972
|
try {
|
|
7940
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
7973
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs71.writeSync(this.fd, this._writingBuf) : fs71.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7941
7974
|
release(null, written);
|
|
7942
7975
|
} catch (err) {
|
|
7943
7976
|
release(err);
|
|
7944
7977
|
}
|
|
7945
7978
|
} else {
|
|
7946
|
-
|
|
7979
|
+
fs71.write(this.fd, this._writingBuf, release);
|
|
7947
7980
|
}
|
|
7948
7981
|
}
|
|
7949
7982
|
function actualWriteBuffer() {
|
|
@@ -7952,7 +7985,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7952
7985
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
7953
7986
|
if (this.sync) {
|
|
7954
7987
|
try {
|
|
7955
|
-
const written =
|
|
7988
|
+
const written = fs71.writeSync(this.fd, this._writingBuf);
|
|
7956
7989
|
release(null, written);
|
|
7957
7990
|
} catch (err) {
|
|
7958
7991
|
release(err);
|
|
@@ -7961,7 +7994,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7961
7994
|
if (kCopyBuffer) {
|
|
7962
7995
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
7963
7996
|
}
|
|
7964
|
-
|
|
7997
|
+
fs71.write(this.fd, this._writingBuf, release);
|
|
7965
7998
|
}
|
|
7966
7999
|
}
|
|
7967
8000
|
function actualClose(sonic) {
|
|
@@ -7977,12 +8010,12 @@ var require_sonic_boom = __commonJS({
|
|
|
7977
8010
|
sonic._lens = [];
|
|
7978
8011
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
7979
8012
|
try {
|
|
7980
|
-
|
|
8013
|
+
fs71.fsync(sonic.fd, closeWrapped);
|
|
7981
8014
|
} catch {
|
|
7982
8015
|
}
|
|
7983
8016
|
function closeWrapped() {
|
|
7984
8017
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
7985
|
-
|
|
8018
|
+
fs71.close(sonic.fd, done);
|
|
7986
8019
|
} else {
|
|
7987
8020
|
done();
|
|
7988
8021
|
}
|
|
@@ -10346,7 +10379,7 @@ var require_multistream = __commonJS({
|
|
|
10346
10379
|
var require_pino = __commonJS({
|
|
10347
10380
|
"../node_modules/.pnpm/pino@9.14.0/node_modules/pino/pino.js"(exports2, module2) {
|
|
10348
10381
|
"use strict";
|
|
10349
|
-
var
|
|
10382
|
+
var os24 = require("os");
|
|
10350
10383
|
var stdSerializers = require_pino_std_serializers();
|
|
10351
10384
|
var caller = require_caller();
|
|
10352
10385
|
var redaction = require_redaction();
|
|
@@ -10393,7 +10426,7 @@ var require_pino = __commonJS({
|
|
|
10393
10426
|
} = symbols;
|
|
10394
10427
|
var { epochTime, nullTime } = time;
|
|
10395
10428
|
var { pid } = process;
|
|
10396
|
-
var hostname =
|
|
10429
|
+
var hostname = os24.hostname();
|
|
10397
10430
|
var defaultErrorSerializer = stdSerializers.err;
|
|
10398
10431
|
var defaultOptions = {
|
|
10399
10432
|
level: "info",
|
|
@@ -11117,11 +11150,11 @@ var init_lib = __esm({
|
|
|
11117
11150
|
}
|
|
11118
11151
|
}
|
|
11119
11152
|
},
|
|
11120
|
-
addToPath: function addToPath(
|
|
11121
|
-
var last =
|
|
11153
|
+
addToPath: function addToPath(path77, added, removed, oldPosInc, options) {
|
|
11154
|
+
var last = path77.lastComponent;
|
|
11122
11155
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
11123
11156
|
return {
|
|
11124
|
-
oldPos:
|
|
11157
|
+
oldPos: path77.oldPos + oldPosInc,
|
|
11125
11158
|
lastComponent: {
|
|
11126
11159
|
count: last.count + 1,
|
|
11127
11160
|
added,
|
|
@@ -11131,7 +11164,7 @@ var init_lib = __esm({
|
|
|
11131
11164
|
};
|
|
11132
11165
|
} else {
|
|
11133
11166
|
return {
|
|
11134
|
-
oldPos:
|
|
11167
|
+
oldPos: path77.oldPos + oldPosInc,
|
|
11135
11168
|
lastComponent: {
|
|
11136
11169
|
count: 1,
|
|
11137
11170
|
added,
|
|
@@ -11636,10 +11669,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
11636
11669
|
const memories = raw.map((m2) => {
|
|
11637
11670
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11638
11671
|
const rec3 = m2;
|
|
11639
|
-
const
|
|
11672
|
+
const path77 = typeof rec3.path === "string" ? rec3.path : null;
|
|
11640
11673
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
11641
|
-
if (!
|
|
11642
|
-
const entry = { path:
|
|
11674
|
+
if (!path77 || content == null) return null;
|
|
11675
|
+
const entry = { path: path77, content };
|
|
11643
11676
|
if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
|
|
11644
11677
|
return entry;
|
|
11645
11678
|
}).filter((m2) => m2 !== null);
|
|
@@ -12442,10 +12475,10 @@ function parseAttachment(obj) {
|
|
|
12442
12475
|
const memories = raw.map((m2) => {
|
|
12443
12476
|
if (!m2 || typeof m2 !== "object") return null;
|
|
12444
12477
|
const rec3 = m2;
|
|
12445
|
-
const
|
|
12478
|
+
const path77 = typeof rec3.path === "string" ? rec3.path : null;
|
|
12446
12479
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
12447
|
-
if (!
|
|
12448
|
-
const out = { path:
|
|
12480
|
+
if (!path77 || content == null) return null;
|
|
12481
|
+
const out = { path: path77, content };
|
|
12449
12482
|
if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
|
|
12450
12483
|
return out;
|
|
12451
12484
|
}).filter((m2) => m2 !== null);
|
|
@@ -33431,8 +33464,8 @@ var require_utils = __commonJS({
|
|
|
33431
33464
|
var result = transform[inputType][outputType](input);
|
|
33432
33465
|
return result;
|
|
33433
33466
|
};
|
|
33434
|
-
exports2.resolve = function(
|
|
33435
|
-
var parts =
|
|
33467
|
+
exports2.resolve = function(path77) {
|
|
33468
|
+
var parts = path77.split("/");
|
|
33436
33469
|
var result = [];
|
|
33437
33470
|
for (var index = 0; index < parts.length; index++) {
|
|
33438
33471
|
var part = parts[index];
|
|
@@ -39285,18 +39318,18 @@ var require_object = __commonJS({
|
|
|
39285
39318
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
39286
39319
|
this.files[name] = object;
|
|
39287
39320
|
};
|
|
39288
|
-
var parentFolder = function(
|
|
39289
|
-
if (
|
|
39290
|
-
|
|
39321
|
+
var parentFolder = function(path77) {
|
|
39322
|
+
if (path77.slice(-1) === "/") {
|
|
39323
|
+
path77 = path77.substring(0, path77.length - 1);
|
|
39291
39324
|
}
|
|
39292
|
-
var lastSlash =
|
|
39293
|
-
return lastSlash > 0 ?
|
|
39325
|
+
var lastSlash = path77.lastIndexOf("/");
|
|
39326
|
+
return lastSlash > 0 ? path77.substring(0, lastSlash) : "";
|
|
39294
39327
|
};
|
|
39295
|
-
var forceTrailingSlash = function(
|
|
39296
|
-
if (
|
|
39297
|
-
|
|
39328
|
+
var forceTrailingSlash = function(path77) {
|
|
39329
|
+
if (path77.slice(-1) !== "/") {
|
|
39330
|
+
path77 += "/";
|
|
39298
39331
|
}
|
|
39299
|
-
return
|
|
39332
|
+
return path77;
|
|
39300
39333
|
};
|
|
39301
39334
|
var folderAdd = function(name, createFolders) {
|
|
39302
39335
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -40298,7 +40331,7 @@ var require_lib3 = __commonJS({
|
|
|
40298
40331
|
// src/run-case/recorder.ts
|
|
40299
40332
|
function startRunCaseRecorder(opts) {
|
|
40300
40333
|
const now = opts.now ?? Date.now;
|
|
40301
|
-
const dir =
|
|
40334
|
+
const dir = import_node_path65.default.dirname(opts.recordPath);
|
|
40302
40335
|
let stream = null;
|
|
40303
40336
|
let closing = false;
|
|
40304
40337
|
let closedSettled = false;
|
|
@@ -40312,8 +40345,8 @@ function startRunCaseRecorder(opts) {
|
|
|
40312
40345
|
});
|
|
40313
40346
|
const ensureStream = () => {
|
|
40314
40347
|
if (stream) return stream;
|
|
40315
|
-
|
|
40316
|
-
stream =
|
|
40348
|
+
import_node_fs53.default.mkdirSync(dir, { recursive: true });
|
|
40349
|
+
stream = import_node_fs53.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
40317
40350
|
stream.on("close", () => closedResolve());
|
|
40318
40351
|
return stream;
|
|
40319
40352
|
};
|
|
@@ -40338,12 +40371,12 @@ function startRunCaseRecorder(opts) {
|
|
|
40338
40371
|
};
|
|
40339
40372
|
return { tap, close, closed };
|
|
40340
40373
|
}
|
|
40341
|
-
var
|
|
40374
|
+
var import_node_fs53, import_node_path65;
|
|
40342
40375
|
var init_recorder = __esm({
|
|
40343
40376
|
"src/run-case/recorder.ts"() {
|
|
40344
40377
|
"use strict";
|
|
40345
|
-
|
|
40346
|
-
|
|
40378
|
+
import_node_fs53 = __toESM(require("fs"), 1);
|
|
40379
|
+
import_node_path65 = __toESM(require("path"), 1);
|
|
40347
40380
|
}
|
|
40348
40381
|
});
|
|
40349
40382
|
|
|
@@ -40386,7 +40419,7 @@ var init_wire = __esm({
|
|
|
40386
40419
|
// src/run-case/controller.ts
|
|
40387
40420
|
async function runController(opts) {
|
|
40388
40421
|
const now = opts.now ?? Date.now;
|
|
40389
|
-
const cwd = opts.cwd ?? (0,
|
|
40422
|
+
const cwd = opts.cwd ?? (0, import_node_fs54.mkdtempSync)(import_node_path66.default.join(import_node_os23.default.tmpdir(), "clawd-runcase-"));
|
|
40390
40423
|
const ownsCwd = opts.cwd === void 0;
|
|
40391
40424
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
40392
40425
|
const spawnCtx = { cwd };
|
|
@@ -40547,19 +40580,19 @@ async function runController(opts) {
|
|
|
40547
40580
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
40548
40581
|
if (ownsCwd) {
|
|
40549
40582
|
try {
|
|
40550
|
-
(0,
|
|
40583
|
+
(0, import_node_fs54.rmSync)(cwd, { recursive: true, force: true });
|
|
40551
40584
|
} catch {
|
|
40552
40585
|
}
|
|
40553
40586
|
}
|
|
40554
40587
|
return exitCode ?? 0;
|
|
40555
40588
|
}
|
|
40556
|
-
var
|
|
40589
|
+
var import_node_fs54, import_node_os23, import_node_path66;
|
|
40557
40590
|
var init_controller = __esm({
|
|
40558
40591
|
"src/run-case/controller.ts"() {
|
|
40559
40592
|
"use strict";
|
|
40560
|
-
|
|
40561
|
-
|
|
40562
|
-
|
|
40593
|
+
import_node_fs54 = require("fs");
|
|
40594
|
+
import_node_os23 = __toESM(require("os"), 1);
|
|
40595
|
+
import_node_path66 = __toESM(require("path"), 1);
|
|
40563
40596
|
init_claude();
|
|
40564
40597
|
init_stdout_splitter();
|
|
40565
40598
|
init_permission_stdio();
|
|
@@ -40957,9 +40990,9 @@ Env (advanced):
|
|
|
40957
40990
|
`;
|
|
40958
40991
|
|
|
40959
40992
|
// src/index.ts
|
|
40960
|
-
var
|
|
40961
|
-
var
|
|
40962
|
-
var
|
|
40993
|
+
var import_node_path64 = __toESM(require("path"), 1);
|
|
40994
|
+
var import_node_fs52 = __toESM(require("fs"), 1);
|
|
40995
|
+
var import_node_os22 = __toESM(require("os"), 1);
|
|
40963
40996
|
|
|
40964
40997
|
// ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
|
|
40965
40998
|
var byteToHex = [];
|
|
@@ -42003,7 +42036,7 @@ ssh -o ProxyCommand='~/.clawd/bin/clawd ssh-relay <A>' \\
|
|
|
42003
42036
|
**\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
|
|
42004
42037
|
|
|
42005
42038
|
**\u6CE8\u610F**\uFF1A
|
|
42006
|
-
- \u6388\u6743\u662F\u5B9E\u65F6\u7684\uFF1AA \u64A4\u9500
|
|
42039
|
+
- \u6388\u6743\u662F\u5B9E\u65F6\u7684\uFF1AA \u64A4\u9500 remoteAccessAllowed \u540E\u4F60\u4E0B\u4E00\u6B21\u62E8\u53F7\u7ACB\u5373\u5931\u6548\uFF08authorized_keys \u7ACB\u5373\u5237\uFF09
|
|
42007
42040
|
- \u62E8\u53F7\u88AB\u62D2\uFF08Permission denied\uFF09\u901A\u5E38\u662F A \u5C1A\u672A\u6388\u6743\u672C\u8BBE\u5907\uFF0C\u6216\u53CC\u65B9\u8001\u7248\u672C\u5EFA\u8054\u3001A \u7684 contact \u91CC\u6CA1\u6709\u672C\u673A pubkey\uFF08\u4EFB\u4E00\u65B9\u91CD\u65B0 device:connect \u4E00\u6B21\u5373\u8865\uFF09\uFF0C\u5982\u5B9E\u544A\u8BC9\u7528\u6237
|
|
42008
42041
|
- \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
|
|
42009
42042
|
`;
|
|
@@ -46760,8 +46793,8 @@ function turnStartInput(text) {
|
|
|
46760
46793
|
const items = [];
|
|
46761
46794
|
let leftover = text;
|
|
46762
46795
|
for (const m2 of text.matchAll(SKILL_RE)) {
|
|
46763
|
-
const [marker, name,
|
|
46764
|
-
items.push({ type: "skill", name, path:
|
|
46796
|
+
const [marker, name, path77] = m2;
|
|
46797
|
+
items.push({ type: "skill", name, path: path77 });
|
|
46765
46798
|
leftover = leftover.replace(marker, "");
|
|
46766
46799
|
}
|
|
46767
46800
|
for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
|
|
@@ -48400,9 +48433,31 @@ async function writeInboxMcpConfig(args) {
|
|
|
48400
48433
|
return cfgPath;
|
|
48401
48434
|
}
|
|
48402
48435
|
|
|
48436
|
+
// src/peer-ops/mcp-config.ts
|
|
48437
|
+
var import_node_fs23 = __toESM(require("fs"), 1);
|
|
48438
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
48439
|
+
function peerOpsMcpConfigPath(dataDir) {
|
|
48440
|
+
return import_node_path20.default.join(dataDir, "peer-ops.mcp.json");
|
|
48441
|
+
}
|
|
48442
|
+
function writePeerOpsMcpConfig(args) {
|
|
48443
|
+
const cfgPath = peerOpsMcpConfigPath(args.dataDir);
|
|
48444
|
+
const cfg = {
|
|
48445
|
+
_comment: "daemon \u542F\u52A8\u81EA\u52A8\u751F\u6210\uFF1Bcc spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002peer-ops-mcp-server.cjs \u662F daemon \u81EA\u5E26 stdio MCP server\uFF08\u8DE8\u8BBE\u5907 exec\uFF09\u3002",
|
|
48446
|
+
mcpServers: {
|
|
48447
|
+
"peer-ops": {
|
|
48448
|
+
command: "node",
|
|
48449
|
+
args: [args.serverScriptPath]
|
|
48450
|
+
}
|
|
48451
|
+
}
|
|
48452
|
+
};
|
|
48453
|
+
import_node_fs23.default.mkdirSync(args.dataDir, { recursive: true });
|
|
48454
|
+
import_node_fs23.default.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
|
|
48455
|
+
return cfgPath;
|
|
48456
|
+
}
|
|
48457
|
+
|
|
48403
48458
|
// src/shift/store.ts
|
|
48404
48459
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
48405
|
-
var
|
|
48460
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
48406
48461
|
var import_node_crypto6 = require("crypto");
|
|
48407
48462
|
|
|
48408
48463
|
// src/shift/constants.ts
|
|
@@ -48485,7 +48540,7 @@ function createShiftStore(deps) {
|
|
|
48485
48540
|
flushTimer = null;
|
|
48486
48541
|
}
|
|
48487
48542
|
const content = { version: 1, shifts };
|
|
48488
|
-
await import_promises.default.mkdir(
|
|
48543
|
+
await import_promises.default.mkdir(import_node_path21.default.dirname(deps.filePath), { recursive: true });
|
|
48489
48544
|
const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
48490
48545
|
await import_promises.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
|
|
48491
48546
|
await import_promises.default.rename(tmp, deps.filePath);
|
|
@@ -49038,6 +49093,7 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
49038
49093
|
}
|
|
49039
49094
|
|
|
49040
49095
|
// src/dispatch/peer-forward.ts
|
|
49096
|
+
init_protocol();
|
|
49041
49097
|
function wsUrlToHttp(url) {
|
|
49042
49098
|
if (url.startsWith("wss://")) return "https://" + url.slice("wss://".length);
|
|
49043
49099
|
if (url.startsWith("ws://")) return "http://" + url.slice("ws://".length);
|
|
@@ -49114,6 +49170,43 @@ async function forwardInboxPostToPeer(args) {
|
|
|
49114
49170
|
}
|
|
49115
49171
|
return { ok: true };
|
|
49116
49172
|
}
|
|
49173
|
+
async function forwardPeerExecToPeer(args) {
|
|
49174
|
+
const f = args.fetchImpl ?? fetch;
|
|
49175
|
+
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
49176
|
+
const url = `${base}/rpc/peerExec:run`;
|
|
49177
|
+
const tunnelFail = (msg) => ({
|
|
49178
|
+
type: "peerExec:run:ok",
|
|
49179
|
+
stdout: "",
|
|
49180
|
+
stderr: msg,
|
|
49181
|
+
exitCode: null,
|
|
49182
|
+
timedOut: false,
|
|
49183
|
+
stdoutTruncated: false,
|
|
49184
|
+
stderrTruncated: false
|
|
49185
|
+
});
|
|
49186
|
+
let res;
|
|
49187
|
+
try {
|
|
49188
|
+
res = await f(url, {
|
|
49189
|
+
method: "POST",
|
|
49190
|
+
headers: {
|
|
49191
|
+
"content-type": "application/json",
|
|
49192
|
+
authorization: `Bearer ${args.contact.connectToken}`
|
|
49193
|
+
},
|
|
49194
|
+
body: JSON.stringify({ command: args.command, ...args.timeoutMs ? { timeoutMs: args.timeoutMs } : {} })
|
|
49195
|
+
});
|
|
49196
|
+
} catch (err) {
|
|
49197
|
+
return tunnelFail(`forward to peer failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
49198
|
+
}
|
|
49199
|
+
let json;
|
|
49200
|
+
try {
|
|
49201
|
+
json = await res.json();
|
|
49202
|
+
} catch {
|
|
49203
|
+
return tunnelFail(`peer returned non-JSON response (HTTP ${res.status})`);
|
|
49204
|
+
}
|
|
49205
|
+
if (json.ok === false) {
|
|
49206
|
+
throw new ClawdError(json.error || ERROR_CODES.INTERNAL, json.message || json.error);
|
|
49207
|
+
}
|
|
49208
|
+
return json.result;
|
|
49209
|
+
}
|
|
49117
49210
|
|
|
49118
49211
|
// src/tools/codex-history.ts
|
|
49119
49212
|
var import_node_child_process5 = require("child_process");
|
|
@@ -49301,13 +49394,13 @@ function mapSkillsListResponse(res) {
|
|
|
49301
49394
|
const r = s ?? {};
|
|
49302
49395
|
const name = str3(r.name);
|
|
49303
49396
|
if (!name) continue;
|
|
49304
|
-
const
|
|
49397
|
+
const path77 = str3(r.path);
|
|
49305
49398
|
const description = str3(r.description);
|
|
49306
49399
|
const isPlugin = name.includes(":");
|
|
49307
49400
|
out.push({
|
|
49308
49401
|
name,
|
|
49309
49402
|
source: isPlugin ? "plugin" : "project",
|
|
49310
|
-
...
|
|
49403
|
+
...path77 ? { path: path77 } : {},
|
|
49311
49404
|
...description ? { description } : {},
|
|
49312
49405
|
...isPlugin ? { plugin: name.split(":")[0] } : {}
|
|
49313
49406
|
});
|
|
@@ -49346,23 +49439,23 @@ async function listCodexSkills(cwd, deps = {}) {
|
|
|
49346
49439
|
}
|
|
49347
49440
|
|
|
49348
49441
|
// src/workspace/browser.ts
|
|
49349
|
-
var
|
|
49442
|
+
var import_node_fs24 = __toESM(require("fs"), 1);
|
|
49350
49443
|
var import_node_os8 = __toESM(require("os"), 1);
|
|
49351
|
-
var
|
|
49444
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
49352
49445
|
init_protocol();
|
|
49353
49446
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
49354
49447
|
function resolveInsideCwd(cwd, subpath) {
|
|
49355
|
-
const absCwd =
|
|
49356
|
-
const joined =
|
|
49357
|
-
const rel =
|
|
49358
|
-
if (rel.startsWith("..") ||
|
|
49448
|
+
const absCwd = import_node_path22.default.resolve(cwd);
|
|
49449
|
+
const joined = import_node_path22.default.resolve(absCwd, subpath ?? ".");
|
|
49450
|
+
const rel = import_node_path22.default.relative(absCwd, joined);
|
|
49451
|
+
if (rel.startsWith("..") || import_node_path22.default.isAbsolute(rel)) {
|
|
49359
49452
|
throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
|
|
49360
49453
|
}
|
|
49361
49454
|
return joined;
|
|
49362
49455
|
}
|
|
49363
49456
|
function ensureCwd(cwd) {
|
|
49364
49457
|
try {
|
|
49365
|
-
const stat =
|
|
49458
|
+
const stat = import_node_fs24.default.statSync(cwd);
|
|
49366
49459
|
if (!stat.isDirectory()) {
|
|
49367
49460
|
throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${cwd}`);
|
|
49368
49461
|
}
|
|
@@ -49376,7 +49469,7 @@ var WorkspaceBrowser = class {
|
|
|
49376
49469
|
const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os8.default.homedir();
|
|
49377
49470
|
ensureCwd(cwd);
|
|
49378
49471
|
const full = resolveInsideCwd(cwd, args.path);
|
|
49379
|
-
const dirents =
|
|
49472
|
+
const dirents = import_node_fs24.default.readdirSync(full, { withFileTypes: true });
|
|
49380
49473
|
const entries = [];
|
|
49381
49474
|
for (const d of dirents) {
|
|
49382
49475
|
if (!args.showHidden && d.name.startsWith(".")) continue;
|
|
@@ -49386,7 +49479,7 @@ var WorkspaceBrowser = class {
|
|
|
49386
49479
|
mtime: ""
|
|
49387
49480
|
};
|
|
49388
49481
|
try {
|
|
49389
|
-
const st =
|
|
49482
|
+
const st = import_node_fs24.default.statSync(import_node_path22.default.join(full, d.name));
|
|
49390
49483
|
entry.mtime = new Date(st.mtimeMs).toISOString();
|
|
49391
49484
|
if (d.isFile()) entry.size = st.size;
|
|
49392
49485
|
} catch {
|
|
@@ -49402,14 +49495,14 @@ var WorkspaceBrowser = class {
|
|
|
49402
49495
|
read(args) {
|
|
49403
49496
|
ensureCwd(args.cwd);
|
|
49404
49497
|
const full = resolveInsideCwd(args.cwd, args.path);
|
|
49405
|
-
const st =
|
|
49498
|
+
const st = import_node_fs24.default.statSync(full);
|
|
49406
49499
|
if (!st.isFile()) {
|
|
49407
49500
|
throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
|
|
49408
49501
|
}
|
|
49409
49502
|
if (st.size > MAX_FILE_BYTES) {
|
|
49410
49503
|
throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES} bytes`);
|
|
49411
49504
|
}
|
|
49412
|
-
const buf =
|
|
49505
|
+
const buf = import_node_fs24.default.readFileSync(full);
|
|
49413
49506
|
const isBinary = buf.includes(0);
|
|
49414
49507
|
if (isBinary) {
|
|
49415
49508
|
return {
|
|
@@ -49431,20 +49524,20 @@ var WorkspaceBrowser = class {
|
|
|
49431
49524
|
};
|
|
49432
49525
|
|
|
49433
49526
|
// src/skills/agents-scanner.ts
|
|
49434
|
-
var
|
|
49527
|
+
var import_node_fs25 = __toESM(require("fs"), 1);
|
|
49435
49528
|
var import_node_os9 = __toESM(require("os"), 1);
|
|
49436
|
-
var
|
|
49529
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
49437
49530
|
var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
|
|
49438
49531
|
function isDirLikeSync2(p2) {
|
|
49439
49532
|
try {
|
|
49440
|
-
return
|
|
49533
|
+
return import_node_fs25.default.statSync(p2).isDirectory();
|
|
49441
49534
|
} catch {
|
|
49442
49535
|
return false;
|
|
49443
49536
|
}
|
|
49444
49537
|
}
|
|
49445
49538
|
function fileExistsSync(p2) {
|
|
49446
49539
|
try {
|
|
49447
|
-
return
|
|
49540
|
+
return import_node_fs25.default.statSync(p2).isFile();
|
|
49448
49541
|
} catch {
|
|
49449
49542
|
return false;
|
|
49450
49543
|
}
|
|
@@ -49452,7 +49545,7 @@ function fileExistsSync(p2) {
|
|
|
49452
49545
|
function parseAgentFile(filePath) {
|
|
49453
49546
|
let content;
|
|
49454
49547
|
try {
|
|
49455
|
-
content =
|
|
49548
|
+
content = import_node_fs25.default.readFileSync(filePath, "utf8");
|
|
49456
49549
|
} catch {
|
|
49457
49550
|
return {};
|
|
49458
49551
|
}
|
|
@@ -49465,16 +49558,16 @@ function parseAgentFile(filePath) {
|
|
|
49465
49558
|
function scanAgentsDir(dir, source, seen, out) {
|
|
49466
49559
|
let entries;
|
|
49467
49560
|
try {
|
|
49468
|
-
entries =
|
|
49561
|
+
entries = import_node_fs25.default.readdirSync(dir, { withFileTypes: true });
|
|
49469
49562
|
} catch {
|
|
49470
49563
|
return;
|
|
49471
49564
|
}
|
|
49472
49565
|
for (const ent of entries) {
|
|
49473
49566
|
if (!ent.name.endsWith(".md")) continue;
|
|
49474
|
-
if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(
|
|
49567
|
+
if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path23.default.join(dir, ent.name)))) {
|
|
49475
49568
|
continue;
|
|
49476
49569
|
}
|
|
49477
|
-
const filePath =
|
|
49570
|
+
const filePath = import_node_path23.default.join(dir, ent.name);
|
|
49478
49571
|
const baseName = ent.name.replace(/\.md$/, "");
|
|
49479
49572
|
if (seen.has(baseName)) continue;
|
|
49480
49573
|
seen.add(baseName);
|
|
@@ -49492,12 +49585,12 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
|
|
|
49492
49585
|
function walk2(dir, namespaces) {
|
|
49493
49586
|
let entries;
|
|
49494
49587
|
try {
|
|
49495
|
-
entries =
|
|
49588
|
+
entries = import_node_fs25.default.readdirSync(dir, { withFileTypes: true });
|
|
49496
49589
|
} catch {
|
|
49497
49590
|
return;
|
|
49498
49591
|
}
|
|
49499
49592
|
for (const ent of entries) {
|
|
49500
|
-
const childPath =
|
|
49593
|
+
const childPath = import_node_path23.default.join(dir, ent.name);
|
|
49501
49594
|
if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
|
|
49502
49595
|
walk2(childPath, [...namespaces, ent.name]);
|
|
49503
49596
|
continue;
|
|
@@ -49522,13 +49615,13 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
|
|
|
49522
49615
|
walk2(root, []);
|
|
49523
49616
|
}
|
|
49524
49617
|
function readInstalledPlugins2(home) {
|
|
49525
|
-
const pluginsDir =
|
|
49526
|
-
const v2 =
|
|
49527
|
-
const v1 =
|
|
49618
|
+
const pluginsDir = import_node_path23.default.join(home, ".claude", "plugins");
|
|
49619
|
+
const v2 = import_node_path23.default.join(pluginsDir, "installed_plugins_v2.json");
|
|
49620
|
+
const v1 = import_node_path23.default.join(pluginsDir, "installed_plugins.json");
|
|
49528
49621
|
let raw = null;
|
|
49529
49622
|
for (const candidate of [v2, v1]) {
|
|
49530
49623
|
try {
|
|
49531
|
-
raw =
|
|
49624
|
+
raw = import_node_fs25.default.readFileSync(candidate, "utf8");
|
|
49532
49625
|
break;
|
|
49533
49626
|
} catch {
|
|
49534
49627
|
}
|
|
@@ -49553,19 +49646,19 @@ function readInstalledPlugins2(home) {
|
|
|
49553
49646
|
return out;
|
|
49554
49647
|
}
|
|
49555
49648
|
function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
|
|
49556
|
-
let cur =
|
|
49557
|
-
const fsRoot =
|
|
49649
|
+
let cur = import_node_path23.default.resolve(startCwd);
|
|
49650
|
+
const fsRoot = import_node_path23.default.parse(cur).root;
|
|
49558
49651
|
while (true) {
|
|
49559
|
-
scanAgentsDir(
|
|
49652
|
+
scanAgentsDir(import_node_path23.default.join(cur, ".claude", "agents"), "project", seen, out);
|
|
49560
49653
|
let hasGit = false;
|
|
49561
49654
|
try {
|
|
49562
|
-
hasGit =
|
|
49655
|
+
hasGit = import_node_fs25.default.existsSync(import_node_path23.default.join(cur, ".git"));
|
|
49563
49656
|
} catch {
|
|
49564
49657
|
}
|
|
49565
49658
|
if (hasGit) return;
|
|
49566
49659
|
if (cur === home) return;
|
|
49567
49660
|
if (cur === fsRoot) return;
|
|
49568
|
-
const parent =
|
|
49661
|
+
const parent = import_node_path23.default.dirname(cur);
|
|
49569
49662
|
if (parent === cur) return;
|
|
49570
49663
|
cur = parent;
|
|
49571
49664
|
}
|
|
@@ -49600,7 +49693,7 @@ var AgentsScanner = class {
|
|
|
49600
49693
|
}
|
|
49601
49694
|
const fsBlock = [];
|
|
49602
49695
|
scanAgentsDir(
|
|
49603
|
-
|
|
49696
|
+
import_node_path23.default.join(this.home, ".claude", "agents"),
|
|
49604
49697
|
"global",
|
|
49605
49698
|
seen,
|
|
49606
49699
|
fsBlock
|
|
@@ -49614,7 +49707,7 @@ var AgentsScanner = class {
|
|
|
49614
49707
|
...this.extraPluginRoots
|
|
49615
49708
|
];
|
|
49616
49709
|
for (const { name, root } of plugins) {
|
|
49617
|
-
const agentsRoot =
|
|
49710
|
+
const agentsRoot = import_node_path23.default.join(root, "agents");
|
|
49618
49711
|
scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
|
|
49619
49712
|
}
|
|
49620
49713
|
return [...builtinBlock, ...fsBlock];
|
|
@@ -49622,15 +49715,15 @@ var AgentsScanner = class {
|
|
|
49622
49715
|
};
|
|
49623
49716
|
|
|
49624
49717
|
// src/observer/session-observer.ts
|
|
49625
|
-
var
|
|
49718
|
+
var import_node_fs27 = __toESM(require("fs"), 1);
|
|
49626
49719
|
var import_node_os11 = __toESM(require("os"), 1);
|
|
49627
|
-
var
|
|
49720
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
49628
49721
|
init_claude_history();
|
|
49629
49722
|
|
|
49630
49723
|
// src/observer/subagent-meta-observer.ts
|
|
49631
|
-
var
|
|
49724
|
+
var import_node_fs26 = __toESM(require("fs"), 1);
|
|
49632
49725
|
var import_node_os10 = __toESM(require("os"), 1);
|
|
49633
|
-
var
|
|
49726
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
49634
49727
|
init_claude_history();
|
|
49635
49728
|
var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
|
|
49636
49729
|
var SubagentMetaObserver = class {
|
|
@@ -49643,7 +49736,7 @@ var SubagentMetaObserver = class {
|
|
|
49643
49736
|
watches = /* @__PURE__ */ new Map();
|
|
49644
49737
|
// public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
|
|
49645
49738
|
resolveSubagentDir(cwd, toolSessionId) {
|
|
49646
|
-
return
|
|
49739
|
+
return import_node_path24.default.join(
|
|
49647
49740
|
this.home,
|
|
49648
49741
|
".claude",
|
|
49649
49742
|
"projects",
|
|
@@ -49673,7 +49766,7 @@ var SubagentMetaObserver = class {
|
|
|
49673
49766
|
attachWatcher(w2) {
|
|
49674
49767
|
if (w2.watcher) return;
|
|
49675
49768
|
try {
|
|
49676
|
-
w2.watcher =
|
|
49769
|
+
w2.watcher = import_node_fs26.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
|
|
49677
49770
|
if (!name) return;
|
|
49678
49771
|
const m2 = META_RE.exec(String(name));
|
|
49679
49772
|
if (!m2) return;
|
|
@@ -49685,7 +49778,7 @@ var SubagentMetaObserver = class {
|
|
|
49685
49778
|
scan(w2) {
|
|
49686
49779
|
let entries;
|
|
49687
49780
|
try {
|
|
49688
|
-
entries =
|
|
49781
|
+
entries = import_node_fs26.default.readdirSync(w2.dirPath);
|
|
49689
49782
|
} catch {
|
|
49690
49783
|
return;
|
|
49691
49784
|
}
|
|
@@ -49699,10 +49792,10 @@ var SubagentMetaObserver = class {
|
|
|
49699
49792
|
if (!m2) return;
|
|
49700
49793
|
const agentId = m2[1];
|
|
49701
49794
|
if (w2.emitted.has(agentId)) return;
|
|
49702
|
-
const file =
|
|
49795
|
+
const file = import_node_path24.default.join(w2.dirPath, name);
|
|
49703
49796
|
let raw;
|
|
49704
49797
|
try {
|
|
49705
|
-
raw =
|
|
49798
|
+
raw = import_node_fs26.default.readFileSync(file, "utf8");
|
|
49706
49799
|
} catch {
|
|
49707
49800
|
return;
|
|
49708
49801
|
}
|
|
@@ -49759,14 +49852,14 @@ var SessionObserver = class {
|
|
|
49759
49852
|
metaObserver;
|
|
49760
49853
|
resolveJsonlPath(cwd, toolSessionId, override) {
|
|
49761
49854
|
if (override) return override;
|
|
49762
|
-
return
|
|
49855
|
+
return import_node_path25.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
|
|
49763
49856
|
}
|
|
49764
49857
|
start(args) {
|
|
49765
49858
|
this.stop(args.sessionId);
|
|
49766
49859
|
const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
|
|
49767
49860
|
let size = 0;
|
|
49768
49861
|
try {
|
|
49769
|
-
size =
|
|
49862
|
+
size = import_node_fs27.default.statSync(filePath).size;
|
|
49770
49863
|
} catch {
|
|
49771
49864
|
}
|
|
49772
49865
|
const w2 = {
|
|
@@ -49779,16 +49872,16 @@ var SessionObserver = class {
|
|
|
49779
49872
|
adapter: args.adapter,
|
|
49780
49873
|
prevIsRejectSentinel: false,
|
|
49781
49874
|
toolSessionId: args.toolSessionId,
|
|
49782
|
-
sawFile:
|
|
49875
|
+
sawFile: import_node_fs27.default.existsSync(filePath),
|
|
49783
49876
|
lastRelocateScanMs: 0,
|
|
49784
49877
|
pendingCwdDerive: false,
|
|
49785
49878
|
lastDeriveMs: 0
|
|
49786
49879
|
};
|
|
49787
49880
|
try {
|
|
49788
|
-
|
|
49881
|
+
import_node_fs27.default.mkdirSync(import_node_path25.default.dirname(filePath), { recursive: true });
|
|
49789
49882
|
} catch {
|
|
49790
49883
|
}
|
|
49791
|
-
w2.watcher =
|
|
49884
|
+
w2.watcher = import_node_fs27.default.watch(import_node_path25.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
|
|
49792
49885
|
if (!changedName || !w2.filePath.endsWith(changedName)) return;
|
|
49793
49886
|
this.poll(w2);
|
|
49794
49887
|
});
|
|
@@ -49811,7 +49904,7 @@ var SessionObserver = class {
|
|
|
49811
49904
|
// 异常静默吞,不阻塞 watcher 启动
|
|
49812
49905
|
hydrateMetaTail(w2, maxLines = 200) {
|
|
49813
49906
|
try {
|
|
49814
|
-
const raw =
|
|
49907
|
+
const raw = import_node_fs27.default.readFileSync(w2.filePath, "utf8");
|
|
49815
49908
|
if (!raw) return;
|
|
49816
49909
|
const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
49817
49910
|
if (allLines.length === 0) return;
|
|
@@ -49835,7 +49928,7 @@ var SessionObserver = class {
|
|
|
49835
49928
|
poll(w2) {
|
|
49836
49929
|
let size = 0;
|
|
49837
49930
|
try {
|
|
49838
|
-
size =
|
|
49931
|
+
size = import_node_fs27.default.statSync(w2.filePath).size;
|
|
49839
49932
|
} catch {
|
|
49840
49933
|
if (w2.sawFile) this.maybeRelocate(w2);
|
|
49841
49934
|
return;
|
|
@@ -49846,11 +49939,11 @@ var SessionObserver = class {
|
|
|
49846
49939
|
w2.buf = "";
|
|
49847
49940
|
}
|
|
49848
49941
|
if (size === w2.lastSize) return;
|
|
49849
|
-
const fd =
|
|
49942
|
+
const fd = import_node_fs27.default.openSync(w2.filePath, "r");
|
|
49850
49943
|
try {
|
|
49851
49944
|
const len = size - w2.lastSize;
|
|
49852
49945
|
const buf = Buffer.alloc(len);
|
|
49853
|
-
|
|
49946
|
+
import_node_fs27.default.readSync(fd, buf, 0, len, w2.lastSize);
|
|
49854
49947
|
w2.lastSize = size;
|
|
49855
49948
|
w2.buf += buf.toString("utf8");
|
|
49856
49949
|
let newlineIndex;
|
|
@@ -49873,7 +49966,7 @@ var SessionObserver = class {
|
|
|
49873
49966
|
}
|
|
49874
49967
|
}
|
|
49875
49968
|
} finally {
|
|
49876
|
-
|
|
49969
|
+
import_node_fs27.default.closeSync(fd);
|
|
49877
49970
|
}
|
|
49878
49971
|
if (w2.pendingCwdDerive) this.maybeDeriveCwd(w2);
|
|
49879
49972
|
}
|
|
@@ -49885,7 +49978,7 @@ var SessionObserver = class {
|
|
|
49885
49978
|
const now = Date.now();
|
|
49886
49979
|
if (now - w2.lastRelocateScanMs < 1e3) return;
|
|
49887
49980
|
w2.lastRelocateScanMs = now;
|
|
49888
|
-
const projectsRoot =
|
|
49981
|
+
const projectsRoot = import_node_path25.default.join(this.home, ".claude", "projects");
|
|
49889
49982
|
const found = findTranscriptByToolSessionId(projectsRoot, w2.toolSessionId);
|
|
49890
49983
|
if (!found || found === w2.filePath) return;
|
|
49891
49984
|
try {
|
|
@@ -49894,7 +49987,7 @@ var SessionObserver = class {
|
|
|
49894
49987
|
}
|
|
49895
49988
|
w2.filePath = found;
|
|
49896
49989
|
try {
|
|
49897
|
-
w2.watcher =
|
|
49990
|
+
w2.watcher = import_node_fs27.default.watch(import_node_path25.default.dirname(found), { persistent: false }, (_event, changedName) => {
|
|
49898
49991
|
if (!changedName || !w2.filePath.endsWith(changedName)) return;
|
|
49899
49992
|
this.poll(w2);
|
|
49900
49993
|
});
|
|
@@ -50486,6 +50579,8 @@ var EXPOSED_RPC_METHODS = [
|
|
|
50486
50579
|
// B 端 dispatch MCP server 调 complete(带 outcome 触发 run 端 resolve)。详见
|
|
50487
50580
|
// protocol/src/dispatch.ts。
|
|
50488
50581
|
"personaDispatch.run",
|
|
50582
|
+
// 跨设备 exec(spec 2026-07-16):A 侧本地 cc 经 /api/rpc 调,带 peerDeviceId → daemon forward。
|
|
50583
|
+
"peerExec.run",
|
|
50489
50584
|
"personaDispatch.complete",
|
|
50490
50585
|
// clawd-shift v1 (spec 2026-06-24-clawd-shift):cc 子进程内的 shift MCP server 调
|
|
50491
50586
|
// 这 5 个 method 管理 owner 的 shift(fire-and-forget 排班)。鉴权靠 body.personaId
|
|
@@ -50753,14 +50848,14 @@ async function authenticate(token, deps) {
|
|
|
50753
50848
|
}
|
|
50754
50849
|
|
|
50755
50850
|
// src/permission/capability-store.ts
|
|
50756
|
-
var
|
|
50757
|
-
var
|
|
50851
|
+
var fs31 = __toESM(require("fs"), 1);
|
|
50852
|
+
var path30 = __toESM(require("path"), 1);
|
|
50758
50853
|
var CAPABILITIES_FILE_NAME = "capabilities.json";
|
|
50759
50854
|
var FILE_VERSION = 1;
|
|
50760
50855
|
var CapabilityStore = class {
|
|
50761
50856
|
constructor(dataDir) {
|
|
50762
50857
|
this.dataDir = dataDir;
|
|
50763
|
-
|
|
50858
|
+
fs31.mkdirSync(dataDir, { recursive: true });
|
|
50764
50859
|
this.cache = this.readFromDisk();
|
|
50765
50860
|
}
|
|
50766
50861
|
dataDir;
|
|
@@ -50784,13 +50879,13 @@ var CapabilityStore = class {
|
|
|
50784
50879
|
this.flush();
|
|
50785
50880
|
}
|
|
50786
50881
|
filePath() {
|
|
50787
|
-
return
|
|
50882
|
+
return path30.join(this.dataDir, CAPABILITIES_FILE_NAME);
|
|
50788
50883
|
}
|
|
50789
50884
|
readFromDisk() {
|
|
50790
50885
|
const file = this.filePath();
|
|
50791
50886
|
let raw;
|
|
50792
50887
|
try {
|
|
50793
|
-
raw =
|
|
50888
|
+
raw = fs31.readFileSync(file, "utf8");
|
|
50794
50889
|
} catch (err) {
|
|
50795
50890
|
if (err?.code === "ENOENT") return [];
|
|
50796
50891
|
return [];
|
|
@@ -50818,10 +50913,10 @@ var CapabilityStore = class {
|
|
|
50818
50913
|
}
|
|
50819
50914
|
atomicWrite(file, content) {
|
|
50820
50915
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
50821
|
-
|
|
50822
|
-
|
|
50916
|
+
fs31.writeFileSync(tmp, content, { mode: 384 });
|
|
50917
|
+
fs31.renameSync(tmp, file);
|
|
50823
50918
|
try {
|
|
50824
|
-
|
|
50919
|
+
fs31.chmodSync(file, 384);
|
|
50825
50920
|
} catch {
|
|
50826
50921
|
}
|
|
50827
50922
|
}
|
|
@@ -50914,14 +51009,14 @@ var CapabilityManager = class {
|
|
|
50914
51009
|
};
|
|
50915
51010
|
|
|
50916
51011
|
// src/permission/cleanup.ts
|
|
50917
|
-
var
|
|
51012
|
+
var fs32 = __toESM(require("fs"), 1);
|
|
50918
51013
|
function cleanupGuestSessionsForCapability(cap, factory) {
|
|
50919
51014
|
const removed = [];
|
|
50920
51015
|
for (const g2 of cap.grants) {
|
|
50921
51016
|
if (g2.resource.type !== "persona") continue;
|
|
50922
51017
|
const dir = factory.vmGuestRoot(g2.resource.id, cap.id);
|
|
50923
51018
|
try {
|
|
50924
|
-
|
|
51019
|
+
fs32.rmSync(dir, { recursive: true, force: true });
|
|
50925
51020
|
removed.push(dir);
|
|
50926
51021
|
} catch {
|
|
50927
51022
|
}
|
|
@@ -50930,13 +51025,13 @@ function cleanupGuestSessionsForCapability(cap, factory) {
|
|
|
50930
51025
|
}
|
|
50931
51026
|
|
|
50932
51027
|
// src/inbox/inbox-store.ts
|
|
50933
|
-
var
|
|
50934
|
-
var
|
|
51028
|
+
var fs33 = __toESM(require("fs"), 1);
|
|
51029
|
+
var path31 = __toESM(require("path"), 1);
|
|
50935
51030
|
var INBOX_SUBDIR = "inbox";
|
|
50936
51031
|
var InboxStore = class {
|
|
50937
51032
|
constructor(dataDir) {
|
|
50938
51033
|
this.dataDir = dataDir;
|
|
50939
|
-
|
|
51034
|
+
fs33.mkdirSync(this.dirPath(), { recursive: true });
|
|
50940
51035
|
}
|
|
50941
51036
|
dataDir;
|
|
50942
51037
|
/**
|
|
@@ -50948,7 +51043,7 @@ var InboxStore = class {
|
|
|
50948
51043
|
const file = this.filePath(peerDeviceId);
|
|
50949
51044
|
let raw;
|
|
50950
51045
|
try {
|
|
50951
|
-
raw =
|
|
51046
|
+
raw = fs33.readFileSync(file, "utf8");
|
|
50952
51047
|
} catch (err) {
|
|
50953
51048
|
if (err?.code === "ENOENT") return [];
|
|
50954
51049
|
return [];
|
|
@@ -50964,7 +51059,7 @@ var InboxStore = class {
|
|
|
50964
51059
|
const dir = this.dirPath();
|
|
50965
51060
|
let entries;
|
|
50966
51061
|
try {
|
|
50967
|
-
entries =
|
|
51062
|
+
entries = fs33.readdirSync(dir);
|
|
50968
51063
|
} catch (err) {
|
|
50969
51064
|
if (err?.code === "ENOENT") return [];
|
|
50970
51065
|
return [];
|
|
@@ -50980,9 +51075,9 @@ var InboxStore = class {
|
|
|
50980
51075
|
if (existing.some((m2) => m2.id === message.id)) return;
|
|
50981
51076
|
const file = this.filePath(message.peerDeviceId);
|
|
50982
51077
|
const line = JSON.stringify(message) + "\n";
|
|
50983
|
-
|
|
51078
|
+
fs33.appendFileSync(file, line, { mode: 384 });
|
|
50984
51079
|
try {
|
|
50985
|
-
|
|
51080
|
+
fs33.chmodSync(file, 384);
|
|
50986
51081
|
} catch {
|
|
50987
51082
|
}
|
|
50988
51083
|
}
|
|
@@ -51012,7 +51107,7 @@ var InboxStore = class {
|
|
|
51012
51107
|
removeByPeerDeviceId(peerDeviceId) {
|
|
51013
51108
|
const file = this.filePath(peerDeviceId);
|
|
51014
51109
|
try {
|
|
51015
|
-
|
|
51110
|
+
fs33.unlinkSync(file);
|
|
51016
51111
|
} catch (err) {
|
|
51017
51112
|
if (err?.code === "ENOENT") return;
|
|
51018
51113
|
}
|
|
@@ -51021,18 +51116,18 @@ var InboxStore = class {
|
|
|
51021
51116
|
const file = this.filePath(peerDeviceId);
|
|
51022
51117
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
51023
51118
|
const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
|
|
51024
|
-
|
|
51025
|
-
|
|
51119
|
+
fs33.writeFileSync(tmp, content, { mode: 384 });
|
|
51120
|
+
fs33.renameSync(tmp, file);
|
|
51026
51121
|
try {
|
|
51027
|
-
|
|
51122
|
+
fs33.chmodSync(file, 384);
|
|
51028
51123
|
} catch {
|
|
51029
51124
|
}
|
|
51030
51125
|
}
|
|
51031
51126
|
dirPath() {
|
|
51032
|
-
return
|
|
51127
|
+
return path31.join(this.dataDir, INBOX_SUBDIR);
|
|
51033
51128
|
}
|
|
51034
51129
|
filePath(peerDeviceId) {
|
|
51035
|
-
return
|
|
51130
|
+
return path31.join(this.dirPath(), `${peerDeviceId}.jsonl`);
|
|
51036
51131
|
}
|
|
51037
51132
|
};
|
|
51038
51133
|
function parseAllLines(raw) {
|
|
@@ -51120,8 +51215,8 @@ var InboxManager = class {
|
|
|
51120
51215
|
};
|
|
51121
51216
|
|
|
51122
51217
|
// src/state/contact-store.ts
|
|
51123
|
-
var
|
|
51124
|
-
var
|
|
51218
|
+
var fs34 = __toESM(require("fs"), 1);
|
|
51219
|
+
var path32 = __toESM(require("path"), 1);
|
|
51125
51220
|
var FILE_NAME = "contacts.json";
|
|
51126
51221
|
var ContactStore = class {
|
|
51127
51222
|
constructor(dataDir) {
|
|
@@ -51131,10 +51226,10 @@ var ContactStore = class {
|
|
|
51131
51226
|
contacts = /* @__PURE__ */ new Map();
|
|
51132
51227
|
load() {
|
|
51133
51228
|
this.contacts.clear();
|
|
51134
|
-
const file =
|
|
51229
|
+
const file = path32.join(this.dataDir, FILE_NAME);
|
|
51135
51230
|
let raw;
|
|
51136
51231
|
try {
|
|
51137
|
-
raw =
|
|
51232
|
+
raw = fs34.readFileSync(file, "utf8");
|
|
51138
51233
|
} catch (err) {
|
|
51139
51234
|
if (err?.code !== "ENOENT") this.renameBak(file);
|
|
51140
51235
|
return;
|
|
@@ -51189,35 +51284,35 @@ var ContactStore = class {
|
|
|
51189
51284
|
return true;
|
|
51190
51285
|
}
|
|
51191
51286
|
/**
|
|
51192
|
-
* 更新单条 contact 的 SSH 授权开关(对齐 setPin pattern)。T-29 去沙箱后只改
|
|
51287
|
+
* 更新单条 contact 的 SSH 授权开关(对齐 setPin pattern)。T-29 去沙箱后只改 remoteAccessAllowed;
|
|
51193
51288
|
* exposedDirs 已废弃,本方法不再触碰它(保留 contact 原值不动)。
|
|
51194
51289
|
* @returns 是否命中:deviceId 不存在返 false;命中即 flush.
|
|
51195
51290
|
*/
|
|
51196
|
-
|
|
51291
|
+
setRemoteAccess(deviceId, opts) {
|
|
51197
51292
|
const existing = this.contacts.get(deviceId);
|
|
51198
51293
|
if (!existing) return false;
|
|
51199
51294
|
this.contacts.set(deviceId, {
|
|
51200
51295
|
...existing,
|
|
51201
|
-
|
|
51296
|
+
remoteAccessAllowed: opts.remoteAccessAllowed
|
|
51202
51297
|
});
|
|
51203
51298
|
this.flush();
|
|
51204
51299
|
return true;
|
|
51205
51300
|
}
|
|
51206
51301
|
flush() {
|
|
51207
|
-
const file =
|
|
51302
|
+
const file = path32.join(this.dataDir, FILE_NAME);
|
|
51208
51303
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
51209
51304
|
const content = JSON.stringify(
|
|
51210
51305
|
{ contacts: Array.from(this.contacts.values()) },
|
|
51211
51306
|
null,
|
|
51212
51307
|
2
|
|
51213
51308
|
);
|
|
51214
|
-
|
|
51215
|
-
|
|
51216
|
-
|
|
51309
|
+
fs34.mkdirSync(this.dataDir, { recursive: true });
|
|
51310
|
+
fs34.writeFileSync(tmp, content, { mode: 384 });
|
|
51311
|
+
fs34.renameSync(tmp, file);
|
|
51217
51312
|
}
|
|
51218
51313
|
renameBak(file) {
|
|
51219
51314
|
try {
|
|
51220
|
-
|
|
51315
|
+
fs34.renameSync(file, `${file}.bak`);
|
|
51221
51316
|
} catch {
|
|
51222
51317
|
}
|
|
51223
51318
|
}
|
|
@@ -51362,7 +51457,7 @@ async function autoReverseContact(args) {
|
|
|
51362
51457
|
grants,
|
|
51363
51458
|
addedAt: now(),
|
|
51364
51459
|
pinnedAt: existing?.pinnedAt ?? null,
|
|
51365
|
-
|
|
51460
|
+
remoteAccessAllowed: existing?.remoteAccessAllowed ?? false,
|
|
51366
51461
|
// 建联交换:缺省不落 undefined 占位(JSON 持久化干净)。见 …#建联交换清单
|
|
51367
51462
|
...sshPublicKey ? { sshPublicKey } : {},
|
|
51368
51463
|
...osUser ? { osUser } : {},
|
|
@@ -51370,7 +51465,7 @@ async function autoReverseContact(args) {
|
|
|
51370
51465
|
};
|
|
51371
51466
|
args.store.upsert(base);
|
|
51372
51467
|
args.broadcast({ type: "contact:added", contact: base });
|
|
51373
|
-
if (base.
|
|
51468
|
+
if (base.remoteAccessAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
|
|
51374
51469
|
args.rebuildAuthorizedKeys?.();
|
|
51375
51470
|
}
|
|
51376
51471
|
if (!args.exchangeToken) return;
|
|
@@ -51387,61 +51482,61 @@ async function autoReverseContact(args) {
|
|
|
51387
51482
|
}
|
|
51388
51483
|
|
|
51389
51484
|
// src/migrations/2026-05-20-flatten-sessions.ts
|
|
51390
|
-
var
|
|
51391
|
-
var
|
|
51485
|
+
var fs35 = __toESM(require("fs"), 1);
|
|
51486
|
+
var path33 = __toESM(require("path"), 1);
|
|
51392
51487
|
var MIGRATION_FLAG_NAME = ".migration.v1.done";
|
|
51393
51488
|
function migrateFlattenSessions(opts) {
|
|
51394
51489
|
const dataDir = opts.dataDir;
|
|
51395
51490
|
const now = opts.now ?? Date.now;
|
|
51396
|
-
const sessionsDir =
|
|
51397
|
-
const flagPath =
|
|
51491
|
+
const sessionsDir = path33.join(dataDir, "sessions");
|
|
51492
|
+
const flagPath = path33.join(sessionsDir, MIGRATION_FLAG_NAME);
|
|
51398
51493
|
if (existsSync3(flagPath)) {
|
|
51399
51494
|
return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
|
|
51400
51495
|
}
|
|
51401
51496
|
let movedBare = 0;
|
|
51402
51497
|
let movedVmOwner = 0;
|
|
51403
51498
|
let archivedListener = 0;
|
|
51404
|
-
const defaultDir =
|
|
51499
|
+
const defaultDir = path33.join(sessionsDir, "default");
|
|
51405
51500
|
if (existsSync3(defaultDir)) {
|
|
51406
51501
|
for (const entry of readdirSafe(defaultDir)) {
|
|
51407
51502
|
if (!entry.endsWith(".json")) continue;
|
|
51408
|
-
const src =
|
|
51409
|
-
const dst =
|
|
51410
|
-
|
|
51503
|
+
const src = path33.join(defaultDir, entry);
|
|
51504
|
+
const dst = path33.join(sessionsDir, entry);
|
|
51505
|
+
fs35.renameSync(src, dst);
|
|
51411
51506
|
movedBare += 1;
|
|
51412
51507
|
}
|
|
51413
51508
|
rmdirIfEmpty(defaultDir);
|
|
51414
51509
|
}
|
|
51415
51510
|
for (const pid of readdirSafe(sessionsDir)) {
|
|
51416
|
-
const personaDir =
|
|
51511
|
+
const personaDir = path33.join(sessionsDir, pid);
|
|
51417
51512
|
if (!isDir(personaDir)) continue;
|
|
51418
51513
|
if (pid === "default") continue;
|
|
51419
|
-
const ownerSrc =
|
|
51514
|
+
const ownerSrc = path33.join(personaDir, "owner");
|
|
51420
51515
|
if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
|
|
51421
|
-
const ownerDst =
|
|
51422
|
-
|
|
51516
|
+
const ownerDst = path33.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
|
|
51517
|
+
fs35.mkdirSync(ownerDst, { recursive: true });
|
|
51423
51518
|
for (const file of readdirSafe(ownerSrc)) {
|
|
51424
51519
|
if (!file.endsWith(".json")) continue;
|
|
51425
|
-
|
|
51520
|
+
fs35.renameSync(path33.join(ownerSrc, file), path33.join(ownerDst, file));
|
|
51426
51521
|
movedVmOwner += 1;
|
|
51427
51522
|
}
|
|
51428
51523
|
rmdirIfEmpty(ownerSrc);
|
|
51429
51524
|
}
|
|
51430
|
-
const listenerSrc =
|
|
51525
|
+
const listenerSrc = path33.join(personaDir, "listener");
|
|
51431
51526
|
if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
|
|
51432
|
-
const archiveDst =
|
|
51433
|
-
|
|
51527
|
+
const archiveDst = path33.join(dataDir, ".legacy", `listener-${pid}`);
|
|
51528
|
+
fs35.mkdirSync(archiveDst, { recursive: true });
|
|
51434
51529
|
for (const file of readdirSafe(listenerSrc)) {
|
|
51435
51530
|
if (!file.endsWith(".json")) continue;
|
|
51436
|
-
|
|
51531
|
+
fs35.renameSync(path33.join(listenerSrc, file), path33.join(archiveDst, file));
|
|
51437
51532
|
archivedListener += 1;
|
|
51438
51533
|
}
|
|
51439
51534
|
rmdirIfEmpty(listenerSrc);
|
|
51440
51535
|
}
|
|
51441
51536
|
rmdirIfEmpty(personaDir);
|
|
51442
51537
|
}
|
|
51443
|
-
|
|
51444
|
-
|
|
51538
|
+
fs35.mkdirSync(sessionsDir, { recursive: true });
|
|
51539
|
+
fs35.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
|
|
51445
51540
|
return {
|
|
51446
51541
|
skipped: false,
|
|
51447
51542
|
flagWritten: true,
|
|
@@ -51452,7 +51547,7 @@ function migrateFlattenSessions(opts) {
|
|
|
51452
51547
|
}
|
|
51453
51548
|
function existsSync3(p2) {
|
|
51454
51549
|
try {
|
|
51455
|
-
|
|
51550
|
+
fs35.statSync(p2);
|
|
51456
51551
|
return true;
|
|
51457
51552
|
} catch {
|
|
51458
51553
|
return false;
|
|
@@ -51460,31 +51555,31 @@ function existsSync3(p2) {
|
|
|
51460
51555
|
}
|
|
51461
51556
|
function isDir(p2) {
|
|
51462
51557
|
try {
|
|
51463
|
-
return
|
|
51558
|
+
return fs35.statSync(p2).isDirectory();
|
|
51464
51559
|
} catch {
|
|
51465
51560
|
return false;
|
|
51466
51561
|
}
|
|
51467
51562
|
}
|
|
51468
51563
|
function readdirSafe(p2) {
|
|
51469
51564
|
try {
|
|
51470
|
-
return
|
|
51565
|
+
return fs35.readdirSync(p2);
|
|
51471
51566
|
} catch {
|
|
51472
51567
|
return [];
|
|
51473
51568
|
}
|
|
51474
51569
|
}
|
|
51475
51570
|
function rmdirIfEmpty(p2) {
|
|
51476
51571
|
try {
|
|
51477
|
-
|
|
51572
|
+
fs35.rmdirSync(p2);
|
|
51478
51573
|
} catch {
|
|
51479
51574
|
}
|
|
51480
51575
|
}
|
|
51481
51576
|
|
|
51482
51577
|
// src/transport/http-router.ts
|
|
51483
|
-
var
|
|
51484
|
-
var
|
|
51578
|
+
var import_node_fs29 = __toESM(require("fs"), 1);
|
|
51579
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
51485
51580
|
|
|
51486
51581
|
// src/attachment/mime.ts
|
|
51487
|
-
var
|
|
51582
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
51488
51583
|
var TEXT_PLAIN = "text/plain; charset=utf-8";
|
|
51489
51584
|
var EXT_TO_NATIVE_MIME = {
|
|
51490
51585
|
// 图片
|
|
@@ -51591,7 +51686,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
51591
51686
|
".mk"
|
|
51592
51687
|
]);
|
|
51593
51688
|
function lookupMime(filePathOrName) {
|
|
51594
|
-
const ext =
|
|
51689
|
+
const ext = import_node_path26.default.extname(filePathOrName).toLowerCase();
|
|
51595
51690
|
if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
|
|
51596
51691
|
if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
|
|
51597
51692
|
return "application/octet-stream";
|
|
@@ -51660,8 +51755,8 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
|
|
|
51660
51755
|
}
|
|
51661
51756
|
|
|
51662
51757
|
// src/attachment/upload.ts
|
|
51663
|
-
var
|
|
51664
|
-
var
|
|
51758
|
+
var import_node_fs28 = __toESM(require("fs"), 1);
|
|
51759
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
51665
51760
|
var import_node_crypto8 = __toESM(require("crypto"), 1);
|
|
51666
51761
|
var import_promises2 = require("stream/promises");
|
|
51667
51762
|
var UploadError = class extends Error {
|
|
@@ -51673,22 +51768,22 @@ var UploadError = class extends Error {
|
|
|
51673
51768
|
code;
|
|
51674
51769
|
};
|
|
51675
51770
|
function assertValidFileName(fileName) {
|
|
51676
|
-
if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !==
|
|
51771
|
+
if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path27.default.basename(fileName)) {
|
|
51677
51772
|
throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
|
|
51678
51773
|
}
|
|
51679
51774
|
}
|
|
51680
51775
|
var HASH_PREFIX_LEN = 16;
|
|
51681
51776
|
async function writeUploadedAttachment(args) {
|
|
51682
51777
|
assertValidFileName(args.fileName);
|
|
51683
|
-
const attachmentsRoot =
|
|
51778
|
+
const attachmentsRoot = import_node_path27.default.join(args.sessionDir, ".attachments");
|
|
51684
51779
|
try {
|
|
51685
|
-
|
|
51780
|
+
import_node_fs28.default.mkdirSync(attachmentsRoot, { recursive: true });
|
|
51686
51781
|
} catch (err) {
|
|
51687
51782
|
throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
|
|
51688
51783
|
}
|
|
51689
51784
|
const hasher = import_node_crypto8.default.createHash("sha256");
|
|
51690
51785
|
let actualSize = 0;
|
|
51691
|
-
const tmpPath =
|
|
51786
|
+
const tmpPath = import_node_path27.default.join(
|
|
51692
51787
|
attachmentsRoot,
|
|
51693
51788
|
`.upload-${process.pid}-${Date.now()}-${import_node_crypto8.default.randomBytes(4).toString("hex")}`
|
|
51694
51789
|
);
|
|
@@ -51703,18 +51798,18 @@ async function writeUploadedAttachment(args) {
|
|
|
51703
51798
|
yield buf;
|
|
51704
51799
|
}
|
|
51705
51800
|
},
|
|
51706
|
-
|
|
51801
|
+
import_node_fs28.default.createWriteStream(tmpPath, { mode: 384 })
|
|
51707
51802
|
);
|
|
51708
51803
|
} catch (err) {
|
|
51709
51804
|
try {
|
|
51710
|
-
|
|
51805
|
+
import_node_fs28.default.unlinkSync(tmpPath);
|
|
51711
51806
|
} catch {
|
|
51712
51807
|
}
|
|
51713
51808
|
throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
|
|
51714
51809
|
}
|
|
51715
51810
|
if (actualSize !== args.contentLength) {
|
|
51716
51811
|
try {
|
|
51717
|
-
|
|
51812
|
+
import_node_fs28.default.unlinkSync(tmpPath);
|
|
51718
51813
|
} catch {
|
|
51719
51814
|
}
|
|
51720
51815
|
throw new UploadError(
|
|
@@ -51723,35 +51818,35 @@ async function writeUploadedAttachment(args) {
|
|
|
51723
51818
|
);
|
|
51724
51819
|
}
|
|
51725
51820
|
const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
|
|
51726
|
-
const hashDir =
|
|
51821
|
+
const hashDir = import_node_path27.default.join(attachmentsRoot, attachmentId);
|
|
51727
51822
|
let finalFileName;
|
|
51728
51823
|
let hashDirExists = false;
|
|
51729
51824
|
try {
|
|
51730
|
-
hashDirExists =
|
|
51825
|
+
hashDirExists = import_node_fs28.default.statSync(hashDir).isDirectory();
|
|
51731
51826
|
} catch {
|
|
51732
51827
|
}
|
|
51733
51828
|
if (hashDirExists) {
|
|
51734
|
-
const existing =
|
|
51829
|
+
const existing = import_node_fs28.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
|
|
51735
51830
|
finalFileName = existing[0] ?? args.fileName;
|
|
51736
51831
|
try {
|
|
51737
|
-
|
|
51832
|
+
import_node_fs28.default.unlinkSync(tmpPath);
|
|
51738
51833
|
} catch {
|
|
51739
51834
|
}
|
|
51740
51835
|
} else {
|
|
51741
51836
|
try {
|
|
51742
|
-
|
|
51837
|
+
import_node_fs28.default.mkdirSync(hashDir, { recursive: true });
|
|
51743
51838
|
finalFileName = args.fileName;
|
|
51744
|
-
|
|
51839
|
+
import_node_fs28.default.renameSync(tmpPath, import_node_path27.default.join(hashDir, finalFileName));
|
|
51745
51840
|
} catch (err) {
|
|
51746
51841
|
try {
|
|
51747
|
-
|
|
51842
|
+
import_node_fs28.default.unlinkSync(tmpPath);
|
|
51748
51843
|
} catch {
|
|
51749
51844
|
}
|
|
51750
51845
|
throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
|
|
51751
51846
|
}
|
|
51752
51847
|
}
|
|
51753
|
-
const absPath =
|
|
51754
|
-
const relPath =
|
|
51848
|
+
const absPath = import_node_path27.default.join(hashDir, finalFileName);
|
|
51849
|
+
const relPath = import_node_path27.default.relative(args.sessionDir, absPath);
|
|
51755
51850
|
args.groupFileStore.upsert(args.scope, args.sessionId, {
|
|
51756
51851
|
relPath: absPath,
|
|
51757
51852
|
// 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
|
|
@@ -51765,7 +51860,7 @@ async function writeUploadedAttachment(args) {
|
|
|
51765
51860
|
|
|
51766
51861
|
// src/extension/import.ts
|
|
51767
51862
|
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
51768
|
-
var
|
|
51863
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
51769
51864
|
var import_node_os12 = __toESM(require("os"), 1);
|
|
51770
51865
|
var import_jszip = __toESM(require_lib3(), 1);
|
|
51771
51866
|
var ImportError = class extends Error {
|
|
@@ -51783,7 +51878,7 @@ async function importZip(buf, root) {
|
|
|
51783
51878
|
throw new ImportError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
51784
51879
|
}
|
|
51785
51880
|
for (const name of Object.keys(zip.files)) {
|
|
51786
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
51881
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path28.default.isAbsolute(name)) {
|
|
51787
51882
|
throw new ImportError("ZIP_INVALID", `unsafe zip entry path: ${name}`);
|
|
51788
51883
|
}
|
|
51789
51884
|
}
|
|
@@ -51816,7 +51911,7 @@ async function importZip(buf, root) {
|
|
|
51816
51911
|
);
|
|
51817
51912
|
}
|
|
51818
51913
|
}
|
|
51819
|
-
const destDir =
|
|
51914
|
+
const destDir = import_node_path28.default.join(root, manifest.id);
|
|
51820
51915
|
let destExists = false;
|
|
51821
51916
|
try {
|
|
51822
51917
|
await import_promises3.default.access(destDir);
|
|
@@ -51826,15 +51921,15 @@ async function importZip(buf, root) {
|
|
|
51826
51921
|
if (destExists) {
|
|
51827
51922
|
throw new ImportError("ALREADY_EXISTS", `extension ${manifest.id} already installed`);
|
|
51828
51923
|
}
|
|
51829
|
-
const stage = await import_promises3.default.mkdtemp(
|
|
51924
|
+
const stage = await import_promises3.default.mkdtemp(import_node_path28.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
|
|
51830
51925
|
try {
|
|
51831
51926
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
51832
|
-
const dest =
|
|
51927
|
+
const dest = import_node_path28.default.join(stage, name);
|
|
51833
51928
|
if (entry.dir) {
|
|
51834
51929
|
await import_promises3.default.mkdir(dest, { recursive: true });
|
|
51835
51930
|
continue;
|
|
51836
51931
|
}
|
|
51837
|
-
await import_promises3.default.mkdir(
|
|
51932
|
+
await import_promises3.default.mkdir(import_node_path28.default.dirname(dest), { recursive: true });
|
|
51838
51933
|
const content = await entry.async("nodebuffer");
|
|
51839
51934
|
await import_promises3.default.writeFile(dest, content);
|
|
51840
51935
|
}
|
|
@@ -51865,7 +51960,7 @@ var SHARE_UI_ASSET_MIME = {
|
|
|
51865
51960
|
".wasm": "application/wasm"
|
|
51866
51961
|
};
|
|
51867
51962
|
function shareUiAssetMime(filePath) {
|
|
51868
|
-
const ext =
|
|
51963
|
+
const ext = import_node_path29.default.extname(filePath).toLowerCase();
|
|
51869
51964
|
return SHARE_UI_ASSET_MIME[ext] ?? lookupMime(filePath);
|
|
51870
51965
|
}
|
|
51871
51966
|
var DISPATCH_HEARTBEAT_MS = 25e3;
|
|
@@ -51952,7 +52047,7 @@ function isValidUploadFileName(fileName) {
|
|
|
51952
52047
|
if (fileName === "." || fileName === "..") return false;
|
|
51953
52048
|
if (fileName.startsWith(".")) return false;
|
|
51954
52049
|
if (fileName.includes("/") || fileName.includes("\\")) return false;
|
|
51955
|
-
return fileName ===
|
|
52050
|
+
return fileName === import_node_path29.default.basename(fileName);
|
|
51956
52051
|
}
|
|
51957
52052
|
function createHttpRouter(deps) {
|
|
51958
52053
|
return async (req, res) => {
|
|
@@ -52024,7 +52119,7 @@ function createHttpRouter(deps) {
|
|
|
52024
52119
|
}
|
|
52025
52120
|
let buf;
|
|
52026
52121
|
try {
|
|
52027
|
-
buf = await
|
|
52122
|
+
buf = await import_node_fs29.default.promises.readFile(abs);
|
|
52028
52123
|
} catch {
|
|
52029
52124
|
sendJson(res, 404, { code: "NOT_FOUND", message: "asset not found" });
|
|
52030
52125
|
return true;
|
|
@@ -52248,7 +52343,7 @@ function createHttpRouter(deps) {
|
|
|
52248
52343
|
sendHtml(res, statusByCode[r.code], loader.renderErrorHtml(r.code, msgByCode[r.code]));
|
|
52249
52344
|
return true;
|
|
52250
52345
|
}
|
|
52251
|
-
sendHtml(res, 200, loader.renderViewerHtml(
|
|
52346
|
+
sendHtml(res, 200, loader.renderViewerHtml(import_node_path29.default.basename(r.absPath)));
|
|
52252
52347
|
return true;
|
|
52253
52348
|
}
|
|
52254
52349
|
const ctx = deps.authResolver.resolveFromHeader(
|
|
@@ -52369,7 +52464,7 @@ function createHttpRouter(deps) {
|
|
|
52369
52464
|
return true;
|
|
52370
52465
|
}
|
|
52371
52466
|
let absPath;
|
|
52372
|
-
if (
|
|
52467
|
+
if (import_node_path29.default.isAbsolute(pathParam)) {
|
|
52373
52468
|
absPath = pathParam;
|
|
52374
52469
|
} else if (deps.sessionStore) {
|
|
52375
52470
|
const file = deps.sessionStore.read(sid);
|
|
@@ -52377,7 +52472,7 @@ function createHttpRouter(deps) {
|
|
|
52377
52472
|
sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
|
|
52378
52473
|
return true;
|
|
52379
52474
|
}
|
|
52380
|
-
absPath =
|
|
52475
|
+
absPath = import_node_path29.default.join(file.cwd, pathParam);
|
|
52381
52476
|
} else {
|
|
52382
52477
|
sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
|
|
52383
52478
|
return true;
|
|
@@ -52464,7 +52559,7 @@ function withCtx(ctx, body) {
|
|
|
52464
52559
|
function streamFile(res, absPath, logger) {
|
|
52465
52560
|
let stat;
|
|
52466
52561
|
try {
|
|
52467
|
-
stat =
|
|
52562
|
+
stat = import_node_fs29.default.statSync(absPath);
|
|
52468
52563
|
} catch (err) {
|
|
52469
52564
|
const code = err?.code;
|
|
52470
52565
|
if (code === "ENOENT") {
|
|
@@ -52479,7 +52574,7 @@ function streamFile(res, absPath, logger) {
|
|
|
52479
52574
|
return;
|
|
52480
52575
|
}
|
|
52481
52576
|
const mime = lookupMime(absPath);
|
|
52482
|
-
const basename =
|
|
52577
|
+
const basename = import_node_path29.default.basename(absPath);
|
|
52483
52578
|
res.writeHead(200, {
|
|
52484
52579
|
"Content-Type": mime,
|
|
52485
52580
|
"Content-Length": String(stat.size),
|
|
@@ -52487,7 +52582,7 @@ function streamFile(res, absPath, logger) {
|
|
|
52487
52582
|
// 防止浏览器把任意 mime 当 html 渲染
|
|
52488
52583
|
"X-Content-Type-Options": "nosniff"
|
|
52489
52584
|
});
|
|
52490
|
-
const stream =
|
|
52585
|
+
const stream = import_node_fs29.default.createReadStream(absPath);
|
|
52491
52586
|
stream.on("error", (err) => {
|
|
52492
52587
|
logger?.warn("streamFile read error", { absPath, err: err.message });
|
|
52493
52588
|
res.destroy();
|
|
@@ -52496,8 +52591,8 @@ function streamFile(res, absPath, logger) {
|
|
|
52496
52591
|
}
|
|
52497
52592
|
|
|
52498
52593
|
// src/attachment/gc.ts
|
|
52499
|
-
var
|
|
52500
|
-
var
|
|
52594
|
+
var import_node_fs30 = __toESM(require("fs"), 1);
|
|
52595
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
52501
52596
|
var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
|
|
52502
52597
|
function runAttachmentGc(args) {
|
|
52503
52598
|
const now = (args.now ?? Date.now)();
|
|
@@ -52506,38 +52601,38 @@ function runAttachmentGc(args) {
|
|
|
52506
52601
|
for (const { scope, sessionId } of args.sessionScopes) {
|
|
52507
52602
|
for (const entry of args.groupFileStore.list(scope, sessionId)) {
|
|
52508
52603
|
if (entry.stale) continue;
|
|
52509
|
-
if (
|
|
52604
|
+
if (import_node_path30.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
|
|
52510
52605
|
}
|
|
52511
52606
|
}
|
|
52512
52607
|
for (const { scope, sessionId } of args.sessionScopes) {
|
|
52513
|
-
const sessionDir = args.getSessionCwd?.(sessionId) ??
|
|
52608
|
+
const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path30.default.join(
|
|
52514
52609
|
args.dataDir,
|
|
52515
52610
|
"sessions",
|
|
52516
52611
|
...scopeSubPath(scope).map(safeFileName),
|
|
52517
52612
|
safeFileName(sessionId)
|
|
52518
52613
|
);
|
|
52519
|
-
const attRoot =
|
|
52614
|
+
const attRoot = import_node_path30.default.join(sessionDir, ".attachments");
|
|
52520
52615
|
let hashDirs;
|
|
52521
52616
|
try {
|
|
52522
|
-
hashDirs =
|
|
52617
|
+
hashDirs = import_node_fs30.default.readdirSync(attRoot);
|
|
52523
52618
|
} catch (err) {
|
|
52524
52619
|
if (err.code === "ENOENT") continue;
|
|
52525
52620
|
args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
|
|
52526
52621
|
continue;
|
|
52527
52622
|
}
|
|
52528
52623
|
for (const hashDir of hashDirs) {
|
|
52529
|
-
const hashDirAbs =
|
|
52624
|
+
const hashDirAbs = import_node_path30.default.join(attRoot, hashDir);
|
|
52530
52625
|
let files;
|
|
52531
52626
|
try {
|
|
52532
|
-
files =
|
|
52627
|
+
files = import_node_fs30.default.readdirSync(hashDirAbs);
|
|
52533
52628
|
} catch {
|
|
52534
52629
|
continue;
|
|
52535
52630
|
}
|
|
52536
52631
|
for (const name of files) {
|
|
52537
|
-
const file =
|
|
52632
|
+
const file = import_node_path30.default.join(hashDirAbs, name);
|
|
52538
52633
|
let stat;
|
|
52539
52634
|
try {
|
|
52540
|
-
stat =
|
|
52635
|
+
stat = import_node_fs30.default.statSync(file);
|
|
52541
52636
|
} catch {
|
|
52542
52637
|
continue;
|
|
52543
52638
|
}
|
|
@@ -52546,26 +52641,26 @@ function runAttachmentGc(args) {
|
|
|
52546
52641
|
if (age < ttlMs) continue;
|
|
52547
52642
|
if (liveAbs.has(file)) continue;
|
|
52548
52643
|
try {
|
|
52549
|
-
|
|
52644
|
+
import_node_fs30.default.unlinkSync(file);
|
|
52550
52645
|
} catch (err) {
|
|
52551
52646
|
args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
|
|
52552
52647
|
}
|
|
52553
52648
|
}
|
|
52554
52649
|
try {
|
|
52555
|
-
if (
|
|
52650
|
+
if (import_node_fs30.default.readdirSync(hashDirAbs).length === 0) import_node_fs30.default.rmdirSync(hashDirAbs);
|
|
52556
52651
|
} catch {
|
|
52557
52652
|
}
|
|
52558
52653
|
}
|
|
52559
52654
|
try {
|
|
52560
|
-
if (
|
|
52655
|
+
if (import_node_fs30.default.readdirSync(attRoot).length === 0) import_node_fs30.default.rmdirSync(attRoot);
|
|
52561
52656
|
} catch {
|
|
52562
52657
|
}
|
|
52563
52658
|
}
|
|
52564
52659
|
}
|
|
52565
52660
|
|
|
52566
52661
|
// src/attachment/group.ts
|
|
52567
|
-
var
|
|
52568
|
-
var
|
|
52662
|
+
var import_node_fs31 = __toESM(require("fs"), 1);
|
|
52663
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
52569
52664
|
var import_node_crypto9 = __toESM(require("crypto"), 1);
|
|
52570
52665
|
init_protocol();
|
|
52571
52666
|
var GroupFileStore = class {
|
|
@@ -52577,11 +52672,11 @@ var GroupFileStore = class {
|
|
|
52577
52672
|
this.logger = opts.logger;
|
|
52578
52673
|
}
|
|
52579
52674
|
rootForScope(scope) {
|
|
52580
|
-
return
|
|
52675
|
+
return import_node_path31.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
|
|
52581
52676
|
}
|
|
52582
52677
|
/** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
|
|
52583
52678
|
filePath(scope, sessionId) {
|
|
52584
|
-
return
|
|
52679
|
+
return import_node_path31.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
|
|
52585
52680
|
}
|
|
52586
52681
|
cacheKey(scope, sessionId) {
|
|
52587
52682
|
return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
|
|
@@ -52590,7 +52685,7 @@ var GroupFileStore = class {
|
|
|
52590
52685
|
readFile(scope, sessionId) {
|
|
52591
52686
|
const file = this.filePath(scope, sessionId);
|
|
52592
52687
|
try {
|
|
52593
|
-
const raw =
|
|
52688
|
+
const raw = import_node_fs31.default.readFileSync(file, "utf8");
|
|
52594
52689
|
const parsed = JSON.parse(raw);
|
|
52595
52690
|
if (!Array.isArray(parsed)) {
|
|
52596
52691
|
this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
|
|
@@ -52616,10 +52711,10 @@ var GroupFileStore = class {
|
|
|
52616
52711
|
}
|
|
52617
52712
|
writeFile(scope, sessionId, entries) {
|
|
52618
52713
|
const file = this.filePath(scope, sessionId);
|
|
52619
|
-
|
|
52714
|
+
import_node_fs31.default.mkdirSync(import_node_path31.default.dirname(file), { recursive: true });
|
|
52620
52715
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
52621
|
-
|
|
52622
|
-
|
|
52716
|
+
import_node_fs31.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
|
|
52717
|
+
import_node_fs31.default.renameSync(tmp, file);
|
|
52623
52718
|
}
|
|
52624
52719
|
/** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
|
|
52625
52720
|
list(scope, sessionId) {
|
|
@@ -52705,10 +52800,10 @@ var GroupFileStore = class {
|
|
|
52705
52800
|
};
|
|
52706
52801
|
|
|
52707
52802
|
// src/discovery/state-file.ts
|
|
52708
|
-
var
|
|
52709
|
-
var
|
|
52803
|
+
var import_node_fs32 = __toESM(require("fs"), 1);
|
|
52804
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
52710
52805
|
function defaultStateFilePath(dataDir) {
|
|
52711
|
-
return
|
|
52806
|
+
return import_node_path32.default.join(dataDir, "state.json");
|
|
52712
52807
|
}
|
|
52713
52808
|
function isPidAlive(pid) {
|
|
52714
52809
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
@@ -52730,7 +52825,7 @@ var StateFileManager = class {
|
|
|
52730
52825
|
}
|
|
52731
52826
|
read() {
|
|
52732
52827
|
try {
|
|
52733
|
-
const raw =
|
|
52828
|
+
const raw = import_node_fs32.default.readFileSync(this.file, "utf8");
|
|
52734
52829
|
const parsed = JSON.parse(raw);
|
|
52735
52830
|
return parsed;
|
|
52736
52831
|
} catch {
|
|
@@ -52744,20 +52839,20 @@ var StateFileManager = class {
|
|
|
52744
52839
|
return { status: "stale", existing };
|
|
52745
52840
|
}
|
|
52746
52841
|
write(state) {
|
|
52747
|
-
|
|
52842
|
+
import_node_fs32.default.mkdirSync(import_node_path32.default.dirname(this.file), { recursive: true });
|
|
52748
52843
|
const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
|
|
52749
|
-
|
|
52750
|
-
|
|
52844
|
+
import_node_fs32.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
|
|
52845
|
+
import_node_fs32.default.renameSync(tmp, this.file);
|
|
52751
52846
|
if (process.platform !== "win32") {
|
|
52752
52847
|
try {
|
|
52753
|
-
|
|
52848
|
+
import_node_fs32.default.chmodSync(this.file, 384);
|
|
52754
52849
|
} catch {
|
|
52755
52850
|
}
|
|
52756
52851
|
}
|
|
52757
52852
|
}
|
|
52758
52853
|
delete() {
|
|
52759
52854
|
try {
|
|
52760
|
-
|
|
52855
|
+
import_node_fs32.default.unlinkSync(this.file);
|
|
52761
52856
|
} catch {
|
|
52762
52857
|
}
|
|
52763
52858
|
}
|
|
@@ -52771,14 +52866,14 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
|
|
|
52771
52866
|
}
|
|
52772
52867
|
|
|
52773
52868
|
// src/tunnel/tunnel-manager.ts
|
|
52774
|
-
var
|
|
52775
|
-
var
|
|
52869
|
+
var import_node_fs36 = __toESM(require("fs"), 1);
|
|
52870
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
52776
52871
|
var import_node_crypto10 = __toESM(require("crypto"), 1);
|
|
52777
52872
|
var import_node_child_process9 = require("child_process");
|
|
52778
52873
|
|
|
52779
52874
|
// src/tunnel/tunnel-store.ts
|
|
52780
|
-
var
|
|
52781
|
-
var
|
|
52875
|
+
var import_node_fs33 = __toESM(require("fs"), 1);
|
|
52876
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
52782
52877
|
var TunnelStore = class {
|
|
52783
52878
|
constructor(filePath) {
|
|
52784
52879
|
this.filePath = filePath;
|
|
@@ -52786,7 +52881,7 @@ var TunnelStore = class {
|
|
|
52786
52881
|
filePath;
|
|
52787
52882
|
async get() {
|
|
52788
52883
|
try {
|
|
52789
|
-
const raw = await
|
|
52884
|
+
const raw = await import_node_fs33.default.promises.readFile(this.filePath, "utf8");
|
|
52790
52885
|
const obj = JSON.parse(raw);
|
|
52791
52886
|
if (!isPersistedTunnel(obj)) return null;
|
|
52792
52887
|
return obj;
|
|
@@ -52797,22 +52892,22 @@ var TunnelStore = class {
|
|
|
52797
52892
|
}
|
|
52798
52893
|
}
|
|
52799
52894
|
async set(v2) {
|
|
52800
|
-
const dir =
|
|
52801
|
-
await
|
|
52895
|
+
const dir = import_node_path33.default.dirname(this.filePath);
|
|
52896
|
+
await import_node_fs33.default.promises.mkdir(dir, { recursive: true });
|
|
52802
52897
|
const data = JSON.stringify(v2, null, 2);
|
|
52803
52898
|
const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
52804
|
-
await
|
|
52899
|
+
await import_node_fs33.default.promises.writeFile(tmp, data, { mode: 384 });
|
|
52805
52900
|
if (process.platform !== "win32") {
|
|
52806
52901
|
try {
|
|
52807
|
-
await
|
|
52902
|
+
await import_node_fs33.default.promises.chmod(tmp, 384);
|
|
52808
52903
|
} catch {
|
|
52809
52904
|
}
|
|
52810
52905
|
}
|
|
52811
|
-
await
|
|
52906
|
+
await import_node_fs33.default.promises.rename(tmp, this.filePath);
|
|
52812
52907
|
}
|
|
52813
52908
|
async clear() {
|
|
52814
52909
|
try {
|
|
52815
|
-
await
|
|
52910
|
+
await import_node_fs33.default.promises.unlink(this.filePath);
|
|
52816
52911
|
} catch (err) {
|
|
52817
52912
|
const code = err?.code;
|
|
52818
52913
|
if (code !== "ENOENT") throw err;
|
|
@@ -52907,9 +53002,9 @@ function escape(v2) {
|
|
|
52907
53002
|
}
|
|
52908
53003
|
|
|
52909
53004
|
// src/tunnel/frpc-binary.ts
|
|
52910
|
-
var
|
|
53005
|
+
var import_node_fs34 = __toESM(require("fs"), 1);
|
|
52911
53006
|
var import_node_os13 = __toESM(require("os"), 1);
|
|
52912
|
-
var
|
|
53007
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
52913
53008
|
var import_node_child_process7 = require("child_process");
|
|
52914
53009
|
var import_node_stream3 = require("stream");
|
|
52915
53010
|
var import_promises4 = require("stream/promises");
|
|
@@ -52941,20 +53036,20 @@ function frpcDownloadUrl(version2, p2) {
|
|
|
52941
53036
|
}
|
|
52942
53037
|
async function ensureFrpcBinary(opts) {
|
|
52943
53038
|
if (opts.override) {
|
|
52944
|
-
if (!
|
|
53039
|
+
if (!import_node_fs34.default.existsSync(opts.override)) {
|
|
52945
53040
|
throw new Error(`frpc binary not found at override path: ${opts.override}`);
|
|
52946
53041
|
}
|
|
52947
53042
|
return opts.override;
|
|
52948
53043
|
}
|
|
52949
53044
|
const version2 = opts.version ?? FRPC_VERSION;
|
|
52950
53045
|
const platform = opts.platform ?? detectPlatform();
|
|
52951
|
-
const binDir =
|
|
52952
|
-
|
|
53046
|
+
const binDir = import_node_path34.default.join(opts.dataDir, "bin");
|
|
53047
|
+
import_node_fs34.default.mkdirSync(binDir, { recursive: true });
|
|
52953
53048
|
cleanupStaleArtifacts(binDir);
|
|
52954
|
-
const stableBin =
|
|
52955
|
-
if (
|
|
53049
|
+
const stableBin = import_node_path34.default.join(binDir, "frpc");
|
|
53050
|
+
if (import_node_fs34.default.existsSync(stableBin)) return stableBin;
|
|
52956
53051
|
const partialBin = `${stableBin}.partial`;
|
|
52957
|
-
const tarballPath =
|
|
53052
|
+
const tarballPath = import_node_path34.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
|
|
52958
53053
|
try {
|
|
52959
53054
|
const url = frpcDownloadUrl(version2, platform);
|
|
52960
53055
|
await downloadToFile(url, tarballPath, opts.fetchImpl);
|
|
@@ -52963,8 +53058,8 @@ async function ensureFrpcBinary(opts) {
|
|
|
52963
53058
|
} else {
|
|
52964
53059
|
await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
|
|
52965
53060
|
}
|
|
52966
|
-
|
|
52967
|
-
|
|
53061
|
+
import_node_fs34.default.chmodSync(partialBin, 493);
|
|
53062
|
+
import_node_fs34.default.renameSync(partialBin, stableBin);
|
|
52968
53063
|
} finally {
|
|
52969
53064
|
safeUnlink(tarballPath);
|
|
52970
53065
|
safeUnlink(partialBin);
|
|
@@ -52974,15 +53069,15 @@ async function ensureFrpcBinary(opts) {
|
|
|
52974
53069
|
function cleanupStaleArtifacts(binDir) {
|
|
52975
53070
|
let entries;
|
|
52976
53071
|
try {
|
|
52977
|
-
entries =
|
|
53072
|
+
entries = import_node_fs34.default.readdirSync(binDir);
|
|
52978
53073
|
} catch {
|
|
52979
53074
|
return;
|
|
52980
53075
|
}
|
|
52981
53076
|
for (const name of entries) {
|
|
52982
53077
|
if (name.endsWith(".partial") || name.startsWith("extract-")) {
|
|
52983
|
-
const full =
|
|
53078
|
+
const full = import_node_path34.default.join(binDir, name);
|
|
52984
53079
|
try {
|
|
52985
|
-
|
|
53080
|
+
import_node_fs34.default.rmSync(full, { recursive: true, force: true });
|
|
52986
53081
|
} catch {
|
|
52987
53082
|
}
|
|
52988
53083
|
}
|
|
@@ -52990,7 +53085,7 @@ function cleanupStaleArtifacts(binDir) {
|
|
|
52990
53085
|
}
|
|
52991
53086
|
function safeUnlink(p2) {
|
|
52992
53087
|
try {
|
|
52993
|
-
|
|
53088
|
+
import_node_fs34.default.unlinkSync(p2);
|
|
52994
53089
|
} catch {
|
|
52995
53090
|
}
|
|
52996
53091
|
}
|
|
@@ -53001,13 +53096,13 @@ async function downloadToFile(url, dest, fetchImpl) {
|
|
|
53001
53096
|
if (!res.ok || !res.body) {
|
|
53002
53097
|
throw new Error(`download failed: ${res.status} ${res.statusText}`);
|
|
53003
53098
|
}
|
|
53004
|
-
const out =
|
|
53099
|
+
const out = import_node_fs34.default.createWriteStream(dest);
|
|
53005
53100
|
const nodeStream = import_node_stream3.Readable.fromWeb(res.body);
|
|
53006
53101
|
await (0, import_promises4.pipeline)(nodeStream, out);
|
|
53007
53102
|
}
|
|
53008
53103
|
async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
|
|
53009
|
-
const work =
|
|
53010
|
-
|
|
53104
|
+
const work = import_node_path34.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
|
|
53105
|
+
import_node_fs34.default.mkdirSync(work, { recursive: true });
|
|
53011
53106
|
try {
|
|
53012
53107
|
await new Promise((resolve6, reject) => {
|
|
53013
53108
|
const proc = (0, import_node_child_process7.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
|
|
@@ -53015,32 +53110,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
|
|
|
53015
53110
|
proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
|
|
53016
53111
|
});
|
|
53017
53112
|
const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
|
|
53018
|
-
const src =
|
|
53019
|
-
if (!
|
|
53113
|
+
const src = import_node_path34.default.join(work, dirName, "frpc");
|
|
53114
|
+
if (!import_node_fs34.default.existsSync(src)) {
|
|
53020
53115
|
throw new Error(`frpc not found inside tarball at ${src}`);
|
|
53021
53116
|
}
|
|
53022
|
-
|
|
53117
|
+
import_node_fs34.default.copyFileSync(src, destBin);
|
|
53023
53118
|
} finally {
|
|
53024
|
-
|
|
53119
|
+
import_node_fs34.default.rmSync(work, { recursive: true, force: true });
|
|
53025
53120
|
}
|
|
53026
53121
|
}
|
|
53027
53122
|
|
|
53028
53123
|
// src/tunnel/frpc-process.ts
|
|
53029
|
-
var
|
|
53030
|
-
var
|
|
53124
|
+
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
53125
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
53031
53126
|
var import_node_child_process8 = require("child_process");
|
|
53032
53127
|
function frpcPidFilePath(dataDir) {
|
|
53033
|
-
return
|
|
53128
|
+
return import_node_path35.default.join(dataDir, "frpc.pid");
|
|
53034
53129
|
}
|
|
53035
53130
|
function writeFrpcPid(dataDir, pid) {
|
|
53036
53131
|
try {
|
|
53037
|
-
|
|
53132
|
+
import_node_fs35.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
|
|
53038
53133
|
} catch {
|
|
53039
53134
|
}
|
|
53040
53135
|
}
|
|
53041
53136
|
function clearFrpcPid(dataDir) {
|
|
53042
53137
|
try {
|
|
53043
|
-
|
|
53138
|
+
import_node_fs35.default.unlinkSync(frpcPidFilePath(dataDir));
|
|
53044
53139
|
} catch {
|
|
53045
53140
|
}
|
|
53046
53141
|
}
|
|
@@ -53056,7 +53151,7 @@ function defaultIsPidAlive(pid) {
|
|
|
53056
53151
|
}
|
|
53057
53152
|
function defaultReadPidFile(file) {
|
|
53058
53153
|
try {
|
|
53059
|
-
return
|
|
53154
|
+
return import_node_fs35.default.readFileSync(file, "utf8");
|
|
53060
53155
|
} catch {
|
|
53061
53156
|
return null;
|
|
53062
53157
|
}
|
|
@@ -53072,7 +53167,7 @@ function defaultSleep(ms) {
|
|
|
53072
53167
|
}
|
|
53073
53168
|
async function killStaleFrpc(deps) {
|
|
53074
53169
|
const pidFile = frpcPidFilePath(deps.dataDir);
|
|
53075
|
-
const tomlPath =
|
|
53170
|
+
const tomlPath = import_node_path35.default.join(deps.dataDir, "frpc.toml");
|
|
53076
53171
|
const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
|
|
53077
53172
|
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
|
|
53078
53173
|
const killPid = deps.killPidImpl ?? defaultKillPid;
|
|
@@ -53096,7 +53191,7 @@ async function killStaleFrpc(deps) {
|
|
|
53096
53191
|
}
|
|
53097
53192
|
if (victims.size === 0) {
|
|
53098
53193
|
try {
|
|
53099
|
-
|
|
53194
|
+
import_node_fs35.default.unlinkSync(pidFile);
|
|
53100
53195
|
} catch {
|
|
53101
53196
|
}
|
|
53102
53197
|
return;
|
|
@@ -53107,7 +53202,7 @@ async function killStaleFrpc(deps) {
|
|
|
53107
53202
|
}
|
|
53108
53203
|
await sleep2(deps.reapWaitMs ?? 300);
|
|
53109
53204
|
try {
|
|
53110
|
-
|
|
53205
|
+
import_node_fs35.default.unlinkSync(pidFile);
|
|
53111
53206
|
} catch {
|
|
53112
53207
|
}
|
|
53113
53208
|
}
|
|
@@ -53144,7 +53239,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
53144
53239
|
var TunnelManager = class {
|
|
53145
53240
|
constructor(deps) {
|
|
53146
53241
|
this.deps = deps;
|
|
53147
|
-
this.store = deps.store ?? new TunnelStore(
|
|
53242
|
+
this.store = deps.store ?? new TunnelStore(import_node_path36.default.join(deps.dataDir, "tunnel.json"));
|
|
53148
53243
|
this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
|
|
53149
53244
|
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
53150
53245
|
}
|
|
@@ -53271,7 +53366,7 @@ var TunnelManager = class {
|
|
|
53271
53366
|
dataDir: this.deps.dataDir,
|
|
53272
53367
|
override: this.deps.frpcBinaryOverride ?? void 0
|
|
53273
53368
|
});
|
|
53274
|
-
const tomlPath =
|
|
53369
|
+
const tomlPath = import_node_path36.default.join(this.deps.dataDir, "frpc.toml");
|
|
53275
53370
|
const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto10.default.randomBytes(3).toString("hex")}`;
|
|
53276
53371
|
const toml = buildFrpcToml({
|
|
53277
53372
|
serverAddr: t.frpsHost,
|
|
@@ -53282,14 +53377,14 @@ var TunnelManager = class {
|
|
|
53282
53377
|
localPort,
|
|
53283
53378
|
logLevel: "info"
|
|
53284
53379
|
});
|
|
53285
|
-
await
|
|
53380
|
+
await import_node_fs36.default.promises.writeFile(tomlPath, toml, { mode: 384 });
|
|
53286
53381
|
const proc = (this.deps.spawnImpl ?? import_node_child_process9.spawn)(frpcBin, ["-c", tomlPath], {
|
|
53287
53382
|
stdio: ["ignore", "pipe", "pipe"]
|
|
53288
53383
|
});
|
|
53289
|
-
const logDir =
|
|
53290
|
-
|
|
53291
|
-
const logFilePath =
|
|
53292
|
-
const logStream =
|
|
53384
|
+
const logDir = import_node_path36.default.join(this.deps.dataDir, "log");
|
|
53385
|
+
import_node_fs36.default.mkdirSync(logDir, { recursive: true });
|
|
53386
|
+
const logFilePath = import_node_path36.default.join(logDir, "frpc.log");
|
|
53387
|
+
const logStream = import_node_fs36.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
|
|
53293
53388
|
logStream.on("error", () => {
|
|
53294
53389
|
});
|
|
53295
53390
|
const tee = (chunk) => {
|
|
@@ -53371,8 +53466,8 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
53371
53466
|
}
|
|
53372
53467
|
|
|
53373
53468
|
// src/sshd/sshd-manager.ts
|
|
53374
|
-
var
|
|
53375
|
-
var
|
|
53469
|
+
var import_node_fs40 = __toESM(require("fs"), 1);
|
|
53470
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
53376
53471
|
var import_node_child_process11 = require("child_process");
|
|
53377
53472
|
|
|
53378
53473
|
// src/sshd/sshd-config.ts
|
|
@@ -53397,23 +53492,23 @@ function buildSshdConfig(input) {
|
|
|
53397
53492
|
}
|
|
53398
53493
|
|
|
53399
53494
|
// src/sshd/sshd-process.ts
|
|
53400
|
-
var
|
|
53401
|
-
var
|
|
53495
|
+
var import_node_fs37 = __toESM(require("fs"), 1);
|
|
53496
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
53402
53497
|
var import_node_child_process10 = require("child_process");
|
|
53403
53498
|
function sshdPidFilePath(dataDir) {
|
|
53404
|
-
return
|
|
53499
|
+
return import_node_path37.default.join(dataDir, "sshd", "sshd.pid");
|
|
53405
53500
|
}
|
|
53406
53501
|
function writeSshdPid(dataDir, pid) {
|
|
53407
53502
|
try {
|
|
53408
53503
|
const p2 = sshdPidFilePath(dataDir);
|
|
53409
|
-
|
|
53410
|
-
|
|
53504
|
+
import_node_fs37.default.mkdirSync(import_node_path37.default.dirname(p2), { recursive: true, mode: 448 });
|
|
53505
|
+
import_node_fs37.default.writeFileSync(p2, String(pid), { mode: 384 });
|
|
53411
53506
|
} catch {
|
|
53412
53507
|
}
|
|
53413
53508
|
}
|
|
53414
53509
|
function clearSshdPid(dataDir) {
|
|
53415
53510
|
try {
|
|
53416
|
-
|
|
53511
|
+
import_node_fs37.default.unlinkSync(sshdPidFilePath(dataDir));
|
|
53417
53512
|
} catch {
|
|
53418
53513
|
}
|
|
53419
53514
|
}
|
|
@@ -53429,7 +53524,7 @@ function defaultIsPidAlive2(pid) {
|
|
|
53429
53524
|
}
|
|
53430
53525
|
function defaultReadPidFile2(file) {
|
|
53431
53526
|
try {
|
|
53432
|
-
return
|
|
53527
|
+
return import_node_fs37.default.readFileSync(file, "utf8");
|
|
53433
53528
|
} catch {
|
|
53434
53529
|
return null;
|
|
53435
53530
|
}
|
|
@@ -53445,7 +53540,7 @@ function defaultSleep2(ms) {
|
|
|
53445
53540
|
}
|
|
53446
53541
|
async function killStaleSshd(deps) {
|
|
53447
53542
|
const pidFile = sshdPidFilePath(deps.dataDir);
|
|
53448
|
-
const configPath =
|
|
53543
|
+
const configPath = import_node_path37.default.join(deps.dataDir, "sshd", "sshd_config");
|
|
53449
53544
|
const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile2;
|
|
53450
53545
|
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive2;
|
|
53451
53546
|
const killPid = deps.killPidImpl ?? defaultKillPid2;
|
|
@@ -53469,7 +53564,7 @@ async function killStaleSshd(deps) {
|
|
|
53469
53564
|
}
|
|
53470
53565
|
if (victims.size === 0) {
|
|
53471
53566
|
try {
|
|
53472
|
-
|
|
53567
|
+
import_node_fs37.default.unlinkSync(pidFile);
|
|
53473
53568
|
} catch {
|
|
53474
53569
|
}
|
|
53475
53570
|
return;
|
|
@@ -53480,7 +53575,7 @@ async function killStaleSshd(deps) {
|
|
|
53480
53575
|
}
|
|
53481
53576
|
await sleep2(deps.reapWaitMs ?? 300);
|
|
53482
53577
|
try {
|
|
53483
|
-
|
|
53578
|
+
import_node_fs37.default.unlinkSync(pidFile);
|
|
53484
53579
|
} catch {
|
|
53485
53580
|
}
|
|
53486
53581
|
}
|
|
@@ -53513,8 +53608,8 @@ async function defaultScanSshdPidsByCmdline(configPath, logger) {
|
|
|
53513
53608
|
}
|
|
53514
53609
|
|
|
53515
53610
|
// src/sshd/jail-script.ts
|
|
53516
|
-
var
|
|
53517
|
-
var
|
|
53611
|
+
var import_node_fs38 = __toESM(require("fs"), 1);
|
|
53612
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
53518
53613
|
var CLAWD_SSH_JAIL_SCRIPT = String.raw`#!/usr/bin/env bash
|
|
53519
53614
|
# clawd-ssh-jail — SSH reverse access sandbox wrapper (managed by clawd; do not edit)
|
|
53520
53615
|
#
|
|
@@ -53523,7 +53618,7 @@ var CLAWD_SSH_JAIL_SCRIPT = String.raw`#!/usr/bin/env bash
|
|
|
53523
53618
|
# 真实请求(interactive shell 时为空)。
|
|
53524
53619
|
#
|
|
53525
53620
|
# 职责:
|
|
53526
|
-
# 1. 读 ~/.clawd/contacts.json 校验 contact.
|
|
53621
|
+
# 1. 读 ~/.clawd/contacts.json 校验 contact.remoteAccessAllowed(不通过即拒)
|
|
53527
53622
|
# 2. 直接 exec shell(去沙箱后无目录级限制)
|
|
53528
53623
|
|
|
53529
53624
|
set -euo pipefail
|
|
@@ -53551,13 +53646,13 @@ log_line INFO jail.entered "clawd-ssh-jail 起来 device=${"${DEVICE_ID}"} cmd=$
|
|
|
53551
53646
|
|
|
53552
53647
|
CONTACTS="${"${HOME}"}/.clawd/contacts.json"
|
|
53553
53648
|
if [ ! -f "$CONTACTS" ]; then
|
|
53554
|
-
log_line ERROR jail.entered "contacts.json 不存在,无法校验
|
|
53649
|
+
log_line ERROR jail.entered "contacts.json 不存在,无法校验 remoteAccessAllowed"
|
|
53555
53650
|
echo "clawd-ssh-jail: contacts.json missing" >&2
|
|
53556
53651
|
exit 1
|
|
53557
53652
|
fi
|
|
53558
53653
|
|
|
53559
|
-
# 校验该 contact 的
|
|
53560
|
-
#
|
|
53654
|
+
# 校验该 contact 的 remoteAccessAllowed(mac/linux 都自带 python3)。去沙箱后不再读目录白名单:
|
|
53655
|
+
# remoteAccessAllowed=false → DENIED exit 2;deviceId 未命中 → NOT_FOUND exit 3;命中且允许 → exit 0。
|
|
53561
53656
|
# set -e 下 python 非 0 退出即终止脚本(不进 exec),只有 exit 0 才继续 exec shell。
|
|
53562
53657
|
python3 -c "
|
|
53563
53658
|
import json, sys
|
|
@@ -53565,13 +53660,13 @@ with open('$CONTACTS') as f:
|
|
|
53565
53660
|
data = json.load(f)
|
|
53566
53661
|
for c in data.get('contacts', []):
|
|
53567
53662
|
if c.get('deviceId') == '$DEVICE_ID':
|
|
53568
|
-
if not c.get('
|
|
53663
|
+
if not c.get('remoteAccessAllowed'):
|
|
53569
53664
|
print('DENIED', file=sys.stderr); sys.exit(2)
|
|
53570
53665
|
sys.exit(0)
|
|
53571
53666
|
print('NOT_FOUND', file=sys.stderr); sys.exit(3)
|
|
53572
53667
|
"
|
|
53573
53668
|
|
|
53574
|
-
log_line INFO jail.entered "
|
|
53669
|
+
log_line INFO jail.entered "remoteAccessAllowed 校验通过,放行 exec shell"
|
|
53575
53670
|
|
|
53576
53671
|
CMD="${"${SSH_ORIGINAL_COMMAND:-}"}"
|
|
53577
53672
|
if [ -z "$CMD" ]; then
|
|
@@ -53582,21 +53677,21 @@ fi
|
|
|
53582
53677
|
|
|
53583
53678
|
# 去掉 OS 沙箱层(原本 macOS Seatbelt 策略 / Linux 命名空间隔离):SSH 进来即该用户
|
|
53584
53679
|
# 权限内的 shell,直接 exec。沙箱只限制文件、拦不住网络/进程,隔离价值有限,且相关 API
|
|
53585
|
-
# 在 macOS 已 deprecated。授权即完全信任——
|
|
53680
|
+
# 在 macOS 已 deprecated。授权即完全信任——remoteAccessAllowed 授权开关 + authorized_keys 即时
|
|
53586
53681
|
# 踢行是唯一访问闸。要加回沙箱:从 git 历史(T-29 之前)恢复本段的 OS 分支即可。
|
|
53587
53682
|
exec "${"${SHELL_CMD[@]}"}"
|
|
53588
53683
|
`;
|
|
53589
53684
|
function ensureJailScript(dataDir) {
|
|
53590
|
-
const binDir =
|
|
53591
|
-
|
|
53592
|
-
const target =
|
|
53593
|
-
|
|
53685
|
+
const binDir = import_node_path38.default.join(dataDir, "bin");
|
|
53686
|
+
import_node_fs38.default.mkdirSync(binDir, { recursive: true, mode: 493 });
|
|
53687
|
+
const target = import_node_path38.default.join(binDir, "clawd-ssh-jail");
|
|
53688
|
+
import_node_fs38.default.writeFileSync(target, CLAWD_SSH_JAIL_SCRIPT, { mode: 493 });
|
|
53594
53689
|
return target;
|
|
53595
53690
|
}
|
|
53596
53691
|
|
|
53597
53692
|
// src/sshd/clawd-shim.ts
|
|
53598
|
-
var
|
|
53599
|
-
var
|
|
53693
|
+
var import_node_fs39 = __toESM(require("fs"), 1);
|
|
53694
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
53600
53695
|
var DEFAULT_FALLBACK_APPS = ["/Applications/Clawd.app", "$HOME/Applications/Clawd.app"];
|
|
53601
53696
|
function shellQuote(s) {
|
|
53602
53697
|
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
@@ -53640,10 +53735,10 @@ exec "$EXEC" "$CLI" "$@"
|
|
|
53640
53735
|
`;
|
|
53641
53736
|
}
|
|
53642
53737
|
function ensureClawdShim(dataDir, execPath, cliPath) {
|
|
53643
|
-
const binDir =
|
|
53644
|
-
|
|
53645
|
-
const target =
|
|
53646
|
-
|
|
53738
|
+
const binDir = import_node_path39.default.join(dataDir, "bin");
|
|
53739
|
+
import_node_fs39.default.mkdirSync(binDir, { recursive: true, mode: 493 });
|
|
53740
|
+
const target = import_node_path39.default.join(binDir, "clawd");
|
|
53741
|
+
import_node_fs39.default.writeFileSync(target, buildClawdShim(execPath, cliPath), { mode: 493 });
|
|
53647
53742
|
return target;
|
|
53648
53743
|
}
|
|
53649
53744
|
|
|
@@ -53651,7 +53746,7 @@ function ensureClawdShim(dataDir, execPath, cliPath) {
|
|
|
53651
53746
|
var SshdManager = class {
|
|
53652
53747
|
constructor(deps) {
|
|
53653
53748
|
this.deps = deps;
|
|
53654
|
-
this.sshdDir =
|
|
53749
|
+
this.sshdDir = import_node_path40.default.join(deps.dataDir, "sshd");
|
|
53655
53750
|
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
53656
53751
|
}
|
|
53657
53752
|
deps;
|
|
@@ -53672,34 +53767,34 @@ var SshdManager = class {
|
|
|
53672
53767
|
ownPid: process.pid,
|
|
53673
53768
|
logger
|
|
53674
53769
|
});
|
|
53675
|
-
|
|
53676
|
-
|
|
53770
|
+
import_node_fs40.default.mkdirSync(this.sshdDir, { recursive: true, mode: 448 });
|
|
53771
|
+
import_node_fs40.default.mkdirSync(import_node_path40.default.join(this.sshdDir, "authorized_keys.d"), { recursive: true, mode: 448 });
|
|
53677
53772
|
ensureJailScript(this.deps.dataDir);
|
|
53678
53773
|
ensureClawdShim(this.deps.dataDir, process.execPath, process.argv[1] ?? "");
|
|
53679
|
-
const hostKeyPath =
|
|
53680
|
-
if (!
|
|
53774
|
+
const hostKeyPath = import_node_path40.default.join(this.sshdDir, "host_key");
|
|
53775
|
+
if (!import_node_fs40.default.existsSync(hostKeyPath)) {
|
|
53681
53776
|
await this.generateHostKey(hostKeyPath);
|
|
53682
53777
|
}
|
|
53683
|
-
const akFile =
|
|
53684
|
-
if (!
|
|
53685
|
-
|
|
53778
|
+
const akFile = import_node_path40.default.join(this.sshdDir, "authorized_keys.d", "clawd-contacts");
|
|
53779
|
+
if (!import_node_fs40.default.existsSync(akFile)) {
|
|
53780
|
+
import_node_fs40.default.writeFileSync(akFile, "", { mode: 384 });
|
|
53686
53781
|
}
|
|
53687
|
-
const configPath =
|
|
53782
|
+
const configPath = import_node_path40.default.join(this.sshdDir, "sshd_config");
|
|
53688
53783
|
const config = buildSshdConfig({
|
|
53689
53784
|
listenAddress: "127.0.0.1",
|
|
53690
53785
|
port: this.deps.port,
|
|
53691
53786
|
hostKeyPath,
|
|
53692
53787
|
authorizedKeysFile: akFile,
|
|
53693
|
-
pidFilePath:
|
|
53788
|
+
pidFilePath: import_node_path40.default.join(this.sshdDir, "sshd.pid")
|
|
53694
53789
|
});
|
|
53695
|
-
|
|
53790
|
+
import_node_fs40.default.writeFileSync(configPath, config, { mode: 384 });
|
|
53696
53791
|
const sshdBin = this.deps.sshdBin ?? "/usr/sbin/sshd";
|
|
53697
53792
|
const proc = (this.deps.spawnImpl ?? import_node_child_process11.spawn)(sshdBin, ["-D", "-e", "-f", configPath], {
|
|
53698
53793
|
stdio: ["ignore", "pipe", "pipe"]
|
|
53699
53794
|
});
|
|
53700
|
-
const sshdLogDir =
|
|
53701
|
-
|
|
53702
|
-
const logStream =
|
|
53795
|
+
const sshdLogDir = import_node_path40.default.join(this.deps.dataDir, "log");
|
|
53796
|
+
import_node_fs40.default.mkdirSync(sshdLogDir, { recursive: true });
|
|
53797
|
+
const logStream = import_node_fs40.default.createWriteStream(import_node_path40.default.join(sshdLogDir, "sshd.log"), {
|
|
53703
53798
|
flags: "a",
|
|
53704
53799
|
mode: 384
|
|
53705
53800
|
});
|
|
@@ -53796,7 +53891,7 @@ ${tail}` : ready.error;
|
|
|
53796
53891
|
p2.on("error", reject);
|
|
53797
53892
|
});
|
|
53798
53893
|
try {
|
|
53799
|
-
|
|
53894
|
+
import_node_fs40.default.chmodSync(hostKeyPath, 384);
|
|
53800
53895
|
} catch {
|
|
53801
53896
|
}
|
|
53802
53897
|
}
|
|
@@ -53868,20 +53963,20 @@ async function waitForSshdReady(proc, timeoutMs) {
|
|
|
53868
53963
|
}
|
|
53869
53964
|
|
|
53870
53965
|
// src/sshd/authorized-keys.ts
|
|
53871
|
-
var
|
|
53872
|
-
var
|
|
53966
|
+
var import_node_fs41 = __toESM(require("fs"), 1);
|
|
53967
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
53873
53968
|
var JAIL_BIN_PATH_ENV = "CLAWD_JAIL_BIN_PATH";
|
|
53874
53969
|
var AUTHORIZED_KEYS_FILE = "clawd-contacts";
|
|
53875
53970
|
function jailBinPath() {
|
|
53876
|
-
return process.env[JAIL_BIN_PATH_ENV] ??
|
|
53971
|
+
return process.env[JAIL_BIN_PATH_ENV] ?? import_node_path41.default.join(process.env.HOME ?? "", ".clawd", "bin", "clawd-ssh-jail");
|
|
53877
53972
|
}
|
|
53878
53973
|
function rebuildAuthorizedKeys(store, sshdDir) {
|
|
53879
|
-
const akDir =
|
|
53880
|
-
const target =
|
|
53881
|
-
|
|
53974
|
+
const akDir = import_node_path41.default.join(sshdDir, "authorized_keys.d");
|
|
53975
|
+
const target = import_node_path41.default.join(akDir, AUTHORIZED_KEYS_FILE);
|
|
53976
|
+
import_node_fs41.default.mkdirSync(akDir, { recursive: true, mode: 448 });
|
|
53882
53977
|
const lines = ["# managed by clawd; do not edit", ""];
|
|
53883
53978
|
for (const c of store.list()) {
|
|
53884
|
-
if (!c.
|
|
53979
|
+
if (!c.remoteAccessAllowed) continue;
|
|
53885
53980
|
const safe = /^[A-Za-z0-9_.-]+$/.test(c.deviceId);
|
|
53886
53981
|
if (!safe) continue;
|
|
53887
53982
|
const pubkey = c.sshPublicKey?.trim();
|
|
@@ -53892,25 +53987,25 @@ function rebuildAuthorizedKeys(store, sshdDir) {
|
|
|
53892
53987
|
}
|
|
53893
53988
|
const body = lines.join("\n") + "\n";
|
|
53894
53989
|
const tmp = `${target}.tmp-${process.pid}-${Date.now()}`;
|
|
53895
|
-
|
|
53896
|
-
|
|
53990
|
+
import_node_fs41.default.writeFileSync(tmp, body, { mode: 384 });
|
|
53991
|
+
import_node_fs41.default.renameSync(tmp, target);
|
|
53897
53992
|
}
|
|
53898
53993
|
|
|
53899
53994
|
// src/sshd/ssh-identity.ts
|
|
53900
|
-
var
|
|
53901
|
-
var
|
|
53995
|
+
var import_node_fs42 = __toESM(require("fs"), 1);
|
|
53996
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
53902
53997
|
var import_node_child_process12 = require("child_process");
|
|
53903
53998
|
var IDENTITY_DIR = "ssh-identity";
|
|
53904
53999
|
var KEY_FILE = "id_ed25519";
|
|
53905
54000
|
function identityPaths(dataDir) {
|
|
53906
|
-
const privPath =
|
|
54001
|
+
const privPath = import_node_path42.default.join(dataDir, IDENTITY_DIR, KEY_FILE);
|
|
53907
54002
|
return { privPath, pubPath: `${privPath}.pub` };
|
|
53908
54003
|
}
|
|
53909
54004
|
async function ensureSshIdentity(dataDir, opts = {}) {
|
|
53910
54005
|
const { privPath, pubPath } = identityPaths(dataDir);
|
|
53911
|
-
|
|
53912
|
-
if (
|
|
53913
|
-
return { privateKeyPath: privPath, publicKeyLine:
|
|
54006
|
+
import_node_fs42.default.mkdirSync(import_node_path42.default.dirname(privPath), { recursive: true, mode: 448 });
|
|
54007
|
+
if (import_node_fs42.default.existsSync(privPath) && import_node_fs42.default.existsSync(pubPath)) {
|
|
54008
|
+
return { privateKeyPath: privPath, publicKeyLine: import_node_fs42.default.readFileSync(pubPath, "utf8").trim() };
|
|
53914
54009
|
}
|
|
53915
54010
|
const bin = opts.keygenBin ?? "/usr/bin/ssh-keygen";
|
|
53916
54011
|
await new Promise((resolve6, reject) => {
|
|
@@ -53923,18 +54018,18 @@ async function ensureSshIdentity(dataDir, opts = {}) {
|
|
|
53923
54018
|
p2.on("error", reject);
|
|
53924
54019
|
});
|
|
53925
54020
|
try {
|
|
53926
|
-
|
|
54021
|
+
import_node_fs42.default.chmodSync(privPath, 384);
|
|
53927
54022
|
} catch {
|
|
53928
54023
|
}
|
|
53929
54024
|
try {
|
|
53930
|
-
|
|
54025
|
+
import_node_fs42.default.chmodSync(pubPath, 420);
|
|
53931
54026
|
} catch {
|
|
53932
54027
|
}
|
|
53933
|
-
return { privateKeyPath: privPath, publicKeyLine:
|
|
54028
|
+
return { privateKeyPath: privPath, publicKeyLine: import_node_fs42.default.readFileSync(pubPath, "utf8").trim() };
|
|
53934
54029
|
}
|
|
53935
54030
|
function readSshIdentityPublicKey(dataDir) {
|
|
53936
54031
|
try {
|
|
53937
|
-
return
|
|
54032
|
+
return import_node_fs42.default.readFileSync(identityPaths(dataDir).pubPath, "utf8").trim();
|
|
53938
54033
|
} catch {
|
|
53939
54034
|
return null;
|
|
53940
54035
|
}
|
|
@@ -53944,10 +54039,10 @@ function readSshIdentityPublicKey(dataDir) {
|
|
|
53944
54039
|
var import_node_net2 = __toESM(require("net"), 1);
|
|
53945
54040
|
|
|
53946
54041
|
// src/sshd/contact-ssh-log.ts
|
|
53947
|
-
var
|
|
53948
|
-
var
|
|
54042
|
+
var import_node_fs43 = __toESM(require("fs"), 1);
|
|
54043
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
53949
54044
|
function createContactSshLog(dataDir) {
|
|
53950
|
-
const file =
|
|
54045
|
+
const file = import_node_path43.default.join(dataDir, "log", "contact-ssh.log");
|
|
53951
54046
|
function append(level, tag, message, meta) {
|
|
53952
54047
|
const time = (/* @__PURE__ */ new Date()).toISOString();
|
|
53953
54048
|
let line = `[${time}] [${level}] [${tag}] ${message}`;
|
|
@@ -53960,8 +54055,8 @@ function createContactSshLog(dataDir) {
|
|
|
53960
54055
|
}
|
|
53961
54056
|
line += "\n";
|
|
53962
54057
|
try {
|
|
53963
|
-
|
|
53964
|
-
|
|
54058
|
+
import_node_fs43.default.mkdirSync(import_node_path43.default.dirname(file), { recursive: true });
|
|
54059
|
+
import_node_fs43.default.appendFileSync(file, line, { mode: 384 });
|
|
53965
54060
|
} catch {
|
|
53966
54061
|
}
|
|
53967
54062
|
}
|
|
@@ -54269,27 +54364,27 @@ function respond404(socket) {
|
|
|
54269
54364
|
|
|
54270
54365
|
// src/tunnel/device-key.ts
|
|
54271
54366
|
var import_node_os14 = __toESM(require("os"), 1);
|
|
54272
|
-
var
|
|
54367
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
54273
54368
|
var import_node_crypto11 = __toESM(require("crypto"), 1);
|
|
54274
54369
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
54275
54370
|
function deriveStableDeviceKey(opts = {}) {
|
|
54276
54371
|
const hostname = opts.hostname ?? import_node_os14.default.hostname();
|
|
54277
54372
|
const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
|
|
54278
54373
|
const home = opts.home ?? import_node_os14.default.homedir();
|
|
54279
|
-
const defaultDataDir =
|
|
54280
|
-
const normalizedDataDir = opts.dataDir ?
|
|
54374
|
+
const defaultDataDir = import_node_path44.default.resolve(import_node_path44.default.join(home, ".clawd"));
|
|
54375
|
+
const normalizedDataDir = opts.dataDir ? import_node_path44.default.resolve(opts.dataDir) : null;
|
|
54281
54376
|
const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
|
|
54282
54377
|
const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
|
|
54283
54378
|
return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
54284
54379
|
}
|
|
54285
54380
|
|
|
54286
54381
|
// src/auth-store.ts
|
|
54287
|
-
var
|
|
54288
|
-
var
|
|
54382
|
+
var import_node_fs44 = __toESM(require("fs"), 1);
|
|
54383
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
54289
54384
|
var import_node_crypto12 = __toESM(require("crypto"), 1);
|
|
54290
54385
|
var AUTH_FILE_NAME = "auth.json";
|
|
54291
54386
|
function authFilePath(dataDir) {
|
|
54292
|
-
return
|
|
54387
|
+
return import_node_path45.default.join(dataDir, AUTH_FILE_NAME);
|
|
54293
54388
|
}
|
|
54294
54389
|
function loadOrCreateAuthFile(opts) {
|
|
54295
54390
|
const file = authFilePath(opts.dataDir);
|
|
@@ -54325,7 +54420,7 @@ function defaultGenerateOwnerPrincipalId() {
|
|
|
54325
54420
|
}
|
|
54326
54421
|
function readAuthFile(file) {
|
|
54327
54422
|
try {
|
|
54328
|
-
const raw =
|
|
54423
|
+
const raw = import_node_fs44.default.readFileSync(file, "utf8");
|
|
54329
54424
|
const parsed = JSON.parse(raw);
|
|
54330
54425
|
if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
|
|
54331
54426
|
return null;
|
|
@@ -54345,25 +54440,25 @@ function readAuthFile(file) {
|
|
|
54345
54440
|
}
|
|
54346
54441
|
}
|
|
54347
54442
|
function writeAuthFile(file, content) {
|
|
54348
|
-
|
|
54349
|
-
|
|
54443
|
+
import_node_fs44.default.mkdirSync(import_node_path45.default.dirname(file), { recursive: true });
|
|
54444
|
+
import_node_fs44.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
54350
54445
|
try {
|
|
54351
|
-
|
|
54446
|
+
import_node_fs44.default.chmodSync(file, 384);
|
|
54352
54447
|
} catch {
|
|
54353
54448
|
}
|
|
54354
54449
|
}
|
|
54355
54450
|
|
|
54356
54451
|
// src/owner-profile.ts
|
|
54357
|
-
var
|
|
54452
|
+
var import_node_fs45 = __toESM(require("fs"), 1);
|
|
54358
54453
|
var import_node_os15 = __toESM(require("os"), 1);
|
|
54359
|
-
var
|
|
54454
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
54360
54455
|
var PROFILE_FILENAME = "profile.json";
|
|
54361
54456
|
function loadOwnerDisplayName(dataDir) {
|
|
54362
54457
|
const fallback = import_node_os15.default.userInfo().username;
|
|
54363
|
-
const profilePath =
|
|
54458
|
+
const profilePath = import_node_path46.default.join(dataDir, PROFILE_FILENAME);
|
|
54364
54459
|
let raw;
|
|
54365
54460
|
try {
|
|
54366
|
-
raw =
|
|
54461
|
+
raw = import_node_fs45.default.readFileSync(profilePath, "utf8");
|
|
54367
54462
|
} catch {
|
|
54368
54463
|
return fallback;
|
|
54369
54464
|
}
|
|
@@ -54386,18 +54481,18 @@ function loadOwnerDisplayName(dataDir) {
|
|
|
54386
54481
|
}
|
|
54387
54482
|
|
|
54388
54483
|
// src/feishu-auth/owner-identity-store.ts
|
|
54389
|
-
var
|
|
54390
|
-
var
|
|
54484
|
+
var import_node_fs46 = __toESM(require("fs"), 1);
|
|
54485
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
54391
54486
|
var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
|
|
54392
54487
|
var OwnerIdentityStore = class {
|
|
54393
54488
|
file;
|
|
54394
54489
|
constructor(dataDir) {
|
|
54395
|
-
this.file =
|
|
54490
|
+
this.file = import_node_path47.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
|
|
54396
54491
|
}
|
|
54397
54492
|
read() {
|
|
54398
54493
|
let raw;
|
|
54399
54494
|
try {
|
|
54400
|
-
raw =
|
|
54495
|
+
raw = import_node_fs46.default.readFileSync(this.file, "utf8");
|
|
54401
54496
|
} catch {
|
|
54402
54497
|
return null;
|
|
54403
54498
|
}
|
|
@@ -54425,16 +54520,16 @@ var OwnerIdentityStore = class {
|
|
|
54425
54520
|
};
|
|
54426
54521
|
}
|
|
54427
54522
|
write(record) {
|
|
54428
|
-
|
|
54429
|
-
|
|
54523
|
+
import_node_fs46.default.mkdirSync(import_node_path47.default.dirname(this.file), { recursive: true });
|
|
54524
|
+
import_node_fs46.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
|
|
54430
54525
|
try {
|
|
54431
|
-
|
|
54526
|
+
import_node_fs46.default.chmodSync(this.file, 384);
|
|
54432
54527
|
} catch {
|
|
54433
54528
|
}
|
|
54434
54529
|
}
|
|
54435
54530
|
clear() {
|
|
54436
54531
|
try {
|
|
54437
|
-
|
|
54532
|
+
import_node_fs46.default.unlinkSync(this.file);
|
|
54438
54533
|
} catch (err) {
|
|
54439
54534
|
const code = err?.code;
|
|
54440
54535
|
if (code !== "ENOENT") throw err;
|
|
@@ -54571,9 +54666,9 @@ var CentralClientError = class extends Error {
|
|
|
54571
54666
|
code;
|
|
54572
54667
|
cause;
|
|
54573
54668
|
};
|
|
54574
|
-
async function centralRequest(opts,
|
|
54669
|
+
async function centralRequest(opts, path77, init) {
|
|
54575
54670
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
54576
|
-
const url = `${opts.api.replace(/\/+$/, "")}${
|
|
54671
|
+
const url = `${opts.api.replace(/\/+$/, "")}${path77}`;
|
|
54577
54672
|
const ctrl = new AbortController();
|
|
54578
54673
|
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
54579
54674
|
let res;
|
|
@@ -54767,8 +54862,8 @@ function verifyConnectToken(args) {
|
|
|
54767
54862
|
}
|
|
54768
54863
|
|
|
54769
54864
|
// src/feishu-auth/server-key.ts
|
|
54770
|
-
var
|
|
54771
|
-
var
|
|
54865
|
+
var fs56 = __toESM(require("fs"), 1);
|
|
54866
|
+
var path56 = __toESM(require("path"), 1);
|
|
54772
54867
|
var FILE_NAME2 = "server-signing-key.json";
|
|
54773
54868
|
var ServerKeyStore = class {
|
|
54774
54869
|
constructor(dataDir) {
|
|
@@ -54776,12 +54871,12 @@ var ServerKeyStore = class {
|
|
|
54776
54871
|
}
|
|
54777
54872
|
dataDir;
|
|
54778
54873
|
filePath() {
|
|
54779
|
-
return
|
|
54874
|
+
return path56.join(this.dataDir, FILE_NAME2);
|
|
54780
54875
|
}
|
|
54781
54876
|
/** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
|
|
54782
54877
|
read() {
|
|
54783
54878
|
try {
|
|
54784
|
-
const raw =
|
|
54879
|
+
const raw = fs56.readFileSync(this.filePath(), "utf8");
|
|
54785
54880
|
const parsed = JSON.parse(raw);
|
|
54786
54881
|
if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
|
|
54787
54882
|
return parsed.publicKeyPem;
|
|
@@ -54796,12 +54891,12 @@ var ServerKeyStore = class {
|
|
|
54796
54891
|
publicKeyPem,
|
|
54797
54892
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
54798
54893
|
};
|
|
54799
|
-
|
|
54800
|
-
|
|
54894
|
+
fs56.mkdirSync(this.dataDir, { recursive: true });
|
|
54895
|
+
fs56.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
|
|
54801
54896
|
}
|
|
54802
54897
|
clear() {
|
|
54803
54898
|
try {
|
|
54804
|
-
|
|
54899
|
+
fs56.unlinkSync(this.filePath());
|
|
54805
54900
|
} catch {
|
|
54806
54901
|
}
|
|
54807
54902
|
}
|
|
@@ -54814,12 +54909,12 @@ init_protocol();
|
|
|
54814
54909
|
init_protocol();
|
|
54815
54910
|
|
|
54816
54911
|
// src/session/fork.ts
|
|
54817
|
-
var
|
|
54912
|
+
var import_node_fs47 = __toESM(require("fs"), 1);
|
|
54818
54913
|
var import_node_os16 = __toESM(require("os"), 1);
|
|
54819
|
-
var
|
|
54914
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
54820
54915
|
init_claude_history();
|
|
54821
54916
|
function readJsonlEntries(file) {
|
|
54822
|
-
const raw =
|
|
54917
|
+
const raw = import_node_fs47.default.readFileSync(file, "utf8");
|
|
54823
54918
|
const out = [];
|
|
54824
54919
|
for (const line of raw.split("\n")) {
|
|
54825
54920
|
const t = line.trim();
|
|
@@ -54832,19 +54927,19 @@ function readJsonlEntries(file) {
|
|
|
54832
54927
|
return out;
|
|
54833
54928
|
}
|
|
54834
54929
|
function forkSession(input) {
|
|
54835
|
-
const baseDir = input.baseDir ??
|
|
54836
|
-
let projectDir =
|
|
54837
|
-
let sourceFile =
|
|
54838
|
-
if (!
|
|
54930
|
+
const baseDir = input.baseDir ?? import_node_path48.default.join(import_node_os16.default.homedir(), ".claude");
|
|
54931
|
+
let projectDir = import_node_path48.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
54932
|
+
let sourceFile = import_node_path48.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
54933
|
+
if (!import_node_fs47.default.existsSync(sourceFile)) {
|
|
54839
54934
|
const found = findTranscriptByToolSessionId(
|
|
54840
|
-
|
|
54935
|
+
import_node_path48.default.join(baseDir, "projects"),
|
|
54841
54936
|
input.toolSessionId
|
|
54842
54937
|
);
|
|
54843
54938
|
if (!found) {
|
|
54844
54939
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
54845
54940
|
}
|
|
54846
54941
|
sourceFile = found;
|
|
54847
|
-
projectDir =
|
|
54942
|
+
projectDir = import_node_path48.default.dirname(found);
|
|
54848
54943
|
}
|
|
54849
54944
|
const entries = readJsonlEntries(sourceFile);
|
|
54850
54945
|
const mainEntries = entries.filter((e) => e.isSidechain !== true);
|
|
@@ -54873,9 +54968,9 @@ function forkSession(input) {
|
|
|
54873
54968
|
}
|
|
54874
54969
|
forkedLines.push(JSON.stringify(forked));
|
|
54875
54970
|
}
|
|
54876
|
-
const forkedFilePath =
|
|
54877
|
-
|
|
54878
|
-
|
|
54971
|
+
const forkedFilePath = import_node_path48.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
54972
|
+
import_node_fs47.default.mkdirSync(projectDir, { recursive: true });
|
|
54973
|
+
import_node_fs47.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
54879
54974
|
return { forkedToolSessionId, forkedFilePath };
|
|
54880
54975
|
}
|
|
54881
54976
|
|
|
@@ -55246,7 +55341,7 @@ function buildPermissionHandlers(deps) {
|
|
|
55246
55341
|
}
|
|
55247
55342
|
|
|
55248
55343
|
// src/handlers/history.ts
|
|
55249
|
-
var
|
|
55344
|
+
var path59 = __toESM(require("path"), 1);
|
|
55250
55345
|
init_protocol();
|
|
55251
55346
|
|
|
55252
55347
|
// src/session/recent-dirs.ts
|
|
@@ -55264,7 +55359,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
55264
55359
|
}
|
|
55265
55360
|
|
|
55266
55361
|
// src/permission/persona-paths.ts
|
|
55267
|
-
var
|
|
55362
|
+
var path58 = __toESM(require("path"), 1);
|
|
55268
55363
|
function getAllowedPersonaIds(grants, action) {
|
|
55269
55364
|
const ids = /* @__PURE__ */ new Set();
|
|
55270
55365
|
for (const g2 of grants) {
|
|
@@ -55277,42 +55372,42 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
55277
55372
|
return ids;
|
|
55278
55373
|
}
|
|
55279
55374
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
|
|
55280
|
-
const target =
|
|
55375
|
+
const target = path58.resolve(absPath);
|
|
55281
55376
|
if (userWorkDir) {
|
|
55282
|
-
const u =
|
|
55283
|
-
const usep = u.endsWith(
|
|
55377
|
+
const u = path58.resolve(userWorkDir);
|
|
55378
|
+
const usep = u.endsWith(path58.sep) ? "" : path58.sep;
|
|
55284
55379
|
if (target === u || target.startsWith(u + usep)) return true;
|
|
55285
55380
|
}
|
|
55286
|
-
const root =
|
|
55287
|
-
const sep3 = root.endsWith(
|
|
55381
|
+
const root = path58.resolve(personaRoot);
|
|
55382
|
+
const sep3 = root.endsWith(path58.sep) ? "" : path58.sep;
|
|
55288
55383
|
if (!target.startsWith(root + sep3)) return false;
|
|
55289
|
-
const rel =
|
|
55384
|
+
const rel = path58.relative(root, target);
|
|
55290
55385
|
if (!rel || rel.startsWith("..")) return false;
|
|
55291
|
-
const personaId = rel.split(
|
|
55386
|
+
const personaId = rel.split(path58.sep)[0];
|
|
55292
55387
|
if (!personaId) return false;
|
|
55293
55388
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
55294
55389
|
if (allowed === "*") return true;
|
|
55295
55390
|
return allowed.has(personaId);
|
|
55296
55391
|
}
|
|
55297
55392
|
function personaIdFromPath(absPath, personaRoot) {
|
|
55298
|
-
const root =
|
|
55299
|
-
const target =
|
|
55300
|
-
const sep3 = root.endsWith(
|
|
55393
|
+
const root = path58.resolve(personaRoot);
|
|
55394
|
+
const target = path58.resolve(absPath);
|
|
55395
|
+
const sep3 = root.endsWith(path58.sep) ? "" : path58.sep;
|
|
55301
55396
|
if (!target.startsWith(root + sep3)) return null;
|
|
55302
|
-
const rel =
|
|
55397
|
+
const rel = path58.relative(root, target);
|
|
55303
55398
|
if (!rel || rel.startsWith("..")) return null;
|
|
55304
|
-
const id = rel.split(
|
|
55399
|
+
const id = rel.split(path58.sep)[0];
|
|
55305
55400
|
return id || null;
|
|
55306
55401
|
}
|
|
55307
55402
|
function isPathWithin(dir, absPath) {
|
|
55308
|
-
const d =
|
|
55309
|
-
const t =
|
|
55310
|
-
const sep3 = d.endsWith(
|
|
55403
|
+
const d = path58.resolve(dir);
|
|
55404
|
+
const t = path58.resolve(absPath);
|
|
55405
|
+
const sep3 = d.endsWith(path58.sep) ? "" : path58.sep;
|
|
55311
55406
|
return t === d || t.startsWith(d + sep3);
|
|
55312
55407
|
}
|
|
55313
55408
|
function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
|
|
55314
55409
|
if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
|
|
55315
|
-
return personaIdFromPath(
|
|
55410
|
+
return personaIdFromPath(path58.resolve(absPath), personaRoot) === personaId;
|
|
55316
55411
|
}
|
|
55317
55412
|
|
|
55318
55413
|
// src/handlers/history.ts
|
|
@@ -55338,7 +55433,7 @@ function buildHistoryHandlers(deps) {
|
|
|
55338
55433
|
if (!pid) return false;
|
|
55339
55434
|
return isGuestPathAllowed(
|
|
55340
55435
|
ctx.grants,
|
|
55341
|
-
|
|
55436
|
+
path59.join(personaRoot, pid),
|
|
55342
55437
|
personaRoot,
|
|
55343
55438
|
"read",
|
|
55344
55439
|
userWorkDir
|
|
@@ -55350,7 +55445,7 @@ function buildHistoryHandlers(deps) {
|
|
|
55350
55445
|
};
|
|
55351
55446
|
const list = async (frame, _client, ctx) => {
|
|
55352
55447
|
const args = HistoryListArgs.parse(frame);
|
|
55353
|
-
assertGuestPath(ctx,
|
|
55448
|
+
assertGuestPath(ctx, path59.resolve(args.projectPath), personaRoot, "history:list");
|
|
55354
55449
|
const sessions = await history.listSessions(args);
|
|
55355
55450
|
return { response: { type: "history:list", sessions } };
|
|
55356
55451
|
};
|
|
@@ -55382,13 +55477,13 @@ function buildHistoryHandlers(deps) {
|
|
|
55382
55477
|
};
|
|
55383
55478
|
const subagents = async (frame, _client, ctx) => {
|
|
55384
55479
|
const args = HistorySubagentsArgs.parse(frame);
|
|
55385
|
-
assertGuestPath(ctx,
|
|
55480
|
+
assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
|
|
55386
55481
|
const subs = await history.listSubagents(args);
|
|
55387
55482
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
55388
55483
|
};
|
|
55389
55484
|
const subagentRead = async (frame, _client, ctx) => {
|
|
55390
55485
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
55391
|
-
assertGuestPath(ctx,
|
|
55486
|
+
assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
|
|
55392
55487
|
const res = await history.readSubagent(args);
|
|
55393
55488
|
return { response: { type: "history:subagent-read", ...res } };
|
|
55394
55489
|
};
|
|
@@ -55397,7 +55492,7 @@ function buildHistoryHandlers(deps) {
|
|
|
55397
55492
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
55398
55493
|
const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
|
|
55399
55494
|
const filtered = dirs.filter(
|
|
55400
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
55495
|
+
(d) => isGuestPathAllowed(ctx.grants, path59.resolve(d.cwd), personaRoot, "read", userWorkDir)
|
|
55401
55496
|
);
|
|
55402
55497
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
55403
55498
|
}
|
|
@@ -55414,7 +55509,7 @@ function buildHistoryHandlers(deps) {
|
|
|
55414
55509
|
}
|
|
55415
55510
|
|
|
55416
55511
|
// src/handlers/workspace.ts
|
|
55417
|
-
var
|
|
55512
|
+
var path60 = __toESM(require("path"), 1);
|
|
55418
55513
|
var os16 = __toESM(require("os"), 1);
|
|
55419
55514
|
init_protocol();
|
|
55420
55515
|
init_protocol();
|
|
@@ -55456,22 +55551,22 @@ function buildWorkspaceHandlers(deps) {
|
|
|
55456
55551
|
const args = WorkspaceListArgs.parse(frame);
|
|
55457
55552
|
const isGuest = ctx?.principal.kind === "guest";
|
|
55458
55553
|
const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
|
|
55459
|
-
const resolvedCwd =
|
|
55460
|
-
const target = args.path ?
|
|
55554
|
+
const resolvedCwd = path60.resolve(args.cwd ?? fallbackCwd);
|
|
55555
|
+
const target = args.path ? path60.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
55461
55556
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
|
|
55462
55557
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
55463
55558
|
return { response: { type: "workspace:list", ...res } };
|
|
55464
55559
|
};
|
|
55465
55560
|
const read = async (frame, _client, ctx) => {
|
|
55466
55561
|
const args = WorkspaceReadArgs.parse(frame);
|
|
55467
|
-
const target =
|
|
55562
|
+
const target = path60.isAbsolute(args.path) ? path60.resolve(args.path) : path60.resolve(args.cwd, args.path);
|
|
55468
55563
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
|
|
55469
55564
|
const res = workspace.read(args);
|
|
55470
55565
|
return { response: { type: "workspace:read", ...res } };
|
|
55471
55566
|
};
|
|
55472
55567
|
const skillsList = async (frame, _client, ctx) => {
|
|
55473
55568
|
const args = SkillsListArgs.parse(frame);
|
|
55474
|
-
const cwdAbs =
|
|
55569
|
+
const cwdAbs = path60.resolve(args.cwd);
|
|
55475
55570
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
|
|
55476
55571
|
const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
|
|
55477
55572
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -55483,7 +55578,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
55483
55578
|
};
|
|
55484
55579
|
const agentsList = async (frame, _client, ctx) => {
|
|
55485
55580
|
const args = AgentsListArgs.parse(frame);
|
|
55486
|
-
const cwdAbs =
|
|
55581
|
+
const cwdAbs = path60.resolve(args.cwd);
|
|
55487
55582
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
|
|
55488
55583
|
if (args.tool === "codex") {
|
|
55489
55584
|
return { response: { type: "agents:list", agents: [] } };
|
|
@@ -55505,20 +55600,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
55505
55600
|
}
|
|
55506
55601
|
|
|
55507
55602
|
// src/handlers/git.ts
|
|
55508
|
-
var
|
|
55603
|
+
var path62 = __toESM(require("path"), 1);
|
|
55509
55604
|
init_protocol();
|
|
55510
55605
|
init_protocol();
|
|
55511
55606
|
|
|
55512
55607
|
// src/workspace/git.ts
|
|
55513
55608
|
var import_node_child_process13 = require("child_process");
|
|
55514
|
-
var
|
|
55515
|
-
var
|
|
55609
|
+
var import_node_fs48 = __toESM(require("fs"), 1);
|
|
55610
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
55516
55611
|
var import_node_util = require("util");
|
|
55517
55612
|
var pexec = (0, import_node_util.promisify)(import_node_child_process13.execFile);
|
|
55518
55613
|
function normalizePath(p2) {
|
|
55519
|
-
const resolved =
|
|
55614
|
+
const resolved = import_node_path49.default.resolve(p2);
|
|
55520
55615
|
try {
|
|
55521
|
-
return
|
|
55616
|
+
return import_node_fs48.default.realpathSync(resolved);
|
|
55522
55617
|
} catch {
|
|
55523
55618
|
return resolved;
|
|
55524
55619
|
}
|
|
@@ -55592,7 +55687,7 @@ async function listGitBranches(cwd) {
|
|
|
55592
55687
|
function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
|
|
55593
55688
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
55594
55689
|
const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
|
|
55595
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
55690
|
+
if (!isGuestPathAllowed(ctx.grants, path62.resolve(cwd), personaRoot, "read", userWorkDir)) {
|
|
55596
55691
|
throw new ClawdError(
|
|
55597
55692
|
ERROR_CODES.UNAUTHORIZED,
|
|
55598
55693
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -55944,21 +56039,21 @@ function ensureOwner2(ctx) {
|
|
|
55944
56039
|
if (!ctx || ctx.principal.kind !== "owner") {
|
|
55945
56040
|
throw new ClawdError(
|
|
55946
56041
|
ERROR_CODES.UNAUTHORIZED,
|
|
55947
|
-
"UNAUTHORIZED: contact:
|
|
56042
|
+
"UNAUTHORIZED: contact:setRemoteAccess requires owner ctx"
|
|
55948
56043
|
);
|
|
55949
56044
|
}
|
|
55950
56045
|
}
|
|
55951
56046
|
function buildContactSshHandlers(deps) {
|
|
55952
56047
|
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
55953
|
-
const
|
|
56048
|
+
const setRemoteAccess = async (frame, _client, ctx) => {
|
|
55954
56049
|
ensureOwner2(ctx);
|
|
55955
56050
|
const { type: _t, requestId: _r, ...rest } = frame;
|
|
55956
|
-
const args =
|
|
55957
|
-
const hit = deps.store.
|
|
55958
|
-
|
|
56051
|
+
const args = ContactSetRemoteAccessArgsSchema.parse(rest);
|
|
56052
|
+
const hit = deps.store.setRemoteAccess(args.deviceId, {
|
|
56053
|
+
remoteAccessAllowed: args.remoteAccessAllowed
|
|
55959
56054
|
});
|
|
55960
56055
|
if (!hit) {
|
|
55961
|
-
sshLog.warn("authz.
|
|
56056
|
+
sshLog.warn("authz.setRemoteAccess", "owner \u5C1D\u8BD5\u6539 SSH \u6388\u6743\u4F46 contact \u4E0D\u5728 store", {
|
|
55962
56057
|
peerDeviceId: args.deviceId
|
|
55963
56058
|
});
|
|
55964
56059
|
throw new ClawdError(
|
|
@@ -55967,25 +56062,25 @@ function buildContactSshHandlers(deps) {
|
|
|
55967
56062
|
);
|
|
55968
56063
|
}
|
|
55969
56064
|
rebuildAuthorizedKeys(deps.store, deps.sshdDir);
|
|
55970
|
-
sshLog.info("authz.
|
|
56065
|
+
sshLog.info("authz.setRemoteAccess", "owner \u6539\u4E86 SSH \u6388\u6743 \u2192 authorized_keys \u5DF2\u91CD\u5EFA", {
|
|
55971
56066
|
peerDeviceId: args.deviceId,
|
|
55972
|
-
|
|
56067
|
+
remoteAccessAllowed: args.remoteAccessAllowed
|
|
55973
56068
|
});
|
|
55974
56069
|
deps.broadcast({
|
|
55975
|
-
type: "contact:
|
|
56070
|
+
type: "contact:remote-access-updated",
|
|
55976
56071
|
deviceId: args.deviceId,
|
|
55977
|
-
|
|
56072
|
+
remoteAccessAllowed: args.remoteAccessAllowed
|
|
55978
56073
|
});
|
|
55979
56074
|
return {
|
|
55980
56075
|
response: {
|
|
55981
|
-
type: "contact:
|
|
56076
|
+
type: "contact:setRemoteAccess:ok",
|
|
55982
56077
|
deviceId: args.deviceId,
|
|
55983
|
-
|
|
56078
|
+
remoteAccessAllowed: args.remoteAccessAllowed
|
|
55984
56079
|
}
|
|
55985
56080
|
};
|
|
55986
56081
|
};
|
|
55987
56082
|
return {
|
|
55988
|
-
"contact:
|
|
56083
|
+
"contact:setRemoteAccess": setRemoteAccess
|
|
55989
56084
|
};
|
|
55990
56085
|
}
|
|
55991
56086
|
|
|
@@ -56153,7 +56248,7 @@ function buildDeviceHandlers(deps) {
|
|
|
56153
56248
|
grants: wh.grants,
|
|
56154
56249
|
addedAt: now(),
|
|
56155
56250
|
pinnedAt: existing?.pinnedAt ?? null,
|
|
56156
|
-
|
|
56251
|
+
remoteAccessAllowed: existing?.remoteAccessAllowed ?? false,
|
|
56157
56252
|
// 建联交换:对方 whoami:ok 下发的 pubkey / osUser 落库;对方老版本不带 →
|
|
56158
56253
|
// 保留既有值 / 不落 undefined 占位。见 doc/glossary/peer-mesh.md#建联交换清单
|
|
56159
56254
|
...sshPublicKey ? { sshPublicKey } : {},
|
|
@@ -56162,7 +56257,7 @@ function buildDeviceHandlers(deps) {
|
|
|
56162
56257
|
};
|
|
56163
56258
|
deps.store.upsert(contact);
|
|
56164
56259
|
deps.broadcast({ type: "contact:added", contact });
|
|
56165
|
-
if (contact.
|
|
56260
|
+
if (contact.remoteAccessAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
|
|
56166
56261
|
deps.rebuildAuthorizedKeys?.();
|
|
56167
56262
|
}
|
|
56168
56263
|
return {
|
|
@@ -56317,7 +56412,7 @@ function buildPersonaHandlers(deps) {
|
|
|
56317
56412
|
}
|
|
56318
56413
|
|
|
56319
56414
|
// src/handlers/attachment.ts
|
|
56320
|
-
var
|
|
56415
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
56321
56416
|
init_protocol();
|
|
56322
56417
|
init_protocol();
|
|
56323
56418
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -56397,12 +56492,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
56397
56492
|
`session ${args.sessionId} scope unresolved`
|
|
56398
56493
|
);
|
|
56399
56494
|
}
|
|
56400
|
-
const cwdAbs =
|
|
56401
|
-
const candidateAbs =
|
|
56495
|
+
const cwdAbs = import_node_path50.default.resolve(sessionFile.cwd);
|
|
56496
|
+
const candidateAbs = import_node_path50.default.isAbsolute(args.relPath) ? import_node_path50.default.resolve(args.relPath) : import_node_path50.default.resolve(cwdAbs, args.relPath);
|
|
56402
56497
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
|
|
56403
56498
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
56404
56499
|
const entry = entries.find((e) => {
|
|
56405
|
-
const storedAbs =
|
|
56500
|
+
const storedAbs = import_node_path50.default.isAbsolute(e.relPath) ? import_node_path50.default.resolve(e.relPath) : import_node_path50.default.resolve(cwdAbs, e.relPath);
|
|
56406
56501
|
return storedAbs === candidateAbs && !e.stale;
|
|
56407
56502
|
});
|
|
56408
56503
|
if (!entry) {
|
|
@@ -56427,7 +56522,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
56427
56522
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
56428
56523
|
const f = deps.sessionStore.read(sessionId);
|
|
56429
56524
|
if (!f) return;
|
|
56430
|
-
assertGuestAttachmentPath(ctx,
|
|
56525
|
+
assertGuestAttachmentPath(ctx, import_node_path50.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
|
|
56431
56526
|
}
|
|
56432
56527
|
const groupAdd = async (frame, _client, ctx) => {
|
|
56433
56528
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -56442,8 +56537,8 @@ function buildAttachmentHandlers(deps) {
|
|
|
56442
56537
|
if (!scope) {
|
|
56443
56538
|
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
|
|
56444
56539
|
}
|
|
56445
|
-
const cwdAbs =
|
|
56446
|
-
const candidateAbs =
|
|
56540
|
+
const cwdAbs = import_node_path50.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
|
|
56541
|
+
const candidateAbs = import_node_path50.default.isAbsolute(args.relPath) ? import_node_path50.default.resolve(args.relPath) : import_node_path50.default.resolve(cwdAbs, args.relPath);
|
|
56447
56542
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
|
|
56448
56543
|
const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
|
|
56449
56544
|
const size = 0;
|
|
@@ -56502,19 +56597,19 @@ function buildAttachmentHandlers(deps) {
|
|
|
56502
56597
|
|
|
56503
56598
|
// src/handlers/extension.ts
|
|
56504
56599
|
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
56505
|
-
var
|
|
56600
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
56506
56601
|
init_protocol();
|
|
56507
56602
|
|
|
56508
56603
|
// src/extension/bundle-zip.ts
|
|
56509
56604
|
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
56510
|
-
var
|
|
56605
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
56511
56606
|
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
56512
56607
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
56513
56608
|
async function bundleExtensionDir(dir) {
|
|
56514
56609
|
const entries = await listFilesSorted(dir);
|
|
56515
56610
|
const zip = new import_jszip2.default();
|
|
56516
56611
|
for (const rel of entries) {
|
|
56517
|
-
const abs =
|
|
56612
|
+
const abs = import_node_path51.default.join(dir, rel);
|
|
56518
56613
|
const content = await import_promises5.default.readFile(abs);
|
|
56519
56614
|
zip.file(rel, content, { date: FIXED_DATE });
|
|
56520
56615
|
}
|
|
@@ -56535,7 +56630,7 @@ async function listFilesSorted(rootDir) {
|
|
|
56535
56630
|
return out;
|
|
56536
56631
|
}
|
|
56537
56632
|
async function walk(absRoot, relPrefix, out) {
|
|
56538
|
-
const dirAbs =
|
|
56633
|
+
const dirAbs = import_node_path51.default.join(absRoot, relPrefix);
|
|
56539
56634
|
const entries = await import_promises5.default.readdir(dirAbs, { withFileTypes: true });
|
|
56540
56635
|
for (const e of entries) {
|
|
56541
56636
|
if (IGNORE_BASENAMES.has(e.name)) continue;
|
|
@@ -56589,25 +56684,25 @@ function computePublishCheck(args) {
|
|
|
56589
56684
|
|
|
56590
56685
|
// src/extension/install-flow.ts
|
|
56591
56686
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
56592
|
-
var
|
|
56687
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
56593
56688
|
var import_node_os19 = __toESM(require("os"), 1);
|
|
56594
56689
|
var import_node_crypto15 = __toESM(require("crypto"), 1);
|
|
56595
56690
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
56596
56691
|
|
|
56597
56692
|
// src/extension/paths.ts
|
|
56598
56693
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
56599
|
-
var
|
|
56694
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
56600
56695
|
function clawdHomeRoot(override) {
|
|
56601
|
-
return override ?? process.env.CLAWD_HOME ??
|
|
56696
|
+
return override ?? process.env.CLAWD_HOME ?? import_node_path52.default.join(import_node_os18.default.homedir(), ".clawd");
|
|
56602
56697
|
}
|
|
56603
56698
|
function extensionsRoot(override) {
|
|
56604
|
-
return
|
|
56699
|
+
return import_node_path52.default.join(clawdHomeRoot(override), "extensions");
|
|
56605
56700
|
}
|
|
56606
56701
|
function publishedChannelsFile(override) {
|
|
56607
|
-
return
|
|
56702
|
+
return import_node_path52.default.join(clawdHomeRoot(override), "extensions-published.json");
|
|
56608
56703
|
}
|
|
56609
56704
|
function bundleCacheRoot(override) {
|
|
56610
|
-
return
|
|
56705
|
+
return import_node_path52.default.join(clawdHomeRoot(override), "extension-bundles");
|
|
56611
56706
|
}
|
|
56612
56707
|
|
|
56613
56708
|
// src/extension/install-flow.ts
|
|
@@ -56634,7 +56729,7 @@ async function installFromChannel(args, deps) {
|
|
|
56634
56729
|
throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56635
56730
|
}
|
|
56636
56731
|
for (const name of Object.keys(zip.files)) {
|
|
56637
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
56732
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path53.default.isAbsolute(name)) {
|
|
56638
56733
|
throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56639
56734
|
}
|
|
56640
56735
|
}
|
|
@@ -56666,7 +56761,7 @@ async function installFromChannel(args, deps) {
|
|
|
56666
56761
|
);
|
|
56667
56762
|
}
|
|
56668
56763
|
const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
|
|
56669
|
-
const destDir =
|
|
56764
|
+
const destDir = import_node_path53.default.join(deps.extensionsRoot, localExtId);
|
|
56670
56765
|
let destExists = false;
|
|
56671
56766
|
try {
|
|
56672
56767
|
await import_promises6.default.access(destDir);
|
|
@@ -56680,16 +56775,16 @@ async function installFromChannel(args, deps) {
|
|
|
56680
56775
|
);
|
|
56681
56776
|
}
|
|
56682
56777
|
const stage = await import_promises6.default.mkdtemp(
|
|
56683
|
-
|
|
56778
|
+
import_node_path53.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
|
|
56684
56779
|
);
|
|
56685
56780
|
try {
|
|
56686
56781
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56687
|
-
const dest =
|
|
56782
|
+
const dest = import_node_path53.default.join(stage, name);
|
|
56688
56783
|
if (entry.dir) {
|
|
56689
56784
|
await import_promises6.default.mkdir(dest, { recursive: true });
|
|
56690
56785
|
continue;
|
|
56691
56786
|
}
|
|
56692
|
-
await import_promises6.default.mkdir(
|
|
56787
|
+
await import_promises6.default.mkdir(import_node_path53.default.dirname(dest), { recursive: true });
|
|
56693
56788
|
if (name === "manifest.json") {
|
|
56694
56789
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56695
56790
|
await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -56710,7 +56805,7 @@ async function installFromChannel(args, deps) {
|
|
|
56710
56805
|
|
|
56711
56806
|
// src/extension/update-flow.ts
|
|
56712
56807
|
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
56713
|
-
var
|
|
56808
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
56714
56809
|
var import_node_os20 = __toESM(require("os"), 1);
|
|
56715
56810
|
var import_node_crypto16 = __toESM(require("crypto"), 1);
|
|
56716
56811
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
@@ -56727,11 +56822,11 @@ async function updateFromChannel(args, deps) {
|
|
|
56727
56822
|
channelRef.extId,
|
|
56728
56823
|
channelRef.ownerPrincipalId
|
|
56729
56824
|
);
|
|
56730
|
-
const liveDir =
|
|
56825
|
+
const liveDir = import_node_path54.default.join(deps.extensionsRoot, localExtId);
|
|
56731
56826
|
const prevDir = `${liveDir}.prev`;
|
|
56732
56827
|
let existingVersion;
|
|
56733
56828
|
try {
|
|
56734
|
-
const raw = await import_promises7.default.readFile(
|
|
56829
|
+
const raw = await import_promises7.default.readFile(import_node_path54.default.join(liveDir, "manifest.json"), "utf8");
|
|
56735
56830
|
const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
|
|
56736
56831
|
if (!parsed2.success) {
|
|
56737
56832
|
throw new UpdateError(
|
|
@@ -56764,7 +56859,7 @@ async function updateFromChannel(args, deps) {
|
|
|
56764
56859
|
throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56765
56860
|
}
|
|
56766
56861
|
for (const name of Object.keys(zip.files)) {
|
|
56767
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
56862
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path54.default.isAbsolute(name)) {
|
|
56768
56863
|
throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56769
56864
|
}
|
|
56770
56865
|
}
|
|
@@ -56799,16 +56894,16 @@ async function updateFromChannel(args, deps) {
|
|
|
56799
56894
|
await import_promises7.default.rm(prevDir, { recursive: true, force: true });
|
|
56800
56895
|
await import_promises7.default.rename(liveDir, prevDir);
|
|
56801
56896
|
const stage = await import_promises7.default.mkdtemp(
|
|
56802
|
-
|
|
56897
|
+
import_node_path54.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
|
|
56803
56898
|
);
|
|
56804
56899
|
try {
|
|
56805
56900
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56806
|
-
const dest =
|
|
56901
|
+
const dest = import_node_path54.default.join(stage, name);
|
|
56807
56902
|
if (entry.dir) {
|
|
56808
56903
|
await import_promises7.default.mkdir(dest, { recursive: true });
|
|
56809
56904
|
continue;
|
|
56810
56905
|
}
|
|
56811
|
-
await import_promises7.default.mkdir(
|
|
56906
|
+
await import_promises7.default.mkdir(import_node_path54.default.dirname(dest), { recursive: true });
|
|
56812
56907
|
if (name === "manifest.json") {
|
|
56813
56908
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56814
56909
|
await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -56901,7 +56996,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
56901
56996
|
);
|
|
56902
56997
|
}
|
|
56903
56998
|
}
|
|
56904
|
-
const manifestPath =
|
|
56999
|
+
const manifestPath = import_node_path55.default.join(root, extId, "manifest.json");
|
|
56905
57000
|
const manifest = await readManifest(root, extId);
|
|
56906
57001
|
const next = { ...manifest, version: newVersion };
|
|
56907
57002
|
const tmp = `${manifestPath}.tmp`;
|
|
@@ -56909,7 +57004,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
56909
57004
|
await import_promises8.default.rename(tmp, manifestPath);
|
|
56910
57005
|
}
|
|
56911
57006
|
async function readManifest(root, extId) {
|
|
56912
|
-
const file =
|
|
57007
|
+
const file = import_node_path55.default.join(root, extId, "manifest.json");
|
|
56913
57008
|
let raw;
|
|
56914
57009
|
try {
|
|
56915
57010
|
raw = await import_promises8.default.readFile(file, "utf8");
|
|
@@ -57000,7 +57095,7 @@ function buildExtensionHandlers(deps) {
|
|
|
57000
57095
|
};
|
|
57001
57096
|
async function buildSnapshotMeta(extId) {
|
|
57002
57097
|
const manifest = await readManifest(deps.root, extId);
|
|
57003
|
-
const { sha256, buffer } = await bundleExtensionDir(
|
|
57098
|
+
const { sha256, buffer } = await bundleExtensionDir(import_node_path55.default.join(deps.root, extId));
|
|
57004
57099
|
return { manifest, contentHash: sha256, buffer };
|
|
57005
57100
|
}
|
|
57006
57101
|
const publish = async (frame, _client, ctx) => {
|
|
@@ -57181,9 +57276,9 @@ function buildExtensionHandlers(deps) {
|
|
|
57181
57276
|
}
|
|
57182
57277
|
|
|
57183
57278
|
// src/app-builder/project-store.ts
|
|
57184
|
-
var
|
|
57279
|
+
var import_node_fs49 = require("fs");
|
|
57185
57280
|
var import_node_child_process14 = require("child_process");
|
|
57186
|
-
var
|
|
57281
|
+
var import_node_path56 = require("path");
|
|
57187
57282
|
init_protocol();
|
|
57188
57283
|
var PROJECTS_DIR = "projects";
|
|
57189
57284
|
var META_FILE = ".clawd-project.json";
|
|
@@ -57197,19 +57292,19 @@ var ProjectStore = class {
|
|
|
57197
57292
|
root;
|
|
57198
57293
|
/** projects/<name>/.clawd-project.json 路径 */
|
|
57199
57294
|
metaPath(name) {
|
|
57200
|
-
return (0,
|
|
57295
|
+
return (0, import_node_path56.join)(this.projectsRoot(), name, META_FILE);
|
|
57201
57296
|
}
|
|
57202
57297
|
/** projects/<name>/ 目录路径(cwd 用) */
|
|
57203
57298
|
projectDir(name) {
|
|
57204
|
-
return (0,
|
|
57299
|
+
return (0, import_node_path56.join)(this.projectsRoot(), name);
|
|
57205
57300
|
}
|
|
57206
57301
|
projectsRoot() {
|
|
57207
|
-
return (0,
|
|
57302
|
+
return (0, import_node_path56.join)(this.root, PROJECTS_DIR);
|
|
57208
57303
|
}
|
|
57209
57304
|
async list() {
|
|
57210
57305
|
let entries;
|
|
57211
57306
|
try {
|
|
57212
|
-
entries = await
|
|
57307
|
+
entries = await import_node_fs49.promises.readdir(this.projectsRoot());
|
|
57213
57308
|
} catch (err) {
|
|
57214
57309
|
if (err.code === "ENOENT") return [];
|
|
57215
57310
|
throw err;
|
|
@@ -57217,7 +57312,7 @@ var ProjectStore = class {
|
|
|
57217
57312
|
const out = [];
|
|
57218
57313
|
for (const name of entries) {
|
|
57219
57314
|
try {
|
|
57220
|
-
const raw = await
|
|
57315
|
+
const raw = await import_node_fs49.promises.readFile(this.metaPath(name), "utf8");
|
|
57221
57316
|
const json = JSON.parse(raw);
|
|
57222
57317
|
let migrated = false;
|
|
57223
57318
|
if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
|
|
@@ -57228,7 +57323,7 @@ var ProjectStore = class {
|
|
|
57228
57323
|
if (parsed.success) {
|
|
57229
57324
|
out.push(parsed.data);
|
|
57230
57325
|
if (migrated) {
|
|
57231
|
-
void
|
|
57326
|
+
void import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
|
|
57232
57327
|
});
|
|
57233
57328
|
}
|
|
57234
57329
|
}
|
|
@@ -57272,8 +57367,8 @@ var ProjectStore = class {
|
|
|
57272
57367
|
throw new Error(`invalid name "${name}": ${validated.error.message}`);
|
|
57273
57368
|
}
|
|
57274
57369
|
const dir = this.projectDir(name);
|
|
57275
|
-
await
|
|
57276
|
-
await
|
|
57370
|
+
await import_node_fs49.promises.mkdir(dir, { recursive: true });
|
|
57371
|
+
await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
|
|
57277
57372
|
return meta;
|
|
57278
57373
|
}
|
|
57279
57374
|
/**
|
|
@@ -57316,7 +57411,7 @@ var ProjectStore = class {
|
|
|
57316
57411
|
}
|
|
57317
57412
|
async delete(name) {
|
|
57318
57413
|
const dir = this.projectDir(name);
|
|
57319
|
-
await
|
|
57414
|
+
await import_node_fs49.promises.rm(dir, { recursive: true, force: true });
|
|
57320
57415
|
}
|
|
57321
57416
|
async updatePort(name, newPort) {
|
|
57322
57417
|
if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
|
|
@@ -57332,7 +57427,7 @@ var ProjectStore = class {
|
|
|
57332
57427
|
throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
|
|
57333
57428
|
}
|
|
57334
57429
|
const updated = { ...target, port: newPort };
|
|
57335
|
-
await
|
|
57430
|
+
await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
57336
57431
|
return updated;
|
|
57337
57432
|
}
|
|
57338
57433
|
/**
|
|
@@ -57349,7 +57444,7 @@ var ProjectStore = class {
|
|
|
57349
57444
|
if (!validated.success) {
|
|
57350
57445
|
throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
|
|
57351
57446
|
}
|
|
57352
|
-
await
|
|
57447
|
+
await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57353
57448
|
return validated.data;
|
|
57354
57449
|
}
|
|
57355
57450
|
/**
|
|
@@ -57370,7 +57465,7 @@ var ProjectStore = class {
|
|
|
57370
57465
|
if (!validated.success) {
|
|
57371
57466
|
throw new Error(`invalid publishJob: ${validated.error.message}`);
|
|
57372
57467
|
}
|
|
57373
|
-
await
|
|
57468
|
+
await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57374
57469
|
return validated.data;
|
|
57375
57470
|
}
|
|
57376
57471
|
/** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
|
|
@@ -57385,7 +57480,7 @@ var ProjectStore = class {
|
|
|
57385
57480
|
if (!validated.success) {
|
|
57386
57481
|
throw new Error(`failed to clear publishJob: ${validated.error.message}`);
|
|
57387
57482
|
}
|
|
57388
|
-
await
|
|
57483
|
+
await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57389
57484
|
return validated.data;
|
|
57390
57485
|
}
|
|
57391
57486
|
};
|
|
@@ -57506,8 +57601,8 @@ var PublishJobRegistry = class {
|
|
|
57506
57601
|
|
|
57507
57602
|
// src/app-builder/publish-job-runner.ts
|
|
57508
57603
|
var import_node_child_process16 = require("child_process");
|
|
57509
|
-
var
|
|
57510
|
-
var
|
|
57604
|
+
var import_node_fs50 = require("fs");
|
|
57605
|
+
var import_node_path57 = require("path");
|
|
57511
57606
|
|
|
57512
57607
|
// src/app-builder/publish-stage-parser.ts
|
|
57513
57608
|
var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
|
|
@@ -57534,19 +57629,19 @@ function tailStderrLines(buf, n) {
|
|
|
57534
57629
|
// src/app-builder/publish-job-runner.ts
|
|
57535
57630
|
async function startPublishJob(deps, args) {
|
|
57536
57631
|
const { registry: registry2, projectDir } = deps;
|
|
57537
|
-
const
|
|
57632
|
+
const spawn16 = deps.spawnImpl ?? import_node_child_process16.spawn;
|
|
57538
57633
|
if (registry2.has(args.name)) {
|
|
57539
57634
|
return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
|
|
57540
57635
|
}
|
|
57541
57636
|
const projDir = projectDir(args.name);
|
|
57542
|
-
const logPath = (0,
|
|
57637
|
+
const logPath = (0, import_node_path57.join)(projDir, ".publish.log");
|
|
57543
57638
|
let logStream = null;
|
|
57544
57639
|
try {
|
|
57545
|
-
logStream = (0,
|
|
57640
|
+
logStream = (0, import_node_fs50.createWriteStream)(logPath, { flags: "w" });
|
|
57546
57641
|
} catch {
|
|
57547
57642
|
logStream = null;
|
|
57548
57643
|
}
|
|
57549
|
-
const child =
|
|
57644
|
+
const child = spawn16("bash", [args.scriptPath, projDir, args.personaRoot ?? ""], {
|
|
57550
57645
|
cwd: projDir,
|
|
57551
57646
|
env: process.env,
|
|
57552
57647
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -57799,8 +57894,8 @@ async function recoverInterruptedJobs(deps) {
|
|
|
57799
57894
|
|
|
57800
57895
|
// src/handlers/app-builder.ts
|
|
57801
57896
|
init_protocol();
|
|
57802
|
-
var
|
|
57803
|
-
var
|
|
57897
|
+
var import_node_path58 = require("path");
|
|
57898
|
+
var import_node_fs51 = require("fs");
|
|
57804
57899
|
var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
|
|
57805
57900
|
var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
|
|
57806
57901
|
async function recoverInterruptedPublishJobs(store, logger) {
|
|
@@ -57881,7 +57976,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
57881
57976
|
async function listAllUsersProjects() {
|
|
57882
57977
|
if (!deps.usersRoot || !deps.getStore) return [];
|
|
57883
57978
|
const getStore = deps.getStore;
|
|
57884
|
-
const userIds = await
|
|
57979
|
+
const userIds = await import_node_fs51.promises.readdir(deps.usersRoot).catch(() => []);
|
|
57885
57980
|
const perUser = await Promise.all(
|
|
57886
57981
|
userIds.map((uid) => getStore(uid).list().catch(() => []))
|
|
57887
57982
|
);
|
|
@@ -57957,8 +58052,8 @@ function buildAppBuilderHandlers(deps) {
|
|
|
57957
58052
|
const project = await userStore.create(f.name, reservedPorts);
|
|
57958
58053
|
try {
|
|
57959
58054
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
|
|
57960
|
-
const templateSrcDir = (0,
|
|
57961
|
-
const scaffoldScript = (0,
|
|
58055
|
+
const templateSrcDir = (0, import_node_path58.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
|
|
58056
|
+
const scaffoldScript = (0, import_node_path58.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
|
|
57962
58057
|
const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
|
|
57963
58058
|
deps.logger?.info("app-builder.scaffold.done", {
|
|
57964
58059
|
name: project.name,
|
|
@@ -58179,7 +58274,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
58179
58274
|
await userStore.clearPublishJob(args.name);
|
|
58180
58275
|
}
|
|
58181
58276
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
|
|
58182
|
-
const scriptPath = (0,
|
|
58277
|
+
const scriptPath = (0, import_node_path58.join)(deps.deployKitRoot, "scripts", "publish.sh");
|
|
58183
58278
|
deps.logger?.info("app-builder.publish.start", {
|
|
58184
58279
|
name: args.name,
|
|
58185
58280
|
sessionId: boundSession.sessionId,
|
|
@@ -58355,7 +58450,7 @@ function buildVisitorHandlers(deps) {
|
|
|
58355
58450
|
|
|
58356
58451
|
// src/extension/registry.ts
|
|
58357
58452
|
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
58358
|
-
var
|
|
58453
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
58359
58454
|
async function loadAll(root) {
|
|
58360
58455
|
let entries;
|
|
58361
58456
|
try {
|
|
@@ -58368,13 +58463,13 @@ async function loadAll(root) {
|
|
|
58368
58463
|
for (const ent of entries) {
|
|
58369
58464
|
if (!ent.isDirectory()) continue;
|
|
58370
58465
|
if (ent.name.startsWith(".")) continue;
|
|
58371
|
-
records.push(await loadOne(
|
|
58466
|
+
records.push(await loadOne(import_node_path59.default.join(root, ent.name), ent.name));
|
|
58372
58467
|
}
|
|
58373
58468
|
records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
|
|
58374
58469
|
return records;
|
|
58375
58470
|
}
|
|
58376
58471
|
async function loadOne(dir, dirName) {
|
|
58377
|
-
const manifestPath =
|
|
58472
|
+
const manifestPath = import_node_path59.default.join(dir, "manifest.json");
|
|
58378
58473
|
let raw;
|
|
58379
58474
|
try {
|
|
58380
58475
|
raw = await import_promises9.default.readFile(manifestPath, "utf8");
|
|
@@ -58419,7 +58514,7 @@ async function loadOne(dir, dirName) {
|
|
|
58419
58514
|
|
|
58420
58515
|
// src/extension/uninstall.ts
|
|
58421
58516
|
var import_promises10 = __toESM(require("fs/promises"), 1);
|
|
58422
|
-
var
|
|
58517
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
58423
58518
|
var UninstallError = class extends Error {
|
|
58424
58519
|
constructor(code, message) {
|
|
58425
58520
|
super(message);
|
|
@@ -58428,7 +58523,7 @@ var UninstallError = class extends Error {
|
|
|
58428
58523
|
code;
|
|
58429
58524
|
};
|
|
58430
58525
|
async function uninstall(deps) {
|
|
58431
|
-
const dir =
|
|
58526
|
+
const dir = import_node_path60.default.join(deps.root, deps.extId);
|
|
58432
58527
|
try {
|
|
58433
58528
|
await import_promises10.default.access(dir);
|
|
58434
58529
|
} catch {
|
|
@@ -58440,6 +58535,131 @@ async function uninstall(deps) {
|
|
|
58440
58535
|
|
|
58441
58536
|
// src/handlers/index.ts
|
|
58442
58537
|
var import_node_crypto18 = require("crypto");
|
|
58538
|
+
|
|
58539
|
+
// src/handlers/peer-exec.ts
|
|
58540
|
+
init_protocol();
|
|
58541
|
+
|
|
58542
|
+
// src/peer-ops/run-command.ts
|
|
58543
|
+
var import_node_child_process17 = require("child_process");
|
|
58544
|
+
var import_node_os21 = __toESM(require("os"), 1);
|
|
58545
|
+
var DEFAULT_TIMEOUT_MS5 = 3e4;
|
|
58546
|
+
var MAX_TIMEOUT_MS = 3e5;
|
|
58547
|
+
var MAX_OUTPUT_BYTES = 1048576;
|
|
58548
|
+
function runCommand(command, opts = {}) {
|
|
58549
|
+
const timeoutMs = Math.min(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS5, MAX_TIMEOUT_MS);
|
|
58550
|
+
return new Promise((resolve6) => {
|
|
58551
|
+
const child = (0, import_node_child_process17.spawn)("bash", ["-c", command], { cwd: import_node_os21.default.homedir() });
|
|
58552
|
+
const out = [];
|
|
58553
|
+
const err = [];
|
|
58554
|
+
let outBytes = 0;
|
|
58555
|
+
let errBytes = 0;
|
|
58556
|
+
let outTrunc = false;
|
|
58557
|
+
let errTrunc = false;
|
|
58558
|
+
let timedOut = false;
|
|
58559
|
+
const capture = (chunk, buf, which) => {
|
|
58560
|
+
const used = which === "out" ? outBytes : errBytes;
|
|
58561
|
+
const room = MAX_OUTPUT_BYTES - used;
|
|
58562
|
+
if (room <= 0) {
|
|
58563
|
+
if (which === "out") outTrunc = true;
|
|
58564
|
+
else errTrunc = true;
|
|
58565
|
+
return;
|
|
58566
|
+
}
|
|
58567
|
+
if (chunk.length > room) {
|
|
58568
|
+
buf.push(chunk.subarray(0, room));
|
|
58569
|
+
if (which === "out") {
|
|
58570
|
+
outBytes += room;
|
|
58571
|
+
outTrunc = true;
|
|
58572
|
+
} else {
|
|
58573
|
+
errBytes += room;
|
|
58574
|
+
errTrunc = true;
|
|
58575
|
+
}
|
|
58576
|
+
} else {
|
|
58577
|
+
buf.push(chunk);
|
|
58578
|
+
if (which === "out") outBytes += chunk.length;
|
|
58579
|
+
else errBytes += chunk.length;
|
|
58580
|
+
}
|
|
58581
|
+
};
|
|
58582
|
+
child.stdout.on("data", (c) => capture(c, out, "out"));
|
|
58583
|
+
child.stderr.on("data", (c) => capture(c, err, "err"));
|
|
58584
|
+
const timer = setTimeout(() => {
|
|
58585
|
+
timedOut = true;
|
|
58586
|
+
child.kill("SIGTERM");
|
|
58587
|
+
setTimeout(() => child.kill("SIGKILL"), 2e3).unref();
|
|
58588
|
+
}, timeoutMs);
|
|
58589
|
+
child.on("close", (code) => {
|
|
58590
|
+
clearTimeout(timer);
|
|
58591
|
+
resolve6({
|
|
58592
|
+
stdout: Buffer.concat(out).toString("utf8"),
|
|
58593
|
+
stderr: Buffer.concat(err).toString("utf8"),
|
|
58594
|
+
exitCode: timedOut ? null : code,
|
|
58595
|
+
timedOut,
|
|
58596
|
+
stdoutTruncated: outTrunc,
|
|
58597
|
+
stderrTruncated: errTrunc
|
|
58598
|
+
});
|
|
58599
|
+
});
|
|
58600
|
+
child.on("error", () => {
|
|
58601
|
+
clearTimeout(timer);
|
|
58602
|
+
resolve6({
|
|
58603
|
+
stdout: Buffer.concat(out).toString("utf8"),
|
|
58604
|
+
stderr: Buffer.concat(err).toString("utf8"),
|
|
58605
|
+
exitCode: null,
|
|
58606
|
+
timedOut,
|
|
58607
|
+
stdoutTruncated: outTrunc,
|
|
58608
|
+
stderrTruncated: errTrunc
|
|
58609
|
+
});
|
|
58610
|
+
});
|
|
58611
|
+
});
|
|
58612
|
+
}
|
|
58613
|
+
|
|
58614
|
+
// src/handlers/peer-exec.ts
|
|
58615
|
+
function buildPeerExecHandlers(deps) {
|
|
58616
|
+
const run = async (frame, _client, ctx) => {
|
|
58617
|
+
const { type: _t, requestId: _r, sessionId: _s, ...rest } = frame;
|
|
58618
|
+
const args = PeerExecRunArgsSchema.parse(rest);
|
|
58619
|
+
if (args.peerDeviceId) {
|
|
58620
|
+
if (ctx?.principal.kind === "guest") {
|
|
58621
|
+
throw new ClawdError(
|
|
58622
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
58623
|
+
"UNAUTHORIZED: cross-device exec cannot be initiated by a guest"
|
|
58624
|
+
);
|
|
58625
|
+
}
|
|
58626
|
+
const contact2 = deps.store.get(args.peerDeviceId);
|
|
58627
|
+
if (!contact2) {
|
|
58628
|
+
throw new ClawdError(
|
|
58629
|
+
ERROR_CODES.CONTACT_NOT_FOUND,
|
|
58630
|
+
`CONTACT_NOT_FOUND: ${args.peerDeviceId} not in store`
|
|
58631
|
+
);
|
|
58632
|
+
}
|
|
58633
|
+
const outcome = await deps.forwardToPeer({
|
|
58634
|
+
contact: { remoteUrl: contact2.remoteUrl, connectToken: contact2.connectToken },
|
|
58635
|
+
command: args.command,
|
|
58636
|
+
...args.timeoutMs ? { timeoutMs: args.timeoutMs } : {}
|
|
58637
|
+
});
|
|
58638
|
+
return { response: outcome };
|
|
58639
|
+
}
|
|
58640
|
+
const callerId = ensureGuest(ctx);
|
|
58641
|
+
const contact = deps.store.get(callerId);
|
|
58642
|
+
if (!contact?.remoteAccessAllowed) {
|
|
58643
|
+
throw new ClawdError(
|
|
58644
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
58645
|
+
`UNAUTHORIZED: ${callerId} not authorized for remote access`
|
|
58646
|
+
);
|
|
58647
|
+
}
|
|
58648
|
+
const result = await runCommand(args.command, args.timeoutMs ? { timeoutMs: args.timeoutMs } : {});
|
|
58649
|
+
return {
|
|
58650
|
+
response: { type: "peerExec:run:ok", ...result }
|
|
58651
|
+
};
|
|
58652
|
+
};
|
|
58653
|
+
return { "peerExec:run": run };
|
|
58654
|
+
}
|
|
58655
|
+
function ensureGuest(ctx) {
|
|
58656
|
+
if (!ctx || ctx.principal.kind !== "guest") {
|
|
58657
|
+
throw new ClawdError(ERROR_CODES.UNAUTHORIZED, "UNAUTHORIZED: peerExec:run requires guest ctx");
|
|
58658
|
+
}
|
|
58659
|
+
return ctx.principal.id;
|
|
58660
|
+
}
|
|
58661
|
+
|
|
58662
|
+
// src/handlers/index.ts
|
|
58443
58663
|
function buildMethodHandlers(deps) {
|
|
58444
58664
|
return {
|
|
58445
58665
|
...buildSessionHandlers({
|
|
@@ -58489,6 +58709,12 @@ function buildMethodHandlers(deps) {
|
|
|
58489
58709
|
sshdDir: deps.sshdDir,
|
|
58490
58710
|
sshLog: deps.contactSshLog
|
|
58491
58711
|
}),
|
|
58712
|
+
// 跨设备 exec(spec 2026-07-16):A 侧带 peerDeviceId → forwardPeerExecToPeer;
|
|
58713
|
+
// B 侧无 peerDeviceId → 本地 runCommand(handler 判 remoteAccessAllowed)。
|
|
58714
|
+
...buildPeerExecHandlers({
|
|
58715
|
+
store: deps.contactStore,
|
|
58716
|
+
forwardToPeer: (a) => forwardPeerExecToPeer(a)
|
|
58717
|
+
}),
|
|
58492
58718
|
whoami: buildWhoamiHandler({
|
|
58493
58719
|
ownerDisplayName: deps.ownerDisplayName,
|
|
58494
58720
|
ownerPrincipalId: deps.ownerPrincipalId,
|
|
@@ -58540,7 +58766,7 @@ function buildMethodHandlers(deps) {
|
|
|
58540
58766
|
}
|
|
58541
58767
|
|
|
58542
58768
|
// src/app-builder/dev-server-supervisor.ts
|
|
58543
|
-
var
|
|
58769
|
+
var import_node_child_process18 = require("child_process");
|
|
58544
58770
|
var import_node_events2 = require("events");
|
|
58545
58771
|
var DEFAULT_READY_PATTERN = /Local:\s+https?:\/\/|Nest application successfully started|server listening on/i;
|
|
58546
58772
|
var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
@@ -58577,7 +58803,7 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
|
58577
58803
|
tunnelHost: args.tunnelHost,
|
|
58578
58804
|
devCommand: cmd
|
|
58579
58805
|
});
|
|
58580
|
-
const child = (0,
|
|
58806
|
+
const child = (0, import_node_child_process18.spawn)("sh", ["-c", cmd], {
|
|
58581
58807
|
cwd: args.cwd,
|
|
58582
58808
|
env,
|
|
58583
58809
|
stdio: "pipe",
|
|
@@ -58829,9 +59055,9 @@ var METHOD_GRANT_MAP = {
|
|
|
58829
59055
|
"contact:list": ADMIN_ANY,
|
|
58830
59056
|
"contact:pin": ADMIN_ANY,
|
|
58831
59057
|
"contact:remove": ADMIN_ANY,
|
|
58832
|
-
// contact:
|
|
59058
|
+
// contact:setRemoteAccess (owner UI 配 SSH 授权):ADMIN_ANY
|
|
58833
59059
|
// (对齐 inbox:postMessage 的"能连上=有 auth,业务在 handler 校"模式)
|
|
58834
|
-
"contact:
|
|
59060
|
+
"contact:setRemoteAccess": ADMIN_ANY,
|
|
58835
59061
|
// ---- visitor:* (访客名单,owner-only) ----
|
|
58836
59062
|
// owner 看完整访客名单(含没开会话的);guest 不可调(handler 内再 assertOwner 兜底)。
|
|
58837
59063
|
"visitor:list": ADMIN_ANY,
|
|
@@ -58910,6 +59136,9 @@ var METHOD_GRANT_MAP = {
|
|
|
58910
59136
|
// run handler 内做 persona.public + assertGrant(send) 双重校验(guest grants 是 wildcard,
|
|
58911
59137
|
// 单 assertGrant 不够)。本地 cc 仍走 /api/rpc(EXPOSED_RPC_METHODS),不经此表。
|
|
58912
59138
|
"personaDispatch:run": CAPABILITY_SCOPED,
|
|
59139
|
+
// 跨设备 exec(spec 2026-07-16):public——"能连上=有 auth",业务在 handler 校
|
|
59140
|
+
// (ensureGuest + remoteAccessAllowed),跟 inbox:postMessage 同模式。
|
|
59141
|
+
"peerExec:run": { kind: "public" },
|
|
58913
59142
|
// 2026-05-26 修 clawd people TUI 渲染 mix 串扰回归(参考 f1133ff0 原 owner 路径
|
|
58914
59143
|
// snapshot/buffer/flush 修复):之前三件套 ADMIN_ANY 把 guest 拦在 dispatcher 401,
|
|
58915
59144
|
// 导致 XtermPanel 重挂载拉 snapshot 在 guest 路径不生效,老的字节流断裂串扰回来。
|
|
@@ -59016,8 +59245,8 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
|
|
|
59016
59245
|
}
|
|
59017
59246
|
|
|
59018
59247
|
// src/extension/runtime.ts
|
|
59019
|
-
var
|
|
59020
|
-
var
|
|
59248
|
+
var import_node_child_process19 = require("child_process");
|
|
59249
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
59021
59250
|
var import_promises11 = require("timers/promises");
|
|
59022
59251
|
|
|
59023
59252
|
// src/extension/port-allocator.ts
|
|
@@ -59118,13 +59347,13 @@ var Runtime = class {
|
|
|
59118
59347
|
/\$CLAWOS_EXT_PORT/g,
|
|
59119
59348
|
String(port)
|
|
59120
59349
|
);
|
|
59121
|
-
const dir =
|
|
59350
|
+
const dir = import_node_path61.default.join(this.root, extId);
|
|
59122
59351
|
const env = {
|
|
59123
59352
|
...process.env,
|
|
59124
59353
|
CLAWOS_EXT_PORT: String(port),
|
|
59125
59354
|
CLAWOS_EXT_ID: extId
|
|
59126
59355
|
};
|
|
59127
|
-
const child = (0,
|
|
59356
|
+
const child = (0, import_node_child_process19.spawn)("sh", ["-c", cmd], {
|
|
59128
59357
|
cwd: dir,
|
|
59129
59358
|
env,
|
|
59130
59359
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -59230,7 +59459,7 @@ ${handle.stderrTail}`
|
|
|
59230
59459
|
|
|
59231
59460
|
// src/extension/published-channels.ts
|
|
59232
59461
|
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
59233
|
-
var
|
|
59462
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
59234
59463
|
init_zod();
|
|
59235
59464
|
var PublishedChannelsError = class extends Error {
|
|
59236
59465
|
constructor(code, message) {
|
|
@@ -59329,7 +59558,7 @@ var PublishedChannelStore = class {
|
|
|
59329
59558
|
)
|
|
59330
59559
|
};
|
|
59331
59560
|
const tmp = `${this.filePath}.tmp`;
|
|
59332
|
-
await import_promises12.default.mkdir(
|
|
59561
|
+
await import_promises12.default.mkdir(import_node_path62.default.dirname(this.filePath), { recursive: true });
|
|
59333
59562
|
await import_promises12.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
59334
59563
|
await import_promises12.default.rename(tmp, this.filePath);
|
|
59335
59564
|
}
|
|
@@ -59337,7 +59566,7 @@ var PublishedChannelStore = class {
|
|
|
59337
59566
|
|
|
59338
59567
|
// src/extension/bundle-cache.ts
|
|
59339
59568
|
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
59340
|
-
var
|
|
59569
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
59341
59570
|
var BundleCache = class {
|
|
59342
59571
|
constructor(rootDir) {
|
|
59343
59572
|
this.rootDir = rootDir;
|
|
@@ -59346,14 +59575,14 @@ var BundleCache = class {
|
|
|
59346
59575
|
/** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
|
|
59347
59576
|
async write(snapshotHash, buffer) {
|
|
59348
59577
|
await import_promises13.default.mkdir(this.rootDir, { recursive: true });
|
|
59349
|
-
const file =
|
|
59578
|
+
const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59350
59579
|
const tmp = `${file}.tmp`;
|
|
59351
59580
|
await import_promises13.default.writeFile(tmp, buffer, { mode: 384 });
|
|
59352
59581
|
await import_promises13.default.rename(tmp, file);
|
|
59353
59582
|
}
|
|
59354
59583
|
/** Returns the bundle bytes, or null when the file doesn't exist. */
|
|
59355
59584
|
async read(snapshotHash) {
|
|
59356
|
-
const file =
|
|
59585
|
+
const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59357
59586
|
try {
|
|
59358
59587
|
return await import_promises13.default.readFile(file);
|
|
59359
59588
|
} catch (e) {
|
|
@@ -59363,7 +59592,7 @@ var BundleCache = class {
|
|
|
59363
59592
|
}
|
|
59364
59593
|
/** Idempotent — missing file is not an error. */
|
|
59365
59594
|
async delete(snapshotHash) {
|
|
59366
|
-
const file =
|
|
59595
|
+
const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59367
59596
|
await import_promises13.default.rm(file, { force: true });
|
|
59368
59597
|
}
|
|
59369
59598
|
};
|
|
@@ -59372,7 +59601,7 @@ var BundleCache = class {
|
|
|
59372
59601
|
var import_meta6 = {};
|
|
59373
59602
|
function currentOsUser() {
|
|
59374
59603
|
try {
|
|
59375
|
-
return
|
|
59604
|
+
return import_node_os22.default.userInfo().username;
|
|
59376
59605
|
} catch {
|
|
59377
59606
|
return "";
|
|
59378
59607
|
}
|
|
@@ -59391,26 +59620,26 @@ async function startDaemon(config) {
|
|
|
59391
59620
|
},
|
|
59392
59621
|
source: "daemon",
|
|
59393
59622
|
sampling: logShippingCfg.sampling,
|
|
59394
|
-
homeDir:
|
|
59623
|
+
homeDir: import_node_os22.default.homedir()
|
|
59395
59624
|
});
|
|
59396
|
-
const logDir =
|
|
59397
|
-
|
|
59625
|
+
const logDir = import_node_path64.default.join(config.dataDir, "log");
|
|
59626
|
+
import_node_fs52.default.mkdirSync(logDir, { recursive: true });
|
|
59398
59627
|
const logger = createLogger({
|
|
59399
59628
|
level: config.logLevel,
|
|
59400
|
-
file:
|
|
59629
|
+
file: import_node_path64.default.join(logDir, "clawd.log"),
|
|
59401
59630
|
logClient
|
|
59402
59631
|
});
|
|
59403
59632
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
59404
59633
|
const screenIdleProbeLogger = createFileOnlyLogger({
|
|
59405
|
-
file:
|
|
59634
|
+
file: import_node_path64.default.join(logDir, "screen-idle-probe.log"),
|
|
59406
59635
|
level: "debug"
|
|
59407
59636
|
});
|
|
59408
59637
|
logger.info("screen-idle probe logger enabled", {
|
|
59409
|
-
file:
|
|
59638
|
+
file: import_node_path64.default.join(logDir, "screen-idle-probe.log")
|
|
59410
59639
|
});
|
|
59411
59640
|
const CAP_TARGETS = [
|
|
59412
|
-
|
|
59413
|
-
|
|
59641
|
+
import_node_path64.default.join(logDir, "clawd.log"),
|
|
59642
|
+
import_node_path64.default.join(logDir, "screen-idle-probe.log")
|
|
59414
59643
|
];
|
|
59415
59644
|
const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
|
|
59416
59645
|
const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
|
|
@@ -59521,7 +59750,7 @@ async function startDaemon(config) {
|
|
|
59521
59750
|
// 立即重建 authorized_keys(重新握手 = 迁移路径)。见 …#建联交换清单
|
|
59522
59751
|
sshPublicKey: selfSshPublicKey,
|
|
59523
59752
|
osUser: selfOsUser,
|
|
59524
|
-
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore,
|
|
59753
|
+
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path64.default.join(config.dataDir, "sshd")),
|
|
59525
59754
|
// 决策 #16 换票补全:用 owner 当前 jwt 换 aud=对方设备 的票(本机主动连对方用)。
|
|
59526
59755
|
// 现读 ownerIdentityStore(热切换后用新身份);失败 → null → 保持空票。
|
|
59527
59756
|
exchangeToken: async (targetDeviceId) => {
|
|
@@ -59569,8 +59798,8 @@ async function startDaemon(config) {
|
|
|
59569
59798
|
const agents = new AgentsScanner();
|
|
59570
59799
|
const history = new ClaudeHistoryReader();
|
|
59571
59800
|
let transport = null;
|
|
59572
|
-
const personaStore = new PersonaStore(
|
|
59573
|
-
const usersRoot =
|
|
59801
|
+
const personaStore = new PersonaStore(import_node_path64.default.join(config.dataDir, "personas"));
|
|
59802
|
+
const usersRoot = import_node_path64.default.join(config.dataDir, "users");
|
|
59574
59803
|
const defaultsRoot = findDefaultsRoot(logger);
|
|
59575
59804
|
if (defaultsRoot) {
|
|
59576
59805
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -59590,17 +59819,17 @@ async function startDaemon(config) {
|
|
|
59590
59819
|
migrateCodexSandbox({ store: personaStore, logger });
|
|
59591
59820
|
const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
|
|
59592
59821
|
const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
|
|
59593
|
-
const here = typeof __dirname === "string" ? __dirname :
|
|
59822
|
+
const here = typeof __dirname === "string" ? __dirname : import_node_path64.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
|
|
59594
59823
|
const mcpConfigs = [];
|
|
59595
59824
|
const dispatchServerCandidates = [
|
|
59596
|
-
|
|
59825
|
+
import_node_path64.default.join(here, "dispatch", "mcp-server.cjs"),
|
|
59597
59826
|
// 生产 dist/index → dist/dispatch/mcp-server.cjs
|
|
59598
|
-
|
|
59827
|
+
import_node_path64.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
|
|
59599
59828
|
// dev tsx src/index → ../dist/dispatch/mcp-server.cjs
|
|
59600
59829
|
];
|
|
59601
|
-
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) =>
|
|
59830
|
+
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
|
|
59602
59831
|
if (dispatchServerScriptPath) {
|
|
59603
|
-
const dispatchLogPath =
|
|
59832
|
+
const dispatchLogPath = import_node_path64.default.join(logDir, "dispatch-mcp-server.log");
|
|
59604
59833
|
const dispatchCfgPath = writeDispatchMcpConfig({
|
|
59605
59834
|
dataDir: config.dataDir,
|
|
59606
59835
|
serverScriptPath: dispatchServerScriptPath,
|
|
@@ -59618,12 +59847,12 @@ async function startDaemon(config) {
|
|
|
59618
59847
|
});
|
|
59619
59848
|
}
|
|
59620
59849
|
const ticketServerCandidates = [
|
|
59621
|
-
|
|
59622
|
-
|
|
59850
|
+
import_node_path64.default.join(here, "ticket", "mcp-server.cjs"),
|
|
59851
|
+
import_node_path64.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
|
|
59623
59852
|
];
|
|
59624
|
-
const ticketServerScriptPath = ticketServerCandidates.find((p2) =>
|
|
59853
|
+
const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
|
|
59625
59854
|
if (ticketServerScriptPath) {
|
|
59626
|
-
const ticketLogPath =
|
|
59855
|
+
const ticketLogPath = import_node_path64.default.join(logDir, "ticket-mcp-server.log");
|
|
59627
59856
|
const ticketCfgPath = writeTicketMcpConfig({
|
|
59628
59857
|
dataDir: config.dataDir,
|
|
59629
59858
|
serverScriptPath: ticketServerScriptPath,
|
|
@@ -59643,12 +59872,12 @@ async function startDaemon(config) {
|
|
|
59643
59872
|
});
|
|
59644
59873
|
}
|
|
59645
59874
|
const shiftServerCandidates = [
|
|
59646
|
-
|
|
59647
|
-
|
|
59875
|
+
import_node_path64.default.join(here, "shift", "mcp-server.cjs"),
|
|
59876
|
+
import_node_path64.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
|
|
59648
59877
|
];
|
|
59649
|
-
const shiftServerScriptPath = shiftServerCandidates.find((p2) =>
|
|
59878
|
+
const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
|
|
59650
59879
|
if (shiftServerScriptPath) {
|
|
59651
|
-
const shiftLogPath =
|
|
59880
|
+
const shiftLogPath = import_node_path64.default.join(logDir, "shift-mcp-server.log");
|
|
59652
59881
|
const shiftCfgPath = await writeShiftMcpConfig({
|
|
59653
59882
|
dataDir: config.dataDir,
|
|
59654
59883
|
serverScriptPath: shiftServerScriptPath,
|
|
@@ -59667,12 +59896,12 @@ async function startDaemon(config) {
|
|
|
59667
59896
|
);
|
|
59668
59897
|
}
|
|
59669
59898
|
const inboxServerCandidates = [
|
|
59670
|
-
|
|
59671
|
-
|
|
59899
|
+
import_node_path64.default.join(here, "inbox", "mcp-server.cjs"),
|
|
59900
|
+
import_node_path64.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
|
|
59672
59901
|
];
|
|
59673
|
-
const inboxServerScriptPath = inboxServerCandidates.find((p2) =>
|
|
59902
|
+
const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
|
|
59674
59903
|
if (inboxServerScriptPath) {
|
|
59675
|
-
const inboxLogPath =
|
|
59904
|
+
const inboxLogPath = import_node_path64.default.join(logDir, "inbox-mcp-server.log");
|
|
59676
59905
|
const inboxCfgPath = await writeInboxMcpConfig({
|
|
59677
59906
|
dataDir: config.dataDir,
|
|
59678
59907
|
serverScriptPath: inboxServerScriptPath,
|
|
@@ -59690,8 +59919,29 @@ async function startDaemon(config) {
|
|
|
59690
59919
|
{ tried: inboxServerCandidates }
|
|
59691
59920
|
);
|
|
59692
59921
|
}
|
|
59922
|
+
const peerOpsServerCandidates = [
|
|
59923
|
+
import_node_path64.default.join(here, "peer-ops", "mcp-server.cjs"),
|
|
59924
|
+
import_node_path64.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
|
|
59925
|
+
];
|
|
59926
|
+
const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
|
|
59927
|
+
if (peerOpsServerScriptPath) {
|
|
59928
|
+
const peerOpsCfgPath = writePeerOpsMcpConfig({
|
|
59929
|
+
dataDir: config.dataDir,
|
|
59930
|
+
serverScriptPath: peerOpsServerScriptPath
|
|
59931
|
+
});
|
|
59932
|
+
mcpConfigs.push({ id: "peer-ops", path: peerOpsCfgPath });
|
|
59933
|
+
logger.info("peer-ops.mcp.json written", {
|
|
59934
|
+
path: peerOpsCfgPath,
|
|
59935
|
+
server: peerOpsServerScriptPath
|
|
59936
|
+
});
|
|
59937
|
+
} else {
|
|
59938
|
+
logger.warn(
|
|
59939
|
+
"peer-ops-mcp-server.cjs not found; cross-device exec MCP tool disabled (dev tsx \u6A21\u5F0F\u4E0B\u9700\u5148\u8DD1\u8FC7 build)",
|
|
59940
|
+
{ tried: peerOpsServerCandidates }
|
|
59941
|
+
);
|
|
59942
|
+
}
|
|
59693
59943
|
const shiftStore = createShiftStore({
|
|
59694
|
-
filePath:
|
|
59944
|
+
filePath: import_node_path64.default.join(config.dataDir, "shift.json"),
|
|
59695
59945
|
ownerIdProvider: () => ownerPrincipalId,
|
|
59696
59946
|
now: () => Date.now()
|
|
59697
59947
|
});
|
|
@@ -59713,7 +59963,7 @@ async function startDaemon(config) {
|
|
|
59713
59963
|
getAdapter,
|
|
59714
59964
|
historyReader: history,
|
|
59715
59965
|
dataDir: config.dataDir,
|
|
59716
|
-
personaRoot:
|
|
59966
|
+
personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
|
|
59717
59967
|
usersRoot,
|
|
59718
59968
|
personaStore,
|
|
59719
59969
|
ownerDisplayName,
|
|
@@ -59751,10 +60001,10 @@ async function startDaemon(config) {
|
|
|
59751
60001
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
59752
60002
|
attachmentGroup: {
|
|
59753
60003
|
onFileEdit: (input) => {
|
|
59754
|
-
const absPath =
|
|
60004
|
+
const absPath = import_node_path64.default.isAbsolute(input.relPath) ? input.relPath : import_node_path64.default.join(input.cwd, input.relPath);
|
|
59755
60005
|
let size = 0;
|
|
59756
60006
|
try {
|
|
59757
|
-
size =
|
|
60007
|
+
size = import_node_fs52.default.statSync(absPath).size;
|
|
59758
60008
|
} catch (err) {
|
|
59759
60009
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
59760
60010
|
sessionId: input.sessionId,
|
|
@@ -59991,11 +60241,11 @@ async function startDaemon(config) {
|
|
|
59991
60241
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
59992
60242
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
59993
60243
|
// guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
|
|
59994
|
-
personaRoot:
|
|
60244
|
+
personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
|
|
59995
60245
|
usersRoot
|
|
59996
60246
|
},
|
|
59997
60247
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
59998
|
-
personaRoot:
|
|
60248
|
+
personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
|
|
59999
60249
|
// v2 多人 persona 隔离:handler 派生 guest user-dir 放行
|
|
60000
60250
|
usersRoot,
|
|
60001
60251
|
// capability:list / delete handler 依赖
|
|
@@ -60024,7 +60274,7 @@ async function startDaemon(config) {
|
|
|
60024
60274
|
contactStore,
|
|
60025
60275
|
// <dataDir>/sshd 绝对路径 —— contact-ssh handlers 用它拼 authorized_keys / keys/ 子路径
|
|
60026
60276
|
// Task 10 会加 SshdManager 起 sshd;handlers wire 提前挂 sshdDir 让 typecheck 过
|
|
60027
|
-
sshdDir:
|
|
60277
|
+
sshdDir: import_node_path64.default.join(config.dataDir, "sshd"),
|
|
60028
60278
|
contactSshLog: sshLog,
|
|
60029
60279
|
// inbox:sendDm 用:sessionId → session 出身(复用 attachment 同款 findOwnedSessionScope)
|
|
60030
60280
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
@@ -60098,7 +60348,7 @@ async function startDaemon(config) {
|
|
|
60098
60348
|
selfSshPublicKey: () => readSshIdentityPublicKey(config.dataDir),
|
|
60099
60349
|
// 建联交换:出站自报本机 OS 账号名。见 doc/glossary/peer-mesh.md#建联交换清单
|
|
60100
60350
|
selfOsUser: currentOsUser,
|
|
60101
|
-
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore,
|
|
60351
|
+
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path64.default.join(config.dataDir, "sshd"))
|
|
60102
60352
|
},
|
|
60103
60353
|
// app-builder
|
|
60104
60354
|
appBuilderStore,
|
|
@@ -60121,11 +60371,11 @@ async function startDaemon(config) {
|
|
|
60121
60371
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2):
|
|
60122
60372
|
// appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
|
|
60123
60373
|
// dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
|
|
60124
|
-
appBuilderPersonaRoot:
|
|
60374
|
+
appBuilderPersonaRoot: import_node_path64.default.join(config.dataDir, "personas", "persona-app-builder"),
|
|
60125
60375
|
// 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
|
|
60126
|
-
deployKitRoot:
|
|
60376
|
+
deployKitRoot: import_node_path64.default.join(config.dataDir, "deploy-kit"),
|
|
60127
60377
|
// scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
|
|
60128
|
-
resolvePersonaRoot: (personaId) =>
|
|
60378
|
+
resolvePersonaRoot: (personaId) => import_node_path64.default.join(config.dataDir, "personas", personaId),
|
|
60129
60379
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
|
|
60130
60380
|
// 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
|
|
60131
60381
|
// 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
|
|
@@ -60168,8 +60418,8 @@ async function startDaemon(config) {
|
|
|
60168
60418
|
}
|
|
60169
60419
|
let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
|
|
60170
60420
|
if (sourceFile && sourceFile.toolSessionId) {
|
|
60171
|
-
sourceJsonlPath =
|
|
60172
|
-
|
|
60421
|
+
sourceJsonlPath = import_node_path64.default.join(
|
|
60422
|
+
import_node_os22.default.homedir(),
|
|
60173
60423
|
".claude",
|
|
60174
60424
|
"projects",
|
|
60175
60425
|
cwdToHashDir(sourceFile.cwd),
|
|
@@ -60528,8 +60778,8 @@ async function startDaemon(config) {
|
|
|
60528
60778
|
const lines = [
|
|
60529
60779
|
`Tunnel: ${r.url}`,
|
|
60530
60780
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
60531
|
-
`Frpc config: ${
|
|
60532
|
-
`Frpc log: ${
|
|
60781
|
+
`Frpc config: ${import_node_path64.default.join(config.dataDir, "frpc.toml")}`,
|
|
60782
|
+
`Frpc log: ${import_node_path64.default.join(logDir, "frpc.log")}`
|
|
60533
60783
|
];
|
|
60534
60784
|
const width = Math.max(...lines.map((l) => l.length));
|
|
60535
60785
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -60542,8 +60792,8 @@ ${bar}
|
|
|
60542
60792
|
|
|
60543
60793
|
`);
|
|
60544
60794
|
try {
|
|
60545
|
-
const connectPath =
|
|
60546
|
-
|
|
60795
|
+
const connectPath = import_node_path64.default.join(config.dataDir, "connect.txt");
|
|
60796
|
+
import_node_fs52.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
60547
60797
|
} catch {
|
|
60548
60798
|
}
|
|
60549
60799
|
} catch (err) {
|
|
@@ -60579,7 +60829,7 @@ ${bar}
|
|
|
60579
60829
|
});
|
|
60580
60830
|
try {
|
|
60581
60831
|
await sshdMgr.start();
|
|
60582
|
-
rebuildAuthorizedKeys(contactStore,
|
|
60832
|
+
rebuildAuthorizedKeys(contactStore, import_node_path64.default.join(config.dataDir, "sshd"));
|
|
60583
60833
|
logger.info("sshd: contact-ssh sandbox ready", { port: config.sshdPort });
|
|
60584
60834
|
} catch (err) {
|
|
60585
60835
|
logger.warn("sshd start failed; contact SSH grant will not work until fixed", {
|
|
@@ -60638,9 +60888,9 @@ ${bar}
|
|
|
60638
60888
|
};
|
|
60639
60889
|
}
|
|
60640
60890
|
function migrateDropPersonsDir(dataDir) {
|
|
60641
|
-
const dir =
|
|
60891
|
+
const dir = import_node_path64.default.join(dataDir, "persons");
|
|
60642
60892
|
try {
|
|
60643
|
-
|
|
60893
|
+
import_node_fs52.default.rmSync(dir, { recursive: true, force: true });
|
|
60644
60894
|
} catch {
|
|
60645
60895
|
}
|
|
60646
60896
|
}
|