@clawos-dev/clawd 0.2.235 → 0.2.237
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,11 @@ 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
|
+
/** 连接方自报本机 SSH identity pubkey;见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5330
|
+
selfSshPublicKey: external_exports.string().min(1).optional(),
|
|
5331
|
+
/** 连接方自报本机 OS 登录账号名;见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5332
|
+
selfOsUser: external_exports.string().min(1).optional()
|
|
5330
5333
|
});
|
|
5331
5334
|
AuthOkFrameSchema = external_exports.object({
|
|
5332
5335
|
type: external_exports.literal("auth:ok"),
|
|
@@ -5649,7 +5652,11 @@ var init_capability = __esm({
|
|
|
5649
5652
|
id: external_exports.string().min(1),
|
|
5650
5653
|
displayName: external_exports.string()
|
|
5651
5654
|
}).strict()
|
|
5652
|
-
)
|
|
5655
|
+
),
|
|
5656
|
+
/** 被连方 SSH identity pubkey;见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5657
|
+
sshPublicKey: external_exports.string().min(1).optional(),
|
|
5658
|
+
/** 被连方 OS 登录账号名;见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5659
|
+
osUser: external_exports.string().min(1).optional()
|
|
5653
5660
|
}).strict();
|
|
5654
5661
|
PERSONAL_CAP_GRANTS = Object.freeze([
|
|
5655
5662
|
Object.freeze({
|
|
@@ -5744,6 +5751,10 @@ var init_contact = __esm({
|
|
|
5744
5751
|
* 极端场景(区别对方多个 persona)留 per-persona override 演进空间。
|
|
5745
5752
|
*/
|
|
5746
5753
|
sshAllowed: external_exports.boolean().default(false),
|
|
5754
|
+
/** 对端设备 SSH identity pubkey(一行 `ssh-ed25519 AAAA... comment`);见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5755
|
+
sshPublicKey: external_exports.string().optional(),
|
|
5756
|
+
/** 对端 OS 登录账号名,SSH 拨号登录名(`<osUser>@127.0.0.1`);见 doc/glossary/peer-mesh.md#建联交换清单 */
|
|
5757
|
+
osUser: external_exports.string().optional(),
|
|
5747
5758
|
/**
|
|
5748
5759
|
* @deprecated T-29 去沙箱后废弃。原为授权访问的目录白名单(clawd-ssh-jail 生成 sbpl/bwrap
|
|
5749
5760
|
* policy 用);去掉 OS 沙箱后不再有目录级限制,SSH 授权即 on/off(见 sshAllowed)。
|
|
@@ -5790,7 +5801,7 @@ var init_contact = __esm({
|
|
|
5790
5801
|
});
|
|
5791
5802
|
|
|
5792
5803
|
// ../protocol/src/contact-ssh.ts
|
|
5793
|
-
var ContactSetSshAccessArgsSchema, ContactSetSshAccessOkSchema,
|
|
5804
|
+
var ContactSetSshAccessArgsSchema, ContactSetSshAccessOkSchema, ContactSshAccessUpdatedFrameSchema;
|
|
5794
5805
|
var init_contact_ssh = __esm({
|
|
5795
5806
|
"../protocol/src/contact-ssh.ts"() {
|
|
5796
5807
|
"use strict";
|
|
@@ -5804,14 +5815,6 @@ var init_contact_ssh = __esm({
|
|
|
5804
5815
|
deviceId: external_exports.string().min(1),
|
|
5805
5816
|
sshAllowed: external_exports.boolean()
|
|
5806
5817
|
}).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
5818
|
ContactSshAccessUpdatedFrameSchema = external_exports.object({
|
|
5816
5819
|
type: external_exports.literal("contact:ssh-access-updated"),
|
|
5817
5820
|
deviceId: external_exports.string().min(1),
|
|
@@ -6506,8 +6509,8 @@ var require_req = __commonJS({
|
|
|
6506
6509
|
if (req.originalUrl) {
|
|
6507
6510
|
_req.url = req.originalUrl;
|
|
6508
6511
|
} else {
|
|
6509
|
-
const
|
|
6510
|
-
_req.url = typeof
|
|
6512
|
+
const path76 = req.path;
|
|
6513
|
+
_req.url = typeof path76 === "string" ? path76 : req.url ? req.url.path || req.url : void 0;
|
|
6511
6514
|
}
|
|
6512
6515
|
if (req.query) {
|
|
6513
6516
|
_req.query = req.query;
|
|
@@ -6672,14 +6675,14 @@ var require_redact = __commonJS({
|
|
|
6672
6675
|
}
|
|
6673
6676
|
return obj;
|
|
6674
6677
|
}
|
|
6675
|
-
function parsePath(
|
|
6678
|
+
function parsePath(path76) {
|
|
6676
6679
|
const parts = [];
|
|
6677
6680
|
let current = "";
|
|
6678
6681
|
let inBrackets = false;
|
|
6679
6682
|
let inQuotes = false;
|
|
6680
6683
|
let quoteChar = "";
|
|
6681
|
-
for (let i = 0; i <
|
|
6682
|
-
const char =
|
|
6684
|
+
for (let i = 0; i < path76.length; i++) {
|
|
6685
|
+
const char = path76[i];
|
|
6683
6686
|
if (!inBrackets && char === ".") {
|
|
6684
6687
|
if (current) {
|
|
6685
6688
|
parts.push(current);
|
|
@@ -6810,10 +6813,10 @@ var require_redact = __commonJS({
|
|
|
6810
6813
|
return current;
|
|
6811
6814
|
}
|
|
6812
6815
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
6813
|
-
for (const
|
|
6814
|
-
const parts = parsePath(
|
|
6816
|
+
for (const path76 of paths) {
|
|
6817
|
+
const parts = parsePath(path76);
|
|
6815
6818
|
if (parts.includes("*")) {
|
|
6816
|
-
redactWildcardPath(obj, parts, censor,
|
|
6819
|
+
redactWildcardPath(obj, parts, censor, path76, remove);
|
|
6817
6820
|
} else {
|
|
6818
6821
|
if (remove) {
|
|
6819
6822
|
removeKey(obj, parts);
|
|
@@ -6898,8 +6901,8 @@ var require_redact = __commonJS({
|
|
|
6898
6901
|
}
|
|
6899
6902
|
} else {
|
|
6900
6903
|
if (afterWildcard.includes("*")) {
|
|
6901
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6902
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
6904
|
+
const wrappedCensor = typeof censor === "function" ? (value, path76) => {
|
|
6905
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path76];
|
|
6903
6906
|
return censor(value, fullPath);
|
|
6904
6907
|
} : censor;
|
|
6905
6908
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -6934,8 +6937,8 @@ var require_redact = __commonJS({
|
|
|
6934
6937
|
return null;
|
|
6935
6938
|
}
|
|
6936
6939
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
6937
|
-
for (const
|
|
6938
|
-
const parts = parsePath(
|
|
6940
|
+
for (const path76 of pathsToClone) {
|
|
6941
|
+
const parts = parsePath(path76);
|
|
6939
6942
|
let current = pathStructure;
|
|
6940
6943
|
for (let i = 0; i < parts.length; i++) {
|
|
6941
6944
|
const part = parts[i];
|
|
@@ -6987,24 +6990,24 @@ var require_redact = __commonJS({
|
|
|
6987
6990
|
}
|
|
6988
6991
|
return cloneSelectively(obj, pathStructure);
|
|
6989
6992
|
}
|
|
6990
|
-
function validatePath(
|
|
6991
|
-
if (typeof
|
|
6993
|
+
function validatePath(path76) {
|
|
6994
|
+
if (typeof path76 !== "string") {
|
|
6992
6995
|
throw new Error("Paths must be (non-empty) strings");
|
|
6993
6996
|
}
|
|
6994
|
-
if (
|
|
6997
|
+
if (path76 === "") {
|
|
6995
6998
|
throw new Error("Invalid redaction path ()");
|
|
6996
6999
|
}
|
|
6997
|
-
if (
|
|
6998
|
-
throw new Error(`Invalid redaction path (${
|
|
7000
|
+
if (path76.includes("..")) {
|
|
7001
|
+
throw new Error(`Invalid redaction path (${path76})`);
|
|
6999
7002
|
}
|
|
7000
|
-
if (
|
|
7001
|
-
throw new Error(`Invalid redaction path (${
|
|
7003
|
+
if (path76.includes(",")) {
|
|
7004
|
+
throw new Error(`Invalid redaction path (${path76})`);
|
|
7002
7005
|
}
|
|
7003
7006
|
let bracketCount = 0;
|
|
7004
7007
|
let inQuotes = false;
|
|
7005
7008
|
let quoteChar = "";
|
|
7006
|
-
for (let i = 0; i <
|
|
7007
|
-
const char =
|
|
7009
|
+
for (let i = 0; i < path76.length; i++) {
|
|
7010
|
+
const char = path76[i];
|
|
7008
7011
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
7009
7012
|
if (!inQuotes) {
|
|
7010
7013
|
inQuotes = true;
|
|
@@ -7018,20 +7021,20 @@ var require_redact = __commonJS({
|
|
|
7018
7021
|
} else if (char === "]" && !inQuotes) {
|
|
7019
7022
|
bracketCount--;
|
|
7020
7023
|
if (bracketCount < 0) {
|
|
7021
|
-
throw new Error(`Invalid redaction path (${
|
|
7024
|
+
throw new Error(`Invalid redaction path (${path76})`);
|
|
7022
7025
|
}
|
|
7023
7026
|
}
|
|
7024
7027
|
}
|
|
7025
7028
|
if (bracketCount !== 0) {
|
|
7026
|
-
throw new Error(`Invalid redaction path (${
|
|
7029
|
+
throw new Error(`Invalid redaction path (${path76})`);
|
|
7027
7030
|
}
|
|
7028
7031
|
}
|
|
7029
7032
|
function validatePaths(paths) {
|
|
7030
7033
|
if (!Array.isArray(paths)) {
|
|
7031
7034
|
throw new TypeError("paths must be an array");
|
|
7032
7035
|
}
|
|
7033
|
-
for (const
|
|
7034
|
-
validatePath(
|
|
7036
|
+
for (const path76 of paths) {
|
|
7037
|
+
validatePath(path76);
|
|
7035
7038
|
}
|
|
7036
7039
|
}
|
|
7037
7040
|
function slowRedact(options = {}) {
|
|
@@ -7199,8 +7202,8 @@ var require_redaction = __commonJS({
|
|
|
7199
7202
|
if (shape[k2] === null) {
|
|
7200
7203
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
7201
7204
|
} else {
|
|
7202
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
7203
|
-
return censor(value, [k2, ...
|
|
7205
|
+
const wrappedCensor = typeof censor === "function" ? (value, path76) => {
|
|
7206
|
+
return censor(value, [k2, ...path76]);
|
|
7204
7207
|
} : censor;
|
|
7205
7208
|
o[k2] = Redact({
|
|
7206
7209
|
paths: shape[k2],
|
|
@@ -7418,10 +7421,10 @@ var require_atomic_sleep = __commonJS({
|
|
|
7418
7421
|
var require_sonic_boom = __commonJS({
|
|
7419
7422
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
7420
7423
|
"use strict";
|
|
7421
|
-
var
|
|
7424
|
+
var fs70 = require("fs");
|
|
7422
7425
|
var EventEmitter3 = require("events");
|
|
7423
7426
|
var inherits = require("util").inherits;
|
|
7424
|
-
var
|
|
7427
|
+
var path76 = require("path");
|
|
7425
7428
|
var sleep2 = require_atomic_sleep();
|
|
7426
7429
|
var assert = require("assert");
|
|
7427
7430
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -7475,20 +7478,20 @@ var require_sonic_boom = __commonJS({
|
|
|
7475
7478
|
const mode = sonic.mode;
|
|
7476
7479
|
if (sonic.sync) {
|
|
7477
7480
|
try {
|
|
7478
|
-
if (sonic.mkdir)
|
|
7479
|
-
const fd =
|
|
7481
|
+
if (sonic.mkdir) fs70.mkdirSync(path76.dirname(file), { recursive: true });
|
|
7482
|
+
const fd = fs70.openSync(file, flags, mode);
|
|
7480
7483
|
fileOpened(null, fd);
|
|
7481
7484
|
} catch (err) {
|
|
7482
7485
|
fileOpened(err);
|
|
7483
7486
|
throw err;
|
|
7484
7487
|
}
|
|
7485
7488
|
} else if (sonic.mkdir) {
|
|
7486
|
-
|
|
7489
|
+
fs70.mkdir(path76.dirname(file), { recursive: true }, (err) => {
|
|
7487
7490
|
if (err) return fileOpened(err);
|
|
7488
|
-
|
|
7491
|
+
fs70.open(file, flags, mode, fileOpened);
|
|
7489
7492
|
});
|
|
7490
7493
|
} else {
|
|
7491
|
-
|
|
7494
|
+
fs70.open(file, flags, mode, fileOpened);
|
|
7492
7495
|
}
|
|
7493
7496
|
}
|
|
7494
7497
|
function SonicBoom(opts) {
|
|
@@ -7529,8 +7532,8 @@ var require_sonic_boom = __commonJS({
|
|
|
7529
7532
|
this.flush = flushBuffer;
|
|
7530
7533
|
this.flushSync = flushBufferSync;
|
|
7531
7534
|
this._actualWrite = actualWriteBuffer;
|
|
7532
|
-
fsWriteSync = () =>
|
|
7533
|
-
fsWrite = () =>
|
|
7535
|
+
fsWriteSync = () => fs70.writeSync(this.fd, this._writingBuf);
|
|
7536
|
+
fsWrite = () => fs70.write(this.fd, this._writingBuf, this.release);
|
|
7534
7537
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
7535
7538
|
this._writingBuf = "";
|
|
7536
7539
|
this.write = write;
|
|
@@ -7539,15 +7542,15 @@ var require_sonic_boom = __commonJS({
|
|
|
7539
7542
|
this._actualWrite = actualWrite;
|
|
7540
7543
|
fsWriteSync = () => {
|
|
7541
7544
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7542
|
-
return
|
|
7545
|
+
return fs70.writeSync(this.fd, this._writingBuf);
|
|
7543
7546
|
}
|
|
7544
|
-
return
|
|
7547
|
+
return fs70.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7545
7548
|
};
|
|
7546
7549
|
fsWrite = () => {
|
|
7547
7550
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7548
|
-
return
|
|
7551
|
+
return fs70.write(this.fd, this._writingBuf, this.release);
|
|
7549
7552
|
}
|
|
7550
|
-
return
|
|
7553
|
+
return fs70.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
7551
7554
|
};
|
|
7552
7555
|
} else {
|
|
7553
7556
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -7604,7 +7607,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7604
7607
|
}
|
|
7605
7608
|
}
|
|
7606
7609
|
if (this._fsync) {
|
|
7607
|
-
|
|
7610
|
+
fs70.fsyncSync(this.fd);
|
|
7608
7611
|
}
|
|
7609
7612
|
const len = this._len;
|
|
7610
7613
|
if (this._reopening) {
|
|
@@ -7718,7 +7721,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7718
7721
|
const onDrain = () => {
|
|
7719
7722
|
if (!this._fsync) {
|
|
7720
7723
|
try {
|
|
7721
|
-
|
|
7724
|
+
fs70.fsync(this.fd, (err) => {
|
|
7722
7725
|
this._flushPending = false;
|
|
7723
7726
|
cb(err);
|
|
7724
7727
|
});
|
|
@@ -7820,7 +7823,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7820
7823
|
const fd = this.fd;
|
|
7821
7824
|
this.once("ready", () => {
|
|
7822
7825
|
if (fd !== this.fd) {
|
|
7823
|
-
|
|
7826
|
+
fs70.close(fd, (err) => {
|
|
7824
7827
|
if (err) {
|
|
7825
7828
|
return this.emit("error", err);
|
|
7826
7829
|
}
|
|
@@ -7869,7 +7872,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7869
7872
|
buf = this._bufs[0];
|
|
7870
7873
|
}
|
|
7871
7874
|
try {
|
|
7872
|
-
const n = Buffer.isBuffer(buf) ?
|
|
7875
|
+
const n = Buffer.isBuffer(buf) ? fs70.writeSync(this.fd, buf) : fs70.writeSync(this.fd, buf, "utf8");
|
|
7873
7876
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
7874
7877
|
buf = releasedBufObj.writingBuf;
|
|
7875
7878
|
this._len = releasedBufObj.len;
|
|
@@ -7885,7 +7888,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7885
7888
|
}
|
|
7886
7889
|
}
|
|
7887
7890
|
try {
|
|
7888
|
-
|
|
7891
|
+
fs70.fsyncSync(this.fd);
|
|
7889
7892
|
} catch {
|
|
7890
7893
|
}
|
|
7891
7894
|
}
|
|
@@ -7906,7 +7909,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7906
7909
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
7907
7910
|
}
|
|
7908
7911
|
try {
|
|
7909
|
-
const n =
|
|
7912
|
+
const n = fs70.writeSync(this.fd, buf);
|
|
7910
7913
|
buf = buf.subarray(n);
|
|
7911
7914
|
this._len = Math.max(this._len - n, 0);
|
|
7912
7915
|
if (buf.length <= 0) {
|
|
@@ -7934,13 +7937,13 @@ var require_sonic_boom = __commonJS({
|
|
|
7934
7937
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
7935
7938
|
if (this.sync) {
|
|
7936
7939
|
try {
|
|
7937
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
7940
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs70.writeSync(this.fd, this._writingBuf) : fs70.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7938
7941
|
release(null, written);
|
|
7939
7942
|
} catch (err) {
|
|
7940
7943
|
release(err);
|
|
7941
7944
|
}
|
|
7942
7945
|
} else {
|
|
7943
|
-
|
|
7946
|
+
fs70.write(this.fd, this._writingBuf, release);
|
|
7944
7947
|
}
|
|
7945
7948
|
}
|
|
7946
7949
|
function actualWriteBuffer() {
|
|
@@ -7949,7 +7952,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7949
7952
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
7950
7953
|
if (this.sync) {
|
|
7951
7954
|
try {
|
|
7952
|
-
const written =
|
|
7955
|
+
const written = fs70.writeSync(this.fd, this._writingBuf);
|
|
7953
7956
|
release(null, written);
|
|
7954
7957
|
} catch (err) {
|
|
7955
7958
|
release(err);
|
|
@@ -7958,7 +7961,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7958
7961
|
if (kCopyBuffer) {
|
|
7959
7962
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
7960
7963
|
}
|
|
7961
|
-
|
|
7964
|
+
fs70.write(this.fd, this._writingBuf, release);
|
|
7962
7965
|
}
|
|
7963
7966
|
}
|
|
7964
7967
|
function actualClose(sonic) {
|
|
@@ -7974,12 +7977,12 @@ var require_sonic_boom = __commonJS({
|
|
|
7974
7977
|
sonic._lens = [];
|
|
7975
7978
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
7976
7979
|
try {
|
|
7977
|
-
|
|
7980
|
+
fs70.fsync(sonic.fd, closeWrapped);
|
|
7978
7981
|
} catch {
|
|
7979
7982
|
}
|
|
7980
7983
|
function closeWrapped() {
|
|
7981
7984
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
7982
|
-
|
|
7985
|
+
fs70.close(sonic.fd, done);
|
|
7983
7986
|
} else {
|
|
7984
7987
|
done();
|
|
7985
7988
|
}
|
|
@@ -11114,11 +11117,11 @@ var init_lib = __esm({
|
|
|
11114
11117
|
}
|
|
11115
11118
|
}
|
|
11116
11119
|
},
|
|
11117
|
-
addToPath: function addToPath(
|
|
11118
|
-
var last =
|
|
11120
|
+
addToPath: function addToPath(path76, added, removed, oldPosInc, options) {
|
|
11121
|
+
var last = path76.lastComponent;
|
|
11119
11122
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
11120
11123
|
return {
|
|
11121
|
-
oldPos:
|
|
11124
|
+
oldPos: path76.oldPos + oldPosInc,
|
|
11122
11125
|
lastComponent: {
|
|
11123
11126
|
count: last.count + 1,
|
|
11124
11127
|
added,
|
|
@@ -11128,7 +11131,7 @@ var init_lib = __esm({
|
|
|
11128
11131
|
};
|
|
11129
11132
|
} else {
|
|
11130
11133
|
return {
|
|
11131
|
-
oldPos:
|
|
11134
|
+
oldPos: path76.oldPos + oldPosInc,
|
|
11132
11135
|
lastComponent: {
|
|
11133
11136
|
count: 1,
|
|
11134
11137
|
added,
|
|
@@ -11633,10 +11636,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
11633
11636
|
const memories = raw.map((m2) => {
|
|
11634
11637
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11635
11638
|
const rec3 = m2;
|
|
11636
|
-
const
|
|
11639
|
+
const path76 = typeof rec3.path === "string" ? rec3.path : null;
|
|
11637
11640
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
11638
|
-
if (!
|
|
11639
|
-
const entry = { path:
|
|
11641
|
+
if (!path76 || content == null) return null;
|
|
11642
|
+
const entry = { path: path76, content };
|
|
11640
11643
|
if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
|
|
11641
11644
|
return entry;
|
|
11642
11645
|
}).filter((m2) => m2 !== null);
|
|
@@ -12439,10 +12442,10 @@ function parseAttachment(obj) {
|
|
|
12439
12442
|
const memories = raw.map((m2) => {
|
|
12440
12443
|
if (!m2 || typeof m2 !== "object") return null;
|
|
12441
12444
|
const rec3 = m2;
|
|
12442
|
-
const
|
|
12445
|
+
const path76 = typeof rec3.path === "string" ? rec3.path : null;
|
|
12443
12446
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
12444
|
-
if (!
|
|
12445
|
-
const out = { path:
|
|
12447
|
+
if (!path76 || content == null) return null;
|
|
12448
|
+
const out = { path: path76, content };
|
|
12446
12449
|
if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
|
|
12447
12450
|
return out;
|
|
12448
12451
|
}).filter((m2) => m2 !== null);
|
|
@@ -33428,8 +33431,8 @@ var require_utils = __commonJS({
|
|
|
33428
33431
|
var result = transform[inputType][outputType](input);
|
|
33429
33432
|
return result;
|
|
33430
33433
|
};
|
|
33431
|
-
exports2.resolve = function(
|
|
33432
|
-
var parts =
|
|
33434
|
+
exports2.resolve = function(path76) {
|
|
33435
|
+
var parts = path76.split("/");
|
|
33433
33436
|
var result = [];
|
|
33434
33437
|
for (var index = 0; index < parts.length; index++) {
|
|
33435
33438
|
var part = parts[index];
|
|
@@ -39282,18 +39285,18 @@ var require_object = __commonJS({
|
|
|
39282
39285
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
39283
39286
|
this.files[name] = object;
|
|
39284
39287
|
};
|
|
39285
|
-
var parentFolder = function(
|
|
39286
|
-
if (
|
|
39287
|
-
|
|
39288
|
+
var parentFolder = function(path76) {
|
|
39289
|
+
if (path76.slice(-1) === "/") {
|
|
39290
|
+
path76 = path76.substring(0, path76.length - 1);
|
|
39288
39291
|
}
|
|
39289
|
-
var lastSlash =
|
|
39290
|
-
return lastSlash > 0 ?
|
|
39292
|
+
var lastSlash = path76.lastIndexOf("/");
|
|
39293
|
+
return lastSlash > 0 ? path76.substring(0, lastSlash) : "";
|
|
39291
39294
|
};
|
|
39292
|
-
var forceTrailingSlash = function(
|
|
39293
|
-
if (
|
|
39294
|
-
|
|
39295
|
+
var forceTrailingSlash = function(path76) {
|
|
39296
|
+
if (path76.slice(-1) !== "/") {
|
|
39297
|
+
path76 += "/";
|
|
39295
39298
|
}
|
|
39296
|
-
return
|
|
39299
|
+
return path76;
|
|
39297
39300
|
};
|
|
39298
39301
|
var folderAdd = function(name, createFolders) {
|
|
39299
39302
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -40295,7 +40298,7 @@ var require_lib3 = __commonJS({
|
|
|
40295
40298
|
// src/run-case/recorder.ts
|
|
40296
40299
|
function startRunCaseRecorder(opts) {
|
|
40297
40300
|
const now = opts.now ?? Date.now;
|
|
40298
|
-
const dir =
|
|
40301
|
+
const dir = import_node_path64.default.dirname(opts.recordPath);
|
|
40299
40302
|
let stream = null;
|
|
40300
40303
|
let closing = false;
|
|
40301
40304
|
let closedSettled = false;
|
|
@@ -40309,8 +40312,8 @@ function startRunCaseRecorder(opts) {
|
|
|
40309
40312
|
});
|
|
40310
40313
|
const ensureStream = () => {
|
|
40311
40314
|
if (stream) return stream;
|
|
40312
|
-
|
|
40313
|
-
stream =
|
|
40315
|
+
import_node_fs52.default.mkdirSync(dir, { recursive: true });
|
|
40316
|
+
stream = import_node_fs52.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
40314
40317
|
stream.on("close", () => closedResolve());
|
|
40315
40318
|
return stream;
|
|
40316
40319
|
};
|
|
@@ -40335,12 +40338,12 @@ function startRunCaseRecorder(opts) {
|
|
|
40335
40338
|
};
|
|
40336
40339
|
return { tap, close, closed };
|
|
40337
40340
|
}
|
|
40338
|
-
var
|
|
40341
|
+
var import_node_fs52, import_node_path64;
|
|
40339
40342
|
var init_recorder = __esm({
|
|
40340
40343
|
"src/run-case/recorder.ts"() {
|
|
40341
40344
|
"use strict";
|
|
40342
|
-
|
|
40343
|
-
|
|
40345
|
+
import_node_fs52 = __toESM(require("fs"), 1);
|
|
40346
|
+
import_node_path64 = __toESM(require("path"), 1);
|
|
40344
40347
|
}
|
|
40345
40348
|
});
|
|
40346
40349
|
|
|
@@ -40383,7 +40386,7 @@ var init_wire = __esm({
|
|
|
40383
40386
|
// src/run-case/controller.ts
|
|
40384
40387
|
async function runController(opts) {
|
|
40385
40388
|
const now = opts.now ?? Date.now;
|
|
40386
|
-
const cwd = opts.cwd ?? (0,
|
|
40389
|
+
const cwd = opts.cwd ?? (0, import_node_fs53.mkdtempSync)(import_node_path65.default.join(import_node_os22.default.tmpdir(), "clawd-runcase-"));
|
|
40387
40390
|
const ownsCwd = opts.cwd === void 0;
|
|
40388
40391
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
40389
40392
|
const spawnCtx = { cwd };
|
|
@@ -40544,19 +40547,19 @@ async function runController(opts) {
|
|
|
40544
40547
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
40545
40548
|
if (ownsCwd) {
|
|
40546
40549
|
try {
|
|
40547
|
-
(0,
|
|
40550
|
+
(0, import_node_fs53.rmSync)(cwd, { recursive: true, force: true });
|
|
40548
40551
|
} catch {
|
|
40549
40552
|
}
|
|
40550
40553
|
}
|
|
40551
40554
|
return exitCode ?? 0;
|
|
40552
40555
|
}
|
|
40553
|
-
var
|
|
40556
|
+
var import_node_fs53, import_node_os22, import_node_path65;
|
|
40554
40557
|
var init_controller = __esm({
|
|
40555
40558
|
"src/run-case/controller.ts"() {
|
|
40556
40559
|
"use strict";
|
|
40557
|
-
|
|
40560
|
+
import_node_fs53 = require("fs");
|
|
40558
40561
|
import_node_os22 = __toESM(require("os"), 1);
|
|
40559
|
-
|
|
40562
|
+
import_node_path65 = __toESM(require("path"), 1);
|
|
40560
40563
|
init_claude();
|
|
40561
40564
|
init_stdout_splitter();
|
|
40562
40565
|
init_permission_stdio();
|
|
@@ -40760,8 +40763,12 @@ Options:
|
|
|
40760
40763
|
|
|
40761
40764
|
Example:
|
|
40762
40765
|
ssh -o ProxyCommand='~/.clawd/bin/clawd ssh-relay <peer-device-id>' \\
|
|
40763
|
-
-i ~/.clawd/
|
|
40764
|
-
|
|
40766
|
+
-i ~/.clawd/ssh-identity/id_ed25519 \\
|
|
40767
|
+
<peer-os-user>@127.0.0.1
|
|
40768
|
+
|
|
40769
|
+
\u767B\u5F55\u540D\u7528\u5BF9\u7AEF\u8D26\u53F7\uFF08Contact.osUser\uFF0C\u5EFA\u8054\u63E1\u624B\u4EA4\u6362\uFF09\u2014\u2014\u4E0D\u662F\u672C\u673A $USER\u3002
|
|
40770
|
+
\u4E24\u673A\u8D26\u53F7\u540D\u4E0D\u540C\u65F6\u7528 $USER \u4F1A Permission denied (publickey)\u3002
|
|
40771
|
+
\u89C1 doc/glossary/peer-mesh.md#\u5EFA\u8054\u4EA4\u6362\u6E05\u5355
|
|
40765
40772
|
`;
|
|
40766
40773
|
}
|
|
40767
40774
|
});
|
|
@@ -40950,8 +40957,8 @@ Env (advanced):
|
|
|
40950
40957
|
`;
|
|
40951
40958
|
|
|
40952
40959
|
// src/index.ts
|
|
40953
|
-
var
|
|
40954
|
-
var
|
|
40960
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
40961
|
+
var import_node_fs51 = __toESM(require("fs"), 1);
|
|
40955
40962
|
var import_node_os21 = __toESM(require("os"), 1);
|
|
40956
40963
|
|
|
40957
40964
|
// ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
|
|
@@ -41702,7 +41709,7 @@ function composeGuestSandbox(base, userWorkDir, spawnCwd) {
|
|
|
41702
41709
|
fsv.allowRead = unionArr(fsv.allowRead, [userWorkDir]);
|
|
41703
41710
|
fsv.allowWrite = unionArr(fsv.allowWrite, [userWorkDir]);
|
|
41704
41711
|
fsv.allowRead = unionArr(fsv.allowRead, [
|
|
41705
|
-
"~/.clawd/
|
|
41712
|
+
"~/.clawd/ssh-identity",
|
|
41706
41713
|
"~/.clawd/bin",
|
|
41707
41714
|
"~/Library/Application Support/@clawos/clawd-desktop/ota-bundles",
|
|
41708
41715
|
"~/.clawd/config.json",
|
|
@@ -41978,28 +41985,27 @@ deviceId \u76F4\u63A5\u53D6\u81EA token\uFF0C\u4E0D\u8981\u6539\u5199\u6216\u731
|
|
|
41978
41985
|
// src/sshd/contact-ssh-prompt.ts
|
|
41979
41986
|
var CONTACT_SSH_SYSTEM_PROMPT_HINT = `## \u8DE8\u8BBE\u5907\u6587\u4EF6\u8BBF\u95EE\uFF08Contact SSH\uFF09
|
|
41980
41987
|
|
|
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
|
-
\`\`\`
|
|
41988
|
+
\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
|
|
41989
|
+
|
|
41990
|
+
\u7528 SSH \u62E8\u53F7\uFF08\u628A \`<A>\` \u6362\u6210\u5BF9\u7AEF deviceId\uFF09\uFF1A
|
|
41991
|
+
\`\`\`bash
|
|
41992
|
+
ssh -o ProxyCommand='~/.clawd/bin/clawd ssh-relay <A>' \\
|
|
41993
|
+
-i ~/.clawd/ssh-identity/id_ed25519 \\
|
|
41994
|
+
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \\
|
|
41995
|
+
$USER@127.0.0.1 <command>
|
|
41996
|
+
# \u4F8B\u5982\uFF1A... $USER@127.0.0.1 cat /Users/xxx/some/file.md
|
|
41997
|
+
# ... $USER@127.0.0.1 ls -la /Users/xxx/proj
|
|
41998
|
+
# \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
|
|
41999
|
+
# \`~/.clawd/bin/clawd\` \u662F daemon \u542F\u52A8\u65F6 seed \u7684 shim\uFF0C\u786C\u7F16\u7801\u5F53\u524D daemon \u7684
|
|
42000
|
+
# 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
|
|
42001
|
+
\`\`\`
|
|
41996
42002
|
|
|
41997
42003
|
**\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
42004
|
|
|
41999
42005
|
**\u6CE8\u610F**\uFF1A
|
|
42000
42006
|
- \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
|
|
42007
|
+
- \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
42008
|
- \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
42009
|
`;
|
|
42004
42010
|
|
|
42005
42011
|
// src/session/reducer.ts
|
|
@@ -46754,8 +46760,8 @@ function turnStartInput(text) {
|
|
|
46754
46760
|
const items = [];
|
|
46755
46761
|
let leftover = text;
|
|
46756
46762
|
for (const m2 of text.matchAll(SKILL_RE)) {
|
|
46757
|
-
const [marker, name,
|
|
46758
|
-
items.push({ type: "skill", name, path:
|
|
46763
|
+
const [marker, name, path76] = m2;
|
|
46764
|
+
items.push({ type: "skill", name, path: path76 });
|
|
46759
46765
|
leftover = leftover.replace(marker, "");
|
|
46760
46766
|
}
|
|
46761
46767
|
for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
|
|
@@ -49108,43 +49114,6 @@ async function forwardInboxPostToPeer(args) {
|
|
|
49108
49114
|
}
|
|
49109
49115
|
return { ok: true };
|
|
49110
49116
|
}
|
|
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
49117
|
|
|
49149
49118
|
// src/tools/codex-history.ts
|
|
49150
49119
|
var import_node_child_process5 = require("child_process");
|
|
@@ -49332,13 +49301,13 @@ function mapSkillsListResponse(res) {
|
|
|
49332
49301
|
const r = s ?? {};
|
|
49333
49302
|
const name = str3(r.name);
|
|
49334
49303
|
if (!name) continue;
|
|
49335
|
-
const
|
|
49304
|
+
const path76 = str3(r.path);
|
|
49336
49305
|
const description = str3(r.description);
|
|
49337
49306
|
const isPlugin = name.includes(":");
|
|
49338
49307
|
out.push({
|
|
49339
49308
|
name,
|
|
49340
49309
|
source: isPlugin ? "plugin" : "project",
|
|
49341
|
-
...
|
|
49310
|
+
...path76 ? { path: path76 } : {},
|
|
49342
49311
|
...description ? { description } : {},
|
|
49343
49312
|
...isPlugin ? { plugin: name.split(":")[0] } : {}
|
|
49344
49313
|
});
|
|
@@ -50624,7 +50593,12 @@ var AuthGate = class {
|
|
|
50624
50593
|
st.pending = true;
|
|
50625
50594
|
let r;
|
|
50626
50595
|
try {
|
|
50627
|
-
r = await this.opts.authenticate(
|
|
50596
|
+
r = await this.opts.authenticate(
|
|
50597
|
+
parsed.data.token,
|
|
50598
|
+
parsed.data.selfUrl,
|
|
50599
|
+
parsed.data.selfSshPublicKey,
|
|
50600
|
+
parsed.data.selfOsUser
|
|
50601
|
+
);
|
|
50628
50602
|
} finally {
|
|
50629
50603
|
st.pending = false;
|
|
50630
50604
|
}
|
|
@@ -51269,7 +51243,11 @@ async function connectRemote(args) {
|
|
|
51269
51243
|
// 缺省时省略字段(不发空串),对方 schema selfUrl 是 .min(1).optional()。
|
|
51270
51244
|
// 决策 #16:身份字段(selfPrincipalId/selfDisplayName/selfDeviceId)已删——
|
|
51271
51245
|
// 对方从 connect token 签名背书取 deviceId/ownerId/provider,不信自报。
|
|
51272
|
-
...args.selfUrl ? { selfUrl: args.selfUrl } : {}
|
|
51246
|
+
...args.selfUrl ? { selfUrl: args.selfUrl } : {},
|
|
51247
|
+
// 建联交换:自报本机 SSH pubkey + OS 账号名;缺省省略字段(不发空串)。
|
|
51248
|
+
// 见 doc/glossary/peer-mesh.md#建联交换清单
|
|
51249
|
+
...args.selfSshPublicKey ? { selfSshPublicKey: args.selfSshPublicKey } : {},
|
|
51250
|
+
...args.selfOsUser ? { selfOsUser: args.selfOsUser } : {}
|
|
51273
51251
|
})
|
|
51274
51252
|
);
|
|
51275
51253
|
await new Promise((resolve6, reject) => {
|
|
@@ -51344,7 +51322,10 @@ async function connectRemote(args) {
|
|
|
51344
51322
|
ownerId: f.owner.ownerId,
|
|
51345
51323
|
provider: f.owner.provider,
|
|
51346
51324
|
displayName: f.owner.displayName,
|
|
51347
|
-
grants: f.capability.grants
|
|
51325
|
+
grants: f.capability.grants,
|
|
51326
|
+
// 建联交换:对方老版本不带 → 省略(不落 undefined 占位)。见 …#建联交换清单
|
|
51327
|
+
...f.sshPublicKey ? { sshPublicKey: f.sshPublicKey } : {},
|
|
51328
|
+
...f.osUser ? { osUser: f.osUser } : {}
|
|
51348
51329
|
};
|
|
51349
51330
|
},
|
|
51350
51331
|
close: () => {
|
|
@@ -51365,6 +51346,9 @@ async function autoReverseContact(args) {
|
|
|
51365
51346
|
resource: { ...g2.resource },
|
|
51366
51347
|
actions: [...g2.actions]
|
|
51367
51348
|
}));
|
|
51349
|
+
const existing = args.store.get(args.deviceId) ?? void 0;
|
|
51350
|
+
const sshPublicKey = args.sshPublicKey ?? existing?.sshPublicKey;
|
|
51351
|
+
const osUser = args.osUser ?? existing?.osUser;
|
|
51368
51352
|
const base = {
|
|
51369
51353
|
deviceId: args.deviceId,
|
|
51370
51354
|
ownerId: args.ownerId,
|
|
@@ -51374,12 +51358,18 @@ 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
|
+
// 建联交换:缺省不落 undefined 占位(JSON 持久化干净)。见 …#建联交换清单
|
|
51364
|
+
...sshPublicKey ? { sshPublicKey } : {},
|
|
51365
|
+
...osUser ? { osUser } : {},
|
|
51379
51366
|
exposedDirs: []
|
|
51380
51367
|
};
|
|
51381
51368
|
args.store.upsert(base);
|
|
51382
51369
|
args.broadcast({ type: "contact:added", contact: base });
|
|
51370
|
+
if (base.sshAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
|
|
51371
|
+
args.rebuildAuthorizedKeys?.();
|
|
51372
|
+
}
|
|
51383
51373
|
if (!args.exchangeToken) return;
|
|
51384
51374
|
let token;
|
|
51385
51375
|
try {
|
|
@@ -53862,10 +53852,10 @@ function rebuildAuthorizedKeys(store, sshdDir) {
|
|
|
53862
53852
|
if (!c.sshAllowed) continue;
|
|
53863
53853
|
const safe = /^[A-Za-z0-9_.-]+$/.test(c.deviceId);
|
|
53864
53854
|
if (!safe) continue;
|
|
53865
|
-
const pubkey =
|
|
53866
|
-
if (!pubkey) continue;
|
|
53855
|
+
const pubkey = c.sshPublicKey?.trim();
|
|
53856
|
+
if (!pubkey || /[\r\n"]/.test(pubkey)) continue;
|
|
53867
53857
|
const bin = jailBinPath();
|
|
53868
|
-
lines.push(`command="${bin} ${c.deviceId}",restrict ${pubkey
|
|
53858
|
+
lines.push(`command="${bin} ${c.deviceId}",restrict ${pubkey}`);
|
|
53869
53859
|
lines.push(`# contact:${c.deviceId}`);
|
|
53870
53860
|
}
|
|
53871
53861
|
const body = lines.join("\n") + "\n";
|
|
@@ -53873,25 +53863,59 @@ function rebuildAuthorizedKeys(store, sshdDir) {
|
|
|
53873
53863
|
import_node_fs40.default.writeFileSync(tmp, body, { mode: 384 });
|
|
53874
53864
|
import_node_fs40.default.renameSync(tmp, target);
|
|
53875
53865
|
}
|
|
53876
|
-
|
|
53877
|
-
|
|
53878
|
-
|
|
53866
|
+
|
|
53867
|
+
// src/sshd/ssh-identity.ts
|
|
53868
|
+
var import_node_fs41 = __toESM(require("fs"), 1);
|
|
53869
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
53870
|
+
var import_node_child_process12 = require("child_process");
|
|
53871
|
+
var IDENTITY_DIR = "ssh-identity";
|
|
53872
|
+
var KEY_FILE = "id_ed25519";
|
|
53873
|
+
function identityPaths(dataDir) {
|
|
53874
|
+
const privPath = import_node_path41.default.join(dataDir, IDENTITY_DIR, KEY_FILE);
|
|
53875
|
+
return { privPath, pubPath: `${privPath}.pub` };
|
|
53876
|
+
}
|
|
53877
|
+
async function ensureSshIdentity(dataDir, opts = {}) {
|
|
53878
|
+
const { privPath, pubPath } = identityPaths(dataDir);
|
|
53879
|
+
import_node_fs41.default.mkdirSync(import_node_path41.default.dirname(privPath), { recursive: true, mode: 448 });
|
|
53880
|
+
if (import_node_fs41.default.existsSync(privPath) && import_node_fs41.default.existsSync(pubPath)) {
|
|
53881
|
+
return { privateKeyPath: privPath, publicKeyLine: import_node_fs41.default.readFileSync(pubPath, "utf8").trim() };
|
|
53882
|
+
}
|
|
53883
|
+
const bin = opts.keygenBin ?? "/usr/bin/ssh-keygen";
|
|
53884
|
+
await new Promise((resolve6, reject) => {
|
|
53885
|
+
const p2 = (opts.spawnImpl ?? import_node_child_process12.spawn)(
|
|
53886
|
+
bin,
|
|
53887
|
+
["-t", "ed25519", "-f", privPath, "-N", "", "-q", "-C", "clawd-ssh-identity"],
|
|
53888
|
+
{ stdio: "ignore" }
|
|
53889
|
+
);
|
|
53890
|
+
p2.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`ssh-keygen exit ${code}`)));
|
|
53891
|
+
p2.on("error", reject);
|
|
53892
|
+
});
|
|
53879
53893
|
try {
|
|
53880
|
-
|
|
53894
|
+
import_node_fs41.default.chmodSync(privPath, 384);
|
|
53895
|
+
} catch {
|
|
53896
|
+
}
|
|
53897
|
+
try {
|
|
53898
|
+
import_node_fs41.default.chmodSync(pubPath, 420);
|
|
53899
|
+
} catch {
|
|
53900
|
+
}
|
|
53901
|
+
return { privateKeyPath: privPath, publicKeyLine: import_node_fs41.default.readFileSync(pubPath, "utf8").trim() };
|
|
53902
|
+
}
|
|
53903
|
+
function readSshIdentityPublicKey(dataDir) {
|
|
53904
|
+
try {
|
|
53905
|
+
return import_node_fs41.default.readFileSync(identityPaths(dataDir).pubPath, "utf8").trim();
|
|
53881
53906
|
} catch {
|
|
53882
53907
|
return null;
|
|
53883
53908
|
}
|
|
53884
53909
|
}
|
|
53885
53910
|
|
|
53886
|
-
// src/sshd/
|
|
53887
|
-
var
|
|
53888
|
-
var import_node_path42 = __toESM(require("path"), 1);
|
|
53911
|
+
// src/sshd/ssh-tunnel-relay.ts
|
|
53912
|
+
var import_node_net2 = __toESM(require("net"), 1);
|
|
53889
53913
|
|
|
53890
53914
|
// src/sshd/contact-ssh-log.ts
|
|
53891
|
-
var
|
|
53892
|
-
var
|
|
53915
|
+
var import_node_fs42 = __toESM(require("fs"), 1);
|
|
53916
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
53893
53917
|
function createContactSshLog(dataDir) {
|
|
53894
|
-
const file =
|
|
53918
|
+
const file = import_node_path42.default.join(dataDir, "log", "contact-ssh.log");
|
|
53895
53919
|
function append(level, tag, message, meta) {
|
|
53896
53920
|
const time = (/* @__PURE__ */ new Date()).toISOString();
|
|
53897
53921
|
let line = `[${time}] [${level}] [${tag}] ${message}`;
|
|
@@ -53904,8 +53928,8 @@ function createContactSshLog(dataDir) {
|
|
|
53904
53928
|
}
|
|
53905
53929
|
line += "\n";
|
|
53906
53930
|
try {
|
|
53907
|
-
|
|
53908
|
-
|
|
53931
|
+
import_node_fs42.default.mkdirSync(import_node_path42.default.dirname(file), { recursive: true });
|
|
53932
|
+
import_node_fs42.default.appendFileSync(file, line, { mode: 384 });
|
|
53909
53933
|
} catch {
|
|
53910
53934
|
}
|
|
53911
53935
|
}
|
|
@@ -53924,118 +53948,7 @@ var nullContactSshLog = {
|
|
|
53924
53948
|
}
|
|
53925
53949
|
};
|
|
53926
53950
|
|
|
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
53951
|
// src/sshd/ssh-tunnel-relay.ts
|
|
54038
|
-
var import_node_net2 = __toESM(require("net"), 1);
|
|
54039
53952
|
async function handleSshTunnelUpgrade(req, socket, head, deps) {
|
|
54040
53953
|
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
54041
53954
|
const clientAddr = (req.socket && "remoteAddress" in req.socket ? req.socket.remoteAddress : null) ?? "unknown";
|
|
@@ -54626,9 +54539,9 @@ var CentralClientError = class extends Error {
|
|
|
54626
54539
|
code;
|
|
54627
54540
|
cause;
|
|
54628
54541
|
};
|
|
54629
|
-
async function centralRequest(opts,
|
|
54542
|
+
async function centralRequest(opts, path76, init) {
|
|
54630
54543
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
54631
|
-
const url = `${opts.api.replace(/\/+$/, "")}${
|
|
54544
|
+
const url = `${opts.api.replace(/\/+$/, "")}${path76}`;
|
|
54632
54545
|
const ctrl = new AbortController();
|
|
54633
54546
|
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
54634
54547
|
let res;
|
|
@@ -55565,11 +55478,11 @@ init_protocol();
|
|
|
55565
55478
|
init_protocol();
|
|
55566
55479
|
|
|
55567
55480
|
// src/workspace/git.ts
|
|
55568
|
-
var
|
|
55481
|
+
var import_node_child_process13 = require("child_process");
|
|
55569
55482
|
var import_node_fs47 = __toESM(require("fs"), 1);
|
|
55570
55483
|
var import_node_path48 = __toESM(require("path"), 1);
|
|
55571
55484
|
var import_node_util = require("util");
|
|
55572
|
-
var pexec = (0, import_node_util.promisify)(
|
|
55485
|
+
var pexec = (0, import_node_util.promisify)(import_node_child_process13.execFile);
|
|
55573
55486
|
function normalizePath(p2) {
|
|
55574
55487
|
const resolved = import_node_path48.default.resolve(p2);
|
|
55575
55488
|
try {
|
|
@@ -55982,51 +55895,6 @@ function buildContactHandlers(deps) {
|
|
|
55982
55895
|
|
|
55983
55896
|
// src/handlers/contact-ssh.ts
|
|
55984
55897
|
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
55898
|
function ensureOwner2(ctx) {
|
|
56031
55899
|
if (!ctx || ctx.principal.kind !== "owner") {
|
|
56032
55900
|
throw new ClawdError(
|
|
@@ -56035,15 +55903,6 @@ function ensureOwner2(ctx) {
|
|
|
56035
55903
|
);
|
|
56036
55904
|
}
|
|
56037
55905
|
}
|
|
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
55906
|
function buildContactSshHandlers(deps) {
|
|
56048
55907
|
const sshLog = deps.sshLog ?? nullContactSshLog;
|
|
56049
55908
|
const setSshAccess = async (frame, _client, ctx) => {
|
|
@@ -56080,47 +55939,8 @@ function buildContactSshHandlers(deps) {
|
|
|
56080
55939
|
}
|
|
56081
55940
|
};
|
|
56082
55941
|
};
|
|
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
55942
|
return {
|
|
56122
|
-
"contact:setSshAccess": setSshAccess
|
|
56123
|
-
"contact:sshKey:issue": sshKeyIssue
|
|
55943
|
+
"contact:setSshAccess": setSshAccess
|
|
56124
55944
|
};
|
|
56125
55945
|
}
|
|
56126
55946
|
|
|
@@ -56172,12 +55992,16 @@ function buildWhoamiHandler(deps) {
|
|
|
56172
55992
|
grantedPersonas.push({ id: file.personaId, displayName: file.label });
|
|
56173
55993
|
}
|
|
56174
55994
|
}
|
|
55995
|
+
const sshPublicKey = deps.sshIdentityPublicKey();
|
|
55996
|
+
const osUser = deps.selfOsUser();
|
|
56175
55997
|
return {
|
|
56176
55998
|
response: {
|
|
56177
55999
|
type: "whoami:ok",
|
|
56178
56000
|
owner,
|
|
56179
56001
|
capability,
|
|
56180
|
-
grantedPersonas
|
|
56002
|
+
grantedPersonas,
|
|
56003
|
+
...sshPublicKey ? { sshPublicKey } : {},
|
|
56004
|
+
...osUser ? { osUser } : {}
|
|
56181
56005
|
}
|
|
56182
56006
|
};
|
|
56183
56007
|
};
|
|
@@ -56253,7 +56077,10 @@ function buildDeviceHandlers(deps) {
|
|
|
56253
56077
|
// 自动反向(spec 决策 #11):自报本机可达地址,让对方反向加我为联系人;
|
|
56254
56078
|
// 本机无 tunnel → null → connectRemote 省略 selfUrl 字段(不造假数据)。
|
|
56255
56079
|
// 身份不自报:对方从 token 签名背书取我的 deviceId/ownerId/provider(决策 #16)。
|
|
56256
|
-
selfUrl: deps.selfUrl() ?? void 0
|
|
56080
|
+
selfUrl: deps.selfUrl() ?? void 0,
|
|
56081
|
+
// 建联交换:自报本机 SSH pubkey + OS 账号名;未生成 → 省略。见 …#建联交换清单
|
|
56082
|
+
selfSshPublicKey: deps.selfSshPublicKey() ?? void 0,
|
|
56083
|
+
selfOsUser: deps.selfOsUser() ?? void 0
|
|
56257
56084
|
});
|
|
56258
56085
|
} catch (e) {
|
|
56259
56086
|
throw new ClawdError(
|
|
@@ -56263,6 +56090,9 @@ function buildDeviceHandlers(deps) {
|
|
|
56263
56090
|
}
|
|
56264
56091
|
try {
|
|
56265
56092
|
const wh = await conn.whoami();
|
|
56093
|
+
const existing = deps.store.get(args.deviceId) ?? void 0;
|
|
56094
|
+
const sshPublicKey = wh.sshPublicKey ?? existing?.sshPublicKey;
|
|
56095
|
+
const osUser = wh.osUser ?? existing?.osUser;
|
|
56266
56096
|
const contact = {
|
|
56267
56097
|
deviceId: args.deviceId,
|
|
56268
56098
|
ownerId: wh.ownerId,
|
|
@@ -56277,12 +56107,19 @@ function buildDeviceHandlers(deps) {
|
|
|
56277
56107
|
connectToken: exchanged.token,
|
|
56278
56108
|
grants: wh.grants,
|
|
56279
56109
|
addedAt: now(),
|
|
56280
|
-
pinnedAt: null,
|
|
56281
|
-
sshAllowed: false,
|
|
56110
|
+
pinnedAt: existing?.pinnedAt ?? null,
|
|
56111
|
+
sshAllowed: existing?.sshAllowed ?? false,
|
|
56112
|
+
// 建联交换:对方 whoami:ok 下发的 pubkey / osUser 落库;对方老版本不带 →
|
|
56113
|
+
// 保留既有值 / 不落 undefined 占位。见 doc/glossary/peer-mesh.md#建联交换清单
|
|
56114
|
+
...sshPublicKey ? { sshPublicKey } : {},
|
|
56115
|
+
...osUser ? { osUser } : {},
|
|
56282
56116
|
exposedDirs: []
|
|
56283
56117
|
};
|
|
56284
56118
|
deps.store.upsert(contact);
|
|
56285
56119
|
deps.broadcast({ type: "contact:added", contact });
|
|
56120
|
+
if (contact.sshAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
|
|
56121
|
+
deps.rebuildAuthorizedKeys?.();
|
|
56122
|
+
}
|
|
56286
56123
|
return {
|
|
56287
56124
|
response: {
|
|
56288
56125
|
type: "device:connect:ok",
|
|
@@ -56435,7 +56272,7 @@ function buildPersonaHandlers(deps) {
|
|
|
56435
56272
|
}
|
|
56436
56273
|
|
|
56437
56274
|
// src/handlers/attachment.ts
|
|
56438
|
-
var
|
|
56275
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
56439
56276
|
init_protocol();
|
|
56440
56277
|
init_protocol();
|
|
56441
56278
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -56515,12 +56352,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
56515
56352
|
`session ${args.sessionId} scope unresolved`
|
|
56516
56353
|
);
|
|
56517
56354
|
}
|
|
56518
|
-
const cwdAbs =
|
|
56519
|
-
const candidateAbs =
|
|
56355
|
+
const cwdAbs = import_node_path49.default.resolve(sessionFile.cwd);
|
|
56356
|
+
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
56357
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
|
|
56521
56358
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
56522
56359
|
const entry = entries.find((e) => {
|
|
56523
|
-
const storedAbs =
|
|
56360
|
+
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
56361
|
return storedAbs === candidateAbs && !e.stale;
|
|
56525
56362
|
});
|
|
56526
56363
|
if (!entry) {
|
|
@@ -56545,7 +56382,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
56545
56382
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
56546
56383
|
const f = deps.sessionStore.read(sessionId);
|
|
56547
56384
|
if (!f) return;
|
|
56548
|
-
assertGuestAttachmentPath(ctx,
|
|
56385
|
+
assertGuestAttachmentPath(ctx, import_node_path49.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
|
|
56549
56386
|
}
|
|
56550
56387
|
const groupAdd = async (frame, _client, ctx) => {
|
|
56551
56388
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -56560,8 +56397,8 @@ function buildAttachmentHandlers(deps) {
|
|
|
56560
56397
|
if (!scope) {
|
|
56561
56398
|
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
|
|
56562
56399
|
}
|
|
56563
|
-
const cwdAbs =
|
|
56564
|
-
const candidateAbs =
|
|
56400
|
+
const cwdAbs = import_node_path49.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
|
|
56401
|
+
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
56402
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
|
|
56566
56403
|
const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
|
|
56567
56404
|
const size = 0;
|
|
@@ -56620,19 +56457,19 @@ function buildAttachmentHandlers(deps) {
|
|
|
56620
56457
|
|
|
56621
56458
|
// src/handlers/extension.ts
|
|
56622
56459
|
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
56623
|
-
var
|
|
56460
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
56624
56461
|
init_protocol();
|
|
56625
56462
|
|
|
56626
56463
|
// src/extension/bundle-zip.ts
|
|
56627
56464
|
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
56628
|
-
var
|
|
56465
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
56629
56466
|
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
56630
56467
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
56631
56468
|
async function bundleExtensionDir(dir) {
|
|
56632
56469
|
const entries = await listFilesSorted(dir);
|
|
56633
56470
|
const zip = new import_jszip2.default();
|
|
56634
56471
|
for (const rel of entries) {
|
|
56635
|
-
const abs =
|
|
56472
|
+
const abs = import_node_path50.default.join(dir, rel);
|
|
56636
56473
|
const content = await import_promises5.default.readFile(abs);
|
|
56637
56474
|
zip.file(rel, content, { date: FIXED_DATE });
|
|
56638
56475
|
}
|
|
@@ -56653,7 +56490,7 @@ async function listFilesSorted(rootDir) {
|
|
|
56653
56490
|
return out;
|
|
56654
56491
|
}
|
|
56655
56492
|
async function walk(absRoot, relPrefix, out) {
|
|
56656
|
-
const dirAbs =
|
|
56493
|
+
const dirAbs = import_node_path50.default.join(absRoot, relPrefix);
|
|
56657
56494
|
const entries = await import_promises5.default.readdir(dirAbs, { withFileTypes: true });
|
|
56658
56495
|
for (const e of entries) {
|
|
56659
56496
|
if (IGNORE_BASENAMES.has(e.name)) continue;
|
|
@@ -56707,25 +56544,25 @@ function computePublishCheck(args) {
|
|
|
56707
56544
|
|
|
56708
56545
|
// src/extension/install-flow.ts
|
|
56709
56546
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
56710
|
-
var
|
|
56547
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
56711
56548
|
var import_node_os19 = __toESM(require("os"), 1);
|
|
56712
56549
|
var import_node_crypto15 = __toESM(require("crypto"), 1);
|
|
56713
56550
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
56714
56551
|
|
|
56715
56552
|
// src/extension/paths.ts
|
|
56716
56553
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
56717
|
-
var
|
|
56554
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
56718
56555
|
function clawdHomeRoot(override) {
|
|
56719
|
-
return override ?? process.env.CLAWD_HOME ??
|
|
56556
|
+
return override ?? process.env.CLAWD_HOME ?? import_node_path51.default.join(import_node_os18.default.homedir(), ".clawd");
|
|
56720
56557
|
}
|
|
56721
56558
|
function extensionsRoot(override) {
|
|
56722
|
-
return
|
|
56559
|
+
return import_node_path51.default.join(clawdHomeRoot(override), "extensions");
|
|
56723
56560
|
}
|
|
56724
56561
|
function publishedChannelsFile(override) {
|
|
56725
|
-
return
|
|
56562
|
+
return import_node_path51.default.join(clawdHomeRoot(override), "extensions-published.json");
|
|
56726
56563
|
}
|
|
56727
56564
|
function bundleCacheRoot(override) {
|
|
56728
|
-
return
|
|
56565
|
+
return import_node_path51.default.join(clawdHomeRoot(override), "extension-bundles");
|
|
56729
56566
|
}
|
|
56730
56567
|
|
|
56731
56568
|
// src/extension/install-flow.ts
|
|
@@ -56752,7 +56589,7 @@ async function installFromChannel(args, deps) {
|
|
|
56752
56589
|
throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56753
56590
|
}
|
|
56754
56591
|
for (const name of Object.keys(zip.files)) {
|
|
56755
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
56592
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path52.default.isAbsolute(name)) {
|
|
56756
56593
|
throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56757
56594
|
}
|
|
56758
56595
|
}
|
|
@@ -56784,7 +56621,7 @@ async function installFromChannel(args, deps) {
|
|
|
56784
56621
|
);
|
|
56785
56622
|
}
|
|
56786
56623
|
const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
|
|
56787
|
-
const destDir =
|
|
56624
|
+
const destDir = import_node_path52.default.join(deps.extensionsRoot, localExtId);
|
|
56788
56625
|
let destExists = false;
|
|
56789
56626
|
try {
|
|
56790
56627
|
await import_promises6.default.access(destDir);
|
|
@@ -56798,16 +56635,16 @@ async function installFromChannel(args, deps) {
|
|
|
56798
56635
|
);
|
|
56799
56636
|
}
|
|
56800
56637
|
const stage = await import_promises6.default.mkdtemp(
|
|
56801
|
-
|
|
56638
|
+
import_node_path52.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
|
|
56802
56639
|
);
|
|
56803
56640
|
try {
|
|
56804
56641
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56805
|
-
const dest =
|
|
56642
|
+
const dest = import_node_path52.default.join(stage, name);
|
|
56806
56643
|
if (entry.dir) {
|
|
56807
56644
|
await import_promises6.default.mkdir(dest, { recursive: true });
|
|
56808
56645
|
continue;
|
|
56809
56646
|
}
|
|
56810
|
-
await import_promises6.default.mkdir(
|
|
56647
|
+
await import_promises6.default.mkdir(import_node_path52.default.dirname(dest), { recursive: true });
|
|
56811
56648
|
if (name === "manifest.json") {
|
|
56812
56649
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56813
56650
|
await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -56828,7 +56665,7 @@ async function installFromChannel(args, deps) {
|
|
|
56828
56665
|
|
|
56829
56666
|
// src/extension/update-flow.ts
|
|
56830
56667
|
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
56831
|
-
var
|
|
56668
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
56832
56669
|
var import_node_os20 = __toESM(require("os"), 1);
|
|
56833
56670
|
var import_node_crypto16 = __toESM(require("crypto"), 1);
|
|
56834
56671
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
@@ -56845,11 +56682,11 @@ async function updateFromChannel(args, deps) {
|
|
|
56845
56682
|
channelRef.extId,
|
|
56846
56683
|
channelRef.ownerPrincipalId
|
|
56847
56684
|
);
|
|
56848
|
-
const liveDir =
|
|
56685
|
+
const liveDir = import_node_path53.default.join(deps.extensionsRoot, localExtId);
|
|
56849
56686
|
const prevDir = `${liveDir}.prev`;
|
|
56850
56687
|
let existingVersion;
|
|
56851
56688
|
try {
|
|
56852
|
-
const raw = await import_promises7.default.readFile(
|
|
56689
|
+
const raw = await import_promises7.default.readFile(import_node_path53.default.join(liveDir, "manifest.json"), "utf8");
|
|
56853
56690
|
const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
|
|
56854
56691
|
if (!parsed2.success) {
|
|
56855
56692
|
throw new UpdateError(
|
|
@@ -56882,7 +56719,7 @@ async function updateFromChannel(args, deps) {
|
|
|
56882
56719
|
throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56883
56720
|
}
|
|
56884
56721
|
for (const name of Object.keys(zip.files)) {
|
|
56885
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
56722
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path53.default.isAbsolute(name)) {
|
|
56886
56723
|
throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56887
56724
|
}
|
|
56888
56725
|
}
|
|
@@ -56917,16 +56754,16 @@ async function updateFromChannel(args, deps) {
|
|
|
56917
56754
|
await import_promises7.default.rm(prevDir, { recursive: true, force: true });
|
|
56918
56755
|
await import_promises7.default.rename(liveDir, prevDir);
|
|
56919
56756
|
const stage = await import_promises7.default.mkdtemp(
|
|
56920
|
-
|
|
56757
|
+
import_node_path53.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
|
|
56921
56758
|
);
|
|
56922
56759
|
try {
|
|
56923
56760
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56924
|
-
const dest =
|
|
56761
|
+
const dest = import_node_path53.default.join(stage, name);
|
|
56925
56762
|
if (entry.dir) {
|
|
56926
56763
|
await import_promises7.default.mkdir(dest, { recursive: true });
|
|
56927
56764
|
continue;
|
|
56928
56765
|
}
|
|
56929
|
-
await import_promises7.default.mkdir(
|
|
56766
|
+
await import_promises7.default.mkdir(import_node_path53.default.dirname(dest), { recursive: true });
|
|
56930
56767
|
if (name === "manifest.json") {
|
|
56931
56768
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56932
56769
|
await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -57019,7 +56856,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
57019
56856
|
);
|
|
57020
56857
|
}
|
|
57021
56858
|
}
|
|
57022
|
-
const manifestPath =
|
|
56859
|
+
const manifestPath = import_node_path54.default.join(root, extId, "manifest.json");
|
|
57023
56860
|
const manifest = await readManifest(root, extId);
|
|
57024
56861
|
const next = { ...manifest, version: newVersion };
|
|
57025
56862
|
const tmp = `${manifestPath}.tmp`;
|
|
@@ -57027,7 +56864,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
57027
56864
|
await import_promises8.default.rename(tmp, manifestPath);
|
|
57028
56865
|
}
|
|
57029
56866
|
async function readManifest(root, extId) {
|
|
57030
|
-
const file =
|
|
56867
|
+
const file = import_node_path54.default.join(root, extId, "manifest.json");
|
|
57031
56868
|
let raw;
|
|
57032
56869
|
try {
|
|
57033
56870
|
raw = await import_promises8.default.readFile(file, "utf8");
|
|
@@ -57118,7 +56955,7 @@ function buildExtensionHandlers(deps) {
|
|
|
57118
56955
|
};
|
|
57119
56956
|
async function buildSnapshotMeta(extId) {
|
|
57120
56957
|
const manifest = await readManifest(deps.root, extId);
|
|
57121
|
-
const { sha256, buffer } = await bundleExtensionDir(
|
|
56958
|
+
const { sha256, buffer } = await bundleExtensionDir(import_node_path54.default.join(deps.root, extId));
|
|
57122
56959
|
return { manifest, contentHash: sha256, buffer };
|
|
57123
56960
|
}
|
|
57124
56961
|
const publish = async (frame, _client, ctx) => {
|
|
@@ -57299,9 +57136,9 @@ function buildExtensionHandlers(deps) {
|
|
|
57299
57136
|
}
|
|
57300
57137
|
|
|
57301
57138
|
// src/app-builder/project-store.ts
|
|
57302
|
-
var
|
|
57139
|
+
var import_node_fs48 = require("fs");
|
|
57303
57140
|
var import_node_child_process14 = require("child_process");
|
|
57304
|
-
var
|
|
57141
|
+
var import_node_path55 = require("path");
|
|
57305
57142
|
init_protocol();
|
|
57306
57143
|
var PROJECTS_DIR = "projects";
|
|
57307
57144
|
var META_FILE = ".clawd-project.json";
|
|
@@ -57315,19 +57152,19 @@ var ProjectStore = class {
|
|
|
57315
57152
|
root;
|
|
57316
57153
|
/** projects/<name>/.clawd-project.json 路径 */
|
|
57317
57154
|
metaPath(name) {
|
|
57318
|
-
return (0,
|
|
57155
|
+
return (0, import_node_path55.join)(this.projectsRoot(), name, META_FILE);
|
|
57319
57156
|
}
|
|
57320
57157
|
/** projects/<name>/ 目录路径(cwd 用) */
|
|
57321
57158
|
projectDir(name) {
|
|
57322
|
-
return (0,
|
|
57159
|
+
return (0, import_node_path55.join)(this.projectsRoot(), name);
|
|
57323
57160
|
}
|
|
57324
57161
|
projectsRoot() {
|
|
57325
|
-
return (0,
|
|
57162
|
+
return (0, import_node_path55.join)(this.root, PROJECTS_DIR);
|
|
57326
57163
|
}
|
|
57327
57164
|
async list() {
|
|
57328
57165
|
let entries;
|
|
57329
57166
|
try {
|
|
57330
|
-
entries = await
|
|
57167
|
+
entries = await import_node_fs48.promises.readdir(this.projectsRoot());
|
|
57331
57168
|
} catch (err) {
|
|
57332
57169
|
if (err.code === "ENOENT") return [];
|
|
57333
57170
|
throw err;
|
|
@@ -57335,7 +57172,7 @@ var ProjectStore = class {
|
|
|
57335
57172
|
const out = [];
|
|
57336
57173
|
for (const name of entries) {
|
|
57337
57174
|
try {
|
|
57338
|
-
const raw = await
|
|
57175
|
+
const raw = await import_node_fs48.promises.readFile(this.metaPath(name), "utf8");
|
|
57339
57176
|
const json = JSON.parse(raw);
|
|
57340
57177
|
let migrated = false;
|
|
57341
57178
|
if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
|
|
@@ -57346,7 +57183,7 @@ var ProjectStore = class {
|
|
|
57346
57183
|
if (parsed.success) {
|
|
57347
57184
|
out.push(parsed.data);
|
|
57348
57185
|
if (migrated) {
|
|
57349
|
-
void
|
|
57186
|
+
void import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
|
|
57350
57187
|
});
|
|
57351
57188
|
}
|
|
57352
57189
|
}
|
|
@@ -57390,8 +57227,8 @@ var ProjectStore = class {
|
|
|
57390
57227
|
throw new Error(`invalid name "${name}": ${validated.error.message}`);
|
|
57391
57228
|
}
|
|
57392
57229
|
const dir = this.projectDir(name);
|
|
57393
|
-
await
|
|
57394
|
-
await
|
|
57230
|
+
await import_node_fs48.promises.mkdir(dir, { recursive: true });
|
|
57231
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
|
|
57395
57232
|
return meta;
|
|
57396
57233
|
}
|
|
57397
57234
|
/**
|
|
@@ -57434,7 +57271,7 @@ var ProjectStore = class {
|
|
|
57434
57271
|
}
|
|
57435
57272
|
async delete(name) {
|
|
57436
57273
|
const dir = this.projectDir(name);
|
|
57437
|
-
await
|
|
57274
|
+
await import_node_fs48.promises.rm(dir, { recursive: true, force: true });
|
|
57438
57275
|
}
|
|
57439
57276
|
async updatePort(name, newPort) {
|
|
57440
57277
|
if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
|
|
@@ -57450,7 +57287,7 @@ var ProjectStore = class {
|
|
|
57450
57287
|
throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
|
|
57451
57288
|
}
|
|
57452
57289
|
const updated = { ...target, port: newPort };
|
|
57453
|
-
await
|
|
57290
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
57454
57291
|
return updated;
|
|
57455
57292
|
}
|
|
57456
57293
|
/**
|
|
@@ -57467,7 +57304,7 @@ var ProjectStore = class {
|
|
|
57467
57304
|
if (!validated.success) {
|
|
57468
57305
|
throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
|
|
57469
57306
|
}
|
|
57470
|
-
await
|
|
57307
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57471
57308
|
return validated.data;
|
|
57472
57309
|
}
|
|
57473
57310
|
/**
|
|
@@ -57488,7 +57325,7 @@ var ProjectStore = class {
|
|
|
57488
57325
|
if (!validated.success) {
|
|
57489
57326
|
throw new Error(`invalid publishJob: ${validated.error.message}`);
|
|
57490
57327
|
}
|
|
57491
|
-
await
|
|
57328
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57492
57329
|
return validated.data;
|
|
57493
57330
|
}
|
|
57494
57331
|
/** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
|
|
@@ -57503,7 +57340,7 @@ var ProjectStore = class {
|
|
|
57503
57340
|
if (!validated.success) {
|
|
57504
57341
|
throw new Error(`failed to clear publishJob: ${validated.error.message}`);
|
|
57505
57342
|
}
|
|
57506
|
-
await
|
|
57343
|
+
await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
57507
57344
|
return validated.data;
|
|
57508
57345
|
}
|
|
57509
57346
|
};
|
|
@@ -57624,8 +57461,8 @@ var PublishJobRegistry = class {
|
|
|
57624
57461
|
|
|
57625
57462
|
// src/app-builder/publish-job-runner.ts
|
|
57626
57463
|
var import_node_child_process16 = require("child_process");
|
|
57627
|
-
var
|
|
57628
|
-
var
|
|
57464
|
+
var import_node_fs49 = require("fs");
|
|
57465
|
+
var import_node_path56 = require("path");
|
|
57629
57466
|
|
|
57630
57467
|
// src/app-builder/publish-stage-parser.ts
|
|
57631
57468
|
var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
|
|
@@ -57657,10 +57494,10 @@ async function startPublishJob(deps, args) {
|
|
|
57657
57494
|
return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
|
|
57658
57495
|
}
|
|
57659
57496
|
const projDir = projectDir(args.name);
|
|
57660
|
-
const logPath = (0,
|
|
57497
|
+
const logPath = (0, import_node_path56.join)(projDir, ".publish.log");
|
|
57661
57498
|
let logStream = null;
|
|
57662
57499
|
try {
|
|
57663
|
-
logStream = (0,
|
|
57500
|
+
logStream = (0, import_node_fs49.createWriteStream)(logPath, { flags: "w" });
|
|
57664
57501
|
} catch {
|
|
57665
57502
|
logStream = null;
|
|
57666
57503
|
}
|
|
@@ -57917,8 +57754,8 @@ async function recoverInterruptedJobs(deps) {
|
|
|
57917
57754
|
|
|
57918
57755
|
// src/handlers/app-builder.ts
|
|
57919
57756
|
init_protocol();
|
|
57920
|
-
var
|
|
57921
|
-
var
|
|
57757
|
+
var import_node_path57 = require("path");
|
|
57758
|
+
var import_node_fs50 = require("fs");
|
|
57922
57759
|
var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
|
|
57923
57760
|
var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
|
|
57924
57761
|
async function recoverInterruptedPublishJobs(store, logger) {
|
|
@@ -57999,7 +57836,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
57999
57836
|
async function listAllUsersProjects() {
|
|
58000
57837
|
if (!deps.usersRoot || !deps.getStore) return [];
|
|
58001
57838
|
const getStore = deps.getStore;
|
|
58002
|
-
const userIds = await
|
|
57839
|
+
const userIds = await import_node_fs50.promises.readdir(deps.usersRoot).catch(() => []);
|
|
58003
57840
|
const perUser = await Promise.all(
|
|
58004
57841
|
userIds.map((uid) => getStore(uid).list().catch(() => []))
|
|
58005
57842
|
);
|
|
@@ -58075,8 +57912,8 @@ function buildAppBuilderHandlers(deps) {
|
|
|
58075
57912
|
const project = await userStore.create(f.name, reservedPorts);
|
|
58076
57913
|
try {
|
|
58077
57914
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
|
|
58078
|
-
const templateSrcDir = (0,
|
|
58079
|
-
const scaffoldScript = (0,
|
|
57915
|
+
const templateSrcDir = (0, import_node_path57.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
|
|
57916
|
+
const scaffoldScript = (0, import_node_path57.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
|
|
58080
57917
|
const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
|
|
58081
57918
|
deps.logger?.info("app-builder.scaffold.done", {
|
|
58082
57919
|
name: project.name,
|
|
@@ -58297,7 +58134,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
58297
58134
|
await userStore.clearPublishJob(args.name);
|
|
58298
58135
|
}
|
|
58299
58136
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
|
|
58300
|
-
const scriptPath = (0,
|
|
58137
|
+
const scriptPath = (0, import_node_path57.join)(deps.deployKitRoot, "scripts", "publish.sh");
|
|
58301
58138
|
deps.logger?.info("app-builder.publish.start", {
|
|
58302
58139
|
name: args.name,
|
|
58303
58140
|
sessionId: boundSession.sessionId,
|
|
@@ -58473,7 +58310,7 @@ function buildVisitorHandlers(deps) {
|
|
|
58473
58310
|
|
|
58474
58311
|
// src/extension/registry.ts
|
|
58475
58312
|
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
58476
|
-
var
|
|
58313
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
58477
58314
|
async function loadAll(root) {
|
|
58478
58315
|
let entries;
|
|
58479
58316
|
try {
|
|
@@ -58486,13 +58323,13 @@ async function loadAll(root) {
|
|
|
58486
58323
|
for (const ent of entries) {
|
|
58487
58324
|
if (!ent.isDirectory()) continue;
|
|
58488
58325
|
if (ent.name.startsWith(".")) continue;
|
|
58489
|
-
records.push(await loadOne(
|
|
58326
|
+
records.push(await loadOne(import_node_path58.default.join(root, ent.name), ent.name));
|
|
58490
58327
|
}
|
|
58491
58328
|
records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
|
|
58492
58329
|
return records;
|
|
58493
58330
|
}
|
|
58494
58331
|
async function loadOne(dir, dirName) {
|
|
58495
|
-
const manifestPath =
|
|
58332
|
+
const manifestPath = import_node_path58.default.join(dir, "manifest.json");
|
|
58496
58333
|
let raw;
|
|
58497
58334
|
try {
|
|
58498
58335
|
raw = await import_promises9.default.readFile(manifestPath, "utf8");
|
|
@@ -58537,7 +58374,7 @@ async function loadOne(dir, dirName) {
|
|
|
58537
58374
|
|
|
58538
58375
|
// src/extension/uninstall.ts
|
|
58539
58376
|
var import_promises10 = __toESM(require("fs/promises"), 1);
|
|
58540
|
-
var
|
|
58377
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
58541
58378
|
var UninstallError = class extends Error {
|
|
58542
58379
|
constructor(code, message) {
|
|
58543
58380
|
super(message);
|
|
@@ -58546,7 +58383,7 @@ var UninstallError = class extends Error {
|
|
|
58546
58383
|
code;
|
|
58547
58384
|
};
|
|
58548
58385
|
async function uninstall(deps) {
|
|
58549
|
-
const dir =
|
|
58386
|
+
const dir = import_node_path59.default.join(deps.root, deps.extId);
|
|
58550
58387
|
try {
|
|
58551
58388
|
await import_promises10.default.access(dir);
|
|
58552
58389
|
} catch {
|
|
@@ -58613,7 +58450,9 @@ function buildMethodHandlers(deps) {
|
|
|
58613
58450
|
ownerId: deps.ownerId,
|
|
58614
58451
|
ownerProvider: deps.ownerProvider,
|
|
58615
58452
|
personaStore: deps.personaStore,
|
|
58616
|
-
capabilityManager: deps.capabilityManager
|
|
58453
|
+
capabilityManager: deps.capabilityManager,
|
|
58454
|
+
sshIdentityPublicKey: deps.sshIdentityPublicKey,
|
|
58455
|
+
selfOsUser: deps.selfOsUser
|
|
58617
58456
|
}),
|
|
58618
58457
|
...buildFeishuAuthHandlers(deps.feishuAuth),
|
|
58619
58458
|
...buildDeviceHandlers(deps.feishuDevice),
|
|
@@ -58946,11 +58785,8 @@ var METHOD_GRANT_MAP = {
|
|
|
58946
58785
|
"contact:pin": ADMIN_ANY,
|
|
58947
58786
|
"contact:remove": ADMIN_ANY,
|
|
58948
58787
|
// 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
58788
|
// (对齐 inbox:postMessage 的"能连上=有 auth,业务在 handler 校"模式)
|
|
58952
58789
|
"contact:setSshAccess": ADMIN_ANY,
|
|
58953
|
-
"contact:sshKey:issue": { kind: "public" },
|
|
58954
58790
|
// ---- visitor:* (访客名单,owner-only) ----
|
|
58955
58791
|
// owner 看完整访客名单(含没开会话的);guest 不可调(handler 内再 assertOwner 兜底)。
|
|
58956
58792
|
"visitor:list": ADMIN_ANY,
|
|
@@ -59136,7 +58972,7 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
|
|
|
59136
58972
|
|
|
59137
58973
|
// src/extension/runtime.ts
|
|
59138
58974
|
var import_node_child_process18 = require("child_process");
|
|
59139
|
-
var
|
|
58975
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
59140
58976
|
var import_promises11 = require("timers/promises");
|
|
59141
58977
|
|
|
59142
58978
|
// src/extension/port-allocator.ts
|
|
@@ -59237,7 +59073,7 @@ var Runtime = class {
|
|
|
59237
59073
|
/\$CLAWOS_EXT_PORT/g,
|
|
59238
59074
|
String(port)
|
|
59239
59075
|
);
|
|
59240
|
-
const dir =
|
|
59076
|
+
const dir = import_node_path60.default.join(this.root, extId);
|
|
59241
59077
|
const env = {
|
|
59242
59078
|
...process.env,
|
|
59243
59079
|
CLAWOS_EXT_PORT: String(port),
|
|
@@ -59349,7 +59185,7 @@ ${handle.stderrTail}`
|
|
|
59349
59185
|
|
|
59350
59186
|
// src/extension/published-channels.ts
|
|
59351
59187
|
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
59352
|
-
var
|
|
59188
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
59353
59189
|
init_zod();
|
|
59354
59190
|
var PublishedChannelsError = class extends Error {
|
|
59355
59191
|
constructor(code, message) {
|
|
@@ -59448,7 +59284,7 @@ var PublishedChannelStore = class {
|
|
|
59448
59284
|
)
|
|
59449
59285
|
};
|
|
59450
59286
|
const tmp = `${this.filePath}.tmp`;
|
|
59451
|
-
await import_promises12.default.mkdir(
|
|
59287
|
+
await import_promises12.default.mkdir(import_node_path61.default.dirname(this.filePath), { recursive: true });
|
|
59452
59288
|
await import_promises12.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
59453
59289
|
await import_promises12.default.rename(tmp, this.filePath);
|
|
59454
59290
|
}
|
|
@@ -59456,7 +59292,7 @@ var PublishedChannelStore = class {
|
|
|
59456
59292
|
|
|
59457
59293
|
// src/extension/bundle-cache.ts
|
|
59458
59294
|
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
59459
|
-
var
|
|
59295
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
59460
59296
|
var BundleCache = class {
|
|
59461
59297
|
constructor(rootDir) {
|
|
59462
59298
|
this.rootDir = rootDir;
|
|
@@ -59465,14 +59301,14 @@ var BundleCache = class {
|
|
|
59465
59301
|
/** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
|
|
59466
59302
|
async write(snapshotHash, buffer) {
|
|
59467
59303
|
await import_promises13.default.mkdir(this.rootDir, { recursive: true });
|
|
59468
|
-
const file =
|
|
59304
|
+
const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59469
59305
|
const tmp = `${file}.tmp`;
|
|
59470
59306
|
await import_promises13.default.writeFile(tmp, buffer, { mode: 384 });
|
|
59471
59307
|
await import_promises13.default.rename(tmp, file);
|
|
59472
59308
|
}
|
|
59473
59309
|
/** Returns the bundle bytes, or null when the file doesn't exist. */
|
|
59474
59310
|
async read(snapshotHash) {
|
|
59475
|
-
const file =
|
|
59311
|
+
const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59476
59312
|
try {
|
|
59477
59313
|
return await import_promises13.default.readFile(file);
|
|
59478
59314
|
} catch (e) {
|
|
@@ -59482,13 +59318,20 @@ var BundleCache = class {
|
|
|
59482
59318
|
}
|
|
59483
59319
|
/** Idempotent — missing file is not an error. */
|
|
59484
59320
|
async delete(snapshotHash) {
|
|
59485
|
-
const file =
|
|
59321
|
+
const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59486
59322
|
await import_promises13.default.rm(file, { force: true });
|
|
59487
59323
|
}
|
|
59488
59324
|
};
|
|
59489
59325
|
|
|
59490
59326
|
// src/index.ts
|
|
59491
59327
|
var import_meta6 = {};
|
|
59328
|
+
function currentOsUser() {
|
|
59329
|
+
try {
|
|
59330
|
+
return import_node_os21.default.userInfo().username;
|
|
59331
|
+
} catch {
|
|
59332
|
+
return "";
|
|
59333
|
+
}
|
|
59334
|
+
}
|
|
59492
59335
|
async function startDaemon(config) {
|
|
59493
59336
|
const authFile = loadOrCreateAuthFile({ dataDir: config.dataDir });
|
|
59494
59337
|
const logShippingCfg = config.logShipping ?? { endpoint: null, sampling: {}, mode: "off" };
|
|
@@ -59505,24 +59348,24 @@ async function startDaemon(config) {
|
|
|
59505
59348
|
sampling: logShippingCfg.sampling,
|
|
59506
59349
|
homeDir: import_node_os21.default.homedir()
|
|
59507
59350
|
});
|
|
59508
|
-
const logDir =
|
|
59509
|
-
|
|
59351
|
+
const logDir = import_node_path63.default.join(config.dataDir, "log");
|
|
59352
|
+
import_node_fs51.default.mkdirSync(logDir, { recursive: true });
|
|
59510
59353
|
const logger = createLogger({
|
|
59511
59354
|
level: config.logLevel,
|
|
59512
|
-
file:
|
|
59355
|
+
file: import_node_path63.default.join(logDir, "clawd.log"),
|
|
59513
59356
|
logClient
|
|
59514
59357
|
});
|
|
59515
59358
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
59516
59359
|
const screenIdleProbeLogger = createFileOnlyLogger({
|
|
59517
|
-
file:
|
|
59360
|
+
file: import_node_path63.default.join(logDir, "screen-idle-probe.log"),
|
|
59518
59361
|
level: "debug"
|
|
59519
59362
|
});
|
|
59520
59363
|
logger.info("screen-idle probe logger enabled", {
|
|
59521
|
-
file:
|
|
59364
|
+
file: import_node_path63.default.join(logDir, "screen-idle-probe.log")
|
|
59522
59365
|
});
|
|
59523
59366
|
const CAP_TARGETS = [
|
|
59524
|
-
|
|
59525
|
-
|
|
59367
|
+
import_node_path63.default.join(logDir, "clawd.log"),
|
|
59368
|
+
import_node_path63.default.join(logDir, "screen-idle-probe.log")
|
|
59526
59369
|
];
|
|
59527
59370
|
const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
|
|
59528
59371
|
const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
|
|
@@ -59618,7 +59461,7 @@ async function startDaemon(config) {
|
|
|
59618
59461
|
// Task 1.7:authenticate 注入路径替代 expectedToken 单 token 比对。
|
|
59619
59462
|
// owner 路径 constantTimeEqual 防侧信道;guest 路径走 capabilityRegistry.
|
|
59620
59463
|
expectedToken: resolvedAuthToken,
|
|
59621
|
-
authenticate: async (t, selfUrl) => {
|
|
59464
|
+
authenticate: async (t, selfUrl, selfSshPublicKey, selfOsUser) => {
|
|
59622
59465
|
const result = await authenticate(t, buildConnectAuthDeps());
|
|
59623
59466
|
if (result.ok && result.context.principal.kind === "guest") {
|
|
59624
59467
|
void autoReverseContact({
|
|
@@ -59629,6 +59472,11 @@ async function startDaemon(config) {
|
|
|
59629
59472
|
provider: result.context.provider ?? "",
|
|
59630
59473
|
displayName: result.context.principal.displayName ?? result.context.principal.id,
|
|
59631
59474
|
selfUrl,
|
|
59475
|
+
// 建联交换:guest auth 帧自报的 pubkey / osUser 落 contact;pubkey 变化且已授权时
|
|
59476
|
+
// 立即重建 authorized_keys(重新握手 = 迁移路径)。见 …#建联交换清单
|
|
59477
|
+
sshPublicKey: selfSshPublicKey,
|
|
59478
|
+
osUser: selfOsUser,
|
|
59479
|
+
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path63.default.join(config.dataDir, "sshd")),
|
|
59632
59480
|
// 决策 #16 换票补全:用 owner 当前 jwt 换 aud=对方设备 的票(本机主动连对方用)。
|
|
59633
59481
|
// 现读 ownerIdentityStore(热切换后用新身份);失败 → null → 保持空票。
|
|
59634
59482
|
exchangeToken: async (targetDeviceId) => {
|
|
@@ -59676,8 +59524,8 @@ async function startDaemon(config) {
|
|
|
59676
59524
|
const agents = new AgentsScanner();
|
|
59677
59525
|
const history = new ClaudeHistoryReader();
|
|
59678
59526
|
let transport = null;
|
|
59679
|
-
const personaStore = new PersonaStore(
|
|
59680
|
-
const usersRoot =
|
|
59527
|
+
const personaStore = new PersonaStore(import_node_path63.default.join(config.dataDir, "personas"));
|
|
59528
|
+
const usersRoot = import_node_path63.default.join(config.dataDir, "users");
|
|
59681
59529
|
const defaultsRoot = findDefaultsRoot(logger);
|
|
59682
59530
|
if (defaultsRoot) {
|
|
59683
59531
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -59697,17 +59545,17 @@ async function startDaemon(config) {
|
|
|
59697
59545
|
migrateCodexSandbox({ store: personaStore, logger });
|
|
59698
59546
|
const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
|
|
59699
59547
|
const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
|
|
59700
|
-
const here = typeof __dirname === "string" ? __dirname :
|
|
59548
|
+
const here = typeof __dirname === "string" ? __dirname : import_node_path63.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
|
|
59701
59549
|
const mcpConfigs = [];
|
|
59702
59550
|
const dispatchServerCandidates = [
|
|
59703
|
-
|
|
59551
|
+
import_node_path63.default.join(here, "dispatch", "mcp-server.cjs"),
|
|
59704
59552
|
// 生产 dist/index → dist/dispatch/mcp-server.cjs
|
|
59705
|
-
|
|
59553
|
+
import_node_path63.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
|
|
59706
59554
|
// dev tsx src/index → ../dist/dispatch/mcp-server.cjs
|
|
59707
59555
|
];
|
|
59708
|
-
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) =>
|
|
59556
|
+
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
|
|
59709
59557
|
if (dispatchServerScriptPath) {
|
|
59710
|
-
const dispatchLogPath =
|
|
59558
|
+
const dispatchLogPath = import_node_path63.default.join(logDir, "dispatch-mcp-server.log");
|
|
59711
59559
|
const dispatchCfgPath = writeDispatchMcpConfig({
|
|
59712
59560
|
dataDir: config.dataDir,
|
|
59713
59561
|
serverScriptPath: dispatchServerScriptPath,
|
|
@@ -59725,12 +59573,12 @@ async function startDaemon(config) {
|
|
|
59725
59573
|
});
|
|
59726
59574
|
}
|
|
59727
59575
|
const ticketServerCandidates = [
|
|
59728
|
-
|
|
59729
|
-
|
|
59576
|
+
import_node_path63.default.join(here, "ticket", "mcp-server.cjs"),
|
|
59577
|
+
import_node_path63.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
|
|
59730
59578
|
];
|
|
59731
|
-
const ticketServerScriptPath = ticketServerCandidates.find((p2) =>
|
|
59579
|
+
const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
|
|
59732
59580
|
if (ticketServerScriptPath) {
|
|
59733
|
-
const ticketLogPath =
|
|
59581
|
+
const ticketLogPath = import_node_path63.default.join(logDir, "ticket-mcp-server.log");
|
|
59734
59582
|
const ticketCfgPath = writeTicketMcpConfig({
|
|
59735
59583
|
dataDir: config.dataDir,
|
|
59736
59584
|
serverScriptPath: ticketServerScriptPath,
|
|
@@ -59750,12 +59598,12 @@ async function startDaemon(config) {
|
|
|
59750
59598
|
});
|
|
59751
59599
|
}
|
|
59752
59600
|
const shiftServerCandidates = [
|
|
59753
|
-
|
|
59754
|
-
|
|
59601
|
+
import_node_path63.default.join(here, "shift", "mcp-server.cjs"),
|
|
59602
|
+
import_node_path63.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
|
|
59755
59603
|
];
|
|
59756
|
-
const shiftServerScriptPath = shiftServerCandidates.find((p2) =>
|
|
59604
|
+
const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
|
|
59757
59605
|
if (shiftServerScriptPath) {
|
|
59758
|
-
const shiftLogPath =
|
|
59606
|
+
const shiftLogPath = import_node_path63.default.join(logDir, "shift-mcp-server.log");
|
|
59759
59607
|
const shiftCfgPath = await writeShiftMcpConfig({
|
|
59760
59608
|
dataDir: config.dataDir,
|
|
59761
59609
|
serverScriptPath: shiftServerScriptPath,
|
|
@@ -59774,12 +59622,12 @@ async function startDaemon(config) {
|
|
|
59774
59622
|
);
|
|
59775
59623
|
}
|
|
59776
59624
|
const inboxServerCandidates = [
|
|
59777
|
-
|
|
59778
|
-
|
|
59625
|
+
import_node_path63.default.join(here, "inbox", "mcp-server.cjs"),
|
|
59626
|
+
import_node_path63.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
|
|
59779
59627
|
];
|
|
59780
|
-
const inboxServerScriptPath = inboxServerCandidates.find((p2) =>
|
|
59628
|
+
const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
|
|
59781
59629
|
if (inboxServerScriptPath) {
|
|
59782
|
-
const inboxLogPath =
|
|
59630
|
+
const inboxLogPath = import_node_path63.default.join(logDir, "inbox-mcp-server.log");
|
|
59783
59631
|
const inboxCfgPath = await writeInboxMcpConfig({
|
|
59784
59632
|
dataDir: config.dataDir,
|
|
59785
59633
|
serverScriptPath: inboxServerScriptPath,
|
|
@@ -59798,7 +59646,7 @@ async function startDaemon(config) {
|
|
|
59798
59646
|
);
|
|
59799
59647
|
}
|
|
59800
59648
|
const shiftStore = createShiftStore({
|
|
59801
|
-
filePath:
|
|
59649
|
+
filePath: import_node_path63.default.join(config.dataDir, "shift.json"),
|
|
59802
59650
|
ownerIdProvider: () => ownerPrincipalId,
|
|
59803
59651
|
now: () => Date.now()
|
|
59804
59652
|
});
|
|
@@ -59820,7 +59668,7 @@ async function startDaemon(config) {
|
|
|
59820
59668
|
getAdapter,
|
|
59821
59669
|
historyReader: history,
|
|
59822
59670
|
dataDir: config.dataDir,
|
|
59823
|
-
personaRoot:
|
|
59671
|
+
personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
|
|
59824
59672
|
usersRoot,
|
|
59825
59673
|
personaStore,
|
|
59826
59674
|
ownerDisplayName,
|
|
@@ -59858,10 +59706,10 @@ async function startDaemon(config) {
|
|
|
59858
59706
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
59859
59707
|
attachmentGroup: {
|
|
59860
59708
|
onFileEdit: (input) => {
|
|
59861
|
-
const absPath =
|
|
59709
|
+
const absPath = import_node_path63.default.isAbsolute(input.relPath) ? input.relPath : import_node_path63.default.join(input.cwd, input.relPath);
|
|
59862
59710
|
let size = 0;
|
|
59863
59711
|
try {
|
|
59864
|
-
size =
|
|
59712
|
+
size = import_node_fs51.default.statSync(absPath).size;
|
|
59865
59713
|
} catch (err) {
|
|
59866
59714
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
59867
59715
|
sessionId: input.sessionId,
|
|
@@ -60048,6 +59896,13 @@ async function startDaemon(config) {
|
|
|
60048
59896
|
/* @__PURE__ */ new Set([...config.previewPorts ?? [], ...appBuilderPortRange])
|
|
60049
59897
|
);
|
|
60050
59898
|
const sshLog = createContactSshLog(config.dataDir);
|
|
59899
|
+
try {
|
|
59900
|
+
await ensureSshIdentity(config.dataDir);
|
|
59901
|
+
} catch (e) {
|
|
59902
|
+
logger.warn("ssh-identity generate failed; contact SSH pubkey exchange disabled", {
|
|
59903
|
+
err: e.message
|
|
59904
|
+
});
|
|
59905
|
+
}
|
|
60051
59906
|
const makeHandlers = () => buildMethodHandlers({
|
|
60052
59907
|
manager,
|
|
60053
59908
|
workspace,
|
|
@@ -60091,11 +59946,11 @@ async function startDaemon(config) {
|
|
|
60091
59946
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
60092
59947
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
60093
59948
|
// guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
|
|
60094
|
-
personaRoot:
|
|
59949
|
+
personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
|
|
60095
59950
|
usersRoot
|
|
60096
59951
|
},
|
|
60097
59952
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
60098
|
-
personaRoot:
|
|
59953
|
+
personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
|
|
60099
59954
|
// v2 多人 persona 隔离:handler 派生 guest user-dir 放行
|
|
60100
59955
|
usersRoot,
|
|
60101
59956
|
// capability:list / delete handler 依赖
|
|
@@ -60108,6 +59963,11 @@ async function startDaemon(config) {
|
|
|
60108
59963
|
ownerFeishuUnionId,
|
|
60109
59964
|
ownerId,
|
|
60110
59965
|
ownerProvider,
|
|
59966
|
+
// ssh identity 互换(spec 2026-07-15):whoami:ok 下发本机 identity pubkey。
|
|
59967
|
+
// 惰性读盘,不依赖 ensureSshIdentity 完成时序(未生成 → null → 帧省略字段)。
|
|
59968
|
+
sshIdentityPublicKey: () => readSshIdentityPublicKey(config.dataDir),
|
|
59969
|
+
// 建联交换:whoami:ok 下发本机 OS 账号名(拨号登录名)。见 doc/glossary/peer-mesh.md#建联交换清单
|
|
59970
|
+
selfOsUser: currentOsUser,
|
|
60111
59971
|
personaStore,
|
|
60112
59972
|
// capability handler 也用 (capability:issue 走 registry / capability:list)
|
|
60113
59973
|
capabilityRegistry,
|
|
@@ -60119,7 +59979,7 @@ async function startDaemon(config) {
|
|
|
60119
59979
|
contactStore,
|
|
60120
59980
|
// <dataDir>/sshd 绝对路径 —— contact-ssh handlers 用它拼 authorized_keys / keys/ 子路径
|
|
60121
59981
|
// Task 10 会加 SshdManager 起 sshd;handlers wire 提前挂 sshdDir 让 typecheck 过
|
|
60122
|
-
sshdDir:
|
|
59982
|
+
sshdDir: import_node_path63.default.join(config.dataDir, "sshd"),
|
|
60123
59983
|
contactSshLog: sshLog,
|
|
60124
59984
|
// inbox:sendDm 用:sessionId → session 出身(复用 attachment 同款 findOwnedSessionScope)
|
|
60125
59985
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
@@ -60187,7 +60047,13 @@ async function startDaemon(config) {
|
|
|
60187
60047
|
// 自动反向(spec 决策 #11):本机可达地址 = tunnel URL(公网),出站连接时
|
|
60188
60048
|
// 透传让对方反向加我为联系人。无 tunnel(仅本机 ws)→ null,对方不自动反向(不造假数据)。
|
|
60189
60049
|
// 决策 #16:身份不自报——本机 deviceId/ownerId/provider 由 exchange 签进 token 背书。
|
|
60190
|
-
selfUrl: () => currentTunnelUrl
|
|
60050
|
+
selfUrl: () => currentTunnelUrl,
|
|
60051
|
+
// ssh identity 互换(spec 2026-07-15):出站 auth 帧自报本机 identity pubkey;
|
|
60052
|
+
// 对方 pubkey 变化且已授权时立即重建 authorized_keys(重新 connect = 迁移路径)
|
|
60053
|
+
selfSshPublicKey: () => readSshIdentityPublicKey(config.dataDir),
|
|
60054
|
+
// 建联交换:出站自报本机 OS 账号名。见 doc/glossary/peer-mesh.md#建联交换清单
|
|
60055
|
+
selfOsUser: currentOsUser,
|
|
60056
|
+
rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path63.default.join(config.dataDir, "sshd"))
|
|
60191
60057
|
},
|
|
60192
60058
|
// app-builder
|
|
60193
60059
|
appBuilderStore,
|
|
@@ -60210,11 +60076,11 @@ async function startDaemon(config) {
|
|
|
60210
60076
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2):
|
|
60211
60077
|
// appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
|
|
60212
60078
|
// dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
|
|
60213
|
-
appBuilderPersonaRoot:
|
|
60079
|
+
appBuilderPersonaRoot: import_node_path63.default.join(config.dataDir, "personas", "persona-app-builder"),
|
|
60214
60080
|
// 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
|
|
60215
|
-
deployKitRoot:
|
|
60081
|
+
deployKitRoot: import_node_path63.default.join(config.dataDir, "deploy-kit"),
|
|
60216
60082
|
// scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
|
|
60217
|
-
resolvePersonaRoot: (personaId) =>
|
|
60083
|
+
resolvePersonaRoot: (personaId) => import_node_path63.default.join(config.dataDir, "personas", personaId),
|
|
60218
60084
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
|
|
60219
60085
|
// 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
|
|
60220
60086
|
// 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
|
|
@@ -60257,7 +60123,7 @@ async function startDaemon(config) {
|
|
|
60257
60123
|
}
|
|
60258
60124
|
let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
|
|
60259
60125
|
if (sourceFile && sourceFile.toolSessionId) {
|
|
60260
|
-
sourceJsonlPath =
|
|
60126
|
+
sourceJsonlPath = import_node_path63.default.join(
|
|
60261
60127
|
import_node_os21.default.homedir(),
|
|
60262
60128
|
".claude",
|
|
60263
60129
|
"projects",
|
|
@@ -60617,8 +60483,8 @@ async function startDaemon(config) {
|
|
|
60617
60483
|
const lines = [
|
|
60618
60484
|
`Tunnel: ${r.url}`,
|
|
60619
60485
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
60620
|
-
`Frpc config: ${
|
|
60621
|
-
`Frpc log: ${
|
|
60486
|
+
`Frpc config: ${import_node_path63.default.join(config.dataDir, "frpc.toml")}`,
|
|
60487
|
+
`Frpc log: ${import_node_path63.default.join(logDir, "frpc.log")}`
|
|
60622
60488
|
];
|
|
60623
60489
|
const width = Math.max(...lines.map((l) => l.length));
|
|
60624
60490
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -60631,8 +60497,8 @@ ${bar}
|
|
|
60631
60497
|
|
|
60632
60498
|
`);
|
|
60633
60499
|
try {
|
|
60634
|
-
const connectPath =
|
|
60635
|
-
|
|
60500
|
+
const connectPath = import_node_path63.default.join(config.dataDir, "connect.txt");
|
|
60501
|
+
import_node_fs51.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
60636
60502
|
} catch {
|
|
60637
60503
|
}
|
|
60638
60504
|
} catch (err) {
|
|
@@ -60668,21 +60534,13 @@ ${bar}
|
|
|
60668
60534
|
});
|
|
60669
60535
|
try {
|
|
60670
60536
|
await sshdMgr.start();
|
|
60671
|
-
rebuildAuthorizedKeys(contactStore,
|
|
60537
|
+
rebuildAuthorizedKeys(contactStore, import_node_path63.default.join(config.dataDir, "sshd"));
|
|
60672
60538
|
logger.info("sshd: contact-ssh sandbox ready", { port: config.sshdPort });
|
|
60673
60539
|
} catch (err) {
|
|
60674
60540
|
logger.warn("sshd start failed; contact SSH grant will not work until fixed", {
|
|
60675
60541
|
err: err.message
|
|
60676
60542
|
});
|
|
60677
60543
|
}
|
|
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
60544
|
void reportDevice();
|
|
60687
60545
|
void fetchServerKey();
|
|
60688
60546
|
const tickAttachmentGc = () => {
|
|
@@ -60717,7 +60575,6 @@ ${bar}
|
|
|
60717
60575
|
if (tunnelMgr) {
|
|
60718
60576
|
await tunnelMgr.stop();
|
|
60719
60577
|
}
|
|
60720
|
-
contactKeyPuller.stop();
|
|
60721
60578
|
await sshdMgr.stop().catch((err) => {
|
|
60722
60579
|
logger.warn("shutdown.sshd-stop-failed", {
|
|
60723
60580
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -60736,9 +60593,9 @@ ${bar}
|
|
|
60736
60593
|
};
|
|
60737
60594
|
}
|
|
60738
60595
|
function migrateDropPersonsDir(dataDir) {
|
|
60739
|
-
const dir =
|
|
60596
|
+
const dir = import_node_path63.default.join(dataDir, "persons");
|
|
60740
60597
|
try {
|
|
60741
|
-
|
|
60598
|
+
import_node_fs51.default.rmSync(dir, { recursive: true, force: true });
|
|
60742
60599
|
} catch {
|
|
60743
60600
|
}
|
|
60744
60601
|
}
|