@clawos-dev/clawd 0.2.235 → 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
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({
|
|
@@ -5744,6 +5755,14 @@ var init_contact = __esm({
|
|
|
5744
5755
|
* 极端场景(区别对方多个 persona)留 per-persona override 演进空间。
|
|
5745
5756
|
*/
|
|
5746
5757
|
sshAllowed: external_exports.boolean().default(false),
|
|
5758
|
+
/**
|
|
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(),
|
|
5747
5766
|
/**
|
|
5748
5767
|
* @deprecated T-29 去沙箱后废弃。原为授权访问的目录白名单(clawd-ssh-jail 生成 sbpl/bwrap
|
|
5749
5768
|
* policy 用);去掉 OS 沙箱后不再有目录级限制,SSH 授权即 on/off(见 sshAllowed)。
|
|
@@ -5790,7 +5809,7 @@ var init_contact = __esm({
|
|
|
5790
5809
|
});
|
|
5791
5810
|
|
|
5792
5811
|
// ../protocol/src/contact-ssh.ts
|
|
5793
|
-
var ContactSetSshAccessArgsSchema, ContactSetSshAccessOkSchema,
|
|
5812
|
+
var ContactSetSshAccessArgsSchema, ContactSetSshAccessOkSchema, ContactSshAccessUpdatedFrameSchema;
|
|
5794
5813
|
var init_contact_ssh = __esm({
|
|
5795
5814
|
"../protocol/src/contact-ssh.ts"() {
|
|
5796
5815
|
"use strict";
|
|
@@ -5804,14 +5823,6 @@ var init_contact_ssh = __esm({
|
|
|
5804
5823
|
deviceId: external_exports.string().min(1),
|
|
5805
5824
|
sshAllowed: external_exports.boolean()
|
|
5806
5825
|
}).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)
|
|
5814
|
-
}).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),
|
|
@@ -6506,8 +6517,8 @@ var require_req = __commonJS({
|
|
|
6506
6517
|
if (req.originalUrl) {
|
|
6507
6518
|
_req.url = req.originalUrl;
|
|
6508
6519
|
} else {
|
|
6509
|
-
const
|
|
6510
|
-
_req.url = typeof
|
|
6520
|
+
const path76 = req.path;
|
|
6521
|
+
_req.url = typeof path76 === "string" ? path76 : req.url ? req.url.path || req.url : void 0;
|
|
6511
6522
|
}
|
|
6512
6523
|
if (req.query) {
|
|
6513
6524
|
_req.query = req.query;
|
|
@@ -6672,14 +6683,14 @@ var require_redact = __commonJS({
|
|
|
6672
6683
|
}
|
|
6673
6684
|
return obj;
|
|
6674
6685
|
}
|
|
6675
|
-
function parsePath(
|
|
6686
|
+
function parsePath(path76) {
|
|
6676
6687
|
const parts = [];
|
|
6677
6688
|
let current = "";
|
|
6678
6689
|
let inBrackets = false;
|
|
6679
6690
|
let inQuotes = false;
|
|
6680
6691
|
let quoteChar = "";
|
|
6681
|
-
for (let i = 0; i <
|
|
6682
|
-
const char =
|
|
6692
|
+
for (let i = 0; i < path76.length; i++) {
|
|
6693
|
+
const char = path76[i];
|
|
6683
6694
|
if (!inBrackets && char === ".") {
|
|
6684
6695
|
if (current) {
|
|
6685
6696
|
parts.push(current);
|
|
@@ -6810,10 +6821,10 @@ var require_redact = __commonJS({
|
|
|
6810
6821
|
return current;
|
|
6811
6822
|
}
|
|
6812
6823
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
6813
|
-
for (const
|
|
6814
|
-
const parts = parsePath(
|
|
6824
|
+
for (const path76 of paths) {
|
|
6825
|
+
const parts = parsePath(path76);
|
|
6815
6826
|
if (parts.includes("*")) {
|
|
6816
|
-
redactWildcardPath(obj, parts, censor,
|
|
6827
|
+
redactWildcardPath(obj, parts, censor, path76, remove);
|
|
6817
6828
|
} else {
|
|
6818
6829
|
if (remove) {
|
|
6819
6830
|
removeKey(obj, parts);
|
|
@@ -6898,8 +6909,8 @@ var require_redact = __commonJS({
|
|
|
6898
6909
|
}
|
|
6899
6910
|
} else {
|
|
6900
6911
|
if (afterWildcard.includes("*")) {
|
|
6901
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6902
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
6912
|
+
const wrappedCensor = typeof censor === "function" ? (value, path76) => {
|
|
6913
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path76];
|
|
6903
6914
|
return censor(value, fullPath);
|
|
6904
6915
|
} : censor;
|
|
6905
6916
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -6934,8 +6945,8 @@ var require_redact = __commonJS({
|
|
|
6934
6945
|
return null;
|
|
6935
6946
|
}
|
|
6936
6947
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
6937
|
-
for (const
|
|
6938
|
-
const parts = parsePath(
|
|
6948
|
+
for (const path76 of pathsToClone) {
|
|
6949
|
+
const parts = parsePath(path76);
|
|
6939
6950
|
let current = pathStructure;
|
|
6940
6951
|
for (let i = 0; i < parts.length; i++) {
|
|
6941
6952
|
const part = parts[i];
|
|
@@ -6987,24 +6998,24 @@ var require_redact = __commonJS({
|
|
|
6987
6998
|
}
|
|
6988
6999
|
return cloneSelectively(obj, pathStructure);
|
|
6989
7000
|
}
|
|
6990
|
-
function validatePath(
|
|
6991
|
-
if (typeof
|
|
7001
|
+
function validatePath(path76) {
|
|
7002
|
+
if (typeof path76 !== "string") {
|
|
6992
7003
|
throw new Error("Paths must be (non-empty) strings");
|
|
6993
7004
|
}
|
|
6994
|
-
if (
|
|
7005
|
+
if (path76 === "") {
|
|
6995
7006
|
throw new Error("Invalid redaction path ()");
|
|
6996
7007
|
}
|
|
6997
|
-
if (
|
|
6998
|
-
throw new Error(`Invalid redaction path (${
|
|
7008
|
+
if (path76.includes("..")) {
|
|
7009
|
+
throw new Error(`Invalid redaction path (${path76})`);
|
|
6999
7010
|
}
|
|
7000
|
-
if (
|
|
7001
|
-
throw new Error(`Invalid redaction path (${
|
|
7011
|
+
if (path76.includes(",")) {
|
|
7012
|
+
throw new Error(`Invalid redaction path (${path76})`);
|
|
7002
7013
|
}
|
|
7003
7014
|
let bracketCount = 0;
|
|
7004
7015
|
let inQuotes = false;
|
|
7005
7016
|
let quoteChar = "";
|
|
7006
|
-
for (let i = 0; i <
|
|
7007
|
-
const char =
|
|
7017
|
+
for (let i = 0; i < path76.length; i++) {
|
|
7018
|
+
const char = path76[i];
|
|
7008
7019
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
7009
7020
|
if (!inQuotes) {
|
|
7010
7021
|
inQuotes = true;
|
|
@@ -7018,20 +7029,20 @@ var require_redact = __commonJS({
|
|
|
7018
7029
|
} else if (char === "]" && !inQuotes) {
|
|
7019
7030
|
bracketCount--;
|
|
7020
7031
|
if (bracketCount < 0) {
|
|
7021
|
-
throw new Error(`Invalid redaction path (${
|
|
7032
|
+
throw new Error(`Invalid redaction path (${path76})`);
|
|
7022
7033
|
}
|
|
7023
7034
|
}
|
|
7024
7035
|
}
|
|
7025
7036
|
if (bracketCount !== 0) {
|
|
7026
|
-
throw new Error(`Invalid redaction path (${
|
|
7037
|
+
throw new Error(`Invalid redaction path (${path76})`);
|
|
7027
7038
|
}
|
|
7028
7039
|
}
|
|
7029
7040
|
function validatePaths(paths) {
|
|
7030
7041
|
if (!Array.isArray(paths)) {
|
|
7031
7042
|
throw new TypeError("paths must be an array");
|
|
7032
7043
|
}
|
|
7033
|
-
for (const
|
|
7034
|
-
validatePath(
|
|
7044
|
+
for (const path76 of paths) {
|
|
7045
|
+
validatePath(path76);
|
|
7035
7046
|
}
|
|
7036
7047
|
}
|
|
7037
7048
|
function slowRedact(options = {}) {
|
|
@@ -7199,8 +7210,8 @@ var require_redaction = __commonJS({
|
|
|
7199
7210
|
if (shape[k2] === null) {
|
|
7200
7211
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
7201
7212
|
} else {
|
|
7202
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
7203
|
-
return censor(value, [k2, ...
|
|
7213
|
+
const wrappedCensor = typeof censor === "function" ? (value, path76) => {
|
|
7214
|
+
return censor(value, [k2, ...path76]);
|
|
7204
7215
|
} : censor;
|
|
7205
7216
|
o[k2] = Redact({
|
|
7206
7217
|
paths: shape[k2],
|
|
@@ -7418,10 +7429,10 @@ var require_atomic_sleep = __commonJS({
|
|
|
7418
7429
|
var require_sonic_boom = __commonJS({
|
|
7419
7430
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
7420
7431
|
"use strict";
|
|
7421
|
-
var
|
|
7432
|
+
var fs70 = require("fs");
|
|
7422
7433
|
var EventEmitter3 = require("events");
|
|
7423
7434
|
var inherits = require("util").inherits;
|
|
7424
|
-
var
|
|
7435
|
+
var path76 = require("path");
|
|
7425
7436
|
var sleep2 = require_atomic_sleep();
|
|
7426
7437
|
var assert = require("assert");
|
|
7427
7438
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -7475,20 +7486,20 @@ var require_sonic_boom = __commonJS({
|
|
|
7475
7486
|
const mode = sonic.mode;
|
|
7476
7487
|
if (sonic.sync) {
|
|
7477
7488
|
try {
|
|
7478
|
-
if (sonic.mkdir)
|
|
7479
|
-
const fd =
|
|
7489
|
+
if (sonic.mkdir) fs70.mkdirSync(path76.dirname(file), { recursive: true });
|
|
7490
|
+
const fd = fs70.openSync(file, flags, mode);
|
|
7480
7491
|
fileOpened(null, fd);
|
|
7481
7492
|
} catch (err) {
|
|
7482
7493
|
fileOpened(err);
|
|
7483
7494
|
throw err;
|
|
7484
7495
|
}
|
|
7485
7496
|
} else if (sonic.mkdir) {
|
|
7486
|
-
|
|
7497
|
+
fs70.mkdir(path76.dirname(file), { recursive: true }, (err) => {
|
|
7487
7498
|
if (err) return fileOpened(err);
|
|
7488
|
-
|
|
7499
|
+
fs70.open(file, flags, mode, fileOpened);
|
|
7489
7500
|
});
|
|
7490
7501
|
} else {
|
|
7491
|
-
|
|
7502
|
+
fs70.open(file, flags, mode, fileOpened);
|
|
7492
7503
|
}
|
|
7493
7504
|
}
|
|
7494
7505
|
function SonicBoom(opts) {
|
|
@@ -7529,8 +7540,8 @@ var require_sonic_boom = __commonJS({
|
|
|
7529
7540
|
this.flush = flushBuffer;
|
|
7530
7541
|
this.flushSync = flushBufferSync;
|
|
7531
7542
|
this._actualWrite = actualWriteBuffer;
|
|
7532
|
-
fsWriteSync = () =>
|
|
7533
|
-
fsWrite = () =>
|
|
7543
|
+
fsWriteSync = () => fs70.writeSync(this.fd, this._writingBuf);
|
|
7544
|
+
fsWrite = () => fs70.write(this.fd, this._writingBuf, this.release);
|
|
7534
7545
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
7535
7546
|
this._writingBuf = "";
|
|
7536
7547
|
this.write = write;
|
|
@@ -7539,15 +7550,15 @@ var require_sonic_boom = __commonJS({
|
|
|
7539
7550
|
this._actualWrite = actualWrite;
|
|
7540
7551
|
fsWriteSync = () => {
|
|
7541
7552
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7542
|
-
return
|
|
7553
|
+
return fs70.writeSync(this.fd, this._writingBuf);
|
|
7543
7554
|
}
|
|
7544
|
-
return
|
|
7555
|
+
return fs70.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7545
7556
|
};
|
|
7546
7557
|
fsWrite = () => {
|
|
7547
7558
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7548
|
-
return
|
|
7559
|
+
return fs70.write(this.fd, this._writingBuf, this.release);
|
|
7549
7560
|
}
|
|
7550
|
-
return
|
|
7561
|
+
return fs70.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
7551
7562
|
};
|
|
7552
7563
|
} else {
|
|
7553
7564
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -7604,7 +7615,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7604
7615
|
}
|
|
7605
7616
|
}
|
|
7606
7617
|
if (this._fsync) {
|
|
7607
|
-
|
|
7618
|
+
fs70.fsyncSync(this.fd);
|
|
7608
7619
|
}
|
|
7609
7620
|
const len = this._len;
|
|
7610
7621
|
if (this._reopening) {
|
|
@@ -7718,7 +7729,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7718
7729
|
const onDrain = () => {
|
|
7719
7730
|
if (!this._fsync) {
|
|
7720
7731
|
try {
|
|
7721
|
-
|
|
7732
|
+
fs70.fsync(this.fd, (err) => {
|
|
7722
7733
|
this._flushPending = false;
|
|
7723
7734
|
cb(err);
|
|
7724
7735
|
});
|
|
@@ -7820,7 +7831,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7820
7831
|
const fd = this.fd;
|
|
7821
7832
|
this.once("ready", () => {
|
|
7822
7833
|
if (fd !== this.fd) {
|
|
7823
|
-
|
|
7834
|
+
fs70.close(fd, (err) => {
|
|
7824
7835
|
if (err) {
|
|
7825
7836
|
return this.emit("error", err);
|
|
7826
7837
|
}
|
|
@@ -7869,7 +7880,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7869
7880
|
buf = this._bufs[0];
|
|
7870
7881
|
}
|
|
7871
7882
|
try {
|
|
7872
|
-
const n = Buffer.isBuffer(buf) ?
|
|
7883
|
+
const n = Buffer.isBuffer(buf) ? fs70.writeSync(this.fd, buf) : fs70.writeSync(this.fd, buf, "utf8");
|
|
7873
7884
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
7874
7885
|
buf = releasedBufObj.writingBuf;
|
|
7875
7886
|
this._len = releasedBufObj.len;
|
|
@@ -7885,7 +7896,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7885
7896
|
}
|
|
7886
7897
|
}
|
|
7887
7898
|
try {
|
|
7888
|
-
|
|
7899
|
+
fs70.fsyncSync(this.fd);
|
|
7889
7900
|
} catch {
|
|
7890
7901
|
}
|
|
7891
7902
|
}
|
|
@@ -7906,7 +7917,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7906
7917
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
7907
7918
|
}
|
|
7908
7919
|
try {
|
|
7909
|
-
const n =
|
|
7920
|
+
const n = fs70.writeSync(this.fd, buf);
|
|
7910
7921
|
buf = buf.subarray(n);
|
|
7911
7922
|
this._len = Math.max(this._len - n, 0);
|
|
7912
7923
|
if (buf.length <= 0) {
|
|
@@ -7934,13 +7945,13 @@ var require_sonic_boom = __commonJS({
|
|
|
7934
7945
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
7935
7946
|
if (this.sync) {
|
|
7936
7947
|
try {
|
|
7937
|
-
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");
|
|
7938
7949
|
release(null, written);
|
|
7939
7950
|
} catch (err) {
|
|
7940
7951
|
release(err);
|
|
7941
7952
|
}
|
|
7942
7953
|
} else {
|
|
7943
|
-
|
|
7954
|
+
fs70.write(this.fd, this._writingBuf, release);
|
|
7944
7955
|
}
|
|
7945
7956
|
}
|
|
7946
7957
|
function actualWriteBuffer() {
|
|
@@ -7949,7 +7960,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7949
7960
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
7950
7961
|
if (this.sync) {
|
|
7951
7962
|
try {
|
|
7952
|
-
const written =
|
|
7963
|
+
const written = fs70.writeSync(this.fd, this._writingBuf);
|
|
7953
7964
|
release(null, written);
|
|
7954
7965
|
} catch (err) {
|
|
7955
7966
|
release(err);
|
|
@@ -7958,7 +7969,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7958
7969
|
if (kCopyBuffer) {
|
|
7959
7970
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
7960
7971
|
}
|
|
7961
|
-
|
|
7972
|
+
fs70.write(this.fd, this._writingBuf, release);
|
|
7962
7973
|
}
|
|
7963
7974
|
}
|
|
7964
7975
|
function actualClose(sonic) {
|
|
@@ -7974,12 +7985,12 @@ var require_sonic_boom = __commonJS({
|
|
|
7974
7985
|
sonic._lens = [];
|
|
7975
7986
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
7976
7987
|
try {
|
|
7977
|
-
|
|
7988
|
+
fs70.fsync(sonic.fd, closeWrapped);
|
|
7978
7989
|
} catch {
|
|
7979
7990
|
}
|
|
7980
7991
|
function closeWrapped() {
|
|
7981
7992
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
7982
|
-
|
|
7993
|
+
fs70.close(sonic.fd, done);
|
|
7983
7994
|
} else {
|
|
7984
7995
|
done();
|
|
7985
7996
|
}
|
|
@@ -11114,11 +11125,11 @@ var init_lib = __esm({
|
|
|
11114
11125
|
}
|
|
11115
11126
|
}
|
|
11116
11127
|
},
|
|
11117
|
-
addToPath: function addToPath(
|
|
11118
|
-
var last =
|
|
11128
|
+
addToPath: function addToPath(path76, added, removed, oldPosInc, options) {
|
|
11129
|
+
var last = path76.lastComponent;
|
|
11119
11130
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
11120
11131
|
return {
|
|
11121
|
-
oldPos:
|
|
11132
|
+
oldPos: path76.oldPos + oldPosInc,
|
|
11122
11133
|
lastComponent: {
|
|
11123
11134
|
count: last.count + 1,
|
|
11124
11135
|
added,
|
|
@@ -11128,7 +11139,7 @@ var init_lib = __esm({
|
|
|
11128
11139
|
};
|
|
11129
11140
|
} else {
|
|
11130
11141
|
return {
|
|
11131
|
-
oldPos:
|
|
11142
|
+
oldPos: path76.oldPos + oldPosInc,
|
|
11132
11143
|
lastComponent: {
|
|
11133
11144
|
count: 1,
|
|
11134
11145
|
added,
|
|
@@ -11633,10 +11644,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
11633
11644
|
const memories = raw.map((m2) => {
|
|
11634
11645
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11635
11646
|
const rec3 = m2;
|
|
11636
|
-
const
|
|
11647
|
+
const path76 = typeof rec3.path === "string" ? rec3.path : null;
|
|
11637
11648
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
11638
|
-
if (!
|
|
11639
|
-
const entry = { path:
|
|
11649
|
+
if (!path76 || content == null) return null;
|
|
11650
|
+
const entry = { path: path76, content };
|
|
11640
11651
|
if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
|
|
11641
11652
|
return entry;
|
|
11642
11653
|
}).filter((m2) => m2 !== null);
|
|
@@ -12439,10 +12450,10 @@ function parseAttachment(obj) {
|
|
|
12439
12450
|
const memories = raw.map((m2) => {
|
|
12440
12451
|
if (!m2 || typeof m2 !== "object") return null;
|
|
12441
12452
|
const rec3 = m2;
|
|
12442
|
-
const
|
|
12453
|
+
const path76 = typeof rec3.path === "string" ? rec3.path : null;
|
|
12443
12454
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
12444
|
-
if (!
|
|
12445
|
-
const out = { path:
|
|
12455
|
+
if (!path76 || content == null) return null;
|
|
12456
|
+
const out = { path: path76, content };
|
|
12446
12457
|
if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
|
|
12447
12458
|
return out;
|
|
12448
12459
|
}).filter((m2) => m2 !== null);
|
|
@@ -33428,8 +33439,8 @@ var require_utils = __commonJS({
|
|
|
33428
33439
|
var result = transform[inputType][outputType](input);
|
|
33429
33440
|
return result;
|
|
33430
33441
|
};
|
|
33431
|
-
exports2.resolve = function(
|
|
33432
|
-
var parts =
|
|
33442
|
+
exports2.resolve = function(path76) {
|
|
33443
|
+
var parts = path76.split("/");
|
|
33433
33444
|
var result = [];
|
|
33434
33445
|
for (var index = 0; index < parts.length; index++) {
|
|
33435
33446
|
var part = parts[index];
|
|
@@ -39282,18 +39293,18 @@ var require_object = __commonJS({
|
|
|
39282
39293
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
39283
39294
|
this.files[name] = object;
|
|
39284
39295
|
};
|
|
39285
|
-
var parentFolder = function(
|
|
39286
|
-
if (
|
|
39287
|
-
|
|
39296
|
+
var parentFolder = function(path76) {
|
|
39297
|
+
if (path76.slice(-1) === "/") {
|
|
39298
|
+
path76 = path76.substring(0, path76.length - 1);
|
|
39288
39299
|
}
|
|
39289
|
-
var lastSlash =
|
|
39290
|
-
return lastSlash > 0 ?
|
|
39300
|
+
var lastSlash = path76.lastIndexOf("/");
|
|
39301
|
+
return lastSlash > 0 ? path76.substring(0, lastSlash) : "";
|
|
39291
39302
|
};
|
|
39292
|
-
var forceTrailingSlash = function(
|
|
39293
|
-
if (
|
|
39294
|
-
|
|
39303
|
+
var forceTrailingSlash = function(path76) {
|
|
39304
|
+
if (path76.slice(-1) !== "/") {
|
|
39305
|
+
path76 += "/";
|
|
39295
39306
|
}
|
|
39296
|
-
return
|
|
39307
|
+
return path76;
|
|
39297
39308
|
};
|
|
39298
39309
|
var folderAdd = function(name, createFolders) {
|
|
39299
39310
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -40295,7 +40306,7 @@ var require_lib3 = __commonJS({
|
|
|
40295
40306
|
// src/run-case/recorder.ts
|
|
40296
40307
|
function startRunCaseRecorder(opts) {
|
|
40297
40308
|
const now = opts.now ?? Date.now;
|
|
40298
|
-
const dir =
|
|
40309
|
+
const dir = import_node_path64.default.dirname(opts.recordPath);
|
|
40299
40310
|
let stream = null;
|
|
40300
40311
|
let closing = false;
|
|
40301
40312
|
let closedSettled = false;
|
|
@@ -40309,8 +40320,8 @@ function startRunCaseRecorder(opts) {
|
|
|
40309
40320
|
});
|
|
40310
40321
|
const ensureStream = () => {
|
|
40311
40322
|
if (stream) return stream;
|
|
40312
|
-
|
|
40313
|
-
stream =
|
|
40323
|
+
import_node_fs52.default.mkdirSync(dir, { recursive: true });
|
|
40324
|
+
stream = import_node_fs52.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
40314
40325
|
stream.on("close", () => closedResolve());
|
|
40315
40326
|
return stream;
|
|
40316
40327
|
};
|
|
@@ -40335,12 +40346,12 @@ function startRunCaseRecorder(opts) {
|
|
|
40335
40346
|
};
|
|
40336
40347
|
return { tap, close, closed };
|
|
40337
40348
|
}
|
|
40338
|
-
var
|
|
40349
|
+
var import_node_fs52, import_node_path64;
|
|
40339
40350
|
var init_recorder = __esm({
|
|
40340
40351
|
"src/run-case/recorder.ts"() {
|
|
40341
40352
|
"use strict";
|
|
40342
|
-
|
|
40343
|
-
|
|
40353
|
+
import_node_fs52 = __toESM(require("fs"), 1);
|
|
40354
|
+
import_node_path64 = __toESM(require("path"), 1);
|
|
40344
40355
|
}
|
|
40345
40356
|
});
|
|
40346
40357
|
|
|
@@ -40383,7 +40394,7 @@ var init_wire = __esm({
|
|
|
40383
40394
|
// src/run-case/controller.ts
|
|
40384
40395
|
async function runController(opts) {
|
|
40385
40396
|
const now = opts.now ?? Date.now;
|
|
40386
|
-
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-"));
|
|
40387
40398
|
const ownsCwd = opts.cwd === void 0;
|
|
40388
40399
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
40389
40400
|
const spawnCtx = { cwd };
|
|
@@ -40544,19 +40555,19 @@ async function runController(opts) {
|
|
|
40544
40555
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
40545
40556
|
if (ownsCwd) {
|
|
40546
40557
|
try {
|
|
40547
|
-
(0,
|
|
40558
|
+
(0, import_node_fs53.rmSync)(cwd, { recursive: true, force: true });
|
|
40548
40559
|
} catch {
|
|
40549
40560
|
}
|
|
40550
40561
|
}
|
|
40551
40562
|
return exitCode ?? 0;
|
|
40552
40563
|
}
|
|
40553
|
-
var
|
|
40564
|
+
var import_node_fs53, import_node_os22, import_node_path65;
|
|
40554
40565
|
var init_controller = __esm({
|
|
40555
40566
|
"src/run-case/controller.ts"() {
|
|
40556
40567
|
"use strict";
|
|
40557
|
-
|
|
40568
|
+
import_node_fs53 = require("fs");
|
|
40558
40569
|
import_node_os22 = __toESM(require("os"), 1);
|
|
40559
|
-
|
|
40570
|
+
import_node_path65 = __toESM(require("path"), 1);
|
|
40560
40571
|
init_claude();
|
|
40561
40572
|
init_stdout_splitter();
|
|
40562
40573
|
init_permission_stdio();
|
|
@@ -40760,7 +40771,7 @@ Options:
|
|
|
40760
40771
|
|
|
40761
40772
|
Example:
|
|
40762
40773
|
ssh -o ProxyCommand='~/.clawd/bin/clawd ssh-relay <peer-device-id>' \\
|
|
40763
|
-
-i ~/.clawd/
|
|
40774
|
+
-i ~/.clawd/ssh-identity/id_ed25519 \\
|
|
40764
40775
|
$USER@127.0.0.1
|
|
40765
40776
|
`;
|
|
40766
40777
|
}
|
|
@@ -40950,8 +40961,8 @@ Env (advanced):
|
|
|
40950
40961
|
`;
|
|
40951
40962
|
|
|
40952
40963
|
// src/index.ts
|
|
40953
|
-
var
|
|
40954
|
-
var
|
|
40964
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
40965
|
+
var import_node_fs51 = __toESM(require("fs"), 1);
|
|
40955
40966
|
var import_node_os21 = __toESM(require("os"), 1);
|
|
40956
40967
|
|
|
40957
40968
|
// ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
|
|
@@ -41702,7 +41713,7 @@ function composeGuestSandbox(base, userWorkDir, spawnCwd) {
|
|
|
41702
41713
|
fsv.allowRead = unionArr(fsv.allowRead, [userWorkDir]);
|
|
41703
41714
|
fsv.allowWrite = unionArr(fsv.allowWrite, [userWorkDir]);
|
|
41704
41715
|
fsv.allowRead = unionArr(fsv.allowRead, [
|
|
41705
|
-
"~/.clawd/
|
|
41716
|
+
"~/.clawd/ssh-identity",
|
|
41706
41717
|
"~/.clawd/bin",
|
|
41707
41718
|
"~/Library/Application Support/@clawos/clawd-desktop/ota-bundles",
|
|
41708
41719
|
"~/.clawd/config.json",
|
|
@@ -41978,28 +41989,27 @@ deviceId \u76F4\u63A5\u53D6\u81EA token\uFF0C\u4E0D\u8981\u6539\u5199\u6216\u731
|
|
|
41978
41989
|
// src/sshd/contact-ssh-prompt.ts
|
|
41979
41990
|
var CONTACT_SSH_SYSTEM_PROMPT_HINT = `## \u8DE8\u8BBE\u5907\u6587\u4EF6\u8BBF\u95EE\uFF08Contact SSH\uFF09
|
|
41980
41991
|
|
|
41981
|
-
\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
|
|
41982
|
-
|
|
41983
|
-
|
|
41984
|
-
|
|
41985
|
-
|
|
41986
|
-
|
|
41987
|
-
|
|
41988
|
-
|
|
41989
|
-
|
|
41990
|
-
|
|
41991
|
-
|
|
41992
|
-
|
|
41993
|
-
|
|
41994
|
-
|
|
41995
|
-
\`\`\`
|
|
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
|
|
41993
|
+
|
|
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
|
+
\`\`\`
|
|
41996
42006
|
|
|
41997
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
|
|
41998
42008
|
|
|
41999
42009
|
**\u6CE8\u610F**\uFF1A
|
|
42000
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
|
|
42001
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
|
|
42002
|
-
- \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
|
|
42003
42013
|
`;
|
|
42004
42014
|
|
|
42005
42015
|
// src/session/reducer.ts
|
|
@@ -46754,8 +46764,8 @@ function turnStartInput(text) {
|
|
|
46754
46764
|
const items = [];
|
|
46755
46765
|
let leftover = text;
|
|
46756
46766
|
for (const m2 of text.matchAll(SKILL_RE)) {
|
|
46757
|
-
const [marker, name,
|
|
46758
|
-
items.push({ type: "skill", name, path:
|
|
46767
|
+
const [marker, name, path76] = m2;
|
|
46768
|
+
items.push({ type: "skill", name, path: path76 });
|
|
46759
46769
|
leftover = leftover.replace(marker, "");
|
|
46760
46770
|
}
|
|
46761
46771
|
for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
|
|
@@ -49108,43 +49118,6 @@ async function forwardInboxPostToPeer(args) {
|
|
|
49108
49118
|
}
|
|
49109
49119
|
return { ok: true };
|
|
49110
49120
|
}
|
|
49111
|
-
async function forwardContactSshKeyIssueToPeer(args) {
|
|
49112
|
-
const f = args.fetchImpl ?? fetch;
|
|
49113
|
-
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
49114
|
-
const url = `${base}/rpc/contact:sshKey:issue`;
|
|
49115
|
-
let res;
|
|
49116
|
-
try {
|
|
49117
|
-
res = await f(url, {
|
|
49118
|
-
method: "POST",
|
|
49119
|
-
headers: {
|
|
49120
|
-
"content-type": "application/json",
|
|
49121
|
-
authorization: `Bearer ${args.contact.connectToken}`
|
|
49122
|
-
},
|
|
49123
|
-
body: JSON.stringify({ deviceId: args.selfDeviceIdForRequest })
|
|
49124
|
-
});
|
|
49125
|
-
} catch (err) {
|
|
49126
|
-
return {
|
|
49127
|
-
ok: false,
|
|
49128
|
-
code: "NETWORK",
|
|
49129
|
-
message: err instanceof Error ? err.message : String(err)
|
|
49130
|
-
};
|
|
49131
|
-
}
|
|
49132
|
-
let json = null;
|
|
49133
|
-
try {
|
|
49134
|
-
json = await res.json();
|
|
49135
|
-
} catch {
|
|
49136
|
-
return { ok: false, code: "PROTOCOL", message: `peer non-JSON (HTTP ${res.status})` };
|
|
49137
|
-
}
|
|
49138
|
-
if (!json) {
|
|
49139
|
-
return { ok: false, code: "PROTOCOL", message: `peer returned null (HTTP ${res.status})` };
|
|
49140
|
-
}
|
|
49141
|
-
if (json.ok === false) {
|
|
49142
|
-
const j = json;
|
|
49143
|
-
return { ok: false, code: j.error ?? "UNKNOWN", message: j.message ?? "" };
|
|
49144
|
-
}
|
|
49145
|
-
const r = json.result;
|
|
49146
|
-
return { ok: true, privateKeyPem: r.privateKeyPem, publicKeyLine: r.publicKeyLine };
|
|
49147
|
-
}
|
|
49148
49121
|
|
|
49149
49122
|
// src/tools/codex-history.ts
|
|
49150
49123
|
var import_node_child_process5 = require("child_process");
|
|
@@ -49332,13 +49305,13 @@ function mapSkillsListResponse(res) {
|
|
|
49332
49305
|
const r = s ?? {};
|
|
49333
49306
|
const name = str3(r.name);
|
|
49334
49307
|
if (!name) continue;
|
|
49335
|
-
const
|
|
49308
|
+
const path76 = str3(r.path);
|
|
49336
49309
|
const description = str3(r.description);
|
|
49337
49310
|
const isPlugin = name.includes(":");
|
|
49338
49311
|
out.push({
|
|
49339
49312
|
name,
|
|
49340
49313
|
source: isPlugin ? "plugin" : "project",
|
|
49341
|
-
...
|
|
49314
|
+
...path76 ? { path: path76 } : {},
|
|
49342
49315
|
...description ? { description } : {},
|
|
49343
49316
|
...isPlugin ? { plugin: name.split(":")[0] } : {}
|
|
49344
49317
|
});
|
|
@@ -50624,7 +50597,11 @@ var AuthGate = class {
|
|
|
50624
50597
|
st.pending = true;
|
|
50625
50598
|
let r;
|
|
50626
50599
|
try {
|
|
50627
|
-
r = await this.opts.authenticate(
|
|
50600
|
+
r = await this.opts.authenticate(
|
|
50601
|
+
parsed.data.token,
|
|
50602
|
+
parsed.data.selfUrl,
|
|
50603
|
+
parsed.data.selfSshPublicKey
|
|
50604
|
+
);
|
|
50628
50605
|
} finally {
|
|
50629
50606
|
st.pending = false;
|
|
50630
50607
|
}
|
|
@@ -51269,7 +51246,10 @@ async function connectRemote(args) {
|
|
|
51269
51246
|
// 缺省时省略字段(不发空串),对方 schema selfUrl 是 .min(1).optional()。
|
|
51270
51247
|
// 决策 #16:身份字段(selfPrincipalId/selfDisplayName/selfDeviceId)已删——
|
|
51271
51248
|
// 对方从 connect token 签名背书取 deviceId/ownerId/provider,不信自报。
|
|
51272
|
-
...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 } : {}
|
|
51273
51253
|
})
|
|
51274
51254
|
);
|
|
51275
51255
|
await new Promise((resolve6, reject) => {
|
|
@@ -51344,7 +51324,9 @@ async function connectRemote(args) {
|
|
|
51344
51324
|
ownerId: f.owner.ownerId,
|
|
51345
51325
|
provider: f.owner.provider,
|
|
51346
51326
|
displayName: f.owner.displayName,
|
|
51347
|
-
grants: f.capability.grants
|
|
51327
|
+
grants: f.capability.grants,
|
|
51328
|
+
// ssh identity 互换(spec 2026-07-15):对方老版本不带 → 省略(不落 undefined 占位)
|
|
51329
|
+
...f.sshPublicKey ? { sshPublicKey: f.sshPublicKey } : {}
|
|
51348
51330
|
};
|
|
51349
51331
|
},
|
|
51350
51332
|
close: () => {
|
|
@@ -51365,6 +51347,8 @@ async function autoReverseContact(args) {
|
|
|
51365
51347
|
resource: { ...g2.resource },
|
|
51366
51348
|
actions: [...g2.actions]
|
|
51367
51349
|
}));
|
|
51350
|
+
const existing = args.store.get(args.deviceId) ?? void 0;
|
|
51351
|
+
const sshPublicKey = args.sshPublicKey ?? existing?.sshPublicKey;
|
|
51368
51352
|
const base = {
|
|
51369
51353
|
deviceId: args.deviceId,
|
|
51370
51354
|
ownerId: args.ownerId,
|
|
@@ -51374,12 +51358,17 @@ async function autoReverseContact(args) {
|
|
|
51374
51358
|
connectToken: "",
|
|
51375
51359
|
grants,
|
|
51376
51360
|
addedAt: now(),
|
|
51377
|
-
pinnedAt: null,
|
|
51378
|
-
sshAllowed: false,
|
|
51361
|
+
pinnedAt: existing?.pinnedAt ?? null,
|
|
51362
|
+
sshAllowed: existing?.sshAllowed ?? false,
|
|
51363
|
+
// ssh identity 互换(spec 2026-07-15):缺省不落 undefined 占位(JSON 持久化干净)
|
|
51364
|
+
...sshPublicKey ? { sshPublicKey } : {},
|
|
51379
51365
|
exposedDirs: []
|
|
51380
51366
|
};
|
|
51381
51367
|
args.store.upsert(base);
|
|
51382
51368
|
args.broadcast({ type: "contact:added", contact: base });
|
|
51369
|
+
if (base.sshAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
|
|
51370
|
+
args.rebuildAuthorizedKeys?.();
|
|
51371
|
+
}
|
|
51383
51372
|
if (!args.exchangeToken) return;
|
|
51384
51373
|
let token;
|
|
51385
51374
|
try {
|
|
@@ -53862,10 +53851,10 @@ function rebuildAuthorizedKeys(store, sshdDir) {
|
|
|
53862
53851
|
if (!c.sshAllowed) continue;
|
|
53863
53852
|
const safe = /^[A-Za-z0-9_.-]+$/.test(c.deviceId);
|
|
53864
53853
|
if (!safe) continue;
|
|
53865
|
-
const pubkey =
|
|
53866
|
-
if (!pubkey) continue;
|
|
53854
|
+
const pubkey = c.sshPublicKey?.trim();
|
|
53855
|
+
if (!pubkey || /[\r\n"]/.test(pubkey)) continue;
|
|
53867
53856
|
const bin = jailBinPath();
|
|
53868
|
-
lines.push(`command="${bin} ${c.deviceId}",restrict ${pubkey
|
|
53857
|
+
lines.push(`command="${bin} ${c.deviceId}",restrict ${pubkey}`);
|
|
53869
53858
|
lines.push(`# contact:${c.deviceId}`);
|
|
53870
53859
|
}
|
|
53871
53860
|
const body = lines.join("\n") + "\n";
|
|
@@ -53873,25 +53862,59 @@ function rebuildAuthorizedKeys(store, sshdDir) {
|
|
|
53873
53862
|
import_node_fs40.default.writeFileSync(tmp, body, { mode: 384 });
|
|
53874
53863
|
import_node_fs40.default.renameSync(tmp, target);
|
|
53875
53864
|
}
|
|
53876
|
-
|
|
53877
|
-
|
|
53878
|
-
|
|
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
|
+
});
|
|
53879
53892
|
try {
|
|
53880
|
-
|
|
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) {
|
|
53903
|
+
try {
|
|
53904
|
+
return import_node_fs41.default.readFileSync(identityPaths(dataDir).pubPath, "utf8").trim();
|
|
53881
53905
|
} catch {
|
|
53882
53906
|
return null;
|
|
53883
53907
|
}
|
|
53884
53908
|
}
|
|
53885
53909
|
|
|
53886
|
-
// src/sshd/
|
|
53887
|
-
var
|
|
53888
|
-
var import_node_path42 = __toESM(require("path"), 1);
|
|
53910
|
+
// src/sshd/ssh-tunnel-relay.ts
|
|
53911
|
+
var import_node_net2 = __toESM(require("net"), 1);
|
|
53889
53912
|
|
|
53890
53913
|
// src/sshd/contact-ssh-log.ts
|
|
53891
|
-
var
|
|
53892
|
-
var
|
|
53914
|
+
var import_node_fs42 = __toESM(require("fs"), 1);
|
|
53915
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
53893
53916
|
function createContactSshLog(dataDir) {
|
|
53894
|
-
const file =
|
|
53917
|
+
const file = import_node_path42.default.join(dataDir, "log", "contact-ssh.log");
|
|
53895
53918
|
function append(level, tag, message, meta) {
|
|
53896
53919
|
const time = (/* @__PURE__ */ new Date()).toISOString();
|
|
53897
53920
|
let line = `[${time}] [${level}] [${tag}] ${message}`;
|
|
@@ -53904,8 +53927,8 @@ function createContactSshLog(dataDir) {
|
|
|
53904
53927
|
}
|
|
53905
53928
|
line += "\n";
|
|
53906
53929
|
try {
|
|
53907
|
-
|
|
53908
|
-
|
|
53930
|
+
import_node_fs42.default.mkdirSync(import_node_path42.default.dirname(file), { recursive: true });
|
|
53931
|
+
import_node_fs42.default.appendFileSync(file, line, { mode: 384 });
|
|
53909
53932
|
} catch {
|
|
53910
53933
|
}
|
|
53911
53934
|
}
|
|
@@ -53924,118 +53947,7 @@ var nullContactSshLog = {
|
|
|
53924
53947
|
}
|
|
53925
53948
|
};
|
|
53926
53949
|
|
|
53927
|
-
// src/sshd/contact-key-puller.ts
|
|
53928
|
-
var CONTACT_KEYS_DIR = "contact-ssh-keys";
|
|
53929
|
-
function safeContactKeyPath(dataDir, deviceId) {
|
|
53930
|
-
const safeId = deviceId.replace(/[\/\\]/g, "_");
|
|
53931
|
-
return import_node_path42.default.join(dataDir, CONTACT_KEYS_DIR, `${safeId}.ed25519`);
|
|
53932
|
-
}
|
|
53933
|
-
async function pullContactSshKeyOnce(deps) {
|
|
53934
|
-
const forward = deps.forwardImpl ?? ((c) => forwardContactSshKeyIssueToPeer({
|
|
53935
|
-
contact: { remoteUrl: c.remoteUrl, connectToken: c.connectToken },
|
|
53936
|
-
selfDeviceIdForRequest: c.deviceId
|
|
53937
|
-
}));
|
|
53938
|
-
const contacts = deps.store.list().filter((c) => c.connectToken.length > 0);
|
|
53939
|
-
const results = await Promise.all(
|
|
53940
|
-
contacts.map(async (c) => {
|
|
53941
|
-
try {
|
|
53942
|
-
const r = await forward(c);
|
|
53943
|
-
return { contact: c, result: r };
|
|
53944
|
-
} catch (err) {
|
|
53945
|
-
return {
|
|
53946
|
-
contact: c,
|
|
53947
|
-
result: {
|
|
53948
|
-
ok: false,
|
|
53949
|
-
code: "NETWORK",
|
|
53950
|
-
message: err instanceof Error ? err.message : String(err)
|
|
53951
|
-
}
|
|
53952
|
-
};
|
|
53953
|
-
}
|
|
53954
|
-
})
|
|
53955
|
-
);
|
|
53956
|
-
const errors = [];
|
|
53957
|
-
let pulled = 0;
|
|
53958
|
-
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
53959
|
-
for (const { contact, result } of results) {
|
|
53960
|
-
if (result.ok) {
|
|
53961
|
-
writeKeyFile(deps.dataDir, contact.deviceId, result.privateKeyPem);
|
|
53962
|
-
pulled++;
|
|
53963
|
-
deps.logger?.info("contact-key-puller: pulled", { deviceId: contact.deviceId });
|
|
53964
|
-
sshLog.info("key.pull.success", "B \u4FA7\u4ECE A \u62C9\u5230 privkey \u5E76\u843D\u76D8", {
|
|
53965
|
-
peerDeviceId: contact.deviceId,
|
|
53966
|
-
peerDisplayName: contact.displayName
|
|
53967
|
-
});
|
|
53968
|
-
} else if (result.code === "UNAUTHORIZED" || result.code === "FORBIDDEN") {
|
|
53969
|
-
const hadStale = removeKeyFile(deps.dataDir, contact.deviceId);
|
|
53970
|
-
sshLog.info("key.pull.rejected", "A \u4FA7\u672A\u6388\u6743\u6211 SSH\uFF08\u6B63\u5E38\u72B6\u6001\uFF1B\u8F6E\u8BE2\u7EE7\u7EED\uFF09", {
|
|
53971
|
-
peerDeviceId: contact.deviceId,
|
|
53972
|
-
peerDisplayName: contact.displayName,
|
|
53973
|
-
clearedStalePrivkey: hadStale
|
|
53974
|
-
});
|
|
53975
|
-
} else {
|
|
53976
|
-
errors.push({
|
|
53977
|
-
deviceId: contact.deviceId,
|
|
53978
|
-
code: result.code,
|
|
53979
|
-
message: result.message
|
|
53980
|
-
});
|
|
53981
|
-
deps.logger?.warn("contact-key-puller: pull failed", {
|
|
53982
|
-
deviceId: contact.deviceId,
|
|
53983
|
-
code: result.code,
|
|
53984
|
-
message: result.message
|
|
53985
|
-
});
|
|
53986
|
-
sshLog.warn("key.pull.error", "\u62C9 privkey \u65F6\u7F51\u7EDC/\u534F\u8BAE\u9519\u8BEF", {
|
|
53987
|
-
peerDeviceId: contact.deviceId,
|
|
53988
|
-
code: result.code,
|
|
53989
|
-
message: result.message
|
|
53990
|
-
});
|
|
53991
|
-
}
|
|
53992
|
-
}
|
|
53993
|
-
return { pulled, errors };
|
|
53994
|
-
}
|
|
53995
|
-
function writeKeyFile(dataDir, deviceId, pem) {
|
|
53996
|
-
const p2 = safeContactKeyPath(dataDir, deviceId);
|
|
53997
|
-
import_node_fs42.default.mkdirSync(import_node_path42.default.dirname(p2), { recursive: true, mode: 448 });
|
|
53998
|
-
import_node_fs42.default.writeFileSync(p2, pem, { mode: 384 });
|
|
53999
|
-
}
|
|
54000
|
-
function removeKeyFile(dataDir, deviceId) {
|
|
54001
|
-
try {
|
|
54002
|
-
import_node_fs42.default.unlinkSync(safeContactKeyPath(dataDir, deviceId));
|
|
54003
|
-
return true;
|
|
54004
|
-
} catch {
|
|
54005
|
-
return false;
|
|
54006
|
-
}
|
|
54007
|
-
}
|
|
54008
|
-
var ContactKeyPuller = class {
|
|
54009
|
-
constructor(deps) {
|
|
54010
|
-
this.deps = deps;
|
|
54011
|
-
}
|
|
54012
|
-
deps;
|
|
54013
|
-
timer = null;
|
|
54014
|
-
async start() {
|
|
54015
|
-
const interval = this.deps.intervalMs ?? 6e4;
|
|
54016
|
-
void this.tick();
|
|
54017
|
-
this.timer = setInterval(() => void this.tick(), interval);
|
|
54018
|
-
this.timer.unref();
|
|
54019
|
-
}
|
|
54020
|
-
stop() {
|
|
54021
|
-
if (this.timer) {
|
|
54022
|
-
clearInterval(this.timer);
|
|
54023
|
-
this.timer = null;
|
|
54024
|
-
}
|
|
54025
|
-
}
|
|
54026
|
-
async tick() {
|
|
54027
|
-
try {
|
|
54028
|
-
await pullContactSshKeyOnce(this.deps);
|
|
54029
|
-
} catch (err) {
|
|
54030
|
-
this.deps.logger?.warn("contact-key-puller: tick failed", {
|
|
54031
|
-
err: err instanceof Error ? err.message : String(err)
|
|
54032
|
-
});
|
|
54033
|
-
}
|
|
54034
|
-
}
|
|
54035
|
-
};
|
|
54036
|
-
|
|
54037
53950
|
// src/sshd/ssh-tunnel-relay.ts
|
|
54038
|
-
var import_node_net2 = __toESM(require("net"), 1);
|
|
54039
53951
|
async function handleSshTunnelUpgrade(req, socket, head, deps) {
|
|
54040
53952
|
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
54041
53953
|
const clientAddr = (req.socket && "remoteAddress" in req.socket ? req.socket.remoteAddress : null) ?? "unknown";
|
|
@@ -54626,9 +54538,9 @@ var CentralClientError = class extends Error {
|
|
|
54626
54538
|
code;
|
|
54627
54539
|
cause;
|
|
54628
54540
|
};
|
|
54629
|
-
async function centralRequest(opts,
|
|
54541
|
+
async function centralRequest(opts, path76, init) {
|
|
54630
54542
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
54631
|
-
const url = `${opts.api.replace(/\/+$/, "")}${
|
|
54543
|
+
const url = `${opts.api.replace(/\/+$/, "")}${path76}`;
|
|
54632
54544
|
const ctrl = new AbortController();
|
|
54633
54545
|
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
54634
54546
|
let res;
|
|
@@ -55565,11 +55477,11 @@ init_protocol();
|
|
|
55565
55477
|
init_protocol();
|
|
55566
55478
|
|
|
55567
55479
|
// src/workspace/git.ts
|
|
55568
|
-
var
|
|
55480
|
+
var import_node_child_process13 = require("child_process");
|
|
55569
55481
|
var import_node_fs47 = __toESM(require("fs"), 1);
|
|
55570
55482
|
var import_node_path48 = __toESM(require("path"), 1);
|
|
55571
55483
|
var import_node_util = require("util");
|
|
55572
|
-
var pexec = (0, import_node_util.promisify)(
|
|
55484
|
+
var pexec = (0, import_node_util.promisify)(import_node_child_process13.execFile);
|
|
55573
55485
|
function normalizePath(p2) {
|
|
55574
55486
|
const resolved = import_node_path48.default.resolve(p2);
|
|
55575
55487
|
try {
|
|
@@ -55982,51 +55894,6 @@ function buildContactHandlers(deps) {
|
|
|
55982
55894
|
|
|
55983
55895
|
// src/handlers/contact-ssh.ts
|
|
55984
55896
|
init_protocol();
|
|
55985
|
-
|
|
55986
|
-
// src/sshd/key-issue.ts
|
|
55987
|
-
var import_node_fs48 = __toESM(require("fs"), 1);
|
|
55988
|
-
var import_node_path49 = __toESM(require("path"), 1);
|
|
55989
|
-
var import_node_child_process13 = require("child_process");
|
|
55990
|
-
function safeDeviceId(deviceId) {
|
|
55991
|
-
return deviceId.replace(/[\/\\]/g, "_");
|
|
55992
|
-
}
|
|
55993
|
-
async function issueContactSshKey(deviceId, sshdDir, opts = {}) {
|
|
55994
|
-
const safeId = safeDeviceId(deviceId);
|
|
55995
|
-
const keysDir = import_node_path49.default.join(sshdDir, "keys");
|
|
55996
|
-
import_node_fs48.default.mkdirSync(keysDir, { recursive: true, mode: 448 });
|
|
55997
|
-
const privPath = import_node_path49.default.join(keysDir, `${safeId}.ed25519`);
|
|
55998
|
-
const pubPath = `${privPath}.pub`;
|
|
55999
|
-
if (import_node_fs48.default.existsSync(privPath) && import_node_fs48.default.existsSync(pubPath)) {
|
|
56000
|
-
return {
|
|
56001
|
-
privateKeyPem: import_node_fs48.default.readFileSync(privPath, "utf8"),
|
|
56002
|
-
publicKeyLine: import_node_fs48.default.readFileSync(pubPath, "utf8").trim()
|
|
56003
|
-
};
|
|
56004
|
-
}
|
|
56005
|
-
const bin = opts.keygenBin ?? "/usr/bin/ssh-keygen";
|
|
56006
|
-
await new Promise((resolve6, reject) => {
|
|
56007
|
-
const p2 = (opts.spawnImpl ?? import_node_child_process13.spawn)(
|
|
56008
|
-
bin,
|
|
56009
|
-
["-t", "ed25519", "-f", privPath, "-N", "", "-q", "-C", `clawd-contact-${safeId}`],
|
|
56010
|
-
{ stdio: "ignore" }
|
|
56011
|
-
);
|
|
56012
|
-
p2.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`ssh-keygen exit ${code}`)));
|
|
56013
|
-
p2.on("error", reject);
|
|
56014
|
-
});
|
|
56015
|
-
try {
|
|
56016
|
-
import_node_fs48.default.chmodSync(privPath, 384);
|
|
56017
|
-
} catch {
|
|
56018
|
-
}
|
|
56019
|
-
try {
|
|
56020
|
-
import_node_fs48.default.chmodSync(pubPath, 420);
|
|
56021
|
-
} catch {
|
|
56022
|
-
}
|
|
56023
|
-
return {
|
|
56024
|
-
privateKeyPem: import_node_fs48.default.readFileSync(privPath, "utf8"),
|
|
56025
|
-
publicKeyLine: import_node_fs48.default.readFileSync(pubPath, "utf8").trim()
|
|
56026
|
-
};
|
|
56027
|
-
}
|
|
56028
|
-
|
|
56029
|
-
// src/handlers/contact-ssh.ts
|
|
56030
55897
|
function ensureOwner2(ctx) {
|
|
56031
55898
|
if (!ctx || ctx.principal.kind !== "owner") {
|
|
56032
55899
|
throw new ClawdError(
|
|
@@ -56035,15 +55902,6 @@ function ensureOwner2(ctx) {
|
|
|
56035
55902
|
);
|
|
56036
55903
|
}
|
|
56037
55904
|
}
|
|
56038
|
-
function ensureGuest(ctx) {
|
|
56039
|
-
if (!ctx || ctx.principal.kind !== "guest") {
|
|
56040
|
-
throw new ClawdError(
|
|
56041
|
-
ERROR_CODES.UNAUTHORIZED,
|
|
56042
|
-
"UNAUTHORIZED: contact:sshKey:issue requires guest ctx"
|
|
56043
|
-
);
|
|
56044
|
-
}
|
|
56045
|
-
return ctx.principal.id;
|
|
56046
|
-
}
|
|
56047
55905
|
function buildContactSshHandlers(deps) {
|
|
56048
55906
|
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
56049
55907
|
const setSshAccess = async (frame, _client, ctx) => {
|
|
@@ -56080,47 +55938,8 @@ function buildContactSshHandlers(deps) {
|
|
|
56080
55938
|
}
|
|
56081
55939
|
};
|
|
56082
55940
|
};
|
|
56083
|
-
const sshKeyIssue = async (frame, _client, ctx) => {
|
|
56084
|
-
const callerDeviceId = ensureGuest(ctx);
|
|
56085
|
-
const { type: _t, requestId: _r, ...rest } = frame;
|
|
56086
|
-
ContactSshKeyIssueArgsSchema.parse(rest);
|
|
56087
|
-
const contact = deps.store.get(callerDeviceId);
|
|
56088
|
-
if (!contact || !contact.sshAllowed) {
|
|
56089
|
-
sshLog.info(
|
|
56090
|
-
"key.issue.rejected",
|
|
56091
|
-
contact ? "guest \u8BF7\u6C42 SSH key \u4F46 owner \u672A\u6388\u6743" : "guest \u8BF7\u6C42 SSH key \u4F46\u4E0D\u5728\u6211\u7684 contactStore",
|
|
56092
|
-
{
|
|
56093
|
-
peerDeviceId: callerDeviceId,
|
|
56094
|
-
peerDisplayName: contact?.displayName
|
|
56095
|
-
}
|
|
56096
|
-
);
|
|
56097
|
-
throw new ClawdError(
|
|
56098
|
-
ERROR_CODES.UNAUTHORIZED,
|
|
56099
|
-
`UNAUTHORIZED: contact ${callerDeviceId} not authorized for SSH`
|
|
56100
|
-
);
|
|
56101
|
-
}
|
|
56102
|
-
const { privateKeyPem, publicKeyLine } = await issueContactSshKey(callerDeviceId, deps.sshdDir);
|
|
56103
|
-
rebuildAuthorizedKeys(deps.store, deps.sshdDir);
|
|
56104
|
-
sshLog.info(
|
|
56105
|
-
"key.issue.success",
|
|
56106
|
-
"A \u4FA7\u53D1 privkey \u7ED9 guest\uFF08\u5E42\u7B49\uFF09\uFF0Cauthorized_keys \u5DF2\u91CD\u5EFA",
|
|
56107
|
-
{
|
|
56108
|
-
peerDeviceId: callerDeviceId,
|
|
56109
|
-
peerDisplayName: contact.displayName,
|
|
56110
|
-
publicKeyFingerprint: publicKeyLine.slice(0, 32) + "..."
|
|
56111
|
-
}
|
|
56112
|
-
);
|
|
56113
|
-
return {
|
|
56114
|
-
response: {
|
|
56115
|
-
type: "contact:sshKey:issue:ok",
|
|
56116
|
-
privateKeyPem,
|
|
56117
|
-
publicKeyLine
|
|
56118
|
-
}
|
|
56119
|
-
};
|
|
56120
|
-
};
|
|
56121
55941
|
return {
|
|
56122
|
-
"contact:setSshAccess": setSshAccess
|
|
56123
|
-
"contact:sshKey:issue": sshKeyIssue
|
|
55942
|
+
"contact:setSshAccess": setSshAccess
|
|
56124
55943
|
};
|
|
56125
55944
|
}
|
|
56126
55945
|
|
|
@@ -56172,12 +55991,14 @@ function buildWhoamiHandler(deps) {
|
|
|
56172
55991
|
grantedPersonas.push({ id: file.personaId, displayName: file.label });
|
|
56173
55992
|
}
|
|
56174
55993
|
}
|
|
55994
|
+
const sshPublicKey = deps.sshIdentityPublicKey();
|
|
56175
55995
|
return {
|
|
56176
55996
|
response: {
|
|
56177
55997
|
type: "whoami:ok",
|
|
56178
55998
|
owner,
|
|
56179
55999
|
capability,
|
|
56180
|
-
grantedPersonas
|
|
56000
|
+
grantedPersonas,
|
|
56001
|
+
...sshPublicKey ? { sshPublicKey } : {}
|
|
56181
56002
|
}
|
|
56182
56003
|
};
|
|
56183
56004
|
};
|
|
@@ -56253,7 +56074,9 @@ function buildDeviceHandlers(deps) {
|
|
|
56253
56074
|
// 自动反向(spec 决策 #11):自报本机可达地址,让对方反向加我为联系人;
|
|
56254
56075
|
// 本机无 tunnel → null → connectRemote 省略 selfUrl 字段(不造假数据)。
|
|
56255
56076
|
// 身份不自报:对方从 token 签名背书取我的 deviceId/ownerId/provider(决策 #16)。
|
|
56256
|
-
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
|
|
56257
56080
|
});
|
|
56258
56081
|
} catch (e) {
|
|
56259
56082
|
throw new ClawdError(
|
|
@@ -56263,6 +56086,8 @@ function buildDeviceHandlers(deps) {
|
|
|
56263
56086
|
}
|
|
56264
56087
|
try {
|
|
56265
56088
|
const wh = await conn.whoami();
|
|
56089
|
+
const existing = deps.store.get(args.deviceId) ?? void 0;
|
|
56090
|
+
const sshPublicKey = wh.sshPublicKey ?? existing?.sshPublicKey;
|
|
56266
56091
|
const contact = {
|
|
56267
56092
|
deviceId: args.deviceId,
|
|
56268
56093
|
ownerId: wh.ownerId,
|
|
@@ -56277,12 +56102,18 @@ function buildDeviceHandlers(deps) {
|
|
|
56277
56102
|
connectToken: exchanged.token,
|
|
56278
56103
|
grants: wh.grants,
|
|
56279
56104
|
addedAt: now(),
|
|
56280
|
-
pinnedAt: null,
|
|
56281
|
-
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 } : {},
|
|
56282
56110
|
exposedDirs: []
|
|
56283
56111
|
};
|
|
56284
56112
|
deps.store.upsert(contact);
|
|
56285
56113
|
deps.broadcast({ type: "contact:added", contact });
|
|
56114
|
+
if (contact.sshAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
|
|
56115
|
+
deps.rebuildAuthorizedKeys?.();
|
|
56116
|
+
}
|
|
56286
56117
|
return {
|
|
56287
56118
|
response: {
|
|
56288
56119
|
type: "device:connect:ok",
|
|
@@ -56435,7 +56266,7 @@ function buildPersonaHandlers(deps) {
|
|
|
56435
56266
|
}
|
|
56436
56267
|
|
|
56437
56268
|
// src/handlers/attachment.ts
|
|
56438
|
-
var
|
|
56269
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
56439
56270
|
init_protocol();
|
|
56440
56271
|
init_protocol();
|
|
56441
56272
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -56515,12 +56346,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
56515
56346
|
`session ${args.sessionId} scope unresolved`
|
|
56516
56347
|
);
|
|
56517
56348
|
}
|
|
56518
|
-
const cwdAbs =
|
|
56519
|
-
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);
|
|
56520
56351
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
|
|
56521
56352
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
56522
56353
|
const entry = entries.find((e) => {
|
|
56523
|
-
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);
|
|
56524
56355
|
return storedAbs === candidateAbs && !e.stale;
|
|
56525
56356
|
});
|
|
56526
56357
|
if (!entry) {
|
|
@@ -56545,7 +56376,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
56545
56376
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
56546
56377
|
const f = deps.sessionStore.read(sessionId);
|
|
56547
56378
|
if (!f) return;
|
|
56548
|
-
assertGuestAttachmentPath(ctx,
|
|
56379
|
+
assertGuestAttachmentPath(ctx, import_node_path49.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
|
|
56549
56380
|
}
|
|
56550
56381
|
const groupAdd = async (frame, _client, ctx) => {
|
|
56551
56382
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -56560,8 +56391,8 @@ function buildAttachmentHandlers(deps) {
|
|
|
56560
56391
|
if (!scope) {
|
|
56561
56392
|
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
|
|
56562
56393
|
}
|
|
56563
|
-
const cwdAbs =
|
|
56564
|
-
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);
|
|
56565
56396
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
|
|
56566
56397
|
const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
|
|
56567
56398
|
const size = 0;
|
|
@@ -56620,19 +56451,19 @@ function buildAttachmentHandlers(deps) {
|
|
|
56620
56451
|
|
|
56621
56452
|
// src/handlers/extension.ts
|
|
56622
56453
|
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
56623
|
-
var
|
|
56454
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
56624
56455
|
init_protocol();
|
|
56625
56456
|
|
|
56626
56457
|
// src/extension/bundle-zip.ts
|
|
56627
56458
|
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
56628
|
-
var
|
|
56459
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
56629
56460
|
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
56630
56461
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
56631
56462
|
async function bundleExtensionDir(dir) {
|
|
56632
56463
|
const entries = await listFilesSorted(dir);
|
|
56633
56464
|
const zip = new import_jszip2.default();
|
|
56634
56465
|
for (const rel of entries) {
|
|
56635
|
-
const abs =
|
|
56466
|
+
const abs = import_node_path50.default.join(dir, rel);
|
|
56636
56467
|
const content = await import_promises5.default.readFile(abs);
|
|
56637
56468
|
zip.file(rel, content, { date: FIXED_DATE });
|
|
56638
56469
|
}
|
|
@@ -56653,7 +56484,7 @@ async function listFilesSorted(rootDir) {
|
|
|
56653
56484
|
return out;
|
|
56654
56485
|
}
|
|
56655
56486
|
async function walk(absRoot, relPrefix, out) {
|
|
56656
|
-
const dirAbs =
|
|
56487
|
+
const dirAbs = import_node_path50.default.join(absRoot, relPrefix);
|
|
56657
56488
|
const entries = await import_promises5.default.readdir(dirAbs, { withFileTypes: true });
|
|
56658
56489
|
for (const e of entries) {
|
|
56659
56490
|
if (IGNORE_BASENAMES.has(e.name)) continue;
|
|
@@ -56707,25 +56538,25 @@ function computePublishCheck(args) {
|
|
|
56707
56538
|
|
|
56708
56539
|
// src/extension/install-flow.ts
|
|
56709
56540
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
56710
|
-
var
|
|
56541
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
56711
56542
|
var import_node_os19 = __toESM(require("os"), 1);
|
|
56712
56543
|
var import_node_crypto15 = __toESM(require("crypto"), 1);
|
|
56713
56544
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
56714
56545
|
|
|
56715
56546
|
// src/extension/paths.ts
|
|
56716
56547
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
56717
|
-
var
|
|
56548
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
56718
56549
|
function clawdHomeRoot(override) {
|
|
56719
|
-
return override ?? process.env.CLAWD_HOME ??
|
|
56550
|
+
return override ?? process.env.CLAWD_HOME ?? import_node_path51.default.join(import_node_os18.default.homedir(), ".clawd");
|
|
56720
56551
|
}
|
|
56721
56552
|
function extensionsRoot(override) {
|
|
56722
|
-
return
|
|
56553
|
+
return import_node_path51.default.join(clawdHomeRoot(override), "extensions");
|
|
56723
56554
|
}
|
|
56724
56555
|
function publishedChannelsFile(override) {
|
|
56725
|
-
return
|
|
56556
|
+
return import_node_path51.default.join(clawdHomeRoot(override), "extensions-published.json");
|
|
56726
56557
|
}
|
|
56727
56558
|
function bundleCacheRoot(override) {
|
|
56728
|
-
return
|
|
56559
|
+
return import_node_path51.default.join(clawdHomeRoot(override), "extension-bundles");
|
|
56729
56560
|
}
|
|
56730
56561
|
|
|
56731
56562
|
// src/extension/install-flow.ts
|
|
@@ -56752,7 +56583,7 @@ async function installFromChannel(args, deps) {
|
|
|
56752
56583
|
throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56753
56584
|
}
|
|
56754
56585
|
for (const name of Object.keys(zip.files)) {
|
|
56755
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
56586
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path52.default.isAbsolute(name)) {
|
|
56756
56587
|
throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56757
56588
|
}
|
|
56758
56589
|
}
|
|
@@ -56784,7 +56615,7 @@ async function installFromChannel(args, deps) {
|
|
|
56784
56615
|
);
|
|
56785
56616
|
}
|
|
56786
56617
|
const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
|
|
56787
|
-
const destDir =
|
|
56618
|
+
const destDir = import_node_path52.default.join(deps.extensionsRoot, localExtId);
|
|
56788
56619
|
let destExists = false;
|
|
56789
56620
|
try {
|
|
56790
56621
|
await import_promises6.default.access(destDir);
|
|
@@ -56798,16 +56629,16 @@ async function installFromChannel(args, deps) {
|
|
|
56798
56629
|
);
|
|
56799
56630
|
}
|
|
56800
56631
|
const stage = await import_promises6.default.mkdtemp(
|
|
56801
|
-
|
|
56632
|
+
import_node_path52.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
|
|
56802
56633
|
);
|
|
56803
56634
|
try {
|
|
56804
56635
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56805
|
-
const dest =
|
|
56636
|
+
const dest = import_node_path52.default.join(stage, name);
|
|
56806
56637
|
if (entry.dir) {
|
|
56807
56638
|
await import_promises6.default.mkdir(dest, { recursive: true });
|
|
56808
56639
|
continue;
|
|
56809
56640
|
}
|
|
56810
|
-
await import_promises6.default.mkdir(
|
|
56641
|
+
await import_promises6.default.mkdir(import_node_path52.default.dirname(dest), { recursive: true });
|
|
56811
56642
|
if (name === "manifest.json") {
|
|
56812
56643
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56813
56644
|
await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -56828,7 +56659,7 @@ async function installFromChannel(args, deps) {
|
|
|
56828
56659
|
|
|
56829
56660
|
// src/extension/update-flow.ts
|
|
56830
56661
|
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
56831
|
-
var
|
|
56662
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
56832
56663
|
var import_node_os20 = __toESM(require("os"), 1);
|
|
56833
56664
|
var import_node_crypto16 = __toESM(require("crypto"), 1);
|
|
56834
56665
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
@@ -56845,11 +56676,11 @@ async function updateFromChannel(args, deps) {
|
|
|
56845
56676
|
channelRef.extId,
|
|
56846
56677
|
channelRef.ownerPrincipalId
|
|
56847
56678
|
);
|
|
56848
|
-
const liveDir =
|
|
56679
|
+
const liveDir = import_node_path53.default.join(deps.extensionsRoot, localExtId);
|
|
56849
56680
|
const prevDir = `${liveDir}.prev`;
|
|
56850
56681
|
let existingVersion;
|
|
56851
56682
|
try {
|
|
56852
|
-
const raw = await import_promises7.default.readFile(
|
|
56683
|
+
const raw = await import_promises7.default.readFile(import_node_path53.default.join(liveDir, "manifest.json"), "utf8");
|
|
56853
56684
|
const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
|
|
56854
56685
|
if (!parsed2.success) {
|
|
56855
56686
|
throw new UpdateError(
|
|
@@ -56882,7 +56713,7 @@ async function updateFromChannel(args, deps) {
|
|
|
56882
56713
|
throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56883
56714
|
}
|
|
56884
56715
|
for (const name of Object.keys(zip.files)) {
|
|
56885
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
56716
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path53.default.isAbsolute(name)) {
|
|
56886
56717
|
throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56887
56718
|
}
|
|
56888
56719
|
}
|
|
@@ -56917,16 +56748,16 @@ async function updateFromChannel(args, deps) {
|
|
|
56917
56748
|
await import_promises7.default.rm(prevDir, { recursive: true, force: true });
|
|
56918
56749
|
await import_promises7.default.rename(liveDir, prevDir);
|
|
56919
56750
|
const stage = await import_promises7.default.mkdtemp(
|
|
56920
|
-
|
|
56751
|
+
import_node_path53.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
|
|
56921
56752
|
);
|
|
56922
56753
|
try {
|
|
56923
56754
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56924
|
-
const dest =
|
|
56755
|
+
const dest = import_node_path53.default.join(stage, name);
|
|
56925
56756
|
if (entry.dir) {
|
|
56926
56757
|
await import_promises7.default.mkdir(dest, { recursive: true });
|
|
56927
56758
|
continue;
|
|
56928
56759
|
}
|
|
56929
|
-
await import_promises7.default.mkdir(
|
|
56760
|
+
await import_promises7.default.mkdir(import_node_path53.default.dirname(dest), { recursive: true });
|
|
56930
56761
|
if (name === "manifest.json") {
|
|
56931
56762
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56932
56763
|
await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -57019,7 +56850,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
57019
56850
|
);
|
|
57020
56851
|
}
|
|
57021
56852
|
}
|
|
57022
|
-
const manifestPath =
|
|
56853
|
+
const manifestPath = import_node_path54.default.join(root, extId, "manifest.json");
|
|
57023
56854
|
const manifest = await readManifest(root, extId);
|
|
57024
56855
|
const next = { ...manifest, version: newVersion };
|
|
57025
56856
|
const tmp = `${manifestPath}.tmp`;
|
|
@@ -57027,7 +56858,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
57027
56858
|
await import_promises8.default.rename(tmp, manifestPath);
|
|
57028
56859
|
}
|
|
57029
56860
|
async function readManifest(root, extId) {
|
|
57030
|
-
const file =
|
|
56861
|
+
const file = import_node_path54.default.join(root, extId, "manifest.json");
|
|
57031
56862
|
let raw;
|
|
57032
56863
|
try {
|
|
57033
56864
|
raw = await import_promises8.default.readFile(file, "utf8");
|
|
@@ -57118,7 +56949,7 @@ function buildExtensionHandlers(deps) {
|
|
|
57118
56949
|
};
|
|
57119
56950
|
async function buildSnapshotMeta(extId) {
|
|
57120
56951
|
const manifest = await readManifest(deps.root, extId);
|
|
57121
|
-
const { sha256, buffer } = await bundleExtensionDir(
|
|
56952
|
+
const { sha256, buffer } = await bundleExtensionDir(import_node_path54.default.join(deps.root, extId));
|
|
57122
56953
|
return { manifest, contentHash: sha256, buffer };
|
|
57123
56954
|
}
|
|
57124
56955
|
const publish = async (frame, _client, ctx) => {
|
|
@@ -57299,9 +57130,9 @@ function buildExtensionHandlers(deps) {
|
|
|
57299
57130
|
}
|
|
57300
57131
|
|
|
57301
57132
|
// src/app-builder/project-store.ts
|
|
57302
|
-
var
|
|
57133
|
+
var import_node_fs48 = require("fs");
|
|
57303
57134
|
var import_node_child_process14 = require("child_process");
|
|
57304
|
-
var
|
|
57135
|
+
var import_node_path55 = require("path");
|
|
57305
57136
|
init_protocol();
|
|
57306
57137
|
var PROJECTS_DIR = "projects";
|
|
57307
57138
|
var META_FILE = ".clawd-project.json";
|
|
@@ -57315,19 +57146,19 @@ var ProjectStore = class {
|
|
|
57315
57146
|
root;
|
|
57316
57147
|
/** projects/<name>/.clawd-project.json 路径 */
|
|
57317
57148
|
metaPath(name) {
|
|
57318
|
-
return (0,
|
|
57149
|
+
return (0, import_node_path55.join)(this.projectsRoot(), name, META_FILE);
|
|
57319
57150
|
}
|
|
57320
57151
|
/** projects/<name>/ 目录路径(cwd 用) */
|
|
57321
57152
|
projectDir(name) {
|
|
57322
|
-
return (0,
|
|
57153
|
+
return (0, import_node_path55.join)(this.projectsRoot(), name);
|
|
57323
57154
|
}
|
|
57324
57155
|
projectsRoot() {
|
|
57325
|
-
return (0,
|
|
57156
|
+
return (0, import_node_path55.join)(this.root, PROJECTS_DIR);
|
|
57326
57157
|
}
|
|
57327
57158
|
async list() {
|
|
57328
57159
|
let entries;
|
|
57329
57160
|
try {
|
|
57330
|
-
entries = await
|
|
57161
|
+
entries = await import_node_fs48.promises.readdir(this.projectsRoot());
|
|
57331
57162
|
} catch (err) {
|
|
57332
57163
|
if (err.code === "ENOENT") return [];
|
|
57333
57164
|
throw err;
|
|
@@ -57335,7 +57166,7 @@ var ProjectStore = class {
|
|
|
57335
57166
|
const out = [];
|
|
57336
57167
|
for (const name of entries) {
|
|
57337
57168
|
try {
|
|
57338
|
-
const raw = await
|
|
57169
|
+
const raw = await import_node_fs48.promises.readFile(this.metaPath(name), "utf8");
|
|
57339
57170
|
const json = JSON.parse(raw);
|
|
57340
57171
|
let migrated = false;
|
|
57341
57172
|
if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
|
|
@@ -57346,7 +57177,7 @@ var ProjectStore = class {
|
|
|
57346
57177
|
if (parsed.success) {
|
|
57347
57178
|
out.push(parsed.data);
|
|
57348
57179
|
if (migrated) {
|
|
57349
|
-
void
|
|
57180
|
+
void import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
|
|
57350
57181
|
});
|
|
57351
57182
|
}
|
|
57352
57183
|
}
|
|
@@ -57390,8 +57221,8 @@ var ProjectStore = class {
|
|
|
57390
57221
|
throw new Error(`invalid name "${name}": ${validated.error.message}`);
|
|
57391
57222
|
}
|
|
57392
57223
|
const dir = this.projectDir(name);
|
|
57393
|
-
await
|
|
57394
|
-
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");
|
|
57395
57226
|
return meta;
|
|
57396
57227
|
}
|
|
57397
57228
|
/**
|
|
@@ -57434,7 +57265,7 @@ var ProjectStore = class {
|
|
|
57434
57265
|
}
|
|
57435
57266
|
async delete(name) {
|
|
57436
57267
|
const dir = this.projectDir(name);
|
|
57437
|
-
await
|
|
57268
|
+
await import_node_fs48.promises.rm(dir, { recursive: true, force: true });
|
|
57438
57269
|
}
|
|
57439
57270
|
async updatePort(name, newPort) {
|
|
57440
57271
|
if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
|
|
@@ -57450,7 +57281,7 @@ var ProjectStore = class {
|
|
|
57450
57281
|
throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
|
|
57451
57282
|
}
|
|
57452
57283
|
const updated = { ...target, port: newPort };
|
|
57453
|
-
await
|
|
57284
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
57454
57285
|
return updated;
|
|
57455
57286
|
}
|
|
57456
57287
|
/**
|
|
@@ -57467,7 +57298,7 @@ var ProjectStore = class {
|
|
|
57467
57298
|
if (!validated.success) {
|
|
57468
57299
|
throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
|
|
57469
57300
|
}
|
|
57470
|
-
await
|
|
57301
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57471
57302
|
return validated.data;
|
|
57472
57303
|
}
|
|
57473
57304
|
/**
|
|
@@ -57488,7 +57319,7 @@ var ProjectStore = class {
|
|
|
57488
57319
|
if (!validated.success) {
|
|
57489
57320
|
throw new Error(`invalid publishJob: ${validated.error.message}`);
|
|
57490
57321
|
}
|
|
57491
|
-
await
|
|
57322
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57492
57323
|
return validated.data;
|
|
57493
57324
|
}
|
|
57494
57325
|
/** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
|
|
@@ -57503,7 +57334,7 @@ var ProjectStore = class {
|
|
|
57503
57334
|
if (!validated.success) {
|
|
57504
57335
|
throw new Error(`failed to clear publishJob: ${validated.error.message}`);
|
|
57505
57336
|
}
|
|
57506
|
-
await
|
|
57337
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57507
57338
|
return validated.data;
|
|
57508
57339
|
}
|
|
57509
57340
|
};
|
|
@@ -57624,8 +57455,8 @@ var PublishJobRegistry = class {
|
|
|
57624
57455
|
|
|
57625
57456
|
// src/app-builder/publish-job-runner.ts
|
|
57626
57457
|
var import_node_child_process16 = require("child_process");
|
|
57627
|
-
var
|
|
57628
|
-
var
|
|
57458
|
+
var import_node_fs49 = require("fs");
|
|
57459
|
+
var import_node_path56 = require("path");
|
|
57629
57460
|
|
|
57630
57461
|
// src/app-builder/publish-stage-parser.ts
|
|
57631
57462
|
var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
|
|
@@ -57657,10 +57488,10 @@ async function startPublishJob(deps, args) {
|
|
|
57657
57488
|
return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
|
|
57658
57489
|
}
|
|
57659
57490
|
const projDir = projectDir(args.name);
|
|
57660
|
-
const logPath = (0,
|
|
57491
|
+
const logPath = (0, import_node_path56.join)(projDir, ".publish.log");
|
|
57661
57492
|
let logStream = null;
|
|
57662
57493
|
try {
|
|
57663
|
-
logStream = (0,
|
|
57494
|
+
logStream = (0, import_node_fs49.createWriteStream)(logPath, { flags: "w" });
|
|
57664
57495
|
} catch {
|
|
57665
57496
|
logStream = null;
|
|
57666
57497
|
}
|
|
@@ -57917,8 +57748,8 @@ async function recoverInterruptedJobs(deps) {
|
|
|
57917
57748
|
|
|
57918
57749
|
// src/handlers/app-builder.ts
|
|
57919
57750
|
init_protocol();
|
|
57920
|
-
var
|
|
57921
|
-
var
|
|
57751
|
+
var import_node_path57 = require("path");
|
|
57752
|
+
var import_node_fs50 = require("fs");
|
|
57922
57753
|
var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
|
|
57923
57754
|
var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
|
|
57924
57755
|
async function recoverInterruptedPublishJobs(store, logger) {
|
|
@@ -57999,7 +57830,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
57999
57830
|
async function listAllUsersProjects() {
|
|
58000
57831
|
if (!deps.usersRoot || !deps.getStore) return [];
|
|
58001
57832
|
const getStore = deps.getStore;
|
|
58002
|
-
const userIds = await
|
|
57833
|
+
const userIds = await import_node_fs50.promises.readdir(deps.usersRoot).catch(() => []);
|
|
58003
57834
|
const perUser = await Promise.all(
|
|
58004
57835
|
userIds.map((uid) => getStore(uid).list().catch(() => []))
|
|
58005
57836
|
);
|
|
@@ -58075,8 +57906,8 @@ function buildAppBuilderHandlers(deps) {
|
|
|
58075
57906
|
const project = await userStore.create(f.name, reservedPorts);
|
|
58076
57907
|
try {
|
|
58077
57908
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
|
|
58078
|
-
const templateSrcDir = (0,
|
|
58079
|
-
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");
|
|
58080
57911
|
const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
|
|
58081
57912
|
deps.logger?.info("app-builder.scaffold.done", {
|
|
58082
57913
|
name: project.name,
|
|
@@ -58297,7 +58128,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
58297
58128
|
await userStore.clearPublishJob(args.name);
|
|
58298
58129
|
}
|
|
58299
58130
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
|
|
58300
|
-
const scriptPath = (0,
|
|
58131
|
+
const scriptPath = (0, import_node_path57.join)(deps.deployKitRoot, "scripts", "publish.sh");
|
|
58301
58132
|
deps.logger?.info("app-builder.publish.start", {
|
|
58302
58133
|
name: args.name,
|
|
58303
58134
|
sessionId: boundSession.sessionId,
|
|
@@ -58473,7 +58304,7 @@ function buildVisitorHandlers(deps) {
|
|
|
58473
58304
|
|
|
58474
58305
|
// src/extension/registry.ts
|
|
58475
58306
|
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
58476
|
-
var
|
|
58307
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
58477
58308
|
async function loadAll(root) {
|
|
58478
58309
|
let entries;
|
|
58479
58310
|
try {
|
|
@@ -58486,13 +58317,13 @@ async function loadAll(root) {
|
|
|
58486
58317
|
for (const ent of entries) {
|
|
58487
58318
|
if (!ent.isDirectory()) continue;
|
|
58488
58319
|
if (ent.name.startsWith(".")) continue;
|
|
58489
|
-
records.push(await loadOne(
|
|
58320
|
+
records.push(await loadOne(import_node_path58.default.join(root, ent.name), ent.name));
|
|
58490
58321
|
}
|
|
58491
58322
|
records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
|
|
58492
58323
|
return records;
|
|
58493
58324
|
}
|
|
58494
58325
|
async function loadOne(dir, dirName) {
|
|
58495
|
-
const manifestPath =
|
|
58326
|
+
const manifestPath = import_node_path58.default.join(dir, "manifest.json");
|
|
58496
58327
|
let raw;
|
|
58497
58328
|
try {
|
|
58498
58329
|
raw = await import_promises9.default.readFile(manifestPath, "utf8");
|
|
@@ -58537,7 +58368,7 @@ async function loadOne(dir, dirName) {
|
|
|
58537
58368
|
|
|
58538
58369
|
// src/extension/uninstall.ts
|
|
58539
58370
|
var import_promises10 = __toESM(require("fs/promises"), 1);
|
|
58540
|
-
var
|
|
58371
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
58541
58372
|
var UninstallError = class extends Error {
|
|
58542
58373
|
constructor(code, message) {
|
|
58543
58374
|
super(message);
|
|
@@ -58546,7 +58377,7 @@ var UninstallError = class extends Error {
|
|
|
58546
58377
|
code;
|
|
58547
58378
|
};
|
|
58548
58379
|
async function uninstall(deps) {
|
|
58549
|
-
const dir =
|
|
58380
|
+
const dir = import_node_path59.default.join(deps.root, deps.extId);
|
|
58550
58381
|
try {
|
|
58551
58382
|
await import_promises10.default.access(dir);
|
|
58552
58383
|
} catch {
|
|
@@ -58613,7 +58444,8 @@ function buildMethodHandlers(deps) {
|
|
|
58613
58444
|
ownerId: deps.ownerId,
|
|
58614
58445
|
ownerProvider: deps.ownerProvider,
|
|
58615
58446
|
personaStore: deps.personaStore,
|
|
58616
|
-
capabilityManager: deps.capabilityManager
|
|
58447
|
+
capabilityManager: deps.capabilityManager,
|
|
58448
|
+
sshIdentityPublicKey: deps.sshIdentityPublicKey
|
|
58617
58449
|
}),
|
|
58618
58450
|
...buildFeishuAuthHandlers(deps.feishuAuth),
|
|
58619
58451
|
...buildDeviceHandlers(deps.feishuDevice),
|
|
@@ -58946,11 +58778,8 @@ var METHOD_GRANT_MAP = {
|
|
|
58946
58778
|
"contact:pin": ADMIN_ANY,
|
|
58947
58779
|
"contact:remove": ADMIN_ANY,
|
|
58948
58780
|
// contact:setSshAccess (owner UI 配 SSH 授权):ADMIN_ANY
|
|
58949
|
-
// contact:sshKey:issue (guest daemon 拉自己的 privkey):public — handler 内校
|
|
58950
|
-
// ctx.principal.kind==='guest' + store.get(callerId).sshAllowed
|
|
58951
58781
|
// (对齐 inbox:postMessage 的"能连上=有 auth,业务在 handler 校"模式)
|
|
58952
58782
|
"contact:setSshAccess": ADMIN_ANY,
|
|
58953
|
-
"contact:sshKey:issue": { kind: "public" },
|
|
58954
58783
|
// ---- visitor:* (访客名单,owner-only) ----
|
|
58955
58784
|
// owner 看完整访客名单(含没开会话的);guest 不可调(handler 内再 assertOwner 兜底)。
|
|
58956
58785
|
"visitor:list": ADMIN_ANY,
|
|
@@ -59136,7 +58965,7 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
|
|
|
59136
58965
|
|
|
59137
58966
|
// src/extension/runtime.ts
|
|
59138
58967
|
var import_node_child_process18 = require("child_process");
|
|
59139
|
-
var
|
|
58968
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
59140
58969
|
var import_promises11 = require("timers/promises");
|
|
59141
58970
|
|
|
59142
58971
|
// src/extension/port-allocator.ts
|
|
@@ -59237,7 +59066,7 @@ var Runtime = class {
|
|
|
59237
59066
|
/\$CLAWOS_EXT_PORT/g,
|
|
59238
59067
|
String(port)
|
|
59239
59068
|
);
|
|
59240
|
-
const dir =
|
|
59069
|
+
const dir = import_node_path60.default.join(this.root, extId);
|
|
59241
59070
|
const env = {
|
|
59242
59071
|
...process.env,
|
|
59243
59072
|
CLAWOS_EXT_PORT: String(port),
|
|
@@ -59349,7 +59178,7 @@ ${handle.stderrTail}`
|
|
|
59349
59178
|
|
|
59350
59179
|
// src/extension/published-channels.ts
|
|
59351
59180
|
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
59352
|
-
var
|
|
59181
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
59353
59182
|
init_zod();
|
|
59354
59183
|
var PublishedChannelsError = class extends Error {
|
|
59355
59184
|
constructor(code, message) {
|
|
@@ -59448,7 +59277,7 @@ var PublishedChannelStore = class {
|
|
|
59448
59277
|
)
|
|
59449
59278
|
};
|
|
59450
59279
|
const tmp = `${this.filePath}.tmp`;
|
|
59451
|
-
await import_promises12.default.mkdir(
|
|
59280
|
+
await import_promises12.default.mkdir(import_node_path61.default.dirname(this.filePath), { recursive: true });
|
|
59452
59281
|
await import_promises12.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
59453
59282
|
await import_promises12.default.rename(tmp, this.filePath);
|
|
59454
59283
|
}
|
|
@@ -59456,7 +59285,7 @@ var PublishedChannelStore = class {
|
|
|
59456
59285
|
|
|
59457
59286
|
// src/extension/bundle-cache.ts
|
|
59458
59287
|
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
59459
|
-
var
|
|
59288
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
59460
59289
|
var BundleCache = class {
|
|
59461
59290
|
constructor(rootDir) {
|
|
59462
59291
|
this.rootDir = rootDir;
|
|
@@ -59465,14 +59294,14 @@ var BundleCache = class {
|
|
|
59465
59294
|
/** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
|
|
59466
59295
|
async write(snapshotHash, buffer) {
|
|
59467
59296
|
await import_promises13.default.mkdir(this.rootDir, { recursive: true });
|
|
59468
|
-
const file =
|
|
59297
|
+
const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59469
59298
|
const tmp = `${file}.tmp`;
|
|
59470
59299
|
await import_promises13.default.writeFile(tmp, buffer, { mode: 384 });
|
|
59471
59300
|
await import_promises13.default.rename(tmp, file);
|
|
59472
59301
|
}
|
|
59473
59302
|
/** Returns the bundle bytes, or null when the file doesn't exist. */
|
|
59474
59303
|
async read(snapshotHash) {
|
|
59475
|
-
const file =
|
|
59304
|
+
const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59476
59305
|
try {
|
|
59477
59306
|
return await import_promises13.default.readFile(file);
|
|
59478
59307
|
} catch (e) {
|
|
@@ -59482,7 +59311,7 @@ var BundleCache = class {
|
|
|
59482
59311
|
}
|
|
59483
59312
|
/** Idempotent — missing file is not an error. */
|
|
59484
59313
|
async delete(snapshotHash) {
|
|
59485
|
-
const file =
|
|
59314
|
+
const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59486
59315
|
await import_promises13.default.rm(file, { force: true });
|
|
59487
59316
|
}
|
|
59488
59317
|
};
|
|
@@ -59505,24 +59334,24 @@ async function startDaemon(config) {
|
|
|
59505
59334
|
sampling: logShippingCfg.sampling,
|
|
59506
59335
|
homeDir: import_node_os21.default.homedir()
|
|
59507
59336
|
});
|
|
59508
|
-
const logDir =
|
|
59509
|
-
|
|
59337
|
+
const logDir = import_node_path63.default.join(config.dataDir, "log");
|
|
59338
|
+
import_node_fs51.default.mkdirSync(logDir, { recursive: true });
|
|
59510
59339
|
const logger = createLogger({
|
|
59511
59340
|
level: config.logLevel,
|
|
59512
|
-
file:
|
|
59341
|
+
file: import_node_path63.default.join(logDir, "clawd.log"),
|
|
59513
59342
|
logClient
|
|
59514
59343
|
});
|
|
59515
59344
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
59516
59345
|
const screenIdleProbeLogger = createFileOnlyLogger({
|
|
59517
|
-
file:
|
|
59346
|
+
file: import_node_path63.default.join(logDir, "screen-idle-probe.log"),
|
|
59518
59347
|
level: "debug"
|
|
59519
59348
|
});
|
|
59520
59349
|
logger.info("screen-idle probe logger enabled", {
|
|
59521
|
-
file:
|
|
59350
|
+
file: import_node_path63.default.join(logDir, "screen-idle-probe.log")
|
|
59522
59351
|
});
|
|
59523
59352
|
const CAP_TARGETS = [
|
|
59524
|
-
|
|
59525
|
-
|
|
59353
|
+
import_node_path63.default.join(logDir, "clawd.log"),
|
|
59354
|
+
import_node_path63.default.join(logDir, "screen-idle-probe.log")
|
|
59526
59355
|
];
|
|
59527
59356
|
const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
|
|
59528
59357
|
const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
|
|
@@ -59618,7 +59447,7 @@ async function startDaemon(config) {
|
|
|
59618
59447
|
// Task 1.7:authenticate 注入路径替代 expectedToken 单 token 比对。
|
|
59619
59448
|
// owner 路径 constantTimeEqual 防侧信道;guest 路径走 capabilityRegistry.
|
|
59620
59449
|
expectedToken: resolvedAuthToken,
|
|
59621
|
-
authenticate: async (t, selfUrl) => {
|
|
59450
|
+
authenticate: async (t, selfUrl, selfSshPublicKey) => {
|
|
59622
59451
|
const result = await authenticate(t, buildConnectAuthDeps());
|
|
59623
59452
|
if (result.ok && result.context.principal.kind === "guest") {
|
|
59624
59453
|
void autoReverseContact({
|
|
@@ -59629,6 +59458,10 @@ async function startDaemon(config) {
|
|
|
59629
59458
|
provider: result.context.provider ?? "",
|
|
59630
59459
|
displayName: result.context.principal.displayName ?? result.context.principal.id,
|
|
59631
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")),
|
|
59632
59465
|
// 决策 #16 换票补全:用 owner 当前 jwt 换 aud=对方设备 的票(本机主动连对方用)。
|
|
59633
59466
|
// 现读 ownerIdentityStore(热切换后用新身份);失败 → null → 保持空票。
|
|
59634
59467
|
exchangeToken: async (targetDeviceId) => {
|
|
@@ -59676,8 +59509,8 @@ async function startDaemon(config) {
|
|
|
59676
59509
|
const agents = new AgentsScanner();
|
|
59677
59510
|
const history = new ClaudeHistoryReader();
|
|
59678
59511
|
let transport = null;
|
|
59679
|
-
const personaStore = new PersonaStore(
|
|
59680
|
-
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");
|
|
59681
59514
|
const defaultsRoot = findDefaultsRoot(logger);
|
|
59682
59515
|
if (defaultsRoot) {
|
|
59683
59516
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -59697,17 +59530,17 @@ async function startDaemon(config) {
|
|
|
59697
59530
|
migrateCodexSandbox({ store: personaStore, logger });
|
|
59698
59531
|
const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
|
|
59699
59532
|
const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
|
|
59700
|
-
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));
|
|
59701
59534
|
const mcpConfigs = [];
|
|
59702
59535
|
const dispatchServerCandidates = [
|
|
59703
|
-
|
|
59536
|
+
import_node_path63.default.join(here, "dispatch", "mcp-server.cjs"),
|
|
59704
59537
|
// 生产 dist/index → dist/dispatch/mcp-server.cjs
|
|
59705
|
-
|
|
59538
|
+
import_node_path63.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
|
|
59706
59539
|
// dev tsx src/index → ../dist/dispatch/mcp-server.cjs
|
|
59707
59540
|
];
|
|
59708
|
-
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) =>
|
|
59541
|
+
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
|
|
59709
59542
|
if (dispatchServerScriptPath) {
|
|
59710
|
-
const dispatchLogPath =
|
|
59543
|
+
const dispatchLogPath = import_node_path63.default.join(logDir, "dispatch-mcp-server.log");
|
|
59711
59544
|
const dispatchCfgPath = writeDispatchMcpConfig({
|
|
59712
59545
|
dataDir: config.dataDir,
|
|
59713
59546
|
serverScriptPath: dispatchServerScriptPath,
|
|
@@ -59725,12 +59558,12 @@ async function startDaemon(config) {
|
|
|
59725
59558
|
});
|
|
59726
59559
|
}
|
|
59727
59560
|
const ticketServerCandidates = [
|
|
59728
|
-
|
|
59729
|
-
|
|
59561
|
+
import_node_path63.default.join(here, "ticket", "mcp-server.cjs"),
|
|
59562
|
+
import_node_path63.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
|
|
59730
59563
|
];
|
|
59731
|
-
const ticketServerScriptPath = ticketServerCandidates.find((p2) =>
|
|
59564
|
+
const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
|
|
59732
59565
|
if (ticketServerScriptPath) {
|
|
59733
|
-
const ticketLogPath =
|
|
59566
|
+
const ticketLogPath = import_node_path63.default.join(logDir, "ticket-mcp-server.log");
|
|
59734
59567
|
const ticketCfgPath = writeTicketMcpConfig({
|
|
59735
59568
|
dataDir: config.dataDir,
|
|
59736
59569
|
serverScriptPath: ticketServerScriptPath,
|
|
@@ -59750,12 +59583,12 @@ async function startDaemon(config) {
|
|
|
59750
59583
|
});
|
|
59751
59584
|
}
|
|
59752
59585
|
const shiftServerCandidates = [
|
|
59753
|
-
|
|
59754
|
-
|
|
59586
|
+
import_node_path63.default.join(here, "shift", "mcp-server.cjs"),
|
|
59587
|
+
import_node_path63.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
|
|
59755
59588
|
];
|
|
59756
|
-
const shiftServerScriptPath = shiftServerCandidates.find((p2) =>
|
|
59589
|
+
const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
|
|
59757
59590
|
if (shiftServerScriptPath) {
|
|
59758
|
-
const shiftLogPath =
|
|
59591
|
+
const shiftLogPath = import_node_path63.default.join(logDir, "shift-mcp-server.log");
|
|
59759
59592
|
const shiftCfgPath = await writeShiftMcpConfig({
|
|
59760
59593
|
dataDir: config.dataDir,
|
|
59761
59594
|
serverScriptPath: shiftServerScriptPath,
|
|
@@ -59774,12 +59607,12 @@ async function startDaemon(config) {
|
|
|
59774
59607
|
);
|
|
59775
59608
|
}
|
|
59776
59609
|
const inboxServerCandidates = [
|
|
59777
|
-
|
|
59778
|
-
|
|
59610
|
+
import_node_path63.default.join(here, "inbox", "mcp-server.cjs"),
|
|
59611
|
+
import_node_path63.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
|
|
59779
59612
|
];
|
|
59780
|
-
const inboxServerScriptPath = inboxServerCandidates.find((p2) =>
|
|
59613
|
+
const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
|
|
59781
59614
|
if (inboxServerScriptPath) {
|
|
59782
|
-
const inboxLogPath =
|
|
59615
|
+
const inboxLogPath = import_node_path63.default.join(logDir, "inbox-mcp-server.log");
|
|
59783
59616
|
const inboxCfgPath = await writeInboxMcpConfig({
|
|
59784
59617
|
dataDir: config.dataDir,
|
|
59785
59618
|
serverScriptPath: inboxServerScriptPath,
|
|
@@ -59798,7 +59631,7 @@ async function startDaemon(config) {
|
|
|
59798
59631
|
);
|
|
59799
59632
|
}
|
|
59800
59633
|
const shiftStore = createShiftStore({
|
|
59801
|
-
filePath:
|
|
59634
|
+
filePath: import_node_path63.default.join(config.dataDir, "shift.json"),
|
|
59802
59635
|
ownerIdProvider: () => ownerPrincipalId,
|
|
59803
59636
|
now: () => Date.now()
|
|
59804
59637
|
});
|
|
@@ -59820,7 +59653,7 @@ async function startDaemon(config) {
|
|
|
59820
59653
|
getAdapter,
|
|
59821
59654
|
historyReader: history,
|
|
59822
59655
|
dataDir: config.dataDir,
|
|
59823
|
-
personaRoot:
|
|
59656
|
+
personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
|
|
59824
59657
|
usersRoot,
|
|
59825
59658
|
personaStore,
|
|
59826
59659
|
ownerDisplayName,
|
|
@@ -59858,10 +59691,10 @@ async function startDaemon(config) {
|
|
|
59858
59691
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
59859
59692
|
attachmentGroup: {
|
|
59860
59693
|
onFileEdit: (input) => {
|
|
59861
|
-
const absPath =
|
|
59694
|
+
const absPath = import_node_path63.default.isAbsolute(input.relPath) ? input.relPath : import_node_path63.default.join(input.cwd, input.relPath);
|
|
59862
59695
|
let size = 0;
|
|
59863
59696
|
try {
|
|
59864
|
-
size =
|
|
59697
|
+
size = import_node_fs51.default.statSync(absPath).size;
|
|
59865
59698
|
} catch (err) {
|
|
59866
59699
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
59867
59700
|
sessionId: input.sessionId,
|
|
@@ -60048,6 +59881,13 @@ async function startDaemon(config) {
|
|
|
60048
59881
|
/* @__PURE__ */ new Set([...config.previewPorts ?? [], ...appBuilderPortRange])
|
|
60049
59882
|
);
|
|
60050
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
|
+
}
|
|
60051
59891
|
const makeHandlers = () => buildMethodHandlers({
|
|
60052
59892
|
manager,
|
|
60053
59893
|
workspace,
|
|
@@ -60091,11 +59931,11 @@ async function startDaemon(config) {
|
|
|
60091
59931
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
60092
59932
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
60093
59933
|
// guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
|
|
60094
|
-
personaRoot:
|
|
59934
|
+
personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
|
|
60095
59935
|
usersRoot
|
|
60096
59936
|
},
|
|
60097
59937
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
60098
|
-
personaRoot:
|
|
59938
|
+
personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
|
|
60099
59939
|
// v2 多人 persona 隔离:handler 派生 guest user-dir 放行
|
|
60100
59940
|
usersRoot,
|
|
60101
59941
|
// capability:list / delete handler 依赖
|
|
@@ -60108,6 +59948,9 @@ async function startDaemon(config) {
|
|
|
60108
59948
|
ownerFeishuUnionId,
|
|
60109
59949
|
ownerId,
|
|
60110
59950
|
ownerProvider,
|
|
59951
|
+
// ssh identity 互换(spec 2026-07-15):whoami:ok 下发本机 identity pubkey。
|
|
59952
|
+
// 惰性读盘,不依赖 ensureSshIdentity 完成时序(未生成 → null → 帧省略字段)。
|
|
59953
|
+
sshIdentityPublicKey: () => readSshIdentityPublicKey(config.dataDir),
|
|
60111
59954
|
personaStore,
|
|
60112
59955
|
// capability handler 也用 (capability:issue 走 registry / capability:list)
|
|
60113
59956
|
capabilityRegistry,
|
|
@@ -60119,7 +59962,7 @@ async function startDaemon(config) {
|
|
|
60119
59962
|
contactStore,
|
|
60120
59963
|
// <dataDir>/sshd 绝对路径 —— contact-ssh handlers 用它拼 authorized_keys / keys/ 子路径
|
|
60121
59964
|
// Task 10 会加 SshdManager 起 sshd;handlers wire 提前挂 sshdDir 让 typecheck 过
|
|
60122
|
-
sshdDir:
|
|
59965
|
+
sshdDir: import_node_path63.default.join(config.dataDir, "sshd"),
|
|
60123
59966
|
contactSshLog: sshLog,
|
|
60124
59967
|
// inbox:sendDm 用:sessionId → session 出身(复用 attachment 同款 findOwnedSessionScope)
|
|
60125
59968
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
@@ -60187,7 +60030,11 @@ async function startDaemon(config) {
|
|
|
60187
60030
|
// 自动反向(spec 决策 #11):本机可达地址 = tunnel URL(公网),出站连接时
|
|
60188
60031
|
// 透传让对方反向加我为联系人。无 tunnel(仅本机 ws)→ null,对方不自动反向(不造假数据)。
|
|
60189
60032
|
// 决策 #16:身份不自报——本机 deviceId/ownerId/provider 由 exchange 签进 token 背书。
|
|
60190
|
-
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"))
|
|
60191
60038
|
},
|
|
60192
60039
|
// app-builder
|
|
60193
60040
|
appBuilderStore,
|
|
@@ -60210,11 +60057,11 @@ async function startDaemon(config) {
|
|
|
60210
60057
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2):
|
|
60211
60058
|
// appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
|
|
60212
60059
|
// dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
|
|
60213
|
-
appBuilderPersonaRoot:
|
|
60060
|
+
appBuilderPersonaRoot: import_node_path63.default.join(config.dataDir, "personas", "persona-app-builder"),
|
|
60214
60061
|
// 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
|
|
60215
|
-
deployKitRoot:
|
|
60062
|
+
deployKitRoot: import_node_path63.default.join(config.dataDir, "deploy-kit"),
|
|
60216
60063
|
// scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
|
|
60217
|
-
resolvePersonaRoot: (personaId) =>
|
|
60064
|
+
resolvePersonaRoot: (personaId) => import_node_path63.default.join(config.dataDir, "personas", personaId),
|
|
60218
60065
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
|
|
60219
60066
|
// 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
|
|
60220
60067
|
// 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
|
|
@@ -60257,7 +60104,7 @@ async function startDaemon(config) {
|
|
|
60257
60104
|
}
|
|
60258
60105
|
let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
|
|
60259
60106
|
if (sourceFile && sourceFile.toolSessionId) {
|
|
60260
|
-
sourceJsonlPath =
|
|
60107
|
+
sourceJsonlPath = import_node_path63.default.join(
|
|
60261
60108
|
import_node_os21.default.homedir(),
|
|
60262
60109
|
".claude",
|
|
60263
60110
|
"projects",
|
|
@@ -60617,8 +60464,8 @@ async function startDaemon(config) {
|
|
|
60617
60464
|
const lines = [
|
|
60618
60465
|
`Tunnel: ${r.url}`,
|
|
60619
60466
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
60620
|
-
`Frpc config: ${
|
|
60621
|
-
`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")}`
|
|
60622
60469
|
];
|
|
60623
60470
|
const width = Math.max(...lines.map((l) => l.length));
|
|
60624
60471
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -60631,8 +60478,8 @@ ${bar}
|
|
|
60631
60478
|
|
|
60632
60479
|
`);
|
|
60633
60480
|
try {
|
|
60634
|
-
const connectPath =
|
|
60635
|
-
|
|
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 });
|
|
60636
60483
|
} catch {
|
|
60637
60484
|
}
|
|
60638
60485
|
} catch (err) {
|
|
@@ -60668,21 +60515,13 @@ ${bar}
|
|
|
60668
60515
|
});
|
|
60669
60516
|
try {
|
|
60670
60517
|
await sshdMgr.start();
|
|
60671
|
-
rebuildAuthorizedKeys(contactStore,
|
|
60518
|
+
rebuildAuthorizedKeys(contactStore, import_node_path63.default.join(config.dataDir, "sshd"));
|
|
60672
60519
|
logger.info("sshd: contact-ssh sandbox ready", { port: config.sshdPort });
|
|
60673
60520
|
} catch (err) {
|
|
60674
60521
|
logger.warn("sshd start failed; contact SSH grant will not work until fixed", {
|
|
60675
60522
|
err: err.message
|
|
60676
60523
|
});
|
|
60677
60524
|
}
|
|
60678
|
-
const contactKeyPuller = new ContactKeyPuller({
|
|
60679
|
-
store: contactStore,
|
|
60680
|
-
dataDir: config.dataDir,
|
|
60681
|
-
logger,
|
|
60682
|
-
intervalMs: 6e4,
|
|
60683
|
-
sshLog
|
|
60684
|
-
});
|
|
60685
|
-
void contactKeyPuller.start();
|
|
60686
60525
|
void reportDevice();
|
|
60687
60526
|
void fetchServerKey();
|
|
60688
60527
|
const tickAttachmentGc = () => {
|
|
@@ -60717,7 +60556,6 @@ ${bar}
|
|
|
60717
60556
|
if (tunnelMgr) {
|
|
60718
60557
|
await tunnelMgr.stop();
|
|
60719
60558
|
}
|
|
60720
|
-
contactKeyPuller.stop();
|
|
60721
60559
|
await sshdMgr.stop().catch((err) => {
|
|
60722
60560
|
logger.warn("shutdown.sshd-stop-failed", {
|
|
60723
60561
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -60736,9 +60574,9 @@ ${bar}
|
|
|
60736
60574
|
};
|
|
60737
60575
|
}
|
|
60738
60576
|
function migrateDropPersonsDir(dataDir) {
|
|
60739
|
-
const dir =
|
|
60577
|
+
const dir = import_node_path63.default.join(dataDir, "persons");
|
|
60740
60578
|
try {
|
|
60741
|
-
|
|
60579
|
+
import_node_fs51.default.rmSync(dir, { recursive: true, force: true });
|
|
60742
60580
|
} catch {
|
|
60743
60581
|
}
|
|
60744
60582
|
}
|