@clawos-dev/clawd 0.2.238 → 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 +923 -644
- 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,39 +53677,68 @@ 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);
|
|
53695
|
+
var DEFAULT_FALLBACK_APPS = ["/Applications/Clawd.app", "$HOME/Applications/Clawd.app"];
|
|
53600
53696
|
function shellQuote(s) {
|
|
53601
53697
|
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
53602
53698
|
}
|
|
53603
|
-
function
|
|
53699
|
+
function fallbackWord(app) {
|
|
53700
|
+
if (app.startsWith("$HOME/")) return `"$HOME"/${shellQuote(app.slice("$HOME/".length))}`;
|
|
53701
|
+
return shellQuote(app);
|
|
53702
|
+
}
|
|
53703
|
+
function buildClawdShim(execPath, cliPath, fallbackApps = DEFAULT_FALLBACK_APPS) {
|
|
53704
|
+
const candidates = fallbackApps.map(fallbackWord).join(" ");
|
|
53604
53705
|
return `#!/usr/bin/env bash
|
|
53605
53706
|
# clawd shim (managed by clawd; do not edit)
|
|
53606
53707
|
#
|
|
53607
|
-
# \
|
|
53608
|
-
#
|
|
53609
|
-
#
|
|
53610
|
-
|
|
53708
|
+
# primary = seed \u672C\u6587\u4EF6\u7684 daemon \u7684 execPath + cliPath\uFF1Bdaemon \u91CD\u542F\u4F1A\u8986\u5199\u672C\u6587\u4EF6\uFF08\u5E42\u7B49 seed\uFF09\u3002
|
|
53709
|
+
# primary \u5931\u6548\uFF08npx \u7F13\u5B58 GC / OTA bundle GC / app \u79FB\u52A8\uFF09\u65F6\u56DE\u9000 installer \u81EA\u5E26 CLI\uFF1B
|
|
53710
|
+
# \u5168\u706D\u7ED9\u53EF\u8BFB\u9519\u8BEF\u3002guest CC \u7528 SSH ProxyCommand \u8D70 ~/.clawd/bin/clawd ssh-relay \u65F6\u547D\u4E2D\u672C shim\u3002
|
|
53711
|
+
#
|
|
53712
|
+
# ELECTRON_RUN_AS_NODE \u663E\u5F0F\u5BFC\u51FA\uFF1Aprimary/fallback \u90FD\u53EF\u80FD\u662F Electron \u4E8C\u8FDB\u5236\uFF08plain node \u5FFD\u7565
|
|
53713
|
+
# \u8BE5\u53D8\u91CF\uFF0C\u65E0\u5BB3\uFF09\u2014\u2014\u4E0D\u518D\u4F9D\u8D56 desktop \u73AF\u5883\u53D8\u91CF\u6070\u597D\u6CC4\u6F0F\u5230 CC \u5B50\u8FDB\u7A0B\u3002
|
|
53714
|
+
export ELECTRON_RUN_AS_NODE=1
|
|
53715
|
+
EXEC=${shellQuote(execPath)}
|
|
53716
|
+
CLI=${shellQuote(cliPath)}
|
|
53717
|
+
if [ ! -x "$EXEC" ] || [ ! -f "$CLI" ]; then
|
|
53718
|
+
for APP in ${candidates}; do
|
|
53719
|
+
FCLI="$APP/Contents/Resources/daemon/dist/cli.cjs"
|
|
53720
|
+
[ -f "$FCLI" ] || continue
|
|
53721
|
+
for FEXE in "$APP"/Contents/MacOS/*; do
|
|
53722
|
+
if [ -f "$FEXE" ] && [ -x "$FEXE" ]; then
|
|
53723
|
+
EXEC="$FEXE"
|
|
53724
|
+
CLI="$FCLI"
|
|
53725
|
+
break 2
|
|
53726
|
+
fi
|
|
53727
|
+
done
|
|
53728
|
+
done
|
|
53729
|
+
fi
|
|
53730
|
+
if [ ! -x "$EXEC" ] || [ ! -f "$CLI" ]; then
|
|
53731
|
+
echo "clawd shim: daemon CLI not found ($CLI); \u91CD\u542F clawd daemon \u53EF\u91CD\u65B0\u751F\u6210\u672C\u6587\u4EF6" >&2
|
|
53732
|
+
exit 127
|
|
53733
|
+
fi
|
|
53734
|
+
exec "$EXEC" "$CLI" "$@"
|
|
53611
53735
|
`;
|
|
53612
53736
|
}
|
|
53613
53737
|
function ensureClawdShim(dataDir, execPath, cliPath) {
|
|
53614
|
-
const binDir =
|
|
53615
|
-
|
|
53616
|
-
const target =
|
|
53617
|
-
|
|
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 });
|
|
53618
53742
|
return target;
|
|
53619
53743
|
}
|
|
53620
53744
|
|
|
@@ -53622,7 +53746,7 @@ function ensureClawdShim(dataDir, execPath, cliPath) {
|
|
|
53622
53746
|
var SshdManager = class {
|
|
53623
53747
|
constructor(deps) {
|
|
53624
53748
|
this.deps = deps;
|
|
53625
|
-
this.sshdDir =
|
|
53749
|
+
this.sshdDir = import_node_path40.default.join(deps.dataDir, "sshd");
|
|
53626
53750
|
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
53627
53751
|
}
|
|
53628
53752
|
deps;
|
|
@@ -53643,34 +53767,34 @@ var SshdManager = class {
|
|
|
53643
53767
|
ownPid: process.pid,
|
|
53644
53768
|
logger
|
|
53645
53769
|
});
|
|
53646
|
-
|
|
53647
|
-
|
|
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 });
|
|
53648
53772
|
ensureJailScript(this.deps.dataDir);
|
|
53649
53773
|
ensureClawdShim(this.deps.dataDir, process.execPath, process.argv[1] ?? "");
|
|
53650
|
-
const hostKeyPath =
|
|
53651
|
-
if (!
|
|
53774
|
+
const hostKeyPath = import_node_path40.default.join(this.sshdDir, "host_key");
|
|
53775
|
+
if (!import_node_fs40.default.existsSync(hostKeyPath)) {
|
|
53652
53776
|
await this.generateHostKey(hostKeyPath);
|
|
53653
53777
|
}
|
|
53654
|
-
const akFile =
|
|
53655
|
-
if (!
|
|
53656
|
-
|
|
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 });
|
|
53657
53781
|
}
|
|
53658
|
-
const configPath =
|
|
53782
|
+
const configPath = import_node_path40.default.join(this.sshdDir, "sshd_config");
|
|
53659
53783
|
const config = buildSshdConfig({
|
|
53660
53784
|
listenAddress: "127.0.0.1",
|
|
53661
53785
|
port: this.deps.port,
|
|
53662
53786
|
hostKeyPath,
|
|
53663
53787
|
authorizedKeysFile: akFile,
|
|
53664
|
-
pidFilePath:
|
|
53788
|
+
pidFilePath: import_node_path40.default.join(this.sshdDir, "sshd.pid")
|
|
53665
53789
|
});
|
|
53666
|
-
|
|
53790
|
+
import_node_fs40.default.writeFileSync(configPath, config, { mode: 384 });
|
|
53667
53791
|
const sshdBin = this.deps.sshdBin ?? "/usr/sbin/sshd";
|
|
53668
53792
|
const proc = (this.deps.spawnImpl ?? import_node_child_process11.spawn)(sshdBin, ["-D", "-e", "-f", configPath], {
|
|
53669
53793
|
stdio: ["ignore", "pipe", "pipe"]
|
|
53670
53794
|
});
|
|
53671
|
-
const sshdLogDir =
|
|
53672
|
-
|
|
53673
|
-
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"), {
|
|
53674
53798
|
flags: "a",
|
|
53675
53799
|
mode: 384
|
|
53676
53800
|
});
|
|
@@ -53767,7 +53891,7 @@ ${tail}` : ready.error;
|
|
|
53767
53891
|
p2.on("error", reject);
|
|
53768
53892
|
});
|
|
53769
53893
|
try {
|
|
53770
|
-
|
|
53894
|
+
import_node_fs40.default.chmodSync(hostKeyPath, 384);
|
|
53771
53895
|
} catch {
|
|
53772
53896
|
}
|
|
53773
53897
|
}
|
|
@@ -53839,20 +53963,20 @@ async function waitForSshdReady(proc, timeoutMs) {
|
|
|
53839
53963
|
}
|
|
53840
53964
|
|
|
53841
53965
|
// src/sshd/authorized-keys.ts
|
|
53842
|
-
var
|
|
53843
|
-
var
|
|
53966
|
+
var import_node_fs41 = __toESM(require("fs"), 1);
|
|
53967
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
53844
53968
|
var JAIL_BIN_PATH_ENV = "CLAWD_JAIL_BIN_PATH";
|
|
53845
53969
|
var AUTHORIZED_KEYS_FILE = "clawd-contacts";
|
|
53846
53970
|
function jailBinPath() {
|
|
53847
|
-
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");
|
|
53848
53972
|
}
|
|
53849
53973
|
function rebuildAuthorizedKeys(store, sshdDir) {
|
|
53850
|
-
const akDir =
|
|
53851
|
-
const target =
|
|
53852
|
-
|
|
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 });
|
|
53853
53977
|
const lines = ["# managed by clawd; do not edit", ""];
|
|
53854
53978
|
for (const c of store.list()) {
|
|
53855
|
-
if (!c.
|
|
53979
|
+
if (!c.remoteAccessAllowed) continue;
|
|
53856
53980
|
const safe = /^[A-Za-z0-9_.-]+$/.test(c.deviceId);
|
|
53857
53981
|
if (!safe) continue;
|
|
53858
53982
|
const pubkey = c.sshPublicKey?.trim();
|
|
@@ -53863,25 +53987,25 @@ function rebuildAuthorizedKeys(store, sshdDir) {
|
|
|
53863
53987
|
}
|
|
53864
53988
|
const body = lines.join("\n") + "\n";
|
|
53865
53989
|
const tmp = `${target}.tmp-${process.pid}-${Date.now()}`;
|
|
53866
|
-
|
|
53867
|
-
|
|
53990
|
+
import_node_fs41.default.writeFileSync(tmp, body, { mode: 384 });
|
|
53991
|
+
import_node_fs41.default.renameSync(tmp, target);
|
|
53868
53992
|
}
|
|
53869
53993
|
|
|
53870
53994
|
// src/sshd/ssh-identity.ts
|
|
53871
|
-
var
|
|
53872
|
-
var
|
|
53995
|
+
var import_node_fs42 = __toESM(require("fs"), 1);
|
|
53996
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
53873
53997
|
var import_node_child_process12 = require("child_process");
|
|
53874
53998
|
var IDENTITY_DIR = "ssh-identity";
|
|
53875
53999
|
var KEY_FILE = "id_ed25519";
|
|
53876
54000
|
function identityPaths(dataDir) {
|
|
53877
|
-
const privPath =
|
|
54001
|
+
const privPath = import_node_path42.default.join(dataDir, IDENTITY_DIR, KEY_FILE);
|
|
53878
54002
|
return { privPath, pubPath: `${privPath}.pub` };
|
|
53879
54003
|
}
|
|
53880
54004
|
async function ensureSshIdentity(dataDir, opts = {}) {
|
|
53881
54005
|
const { privPath, pubPath } = identityPaths(dataDir);
|
|
53882
|
-
|
|
53883
|
-
if (
|
|
53884
|
-
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() };
|
|
53885
54009
|
}
|
|
53886
54010
|
const bin = opts.keygenBin ?? "/usr/bin/ssh-keygen";
|
|
53887
54011
|
await new Promise((resolve6, reject) => {
|
|
@@ -53894,18 +54018,18 @@ async function ensureSshIdentity(dataDir, opts = {}) {
|
|
|
53894
54018
|
p2.on("error", reject);
|
|
53895
54019
|
});
|
|
53896
54020
|
try {
|
|
53897
|
-
|
|
54021
|
+
import_node_fs42.default.chmodSync(privPath, 384);
|
|
53898
54022
|
} catch {
|
|
53899
54023
|
}
|
|
53900
54024
|
try {
|
|
53901
|
-
|
|
54025
|
+
import_node_fs42.default.chmodSync(pubPath, 420);
|
|
53902
54026
|
} catch {
|
|
53903
54027
|
}
|
|
53904
|
-
return { privateKeyPath: privPath, publicKeyLine:
|
|
54028
|
+
return { privateKeyPath: privPath, publicKeyLine: import_node_fs42.default.readFileSync(pubPath, "utf8").trim() };
|
|
53905
54029
|
}
|
|
53906
54030
|
function readSshIdentityPublicKey(dataDir) {
|
|
53907
54031
|
try {
|
|
53908
|
-
return
|
|
54032
|
+
return import_node_fs42.default.readFileSync(identityPaths(dataDir).pubPath, "utf8").trim();
|
|
53909
54033
|
} catch {
|
|
53910
54034
|
return null;
|
|
53911
54035
|
}
|
|
@@ -53915,10 +54039,10 @@ function readSshIdentityPublicKey(dataDir) {
|
|
|
53915
54039
|
var import_node_net2 = __toESM(require("net"), 1);
|
|
53916
54040
|
|
|
53917
54041
|
// src/sshd/contact-ssh-log.ts
|
|
53918
|
-
var
|
|
53919
|
-
var
|
|
54042
|
+
var import_node_fs43 = __toESM(require("fs"), 1);
|
|
54043
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
53920
54044
|
function createContactSshLog(dataDir) {
|
|
53921
|
-
const file =
|
|
54045
|
+
const file = import_node_path43.default.join(dataDir, "log", "contact-ssh.log");
|
|
53922
54046
|
function append(level, tag, message, meta) {
|
|
53923
54047
|
const time = (/* @__PURE__ */ new Date()).toISOString();
|
|
53924
54048
|
let line = `[${time}] [${level}] [${tag}] ${message}`;
|
|
@@ -53931,8 +54055,8 @@ function createContactSshLog(dataDir) {
|
|
|
53931
54055
|
}
|
|
53932
54056
|
line += "\n";
|
|
53933
54057
|
try {
|
|
53934
|
-
|
|
53935
|
-
|
|
54058
|
+
import_node_fs43.default.mkdirSync(import_node_path43.default.dirname(file), { recursive: true });
|
|
54059
|
+
import_node_fs43.default.appendFileSync(file, line, { mode: 384 });
|
|
53936
54060
|
} catch {
|
|
53937
54061
|
}
|
|
53938
54062
|
}
|
|
@@ -54240,27 +54364,27 @@ function respond404(socket) {
|
|
|
54240
54364
|
|
|
54241
54365
|
// src/tunnel/device-key.ts
|
|
54242
54366
|
var import_node_os14 = __toESM(require("os"), 1);
|
|
54243
|
-
var
|
|
54367
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
54244
54368
|
var import_node_crypto11 = __toESM(require("crypto"), 1);
|
|
54245
54369
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
54246
54370
|
function deriveStableDeviceKey(opts = {}) {
|
|
54247
54371
|
const hostname = opts.hostname ?? import_node_os14.default.hostname();
|
|
54248
54372
|
const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
|
|
54249
54373
|
const home = opts.home ?? import_node_os14.default.homedir();
|
|
54250
|
-
const defaultDataDir =
|
|
54251
|
-
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;
|
|
54252
54376
|
const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
|
|
54253
54377
|
const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
|
|
54254
54378
|
return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
54255
54379
|
}
|
|
54256
54380
|
|
|
54257
54381
|
// src/auth-store.ts
|
|
54258
|
-
var
|
|
54259
|
-
var
|
|
54382
|
+
var import_node_fs44 = __toESM(require("fs"), 1);
|
|
54383
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
54260
54384
|
var import_node_crypto12 = __toESM(require("crypto"), 1);
|
|
54261
54385
|
var AUTH_FILE_NAME = "auth.json";
|
|
54262
54386
|
function authFilePath(dataDir) {
|
|
54263
|
-
return
|
|
54387
|
+
return import_node_path45.default.join(dataDir, AUTH_FILE_NAME);
|
|
54264
54388
|
}
|
|
54265
54389
|
function loadOrCreateAuthFile(opts) {
|
|
54266
54390
|
const file = authFilePath(opts.dataDir);
|
|
@@ -54296,7 +54420,7 @@ function defaultGenerateOwnerPrincipalId() {
|
|
|
54296
54420
|
}
|
|
54297
54421
|
function readAuthFile(file) {
|
|
54298
54422
|
try {
|
|
54299
|
-
const raw =
|
|
54423
|
+
const raw = import_node_fs44.default.readFileSync(file, "utf8");
|
|
54300
54424
|
const parsed = JSON.parse(raw);
|
|
54301
54425
|
if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
|
|
54302
54426
|
return null;
|
|
@@ -54316,25 +54440,25 @@ function readAuthFile(file) {
|
|
|
54316
54440
|
}
|
|
54317
54441
|
}
|
|
54318
54442
|
function writeAuthFile(file, content) {
|
|
54319
|
-
|
|
54320
|
-
|
|
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 });
|
|
54321
54445
|
try {
|
|
54322
|
-
|
|
54446
|
+
import_node_fs44.default.chmodSync(file, 384);
|
|
54323
54447
|
} catch {
|
|
54324
54448
|
}
|
|
54325
54449
|
}
|
|
54326
54450
|
|
|
54327
54451
|
// src/owner-profile.ts
|
|
54328
|
-
var
|
|
54452
|
+
var import_node_fs45 = __toESM(require("fs"), 1);
|
|
54329
54453
|
var import_node_os15 = __toESM(require("os"), 1);
|
|
54330
|
-
var
|
|
54454
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
54331
54455
|
var PROFILE_FILENAME = "profile.json";
|
|
54332
54456
|
function loadOwnerDisplayName(dataDir) {
|
|
54333
54457
|
const fallback = import_node_os15.default.userInfo().username;
|
|
54334
|
-
const profilePath =
|
|
54458
|
+
const profilePath = import_node_path46.default.join(dataDir, PROFILE_FILENAME);
|
|
54335
54459
|
let raw;
|
|
54336
54460
|
try {
|
|
54337
|
-
raw =
|
|
54461
|
+
raw = import_node_fs45.default.readFileSync(profilePath, "utf8");
|
|
54338
54462
|
} catch {
|
|
54339
54463
|
return fallback;
|
|
54340
54464
|
}
|
|
@@ -54357,18 +54481,18 @@ function loadOwnerDisplayName(dataDir) {
|
|
|
54357
54481
|
}
|
|
54358
54482
|
|
|
54359
54483
|
// src/feishu-auth/owner-identity-store.ts
|
|
54360
|
-
var
|
|
54361
|
-
var
|
|
54484
|
+
var import_node_fs46 = __toESM(require("fs"), 1);
|
|
54485
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
54362
54486
|
var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
|
|
54363
54487
|
var OwnerIdentityStore = class {
|
|
54364
54488
|
file;
|
|
54365
54489
|
constructor(dataDir) {
|
|
54366
|
-
this.file =
|
|
54490
|
+
this.file = import_node_path47.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
|
|
54367
54491
|
}
|
|
54368
54492
|
read() {
|
|
54369
54493
|
let raw;
|
|
54370
54494
|
try {
|
|
54371
|
-
raw =
|
|
54495
|
+
raw = import_node_fs46.default.readFileSync(this.file, "utf8");
|
|
54372
54496
|
} catch {
|
|
54373
54497
|
return null;
|
|
54374
54498
|
}
|
|
@@ -54396,16 +54520,16 @@ var OwnerIdentityStore = class {
|
|
|
54396
54520
|
};
|
|
54397
54521
|
}
|
|
54398
54522
|
write(record) {
|
|
54399
|
-
|
|
54400
|
-
|
|
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 });
|
|
54401
54525
|
try {
|
|
54402
|
-
|
|
54526
|
+
import_node_fs46.default.chmodSync(this.file, 384);
|
|
54403
54527
|
} catch {
|
|
54404
54528
|
}
|
|
54405
54529
|
}
|
|
54406
54530
|
clear() {
|
|
54407
54531
|
try {
|
|
54408
|
-
|
|
54532
|
+
import_node_fs46.default.unlinkSync(this.file);
|
|
54409
54533
|
} catch (err) {
|
|
54410
54534
|
const code = err?.code;
|
|
54411
54535
|
if (code !== "ENOENT") throw err;
|
|
@@ -54542,9 +54666,9 @@ var CentralClientError = class extends Error {
|
|
|
54542
54666
|
code;
|
|
54543
54667
|
cause;
|
|
54544
54668
|
};
|
|
54545
|
-
async function centralRequest(opts,
|
|
54669
|
+
async function centralRequest(opts, path77, init) {
|
|
54546
54670
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
54547
|
-
const url = `${opts.api.replace(/\/+$/, "")}${
|
|
54671
|
+
const url = `${opts.api.replace(/\/+$/, "")}${path77}`;
|
|
54548
54672
|
const ctrl = new AbortController();
|
|
54549
54673
|
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
54550
54674
|
let res;
|
|
@@ -54738,8 +54862,8 @@ function verifyConnectToken(args) {
|
|
|
54738
54862
|
}
|
|
54739
54863
|
|
|
54740
54864
|
// src/feishu-auth/server-key.ts
|
|
54741
|
-
var
|
|
54742
|
-
var
|
|
54865
|
+
var fs56 = __toESM(require("fs"), 1);
|
|
54866
|
+
var path56 = __toESM(require("path"), 1);
|
|
54743
54867
|
var FILE_NAME2 = "server-signing-key.json";
|
|
54744
54868
|
var ServerKeyStore = class {
|
|
54745
54869
|
constructor(dataDir) {
|
|
@@ -54747,12 +54871,12 @@ var ServerKeyStore = class {
|
|
|
54747
54871
|
}
|
|
54748
54872
|
dataDir;
|
|
54749
54873
|
filePath() {
|
|
54750
|
-
return
|
|
54874
|
+
return path56.join(this.dataDir, FILE_NAME2);
|
|
54751
54875
|
}
|
|
54752
54876
|
/** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
|
|
54753
54877
|
read() {
|
|
54754
54878
|
try {
|
|
54755
|
-
const raw =
|
|
54879
|
+
const raw = fs56.readFileSync(this.filePath(), "utf8");
|
|
54756
54880
|
const parsed = JSON.parse(raw);
|
|
54757
54881
|
if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
|
|
54758
54882
|
return parsed.publicKeyPem;
|
|
@@ -54767,12 +54891,12 @@ var ServerKeyStore = class {
|
|
|
54767
54891
|
publicKeyPem,
|
|
54768
54892
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
54769
54893
|
};
|
|
54770
|
-
|
|
54771
|
-
|
|
54894
|
+
fs56.mkdirSync(this.dataDir, { recursive: true });
|
|
54895
|
+
fs56.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
|
|
54772
54896
|
}
|
|
54773
54897
|
clear() {
|
|
54774
54898
|
try {
|
|
54775
|
-
|
|
54899
|
+
fs56.unlinkSync(this.filePath());
|
|
54776
54900
|
} catch {
|
|
54777
54901
|
}
|
|
54778
54902
|
}
|
|
@@ -54785,12 +54909,12 @@ init_protocol();
|
|
|
54785
54909
|
init_protocol();
|
|
54786
54910
|
|
|
54787
54911
|
// src/session/fork.ts
|
|
54788
|
-
var
|
|
54912
|
+
var import_node_fs47 = __toESM(require("fs"), 1);
|
|
54789
54913
|
var import_node_os16 = __toESM(require("os"), 1);
|
|
54790
|
-
var
|
|
54914
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
54791
54915
|
init_claude_history();
|
|
54792
54916
|
function readJsonlEntries(file) {
|
|
54793
|
-
const raw =
|
|
54917
|
+
const raw = import_node_fs47.default.readFileSync(file, "utf8");
|
|
54794
54918
|
const out = [];
|
|
54795
54919
|
for (const line of raw.split("\n")) {
|
|
54796
54920
|
const t = line.trim();
|
|
@@ -54803,19 +54927,19 @@ function readJsonlEntries(file) {
|
|
|
54803
54927
|
return out;
|
|
54804
54928
|
}
|
|
54805
54929
|
function forkSession(input) {
|
|
54806
|
-
const baseDir = input.baseDir ??
|
|
54807
|
-
let projectDir =
|
|
54808
|
-
let sourceFile =
|
|
54809
|
-
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)) {
|
|
54810
54934
|
const found = findTranscriptByToolSessionId(
|
|
54811
|
-
|
|
54935
|
+
import_node_path48.default.join(baseDir, "projects"),
|
|
54812
54936
|
input.toolSessionId
|
|
54813
54937
|
);
|
|
54814
54938
|
if (!found) {
|
|
54815
54939
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
54816
54940
|
}
|
|
54817
54941
|
sourceFile = found;
|
|
54818
|
-
projectDir =
|
|
54942
|
+
projectDir = import_node_path48.default.dirname(found);
|
|
54819
54943
|
}
|
|
54820
54944
|
const entries = readJsonlEntries(sourceFile);
|
|
54821
54945
|
const mainEntries = entries.filter((e) => e.isSidechain !== true);
|
|
@@ -54844,9 +54968,9 @@ function forkSession(input) {
|
|
|
54844
54968
|
}
|
|
54845
54969
|
forkedLines.push(JSON.stringify(forked));
|
|
54846
54970
|
}
|
|
54847
|
-
const forkedFilePath =
|
|
54848
|
-
|
|
54849
|
-
|
|
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 });
|
|
54850
54974
|
return { forkedToolSessionId, forkedFilePath };
|
|
54851
54975
|
}
|
|
54852
54976
|
|
|
@@ -55217,7 +55341,7 @@ function buildPermissionHandlers(deps) {
|
|
|
55217
55341
|
}
|
|
55218
55342
|
|
|
55219
55343
|
// src/handlers/history.ts
|
|
55220
|
-
var
|
|
55344
|
+
var path59 = __toESM(require("path"), 1);
|
|
55221
55345
|
init_protocol();
|
|
55222
55346
|
|
|
55223
55347
|
// src/session/recent-dirs.ts
|
|
@@ -55235,7 +55359,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
55235
55359
|
}
|
|
55236
55360
|
|
|
55237
55361
|
// src/permission/persona-paths.ts
|
|
55238
|
-
var
|
|
55362
|
+
var path58 = __toESM(require("path"), 1);
|
|
55239
55363
|
function getAllowedPersonaIds(grants, action) {
|
|
55240
55364
|
const ids = /* @__PURE__ */ new Set();
|
|
55241
55365
|
for (const g2 of grants) {
|
|
@@ -55248,42 +55372,42 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
55248
55372
|
return ids;
|
|
55249
55373
|
}
|
|
55250
55374
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
|
|
55251
|
-
const target =
|
|
55375
|
+
const target = path58.resolve(absPath);
|
|
55252
55376
|
if (userWorkDir) {
|
|
55253
|
-
const u =
|
|
55254
|
-
const usep = u.endsWith(
|
|
55377
|
+
const u = path58.resolve(userWorkDir);
|
|
55378
|
+
const usep = u.endsWith(path58.sep) ? "" : path58.sep;
|
|
55255
55379
|
if (target === u || target.startsWith(u + usep)) return true;
|
|
55256
55380
|
}
|
|
55257
|
-
const root =
|
|
55258
|
-
const sep3 = root.endsWith(
|
|
55381
|
+
const root = path58.resolve(personaRoot);
|
|
55382
|
+
const sep3 = root.endsWith(path58.sep) ? "" : path58.sep;
|
|
55259
55383
|
if (!target.startsWith(root + sep3)) return false;
|
|
55260
|
-
const rel =
|
|
55384
|
+
const rel = path58.relative(root, target);
|
|
55261
55385
|
if (!rel || rel.startsWith("..")) return false;
|
|
55262
|
-
const personaId = rel.split(
|
|
55386
|
+
const personaId = rel.split(path58.sep)[0];
|
|
55263
55387
|
if (!personaId) return false;
|
|
55264
55388
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
55265
55389
|
if (allowed === "*") return true;
|
|
55266
55390
|
return allowed.has(personaId);
|
|
55267
55391
|
}
|
|
55268
55392
|
function personaIdFromPath(absPath, personaRoot) {
|
|
55269
|
-
const root =
|
|
55270
|
-
const target =
|
|
55271
|
-
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;
|
|
55272
55396
|
if (!target.startsWith(root + sep3)) return null;
|
|
55273
|
-
const rel =
|
|
55397
|
+
const rel = path58.relative(root, target);
|
|
55274
55398
|
if (!rel || rel.startsWith("..")) return null;
|
|
55275
|
-
const id = rel.split(
|
|
55399
|
+
const id = rel.split(path58.sep)[0];
|
|
55276
55400
|
return id || null;
|
|
55277
55401
|
}
|
|
55278
55402
|
function isPathWithin(dir, absPath) {
|
|
55279
|
-
const d =
|
|
55280
|
-
const t =
|
|
55281
|
-
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;
|
|
55282
55406
|
return t === d || t.startsWith(d + sep3);
|
|
55283
55407
|
}
|
|
55284
55408
|
function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
|
|
55285
55409
|
if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
|
|
55286
|
-
return personaIdFromPath(
|
|
55410
|
+
return personaIdFromPath(path58.resolve(absPath), personaRoot) === personaId;
|
|
55287
55411
|
}
|
|
55288
55412
|
|
|
55289
55413
|
// src/handlers/history.ts
|
|
@@ -55309,7 +55433,7 @@ function buildHistoryHandlers(deps) {
|
|
|
55309
55433
|
if (!pid) return false;
|
|
55310
55434
|
return isGuestPathAllowed(
|
|
55311
55435
|
ctx.grants,
|
|
55312
|
-
|
|
55436
|
+
path59.join(personaRoot, pid),
|
|
55313
55437
|
personaRoot,
|
|
55314
55438
|
"read",
|
|
55315
55439
|
userWorkDir
|
|
@@ -55321,7 +55445,7 @@ function buildHistoryHandlers(deps) {
|
|
|
55321
55445
|
};
|
|
55322
55446
|
const list = async (frame, _client, ctx) => {
|
|
55323
55447
|
const args = HistoryListArgs.parse(frame);
|
|
55324
|
-
assertGuestPath(ctx,
|
|
55448
|
+
assertGuestPath(ctx, path59.resolve(args.projectPath), personaRoot, "history:list");
|
|
55325
55449
|
const sessions = await history.listSessions(args);
|
|
55326
55450
|
return { response: { type: "history:list", sessions } };
|
|
55327
55451
|
};
|
|
@@ -55353,13 +55477,13 @@ function buildHistoryHandlers(deps) {
|
|
|
55353
55477
|
};
|
|
55354
55478
|
const subagents = async (frame, _client, ctx) => {
|
|
55355
55479
|
const args = HistorySubagentsArgs.parse(frame);
|
|
55356
|
-
assertGuestPath(ctx,
|
|
55480
|
+
assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
|
|
55357
55481
|
const subs = await history.listSubagents(args);
|
|
55358
55482
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
55359
55483
|
};
|
|
55360
55484
|
const subagentRead = async (frame, _client, ctx) => {
|
|
55361
55485
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
55362
|
-
assertGuestPath(ctx,
|
|
55486
|
+
assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
|
|
55363
55487
|
const res = await history.readSubagent(args);
|
|
55364
55488
|
return { response: { type: "history:subagent-read", ...res } };
|
|
55365
55489
|
};
|
|
@@ -55368,7 +55492,7 @@ function buildHistoryHandlers(deps) {
|
|
|
55368
55492
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
55369
55493
|
const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
|
|
55370
55494
|
const filtered = dirs.filter(
|
|
55371
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
55495
|
+
(d) => isGuestPathAllowed(ctx.grants, path59.resolve(d.cwd), personaRoot, "read", userWorkDir)
|
|
55372
55496
|
);
|
|
55373
55497
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
55374
55498
|
}
|
|
@@ -55385,7 +55509,7 @@ function buildHistoryHandlers(deps) {
|
|
|
55385
55509
|
}
|
|
55386
55510
|
|
|
55387
55511
|
// src/handlers/workspace.ts
|
|
55388
|
-
var
|
|
55512
|
+
var path60 = __toESM(require("path"), 1);
|
|
55389
55513
|
var os16 = __toESM(require("os"), 1);
|
|
55390
55514
|
init_protocol();
|
|
55391
55515
|
init_protocol();
|
|
@@ -55427,22 +55551,22 @@ function buildWorkspaceHandlers(deps) {
|
|
|
55427
55551
|
const args = WorkspaceListArgs.parse(frame);
|
|
55428
55552
|
const isGuest = ctx?.principal.kind === "guest";
|
|
55429
55553
|
const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
|
|
55430
|
-
const resolvedCwd =
|
|
55431
|
-
const target = args.path ?
|
|
55554
|
+
const resolvedCwd = path60.resolve(args.cwd ?? fallbackCwd);
|
|
55555
|
+
const target = args.path ? path60.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
55432
55556
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
|
|
55433
55557
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
55434
55558
|
return { response: { type: "workspace:list", ...res } };
|
|
55435
55559
|
};
|
|
55436
55560
|
const read = async (frame, _client, ctx) => {
|
|
55437
55561
|
const args = WorkspaceReadArgs.parse(frame);
|
|
55438
|
-
const target =
|
|
55562
|
+
const target = path60.isAbsolute(args.path) ? path60.resolve(args.path) : path60.resolve(args.cwd, args.path);
|
|
55439
55563
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
|
|
55440
55564
|
const res = workspace.read(args);
|
|
55441
55565
|
return { response: { type: "workspace:read", ...res } };
|
|
55442
55566
|
};
|
|
55443
55567
|
const skillsList = async (frame, _client, ctx) => {
|
|
55444
55568
|
const args = SkillsListArgs.parse(frame);
|
|
55445
|
-
const cwdAbs =
|
|
55569
|
+
const cwdAbs = path60.resolve(args.cwd);
|
|
55446
55570
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
|
|
55447
55571
|
const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
|
|
55448
55572
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -55454,7 +55578,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
55454
55578
|
};
|
|
55455
55579
|
const agentsList = async (frame, _client, ctx) => {
|
|
55456
55580
|
const args = AgentsListArgs.parse(frame);
|
|
55457
|
-
const cwdAbs =
|
|
55581
|
+
const cwdAbs = path60.resolve(args.cwd);
|
|
55458
55582
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
|
|
55459
55583
|
if (args.tool === "codex") {
|
|
55460
55584
|
return { response: { type: "agents:list", agents: [] } };
|
|
@@ -55476,20 +55600,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
55476
55600
|
}
|
|
55477
55601
|
|
|
55478
55602
|
// src/handlers/git.ts
|
|
55479
|
-
var
|
|
55603
|
+
var path62 = __toESM(require("path"), 1);
|
|
55480
55604
|
init_protocol();
|
|
55481
55605
|
init_protocol();
|
|
55482
55606
|
|
|
55483
55607
|
// src/workspace/git.ts
|
|
55484
55608
|
var import_node_child_process13 = require("child_process");
|
|
55485
|
-
var
|
|
55486
|
-
var
|
|
55609
|
+
var import_node_fs48 = __toESM(require("fs"), 1);
|
|
55610
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
55487
55611
|
var import_node_util = require("util");
|
|
55488
55612
|
var pexec = (0, import_node_util.promisify)(import_node_child_process13.execFile);
|
|
55489
55613
|
function normalizePath(p2) {
|
|
55490
|
-
const resolved =
|
|
55614
|
+
const resolved = import_node_path49.default.resolve(p2);
|
|
55491
55615
|
try {
|
|
55492
|
-
return
|
|
55616
|
+
return import_node_fs48.default.realpathSync(resolved);
|
|
55493
55617
|
} catch {
|
|
55494
55618
|
return resolved;
|
|
55495
55619
|
}
|
|
@@ -55563,7 +55687,7 @@ async function listGitBranches(cwd) {
|
|
|
55563
55687
|
function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
|
|
55564
55688
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
55565
55689
|
const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
|
|
55566
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
55690
|
+
if (!isGuestPathAllowed(ctx.grants, path62.resolve(cwd), personaRoot, "read", userWorkDir)) {
|
|
55567
55691
|
throw new ClawdError(
|
|
55568
55692
|
ERROR_CODES.UNAUTHORIZED,
|
|
55569
55693
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -55915,21 +56039,21 @@ function ensureOwner2(ctx) {
|
|
|
55915
56039
|
if (!ctx || ctx.principal.kind !== "owner") {
|
|
55916
56040
|
throw new ClawdError(
|
|
55917
56041
|
ERROR_CODES.UNAUTHORIZED,
|
|
55918
|
-
"UNAUTHORIZED: contact:
|
|
56042
|
+
"UNAUTHORIZED: contact:setRemoteAccess requires owner ctx"
|
|
55919
56043
|
);
|
|
55920
56044
|
}
|
|
55921
56045
|
}
|
|
55922
56046
|
function buildContactSshHandlers(deps) {
|
|
55923
56047
|
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
55924
|
-
const
|
|
56048
|
+
const setRemoteAccess = async (frame, _client, ctx) => {
|
|
55925
56049
|
ensureOwner2(ctx);
|
|
55926
56050
|
const { type: _t, requestId: _r, ...rest } = frame;
|
|
55927
|
-
const args =
|
|
55928
|
-
const hit = deps.store.
|
|
55929
|
-
|
|
56051
|
+
const args = ContactSetRemoteAccessArgsSchema.parse(rest);
|
|
56052
|
+
const hit = deps.store.setRemoteAccess(args.deviceId, {
|
|
56053
|
+
remoteAccessAllowed: args.remoteAccessAllowed
|
|
55930
56054
|
});
|
|
55931
56055
|
if (!hit) {
|
|
55932
|
-
sshLog.warn("authz.
|
|
56056
|
+
sshLog.warn("authz.setRemoteAccess", "owner \u5C1D\u8BD5\u6539 SSH \u6388\u6743\u4F46 contact \u4E0D\u5728 store", {
|
|
55933
56057
|
peerDeviceId: args.deviceId
|
|
55934
56058
|
});
|
|
55935
56059
|
throw new ClawdError(
|
|
@@ -55938,25 +56062,25 @@ function buildContactSshHandlers(deps) {
|
|
|
55938
56062
|
);
|
|
55939
56063
|
}
|
|
55940
56064
|
rebuildAuthorizedKeys(deps.store, deps.sshdDir);
|
|
55941
|
-
sshLog.info("authz.
|
|
56065
|
+
sshLog.info("authz.setRemoteAccess", "owner \u6539\u4E86 SSH \u6388\u6743 \u2192 authorized_keys \u5DF2\u91CD\u5EFA", {
|
|
55942
56066
|
peerDeviceId: args.deviceId,
|
|
55943
|
-
|
|
56067
|
+
remoteAccessAllowed: args.remoteAccessAllowed
|
|
55944
56068
|
});
|
|
55945
56069
|
deps.broadcast({
|
|
55946
|
-
type: "contact:
|
|
56070
|
+
type: "contact:remote-access-updated",
|
|
55947
56071
|
deviceId: args.deviceId,
|
|
55948
|
-
|
|
56072
|
+
remoteAccessAllowed: args.remoteAccessAllowed
|
|
55949
56073
|
});
|
|
55950
56074
|
return {
|
|
55951
56075
|
response: {
|
|
55952
|
-
type: "contact:
|
|
56076
|
+
type: "contact:setRemoteAccess:ok",
|
|
55953
56077
|
deviceId: args.deviceId,
|
|
55954
|
-
|
|
56078
|
+
remoteAccessAllowed: args.remoteAccessAllowed
|
|
55955
56079
|
}
|
|
55956
56080
|
};
|
|
55957
56081
|
};
|
|
55958
56082
|
return {
|
|
55959
|
-
"contact:
|
|
56083
|
+
"contact:setRemoteAccess": setRemoteAccess
|
|
55960
56084
|
};
|
|
55961
56085
|
}
|
|
55962
56086
|
|
|
@@ -56124,7 +56248,7 @@ function buildDeviceHandlers(deps) {
|
|
|
56124
56248
|
grants: wh.grants,
|
|
56125
56249
|
addedAt: now(),
|
|
56126
56250
|
pinnedAt: existing?.pinnedAt ?? null,
|
|
56127
|
-
|
|
56251
|
+
remoteAccessAllowed: existing?.remoteAccessAllowed ?? false,
|
|
56128
56252
|
// 建联交换:对方 whoami:ok 下发的 pubkey / osUser 落库;对方老版本不带 →
|
|
56129
56253
|
// 保留既有值 / 不落 undefined 占位。见 doc/glossary/peer-mesh.md#建联交换清单
|
|
56130
56254
|
...sshPublicKey ? { sshPublicKey } : {},
|
|
@@ -56133,7 +56257,7 @@ function buildDeviceHandlers(deps) {
|
|
|
56133
56257
|
};
|
|
56134
56258
|
deps.store.upsert(contact);
|
|
56135
56259
|
deps.broadcast({ type: "contact:added", contact });
|
|
56136
|
-
if (contact.
|
|
56260
|
+
if (contact.remoteAccessAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
|
|
56137
56261
|
deps.rebuildAuthorizedKeys?.();
|
|
56138
56262
|
}
|
|
56139
56263
|
return {
|
|
@@ -56288,7 +56412,7 @@ function buildPersonaHandlers(deps) {
|
|
|
56288
56412
|
}
|
|
56289
56413
|
|
|
56290
56414
|
// src/handlers/attachment.ts
|
|
56291
|
-
var
|
|
56415
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
56292
56416
|
init_protocol();
|
|
56293
56417
|
init_protocol();
|
|
56294
56418
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -56368,12 +56492,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
56368
56492
|
`session ${args.sessionId} scope unresolved`
|
|
56369
56493
|
);
|
|
56370
56494
|
}
|
|
56371
|
-
const cwdAbs =
|
|
56372
|
-
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);
|
|
56373
56497
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
|
|
56374
56498
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
56375
56499
|
const entry = entries.find((e) => {
|
|
56376
|
-
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);
|
|
56377
56501
|
return storedAbs === candidateAbs && !e.stale;
|
|
56378
56502
|
});
|
|
56379
56503
|
if (!entry) {
|
|
@@ -56398,7 +56522,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
56398
56522
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
56399
56523
|
const f = deps.sessionStore.read(sessionId);
|
|
56400
56524
|
if (!f) return;
|
|
56401
|
-
assertGuestAttachmentPath(ctx,
|
|
56525
|
+
assertGuestAttachmentPath(ctx, import_node_path50.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
|
|
56402
56526
|
}
|
|
56403
56527
|
const groupAdd = async (frame, _client, ctx) => {
|
|
56404
56528
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -56413,8 +56537,8 @@ function buildAttachmentHandlers(deps) {
|
|
|
56413
56537
|
if (!scope) {
|
|
56414
56538
|
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
|
|
56415
56539
|
}
|
|
56416
|
-
const cwdAbs =
|
|
56417
|
-
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);
|
|
56418
56542
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
|
|
56419
56543
|
const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
|
|
56420
56544
|
const size = 0;
|
|
@@ -56473,19 +56597,19 @@ function buildAttachmentHandlers(deps) {
|
|
|
56473
56597
|
|
|
56474
56598
|
// src/handlers/extension.ts
|
|
56475
56599
|
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
56476
|
-
var
|
|
56600
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
56477
56601
|
init_protocol();
|
|
56478
56602
|
|
|
56479
56603
|
// src/extension/bundle-zip.ts
|
|
56480
56604
|
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
56481
|
-
var
|
|
56605
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
56482
56606
|
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
56483
56607
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
56484
56608
|
async function bundleExtensionDir(dir) {
|
|
56485
56609
|
const entries = await listFilesSorted(dir);
|
|
56486
56610
|
const zip = new import_jszip2.default();
|
|
56487
56611
|
for (const rel of entries) {
|
|
56488
|
-
const abs =
|
|
56612
|
+
const abs = import_node_path51.default.join(dir, rel);
|
|
56489
56613
|
const content = await import_promises5.default.readFile(abs);
|
|
56490
56614
|
zip.file(rel, content, { date: FIXED_DATE });
|
|
56491
56615
|
}
|
|
@@ -56506,7 +56630,7 @@ async function listFilesSorted(rootDir) {
|
|
|
56506
56630
|
return out;
|
|
56507
56631
|
}
|
|
56508
56632
|
async function walk(absRoot, relPrefix, out) {
|
|
56509
|
-
const dirAbs =
|
|
56633
|
+
const dirAbs = import_node_path51.default.join(absRoot, relPrefix);
|
|
56510
56634
|
const entries = await import_promises5.default.readdir(dirAbs, { withFileTypes: true });
|
|
56511
56635
|
for (const e of entries) {
|
|
56512
56636
|
if (IGNORE_BASENAMES.has(e.name)) continue;
|
|
@@ -56560,25 +56684,25 @@ function computePublishCheck(args) {
|
|
|
56560
56684
|
|
|
56561
56685
|
// src/extension/install-flow.ts
|
|
56562
56686
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
56563
|
-
var
|
|
56687
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
56564
56688
|
var import_node_os19 = __toESM(require("os"), 1);
|
|
56565
56689
|
var import_node_crypto15 = __toESM(require("crypto"), 1);
|
|
56566
56690
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
56567
56691
|
|
|
56568
56692
|
// src/extension/paths.ts
|
|
56569
56693
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
56570
|
-
var
|
|
56694
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
56571
56695
|
function clawdHomeRoot(override) {
|
|
56572
|
-
return override ?? process.env.CLAWD_HOME ??
|
|
56696
|
+
return override ?? process.env.CLAWD_HOME ?? import_node_path52.default.join(import_node_os18.default.homedir(), ".clawd");
|
|
56573
56697
|
}
|
|
56574
56698
|
function extensionsRoot(override) {
|
|
56575
|
-
return
|
|
56699
|
+
return import_node_path52.default.join(clawdHomeRoot(override), "extensions");
|
|
56576
56700
|
}
|
|
56577
56701
|
function publishedChannelsFile(override) {
|
|
56578
|
-
return
|
|
56702
|
+
return import_node_path52.default.join(clawdHomeRoot(override), "extensions-published.json");
|
|
56579
56703
|
}
|
|
56580
56704
|
function bundleCacheRoot(override) {
|
|
56581
|
-
return
|
|
56705
|
+
return import_node_path52.default.join(clawdHomeRoot(override), "extension-bundles");
|
|
56582
56706
|
}
|
|
56583
56707
|
|
|
56584
56708
|
// src/extension/install-flow.ts
|
|
@@ -56605,7 +56729,7 @@ async function installFromChannel(args, deps) {
|
|
|
56605
56729
|
throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56606
56730
|
}
|
|
56607
56731
|
for (const name of Object.keys(zip.files)) {
|
|
56608
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
56732
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path53.default.isAbsolute(name)) {
|
|
56609
56733
|
throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56610
56734
|
}
|
|
56611
56735
|
}
|
|
@@ -56637,7 +56761,7 @@ async function installFromChannel(args, deps) {
|
|
|
56637
56761
|
);
|
|
56638
56762
|
}
|
|
56639
56763
|
const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
|
|
56640
|
-
const destDir =
|
|
56764
|
+
const destDir = import_node_path53.default.join(deps.extensionsRoot, localExtId);
|
|
56641
56765
|
let destExists = false;
|
|
56642
56766
|
try {
|
|
56643
56767
|
await import_promises6.default.access(destDir);
|
|
@@ -56651,16 +56775,16 @@ async function installFromChannel(args, deps) {
|
|
|
56651
56775
|
);
|
|
56652
56776
|
}
|
|
56653
56777
|
const stage = await import_promises6.default.mkdtemp(
|
|
56654
|
-
|
|
56778
|
+
import_node_path53.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
|
|
56655
56779
|
);
|
|
56656
56780
|
try {
|
|
56657
56781
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56658
|
-
const dest =
|
|
56782
|
+
const dest = import_node_path53.default.join(stage, name);
|
|
56659
56783
|
if (entry.dir) {
|
|
56660
56784
|
await import_promises6.default.mkdir(dest, { recursive: true });
|
|
56661
56785
|
continue;
|
|
56662
56786
|
}
|
|
56663
|
-
await import_promises6.default.mkdir(
|
|
56787
|
+
await import_promises6.default.mkdir(import_node_path53.default.dirname(dest), { recursive: true });
|
|
56664
56788
|
if (name === "manifest.json") {
|
|
56665
56789
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56666
56790
|
await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -56681,7 +56805,7 @@ async function installFromChannel(args, deps) {
|
|
|
56681
56805
|
|
|
56682
56806
|
// src/extension/update-flow.ts
|
|
56683
56807
|
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
56684
|
-
var
|
|
56808
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
56685
56809
|
var import_node_os20 = __toESM(require("os"), 1);
|
|
56686
56810
|
var import_node_crypto16 = __toESM(require("crypto"), 1);
|
|
56687
56811
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
@@ -56698,11 +56822,11 @@ async function updateFromChannel(args, deps) {
|
|
|
56698
56822
|
channelRef.extId,
|
|
56699
56823
|
channelRef.ownerPrincipalId
|
|
56700
56824
|
);
|
|
56701
|
-
const liveDir =
|
|
56825
|
+
const liveDir = import_node_path54.default.join(deps.extensionsRoot, localExtId);
|
|
56702
56826
|
const prevDir = `${liveDir}.prev`;
|
|
56703
56827
|
let existingVersion;
|
|
56704
56828
|
try {
|
|
56705
|
-
const raw = await import_promises7.default.readFile(
|
|
56829
|
+
const raw = await import_promises7.default.readFile(import_node_path54.default.join(liveDir, "manifest.json"), "utf8");
|
|
56706
56830
|
const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
|
|
56707
56831
|
if (!parsed2.success) {
|
|
56708
56832
|
throw new UpdateError(
|
|
@@ -56735,7 +56859,7 @@ async function updateFromChannel(args, deps) {
|
|
|
56735
56859
|
throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56736
56860
|
}
|
|
56737
56861
|
for (const name of Object.keys(zip.files)) {
|
|
56738
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
56862
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path54.default.isAbsolute(name)) {
|
|
56739
56863
|
throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56740
56864
|
}
|
|
56741
56865
|
}
|
|
@@ -56770,16 +56894,16 @@ async function updateFromChannel(args, deps) {
|
|
|
56770
56894
|
await import_promises7.default.rm(prevDir, { recursive: true, force: true });
|
|
56771
56895
|
await import_promises7.default.rename(liveDir, prevDir);
|
|
56772
56896
|
const stage = await import_promises7.default.mkdtemp(
|
|
56773
|
-
|
|
56897
|
+
import_node_path54.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
|
|
56774
56898
|
);
|
|
56775
56899
|
try {
|
|
56776
56900
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56777
|
-
const dest =
|
|
56901
|
+
const dest = import_node_path54.default.join(stage, name);
|
|
56778
56902
|
if (entry.dir) {
|
|
56779
56903
|
await import_promises7.default.mkdir(dest, { recursive: true });
|
|
56780
56904
|
continue;
|
|
56781
56905
|
}
|
|
56782
|
-
await import_promises7.default.mkdir(
|
|
56906
|
+
await import_promises7.default.mkdir(import_node_path54.default.dirname(dest), { recursive: true });
|
|
56783
56907
|
if (name === "manifest.json") {
|
|
56784
56908
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56785
56909
|
await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -56872,7 +56996,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
56872
56996
|
);
|
|
56873
56997
|
}
|
|
56874
56998
|
}
|
|
56875
|
-
const manifestPath =
|
|
56999
|
+
const manifestPath = import_node_path55.default.join(root, extId, "manifest.json");
|
|
56876
57000
|
const manifest = await readManifest(root, extId);
|
|
56877
57001
|
const next = { ...manifest, version: newVersion };
|
|
56878
57002
|
const tmp = `${manifestPath}.tmp`;
|
|
@@ -56880,7 +57004,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
56880
57004
|
await import_promises8.default.rename(tmp, manifestPath);
|
|
56881
57005
|
}
|
|
56882
57006
|
async function readManifest(root, extId) {
|
|
56883
|
-
const file =
|
|
57007
|
+
const file = import_node_path55.default.join(root, extId, "manifest.json");
|
|
56884
57008
|
let raw;
|
|
56885
57009
|
try {
|
|
56886
57010
|
raw = await import_promises8.default.readFile(file, "utf8");
|
|
@@ -56971,7 +57095,7 @@ function buildExtensionHandlers(deps) {
|
|
|
56971
57095
|
};
|
|
56972
57096
|
async function buildSnapshotMeta(extId) {
|
|
56973
57097
|
const manifest = await readManifest(deps.root, extId);
|
|
56974
|
-
const { sha256, buffer } = await bundleExtensionDir(
|
|
57098
|
+
const { sha256, buffer } = await bundleExtensionDir(import_node_path55.default.join(deps.root, extId));
|
|
56975
57099
|
return { manifest, contentHash: sha256, buffer };
|
|
56976
57100
|
}
|
|
56977
57101
|
const publish = async (frame, _client, ctx) => {
|
|
@@ -57152,9 +57276,9 @@ function buildExtensionHandlers(deps) {
|
|
|
57152
57276
|
}
|
|
57153
57277
|
|
|
57154
57278
|
// src/app-builder/project-store.ts
|
|
57155
|
-
var
|
|
57279
|
+
var import_node_fs49 = require("fs");
|
|
57156
57280
|
var import_node_child_process14 = require("child_process");
|
|
57157
|
-
var
|
|
57281
|
+
var import_node_path56 = require("path");
|
|
57158
57282
|
init_protocol();
|
|
57159
57283
|
var PROJECTS_DIR = "projects";
|
|
57160
57284
|
var META_FILE = ".clawd-project.json";
|
|
@@ -57168,19 +57292,19 @@ var ProjectStore = class {
|
|
|
57168
57292
|
root;
|
|
57169
57293
|
/** projects/<name>/.clawd-project.json 路径 */
|
|
57170
57294
|
metaPath(name) {
|
|
57171
|
-
return (0,
|
|
57295
|
+
return (0, import_node_path56.join)(this.projectsRoot(), name, META_FILE);
|
|
57172
57296
|
}
|
|
57173
57297
|
/** projects/<name>/ 目录路径(cwd 用) */
|
|
57174
57298
|
projectDir(name) {
|
|
57175
|
-
return (0,
|
|
57299
|
+
return (0, import_node_path56.join)(this.projectsRoot(), name);
|
|
57176
57300
|
}
|
|
57177
57301
|
projectsRoot() {
|
|
57178
|
-
return (0,
|
|
57302
|
+
return (0, import_node_path56.join)(this.root, PROJECTS_DIR);
|
|
57179
57303
|
}
|
|
57180
57304
|
async list() {
|
|
57181
57305
|
let entries;
|
|
57182
57306
|
try {
|
|
57183
|
-
entries = await
|
|
57307
|
+
entries = await import_node_fs49.promises.readdir(this.projectsRoot());
|
|
57184
57308
|
} catch (err) {
|
|
57185
57309
|
if (err.code === "ENOENT") return [];
|
|
57186
57310
|
throw err;
|
|
@@ -57188,7 +57312,7 @@ var ProjectStore = class {
|
|
|
57188
57312
|
const out = [];
|
|
57189
57313
|
for (const name of entries) {
|
|
57190
57314
|
try {
|
|
57191
|
-
const raw = await
|
|
57315
|
+
const raw = await import_node_fs49.promises.readFile(this.metaPath(name), "utf8");
|
|
57192
57316
|
const json = JSON.parse(raw);
|
|
57193
57317
|
let migrated = false;
|
|
57194
57318
|
if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
|
|
@@ -57199,7 +57323,7 @@ var ProjectStore = class {
|
|
|
57199
57323
|
if (parsed.success) {
|
|
57200
57324
|
out.push(parsed.data);
|
|
57201
57325
|
if (migrated) {
|
|
57202
|
-
void
|
|
57326
|
+
void import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
|
|
57203
57327
|
});
|
|
57204
57328
|
}
|
|
57205
57329
|
}
|
|
@@ -57243,8 +57367,8 @@ var ProjectStore = class {
|
|
|
57243
57367
|
throw new Error(`invalid name "${name}": ${validated.error.message}`);
|
|
57244
57368
|
}
|
|
57245
57369
|
const dir = this.projectDir(name);
|
|
57246
|
-
await
|
|
57247
|
-
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");
|
|
57248
57372
|
return meta;
|
|
57249
57373
|
}
|
|
57250
57374
|
/**
|
|
@@ -57287,7 +57411,7 @@ var ProjectStore = class {
|
|
|
57287
57411
|
}
|
|
57288
57412
|
async delete(name) {
|
|
57289
57413
|
const dir = this.projectDir(name);
|
|
57290
|
-
await
|
|
57414
|
+
await import_node_fs49.promises.rm(dir, { recursive: true, force: true });
|
|
57291
57415
|
}
|
|
57292
57416
|
async updatePort(name, newPort) {
|
|
57293
57417
|
if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
|
|
@@ -57303,7 +57427,7 @@ var ProjectStore = class {
|
|
|
57303
57427
|
throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
|
|
57304
57428
|
}
|
|
57305
57429
|
const updated = { ...target, port: newPort };
|
|
57306
|
-
await
|
|
57430
|
+
await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
57307
57431
|
return updated;
|
|
57308
57432
|
}
|
|
57309
57433
|
/**
|
|
@@ -57320,7 +57444,7 @@ var ProjectStore = class {
|
|
|
57320
57444
|
if (!validated.success) {
|
|
57321
57445
|
throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
|
|
57322
57446
|
}
|
|
57323
|
-
await
|
|
57447
|
+
await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57324
57448
|
return validated.data;
|
|
57325
57449
|
}
|
|
57326
57450
|
/**
|
|
@@ -57341,7 +57465,7 @@ var ProjectStore = class {
|
|
|
57341
57465
|
if (!validated.success) {
|
|
57342
57466
|
throw new Error(`invalid publishJob: ${validated.error.message}`);
|
|
57343
57467
|
}
|
|
57344
|
-
await
|
|
57468
|
+
await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57345
57469
|
return validated.data;
|
|
57346
57470
|
}
|
|
57347
57471
|
/** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
|
|
@@ -57356,7 +57480,7 @@ var ProjectStore = class {
|
|
|
57356
57480
|
if (!validated.success) {
|
|
57357
57481
|
throw new Error(`failed to clear publishJob: ${validated.error.message}`);
|
|
57358
57482
|
}
|
|
57359
|
-
await
|
|
57483
|
+
await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57360
57484
|
return validated.data;
|
|
57361
57485
|
}
|
|
57362
57486
|
};
|
|
@@ -57477,8 +57601,8 @@ var PublishJobRegistry = class {
|
|
|
57477
57601
|
|
|
57478
57602
|
// src/app-builder/publish-job-runner.ts
|
|
57479
57603
|
var import_node_child_process16 = require("child_process");
|
|
57480
|
-
var
|
|
57481
|
-
var
|
|
57604
|
+
var import_node_fs50 = require("fs");
|
|
57605
|
+
var import_node_path57 = require("path");
|
|
57482
57606
|
|
|
57483
57607
|
// src/app-builder/publish-stage-parser.ts
|
|
57484
57608
|
var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
|
|
@@ -57505,19 +57629,19 @@ function tailStderrLines(buf, n) {
|
|
|
57505
57629
|
// src/app-builder/publish-job-runner.ts
|
|
57506
57630
|
async function startPublishJob(deps, args) {
|
|
57507
57631
|
const { registry: registry2, projectDir } = deps;
|
|
57508
|
-
const
|
|
57632
|
+
const spawn16 = deps.spawnImpl ?? import_node_child_process16.spawn;
|
|
57509
57633
|
if (registry2.has(args.name)) {
|
|
57510
57634
|
return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
|
|
57511
57635
|
}
|
|
57512
57636
|
const projDir = projectDir(args.name);
|
|
57513
|
-
const logPath = (0,
|
|
57637
|
+
const logPath = (0, import_node_path57.join)(projDir, ".publish.log");
|
|
57514
57638
|
let logStream = null;
|
|
57515
57639
|
try {
|
|
57516
|
-
logStream = (0,
|
|
57640
|
+
logStream = (0, import_node_fs50.createWriteStream)(logPath, { flags: "w" });
|
|
57517
57641
|
} catch {
|
|
57518
57642
|
logStream = null;
|
|
57519
57643
|
}
|
|
57520
|
-
const child =
|
|
57644
|
+
const child = spawn16("bash", [args.scriptPath, projDir, args.personaRoot ?? ""], {
|
|
57521
57645
|
cwd: projDir,
|
|
57522
57646
|
env: process.env,
|
|
57523
57647
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -57770,8 +57894,8 @@ async function recoverInterruptedJobs(deps) {
|
|
|
57770
57894
|
|
|
57771
57895
|
// src/handlers/app-builder.ts
|
|
57772
57896
|
init_protocol();
|
|
57773
|
-
var
|
|
57774
|
-
var
|
|
57897
|
+
var import_node_path58 = require("path");
|
|
57898
|
+
var import_node_fs51 = require("fs");
|
|
57775
57899
|
var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
|
|
57776
57900
|
var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
|
|
57777
57901
|
async function recoverInterruptedPublishJobs(store, logger) {
|
|
@@ -57852,7 +57976,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
57852
57976
|
async function listAllUsersProjects() {
|
|
57853
57977
|
if (!deps.usersRoot || !deps.getStore) return [];
|
|
57854
57978
|
const getStore = deps.getStore;
|
|
57855
|
-
const userIds = await
|
|
57979
|
+
const userIds = await import_node_fs51.promises.readdir(deps.usersRoot).catch(() => []);
|
|
57856
57980
|
const perUser = await Promise.all(
|
|
57857
57981
|
userIds.map((uid) => getStore(uid).list().catch(() => []))
|
|
57858
57982
|
);
|
|
@@ -57928,8 +58052,8 @@ function buildAppBuilderHandlers(deps) {
|
|
|
57928
58052
|
const project = await userStore.create(f.name, reservedPorts);
|
|
57929
58053
|
try {
|
|
57930
58054
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
|
|
57931
|
-
const templateSrcDir = (0,
|
|
57932
|
-
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");
|
|
57933
58057
|
const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
|
|
57934
58058
|
deps.logger?.info("app-builder.scaffold.done", {
|
|
57935
58059
|
name: project.name,
|
|
@@ -58150,7 +58274,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
58150
58274
|
await userStore.clearPublishJob(args.name);
|
|
58151
58275
|
}
|
|
58152
58276
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
|
|
58153
|
-
const scriptPath = (0,
|
|
58277
|
+
const scriptPath = (0, import_node_path58.join)(deps.deployKitRoot, "scripts", "publish.sh");
|
|
58154
58278
|
deps.logger?.info("app-builder.publish.start", {
|
|
58155
58279
|
name: args.name,
|
|
58156
58280
|
sessionId: boundSession.sessionId,
|
|
@@ -58326,7 +58450,7 @@ function buildVisitorHandlers(deps) {
|
|
|
58326
58450
|
|
|
58327
58451
|
// src/extension/registry.ts
|
|
58328
58452
|
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
58329
|
-
var
|
|
58453
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
58330
58454
|
async function loadAll(root) {
|
|
58331
58455
|
let entries;
|
|
58332
58456
|
try {
|
|
@@ -58339,13 +58463,13 @@ async function loadAll(root) {
|
|
|
58339
58463
|
for (const ent of entries) {
|
|
58340
58464
|
if (!ent.isDirectory()) continue;
|
|
58341
58465
|
if (ent.name.startsWith(".")) continue;
|
|
58342
|
-
records.push(await loadOne(
|
|
58466
|
+
records.push(await loadOne(import_node_path59.default.join(root, ent.name), ent.name));
|
|
58343
58467
|
}
|
|
58344
58468
|
records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
|
|
58345
58469
|
return records;
|
|
58346
58470
|
}
|
|
58347
58471
|
async function loadOne(dir, dirName) {
|
|
58348
|
-
const manifestPath =
|
|
58472
|
+
const manifestPath = import_node_path59.default.join(dir, "manifest.json");
|
|
58349
58473
|
let raw;
|
|
58350
58474
|
try {
|
|
58351
58475
|
raw = await import_promises9.default.readFile(manifestPath, "utf8");
|
|
@@ -58390,7 +58514,7 @@ async function loadOne(dir, dirName) {
|
|
|
58390
58514
|
|
|
58391
58515
|
// src/extension/uninstall.ts
|
|
58392
58516
|
var import_promises10 = __toESM(require("fs/promises"), 1);
|
|
58393
|
-
var
|
|
58517
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
58394
58518
|
var UninstallError = class extends Error {
|
|
58395
58519
|
constructor(code, message) {
|
|
58396
58520
|
super(message);
|
|
@@ -58399,7 +58523,7 @@ var UninstallError = class extends Error {
|
|
|
58399
58523
|
code;
|
|
58400
58524
|
};
|
|
58401
58525
|
async function uninstall(deps) {
|
|
58402
|
-
const dir =
|
|
58526
|
+
const dir = import_node_path60.default.join(deps.root, deps.extId);
|
|
58403
58527
|
try {
|
|
58404
58528
|
await import_promises10.default.access(dir);
|
|
58405
58529
|
} catch {
|
|
@@ -58411,6 +58535,131 @@ async function uninstall(deps) {
|
|
|
58411
58535
|
|
|
58412
58536
|
// src/handlers/index.ts
|
|
58413
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
|
|
58414
58663
|
function buildMethodHandlers(deps) {
|
|
58415
58664
|
return {
|
|
58416
58665
|
...buildSessionHandlers({
|
|
@@ -58460,6 +58709,12 @@ function buildMethodHandlers(deps) {
|
|
|
58460
58709
|
sshdDir: deps.sshdDir,
|
|
58461
58710
|
sshLog: deps.contactSshLog
|
|
58462
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
|
+
}),
|
|
58463
58718
|
whoami: buildWhoamiHandler({
|
|
58464
58719
|
ownerDisplayName: deps.ownerDisplayName,
|
|
58465
58720
|
ownerPrincipalId: deps.ownerPrincipalId,
|
|
@@ -58511,7 +58766,7 @@ function buildMethodHandlers(deps) {
|
|
|
58511
58766
|
}
|
|
58512
58767
|
|
|
58513
58768
|
// src/app-builder/dev-server-supervisor.ts
|
|
58514
|
-
var
|
|
58769
|
+
var import_node_child_process18 = require("child_process");
|
|
58515
58770
|
var import_node_events2 = require("events");
|
|
58516
58771
|
var DEFAULT_READY_PATTERN = /Local:\s+https?:\/\/|Nest application successfully started|server listening on/i;
|
|
58517
58772
|
var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
@@ -58548,7 +58803,7 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
|
58548
58803
|
tunnelHost: args.tunnelHost,
|
|
58549
58804
|
devCommand: cmd
|
|
58550
58805
|
});
|
|
58551
|
-
const child = (0,
|
|
58806
|
+
const child = (0, import_node_child_process18.spawn)("sh", ["-c", cmd], {
|
|
58552
58807
|
cwd: args.cwd,
|
|
58553
58808
|
env,
|
|
58554
58809
|
stdio: "pipe",
|
|
@@ -58800,9 +59055,9 @@ var METHOD_GRANT_MAP = {
|
|
|
58800
59055
|
"contact:list": ADMIN_ANY,
|
|
58801
59056
|
"contact:pin": ADMIN_ANY,
|
|
58802
59057
|
"contact:remove": ADMIN_ANY,
|
|
58803
|
-
// contact:
|
|
59058
|
+
// contact:setRemoteAccess (owner UI 配 SSH 授权):ADMIN_ANY
|
|
58804
59059
|
// (对齐 inbox:postMessage 的"能连上=有 auth,业务在 handler 校"模式)
|
|
58805
|
-
"contact:
|
|
59060
|
+
"contact:setRemoteAccess": ADMIN_ANY,
|
|
58806
59061
|
// ---- visitor:* (访客名单,owner-only) ----
|
|
58807
59062
|
// owner 看完整访客名单(含没开会话的);guest 不可调(handler 内再 assertOwner 兜底)。
|
|
58808
59063
|
"visitor:list": ADMIN_ANY,
|
|
@@ -58881,6 +59136,9 @@ var METHOD_GRANT_MAP = {
|
|
|
58881
59136
|
// run handler 内做 persona.public + assertGrant(send) 双重校验(guest grants 是 wildcard,
|
|
58882
59137
|
// 单 assertGrant 不够)。本地 cc 仍走 /api/rpc(EXPOSED_RPC_METHODS),不经此表。
|
|
58883
59138
|
"personaDispatch:run": CAPABILITY_SCOPED,
|
|
59139
|
+
// 跨设备 exec(spec 2026-07-16):public——"能连上=有 auth",业务在 handler 校
|
|
59140
|
+
// (ensureGuest + remoteAccessAllowed),跟 inbox:postMessage 同模式。
|
|
59141
|
+
"peerExec:run": { kind: "public" },
|
|
58884
59142
|
// 2026-05-26 修 clawd people TUI 渲染 mix 串扰回归(参考 f1133ff0 原 owner 路径
|
|
58885
59143
|
// snapshot/buffer/flush 修复):之前三件套 ADMIN_ANY 把 guest 拦在 dispatcher 401,
|
|
58886
59144
|
// 导致 XtermPanel 重挂载拉 snapshot 在 guest 路径不生效,老的字节流断裂串扰回来。
|
|
@@ -58987,8 +59245,8 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
|
|
|
58987
59245
|
}
|
|
58988
59246
|
|
|
58989
59247
|
// src/extension/runtime.ts
|
|
58990
|
-
var
|
|
58991
|
-
var
|
|
59248
|
+
var import_node_child_process19 = require("child_process");
|
|
59249
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
58992
59250
|
var import_promises11 = require("timers/promises");
|
|
58993
59251
|
|
|
58994
59252
|
// src/extension/port-allocator.ts
|
|
@@ -59089,13 +59347,13 @@ var Runtime = class {
|
|
|
59089
59347
|
/\$CLAWOS_EXT_PORT/g,
|
|
59090
59348
|
String(port)
|
|
59091
59349
|
);
|
|
59092
|
-
const dir =
|
|
59350
|
+
const dir = import_node_path61.default.join(this.root, extId);
|
|
59093
59351
|
const env = {
|
|
59094
59352
|
...process.env,
|
|
59095
59353
|
CLAWOS_EXT_PORT: String(port),
|
|
59096
59354
|
CLAWOS_EXT_ID: extId
|
|
59097
59355
|
};
|
|
59098
|
-
const child = (0,
|
|
59356
|
+
const child = (0, import_node_child_process19.spawn)("sh", ["-c", cmd], {
|
|
59099
59357
|
cwd: dir,
|
|
59100
59358
|
env,
|
|
59101
59359
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -59201,7 +59459,7 @@ ${handle.stderrTail}`
|
|
|
59201
59459
|
|
|
59202
59460
|
// src/extension/published-channels.ts
|
|
59203
59461
|
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
59204
|
-
var
|
|
59462
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
59205
59463
|
init_zod();
|
|
59206
59464
|
var PublishedChannelsError = class extends Error {
|
|
59207
59465
|
constructor(code, message) {
|
|
@@ -59300,7 +59558,7 @@ var PublishedChannelStore = class {
|
|
|
59300
59558
|
)
|
|
59301
59559
|
};
|
|
59302
59560
|
const tmp = `${this.filePath}.tmp`;
|
|
59303
|
-
await import_promises12.default.mkdir(
|
|
59561
|
+
await import_promises12.default.mkdir(import_node_path62.default.dirname(this.filePath), { recursive: true });
|
|
59304
59562
|
await import_promises12.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
59305
59563
|
await import_promises12.default.rename(tmp, this.filePath);
|
|
59306
59564
|
}
|
|
@@ -59308,7 +59566,7 @@ var PublishedChannelStore = class {
|
|
|
59308
59566
|
|
|
59309
59567
|
// src/extension/bundle-cache.ts
|
|
59310
59568
|
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
59311
|
-
var
|
|
59569
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
59312
59570
|
var BundleCache = class {
|
|
59313
59571
|
constructor(rootDir) {
|
|
59314
59572
|
this.rootDir = rootDir;
|
|
@@ -59317,14 +59575,14 @@ var BundleCache = class {
|
|
|
59317
59575
|
/** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
|
|
59318
59576
|
async write(snapshotHash, buffer) {
|
|
59319
59577
|
await import_promises13.default.mkdir(this.rootDir, { recursive: true });
|
|
59320
|
-
const file =
|
|
59578
|
+
const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59321
59579
|
const tmp = `${file}.tmp`;
|
|
59322
59580
|
await import_promises13.default.writeFile(tmp, buffer, { mode: 384 });
|
|
59323
59581
|
await import_promises13.default.rename(tmp, file);
|
|
59324
59582
|
}
|
|
59325
59583
|
/** Returns the bundle bytes, or null when the file doesn't exist. */
|
|
59326
59584
|
async read(snapshotHash) {
|
|
59327
|
-
const file =
|
|
59585
|
+
const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59328
59586
|
try {
|
|
59329
59587
|
return await import_promises13.default.readFile(file);
|
|
59330
59588
|
} catch (e) {
|
|
@@ -59334,7 +59592,7 @@ var BundleCache = class {
|
|
|
59334
59592
|
}
|
|
59335
59593
|
/** Idempotent — missing file is not an error. */
|
|
59336
59594
|
async delete(snapshotHash) {
|
|
59337
|
-
const file =
|
|
59595
|
+
const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59338
59596
|
await import_promises13.default.rm(file, { force: true });
|
|
59339
59597
|
}
|
|
59340
59598
|
};
|
|
@@ -59343,7 +59601,7 @@ var BundleCache = class {
|
|
|
59343
59601
|
var import_meta6 = {};
|
|
59344
59602
|
function currentOsUser() {
|
|
59345
59603
|
try {
|
|
59346
|
-
return
|
|
59604
|
+
return import_node_os22.default.userInfo().username;
|
|
59347
59605
|
} catch {
|
|
59348
59606
|
return "";
|
|
59349
59607
|
}
|
|
@@ -59362,26 +59620,26 @@ async function startDaemon(config) {
|
|
|
59362
59620
|
},
|
|
59363
59621
|
source: "daemon",
|
|
59364
59622
|
sampling: logShippingCfg.sampling,
|
|
59365
|
-
homeDir:
|
|
59623
|
+
homeDir: import_node_os22.default.homedir()
|
|
59366
59624
|
});
|
|
59367
|
-
const logDir =
|
|
59368
|
-
|
|
59625
|
+
const logDir = import_node_path64.default.join(config.dataDir, "log");
|
|
59626
|
+
import_node_fs52.default.mkdirSync(logDir, { recursive: true });
|
|
59369
59627
|
const logger = createLogger({
|
|
59370
59628
|
level: config.logLevel,
|
|
59371
|
-
file:
|
|
59629
|
+
file: import_node_path64.default.join(logDir, "clawd.log"),
|
|
59372
59630
|
logClient
|
|
59373
59631
|
});
|
|
59374
59632
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
59375
59633
|
const screenIdleProbeLogger = createFileOnlyLogger({
|
|
59376
|
-
file:
|
|
59634
|
+
file: import_node_path64.default.join(logDir, "screen-idle-probe.log"),
|
|
59377
59635
|
level: "debug"
|
|
59378
59636
|
});
|
|
59379
59637
|
logger.info("screen-idle probe logger enabled", {
|
|
59380
|
-
file:
|
|
59638
|
+
file: import_node_path64.default.join(logDir, "screen-idle-probe.log")
|
|
59381
59639
|
});
|
|
59382
59640
|
const CAP_TARGETS = [
|
|
59383
|
-
|
|
59384
|
-
|
|
59641
|
+
import_node_path64.default.join(logDir, "clawd.log"),
|
|
59642
|
+
import_node_path64.default.join(logDir, "screen-idle-probe.log")
|
|
59385
59643
|
];
|
|
59386
59644
|
const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
|
|
59387
59645
|
const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
|
|
@@ -59492,7 +59750,7 @@ async function startDaemon(config) {
|
|
|
59492
59750
|
// 立即重建 authorized_keys(重新握手 = 迁移路径)。见 …#建联交换清单
|
|
59493
59751
|
sshPublicKey: selfSshPublicKey,
|
|
59494
59752
|
osUser: selfOsUser,
|
|
59495
|
-
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore,
|
|
59753
|
+
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path64.default.join(config.dataDir, "sshd")),
|
|
59496
59754
|
// 决策 #16 换票补全:用 owner 当前 jwt 换 aud=对方设备 的票(本机主动连对方用)。
|
|
59497
59755
|
// 现读 ownerIdentityStore(热切换后用新身份);失败 → null → 保持空票。
|
|
59498
59756
|
exchangeToken: async (targetDeviceId) => {
|
|
@@ -59540,8 +59798,8 @@ async function startDaemon(config) {
|
|
|
59540
59798
|
const agents = new AgentsScanner();
|
|
59541
59799
|
const history = new ClaudeHistoryReader();
|
|
59542
59800
|
let transport = null;
|
|
59543
|
-
const personaStore = new PersonaStore(
|
|
59544
|
-
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");
|
|
59545
59803
|
const defaultsRoot = findDefaultsRoot(logger);
|
|
59546
59804
|
if (defaultsRoot) {
|
|
59547
59805
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -59561,17 +59819,17 @@ async function startDaemon(config) {
|
|
|
59561
59819
|
migrateCodexSandbox({ store: personaStore, logger });
|
|
59562
59820
|
const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
|
|
59563
59821
|
const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
|
|
59564
|
-
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));
|
|
59565
59823
|
const mcpConfigs = [];
|
|
59566
59824
|
const dispatchServerCandidates = [
|
|
59567
|
-
|
|
59825
|
+
import_node_path64.default.join(here, "dispatch", "mcp-server.cjs"),
|
|
59568
59826
|
// 生产 dist/index → dist/dispatch/mcp-server.cjs
|
|
59569
|
-
|
|
59827
|
+
import_node_path64.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
|
|
59570
59828
|
// dev tsx src/index → ../dist/dispatch/mcp-server.cjs
|
|
59571
59829
|
];
|
|
59572
|
-
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) =>
|
|
59830
|
+
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
|
|
59573
59831
|
if (dispatchServerScriptPath) {
|
|
59574
|
-
const dispatchLogPath =
|
|
59832
|
+
const dispatchLogPath = import_node_path64.default.join(logDir, "dispatch-mcp-server.log");
|
|
59575
59833
|
const dispatchCfgPath = writeDispatchMcpConfig({
|
|
59576
59834
|
dataDir: config.dataDir,
|
|
59577
59835
|
serverScriptPath: dispatchServerScriptPath,
|
|
@@ -59589,12 +59847,12 @@ async function startDaemon(config) {
|
|
|
59589
59847
|
});
|
|
59590
59848
|
}
|
|
59591
59849
|
const ticketServerCandidates = [
|
|
59592
|
-
|
|
59593
|
-
|
|
59850
|
+
import_node_path64.default.join(here, "ticket", "mcp-server.cjs"),
|
|
59851
|
+
import_node_path64.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
|
|
59594
59852
|
];
|
|
59595
|
-
const ticketServerScriptPath = ticketServerCandidates.find((p2) =>
|
|
59853
|
+
const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
|
|
59596
59854
|
if (ticketServerScriptPath) {
|
|
59597
|
-
const ticketLogPath =
|
|
59855
|
+
const ticketLogPath = import_node_path64.default.join(logDir, "ticket-mcp-server.log");
|
|
59598
59856
|
const ticketCfgPath = writeTicketMcpConfig({
|
|
59599
59857
|
dataDir: config.dataDir,
|
|
59600
59858
|
serverScriptPath: ticketServerScriptPath,
|
|
@@ -59614,12 +59872,12 @@ async function startDaemon(config) {
|
|
|
59614
59872
|
});
|
|
59615
59873
|
}
|
|
59616
59874
|
const shiftServerCandidates = [
|
|
59617
|
-
|
|
59618
|
-
|
|
59875
|
+
import_node_path64.default.join(here, "shift", "mcp-server.cjs"),
|
|
59876
|
+
import_node_path64.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
|
|
59619
59877
|
];
|
|
59620
|
-
const shiftServerScriptPath = shiftServerCandidates.find((p2) =>
|
|
59878
|
+
const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
|
|
59621
59879
|
if (shiftServerScriptPath) {
|
|
59622
|
-
const shiftLogPath =
|
|
59880
|
+
const shiftLogPath = import_node_path64.default.join(logDir, "shift-mcp-server.log");
|
|
59623
59881
|
const shiftCfgPath = await writeShiftMcpConfig({
|
|
59624
59882
|
dataDir: config.dataDir,
|
|
59625
59883
|
serverScriptPath: shiftServerScriptPath,
|
|
@@ -59638,12 +59896,12 @@ async function startDaemon(config) {
|
|
|
59638
59896
|
);
|
|
59639
59897
|
}
|
|
59640
59898
|
const inboxServerCandidates = [
|
|
59641
|
-
|
|
59642
|
-
|
|
59899
|
+
import_node_path64.default.join(here, "inbox", "mcp-server.cjs"),
|
|
59900
|
+
import_node_path64.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
|
|
59643
59901
|
];
|
|
59644
|
-
const inboxServerScriptPath = inboxServerCandidates.find((p2) =>
|
|
59902
|
+
const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
|
|
59645
59903
|
if (inboxServerScriptPath) {
|
|
59646
|
-
const inboxLogPath =
|
|
59904
|
+
const inboxLogPath = import_node_path64.default.join(logDir, "inbox-mcp-server.log");
|
|
59647
59905
|
const inboxCfgPath = await writeInboxMcpConfig({
|
|
59648
59906
|
dataDir: config.dataDir,
|
|
59649
59907
|
serverScriptPath: inboxServerScriptPath,
|
|
@@ -59661,8 +59919,29 @@ async function startDaemon(config) {
|
|
|
59661
59919
|
{ tried: inboxServerCandidates }
|
|
59662
59920
|
);
|
|
59663
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
|
+
}
|
|
59664
59943
|
const shiftStore = createShiftStore({
|
|
59665
|
-
filePath:
|
|
59944
|
+
filePath: import_node_path64.default.join(config.dataDir, "shift.json"),
|
|
59666
59945
|
ownerIdProvider: () => ownerPrincipalId,
|
|
59667
59946
|
now: () => Date.now()
|
|
59668
59947
|
});
|
|
@@ -59684,7 +59963,7 @@ async function startDaemon(config) {
|
|
|
59684
59963
|
getAdapter,
|
|
59685
59964
|
historyReader: history,
|
|
59686
59965
|
dataDir: config.dataDir,
|
|
59687
|
-
personaRoot:
|
|
59966
|
+
personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
|
|
59688
59967
|
usersRoot,
|
|
59689
59968
|
personaStore,
|
|
59690
59969
|
ownerDisplayName,
|
|
@@ -59722,10 +60001,10 @@ async function startDaemon(config) {
|
|
|
59722
60001
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
59723
60002
|
attachmentGroup: {
|
|
59724
60003
|
onFileEdit: (input) => {
|
|
59725
|
-
const absPath =
|
|
60004
|
+
const absPath = import_node_path64.default.isAbsolute(input.relPath) ? input.relPath : import_node_path64.default.join(input.cwd, input.relPath);
|
|
59726
60005
|
let size = 0;
|
|
59727
60006
|
try {
|
|
59728
|
-
size =
|
|
60007
|
+
size = import_node_fs52.default.statSync(absPath).size;
|
|
59729
60008
|
} catch (err) {
|
|
59730
60009
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
59731
60010
|
sessionId: input.sessionId,
|
|
@@ -59962,11 +60241,11 @@ async function startDaemon(config) {
|
|
|
59962
60241
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
59963
60242
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
59964
60243
|
// guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
|
|
59965
|
-
personaRoot:
|
|
60244
|
+
personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
|
|
59966
60245
|
usersRoot
|
|
59967
60246
|
},
|
|
59968
60247
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
59969
|
-
personaRoot:
|
|
60248
|
+
personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
|
|
59970
60249
|
// v2 多人 persona 隔离:handler 派生 guest user-dir 放行
|
|
59971
60250
|
usersRoot,
|
|
59972
60251
|
// capability:list / delete handler 依赖
|
|
@@ -59995,7 +60274,7 @@ async function startDaemon(config) {
|
|
|
59995
60274
|
contactStore,
|
|
59996
60275
|
// <dataDir>/sshd 绝对路径 —— contact-ssh handlers 用它拼 authorized_keys / keys/ 子路径
|
|
59997
60276
|
// Task 10 会加 SshdManager 起 sshd;handlers wire 提前挂 sshdDir 让 typecheck 过
|
|
59998
|
-
sshdDir:
|
|
60277
|
+
sshdDir: import_node_path64.default.join(config.dataDir, "sshd"),
|
|
59999
60278
|
contactSshLog: sshLog,
|
|
60000
60279
|
// inbox:sendDm 用:sessionId → session 出身(复用 attachment 同款 findOwnedSessionScope)
|
|
60001
60280
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
@@ -60069,7 +60348,7 @@ async function startDaemon(config) {
|
|
|
60069
60348
|
selfSshPublicKey: () => readSshIdentityPublicKey(config.dataDir),
|
|
60070
60349
|
// 建联交换:出站自报本机 OS 账号名。见 doc/glossary/peer-mesh.md#建联交换清单
|
|
60071
60350
|
selfOsUser: currentOsUser,
|
|
60072
|
-
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore,
|
|
60351
|
+
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path64.default.join(config.dataDir, "sshd"))
|
|
60073
60352
|
},
|
|
60074
60353
|
// app-builder
|
|
60075
60354
|
appBuilderStore,
|
|
@@ -60092,11 +60371,11 @@ async function startDaemon(config) {
|
|
|
60092
60371
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2):
|
|
60093
60372
|
// appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
|
|
60094
60373
|
// dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
|
|
60095
|
-
appBuilderPersonaRoot:
|
|
60374
|
+
appBuilderPersonaRoot: import_node_path64.default.join(config.dataDir, "personas", "persona-app-builder"),
|
|
60096
60375
|
// 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
|
|
60097
|
-
deployKitRoot:
|
|
60376
|
+
deployKitRoot: import_node_path64.default.join(config.dataDir, "deploy-kit"),
|
|
60098
60377
|
// scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
|
|
60099
|
-
resolvePersonaRoot: (personaId) =>
|
|
60378
|
+
resolvePersonaRoot: (personaId) => import_node_path64.default.join(config.dataDir, "personas", personaId),
|
|
60100
60379
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
|
|
60101
60380
|
// 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
|
|
60102
60381
|
// 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
|
|
@@ -60139,8 +60418,8 @@ async function startDaemon(config) {
|
|
|
60139
60418
|
}
|
|
60140
60419
|
let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
|
|
60141
60420
|
if (sourceFile && sourceFile.toolSessionId) {
|
|
60142
|
-
sourceJsonlPath =
|
|
60143
|
-
|
|
60421
|
+
sourceJsonlPath = import_node_path64.default.join(
|
|
60422
|
+
import_node_os22.default.homedir(),
|
|
60144
60423
|
".claude",
|
|
60145
60424
|
"projects",
|
|
60146
60425
|
cwdToHashDir(sourceFile.cwd),
|
|
@@ -60499,8 +60778,8 @@ async function startDaemon(config) {
|
|
|
60499
60778
|
const lines = [
|
|
60500
60779
|
`Tunnel: ${r.url}`,
|
|
60501
60780
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
60502
|
-
`Frpc config: ${
|
|
60503
|
-
`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")}`
|
|
60504
60783
|
];
|
|
60505
60784
|
const width = Math.max(...lines.map((l) => l.length));
|
|
60506
60785
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -60513,8 +60792,8 @@ ${bar}
|
|
|
60513
60792
|
|
|
60514
60793
|
`);
|
|
60515
60794
|
try {
|
|
60516
|
-
const connectPath =
|
|
60517
|
-
|
|
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 });
|
|
60518
60797
|
} catch {
|
|
60519
60798
|
}
|
|
60520
60799
|
} catch (err) {
|
|
@@ -60550,7 +60829,7 @@ ${bar}
|
|
|
60550
60829
|
});
|
|
60551
60830
|
try {
|
|
60552
60831
|
await sshdMgr.start();
|
|
60553
|
-
rebuildAuthorizedKeys(contactStore,
|
|
60832
|
+
rebuildAuthorizedKeys(contactStore, import_node_path64.default.join(config.dataDir, "sshd"));
|
|
60554
60833
|
logger.info("sshd: contact-ssh sandbox ready", { port: config.sshdPort });
|
|
60555
60834
|
} catch (err) {
|
|
60556
60835
|
logger.warn("sshd start failed; contact SSH grant will not work until fixed", {
|
|
@@ -60609,9 +60888,9 @@ ${bar}
|
|
|
60609
60888
|
};
|
|
60610
60889
|
}
|
|
60611
60890
|
function migrateDropPersonsDir(dataDir) {
|
|
60612
|
-
const dir =
|
|
60891
|
+
const dir = import_node_path64.default.join(dataDir, "persons");
|
|
60613
60892
|
try {
|
|
60614
|
-
|
|
60893
|
+
import_node_fs52.default.rmSync(dir, { recursive: true, force: true });
|
|
60615
60894
|
} catch {
|
|
60616
60895
|
}
|
|
60617
60896
|
}
|