@clawos-dev/clawd 0.2.240 → 0.2.242
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 +362 -1653
- package/dist/dispatch/mcp-server.cjs +5 -25
- package/dist/persona-defaults/persona-clawd-helper/CLAUDE.md +1 -1
- package/dist/share-md-viewer-error.html +1 -1
- package/dist/share-md-viewer.html +1 -1
- package/dist/share-ui/assets/{guest-DIEZ-f-B.js → guest-BtdshAz9.js} +12 -12
- package/dist/share-ui/assets/guest-Dx-pn5IU.css +32 -0
- package/dist/share-ui/guest.html +2 -2
- package/package.json +2 -2
- package/dist/share-ui/assets/guest-BXzrtMaF.css +0 -32
package/dist/cli.cjs
CHANGED
|
@@ -167,8 +167,8 @@ var init_methods = __esm({
|
|
|
167
167
|
"contact:pin",
|
|
168
168
|
"contact:remove",
|
|
169
169
|
// ---- contact:setRemoteAccess (远程访问授权,spec 2026-07-16) ----
|
|
170
|
-
// owner UI 授权 contact
|
|
171
|
-
// 详见 protocol/src/contact-
|
|
170
|
+
// owner UI 授权 contact 反向操作我的设备(跨设备 exec 开关 remoteAccessAllowed)。
|
|
171
|
+
// 详见 protocol/src/contact-remote-access.ts.
|
|
172
172
|
"contact:setRemoteAccess",
|
|
173
173
|
// ---- visitor:* (web 访客 · persona web 分享,spec 2026-06-24-persona-web-share-design) ----
|
|
174
174
|
// owner-only:列出登录访问过本机 public persona 的 web 访客(零安装、飞书登录、daemon 自签
|
|
@@ -749,8 +749,8 @@ var init_parseUtil = __esm({
|
|
|
749
749
|
init_errors2();
|
|
750
750
|
init_en();
|
|
751
751
|
makeIssue = (params) => {
|
|
752
|
-
const { data, path:
|
|
753
|
-
const fullPath = [...
|
|
752
|
+
const { data, path: path70, errorMaps, issueData } = params;
|
|
753
|
+
const fullPath = [...path70, ...issueData.path || []];
|
|
754
754
|
const fullIssue = {
|
|
755
755
|
...issueData,
|
|
756
756
|
path: fullPath
|
|
@@ -1061,11 +1061,11 @@ var init_types = __esm({
|
|
|
1061
1061
|
init_parseUtil();
|
|
1062
1062
|
init_util();
|
|
1063
1063
|
ParseInputLazyPath = class {
|
|
1064
|
-
constructor(parent, value,
|
|
1064
|
+
constructor(parent, value, path70, key) {
|
|
1065
1065
|
this._cachedPath = [];
|
|
1066
1066
|
this.parent = parent;
|
|
1067
1067
|
this.data = value;
|
|
1068
|
-
this._path =
|
|
1068
|
+
this._path = path70;
|
|
1069
1069
|
this._key = key;
|
|
1070
1070
|
}
|
|
1071
1071
|
get path() {
|
|
@@ -5330,11 +5330,7 @@ var init_schemas = __esm({
|
|
|
5330
5330
|
* selfUrl 保留:它只是"可达地址"(auto-reverse 反向连接用),不是身份——填错只影响
|
|
5331
5331
|
* 反向连通性,不构成伪造风险,且 url 不在 token 里(token 只签身份不签地址)。
|
|
5332
5332
|
*/
|
|
5333
|
-
selfUrl: external_exports.string().min(1).optional()
|
|
5334
|
-
/** 连接方自报本机 SSH identity pubkey;见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5335
|
-
selfSshPublicKey: external_exports.string().min(1).optional(),
|
|
5336
|
-
/** 连接方自报本机 OS 登录账号名;见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5337
|
-
selfOsUser: external_exports.string().min(1).optional()
|
|
5333
|
+
selfUrl: external_exports.string().min(1).optional()
|
|
5338
5334
|
});
|
|
5339
5335
|
AuthOkFrameSchema = external_exports.object({
|
|
5340
5336
|
type: external_exports.literal("auth:ok"),
|
|
@@ -5657,11 +5653,7 @@ var init_capability = __esm({
|
|
|
5657
5653
|
id: external_exports.string().min(1),
|
|
5658
5654
|
displayName: external_exports.string()
|
|
5659
5655
|
}).strict()
|
|
5660
|
-
)
|
|
5661
|
-
/** 被连方 SSH identity pubkey;见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5662
|
-
sshPublicKey: external_exports.string().min(1).optional(),
|
|
5663
|
-
/** 被连方 OS 登录账号名;见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5664
|
-
osUser: external_exports.string().min(1).optional()
|
|
5656
|
+
)
|
|
5665
5657
|
}).strict();
|
|
5666
5658
|
PERSONAL_CAP_GRANTS = Object.freeze([
|
|
5667
5659
|
Object.freeze({
|
|
@@ -5751,22 +5743,10 @@ var init_contact = __esm({
|
|
|
5751
5743
|
pinnedAt: external_exports.number().int().nullable().default(null),
|
|
5752
5744
|
/**
|
|
5753
5745
|
* 远程访问授权(spec 2026-07-16)。owner 视角:允许该 contact 反向操作我的设备——
|
|
5754
|
-
*
|
|
5755
|
-
* (rebuildAuthorizedKeys 读它)。default=false,owner 显式开。
|
|
5746
|
+
* 跨设备 exec 的总开关(peerExec:run handler 运行时判它)。default=false,owner 显式开。
|
|
5756
5747
|
* 授权粒度 per-people (per-contact):借用别人 persona 本来就带信任对方本人的前提。
|
|
5757
5748
|
*/
|
|
5758
|
-
remoteAccessAllowed: external_exports.boolean().default(false)
|
|
5759
|
-
/** 对端设备 SSH identity pubkey(一行 `ssh-ed25519 AAAA... comment`);见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5760
|
-
sshPublicKey: external_exports.string().optional(),
|
|
5761
|
-
/** 对端 OS 登录账号名,SSH 拨号登录名(`<osUser>@127.0.0.1`);见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5762
|
-
osUser: external_exports.string().optional(),
|
|
5763
|
-
/**
|
|
5764
|
-
* @deprecated T-29 去沙箱后废弃。原为授权访问的目录白名单(clawd-ssh-jail 生成 sbpl/bwrap
|
|
5765
|
-
* policy 用);去掉 OS 沙箱后不再有目录级限制,远程访问授权即 on/off(见 remoteAccessAllowed)。
|
|
5766
|
-
* 字段仅为兼容老 contacts.json 保留(default [] 保证 strict parse 不炸),不再有代码读写它,
|
|
5767
|
-
* 也不再进 setRemoteAccess RPC / ContactSettingsDrawer / jail。勿新增消费方。
|
|
5768
|
-
*/
|
|
5769
|
-
exposedDirs: external_exports.array(external_exports.string()).default([])
|
|
5749
|
+
remoteAccessAllowed: external_exports.boolean().default(false)
|
|
5770
5750
|
}).strict();
|
|
5771
5751
|
ContactWireSchema = ContactSchema;
|
|
5772
5752
|
ContactRemoveArgsSchema = external_exports.object({
|
|
@@ -5805,10 +5785,10 @@ var init_contact = __esm({
|
|
|
5805
5785
|
}
|
|
5806
5786
|
});
|
|
5807
5787
|
|
|
5808
|
-
// ../protocol/src/contact-
|
|
5788
|
+
// ../protocol/src/contact-remote-access.ts
|
|
5809
5789
|
var ContactSetRemoteAccessArgsSchema, ContactSetRemoteAccessOkSchema, ContactRemoteAccessUpdatedFrameSchema;
|
|
5810
|
-
var
|
|
5811
|
-
"../protocol/src/contact-
|
|
5790
|
+
var init_contact_remote_access = __esm({
|
|
5791
|
+
"../protocol/src/contact-remote-access.ts"() {
|
|
5812
5792
|
"use strict";
|
|
5813
5793
|
init_zod();
|
|
5814
5794
|
ContactSetRemoteAccessArgsSchema = external_exports.object({
|
|
@@ -6261,7 +6241,7 @@ var init_runtime = __esm({
|
|
|
6261
6241
|
init_capability();
|
|
6262
6242
|
init_inbox();
|
|
6263
6243
|
init_contact();
|
|
6264
|
-
|
|
6244
|
+
init_contact_remote_access();
|
|
6265
6245
|
init_peer_exec();
|
|
6266
6246
|
init_extension();
|
|
6267
6247
|
init_feishu_auth();
|
|
@@ -6542,8 +6522,8 @@ var require_req = __commonJS({
|
|
|
6542
6522
|
if (req.originalUrl) {
|
|
6543
6523
|
_req.url = req.originalUrl;
|
|
6544
6524
|
} else {
|
|
6545
|
-
const
|
|
6546
|
-
_req.url = typeof
|
|
6525
|
+
const path70 = req.path;
|
|
6526
|
+
_req.url = typeof path70 === "string" ? path70 : req.url ? req.url.path || req.url : void 0;
|
|
6547
6527
|
}
|
|
6548
6528
|
if (req.query) {
|
|
6549
6529
|
_req.query = req.query;
|
|
@@ -6708,14 +6688,14 @@ var require_redact = __commonJS({
|
|
|
6708
6688
|
}
|
|
6709
6689
|
return obj;
|
|
6710
6690
|
}
|
|
6711
|
-
function parsePath(
|
|
6691
|
+
function parsePath(path70) {
|
|
6712
6692
|
const parts = [];
|
|
6713
6693
|
let current = "";
|
|
6714
6694
|
let inBrackets = false;
|
|
6715
6695
|
let inQuotes = false;
|
|
6716
6696
|
let quoteChar = "";
|
|
6717
|
-
for (let i = 0; i <
|
|
6718
|
-
const char =
|
|
6697
|
+
for (let i = 0; i < path70.length; i++) {
|
|
6698
|
+
const char = path70[i];
|
|
6719
6699
|
if (!inBrackets && char === ".") {
|
|
6720
6700
|
if (current) {
|
|
6721
6701
|
parts.push(current);
|
|
@@ -6846,10 +6826,10 @@ var require_redact = __commonJS({
|
|
|
6846
6826
|
return current;
|
|
6847
6827
|
}
|
|
6848
6828
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
6849
|
-
for (const
|
|
6850
|
-
const parts = parsePath(
|
|
6829
|
+
for (const path70 of paths) {
|
|
6830
|
+
const parts = parsePath(path70);
|
|
6851
6831
|
if (parts.includes("*")) {
|
|
6852
|
-
redactWildcardPath(obj, parts, censor,
|
|
6832
|
+
redactWildcardPath(obj, parts, censor, path70, remove);
|
|
6853
6833
|
} else {
|
|
6854
6834
|
if (remove) {
|
|
6855
6835
|
removeKey(obj, parts);
|
|
@@ -6934,8 +6914,8 @@ var require_redact = __commonJS({
|
|
|
6934
6914
|
}
|
|
6935
6915
|
} else {
|
|
6936
6916
|
if (afterWildcard.includes("*")) {
|
|
6937
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6938
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
6917
|
+
const wrappedCensor = typeof censor === "function" ? (value, path70) => {
|
|
6918
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path70];
|
|
6939
6919
|
return censor(value, fullPath);
|
|
6940
6920
|
} : censor;
|
|
6941
6921
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -6970,8 +6950,8 @@ var require_redact = __commonJS({
|
|
|
6970
6950
|
return null;
|
|
6971
6951
|
}
|
|
6972
6952
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
6973
|
-
for (const
|
|
6974
|
-
const parts = parsePath(
|
|
6953
|
+
for (const path70 of pathsToClone) {
|
|
6954
|
+
const parts = parsePath(path70);
|
|
6975
6955
|
let current = pathStructure;
|
|
6976
6956
|
for (let i = 0; i < parts.length; i++) {
|
|
6977
6957
|
const part = parts[i];
|
|
@@ -7023,24 +7003,24 @@ var require_redact = __commonJS({
|
|
|
7023
7003
|
}
|
|
7024
7004
|
return cloneSelectively(obj, pathStructure);
|
|
7025
7005
|
}
|
|
7026
|
-
function validatePath(
|
|
7027
|
-
if (typeof
|
|
7006
|
+
function validatePath(path70) {
|
|
7007
|
+
if (typeof path70 !== "string") {
|
|
7028
7008
|
throw new Error("Paths must be (non-empty) strings");
|
|
7029
7009
|
}
|
|
7030
|
-
if (
|
|
7010
|
+
if (path70 === "") {
|
|
7031
7011
|
throw new Error("Invalid redaction path ()");
|
|
7032
7012
|
}
|
|
7033
|
-
if (
|
|
7034
|
-
throw new Error(`Invalid redaction path (${
|
|
7013
|
+
if (path70.includes("..")) {
|
|
7014
|
+
throw new Error(`Invalid redaction path (${path70})`);
|
|
7035
7015
|
}
|
|
7036
|
-
if (
|
|
7037
|
-
throw new Error(`Invalid redaction path (${
|
|
7016
|
+
if (path70.includes(",")) {
|
|
7017
|
+
throw new Error(`Invalid redaction path (${path70})`);
|
|
7038
7018
|
}
|
|
7039
7019
|
let bracketCount = 0;
|
|
7040
7020
|
let inQuotes = false;
|
|
7041
7021
|
let quoteChar = "";
|
|
7042
|
-
for (let i = 0; i <
|
|
7043
|
-
const char =
|
|
7022
|
+
for (let i = 0; i < path70.length; i++) {
|
|
7023
|
+
const char = path70[i];
|
|
7044
7024
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
7045
7025
|
if (!inQuotes) {
|
|
7046
7026
|
inQuotes = true;
|
|
@@ -7054,20 +7034,20 @@ var require_redact = __commonJS({
|
|
|
7054
7034
|
} else if (char === "]" && !inQuotes) {
|
|
7055
7035
|
bracketCount--;
|
|
7056
7036
|
if (bracketCount < 0) {
|
|
7057
|
-
throw new Error(`Invalid redaction path (${
|
|
7037
|
+
throw new Error(`Invalid redaction path (${path70})`);
|
|
7058
7038
|
}
|
|
7059
7039
|
}
|
|
7060
7040
|
}
|
|
7061
7041
|
if (bracketCount !== 0) {
|
|
7062
|
-
throw new Error(`Invalid redaction path (${
|
|
7042
|
+
throw new Error(`Invalid redaction path (${path70})`);
|
|
7063
7043
|
}
|
|
7064
7044
|
}
|
|
7065
7045
|
function validatePaths(paths) {
|
|
7066
7046
|
if (!Array.isArray(paths)) {
|
|
7067
7047
|
throw new TypeError("paths must be an array");
|
|
7068
7048
|
}
|
|
7069
|
-
for (const
|
|
7070
|
-
validatePath(
|
|
7049
|
+
for (const path70 of paths) {
|
|
7050
|
+
validatePath(path70);
|
|
7071
7051
|
}
|
|
7072
7052
|
}
|
|
7073
7053
|
function slowRedact(options = {}) {
|
|
@@ -7235,8 +7215,8 @@ var require_redaction = __commonJS({
|
|
|
7235
7215
|
if (shape[k2] === null) {
|
|
7236
7216
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
7237
7217
|
} else {
|
|
7238
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
7239
|
-
return censor(value, [k2, ...
|
|
7218
|
+
const wrappedCensor = typeof censor === "function" ? (value, path70) => {
|
|
7219
|
+
return censor(value, [k2, ...path70]);
|
|
7240
7220
|
} : censor;
|
|
7241
7221
|
o[k2] = Redact({
|
|
7242
7222
|
paths: shape[k2],
|
|
@@ -7454,10 +7434,10 @@ var require_atomic_sleep = __commonJS({
|
|
|
7454
7434
|
var require_sonic_boom = __commonJS({
|
|
7455
7435
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
7456
7436
|
"use strict";
|
|
7457
|
-
var
|
|
7437
|
+
var fs64 = require("fs");
|
|
7458
7438
|
var EventEmitter3 = require("events");
|
|
7459
7439
|
var inherits = require("util").inherits;
|
|
7460
|
-
var
|
|
7440
|
+
var path70 = require("path");
|
|
7461
7441
|
var sleep2 = require_atomic_sleep();
|
|
7462
7442
|
var assert = require("assert");
|
|
7463
7443
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -7511,20 +7491,20 @@ var require_sonic_boom = __commonJS({
|
|
|
7511
7491
|
const mode = sonic.mode;
|
|
7512
7492
|
if (sonic.sync) {
|
|
7513
7493
|
try {
|
|
7514
|
-
if (sonic.mkdir)
|
|
7515
|
-
const fd =
|
|
7494
|
+
if (sonic.mkdir) fs64.mkdirSync(path70.dirname(file), { recursive: true });
|
|
7495
|
+
const fd = fs64.openSync(file, flags, mode);
|
|
7516
7496
|
fileOpened(null, fd);
|
|
7517
7497
|
} catch (err) {
|
|
7518
7498
|
fileOpened(err);
|
|
7519
7499
|
throw err;
|
|
7520
7500
|
}
|
|
7521
7501
|
} else if (sonic.mkdir) {
|
|
7522
|
-
|
|
7502
|
+
fs64.mkdir(path70.dirname(file), { recursive: true }, (err) => {
|
|
7523
7503
|
if (err) return fileOpened(err);
|
|
7524
|
-
|
|
7504
|
+
fs64.open(file, flags, mode, fileOpened);
|
|
7525
7505
|
});
|
|
7526
7506
|
} else {
|
|
7527
|
-
|
|
7507
|
+
fs64.open(file, flags, mode, fileOpened);
|
|
7528
7508
|
}
|
|
7529
7509
|
}
|
|
7530
7510
|
function SonicBoom(opts) {
|
|
@@ -7565,8 +7545,8 @@ var require_sonic_boom = __commonJS({
|
|
|
7565
7545
|
this.flush = flushBuffer;
|
|
7566
7546
|
this.flushSync = flushBufferSync;
|
|
7567
7547
|
this._actualWrite = actualWriteBuffer;
|
|
7568
|
-
fsWriteSync = () =>
|
|
7569
|
-
fsWrite = () =>
|
|
7548
|
+
fsWriteSync = () => fs64.writeSync(this.fd, this._writingBuf);
|
|
7549
|
+
fsWrite = () => fs64.write(this.fd, this._writingBuf, this.release);
|
|
7570
7550
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
7571
7551
|
this._writingBuf = "";
|
|
7572
7552
|
this.write = write;
|
|
@@ -7575,15 +7555,15 @@ var require_sonic_boom = __commonJS({
|
|
|
7575
7555
|
this._actualWrite = actualWrite;
|
|
7576
7556
|
fsWriteSync = () => {
|
|
7577
7557
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7578
|
-
return
|
|
7558
|
+
return fs64.writeSync(this.fd, this._writingBuf);
|
|
7579
7559
|
}
|
|
7580
|
-
return
|
|
7560
|
+
return fs64.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7581
7561
|
};
|
|
7582
7562
|
fsWrite = () => {
|
|
7583
7563
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7584
|
-
return
|
|
7564
|
+
return fs64.write(this.fd, this._writingBuf, this.release);
|
|
7585
7565
|
}
|
|
7586
|
-
return
|
|
7566
|
+
return fs64.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
7587
7567
|
};
|
|
7588
7568
|
} else {
|
|
7589
7569
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -7640,7 +7620,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7640
7620
|
}
|
|
7641
7621
|
}
|
|
7642
7622
|
if (this._fsync) {
|
|
7643
|
-
|
|
7623
|
+
fs64.fsyncSync(this.fd);
|
|
7644
7624
|
}
|
|
7645
7625
|
const len = this._len;
|
|
7646
7626
|
if (this._reopening) {
|
|
@@ -7754,7 +7734,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7754
7734
|
const onDrain = () => {
|
|
7755
7735
|
if (!this._fsync) {
|
|
7756
7736
|
try {
|
|
7757
|
-
|
|
7737
|
+
fs64.fsync(this.fd, (err) => {
|
|
7758
7738
|
this._flushPending = false;
|
|
7759
7739
|
cb(err);
|
|
7760
7740
|
});
|
|
@@ -7856,7 +7836,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7856
7836
|
const fd = this.fd;
|
|
7857
7837
|
this.once("ready", () => {
|
|
7858
7838
|
if (fd !== this.fd) {
|
|
7859
|
-
|
|
7839
|
+
fs64.close(fd, (err) => {
|
|
7860
7840
|
if (err) {
|
|
7861
7841
|
return this.emit("error", err);
|
|
7862
7842
|
}
|
|
@@ -7905,7 +7885,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7905
7885
|
buf = this._bufs[0];
|
|
7906
7886
|
}
|
|
7907
7887
|
try {
|
|
7908
|
-
const n = Buffer.isBuffer(buf) ?
|
|
7888
|
+
const n = Buffer.isBuffer(buf) ? fs64.writeSync(this.fd, buf) : fs64.writeSync(this.fd, buf, "utf8");
|
|
7909
7889
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
7910
7890
|
buf = releasedBufObj.writingBuf;
|
|
7911
7891
|
this._len = releasedBufObj.len;
|
|
@@ -7921,7 +7901,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7921
7901
|
}
|
|
7922
7902
|
}
|
|
7923
7903
|
try {
|
|
7924
|
-
|
|
7904
|
+
fs64.fsyncSync(this.fd);
|
|
7925
7905
|
} catch {
|
|
7926
7906
|
}
|
|
7927
7907
|
}
|
|
@@ -7942,7 +7922,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7942
7922
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
7943
7923
|
}
|
|
7944
7924
|
try {
|
|
7945
|
-
const n =
|
|
7925
|
+
const n = fs64.writeSync(this.fd, buf);
|
|
7946
7926
|
buf = buf.subarray(n);
|
|
7947
7927
|
this._len = Math.max(this._len - n, 0);
|
|
7948
7928
|
if (buf.length <= 0) {
|
|
@@ -7970,13 +7950,13 @@ var require_sonic_boom = __commonJS({
|
|
|
7970
7950
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
7971
7951
|
if (this.sync) {
|
|
7972
7952
|
try {
|
|
7973
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
7953
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs64.writeSync(this.fd, this._writingBuf) : fs64.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7974
7954
|
release(null, written);
|
|
7975
7955
|
} catch (err) {
|
|
7976
7956
|
release(err);
|
|
7977
7957
|
}
|
|
7978
7958
|
} else {
|
|
7979
|
-
|
|
7959
|
+
fs64.write(this.fd, this._writingBuf, release);
|
|
7980
7960
|
}
|
|
7981
7961
|
}
|
|
7982
7962
|
function actualWriteBuffer() {
|
|
@@ -7985,7 +7965,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7985
7965
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
7986
7966
|
if (this.sync) {
|
|
7987
7967
|
try {
|
|
7988
|
-
const written =
|
|
7968
|
+
const written = fs64.writeSync(this.fd, this._writingBuf);
|
|
7989
7969
|
release(null, written);
|
|
7990
7970
|
} catch (err) {
|
|
7991
7971
|
release(err);
|
|
@@ -7994,7 +7974,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7994
7974
|
if (kCopyBuffer) {
|
|
7995
7975
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
7996
7976
|
}
|
|
7997
|
-
|
|
7977
|
+
fs64.write(this.fd, this._writingBuf, release);
|
|
7998
7978
|
}
|
|
7999
7979
|
}
|
|
8000
7980
|
function actualClose(sonic) {
|
|
@@ -8010,12 +7990,12 @@ var require_sonic_boom = __commonJS({
|
|
|
8010
7990
|
sonic._lens = [];
|
|
8011
7991
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
8012
7992
|
try {
|
|
8013
|
-
|
|
7993
|
+
fs64.fsync(sonic.fd, closeWrapped);
|
|
8014
7994
|
} catch {
|
|
8015
7995
|
}
|
|
8016
7996
|
function closeWrapped() {
|
|
8017
7997
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
8018
|
-
|
|
7998
|
+
fs64.close(sonic.fd, done);
|
|
8019
7999
|
} else {
|
|
8020
8000
|
done();
|
|
8021
8001
|
}
|
|
@@ -11150,11 +11130,11 @@ var init_lib = __esm({
|
|
|
11150
11130
|
}
|
|
11151
11131
|
}
|
|
11152
11132
|
},
|
|
11153
|
-
addToPath: function addToPath(
|
|
11154
|
-
var last =
|
|
11133
|
+
addToPath: function addToPath(path70, added, removed, oldPosInc, options) {
|
|
11134
|
+
var last = path70.lastComponent;
|
|
11155
11135
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
11156
11136
|
return {
|
|
11157
|
-
oldPos:
|
|
11137
|
+
oldPos: path70.oldPos + oldPosInc,
|
|
11158
11138
|
lastComponent: {
|
|
11159
11139
|
count: last.count + 1,
|
|
11160
11140
|
added,
|
|
@@ -11164,7 +11144,7 @@ var init_lib = __esm({
|
|
|
11164
11144
|
};
|
|
11165
11145
|
} else {
|
|
11166
11146
|
return {
|
|
11167
|
-
oldPos:
|
|
11147
|
+
oldPos: path70.oldPos + oldPosInc,
|
|
11168
11148
|
lastComponent: {
|
|
11169
11149
|
count: 1,
|
|
11170
11150
|
added,
|
|
@@ -11669,10 +11649,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
11669
11649
|
const memories = raw.map((m2) => {
|
|
11670
11650
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11671
11651
|
const rec3 = m2;
|
|
11672
|
-
const
|
|
11652
|
+
const path70 = typeof rec3.path === "string" ? rec3.path : null;
|
|
11673
11653
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
11674
|
-
if (!
|
|
11675
|
-
const entry = { path:
|
|
11654
|
+
if (!path70 || content == null) return null;
|
|
11655
|
+
const entry = { path: path70, content };
|
|
11676
11656
|
if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
|
|
11677
11657
|
return entry;
|
|
11678
11658
|
}).filter((m2) => m2 !== null);
|
|
@@ -12475,10 +12455,10 @@ function parseAttachment(obj) {
|
|
|
12475
12455
|
const memories = raw.map((m2) => {
|
|
12476
12456
|
if (!m2 || typeof m2 !== "object") return null;
|
|
12477
12457
|
const rec3 = m2;
|
|
12478
|
-
const
|
|
12458
|
+
const path70 = typeof rec3.path === "string" ? rec3.path : null;
|
|
12479
12459
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
12480
|
-
if (!
|
|
12481
|
-
const out = { path:
|
|
12460
|
+
if (!path70 || content == null) return null;
|
|
12461
|
+
const out = { path: path70, content };
|
|
12482
12462
|
if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
|
|
12483
12463
|
return out;
|
|
12484
12464
|
}).filter((m2) => m2 !== null);
|
|
@@ -27014,8 +26994,8 @@ var require_buffer_util = __commonJS({
|
|
|
27014
26994
|
}
|
|
27015
26995
|
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
|
|
27016
26996
|
}
|
|
27017
|
-
function
|
|
27018
|
-
|
|
26997
|
+
function toBuffer(data) {
|
|
26998
|
+
toBuffer.readOnly = true;
|
|
27019
26999
|
if (Buffer.isBuffer(data)) return data;
|
|
27020
27000
|
let buf;
|
|
27021
27001
|
if (data instanceof ArrayBuffer) {
|
|
@@ -27024,7 +27004,7 @@ var require_buffer_util = __commonJS({
|
|
|
27024
27004
|
buf = new FastBuffer(data.buffer, data.byteOffset, data.byteLength);
|
|
27025
27005
|
} else {
|
|
27026
27006
|
buf = Buffer.from(data);
|
|
27027
|
-
|
|
27007
|
+
toBuffer.readOnly = false;
|
|
27028
27008
|
}
|
|
27029
27009
|
return buf;
|
|
27030
27010
|
}
|
|
@@ -27032,7 +27012,7 @@ var require_buffer_util = __commonJS({
|
|
|
27032
27012
|
concat,
|
|
27033
27013
|
mask: _mask,
|
|
27034
27014
|
toArrayBuffer,
|
|
27035
|
-
toBuffer
|
|
27015
|
+
toBuffer,
|
|
27036
27016
|
unmask: _unmask
|
|
27037
27017
|
};
|
|
27038
27018
|
if (!process.env.WS_NO_BUFFER_UTIL) {
|
|
@@ -28287,7 +28267,7 @@ var require_sender = __commonJS({
|
|
|
28287
28267
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
28288
28268
|
var { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants2();
|
|
28289
28269
|
var { isBlob, isValidStatusCode } = require_validation();
|
|
28290
|
-
var { mask: applyMask, toBuffer
|
|
28270
|
+
var { mask: applyMask, toBuffer } = require_buffer_util();
|
|
28291
28271
|
var kByteLength = /* @__PURE__ */ Symbol("kByteLength");
|
|
28292
28272
|
var maskBuffer = Buffer.alloc(4);
|
|
28293
28273
|
var RANDOM_POOL_SIZE = 8 * 1024;
|
|
@@ -28475,9 +28455,9 @@ var require_sender = __commonJS({
|
|
|
28475
28455
|
byteLength = data.size;
|
|
28476
28456
|
readOnly = false;
|
|
28477
28457
|
} else {
|
|
28478
|
-
data =
|
|
28458
|
+
data = toBuffer(data);
|
|
28479
28459
|
byteLength = data.length;
|
|
28480
|
-
readOnly =
|
|
28460
|
+
readOnly = toBuffer.readOnly;
|
|
28481
28461
|
}
|
|
28482
28462
|
if (byteLength > 125) {
|
|
28483
28463
|
throw new RangeError("The data size must not be greater than 125 bytes");
|
|
@@ -28522,9 +28502,9 @@ var require_sender = __commonJS({
|
|
|
28522
28502
|
byteLength = data.size;
|
|
28523
28503
|
readOnly = false;
|
|
28524
28504
|
} else {
|
|
28525
|
-
data =
|
|
28505
|
+
data = toBuffer(data);
|
|
28526
28506
|
byteLength = data.length;
|
|
28527
|
-
readOnly =
|
|
28507
|
+
readOnly = toBuffer.readOnly;
|
|
28528
28508
|
}
|
|
28529
28509
|
if (byteLength > 125) {
|
|
28530
28510
|
throw new RangeError("The data size must not be greater than 125 bytes");
|
|
@@ -28580,9 +28560,9 @@ var require_sender = __commonJS({
|
|
|
28580
28560
|
byteLength = data.size;
|
|
28581
28561
|
readOnly = false;
|
|
28582
28562
|
} else {
|
|
28583
|
-
data =
|
|
28563
|
+
data = toBuffer(data);
|
|
28584
28564
|
byteLength = data.length;
|
|
28585
|
-
readOnly =
|
|
28565
|
+
readOnly = toBuffer.readOnly;
|
|
28586
28566
|
}
|
|
28587
28567
|
if (this._firstFragment) {
|
|
28588
28568
|
this._firstFragment = false;
|
|
@@ -28652,7 +28632,7 @@ var require_sender = __commonJS({
|
|
|
28652
28632
|
return;
|
|
28653
28633
|
}
|
|
28654
28634
|
this._bufferedBytes -= options[kByteLength];
|
|
28655
|
-
const data =
|
|
28635
|
+
const data = toBuffer(arrayBuffer);
|
|
28656
28636
|
if (!compress) {
|
|
28657
28637
|
this._state = DEFAULT;
|
|
28658
28638
|
this.sendFrame(_Sender.frame(data, options), cb);
|
|
@@ -29155,7 +29135,7 @@ var require_websocket = __commonJS({
|
|
|
29155
29135
|
var EventEmitter3 = require("events");
|
|
29156
29136
|
var https = require("https");
|
|
29157
29137
|
var http3 = require("http");
|
|
29158
|
-
var
|
|
29138
|
+
var net3 = require("net");
|
|
29159
29139
|
var tls = require("tls");
|
|
29160
29140
|
var { randomBytes, createHash: createHash3 } = require("crypto");
|
|
29161
29141
|
var { Duplex, Readable: Readable3 } = require("stream");
|
|
@@ -29179,7 +29159,7 @@ var require_websocket = __commonJS({
|
|
|
29179
29159
|
EventTarget: { addEventListener, removeEventListener }
|
|
29180
29160
|
} = require_event_target();
|
|
29181
29161
|
var { format, parse } = require_extension();
|
|
29182
|
-
var { toBuffer
|
|
29162
|
+
var { toBuffer } = require_buffer_util();
|
|
29183
29163
|
var kAborted = /* @__PURE__ */ Symbol("kAborted");
|
|
29184
29164
|
var protocolVersions = [8, 13];
|
|
29185
29165
|
var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
|
|
@@ -29889,12 +29869,12 @@ var require_websocket = __commonJS({
|
|
|
29889
29869
|
}
|
|
29890
29870
|
function netConnect(options) {
|
|
29891
29871
|
options.path = options.socketPath;
|
|
29892
|
-
return
|
|
29872
|
+
return net3.connect(options);
|
|
29893
29873
|
}
|
|
29894
29874
|
function tlsConnect(options) {
|
|
29895
29875
|
options.path = void 0;
|
|
29896
29876
|
if (!options.servername && options.servername !== "") {
|
|
29897
|
-
options.servername =
|
|
29877
|
+
options.servername = net3.isIP(options.host) ? "" : options.host;
|
|
29898
29878
|
}
|
|
29899
29879
|
return tls.connect(options);
|
|
29900
29880
|
}
|
|
@@ -29917,7 +29897,7 @@ var require_websocket = __commonJS({
|
|
|
29917
29897
|
}
|
|
29918
29898
|
function sendAfterClose(websocket, data, cb) {
|
|
29919
29899
|
if (data) {
|
|
29920
|
-
const length = isBlob(data) ? data.size :
|
|
29900
|
+
const length = isBlob(data) ? data.size : toBuffer(data).length;
|
|
29921
29901
|
if (websocket._socket) websocket._sender._bufferedBytes += length;
|
|
29922
29902
|
else websocket._bufferedAmount += length;
|
|
29923
29903
|
}
|
|
@@ -30570,23 +30550,6 @@ var require_websocket_server = __commonJS({
|
|
|
30570
30550
|
}
|
|
30571
30551
|
});
|
|
30572
30552
|
|
|
30573
|
-
// ../node_modules/.pnpm/ws@8.20.0/node_modules/ws/wrapper.mjs
|
|
30574
|
-
var import_stream, import_extension, import_permessage_deflate, import_receiver, import_sender, import_subprotocol, import_websocket, import_websocket_server, wrapper_default;
|
|
30575
|
-
var init_wrapper = __esm({
|
|
30576
|
-
"../node_modules/.pnpm/ws@8.20.0/node_modules/ws/wrapper.mjs"() {
|
|
30577
|
-
"use strict";
|
|
30578
|
-
import_stream = __toESM(require_stream(), 1);
|
|
30579
|
-
import_extension = __toESM(require_extension(), 1);
|
|
30580
|
-
import_permessage_deflate = __toESM(require_permessage_deflate(), 1);
|
|
30581
|
-
import_receiver = __toESM(require_receiver(), 1);
|
|
30582
|
-
import_sender = __toESM(require_sender(), 1);
|
|
30583
|
-
import_subprotocol = __toESM(require_subprotocol(), 1);
|
|
30584
|
-
import_websocket = __toESM(require_websocket(), 1);
|
|
30585
|
-
import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
30586
|
-
wrapper_default = import_websocket.default;
|
|
30587
|
-
}
|
|
30588
|
-
});
|
|
30589
|
-
|
|
30590
30553
|
// ../node_modules/.pnpm/process-nextick-args@2.0.1/node_modules/process-nextick-args/index.js
|
|
30591
30554
|
var require_process_nextick_args = __commonJS({
|
|
30592
30555
|
"../node_modules/.pnpm/process-nextick-args@2.0.1/node_modules/process-nextick-args/index.js"(exports2, module2) {
|
|
@@ -33464,8 +33427,8 @@ var require_utils = __commonJS({
|
|
|
33464
33427
|
var result = transform[inputType][outputType](input);
|
|
33465
33428
|
return result;
|
|
33466
33429
|
};
|
|
33467
|
-
exports2.resolve = function(
|
|
33468
|
-
var parts =
|
|
33430
|
+
exports2.resolve = function(path70) {
|
|
33431
|
+
var parts = path70.split("/");
|
|
33469
33432
|
var result = [];
|
|
33470
33433
|
for (var index = 0; index < parts.length; index++) {
|
|
33471
33434
|
var part = parts[index];
|
|
@@ -39318,18 +39281,18 @@ var require_object = __commonJS({
|
|
|
39318
39281
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
39319
39282
|
this.files[name] = object;
|
|
39320
39283
|
};
|
|
39321
|
-
var parentFolder = function(
|
|
39322
|
-
if (
|
|
39323
|
-
|
|
39284
|
+
var parentFolder = function(path70) {
|
|
39285
|
+
if (path70.slice(-1) === "/") {
|
|
39286
|
+
path70 = path70.substring(0, path70.length - 1);
|
|
39324
39287
|
}
|
|
39325
|
-
var lastSlash =
|
|
39326
|
-
return lastSlash > 0 ?
|
|
39288
|
+
var lastSlash = path70.lastIndexOf("/");
|
|
39289
|
+
return lastSlash > 0 ? path70.substring(0, lastSlash) : "";
|
|
39327
39290
|
};
|
|
39328
|
-
var forceTrailingSlash = function(
|
|
39329
|
-
if (
|
|
39330
|
-
|
|
39291
|
+
var forceTrailingSlash = function(path70) {
|
|
39292
|
+
if (path70.slice(-1) !== "/") {
|
|
39293
|
+
path70 += "/";
|
|
39331
39294
|
}
|
|
39332
|
-
return
|
|
39295
|
+
return path70;
|
|
39333
39296
|
};
|
|
39334
39297
|
var folderAdd = function(name, createFolders) {
|
|
39335
39298
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -40331,7 +40294,7 @@ var require_lib3 = __commonJS({
|
|
|
40331
40294
|
// src/run-case/recorder.ts
|
|
40332
40295
|
function startRunCaseRecorder(opts) {
|
|
40333
40296
|
const now = opts.now ?? Date.now;
|
|
40334
|
-
const dir =
|
|
40297
|
+
const dir = import_node_path58.default.dirname(opts.recordPath);
|
|
40335
40298
|
let stream = null;
|
|
40336
40299
|
let closing = false;
|
|
40337
40300
|
let closedSettled = false;
|
|
@@ -40345,8 +40308,8 @@ function startRunCaseRecorder(opts) {
|
|
|
40345
40308
|
});
|
|
40346
40309
|
const ensureStream = () => {
|
|
40347
40310
|
if (stream) return stream;
|
|
40348
|
-
|
|
40349
|
-
stream =
|
|
40311
|
+
import_node_fs46.default.mkdirSync(dir, { recursive: true });
|
|
40312
|
+
stream = import_node_fs46.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
40350
40313
|
stream.on("close", () => closedResolve());
|
|
40351
40314
|
return stream;
|
|
40352
40315
|
};
|
|
@@ -40371,12 +40334,12 @@ function startRunCaseRecorder(opts) {
|
|
|
40371
40334
|
};
|
|
40372
40335
|
return { tap, close, closed };
|
|
40373
40336
|
}
|
|
40374
|
-
var
|
|
40337
|
+
var import_node_fs46, import_node_path58;
|
|
40375
40338
|
var init_recorder = __esm({
|
|
40376
40339
|
"src/run-case/recorder.ts"() {
|
|
40377
40340
|
"use strict";
|
|
40378
|
-
|
|
40379
|
-
|
|
40341
|
+
import_node_fs46 = __toESM(require("fs"), 1);
|
|
40342
|
+
import_node_path58 = __toESM(require("path"), 1);
|
|
40380
40343
|
}
|
|
40381
40344
|
});
|
|
40382
40345
|
|
|
@@ -40419,7 +40382,7 @@ var init_wire = __esm({
|
|
|
40419
40382
|
// src/run-case/controller.ts
|
|
40420
40383
|
async function runController(opts) {
|
|
40421
40384
|
const now = opts.now ?? Date.now;
|
|
40422
|
-
const cwd = opts.cwd ?? (0,
|
|
40385
|
+
const cwd = opts.cwd ?? (0, import_node_fs47.mkdtempSync)(import_node_path59.default.join(import_node_os23.default.tmpdir(), "clawd-runcase-"));
|
|
40423
40386
|
const ownsCwd = opts.cwd === void 0;
|
|
40424
40387
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
40425
40388
|
const spawnCtx = { cwd };
|
|
@@ -40580,19 +40543,19 @@ async function runController(opts) {
|
|
|
40580
40543
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
40581
40544
|
if (ownsCwd) {
|
|
40582
40545
|
try {
|
|
40583
|
-
(0,
|
|
40546
|
+
(0, import_node_fs47.rmSync)(cwd, { recursive: true, force: true });
|
|
40584
40547
|
} catch {
|
|
40585
40548
|
}
|
|
40586
40549
|
}
|
|
40587
40550
|
return exitCode ?? 0;
|
|
40588
40551
|
}
|
|
40589
|
-
var
|
|
40552
|
+
var import_node_fs47, import_node_os23, import_node_path59;
|
|
40590
40553
|
var init_controller = __esm({
|
|
40591
40554
|
"src/run-case/controller.ts"() {
|
|
40592
40555
|
"use strict";
|
|
40593
|
-
|
|
40556
|
+
import_node_fs47 = require("fs");
|
|
40594
40557
|
import_node_os23 = __toESM(require("os"), 1);
|
|
40595
|
-
|
|
40558
|
+
import_node_path59 = __toESM(require("path"), 1);
|
|
40596
40559
|
init_claude();
|
|
40597
40560
|
init_stdout_splitter();
|
|
40598
40561
|
init_permission_stdio();
|
|
@@ -40688,124 +40651,6 @@ stdout \u4E8B\u4EF6\uFF08\u884C JSON\uFF09\uFF1A
|
|
|
40688
40651
|
}
|
|
40689
40652
|
});
|
|
40690
40653
|
|
|
40691
|
-
// src/sshd/sshd-cli-relay.ts
|
|
40692
|
-
var sshd_cli_relay_exports = {};
|
|
40693
|
-
__export(sshd_cli_relay_exports, {
|
|
40694
|
-
sshRelay: () => sshRelay
|
|
40695
|
-
});
|
|
40696
|
-
async function sshRelay(argv) {
|
|
40697
|
-
const args = parseSshRelayArgs(argv);
|
|
40698
|
-
if (args.help) {
|
|
40699
|
-
process.stdout.write(SSH_RELAY_HELP);
|
|
40700
|
-
return 0;
|
|
40701
|
-
}
|
|
40702
|
-
if (!args.peerDeviceId) {
|
|
40703
|
-
process.stderr.write("clawd ssh-relay: missing <peer-device-id>\n" + SSH_RELAY_HELP);
|
|
40704
|
-
return 2;
|
|
40705
|
-
}
|
|
40706
|
-
const daemonUrl = process.env.CLAWD_DAEMON_URL;
|
|
40707
|
-
const sessionId = process.env.CLAWD_SESSION_ID;
|
|
40708
|
-
if (!daemonUrl) {
|
|
40709
|
-
process.stderr.write(
|
|
40710
|
-
"clawd ssh-relay: CLAWD_DAEMON_URL env not set (relay must run inside a clawd CC session)\n"
|
|
40711
|
-
);
|
|
40712
|
-
return 2;
|
|
40713
|
-
}
|
|
40714
|
-
if (!sessionId) {
|
|
40715
|
-
process.stderr.write(
|
|
40716
|
-
"clawd ssh-relay: CLAWD_SESSION_ID env not set (relay must run inside a clawd CC session)\n"
|
|
40717
|
-
);
|
|
40718
|
-
return 2;
|
|
40719
|
-
}
|
|
40720
|
-
const wsBase = daemonUrl.replace(/^http/, "ws").replace(/\/+$/, "");
|
|
40721
|
-
const url = `${wsBase}/rpc/ssh-tunnel-local?peerDeviceId=${encodeURIComponent(args.peerDeviceId)}`;
|
|
40722
|
-
return new Promise((resolve6) => {
|
|
40723
|
-
const ws = new import_websocket.default(url, {
|
|
40724
|
-
headers: { "X-Clawd-Session-Id": sessionId }
|
|
40725
|
-
});
|
|
40726
|
-
let settled = false;
|
|
40727
|
-
const settle = (code) => {
|
|
40728
|
-
if (settled) return;
|
|
40729
|
-
settled = true;
|
|
40730
|
-
try {
|
|
40731
|
-
ws.close();
|
|
40732
|
-
} catch {
|
|
40733
|
-
}
|
|
40734
|
-
resolve6(code);
|
|
40735
|
-
};
|
|
40736
|
-
ws.on("open", () => {
|
|
40737
|
-
process.stdin.on("data", (chunk) => {
|
|
40738
|
-
if (ws.readyState === ws.OPEN) ws.send(chunk, { binary: true });
|
|
40739
|
-
});
|
|
40740
|
-
process.stdin.on("end", () => {
|
|
40741
|
-
try {
|
|
40742
|
-
ws.close(1e3, "stdin end");
|
|
40743
|
-
} catch {
|
|
40744
|
-
}
|
|
40745
|
-
});
|
|
40746
|
-
process.stdin.on("error", () => settle(1));
|
|
40747
|
-
});
|
|
40748
|
-
ws.on("message", (data) => {
|
|
40749
|
-
if (Buffer.isBuffer(data)) process.stdout.write(data);
|
|
40750
|
-
else if (Array.isArray(data)) process.stdout.write(Buffer.concat(data));
|
|
40751
|
-
else if (data instanceof ArrayBuffer) process.stdout.write(Buffer.from(data));
|
|
40752
|
-
});
|
|
40753
|
-
ws.on("close", (code) => {
|
|
40754
|
-
settle(code === 1e3 ? 0 : 1);
|
|
40755
|
-
});
|
|
40756
|
-
ws.on("error", (err) => {
|
|
40757
|
-
process.stderr.write(`clawd ssh-relay: ws error ${err.message}
|
|
40758
|
-
`);
|
|
40759
|
-
settle(1);
|
|
40760
|
-
});
|
|
40761
|
-
const onSignal = () => settle(0);
|
|
40762
|
-
process.once("SIGINT", onSignal);
|
|
40763
|
-
process.once("SIGTERM", onSignal);
|
|
40764
|
-
});
|
|
40765
|
-
}
|
|
40766
|
-
function parseSshRelayArgs(argv) {
|
|
40767
|
-
const out = {};
|
|
40768
|
-
for (let i = 0; i < argv.length; i++) {
|
|
40769
|
-
const a = argv[i];
|
|
40770
|
-
if (a === "--help" || a === "-h") {
|
|
40771
|
-
out.help = true;
|
|
40772
|
-
continue;
|
|
40773
|
-
}
|
|
40774
|
-
if (a.startsWith("--")) {
|
|
40775
|
-
throw new Error(`unknown flag: ${a}`);
|
|
40776
|
-
}
|
|
40777
|
-
if (!out.peerDeviceId) {
|
|
40778
|
-
out.peerDeviceId = a;
|
|
40779
|
-
}
|
|
40780
|
-
}
|
|
40781
|
-
return out;
|
|
40782
|
-
}
|
|
40783
|
-
var SSH_RELAY_HELP;
|
|
40784
|
-
var init_sshd_cli_relay = __esm({
|
|
40785
|
-
"src/sshd/sshd-cli-relay.ts"() {
|
|
40786
|
-
"use strict";
|
|
40787
|
-
init_wrapper();
|
|
40788
|
-
SSH_RELAY_HELP = `clawd ssh-relay <peer-device-id>
|
|
40789
|
-
|
|
40790
|
-
WebSocket relay to a peer daemon's /rpc/ssh-tunnel via the local daemon
|
|
40791
|
-
(reads CLAWD_DAEMON_URL + CLAWD_SESSION_ID from env). Meant to be used as
|
|
40792
|
-
SSH ProxyCommand from inside a clawd CC session.
|
|
40793
|
-
|
|
40794
|
-
Options:
|
|
40795
|
-
--help / -h \u663E\u793A\u5E2E\u52A9
|
|
40796
|
-
|
|
40797
|
-
Example:
|
|
40798
|
-
ssh -o ProxyCommand='~/.clawd/bin/clawd ssh-relay <peer-device-id>' \\
|
|
40799
|
-
-i ~/.clawd/ssh-identity/id_ed25519 \\
|
|
40800
|
-
<peer-os-user>@127.0.0.1
|
|
40801
|
-
|
|
40802
|
-
\u767B\u5F55\u540D\u7528\u5BF9\u7AEF\u8D26\u53F7\uFF08Contact.osUser\uFF0C\u5EFA\u8054\u63E1\u624B\u4EA4\u6362\uFF09\u2014\u2014\u4E0D\u662F\u672C\u673A $USER\u3002
|
|
40803
|
-
\u4E24\u673A\u8D26\u53F7\u540D\u4E0D\u540C\u65F6\u7528 $USER \u4F1A Permission denied (publickey)\u3002
|
|
40804
|
-
\u89C1 doc/glossary/peer-mesh.md#\u5EFA\u8054\u4EA4\u6362\u6E05\u5355
|
|
40805
|
-
`;
|
|
40806
|
-
}
|
|
40807
|
-
});
|
|
40808
|
-
|
|
40809
40654
|
// src/config.ts
|
|
40810
40655
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
40811
40656
|
var import_node_os = __toESM(require("os"), 1);
|
|
@@ -40813,7 +40658,6 @@ var import_node_path = __toESM(require("path"), 1);
|
|
|
40813
40658
|
init_protocol();
|
|
40814
40659
|
var DEFAULT_PORT = 18790;
|
|
40815
40660
|
var DEFAULT_HOST = "127.0.0.1";
|
|
40816
|
-
var DEFAULT_SSHD_PORT = 22422;
|
|
40817
40661
|
var DEFAULT_CLAWOS_API = "https://api.clawos.chat";
|
|
40818
40662
|
var DEFAULT_LOG_ENDPOINT = "https://clawd-prod.cn-hangzhou.log.aliyuncs.com/logstores/app-logs/track";
|
|
40819
40663
|
function resolveLogShipping(raw, cliNoShipping) {
|
|
@@ -40881,14 +40725,6 @@ function parseArgs(argv) {
|
|
|
40881
40725
|
case "--no-log-shipping":
|
|
40882
40726
|
out.noLogShipping = true;
|
|
40883
40727
|
break;
|
|
40884
|
-
case "--sshd-port": {
|
|
40885
|
-
const n = Number.parseInt(next() ?? "", 10);
|
|
40886
|
-
if (!Number.isFinite(n) || n <= 0 || n > 65535) {
|
|
40887
|
-
throw new Error(`invalid --sshd-port value: ${argv[i]}`);
|
|
40888
|
-
}
|
|
40889
|
-
out.sshdPort = n;
|
|
40890
|
-
break;
|
|
40891
|
-
}
|
|
40892
40728
|
default:
|
|
40893
40729
|
if (a.startsWith("--")) throw new Error(`unknown flag: ${a}`);
|
|
40894
40730
|
break;
|
|
@@ -40938,7 +40774,6 @@ function resolveConfig(opts) {
|
|
|
40938
40774
|
fileCfg.logShipping,
|
|
40939
40775
|
args.noLogShipping ?? false
|
|
40940
40776
|
);
|
|
40941
|
-
const sshdPort = args.sshdPort ?? (typeof fileCfg.sshdPort === "number" ? fileCfg.sshdPort : DEFAULT_SSHD_PORT);
|
|
40942
40777
|
return {
|
|
40943
40778
|
port,
|
|
40944
40779
|
host,
|
|
@@ -40952,8 +40787,7 @@ function resolveConfig(opts) {
|
|
|
40952
40787
|
frpcBinary,
|
|
40953
40788
|
mode,
|
|
40954
40789
|
previewPorts,
|
|
40955
|
-
logShipping
|
|
40956
|
-
sshdPort
|
|
40790
|
+
logShipping
|
|
40957
40791
|
};
|
|
40958
40792
|
}
|
|
40959
40793
|
var HELP_TEXT = `clawd [options]
|
|
@@ -40965,7 +40799,6 @@ var HELP_TEXT = `clawd [options]
|
|
|
40965
40799
|
--clawos-api <url> tunnel register \u63A5\u53E3\u7684 base url\uFF08\u9ED8\u8BA4 https://api.clawos.chat\uFF09
|
|
40966
40800
|
--auth-token <s> \u6307\u5B9A daemon auth token\uFF1B\u7F3A\u7701\u65F6 tunnel \u6A21\u5F0F\u4ECE ~/.clawd/auth.json \u590D\u7528\uFF0C\u6CA1\u6709\u5C31\u751F\u6210
|
|
40967
40801
|
--no-log-shipping \u7981\u7528 SLS \u65E5\u5FD7\u4E0A\u884C\uFF08\u8986\u76D6 config.json logShipping.mode=off\uFF09
|
|
40968
|
-
--sshd-port <n> Contact SSH \u53CD\u5411\u8BBF\u95EE loopback \u7AEF\u53E3\uFF0C\u9ED8\u8BA4 22422
|
|
40969
40802
|
--help / -h \u663E\u793A\u5E2E\u52A9
|
|
40970
40803
|
--version / -v \u663E\u793A\u7248\u672C
|
|
40971
40804
|
|
|
@@ -40974,12 +40807,6 @@ Subcommands:
|
|
|
40974
40807
|
\u72EC\u7ACB CC \u63A7\u5236\u5668\u8FDB\u7A0B\uFF1A\u5916\u90E8 AI \u901A\u8FC7 stdin/stdout \u884C JSON \u534F\u8BAE
|
|
40975
40808
|
\u9A71\u52A8 CC \u591A\u8F6E\u5BF9\u8BDD\u4E0E\u6743\u9650\u51B3\u7B56\uFF0CIPC \u5168\u7A0B\u5F55\u5230 --record JSONL\u3002
|
|
40976
40809
|
\u8BE6\u89C1 'clawd run-case --help'
|
|
40977
|
-
ssh-relay <peer-device-id>
|
|
40978
|
-
WebSocket relay via local daemon (reads
|
|
40979
|
-
CLAWD_DAEMON_URL + CLAWD_SESSION_ID from env)
|
|
40980
|
-
to peer /rpc/ssh-tunnel\uFF0C\u4F5C\u4E3A SSH ProxyCommand
|
|
40981
|
-
\u4F7F\u7528\uFF08Contact SSH tunnel v2\uFF09\u3002
|
|
40982
|
-
\u8BE6\u89C1 'clawd ssh-relay --help'
|
|
40983
40810
|
|
|
40984
40811
|
Env (advanced):
|
|
40985
40812
|
CLAWD_FRPC_BIN \u81EA\u5E26 frpc \u4E8C\u8FDB\u5236\u8DEF\u5F84\uFF08\u9ED8\u8BA4\u6309\u9700\u4E0B\u8F7D\u5230 ~/.clawd/bin/frpc\uFF09
|
|
@@ -40990,8 +40817,8 @@ Env (advanced):
|
|
|
40990
40817
|
`;
|
|
40991
40818
|
|
|
40992
40819
|
// src/index.ts
|
|
40993
|
-
var
|
|
40994
|
-
var
|
|
40820
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
40821
|
+
var import_node_fs45 = __toESM(require("fs"), 1);
|
|
40995
40822
|
var import_node_os22 = __toESM(require("os"), 1);
|
|
40996
40823
|
|
|
40997
40824
|
// ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
|
|
@@ -41741,13 +41568,6 @@ function composeGuestSandbox(base, userWorkDir, spawnCwd) {
|
|
|
41741
41568
|
fsv.denyWrite = unionArr((fsv.denyWrite ?? []).filter((p2) => p2 !== "~/"), [spawnCwd]);
|
|
41742
41569
|
fsv.allowRead = unionArr(fsv.allowRead, [userWorkDir]);
|
|
41743
41570
|
fsv.allowWrite = unionArr(fsv.allowWrite, [userWorkDir]);
|
|
41744
|
-
fsv.allowRead = unionArr(fsv.allowRead, [
|
|
41745
|
-
"~/.clawd/ssh-identity",
|
|
41746
|
-
"~/.clawd/bin",
|
|
41747
|
-
"~/Library/Application Support/@clawos/clawd-desktop/ota-bundles",
|
|
41748
|
-
"~/.clawd/config.json",
|
|
41749
|
-
"~/.clawd/auth.json"
|
|
41750
|
-
]);
|
|
41751
41571
|
return s;
|
|
41752
41572
|
}
|
|
41753
41573
|
|
|
@@ -42015,32 +41835,6 @@ deviceId \u76F4\u63A5\u53D6\u81EA token\uFF0C\u4E0D\u8981\u6539\u5199\u6216\u731
|
|
|
42015
41835
|
- \u5931\u8D25\uFF1A\`{ isFailure: true, reason: "..." }\`
|
|
42016
41836
|
- \u62A5\u544A\u5B8C\u540E\u4F60\u7684 session \u5C31\u7ED3\u675F\u4E86\uFF0C\u4E0D\u8981\u7EE7\u7EED\u8F93\u51FA`;
|
|
42017
41837
|
|
|
42018
|
-
// src/sshd/contact-ssh-prompt.ts
|
|
42019
|
-
var CONTACT_SSH_SYSTEM_PROMPT_HINT = `## \u8DE8\u8BBE\u5907\u6587\u4EF6\u8BBF\u95EE\uFF08Contact SSH\uFF09
|
|
42020
|
-
|
|
42021
|
-
\u7528\u6237\u53EF\u80FD\u6709\u591A\u53F0 clawd \u8BBE\u5907\u4E92\u76F8\u6388\u6743\u3002\u4F60\u5728\u8FD9\u53F0\u673A\u5668\u4E0A\u8BBF\u95EE\u5DF2\u6388\u6743\u5BF9\u7AEF\uFF08\u4E0B\u79F0 A\uFF0CdeviceId \u7531\u7528\u6237/\u4EFB\u52A1\u4E0A\u4E0B\u6587\u7ED9\u51FA\uFF09\u7684\u65B9\u6CD5\uFF1A
|
|
42022
|
-
|
|
42023
|
-
\u7528 SSH \u62E8\u53F7\uFF08\u628A \`<A>\` \u6362\u6210\u5BF9\u7AEF deviceId\uFF09\uFF1A
|
|
42024
|
-
\`\`\`bash
|
|
42025
|
-
ssh -o ProxyCommand='~/.clawd/bin/clawd ssh-relay <A>' \\
|
|
42026
|
-
-i ~/.clawd/ssh-identity/id_ed25519 \\
|
|
42027
|
-
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \\
|
|
42028
|
-
$USER@127.0.0.1 <command>
|
|
42029
|
-
# \u4F8B\u5982\uFF1A... $USER@127.0.0.1 cat /Users/xxx/some/file.md
|
|
42030
|
-
# ... $USER@127.0.0.1 ls -la /Users/xxx/proj
|
|
42031
|
-
# \u8BF4\u660E\uFF1A\`-i\` \u7528\u672C\u8BBE\u5907\u552F\u4E00\u7684 SSH identity key\uFF08\u6240\u6709\u5BF9\u7AEF\u590D\u7528\u4E00\u628A\uFF09\u3002
|
|
42032
|
-
# \`~/.clawd/bin/clawd\` \u662F daemon \u542F\u52A8\u65F6 seed \u7684 shim\uFF0C\u786C\u7F16\u7801\u5F53\u524D daemon \u7684
|
|
42033
|
-
# execPath+cliPath\uFF0C\u4FDD\u8BC1 relay \u8DDF daemon \u7248\u672C\u540C\u6B65\u3002\u4E0D\u8981\u7528\u88F8 \`clawd\`\uFF08\u8D70 $PATH \u4F1A\u6F02\uFF09\u3002
|
|
42034
|
-
\`\`\`
|
|
42035
|
-
|
|
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
|
|
42037
|
-
|
|
42038
|
-
**\u6CE8\u610F**\uFF1A
|
|
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
|
|
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
|
|
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
|
|
42042
|
-
`;
|
|
42043
|
-
|
|
42044
41838
|
// src/session/reducer.ts
|
|
42045
41839
|
function cloneState(s) {
|
|
42046
41840
|
return {
|
|
@@ -42176,9 +41970,6 @@ function buildSpawnContext(state, deps) {
|
|
|
42176
41970
|
if (daemonUrl) {
|
|
42177
41971
|
ctx.extraSystemPrompt = (ctx.extraSystemPrompt ? ctx.extraSystemPrompt + "\n\n" : "") + ATTACHMENT_SHARING_HINT;
|
|
42178
41972
|
}
|
|
42179
|
-
if (meta?.personaMode === "guest") {
|
|
42180
|
-
ctx.extraSystemPrompt = (ctx.extraSystemPrompt ? ctx.extraSystemPrompt + "\n\n" : "") + CONTACT_SSH_SYSTEM_PROMPT_HINT;
|
|
42181
|
-
}
|
|
42182
41973
|
if (meta?.extraSettings) {
|
|
42183
41974
|
ctx.extraSettings = meta.extraSettings;
|
|
42184
41975
|
}
|
|
@@ -46352,7 +46143,7 @@ function tryLoadViewerAssets(logger) {
|
|
|
46352
46143
|
}
|
|
46353
46144
|
logger?.warn("share-md-viewer.assets-not-found", {
|
|
46354
46145
|
candidates,
|
|
46355
|
-
hint: "run `pnpm -F @clawos/
|
|
46146
|
+
hint: "run `pnpm -F @clawos/clawd-ui build && pnpm -F @clawos-dev/clawd build`"
|
|
46356
46147
|
});
|
|
46357
46148
|
return null;
|
|
46358
46149
|
}
|
|
@@ -46425,7 +46216,7 @@ function tryLoadShareUi(logger) {
|
|
|
46425
46216
|
}
|
|
46426
46217
|
logger?.warn("share-ui.assets-not-found", {
|
|
46427
46218
|
candidates,
|
|
46428
|
-
hint: "run `pnpm -F @clawos/
|
|
46219
|
+
hint: "run `pnpm -F @clawos/clawd-ui build && pnpm -F @clawos-dev/clawd build`"
|
|
46429
46220
|
});
|
|
46430
46221
|
return null;
|
|
46431
46222
|
}
|
|
@@ -46793,8 +46584,8 @@ function turnStartInput(text) {
|
|
|
46793
46584
|
const items = [];
|
|
46794
46585
|
let leftover = text;
|
|
46795
46586
|
for (const m2 of text.matchAll(SKILL_RE)) {
|
|
46796
|
-
const [marker, name,
|
|
46797
|
-
items.push({ type: "skill", name, path:
|
|
46587
|
+
const [marker, name, path70] = m2;
|
|
46588
|
+
items.push({ type: "skill", name, path: path70 });
|
|
46798
46589
|
leftover = leftover.replace(marker, "");
|
|
46799
46590
|
}
|
|
46800
46591
|
for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
|
|
@@ -49394,13 +49185,13 @@ function mapSkillsListResponse(res) {
|
|
|
49394
49185
|
const r = s ?? {};
|
|
49395
49186
|
const name = str3(r.name);
|
|
49396
49187
|
if (!name) continue;
|
|
49397
|
-
const
|
|
49188
|
+
const path70 = str3(r.path);
|
|
49398
49189
|
const description = str3(r.description);
|
|
49399
49190
|
const isPlugin = name.includes(":");
|
|
49400
49191
|
out.push({
|
|
49401
49192
|
name,
|
|
49402
49193
|
source: isPlugin ? "plugin" : "project",
|
|
49403
|
-
...
|
|
49194
|
+
...path70 ? { path: path70 } : {},
|
|
49404
49195
|
...description ? { description } : {},
|
|
49405
49196
|
...isPlugin ? { plugin: name.split(":")[0] } : {}
|
|
49406
49197
|
});
|
|
@@ -50106,8 +49897,18 @@ function listRegistered() {
|
|
|
50106
49897
|
return [...registry.keys()];
|
|
50107
49898
|
}
|
|
50108
49899
|
|
|
49900
|
+
// ../node_modules/.pnpm/ws@8.20.0/node_modules/ws/wrapper.mjs
|
|
49901
|
+
var import_stream = __toESM(require_stream(), 1);
|
|
49902
|
+
var import_extension = __toESM(require_extension(), 1);
|
|
49903
|
+
var import_permessage_deflate = __toESM(require_permessage_deflate(), 1);
|
|
49904
|
+
var import_receiver = __toESM(require_receiver(), 1);
|
|
49905
|
+
var import_sender = __toESM(require_sender(), 1);
|
|
49906
|
+
var import_subprotocol = __toESM(require_subprotocol(), 1);
|
|
49907
|
+
var import_websocket = __toESM(require_websocket(), 1);
|
|
49908
|
+
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
49909
|
+
var wrapper_default = import_websocket.default;
|
|
49910
|
+
|
|
50109
49911
|
// src/transport/local-ws-server.ts
|
|
50110
|
-
init_wrapper();
|
|
50111
49912
|
var import_node_http2 = __toESM(require("http"), 1);
|
|
50112
49913
|
|
|
50113
49914
|
// src/transport/preview-proxy.ts
|
|
@@ -50183,30 +49984,6 @@ var LocalWsServer = class {
|
|
|
50183
49984
|
const httpServer = import_node_http2.default.createServer((req, res) => this.handleHttpRequest(req, res));
|
|
50184
49985
|
const wss = new import_websocket_server.default({ noServer: true, clientTracking: true });
|
|
50185
49986
|
httpServer.on("upgrade", (req, socket, head) => {
|
|
50186
|
-
if (this.opts.sshTunnelUpgradeHandler) {
|
|
50187
|
-
const [urlPath] = (req.url ?? "").split("?");
|
|
50188
|
-
if (urlPath === "/rpc/ssh-tunnel") {
|
|
50189
|
-
void this.opts.sshTunnelUpgradeHandler(
|
|
50190
|
-
req,
|
|
50191
|
-
socket,
|
|
50192
|
-
head,
|
|
50193
|
-
wss
|
|
50194
|
-
);
|
|
50195
|
-
return;
|
|
50196
|
-
}
|
|
50197
|
-
}
|
|
50198
|
-
if (this.opts.sshTunnelLocalUpgradeHandler) {
|
|
50199
|
-
const [urlPath] = (req.url ?? "").split("?");
|
|
50200
|
-
if (urlPath === "/rpc/ssh-tunnel-local") {
|
|
50201
|
-
void this.opts.sshTunnelLocalUpgradeHandler(
|
|
50202
|
-
req,
|
|
50203
|
-
socket,
|
|
50204
|
-
head,
|
|
50205
|
-
wss
|
|
50206
|
-
);
|
|
50207
|
-
return;
|
|
50208
|
-
}
|
|
50209
|
-
}
|
|
50210
49987
|
if (req.url?.startsWith("/preview/")) {
|
|
50211
49988
|
const pathname = (() => {
|
|
50212
49989
|
try {
|
|
@@ -50601,10 +50378,7 @@ var EXPOSED_RPC_METHODS = [
|
|
|
50601
50378
|
// 查当前 turn caller —— cc 长驻 + owner/guest 交替驱动同一 session 时不能靠启动时 env
|
|
50602
50379
|
// 写死身份,必须运行时 fetch。header X-Clawd-Session-Id 作 session 定位,handler 内查
|
|
50603
50380
|
// manager.tryGetState(sid).currentTurnSender 返 SenderInfo | null。
|
|
50604
|
-
"session.currentTurnSender"
|
|
50605
|
-
// 建联交换(T-32):本机 cc 拨号前查对端 osUser 拼 `<osUser>@127.0.0.1`。窄读——handler
|
|
50606
|
-
// 只返 deviceId / displayName / osUser(无 connectToken 等敏感字段),见 handlers/contact.ts dialInfo。
|
|
50607
|
-
"contact.dialInfo"
|
|
50381
|
+
"session.currentTurnSender"
|
|
50608
50382
|
];
|
|
50609
50383
|
async function handleRpcRequest(input) {
|
|
50610
50384
|
const { method, body, sessionId, dispatcher } = input;
|
|
@@ -50693,9 +50467,7 @@ var AuthGate = class {
|
|
|
50693
50467
|
try {
|
|
50694
50468
|
r = await this.opts.authenticate(
|
|
50695
50469
|
parsed.data.token,
|
|
50696
|
-
parsed.data.selfUrl
|
|
50697
|
-
parsed.data.selfSshPublicKey,
|
|
50698
|
-
parsed.data.selfOsUser
|
|
50470
|
+
parsed.data.selfUrl
|
|
50699
50471
|
);
|
|
50700
50472
|
} finally {
|
|
50701
50473
|
st.pending = false;
|
|
@@ -51284,8 +51056,7 @@ var ContactStore = class {
|
|
|
51284
51056
|
return true;
|
|
51285
51057
|
}
|
|
51286
51058
|
/**
|
|
51287
|
-
* 更新单条 contact
|
|
51288
|
-
* exposedDirs 已废弃,本方法不再触碰它(保留 contact 原值不动)。
|
|
51059
|
+
* 更新单条 contact 的远程访问授权开关(对齐 setPin pattern)。
|
|
51289
51060
|
* @returns 是否命中:deviceId 不存在返 false;命中即 flush.
|
|
51290
51061
|
*/
|
|
51291
51062
|
setRemoteAccess(deviceId, opts) {
|
|
@@ -51319,7 +51090,6 @@ var ContactStore = class {
|
|
|
51319
51090
|
};
|
|
51320
51091
|
|
|
51321
51092
|
// src/contact/connect-remote.ts
|
|
51322
|
-
init_wrapper();
|
|
51323
51093
|
var crypto6 = __toESM(require("crypto"), 1);
|
|
51324
51094
|
var HANDSHAKE_TIMEOUT_MS = 5e3;
|
|
51325
51095
|
var RPC_TIMEOUT_MS = 5e3;
|
|
@@ -51341,11 +51111,7 @@ async function connectRemote(args) {
|
|
|
51341
51111
|
// 缺省时省略字段(不发空串),对方 schema selfUrl 是 .min(1).optional()。
|
|
51342
51112
|
// 决策 #16:身份字段(selfPrincipalId/selfDisplayName/selfDeviceId)已删——
|
|
51343
51113
|
// 对方从 connect token 签名背书取 deviceId/ownerId/provider,不信自报。
|
|
51344
|
-
...args.selfUrl ? { selfUrl: args.selfUrl } : {}
|
|
51345
|
-
// 建联交换:自报本机 SSH pubkey + OS 账号名;缺省省略字段(不发空串)。
|
|
51346
|
-
// 见 doc/glossary/peer-mesh.md#建联交换清单
|
|
51347
|
-
...args.selfSshPublicKey ? { selfSshPublicKey: args.selfSshPublicKey } : {},
|
|
51348
|
-
...args.selfOsUser ? { selfOsUser: args.selfOsUser } : {}
|
|
51114
|
+
...args.selfUrl ? { selfUrl: args.selfUrl } : {}
|
|
51349
51115
|
})
|
|
51350
51116
|
);
|
|
51351
51117
|
await new Promise((resolve6, reject) => {
|
|
@@ -51420,10 +51186,7 @@ async function connectRemote(args) {
|
|
|
51420
51186
|
ownerId: f.owner.ownerId,
|
|
51421
51187
|
provider: f.owner.provider,
|
|
51422
51188
|
displayName: f.owner.displayName,
|
|
51423
|
-
grants: f.capability.grants
|
|
51424
|
-
// 建联交换:对方老版本不带 → 省略(不落 undefined 占位)。见 …#建联交换清单
|
|
51425
|
-
...f.sshPublicKey ? { sshPublicKey: f.sshPublicKey } : {},
|
|
51426
|
-
...f.osUser ? { osUser: f.osUser } : {}
|
|
51189
|
+
grants: f.capability.grants
|
|
51427
51190
|
};
|
|
51428
51191
|
},
|
|
51429
51192
|
close: () => {
|
|
@@ -51445,8 +51208,6 @@ async function autoReverseContact(args) {
|
|
|
51445
51208
|
actions: [...g2.actions]
|
|
51446
51209
|
}));
|
|
51447
51210
|
const existing = args.store.get(args.deviceId) ?? void 0;
|
|
51448
|
-
const sshPublicKey = args.sshPublicKey ?? existing?.sshPublicKey;
|
|
51449
|
-
const osUser = args.osUser ?? existing?.osUser;
|
|
51450
51211
|
const base = {
|
|
51451
51212
|
deviceId: args.deviceId,
|
|
51452
51213
|
ownerId: args.ownerId,
|
|
@@ -51457,17 +51218,10 @@ async function autoReverseContact(args) {
|
|
|
51457
51218
|
grants,
|
|
51458
51219
|
addedAt: now(),
|
|
51459
51220
|
pinnedAt: existing?.pinnedAt ?? null,
|
|
51460
|
-
remoteAccessAllowed: existing?.remoteAccessAllowed ?? false
|
|
51461
|
-
// 建联交换:缺省不落 undefined 占位(JSON 持久化干净)。见 …#建联交换清单
|
|
51462
|
-
...sshPublicKey ? { sshPublicKey } : {},
|
|
51463
|
-
...osUser ? { osUser } : {},
|
|
51464
|
-
exposedDirs: []
|
|
51221
|
+
remoteAccessAllowed: existing?.remoteAccessAllowed ?? false
|
|
51465
51222
|
};
|
|
51466
51223
|
args.store.upsert(base);
|
|
51467
51224
|
args.broadcast({ type: "contact:added", contact: base });
|
|
51468
|
-
if (base.remoteAccessAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
|
|
51469
|
-
args.rebuildAuthorizedKeys?.();
|
|
51470
|
-
}
|
|
51471
51225
|
if (!args.exchangeToken) return;
|
|
51472
51226
|
let token;
|
|
51473
51227
|
try {
|
|
@@ -53465,926 +53219,29 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
53465
53219
|
});
|
|
53466
53220
|
}
|
|
53467
53221
|
|
|
53468
|
-
// src/sshd/sshd-manager.ts
|
|
53469
|
-
var import_node_fs40 = __toESM(require("fs"), 1);
|
|
53470
|
-
var import_node_path40 = __toESM(require("path"), 1);
|
|
53471
|
-
var import_node_child_process11 = require("child_process");
|
|
53472
|
-
|
|
53473
|
-
// src/sshd/sshd-config.ts
|
|
53474
|
-
function buildSshdConfig(input) {
|
|
53475
|
-
const lines = [
|
|
53476
|
-
`ListenAddress ${input.listenAddress}`,
|
|
53477
|
-
`Port ${input.port}`,
|
|
53478
|
-
`HostKey ${input.hostKeyPath}`,
|
|
53479
|
-
`PidFile ${input.pidFilePath}`,
|
|
53480
|
-
`AuthorizedKeysFile ${input.authorizedKeysFile}`,
|
|
53481
|
-
`PubkeyAuthentication yes`,
|
|
53482
|
-
`PasswordAuthentication no`,
|
|
53483
|
-
`ChallengeResponseAuthentication no`,
|
|
53484
|
-
`KbdInteractiveAuthentication no`,
|
|
53485
|
-
`PermitRootLogin no`,
|
|
53486
|
-
`StrictModes no`,
|
|
53487
|
-
`UsePAM no`,
|
|
53488
|
-
`LogLevel INFO`,
|
|
53489
|
-
`Subsystem sftp internal-sftp`
|
|
53490
|
-
];
|
|
53491
|
-
return lines.join("\n") + "\n";
|
|
53492
|
-
}
|
|
53493
|
-
|
|
53494
|
-
// src/sshd/sshd-process.ts
|
|
53495
|
-
var import_node_fs37 = __toESM(require("fs"), 1);
|
|
53496
|
-
var import_node_path37 = __toESM(require("path"), 1);
|
|
53497
|
-
var import_node_child_process10 = require("child_process");
|
|
53498
|
-
function sshdPidFilePath(dataDir) {
|
|
53499
|
-
return import_node_path37.default.join(dataDir, "sshd", "sshd.pid");
|
|
53500
|
-
}
|
|
53501
|
-
function writeSshdPid(dataDir, pid) {
|
|
53502
|
-
try {
|
|
53503
|
-
const p2 = sshdPidFilePath(dataDir);
|
|
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 });
|
|
53506
|
-
} catch {
|
|
53507
|
-
}
|
|
53508
|
-
}
|
|
53509
|
-
function clearSshdPid(dataDir) {
|
|
53510
|
-
try {
|
|
53511
|
-
import_node_fs37.default.unlinkSync(sshdPidFilePath(dataDir));
|
|
53512
|
-
} catch {
|
|
53513
|
-
}
|
|
53514
|
-
}
|
|
53515
|
-
function defaultIsPidAlive2(pid) {
|
|
53516
|
-
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
53517
|
-
try {
|
|
53518
|
-
process.kill(pid, 0);
|
|
53519
|
-
return true;
|
|
53520
|
-
} catch (err) {
|
|
53521
|
-
const code = err.code;
|
|
53522
|
-
return code === "EPERM";
|
|
53523
|
-
}
|
|
53524
|
-
}
|
|
53525
|
-
function defaultReadPidFile2(file) {
|
|
53526
|
-
try {
|
|
53527
|
-
return import_node_fs37.default.readFileSync(file, "utf8");
|
|
53528
|
-
} catch {
|
|
53529
|
-
return null;
|
|
53530
|
-
}
|
|
53531
|
-
}
|
|
53532
|
-
function defaultKillPid2(pid, signal) {
|
|
53533
|
-
try {
|
|
53534
|
-
process.kill(pid, signal);
|
|
53535
|
-
} catch {
|
|
53536
|
-
}
|
|
53537
|
-
}
|
|
53538
|
-
function defaultSleep2(ms) {
|
|
53539
|
-
return new Promise((r) => setTimeout(r, ms));
|
|
53540
|
-
}
|
|
53541
|
-
async function killStaleSshd(deps) {
|
|
53542
|
-
const pidFile = sshdPidFilePath(deps.dataDir);
|
|
53543
|
-
const configPath = import_node_path37.default.join(deps.dataDir, "sshd", "sshd_config");
|
|
53544
|
-
const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile2;
|
|
53545
|
-
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive2;
|
|
53546
|
-
const killPid = deps.killPidImpl ?? defaultKillPid2;
|
|
53547
|
-
const scanPids = deps.scanSshdPidsImpl ?? ((cp) => defaultScanSshdPidsByCmdline(cp, deps.logger));
|
|
53548
|
-
const sleep2 = deps.sleepImpl ?? defaultSleep2;
|
|
53549
|
-
const victims = /* @__PURE__ */ new Set();
|
|
53550
|
-
const raw = readPidFile(pidFile);
|
|
53551
|
-
if (raw) {
|
|
53552
|
-
const pid = parseInt(raw.trim(), 10);
|
|
53553
|
-
if (Number.isFinite(pid) && pid > 0 && pid !== deps.ownPid && isAlive(pid)) {
|
|
53554
|
-
victims.add(pid);
|
|
53555
|
-
}
|
|
53556
|
-
}
|
|
53557
|
-
try {
|
|
53558
|
-
const scanned = await scanPids(configPath);
|
|
53559
|
-
for (const pid of scanned) {
|
|
53560
|
-
if (pid > 0 && pid !== deps.ownPid && isAlive(pid)) victims.add(pid);
|
|
53561
|
-
}
|
|
53562
|
-
} catch (e) {
|
|
53563
|
-
deps.logger?.warn("sshd: stale-sshd cmdline scan failed", { err: e.message });
|
|
53564
|
-
}
|
|
53565
|
-
if (victims.size === 0) {
|
|
53566
|
-
try {
|
|
53567
|
-
import_node_fs37.default.unlinkSync(pidFile);
|
|
53568
|
-
} catch {
|
|
53569
|
-
}
|
|
53570
|
-
return;
|
|
53571
|
-
}
|
|
53572
|
-
for (const pid of victims) {
|
|
53573
|
-
deps.logger?.warn("sshd: killing stale sshd before respawn", { pid });
|
|
53574
|
-
killPid(pid, "SIGKILL");
|
|
53575
|
-
}
|
|
53576
|
-
await sleep2(deps.reapWaitMs ?? 300);
|
|
53577
|
-
try {
|
|
53578
|
-
import_node_fs37.default.unlinkSync(pidFile);
|
|
53579
|
-
} catch {
|
|
53580
|
-
}
|
|
53581
|
-
}
|
|
53582
|
-
async function defaultScanSshdPidsByCmdline(configPath, logger) {
|
|
53583
|
-
if (process.platform === "win32") return [];
|
|
53584
|
-
return new Promise((resolve6) => {
|
|
53585
|
-
const ps = (0, import_node_child_process10.spawn)("ps", ["-axo", "pid=,command="], { stdio: ["ignore", "pipe", "ignore"] });
|
|
53586
|
-
let buf = "";
|
|
53587
|
-
ps.stdout.on("data", (c) => {
|
|
53588
|
-
buf += c.toString();
|
|
53589
|
-
});
|
|
53590
|
-
ps.on("exit", () => {
|
|
53591
|
-
const pids = [];
|
|
53592
|
-
for (const line of buf.split("\n")) {
|
|
53593
|
-
const m2 = /^\s*(\d+)\s+(.*)$/.exec(line);
|
|
53594
|
-
if (!m2) continue;
|
|
53595
|
-
const cmd = m2[2];
|
|
53596
|
-
if (!/\bsshd\b/.test(cmd)) continue;
|
|
53597
|
-
if (!cmd.includes(configPath)) continue;
|
|
53598
|
-
const pid = parseInt(m2[1], 10);
|
|
53599
|
-
if (Number.isFinite(pid) && pid > 0) pids.push(pid);
|
|
53600
|
-
}
|
|
53601
|
-
resolve6(pids);
|
|
53602
|
-
});
|
|
53603
|
-
ps.on("error", (e) => {
|
|
53604
|
-
logger?.warn("sshd: ps scan failed", { err: e.message });
|
|
53605
|
-
resolve6([]);
|
|
53606
|
-
});
|
|
53607
|
-
});
|
|
53608
|
-
}
|
|
53609
|
-
|
|
53610
|
-
// src/sshd/jail-script.ts
|
|
53611
|
-
var import_node_fs38 = __toESM(require("fs"), 1);
|
|
53612
|
-
var import_node_path38 = __toESM(require("path"), 1);
|
|
53613
|
-
var CLAWD_SSH_JAIL_SCRIPT = String.raw`#!/usr/bin/env bash
|
|
53614
|
-
# clawd-ssh-jail — SSH reverse access sandbox wrapper (managed by clawd; do not edit)
|
|
53615
|
-
#
|
|
53616
|
-
# 由 sshd authorized_keys 的 command= 强制入口调用。
|
|
53617
|
-
# 用法: sshd 会以 \`clawd-ssh-jail <deviceId>\` 起本脚本;$SSH_ORIGINAL_COMMAND = client
|
|
53618
|
-
# 真实请求(interactive shell 时为空)。
|
|
53619
|
-
#
|
|
53620
|
-
# 职责:
|
|
53621
|
-
# 1. 读 ~/.clawd/contacts.json 校验 contact.remoteAccessAllowed(不通过即拒)
|
|
53622
|
-
# 2. 直接 exec shell(去沙箱后无目录级限制)
|
|
53623
|
-
|
|
53624
|
-
set -euo pipefail
|
|
53625
|
-
|
|
53626
|
-
# 用户可读审计日志:追加到 ~/.clawd/log/contact-ssh.log(跟 daemon TS 侧 contact-ssh-log.ts 共写)
|
|
53627
|
-
# bash 侧写死 ~/.clawd/log/,因为 jail 脚本在 authorized_keys command= 里被 sshd 起,
|
|
53628
|
-
# 拿不到 daemon --data-dir 覆盖。dev daemon 用 --data-dir=/tmp/x 时 jail 仍写 ~/.clawd/log/,
|
|
53629
|
-
# 跟改动前 (~/.clawd/contact-ssh.log) 一样的分歧行为,不做统一。
|
|
53630
|
-
CONTACT_SSH_LOG="${"${HOME}"}/.clawd/log/contact-ssh.log"
|
|
53631
|
-
log_line() {
|
|
53632
|
-
# 参数: level tag msg
|
|
53633
|
-
local ts
|
|
53634
|
-
ts=$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ" 2>/dev/null || date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
53635
|
-
printf '[%s] [%s] [%s] %s\n' "$ts" "$1" "$2" "$3" >> "$CONTACT_SSH_LOG" 2>/dev/null || true
|
|
53636
|
-
}
|
|
53637
|
-
|
|
53638
|
-
DEVICE_ID="${"${1:-}"}"
|
|
53639
|
-
if [ -z "$DEVICE_ID" ]; then
|
|
53640
|
-
log_line ERROR jail.entered "clawd-ssh-jail 缺 deviceId 参数"
|
|
53641
|
-
echo "clawd-ssh-jail: missing deviceId" >&2
|
|
53642
|
-
exit 1
|
|
53643
|
-
fi
|
|
53644
|
-
|
|
53645
|
-
log_line INFO jail.entered "clawd-ssh-jail 起来 device=${"${DEVICE_ID}"} cmd=${"${SSH_ORIGINAL_COMMAND:-<interactive-shell>}"}"
|
|
53646
|
-
|
|
53647
|
-
CONTACTS="${"${HOME}"}/.clawd/contacts.json"
|
|
53648
|
-
if [ ! -f "$CONTACTS" ]; then
|
|
53649
|
-
log_line ERROR jail.entered "contacts.json 不存在,无法校验 remoteAccessAllowed"
|
|
53650
|
-
echo "clawd-ssh-jail: contacts.json missing" >&2
|
|
53651
|
-
exit 1
|
|
53652
|
-
fi
|
|
53653
|
-
|
|
53654
|
-
# 校验该 contact 的 remoteAccessAllowed(mac/linux 都自带 python3)。去沙箱后不再读目录白名单:
|
|
53655
|
-
# remoteAccessAllowed=false → DENIED exit 2;deviceId 未命中 → NOT_FOUND exit 3;命中且允许 → exit 0。
|
|
53656
|
-
# set -e 下 python 非 0 退出即终止脚本(不进 exec),只有 exit 0 才继续 exec shell。
|
|
53657
|
-
python3 -c "
|
|
53658
|
-
import json, sys
|
|
53659
|
-
with open('$CONTACTS') as f:
|
|
53660
|
-
data = json.load(f)
|
|
53661
|
-
for c in data.get('contacts', []):
|
|
53662
|
-
if c.get('deviceId') == '$DEVICE_ID':
|
|
53663
|
-
if not c.get('remoteAccessAllowed'):
|
|
53664
|
-
print('DENIED', file=sys.stderr); sys.exit(2)
|
|
53665
|
-
sys.exit(0)
|
|
53666
|
-
print('NOT_FOUND', file=sys.stderr); sys.exit(3)
|
|
53667
|
-
"
|
|
53668
|
-
|
|
53669
|
-
log_line INFO jail.entered "remoteAccessAllowed 校验通过,放行 exec shell"
|
|
53670
|
-
|
|
53671
|
-
CMD="${"${SSH_ORIGINAL_COMMAND:-}"}"
|
|
53672
|
-
if [ -z "$CMD" ]; then
|
|
53673
|
-
SHELL_CMD=(bash --login)
|
|
53674
|
-
else
|
|
53675
|
-
SHELL_CMD=(bash -c "$CMD")
|
|
53676
|
-
fi
|
|
53677
|
-
|
|
53678
|
-
# 去掉 OS 沙箱层(原本 macOS Seatbelt 策略 / Linux 命名空间隔离):SSH 进来即该用户
|
|
53679
|
-
# 权限内的 shell,直接 exec。沙箱只限制文件、拦不住网络/进程,隔离价值有限,且相关 API
|
|
53680
|
-
# 在 macOS 已 deprecated。授权即完全信任——remoteAccessAllowed 授权开关 + authorized_keys 即时
|
|
53681
|
-
# 踢行是唯一访问闸。要加回沙箱:从 git 历史(T-29 之前)恢复本段的 OS 分支即可。
|
|
53682
|
-
exec "${"${SHELL_CMD[@]}"}"
|
|
53683
|
-
`;
|
|
53684
|
-
function ensureJailScript(dataDir) {
|
|
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 });
|
|
53689
|
-
return target;
|
|
53690
|
-
}
|
|
53691
|
-
|
|
53692
|
-
// src/sshd/clawd-shim.ts
|
|
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"];
|
|
53696
|
-
function shellQuote(s) {
|
|
53697
|
-
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
53698
|
-
}
|
|
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(" ");
|
|
53705
|
-
return `#!/usr/bin/env bash
|
|
53706
|
-
# clawd shim (managed by clawd; do not edit)
|
|
53707
|
-
#
|
|
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" "$@"
|
|
53735
|
-
`;
|
|
53736
|
-
}
|
|
53737
|
-
function ensureClawdShim(dataDir, execPath, cliPath) {
|
|
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 });
|
|
53742
|
-
return target;
|
|
53743
|
-
}
|
|
53744
|
-
|
|
53745
|
-
// src/sshd/sshd-manager.ts
|
|
53746
|
-
var SshdManager = class {
|
|
53747
|
-
constructor(deps) {
|
|
53748
|
-
this.deps = deps;
|
|
53749
|
-
this.sshdDir = import_node_path40.default.join(deps.dataDir, "sshd");
|
|
53750
|
-
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
53751
|
-
}
|
|
53752
|
-
deps;
|
|
53753
|
-
proc = null;
|
|
53754
|
-
sshdDir;
|
|
53755
|
-
stopping = false;
|
|
53756
|
-
exitHookInstalled = false;
|
|
53757
|
-
startupTimeoutMs;
|
|
53758
|
-
// sshd stderr 按行嗌探 pubkey 认证结果时的半行缓冲(防关键字被 chunk 边界切断)
|
|
53759
|
-
sniffBuf = "";
|
|
53760
|
-
get port() {
|
|
53761
|
-
return this.deps.port;
|
|
53762
|
-
}
|
|
53763
|
-
async start() {
|
|
53764
|
-
const { logger } = this.deps;
|
|
53765
|
-
await (this.deps.killStaleImpl ?? killStaleSshd)({
|
|
53766
|
-
dataDir: this.deps.dataDir,
|
|
53767
|
-
ownPid: process.pid,
|
|
53768
|
-
logger
|
|
53769
|
-
});
|
|
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 });
|
|
53772
|
-
ensureJailScript(this.deps.dataDir);
|
|
53773
|
-
ensureClawdShim(this.deps.dataDir, process.execPath, process.argv[1] ?? "");
|
|
53774
|
-
const hostKeyPath = import_node_path40.default.join(this.sshdDir, "host_key");
|
|
53775
|
-
if (!import_node_fs40.default.existsSync(hostKeyPath)) {
|
|
53776
|
-
await this.generateHostKey(hostKeyPath);
|
|
53777
|
-
}
|
|
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 });
|
|
53781
|
-
}
|
|
53782
|
-
const configPath = import_node_path40.default.join(this.sshdDir, "sshd_config");
|
|
53783
|
-
const config = buildSshdConfig({
|
|
53784
|
-
listenAddress: "127.0.0.1",
|
|
53785
|
-
port: this.deps.port,
|
|
53786
|
-
hostKeyPath,
|
|
53787
|
-
authorizedKeysFile: akFile,
|
|
53788
|
-
pidFilePath: import_node_path40.default.join(this.sshdDir, "sshd.pid")
|
|
53789
|
-
});
|
|
53790
|
-
import_node_fs40.default.writeFileSync(configPath, config, { mode: 384 });
|
|
53791
|
-
const sshdBin = this.deps.sshdBin ?? "/usr/sbin/sshd";
|
|
53792
|
-
const proc = (this.deps.spawnImpl ?? import_node_child_process11.spawn)(sshdBin, ["-D", "-e", "-f", configPath], {
|
|
53793
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
53794
|
-
});
|
|
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"), {
|
|
53798
|
-
flags: "a",
|
|
53799
|
-
mode: 384
|
|
53800
|
-
});
|
|
53801
|
-
logStream.on("error", () => {
|
|
53802
|
-
});
|
|
53803
|
-
const tee = (c) => {
|
|
53804
|
-
logStream.write(String(c));
|
|
53805
|
-
};
|
|
53806
|
-
proc.stdout?.on("data", tee);
|
|
53807
|
-
proc.stderr?.on("data", (c) => {
|
|
53808
|
-
const s = String(c);
|
|
53809
|
-
logStream.write(s);
|
|
53810
|
-
this.sniffSshdAuth(s);
|
|
53811
|
-
});
|
|
53812
|
-
proc.once("exit", () => logStream.end());
|
|
53813
|
-
const ready = await waitForSshdReady(proc, this.startupTimeoutMs);
|
|
53814
|
-
if (!ready.ok) {
|
|
53815
|
-
try {
|
|
53816
|
-
proc.kill("SIGTERM");
|
|
53817
|
-
} catch {
|
|
53818
|
-
}
|
|
53819
|
-
const tail = ready.output.slice(-500);
|
|
53820
|
-
const msg = tail ? `${ready.error}
|
|
53821
|
-
${tail}` : ready.error;
|
|
53822
|
-
throw new Error(msg);
|
|
53823
|
-
}
|
|
53824
|
-
if (typeof proc.pid === "number") writeSshdPid(this.deps.dataDir, proc.pid);
|
|
53825
|
-
this.proc = proc;
|
|
53826
|
-
this.installProcessExitHandlersIfNeeded();
|
|
53827
|
-
this.attachExitListener(proc);
|
|
53828
|
-
logger?.info("sshd: up", { port: this.deps.port, pid: proc.pid ?? null });
|
|
53829
|
-
return { port: this.deps.port };
|
|
53830
|
-
}
|
|
53831
|
-
async stop() {
|
|
53832
|
-
this.stopping = true;
|
|
53833
|
-
const proc = this.proc;
|
|
53834
|
-
this.proc = null;
|
|
53835
|
-
if (!proc) {
|
|
53836
|
-
clearSshdPid(this.deps.dataDir);
|
|
53837
|
-
return;
|
|
53838
|
-
}
|
|
53839
|
-
proc.kill("SIGTERM");
|
|
53840
|
-
await new Promise((resolve6) => {
|
|
53841
|
-
const t = setTimeout(() => {
|
|
53842
|
-
try {
|
|
53843
|
-
proc.kill("SIGKILL");
|
|
53844
|
-
} catch {
|
|
53845
|
-
}
|
|
53846
|
-
resolve6();
|
|
53847
|
-
}, 5e3);
|
|
53848
|
-
proc.once("exit", () => {
|
|
53849
|
-
clearTimeout(t);
|
|
53850
|
-
resolve6();
|
|
53851
|
-
});
|
|
53852
|
-
});
|
|
53853
|
-
clearSshdPid(this.deps.dataDir);
|
|
53854
|
-
}
|
|
53855
|
-
killSync() {
|
|
53856
|
-
const proc = this.proc;
|
|
53857
|
-
this.proc = null;
|
|
53858
|
-
clearSshdPid(this.deps.dataDir);
|
|
53859
|
-
if (!proc) return;
|
|
53860
|
-
try {
|
|
53861
|
-
proc.kill("SIGTERM");
|
|
53862
|
-
} catch {
|
|
53863
|
-
}
|
|
53864
|
-
}
|
|
53865
|
-
attachExitListener(proc) {
|
|
53866
|
-
proc.on("exit", (code) => {
|
|
53867
|
-
this.deps.logger?.warn("sshd exited", { code });
|
|
53868
|
-
if (this.stopping) return;
|
|
53869
|
-
this.proc = null;
|
|
53870
|
-
this.deps.onSshdExit?.({ code });
|
|
53871
|
-
});
|
|
53872
|
-
}
|
|
53873
|
-
installProcessExitHandlersIfNeeded() {
|
|
53874
|
-
if (this.exitHookInstalled) return;
|
|
53875
|
-
if (this.deps.installProcessExitHandlers !== true) return;
|
|
53876
|
-
this.exitHookInstalled = true;
|
|
53877
|
-
const sync = () => this.killSync();
|
|
53878
|
-
process.once("exit", sync);
|
|
53879
|
-
process.once("SIGHUP", sync);
|
|
53880
|
-
process.once("uncaughtException", sync);
|
|
53881
|
-
}
|
|
53882
|
-
async generateHostKey(hostKeyPath) {
|
|
53883
|
-
const keygenBin = this.deps.keygenBin ?? "/usr/bin/ssh-keygen";
|
|
53884
|
-
await new Promise((resolve6, reject) => {
|
|
53885
|
-
const p2 = (this.deps.spawnImpl ?? import_node_child_process11.spawn)(
|
|
53886
|
-
keygenBin,
|
|
53887
|
-
["-t", "ed25519", "-f", hostKeyPath, "-N", "", "-q"],
|
|
53888
|
-
{ stdio: "ignore" }
|
|
53889
|
-
);
|
|
53890
|
-
p2.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`ssh-keygen exit ${code}`)));
|
|
53891
|
-
p2.on("error", reject);
|
|
53892
|
-
});
|
|
53893
|
-
try {
|
|
53894
|
-
import_node_fs40.default.chmodSync(hostKeyPath, 384);
|
|
53895
|
-
} catch {
|
|
53896
|
-
}
|
|
53897
|
-
}
|
|
53898
|
-
// 把 sshd -e 输出里的 pubkey 认证结果转写一条到 contact-ssh.log,让 A 侧审计日志一处即可
|
|
53899
|
-
// 看到"隧道连上但 sshd 拒了 key"(否则只有 tunnel.connected + tunnel.disconnected,为什么
|
|
53900
|
-
// 被拒要翻另一个 sshd.log 对时间线)。按 \n 切行嗅探,半行留 sniffBuf 等下个 chunk 拼回。
|
|
53901
|
-
sniffSshdAuth(chunk) {
|
|
53902
|
-
const sshLog = this.deps.sshLog;
|
|
53903
|
-
if (!sshLog) return;
|
|
53904
|
-
this.sniffBuf += chunk;
|
|
53905
|
-
const parts = this.sniffBuf.split("\n");
|
|
53906
|
-
this.sniffBuf = parts.pop() ?? "";
|
|
53907
|
-
for (const line of parts) {
|
|
53908
|
-
if (/Accepted publickey/i.test(line)) {
|
|
53909
|
-
sshLog.info(
|
|
53910
|
-
"ssh.auth-ok",
|
|
53911
|
-
"sshd \u63A5\u53D7\u4E86 pubkey \u8BA4\u8BC1\uFF08\u5BF9\u7AEF key \u547D\u4E2D authorized_keys\uFF09",
|
|
53912
|
-
sshdAuthMeta(line)
|
|
53913
|
-
);
|
|
53914
|
-
} else if (/Failed publickey/i.test(line) || /Connection closed by (authenticating|invalid) user/i.test(line)) {
|
|
53915
|
-
sshLog.warn(
|
|
53916
|
-
"ssh.auth-rejected",
|
|
53917
|
-
"sshd \u62D2\u7EDD\u4E86 pubkey \u8BA4\u8BC1\uFF08\u5BF9\u7AEF key \u4E0D\u5728 authorized_keys / \u672A\u6388\u6743 / key \u672A\u62C9\u5230\uFF09",
|
|
53918
|
-
sshdAuthMeta(line)
|
|
53919
|
-
);
|
|
53920
|
-
}
|
|
53921
|
-
}
|
|
53922
|
-
}
|
|
53923
|
-
};
|
|
53924
|
-
function sshdAuthMeta(line) {
|
|
53925
|
-
const m2 = /port (\d+)/.exec(line);
|
|
53926
|
-
return m2 ? { clientPort: Number(m2[1]) } : {};
|
|
53927
|
-
}
|
|
53928
|
-
async function waitForSshdReady(proc, timeoutMs) {
|
|
53929
|
-
return new Promise((resolve6) => {
|
|
53930
|
-
let settled = false;
|
|
53931
|
-
let buf = "";
|
|
53932
|
-
const finish = (r) => {
|
|
53933
|
-
if (settled) return;
|
|
53934
|
-
settled = true;
|
|
53935
|
-
cleanup();
|
|
53936
|
-
resolve6(r);
|
|
53937
|
-
};
|
|
53938
|
-
const onData = (chunk) => {
|
|
53939
|
-
buf += String(chunk);
|
|
53940
|
-
if (/Server listening on/i.test(buf)) finish({ ok: true });
|
|
53941
|
-
if (/fatal:/i.test(buf) || /error: Bind to port/i.test(buf)) {
|
|
53942
|
-
finish({ ok: false, error: "sshd startup failed", output: buf });
|
|
53943
|
-
}
|
|
53944
|
-
};
|
|
53945
|
-
const onExit = (code) => finish({ ok: false, error: `sshd exited before ready (code=${code})`, output: buf });
|
|
53946
|
-
const onErr = (err) => finish({ ok: false, error: `sshd spawn error: ${err.message}`, output: buf });
|
|
53947
|
-
const cleanup = () => {
|
|
53948
|
-
proc.stdout?.off("data", onData);
|
|
53949
|
-
proc.stderr?.off("data", onData);
|
|
53950
|
-
proc.off("exit", onExit);
|
|
53951
|
-
proc.off("error", onErr);
|
|
53952
|
-
clearTimeout(timer);
|
|
53953
|
-
};
|
|
53954
|
-
proc.stdout?.on("data", onData);
|
|
53955
|
-
proc.stderr?.on("data", onData);
|
|
53956
|
-
proc.on("exit", onExit);
|
|
53957
|
-
proc.on("error", onErr);
|
|
53958
|
-
const timer = setTimeout(
|
|
53959
|
-
() => finish({ ok: false, error: `sshd startup timeout after ${timeoutMs}ms`, output: buf }),
|
|
53960
|
-
timeoutMs
|
|
53961
|
-
);
|
|
53962
|
-
});
|
|
53963
|
-
}
|
|
53964
|
-
|
|
53965
|
-
// src/sshd/authorized-keys.ts
|
|
53966
|
-
var import_node_fs41 = __toESM(require("fs"), 1);
|
|
53967
|
-
var import_node_path41 = __toESM(require("path"), 1);
|
|
53968
|
-
var JAIL_BIN_PATH_ENV = "CLAWD_JAIL_BIN_PATH";
|
|
53969
|
-
var AUTHORIZED_KEYS_FILE = "clawd-contacts";
|
|
53970
|
-
function jailBinPath() {
|
|
53971
|
-
return process.env[JAIL_BIN_PATH_ENV] ?? import_node_path41.default.join(process.env.HOME ?? "", ".clawd", "bin", "clawd-ssh-jail");
|
|
53972
|
-
}
|
|
53973
|
-
function rebuildAuthorizedKeys(store, sshdDir) {
|
|
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 });
|
|
53977
|
-
const lines = ["# managed by clawd; do not edit", ""];
|
|
53978
|
-
for (const c of store.list()) {
|
|
53979
|
-
if (!c.remoteAccessAllowed) continue;
|
|
53980
|
-
const safe = /^[A-Za-z0-9_.-]+$/.test(c.deviceId);
|
|
53981
|
-
if (!safe) continue;
|
|
53982
|
-
const pubkey = c.sshPublicKey?.trim();
|
|
53983
|
-
if (!pubkey || /[\r\n"]/.test(pubkey)) continue;
|
|
53984
|
-
const bin = jailBinPath();
|
|
53985
|
-
lines.push(`command="${bin} ${c.deviceId}",restrict ${pubkey}`);
|
|
53986
|
-
lines.push(`# contact:${c.deviceId}`);
|
|
53987
|
-
}
|
|
53988
|
-
const body = lines.join("\n") + "\n";
|
|
53989
|
-
const tmp = `${target}.tmp-${process.pid}-${Date.now()}`;
|
|
53990
|
-
import_node_fs41.default.writeFileSync(tmp, body, { mode: 384 });
|
|
53991
|
-
import_node_fs41.default.renameSync(tmp, target);
|
|
53992
|
-
}
|
|
53993
|
-
|
|
53994
|
-
// src/sshd/ssh-identity.ts
|
|
53995
|
-
var import_node_fs42 = __toESM(require("fs"), 1);
|
|
53996
|
-
var import_node_path42 = __toESM(require("path"), 1);
|
|
53997
|
-
var import_node_child_process12 = require("child_process");
|
|
53998
|
-
var IDENTITY_DIR = "ssh-identity";
|
|
53999
|
-
var KEY_FILE = "id_ed25519";
|
|
54000
|
-
function identityPaths(dataDir) {
|
|
54001
|
-
const privPath = import_node_path42.default.join(dataDir, IDENTITY_DIR, KEY_FILE);
|
|
54002
|
-
return { privPath, pubPath: `${privPath}.pub` };
|
|
54003
|
-
}
|
|
54004
|
-
async function ensureSshIdentity(dataDir, opts = {}) {
|
|
54005
|
-
const { privPath, pubPath } = identityPaths(dataDir);
|
|
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() };
|
|
54009
|
-
}
|
|
54010
|
-
const bin = opts.keygenBin ?? "/usr/bin/ssh-keygen";
|
|
54011
|
-
await new Promise((resolve6, reject) => {
|
|
54012
|
-
const p2 = (opts.spawnImpl ?? import_node_child_process12.spawn)(
|
|
54013
|
-
bin,
|
|
54014
|
-
["-t", "ed25519", "-f", privPath, "-N", "", "-q", "-C", "clawd-ssh-identity"],
|
|
54015
|
-
{ stdio: "ignore" }
|
|
54016
|
-
);
|
|
54017
|
-
p2.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`ssh-keygen exit ${code}`)));
|
|
54018
|
-
p2.on("error", reject);
|
|
54019
|
-
});
|
|
54020
|
-
try {
|
|
54021
|
-
import_node_fs42.default.chmodSync(privPath, 384);
|
|
54022
|
-
} catch {
|
|
54023
|
-
}
|
|
54024
|
-
try {
|
|
54025
|
-
import_node_fs42.default.chmodSync(pubPath, 420);
|
|
54026
|
-
} catch {
|
|
54027
|
-
}
|
|
54028
|
-
return { privateKeyPath: privPath, publicKeyLine: import_node_fs42.default.readFileSync(pubPath, "utf8").trim() };
|
|
54029
|
-
}
|
|
54030
|
-
function readSshIdentityPublicKey(dataDir) {
|
|
54031
|
-
try {
|
|
54032
|
-
return import_node_fs42.default.readFileSync(identityPaths(dataDir).pubPath, "utf8").trim();
|
|
54033
|
-
} catch {
|
|
54034
|
-
return null;
|
|
54035
|
-
}
|
|
54036
|
-
}
|
|
54037
|
-
|
|
54038
|
-
// src/sshd/ssh-tunnel-relay.ts
|
|
54039
|
-
var import_node_net2 = __toESM(require("net"), 1);
|
|
54040
|
-
|
|
54041
|
-
// src/sshd/contact-ssh-log.ts
|
|
54042
|
-
var import_node_fs43 = __toESM(require("fs"), 1);
|
|
54043
|
-
var import_node_path43 = __toESM(require("path"), 1);
|
|
54044
|
-
function createContactSshLog(dataDir) {
|
|
54045
|
-
const file = import_node_path43.default.join(dataDir, "log", "contact-ssh.log");
|
|
54046
|
-
function append(level, tag, message, meta) {
|
|
54047
|
-
const time = (/* @__PURE__ */ new Date()).toISOString();
|
|
54048
|
-
let line = `[${time}] [${level}] [${tag}] ${message}`;
|
|
54049
|
-
if (meta && Object.keys(meta).length > 0) {
|
|
54050
|
-
try {
|
|
54051
|
-
line += " " + JSON.stringify(meta);
|
|
54052
|
-
} catch {
|
|
54053
|
-
line += " [meta-serialize-failed]";
|
|
54054
|
-
}
|
|
54055
|
-
}
|
|
54056
|
-
line += "\n";
|
|
54057
|
-
try {
|
|
54058
|
-
import_node_fs43.default.mkdirSync(import_node_path43.default.dirname(file), { recursive: true });
|
|
54059
|
-
import_node_fs43.default.appendFileSync(file, line, { mode: 384 });
|
|
54060
|
-
} catch {
|
|
54061
|
-
}
|
|
54062
|
-
}
|
|
54063
|
-
return {
|
|
54064
|
-
info: (tag, message, meta) => append("INFO", tag, message, meta),
|
|
54065
|
-
warn: (tag, message, meta) => append("WARN", tag, message, meta),
|
|
54066
|
-
error: (tag, message, meta) => append("ERROR", tag, message, meta)
|
|
54067
|
-
};
|
|
54068
|
-
}
|
|
54069
|
-
var nullContactSshLog = {
|
|
54070
|
-
info: () => {
|
|
54071
|
-
},
|
|
54072
|
-
warn: () => {
|
|
54073
|
-
},
|
|
54074
|
-
error: () => {
|
|
54075
|
-
}
|
|
54076
|
-
};
|
|
54077
|
-
|
|
54078
|
-
// src/sshd/ssh-tunnel-relay.ts
|
|
54079
|
-
async function handleSshTunnelUpgrade(req, socket, head, deps) {
|
|
54080
|
-
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
54081
|
-
const clientAddr = (req.socket && "remoteAddress" in req.socket ? req.socket.remoteAddress : null) ?? "unknown";
|
|
54082
|
-
const auth = req.headers.authorization ?? "";
|
|
54083
|
-
const m2 = /^Bearer\s+(.+)$/i.exec(auth);
|
|
54084
|
-
if (!m2) {
|
|
54085
|
-
sshLog.warn("tunnel.auth-failed", "/rpc/ssh-tunnel \u8BF7\u6C42\u7F3A Bearer token", {
|
|
54086
|
-
clientAddr
|
|
54087
|
-
});
|
|
54088
|
-
socket.write(
|
|
54089
|
-
"HTTP/1.1 401 Unauthorized\r\nContent-Length: 0\r\n\r\n"
|
|
54090
|
-
);
|
|
54091
|
-
socket.destroy();
|
|
54092
|
-
return;
|
|
54093
|
-
}
|
|
54094
|
-
const token = m2[1].trim();
|
|
54095
|
-
let ok = false;
|
|
54096
|
-
try {
|
|
54097
|
-
ok = await deps.authorize(token);
|
|
54098
|
-
} catch (err) {
|
|
54099
|
-
deps.logger?.warn("ssh-tunnel: authorize threw", {
|
|
54100
|
-
err: err instanceof Error ? err.message : String(err)
|
|
54101
|
-
});
|
|
54102
|
-
}
|
|
54103
|
-
if (!ok) {
|
|
54104
|
-
sshLog.warn("tunnel.auth-failed", "/rpc/ssh-tunnel Bearer token \u9A8C\u8BC1\u5931\u8D25", {
|
|
54105
|
-
clientAddr
|
|
54106
|
-
});
|
|
54107
|
-
socket.write(
|
|
54108
|
-
"HTTP/1.1 401 Unauthorized\r\nContent-Length: 0\r\n\r\n"
|
|
54109
|
-
);
|
|
54110
|
-
socket.destroy();
|
|
54111
|
-
return;
|
|
54112
|
-
}
|
|
54113
|
-
sshLog.info("tunnel.auth-ok", "/rpc/ssh-tunnel \u8BA4\u8BC1\u901A\u8FC7\uFF0C\u5347\u7EA7 WS", {
|
|
54114
|
-
clientAddr
|
|
54115
|
-
});
|
|
54116
|
-
deps.wss.handleUpgrade(req, socket, head, (ws) => {
|
|
54117
|
-
pumpWsToSshd(ws, deps, clientAddr);
|
|
54118
|
-
});
|
|
54119
|
-
}
|
|
54120
|
-
function pumpWsToSshd(ws, deps, clientAddr) {
|
|
54121
|
-
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
54122
|
-
const tcp = import_node_net2.default.connect(deps.sshdPort, "127.0.0.1");
|
|
54123
|
-
let tcpConnected = false;
|
|
54124
|
-
let bytesFromWs = 0;
|
|
54125
|
-
let bytesToWs = 0;
|
|
54126
|
-
const startedAt = Date.now();
|
|
54127
|
-
const cleanup = (reason) => {
|
|
54128
|
-
try {
|
|
54129
|
-
ws.close(1e3, reason);
|
|
54130
|
-
} catch {
|
|
54131
|
-
}
|
|
54132
|
-
try {
|
|
54133
|
-
tcp.destroy();
|
|
54134
|
-
} catch {
|
|
54135
|
-
}
|
|
54136
|
-
sshLog.info("tunnel.disconnected", "SSH tunnel \u4F1A\u8BDD\u7ED3\u675F", {
|
|
54137
|
-
clientAddr,
|
|
54138
|
-
reason,
|
|
54139
|
-
bytesFromWs,
|
|
54140
|
-
bytesToWs,
|
|
54141
|
-
durationMs: Date.now() - startedAt
|
|
54142
|
-
});
|
|
54143
|
-
};
|
|
54144
|
-
tcp.once("connect", () => {
|
|
54145
|
-
tcpConnected = true;
|
|
54146
|
-
deps.logger?.info("ssh-tunnel: tcp connected to sshd", { port: deps.sshdPort });
|
|
54147
|
-
sshLog.info("tunnel.connected", "WS \u2194 \u672C\u673A sshd raw byte relay \u5DF2\u5C31\u7EEA", {
|
|
54148
|
-
clientAddr,
|
|
54149
|
-
sshdPort: deps.sshdPort
|
|
54150
|
-
});
|
|
54151
|
-
});
|
|
54152
|
-
tcp.on("data", (chunk) => {
|
|
54153
|
-
if (ws.readyState === ws.OPEN) {
|
|
54154
|
-
bytesToWs += chunk.length;
|
|
54155
|
-
ws.send(chunk, { binary: true });
|
|
54156
|
-
}
|
|
54157
|
-
});
|
|
54158
|
-
tcp.on("error", (err) => {
|
|
54159
|
-
deps.logger?.warn("ssh-tunnel: tcp error", { err: err.message, tcpConnected });
|
|
54160
|
-
sshLog.error(
|
|
54161
|
-
tcpConnected ? "tunnel.tcp-error" : "tunnel.tcp-connect-failed",
|
|
54162
|
-
tcpConnected ? "WS \u2194 sshd relay \u671F\u95F4 TCP \u4FA7\u51FA\u9519" : "\u65E0\u6CD5\u8FDE\u5230\u672C\u673A sshd\uFF08sshd \u672A\u8D77\uFF1F\u7AEF\u53E3\u9519\uFF1F\uFF09",
|
|
54163
|
-
{ clientAddr, sshdPort: deps.sshdPort, err: err.message }
|
|
54164
|
-
);
|
|
54165
|
-
cleanup("tcp error");
|
|
54166
|
-
});
|
|
54167
|
-
tcp.on("end", () => cleanup("tcp end"));
|
|
54168
|
-
tcp.on("close", () => cleanup("tcp close"));
|
|
54169
|
-
ws.on("message", (data) => {
|
|
54170
|
-
let buf = null;
|
|
54171
|
-
if (Buffer.isBuffer(data)) buf = data;
|
|
54172
|
-
else if (Array.isArray(data)) buf = Buffer.concat(data);
|
|
54173
|
-
else if (data instanceof ArrayBuffer) buf = Buffer.from(data);
|
|
54174
|
-
if (buf) {
|
|
54175
|
-
bytesFromWs += buf.length;
|
|
54176
|
-
tcp.write(buf);
|
|
54177
|
-
}
|
|
54178
|
-
});
|
|
54179
|
-
ws.on("close", () => cleanup("ws close"));
|
|
54180
|
-
ws.on("error", (err) => {
|
|
54181
|
-
deps.logger?.warn("ssh-tunnel: ws error", { err: err.message });
|
|
54182
|
-
cleanup("ws error");
|
|
54183
|
-
});
|
|
54184
|
-
}
|
|
54185
|
-
|
|
54186
|
-
// src/sshd/ssh-tunnel-local-relay.ts
|
|
54187
|
-
init_wrapper();
|
|
54188
|
-
function parsePeerDeviceId(rawUrl) {
|
|
54189
|
-
if (!rawUrl) return null;
|
|
54190
|
-
try {
|
|
54191
|
-
const u = new URL(rawUrl, "http://127.0.0.1");
|
|
54192
|
-
const v2 = u.searchParams.get("peerDeviceId");
|
|
54193
|
-
return v2 && v2.length > 0 ? v2 : null;
|
|
54194
|
-
} catch {
|
|
54195
|
-
return null;
|
|
54196
|
-
}
|
|
54197
|
-
}
|
|
54198
|
-
async function handleSshTunnelLocalUpgrade(req, socket, head, deps) {
|
|
54199
|
-
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
54200
|
-
const clientAddr = (req.socket && "remoteAddress" in req.socket ? req.socket.remoteAddress : null) ?? "unknown";
|
|
54201
|
-
const sessionId = req.headers["x-clawd-session-id"] ?? "";
|
|
54202
|
-
if (!sessionId) {
|
|
54203
|
-
sshLog.warn("relay.auth-failed", "/rpc/ssh-tunnel-local \u7F3A X-Clawd-Session-Id header", {
|
|
54204
|
-
clientAddr
|
|
54205
|
-
});
|
|
54206
|
-
respond401(socket);
|
|
54207
|
-
return;
|
|
54208
|
-
}
|
|
54209
|
-
const peerDeviceId = parsePeerDeviceId(req.url);
|
|
54210
|
-
if (!peerDeviceId) {
|
|
54211
|
-
sshLog.warn("relay.auth-failed", "/rpc/ssh-tunnel-local \u7F3A peerDeviceId query", { clientAddr });
|
|
54212
|
-
respond400(socket);
|
|
54213
|
-
return;
|
|
54214
|
-
}
|
|
54215
|
-
const contact = deps.store.get(peerDeviceId);
|
|
54216
|
-
if (!contact) {
|
|
54217
|
-
sshLog.error("relay.contact-not-found", "ssh-tunnel-local \u627E\u4E0D\u5230 peer contact", {
|
|
54218
|
-
peerDeviceId
|
|
54219
|
-
});
|
|
54220
|
-
respond404(socket);
|
|
54221
|
-
return;
|
|
54222
|
-
}
|
|
54223
|
-
if (!contact.connectToken) {
|
|
54224
|
-
sshLog.error(
|
|
54225
|
-
"relay.token-missing",
|
|
54226
|
-
"contact \u6709\u8BB0\u5F55\u4F46\u7F3A connectToken\uFF08auto-reverse \u672A\u6362\u7968\uFF09",
|
|
54227
|
-
{ peerDeviceId, peerDisplayName: contact.displayName }
|
|
54228
|
-
);
|
|
54229
|
-
respond403(socket);
|
|
54230
|
-
return;
|
|
54231
|
-
}
|
|
54232
|
-
const baseHttp = wsUrlToHttp(contact.remoteUrl).replace(/\/+$/, "");
|
|
54233
|
-
const wsBase = baseHttp.replace(/^http:/, "ws:").replace(/^https:/, "wss:");
|
|
54234
|
-
const outboundUrl = `${wsBase}/rpc/ssh-tunnel`;
|
|
54235
|
-
sshLog.info("relay.dial", "\u5F00\u59CB\u62E8 A \u4FA7 /rpc/ssh-tunnel", {
|
|
54236
|
-
peerDeviceId,
|
|
54237
|
-
peerDisplayName: contact.displayName,
|
|
54238
|
-
url: outboundUrl
|
|
54239
|
-
});
|
|
54240
|
-
deps.wss.handleUpgrade(req, socket, head, (clientWs) => {
|
|
54241
|
-
sshLog.info(
|
|
54242
|
-
"relay.client-connected",
|
|
54243
|
-
"relay CLI \u5DF2\u8FDE\u4E0A\u672C\u5730 daemon\uFF08client WS \u5347\u7EA7\u6210\u529F\uFF09\uFF0C\u5F00\u59CB\u62E8 peer",
|
|
54244
|
-
{ peerDeviceId, peerDisplayName: contact.displayName }
|
|
54245
|
-
);
|
|
54246
|
-
const makeOut = deps.makeOutboundWs ?? ((u, o) => new import_websocket.default(u, o));
|
|
54247
|
-
const outbound = makeOut(outboundUrl, {
|
|
54248
|
-
headers: { Authorization: `Bearer ${contact.connectToken}` }
|
|
54249
|
-
});
|
|
54250
|
-
pumpClientToOutbound(clientWs, outbound, peerDeviceId, contact.displayName, deps);
|
|
54251
|
-
});
|
|
54252
|
-
}
|
|
54253
|
-
function pumpClientToOutbound(client, outbound, peerDeviceId, peerDisplayName, deps) {
|
|
54254
|
-
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
54255
|
-
const startedAt = Date.now();
|
|
54256
|
-
let outboundOpened = false;
|
|
54257
|
-
let bytesClientToOutbound = 0;
|
|
54258
|
-
let bytesOutboundToClient = 0;
|
|
54259
|
-
let settled = false;
|
|
54260
|
-
const preOpenQueue = [];
|
|
54261
|
-
let preOpenQueuedBytes = 0;
|
|
54262
|
-
const cleanup = (reason, code = 1e3) => {
|
|
54263
|
-
if (settled) return;
|
|
54264
|
-
settled = true;
|
|
54265
|
-
try {
|
|
54266
|
-
client.close(code, reason);
|
|
54267
|
-
} catch {
|
|
54268
|
-
}
|
|
54269
|
-
try {
|
|
54270
|
-
outbound.close();
|
|
54271
|
-
} catch {
|
|
54272
|
-
}
|
|
54273
|
-
const level = code === 1e3 ? "info" : "warn";
|
|
54274
|
-
sshLog[level](
|
|
54275
|
-
code === 1e3 ? "relay.dial-close" : "relay.dial-close-abnormal",
|
|
54276
|
-
code === 1e3 ? "tunnel \u4F1A\u8BDD\u6B63\u5E38\u7ED3\u675F" : "tunnel \u4F1A\u8BDD\u975E\u6B63\u5E38\u5173\u95ED",
|
|
54277
|
-
{
|
|
54278
|
-
peerDeviceId,
|
|
54279
|
-
peerDisplayName,
|
|
54280
|
-
reason,
|
|
54281
|
-
code,
|
|
54282
|
-
outboundOpened,
|
|
54283
|
-
bytesClientToOutbound,
|
|
54284
|
-
bytesOutboundToClient,
|
|
54285
|
-
durationMs: Date.now() - startedAt
|
|
54286
|
-
}
|
|
54287
|
-
);
|
|
54288
|
-
};
|
|
54289
|
-
outbound.on("open", () => {
|
|
54290
|
-
outboundOpened = true;
|
|
54291
|
-
const queuedFrames = preOpenQueue.length;
|
|
54292
|
-
if (queuedFrames > 0) {
|
|
54293
|
-
for (const buf of preOpenQueue) outbound.send(buf, { binary: true });
|
|
54294
|
-
preOpenQueue.length = 0;
|
|
54295
|
-
sshLog.info(
|
|
54296
|
-
"relay.preopen-flush",
|
|
54297
|
-
"outbound \u5C31\u7EEA\u524D\u7F13\u51B2\u7684 client \u5B57\u8282\u5DF2\u6309\u5E8F flush\uFF08\u542B SSH banner\uFF09",
|
|
54298
|
-
{ peerDeviceId, peerDisplayName, queuedFrames, queuedBytes: preOpenQueuedBytes }
|
|
54299
|
-
);
|
|
54300
|
-
}
|
|
54301
|
-
sshLog.info("relay.dial-open", "outbound ws open\uFF0C\u8FDB\u5165\u53CC\u5411 pipe", {
|
|
54302
|
-
peerDeviceId,
|
|
54303
|
-
peerDisplayName,
|
|
54304
|
-
preOpenQueuedFrames: queuedFrames
|
|
54305
|
-
});
|
|
54306
|
-
});
|
|
54307
|
-
outbound.on("message", (data) => {
|
|
54308
|
-
const buf = toBuffer(data);
|
|
54309
|
-
if (!buf) return;
|
|
54310
|
-
bytesOutboundToClient += buf.length;
|
|
54311
|
-
if (client.readyState === client.OPEN) client.send(buf, { binary: true });
|
|
54312
|
-
});
|
|
54313
|
-
outbound.on("error", (err) => {
|
|
54314
|
-
deps.logger?.warn("ssh-tunnel-local: outbound ws error", { err: err.message });
|
|
54315
|
-
sshLog.error("relay.dial-error", "outbound ws \u8FDE\u63A5\u9519\u8BEF\uFF08peer \u4E0D\u53EF\u8FBE / TLS \u5931\u8D25\uFF09", {
|
|
54316
|
-
peerDeviceId,
|
|
54317
|
-
peerDisplayName,
|
|
54318
|
-
message: err.message
|
|
54319
|
-
});
|
|
54320
|
-
cleanup("outbound error", 1011);
|
|
54321
|
-
});
|
|
54322
|
-
outbound.on("close", (code) => {
|
|
54323
|
-
if (!settled) cleanup(`outbound close ${code}`, code === 1e3 ? 1e3 : 1011);
|
|
54324
|
-
});
|
|
54325
|
-
client.on("message", (data) => {
|
|
54326
|
-
const buf = toBuffer(data);
|
|
54327
|
-
if (!buf) return;
|
|
54328
|
-
bytesClientToOutbound += buf.length;
|
|
54329
|
-
if (outbound.readyState === outbound.OPEN) {
|
|
54330
|
-
outbound.send(buf, { binary: true });
|
|
54331
|
-
} else {
|
|
54332
|
-
preOpenQueue.push(buf);
|
|
54333
|
-
preOpenQueuedBytes += buf.length;
|
|
54334
|
-
}
|
|
54335
|
-
});
|
|
54336
|
-
client.on("close", () => cleanup("client close", 1e3));
|
|
54337
|
-
client.on("error", (err) => {
|
|
54338
|
-
deps.logger?.warn("ssh-tunnel-local: client ws error", { err: err.message });
|
|
54339
|
-
cleanup("client error", 1011);
|
|
54340
|
-
});
|
|
54341
|
-
}
|
|
54342
|
-
function toBuffer(data) {
|
|
54343
|
-
if (Buffer.isBuffer(data)) return data;
|
|
54344
|
-
if (Array.isArray(data)) return Buffer.concat(data);
|
|
54345
|
-
if (data instanceof ArrayBuffer) return Buffer.from(data);
|
|
54346
|
-
return null;
|
|
54347
|
-
}
|
|
54348
|
-
function respond401(socket) {
|
|
54349
|
-
socket.write("HTTP/1.1 401 Unauthorized\r\nContent-Length: 0\r\n\r\n");
|
|
54350
|
-
socket.destroy();
|
|
54351
|
-
}
|
|
54352
|
-
function respond400(socket) {
|
|
54353
|
-
socket.write("HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n");
|
|
54354
|
-
socket.destroy();
|
|
54355
|
-
}
|
|
54356
|
-
function respond403(socket) {
|
|
54357
|
-
socket.write("HTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\n\r\n");
|
|
54358
|
-
socket.destroy();
|
|
54359
|
-
}
|
|
54360
|
-
function respond404(socket) {
|
|
54361
|
-
socket.write("HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n");
|
|
54362
|
-
socket.destroy();
|
|
54363
|
-
}
|
|
54364
|
-
|
|
54365
53222
|
// src/tunnel/device-key.ts
|
|
54366
53223
|
var import_node_os14 = __toESM(require("os"), 1);
|
|
54367
|
-
var
|
|
53224
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
54368
53225
|
var import_node_crypto11 = __toESM(require("crypto"), 1);
|
|
54369
53226
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
54370
53227
|
function deriveStableDeviceKey(opts = {}) {
|
|
54371
53228
|
const hostname = opts.hostname ?? import_node_os14.default.hostname();
|
|
54372
53229
|
const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
|
|
54373
53230
|
const home = opts.home ?? import_node_os14.default.homedir();
|
|
54374
|
-
const defaultDataDir =
|
|
54375
|
-
const normalizedDataDir = opts.dataDir ?
|
|
53231
|
+
const defaultDataDir = import_node_path37.default.resolve(import_node_path37.default.join(home, ".clawd"));
|
|
53232
|
+
const normalizedDataDir = opts.dataDir ? import_node_path37.default.resolve(opts.dataDir) : null;
|
|
54376
53233
|
const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
|
|
54377
53234
|
const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
|
|
54378
53235
|
return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
54379
53236
|
}
|
|
54380
53237
|
|
|
54381
53238
|
// src/auth-store.ts
|
|
54382
|
-
var
|
|
54383
|
-
var
|
|
53239
|
+
var import_node_fs37 = __toESM(require("fs"), 1);
|
|
53240
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
54384
53241
|
var import_node_crypto12 = __toESM(require("crypto"), 1);
|
|
54385
53242
|
var AUTH_FILE_NAME = "auth.json";
|
|
54386
53243
|
function authFilePath(dataDir) {
|
|
54387
|
-
return
|
|
53244
|
+
return import_node_path38.default.join(dataDir, AUTH_FILE_NAME);
|
|
54388
53245
|
}
|
|
54389
53246
|
function loadOrCreateAuthFile(opts) {
|
|
54390
53247
|
const file = authFilePath(opts.dataDir);
|
|
@@ -54420,7 +53277,7 @@ function defaultGenerateOwnerPrincipalId() {
|
|
|
54420
53277
|
}
|
|
54421
53278
|
function readAuthFile(file) {
|
|
54422
53279
|
try {
|
|
54423
|
-
const raw =
|
|
53280
|
+
const raw = import_node_fs37.default.readFileSync(file, "utf8");
|
|
54424
53281
|
const parsed = JSON.parse(raw);
|
|
54425
53282
|
if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
|
|
54426
53283
|
return null;
|
|
@@ -54440,25 +53297,25 @@ function readAuthFile(file) {
|
|
|
54440
53297
|
}
|
|
54441
53298
|
}
|
|
54442
53299
|
function writeAuthFile(file, content) {
|
|
54443
|
-
|
|
54444
|
-
|
|
53300
|
+
import_node_fs37.default.mkdirSync(import_node_path38.default.dirname(file), { recursive: true });
|
|
53301
|
+
import_node_fs37.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
54445
53302
|
try {
|
|
54446
|
-
|
|
53303
|
+
import_node_fs37.default.chmodSync(file, 384);
|
|
54447
53304
|
} catch {
|
|
54448
53305
|
}
|
|
54449
53306
|
}
|
|
54450
53307
|
|
|
54451
53308
|
// src/owner-profile.ts
|
|
54452
|
-
var
|
|
53309
|
+
var import_node_fs38 = __toESM(require("fs"), 1);
|
|
54453
53310
|
var import_node_os15 = __toESM(require("os"), 1);
|
|
54454
|
-
var
|
|
53311
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
54455
53312
|
var PROFILE_FILENAME = "profile.json";
|
|
54456
53313
|
function loadOwnerDisplayName(dataDir) {
|
|
54457
53314
|
const fallback = import_node_os15.default.userInfo().username;
|
|
54458
|
-
const profilePath =
|
|
53315
|
+
const profilePath = import_node_path39.default.join(dataDir, PROFILE_FILENAME);
|
|
54459
53316
|
let raw;
|
|
54460
53317
|
try {
|
|
54461
|
-
raw =
|
|
53318
|
+
raw = import_node_fs38.default.readFileSync(profilePath, "utf8");
|
|
54462
53319
|
} catch {
|
|
54463
53320
|
return fallback;
|
|
54464
53321
|
}
|
|
@@ -54481,18 +53338,18 @@ function loadOwnerDisplayName(dataDir) {
|
|
|
54481
53338
|
}
|
|
54482
53339
|
|
|
54483
53340
|
// src/feishu-auth/owner-identity-store.ts
|
|
54484
|
-
var
|
|
54485
|
-
var
|
|
53341
|
+
var import_node_fs39 = __toESM(require("fs"), 1);
|
|
53342
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
54486
53343
|
var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
|
|
54487
53344
|
var OwnerIdentityStore = class {
|
|
54488
53345
|
file;
|
|
54489
53346
|
constructor(dataDir) {
|
|
54490
|
-
this.file =
|
|
53347
|
+
this.file = import_node_path40.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
|
|
54491
53348
|
}
|
|
54492
53349
|
read() {
|
|
54493
53350
|
let raw;
|
|
54494
53351
|
try {
|
|
54495
|
-
raw =
|
|
53352
|
+
raw = import_node_fs39.default.readFileSync(this.file, "utf8");
|
|
54496
53353
|
} catch {
|
|
54497
53354
|
return null;
|
|
54498
53355
|
}
|
|
@@ -54520,16 +53377,16 @@ var OwnerIdentityStore = class {
|
|
|
54520
53377
|
};
|
|
54521
53378
|
}
|
|
54522
53379
|
write(record) {
|
|
54523
|
-
|
|
54524
|
-
|
|
53380
|
+
import_node_fs39.default.mkdirSync(import_node_path40.default.dirname(this.file), { recursive: true });
|
|
53381
|
+
import_node_fs39.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
|
|
54525
53382
|
try {
|
|
54526
|
-
|
|
53383
|
+
import_node_fs39.default.chmodSync(this.file, 384);
|
|
54527
53384
|
} catch {
|
|
54528
53385
|
}
|
|
54529
53386
|
}
|
|
54530
53387
|
clear() {
|
|
54531
53388
|
try {
|
|
54532
|
-
|
|
53389
|
+
import_node_fs39.default.unlinkSync(this.file);
|
|
54533
53390
|
} catch (err) {
|
|
54534
53391
|
const code = err?.code;
|
|
54535
53392
|
if (code !== "ENOENT") throw err;
|
|
@@ -54666,9 +53523,9 @@ var CentralClientError = class extends Error {
|
|
|
54666
53523
|
code;
|
|
54667
53524
|
cause;
|
|
54668
53525
|
};
|
|
54669
|
-
async function centralRequest(opts,
|
|
53526
|
+
async function centralRequest(opts, path70, init) {
|
|
54670
53527
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
54671
|
-
const url = `${opts.api.replace(/\/+$/, "")}${
|
|
53528
|
+
const url = `${opts.api.replace(/\/+$/, "")}${path70}`;
|
|
54672
53529
|
const ctrl = new AbortController();
|
|
54673
53530
|
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
54674
53531
|
let res;
|
|
@@ -54862,8 +53719,8 @@ function verifyConnectToken(args) {
|
|
|
54862
53719
|
}
|
|
54863
53720
|
|
|
54864
53721
|
// src/feishu-auth/server-key.ts
|
|
54865
|
-
var
|
|
54866
|
-
var
|
|
53722
|
+
var fs49 = __toESM(require("fs"), 1);
|
|
53723
|
+
var path49 = __toESM(require("path"), 1);
|
|
54867
53724
|
var FILE_NAME2 = "server-signing-key.json";
|
|
54868
53725
|
var ServerKeyStore = class {
|
|
54869
53726
|
constructor(dataDir) {
|
|
@@ -54871,12 +53728,12 @@ var ServerKeyStore = class {
|
|
|
54871
53728
|
}
|
|
54872
53729
|
dataDir;
|
|
54873
53730
|
filePath() {
|
|
54874
|
-
return
|
|
53731
|
+
return path49.join(this.dataDir, FILE_NAME2);
|
|
54875
53732
|
}
|
|
54876
53733
|
/** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
|
|
54877
53734
|
read() {
|
|
54878
53735
|
try {
|
|
54879
|
-
const raw =
|
|
53736
|
+
const raw = fs49.readFileSync(this.filePath(), "utf8");
|
|
54880
53737
|
const parsed = JSON.parse(raw);
|
|
54881
53738
|
if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
|
|
54882
53739
|
return parsed.publicKeyPem;
|
|
@@ -54891,12 +53748,12 @@ var ServerKeyStore = class {
|
|
|
54891
53748
|
publicKeyPem,
|
|
54892
53749
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
54893
53750
|
};
|
|
54894
|
-
|
|
54895
|
-
|
|
53751
|
+
fs49.mkdirSync(this.dataDir, { recursive: true });
|
|
53752
|
+
fs49.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
|
|
54896
53753
|
}
|
|
54897
53754
|
clear() {
|
|
54898
53755
|
try {
|
|
54899
|
-
|
|
53756
|
+
fs49.unlinkSync(this.filePath());
|
|
54900
53757
|
} catch {
|
|
54901
53758
|
}
|
|
54902
53759
|
}
|
|
@@ -54909,12 +53766,12 @@ init_protocol();
|
|
|
54909
53766
|
init_protocol();
|
|
54910
53767
|
|
|
54911
53768
|
// src/session/fork.ts
|
|
54912
|
-
var
|
|
53769
|
+
var import_node_fs40 = __toESM(require("fs"), 1);
|
|
54913
53770
|
var import_node_os16 = __toESM(require("os"), 1);
|
|
54914
|
-
var
|
|
53771
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
54915
53772
|
init_claude_history();
|
|
54916
53773
|
function readJsonlEntries(file) {
|
|
54917
|
-
const raw =
|
|
53774
|
+
const raw = import_node_fs40.default.readFileSync(file, "utf8");
|
|
54918
53775
|
const out = [];
|
|
54919
53776
|
for (const line of raw.split("\n")) {
|
|
54920
53777
|
const t = line.trim();
|
|
@@ -54927,19 +53784,19 @@ function readJsonlEntries(file) {
|
|
|
54927
53784
|
return out;
|
|
54928
53785
|
}
|
|
54929
53786
|
function forkSession(input) {
|
|
54930
|
-
const baseDir = input.baseDir ??
|
|
54931
|
-
let projectDir =
|
|
54932
|
-
let sourceFile =
|
|
54933
|
-
if (!
|
|
53787
|
+
const baseDir = input.baseDir ?? import_node_path41.default.join(import_node_os16.default.homedir(), ".claude");
|
|
53788
|
+
let projectDir = import_node_path41.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
53789
|
+
let sourceFile = import_node_path41.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
53790
|
+
if (!import_node_fs40.default.existsSync(sourceFile)) {
|
|
54934
53791
|
const found = findTranscriptByToolSessionId(
|
|
54935
|
-
|
|
53792
|
+
import_node_path41.default.join(baseDir, "projects"),
|
|
54936
53793
|
input.toolSessionId
|
|
54937
53794
|
);
|
|
54938
53795
|
if (!found) {
|
|
54939
53796
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
54940
53797
|
}
|
|
54941
53798
|
sourceFile = found;
|
|
54942
|
-
projectDir =
|
|
53799
|
+
projectDir = import_node_path41.default.dirname(found);
|
|
54943
53800
|
}
|
|
54944
53801
|
const entries = readJsonlEntries(sourceFile);
|
|
54945
53802
|
const mainEntries = entries.filter((e) => e.isSidechain !== true);
|
|
@@ -54968,9 +53825,9 @@ function forkSession(input) {
|
|
|
54968
53825
|
}
|
|
54969
53826
|
forkedLines.push(JSON.stringify(forked));
|
|
54970
53827
|
}
|
|
54971
|
-
const forkedFilePath =
|
|
54972
|
-
|
|
54973
|
-
|
|
53828
|
+
const forkedFilePath = import_node_path41.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
53829
|
+
import_node_fs40.default.mkdirSync(projectDir, { recursive: true });
|
|
53830
|
+
import_node_fs40.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
54974
53831
|
return { forkedToolSessionId, forkedFilePath };
|
|
54975
53832
|
}
|
|
54976
53833
|
|
|
@@ -55341,7 +54198,7 @@ function buildPermissionHandlers(deps) {
|
|
|
55341
54198
|
}
|
|
55342
54199
|
|
|
55343
54200
|
// src/handlers/history.ts
|
|
55344
|
-
var
|
|
54201
|
+
var path52 = __toESM(require("path"), 1);
|
|
55345
54202
|
init_protocol();
|
|
55346
54203
|
|
|
55347
54204
|
// src/session/recent-dirs.ts
|
|
@@ -55359,7 +54216,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
55359
54216
|
}
|
|
55360
54217
|
|
|
55361
54218
|
// src/permission/persona-paths.ts
|
|
55362
|
-
var
|
|
54219
|
+
var path51 = __toESM(require("path"), 1);
|
|
55363
54220
|
function getAllowedPersonaIds(grants, action) {
|
|
55364
54221
|
const ids = /* @__PURE__ */ new Set();
|
|
55365
54222
|
for (const g2 of grants) {
|
|
@@ -55372,42 +54229,42 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
55372
54229
|
return ids;
|
|
55373
54230
|
}
|
|
55374
54231
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
|
|
55375
|
-
const target =
|
|
54232
|
+
const target = path51.resolve(absPath);
|
|
55376
54233
|
if (userWorkDir) {
|
|
55377
|
-
const u =
|
|
55378
|
-
const usep = u.endsWith(
|
|
54234
|
+
const u = path51.resolve(userWorkDir);
|
|
54235
|
+
const usep = u.endsWith(path51.sep) ? "" : path51.sep;
|
|
55379
54236
|
if (target === u || target.startsWith(u + usep)) return true;
|
|
55380
54237
|
}
|
|
55381
|
-
const root =
|
|
55382
|
-
const sep3 = root.endsWith(
|
|
54238
|
+
const root = path51.resolve(personaRoot);
|
|
54239
|
+
const sep3 = root.endsWith(path51.sep) ? "" : path51.sep;
|
|
55383
54240
|
if (!target.startsWith(root + sep3)) return false;
|
|
55384
|
-
const rel =
|
|
54241
|
+
const rel = path51.relative(root, target);
|
|
55385
54242
|
if (!rel || rel.startsWith("..")) return false;
|
|
55386
|
-
const personaId = rel.split(
|
|
54243
|
+
const personaId = rel.split(path51.sep)[0];
|
|
55387
54244
|
if (!personaId) return false;
|
|
55388
54245
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
55389
54246
|
if (allowed === "*") return true;
|
|
55390
54247
|
return allowed.has(personaId);
|
|
55391
54248
|
}
|
|
55392
54249
|
function personaIdFromPath(absPath, personaRoot) {
|
|
55393
|
-
const root =
|
|
55394
|
-
const target =
|
|
55395
|
-
const sep3 = root.endsWith(
|
|
54250
|
+
const root = path51.resolve(personaRoot);
|
|
54251
|
+
const target = path51.resolve(absPath);
|
|
54252
|
+
const sep3 = root.endsWith(path51.sep) ? "" : path51.sep;
|
|
55396
54253
|
if (!target.startsWith(root + sep3)) return null;
|
|
55397
|
-
const rel =
|
|
54254
|
+
const rel = path51.relative(root, target);
|
|
55398
54255
|
if (!rel || rel.startsWith("..")) return null;
|
|
55399
|
-
const id = rel.split(
|
|
54256
|
+
const id = rel.split(path51.sep)[0];
|
|
55400
54257
|
return id || null;
|
|
55401
54258
|
}
|
|
55402
54259
|
function isPathWithin(dir, absPath) {
|
|
55403
|
-
const d =
|
|
55404
|
-
const t =
|
|
55405
|
-
const sep3 = d.endsWith(
|
|
54260
|
+
const d = path51.resolve(dir);
|
|
54261
|
+
const t = path51.resolve(absPath);
|
|
54262
|
+
const sep3 = d.endsWith(path51.sep) ? "" : path51.sep;
|
|
55406
54263
|
return t === d || t.startsWith(d + sep3);
|
|
55407
54264
|
}
|
|
55408
54265
|
function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
|
|
55409
54266
|
if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
|
|
55410
|
-
return personaIdFromPath(
|
|
54267
|
+
return personaIdFromPath(path51.resolve(absPath), personaRoot) === personaId;
|
|
55411
54268
|
}
|
|
55412
54269
|
|
|
55413
54270
|
// src/handlers/history.ts
|
|
@@ -55433,7 +54290,7 @@ function buildHistoryHandlers(deps) {
|
|
|
55433
54290
|
if (!pid) return false;
|
|
55434
54291
|
return isGuestPathAllowed(
|
|
55435
54292
|
ctx.grants,
|
|
55436
|
-
|
|
54293
|
+
path52.join(personaRoot, pid),
|
|
55437
54294
|
personaRoot,
|
|
55438
54295
|
"read",
|
|
55439
54296
|
userWorkDir
|
|
@@ -55445,7 +54302,7 @@ function buildHistoryHandlers(deps) {
|
|
|
55445
54302
|
};
|
|
55446
54303
|
const list = async (frame, _client, ctx) => {
|
|
55447
54304
|
const args = HistoryListArgs.parse(frame);
|
|
55448
|
-
assertGuestPath(ctx,
|
|
54305
|
+
assertGuestPath(ctx, path52.resolve(args.projectPath), personaRoot, "history:list");
|
|
55449
54306
|
const sessions = await history.listSessions(args);
|
|
55450
54307
|
return { response: { type: "history:list", sessions } };
|
|
55451
54308
|
};
|
|
@@ -55477,13 +54334,13 @@ function buildHistoryHandlers(deps) {
|
|
|
55477
54334
|
};
|
|
55478
54335
|
const subagents = async (frame, _client, ctx) => {
|
|
55479
54336
|
const args = HistorySubagentsArgs.parse(frame);
|
|
55480
|
-
assertGuestPath(ctx,
|
|
54337
|
+
assertGuestPath(ctx, path52.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
|
|
55481
54338
|
const subs = await history.listSubagents(args);
|
|
55482
54339
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
55483
54340
|
};
|
|
55484
54341
|
const subagentRead = async (frame, _client, ctx) => {
|
|
55485
54342
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
55486
|
-
assertGuestPath(ctx,
|
|
54343
|
+
assertGuestPath(ctx, path52.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
|
|
55487
54344
|
const res = await history.readSubagent(args);
|
|
55488
54345
|
return { response: { type: "history:subagent-read", ...res } };
|
|
55489
54346
|
};
|
|
@@ -55492,7 +54349,7 @@ function buildHistoryHandlers(deps) {
|
|
|
55492
54349
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
55493
54350
|
const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
|
|
55494
54351
|
const filtered = dirs.filter(
|
|
55495
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
54352
|
+
(d) => isGuestPathAllowed(ctx.grants, path52.resolve(d.cwd), personaRoot, "read", userWorkDir)
|
|
55496
54353
|
);
|
|
55497
54354
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
55498
54355
|
}
|
|
@@ -55509,7 +54366,7 @@ function buildHistoryHandlers(deps) {
|
|
|
55509
54366
|
}
|
|
55510
54367
|
|
|
55511
54368
|
// src/handlers/workspace.ts
|
|
55512
|
-
var
|
|
54369
|
+
var path53 = __toESM(require("path"), 1);
|
|
55513
54370
|
var os16 = __toESM(require("os"), 1);
|
|
55514
54371
|
init_protocol();
|
|
55515
54372
|
init_protocol();
|
|
@@ -55551,22 +54408,22 @@ function buildWorkspaceHandlers(deps) {
|
|
|
55551
54408
|
const args = WorkspaceListArgs.parse(frame);
|
|
55552
54409
|
const isGuest = ctx?.principal.kind === "guest";
|
|
55553
54410
|
const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
|
|
55554
|
-
const resolvedCwd =
|
|
55555
|
-
const target = args.path ?
|
|
54411
|
+
const resolvedCwd = path53.resolve(args.cwd ?? fallbackCwd);
|
|
54412
|
+
const target = args.path ? path53.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
55556
54413
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
|
|
55557
54414
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
55558
54415
|
return { response: { type: "workspace:list", ...res } };
|
|
55559
54416
|
};
|
|
55560
54417
|
const read = async (frame, _client, ctx) => {
|
|
55561
54418
|
const args = WorkspaceReadArgs.parse(frame);
|
|
55562
|
-
const target =
|
|
54419
|
+
const target = path53.isAbsolute(args.path) ? path53.resolve(args.path) : path53.resolve(args.cwd, args.path);
|
|
55563
54420
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
|
|
55564
54421
|
const res = workspace.read(args);
|
|
55565
54422
|
return { response: { type: "workspace:read", ...res } };
|
|
55566
54423
|
};
|
|
55567
54424
|
const skillsList = async (frame, _client, ctx) => {
|
|
55568
54425
|
const args = SkillsListArgs.parse(frame);
|
|
55569
|
-
const cwdAbs =
|
|
54426
|
+
const cwdAbs = path53.resolve(args.cwd);
|
|
55570
54427
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
|
|
55571
54428
|
const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
|
|
55572
54429
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -55578,7 +54435,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
55578
54435
|
};
|
|
55579
54436
|
const agentsList = async (frame, _client, ctx) => {
|
|
55580
54437
|
const args = AgentsListArgs.parse(frame);
|
|
55581
|
-
const cwdAbs =
|
|
54438
|
+
const cwdAbs = path53.resolve(args.cwd);
|
|
55582
54439
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
|
|
55583
54440
|
if (args.tool === "codex") {
|
|
55584
54441
|
return { response: { type: "agents:list", agents: [] } };
|
|
@@ -55600,20 +54457,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
55600
54457
|
}
|
|
55601
54458
|
|
|
55602
54459
|
// src/handlers/git.ts
|
|
55603
|
-
var
|
|
54460
|
+
var path55 = __toESM(require("path"), 1);
|
|
55604
54461
|
init_protocol();
|
|
55605
54462
|
init_protocol();
|
|
55606
54463
|
|
|
55607
54464
|
// src/workspace/git.ts
|
|
55608
|
-
var
|
|
55609
|
-
var
|
|
55610
|
-
var
|
|
54465
|
+
var import_node_child_process10 = require("child_process");
|
|
54466
|
+
var import_node_fs41 = __toESM(require("fs"), 1);
|
|
54467
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
55611
54468
|
var import_node_util = require("util");
|
|
55612
|
-
var pexec = (0, import_node_util.promisify)(
|
|
54469
|
+
var pexec = (0, import_node_util.promisify)(import_node_child_process10.execFile);
|
|
55613
54470
|
function normalizePath(p2) {
|
|
55614
|
-
const resolved =
|
|
54471
|
+
const resolved = import_node_path42.default.resolve(p2);
|
|
55615
54472
|
try {
|
|
55616
|
-
return
|
|
54473
|
+
return import_node_fs41.default.realpathSync(resolved);
|
|
55617
54474
|
} catch {
|
|
55618
54475
|
return resolved;
|
|
55619
54476
|
}
|
|
@@ -55687,7 +54544,7 @@ async function listGitBranches(cwd) {
|
|
|
55687
54544
|
function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
|
|
55688
54545
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
55689
54546
|
const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
|
|
55690
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
54547
|
+
if (!isGuestPathAllowed(ctx.grants, path55.resolve(cwd), personaRoot, "read", userWorkDir)) {
|
|
55691
54548
|
throw new ClawdError(
|
|
55692
54549
|
ERROR_CODES.UNAUTHORIZED,
|
|
55693
54550
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -55972,18 +54829,6 @@ function buildContactHandlers(deps) {
|
|
|
55972
54829
|
}
|
|
55973
54830
|
};
|
|
55974
54831
|
};
|
|
55975
|
-
const dialInfo = async () => {
|
|
55976
|
-
return {
|
|
55977
|
-
response: {
|
|
55978
|
-
type: "contact:dialInfo:ok",
|
|
55979
|
-
contacts: deps.store.list().map((c) => ({
|
|
55980
|
-
deviceId: c.deviceId,
|
|
55981
|
-
displayName: c.displayName,
|
|
55982
|
-
...c.osUser ? { osUser: c.osUser } : {}
|
|
55983
|
-
}))
|
|
55984
|
-
}
|
|
55985
|
-
};
|
|
55986
|
-
};
|
|
55987
54832
|
const pin = async (frame, _client, ctx) => {
|
|
55988
54833
|
ensureOwner(ctx);
|
|
55989
54834
|
const { type: _t, requestId: _r, ...rest } = frame;
|
|
@@ -56027,13 +54872,12 @@ function buildContactHandlers(deps) {
|
|
|
56027
54872
|
};
|
|
56028
54873
|
return {
|
|
56029
54874
|
"contact:list": list,
|
|
56030
|
-
"contact:dialInfo": dialInfo,
|
|
56031
54875
|
"contact:pin": pin,
|
|
56032
54876
|
"contact:remove": remove
|
|
56033
54877
|
};
|
|
56034
54878
|
}
|
|
56035
54879
|
|
|
56036
|
-
// src/handlers/contact-
|
|
54880
|
+
// src/handlers/contact-remote-access.ts
|
|
56037
54881
|
init_protocol();
|
|
56038
54882
|
function ensureOwner2(ctx) {
|
|
56039
54883
|
if (!ctx || ctx.principal.kind !== "owner") {
|
|
@@ -56043,8 +54887,7 @@ function ensureOwner2(ctx) {
|
|
|
56043
54887
|
);
|
|
56044
54888
|
}
|
|
56045
54889
|
}
|
|
56046
|
-
function
|
|
56047
|
-
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
54890
|
+
function buildContactRemoteAccessHandlers(deps) {
|
|
56048
54891
|
const setRemoteAccess = async (frame, _client, ctx) => {
|
|
56049
54892
|
ensureOwner2(ctx);
|
|
56050
54893
|
const { type: _t, requestId: _r, ...rest } = frame;
|
|
@@ -56053,19 +54896,11 @@ function buildContactSshHandlers(deps) {
|
|
|
56053
54896
|
remoteAccessAllowed: args.remoteAccessAllowed
|
|
56054
54897
|
});
|
|
56055
54898
|
if (!hit) {
|
|
56056
|
-
sshLog.warn("authz.setRemoteAccess", "owner \u5C1D\u8BD5\u6539 SSH \u6388\u6743\u4F46 contact \u4E0D\u5728 store", {
|
|
56057
|
-
peerDeviceId: args.deviceId
|
|
56058
|
-
});
|
|
56059
54899
|
throw new ClawdError(
|
|
56060
54900
|
ERROR_CODES.CONTACT_NOT_FOUND,
|
|
56061
54901
|
`CONTACT_NOT_FOUND: contact ${args.deviceId} not in store`
|
|
56062
54902
|
);
|
|
56063
54903
|
}
|
|
56064
|
-
rebuildAuthorizedKeys(deps.store, deps.sshdDir);
|
|
56065
|
-
sshLog.info("authz.setRemoteAccess", "owner \u6539\u4E86 SSH \u6388\u6743 \u2192 authorized_keys \u5DF2\u91CD\u5EFA", {
|
|
56066
|
-
peerDeviceId: args.deviceId,
|
|
56067
|
-
remoteAccessAllowed: args.remoteAccessAllowed
|
|
56068
|
-
});
|
|
56069
54904
|
deps.broadcast({
|
|
56070
54905
|
type: "contact:remote-access-updated",
|
|
56071
54906
|
deviceId: args.deviceId,
|
|
@@ -56132,16 +54967,12 @@ function buildWhoamiHandler(deps) {
|
|
|
56132
54967
|
grantedPersonas.push({ id: file.personaId, displayName: file.label });
|
|
56133
54968
|
}
|
|
56134
54969
|
}
|
|
56135
|
-
const sshPublicKey = deps.sshIdentityPublicKey();
|
|
56136
|
-
const osUser = deps.selfOsUser();
|
|
56137
54970
|
return {
|
|
56138
54971
|
response: {
|
|
56139
54972
|
type: "whoami:ok",
|
|
56140
54973
|
owner,
|
|
56141
54974
|
capability,
|
|
56142
|
-
grantedPersonas
|
|
56143
|
-
...sshPublicKey ? { sshPublicKey } : {},
|
|
56144
|
-
...osUser ? { osUser } : {}
|
|
54975
|
+
grantedPersonas
|
|
56145
54976
|
}
|
|
56146
54977
|
};
|
|
56147
54978
|
};
|
|
@@ -56217,10 +55048,7 @@ function buildDeviceHandlers(deps) {
|
|
|
56217
55048
|
// 自动反向(spec 决策 #11):自报本机可达地址,让对方反向加我为联系人;
|
|
56218
55049
|
// 本机无 tunnel → null → connectRemote 省略 selfUrl 字段(不造假数据)。
|
|
56219
55050
|
// 身份不自报:对方从 token 签名背书取我的 deviceId/ownerId/provider(决策 #16)。
|
|
56220
|
-
selfUrl: deps.selfUrl() ?? void 0
|
|
56221
|
-
// 建联交换:自报本机 SSH pubkey + OS 账号名;未生成 → 省略。见 …#建联交换清单
|
|
56222
|
-
selfSshPublicKey: deps.selfSshPublicKey() ?? void 0,
|
|
56223
|
-
selfOsUser: deps.selfOsUser() ?? void 0
|
|
55051
|
+
selfUrl: deps.selfUrl() ?? void 0
|
|
56224
55052
|
});
|
|
56225
55053
|
} catch (e) {
|
|
56226
55054
|
throw new ClawdError(
|
|
@@ -56231,8 +55059,6 @@ function buildDeviceHandlers(deps) {
|
|
|
56231
55059
|
try {
|
|
56232
55060
|
const wh = await conn.whoami();
|
|
56233
55061
|
const existing = deps.store.get(args.deviceId) ?? void 0;
|
|
56234
|
-
const sshPublicKey = wh.sshPublicKey ?? existing?.sshPublicKey;
|
|
56235
|
-
const osUser = wh.osUser ?? existing?.osUser;
|
|
56236
55062
|
const contact = {
|
|
56237
55063
|
deviceId: args.deviceId,
|
|
56238
55064
|
ownerId: wh.ownerId,
|
|
@@ -56248,18 +55074,10 @@ function buildDeviceHandlers(deps) {
|
|
|
56248
55074
|
grants: wh.grants,
|
|
56249
55075
|
addedAt: now(),
|
|
56250
55076
|
pinnedAt: existing?.pinnedAt ?? null,
|
|
56251
|
-
remoteAccessAllowed: existing?.remoteAccessAllowed ?? false
|
|
56252
|
-
// 建联交换:对方 whoami:ok 下发的 pubkey / osUser 落库;对方老版本不带 →
|
|
56253
|
-
// 保留既有值 / 不落 undefined 占位。见 doc/glossary/peer-mesh.md#建联交换清单
|
|
56254
|
-
...sshPublicKey ? { sshPublicKey } : {},
|
|
56255
|
-
...osUser ? { osUser } : {},
|
|
56256
|
-
exposedDirs: []
|
|
55077
|
+
remoteAccessAllowed: existing?.remoteAccessAllowed ?? false
|
|
56257
55078
|
};
|
|
56258
55079
|
deps.store.upsert(contact);
|
|
56259
55080
|
deps.broadcast({ type: "contact:added", contact });
|
|
56260
|
-
if (contact.remoteAccessAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
|
|
56261
|
-
deps.rebuildAuthorizedKeys?.();
|
|
56262
|
-
}
|
|
56263
55081
|
return {
|
|
56264
55082
|
response: {
|
|
56265
55083
|
type: "device:connect:ok",
|
|
@@ -56412,7 +55230,7 @@ function buildPersonaHandlers(deps) {
|
|
|
56412
55230
|
}
|
|
56413
55231
|
|
|
56414
55232
|
// src/handlers/attachment.ts
|
|
56415
|
-
var
|
|
55233
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
56416
55234
|
init_protocol();
|
|
56417
55235
|
init_protocol();
|
|
56418
55236
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -56492,12 +55310,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
56492
55310
|
`session ${args.sessionId} scope unresolved`
|
|
56493
55311
|
);
|
|
56494
55312
|
}
|
|
56495
|
-
const cwdAbs =
|
|
56496
|
-
const candidateAbs =
|
|
55313
|
+
const cwdAbs = import_node_path43.default.resolve(sessionFile.cwd);
|
|
55314
|
+
const candidateAbs = import_node_path43.default.isAbsolute(args.relPath) ? import_node_path43.default.resolve(args.relPath) : import_node_path43.default.resolve(cwdAbs, args.relPath);
|
|
56497
55315
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
|
|
56498
55316
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
56499
55317
|
const entry = entries.find((e) => {
|
|
56500
|
-
const storedAbs =
|
|
55318
|
+
const storedAbs = import_node_path43.default.isAbsolute(e.relPath) ? import_node_path43.default.resolve(e.relPath) : import_node_path43.default.resolve(cwdAbs, e.relPath);
|
|
56501
55319
|
return storedAbs === candidateAbs && !e.stale;
|
|
56502
55320
|
});
|
|
56503
55321
|
if (!entry) {
|
|
@@ -56522,7 +55340,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
56522
55340
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
56523
55341
|
const f = deps.sessionStore.read(sessionId);
|
|
56524
55342
|
if (!f) return;
|
|
56525
|
-
assertGuestAttachmentPath(ctx,
|
|
55343
|
+
assertGuestAttachmentPath(ctx, import_node_path43.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
|
|
56526
55344
|
}
|
|
56527
55345
|
const groupAdd = async (frame, _client, ctx) => {
|
|
56528
55346
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -56537,8 +55355,8 @@ function buildAttachmentHandlers(deps) {
|
|
|
56537
55355
|
if (!scope) {
|
|
56538
55356
|
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
|
|
56539
55357
|
}
|
|
56540
|
-
const cwdAbs =
|
|
56541
|
-
const candidateAbs =
|
|
55358
|
+
const cwdAbs = import_node_path43.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
|
|
55359
|
+
const candidateAbs = import_node_path43.default.isAbsolute(args.relPath) ? import_node_path43.default.resolve(args.relPath) : import_node_path43.default.resolve(cwdAbs, args.relPath);
|
|
56542
55360
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
|
|
56543
55361
|
const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
|
|
56544
55362
|
const size = 0;
|
|
@@ -56597,19 +55415,19 @@ function buildAttachmentHandlers(deps) {
|
|
|
56597
55415
|
|
|
56598
55416
|
// src/handlers/extension.ts
|
|
56599
55417
|
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
56600
|
-
var
|
|
55418
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
56601
55419
|
init_protocol();
|
|
56602
55420
|
|
|
56603
55421
|
// src/extension/bundle-zip.ts
|
|
56604
55422
|
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
56605
|
-
var
|
|
55423
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
56606
55424
|
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
56607
55425
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
56608
55426
|
async function bundleExtensionDir(dir) {
|
|
56609
55427
|
const entries = await listFilesSorted(dir);
|
|
56610
55428
|
const zip = new import_jszip2.default();
|
|
56611
55429
|
for (const rel of entries) {
|
|
56612
|
-
const abs =
|
|
55430
|
+
const abs = import_node_path44.default.join(dir, rel);
|
|
56613
55431
|
const content = await import_promises5.default.readFile(abs);
|
|
56614
55432
|
zip.file(rel, content, { date: FIXED_DATE });
|
|
56615
55433
|
}
|
|
@@ -56630,7 +55448,7 @@ async function listFilesSorted(rootDir) {
|
|
|
56630
55448
|
return out;
|
|
56631
55449
|
}
|
|
56632
55450
|
async function walk(absRoot, relPrefix, out) {
|
|
56633
|
-
const dirAbs =
|
|
55451
|
+
const dirAbs = import_node_path44.default.join(absRoot, relPrefix);
|
|
56634
55452
|
const entries = await import_promises5.default.readdir(dirAbs, { withFileTypes: true });
|
|
56635
55453
|
for (const e of entries) {
|
|
56636
55454
|
if (IGNORE_BASENAMES.has(e.name)) continue;
|
|
@@ -56684,25 +55502,25 @@ function computePublishCheck(args) {
|
|
|
56684
55502
|
|
|
56685
55503
|
// src/extension/install-flow.ts
|
|
56686
55504
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
56687
|
-
var
|
|
55505
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
56688
55506
|
var import_node_os19 = __toESM(require("os"), 1);
|
|
56689
55507
|
var import_node_crypto15 = __toESM(require("crypto"), 1);
|
|
56690
55508
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
56691
55509
|
|
|
56692
55510
|
// src/extension/paths.ts
|
|
56693
55511
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
56694
|
-
var
|
|
55512
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
56695
55513
|
function clawdHomeRoot(override) {
|
|
56696
|
-
return override ?? process.env.CLAWD_HOME ??
|
|
55514
|
+
return override ?? process.env.CLAWD_HOME ?? import_node_path45.default.join(import_node_os18.default.homedir(), ".clawd");
|
|
56697
55515
|
}
|
|
56698
55516
|
function extensionsRoot(override) {
|
|
56699
|
-
return
|
|
55517
|
+
return import_node_path45.default.join(clawdHomeRoot(override), "extensions");
|
|
56700
55518
|
}
|
|
56701
55519
|
function publishedChannelsFile(override) {
|
|
56702
|
-
return
|
|
55520
|
+
return import_node_path45.default.join(clawdHomeRoot(override), "extensions-published.json");
|
|
56703
55521
|
}
|
|
56704
55522
|
function bundleCacheRoot(override) {
|
|
56705
|
-
return
|
|
55523
|
+
return import_node_path45.default.join(clawdHomeRoot(override), "extension-bundles");
|
|
56706
55524
|
}
|
|
56707
55525
|
|
|
56708
55526
|
// src/extension/install-flow.ts
|
|
@@ -56729,7 +55547,7 @@ async function installFromChannel(args, deps) {
|
|
|
56729
55547
|
throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56730
55548
|
}
|
|
56731
55549
|
for (const name of Object.keys(zip.files)) {
|
|
56732
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
55550
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path46.default.isAbsolute(name)) {
|
|
56733
55551
|
throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56734
55552
|
}
|
|
56735
55553
|
}
|
|
@@ -56761,7 +55579,7 @@ async function installFromChannel(args, deps) {
|
|
|
56761
55579
|
);
|
|
56762
55580
|
}
|
|
56763
55581
|
const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
|
|
56764
|
-
const destDir =
|
|
55582
|
+
const destDir = import_node_path46.default.join(deps.extensionsRoot, localExtId);
|
|
56765
55583
|
let destExists = false;
|
|
56766
55584
|
try {
|
|
56767
55585
|
await import_promises6.default.access(destDir);
|
|
@@ -56775,16 +55593,16 @@ async function installFromChannel(args, deps) {
|
|
|
56775
55593
|
);
|
|
56776
55594
|
}
|
|
56777
55595
|
const stage = await import_promises6.default.mkdtemp(
|
|
56778
|
-
|
|
55596
|
+
import_node_path46.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
|
|
56779
55597
|
);
|
|
56780
55598
|
try {
|
|
56781
55599
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56782
|
-
const dest =
|
|
55600
|
+
const dest = import_node_path46.default.join(stage, name);
|
|
56783
55601
|
if (entry.dir) {
|
|
56784
55602
|
await import_promises6.default.mkdir(dest, { recursive: true });
|
|
56785
55603
|
continue;
|
|
56786
55604
|
}
|
|
56787
|
-
await import_promises6.default.mkdir(
|
|
55605
|
+
await import_promises6.default.mkdir(import_node_path46.default.dirname(dest), { recursive: true });
|
|
56788
55606
|
if (name === "manifest.json") {
|
|
56789
55607
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56790
55608
|
await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -56805,7 +55623,7 @@ async function installFromChannel(args, deps) {
|
|
|
56805
55623
|
|
|
56806
55624
|
// src/extension/update-flow.ts
|
|
56807
55625
|
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
56808
|
-
var
|
|
55626
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
56809
55627
|
var import_node_os20 = __toESM(require("os"), 1);
|
|
56810
55628
|
var import_node_crypto16 = __toESM(require("crypto"), 1);
|
|
56811
55629
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
@@ -56822,11 +55640,11 @@ async function updateFromChannel(args, deps) {
|
|
|
56822
55640
|
channelRef.extId,
|
|
56823
55641
|
channelRef.ownerPrincipalId
|
|
56824
55642
|
);
|
|
56825
|
-
const liveDir =
|
|
55643
|
+
const liveDir = import_node_path47.default.join(deps.extensionsRoot, localExtId);
|
|
56826
55644
|
const prevDir = `${liveDir}.prev`;
|
|
56827
55645
|
let existingVersion;
|
|
56828
55646
|
try {
|
|
56829
|
-
const raw = await import_promises7.default.readFile(
|
|
55647
|
+
const raw = await import_promises7.default.readFile(import_node_path47.default.join(liveDir, "manifest.json"), "utf8");
|
|
56830
55648
|
const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
|
|
56831
55649
|
if (!parsed2.success) {
|
|
56832
55650
|
throw new UpdateError(
|
|
@@ -56859,7 +55677,7 @@ async function updateFromChannel(args, deps) {
|
|
|
56859
55677
|
throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56860
55678
|
}
|
|
56861
55679
|
for (const name of Object.keys(zip.files)) {
|
|
56862
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
55680
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path47.default.isAbsolute(name)) {
|
|
56863
55681
|
throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56864
55682
|
}
|
|
56865
55683
|
}
|
|
@@ -56894,16 +55712,16 @@ async function updateFromChannel(args, deps) {
|
|
|
56894
55712
|
await import_promises7.default.rm(prevDir, { recursive: true, force: true });
|
|
56895
55713
|
await import_promises7.default.rename(liveDir, prevDir);
|
|
56896
55714
|
const stage = await import_promises7.default.mkdtemp(
|
|
56897
|
-
|
|
55715
|
+
import_node_path47.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
|
|
56898
55716
|
);
|
|
56899
55717
|
try {
|
|
56900
55718
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56901
|
-
const dest =
|
|
55719
|
+
const dest = import_node_path47.default.join(stage, name);
|
|
56902
55720
|
if (entry.dir) {
|
|
56903
55721
|
await import_promises7.default.mkdir(dest, { recursive: true });
|
|
56904
55722
|
continue;
|
|
56905
55723
|
}
|
|
56906
|
-
await import_promises7.default.mkdir(
|
|
55724
|
+
await import_promises7.default.mkdir(import_node_path47.default.dirname(dest), { recursive: true });
|
|
56907
55725
|
if (name === "manifest.json") {
|
|
56908
55726
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56909
55727
|
await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -56996,7 +55814,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
56996
55814
|
);
|
|
56997
55815
|
}
|
|
56998
55816
|
}
|
|
56999
|
-
const manifestPath =
|
|
55817
|
+
const manifestPath = import_node_path48.default.join(root, extId, "manifest.json");
|
|
57000
55818
|
const manifest = await readManifest(root, extId);
|
|
57001
55819
|
const next = { ...manifest, version: newVersion };
|
|
57002
55820
|
const tmp = `${manifestPath}.tmp`;
|
|
@@ -57004,7 +55822,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
57004
55822
|
await import_promises8.default.rename(tmp, manifestPath);
|
|
57005
55823
|
}
|
|
57006
55824
|
async function readManifest(root, extId) {
|
|
57007
|
-
const file =
|
|
55825
|
+
const file = import_node_path48.default.join(root, extId, "manifest.json");
|
|
57008
55826
|
let raw;
|
|
57009
55827
|
try {
|
|
57010
55828
|
raw = await import_promises8.default.readFile(file, "utf8");
|
|
@@ -57095,7 +55913,7 @@ function buildExtensionHandlers(deps) {
|
|
|
57095
55913
|
};
|
|
57096
55914
|
async function buildSnapshotMeta(extId) {
|
|
57097
55915
|
const manifest = await readManifest(deps.root, extId);
|
|
57098
|
-
const { sha256, buffer } = await bundleExtensionDir(
|
|
55916
|
+
const { sha256, buffer } = await bundleExtensionDir(import_node_path48.default.join(deps.root, extId));
|
|
57099
55917
|
return { manifest, contentHash: sha256, buffer };
|
|
57100
55918
|
}
|
|
57101
55919
|
const publish = async (frame, _client, ctx) => {
|
|
@@ -57276,9 +56094,9 @@ function buildExtensionHandlers(deps) {
|
|
|
57276
56094
|
}
|
|
57277
56095
|
|
|
57278
56096
|
// src/app-builder/project-store.ts
|
|
57279
|
-
var
|
|
57280
|
-
var
|
|
57281
|
-
var
|
|
56097
|
+
var import_node_fs42 = require("fs");
|
|
56098
|
+
var import_node_child_process11 = require("child_process");
|
|
56099
|
+
var import_node_path49 = require("path");
|
|
57282
56100
|
init_protocol();
|
|
57283
56101
|
var PROJECTS_DIR = "projects";
|
|
57284
56102
|
var META_FILE = ".clawd-project.json";
|
|
@@ -57292,19 +56110,19 @@ var ProjectStore = class {
|
|
|
57292
56110
|
root;
|
|
57293
56111
|
/** projects/<name>/.clawd-project.json 路径 */
|
|
57294
56112
|
metaPath(name) {
|
|
57295
|
-
return (0,
|
|
56113
|
+
return (0, import_node_path49.join)(this.projectsRoot(), name, META_FILE);
|
|
57296
56114
|
}
|
|
57297
56115
|
/** projects/<name>/ 目录路径(cwd 用) */
|
|
57298
56116
|
projectDir(name) {
|
|
57299
|
-
return (0,
|
|
56117
|
+
return (0, import_node_path49.join)(this.projectsRoot(), name);
|
|
57300
56118
|
}
|
|
57301
56119
|
projectsRoot() {
|
|
57302
|
-
return (0,
|
|
56120
|
+
return (0, import_node_path49.join)(this.root, PROJECTS_DIR);
|
|
57303
56121
|
}
|
|
57304
56122
|
async list() {
|
|
57305
56123
|
let entries;
|
|
57306
56124
|
try {
|
|
57307
|
-
entries = await
|
|
56125
|
+
entries = await import_node_fs42.promises.readdir(this.projectsRoot());
|
|
57308
56126
|
} catch (err) {
|
|
57309
56127
|
if (err.code === "ENOENT") return [];
|
|
57310
56128
|
throw err;
|
|
@@ -57312,7 +56130,7 @@ var ProjectStore = class {
|
|
|
57312
56130
|
const out = [];
|
|
57313
56131
|
for (const name of entries) {
|
|
57314
56132
|
try {
|
|
57315
|
-
const raw = await
|
|
56133
|
+
const raw = await import_node_fs42.promises.readFile(this.metaPath(name), "utf8");
|
|
57316
56134
|
const json = JSON.parse(raw);
|
|
57317
56135
|
let migrated = false;
|
|
57318
56136
|
if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
|
|
@@ -57323,7 +56141,7 @@ var ProjectStore = class {
|
|
|
57323
56141
|
if (parsed.success) {
|
|
57324
56142
|
out.push(parsed.data);
|
|
57325
56143
|
if (migrated) {
|
|
57326
|
-
void
|
|
56144
|
+
void import_node_fs42.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
|
|
57327
56145
|
});
|
|
57328
56146
|
}
|
|
57329
56147
|
}
|
|
@@ -57367,8 +56185,8 @@ var ProjectStore = class {
|
|
|
57367
56185
|
throw new Error(`invalid name "${name}": ${validated.error.message}`);
|
|
57368
56186
|
}
|
|
57369
56187
|
const dir = this.projectDir(name);
|
|
57370
|
-
await
|
|
57371
|
-
await
|
|
56188
|
+
await import_node_fs42.promises.mkdir(dir, { recursive: true });
|
|
56189
|
+
await import_node_fs42.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
|
|
57372
56190
|
return meta;
|
|
57373
56191
|
}
|
|
57374
56192
|
/**
|
|
@@ -57382,7 +56200,7 @@ var ProjectStore = class {
|
|
|
57382
56200
|
async scaffold(name, templateSrcDir, scaffoldScriptPath) {
|
|
57383
56201
|
const destDir = this.projectDir(name);
|
|
57384
56202
|
return await new Promise((resolve6, reject) => {
|
|
57385
|
-
const child = (0,
|
|
56203
|
+
const child = (0, import_node_child_process11.spawn)("bash", [scaffoldScriptPath, name, templateSrcDir, destDir], {
|
|
57386
56204
|
env: { ...process.env, PATH: process.env.PATH ?? "" },
|
|
57387
56205
|
stdio: ["ignore", "pipe", "pipe"]
|
|
57388
56206
|
});
|
|
@@ -57411,7 +56229,7 @@ var ProjectStore = class {
|
|
|
57411
56229
|
}
|
|
57412
56230
|
async delete(name) {
|
|
57413
56231
|
const dir = this.projectDir(name);
|
|
57414
|
-
await
|
|
56232
|
+
await import_node_fs42.promises.rm(dir, { recursive: true, force: true });
|
|
57415
56233
|
}
|
|
57416
56234
|
async updatePort(name, newPort) {
|
|
57417
56235
|
if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
|
|
@@ -57427,7 +56245,7 @@ var ProjectStore = class {
|
|
|
57427
56245
|
throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
|
|
57428
56246
|
}
|
|
57429
56247
|
const updated = { ...target, port: newPort };
|
|
57430
|
-
await
|
|
56248
|
+
await import_node_fs42.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
57431
56249
|
return updated;
|
|
57432
56250
|
}
|
|
57433
56251
|
/**
|
|
@@ -57444,7 +56262,7 @@ var ProjectStore = class {
|
|
|
57444
56262
|
if (!validated.success) {
|
|
57445
56263
|
throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
|
|
57446
56264
|
}
|
|
57447
|
-
await
|
|
56265
|
+
await import_node_fs42.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57448
56266
|
return validated.data;
|
|
57449
56267
|
}
|
|
57450
56268
|
/**
|
|
@@ -57465,7 +56283,7 @@ var ProjectStore = class {
|
|
|
57465
56283
|
if (!validated.success) {
|
|
57466
56284
|
throw new Error(`invalid publishJob: ${validated.error.message}`);
|
|
57467
56285
|
}
|
|
57468
|
-
await
|
|
56286
|
+
await import_node_fs42.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57469
56287
|
return validated.data;
|
|
57470
56288
|
}
|
|
57471
56289
|
/** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
|
|
@@ -57480,13 +56298,13 @@ var ProjectStore = class {
|
|
|
57480
56298
|
if (!validated.success) {
|
|
57481
56299
|
throw new Error(`failed to clear publishJob: ${validated.error.message}`);
|
|
57482
56300
|
}
|
|
57483
|
-
await
|
|
56301
|
+
await import_node_fs42.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57484
56302
|
return validated.data;
|
|
57485
56303
|
}
|
|
57486
56304
|
};
|
|
57487
56305
|
|
|
57488
56306
|
// src/app-builder/kill-port.ts
|
|
57489
|
-
var
|
|
56307
|
+
var import_node_child_process12 = require("child_process");
|
|
57490
56308
|
async function killPortOccupants(port, ownedPids, logger) {
|
|
57491
56309
|
let pids;
|
|
57492
56310
|
try {
|
|
@@ -57528,7 +56346,7 @@ async function killPortOccupants(port, ownedPids, logger) {
|
|
|
57528
56346
|
}
|
|
57529
56347
|
function listPidsOnPort(port) {
|
|
57530
56348
|
return new Promise((resolve6, reject) => {
|
|
57531
|
-
(0,
|
|
56349
|
+
(0, import_node_child_process12.execFile)(
|
|
57532
56350
|
"lsof",
|
|
57533
56351
|
["-ti", `:${port}`],
|
|
57534
56352
|
{ timeout: 3e3 },
|
|
@@ -57600,9 +56418,9 @@ var PublishJobRegistry = class {
|
|
|
57600
56418
|
};
|
|
57601
56419
|
|
|
57602
56420
|
// src/app-builder/publish-job-runner.ts
|
|
57603
|
-
var
|
|
57604
|
-
var
|
|
57605
|
-
var
|
|
56421
|
+
var import_node_child_process13 = require("child_process");
|
|
56422
|
+
var import_node_fs43 = require("fs");
|
|
56423
|
+
var import_node_path50 = require("path");
|
|
57606
56424
|
|
|
57607
56425
|
// src/app-builder/publish-stage-parser.ts
|
|
57608
56426
|
var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
|
|
@@ -57629,19 +56447,19 @@ function tailStderrLines(buf, n) {
|
|
|
57629
56447
|
// src/app-builder/publish-job-runner.ts
|
|
57630
56448
|
async function startPublishJob(deps, args) {
|
|
57631
56449
|
const { registry: registry2, projectDir } = deps;
|
|
57632
|
-
const
|
|
56450
|
+
const spawn13 = deps.spawnImpl ?? import_node_child_process13.spawn;
|
|
57633
56451
|
if (registry2.has(args.name)) {
|
|
57634
56452
|
return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
|
|
57635
56453
|
}
|
|
57636
56454
|
const projDir = projectDir(args.name);
|
|
57637
|
-
const logPath = (0,
|
|
56455
|
+
const logPath = (0, import_node_path50.join)(projDir, ".publish.log");
|
|
57638
56456
|
let logStream = null;
|
|
57639
56457
|
try {
|
|
57640
|
-
logStream = (0,
|
|
56458
|
+
logStream = (0, import_node_fs43.createWriteStream)(logPath, { flags: "w" });
|
|
57641
56459
|
} catch {
|
|
57642
56460
|
logStream = null;
|
|
57643
56461
|
}
|
|
57644
|
-
const child =
|
|
56462
|
+
const child = spawn13("bash", [args.scriptPath, projDir, args.personaRoot ?? ""], {
|
|
57645
56463
|
cwd: projDir,
|
|
57646
56464
|
env: process.env,
|
|
57647
56465
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -57894,8 +56712,8 @@ async function recoverInterruptedJobs(deps) {
|
|
|
57894
56712
|
|
|
57895
56713
|
// src/handlers/app-builder.ts
|
|
57896
56714
|
init_protocol();
|
|
57897
|
-
var
|
|
57898
|
-
var
|
|
56715
|
+
var import_node_path51 = require("path");
|
|
56716
|
+
var import_node_fs44 = require("fs");
|
|
57899
56717
|
var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
|
|
57900
56718
|
var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
|
|
57901
56719
|
async function recoverInterruptedPublishJobs(store, logger) {
|
|
@@ -57976,7 +56794,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
57976
56794
|
async function listAllUsersProjects() {
|
|
57977
56795
|
if (!deps.usersRoot || !deps.getStore) return [];
|
|
57978
56796
|
const getStore = deps.getStore;
|
|
57979
|
-
const userIds = await
|
|
56797
|
+
const userIds = await import_node_fs44.promises.readdir(deps.usersRoot).catch(() => []);
|
|
57980
56798
|
const perUser = await Promise.all(
|
|
57981
56799
|
userIds.map((uid) => getStore(uid).list().catch(() => []))
|
|
57982
56800
|
);
|
|
@@ -58052,8 +56870,8 @@ function buildAppBuilderHandlers(deps) {
|
|
|
58052
56870
|
const project = await userStore.create(f.name, reservedPorts);
|
|
58053
56871
|
try {
|
|
58054
56872
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
|
|
58055
|
-
const templateSrcDir = (0,
|
|
58056
|
-
const scaffoldScript = (0,
|
|
56873
|
+
const templateSrcDir = (0, import_node_path51.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
|
|
56874
|
+
const scaffoldScript = (0, import_node_path51.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
|
|
58057
56875
|
const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
|
|
58058
56876
|
deps.logger?.info("app-builder.scaffold.done", {
|
|
58059
56877
|
name: project.name,
|
|
@@ -58274,7 +57092,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
58274
57092
|
await userStore.clearPublishJob(args.name);
|
|
58275
57093
|
}
|
|
58276
57094
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
|
|
58277
|
-
const scriptPath = (0,
|
|
57095
|
+
const scriptPath = (0, import_node_path51.join)(deps.deployKitRoot, "scripts", "publish.sh");
|
|
58278
57096
|
deps.logger?.info("app-builder.publish.start", {
|
|
58279
57097
|
name: args.name,
|
|
58280
57098
|
sessionId: boundSession.sessionId,
|
|
@@ -58450,7 +57268,7 @@ function buildVisitorHandlers(deps) {
|
|
|
58450
57268
|
|
|
58451
57269
|
// src/extension/registry.ts
|
|
58452
57270
|
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
58453
|
-
var
|
|
57271
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
58454
57272
|
async function loadAll(root) {
|
|
58455
57273
|
let entries;
|
|
58456
57274
|
try {
|
|
@@ -58463,13 +57281,13 @@ async function loadAll(root) {
|
|
|
58463
57281
|
for (const ent of entries) {
|
|
58464
57282
|
if (!ent.isDirectory()) continue;
|
|
58465
57283
|
if (ent.name.startsWith(".")) continue;
|
|
58466
|
-
records.push(await loadOne(
|
|
57284
|
+
records.push(await loadOne(import_node_path52.default.join(root, ent.name), ent.name));
|
|
58467
57285
|
}
|
|
58468
57286
|
records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
|
|
58469
57287
|
return records;
|
|
58470
57288
|
}
|
|
58471
57289
|
async function loadOne(dir, dirName) {
|
|
58472
|
-
const manifestPath =
|
|
57290
|
+
const manifestPath = import_node_path52.default.join(dir, "manifest.json");
|
|
58473
57291
|
let raw;
|
|
58474
57292
|
try {
|
|
58475
57293
|
raw = await import_promises9.default.readFile(manifestPath, "utf8");
|
|
@@ -58514,7 +57332,7 @@ async function loadOne(dir, dirName) {
|
|
|
58514
57332
|
|
|
58515
57333
|
// src/extension/uninstall.ts
|
|
58516
57334
|
var import_promises10 = __toESM(require("fs/promises"), 1);
|
|
58517
|
-
var
|
|
57335
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
58518
57336
|
var UninstallError = class extends Error {
|
|
58519
57337
|
constructor(code, message) {
|
|
58520
57338
|
super(message);
|
|
@@ -58523,7 +57341,7 @@ var UninstallError = class extends Error {
|
|
|
58523
57341
|
code;
|
|
58524
57342
|
};
|
|
58525
57343
|
async function uninstall(deps) {
|
|
58526
|
-
const dir =
|
|
57344
|
+
const dir = import_node_path53.default.join(deps.root, deps.extId);
|
|
58527
57345
|
try {
|
|
58528
57346
|
await import_promises10.default.access(dir);
|
|
58529
57347
|
} catch {
|
|
@@ -58540,7 +57358,7 @@ var import_node_crypto18 = require("crypto");
|
|
|
58540
57358
|
init_protocol();
|
|
58541
57359
|
|
|
58542
57360
|
// src/peer-ops/run-command.ts
|
|
58543
|
-
var
|
|
57361
|
+
var import_node_child_process14 = require("child_process");
|
|
58544
57362
|
var import_node_os21 = __toESM(require("os"), 1);
|
|
58545
57363
|
var DEFAULT_TIMEOUT_MS5 = 3e4;
|
|
58546
57364
|
var MAX_TIMEOUT_MS = 3e5;
|
|
@@ -58548,7 +57366,7 @@ var MAX_OUTPUT_BYTES = 1048576;
|
|
|
58548
57366
|
function runCommand(command, opts = {}) {
|
|
58549
57367
|
const timeoutMs = Math.min(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS5, MAX_TIMEOUT_MS);
|
|
58550
57368
|
return new Promise((resolve6) => {
|
|
58551
|
-
const child = (0,
|
|
57369
|
+
const child = (0, import_node_child_process14.spawn)("bash", ["-c", command], { cwd: import_node_os21.default.homedir() });
|
|
58552
57370
|
const out = [];
|
|
58553
57371
|
const err = [];
|
|
58554
57372
|
let outBytes = 0;
|
|
@@ -58703,11 +57521,9 @@ function buildMethodHandlers(deps) {
|
|
|
58703
57521
|
broadcast: deps.broadcastToOwners,
|
|
58704
57522
|
now: () => Date.now()
|
|
58705
57523
|
}),
|
|
58706
|
-
...
|
|
57524
|
+
...buildContactRemoteAccessHandlers({
|
|
58707
57525
|
store: deps.contactStore,
|
|
58708
|
-
broadcast: deps.broadcastToOwners
|
|
58709
|
-
sshdDir: deps.sshdDir,
|
|
58710
|
-
sshLog: deps.contactSshLog
|
|
57526
|
+
broadcast: deps.broadcastToOwners
|
|
58711
57527
|
}),
|
|
58712
57528
|
// 跨设备 exec(spec 2026-07-16):A 侧带 peerDeviceId → forwardPeerExecToPeer;
|
|
58713
57529
|
// B 侧无 peerDeviceId → 本地 runCommand(handler 判 remoteAccessAllowed)。
|
|
@@ -58721,9 +57537,7 @@ function buildMethodHandlers(deps) {
|
|
|
58721
57537
|
ownerId: deps.ownerId,
|
|
58722
57538
|
ownerProvider: deps.ownerProvider,
|
|
58723
57539
|
personaStore: deps.personaStore,
|
|
58724
|
-
capabilityManager: deps.capabilityManager
|
|
58725
|
-
sshIdentityPublicKey: deps.sshIdentityPublicKey,
|
|
58726
|
-
selfOsUser: deps.selfOsUser
|
|
57540
|
+
capabilityManager: deps.capabilityManager
|
|
58727
57541
|
}),
|
|
58728
57542
|
...buildFeishuAuthHandlers(deps.feishuAuth),
|
|
58729
57543
|
...buildDeviceHandlers(deps.feishuDevice),
|
|
@@ -58766,7 +57580,7 @@ function buildMethodHandlers(deps) {
|
|
|
58766
57580
|
}
|
|
58767
57581
|
|
|
58768
57582
|
// src/app-builder/dev-server-supervisor.ts
|
|
58769
|
-
var
|
|
57583
|
+
var import_node_child_process15 = require("child_process");
|
|
58770
57584
|
var import_node_events2 = require("events");
|
|
58771
57585
|
var DEFAULT_READY_PATTERN = /Local:\s+https?:\/\/|Nest application successfully started|server listening on/i;
|
|
58772
57586
|
var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
@@ -58803,7 +57617,7 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
|
58803
57617
|
tunnelHost: args.tunnelHost,
|
|
58804
57618
|
devCommand: cmd
|
|
58805
57619
|
});
|
|
58806
|
-
const child = (0,
|
|
57620
|
+
const child = (0, import_node_child_process15.spawn)("sh", ["-c", cmd], {
|
|
58807
57621
|
cwd: args.cwd,
|
|
58808
57622
|
env,
|
|
58809
57623
|
stdio: "pipe",
|
|
@@ -59055,7 +57869,7 @@ var METHOD_GRANT_MAP = {
|
|
|
59055
57869
|
"contact:list": ADMIN_ANY,
|
|
59056
57870
|
"contact:pin": ADMIN_ANY,
|
|
59057
57871
|
"contact:remove": ADMIN_ANY,
|
|
59058
|
-
// contact:setRemoteAccess (owner UI
|
|
57872
|
+
// contact:setRemoteAccess (owner UI 配远程访问授权):ADMIN_ANY
|
|
59059
57873
|
// (对齐 inbox:postMessage 的"能连上=有 auth,业务在 handler 校"模式)
|
|
59060
57874
|
"contact:setRemoteAccess": ADMIN_ANY,
|
|
59061
57875
|
// ---- visitor:* (访客名单,owner-only) ----
|
|
@@ -59245,12 +58059,12 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
|
|
|
59245
58059
|
}
|
|
59246
58060
|
|
|
59247
58061
|
// src/extension/runtime.ts
|
|
59248
|
-
var
|
|
59249
|
-
var
|
|
58062
|
+
var import_node_child_process16 = require("child_process");
|
|
58063
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
59250
58064
|
var import_promises11 = require("timers/promises");
|
|
59251
58065
|
|
|
59252
58066
|
// src/extension/port-allocator.ts
|
|
59253
|
-
var
|
|
58067
|
+
var import_node_net2 = __toESM(require("net"), 1);
|
|
59254
58068
|
var PortExhaustedError = class extends Error {
|
|
59255
58069
|
constructor(min, max) {
|
|
59256
58070
|
super(`no free port in [${min},${max}]`);
|
|
@@ -59263,7 +58077,7 @@ var PortExhaustedError = class extends Error {
|
|
|
59263
58077
|
};
|
|
59264
58078
|
function probe(port) {
|
|
59265
58079
|
return new Promise((resolve6) => {
|
|
59266
|
-
const srv =
|
|
58080
|
+
const srv = import_node_net2.default.createServer();
|
|
59267
58081
|
srv.once("error", () => resolve6(false));
|
|
59268
58082
|
srv.once("listening", () => {
|
|
59269
58083
|
srv.close(() => resolve6(true));
|
|
@@ -59347,13 +58161,13 @@ var Runtime = class {
|
|
|
59347
58161
|
/\$CLAWOS_EXT_PORT/g,
|
|
59348
58162
|
String(port)
|
|
59349
58163
|
);
|
|
59350
|
-
const dir =
|
|
58164
|
+
const dir = import_node_path54.default.join(this.root, extId);
|
|
59351
58165
|
const env = {
|
|
59352
58166
|
...process.env,
|
|
59353
58167
|
CLAWOS_EXT_PORT: String(port),
|
|
59354
58168
|
CLAWOS_EXT_ID: extId
|
|
59355
58169
|
};
|
|
59356
|
-
const child = (0,
|
|
58170
|
+
const child = (0, import_node_child_process16.spawn)("sh", ["-c", cmd], {
|
|
59357
58171
|
cwd: dir,
|
|
59358
58172
|
env,
|
|
59359
58173
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -59459,7 +58273,7 @@ ${handle.stderrTail}`
|
|
|
59459
58273
|
|
|
59460
58274
|
// src/extension/published-channels.ts
|
|
59461
58275
|
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
59462
|
-
var
|
|
58276
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
59463
58277
|
init_zod();
|
|
59464
58278
|
var PublishedChannelsError = class extends Error {
|
|
59465
58279
|
constructor(code, message) {
|
|
@@ -59558,7 +58372,7 @@ var PublishedChannelStore = class {
|
|
|
59558
58372
|
)
|
|
59559
58373
|
};
|
|
59560
58374
|
const tmp = `${this.filePath}.tmp`;
|
|
59561
|
-
await import_promises12.default.mkdir(
|
|
58375
|
+
await import_promises12.default.mkdir(import_node_path55.default.dirname(this.filePath), { recursive: true });
|
|
59562
58376
|
await import_promises12.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
59563
58377
|
await import_promises12.default.rename(tmp, this.filePath);
|
|
59564
58378
|
}
|
|
@@ -59566,7 +58380,7 @@ var PublishedChannelStore = class {
|
|
|
59566
58380
|
|
|
59567
58381
|
// src/extension/bundle-cache.ts
|
|
59568
58382
|
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
59569
|
-
var
|
|
58383
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
59570
58384
|
var BundleCache = class {
|
|
59571
58385
|
constructor(rootDir) {
|
|
59572
58386
|
this.rootDir = rootDir;
|
|
@@ -59575,14 +58389,14 @@ var BundleCache = class {
|
|
|
59575
58389
|
/** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
|
|
59576
58390
|
async write(snapshotHash, buffer) {
|
|
59577
58391
|
await import_promises13.default.mkdir(this.rootDir, { recursive: true });
|
|
59578
|
-
const file =
|
|
58392
|
+
const file = import_node_path56.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59579
58393
|
const tmp = `${file}.tmp`;
|
|
59580
58394
|
await import_promises13.default.writeFile(tmp, buffer, { mode: 384 });
|
|
59581
58395
|
await import_promises13.default.rename(tmp, file);
|
|
59582
58396
|
}
|
|
59583
58397
|
/** Returns the bundle bytes, or null when the file doesn't exist. */
|
|
59584
58398
|
async read(snapshotHash) {
|
|
59585
|
-
const file =
|
|
58399
|
+
const file = import_node_path56.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59586
58400
|
try {
|
|
59587
58401
|
return await import_promises13.default.readFile(file);
|
|
59588
58402
|
} catch (e) {
|
|
@@ -59592,20 +58406,13 @@ var BundleCache = class {
|
|
|
59592
58406
|
}
|
|
59593
58407
|
/** Idempotent — missing file is not an error. */
|
|
59594
58408
|
async delete(snapshotHash) {
|
|
59595
|
-
const file =
|
|
58409
|
+
const file = import_node_path56.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59596
58410
|
await import_promises13.default.rm(file, { force: true });
|
|
59597
58411
|
}
|
|
59598
58412
|
};
|
|
59599
58413
|
|
|
59600
58414
|
// src/index.ts
|
|
59601
58415
|
var import_meta6 = {};
|
|
59602
|
-
function currentOsUser() {
|
|
59603
|
-
try {
|
|
59604
|
-
return import_node_os22.default.userInfo().username;
|
|
59605
|
-
} catch {
|
|
59606
|
-
return "";
|
|
59607
|
-
}
|
|
59608
|
-
}
|
|
59609
58416
|
async function startDaemon(config) {
|
|
59610
58417
|
const authFile = loadOrCreateAuthFile({ dataDir: config.dataDir });
|
|
59611
58418
|
const logShippingCfg = config.logShipping ?? { endpoint: null, sampling: {}, mode: "off" };
|
|
@@ -59622,24 +58429,24 @@ async function startDaemon(config) {
|
|
|
59622
58429
|
sampling: logShippingCfg.sampling,
|
|
59623
58430
|
homeDir: import_node_os22.default.homedir()
|
|
59624
58431
|
});
|
|
59625
|
-
const logDir =
|
|
59626
|
-
|
|
58432
|
+
const logDir = import_node_path57.default.join(config.dataDir, "log");
|
|
58433
|
+
import_node_fs45.default.mkdirSync(logDir, { recursive: true });
|
|
59627
58434
|
const logger = createLogger({
|
|
59628
58435
|
level: config.logLevel,
|
|
59629
|
-
file:
|
|
58436
|
+
file: import_node_path57.default.join(logDir, "clawd.log"),
|
|
59630
58437
|
logClient
|
|
59631
58438
|
});
|
|
59632
58439
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
59633
58440
|
const screenIdleProbeLogger = createFileOnlyLogger({
|
|
59634
|
-
file:
|
|
58441
|
+
file: import_node_path57.default.join(logDir, "screen-idle-probe.log"),
|
|
59635
58442
|
level: "debug"
|
|
59636
58443
|
});
|
|
59637
58444
|
logger.info("screen-idle probe logger enabled", {
|
|
59638
|
-
file:
|
|
58445
|
+
file: import_node_path57.default.join(logDir, "screen-idle-probe.log")
|
|
59639
58446
|
});
|
|
59640
58447
|
const CAP_TARGETS = [
|
|
59641
|
-
|
|
59642
|
-
|
|
58448
|
+
import_node_path57.default.join(logDir, "clawd.log"),
|
|
58449
|
+
import_node_path57.default.join(logDir, "screen-idle-probe.log")
|
|
59643
58450
|
];
|
|
59644
58451
|
const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
|
|
59645
58452
|
const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
|
|
@@ -59735,7 +58542,7 @@ async function startDaemon(config) {
|
|
|
59735
58542
|
// Task 1.7:authenticate 注入路径替代 expectedToken 单 token 比对。
|
|
59736
58543
|
// owner 路径 constantTimeEqual 防侧信道;guest 路径走 capabilityRegistry.
|
|
59737
58544
|
expectedToken: resolvedAuthToken,
|
|
59738
|
-
authenticate: async (t, selfUrl
|
|
58545
|
+
authenticate: async (t, selfUrl) => {
|
|
59739
58546
|
const result = await authenticate(t, buildConnectAuthDeps());
|
|
59740
58547
|
if (result.ok && result.context.principal.kind === "guest") {
|
|
59741
58548
|
void autoReverseContact({
|
|
@@ -59746,11 +58553,6 @@ async function startDaemon(config) {
|
|
|
59746
58553
|
provider: result.context.provider ?? "",
|
|
59747
58554
|
displayName: result.context.principal.displayName ?? result.context.principal.id,
|
|
59748
58555
|
selfUrl,
|
|
59749
|
-
// 建联交换:guest auth 帧自报的 pubkey / osUser 落 contact;pubkey 变化且已授权时
|
|
59750
|
-
// 立即重建 authorized_keys(重新握手 = 迁移路径)。见 …#建联交换清单
|
|
59751
|
-
sshPublicKey: selfSshPublicKey,
|
|
59752
|
-
osUser: selfOsUser,
|
|
59753
|
-
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path64.default.join(config.dataDir, "sshd")),
|
|
59754
58556
|
// 决策 #16 换票补全:用 owner 当前 jwt 换 aud=对方设备 的票(本机主动连对方用)。
|
|
59755
58557
|
// 现读 ownerIdentityStore(热切换后用新身份);失败 → null → 保持空票。
|
|
59756
58558
|
exchangeToken: async (targetDeviceId) => {
|
|
@@ -59798,8 +58600,8 @@ async function startDaemon(config) {
|
|
|
59798
58600
|
const agents = new AgentsScanner();
|
|
59799
58601
|
const history = new ClaudeHistoryReader();
|
|
59800
58602
|
let transport = null;
|
|
59801
|
-
const personaStore = new PersonaStore(
|
|
59802
|
-
const usersRoot =
|
|
58603
|
+
const personaStore = new PersonaStore(import_node_path57.default.join(config.dataDir, "personas"));
|
|
58604
|
+
const usersRoot = import_node_path57.default.join(config.dataDir, "users");
|
|
59803
58605
|
const defaultsRoot = findDefaultsRoot(logger);
|
|
59804
58606
|
if (defaultsRoot) {
|
|
59805
58607
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -59819,17 +58621,17 @@ async function startDaemon(config) {
|
|
|
59819
58621
|
migrateCodexSandbox({ store: personaStore, logger });
|
|
59820
58622
|
const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
|
|
59821
58623
|
const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
|
|
59822
|
-
const here = typeof __dirname === "string" ? __dirname :
|
|
58624
|
+
const here = typeof __dirname === "string" ? __dirname : import_node_path57.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
|
|
59823
58625
|
const mcpConfigs = [];
|
|
59824
58626
|
const dispatchServerCandidates = [
|
|
59825
|
-
|
|
58627
|
+
import_node_path57.default.join(here, "dispatch", "mcp-server.cjs"),
|
|
59826
58628
|
// 生产 dist/index → dist/dispatch/mcp-server.cjs
|
|
59827
|
-
|
|
58629
|
+
import_node_path57.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
|
|
59828
58630
|
// dev tsx src/index → ../dist/dispatch/mcp-server.cjs
|
|
59829
58631
|
];
|
|
59830
|
-
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) =>
|
|
58632
|
+
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
|
|
59831
58633
|
if (dispatchServerScriptPath) {
|
|
59832
|
-
const dispatchLogPath =
|
|
58634
|
+
const dispatchLogPath = import_node_path57.default.join(logDir, "dispatch-mcp-server.log");
|
|
59833
58635
|
const dispatchCfgPath = writeDispatchMcpConfig({
|
|
59834
58636
|
dataDir: config.dataDir,
|
|
59835
58637
|
serverScriptPath: dispatchServerScriptPath,
|
|
@@ -59847,12 +58649,12 @@ async function startDaemon(config) {
|
|
|
59847
58649
|
});
|
|
59848
58650
|
}
|
|
59849
58651
|
const ticketServerCandidates = [
|
|
59850
|
-
|
|
59851
|
-
|
|
58652
|
+
import_node_path57.default.join(here, "ticket", "mcp-server.cjs"),
|
|
58653
|
+
import_node_path57.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
|
|
59852
58654
|
];
|
|
59853
|
-
const ticketServerScriptPath = ticketServerCandidates.find((p2) =>
|
|
58655
|
+
const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
|
|
59854
58656
|
if (ticketServerScriptPath) {
|
|
59855
|
-
const ticketLogPath =
|
|
58657
|
+
const ticketLogPath = import_node_path57.default.join(logDir, "ticket-mcp-server.log");
|
|
59856
58658
|
const ticketCfgPath = writeTicketMcpConfig({
|
|
59857
58659
|
dataDir: config.dataDir,
|
|
59858
58660
|
serverScriptPath: ticketServerScriptPath,
|
|
@@ -59872,12 +58674,12 @@ async function startDaemon(config) {
|
|
|
59872
58674
|
});
|
|
59873
58675
|
}
|
|
59874
58676
|
const shiftServerCandidates = [
|
|
59875
|
-
|
|
59876
|
-
|
|
58677
|
+
import_node_path57.default.join(here, "shift", "mcp-server.cjs"),
|
|
58678
|
+
import_node_path57.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
|
|
59877
58679
|
];
|
|
59878
|
-
const shiftServerScriptPath = shiftServerCandidates.find((p2) =>
|
|
58680
|
+
const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
|
|
59879
58681
|
if (shiftServerScriptPath) {
|
|
59880
|
-
const shiftLogPath =
|
|
58682
|
+
const shiftLogPath = import_node_path57.default.join(logDir, "shift-mcp-server.log");
|
|
59881
58683
|
const shiftCfgPath = await writeShiftMcpConfig({
|
|
59882
58684
|
dataDir: config.dataDir,
|
|
59883
58685
|
serverScriptPath: shiftServerScriptPath,
|
|
@@ -59896,12 +58698,12 @@ async function startDaemon(config) {
|
|
|
59896
58698
|
);
|
|
59897
58699
|
}
|
|
59898
58700
|
const inboxServerCandidates = [
|
|
59899
|
-
|
|
59900
|
-
|
|
58701
|
+
import_node_path57.default.join(here, "inbox", "mcp-server.cjs"),
|
|
58702
|
+
import_node_path57.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
|
|
59901
58703
|
];
|
|
59902
|
-
const inboxServerScriptPath = inboxServerCandidates.find((p2) =>
|
|
58704
|
+
const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
|
|
59903
58705
|
if (inboxServerScriptPath) {
|
|
59904
|
-
const inboxLogPath =
|
|
58706
|
+
const inboxLogPath = import_node_path57.default.join(logDir, "inbox-mcp-server.log");
|
|
59905
58707
|
const inboxCfgPath = await writeInboxMcpConfig({
|
|
59906
58708
|
dataDir: config.dataDir,
|
|
59907
58709
|
serverScriptPath: inboxServerScriptPath,
|
|
@@ -59920,10 +58722,10 @@ async function startDaemon(config) {
|
|
|
59920
58722
|
);
|
|
59921
58723
|
}
|
|
59922
58724
|
const peerOpsServerCandidates = [
|
|
59923
|
-
|
|
59924
|
-
|
|
58725
|
+
import_node_path57.default.join(here, "peer-ops", "mcp-server.cjs"),
|
|
58726
|
+
import_node_path57.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
|
|
59925
58727
|
];
|
|
59926
|
-
const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) =>
|
|
58728
|
+
const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
|
|
59927
58729
|
if (peerOpsServerScriptPath) {
|
|
59928
58730
|
const peerOpsCfgPath = writePeerOpsMcpConfig({
|
|
59929
58731
|
dataDir: config.dataDir,
|
|
@@ -59941,7 +58743,7 @@ async function startDaemon(config) {
|
|
|
59941
58743
|
);
|
|
59942
58744
|
}
|
|
59943
58745
|
const shiftStore = createShiftStore({
|
|
59944
|
-
filePath:
|
|
58746
|
+
filePath: import_node_path57.default.join(config.dataDir, "shift.json"),
|
|
59945
58747
|
ownerIdProvider: () => ownerPrincipalId,
|
|
59946
58748
|
now: () => Date.now()
|
|
59947
58749
|
});
|
|
@@ -59963,7 +58765,7 @@ async function startDaemon(config) {
|
|
|
59963
58765
|
getAdapter,
|
|
59964
58766
|
historyReader: history,
|
|
59965
58767
|
dataDir: config.dataDir,
|
|
59966
|
-
personaRoot:
|
|
58768
|
+
personaRoot: import_node_path57.default.join(config.dataDir, "personas"),
|
|
59967
58769
|
usersRoot,
|
|
59968
58770
|
personaStore,
|
|
59969
58771
|
ownerDisplayName,
|
|
@@ -60001,10 +58803,10 @@ async function startDaemon(config) {
|
|
|
60001
58803
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
60002
58804
|
attachmentGroup: {
|
|
60003
58805
|
onFileEdit: (input) => {
|
|
60004
|
-
const absPath =
|
|
58806
|
+
const absPath = import_node_path57.default.isAbsolute(input.relPath) ? input.relPath : import_node_path57.default.join(input.cwd, input.relPath);
|
|
60005
58807
|
let size = 0;
|
|
60006
58808
|
try {
|
|
60007
|
-
size =
|
|
58809
|
+
size = import_node_fs45.default.statSync(absPath).size;
|
|
60008
58810
|
} catch (err) {
|
|
60009
58811
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
60010
58812
|
sessionId: input.sessionId,
|
|
@@ -60190,14 +58992,6 @@ async function startDaemon(config) {
|
|
|
60190
58992
|
const effectivePreviewPorts = Array.from(
|
|
60191
58993
|
/* @__PURE__ */ new Set([...config.previewPorts ?? [], ...appBuilderPortRange])
|
|
60192
58994
|
);
|
|
60193
|
-
const sshLog = createContactSshLog(config.dataDir);
|
|
60194
|
-
try {
|
|
60195
|
-
await ensureSshIdentity(config.dataDir);
|
|
60196
|
-
} catch (e) {
|
|
60197
|
-
logger.warn("ssh-identity generate failed; contact SSH pubkey exchange disabled", {
|
|
60198
|
-
err: e.message
|
|
60199
|
-
});
|
|
60200
|
-
}
|
|
60201
58995
|
const makeHandlers = () => buildMethodHandlers({
|
|
60202
58996
|
manager,
|
|
60203
58997
|
workspace,
|
|
@@ -60241,11 +59035,11 @@ async function startDaemon(config) {
|
|
|
60241
59035
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
60242
59036
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
60243
59037
|
// guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
|
|
60244
|
-
personaRoot:
|
|
59038
|
+
personaRoot: import_node_path57.default.join(config.dataDir, "personas"),
|
|
60245
59039
|
usersRoot
|
|
60246
59040
|
},
|
|
60247
59041
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
60248
|
-
personaRoot:
|
|
59042
|
+
personaRoot: import_node_path57.default.join(config.dataDir, "personas"),
|
|
60249
59043
|
// v2 多人 persona 隔离:handler 派生 guest user-dir 放行
|
|
60250
59044
|
usersRoot,
|
|
60251
59045
|
// capability:list / delete handler 依赖
|
|
@@ -60258,11 +59052,6 @@ async function startDaemon(config) {
|
|
|
60258
59052
|
ownerFeishuUnionId,
|
|
60259
59053
|
ownerId,
|
|
60260
59054
|
ownerProvider,
|
|
60261
|
-
// ssh identity 互换(spec 2026-07-15):whoami:ok 下发本机 identity pubkey。
|
|
60262
|
-
// 惰性读盘,不依赖 ensureSshIdentity 完成时序(未生成 → null → 帧省略字段)。
|
|
60263
|
-
sshIdentityPublicKey: () => readSshIdentityPublicKey(config.dataDir),
|
|
60264
|
-
// 建联交换:whoami:ok 下发本机 OS 账号名(拨号登录名)。见 doc/glossary/peer-mesh.md#建联交换清单
|
|
60265
|
-
selfOsUser: currentOsUser,
|
|
60266
59055
|
personaStore,
|
|
60267
59056
|
// capability handler 也用 (capability:issue 走 registry / capability:list)
|
|
60268
59057
|
capabilityRegistry,
|
|
@@ -60272,10 +59061,6 @@ async function startDaemon(config) {
|
|
|
60272
59061
|
inboxStore,
|
|
60273
59062
|
// 联系人列表 store(device:connect / 自动反向落同一 store)
|
|
60274
59063
|
contactStore,
|
|
60275
|
-
// <dataDir>/sshd 绝对路径 —— contact-ssh handlers 用它拼 authorized_keys / keys/ 子路径
|
|
60276
|
-
// Task 10 会加 SshdManager 起 sshd;handlers wire 提前挂 sshdDir 让 typecheck 过
|
|
60277
|
-
sshdDir: import_node_path64.default.join(config.dataDir, "sshd"),
|
|
60278
|
-
contactSshLog: sshLog,
|
|
60279
59064
|
// inbox:sendDm 用:sessionId → session 出身(复用 attachment 同款 findOwnedSessionScope)
|
|
60280
59065
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
60281
59066
|
// contact:removed broadcast;复用 capability:tokenIssued 同款通路
|
|
@@ -60342,13 +59127,7 @@ async function startDaemon(config) {
|
|
|
60342
59127
|
// 自动反向(spec 决策 #11):本机可达地址 = tunnel URL(公网),出站连接时
|
|
60343
59128
|
// 透传让对方反向加我为联系人。无 tunnel(仅本机 ws)→ null,对方不自动反向(不造假数据)。
|
|
60344
59129
|
// 决策 #16:身份不自报——本机 deviceId/ownerId/provider 由 exchange 签进 token 背书。
|
|
60345
|
-
selfUrl: () => currentTunnelUrl
|
|
60346
|
-
// ssh identity 互换(spec 2026-07-15):出站 auth 帧自报本机 identity pubkey;
|
|
60347
|
-
// 对方 pubkey 变化且已授权时立即重建 authorized_keys(重新 connect = 迁移路径)
|
|
60348
|
-
selfSshPublicKey: () => readSshIdentityPublicKey(config.dataDir),
|
|
60349
|
-
// 建联交换:出站自报本机 OS 账号名。见 doc/glossary/peer-mesh.md#建联交换清单
|
|
60350
|
-
selfOsUser: currentOsUser,
|
|
60351
|
-
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path64.default.join(config.dataDir, "sshd"))
|
|
59130
|
+
selfUrl: () => currentTunnelUrl
|
|
60352
59131
|
},
|
|
60353
59132
|
// app-builder
|
|
60354
59133
|
appBuilderStore,
|
|
@@ -60371,11 +59150,11 @@ async function startDaemon(config) {
|
|
|
60371
59150
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2):
|
|
60372
59151
|
// appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
|
|
60373
59152
|
// dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
|
|
60374
|
-
appBuilderPersonaRoot:
|
|
59153
|
+
appBuilderPersonaRoot: import_node_path57.default.join(config.dataDir, "personas", "persona-app-builder"),
|
|
60375
59154
|
// 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
|
|
60376
|
-
deployKitRoot:
|
|
59155
|
+
deployKitRoot: import_node_path57.default.join(config.dataDir, "deploy-kit"),
|
|
60377
59156
|
// scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
|
|
60378
|
-
resolvePersonaRoot: (personaId) =>
|
|
59157
|
+
resolvePersonaRoot: (personaId) => import_node_path57.default.join(config.dataDir, "personas", personaId),
|
|
60379
59158
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
|
|
60380
59159
|
// 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
|
|
60381
59160
|
// 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
|
|
@@ -60418,7 +59197,7 @@ async function startDaemon(config) {
|
|
|
60418
59197
|
}
|
|
60419
59198
|
let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
|
|
60420
59199
|
if (sourceFile && sourceFile.toolSessionId) {
|
|
60421
|
-
sourceJsonlPath =
|
|
59200
|
+
sourceJsonlPath = import_node_path57.default.join(
|
|
60422
59201
|
import_node_os22.default.homedir(),
|
|
60423
59202
|
".claude",
|
|
60424
59203
|
"projects",
|
|
@@ -60625,48 +59404,6 @@ async function startDaemon(config) {
|
|
|
60625
59404
|
httpRequestHandler: httpRouter,
|
|
60626
59405
|
// app-builder build 模式:/preview/<port>/ HMR websocket upgrade 转发的端口白名单(同 http-router 配置)
|
|
60627
59406
|
previewPorts: effectivePreviewPorts,
|
|
60628
|
-
// Contact SSH tunnel (PR: contact-ssh-tunnel): /rpc/ssh-tunnel WS 前置拦截 →
|
|
60629
|
-
// Bearer auth (owner token / connect token / capability token 三选一) → net.connect 到本机 sshd →
|
|
60630
|
-
// raw byte relay. 用于 B 侧 `clawd ssh-relay` CLI 拨号(SSH ProxyCommand)
|
|
60631
|
-
sshTunnelUpgradeHandler: async (req, socket, head, wss2) => {
|
|
60632
|
-
await handleSshTunnelUpgrade(req, socket, head, {
|
|
60633
|
-
wss: wss2,
|
|
60634
|
-
sshdPort: config.sshdPort,
|
|
60635
|
-
logger,
|
|
60636
|
-
sshLog,
|
|
60637
|
-
authorize: async (token) => {
|
|
60638
|
-
if (!token) return false;
|
|
60639
|
-
if (resolvedAuthToken && token === resolvedAuthToken) return true;
|
|
60640
|
-
const cap = capabilityRegistry.verifyToken(token);
|
|
60641
|
-
if (cap.ok) return true;
|
|
60642
|
-
const vis = verifyVisitorToken(authFile.visitorTokenSecret, token, Math.floor(Date.now() / 1e3));
|
|
60643
|
-
if (vis.ok) return true;
|
|
60644
|
-
const publicKeyPem = serverKeyStore.read();
|
|
60645
|
-
if (publicKeyPem) {
|
|
60646
|
-
const r = verifyConnectToken({
|
|
60647
|
-
token,
|
|
60648
|
-
publicKeyPem,
|
|
60649
|
-
expectedDeviceId: authFile.deviceId
|
|
60650
|
-
});
|
|
60651
|
-
if (r.ok) return true;
|
|
60652
|
-
}
|
|
60653
|
-
return false;
|
|
60654
|
-
}
|
|
60655
|
-
});
|
|
60656
|
-
return true;
|
|
60657
|
-
},
|
|
60658
|
-
// Contact SSH relay v2 (spec 2026-07-06-contact-ssh-relay-via-local-daemon-tunnel):
|
|
60659
|
-
// /rpc/ssh-tunnel-local WS 前置拦截。guest CC 沙箱从 localhost 连本地 daemon,daemon
|
|
60660
|
-
// 代理起 outbound wss 到 peer /rpc/ssh-tunnel。findContact / connectToken 处理下沉到
|
|
60661
|
-
// 本 handler;relay CLI 变纯粹的 stdio ↔ local WS pump(不再读 contacts.json / 持 token)。
|
|
60662
|
-
sshTunnelLocalUpgradeHandler: async (req, socket, head, wss2) => {
|
|
60663
|
-
await handleSshTunnelLocalUpgrade(req, socket, head, {
|
|
60664
|
-
wss: wss2,
|
|
60665
|
-
store: contactStore,
|
|
60666
|
-
logger,
|
|
60667
|
-
sshLog
|
|
60668
|
-
});
|
|
60669
|
-
},
|
|
60670
59407
|
// 订阅成功后给该 client 重放 in-flight pendingQuestions(plan: clawd-question-server-truth)。
|
|
60671
59408
|
// daemon 是 pendingQuestions 的唯一 source of truth;新 client 接入 / 刷新页面时
|
|
60672
59409
|
// 把当前所有未决 question 以 session:question 帧定向回放,让 UI 不再误显示 Ended。
|
|
@@ -60778,8 +59515,8 @@ async function startDaemon(config) {
|
|
|
60778
59515
|
const lines = [
|
|
60779
59516
|
`Tunnel: ${r.url}`,
|
|
60780
59517
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
60781
|
-
`Frpc config: ${
|
|
60782
|
-
`Frpc log: ${
|
|
59518
|
+
`Frpc config: ${import_node_path57.default.join(config.dataDir, "frpc.toml")}`,
|
|
59519
|
+
`Frpc log: ${import_node_path57.default.join(logDir, "frpc.log")}`
|
|
60783
59520
|
];
|
|
60784
59521
|
const width = Math.max(...lines.map((l) => l.length));
|
|
60785
59522
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -60792,8 +59529,8 @@ ${bar}
|
|
|
60792
59529
|
|
|
60793
59530
|
`);
|
|
60794
59531
|
try {
|
|
60795
|
-
const connectPath =
|
|
60796
|
-
|
|
59532
|
+
const connectPath = import_node_path57.default.join(config.dataDir, "connect.txt");
|
|
59533
|
+
import_node_fs45.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
60797
59534
|
} catch {
|
|
60798
59535
|
}
|
|
60799
59536
|
} catch (err) {
|
|
@@ -60818,24 +59555,6 @@ ${bar}
|
|
|
60818
59555
|
logger.warn("tunnel unavailable, degraded to local mode", { reason: tunnelError });
|
|
60819
59556
|
}
|
|
60820
59557
|
}
|
|
60821
|
-
const sshdMgr = new SshdManager({
|
|
60822
|
-
dataDir: config.dataDir,
|
|
60823
|
-
port: config.sshdPort,
|
|
60824
|
-
logger,
|
|
60825
|
-
installProcessExitHandlers: true,
|
|
60826
|
-
onSshdExit: (info) => logger.warn("sshd exited unexpectedly", info),
|
|
60827
|
-
// sshd 的 pubkey 认证结果转写进 contact-ssh.log(补 A 侧「隧道连上但 sshd 拒了 key」盲区)
|
|
60828
|
-
sshLog
|
|
60829
|
-
});
|
|
60830
|
-
try {
|
|
60831
|
-
await sshdMgr.start();
|
|
60832
|
-
rebuildAuthorizedKeys(contactStore, import_node_path64.default.join(config.dataDir, "sshd"));
|
|
60833
|
-
logger.info("sshd: contact-ssh sandbox ready", { port: config.sshdPort });
|
|
60834
|
-
} catch (err) {
|
|
60835
|
-
logger.warn("sshd start failed; contact SSH grant will not work until fixed", {
|
|
60836
|
-
err: err.message
|
|
60837
|
-
});
|
|
60838
|
-
}
|
|
60839
59558
|
void reportDevice();
|
|
60840
59559
|
void fetchServerKey();
|
|
60841
59560
|
const tickAttachmentGc = () => {
|
|
@@ -60870,11 +59589,6 @@ ${bar}
|
|
|
60870
59589
|
if (tunnelMgr) {
|
|
60871
59590
|
await tunnelMgr.stop();
|
|
60872
59591
|
}
|
|
60873
|
-
await sshdMgr.stop().catch((err) => {
|
|
60874
|
-
logger.warn("shutdown.sshd-stop-failed", {
|
|
60875
|
-
error: err instanceof Error ? err.message : String(err)
|
|
60876
|
-
});
|
|
60877
|
-
});
|
|
60878
59592
|
await wss.stop();
|
|
60879
59593
|
stateMgr.delete();
|
|
60880
59594
|
if (logClient) await logClient.dispose();
|
|
@@ -60888,9 +59602,9 @@ ${bar}
|
|
|
60888
59602
|
};
|
|
60889
59603
|
}
|
|
60890
59604
|
function migrateDropPersonsDir(dataDir) {
|
|
60891
|
-
const dir =
|
|
59605
|
+
const dir = import_node_path57.default.join(dataDir, "persons");
|
|
60892
59606
|
try {
|
|
60893
|
-
|
|
59607
|
+
import_node_fs45.default.rmSync(dir, { recursive: true, force: true });
|
|
60894
59608
|
} catch {
|
|
60895
59609
|
}
|
|
60896
59610
|
}
|
|
@@ -60903,11 +59617,6 @@ async function main() {
|
|
|
60903
59617
|
const code = await runCase2(argv.slice(1));
|
|
60904
59618
|
process.exit(code);
|
|
60905
59619
|
}
|
|
60906
|
-
if (argv[0] === "ssh-relay") {
|
|
60907
|
-
const { sshRelay: sshRelay2 } = await Promise.resolve().then(() => (init_sshd_cli_relay(), sshd_cli_relay_exports));
|
|
60908
|
-
const code = await sshRelay2(argv.slice(1));
|
|
60909
|
-
process.exit(code);
|
|
60910
|
-
}
|
|
60911
59620
|
const parsed = parseArgs(argv);
|
|
60912
59621
|
if (parsed.help) {
|
|
60913
59622
|
process.stdout.write(HELP_TEXT);
|