@clawos-dev/clawd 0.2.234 → 0.2.236
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 +390 -571
- package/dist/dispatch/mcp-server.cjs +29 -18
- package/dist/share-md-viewer-error.html +1 -1
- package/dist/share-md-viewer.html +1 -1
- package/dist/share-ui/assets/{guest-mDZsohRW.js → guest-BUXQ-nR5.js} +13 -13
- package/dist/share-ui/assets/guest-BXzrtMaF.css +32 -0
- package/dist/share-ui/guest.html +2 -2
- package/package.json +1 -1
- package/dist/share-ui/assets/guest-AOFHLDRS.css +0 -32
package/dist/cli.cjs
CHANGED
|
@@ -161,11 +161,10 @@ var init_methods = __esm({
|
|
|
161
161
|
"contact:list",
|
|
162
162
|
"contact:pin",
|
|
163
163
|
"contact:remove",
|
|
164
|
-
// ---- contact:setSshAccess
|
|
165
|
-
// owner UI 授权 contact 通过 daemon 专用 sshd 反向 SSH 进本机;
|
|
166
|
-
//
|
|
164
|
+
// ---- contact:setSshAccess (SSH 反向访问,PR: contact-ssh-sandbox) ----
|
|
165
|
+
// owner UI 授权 contact 通过 daemon 专用 sshd 反向 SSH 进本机;key 分发走建联握手
|
|
166
|
+
// pubkey 互换(spec 2026-07-15,无签发 RPC)。详见 protocol/src/contact-ssh.ts.
|
|
167
167
|
"contact:setSshAccess",
|
|
168
|
-
"contact:sshKey:issue",
|
|
169
168
|
// ---- visitor:* (web 访客 · persona web 分享,spec 2026-06-24-persona-web-share-design) ----
|
|
170
169
|
// owner-only:列出登录访问过本机 public persona 的 web 访客(零安装、飞书登录、daemon 自签
|
|
171
170
|
// visitor token)。存储 ~/.clawd/visitors.json(VisitorStore),登录(exchange)即 upsert;
|
|
@@ -745,8 +744,8 @@ var init_parseUtil = __esm({
|
|
|
745
744
|
init_errors2();
|
|
746
745
|
init_en();
|
|
747
746
|
makeIssue = (params) => {
|
|
748
|
-
const { data, path:
|
|
749
|
-
const fullPath = [...
|
|
747
|
+
const { data, path: path76, errorMaps, issueData } = params;
|
|
748
|
+
const fullPath = [...path76, ...issueData.path || []];
|
|
750
749
|
const fullIssue = {
|
|
751
750
|
...issueData,
|
|
752
751
|
path: fullPath
|
|
@@ -1057,11 +1056,11 @@ var init_types = __esm({
|
|
|
1057
1056
|
init_parseUtil();
|
|
1058
1057
|
init_util();
|
|
1059
1058
|
ParseInputLazyPath = class {
|
|
1060
|
-
constructor(parent, value,
|
|
1059
|
+
constructor(parent, value, path76, key) {
|
|
1061
1060
|
this._cachedPath = [];
|
|
1062
1061
|
this.parent = parent;
|
|
1063
1062
|
this.data = value;
|
|
1064
|
-
this._path =
|
|
1063
|
+
this._path = path76;
|
|
1065
1064
|
this._key = key;
|
|
1066
1065
|
}
|
|
1067
1066
|
get path() {
|
|
@@ -5326,7 +5325,14 @@ var init_schemas = __esm({
|
|
|
5326
5325
|
* selfUrl 保留:它只是"可达地址"(auto-reverse 反向连接用),不是身份——填错只影响
|
|
5327
5326
|
* 反向连通性,不构成伪造风险,且 url 不在 token 里(token 只签身份不签地址)。
|
|
5328
5327
|
*/
|
|
5329
|
-
selfUrl: external_exports.string().min(1).optional()
|
|
5328
|
+
selfUrl: external_exports.string().min(1).optional(),
|
|
5329
|
+
/**
|
|
5330
|
+
* ssh identity 互换(spec 2026-07-15):连接方自报本机 SSH identity pubkey
|
|
5331
|
+
* (一行 `ssh-ed25519 AAAA... comment`)。跟 selfUrl 同理不是身份——pubkey 是公开
|
|
5332
|
+
* 材料,报假只会让自己连不上(拨号还要对应 privkey 签名验证),被连方 auto-reverse
|
|
5333
|
+
* 落 contact.sshPublicKey。缺省 = 老客户端 / identity 未生成。
|
|
5334
|
+
*/
|
|
5335
|
+
selfSshPublicKey: external_exports.string().min(1).optional()
|
|
5330
5336
|
});
|
|
5331
5337
|
AuthOkFrameSchema = external_exports.object({
|
|
5332
5338
|
type: external_exports.literal("auth:ok"),
|
|
@@ -5649,7 +5655,12 @@ var init_capability = __esm({
|
|
|
5649
5655
|
id: external_exports.string().min(1),
|
|
5650
5656
|
displayName: external_exports.string()
|
|
5651
5657
|
}).strict()
|
|
5652
|
-
)
|
|
5658
|
+
),
|
|
5659
|
+
/**
|
|
5660
|
+
* 被连方设备 SSH identity pubkey(spec 2026-07-15 ssh identity 互换):
|
|
5661
|
+
* 连接方 device:connect 读走落 contact.sshPublicKey。identity 未生成时省略。
|
|
5662
|
+
*/
|
|
5663
|
+
sshPublicKey: external_exports.string().min(1).optional()
|
|
5653
5664
|
}).strict();
|
|
5654
5665
|
PERSONAL_CAP_GRANTS = Object.freeze([
|
|
5655
5666
|
Object.freeze({
|
|
@@ -5745,8 +5756,18 @@ var init_contact = __esm({
|
|
|
5745
5756
|
*/
|
|
5746
5757
|
sshAllowed: external_exports.boolean().default(false),
|
|
5747
5758
|
/**
|
|
5748
|
-
*
|
|
5749
|
-
*
|
|
5759
|
+
* 对端设备 SSH identity pubkey(一行 `ssh-ed25519 AAAA... comment`,spec 2026-07-15)。
|
|
5760
|
+
* 建联握手双向交换写入(连入方 auth 帧自报 / 被连方 whoami:ok 下发),
|
|
5761
|
+
* setSshAccess=true 时由 rebuildAuthorizedKeys 写进 authorized_keys。
|
|
5762
|
+
* pubkey 公开可自报无伪造风险——拨号还要 privkey 签名验证。
|
|
5763
|
+
* 缺省 = 老 contact / 对方老版本未重连,授权时写不出 authorized_keys 行。
|
|
5764
|
+
*/
|
|
5765
|
+
sshPublicKey: external_exports.string().optional(),
|
|
5766
|
+
/**
|
|
5767
|
+
* @deprecated T-29 去沙箱后废弃。原为授权访问的目录白名单(clawd-ssh-jail 生成 sbpl/bwrap
|
|
5768
|
+
* policy 用);去掉 OS 沙箱后不再有目录级限制,SSH 授权即 on/off(见 sshAllowed)。
|
|
5769
|
+
* 字段仅为兼容老 contacts.json 保留(default [] 保证 strict parse 不炸),不再有代码读写它,
|
|
5770
|
+
* 也不再进 setSshAccess RPC / ContactSettingsDrawer / jail。勿新增消费方。
|
|
5750
5771
|
*/
|
|
5751
5772
|
exposedDirs: external_exports.array(external_exports.string()).default([])
|
|
5752
5773
|
}).strict();
|
|
@@ -5788,35 +5809,24 @@ var init_contact = __esm({
|
|
|
5788
5809
|
});
|
|
5789
5810
|
|
|
5790
5811
|
// ../protocol/src/contact-ssh.ts
|
|
5791
|
-
var ContactSetSshAccessArgsSchema, ContactSetSshAccessOkSchema,
|
|
5812
|
+
var ContactSetSshAccessArgsSchema, ContactSetSshAccessOkSchema, ContactSshAccessUpdatedFrameSchema;
|
|
5792
5813
|
var init_contact_ssh = __esm({
|
|
5793
5814
|
"../protocol/src/contact-ssh.ts"() {
|
|
5794
5815
|
"use strict";
|
|
5795
5816
|
init_zod();
|
|
5796
5817
|
ContactSetSshAccessArgsSchema = external_exports.object({
|
|
5797
5818
|
deviceId: external_exports.string().min(1),
|
|
5798
|
-
sshAllowed: external_exports.boolean()
|
|
5799
|
-
exposedDirs: external_exports.array(external_exports.string())
|
|
5819
|
+
sshAllowed: external_exports.boolean()
|
|
5800
5820
|
}).strict();
|
|
5801
5821
|
ContactSetSshAccessOkSchema = external_exports.object({
|
|
5802
5822
|
type: external_exports.literal("contact:setSshAccess:ok"),
|
|
5803
5823
|
deviceId: external_exports.string().min(1),
|
|
5804
|
-
sshAllowed: external_exports.boolean()
|
|
5805
|
-
exposedDirs: external_exports.array(external_exports.string())
|
|
5806
|
-
}).strict();
|
|
5807
|
-
ContactSshKeyIssueArgsSchema = external_exports.object({
|
|
5808
|
-
deviceId: external_exports.string().min(1)
|
|
5809
|
-
}).strict();
|
|
5810
|
-
ContactSshKeyIssueOkSchema = external_exports.object({
|
|
5811
|
-
type: external_exports.literal("contact:sshKey:issue:ok"),
|
|
5812
|
-
privateKeyPem: external_exports.string().min(1),
|
|
5813
|
-
publicKeyLine: external_exports.string().min(1)
|
|
5824
|
+
sshAllowed: external_exports.boolean()
|
|
5814
5825
|
}).strict();
|
|
5815
5826
|
ContactSshAccessUpdatedFrameSchema = external_exports.object({
|
|
5816
5827
|
type: external_exports.literal("contact:ssh-access-updated"),
|
|
5817
5828
|
deviceId: external_exports.string().min(1),
|
|
5818
|
-
sshAllowed: external_exports.boolean()
|
|
5819
|
-
exposedDirs: external_exports.array(external_exports.string())
|
|
5829
|
+
sshAllowed: external_exports.boolean()
|
|
5820
5830
|
}).strict();
|
|
5821
5831
|
}
|
|
5822
5832
|
});
|
|
@@ -6507,8 +6517,8 @@ var require_req = __commonJS({
|
|
|
6507
6517
|
if (req.originalUrl) {
|
|
6508
6518
|
_req.url = req.originalUrl;
|
|
6509
6519
|
} else {
|
|
6510
|
-
const
|
|
6511
|
-
_req.url = typeof
|
|
6520
|
+
const path76 = req.path;
|
|
6521
|
+
_req.url = typeof path76 === "string" ? path76 : req.url ? req.url.path || req.url : void 0;
|
|
6512
6522
|
}
|
|
6513
6523
|
if (req.query) {
|
|
6514
6524
|
_req.query = req.query;
|
|
@@ -6673,14 +6683,14 @@ var require_redact = __commonJS({
|
|
|
6673
6683
|
}
|
|
6674
6684
|
return obj;
|
|
6675
6685
|
}
|
|
6676
|
-
function parsePath(
|
|
6686
|
+
function parsePath(path76) {
|
|
6677
6687
|
const parts = [];
|
|
6678
6688
|
let current = "";
|
|
6679
6689
|
let inBrackets = false;
|
|
6680
6690
|
let inQuotes = false;
|
|
6681
6691
|
let quoteChar = "";
|
|
6682
|
-
for (let i = 0; i <
|
|
6683
|
-
const char =
|
|
6692
|
+
for (let i = 0; i < path76.length; i++) {
|
|
6693
|
+
const char = path76[i];
|
|
6684
6694
|
if (!inBrackets && char === ".") {
|
|
6685
6695
|
if (current) {
|
|
6686
6696
|
parts.push(current);
|
|
@@ -6811,10 +6821,10 @@ var require_redact = __commonJS({
|
|
|
6811
6821
|
return current;
|
|
6812
6822
|
}
|
|
6813
6823
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
6814
|
-
for (const
|
|
6815
|
-
const parts = parsePath(
|
|
6824
|
+
for (const path76 of paths) {
|
|
6825
|
+
const parts = parsePath(path76);
|
|
6816
6826
|
if (parts.includes("*")) {
|
|
6817
|
-
redactWildcardPath(obj, parts, censor,
|
|
6827
|
+
redactWildcardPath(obj, parts, censor, path76, remove);
|
|
6818
6828
|
} else {
|
|
6819
6829
|
if (remove) {
|
|
6820
6830
|
removeKey(obj, parts);
|
|
@@ -6899,8 +6909,8 @@ var require_redact = __commonJS({
|
|
|
6899
6909
|
}
|
|
6900
6910
|
} else {
|
|
6901
6911
|
if (afterWildcard.includes("*")) {
|
|
6902
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6903
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
6912
|
+
const wrappedCensor = typeof censor === "function" ? (value, path76) => {
|
|
6913
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path76];
|
|
6904
6914
|
return censor(value, fullPath);
|
|
6905
6915
|
} : censor;
|
|
6906
6916
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -6935,8 +6945,8 @@ var require_redact = __commonJS({
|
|
|
6935
6945
|
return null;
|
|
6936
6946
|
}
|
|
6937
6947
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
6938
|
-
for (const
|
|
6939
|
-
const parts = parsePath(
|
|
6948
|
+
for (const path76 of pathsToClone) {
|
|
6949
|
+
const parts = parsePath(path76);
|
|
6940
6950
|
let current = pathStructure;
|
|
6941
6951
|
for (let i = 0; i < parts.length; i++) {
|
|
6942
6952
|
const part = parts[i];
|
|
@@ -6988,24 +6998,24 @@ var require_redact = __commonJS({
|
|
|
6988
6998
|
}
|
|
6989
6999
|
return cloneSelectively(obj, pathStructure);
|
|
6990
7000
|
}
|
|
6991
|
-
function validatePath(
|
|
6992
|
-
if (typeof
|
|
7001
|
+
function validatePath(path76) {
|
|
7002
|
+
if (typeof path76 !== "string") {
|
|
6993
7003
|
throw new Error("Paths must be (non-empty) strings");
|
|
6994
7004
|
}
|
|
6995
|
-
if (
|
|
7005
|
+
if (path76 === "") {
|
|
6996
7006
|
throw new Error("Invalid redaction path ()");
|
|
6997
7007
|
}
|
|
6998
|
-
if (
|
|
6999
|
-
throw new Error(`Invalid redaction path (${
|
|
7008
|
+
if (path76.includes("..")) {
|
|
7009
|
+
throw new Error(`Invalid redaction path (${path76})`);
|
|
7000
7010
|
}
|
|
7001
|
-
if (
|
|
7002
|
-
throw new Error(`Invalid redaction path (${
|
|
7011
|
+
if (path76.includes(",")) {
|
|
7012
|
+
throw new Error(`Invalid redaction path (${path76})`);
|
|
7003
7013
|
}
|
|
7004
7014
|
let bracketCount = 0;
|
|
7005
7015
|
let inQuotes = false;
|
|
7006
7016
|
let quoteChar = "";
|
|
7007
|
-
for (let i = 0; i <
|
|
7008
|
-
const char =
|
|
7017
|
+
for (let i = 0; i < path76.length; i++) {
|
|
7018
|
+
const char = path76[i];
|
|
7009
7019
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
7010
7020
|
if (!inQuotes) {
|
|
7011
7021
|
inQuotes = true;
|
|
@@ -7019,20 +7029,20 @@ var require_redact = __commonJS({
|
|
|
7019
7029
|
} else if (char === "]" && !inQuotes) {
|
|
7020
7030
|
bracketCount--;
|
|
7021
7031
|
if (bracketCount < 0) {
|
|
7022
|
-
throw new Error(`Invalid redaction path (${
|
|
7032
|
+
throw new Error(`Invalid redaction path (${path76})`);
|
|
7023
7033
|
}
|
|
7024
7034
|
}
|
|
7025
7035
|
}
|
|
7026
7036
|
if (bracketCount !== 0) {
|
|
7027
|
-
throw new Error(`Invalid redaction path (${
|
|
7037
|
+
throw new Error(`Invalid redaction path (${path76})`);
|
|
7028
7038
|
}
|
|
7029
7039
|
}
|
|
7030
7040
|
function validatePaths(paths) {
|
|
7031
7041
|
if (!Array.isArray(paths)) {
|
|
7032
7042
|
throw new TypeError("paths must be an array");
|
|
7033
7043
|
}
|
|
7034
|
-
for (const
|
|
7035
|
-
validatePath(
|
|
7044
|
+
for (const path76 of paths) {
|
|
7045
|
+
validatePath(path76);
|
|
7036
7046
|
}
|
|
7037
7047
|
}
|
|
7038
7048
|
function slowRedact(options = {}) {
|
|
@@ -7200,8 +7210,8 @@ var require_redaction = __commonJS({
|
|
|
7200
7210
|
if (shape[k2] === null) {
|
|
7201
7211
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
7202
7212
|
} else {
|
|
7203
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
7204
|
-
return censor(value, [k2, ...
|
|
7213
|
+
const wrappedCensor = typeof censor === "function" ? (value, path76) => {
|
|
7214
|
+
return censor(value, [k2, ...path76]);
|
|
7205
7215
|
} : censor;
|
|
7206
7216
|
o[k2] = Redact({
|
|
7207
7217
|
paths: shape[k2],
|
|
@@ -7419,10 +7429,10 @@ var require_atomic_sleep = __commonJS({
|
|
|
7419
7429
|
var require_sonic_boom = __commonJS({
|
|
7420
7430
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
7421
7431
|
"use strict";
|
|
7422
|
-
var
|
|
7432
|
+
var fs70 = require("fs");
|
|
7423
7433
|
var EventEmitter3 = require("events");
|
|
7424
7434
|
var inherits = require("util").inherits;
|
|
7425
|
-
var
|
|
7435
|
+
var path76 = require("path");
|
|
7426
7436
|
var sleep2 = require_atomic_sleep();
|
|
7427
7437
|
var assert = require("assert");
|
|
7428
7438
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -7476,20 +7486,20 @@ var require_sonic_boom = __commonJS({
|
|
|
7476
7486
|
const mode = sonic.mode;
|
|
7477
7487
|
if (sonic.sync) {
|
|
7478
7488
|
try {
|
|
7479
|
-
if (sonic.mkdir)
|
|
7480
|
-
const fd =
|
|
7489
|
+
if (sonic.mkdir) fs70.mkdirSync(path76.dirname(file), { recursive: true });
|
|
7490
|
+
const fd = fs70.openSync(file, flags, mode);
|
|
7481
7491
|
fileOpened(null, fd);
|
|
7482
7492
|
} catch (err) {
|
|
7483
7493
|
fileOpened(err);
|
|
7484
7494
|
throw err;
|
|
7485
7495
|
}
|
|
7486
7496
|
} else if (sonic.mkdir) {
|
|
7487
|
-
|
|
7497
|
+
fs70.mkdir(path76.dirname(file), { recursive: true }, (err) => {
|
|
7488
7498
|
if (err) return fileOpened(err);
|
|
7489
|
-
|
|
7499
|
+
fs70.open(file, flags, mode, fileOpened);
|
|
7490
7500
|
});
|
|
7491
7501
|
} else {
|
|
7492
|
-
|
|
7502
|
+
fs70.open(file, flags, mode, fileOpened);
|
|
7493
7503
|
}
|
|
7494
7504
|
}
|
|
7495
7505
|
function SonicBoom(opts) {
|
|
@@ -7530,8 +7540,8 @@ var require_sonic_boom = __commonJS({
|
|
|
7530
7540
|
this.flush = flushBuffer;
|
|
7531
7541
|
this.flushSync = flushBufferSync;
|
|
7532
7542
|
this._actualWrite = actualWriteBuffer;
|
|
7533
|
-
fsWriteSync = () =>
|
|
7534
|
-
fsWrite = () =>
|
|
7543
|
+
fsWriteSync = () => fs70.writeSync(this.fd, this._writingBuf);
|
|
7544
|
+
fsWrite = () => fs70.write(this.fd, this._writingBuf, this.release);
|
|
7535
7545
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
7536
7546
|
this._writingBuf = "";
|
|
7537
7547
|
this.write = write;
|
|
@@ -7540,15 +7550,15 @@ var require_sonic_boom = __commonJS({
|
|
|
7540
7550
|
this._actualWrite = actualWrite;
|
|
7541
7551
|
fsWriteSync = () => {
|
|
7542
7552
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7543
|
-
return
|
|
7553
|
+
return fs70.writeSync(this.fd, this._writingBuf);
|
|
7544
7554
|
}
|
|
7545
|
-
return
|
|
7555
|
+
return fs70.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7546
7556
|
};
|
|
7547
7557
|
fsWrite = () => {
|
|
7548
7558
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7549
|
-
return
|
|
7559
|
+
return fs70.write(this.fd, this._writingBuf, this.release);
|
|
7550
7560
|
}
|
|
7551
|
-
return
|
|
7561
|
+
return fs70.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
7552
7562
|
};
|
|
7553
7563
|
} else {
|
|
7554
7564
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -7605,7 +7615,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7605
7615
|
}
|
|
7606
7616
|
}
|
|
7607
7617
|
if (this._fsync) {
|
|
7608
|
-
|
|
7618
|
+
fs70.fsyncSync(this.fd);
|
|
7609
7619
|
}
|
|
7610
7620
|
const len = this._len;
|
|
7611
7621
|
if (this._reopening) {
|
|
@@ -7719,7 +7729,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7719
7729
|
const onDrain = () => {
|
|
7720
7730
|
if (!this._fsync) {
|
|
7721
7731
|
try {
|
|
7722
|
-
|
|
7732
|
+
fs70.fsync(this.fd, (err) => {
|
|
7723
7733
|
this._flushPending = false;
|
|
7724
7734
|
cb(err);
|
|
7725
7735
|
});
|
|
@@ -7821,7 +7831,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7821
7831
|
const fd = this.fd;
|
|
7822
7832
|
this.once("ready", () => {
|
|
7823
7833
|
if (fd !== this.fd) {
|
|
7824
|
-
|
|
7834
|
+
fs70.close(fd, (err) => {
|
|
7825
7835
|
if (err) {
|
|
7826
7836
|
return this.emit("error", err);
|
|
7827
7837
|
}
|
|
@@ -7870,7 +7880,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7870
7880
|
buf = this._bufs[0];
|
|
7871
7881
|
}
|
|
7872
7882
|
try {
|
|
7873
|
-
const n = Buffer.isBuffer(buf) ?
|
|
7883
|
+
const n = Buffer.isBuffer(buf) ? fs70.writeSync(this.fd, buf) : fs70.writeSync(this.fd, buf, "utf8");
|
|
7874
7884
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
7875
7885
|
buf = releasedBufObj.writingBuf;
|
|
7876
7886
|
this._len = releasedBufObj.len;
|
|
@@ -7886,7 +7896,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7886
7896
|
}
|
|
7887
7897
|
}
|
|
7888
7898
|
try {
|
|
7889
|
-
|
|
7899
|
+
fs70.fsyncSync(this.fd);
|
|
7890
7900
|
} catch {
|
|
7891
7901
|
}
|
|
7892
7902
|
}
|
|
@@ -7907,7 +7917,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7907
7917
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
7908
7918
|
}
|
|
7909
7919
|
try {
|
|
7910
|
-
const n =
|
|
7920
|
+
const n = fs70.writeSync(this.fd, buf);
|
|
7911
7921
|
buf = buf.subarray(n);
|
|
7912
7922
|
this._len = Math.max(this._len - n, 0);
|
|
7913
7923
|
if (buf.length <= 0) {
|
|
@@ -7935,13 +7945,13 @@ var require_sonic_boom = __commonJS({
|
|
|
7935
7945
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
7936
7946
|
if (this.sync) {
|
|
7937
7947
|
try {
|
|
7938
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
7948
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs70.writeSync(this.fd, this._writingBuf) : fs70.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7939
7949
|
release(null, written);
|
|
7940
7950
|
} catch (err) {
|
|
7941
7951
|
release(err);
|
|
7942
7952
|
}
|
|
7943
7953
|
} else {
|
|
7944
|
-
|
|
7954
|
+
fs70.write(this.fd, this._writingBuf, release);
|
|
7945
7955
|
}
|
|
7946
7956
|
}
|
|
7947
7957
|
function actualWriteBuffer() {
|
|
@@ -7950,7 +7960,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7950
7960
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
7951
7961
|
if (this.sync) {
|
|
7952
7962
|
try {
|
|
7953
|
-
const written =
|
|
7963
|
+
const written = fs70.writeSync(this.fd, this._writingBuf);
|
|
7954
7964
|
release(null, written);
|
|
7955
7965
|
} catch (err) {
|
|
7956
7966
|
release(err);
|
|
@@ -7959,7 +7969,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7959
7969
|
if (kCopyBuffer) {
|
|
7960
7970
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
7961
7971
|
}
|
|
7962
|
-
|
|
7972
|
+
fs70.write(this.fd, this._writingBuf, release);
|
|
7963
7973
|
}
|
|
7964
7974
|
}
|
|
7965
7975
|
function actualClose(sonic) {
|
|
@@ -7975,12 +7985,12 @@ var require_sonic_boom = __commonJS({
|
|
|
7975
7985
|
sonic._lens = [];
|
|
7976
7986
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
7977
7987
|
try {
|
|
7978
|
-
|
|
7988
|
+
fs70.fsync(sonic.fd, closeWrapped);
|
|
7979
7989
|
} catch {
|
|
7980
7990
|
}
|
|
7981
7991
|
function closeWrapped() {
|
|
7982
7992
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
7983
|
-
|
|
7993
|
+
fs70.close(sonic.fd, done);
|
|
7984
7994
|
} else {
|
|
7985
7995
|
done();
|
|
7986
7996
|
}
|
|
@@ -11115,11 +11125,11 @@ var init_lib = __esm({
|
|
|
11115
11125
|
}
|
|
11116
11126
|
}
|
|
11117
11127
|
},
|
|
11118
|
-
addToPath: function addToPath(
|
|
11119
|
-
var last =
|
|
11128
|
+
addToPath: function addToPath(path76, added, removed, oldPosInc, options) {
|
|
11129
|
+
var last = path76.lastComponent;
|
|
11120
11130
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
11121
11131
|
return {
|
|
11122
|
-
oldPos:
|
|
11132
|
+
oldPos: path76.oldPos + oldPosInc,
|
|
11123
11133
|
lastComponent: {
|
|
11124
11134
|
count: last.count + 1,
|
|
11125
11135
|
added,
|
|
@@ -11129,7 +11139,7 @@ var init_lib = __esm({
|
|
|
11129
11139
|
};
|
|
11130
11140
|
} else {
|
|
11131
11141
|
return {
|
|
11132
|
-
oldPos:
|
|
11142
|
+
oldPos: path76.oldPos + oldPosInc,
|
|
11133
11143
|
lastComponent: {
|
|
11134
11144
|
count: 1,
|
|
11135
11145
|
added,
|
|
@@ -11634,10 +11644,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
11634
11644
|
const memories = raw.map((m2) => {
|
|
11635
11645
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11636
11646
|
const rec3 = m2;
|
|
11637
|
-
const
|
|
11647
|
+
const path76 = typeof rec3.path === "string" ? rec3.path : null;
|
|
11638
11648
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
11639
|
-
if (!
|
|
11640
|
-
const entry = { path:
|
|
11649
|
+
if (!path76 || content == null) return null;
|
|
11650
|
+
const entry = { path: path76, content };
|
|
11641
11651
|
if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
|
|
11642
11652
|
return entry;
|
|
11643
11653
|
}).filter((m2) => m2 !== null);
|
|
@@ -12440,10 +12450,10 @@ function parseAttachment(obj) {
|
|
|
12440
12450
|
const memories = raw.map((m2) => {
|
|
12441
12451
|
if (!m2 || typeof m2 !== "object") return null;
|
|
12442
12452
|
const rec3 = m2;
|
|
12443
|
-
const
|
|
12453
|
+
const path76 = typeof rec3.path === "string" ? rec3.path : null;
|
|
12444
12454
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
12445
|
-
if (!
|
|
12446
|
-
const out = { path:
|
|
12455
|
+
if (!path76 || content == null) return null;
|
|
12456
|
+
const out = { path: path76, content };
|
|
12447
12457
|
if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
|
|
12448
12458
|
return out;
|
|
12449
12459
|
}).filter((m2) => m2 !== null);
|
|
@@ -33429,8 +33439,8 @@ var require_utils = __commonJS({
|
|
|
33429
33439
|
var result = transform[inputType][outputType](input);
|
|
33430
33440
|
return result;
|
|
33431
33441
|
};
|
|
33432
|
-
exports2.resolve = function(
|
|
33433
|
-
var parts =
|
|
33442
|
+
exports2.resolve = function(path76) {
|
|
33443
|
+
var parts = path76.split("/");
|
|
33434
33444
|
var result = [];
|
|
33435
33445
|
for (var index = 0; index < parts.length; index++) {
|
|
33436
33446
|
var part = parts[index];
|
|
@@ -39283,18 +39293,18 @@ var require_object = __commonJS({
|
|
|
39283
39293
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
39284
39294
|
this.files[name] = object;
|
|
39285
39295
|
};
|
|
39286
|
-
var parentFolder = function(
|
|
39287
|
-
if (
|
|
39288
|
-
|
|
39296
|
+
var parentFolder = function(path76) {
|
|
39297
|
+
if (path76.slice(-1) === "/") {
|
|
39298
|
+
path76 = path76.substring(0, path76.length - 1);
|
|
39289
39299
|
}
|
|
39290
|
-
var lastSlash =
|
|
39291
|
-
return lastSlash > 0 ?
|
|
39300
|
+
var lastSlash = path76.lastIndexOf("/");
|
|
39301
|
+
return lastSlash > 0 ? path76.substring(0, lastSlash) : "";
|
|
39292
39302
|
};
|
|
39293
|
-
var forceTrailingSlash = function(
|
|
39294
|
-
if (
|
|
39295
|
-
|
|
39303
|
+
var forceTrailingSlash = function(path76) {
|
|
39304
|
+
if (path76.slice(-1) !== "/") {
|
|
39305
|
+
path76 += "/";
|
|
39296
39306
|
}
|
|
39297
|
-
return
|
|
39307
|
+
return path76;
|
|
39298
39308
|
};
|
|
39299
39309
|
var folderAdd = function(name, createFolders) {
|
|
39300
39310
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -40296,7 +40306,7 @@ var require_lib3 = __commonJS({
|
|
|
40296
40306
|
// src/run-case/recorder.ts
|
|
40297
40307
|
function startRunCaseRecorder(opts) {
|
|
40298
40308
|
const now = opts.now ?? Date.now;
|
|
40299
|
-
const dir =
|
|
40309
|
+
const dir = import_node_path64.default.dirname(opts.recordPath);
|
|
40300
40310
|
let stream = null;
|
|
40301
40311
|
let closing = false;
|
|
40302
40312
|
let closedSettled = false;
|
|
@@ -40310,8 +40320,8 @@ function startRunCaseRecorder(opts) {
|
|
|
40310
40320
|
});
|
|
40311
40321
|
const ensureStream = () => {
|
|
40312
40322
|
if (stream) return stream;
|
|
40313
|
-
|
|
40314
|
-
stream =
|
|
40323
|
+
import_node_fs52.default.mkdirSync(dir, { recursive: true });
|
|
40324
|
+
stream = import_node_fs52.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
40315
40325
|
stream.on("close", () => closedResolve());
|
|
40316
40326
|
return stream;
|
|
40317
40327
|
};
|
|
@@ -40336,12 +40346,12 @@ function startRunCaseRecorder(opts) {
|
|
|
40336
40346
|
};
|
|
40337
40347
|
return { tap, close, closed };
|
|
40338
40348
|
}
|
|
40339
|
-
var
|
|
40349
|
+
var import_node_fs52, import_node_path64;
|
|
40340
40350
|
var init_recorder = __esm({
|
|
40341
40351
|
"src/run-case/recorder.ts"() {
|
|
40342
40352
|
"use strict";
|
|
40343
|
-
|
|
40344
|
-
|
|
40353
|
+
import_node_fs52 = __toESM(require("fs"), 1);
|
|
40354
|
+
import_node_path64 = __toESM(require("path"), 1);
|
|
40345
40355
|
}
|
|
40346
40356
|
});
|
|
40347
40357
|
|
|
@@ -40384,7 +40394,7 @@ var init_wire = __esm({
|
|
|
40384
40394
|
// src/run-case/controller.ts
|
|
40385
40395
|
async function runController(opts) {
|
|
40386
40396
|
const now = opts.now ?? Date.now;
|
|
40387
|
-
const cwd = opts.cwd ?? (0,
|
|
40397
|
+
const cwd = opts.cwd ?? (0, import_node_fs53.mkdtempSync)(import_node_path65.default.join(import_node_os22.default.tmpdir(), "clawd-runcase-"));
|
|
40388
40398
|
const ownsCwd = opts.cwd === void 0;
|
|
40389
40399
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
40390
40400
|
const spawnCtx = { cwd };
|
|
@@ -40545,19 +40555,19 @@ async function runController(opts) {
|
|
|
40545
40555
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
40546
40556
|
if (ownsCwd) {
|
|
40547
40557
|
try {
|
|
40548
|
-
(0,
|
|
40558
|
+
(0, import_node_fs53.rmSync)(cwd, { recursive: true, force: true });
|
|
40549
40559
|
} catch {
|
|
40550
40560
|
}
|
|
40551
40561
|
}
|
|
40552
40562
|
return exitCode ?? 0;
|
|
40553
40563
|
}
|
|
40554
|
-
var
|
|
40564
|
+
var import_node_fs53, import_node_os22, import_node_path65;
|
|
40555
40565
|
var init_controller = __esm({
|
|
40556
40566
|
"src/run-case/controller.ts"() {
|
|
40557
40567
|
"use strict";
|
|
40558
|
-
|
|
40568
|
+
import_node_fs53 = require("fs");
|
|
40559
40569
|
import_node_os22 = __toESM(require("os"), 1);
|
|
40560
|
-
|
|
40570
|
+
import_node_path65 = __toESM(require("path"), 1);
|
|
40561
40571
|
init_claude();
|
|
40562
40572
|
init_stdout_splitter();
|
|
40563
40573
|
init_permission_stdio();
|
|
@@ -40761,7 +40771,7 @@ Options:
|
|
|
40761
40771
|
|
|
40762
40772
|
Example:
|
|
40763
40773
|
ssh -o ProxyCommand='~/.clawd/bin/clawd ssh-relay <peer-device-id>' \\
|
|
40764
|
-
-i ~/.clawd/
|
|
40774
|
+
-i ~/.clawd/ssh-identity/id_ed25519 \\
|
|
40765
40775
|
$USER@127.0.0.1
|
|
40766
40776
|
`;
|
|
40767
40777
|
}
|
|
@@ -40951,8 +40961,8 @@ Env (advanced):
|
|
|
40951
40961
|
`;
|
|
40952
40962
|
|
|
40953
40963
|
// src/index.ts
|
|
40954
|
-
var
|
|
40955
|
-
var
|
|
40964
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
40965
|
+
var import_node_fs51 = __toESM(require("fs"), 1);
|
|
40956
40966
|
var import_node_os21 = __toESM(require("os"), 1);
|
|
40957
40967
|
|
|
40958
40968
|
// ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
|
|
@@ -41703,7 +41713,7 @@ function composeGuestSandbox(base, userWorkDir, spawnCwd) {
|
|
|
41703
41713
|
fsv.allowRead = unionArr(fsv.allowRead, [userWorkDir]);
|
|
41704
41714
|
fsv.allowWrite = unionArr(fsv.allowWrite, [userWorkDir]);
|
|
41705
41715
|
fsv.allowRead = unionArr(fsv.allowRead, [
|
|
41706
|
-
"~/.clawd/
|
|
41716
|
+
"~/.clawd/ssh-identity",
|
|
41707
41717
|
"~/.clawd/bin",
|
|
41708
41718
|
"~/Library/Application Support/@clawos/clawd-desktop/ota-bundles",
|
|
41709
41719
|
"~/.clawd/config.json",
|
|
@@ -41979,28 +41989,27 @@ deviceId \u76F4\u63A5\u53D6\u81EA token\uFF0C\u4E0D\u8981\u6539\u5199\u6216\u731
|
|
|
41979
41989
|
// src/sshd/contact-ssh-prompt.ts
|
|
41980
41990
|
var CONTACT_SSH_SYSTEM_PROMPT_HINT = `## \u8DE8\u8BBE\u5907\u6587\u4EF6\u8BBF\u95EE\uFF08Contact SSH\uFF09
|
|
41981
41991
|
|
|
41982
|
-
\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\uFF09\u7684\u65B9\u6CD5\uFF1A
|
|
41992
|
+
\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
|
|
41983
41993
|
|
|
41984
|
-
|
|
41985
|
-
|
|
41986
|
-
|
|
41987
|
-
|
|
41988
|
-
|
|
41989
|
-
|
|
41990
|
-
|
|
41991
|
-
|
|
41992
|
-
|
|
41993
|
-
|
|
41994
|
-
|
|
41995
|
-
|
|
41996
|
-
\`\`\`
|
|
41994
|
+
\u7528 SSH \u62E8\u53F7\uFF08\u628A \`<A>\` \u6362\u6210\u5BF9\u7AEF deviceId\uFF09\uFF1A
|
|
41995
|
+
\`\`\`bash
|
|
41996
|
+
ssh -o ProxyCommand='~/.clawd/bin/clawd ssh-relay <A>' \\
|
|
41997
|
+
-i ~/.clawd/ssh-identity/id_ed25519 \\
|
|
41998
|
+
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \\
|
|
41999
|
+
$USER@127.0.0.1 <command>
|
|
42000
|
+
# \u4F8B\u5982\uFF1A... $USER@127.0.0.1 cat /Users/xxx/some/file.md
|
|
42001
|
+
# ... $USER@127.0.0.1 ls -la /Users/xxx/proj
|
|
42002
|
+
# \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
|
|
42003
|
+
# \`~/.clawd/bin/clawd\` \u662F daemon \u542F\u52A8\u65F6 seed \u7684 shim\uFF0C\u786C\u7F16\u7801\u5F53\u524D daemon \u7684
|
|
42004
|
+
# 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
|
|
42005
|
+
\`\`\`
|
|
41997
42006
|
|
|
41998
|
-
|
|
42007
|
+
**\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
|
|
41999
42008
|
|
|
42000
42009
|
**\u6CE8\u610F**\uFF1A
|
|
42001
|
-
- \
|
|
42010
|
+
- \u6388\u6743\u662F\u5B9E\u65F6\u7684\uFF1AA \u64A4\u9500 sshAllowed \u540E\u4F60\u4E0B\u4E00\u6B21\u62E8\u53F7\u7ACB\u5373\u5931\u6548\uFF08authorized_keys \u7ACB\u5373\u5237\uFF09
|
|
42011
|
+
- \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
|
|
42002
42012
|
- \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
|
|
42003
|
-
- \u5982\u679C \`ls ~/.clawd/contact-ssh-keys/\` \u4E3A\u7A7A\uFF0C\u8BF4\u660E\u8FD8\u6CA1\u6709\u5BF9\u7AEF\u6388\u6743\u4F60\u6216 key \u8FD8\u6CA1\u62C9\u5230\uFF08B \u4FA7 daemon \u6BCF 60s \u4E00\u6B21\u81EA\u52A8\u62C9\uFF09\uFF0C\u5982\u5B9E\u544A\u8BC9\u7528\u6237
|
|
42004
42013
|
`;
|
|
42005
42014
|
|
|
42006
42015
|
// src/session/reducer.ts
|
|
@@ -46755,8 +46764,8 @@ function turnStartInput(text) {
|
|
|
46755
46764
|
const items = [];
|
|
46756
46765
|
let leftover = text;
|
|
46757
46766
|
for (const m2 of text.matchAll(SKILL_RE)) {
|
|
46758
|
-
const [marker, name,
|
|
46759
|
-
items.push({ type: "skill", name, path:
|
|
46767
|
+
const [marker, name, path76] = m2;
|
|
46768
|
+
items.push({ type: "skill", name, path: path76 });
|
|
46760
46769
|
leftover = leftover.replace(marker, "");
|
|
46761
46770
|
}
|
|
46762
46771
|
for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
|
|
@@ -49109,43 +49118,6 @@ async function forwardInboxPostToPeer(args) {
|
|
|
49109
49118
|
}
|
|
49110
49119
|
return { ok: true };
|
|
49111
49120
|
}
|
|
49112
|
-
async function forwardContactSshKeyIssueToPeer(args) {
|
|
49113
|
-
const f = args.fetchImpl ?? fetch;
|
|
49114
|
-
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
49115
|
-
const url = `${base}/rpc/contact:sshKey:issue`;
|
|
49116
|
-
let res;
|
|
49117
|
-
try {
|
|
49118
|
-
res = await f(url, {
|
|
49119
|
-
method: "POST",
|
|
49120
|
-
headers: {
|
|
49121
|
-
"content-type": "application/json",
|
|
49122
|
-
authorization: `Bearer ${args.contact.connectToken}`
|
|
49123
|
-
},
|
|
49124
|
-
body: JSON.stringify({ deviceId: args.selfDeviceIdForRequest })
|
|
49125
|
-
});
|
|
49126
|
-
} catch (err) {
|
|
49127
|
-
return {
|
|
49128
|
-
ok: false,
|
|
49129
|
-
code: "NETWORK",
|
|
49130
|
-
message: err instanceof Error ? err.message : String(err)
|
|
49131
|
-
};
|
|
49132
|
-
}
|
|
49133
|
-
let json = null;
|
|
49134
|
-
try {
|
|
49135
|
-
json = await res.json();
|
|
49136
|
-
} catch {
|
|
49137
|
-
return { ok: false, code: "PROTOCOL", message: `peer non-JSON (HTTP ${res.status})` };
|
|
49138
|
-
}
|
|
49139
|
-
if (!json) {
|
|
49140
|
-
return { ok: false, code: "PROTOCOL", message: `peer returned null (HTTP ${res.status})` };
|
|
49141
|
-
}
|
|
49142
|
-
if (json.ok === false) {
|
|
49143
|
-
const j = json;
|
|
49144
|
-
return { ok: false, code: j.error ?? "UNKNOWN", message: j.message ?? "" };
|
|
49145
|
-
}
|
|
49146
|
-
const r = json.result;
|
|
49147
|
-
return { ok: true, privateKeyPem: r.privateKeyPem, publicKeyLine: r.publicKeyLine };
|
|
49148
|
-
}
|
|
49149
49121
|
|
|
49150
49122
|
// src/tools/codex-history.ts
|
|
49151
49123
|
var import_node_child_process5 = require("child_process");
|
|
@@ -49333,13 +49305,13 @@ function mapSkillsListResponse(res) {
|
|
|
49333
49305
|
const r = s ?? {};
|
|
49334
49306
|
const name = str3(r.name);
|
|
49335
49307
|
if (!name) continue;
|
|
49336
|
-
const
|
|
49308
|
+
const path76 = str3(r.path);
|
|
49337
49309
|
const description = str3(r.description);
|
|
49338
49310
|
const isPlugin = name.includes(":");
|
|
49339
49311
|
out.push({
|
|
49340
49312
|
name,
|
|
49341
49313
|
source: isPlugin ? "plugin" : "project",
|
|
49342
|
-
...
|
|
49314
|
+
...path76 ? { path: path76 } : {},
|
|
49343
49315
|
...description ? { description } : {},
|
|
49344
49316
|
...isPlugin ? { plugin: name.split(":")[0] } : {}
|
|
49345
49317
|
});
|
|
@@ -50625,7 +50597,11 @@ var AuthGate = class {
|
|
|
50625
50597
|
st.pending = true;
|
|
50626
50598
|
let r;
|
|
50627
50599
|
try {
|
|
50628
|
-
r = await this.opts.authenticate(
|
|
50600
|
+
r = await this.opts.authenticate(
|
|
50601
|
+
parsed.data.token,
|
|
50602
|
+
parsed.data.selfUrl,
|
|
50603
|
+
parsed.data.selfSshPublicKey
|
|
50604
|
+
);
|
|
50629
50605
|
} finally {
|
|
50630
50606
|
st.pending = false;
|
|
50631
50607
|
}
|
|
@@ -51213,9 +51189,8 @@ var ContactStore = class {
|
|
|
51213
51189
|
return true;
|
|
51214
51190
|
}
|
|
51215
51191
|
/**
|
|
51216
|
-
* 更新单条 contact 的 SSH
|
|
51217
|
-
*
|
|
51218
|
-
* 那是 handler / UI 的责任。数组语义是完全替换(不 append)。
|
|
51192
|
+
* 更新单条 contact 的 SSH 授权开关(对齐 setPin pattern)。T-29 去沙箱后只改 sshAllowed;
|
|
51193
|
+
* exposedDirs 已废弃,本方法不再触碰它(保留 contact 原值不动)。
|
|
51219
51194
|
* @returns 是否命中:deviceId 不存在返 false;命中即 flush.
|
|
51220
51195
|
*/
|
|
51221
51196
|
setSshAccess(deviceId, opts) {
|
|
@@ -51223,8 +51198,7 @@ var ContactStore = class {
|
|
|
51223
51198
|
if (!existing) return false;
|
|
51224
51199
|
this.contacts.set(deviceId, {
|
|
51225
51200
|
...existing,
|
|
51226
|
-
sshAllowed: opts.sshAllowed
|
|
51227
|
-
exposedDirs: opts.exposedDirs
|
|
51201
|
+
sshAllowed: opts.sshAllowed
|
|
51228
51202
|
});
|
|
51229
51203
|
this.flush();
|
|
51230
51204
|
return true;
|
|
@@ -51272,7 +51246,10 @@ async function connectRemote(args) {
|
|
|
51272
51246
|
// 缺省时省略字段(不发空串),对方 schema selfUrl 是 .min(1).optional()。
|
|
51273
51247
|
// 决策 #16:身份字段(selfPrincipalId/selfDisplayName/selfDeviceId)已删——
|
|
51274
51248
|
// 对方从 connect token 签名背书取 deviceId/ownerId/provider,不信自报。
|
|
51275
|
-
...args.selfUrl ? { selfUrl: args.selfUrl } : {}
|
|
51249
|
+
...args.selfUrl ? { selfUrl: args.selfUrl } : {},
|
|
51250
|
+
// ssh identity 互换(spec 2026-07-15):自报本机 identity pubkey;缺省省略字段
|
|
51251
|
+
// (对方 schema selfSshPublicKey 是 .min(1).optional(),不发空串)。
|
|
51252
|
+
...args.selfSshPublicKey ? { selfSshPublicKey: args.selfSshPublicKey } : {}
|
|
51276
51253
|
})
|
|
51277
51254
|
);
|
|
51278
51255
|
await new Promise((resolve6, reject) => {
|
|
@@ -51347,7 +51324,9 @@ async function connectRemote(args) {
|
|
|
51347
51324
|
ownerId: f.owner.ownerId,
|
|
51348
51325
|
provider: f.owner.provider,
|
|
51349
51326
|
displayName: f.owner.displayName,
|
|
51350
|
-
grants: f.capability.grants
|
|
51327
|
+
grants: f.capability.grants,
|
|
51328
|
+
// ssh identity 互换(spec 2026-07-15):对方老版本不带 → 省略(不落 undefined 占位)
|
|
51329
|
+
...f.sshPublicKey ? { sshPublicKey: f.sshPublicKey } : {}
|
|
51351
51330
|
};
|
|
51352
51331
|
},
|
|
51353
51332
|
close: () => {
|
|
@@ -51368,6 +51347,8 @@ async function autoReverseContact(args) {
|
|
|
51368
51347
|
resource: { ...g2.resource },
|
|
51369
51348
|
actions: [...g2.actions]
|
|
51370
51349
|
}));
|
|
51350
|
+
const existing = args.store.get(args.deviceId) ?? void 0;
|
|
51351
|
+
const sshPublicKey = args.sshPublicKey ?? existing?.sshPublicKey;
|
|
51371
51352
|
const base = {
|
|
51372
51353
|
deviceId: args.deviceId,
|
|
51373
51354
|
ownerId: args.ownerId,
|
|
@@ -51377,12 +51358,17 @@ async function autoReverseContact(args) {
|
|
|
51377
51358
|
connectToken: "",
|
|
51378
51359
|
grants,
|
|
51379
51360
|
addedAt: now(),
|
|
51380
|
-
pinnedAt: null,
|
|
51381
|
-
sshAllowed: false,
|
|
51361
|
+
pinnedAt: existing?.pinnedAt ?? null,
|
|
51362
|
+
sshAllowed: existing?.sshAllowed ?? false,
|
|
51363
|
+
// ssh identity 互换(spec 2026-07-15):缺省不落 undefined 占位(JSON 持久化干净)
|
|
51364
|
+
...sshPublicKey ? { sshPublicKey } : {},
|
|
51382
51365
|
exposedDirs: []
|
|
51383
51366
|
};
|
|
51384
51367
|
args.store.upsert(base);
|
|
51385
51368
|
args.broadcast({ type: "contact:added", contact: base });
|
|
51369
|
+
if (base.sshAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
|
|
51370
|
+
args.rebuildAuthorizedKeys?.();
|
|
51371
|
+
}
|
|
51386
51372
|
if (!args.exchangeToken) return;
|
|
51387
51373
|
let token;
|
|
51388
51374
|
try {
|
|
@@ -53533,8 +53519,8 @@ var CLAWD_SSH_JAIL_SCRIPT = String.raw`#!/usr/bin/env bash
|
|
|
53533
53519
|
# 真实请求(interactive shell 时为空)。
|
|
53534
53520
|
#
|
|
53535
53521
|
# 职责:
|
|
53536
|
-
# 1. 读 ~/.clawd/contacts.json 校验 contact.sshAllowed
|
|
53537
|
-
# 2. 直接 exec shell
|
|
53522
|
+
# 1. 读 ~/.clawd/contacts.json 校验 contact.sshAllowed(不通过即拒)
|
|
53523
|
+
# 2. 直接 exec shell(去沙箱后无目录级限制)
|
|
53538
53524
|
|
|
53539
53525
|
set -euo pipefail
|
|
53540
53526
|
|
|
@@ -53561,13 +53547,15 @@ log_line INFO jail.entered "clawd-ssh-jail 起来 device=${"${DEVICE_ID}"} cmd=$
|
|
|
53561
53547
|
|
|
53562
53548
|
CONTACTS="${"${HOME}"}/.clawd/contacts.json"
|
|
53563
53549
|
if [ ! -f "$CONTACTS" ]; then
|
|
53564
|
-
log_line ERROR jail.entered "contacts.json
|
|
53550
|
+
log_line ERROR jail.entered "contacts.json 不存在,无法校验 sshAllowed"
|
|
53565
53551
|
echo "clawd-ssh-jail: contacts.json missing" >&2
|
|
53566
53552
|
exit 1
|
|
53567
53553
|
fi
|
|
53568
53554
|
|
|
53569
|
-
#
|
|
53570
|
-
|
|
53555
|
+
# 校验该 contact 的 sshAllowed(mac/linux 都自带 python3)。去沙箱后不再读目录白名单:
|
|
53556
|
+
# sshAllowed=false → DENIED exit 2;deviceId 未命中 → NOT_FOUND exit 3;命中且允许 → exit 0。
|
|
53557
|
+
# set -e 下 python 非 0 退出即终止脚本(不进 exec),只有 exit 0 才继续 exec shell。
|
|
53558
|
+
python3 -c "
|
|
53571
53559
|
import json, sys
|
|
53572
53560
|
with open('$CONTACTS') as f:
|
|
53573
53561
|
data = json.load(f)
|
|
@@ -53575,25 +53563,11 @@ for c in data.get('contacts', []):
|
|
|
53575
53563
|
if c.get('deviceId') == '$DEVICE_ID':
|
|
53576
53564
|
if not c.get('sshAllowed'):
|
|
53577
53565
|
print('DENIED', file=sys.stderr); sys.exit(2)
|
|
53578
|
-
for d in c.get('exposedDirs', []):
|
|
53579
|
-
print(d)
|
|
53580
53566
|
sys.exit(0)
|
|
53581
|
-
sys.exit(3)
|
|
53582
|
-
"
|
|
53583
|
-
|
|
53584
|
-
|
|
53585
|
-
if [ -n "$EXPOSED_JSON" ]; then
|
|
53586
|
-
log_line INFO jail.entered "exposedDirs 提示已加载,放行 exec shell"
|
|
53587
|
-
# 告知 SSH client 建议目录(发到 stderr)。去沙箱后这些只是建议,不做目录级强制限制;
|
|
53588
|
-
# exposedDirs 仅供对端参考,运行时读,A 改后下次拨号即感知。
|
|
53589
|
-
echo "[clawd-ssh-jail] Suggested working directories for this contact:" >&2
|
|
53590
|
-
while IFS= read -r d; do
|
|
53591
|
-
echo " - $d" >&2
|
|
53592
|
-
done <<< "$EXPOSED_JSON"
|
|
53593
|
-
echo "[clawd-ssh-jail] Note: no directory-level restriction is enforced." >&2
|
|
53594
|
-
else
|
|
53595
|
-
log_line INFO jail.entered "sshAllowed=true 且未配 exposedDirs,直接放行 exec shell"
|
|
53596
|
-
fi
|
|
53567
|
+
print('NOT_FOUND', file=sys.stderr); sys.exit(3)
|
|
53568
|
+
"
|
|
53569
|
+
|
|
53570
|
+
log_line INFO jail.entered "sshAllowed 校验通过,放行 exec shell"
|
|
53597
53571
|
|
|
53598
53572
|
CMD="${"${SSH_ORIGINAL_COMMAND:-}"}"
|
|
53599
53573
|
if [ -z "$CMD" ]; then
|
|
@@ -53877,10 +53851,10 @@ function rebuildAuthorizedKeys(store, sshdDir) {
|
|
|
53877
53851
|
if (!c.sshAllowed) continue;
|
|
53878
53852
|
const safe = /^[A-Za-z0-9_.-]+$/.test(c.deviceId);
|
|
53879
53853
|
if (!safe) continue;
|
|
53880
|
-
const pubkey =
|
|
53881
|
-
if (!pubkey) continue;
|
|
53854
|
+
const pubkey = c.sshPublicKey?.trim();
|
|
53855
|
+
if (!pubkey || /[\r\n"]/.test(pubkey)) continue;
|
|
53882
53856
|
const bin = jailBinPath();
|
|
53883
|
-
lines.push(`command="${bin} ${c.deviceId}",restrict ${pubkey
|
|
53857
|
+
lines.push(`command="${bin} ${c.deviceId}",restrict ${pubkey}`);
|
|
53884
53858
|
lines.push(`# contact:${c.deviceId}`);
|
|
53885
53859
|
}
|
|
53886
53860
|
const body = lines.join("\n") + "\n";
|
|
@@ -53888,25 +53862,59 @@ function rebuildAuthorizedKeys(store, sshdDir) {
|
|
|
53888
53862
|
import_node_fs40.default.writeFileSync(tmp, body, { mode: 384 });
|
|
53889
53863
|
import_node_fs40.default.renameSync(tmp, target);
|
|
53890
53864
|
}
|
|
53891
|
-
|
|
53892
|
-
|
|
53893
|
-
|
|
53865
|
+
|
|
53866
|
+
// src/sshd/ssh-identity.ts
|
|
53867
|
+
var import_node_fs41 = __toESM(require("fs"), 1);
|
|
53868
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
53869
|
+
var import_node_child_process12 = require("child_process");
|
|
53870
|
+
var IDENTITY_DIR = "ssh-identity";
|
|
53871
|
+
var KEY_FILE = "id_ed25519";
|
|
53872
|
+
function identityPaths(dataDir) {
|
|
53873
|
+
const privPath = import_node_path41.default.join(dataDir, IDENTITY_DIR, KEY_FILE);
|
|
53874
|
+
return { privPath, pubPath: `${privPath}.pub` };
|
|
53875
|
+
}
|
|
53876
|
+
async function ensureSshIdentity(dataDir, opts = {}) {
|
|
53877
|
+
const { privPath, pubPath } = identityPaths(dataDir);
|
|
53878
|
+
import_node_fs41.default.mkdirSync(import_node_path41.default.dirname(privPath), { recursive: true, mode: 448 });
|
|
53879
|
+
if (import_node_fs41.default.existsSync(privPath) && import_node_fs41.default.existsSync(pubPath)) {
|
|
53880
|
+
return { privateKeyPath: privPath, publicKeyLine: import_node_fs41.default.readFileSync(pubPath, "utf8").trim() };
|
|
53881
|
+
}
|
|
53882
|
+
const bin = opts.keygenBin ?? "/usr/bin/ssh-keygen";
|
|
53883
|
+
await new Promise((resolve6, reject) => {
|
|
53884
|
+
const p2 = (opts.spawnImpl ?? import_node_child_process12.spawn)(
|
|
53885
|
+
bin,
|
|
53886
|
+
["-t", "ed25519", "-f", privPath, "-N", "", "-q", "-C", "clawd-ssh-identity"],
|
|
53887
|
+
{ stdio: "ignore" }
|
|
53888
|
+
);
|
|
53889
|
+
p2.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`ssh-keygen exit ${code}`)));
|
|
53890
|
+
p2.on("error", reject);
|
|
53891
|
+
});
|
|
53892
|
+
try {
|
|
53893
|
+
import_node_fs41.default.chmodSync(privPath, 384);
|
|
53894
|
+
} catch {
|
|
53895
|
+
}
|
|
53896
|
+
try {
|
|
53897
|
+
import_node_fs41.default.chmodSync(pubPath, 420);
|
|
53898
|
+
} catch {
|
|
53899
|
+
}
|
|
53900
|
+
return { privateKeyPath: privPath, publicKeyLine: import_node_fs41.default.readFileSync(pubPath, "utf8").trim() };
|
|
53901
|
+
}
|
|
53902
|
+
function readSshIdentityPublicKey(dataDir) {
|
|
53894
53903
|
try {
|
|
53895
|
-
return
|
|
53904
|
+
return import_node_fs41.default.readFileSync(identityPaths(dataDir).pubPath, "utf8").trim();
|
|
53896
53905
|
} catch {
|
|
53897
53906
|
return null;
|
|
53898
53907
|
}
|
|
53899
53908
|
}
|
|
53900
53909
|
|
|
53901
|
-
// src/sshd/
|
|
53902
|
-
var
|
|
53903
|
-
var import_node_path42 = __toESM(require("path"), 1);
|
|
53910
|
+
// src/sshd/ssh-tunnel-relay.ts
|
|
53911
|
+
var import_node_net2 = __toESM(require("net"), 1);
|
|
53904
53912
|
|
|
53905
53913
|
// src/sshd/contact-ssh-log.ts
|
|
53906
|
-
var
|
|
53907
|
-
var
|
|
53914
|
+
var import_node_fs42 = __toESM(require("fs"), 1);
|
|
53915
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
53908
53916
|
function createContactSshLog(dataDir) {
|
|
53909
|
-
const file =
|
|
53917
|
+
const file = import_node_path42.default.join(dataDir, "log", "contact-ssh.log");
|
|
53910
53918
|
function append(level, tag, message, meta) {
|
|
53911
53919
|
const time = (/* @__PURE__ */ new Date()).toISOString();
|
|
53912
53920
|
let line = `[${time}] [${level}] [${tag}] ${message}`;
|
|
@@ -53919,8 +53927,8 @@ function createContactSshLog(dataDir) {
|
|
|
53919
53927
|
}
|
|
53920
53928
|
line += "\n";
|
|
53921
53929
|
try {
|
|
53922
|
-
|
|
53923
|
-
|
|
53930
|
+
import_node_fs42.default.mkdirSync(import_node_path42.default.dirname(file), { recursive: true });
|
|
53931
|
+
import_node_fs42.default.appendFileSync(file, line, { mode: 384 });
|
|
53924
53932
|
} catch {
|
|
53925
53933
|
}
|
|
53926
53934
|
}
|
|
@@ -53939,118 +53947,7 @@ var nullContactSshLog = {
|
|
|
53939
53947
|
}
|
|
53940
53948
|
};
|
|
53941
53949
|
|
|
53942
|
-
// src/sshd/contact-key-puller.ts
|
|
53943
|
-
var CONTACT_KEYS_DIR = "contact-ssh-keys";
|
|
53944
|
-
function safeContactKeyPath(dataDir, deviceId) {
|
|
53945
|
-
const safeId = deviceId.replace(/[\/\\]/g, "_");
|
|
53946
|
-
return import_node_path42.default.join(dataDir, CONTACT_KEYS_DIR, `${safeId}.ed25519`);
|
|
53947
|
-
}
|
|
53948
|
-
async function pullContactSshKeyOnce(deps) {
|
|
53949
|
-
const forward = deps.forwardImpl ?? ((c) => forwardContactSshKeyIssueToPeer({
|
|
53950
|
-
contact: { remoteUrl: c.remoteUrl, connectToken: c.connectToken },
|
|
53951
|
-
selfDeviceIdForRequest: c.deviceId
|
|
53952
|
-
}));
|
|
53953
|
-
const contacts = deps.store.list().filter((c) => c.connectToken.length > 0);
|
|
53954
|
-
const results = await Promise.all(
|
|
53955
|
-
contacts.map(async (c) => {
|
|
53956
|
-
try {
|
|
53957
|
-
const r = await forward(c);
|
|
53958
|
-
return { contact: c, result: r };
|
|
53959
|
-
} catch (err) {
|
|
53960
|
-
return {
|
|
53961
|
-
contact: c,
|
|
53962
|
-
result: {
|
|
53963
|
-
ok: false,
|
|
53964
|
-
code: "NETWORK",
|
|
53965
|
-
message: err instanceof Error ? err.message : String(err)
|
|
53966
|
-
}
|
|
53967
|
-
};
|
|
53968
|
-
}
|
|
53969
|
-
})
|
|
53970
|
-
);
|
|
53971
|
-
const errors = [];
|
|
53972
|
-
let pulled = 0;
|
|
53973
|
-
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
53974
|
-
for (const { contact, result } of results) {
|
|
53975
|
-
if (result.ok) {
|
|
53976
|
-
writeKeyFile(deps.dataDir, contact.deviceId, result.privateKeyPem);
|
|
53977
|
-
pulled++;
|
|
53978
|
-
deps.logger?.info("contact-key-puller: pulled", { deviceId: contact.deviceId });
|
|
53979
|
-
sshLog.info("key.pull.success", "B \u4FA7\u4ECE A \u62C9\u5230 privkey \u5E76\u843D\u76D8", {
|
|
53980
|
-
peerDeviceId: contact.deviceId,
|
|
53981
|
-
peerDisplayName: contact.displayName
|
|
53982
|
-
});
|
|
53983
|
-
} else if (result.code === "UNAUTHORIZED" || result.code === "FORBIDDEN") {
|
|
53984
|
-
const hadStale = removeKeyFile(deps.dataDir, contact.deviceId);
|
|
53985
|
-
sshLog.info("key.pull.rejected", "A \u4FA7\u672A\u6388\u6743\u6211 SSH\uFF08\u6B63\u5E38\u72B6\u6001\uFF1B\u8F6E\u8BE2\u7EE7\u7EED\uFF09", {
|
|
53986
|
-
peerDeviceId: contact.deviceId,
|
|
53987
|
-
peerDisplayName: contact.displayName,
|
|
53988
|
-
clearedStalePrivkey: hadStale
|
|
53989
|
-
});
|
|
53990
|
-
} else {
|
|
53991
|
-
errors.push({
|
|
53992
|
-
deviceId: contact.deviceId,
|
|
53993
|
-
code: result.code,
|
|
53994
|
-
message: result.message
|
|
53995
|
-
});
|
|
53996
|
-
deps.logger?.warn("contact-key-puller: pull failed", {
|
|
53997
|
-
deviceId: contact.deviceId,
|
|
53998
|
-
code: result.code,
|
|
53999
|
-
message: result.message
|
|
54000
|
-
});
|
|
54001
|
-
sshLog.warn("key.pull.error", "\u62C9 privkey \u65F6\u7F51\u7EDC/\u534F\u8BAE\u9519\u8BEF", {
|
|
54002
|
-
peerDeviceId: contact.deviceId,
|
|
54003
|
-
code: result.code,
|
|
54004
|
-
message: result.message
|
|
54005
|
-
});
|
|
54006
|
-
}
|
|
54007
|
-
}
|
|
54008
|
-
return { pulled, errors };
|
|
54009
|
-
}
|
|
54010
|
-
function writeKeyFile(dataDir, deviceId, pem) {
|
|
54011
|
-
const p2 = safeContactKeyPath(dataDir, deviceId);
|
|
54012
|
-
import_node_fs42.default.mkdirSync(import_node_path42.default.dirname(p2), { recursive: true, mode: 448 });
|
|
54013
|
-
import_node_fs42.default.writeFileSync(p2, pem, { mode: 384 });
|
|
54014
|
-
}
|
|
54015
|
-
function removeKeyFile(dataDir, deviceId) {
|
|
54016
|
-
try {
|
|
54017
|
-
import_node_fs42.default.unlinkSync(safeContactKeyPath(dataDir, deviceId));
|
|
54018
|
-
return true;
|
|
54019
|
-
} catch {
|
|
54020
|
-
return false;
|
|
54021
|
-
}
|
|
54022
|
-
}
|
|
54023
|
-
var ContactKeyPuller = class {
|
|
54024
|
-
constructor(deps) {
|
|
54025
|
-
this.deps = deps;
|
|
54026
|
-
}
|
|
54027
|
-
deps;
|
|
54028
|
-
timer = null;
|
|
54029
|
-
async start() {
|
|
54030
|
-
const interval = this.deps.intervalMs ?? 6e4;
|
|
54031
|
-
void this.tick();
|
|
54032
|
-
this.timer = setInterval(() => void this.tick(), interval);
|
|
54033
|
-
this.timer.unref();
|
|
54034
|
-
}
|
|
54035
|
-
stop() {
|
|
54036
|
-
if (this.timer) {
|
|
54037
|
-
clearInterval(this.timer);
|
|
54038
|
-
this.timer = null;
|
|
54039
|
-
}
|
|
54040
|
-
}
|
|
54041
|
-
async tick() {
|
|
54042
|
-
try {
|
|
54043
|
-
await pullContactSshKeyOnce(this.deps);
|
|
54044
|
-
} catch (err) {
|
|
54045
|
-
this.deps.logger?.warn("contact-key-puller: tick failed", {
|
|
54046
|
-
err: err instanceof Error ? err.message : String(err)
|
|
54047
|
-
});
|
|
54048
|
-
}
|
|
54049
|
-
}
|
|
54050
|
-
};
|
|
54051
|
-
|
|
54052
53950
|
// src/sshd/ssh-tunnel-relay.ts
|
|
54053
|
-
var import_node_net2 = __toESM(require("net"), 1);
|
|
54054
53951
|
async function handleSshTunnelUpgrade(req, socket, head, deps) {
|
|
54055
53952
|
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
54056
53953
|
const clientAddr = (req.socket && "remoteAddress" in req.socket ? req.socket.remoteAddress : null) ?? "unknown";
|
|
@@ -54641,9 +54538,9 @@ var CentralClientError = class extends Error {
|
|
|
54641
54538
|
code;
|
|
54642
54539
|
cause;
|
|
54643
54540
|
};
|
|
54644
|
-
async function centralRequest(opts,
|
|
54541
|
+
async function centralRequest(opts, path76, init) {
|
|
54645
54542
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
54646
|
-
const url = `${opts.api.replace(/\/+$/, "")}${
|
|
54543
|
+
const url = `${opts.api.replace(/\/+$/, "")}${path76}`;
|
|
54647
54544
|
const ctrl = new AbortController();
|
|
54648
54545
|
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
54649
54546
|
let res;
|
|
@@ -55580,11 +55477,11 @@ init_protocol();
|
|
|
55580
55477
|
init_protocol();
|
|
55581
55478
|
|
|
55582
55479
|
// src/workspace/git.ts
|
|
55583
|
-
var
|
|
55480
|
+
var import_node_child_process13 = require("child_process");
|
|
55584
55481
|
var import_node_fs47 = __toESM(require("fs"), 1);
|
|
55585
55482
|
var import_node_path48 = __toESM(require("path"), 1);
|
|
55586
55483
|
var import_node_util = require("util");
|
|
55587
|
-
var pexec = (0, import_node_util.promisify)(
|
|
55484
|
+
var pexec = (0, import_node_util.promisify)(import_node_child_process13.execFile);
|
|
55588
55485
|
function normalizePath(p2) {
|
|
55589
55486
|
const resolved = import_node_path48.default.resolve(p2);
|
|
55590
55487
|
try {
|
|
@@ -55997,51 +55894,6 @@ function buildContactHandlers(deps) {
|
|
|
55997
55894
|
|
|
55998
55895
|
// src/handlers/contact-ssh.ts
|
|
55999
55896
|
init_protocol();
|
|
56000
|
-
|
|
56001
|
-
// src/sshd/key-issue.ts
|
|
56002
|
-
var import_node_fs48 = __toESM(require("fs"), 1);
|
|
56003
|
-
var import_node_path49 = __toESM(require("path"), 1);
|
|
56004
|
-
var import_node_child_process13 = require("child_process");
|
|
56005
|
-
function safeDeviceId(deviceId) {
|
|
56006
|
-
return deviceId.replace(/[\/\\]/g, "_");
|
|
56007
|
-
}
|
|
56008
|
-
async function issueContactSshKey(deviceId, sshdDir, opts = {}) {
|
|
56009
|
-
const safeId = safeDeviceId(deviceId);
|
|
56010
|
-
const keysDir = import_node_path49.default.join(sshdDir, "keys");
|
|
56011
|
-
import_node_fs48.default.mkdirSync(keysDir, { recursive: true, mode: 448 });
|
|
56012
|
-
const privPath = import_node_path49.default.join(keysDir, `${safeId}.ed25519`);
|
|
56013
|
-
const pubPath = `${privPath}.pub`;
|
|
56014
|
-
if (import_node_fs48.default.existsSync(privPath) && import_node_fs48.default.existsSync(pubPath)) {
|
|
56015
|
-
return {
|
|
56016
|
-
privateKeyPem: import_node_fs48.default.readFileSync(privPath, "utf8"),
|
|
56017
|
-
publicKeyLine: import_node_fs48.default.readFileSync(pubPath, "utf8").trim()
|
|
56018
|
-
};
|
|
56019
|
-
}
|
|
56020
|
-
const bin = opts.keygenBin ?? "/usr/bin/ssh-keygen";
|
|
56021
|
-
await new Promise((resolve6, reject) => {
|
|
56022
|
-
const p2 = (opts.spawnImpl ?? import_node_child_process13.spawn)(
|
|
56023
|
-
bin,
|
|
56024
|
-
["-t", "ed25519", "-f", privPath, "-N", "", "-q", "-C", `clawd-contact-${safeId}`],
|
|
56025
|
-
{ stdio: "ignore" }
|
|
56026
|
-
);
|
|
56027
|
-
p2.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`ssh-keygen exit ${code}`)));
|
|
56028
|
-
p2.on("error", reject);
|
|
56029
|
-
});
|
|
56030
|
-
try {
|
|
56031
|
-
import_node_fs48.default.chmodSync(privPath, 384);
|
|
56032
|
-
} catch {
|
|
56033
|
-
}
|
|
56034
|
-
try {
|
|
56035
|
-
import_node_fs48.default.chmodSync(pubPath, 420);
|
|
56036
|
-
} catch {
|
|
56037
|
-
}
|
|
56038
|
-
return {
|
|
56039
|
-
privateKeyPem: import_node_fs48.default.readFileSync(privPath, "utf8"),
|
|
56040
|
-
publicKeyLine: import_node_fs48.default.readFileSync(pubPath, "utf8").trim()
|
|
56041
|
-
};
|
|
56042
|
-
}
|
|
56043
|
-
|
|
56044
|
-
// src/handlers/contact-ssh.ts
|
|
56045
55897
|
function ensureOwner2(ctx) {
|
|
56046
55898
|
if (!ctx || ctx.principal.kind !== "owner") {
|
|
56047
55899
|
throw new ClawdError(
|
|
@@ -56050,15 +55902,6 @@ function ensureOwner2(ctx) {
|
|
|
56050
55902
|
);
|
|
56051
55903
|
}
|
|
56052
55904
|
}
|
|
56053
|
-
function ensureGuest(ctx) {
|
|
56054
|
-
if (!ctx || ctx.principal.kind !== "guest") {
|
|
56055
|
-
throw new ClawdError(
|
|
56056
|
-
ERROR_CODES.UNAUTHORIZED,
|
|
56057
|
-
"UNAUTHORIZED: contact:sshKey:issue requires guest ctx"
|
|
56058
|
-
);
|
|
56059
|
-
}
|
|
56060
|
-
return ctx.principal.id;
|
|
56061
|
-
}
|
|
56062
55905
|
function buildContactSshHandlers(deps) {
|
|
56063
55906
|
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
56064
55907
|
const setSshAccess = async (frame, _client, ctx) => {
|
|
@@ -56066,8 +55909,7 @@ function buildContactSshHandlers(deps) {
|
|
|
56066
55909
|
const { type: _t, requestId: _r, ...rest } = frame;
|
|
56067
55910
|
const args = ContactSetSshAccessArgsSchema.parse(rest);
|
|
56068
55911
|
const hit = deps.store.setSshAccess(args.deviceId, {
|
|
56069
|
-
sshAllowed: args.sshAllowed
|
|
56070
|
-
exposedDirs: args.exposedDirs
|
|
55912
|
+
sshAllowed: args.sshAllowed
|
|
56071
55913
|
});
|
|
56072
55914
|
if (!hit) {
|
|
56073
55915
|
sshLog.warn("authz.setSshAccess", "owner \u5C1D\u8BD5\u6539 SSH \u6388\u6743\u4F46 contact \u4E0D\u5728 store", {
|
|
@@ -56081,65 +55923,23 @@ function buildContactSshHandlers(deps) {
|
|
|
56081
55923
|
rebuildAuthorizedKeys(deps.store, deps.sshdDir);
|
|
56082
55924
|
sshLog.info("authz.setSshAccess", "owner \u6539\u4E86 SSH \u6388\u6743 \u2192 authorized_keys \u5DF2\u91CD\u5EFA", {
|
|
56083
55925
|
peerDeviceId: args.deviceId,
|
|
56084
|
-
sshAllowed: args.sshAllowed
|
|
56085
|
-
exposedDirs: args.exposedDirs
|
|
55926
|
+
sshAllowed: args.sshAllowed
|
|
56086
55927
|
});
|
|
56087
55928
|
deps.broadcast({
|
|
56088
55929
|
type: "contact:ssh-access-updated",
|
|
56089
55930
|
deviceId: args.deviceId,
|
|
56090
|
-
sshAllowed: args.sshAllowed
|
|
56091
|
-
exposedDirs: args.exposedDirs
|
|
55931
|
+
sshAllowed: args.sshAllowed
|
|
56092
55932
|
});
|
|
56093
55933
|
return {
|
|
56094
55934
|
response: {
|
|
56095
55935
|
type: "contact:setSshAccess:ok",
|
|
56096
55936
|
deviceId: args.deviceId,
|
|
56097
|
-
sshAllowed: args.sshAllowed
|
|
56098
|
-
exposedDirs: args.exposedDirs
|
|
56099
|
-
}
|
|
56100
|
-
};
|
|
56101
|
-
};
|
|
56102
|
-
const sshKeyIssue = async (frame, _client, ctx) => {
|
|
56103
|
-
const callerDeviceId = ensureGuest(ctx);
|
|
56104
|
-
const { type: _t, requestId: _r, ...rest } = frame;
|
|
56105
|
-
ContactSshKeyIssueArgsSchema.parse(rest);
|
|
56106
|
-
const contact = deps.store.get(callerDeviceId);
|
|
56107
|
-
if (!contact || !contact.sshAllowed) {
|
|
56108
|
-
sshLog.info(
|
|
56109
|
-
"key.issue.rejected",
|
|
56110
|
-
contact ? "guest \u8BF7\u6C42 SSH key \u4F46 owner \u672A\u6388\u6743" : "guest \u8BF7\u6C42 SSH key \u4F46\u4E0D\u5728\u6211\u7684 contactStore",
|
|
56111
|
-
{
|
|
56112
|
-
peerDeviceId: callerDeviceId,
|
|
56113
|
-
peerDisplayName: contact?.displayName
|
|
56114
|
-
}
|
|
56115
|
-
);
|
|
56116
|
-
throw new ClawdError(
|
|
56117
|
-
ERROR_CODES.UNAUTHORIZED,
|
|
56118
|
-
`UNAUTHORIZED: contact ${callerDeviceId} not authorized for SSH`
|
|
56119
|
-
);
|
|
56120
|
-
}
|
|
56121
|
-
const { privateKeyPem, publicKeyLine } = await issueContactSshKey(callerDeviceId, deps.sshdDir);
|
|
56122
|
-
rebuildAuthorizedKeys(deps.store, deps.sshdDir);
|
|
56123
|
-
sshLog.info(
|
|
56124
|
-
"key.issue.success",
|
|
56125
|
-
"A \u4FA7\u53D1 privkey \u7ED9 guest\uFF08\u5E42\u7B49\uFF09\uFF0Cauthorized_keys \u5DF2\u91CD\u5EFA",
|
|
56126
|
-
{
|
|
56127
|
-
peerDeviceId: callerDeviceId,
|
|
56128
|
-
peerDisplayName: contact.displayName,
|
|
56129
|
-
publicKeyFingerprint: publicKeyLine.slice(0, 32) + "..."
|
|
56130
|
-
}
|
|
56131
|
-
);
|
|
56132
|
-
return {
|
|
56133
|
-
response: {
|
|
56134
|
-
type: "contact:sshKey:issue:ok",
|
|
56135
|
-
privateKeyPem,
|
|
56136
|
-
publicKeyLine
|
|
55937
|
+
sshAllowed: args.sshAllowed
|
|
56137
55938
|
}
|
|
56138
55939
|
};
|
|
56139
55940
|
};
|
|
56140
55941
|
return {
|
|
56141
|
-
"contact:setSshAccess": setSshAccess
|
|
56142
|
-
"contact:sshKey:issue": sshKeyIssue
|
|
55942
|
+
"contact:setSshAccess": setSshAccess
|
|
56143
55943
|
};
|
|
56144
55944
|
}
|
|
56145
55945
|
|
|
@@ -56191,12 +55991,14 @@ function buildWhoamiHandler(deps) {
|
|
|
56191
55991
|
grantedPersonas.push({ id: file.personaId, displayName: file.label });
|
|
56192
55992
|
}
|
|
56193
55993
|
}
|
|
55994
|
+
const sshPublicKey = deps.sshIdentityPublicKey();
|
|
56194
55995
|
return {
|
|
56195
55996
|
response: {
|
|
56196
55997
|
type: "whoami:ok",
|
|
56197
55998
|
owner,
|
|
56198
55999
|
capability,
|
|
56199
|
-
grantedPersonas
|
|
56000
|
+
grantedPersonas,
|
|
56001
|
+
...sshPublicKey ? { sshPublicKey } : {}
|
|
56200
56002
|
}
|
|
56201
56003
|
};
|
|
56202
56004
|
};
|
|
@@ -56272,7 +56074,9 @@ function buildDeviceHandlers(deps) {
|
|
|
56272
56074
|
// 自动反向(spec 决策 #11):自报本机可达地址,让对方反向加我为联系人;
|
|
56273
56075
|
// 本机无 tunnel → null → connectRemote 省略 selfUrl 字段(不造假数据)。
|
|
56274
56076
|
// 身份不自报:对方从 token 签名背书取我的 deviceId/ownerId/provider(决策 #16)。
|
|
56275
|
-
selfUrl: deps.selfUrl() ?? void 0
|
|
56077
|
+
selfUrl: deps.selfUrl() ?? void 0,
|
|
56078
|
+
// ssh identity 互换(spec 2026-07-15):自报本机 pubkey;未生成 → 省略
|
|
56079
|
+
selfSshPublicKey: deps.selfSshPublicKey() ?? void 0
|
|
56276
56080
|
});
|
|
56277
56081
|
} catch (e) {
|
|
56278
56082
|
throw new ClawdError(
|
|
@@ -56282,6 +56086,8 @@ function buildDeviceHandlers(deps) {
|
|
|
56282
56086
|
}
|
|
56283
56087
|
try {
|
|
56284
56088
|
const wh = await conn.whoami();
|
|
56089
|
+
const existing = deps.store.get(args.deviceId) ?? void 0;
|
|
56090
|
+
const sshPublicKey = wh.sshPublicKey ?? existing?.sshPublicKey;
|
|
56285
56091
|
const contact = {
|
|
56286
56092
|
deviceId: args.deviceId,
|
|
56287
56093
|
ownerId: wh.ownerId,
|
|
@@ -56296,12 +56102,18 @@ function buildDeviceHandlers(deps) {
|
|
|
56296
56102
|
connectToken: exchanged.token,
|
|
56297
56103
|
grants: wh.grants,
|
|
56298
56104
|
addedAt: now(),
|
|
56299
|
-
pinnedAt: null,
|
|
56300
|
-
sshAllowed: false,
|
|
56105
|
+
pinnedAt: existing?.pinnedAt ?? null,
|
|
56106
|
+
sshAllowed: existing?.sshAllowed ?? false,
|
|
56107
|
+
// ssh identity 互换(spec 2026-07-15):对方 whoami:ok 下发的 pubkey 落库;
|
|
56108
|
+
// 对方老版本不带 → 保留既有值 / 不落 undefined 占位
|
|
56109
|
+
...sshPublicKey ? { sshPublicKey } : {},
|
|
56301
56110
|
exposedDirs: []
|
|
56302
56111
|
};
|
|
56303
56112
|
deps.store.upsert(contact);
|
|
56304
56113
|
deps.broadcast({ type: "contact:added", contact });
|
|
56114
|
+
if (contact.sshAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
|
|
56115
|
+
deps.rebuildAuthorizedKeys?.();
|
|
56116
|
+
}
|
|
56305
56117
|
return {
|
|
56306
56118
|
response: {
|
|
56307
56119
|
type: "device:connect:ok",
|
|
@@ -56454,7 +56266,7 @@ function buildPersonaHandlers(deps) {
|
|
|
56454
56266
|
}
|
|
56455
56267
|
|
|
56456
56268
|
// src/handlers/attachment.ts
|
|
56457
|
-
var
|
|
56269
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
56458
56270
|
init_protocol();
|
|
56459
56271
|
init_protocol();
|
|
56460
56272
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -56534,12 +56346,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
56534
56346
|
`session ${args.sessionId} scope unresolved`
|
|
56535
56347
|
);
|
|
56536
56348
|
}
|
|
56537
|
-
const cwdAbs =
|
|
56538
|
-
const candidateAbs =
|
|
56349
|
+
const cwdAbs = import_node_path49.default.resolve(sessionFile.cwd);
|
|
56350
|
+
const candidateAbs = import_node_path49.default.isAbsolute(args.relPath) ? import_node_path49.default.resolve(args.relPath) : import_node_path49.default.resolve(cwdAbs, args.relPath);
|
|
56539
56351
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
|
|
56540
56352
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
56541
56353
|
const entry = entries.find((e) => {
|
|
56542
|
-
const storedAbs =
|
|
56354
|
+
const storedAbs = import_node_path49.default.isAbsolute(e.relPath) ? import_node_path49.default.resolve(e.relPath) : import_node_path49.default.resolve(cwdAbs, e.relPath);
|
|
56543
56355
|
return storedAbs === candidateAbs && !e.stale;
|
|
56544
56356
|
});
|
|
56545
56357
|
if (!entry) {
|
|
@@ -56564,7 +56376,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
56564
56376
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
56565
56377
|
const f = deps.sessionStore.read(sessionId);
|
|
56566
56378
|
if (!f) return;
|
|
56567
|
-
assertGuestAttachmentPath(ctx,
|
|
56379
|
+
assertGuestAttachmentPath(ctx, import_node_path49.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
|
|
56568
56380
|
}
|
|
56569
56381
|
const groupAdd = async (frame, _client, ctx) => {
|
|
56570
56382
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -56579,8 +56391,8 @@ function buildAttachmentHandlers(deps) {
|
|
|
56579
56391
|
if (!scope) {
|
|
56580
56392
|
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
|
|
56581
56393
|
}
|
|
56582
|
-
const cwdAbs =
|
|
56583
|
-
const candidateAbs =
|
|
56394
|
+
const cwdAbs = import_node_path49.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
|
|
56395
|
+
const candidateAbs = import_node_path49.default.isAbsolute(args.relPath) ? import_node_path49.default.resolve(args.relPath) : import_node_path49.default.resolve(cwdAbs, args.relPath);
|
|
56584
56396
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
|
|
56585
56397
|
const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
|
|
56586
56398
|
const size = 0;
|
|
@@ -56639,19 +56451,19 @@ function buildAttachmentHandlers(deps) {
|
|
|
56639
56451
|
|
|
56640
56452
|
// src/handlers/extension.ts
|
|
56641
56453
|
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
56642
|
-
var
|
|
56454
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
56643
56455
|
init_protocol();
|
|
56644
56456
|
|
|
56645
56457
|
// src/extension/bundle-zip.ts
|
|
56646
56458
|
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
56647
|
-
var
|
|
56459
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
56648
56460
|
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
56649
56461
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
56650
56462
|
async function bundleExtensionDir(dir) {
|
|
56651
56463
|
const entries = await listFilesSorted(dir);
|
|
56652
56464
|
const zip = new import_jszip2.default();
|
|
56653
56465
|
for (const rel of entries) {
|
|
56654
|
-
const abs =
|
|
56466
|
+
const abs = import_node_path50.default.join(dir, rel);
|
|
56655
56467
|
const content = await import_promises5.default.readFile(abs);
|
|
56656
56468
|
zip.file(rel, content, { date: FIXED_DATE });
|
|
56657
56469
|
}
|
|
@@ -56672,7 +56484,7 @@ async function listFilesSorted(rootDir) {
|
|
|
56672
56484
|
return out;
|
|
56673
56485
|
}
|
|
56674
56486
|
async function walk(absRoot, relPrefix, out) {
|
|
56675
|
-
const dirAbs =
|
|
56487
|
+
const dirAbs = import_node_path50.default.join(absRoot, relPrefix);
|
|
56676
56488
|
const entries = await import_promises5.default.readdir(dirAbs, { withFileTypes: true });
|
|
56677
56489
|
for (const e of entries) {
|
|
56678
56490
|
if (IGNORE_BASENAMES.has(e.name)) continue;
|
|
@@ -56726,25 +56538,25 @@ function computePublishCheck(args) {
|
|
|
56726
56538
|
|
|
56727
56539
|
// src/extension/install-flow.ts
|
|
56728
56540
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
56729
|
-
var
|
|
56541
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
56730
56542
|
var import_node_os19 = __toESM(require("os"), 1);
|
|
56731
56543
|
var import_node_crypto15 = __toESM(require("crypto"), 1);
|
|
56732
56544
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
56733
56545
|
|
|
56734
56546
|
// src/extension/paths.ts
|
|
56735
56547
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
56736
|
-
var
|
|
56548
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
56737
56549
|
function clawdHomeRoot(override) {
|
|
56738
|
-
return override ?? process.env.CLAWD_HOME ??
|
|
56550
|
+
return override ?? process.env.CLAWD_HOME ?? import_node_path51.default.join(import_node_os18.default.homedir(), ".clawd");
|
|
56739
56551
|
}
|
|
56740
56552
|
function extensionsRoot(override) {
|
|
56741
|
-
return
|
|
56553
|
+
return import_node_path51.default.join(clawdHomeRoot(override), "extensions");
|
|
56742
56554
|
}
|
|
56743
56555
|
function publishedChannelsFile(override) {
|
|
56744
|
-
return
|
|
56556
|
+
return import_node_path51.default.join(clawdHomeRoot(override), "extensions-published.json");
|
|
56745
56557
|
}
|
|
56746
56558
|
function bundleCacheRoot(override) {
|
|
56747
|
-
return
|
|
56559
|
+
return import_node_path51.default.join(clawdHomeRoot(override), "extension-bundles");
|
|
56748
56560
|
}
|
|
56749
56561
|
|
|
56750
56562
|
// src/extension/install-flow.ts
|
|
@@ -56771,7 +56583,7 @@ async function installFromChannel(args, deps) {
|
|
|
56771
56583
|
throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56772
56584
|
}
|
|
56773
56585
|
for (const name of Object.keys(zip.files)) {
|
|
56774
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
56586
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path52.default.isAbsolute(name)) {
|
|
56775
56587
|
throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56776
56588
|
}
|
|
56777
56589
|
}
|
|
@@ -56803,7 +56615,7 @@ async function installFromChannel(args, deps) {
|
|
|
56803
56615
|
);
|
|
56804
56616
|
}
|
|
56805
56617
|
const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
|
|
56806
|
-
const destDir =
|
|
56618
|
+
const destDir = import_node_path52.default.join(deps.extensionsRoot, localExtId);
|
|
56807
56619
|
let destExists = false;
|
|
56808
56620
|
try {
|
|
56809
56621
|
await import_promises6.default.access(destDir);
|
|
@@ -56817,16 +56629,16 @@ async function installFromChannel(args, deps) {
|
|
|
56817
56629
|
);
|
|
56818
56630
|
}
|
|
56819
56631
|
const stage = await import_promises6.default.mkdtemp(
|
|
56820
|
-
|
|
56632
|
+
import_node_path52.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
|
|
56821
56633
|
);
|
|
56822
56634
|
try {
|
|
56823
56635
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56824
|
-
const dest =
|
|
56636
|
+
const dest = import_node_path52.default.join(stage, name);
|
|
56825
56637
|
if (entry.dir) {
|
|
56826
56638
|
await import_promises6.default.mkdir(dest, { recursive: true });
|
|
56827
56639
|
continue;
|
|
56828
56640
|
}
|
|
56829
|
-
await import_promises6.default.mkdir(
|
|
56641
|
+
await import_promises6.default.mkdir(import_node_path52.default.dirname(dest), { recursive: true });
|
|
56830
56642
|
if (name === "manifest.json") {
|
|
56831
56643
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56832
56644
|
await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -56847,7 +56659,7 @@ async function installFromChannel(args, deps) {
|
|
|
56847
56659
|
|
|
56848
56660
|
// src/extension/update-flow.ts
|
|
56849
56661
|
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
56850
|
-
var
|
|
56662
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
56851
56663
|
var import_node_os20 = __toESM(require("os"), 1);
|
|
56852
56664
|
var import_node_crypto16 = __toESM(require("crypto"), 1);
|
|
56853
56665
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
@@ -56864,11 +56676,11 @@ async function updateFromChannel(args, deps) {
|
|
|
56864
56676
|
channelRef.extId,
|
|
56865
56677
|
channelRef.ownerPrincipalId
|
|
56866
56678
|
);
|
|
56867
|
-
const liveDir =
|
|
56679
|
+
const liveDir = import_node_path53.default.join(deps.extensionsRoot, localExtId);
|
|
56868
56680
|
const prevDir = `${liveDir}.prev`;
|
|
56869
56681
|
let existingVersion;
|
|
56870
56682
|
try {
|
|
56871
|
-
const raw = await import_promises7.default.readFile(
|
|
56683
|
+
const raw = await import_promises7.default.readFile(import_node_path53.default.join(liveDir, "manifest.json"), "utf8");
|
|
56872
56684
|
const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
|
|
56873
56685
|
if (!parsed2.success) {
|
|
56874
56686
|
throw new UpdateError(
|
|
@@ -56901,7 +56713,7 @@ async function updateFromChannel(args, deps) {
|
|
|
56901
56713
|
throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56902
56714
|
}
|
|
56903
56715
|
for (const name of Object.keys(zip.files)) {
|
|
56904
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
56716
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path53.default.isAbsolute(name)) {
|
|
56905
56717
|
throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56906
56718
|
}
|
|
56907
56719
|
}
|
|
@@ -56936,16 +56748,16 @@ async function updateFromChannel(args, deps) {
|
|
|
56936
56748
|
await import_promises7.default.rm(prevDir, { recursive: true, force: true });
|
|
56937
56749
|
await import_promises7.default.rename(liveDir, prevDir);
|
|
56938
56750
|
const stage = await import_promises7.default.mkdtemp(
|
|
56939
|
-
|
|
56751
|
+
import_node_path53.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
|
|
56940
56752
|
);
|
|
56941
56753
|
try {
|
|
56942
56754
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56943
|
-
const dest =
|
|
56755
|
+
const dest = import_node_path53.default.join(stage, name);
|
|
56944
56756
|
if (entry.dir) {
|
|
56945
56757
|
await import_promises7.default.mkdir(dest, { recursive: true });
|
|
56946
56758
|
continue;
|
|
56947
56759
|
}
|
|
56948
|
-
await import_promises7.default.mkdir(
|
|
56760
|
+
await import_promises7.default.mkdir(import_node_path53.default.dirname(dest), { recursive: true });
|
|
56949
56761
|
if (name === "manifest.json") {
|
|
56950
56762
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56951
56763
|
await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -57038,7 +56850,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
57038
56850
|
);
|
|
57039
56851
|
}
|
|
57040
56852
|
}
|
|
57041
|
-
const manifestPath =
|
|
56853
|
+
const manifestPath = import_node_path54.default.join(root, extId, "manifest.json");
|
|
57042
56854
|
const manifest = await readManifest(root, extId);
|
|
57043
56855
|
const next = { ...manifest, version: newVersion };
|
|
57044
56856
|
const tmp = `${manifestPath}.tmp`;
|
|
@@ -57046,7 +56858,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
57046
56858
|
await import_promises8.default.rename(tmp, manifestPath);
|
|
57047
56859
|
}
|
|
57048
56860
|
async function readManifest(root, extId) {
|
|
57049
|
-
const file =
|
|
56861
|
+
const file = import_node_path54.default.join(root, extId, "manifest.json");
|
|
57050
56862
|
let raw;
|
|
57051
56863
|
try {
|
|
57052
56864
|
raw = await import_promises8.default.readFile(file, "utf8");
|
|
@@ -57137,7 +56949,7 @@ function buildExtensionHandlers(deps) {
|
|
|
57137
56949
|
};
|
|
57138
56950
|
async function buildSnapshotMeta(extId) {
|
|
57139
56951
|
const manifest = await readManifest(deps.root, extId);
|
|
57140
|
-
const { sha256, buffer } = await bundleExtensionDir(
|
|
56952
|
+
const { sha256, buffer } = await bundleExtensionDir(import_node_path54.default.join(deps.root, extId));
|
|
57141
56953
|
return { manifest, contentHash: sha256, buffer };
|
|
57142
56954
|
}
|
|
57143
56955
|
const publish = async (frame, _client, ctx) => {
|
|
@@ -57318,9 +57130,9 @@ function buildExtensionHandlers(deps) {
|
|
|
57318
57130
|
}
|
|
57319
57131
|
|
|
57320
57132
|
// src/app-builder/project-store.ts
|
|
57321
|
-
var
|
|
57133
|
+
var import_node_fs48 = require("fs");
|
|
57322
57134
|
var import_node_child_process14 = require("child_process");
|
|
57323
|
-
var
|
|
57135
|
+
var import_node_path55 = require("path");
|
|
57324
57136
|
init_protocol();
|
|
57325
57137
|
var PROJECTS_DIR = "projects";
|
|
57326
57138
|
var META_FILE = ".clawd-project.json";
|
|
@@ -57334,19 +57146,19 @@ var ProjectStore = class {
|
|
|
57334
57146
|
root;
|
|
57335
57147
|
/** projects/<name>/.clawd-project.json 路径 */
|
|
57336
57148
|
metaPath(name) {
|
|
57337
|
-
return (0,
|
|
57149
|
+
return (0, import_node_path55.join)(this.projectsRoot(), name, META_FILE);
|
|
57338
57150
|
}
|
|
57339
57151
|
/** projects/<name>/ 目录路径(cwd 用) */
|
|
57340
57152
|
projectDir(name) {
|
|
57341
|
-
return (0,
|
|
57153
|
+
return (0, import_node_path55.join)(this.projectsRoot(), name);
|
|
57342
57154
|
}
|
|
57343
57155
|
projectsRoot() {
|
|
57344
|
-
return (0,
|
|
57156
|
+
return (0, import_node_path55.join)(this.root, PROJECTS_DIR);
|
|
57345
57157
|
}
|
|
57346
57158
|
async list() {
|
|
57347
57159
|
let entries;
|
|
57348
57160
|
try {
|
|
57349
|
-
entries = await
|
|
57161
|
+
entries = await import_node_fs48.promises.readdir(this.projectsRoot());
|
|
57350
57162
|
} catch (err) {
|
|
57351
57163
|
if (err.code === "ENOENT") return [];
|
|
57352
57164
|
throw err;
|
|
@@ -57354,7 +57166,7 @@ var ProjectStore = class {
|
|
|
57354
57166
|
const out = [];
|
|
57355
57167
|
for (const name of entries) {
|
|
57356
57168
|
try {
|
|
57357
|
-
const raw = await
|
|
57169
|
+
const raw = await import_node_fs48.promises.readFile(this.metaPath(name), "utf8");
|
|
57358
57170
|
const json = JSON.parse(raw);
|
|
57359
57171
|
let migrated = false;
|
|
57360
57172
|
if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
|
|
@@ -57365,7 +57177,7 @@ var ProjectStore = class {
|
|
|
57365
57177
|
if (parsed.success) {
|
|
57366
57178
|
out.push(parsed.data);
|
|
57367
57179
|
if (migrated) {
|
|
57368
|
-
void
|
|
57180
|
+
void import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
|
|
57369
57181
|
});
|
|
57370
57182
|
}
|
|
57371
57183
|
}
|
|
@@ -57409,8 +57221,8 @@ var ProjectStore = class {
|
|
|
57409
57221
|
throw new Error(`invalid name "${name}": ${validated.error.message}`);
|
|
57410
57222
|
}
|
|
57411
57223
|
const dir = this.projectDir(name);
|
|
57412
|
-
await
|
|
57413
|
-
await
|
|
57224
|
+
await import_node_fs48.promises.mkdir(dir, { recursive: true });
|
|
57225
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
|
|
57414
57226
|
return meta;
|
|
57415
57227
|
}
|
|
57416
57228
|
/**
|
|
@@ -57453,7 +57265,7 @@ var ProjectStore = class {
|
|
|
57453
57265
|
}
|
|
57454
57266
|
async delete(name) {
|
|
57455
57267
|
const dir = this.projectDir(name);
|
|
57456
|
-
await
|
|
57268
|
+
await import_node_fs48.promises.rm(dir, { recursive: true, force: true });
|
|
57457
57269
|
}
|
|
57458
57270
|
async updatePort(name, newPort) {
|
|
57459
57271
|
if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
|
|
@@ -57469,7 +57281,7 @@ var ProjectStore = class {
|
|
|
57469
57281
|
throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
|
|
57470
57282
|
}
|
|
57471
57283
|
const updated = { ...target, port: newPort };
|
|
57472
|
-
await
|
|
57284
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
57473
57285
|
return updated;
|
|
57474
57286
|
}
|
|
57475
57287
|
/**
|
|
@@ -57486,7 +57298,7 @@ var ProjectStore = class {
|
|
|
57486
57298
|
if (!validated.success) {
|
|
57487
57299
|
throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
|
|
57488
57300
|
}
|
|
57489
|
-
await
|
|
57301
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57490
57302
|
return validated.data;
|
|
57491
57303
|
}
|
|
57492
57304
|
/**
|
|
@@ -57507,7 +57319,7 @@ var ProjectStore = class {
|
|
|
57507
57319
|
if (!validated.success) {
|
|
57508
57320
|
throw new Error(`invalid publishJob: ${validated.error.message}`);
|
|
57509
57321
|
}
|
|
57510
|
-
await
|
|
57322
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57511
57323
|
return validated.data;
|
|
57512
57324
|
}
|
|
57513
57325
|
/** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
|
|
@@ -57522,7 +57334,7 @@ var ProjectStore = class {
|
|
|
57522
57334
|
if (!validated.success) {
|
|
57523
57335
|
throw new Error(`failed to clear publishJob: ${validated.error.message}`);
|
|
57524
57336
|
}
|
|
57525
|
-
await
|
|
57337
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57526
57338
|
return validated.data;
|
|
57527
57339
|
}
|
|
57528
57340
|
};
|
|
@@ -57643,8 +57455,8 @@ var PublishJobRegistry = class {
|
|
|
57643
57455
|
|
|
57644
57456
|
// src/app-builder/publish-job-runner.ts
|
|
57645
57457
|
var import_node_child_process16 = require("child_process");
|
|
57646
|
-
var
|
|
57647
|
-
var
|
|
57458
|
+
var import_node_fs49 = require("fs");
|
|
57459
|
+
var import_node_path56 = require("path");
|
|
57648
57460
|
|
|
57649
57461
|
// src/app-builder/publish-stage-parser.ts
|
|
57650
57462
|
var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
|
|
@@ -57676,10 +57488,10 @@ async function startPublishJob(deps, args) {
|
|
|
57676
57488
|
return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
|
|
57677
57489
|
}
|
|
57678
57490
|
const projDir = projectDir(args.name);
|
|
57679
|
-
const logPath = (0,
|
|
57491
|
+
const logPath = (0, import_node_path56.join)(projDir, ".publish.log");
|
|
57680
57492
|
let logStream = null;
|
|
57681
57493
|
try {
|
|
57682
|
-
logStream = (0,
|
|
57494
|
+
logStream = (0, import_node_fs49.createWriteStream)(logPath, { flags: "w" });
|
|
57683
57495
|
} catch {
|
|
57684
57496
|
logStream = null;
|
|
57685
57497
|
}
|
|
@@ -57936,8 +57748,8 @@ async function recoverInterruptedJobs(deps) {
|
|
|
57936
57748
|
|
|
57937
57749
|
// src/handlers/app-builder.ts
|
|
57938
57750
|
init_protocol();
|
|
57939
|
-
var
|
|
57940
|
-
var
|
|
57751
|
+
var import_node_path57 = require("path");
|
|
57752
|
+
var import_node_fs50 = require("fs");
|
|
57941
57753
|
var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
|
|
57942
57754
|
var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
|
|
57943
57755
|
async function recoverInterruptedPublishJobs(store, logger) {
|
|
@@ -58018,7 +57830,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
58018
57830
|
async function listAllUsersProjects() {
|
|
58019
57831
|
if (!deps.usersRoot || !deps.getStore) return [];
|
|
58020
57832
|
const getStore = deps.getStore;
|
|
58021
|
-
const userIds = await
|
|
57833
|
+
const userIds = await import_node_fs50.promises.readdir(deps.usersRoot).catch(() => []);
|
|
58022
57834
|
const perUser = await Promise.all(
|
|
58023
57835
|
userIds.map((uid) => getStore(uid).list().catch(() => []))
|
|
58024
57836
|
);
|
|
@@ -58094,8 +57906,8 @@ function buildAppBuilderHandlers(deps) {
|
|
|
58094
57906
|
const project = await userStore.create(f.name, reservedPorts);
|
|
58095
57907
|
try {
|
|
58096
57908
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
|
|
58097
|
-
const templateSrcDir = (0,
|
|
58098
|
-
const scaffoldScript = (0,
|
|
57909
|
+
const templateSrcDir = (0, import_node_path57.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
|
|
57910
|
+
const scaffoldScript = (0, import_node_path57.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
|
|
58099
57911
|
const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
|
|
58100
57912
|
deps.logger?.info("app-builder.scaffold.done", {
|
|
58101
57913
|
name: project.name,
|
|
@@ -58316,7 +58128,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
58316
58128
|
await userStore.clearPublishJob(args.name);
|
|
58317
58129
|
}
|
|
58318
58130
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
|
|
58319
|
-
const scriptPath = (0,
|
|
58131
|
+
const scriptPath = (0, import_node_path57.join)(deps.deployKitRoot, "scripts", "publish.sh");
|
|
58320
58132
|
deps.logger?.info("app-builder.publish.start", {
|
|
58321
58133
|
name: args.name,
|
|
58322
58134
|
sessionId: boundSession.sessionId,
|
|
@@ -58492,7 +58304,7 @@ function buildVisitorHandlers(deps) {
|
|
|
58492
58304
|
|
|
58493
58305
|
// src/extension/registry.ts
|
|
58494
58306
|
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
58495
|
-
var
|
|
58307
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
58496
58308
|
async function loadAll(root) {
|
|
58497
58309
|
let entries;
|
|
58498
58310
|
try {
|
|
@@ -58505,13 +58317,13 @@ async function loadAll(root) {
|
|
|
58505
58317
|
for (const ent of entries) {
|
|
58506
58318
|
if (!ent.isDirectory()) continue;
|
|
58507
58319
|
if (ent.name.startsWith(".")) continue;
|
|
58508
|
-
records.push(await loadOne(
|
|
58320
|
+
records.push(await loadOne(import_node_path58.default.join(root, ent.name), ent.name));
|
|
58509
58321
|
}
|
|
58510
58322
|
records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
|
|
58511
58323
|
return records;
|
|
58512
58324
|
}
|
|
58513
58325
|
async function loadOne(dir, dirName) {
|
|
58514
|
-
const manifestPath =
|
|
58326
|
+
const manifestPath = import_node_path58.default.join(dir, "manifest.json");
|
|
58515
58327
|
let raw;
|
|
58516
58328
|
try {
|
|
58517
58329
|
raw = await import_promises9.default.readFile(manifestPath, "utf8");
|
|
@@ -58556,7 +58368,7 @@ async function loadOne(dir, dirName) {
|
|
|
58556
58368
|
|
|
58557
58369
|
// src/extension/uninstall.ts
|
|
58558
58370
|
var import_promises10 = __toESM(require("fs/promises"), 1);
|
|
58559
|
-
var
|
|
58371
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
58560
58372
|
var UninstallError = class extends Error {
|
|
58561
58373
|
constructor(code, message) {
|
|
58562
58374
|
super(message);
|
|
@@ -58565,7 +58377,7 @@ var UninstallError = class extends Error {
|
|
|
58565
58377
|
code;
|
|
58566
58378
|
};
|
|
58567
58379
|
async function uninstall(deps) {
|
|
58568
|
-
const dir =
|
|
58380
|
+
const dir = import_node_path59.default.join(deps.root, deps.extId);
|
|
58569
58381
|
try {
|
|
58570
58382
|
await import_promises10.default.access(dir);
|
|
58571
58383
|
} catch {
|
|
@@ -58632,7 +58444,8 @@ function buildMethodHandlers(deps) {
|
|
|
58632
58444
|
ownerId: deps.ownerId,
|
|
58633
58445
|
ownerProvider: deps.ownerProvider,
|
|
58634
58446
|
personaStore: deps.personaStore,
|
|
58635
|
-
capabilityManager: deps.capabilityManager
|
|
58447
|
+
capabilityManager: deps.capabilityManager,
|
|
58448
|
+
sshIdentityPublicKey: deps.sshIdentityPublicKey
|
|
58636
58449
|
}),
|
|
58637
58450
|
...buildFeishuAuthHandlers(deps.feishuAuth),
|
|
58638
58451
|
...buildDeviceHandlers(deps.feishuDevice),
|
|
@@ -58965,11 +58778,8 @@ var METHOD_GRANT_MAP = {
|
|
|
58965
58778
|
"contact:pin": ADMIN_ANY,
|
|
58966
58779
|
"contact:remove": ADMIN_ANY,
|
|
58967
58780
|
// contact:setSshAccess (owner UI 配 SSH 授权):ADMIN_ANY
|
|
58968
|
-
// contact:sshKey:issue (guest daemon 拉自己的 privkey):public — handler 内校
|
|
58969
|
-
// ctx.principal.kind==='guest' + store.get(callerId).sshAllowed
|
|
58970
58781
|
// (对齐 inbox:postMessage 的"能连上=有 auth,业务在 handler 校"模式)
|
|
58971
58782
|
"contact:setSshAccess": ADMIN_ANY,
|
|
58972
|
-
"contact:sshKey:issue": { kind: "public" },
|
|
58973
58783
|
// ---- visitor:* (访客名单,owner-only) ----
|
|
58974
58784
|
// owner 看完整访客名单(含没开会话的);guest 不可调(handler 内再 assertOwner 兜底)。
|
|
58975
58785
|
"visitor:list": ADMIN_ANY,
|
|
@@ -59155,7 +58965,7 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
|
|
|
59155
58965
|
|
|
59156
58966
|
// src/extension/runtime.ts
|
|
59157
58967
|
var import_node_child_process18 = require("child_process");
|
|
59158
|
-
var
|
|
58968
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
59159
58969
|
var import_promises11 = require("timers/promises");
|
|
59160
58970
|
|
|
59161
58971
|
// src/extension/port-allocator.ts
|
|
@@ -59256,7 +59066,7 @@ var Runtime = class {
|
|
|
59256
59066
|
/\$CLAWOS_EXT_PORT/g,
|
|
59257
59067
|
String(port)
|
|
59258
59068
|
);
|
|
59259
|
-
const dir =
|
|
59069
|
+
const dir = import_node_path60.default.join(this.root, extId);
|
|
59260
59070
|
const env = {
|
|
59261
59071
|
...process.env,
|
|
59262
59072
|
CLAWOS_EXT_PORT: String(port),
|
|
@@ -59368,7 +59178,7 @@ ${handle.stderrTail}`
|
|
|
59368
59178
|
|
|
59369
59179
|
// src/extension/published-channels.ts
|
|
59370
59180
|
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
59371
|
-
var
|
|
59181
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
59372
59182
|
init_zod();
|
|
59373
59183
|
var PublishedChannelsError = class extends Error {
|
|
59374
59184
|
constructor(code, message) {
|
|
@@ -59467,7 +59277,7 @@ var PublishedChannelStore = class {
|
|
|
59467
59277
|
)
|
|
59468
59278
|
};
|
|
59469
59279
|
const tmp = `${this.filePath}.tmp`;
|
|
59470
|
-
await import_promises12.default.mkdir(
|
|
59280
|
+
await import_promises12.default.mkdir(import_node_path61.default.dirname(this.filePath), { recursive: true });
|
|
59471
59281
|
await import_promises12.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
59472
59282
|
await import_promises12.default.rename(tmp, this.filePath);
|
|
59473
59283
|
}
|
|
@@ -59475,7 +59285,7 @@ var PublishedChannelStore = class {
|
|
|
59475
59285
|
|
|
59476
59286
|
// src/extension/bundle-cache.ts
|
|
59477
59287
|
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
59478
|
-
var
|
|
59288
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
59479
59289
|
var BundleCache = class {
|
|
59480
59290
|
constructor(rootDir) {
|
|
59481
59291
|
this.rootDir = rootDir;
|
|
@@ -59484,14 +59294,14 @@ var BundleCache = class {
|
|
|
59484
59294
|
/** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
|
|
59485
59295
|
async write(snapshotHash, buffer) {
|
|
59486
59296
|
await import_promises13.default.mkdir(this.rootDir, { recursive: true });
|
|
59487
|
-
const file =
|
|
59297
|
+
const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59488
59298
|
const tmp = `${file}.tmp`;
|
|
59489
59299
|
await import_promises13.default.writeFile(tmp, buffer, { mode: 384 });
|
|
59490
59300
|
await import_promises13.default.rename(tmp, file);
|
|
59491
59301
|
}
|
|
59492
59302
|
/** Returns the bundle bytes, or null when the file doesn't exist. */
|
|
59493
59303
|
async read(snapshotHash) {
|
|
59494
|
-
const file =
|
|
59304
|
+
const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59495
59305
|
try {
|
|
59496
59306
|
return await import_promises13.default.readFile(file);
|
|
59497
59307
|
} catch (e) {
|
|
@@ -59501,7 +59311,7 @@ var BundleCache = class {
|
|
|
59501
59311
|
}
|
|
59502
59312
|
/** Idempotent — missing file is not an error. */
|
|
59503
59313
|
async delete(snapshotHash) {
|
|
59504
|
-
const file =
|
|
59314
|
+
const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59505
59315
|
await import_promises13.default.rm(file, { force: true });
|
|
59506
59316
|
}
|
|
59507
59317
|
};
|
|
@@ -59524,24 +59334,24 @@ async function startDaemon(config) {
|
|
|
59524
59334
|
sampling: logShippingCfg.sampling,
|
|
59525
59335
|
homeDir: import_node_os21.default.homedir()
|
|
59526
59336
|
});
|
|
59527
|
-
const logDir =
|
|
59528
|
-
|
|
59337
|
+
const logDir = import_node_path63.default.join(config.dataDir, "log");
|
|
59338
|
+
import_node_fs51.default.mkdirSync(logDir, { recursive: true });
|
|
59529
59339
|
const logger = createLogger({
|
|
59530
59340
|
level: config.logLevel,
|
|
59531
|
-
file:
|
|
59341
|
+
file: import_node_path63.default.join(logDir, "clawd.log"),
|
|
59532
59342
|
logClient
|
|
59533
59343
|
});
|
|
59534
59344
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
59535
59345
|
const screenIdleProbeLogger = createFileOnlyLogger({
|
|
59536
|
-
file:
|
|
59346
|
+
file: import_node_path63.default.join(logDir, "screen-idle-probe.log"),
|
|
59537
59347
|
level: "debug"
|
|
59538
59348
|
});
|
|
59539
59349
|
logger.info("screen-idle probe logger enabled", {
|
|
59540
|
-
file:
|
|
59350
|
+
file: import_node_path63.default.join(logDir, "screen-idle-probe.log")
|
|
59541
59351
|
});
|
|
59542
59352
|
const CAP_TARGETS = [
|
|
59543
|
-
|
|
59544
|
-
|
|
59353
|
+
import_node_path63.default.join(logDir, "clawd.log"),
|
|
59354
|
+
import_node_path63.default.join(logDir, "screen-idle-probe.log")
|
|
59545
59355
|
];
|
|
59546
59356
|
const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
|
|
59547
59357
|
const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
|
|
@@ -59637,7 +59447,7 @@ async function startDaemon(config) {
|
|
|
59637
59447
|
// Task 1.7:authenticate 注入路径替代 expectedToken 单 token 比对。
|
|
59638
59448
|
// owner 路径 constantTimeEqual 防侧信道;guest 路径走 capabilityRegistry.
|
|
59639
59449
|
expectedToken: resolvedAuthToken,
|
|
59640
|
-
authenticate: async (t, selfUrl) => {
|
|
59450
|
+
authenticate: async (t, selfUrl, selfSshPublicKey) => {
|
|
59641
59451
|
const result = await authenticate(t, buildConnectAuthDeps());
|
|
59642
59452
|
if (result.ok && result.context.principal.kind === "guest") {
|
|
59643
59453
|
void autoReverseContact({
|
|
@@ -59648,6 +59458,10 @@ async function startDaemon(config) {
|
|
|
59648
59458
|
provider: result.context.provider ?? "",
|
|
59649
59459
|
displayName: result.context.principal.displayName ?? result.context.principal.id,
|
|
59650
59460
|
selfUrl,
|
|
59461
|
+
// ssh identity 互换(spec 2026-07-15):guest auth 帧自报的 pubkey 落 contact;
|
|
59462
|
+
// pubkey 变化且已授权时立即重建 authorized_keys(重新握手 = 迁移路径)
|
|
59463
|
+
sshPublicKey: selfSshPublicKey,
|
|
59464
|
+
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path63.default.join(config.dataDir, "sshd")),
|
|
59651
59465
|
// 决策 #16 换票补全:用 owner 当前 jwt 换 aud=对方设备 的票(本机主动连对方用)。
|
|
59652
59466
|
// 现读 ownerIdentityStore(热切换后用新身份);失败 → null → 保持空票。
|
|
59653
59467
|
exchangeToken: async (targetDeviceId) => {
|
|
@@ -59695,8 +59509,8 @@ async function startDaemon(config) {
|
|
|
59695
59509
|
const agents = new AgentsScanner();
|
|
59696
59510
|
const history = new ClaudeHistoryReader();
|
|
59697
59511
|
let transport = null;
|
|
59698
|
-
const personaStore = new PersonaStore(
|
|
59699
|
-
const usersRoot =
|
|
59512
|
+
const personaStore = new PersonaStore(import_node_path63.default.join(config.dataDir, "personas"));
|
|
59513
|
+
const usersRoot = import_node_path63.default.join(config.dataDir, "users");
|
|
59700
59514
|
const defaultsRoot = findDefaultsRoot(logger);
|
|
59701
59515
|
if (defaultsRoot) {
|
|
59702
59516
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -59716,17 +59530,17 @@ async function startDaemon(config) {
|
|
|
59716
59530
|
migrateCodexSandbox({ store: personaStore, logger });
|
|
59717
59531
|
const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
|
|
59718
59532
|
const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
|
|
59719
|
-
const here = typeof __dirname === "string" ? __dirname :
|
|
59533
|
+
const here = typeof __dirname === "string" ? __dirname : import_node_path63.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
|
|
59720
59534
|
const mcpConfigs = [];
|
|
59721
59535
|
const dispatchServerCandidates = [
|
|
59722
|
-
|
|
59536
|
+
import_node_path63.default.join(here, "dispatch", "mcp-server.cjs"),
|
|
59723
59537
|
// 生产 dist/index → dist/dispatch/mcp-server.cjs
|
|
59724
|
-
|
|
59538
|
+
import_node_path63.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
|
|
59725
59539
|
// dev tsx src/index → ../dist/dispatch/mcp-server.cjs
|
|
59726
59540
|
];
|
|
59727
|
-
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) =>
|
|
59541
|
+
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
|
|
59728
59542
|
if (dispatchServerScriptPath) {
|
|
59729
|
-
const dispatchLogPath =
|
|
59543
|
+
const dispatchLogPath = import_node_path63.default.join(logDir, "dispatch-mcp-server.log");
|
|
59730
59544
|
const dispatchCfgPath = writeDispatchMcpConfig({
|
|
59731
59545
|
dataDir: config.dataDir,
|
|
59732
59546
|
serverScriptPath: dispatchServerScriptPath,
|
|
@@ -59744,12 +59558,12 @@ async function startDaemon(config) {
|
|
|
59744
59558
|
});
|
|
59745
59559
|
}
|
|
59746
59560
|
const ticketServerCandidates = [
|
|
59747
|
-
|
|
59748
|
-
|
|
59561
|
+
import_node_path63.default.join(here, "ticket", "mcp-server.cjs"),
|
|
59562
|
+
import_node_path63.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
|
|
59749
59563
|
];
|
|
59750
|
-
const ticketServerScriptPath = ticketServerCandidates.find((p2) =>
|
|
59564
|
+
const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
|
|
59751
59565
|
if (ticketServerScriptPath) {
|
|
59752
|
-
const ticketLogPath =
|
|
59566
|
+
const ticketLogPath = import_node_path63.default.join(logDir, "ticket-mcp-server.log");
|
|
59753
59567
|
const ticketCfgPath = writeTicketMcpConfig({
|
|
59754
59568
|
dataDir: config.dataDir,
|
|
59755
59569
|
serverScriptPath: ticketServerScriptPath,
|
|
@@ -59769,12 +59583,12 @@ async function startDaemon(config) {
|
|
|
59769
59583
|
});
|
|
59770
59584
|
}
|
|
59771
59585
|
const shiftServerCandidates = [
|
|
59772
|
-
|
|
59773
|
-
|
|
59586
|
+
import_node_path63.default.join(here, "shift", "mcp-server.cjs"),
|
|
59587
|
+
import_node_path63.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
|
|
59774
59588
|
];
|
|
59775
|
-
const shiftServerScriptPath = shiftServerCandidates.find((p2) =>
|
|
59589
|
+
const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
|
|
59776
59590
|
if (shiftServerScriptPath) {
|
|
59777
|
-
const shiftLogPath =
|
|
59591
|
+
const shiftLogPath = import_node_path63.default.join(logDir, "shift-mcp-server.log");
|
|
59778
59592
|
const shiftCfgPath = await writeShiftMcpConfig({
|
|
59779
59593
|
dataDir: config.dataDir,
|
|
59780
59594
|
serverScriptPath: shiftServerScriptPath,
|
|
@@ -59793,12 +59607,12 @@ async function startDaemon(config) {
|
|
|
59793
59607
|
);
|
|
59794
59608
|
}
|
|
59795
59609
|
const inboxServerCandidates = [
|
|
59796
|
-
|
|
59797
|
-
|
|
59610
|
+
import_node_path63.default.join(here, "inbox", "mcp-server.cjs"),
|
|
59611
|
+
import_node_path63.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
|
|
59798
59612
|
];
|
|
59799
|
-
const inboxServerScriptPath = inboxServerCandidates.find((p2) =>
|
|
59613
|
+
const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
|
|
59800
59614
|
if (inboxServerScriptPath) {
|
|
59801
|
-
const inboxLogPath =
|
|
59615
|
+
const inboxLogPath = import_node_path63.default.join(logDir, "inbox-mcp-server.log");
|
|
59802
59616
|
const inboxCfgPath = await writeInboxMcpConfig({
|
|
59803
59617
|
dataDir: config.dataDir,
|
|
59804
59618
|
serverScriptPath: inboxServerScriptPath,
|
|
@@ -59817,7 +59631,7 @@ async function startDaemon(config) {
|
|
|
59817
59631
|
);
|
|
59818
59632
|
}
|
|
59819
59633
|
const shiftStore = createShiftStore({
|
|
59820
|
-
filePath:
|
|
59634
|
+
filePath: import_node_path63.default.join(config.dataDir, "shift.json"),
|
|
59821
59635
|
ownerIdProvider: () => ownerPrincipalId,
|
|
59822
59636
|
now: () => Date.now()
|
|
59823
59637
|
});
|
|
@@ -59839,7 +59653,7 @@ async function startDaemon(config) {
|
|
|
59839
59653
|
getAdapter,
|
|
59840
59654
|
historyReader: history,
|
|
59841
59655
|
dataDir: config.dataDir,
|
|
59842
|
-
personaRoot:
|
|
59656
|
+
personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
|
|
59843
59657
|
usersRoot,
|
|
59844
59658
|
personaStore,
|
|
59845
59659
|
ownerDisplayName,
|
|
@@ -59877,10 +59691,10 @@ async function startDaemon(config) {
|
|
|
59877
59691
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
59878
59692
|
attachmentGroup: {
|
|
59879
59693
|
onFileEdit: (input) => {
|
|
59880
|
-
const absPath =
|
|
59694
|
+
const absPath = import_node_path63.default.isAbsolute(input.relPath) ? input.relPath : import_node_path63.default.join(input.cwd, input.relPath);
|
|
59881
59695
|
let size = 0;
|
|
59882
59696
|
try {
|
|
59883
|
-
size =
|
|
59697
|
+
size = import_node_fs51.default.statSync(absPath).size;
|
|
59884
59698
|
} catch (err) {
|
|
59885
59699
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
59886
59700
|
sessionId: input.sessionId,
|
|
@@ -60067,6 +59881,13 @@ async function startDaemon(config) {
|
|
|
60067
59881
|
/* @__PURE__ */ new Set([...config.previewPorts ?? [], ...appBuilderPortRange])
|
|
60068
59882
|
);
|
|
60069
59883
|
const sshLog = createContactSshLog(config.dataDir);
|
|
59884
|
+
try {
|
|
59885
|
+
await ensureSshIdentity(config.dataDir);
|
|
59886
|
+
} catch (e) {
|
|
59887
|
+
logger.warn("ssh-identity generate failed; contact SSH pubkey exchange disabled", {
|
|
59888
|
+
err: e.message
|
|
59889
|
+
});
|
|
59890
|
+
}
|
|
60070
59891
|
const makeHandlers = () => buildMethodHandlers({
|
|
60071
59892
|
manager,
|
|
60072
59893
|
workspace,
|
|
@@ -60110,11 +59931,11 @@ async function startDaemon(config) {
|
|
|
60110
59931
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
60111
59932
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
60112
59933
|
// guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
|
|
60113
|
-
personaRoot:
|
|
59934
|
+
personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
|
|
60114
59935
|
usersRoot
|
|
60115
59936
|
},
|
|
60116
59937
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
60117
|
-
personaRoot:
|
|
59938
|
+
personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
|
|
60118
59939
|
// v2 多人 persona 隔离:handler 派生 guest user-dir 放行
|
|
60119
59940
|
usersRoot,
|
|
60120
59941
|
// capability:list / delete handler 依赖
|
|
@@ -60127,6 +59948,9 @@ async function startDaemon(config) {
|
|
|
60127
59948
|
ownerFeishuUnionId,
|
|
60128
59949
|
ownerId,
|
|
60129
59950
|
ownerProvider,
|
|
59951
|
+
// ssh identity 互换(spec 2026-07-15):whoami:ok 下发本机 identity pubkey。
|
|
59952
|
+
// 惰性读盘,不依赖 ensureSshIdentity 完成时序(未生成 → null → 帧省略字段)。
|
|
59953
|
+
sshIdentityPublicKey: () => readSshIdentityPublicKey(config.dataDir),
|
|
60130
59954
|
personaStore,
|
|
60131
59955
|
// capability handler 也用 (capability:issue 走 registry / capability:list)
|
|
60132
59956
|
capabilityRegistry,
|
|
@@ -60138,7 +59962,7 @@ async function startDaemon(config) {
|
|
|
60138
59962
|
contactStore,
|
|
60139
59963
|
// <dataDir>/sshd 绝对路径 —— contact-ssh handlers 用它拼 authorized_keys / keys/ 子路径
|
|
60140
59964
|
// Task 10 会加 SshdManager 起 sshd;handlers wire 提前挂 sshdDir 让 typecheck 过
|
|
60141
|
-
sshdDir:
|
|
59965
|
+
sshdDir: import_node_path63.default.join(config.dataDir, "sshd"),
|
|
60142
59966
|
contactSshLog: sshLog,
|
|
60143
59967
|
// inbox:sendDm 用:sessionId → session 出身(复用 attachment 同款 findOwnedSessionScope)
|
|
60144
59968
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
@@ -60206,7 +60030,11 @@ async function startDaemon(config) {
|
|
|
60206
60030
|
// 自动反向(spec 决策 #11):本机可达地址 = tunnel URL(公网),出站连接时
|
|
60207
60031
|
// 透传让对方反向加我为联系人。无 tunnel(仅本机 ws)→ null,对方不自动反向(不造假数据)。
|
|
60208
60032
|
// 决策 #16:身份不自报——本机 deviceId/ownerId/provider 由 exchange 签进 token 背书。
|
|
60209
|
-
selfUrl: () => currentTunnelUrl
|
|
60033
|
+
selfUrl: () => currentTunnelUrl,
|
|
60034
|
+
// ssh identity 互换(spec 2026-07-15):出站 auth 帧自报本机 identity pubkey;
|
|
60035
|
+
// 对方 pubkey 变化且已授权时立即重建 authorized_keys(重新 connect = 迁移路径)
|
|
60036
|
+
selfSshPublicKey: () => readSshIdentityPublicKey(config.dataDir),
|
|
60037
|
+
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path63.default.join(config.dataDir, "sshd"))
|
|
60210
60038
|
},
|
|
60211
60039
|
// app-builder
|
|
60212
60040
|
appBuilderStore,
|
|
@@ -60229,11 +60057,11 @@ async function startDaemon(config) {
|
|
|
60229
60057
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2):
|
|
60230
60058
|
// appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
|
|
60231
60059
|
// dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
|
|
60232
|
-
appBuilderPersonaRoot:
|
|
60060
|
+
appBuilderPersonaRoot: import_node_path63.default.join(config.dataDir, "personas", "persona-app-builder"),
|
|
60233
60061
|
// 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
|
|
60234
|
-
deployKitRoot:
|
|
60062
|
+
deployKitRoot: import_node_path63.default.join(config.dataDir, "deploy-kit"),
|
|
60235
60063
|
// scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
|
|
60236
|
-
resolvePersonaRoot: (personaId) =>
|
|
60064
|
+
resolvePersonaRoot: (personaId) => import_node_path63.default.join(config.dataDir, "personas", personaId),
|
|
60237
60065
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
|
|
60238
60066
|
// 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
|
|
60239
60067
|
// 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
|
|
@@ -60276,7 +60104,7 @@ async function startDaemon(config) {
|
|
|
60276
60104
|
}
|
|
60277
60105
|
let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
|
|
60278
60106
|
if (sourceFile && sourceFile.toolSessionId) {
|
|
60279
|
-
sourceJsonlPath =
|
|
60107
|
+
sourceJsonlPath = import_node_path63.default.join(
|
|
60280
60108
|
import_node_os21.default.homedir(),
|
|
60281
60109
|
".claude",
|
|
60282
60110
|
"projects",
|
|
@@ -60636,8 +60464,8 @@ async function startDaemon(config) {
|
|
|
60636
60464
|
const lines = [
|
|
60637
60465
|
`Tunnel: ${r.url}`,
|
|
60638
60466
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
60639
|
-
`Frpc config: ${
|
|
60640
|
-
`Frpc log: ${
|
|
60467
|
+
`Frpc config: ${import_node_path63.default.join(config.dataDir, "frpc.toml")}`,
|
|
60468
|
+
`Frpc log: ${import_node_path63.default.join(logDir, "frpc.log")}`
|
|
60641
60469
|
];
|
|
60642
60470
|
const width = Math.max(...lines.map((l) => l.length));
|
|
60643
60471
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -60650,8 +60478,8 @@ ${bar}
|
|
|
60650
60478
|
|
|
60651
60479
|
`);
|
|
60652
60480
|
try {
|
|
60653
|
-
const connectPath =
|
|
60654
|
-
|
|
60481
|
+
const connectPath = import_node_path63.default.join(config.dataDir, "connect.txt");
|
|
60482
|
+
import_node_fs51.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
60655
60483
|
} catch {
|
|
60656
60484
|
}
|
|
60657
60485
|
} catch (err) {
|
|
@@ -60687,21 +60515,13 @@ ${bar}
|
|
|
60687
60515
|
});
|
|
60688
60516
|
try {
|
|
60689
60517
|
await sshdMgr.start();
|
|
60690
|
-
rebuildAuthorizedKeys(contactStore,
|
|
60518
|
+
rebuildAuthorizedKeys(contactStore, import_node_path63.default.join(config.dataDir, "sshd"));
|
|
60691
60519
|
logger.info("sshd: contact-ssh sandbox ready", { port: config.sshdPort });
|
|
60692
60520
|
} catch (err) {
|
|
60693
60521
|
logger.warn("sshd start failed; contact SSH grant will not work until fixed", {
|
|
60694
60522
|
err: err.message
|
|
60695
60523
|
});
|
|
60696
60524
|
}
|
|
60697
|
-
const contactKeyPuller = new ContactKeyPuller({
|
|
60698
|
-
store: contactStore,
|
|
60699
|
-
dataDir: config.dataDir,
|
|
60700
|
-
logger,
|
|
60701
|
-
intervalMs: 6e4,
|
|
60702
|
-
sshLog
|
|
60703
|
-
});
|
|
60704
|
-
void contactKeyPuller.start();
|
|
60705
60525
|
void reportDevice();
|
|
60706
60526
|
void fetchServerKey();
|
|
60707
60527
|
const tickAttachmentGc = () => {
|
|
@@ -60736,7 +60556,6 @@ ${bar}
|
|
|
60736
60556
|
if (tunnelMgr) {
|
|
60737
60557
|
await tunnelMgr.stop();
|
|
60738
60558
|
}
|
|
60739
|
-
contactKeyPuller.stop();
|
|
60740
60559
|
await sshdMgr.stop().catch((err) => {
|
|
60741
60560
|
logger.warn("shutdown.sshd-stop-failed", {
|
|
60742
60561
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -60755,9 +60574,9 @@ ${bar}
|
|
|
60755
60574
|
};
|
|
60756
60575
|
}
|
|
60757
60576
|
function migrateDropPersonsDir(dataDir) {
|
|
60758
|
-
const dir =
|
|
60577
|
+
const dir = import_node_path63.default.join(dataDir, "persons");
|
|
60759
60578
|
try {
|
|
60760
|
-
|
|
60579
|
+
import_node_fs51.default.rmSync(dir, { recursive: true, force: true });
|
|
60761
60580
|
} catch {
|
|
60762
60581
|
}
|
|
60763
60582
|
}
|