@clawos-dev/clawd 0.2.199 → 0.2.200
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 +1023 -300
- package/dist/share-md-viewer-error.html +1 -1
- package/dist/share-md-viewer.html +1 -1
- package/dist/share-ui/assets/guest-B5ChN9T3.css +32 -0
- package/dist/share-ui/assets/{guest-BtMmEdS6.js → guest-M2MlB17Y.js} +23 -23
- package/dist/share-ui/guest.html +2 -2
- package/package.json +1 -1
- package/dist/share-ui/assets/guest-CCfI4zdC.css +0 -32
package/dist/cli.cjs
CHANGED
|
@@ -158,6 +158,11 @@ var init_methods = __esm({
|
|
|
158
158
|
"contact:list",
|
|
159
159
|
"contact:pin",
|
|
160
160
|
"contact:remove",
|
|
161
|
+
// ---- contact:setSshAccess / contact:sshKey:issue (SSH 反向访问,PR: contact-ssh-sandbox) ----
|
|
162
|
+
// owner UI 授权 contact 通过 daemon 专用 sshd 反向 SSH 进本机;guest daemon 用 sshKey:issue 拉 privkey.
|
|
163
|
+
// 详见 protocol/src/contact-ssh.ts.
|
|
164
|
+
"contact:setSshAccess",
|
|
165
|
+
"contact:sshKey:issue",
|
|
161
166
|
// ---- visitor:* (web 访客 · persona web 分享,spec 2026-06-24-persona-web-share-design) ----
|
|
162
167
|
// owner-only:列出登录访问过本机 public persona 的 web 访客(零安装、飞书登录、daemon 自签
|
|
163
168
|
// visitor token)。存储 ~/.clawd/visitors.json(VisitorStore),登录(exchange)即 upsert;
|
|
@@ -737,8 +742,8 @@ var init_parseUtil = __esm({
|
|
|
737
742
|
init_errors2();
|
|
738
743
|
init_en();
|
|
739
744
|
makeIssue = (params) => {
|
|
740
|
-
const { data, path:
|
|
741
|
-
const fullPath = [...
|
|
745
|
+
const { data, path: path73, errorMaps, issueData } = params;
|
|
746
|
+
const fullPath = [...path73, ...issueData.path || []];
|
|
742
747
|
const fullIssue = {
|
|
743
748
|
...issueData,
|
|
744
749
|
path: fullPath
|
|
@@ -1049,11 +1054,11 @@ var init_types = __esm({
|
|
|
1049
1054
|
init_parseUtil();
|
|
1050
1055
|
init_util();
|
|
1051
1056
|
ParseInputLazyPath = class {
|
|
1052
|
-
constructor(parent, value,
|
|
1057
|
+
constructor(parent, value, path73, key) {
|
|
1053
1058
|
this._cachedPath = [];
|
|
1054
1059
|
this.parent = parent;
|
|
1055
1060
|
this.data = value;
|
|
1056
|
-
this._path =
|
|
1061
|
+
this._path = path73;
|
|
1057
1062
|
this._key = key;
|
|
1058
1063
|
}
|
|
1059
1064
|
get path() {
|
|
@@ -5678,7 +5683,19 @@ var init_contact = __esm({
|
|
|
5678
5683
|
* 老 contacts.json 缺此字段 → zod default 补 null(无破坏性升级;不 orphan)。
|
|
5679
5684
|
* 对齐 SessionFile.pinnedAt 语义(daemon 侧持久化,contact:pin RPC 更新 → contact:pinned push)。
|
|
5680
5685
|
*/
|
|
5681
|
-
pinnedAt: external_exports.number().int().nullable().default(null)
|
|
5686
|
+
pinnedAt: external_exports.number().int().nullable().default(null),
|
|
5687
|
+
/**
|
|
5688
|
+
* SSH 反向访问授权(PR: contact-ssh-sandbox)。owner 视角:允许该 contact 通过 daemon
|
|
5689
|
+
* 专用 sshd 反向 SSH 进本机。default=false 保证老 contacts.json 兼容(zod default 补齐)。
|
|
5690
|
+
* 授权粒度 per-people (per-contact):借用别人 persona 本来就带信任对方本人的前提;
|
|
5691
|
+
* 极端场景(区别对方多个 persona)留 per-persona override 演进空间。
|
|
5692
|
+
*/
|
|
5693
|
+
sshAllowed: external_exports.boolean().default(false),
|
|
5694
|
+
/**
|
|
5695
|
+
* 授权访问的目录白名单(绝对路径)。空数组 + sshAllowed=true 意味 SSH 通道能拨到但无处可读,
|
|
5696
|
+
* jail 会拒绝 shell 启动。由 clawd-ssh-jail 运行时读,生成 sbpl / bwrap policy 白名单。
|
|
5697
|
+
*/
|
|
5698
|
+
exposedDirs: external_exports.array(external_exports.string()).default([])
|
|
5682
5699
|
}).strict();
|
|
5683
5700
|
ContactWireSchema = ContactSchema;
|
|
5684
5701
|
ContactRemoveArgsSchema = external_exports.object({
|
|
@@ -5717,6 +5734,40 @@ var init_contact = __esm({
|
|
|
5717
5734
|
}
|
|
5718
5735
|
});
|
|
5719
5736
|
|
|
5737
|
+
// ../protocol/src/contact-ssh.ts
|
|
5738
|
+
var ContactSetSshAccessArgsSchema, ContactSetSshAccessOkSchema, ContactSshKeyIssueArgsSchema, ContactSshKeyIssueOkSchema, ContactSshAccessUpdatedFrameSchema;
|
|
5739
|
+
var init_contact_ssh = __esm({
|
|
5740
|
+
"../protocol/src/contact-ssh.ts"() {
|
|
5741
|
+
"use strict";
|
|
5742
|
+
init_zod();
|
|
5743
|
+
ContactSetSshAccessArgsSchema = external_exports.object({
|
|
5744
|
+
deviceId: external_exports.string().min(1),
|
|
5745
|
+
sshAllowed: external_exports.boolean(),
|
|
5746
|
+
exposedDirs: external_exports.array(external_exports.string())
|
|
5747
|
+
}).strict();
|
|
5748
|
+
ContactSetSshAccessOkSchema = external_exports.object({
|
|
5749
|
+
type: external_exports.literal("contact:setSshAccess:ok"),
|
|
5750
|
+
deviceId: external_exports.string().min(1),
|
|
5751
|
+
sshAllowed: external_exports.boolean(),
|
|
5752
|
+
exposedDirs: external_exports.array(external_exports.string())
|
|
5753
|
+
}).strict();
|
|
5754
|
+
ContactSshKeyIssueArgsSchema = external_exports.object({
|
|
5755
|
+
deviceId: external_exports.string().min(1)
|
|
5756
|
+
}).strict();
|
|
5757
|
+
ContactSshKeyIssueOkSchema = external_exports.object({
|
|
5758
|
+
type: external_exports.literal("contact:sshKey:issue:ok"),
|
|
5759
|
+
privateKeyPem: external_exports.string().min(1),
|
|
5760
|
+
publicKeyLine: external_exports.string().min(1)
|
|
5761
|
+
}).strict();
|
|
5762
|
+
ContactSshAccessUpdatedFrameSchema = external_exports.object({
|
|
5763
|
+
type: external_exports.literal("contact:ssh-access-updated"),
|
|
5764
|
+
deviceId: external_exports.string().min(1),
|
|
5765
|
+
sshAllowed: external_exports.boolean(),
|
|
5766
|
+
exposedDirs: external_exports.array(external_exports.string())
|
|
5767
|
+
}).strict();
|
|
5768
|
+
}
|
|
5769
|
+
});
|
|
5770
|
+
|
|
5720
5771
|
// ../protocol/src/extension.ts
|
|
5721
5772
|
function isAllowedHostedUrl(raw) {
|
|
5722
5773
|
let u;
|
|
@@ -6118,6 +6169,7 @@ var init_runtime = __esm({
|
|
|
6118
6169
|
init_capability();
|
|
6119
6170
|
init_inbox();
|
|
6120
6171
|
init_contact();
|
|
6172
|
+
init_contact_ssh();
|
|
6121
6173
|
init_extension();
|
|
6122
6174
|
init_feishu_auth();
|
|
6123
6175
|
init_dispatch();
|
|
@@ -6397,8 +6449,8 @@ var require_req = __commonJS({
|
|
|
6397
6449
|
if (req.originalUrl) {
|
|
6398
6450
|
_req.url = req.originalUrl;
|
|
6399
6451
|
} else {
|
|
6400
|
-
const
|
|
6401
|
-
_req.url = typeof
|
|
6452
|
+
const path73 = req.path;
|
|
6453
|
+
_req.url = typeof path73 === "string" ? path73 : req.url ? req.url.path || req.url : void 0;
|
|
6402
6454
|
}
|
|
6403
6455
|
if (req.query) {
|
|
6404
6456
|
_req.query = req.query;
|
|
@@ -6563,14 +6615,14 @@ var require_redact = __commonJS({
|
|
|
6563
6615
|
}
|
|
6564
6616
|
return obj;
|
|
6565
6617
|
}
|
|
6566
|
-
function parsePath(
|
|
6618
|
+
function parsePath(path73) {
|
|
6567
6619
|
const parts = [];
|
|
6568
6620
|
let current = "";
|
|
6569
6621
|
let inBrackets = false;
|
|
6570
6622
|
let inQuotes = false;
|
|
6571
6623
|
let quoteChar = "";
|
|
6572
|
-
for (let i = 0; i <
|
|
6573
|
-
const char =
|
|
6624
|
+
for (let i = 0; i < path73.length; i++) {
|
|
6625
|
+
const char = path73[i];
|
|
6574
6626
|
if (!inBrackets && char === ".") {
|
|
6575
6627
|
if (current) {
|
|
6576
6628
|
parts.push(current);
|
|
@@ -6701,10 +6753,10 @@ var require_redact = __commonJS({
|
|
|
6701
6753
|
return current;
|
|
6702
6754
|
}
|
|
6703
6755
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
6704
|
-
for (const
|
|
6705
|
-
const parts = parsePath(
|
|
6756
|
+
for (const path73 of paths) {
|
|
6757
|
+
const parts = parsePath(path73);
|
|
6706
6758
|
if (parts.includes("*")) {
|
|
6707
|
-
redactWildcardPath(obj, parts, censor,
|
|
6759
|
+
redactWildcardPath(obj, parts, censor, path73, remove);
|
|
6708
6760
|
} else {
|
|
6709
6761
|
if (remove) {
|
|
6710
6762
|
removeKey(obj, parts);
|
|
@@ -6789,8 +6841,8 @@ var require_redact = __commonJS({
|
|
|
6789
6841
|
}
|
|
6790
6842
|
} else {
|
|
6791
6843
|
if (afterWildcard.includes("*")) {
|
|
6792
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6793
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
6844
|
+
const wrappedCensor = typeof censor === "function" ? (value, path73) => {
|
|
6845
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path73];
|
|
6794
6846
|
return censor(value, fullPath);
|
|
6795
6847
|
} : censor;
|
|
6796
6848
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -6825,8 +6877,8 @@ var require_redact = __commonJS({
|
|
|
6825
6877
|
return null;
|
|
6826
6878
|
}
|
|
6827
6879
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
6828
|
-
for (const
|
|
6829
|
-
const parts = parsePath(
|
|
6880
|
+
for (const path73 of pathsToClone) {
|
|
6881
|
+
const parts = parsePath(path73);
|
|
6830
6882
|
let current = pathStructure;
|
|
6831
6883
|
for (let i = 0; i < parts.length; i++) {
|
|
6832
6884
|
const part = parts[i];
|
|
@@ -6878,24 +6930,24 @@ var require_redact = __commonJS({
|
|
|
6878
6930
|
}
|
|
6879
6931
|
return cloneSelectively(obj, pathStructure);
|
|
6880
6932
|
}
|
|
6881
|
-
function validatePath(
|
|
6882
|
-
if (typeof
|
|
6933
|
+
function validatePath(path73) {
|
|
6934
|
+
if (typeof path73 !== "string") {
|
|
6883
6935
|
throw new Error("Paths must be (non-empty) strings");
|
|
6884
6936
|
}
|
|
6885
|
-
if (
|
|
6937
|
+
if (path73 === "") {
|
|
6886
6938
|
throw new Error("Invalid redaction path ()");
|
|
6887
6939
|
}
|
|
6888
|
-
if (
|
|
6889
|
-
throw new Error(`Invalid redaction path (${
|
|
6940
|
+
if (path73.includes("..")) {
|
|
6941
|
+
throw new Error(`Invalid redaction path (${path73})`);
|
|
6890
6942
|
}
|
|
6891
|
-
if (
|
|
6892
|
-
throw new Error(`Invalid redaction path (${
|
|
6943
|
+
if (path73.includes(",")) {
|
|
6944
|
+
throw new Error(`Invalid redaction path (${path73})`);
|
|
6893
6945
|
}
|
|
6894
6946
|
let bracketCount = 0;
|
|
6895
6947
|
let inQuotes = false;
|
|
6896
6948
|
let quoteChar = "";
|
|
6897
|
-
for (let i = 0; i <
|
|
6898
|
-
const char =
|
|
6949
|
+
for (let i = 0; i < path73.length; i++) {
|
|
6950
|
+
const char = path73[i];
|
|
6899
6951
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
6900
6952
|
if (!inQuotes) {
|
|
6901
6953
|
inQuotes = true;
|
|
@@ -6909,20 +6961,20 @@ var require_redact = __commonJS({
|
|
|
6909
6961
|
} else if (char === "]" && !inQuotes) {
|
|
6910
6962
|
bracketCount--;
|
|
6911
6963
|
if (bracketCount < 0) {
|
|
6912
|
-
throw new Error(`Invalid redaction path (${
|
|
6964
|
+
throw new Error(`Invalid redaction path (${path73})`);
|
|
6913
6965
|
}
|
|
6914
6966
|
}
|
|
6915
6967
|
}
|
|
6916
6968
|
if (bracketCount !== 0) {
|
|
6917
|
-
throw new Error(`Invalid redaction path (${
|
|
6969
|
+
throw new Error(`Invalid redaction path (${path73})`);
|
|
6918
6970
|
}
|
|
6919
6971
|
}
|
|
6920
6972
|
function validatePaths(paths) {
|
|
6921
6973
|
if (!Array.isArray(paths)) {
|
|
6922
6974
|
throw new TypeError("paths must be an array");
|
|
6923
6975
|
}
|
|
6924
|
-
for (const
|
|
6925
|
-
validatePath(
|
|
6976
|
+
for (const path73 of paths) {
|
|
6977
|
+
validatePath(path73);
|
|
6926
6978
|
}
|
|
6927
6979
|
}
|
|
6928
6980
|
function slowRedact(options = {}) {
|
|
@@ -7090,8 +7142,8 @@ var require_redaction = __commonJS({
|
|
|
7090
7142
|
if (shape[k2] === null) {
|
|
7091
7143
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
7092
7144
|
} else {
|
|
7093
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
7094
|
-
return censor(value, [k2, ...
|
|
7145
|
+
const wrappedCensor = typeof censor === "function" ? (value, path73) => {
|
|
7146
|
+
return censor(value, [k2, ...path73]);
|
|
7095
7147
|
} : censor;
|
|
7096
7148
|
o[k2] = Redact({
|
|
7097
7149
|
paths: shape[k2],
|
|
@@ -7309,10 +7361,10 @@ var require_atomic_sleep = __commonJS({
|
|
|
7309
7361
|
var require_sonic_boom = __commonJS({
|
|
7310
7362
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
7311
7363
|
"use strict";
|
|
7312
|
-
var
|
|
7364
|
+
var fs66 = require("fs");
|
|
7313
7365
|
var EventEmitter3 = require("events");
|
|
7314
7366
|
var inherits = require("util").inherits;
|
|
7315
|
-
var
|
|
7367
|
+
var path73 = require("path");
|
|
7316
7368
|
var sleep2 = require_atomic_sleep();
|
|
7317
7369
|
var assert = require("assert");
|
|
7318
7370
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -7366,20 +7418,20 @@ var require_sonic_boom = __commonJS({
|
|
|
7366
7418
|
const mode = sonic.mode;
|
|
7367
7419
|
if (sonic.sync) {
|
|
7368
7420
|
try {
|
|
7369
|
-
if (sonic.mkdir)
|
|
7370
|
-
const fd =
|
|
7421
|
+
if (sonic.mkdir) fs66.mkdirSync(path73.dirname(file), { recursive: true });
|
|
7422
|
+
const fd = fs66.openSync(file, flags, mode);
|
|
7371
7423
|
fileOpened(null, fd);
|
|
7372
7424
|
} catch (err) {
|
|
7373
7425
|
fileOpened(err);
|
|
7374
7426
|
throw err;
|
|
7375
7427
|
}
|
|
7376
7428
|
} else if (sonic.mkdir) {
|
|
7377
|
-
|
|
7429
|
+
fs66.mkdir(path73.dirname(file), { recursive: true }, (err) => {
|
|
7378
7430
|
if (err) return fileOpened(err);
|
|
7379
|
-
|
|
7431
|
+
fs66.open(file, flags, mode, fileOpened);
|
|
7380
7432
|
});
|
|
7381
7433
|
} else {
|
|
7382
|
-
|
|
7434
|
+
fs66.open(file, flags, mode, fileOpened);
|
|
7383
7435
|
}
|
|
7384
7436
|
}
|
|
7385
7437
|
function SonicBoom(opts) {
|
|
@@ -7420,8 +7472,8 @@ var require_sonic_boom = __commonJS({
|
|
|
7420
7472
|
this.flush = flushBuffer;
|
|
7421
7473
|
this.flushSync = flushBufferSync;
|
|
7422
7474
|
this._actualWrite = actualWriteBuffer;
|
|
7423
|
-
fsWriteSync = () =>
|
|
7424
|
-
fsWrite = () =>
|
|
7475
|
+
fsWriteSync = () => fs66.writeSync(this.fd, this._writingBuf);
|
|
7476
|
+
fsWrite = () => fs66.write(this.fd, this._writingBuf, this.release);
|
|
7425
7477
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
7426
7478
|
this._writingBuf = "";
|
|
7427
7479
|
this.write = write;
|
|
@@ -7430,15 +7482,15 @@ var require_sonic_boom = __commonJS({
|
|
|
7430
7482
|
this._actualWrite = actualWrite;
|
|
7431
7483
|
fsWriteSync = () => {
|
|
7432
7484
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7433
|
-
return
|
|
7485
|
+
return fs66.writeSync(this.fd, this._writingBuf);
|
|
7434
7486
|
}
|
|
7435
|
-
return
|
|
7487
|
+
return fs66.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7436
7488
|
};
|
|
7437
7489
|
fsWrite = () => {
|
|
7438
7490
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7439
|
-
return
|
|
7491
|
+
return fs66.write(this.fd, this._writingBuf, this.release);
|
|
7440
7492
|
}
|
|
7441
|
-
return
|
|
7493
|
+
return fs66.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
7442
7494
|
};
|
|
7443
7495
|
} else {
|
|
7444
7496
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -7495,7 +7547,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7495
7547
|
}
|
|
7496
7548
|
}
|
|
7497
7549
|
if (this._fsync) {
|
|
7498
|
-
|
|
7550
|
+
fs66.fsyncSync(this.fd);
|
|
7499
7551
|
}
|
|
7500
7552
|
const len = this._len;
|
|
7501
7553
|
if (this._reopening) {
|
|
@@ -7609,7 +7661,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7609
7661
|
const onDrain = () => {
|
|
7610
7662
|
if (!this._fsync) {
|
|
7611
7663
|
try {
|
|
7612
|
-
|
|
7664
|
+
fs66.fsync(this.fd, (err) => {
|
|
7613
7665
|
this._flushPending = false;
|
|
7614
7666
|
cb(err);
|
|
7615
7667
|
});
|
|
@@ -7711,7 +7763,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7711
7763
|
const fd = this.fd;
|
|
7712
7764
|
this.once("ready", () => {
|
|
7713
7765
|
if (fd !== this.fd) {
|
|
7714
|
-
|
|
7766
|
+
fs66.close(fd, (err) => {
|
|
7715
7767
|
if (err) {
|
|
7716
7768
|
return this.emit("error", err);
|
|
7717
7769
|
}
|
|
@@ -7760,7 +7812,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7760
7812
|
buf = this._bufs[0];
|
|
7761
7813
|
}
|
|
7762
7814
|
try {
|
|
7763
|
-
const n = Buffer.isBuffer(buf) ?
|
|
7815
|
+
const n = Buffer.isBuffer(buf) ? fs66.writeSync(this.fd, buf) : fs66.writeSync(this.fd, buf, "utf8");
|
|
7764
7816
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
7765
7817
|
buf = releasedBufObj.writingBuf;
|
|
7766
7818
|
this._len = releasedBufObj.len;
|
|
@@ -7776,7 +7828,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7776
7828
|
}
|
|
7777
7829
|
}
|
|
7778
7830
|
try {
|
|
7779
|
-
|
|
7831
|
+
fs66.fsyncSync(this.fd);
|
|
7780
7832
|
} catch {
|
|
7781
7833
|
}
|
|
7782
7834
|
}
|
|
@@ -7797,7 +7849,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7797
7849
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
7798
7850
|
}
|
|
7799
7851
|
try {
|
|
7800
|
-
const n =
|
|
7852
|
+
const n = fs66.writeSync(this.fd, buf);
|
|
7801
7853
|
buf = buf.subarray(n);
|
|
7802
7854
|
this._len = Math.max(this._len - n, 0);
|
|
7803
7855
|
if (buf.length <= 0) {
|
|
@@ -7825,13 +7877,13 @@ var require_sonic_boom = __commonJS({
|
|
|
7825
7877
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
7826
7878
|
if (this.sync) {
|
|
7827
7879
|
try {
|
|
7828
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
7880
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs66.writeSync(this.fd, this._writingBuf) : fs66.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7829
7881
|
release(null, written);
|
|
7830
7882
|
} catch (err) {
|
|
7831
7883
|
release(err);
|
|
7832
7884
|
}
|
|
7833
7885
|
} else {
|
|
7834
|
-
|
|
7886
|
+
fs66.write(this.fd, this._writingBuf, release);
|
|
7835
7887
|
}
|
|
7836
7888
|
}
|
|
7837
7889
|
function actualWriteBuffer() {
|
|
@@ -7840,7 +7892,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7840
7892
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
7841
7893
|
if (this.sync) {
|
|
7842
7894
|
try {
|
|
7843
|
-
const written =
|
|
7895
|
+
const written = fs66.writeSync(this.fd, this._writingBuf);
|
|
7844
7896
|
release(null, written);
|
|
7845
7897
|
} catch (err) {
|
|
7846
7898
|
release(err);
|
|
@@ -7849,7 +7901,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7849
7901
|
if (kCopyBuffer) {
|
|
7850
7902
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
7851
7903
|
}
|
|
7852
|
-
|
|
7904
|
+
fs66.write(this.fd, this._writingBuf, release);
|
|
7853
7905
|
}
|
|
7854
7906
|
}
|
|
7855
7907
|
function actualClose(sonic) {
|
|
@@ -7865,12 +7917,12 @@ var require_sonic_boom = __commonJS({
|
|
|
7865
7917
|
sonic._lens = [];
|
|
7866
7918
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
7867
7919
|
try {
|
|
7868
|
-
|
|
7920
|
+
fs66.fsync(sonic.fd, closeWrapped);
|
|
7869
7921
|
} catch {
|
|
7870
7922
|
}
|
|
7871
7923
|
function closeWrapped() {
|
|
7872
7924
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
7873
|
-
|
|
7925
|
+
fs66.close(sonic.fd, done);
|
|
7874
7926
|
} else {
|
|
7875
7927
|
done();
|
|
7876
7928
|
}
|
|
@@ -11005,11 +11057,11 @@ var init_lib = __esm({
|
|
|
11005
11057
|
}
|
|
11006
11058
|
}
|
|
11007
11059
|
},
|
|
11008
|
-
addToPath: function addToPath(
|
|
11009
|
-
var last =
|
|
11060
|
+
addToPath: function addToPath(path73, added, removed, oldPosInc, options) {
|
|
11061
|
+
var last = path73.lastComponent;
|
|
11010
11062
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
11011
11063
|
return {
|
|
11012
|
-
oldPos:
|
|
11064
|
+
oldPos: path73.oldPos + oldPosInc,
|
|
11013
11065
|
lastComponent: {
|
|
11014
11066
|
count: last.count + 1,
|
|
11015
11067
|
added,
|
|
@@ -11019,7 +11071,7 @@ var init_lib = __esm({
|
|
|
11019
11071
|
};
|
|
11020
11072
|
} else {
|
|
11021
11073
|
return {
|
|
11022
|
-
oldPos:
|
|
11074
|
+
oldPos: path73.oldPos + oldPosInc,
|
|
11023
11075
|
lastComponent: {
|
|
11024
11076
|
count: 1,
|
|
11025
11077
|
added,
|
|
@@ -11485,10 +11537,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
11485
11537
|
const memories = raw.map((m2) => {
|
|
11486
11538
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11487
11539
|
const rec3 = m2;
|
|
11488
|
-
const
|
|
11540
|
+
const path73 = typeof rec3.path === "string" ? rec3.path : null;
|
|
11489
11541
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
11490
|
-
if (!
|
|
11491
|
-
const entry = { path:
|
|
11542
|
+
if (!path73 || content == null) return null;
|
|
11543
|
+
const entry = { path: path73, content };
|
|
11492
11544
|
if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
|
|
11493
11545
|
return entry;
|
|
11494
11546
|
}).filter((m2) => m2 !== null);
|
|
@@ -12300,10 +12352,10 @@ function parseAttachment(obj) {
|
|
|
12300
12352
|
const memories = raw.map((m2) => {
|
|
12301
12353
|
if (!m2 || typeof m2 !== "object") return null;
|
|
12302
12354
|
const rec3 = m2;
|
|
12303
|
-
const
|
|
12355
|
+
const path73 = typeof rec3.path === "string" ? rec3.path : null;
|
|
12304
12356
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
12305
|
-
if (!
|
|
12306
|
-
const out = { path:
|
|
12357
|
+
if (!path73 || content == null) return null;
|
|
12358
|
+
const out = { path: path73, content };
|
|
12307
12359
|
if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
|
|
12308
12360
|
return out;
|
|
12309
12361
|
}).filter((m2) => m2 !== null);
|
|
@@ -33268,8 +33320,8 @@ var require_utils = __commonJS({
|
|
|
33268
33320
|
var result = transform[inputType][outputType](input);
|
|
33269
33321
|
return result;
|
|
33270
33322
|
};
|
|
33271
|
-
exports2.resolve = function(
|
|
33272
|
-
var parts =
|
|
33323
|
+
exports2.resolve = function(path73) {
|
|
33324
|
+
var parts = path73.split("/");
|
|
33273
33325
|
var result = [];
|
|
33274
33326
|
for (var index = 0; index < parts.length; index++) {
|
|
33275
33327
|
var part = parts[index];
|
|
@@ -39122,18 +39174,18 @@ var require_object = __commonJS({
|
|
|
39122
39174
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
39123
39175
|
this.files[name] = object;
|
|
39124
39176
|
};
|
|
39125
|
-
var parentFolder = function(
|
|
39126
|
-
if (
|
|
39127
|
-
|
|
39177
|
+
var parentFolder = function(path73) {
|
|
39178
|
+
if (path73.slice(-1) === "/") {
|
|
39179
|
+
path73 = path73.substring(0, path73.length - 1);
|
|
39128
39180
|
}
|
|
39129
|
-
var lastSlash =
|
|
39130
|
-
return lastSlash > 0 ?
|
|
39181
|
+
var lastSlash = path73.lastIndexOf("/");
|
|
39182
|
+
return lastSlash > 0 ? path73.substring(0, lastSlash) : "";
|
|
39131
39183
|
};
|
|
39132
|
-
var forceTrailingSlash = function(
|
|
39133
|
-
if (
|
|
39134
|
-
|
|
39184
|
+
var forceTrailingSlash = function(path73) {
|
|
39185
|
+
if (path73.slice(-1) !== "/") {
|
|
39186
|
+
path73 += "/";
|
|
39135
39187
|
}
|
|
39136
|
-
return
|
|
39188
|
+
return path73;
|
|
39137
39189
|
};
|
|
39138
39190
|
var folderAdd = function(name, createFolders) {
|
|
39139
39191
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -40135,7 +40187,7 @@ var require_lib3 = __commonJS({
|
|
|
40135
40187
|
// src/run-case/recorder.ts
|
|
40136
40188
|
function startRunCaseRecorder(opts) {
|
|
40137
40189
|
const now = opts.now ?? Date.now;
|
|
40138
|
-
const dir =
|
|
40190
|
+
const dir = import_node_path61.default.dirname(opts.recordPath);
|
|
40139
40191
|
let stream = null;
|
|
40140
40192
|
let closing = false;
|
|
40141
40193
|
let closedSettled = false;
|
|
@@ -40149,8 +40201,8 @@ function startRunCaseRecorder(opts) {
|
|
|
40149
40201
|
});
|
|
40150
40202
|
const ensureStream = () => {
|
|
40151
40203
|
if (stream) return stream;
|
|
40152
|
-
|
|
40153
|
-
stream =
|
|
40204
|
+
import_node_fs48.default.mkdirSync(dir, { recursive: true });
|
|
40205
|
+
stream = import_node_fs48.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
40154
40206
|
stream.on("close", () => closedResolve());
|
|
40155
40207
|
return stream;
|
|
40156
40208
|
};
|
|
@@ -40175,12 +40227,12 @@ function startRunCaseRecorder(opts) {
|
|
|
40175
40227
|
};
|
|
40176
40228
|
return { tap, close, closed };
|
|
40177
40229
|
}
|
|
40178
|
-
var
|
|
40230
|
+
var import_node_fs48, import_node_path61;
|
|
40179
40231
|
var init_recorder = __esm({
|
|
40180
40232
|
"src/run-case/recorder.ts"() {
|
|
40181
40233
|
"use strict";
|
|
40182
|
-
|
|
40183
|
-
|
|
40234
|
+
import_node_fs48 = __toESM(require("fs"), 1);
|
|
40235
|
+
import_node_path61 = __toESM(require("path"), 1);
|
|
40184
40236
|
}
|
|
40185
40237
|
});
|
|
40186
40238
|
|
|
@@ -40223,7 +40275,7 @@ var init_wire = __esm({
|
|
|
40223
40275
|
// src/run-case/controller.ts
|
|
40224
40276
|
async function runController(opts) {
|
|
40225
40277
|
const now = opts.now ?? Date.now;
|
|
40226
|
-
const cwd = opts.cwd ?? (0,
|
|
40278
|
+
const cwd = opts.cwd ?? (0, import_node_fs49.mkdtempSync)(import_node_path62.default.join(import_node_os22.default.tmpdir(), "clawd-runcase-"));
|
|
40227
40279
|
const ownsCwd = opts.cwd === void 0;
|
|
40228
40280
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
40229
40281
|
const spawnCtx = { cwd };
|
|
@@ -40384,19 +40436,19 @@ async function runController(opts) {
|
|
|
40384
40436
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
40385
40437
|
if (ownsCwd) {
|
|
40386
40438
|
try {
|
|
40387
|
-
(0,
|
|
40439
|
+
(0, import_node_fs49.rmSync)(cwd, { recursive: true, force: true });
|
|
40388
40440
|
} catch {
|
|
40389
40441
|
}
|
|
40390
40442
|
}
|
|
40391
40443
|
return exitCode ?? 0;
|
|
40392
40444
|
}
|
|
40393
|
-
var
|
|
40445
|
+
var import_node_fs49, import_node_os22, import_node_path62;
|
|
40394
40446
|
var init_controller = __esm({
|
|
40395
40447
|
"src/run-case/controller.ts"() {
|
|
40396
40448
|
"use strict";
|
|
40397
|
-
|
|
40449
|
+
import_node_fs49 = require("fs");
|
|
40398
40450
|
import_node_os22 = __toESM(require("os"), 1);
|
|
40399
|
-
|
|
40451
|
+
import_node_path62 = __toESM(require("path"), 1);
|
|
40400
40452
|
init_claude();
|
|
40401
40453
|
init_stdout_splitter();
|
|
40402
40454
|
init_permission_stdio();
|
|
@@ -40499,6 +40551,7 @@ var import_node_path = __toESM(require("path"), 1);
|
|
|
40499
40551
|
init_protocol();
|
|
40500
40552
|
var DEFAULT_PORT = 18790;
|
|
40501
40553
|
var DEFAULT_HOST = "127.0.0.1";
|
|
40554
|
+
var DEFAULT_SSHD_PORT = 22422;
|
|
40502
40555
|
var DEFAULT_CLAWOS_API = "https://api.clawos.chat";
|
|
40503
40556
|
var DEFAULT_LOG_ENDPOINT = "https://clawd-prod.cn-hangzhou.log.aliyuncs.com/logstores/app-logs/track";
|
|
40504
40557
|
function resolveLogShipping(raw, cliNoShipping) {
|
|
@@ -40566,6 +40619,14 @@ function parseArgs(argv) {
|
|
|
40566
40619
|
case "--no-log-shipping":
|
|
40567
40620
|
out.noLogShipping = true;
|
|
40568
40621
|
break;
|
|
40622
|
+
case "--sshd-port": {
|
|
40623
|
+
const n = Number.parseInt(next() ?? "", 10);
|
|
40624
|
+
if (!Number.isFinite(n) || n <= 0 || n > 65535) {
|
|
40625
|
+
throw new Error(`invalid --sshd-port value: ${argv[i]}`);
|
|
40626
|
+
}
|
|
40627
|
+
out.sshdPort = n;
|
|
40628
|
+
break;
|
|
40629
|
+
}
|
|
40569
40630
|
default:
|
|
40570
40631
|
if (a.startsWith("--")) throw new Error(`unknown flag: ${a}`);
|
|
40571
40632
|
break;
|
|
@@ -40615,6 +40676,7 @@ function resolveConfig(opts) {
|
|
|
40615
40676
|
fileCfg.logShipping,
|
|
40616
40677
|
args.noLogShipping ?? false
|
|
40617
40678
|
);
|
|
40679
|
+
const sshdPort = args.sshdPort ?? (typeof fileCfg.sshdPort === "number" ? fileCfg.sshdPort : DEFAULT_SSHD_PORT);
|
|
40618
40680
|
return {
|
|
40619
40681
|
port,
|
|
40620
40682
|
host,
|
|
@@ -40628,7 +40690,8 @@ function resolveConfig(opts) {
|
|
|
40628
40690
|
frpcBinary,
|
|
40629
40691
|
mode,
|
|
40630
40692
|
previewPorts,
|
|
40631
|
-
logShipping
|
|
40693
|
+
logShipping,
|
|
40694
|
+
sshdPort
|
|
40632
40695
|
};
|
|
40633
40696
|
}
|
|
40634
40697
|
var HELP_TEXT = `clawd [options]
|
|
@@ -40640,6 +40703,7 @@ var HELP_TEXT = `clawd [options]
|
|
|
40640
40703
|
--clawos-api <url> tunnel register \u63A5\u53E3\u7684 base url\uFF08\u9ED8\u8BA4 https://api.clawos.chat\uFF09
|
|
40641
40704
|
--auth-token <s> \u6307\u5B9A daemon auth token\uFF1B\u7F3A\u7701\u65F6 tunnel \u6A21\u5F0F\u4ECE ~/.clawd/auth.json \u590D\u7528\uFF0C\u6CA1\u6709\u5C31\u751F\u6210
|
|
40642
40705
|
--no-log-shipping \u7981\u7528 SLS \u65E5\u5FD7\u4E0A\u884C\uFF08\u8986\u76D6 config.json logShipping.mode=off\uFF09
|
|
40706
|
+
--sshd-port <n> Contact SSH \u53CD\u5411\u8BBF\u95EE loopback \u7AEF\u53E3\uFF0C\u9ED8\u8BA4 22422
|
|
40643
40707
|
--help / -h \u663E\u793A\u5E2E\u52A9
|
|
40644
40708
|
--version / -v \u663E\u793A\u7248\u672C
|
|
40645
40709
|
|
|
@@ -40658,8 +40722,8 @@ Env (advanced):
|
|
|
40658
40722
|
`;
|
|
40659
40723
|
|
|
40660
40724
|
// src/index.ts
|
|
40661
|
-
var
|
|
40662
|
-
var
|
|
40725
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
40726
|
+
var import_node_fs47 = __toESM(require("fs"), 1);
|
|
40663
40727
|
var import_node_os21 = __toESM(require("os"), 1);
|
|
40664
40728
|
|
|
40665
40729
|
// ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
|
|
@@ -46170,8 +46234,8 @@ function turnStartInput(text) {
|
|
|
46170
46234
|
const items = [];
|
|
46171
46235
|
let leftover = text;
|
|
46172
46236
|
for (const m2 of text.matchAll(SKILL_RE)) {
|
|
46173
|
-
const [marker, name,
|
|
46174
|
-
items.push({ type: "skill", name, path:
|
|
46237
|
+
const [marker, name, path73] = m2;
|
|
46238
|
+
items.push({ type: "skill", name, path: path73 });
|
|
46175
46239
|
leftover = leftover.replace(marker, "");
|
|
46176
46240
|
}
|
|
46177
46241
|
for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
|
|
@@ -48695,13 +48759,13 @@ function mapSkillsListResponse(res) {
|
|
|
48695
48759
|
const r = s ?? {};
|
|
48696
48760
|
const name = str3(r.name);
|
|
48697
48761
|
if (!name) continue;
|
|
48698
|
-
const
|
|
48762
|
+
const path73 = str3(r.path);
|
|
48699
48763
|
const description = str3(r.description);
|
|
48700
48764
|
const isPlugin = name.includes(":");
|
|
48701
48765
|
out.push({
|
|
48702
48766
|
name,
|
|
48703
48767
|
source: isPlugin ? "plugin" : "project",
|
|
48704
|
-
...
|
|
48768
|
+
...path73 ? { path: path73 } : {},
|
|
48705
48769
|
...description ? { description } : {},
|
|
48706
48770
|
...isPlugin ? { plugin: name.split(":")[0] } : {}
|
|
48707
48771
|
});
|
|
@@ -50495,6 +50559,23 @@ var ContactStore = class {
|
|
|
50495
50559
|
this.flush();
|
|
50496
50560
|
return true;
|
|
50497
50561
|
}
|
|
50562
|
+
/**
|
|
50563
|
+
* 更新单条 contact 的 SSH 授权(PR: contact-ssh-sandbox)。对齐 setPin pattern:
|
|
50564
|
+
* store 只做原始 mutation,不做业务校验(如"sshAllowed=false 时清空 exposedDirs")——
|
|
50565
|
+
* 那是 handler / UI 的责任。数组语义是完全替换(不 append)。
|
|
50566
|
+
* @returns 是否命中:deviceId 不存在返 false;命中即 flush.
|
|
50567
|
+
*/
|
|
50568
|
+
setSshAccess(deviceId, opts) {
|
|
50569
|
+
const existing = this.contacts.get(deviceId);
|
|
50570
|
+
if (!existing) return false;
|
|
50571
|
+
this.contacts.set(deviceId, {
|
|
50572
|
+
...existing,
|
|
50573
|
+
sshAllowed: opts.sshAllowed,
|
|
50574
|
+
exposedDirs: opts.exposedDirs
|
|
50575
|
+
});
|
|
50576
|
+
this.flush();
|
|
50577
|
+
return true;
|
|
50578
|
+
}
|
|
50498
50579
|
flush() {
|
|
50499
50580
|
const file = path30.join(this.dataDir, FILE_NAME);
|
|
50500
50581
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
@@ -50642,7 +50723,9 @@ async function autoReverseContact(args) {
|
|
|
50642
50723
|
connectToken: "",
|
|
50643
50724
|
grants,
|
|
50644
50725
|
addedAt: now(),
|
|
50645
|
-
pinnedAt: null
|
|
50726
|
+
pinnedAt: null,
|
|
50727
|
+
sshAllowed: false,
|
|
50728
|
+
exposedDirs: []
|
|
50646
50729
|
};
|
|
50647
50730
|
args.store.upsert(base);
|
|
50648
50731
|
args.broadcast({ type: "contact:added", contact: base });
|
|
@@ -52642,29 +52725,510 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
52642
52725
|
});
|
|
52643
52726
|
}
|
|
52644
52727
|
|
|
52728
|
+
// src/sshd/sshd-manager.ts
|
|
52729
|
+
var import_node_fs36 = __toESM(require("fs"), 1);
|
|
52730
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
52731
|
+
var import_node_child_process11 = require("child_process");
|
|
52732
|
+
|
|
52733
|
+
// src/sshd/sshd-config.ts
|
|
52734
|
+
function buildSshdConfig(input) {
|
|
52735
|
+
const lines = [
|
|
52736
|
+
`ListenAddress ${input.listenAddress}`,
|
|
52737
|
+
`Port ${input.port}`,
|
|
52738
|
+
`HostKey ${input.hostKeyPath}`,
|
|
52739
|
+
`PidFile ${input.pidFilePath}`,
|
|
52740
|
+
`AuthorizedKeysFile ${input.authorizedKeysFile}`,
|
|
52741
|
+
`PubkeyAuthentication yes`,
|
|
52742
|
+
`PasswordAuthentication no`,
|
|
52743
|
+
`ChallengeResponseAuthentication no`,
|
|
52744
|
+
`KbdInteractiveAuthentication no`,
|
|
52745
|
+
`PermitRootLogin no`,
|
|
52746
|
+
`StrictModes no`,
|
|
52747
|
+
`UsePAM no`,
|
|
52748
|
+
`LogLevel INFO`,
|
|
52749
|
+
`Subsystem sftp internal-sftp`
|
|
52750
|
+
];
|
|
52751
|
+
return lines.join("\n") + "\n";
|
|
52752
|
+
}
|
|
52753
|
+
|
|
52754
|
+
// src/sshd/sshd-process.ts
|
|
52755
|
+
var import_node_fs34 = __toESM(require("fs"), 1);
|
|
52756
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
52757
|
+
var import_node_child_process10 = require("child_process");
|
|
52758
|
+
function sshdPidFilePath(dataDir) {
|
|
52759
|
+
return import_node_path35.default.join(dataDir, "sshd", "sshd.pid");
|
|
52760
|
+
}
|
|
52761
|
+
function writeSshdPid(dataDir, pid) {
|
|
52762
|
+
try {
|
|
52763
|
+
const p2 = sshdPidFilePath(dataDir);
|
|
52764
|
+
import_node_fs34.default.mkdirSync(import_node_path35.default.dirname(p2), { recursive: true, mode: 448 });
|
|
52765
|
+
import_node_fs34.default.writeFileSync(p2, String(pid), { mode: 384 });
|
|
52766
|
+
} catch {
|
|
52767
|
+
}
|
|
52768
|
+
}
|
|
52769
|
+
function clearSshdPid(dataDir) {
|
|
52770
|
+
try {
|
|
52771
|
+
import_node_fs34.default.unlinkSync(sshdPidFilePath(dataDir));
|
|
52772
|
+
} catch {
|
|
52773
|
+
}
|
|
52774
|
+
}
|
|
52775
|
+
function defaultIsPidAlive2(pid) {
|
|
52776
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
52777
|
+
try {
|
|
52778
|
+
process.kill(pid, 0);
|
|
52779
|
+
return true;
|
|
52780
|
+
} catch (err) {
|
|
52781
|
+
const code = err.code;
|
|
52782
|
+
return code === "EPERM";
|
|
52783
|
+
}
|
|
52784
|
+
}
|
|
52785
|
+
function defaultReadPidFile2(file) {
|
|
52786
|
+
try {
|
|
52787
|
+
return import_node_fs34.default.readFileSync(file, "utf8");
|
|
52788
|
+
} catch {
|
|
52789
|
+
return null;
|
|
52790
|
+
}
|
|
52791
|
+
}
|
|
52792
|
+
function defaultKillPid2(pid, signal) {
|
|
52793
|
+
try {
|
|
52794
|
+
process.kill(pid, signal);
|
|
52795
|
+
} catch {
|
|
52796
|
+
}
|
|
52797
|
+
}
|
|
52798
|
+
function defaultSleep2(ms) {
|
|
52799
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
52800
|
+
}
|
|
52801
|
+
async function killStaleSshd(deps) {
|
|
52802
|
+
const pidFile = sshdPidFilePath(deps.dataDir);
|
|
52803
|
+
const configPath = import_node_path35.default.join(deps.dataDir, "sshd", "sshd_config");
|
|
52804
|
+
const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile2;
|
|
52805
|
+
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive2;
|
|
52806
|
+
const killPid = deps.killPidImpl ?? defaultKillPid2;
|
|
52807
|
+
const scanPids = deps.scanSshdPidsImpl ?? ((cp) => defaultScanSshdPidsByCmdline(cp, deps.logger));
|
|
52808
|
+
const sleep2 = deps.sleepImpl ?? defaultSleep2;
|
|
52809
|
+
const victims = /* @__PURE__ */ new Set();
|
|
52810
|
+
const raw = readPidFile(pidFile);
|
|
52811
|
+
if (raw) {
|
|
52812
|
+
const pid = parseInt(raw.trim(), 10);
|
|
52813
|
+
if (Number.isFinite(pid) && pid > 0 && pid !== deps.ownPid && isAlive(pid)) {
|
|
52814
|
+
victims.add(pid);
|
|
52815
|
+
}
|
|
52816
|
+
}
|
|
52817
|
+
try {
|
|
52818
|
+
const scanned = await scanPids(configPath);
|
|
52819
|
+
for (const pid of scanned) {
|
|
52820
|
+
if (pid > 0 && pid !== deps.ownPid && isAlive(pid)) victims.add(pid);
|
|
52821
|
+
}
|
|
52822
|
+
} catch (e) {
|
|
52823
|
+
deps.logger?.warn("sshd: stale-sshd cmdline scan failed", { err: e.message });
|
|
52824
|
+
}
|
|
52825
|
+
if (victims.size === 0) {
|
|
52826
|
+
try {
|
|
52827
|
+
import_node_fs34.default.unlinkSync(pidFile);
|
|
52828
|
+
} catch {
|
|
52829
|
+
}
|
|
52830
|
+
return;
|
|
52831
|
+
}
|
|
52832
|
+
for (const pid of victims) {
|
|
52833
|
+
deps.logger?.warn("sshd: killing stale sshd before respawn", { pid });
|
|
52834
|
+
killPid(pid, "SIGKILL");
|
|
52835
|
+
}
|
|
52836
|
+
await sleep2(deps.reapWaitMs ?? 300);
|
|
52837
|
+
try {
|
|
52838
|
+
import_node_fs34.default.unlinkSync(pidFile);
|
|
52839
|
+
} catch {
|
|
52840
|
+
}
|
|
52841
|
+
}
|
|
52842
|
+
async function defaultScanSshdPidsByCmdline(configPath, logger) {
|
|
52843
|
+
if (process.platform === "win32") return [];
|
|
52844
|
+
return new Promise((resolve6) => {
|
|
52845
|
+
const ps = (0, import_node_child_process10.spawn)("ps", ["-axo", "pid=,command="], { stdio: ["ignore", "pipe", "ignore"] });
|
|
52846
|
+
let buf = "";
|
|
52847
|
+
ps.stdout.on("data", (c) => {
|
|
52848
|
+
buf += c.toString();
|
|
52849
|
+
});
|
|
52850
|
+
ps.on("exit", () => {
|
|
52851
|
+
const pids = [];
|
|
52852
|
+
for (const line of buf.split("\n")) {
|
|
52853
|
+
const m2 = /^\s*(\d+)\s+(.*)$/.exec(line);
|
|
52854
|
+
if (!m2) continue;
|
|
52855
|
+
const cmd = m2[2];
|
|
52856
|
+
if (!/\bsshd\b/.test(cmd)) continue;
|
|
52857
|
+
if (!cmd.includes(configPath)) continue;
|
|
52858
|
+
const pid = parseInt(m2[1], 10);
|
|
52859
|
+
if (Number.isFinite(pid) && pid > 0) pids.push(pid);
|
|
52860
|
+
}
|
|
52861
|
+
resolve6(pids);
|
|
52862
|
+
});
|
|
52863
|
+
ps.on("error", (e) => {
|
|
52864
|
+
logger?.warn("sshd: ps scan failed", { err: e.message });
|
|
52865
|
+
resolve6([]);
|
|
52866
|
+
});
|
|
52867
|
+
});
|
|
52868
|
+
}
|
|
52869
|
+
|
|
52870
|
+
// src/sshd/jail-script.ts
|
|
52871
|
+
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
52872
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
52873
|
+
var CLAWD_SSH_JAIL_SCRIPT = String.raw`#!/usr/bin/env bash
|
|
52874
|
+
# clawd-ssh-jail — SSH reverse access sandbox wrapper (managed by clawd; do not edit)
|
|
52875
|
+
#
|
|
52876
|
+
# 由 sshd authorized_keys 的 command= 强制入口调用。
|
|
52877
|
+
# 用法: sshd 会以 \`clawd-ssh-jail <deviceId>\` 起本脚本;$SSH_ORIGINAL_COMMAND = client
|
|
52878
|
+
# 真实请求(interactive shell 时为空)。
|
|
52879
|
+
#
|
|
52880
|
+
# 职责:
|
|
52881
|
+
# 1. 读 ~/.clawd/contacts.json 找 contact.exposedDirs
|
|
52882
|
+
# 2. macOS 用 sandbox-exec + sbpl; Linux 用 bwrap
|
|
52883
|
+
# 3. exec 沙箱 shell
|
|
52884
|
+
|
|
52885
|
+
set -euo pipefail
|
|
52886
|
+
|
|
52887
|
+
DEVICE_ID="\${1:-}"
|
|
52888
|
+
if [ -z "$DEVICE_ID" ]; then
|
|
52889
|
+
echo "clawd-ssh-jail: missing deviceId" >&2
|
|
52890
|
+
exit 1
|
|
52891
|
+
fi
|
|
52892
|
+
|
|
52893
|
+
CONTACTS="\${HOME}/.clawd/contacts.json"
|
|
52894
|
+
if [ ! -f "$CONTACTS" ]; then
|
|
52895
|
+
echo "clawd-ssh-jail: contacts.json missing" >&2
|
|
52896
|
+
exit 1
|
|
52897
|
+
fi
|
|
52898
|
+
|
|
52899
|
+
# 读 contact 的 exposedDirs (mac/linux 都自带 python3)
|
|
52900
|
+
EXPOSED_JSON=$(python3 -c "
|
|
52901
|
+
import json, sys
|
|
52902
|
+
with open('$CONTACTS') as f:
|
|
52903
|
+
data = json.load(f)
|
|
52904
|
+
for c in data.get('contacts', []):
|
|
52905
|
+
if c.get('deviceId') == '$DEVICE_ID':
|
|
52906
|
+
if not c.get('sshAllowed'):
|
|
52907
|
+
print('DENIED', file=sys.stderr); sys.exit(2)
|
|
52908
|
+
for d in c.get('exposedDirs', []):
|
|
52909
|
+
print(d)
|
|
52910
|
+
sys.exit(0)
|
|
52911
|
+
sys.exit(3)
|
|
52912
|
+
")
|
|
52913
|
+
|
|
52914
|
+
if [ -z "$EXPOSED_JSON" ]; then
|
|
52915
|
+
echo "clawd-ssh-jail: contact not found or no exposed dirs" >&2
|
|
52916
|
+
exit 1
|
|
52917
|
+
fi
|
|
52918
|
+
|
|
52919
|
+
# 校验路径安全(bash 侧二次防御)
|
|
52920
|
+
while IFS= read -r line; do
|
|
52921
|
+
case "$line" in
|
|
52922
|
+
/*) : ;;
|
|
52923
|
+
*) echo "clawd-ssh-jail: bad path: $line" >&2; exit 1 ;;
|
|
52924
|
+
esac
|
|
52925
|
+
case "$line" in
|
|
52926
|
+
*[\"\'\`\$\;\|\&\(\)\{\}\[\]\<\>\*\?]*)
|
|
52927
|
+
echo "clawd-ssh-jail: unsafe path: $line" >&2; exit 1 ;;
|
|
52928
|
+
esac
|
|
52929
|
+
done <<< "$EXPOSED_JSON"
|
|
52930
|
+
|
|
52931
|
+
CMD="\${SSH_ORIGINAL_COMMAND:-}"
|
|
52932
|
+
if [ -z "$CMD" ]; then
|
|
52933
|
+
SHELL_CMD=(bash --login)
|
|
52934
|
+
else
|
|
52935
|
+
SHELL_CMD=(bash -c "$CMD")
|
|
52936
|
+
fi
|
|
52937
|
+
|
|
52938
|
+
case "$(uname -s)" in
|
|
52939
|
+
Darwin)
|
|
52940
|
+
POLICY="(version 1)
|
|
52941
|
+
(deny default)
|
|
52942
|
+
(allow process*)
|
|
52943
|
+
(allow signal (target self))
|
|
52944
|
+
(allow sysctl-read)
|
|
52945
|
+
(allow mach-lookup)
|
|
52946
|
+
(allow file-read-metadata)
|
|
52947
|
+
(allow network*)
|
|
52948
|
+
(allow file-read* (subpath \"/usr\"))
|
|
52949
|
+
(allow file-read* (subpath \"/bin\"))
|
|
52950
|
+
(allow file-read* (subpath \"/sbin\"))
|
|
52951
|
+
(allow file-read* (subpath \"/System\"))
|
|
52952
|
+
(allow file-read* (subpath \"/Library\"))
|
|
52953
|
+
(allow file-read* (subpath \"/etc\"))
|
|
52954
|
+
(allow file-read* (subpath \"/private/etc\"))"
|
|
52955
|
+
while IFS= read -r d; do
|
|
52956
|
+
POLICY+="
|
|
52957
|
+
(allow file-read* file-write* (subpath \"$d\"))"
|
|
52958
|
+
done <<< "$EXPOSED_JSON"
|
|
52959
|
+
exec sandbox-exec -p "$POLICY" "\${SHELL_CMD[@]}"
|
|
52960
|
+
;;
|
|
52961
|
+
Linux)
|
|
52962
|
+
BWRAP_ARGS=(
|
|
52963
|
+
--unshare-user --unshare-ipc --unshare-pid --unshare-uts
|
|
52964
|
+
--die-with-parent
|
|
52965
|
+
--proc /proc --dev /dev --tmpfs /tmp
|
|
52966
|
+
--ro-bind /usr /usr --ro-bind /bin /bin --ro-bind /sbin /sbin
|
|
52967
|
+
--ro-bind /lib /lib --ro-bind /etc /etc
|
|
52968
|
+
)
|
|
52969
|
+
if [ -d /lib64 ]; then BWRAP_ARGS+=(--ro-bind /lib64 /lib64); fi
|
|
52970
|
+
while IFS= read -r d; do
|
|
52971
|
+
BWRAP_ARGS+=(--bind "$d" "$d")
|
|
52972
|
+
done <<< "$EXPOSED_JSON"
|
|
52973
|
+
exec bwrap "\${BWRAP_ARGS[@]}" "\${SHELL_CMD[@]}"
|
|
52974
|
+
;;
|
|
52975
|
+
*)
|
|
52976
|
+
echo "clawd-ssh-jail: unsupported OS $(uname -s)" >&2
|
|
52977
|
+
exit 1
|
|
52978
|
+
;;
|
|
52979
|
+
esac
|
|
52980
|
+
`;
|
|
52981
|
+
function ensureJailScript(dataDir) {
|
|
52982
|
+
const binDir = import_node_path36.default.join(dataDir, "bin");
|
|
52983
|
+
import_node_fs35.default.mkdirSync(binDir, { recursive: true, mode: 493 });
|
|
52984
|
+
const target = import_node_path36.default.join(binDir, "clawd-ssh-jail");
|
|
52985
|
+
import_node_fs35.default.writeFileSync(target, CLAWD_SSH_JAIL_SCRIPT, { mode: 493 });
|
|
52986
|
+
return target;
|
|
52987
|
+
}
|
|
52988
|
+
|
|
52989
|
+
// src/sshd/sshd-manager.ts
|
|
52990
|
+
var SshdManager = class {
|
|
52991
|
+
constructor(deps) {
|
|
52992
|
+
this.deps = deps;
|
|
52993
|
+
this.sshdDir = import_node_path37.default.join(deps.dataDir, "sshd");
|
|
52994
|
+
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
52995
|
+
}
|
|
52996
|
+
deps;
|
|
52997
|
+
proc = null;
|
|
52998
|
+
sshdDir;
|
|
52999
|
+
stopping = false;
|
|
53000
|
+
exitHookInstalled = false;
|
|
53001
|
+
startupTimeoutMs;
|
|
53002
|
+
get port() {
|
|
53003
|
+
return this.deps.port;
|
|
53004
|
+
}
|
|
53005
|
+
async start() {
|
|
53006
|
+
const { logger } = this.deps;
|
|
53007
|
+
await (this.deps.killStaleImpl ?? killStaleSshd)({
|
|
53008
|
+
dataDir: this.deps.dataDir,
|
|
53009
|
+
ownPid: process.pid,
|
|
53010
|
+
logger
|
|
53011
|
+
});
|
|
53012
|
+
import_node_fs36.default.mkdirSync(this.sshdDir, { recursive: true, mode: 448 });
|
|
53013
|
+
import_node_fs36.default.mkdirSync(import_node_path37.default.join(this.sshdDir, "authorized_keys.d"), { recursive: true, mode: 448 });
|
|
53014
|
+
ensureJailScript(this.deps.dataDir);
|
|
53015
|
+
const hostKeyPath = import_node_path37.default.join(this.sshdDir, "host_key");
|
|
53016
|
+
if (!import_node_fs36.default.existsSync(hostKeyPath)) {
|
|
53017
|
+
await this.generateHostKey(hostKeyPath);
|
|
53018
|
+
}
|
|
53019
|
+
const akFile = import_node_path37.default.join(this.sshdDir, "authorized_keys.d", "clawd-contacts");
|
|
53020
|
+
if (!import_node_fs36.default.existsSync(akFile)) {
|
|
53021
|
+
import_node_fs36.default.writeFileSync(akFile, "", { mode: 384 });
|
|
53022
|
+
}
|
|
53023
|
+
const configPath = import_node_path37.default.join(this.sshdDir, "sshd_config");
|
|
53024
|
+
const config = buildSshdConfig({
|
|
53025
|
+
listenAddress: "127.0.0.1",
|
|
53026
|
+
port: this.deps.port,
|
|
53027
|
+
hostKeyPath,
|
|
53028
|
+
authorizedKeysFile: akFile,
|
|
53029
|
+
pidFilePath: import_node_path37.default.join(this.sshdDir, "sshd.pid")
|
|
53030
|
+
});
|
|
53031
|
+
import_node_fs36.default.writeFileSync(configPath, config, { mode: 384 });
|
|
53032
|
+
const sshdBin = this.deps.sshdBin ?? "/usr/sbin/sshd";
|
|
53033
|
+
const proc = (this.deps.spawnImpl ?? import_node_child_process11.spawn)(sshdBin, ["-D", "-e", "-f", configPath], {
|
|
53034
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
53035
|
+
});
|
|
53036
|
+
const logStream = import_node_fs36.default.createWriteStream(import_node_path37.default.join(this.sshdDir, "sshd.log"), {
|
|
53037
|
+
flags: "a",
|
|
53038
|
+
mode: 384
|
|
53039
|
+
});
|
|
53040
|
+
logStream.on("error", () => {
|
|
53041
|
+
});
|
|
53042
|
+
const tee = (c) => {
|
|
53043
|
+
logStream.write(String(c));
|
|
53044
|
+
};
|
|
53045
|
+
proc.stdout?.on("data", tee);
|
|
53046
|
+
proc.stderr?.on("data", tee);
|
|
53047
|
+
proc.once("exit", () => logStream.end());
|
|
53048
|
+
const ready = await waitForSshdReady(proc, this.startupTimeoutMs);
|
|
53049
|
+
if (!ready.ok) {
|
|
53050
|
+
try {
|
|
53051
|
+
proc.kill("SIGTERM");
|
|
53052
|
+
} catch {
|
|
53053
|
+
}
|
|
53054
|
+
const tail = ready.output.slice(-500);
|
|
53055
|
+
const msg = tail ? `${ready.error}
|
|
53056
|
+
${tail}` : ready.error;
|
|
53057
|
+
throw new Error(msg);
|
|
53058
|
+
}
|
|
53059
|
+
if (typeof proc.pid === "number") writeSshdPid(this.deps.dataDir, proc.pid);
|
|
53060
|
+
this.proc = proc;
|
|
53061
|
+
this.installProcessExitHandlersIfNeeded();
|
|
53062
|
+
this.attachExitListener(proc);
|
|
53063
|
+
logger?.info("sshd: up", { port: this.deps.port, pid: proc.pid ?? null });
|
|
53064
|
+
return { port: this.deps.port };
|
|
53065
|
+
}
|
|
53066
|
+
async stop() {
|
|
53067
|
+
this.stopping = true;
|
|
53068
|
+
const proc = this.proc;
|
|
53069
|
+
this.proc = null;
|
|
53070
|
+
if (!proc) {
|
|
53071
|
+
clearSshdPid(this.deps.dataDir);
|
|
53072
|
+
return;
|
|
53073
|
+
}
|
|
53074
|
+
proc.kill("SIGTERM");
|
|
53075
|
+
await new Promise((resolve6) => {
|
|
53076
|
+
const t = setTimeout(() => {
|
|
53077
|
+
try {
|
|
53078
|
+
proc.kill("SIGKILL");
|
|
53079
|
+
} catch {
|
|
53080
|
+
}
|
|
53081
|
+
resolve6();
|
|
53082
|
+
}, 5e3);
|
|
53083
|
+
proc.once("exit", () => {
|
|
53084
|
+
clearTimeout(t);
|
|
53085
|
+
resolve6();
|
|
53086
|
+
});
|
|
53087
|
+
});
|
|
53088
|
+
clearSshdPid(this.deps.dataDir);
|
|
53089
|
+
}
|
|
53090
|
+
killSync() {
|
|
53091
|
+
const proc = this.proc;
|
|
53092
|
+
this.proc = null;
|
|
53093
|
+
clearSshdPid(this.deps.dataDir);
|
|
53094
|
+
if (!proc) return;
|
|
53095
|
+
try {
|
|
53096
|
+
proc.kill("SIGTERM");
|
|
53097
|
+
} catch {
|
|
53098
|
+
}
|
|
53099
|
+
}
|
|
53100
|
+
attachExitListener(proc) {
|
|
53101
|
+
proc.on("exit", (code) => {
|
|
53102
|
+
this.deps.logger?.warn("sshd exited", { code });
|
|
53103
|
+
if (this.stopping) return;
|
|
53104
|
+
this.proc = null;
|
|
53105
|
+
this.deps.onSshdExit?.({ code });
|
|
53106
|
+
});
|
|
53107
|
+
}
|
|
53108
|
+
installProcessExitHandlersIfNeeded() {
|
|
53109
|
+
if (this.exitHookInstalled) return;
|
|
53110
|
+
if (this.deps.installProcessExitHandlers !== true) return;
|
|
53111
|
+
this.exitHookInstalled = true;
|
|
53112
|
+
const sync = () => this.killSync();
|
|
53113
|
+
process.once("exit", sync);
|
|
53114
|
+
process.once("SIGHUP", sync);
|
|
53115
|
+
process.once("uncaughtException", sync);
|
|
53116
|
+
}
|
|
53117
|
+
async generateHostKey(hostKeyPath) {
|
|
53118
|
+
const keygenBin = this.deps.keygenBin ?? "/usr/bin/ssh-keygen";
|
|
53119
|
+
await new Promise((resolve6, reject) => {
|
|
53120
|
+
const p2 = (this.deps.spawnImpl ?? import_node_child_process11.spawn)(
|
|
53121
|
+
keygenBin,
|
|
53122
|
+
["-t", "ed25519", "-f", hostKeyPath, "-N", "", "-q"],
|
|
53123
|
+
{ stdio: "ignore" }
|
|
53124
|
+
);
|
|
53125
|
+
p2.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`ssh-keygen exit ${code}`)));
|
|
53126
|
+
p2.on("error", reject);
|
|
53127
|
+
});
|
|
53128
|
+
try {
|
|
53129
|
+
import_node_fs36.default.chmodSync(hostKeyPath, 384);
|
|
53130
|
+
} catch {
|
|
53131
|
+
}
|
|
53132
|
+
}
|
|
53133
|
+
};
|
|
53134
|
+
async function waitForSshdReady(proc, timeoutMs) {
|
|
53135
|
+
return new Promise((resolve6) => {
|
|
53136
|
+
let settled = false;
|
|
53137
|
+
let buf = "";
|
|
53138
|
+
const finish = (r) => {
|
|
53139
|
+
if (settled) return;
|
|
53140
|
+
settled = true;
|
|
53141
|
+
cleanup();
|
|
53142
|
+
resolve6(r);
|
|
53143
|
+
};
|
|
53144
|
+
const onData = (chunk) => {
|
|
53145
|
+
buf += String(chunk);
|
|
53146
|
+
if (/Server listening on/i.test(buf)) finish({ ok: true });
|
|
53147
|
+
if (/fatal:/i.test(buf) || /error: Bind to port/i.test(buf)) {
|
|
53148
|
+
finish({ ok: false, error: "sshd startup failed", output: buf });
|
|
53149
|
+
}
|
|
53150
|
+
};
|
|
53151
|
+
const onExit = (code) => finish({ ok: false, error: `sshd exited before ready (code=${code})`, output: buf });
|
|
53152
|
+
const onErr = (err) => finish({ ok: false, error: `sshd spawn error: ${err.message}`, output: buf });
|
|
53153
|
+
const cleanup = () => {
|
|
53154
|
+
proc.stdout?.off("data", onData);
|
|
53155
|
+
proc.stderr?.off("data", onData);
|
|
53156
|
+
proc.off("exit", onExit);
|
|
53157
|
+
proc.off("error", onErr);
|
|
53158
|
+
clearTimeout(timer);
|
|
53159
|
+
};
|
|
53160
|
+
proc.stdout?.on("data", onData);
|
|
53161
|
+
proc.stderr?.on("data", onData);
|
|
53162
|
+
proc.on("exit", onExit);
|
|
53163
|
+
proc.on("error", onErr);
|
|
53164
|
+
const timer = setTimeout(
|
|
53165
|
+
() => finish({ ok: false, error: `sshd startup timeout after ${timeoutMs}ms`, output: buf }),
|
|
53166
|
+
timeoutMs
|
|
53167
|
+
);
|
|
53168
|
+
});
|
|
53169
|
+
}
|
|
53170
|
+
|
|
53171
|
+
// src/sshd/authorized-keys.ts
|
|
53172
|
+
var import_node_fs37 = __toESM(require("fs"), 1);
|
|
53173
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
53174
|
+
var JAIL_BIN_PATH_ENV = "CLAWD_JAIL_BIN_PATH";
|
|
53175
|
+
var AUTHORIZED_KEYS_FILE = "clawd-contacts";
|
|
53176
|
+
function jailBinPath() {
|
|
53177
|
+
return process.env[JAIL_BIN_PATH_ENV] ?? import_node_path38.default.join(process.env.HOME ?? "", ".clawd", "bin", "clawd-ssh-jail");
|
|
53178
|
+
}
|
|
53179
|
+
function rebuildAuthorizedKeys(store, sshdDir) {
|
|
53180
|
+
const akDir = import_node_path38.default.join(sshdDir, "authorized_keys.d");
|
|
53181
|
+
const target = import_node_path38.default.join(akDir, AUTHORIZED_KEYS_FILE);
|
|
53182
|
+
import_node_fs37.default.mkdirSync(akDir, { recursive: true, mode: 448 });
|
|
53183
|
+
const lines = ["# managed by clawd; do not edit", ""];
|
|
53184
|
+
for (const c of store.list()) {
|
|
53185
|
+
if (!c.sshAllowed) continue;
|
|
53186
|
+
const safe = /^[A-Za-z0-9_.-]+$/.test(c.deviceId);
|
|
53187
|
+
if (!safe) continue;
|
|
53188
|
+
const pubkey = readIssuedPubkey(sshdDir, c.deviceId);
|
|
53189
|
+
if (!pubkey) continue;
|
|
53190
|
+
const bin = jailBinPath();
|
|
53191
|
+
lines.push(`command="${bin} ${c.deviceId}",restrict ${pubkey.trim()}`);
|
|
53192
|
+
lines.push(`# contact:${c.deviceId}`);
|
|
53193
|
+
}
|
|
53194
|
+
const body = lines.join("\n") + "\n";
|
|
53195
|
+
const tmp = `${target}.tmp-${process.pid}-${Date.now()}`;
|
|
53196
|
+
import_node_fs37.default.writeFileSync(tmp, body, { mode: 384 });
|
|
53197
|
+
import_node_fs37.default.renameSync(tmp, target);
|
|
53198
|
+
}
|
|
53199
|
+
function readIssuedPubkey(sshdDir, deviceId) {
|
|
53200
|
+
const safeId = deviceId.replace(/[\/\\]/g, "_");
|
|
53201
|
+
const p2 = import_node_path38.default.join(sshdDir, "keys", `${safeId}.ed25519.pub`);
|
|
53202
|
+
try {
|
|
53203
|
+
return import_node_fs37.default.readFileSync(p2, "utf8");
|
|
53204
|
+
} catch {
|
|
53205
|
+
return null;
|
|
53206
|
+
}
|
|
53207
|
+
}
|
|
53208
|
+
|
|
52645
53209
|
// src/tunnel/device-key.ts
|
|
52646
53210
|
var import_node_os14 = __toESM(require("os"), 1);
|
|
52647
|
-
var
|
|
53211
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
52648
53212
|
var import_node_crypto11 = __toESM(require("crypto"), 1);
|
|
52649
53213
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
52650
53214
|
function deriveStableDeviceKey(opts = {}) {
|
|
52651
53215
|
const hostname = opts.hostname ?? import_node_os14.default.hostname();
|
|
52652
53216
|
const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
|
|
52653
53217
|
const home = opts.home ?? import_node_os14.default.homedir();
|
|
52654
|
-
const defaultDataDir =
|
|
52655
|
-
const normalizedDataDir = opts.dataDir ?
|
|
53218
|
+
const defaultDataDir = import_node_path39.default.resolve(import_node_path39.default.join(home, ".clawd"));
|
|
53219
|
+
const normalizedDataDir = opts.dataDir ? import_node_path39.default.resolve(opts.dataDir) : null;
|
|
52656
53220
|
const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
|
|
52657
53221
|
const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
|
|
52658
53222
|
return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
52659
53223
|
}
|
|
52660
53224
|
|
|
52661
53225
|
// src/auth-store.ts
|
|
52662
|
-
var
|
|
52663
|
-
var
|
|
53226
|
+
var import_node_fs38 = __toESM(require("fs"), 1);
|
|
53227
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
52664
53228
|
var import_node_crypto12 = __toESM(require("crypto"), 1);
|
|
52665
53229
|
var AUTH_FILE_NAME = "auth.json";
|
|
52666
53230
|
function authFilePath(dataDir) {
|
|
52667
|
-
return
|
|
53231
|
+
return import_node_path40.default.join(dataDir, AUTH_FILE_NAME);
|
|
52668
53232
|
}
|
|
52669
53233
|
function loadOrCreateAuthFile(opts) {
|
|
52670
53234
|
const file = authFilePath(opts.dataDir);
|
|
@@ -52700,7 +53264,7 @@ function defaultGenerateOwnerPrincipalId() {
|
|
|
52700
53264
|
}
|
|
52701
53265
|
function readAuthFile(file) {
|
|
52702
53266
|
try {
|
|
52703
|
-
const raw =
|
|
53267
|
+
const raw = import_node_fs38.default.readFileSync(file, "utf8");
|
|
52704
53268
|
const parsed = JSON.parse(raw);
|
|
52705
53269
|
if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
|
|
52706
53270
|
return null;
|
|
@@ -52720,25 +53284,25 @@ function readAuthFile(file) {
|
|
|
52720
53284
|
}
|
|
52721
53285
|
}
|
|
52722
53286
|
function writeAuthFile(file, content) {
|
|
52723
|
-
|
|
52724
|
-
|
|
53287
|
+
import_node_fs38.default.mkdirSync(import_node_path40.default.dirname(file), { recursive: true });
|
|
53288
|
+
import_node_fs38.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
52725
53289
|
try {
|
|
52726
|
-
|
|
53290
|
+
import_node_fs38.default.chmodSync(file, 384);
|
|
52727
53291
|
} catch {
|
|
52728
53292
|
}
|
|
52729
53293
|
}
|
|
52730
53294
|
|
|
52731
53295
|
// src/owner-profile.ts
|
|
52732
|
-
var
|
|
53296
|
+
var import_node_fs39 = __toESM(require("fs"), 1);
|
|
52733
53297
|
var import_node_os15 = __toESM(require("os"), 1);
|
|
52734
|
-
var
|
|
53298
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
52735
53299
|
var PROFILE_FILENAME = "profile.json";
|
|
52736
53300
|
function loadOwnerDisplayName(dataDir) {
|
|
52737
53301
|
const fallback = import_node_os15.default.userInfo().username;
|
|
52738
|
-
const profilePath =
|
|
53302
|
+
const profilePath = import_node_path41.default.join(dataDir, PROFILE_FILENAME);
|
|
52739
53303
|
let raw;
|
|
52740
53304
|
try {
|
|
52741
|
-
raw =
|
|
53305
|
+
raw = import_node_fs39.default.readFileSync(profilePath, "utf8");
|
|
52742
53306
|
} catch {
|
|
52743
53307
|
return fallback;
|
|
52744
53308
|
}
|
|
@@ -52761,18 +53325,18 @@ function loadOwnerDisplayName(dataDir) {
|
|
|
52761
53325
|
}
|
|
52762
53326
|
|
|
52763
53327
|
// src/feishu-auth/owner-identity-store.ts
|
|
52764
|
-
var
|
|
52765
|
-
var
|
|
53328
|
+
var import_node_fs40 = __toESM(require("fs"), 1);
|
|
53329
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
52766
53330
|
var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
|
|
52767
53331
|
var OwnerIdentityStore = class {
|
|
52768
53332
|
file;
|
|
52769
53333
|
constructor(dataDir) {
|
|
52770
|
-
this.file =
|
|
53334
|
+
this.file = import_node_path42.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
|
|
52771
53335
|
}
|
|
52772
53336
|
read() {
|
|
52773
53337
|
let raw;
|
|
52774
53338
|
try {
|
|
52775
|
-
raw =
|
|
53339
|
+
raw = import_node_fs40.default.readFileSync(this.file, "utf8");
|
|
52776
53340
|
} catch {
|
|
52777
53341
|
return null;
|
|
52778
53342
|
}
|
|
@@ -52800,16 +53364,16 @@ var OwnerIdentityStore = class {
|
|
|
52800
53364
|
};
|
|
52801
53365
|
}
|
|
52802
53366
|
write(record) {
|
|
52803
|
-
|
|
52804
|
-
|
|
53367
|
+
import_node_fs40.default.mkdirSync(import_node_path42.default.dirname(this.file), { recursive: true });
|
|
53368
|
+
import_node_fs40.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
|
|
52805
53369
|
try {
|
|
52806
|
-
|
|
53370
|
+
import_node_fs40.default.chmodSync(this.file, 384);
|
|
52807
53371
|
} catch {
|
|
52808
53372
|
}
|
|
52809
53373
|
}
|
|
52810
53374
|
clear() {
|
|
52811
53375
|
try {
|
|
52812
|
-
|
|
53376
|
+
import_node_fs40.default.unlinkSync(this.file);
|
|
52813
53377
|
} catch (err) {
|
|
52814
53378
|
const code = err?.code;
|
|
52815
53379
|
if (code !== "ENOENT") throw err;
|
|
@@ -52930,9 +53494,9 @@ var CentralClientError = class extends Error {
|
|
|
52930
53494
|
code;
|
|
52931
53495
|
cause;
|
|
52932
53496
|
};
|
|
52933
|
-
async function centralRequest(opts,
|
|
53497
|
+
async function centralRequest(opts, path73, init) {
|
|
52934
53498
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
52935
|
-
const url = `${opts.api.replace(/\/+$/, "")}${
|
|
53499
|
+
const url = `${opts.api.replace(/\/+$/, "")}${path73}`;
|
|
52936
53500
|
const ctrl = new AbortController();
|
|
52937
53501
|
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
52938
53502
|
let res;
|
|
@@ -53074,8 +53638,8 @@ function verifyConnectToken(args) {
|
|
|
53074
53638
|
}
|
|
53075
53639
|
|
|
53076
53640
|
// src/feishu-auth/server-key.ts
|
|
53077
|
-
var
|
|
53078
|
-
var
|
|
53641
|
+
var fs50 = __toESM(require("fs"), 1);
|
|
53642
|
+
var path51 = __toESM(require("path"), 1);
|
|
53079
53643
|
var FILE_NAME2 = "server-signing-key.json";
|
|
53080
53644
|
var ServerKeyStore = class {
|
|
53081
53645
|
constructor(dataDir) {
|
|
@@ -53083,12 +53647,12 @@ var ServerKeyStore = class {
|
|
|
53083
53647
|
}
|
|
53084
53648
|
dataDir;
|
|
53085
53649
|
filePath() {
|
|
53086
|
-
return
|
|
53650
|
+
return path51.join(this.dataDir, FILE_NAME2);
|
|
53087
53651
|
}
|
|
53088
53652
|
/** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
|
|
53089
53653
|
read() {
|
|
53090
53654
|
try {
|
|
53091
|
-
const raw =
|
|
53655
|
+
const raw = fs50.readFileSync(this.filePath(), "utf8");
|
|
53092
53656
|
const parsed = JSON.parse(raw);
|
|
53093
53657
|
if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
|
|
53094
53658
|
return parsed.publicKeyPem;
|
|
@@ -53103,12 +53667,12 @@ var ServerKeyStore = class {
|
|
|
53103
53667
|
publicKeyPem,
|
|
53104
53668
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
53105
53669
|
};
|
|
53106
|
-
|
|
53107
|
-
|
|
53670
|
+
fs50.mkdirSync(this.dataDir, { recursive: true });
|
|
53671
|
+
fs50.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
|
|
53108
53672
|
}
|
|
53109
53673
|
clear() {
|
|
53110
53674
|
try {
|
|
53111
|
-
|
|
53675
|
+
fs50.unlinkSync(this.filePath());
|
|
53112
53676
|
} catch {
|
|
53113
53677
|
}
|
|
53114
53678
|
}
|
|
@@ -53121,12 +53685,12 @@ init_protocol();
|
|
|
53121
53685
|
init_protocol();
|
|
53122
53686
|
|
|
53123
53687
|
// src/session/fork.ts
|
|
53124
|
-
var
|
|
53688
|
+
var import_node_fs41 = __toESM(require("fs"), 1);
|
|
53125
53689
|
var import_node_os16 = __toESM(require("os"), 1);
|
|
53126
|
-
var
|
|
53690
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
53127
53691
|
init_claude_history();
|
|
53128
53692
|
function readJsonlEntries(file) {
|
|
53129
|
-
const raw =
|
|
53693
|
+
const raw = import_node_fs41.default.readFileSync(file, "utf8");
|
|
53130
53694
|
const out = [];
|
|
53131
53695
|
for (const line of raw.split("\n")) {
|
|
53132
53696
|
const t = line.trim();
|
|
@@ -53139,10 +53703,10 @@ function readJsonlEntries(file) {
|
|
|
53139
53703
|
return out;
|
|
53140
53704
|
}
|
|
53141
53705
|
function forkSession(input) {
|
|
53142
|
-
const baseDir = input.baseDir ??
|
|
53143
|
-
const projectDir =
|
|
53144
|
-
const sourceFile =
|
|
53145
|
-
if (!
|
|
53706
|
+
const baseDir = input.baseDir ?? import_node_path43.default.join(import_node_os16.default.homedir(), ".claude");
|
|
53707
|
+
const projectDir = import_node_path43.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
53708
|
+
const sourceFile = import_node_path43.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
53709
|
+
if (!import_node_fs41.default.existsSync(sourceFile)) {
|
|
53146
53710
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
53147
53711
|
}
|
|
53148
53712
|
const entries = readJsonlEntries(sourceFile);
|
|
@@ -53172,9 +53736,9 @@ function forkSession(input) {
|
|
|
53172
53736
|
}
|
|
53173
53737
|
forkedLines.push(JSON.stringify(forked));
|
|
53174
53738
|
}
|
|
53175
|
-
const forkedFilePath =
|
|
53176
|
-
|
|
53177
|
-
|
|
53739
|
+
const forkedFilePath = import_node_path43.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
53740
|
+
import_node_fs41.default.mkdirSync(projectDir, { recursive: true });
|
|
53741
|
+
import_node_fs41.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
53178
53742
|
return { forkedToolSessionId, forkedFilePath };
|
|
53179
53743
|
}
|
|
53180
53744
|
|
|
@@ -53526,7 +54090,7 @@ function buildPermissionHandlers(deps) {
|
|
|
53526
54090
|
}
|
|
53527
54091
|
|
|
53528
54092
|
// src/handlers/history.ts
|
|
53529
|
-
var
|
|
54093
|
+
var path54 = __toESM(require("path"), 1);
|
|
53530
54094
|
init_protocol();
|
|
53531
54095
|
|
|
53532
54096
|
// src/session/recent-dirs.ts
|
|
@@ -53544,7 +54108,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
53544
54108
|
}
|
|
53545
54109
|
|
|
53546
54110
|
// src/permission/persona-paths.ts
|
|
53547
|
-
var
|
|
54111
|
+
var path53 = __toESM(require("path"), 1);
|
|
53548
54112
|
function getAllowedPersonaIds(grants, action) {
|
|
53549
54113
|
const ids = /* @__PURE__ */ new Set();
|
|
53550
54114
|
for (const g2 of grants) {
|
|
@@ -53557,42 +54121,42 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
53557
54121
|
return ids;
|
|
53558
54122
|
}
|
|
53559
54123
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
|
|
53560
|
-
const target =
|
|
54124
|
+
const target = path53.resolve(absPath);
|
|
53561
54125
|
if (userWorkDir) {
|
|
53562
|
-
const u =
|
|
53563
|
-
const usep = u.endsWith(
|
|
54126
|
+
const u = path53.resolve(userWorkDir);
|
|
54127
|
+
const usep = u.endsWith(path53.sep) ? "" : path53.sep;
|
|
53564
54128
|
if (target === u || target.startsWith(u + usep)) return true;
|
|
53565
54129
|
}
|
|
53566
|
-
const root =
|
|
53567
|
-
const sep3 = root.endsWith(
|
|
54130
|
+
const root = path53.resolve(personaRoot);
|
|
54131
|
+
const sep3 = root.endsWith(path53.sep) ? "" : path53.sep;
|
|
53568
54132
|
if (!target.startsWith(root + sep3)) return false;
|
|
53569
|
-
const rel =
|
|
54133
|
+
const rel = path53.relative(root, target);
|
|
53570
54134
|
if (!rel || rel.startsWith("..")) return false;
|
|
53571
|
-
const personaId = rel.split(
|
|
54135
|
+
const personaId = rel.split(path53.sep)[0];
|
|
53572
54136
|
if (!personaId) return false;
|
|
53573
54137
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
53574
54138
|
if (allowed === "*") return true;
|
|
53575
54139
|
return allowed.has(personaId);
|
|
53576
54140
|
}
|
|
53577
54141
|
function personaIdFromPath(absPath, personaRoot) {
|
|
53578
|
-
const root =
|
|
53579
|
-
const target =
|
|
53580
|
-
const sep3 = root.endsWith(
|
|
54142
|
+
const root = path53.resolve(personaRoot);
|
|
54143
|
+
const target = path53.resolve(absPath);
|
|
54144
|
+
const sep3 = root.endsWith(path53.sep) ? "" : path53.sep;
|
|
53581
54145
|
if (!target.startsWith(root + sep3)) return null;
|
|
53582
|
-
const rel =
|
|
54146
|
+
const rel = path53.relative(root, target);
|
|
53583
54147
|
if (!rel || rel.startsWith("..")) return null;
|
|
53584
|
-
const id = rel.split(
|
|
54148
|
+
const id = rel.split(path53.sep)[0];
|
|
53585
54149
|
return id || null;
|
|
53586
54150
|
}
|
|
53587
54151
|
function isPathWithin(dir, absPath) {
|
|
53588
|
-
const d =
|
|
53589
|
-
const t =
|
|
53590
|
-
const sep3 = d.endsWith(
|
|
54152
|
+
const d = path53.resolve(dir);
|
|
54153
|
+
const t = path53.resolve(absPath);
|
|
54154
|
+
const sep3 = d.endsWith(path53.sep) ? "" : path53.sep;
|
|
53591
54155
|
return t === d || t.startsWith(d + sep3);
|
|
53592
54156
|
}
|
|
53593
54157
|
function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
|
|
53594
54158
|
if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
|
|
53595
|
-
return personaIdFromPath(
|
|
54159
|
+
return personaIdFromPath(path53.resolve(absPath), personaRoot) === personaId;
|
|
53596
54160
|
}
|
|
53597
54161
|
|
|
53598
54162
|
// src/handlers/history.ts
|
|
@@ -53618,7 +54182,7 @@ function buildHistoryHandlers(deps) {
|
|
|
53618
54182
|
if (!pid) return false;
|
|
53619
54183
|
return isGuestPathAllowed(
|
|
53620
54184
|
ctx.grants,
|
|
53621
|
-
|
|
54185
|
+
path54.join(personaRoot, pid),
|
|
53622
54186
|
personaRoot,
|
|
53623
54187
|
"read",
|
|
53624
54188
|
userWorkDir
|
|
@@ -53630,7 +54194,7 @@ function buildHistoryHandlers(deps) {
|
|
|
53630
54194
|
};
|
|
53631
54195
|
const list = async (frame, _client, ctx) => {
|
|
53632
54196
|
const args = HistoryListArgs.parse(frame);
|
|
53633
|
-
assertGuestPath(ctx,
|
|
54197
|
+
assertGuestPath(ctx, path54.resolve(args.projectPath), personaRoot, "history:list");
|
|
53634
54198
|
const sessions = await history.listSessions(args);
|
|
53635
54199
|
return { response: { type: "history:list", sessions } };
|
|
53636
54200
|
};
|
|
@@ -53662,13 +54226,13 @@ function buildHistoryHandlers(deps) {
|
|
|
53662
54226
|
};
|
|
53663
54227
|
const subagents = async (frame, _client, ctx) => {
|
|
53664
54228
|
const args = HistorySubagentsArgs.parse(frame);
|
|
53665
|
-
assertGuestPath(ctx,
|
|
54229
|
+
assertGuestPath(ctx, path54.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
|
|
53666
54230
|
const subs = await history.listSubagents(args);
|
|
53667
54231
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
53668
54232
|
};
|
|
53669
54233
|
const subagentRead = async (frame, _client, ctx) => {
|
|
53670
54234
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
53671
|
-
assertGuestPath(ctx,
|
|
54235
|
+
assertGuestPath(ctx, path54.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
|
|
53672
54236
|
const res = await history.readSubagent(args);
|
|
53673
54237
|
return { response: { type: "history:subagent-read", ...res } };
|
|
53674
54238
|
};
|
|
@@ -53677,7 +54241,7 @@ function buildHistoryHandlers(deps) {
|
|
|
53677
54241
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
53678
54242
|
const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
|
|
53679
54243
|
const filtered = dirs.filter(
|
|
53680
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
54244
|
+
(d) => isGuestPathAllowed(ctx.grants, path54.resolve(d.cwd), personaRoot, "read", userWorkDir)
|
|
53681
54245
|
);
|
|
53682
54246
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
53683
54247
|
}
|
|
@@ -53694,7 +54258,7 @@ function buildHistoryHandlers(deps) {
|
|
|
53694
54258
|
}
|
|
53695
54259
|
|
|
53696
54260
|
// src/handlers/workspace.ts
|
|
53697
|
-
var
|
|
54261
|
+
var path55 = __toESM(require("path"), 1);
|
|
53698
54262
|
var os16 = __toESM(require("os"), 1);
|
|
53699
54263
|
init_protocol();
|
|
53700
54264
|
init_protocol();
|
|
@@ -53736,22 +54300,22 @@ function buildWorkspaceHandlers(deps) {
|
|
|
53736
54300
|
const args = WorkspaceListArgs.parse(frame);
|
|
53737
54301
|
const isGuest = ctx?.principal.kind === "guest";
|
|
53738
54302
|
const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
|
|
53739
|
-
const resolvedCwd =
|
|
53740
|
-
const target = args.path ?
|
|
54303
|
+
const resolvedCwd = path55.resolve(args.cwd ?? fallbackCwd);
|
|
54304
|
+
const target = args.path ? path55.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
53741
54305
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
|
|
53742
54306
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
53743
54307
|
return { response: { type: "workspace:list", ...res } };
|
|
53744
54308
|
};
|
|
53745
54309
|
const read = async (frame, _client, ctx) => {
|
|
53746
54310
|
const args = WorkspaceReadArgs.parse(frame);
|
|
53747
|
-
const target =
|
|
54311
|
+
const target = path55.isAbsolute(args.path) ? path55.resolve(args.path) : path55.resolve(args.cwd, args.path);
|
|
53748
54312
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
|
|
53749
54313
|
const res = workspace.read(args);
|
|
53750
54314
|
return { response: { type: "workspace:read", ...res } };
|
|
53751
54315
|
};
|
|
53752
54316
|
const skillsList = async (frame, _client, ctx) => {
|
|
53753
54317
|
const args = SkillsListArgs.parse(frame);
|
|
53754
|
-
const cwdAbs =
|
|
54318
|
+
const cwdAbs = path55.resolve(args.cwd);
|
|
53755
54319
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
|
|
53756
54320
|
const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
|
|
53757
54321
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -53763,7 +54327,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
53763
54327
|
};
|
|
53764
54328
|
const agentsList = async (frame, _client, ctx) => {
|
|
53765
54329
|
const args = AgentsListArgs.parse(frame);
|
|
53766
|
-
const cwdAbs =
|
|
54330
|
+
const cwdAbs = path55.resolve(args.cwd);
|
|
53767
54331
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
|
|
53768
54332
|
if (args.tool === "codex") {
|
|
53769
54333
|
return { response: { type: "agents:list", agents: [] } };
|
|
@@ -53785,20 +54349,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
53785
54349
|
}
|
|
53786
54350
|
|
|
53787
54351
|
// src/handlers/git.ts
|
|
53788
|
-
var
|
|
54352
|
+
var path57 = __toESM(require("path"), 1);
|
|
53789
54353
|
init_protocol();
|
|
53790
54354
|
init_protocol();
|
|
53791
54355
|
|
|
53792
54356
|
// src/workspace/git.ts
|
|
53793
|
-
var
|
|
53794
|
-
var
|
|
53795
|
-
var
|
|
54357
|
+
var import_node_child_process12 = require("child_process");
|
|
54358
|
+
var import_node_fs42 = __toESM(require("fs"), 1);
|
|
54359
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
53796
54360
|
var import_node_util = require("util");
|
|
53797
|
-
var pexec = (0, import_node_util.promisify)(
|
|
54361
|
+
var pexec = (0, import_node_util.promisify)(import_node_child_process12.execFile);
|
|
53798
54362
|
function normalizePath(p2) {
|
|
53799
|
-
const resolved =
|
|
54363
|
+
const resolved = import_node_path44.default.resolve(p2);
|
|
53800
54364
|
try {
|
|
53801
|
-
return
|
|
54365
|
+
return import_node_fs42.default.realpathSync(resolved);
|
|
53802
54366
|
} catch {
|
|
53803
54367
|
return resolved;
|
|
53804
54368
|
}
|
|
@@ -53872,7 +54436,7 @@ async function listGitBranches(cwd) {
|
|
|
53872
54436
|
function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
|
|
53873
54437
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
53874
54438
|
const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
|
|
53875
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
54439
|
+
if (!isGuestPathAllowed(ctx.grants, path57.resolve(cwd), personaRoot, "read", userWorkDir)) {
|
|
53876
54440
|
throw new ClawdError(
|
|
53877
54441
|
ERROR_CODES.UNAUTHORIZED,
|
|
53878
54442
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -54205,6 +54769,128 @@ function buildContactHandlers(deps) {
|
|
|
54205
54769
|
};
|
|
54206
54770
|
}
|
|
54207
54771
|
|
|
54772
|
+
// src/handlers/contact-ssh.ts
|
|
54773
|
+
init_protocol();
|
|
54774
|
+
|
|
54775
|
+
// src/sshd/key-issue.ts
|
|
54776
|
+
var import_node_fs43 = __toESM(require("fs"), 1);
|
|
54777
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
54778
|
+
var import_node_child_process13 = require("child_process");
|
|
54779
|
+
function safeDeviceId(deviceId) {
|
|
54780
|
+
return deviceId.replace(/[\/\\]/g, "_");
|
|
54781
|
+
}
|
|
54782
|
+
async function issueContactSshKey(deviceId, sshdDir, opts = {}) {
|
|
54783
|
+
const safeId = safeDeviceId(deviceId);
|
|
54784
|
+
const keysDir = import_node_path45.default.join(sshdDir, "keys");
|
|
54785
|
+
import_node_fs43.default.mkdirSync(keysDir, { recursive: true, mode: 448 });
|
|
54786
|
+
const privPath = import_node_path45.default.join(keysDir, `${safeId}.ed25519`);
|
|
54787
|
+
const pubPath = `${privPath}.pub`;
|
|
54788
|
+
if (import_node_fs43.default.existsSync(privPath) && import_node_fs43.default.existsSync(pubPath)) {
|
|
54789
|
+
return {
|
|
54790
|
+
privateKeyPem: import_node_fs43.default.readFileSync(privPath, "utf8"),
|
|
54791
|
+
publicKeyLine: import_node_fs43.default.readFileSync(pubPath, "utf8").trim()
|
|
54792
|
+
};
|
|
54793
|
+
}
|
|
54794
|
+
const bin = opts.keygenBin ?? "/usr/bin/ssh-keygen";
|
|
54795
|
+
await new Promise((resolve6, reject) => {
|
|
54796
|
+
const p2 = (opts.spawnImpl ?? import_node_child_process13.spawn)(
|
|
54797
|
+
bin,
|
|
54798
|
+
["-t", "ed25519", "-f", privPath, "-N", "", "-q", "-C", `clawd-contact-${safeId}`],
|
|
54799
|
+
{ stdio: "ignore" }
|
|
54800
|
+
);
|
|
54801
|
+
p2.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`ssh-keygen exit ${code}`)));
|
|
54802
|
+
p2.on("error", reject);
|
|
54803
|
+
});
|
|
54804
|
+
try {
|
|
54805
|
+
import_node_fs43.default.chmodSync(privPath, 384);
|
|
54806
|
+
} catch {
|
|
54807
|
+
}
|
|
54808
|
+
try {
|
|
54809
|
+
import_node_fs43.default.chmodSync(pubPath, 420);
|
|
54810
|
+
} catch {
|
|
54811
|
+
}
|
|
54812
|
+
return {
|
|
54813
|
+
privateKeyPem: import_node_fs43.default.readFileSync(privPath, "utf8"),
|
|
54814
|
+
publicKeyLine: import_node_fs43.default.readFileSync(pubPath, "utf8").trim()
|
|
54815
|
+
};
|
|
54816
|
+
}
|
|
54817
|
+
|
|
54818
|
+
// src/handlers/contact-ssh.ts
|
|
54819
|
+
function ensureOwner2(ctx) {
|
|
54820
|
+
if (!ctx || ctx.principal.kind !== "owner") {
|
|
54821
|
+
throw new ClawdError(
|
|
54822
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
54823
|
+
"UNAUTHORIZED: contact:setSshAccess requires owner ctx"
|
|
54824
|
+
);
|
|
54825
|
+
}
|
|
54826
|
+
}
|
|
54827
|
+
function ensureGuest(ctx) {
|
|
54828
|
+
if (!ctx || ctx.principal.kind !== "guest") {
|
|
54829
|
+
throw new ClawdError(
|
|
54830
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
54831
|
+
"UNAUTHORIZED: contact:sshKey:issue requires guest ctx"
|
|
54832
|
+
);
|
|
54833
|
+
}
|
|
54834
|
+
return ctx.principal.id;
|
|
54835
|
+
}
|
|
54836
|
+
function buildContactSshHandlers(deps) {
|
|
54837
|
+
const setSshAccess = async (frame, _client, ctx) => {
|
|
54838
|
+
ensureOwner2(ctx);
|
|
54839
|
+
const { type: _t, requestId: _r, ...rest } = frame;
|
|
54840
|
+
const args = ContactSetSshAccessArgsSchema.parse(rest);
|
|
54841
|
+
const hit = deps.store.setSshAccess(args.deviceId, {
|
|
54842
|
+
sshAllowed: args.sshAllowed,
|
|
54843
|
+
exposedDirs: args.exposedDirs
|
|
54844
|
+
});
|
|
54845
|
+
if (!hit) {
|
|
54846
|
+
throw new ClawdError(
|
|
54847
|
+
ERROR_CODES.CONTACT_NOT_FOUND,
|
|
54848
|
+
`CONTACT_NOT_FOUND: contact ${args.deviceId} not in store`
|
|
54849
|
+
);
|
|
54850
|
+
}
|
|
54851
|
+
rebuildAuthorizedKeys(deps.store, deps.sshdDir);
|
|
54852
|
+
deps.broadcast({
|
|
54853
|
+
type: "contact:ssh-access-updated",
|
|
54854
|
+
deviceId: args.deviceId,
|
|
54855
|
+
sshAllowed: args.sshAllowed,
|
|
54856
|
+
exposedDirs: args.exposedDirs
|
|
54857
|
+
});
|
|
54858
|
+
return {
|
|
54859
|
+
response: {
|
|
54860
|
+
type: "contact:setSshAccess:ok",
|
|
54861
|
+
deviceId: args.deviceId,
|
|
54862
|
+
sshAllowed: args.sshAllowed,
|
|
54863
|
+
exposedDirs: args.exposedDirs
|
|
54864
|
+
}
|
|
54865
|
+
};
|
|
54866
|
+
};
|
|
54867
|
+
const sshKeyIssue = async (frame, _client, ctx) => {
|
|
54868
|
+
const callerDeviceId = ensureGuest(ctx);
|
|
54869
|
+
const { type: _t, requestId: _r, ...rest } = frame;
|
|
54870
|
+
ContactSshKeyIssueArgsSchema.parse(rest);
|
|
54871
|
+
const contact = deps.store.get(callerDeviceId);
|
|
54872
|
+
if (!contact || !contact.sshAllowed) {
|
|
54873
|
+
throw new ClawdError(
|
|
54874
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
54875
|
+
`UNAUTHORIZED: contact ${callerDeviceId} not authorized for SSH`
|
|
54876
|
+
);
|
|
54877
|
+
}
|
|
54878
|
+
const { privateKeyPem, publicKeyLine } = await issueContactSshKey(callerDeviceId, deps.sshdDir);
|
|
54879
|
+
rebuildAuthorizedKeys(deps.store, deps.sshdDir);
|
|
54880
|
+
return {
|
|
54881
|
+
response: {
|
|
54882
|
+
type: "contact:sshKey:issue:ok",
|
|
54883
|
+
privateKeyPem,
|
|
54884
|
+
publicKeyLine
|
|
54885
|
+
}
|
|
54886
|
+
};
|
|
54887
|
+
};
|
|
54888
|
+
return {
|
|
54889
|
+
"contact:setSshAccess": setSshAccess,
|
|
54890
|
+
"contact:sshKey:issue": sshKeyIssue
|
|
54891
|
+
};
|
|
54892
|
+
}
|
|
54893
|
+
|
|
54208
54894
|
// src/handlers/whoami.ts
|
|
54209
54895
|
init_protocol();
|
|
54210
54896
|
function buildWhoamiHandler(deps) {
|
|
@@ -54300,7 +54986,7 @@ function buildFeishuAuthHandlers(deps) {
|
|
|
54300
54986
|
|
|
54301
54987
|
// src/handlers/device.ts
|
|
54302
54988
|
init_protocol();
|
|
54303
|
-
function
|
|
54989
|
+
function ensureOwner3(ctx) {
|
|
54304
54990
|
if (!ctx || ctx.principal.kind !== "owner") {
|
|
54305
54991
|
throw new ClawdError(ERROR_CODES.UNAUTHORIZED, "UNAUTHORIZED: device:* requires owner ctx");
|
|
54306
54992
|
}
|
|
@@ -54308,14 +54994,14 @@ function ensureOwner2(ctx) {
|
|
|
54308
54994
|
function buildDeviceHandlers(deps) {
|
|
54309
54995
|
const now = deps.now ?? Date.now;
|
|
54310
54996
|
const list = async (_frame, _client, ctx) => {
|
|
54311
|
-
|
|
54997
|
+
ensureOwner3(ctx);
|
|
54312
54998
|
const devices = await deps.listDevices();
|
|
54313
54999
|
return {
|
|
54314
55000
|
response: { type: "device:list:ok", devices }
|
|
54315
55001
|
};
|
|
54316
55002
|
};
|
|
54317
55003
|
const connect = async (frame, _client, ctx) => {
|
|
54318
|
-
|
|
55004
|
+
ensureOwner3(ctx);
|
|
54319
55005
|
const { type: _t, requestId: _r, ...rest } = frame;
|
|
54320
55006
|
const args = DeviceConnectArgsSchema.parse(rest);
|
|
54321
55007
|
const exchanged = await deps.exchange(args.deviceId);
|
|
@@ -54358,7 +55044,9 @@ function buildDeviceHandlers(deps) {
|
|
|
54358
55044
|
connectToken: exchanged.token,
|
|
54359
55045
|
grants: wh.grants,
|
|
54360
55046
|
addedAt: now(),
|
|
54361
|
-
pinnedAt: null
|
|
55047
|
+
pinnedAt: null,
|
|
55048
|
+
sshAllowed: false,
|
|
55049
|
+
exposedDirs: []
|
|
54362
55050
|
};
|
|
54363
55051
|
deps.store.upsert(contact);
|
|
54364
55052
|
deps.broadcast({ type: "contact:added", contact });
|
|
@@ -54514,7 +55202,7 @@ function buildPersonaHandlers(deps) {
|
|
|
54514
55202
|
}
|
|
54515
55203
|
|
|
54516
55204
|
// src/handlers/attachment.ts
|
|
54517
|
-
var
|
|
55205
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
54518
55206
|
init_protocol();
|
|
54519
55207
|
init_protocol();
|
|
54520
55208
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -54594,12 +55282,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
54594
55282
|
`session ${args.sessionId} scope unresolved`
|
|
54595
55283
|
);
|
|
54596
55284
|
}
|
|
54597
|
-
const cwdAbs =
|
|
54598
|
-
const candidateAbs =
|
|
55285
|
+
const cwdAbs = import_node_path46.default.resolve(sessionFile.cwd);
|
|
55286
|
+
const candidateAbs = import_node_path46.default.isAbsolute(args.relPath) ? import_node_path46.default.resolve(args.relPath) : import_node_path46.default.resolve(cwdAbs, args.relPath);
|
|
54599
55287
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
|
|
54600
55288
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
54601
55289
|
const entry = entries.find((e) => {
|
|
54602
|
-
const storedAbs =
|
|
55290
|
+
const storedAbs = import_node_path46.default.isAbsolute(e.relPath) ? import_node_path46.default.resolve(e.relPath) : import_node_path46.default.resolve(cwdAbs, e.relPath);
|
|
54603
55291
|
return storedAbs === candidateAbs && !e.stale;
|
|
54604
55292
|
});
|
|
54605
55293
|
if (!entry) {
|
|
@@ -54624,7 +55312,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
54624
55312
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
54625
55313
|
const f = deps.sessionStore.read(sessionId);
|
|
54626
55314
|
if (!f) return;
|
|
54627
|
-
assertGuestAttachmentPath(ctx,
|
|
55315
|
+
assertGuestAttachmentPath(ctx, import_node_path46.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
|
|
54628
55316
|
}
|
|
54629
55317
|
const groupAdd = async (frame, _client, ctx) => {
|
|
54630
55318
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -54639,8 +55327,8 @@ function buildAttachmentHandlers(deps) {
|
|
|
54639
55327
|
if (!scope) {
|
|
54640
55328
|
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
|
|
54641
55329
|
}
|
|
54642
|
-
const cwdAbs =
|
|
54643
|
-
const candidateAbs =
|
|
55330
|
+
const cwdAbs = import_node_path46.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
|
|
55331
|
+
const candidateAbs = import_node_path46.default.isAbsolute(args.relPath) ? import_node_path46.default.resolve(args.relPath) : import_node_path46.default.resolve(cwdAbs, args.relPath);
|
|
54644
55332
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
|
|
54645
55333
|
const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
|
|
54646
55334
|
const size = 0;
|
|
@@ -54699,19 +55387,19 @@ function buildAttachmentHandlers(deps) {
|
|
|
54699
55387
|
|
|
54700
55388
|
// src/handlers/extension.ts
|
|
54701
55389
|
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
54702
|
-
var
|
|
55390
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
54703
55391
|
init_protocol();
|
|
54704
55392
|
|
|
54705
55393
|
// src/extension/bundle-zip.ts
|
|
54706
55394
|
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
54707
|
-
var
|
|
55395
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
54708
55396
|
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
54709
55397
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
54710
55398
|
async function bundleExtensionDir(dir) {
|
|
54711
55399
|
const entries = await listFilesSorted(dir);
|
|
54712
55400
|
const zip = new import_jszip2.default();
|
|
54713
55401
|
for (const rel of entries) {
|
|
54714
|
-
const abs =
|
|
55402
|
+
const abs = import_node_path47.default.join(dir, rel);
|
|
54715
55403
|
const content = await import_promises5.default.readFile(abs);
|
|
54716
55404
|
zip.file(rel, content, { date: FIXED_DATE });
|
|
54717
55405
|
}
|
|
@@ -54732,7 +55420,7 @@ async function listFilesSorted(rootDir) {
|
|
|
54732
55420
|
return out;
|
|
54733
55421
|
}
|
|
54734
55422
|
async function walk(absRoot, relPrefix, out) {
|
|
54735
|
-
const dirAbs =
|
|
55423
|
+
const dirAbs = import_node_path47.default.join(absRoot, relPrefix);
|
|
54736
55424
|
const entries = await import_promises5.default.readdir(dirAbs, { withFileTypes: true });
|
|
54737
55425
|
for (const e of entries) {
|
|
54738
55426
|
if (IGNORE_BASENAMES.has(e.name)) continue;
|
|
@@ -54786,25 +55474,25 @@ function computePublishCheck(args) {
|
|
|
54786
55474
|
|
|
54787
55475
|
// src/extension/install-flow.ts
|
|
54788
55476
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
54789
|
-
var
|
|
55477
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
54790
55478
|
var import_node_os19 = __toESM(require("os"), 1);
|
|
54791
55479
|
var import_node_crypto15 = __toESM(require("crypto"), 1);
|
|
54792
55480
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
54793
55481
|
|
|
54794
55482
|
// src/extension/paths.ts
|
|
54795
55483
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
54796
|
-
var
|
|
55484
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
54797
55485
|
function clawdHomeRoot(override) {
|
|
54798
|
-
return override ?? process.env.CLAWD_HOME ??
|
|
55486
|
+
return override ?? process.env.CLAWD_HOME ?? import_node_path48.default.join(import_node_os18.default.homedir(), ".clawd");
|
|
54799
55487
|
}
|
|
54800
55488
|
function extensionsRoot(override) {
|
|
54801
|
-
return
|
|
55489
|
+
return import_node_path48.default.join(clawdHomeRoot(override), "extensions");
|
|
54802
55490
|
}
|
|
54803
55491
|
function publishedChannelsFile(override) {
|
|
54804
|
-
return
|
|
55492
|
+
return import_node_path48.default.join(clawdHomeRoot(override), "extensions-published.json");
|
|
54805
55493
|
}
|
|
54806
55494
|
function bundleCacheRoot(override) {
|
|
54807
|
-
return
|
|
55495
|
+
return import_node_path48.default.join(clawdHomeRoot(override), "extension-bundles");
|
|
54808
55496
|
}
|
|
54809
55497
|
|
|
54810
55498
|
// src/extension/install-flow.ts
|
|
@@ -54831,7 +55519,7 @@ async function installFromChannel(args, deps) {
|
|
|
54831
55519
|
throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
54832
55520
|
}
|
|
54833
55521
|
for (const name of Object.keys(zip.files)) {
|
|
54834
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
55522
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path49.default.isAbsolute(name)) {
|
|
54835
55523
|
throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
54836
55524
|
}
|
|
54837
55525
|
}
|
|
@@ -54863,7 +55551,7 @@ async function installFromChannel(args, deps) {
|
|
|
54863
55551
|
);
|
|
54864
55552
|
}
|
|
54865
55553
|
const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
|
|
54866
|
-
const destDir =
|
|
55554
|
+
const destDir = import_node_path49.default.join(deps.extensionsRoot, localExtId);
|
|
54867
55555
|
let destExists = false;
|
|
54868
55556
|
try {
|
|
54869
55557
|
await import_promises6.default.access(destDir);
|
|
@@ -54877,16 +55565,16 @@ async function installFromChannel(args, deps) {
|
|
|
54877
55565
|
);
|
|
54878
55566
|
}
|
|
54879
55567
|
const stage = await import_promises6.default.mkdtemp(
|
|
54880
|
-
|
|
55568
|
+
import_node_path49.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
|
|
54881
55569
|
);
|
|
54882
55570
|
try {
|
|
54883
55571
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
54884
|
-
const dest =
|
|
55572
|
+
const dest = import_node_path49.default.join(stage, name);
|
|
54885
55573
|
if (entry.dir) {
|
|
54886
55574
|
await import_promises6.default.mkdir(dest, { recursive: true });
|
|
54887
55575
|
continue;
|
|
54888
55576
|
}
|
|
54889
|
-
await import_promises6.default.mkdir(
|
|
55577
|
+
await import_promises6.default.mkdir(import_node_path49.default.dirname(dest), { recursive: true });
|
|
54890
55578
|
if (name === "manifest.json") {
|
|
54891
55579
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
54892
55580
|
await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -54907,7 +55595,7 @@ async function installFromChannel(args, deps) {
|
|
|
54907
55595
|
|
|
54908
55596
|
// src/extension/update-flow.ts
|
|
54909
55597
|
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
54910
|
-
var
|
|
55598
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
54911
55599
|
var import_node_os20 = __toESM(require("os"), 1);
|
|
54912
55600
|
var import_node_crypto16 = __toESM(require("crypto"), 1);
|
|
54913
55601
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
@@ -54924,11 +55612,11 @@ async function updateFromChannel(args, deps) {
|
|
|
54924
55612
|
channelRef.extId,
|
|
54925
55613
|
channelRef.ownerPrincipalId
|
|
54926
55614
|
);
|
|
54927
|
-
const liveDir =
|
|
55615
|
+
const liveDir = import_node_path50.default.join(deps.extensionsRoot, localExtId);
|
|
54928
55616
|
const prevDir = `${liveDir}.prev`;
|
|
54929
55617
|
let existingVersion;
|
|
54930
55618
|
try {
|
|
54931
|
-
const raw = await import_promises7.default.readFile(
|
|
55619
|
+
const raw = await import_promises7.default.readFile(import_node_path50.default.join(liveDir, "manifest.json"), "utf8");
|
|
54932
55620
|
const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
|
|
54933
55621
|
if (!parsed2.success) {
|
|
54934
55622
|
throw new UpdateError(
|
|
@@ -54961,7 +55649,7 @@ async function updateFromChannel(args, deps) {
|
|
|
54961
55649
|
throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
54962
55650
|
}
|
|
54963
55651
|
for (const name of Object.keys(zip.files)) {
|
|
54964
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
55652
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path50.default.isAbsolute(name)) {
|
|
54965
55653
|
throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
54966
55654
|
}
|
|
54967
55655
|
}
|
|
@@ -54996,16 +55684,16 @@ async function updateFromChannel(args, deps) {
|
|
|
54996
55684
|
await import_promises7.default.rm(prevDir, { recursive: true, force: true });
|
|
54997
55685
|
await import_promises7.default.rename(liveDir, prevDir);
|
|
54998
55686
|
const stage = await import_promises7.default.mkdtemp(
|
|
54999
|
-
|
|
55687
|
+
import_node_path50.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
|
|
55000
55688
|
);
|
|
55001
55689
|
try {
|
|
55002
55690
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
55003
|
-
const dest =
|
|
55691
|
+
const dest = import_node_path50.default.join(stage, name);
|
|
55004
55692
|
if (entry.dir) {
|
|
55005
55693
|
await import_promises7.default.mkdir(dest, { recursive: true });
|
|
55006
55694
|
continue;
|
|
55007
55695
|
}
|
|
55008
|
-
await import_promises7.default.mkdir(
|
|
55696
|
+
await import_promises7.default.mkdir(import_node_path50.default.dirname(dest), { recursive: true });
|
|
55009
55697
|
if (name === "manifest.json") {
|
|
55010
55698
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
55011
55699
|
await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -55098,7 +55786,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
55098
55786
|
);
|
|
55099
55787
|
}
|
|
55100
55788
|
}
|
|
55101
|
-
const manifestPath =
|
|
55789
|
+
const manifestPath = import_node_path51.default.join(root, extId, "manifest.json");
|
|
55102
55790
|
const manifest = await readManifest(root, extId);
|
|
55103
55791
|
const next = { ...manifest, version: newVersion };
|
|
55104
55792
|
const tmp = `${manifestPath}.tmp`;
|
|
@@ -55106,7 +55794,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
55106
55794
|
await import_promises8.default.rename(tmp, manifestPath);
|
|
55107
55795
|
}
|
|
55108
55796
|
async function readManifest(root, extId) {
|
|
55109
|
-
const file =
|
|
55797
|
+
const file = import_node_path51.default.join(root, extId, "manifest.json");
|
|
55110
55798
|
let raw;
|
|
55111
55799
|
try {
|
|
55112
55800
|
raw = await import_promises8.default.readFile(file, "utf8");
|
|
@@ -55197,7 +55885,7 @@ function buildExtensionHandlers(deps) {
|
|
|
55197
55885
|
};
|
|
55198
55886
|
async function buildSnapshotMeta(extId) {
|
|
55199
55887
|
const manifest = await readManifest(deps.root, extId);
|
|
55200
|
-
const { sha256, buffer } = await bundleExtensionDir(
|
|
55888
|
+
const { sha256, buffer } = await bundleExtensionDir(import_node_path51.default.join(deps.root, extId));
|
|
55201
55889
|
return { manifest, contentHash: sha256, buffer };
|
|
55202
55890
|
}
|
|
55203
55891
|
const publish = async (frame, _client, ctx) => {
|
|
@@ -55378,9 +56066,9 @@ function buildExtensionHandlers(deps) {
|
|
|
55378
56066
|
}
|
|
55379
56067
|
|
|
55380
56068
|
// src/app-builder/project-store.ts
|
|
55381
|
-
var
|
|
55382
|
-
var
|
|
55383
|
-
var
|
|
56069
|
+
var import_node_fs44 = require("fs");
|
|
56070
|
+
var import_node_child_process14 = require("child_process");
|
|
56071
|
+
var import_node_path52 = require("path");
|
|
55384
56072
|
init_protocol();
|
|
55385
56073
|
var PROJECTS_DIR = "projects";
|
|
55386
56074
|
var META_FILE = ".clawd-project.json";
|
|
@@ -55394,19 +56082,19 @@ var ProjectStore = class {
|
|
|
55394
56082
|
root;
|
|
55395
56083
|
/** projects/<name>/.clawd-project.json 路径 */
|
|
55396
56084
|
metaPath(name) {
|
|
55397
|
-
return (0,
|
|
56085
|
+
return (0, import_node_path52.join)(this.projectsRoot(), name, META_FILE);
|
|
55398
56086
|
}
|
|
55399
56087
|
/** projects/<name>/ 目录路径(cwd 用) */
|
|
55400
56088
|
projectDir(name) {
|
|
55401
|
-
return (0,
|
|
56089
|
+
return (0, import_node_path52.join)(this.projectsRoot(), name);
|
|
55402
56090
|
}
|
|
55403
56091
|
projectsRoot() {
|
|
55404
|
-
return (0,
|
|
56092
|
+
return (0, import_node_path52.join)(this.root, PROJECTS_DIR);
|
|
55405
56093
|
}
|
|
55406
56094
|
async list() {
|
|
55407
56095
|
let entries;
|
|
55408
56096
|
try {
|
|
55409
|
-
entries = await
|
|
56097
|
+
entries = await import_node_fs44.promises.readdir(this.projectsRoot());
|
|
55410
56098
|
} catch (err) {
|
|
55411
56099
|
if (err.code === "ENOENT") return [];
|
|
55412
56100
|
throw err;
|
|
@@ -55414,7 +56102,7 @@ var ProjectStore = class {
|
|
|
55414
56102
|
const out = [];
|
|
55415
56103
|
for (const name of entries) {
|
|
55416
56104
|
try {
|
|
55417
|
-
const raw = await
|
|
56105
|
+
const raw = await import_node_fs44.promises.readFile(this.metaPath(name), "utf8");
|
|
55418
56106
|
const json = JSON.parse(raw);
|
|
55419
56107
|
let migrated = false;
|
|
55420
56108
|
if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
|
|
@@ -55425,7 +56113,7 @@ var ProjectStore = class {
|
|
|
55425
56113
|
if (parsed.success) {
|
|
55426
56114
|
out.push(parsed.data);
|
|
55427
56115
|
if (migrated) {
|
|
55428
|
-
void
|
|
56116
|
+
void import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
|
|
55429
56117
|
});
|
|
55430
56118
|
}
|
|
55431
56119
|
}
|
|
@@ -55469,8 +56157,8 @@ var ProjectStore = class {
|
|
|
55469
56157
|
throw new Error(`invalid name "${name}": ${validated.error.message}`);
|
|
55470
56158
|
}
|
|
55471
56159
|
const dir = this.projectDir(name);
|
|
55472
|
-
await
|
|
55473
|
-
await
|
|
56160
|
+
await import_node_fs44.promises.mkdir(dir, { recursive: true });
|
|
56161
|
+
await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
|
|
55474
56162
|
return meta;
|
|
55475
56163
|
}
|
|
55476
56164
|
/**
|
|
@@ -55484,7 +56172,7 @@ var ProjectStore = class {
|
|
|
55484
56172
|
async scaffold(name, templateSrcDir, scaffoldScriptPath) {
|
|
55485
56173
|
const destDir = this.projectDir(name);
|
|
55486
56174
|
return await new Promise((resolve6, reject) => {
|
|
55487
|
-
const child = (0,
|
|
56175
|
+
const child = (0, import_node_child_process14.spawn)("bash", [scaffoldScriptPath, name, templateSrcDir, destDir], {
|
|
55488
56176
|
env: { ...process.env, PATH: process.env.PATH ?? "" },
|
|
55489
56177
|
stdio: ["ignore", "pipe", "pipe"]
|
|
55490
56178
|
});
|
|
@@ -55513,7 +56201,7 @@ var ProjectStore = class {
|
|
|
55513
56201
|
}
|
|
55514
56202
|
async delete(name) {
|
|
55515
56203
|
const dir = this.projectDir(name);
|
|
55516
|
-
await
|
|
56204
|
+
await import_node_fs44.promises.rm(dir, { recursive: true, force: true });
|
|
55517
56205
|
}
|
|
55518
56206
|
async updatePort(name, newPort) {
|
|
55519
56207
|
if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
|
|
@@ -55529,7 +56217,7 @@ var ProjectStore = class {
|
|
|
55529
56217
|
throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
|
|
55530
56218
|
}
|
|
55531
56219
|
const updated = { ...target, port: newPort };
|
|
55532
|
-
await
|
|
56220
|
+
await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
55533
56221
|
return updated;
|
|
55534
56222
|
}
|
|
55535
56223
|
/**
|
|
@@ -55546,7 +56234,7 @@ var ProjectStore = class {
|
|
|
55546
56234
|
if (!validated.success) {
|
|
55547
56235
|
throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
|
|
55548
56236
|
}
|
|
55549
|
-
await
|
|
56237
|
+
await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
55550
56238
|
return validated.data;
|
|
55551
56239
|
}
|
|
55552
56240
|
/**
|
|
@@ -55567,7 +56255,7 @@ var ProjectStore = class {
|
|
|
55567
56255
|
if (!validated.success) {
|
|
55568
56256
|
throw new Error(`invalid publishJob: ${validated.error.message}`);
|
|
55569
56257
|
}
|
|
55570
|
-
await
|
|
56258
|
+
await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
55571
56259
|
return validated.data;
|
|
55572
56260
|
}
|
|
55573
56261
|
/** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
|
|
@@ -55582,13 +56270,13 @@ var ProjectStore = class {
|
|
|
55582
56270
|
if (!validated.success) {
|
|
55583
56271
|
throw new Error(`failed to clear publishJob: ${validated.error.message}`);
|
|
55584
56272
|
}
|
|
55585
|
-
await
|
|
56273
|
+
await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
55586
56274
|
return validated.data;
|
|
55587
56275
|
}
|
|
55588
56276
|
};
|
|
55589
56277
|
|
|
55590
56278
|
// src/app-builder/kill-port.ts
|
|
55591
|
-
var
|
|
56279
|
+
var import_node_child_process15 = require("child_process");
|
|
55592
56280
|
async function killPortOccupants(port, ownedPids, logger) {
|
|
55593
56281
|
let pids;
|
|
55594
56282
|
try {
|
|
@@ -55630,7 +56318,7 @@ async function killPortOccupants(port, ownedPids, logger) {
|
|
|
55630
56318
|
}
|
|
55631
56319
|
function listPidsOnPort(port) {
|
|
55632
56320
|
return new Promise((resolve6, reject) => {
|
|
55633
|
-
(0,
|
|
56321
|
+
(0, import_node_child_process15.execFile)(
|
|
55634
56322
|
"lsof",
|
|
55635
56323
|
["-ti", `:${port}`],
|
|
55636
56324
|
{ timeout: 3e3 },
|
|
@@ -55702,9 +56390,9 @@ var PublishJobRegistry = class {
|
|
|
55702
56390
|
};
|
|
55703
56391
|
|
|
55704
56392
|
// src/app-builder/publish-job-runner.ts
|
|
55705
|
-
var
|
|
55706
|
-
var
|
|
55707
|
-
var
|
|
56393
|
+
var import_node_child_process16 = require("child_process");
|
|
56394
|
+
var import_node_fs45 = require("fs");
|
|
56395
|
+
var import_node_path53 = require("path");
|
|
55708
56396
|
|
|
55709
56397
|
// src/app-builder/publish-stage-parser.ts
|
|
55710
56398
|
var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
|
|
@@ -55731,19 +56419,19 @@ function tailStderrLines(buf, n) {
|
|
|
55731
56419
|
// src/app-builder/publish-job-runner.ts
|
|
55732
56420
|
async function startPublishJob(deps, args) {
|
|
55733
56421
|
const { registry: registry2, projectDir } = deps;
|
|
55734
|
-
const
|
|
56422
|
+
const spawn15 = deps.spawnImpl ?? import_node_child_process16.spawn;
|
|
55735
56423
|
if (registry2.has(args.name)) {
|
|
55736
56424
|
return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
|
|
55737
56425
|
}
|
|
55738
56426
|
const projDir = projectDir(args.name);
|
|
55739
|
-
const logPath = (0,
|
|
56427
|
+
const logPath = (0, import_node_path53.join)(projDir, ".publish.log");
|
|
55740
56428
|
let logStream = null;
|
|
55741
56429
|
try {
|
|
55742
|
-
logStream = (0,
|
|
56430
|
+
logStream = (0, import_node_fs45.createWriteStream)(logPath, { flags: "w" });
|
|
55743
56431
|
} catch {
|
|
55744
56432
|
logStream = null;
|
|
55745
56433
|
}
|
|
55746
|
-
const child =
|
|
56434
|
+
const child = spawn15("bash", [args.scriptPath, projDir, args.personaRoot ?? ""], {
|
|
55747
56435
|
cwd: projDir,
|
|
55748
56436
|
env: process.env,
|
|
55749
56437
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -55996,8 +56684,8 @@ async function recoverInterruptedJobs(deps) {
|
|
|
55996
56684
|
|
|
55997
56685
|
// src/handlers/app-builder.ts
|
|
55998
56686
|
init_protocol();
|
|
55999
|
-
var
|
|
56000
|
-
var
|
|
56687
|
+
var import_node_path54 = require("path");
|
|
56688
|
+
var import_node_fs46 = require("fs");
|
|
56001
56689
|
var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
|
|
56002
56690
|
var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
|
|
56003
56691
|
async function recoverInterruptedPublishJobs(store, logger) {
|
|
@@ -56078,7 +56766,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
56078
56766
|
async function listAllUsersProjects() {
|
|
56079
56767
|
if (!deps.usersRoot || !deps.getStore) return [];
|
|
56080
56768
|
const getStore = deps.getStore;
|
|
56081
|
-
const userIds = await
|
|
56769
|
+
const userIds = await import_node_fs46.promises.readdir(deps.usersRoot).catch(() => []);
|
|
56082
56770
|
const perUser = await Promise.all(
|
|
56083
56771
|
userIds.map((uid) => getStore(uid).list().catch(() => []))
|
|
56084
56772
|
);
|
|
@@ -56154,8 +56842,8 @@ function buildAppBuilderHandlers(deps) {
|
|
|
56154
56842
|
const project = await userStore.create(f.name, reservedPorts);
|
|
56155
56843
|
try {
|
|
56156
56844
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
|
|
56157
|
-
const templateSrcDir = (0,
|
|
56158
|
-
const scaffoldScript = (0,
|
|
56845
|
+
const templateSrcDir = (0, import_node_path54.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
|
|
56846
|
+
const scaffoldScript = (0, import_node_path54.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
|
|
56159
56847
|
const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
|
|
56160
56848
|
deps.logger?.info("app-builder.scaffold.done", {
|
|
56161
56849
|
name: project.name,
|
|
@@ -56376,7 +57064,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
56376
57064
|
await userStore.clearPublishJob(args.name);
|
|
56377
57065
|
}
|
|
56378
57066
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
|
|
56379
|
-
const scriptPath = (0,
|
|
57067
|
+
const scriptPath = (0, import_node_path54.join)(deps.deployKitRoot, "scripts", "publish.sh");
|
|
56380
57068
|
deps.logger?.info("app-builder.publish.start", {
|
|
56381
57069
|
name: args.name,
|
|
56382
57070
|
sessionId: boundSession.sessionId,
|
|
@@ -56520,7 +57208,7 @@ function buildShiftHandlers(deps) {
|
|
|
56520
57208
|
|
|
56521
57209
|
// src/handlers/visitor.ts
|
|
56522
57210
|
init_protocol();
|
|
56523
|
-
function
|
|
57211
|
+
function ensureOwner4(ctx) {
|
|
56524
57212
|
if (!ctx || ctx.principal.kind !== "owner") {
|
|
56525
57213
|
throw new ClawdError(
|
|
56526
57214
|
ERROR_CODES.UNAUTHORIZED,
|
|
@@ -56530,7 +57218,7 @@ function ensureOwner3(ctx) {
|
|
|
56530
57218
|
}
|
|
56531
57219
|
function buildVisitorHandlers(deps) {
|
|
56532
57220
|
const list = async (_frame, _client, ctx) => {
|
|
56533
|
-
|
|
57221
|
+
ensureOwner4(ctx);
|
|
56534
57222
|
return {
|
|
56535
57223
|
response: {
|
|
56536
57224
|
type: "visitor:list",
|
|
@@ -56545,7 +57233,7 @@ function buildVisitorHandlers(deps) {
|
|
|
56545
57233
|
|
|
56546
57234
|
// src/extension/registry.ts
|
|
56547
57235
|
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
56548
|
-
var
|
|
57236
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
56549
57237
|
async function loadAll(root) {
|
|
56550
57238
|
let entries;
|
|
56551
57239
|
try {
|
|
@@ -56558,13 +57246,13 @@ async function loadAll(root) {
|
|
|
56558
57246
|
for (const ent of entries) {
|
|
56559
57247
|
if (!ent.isDirectory()) continue;
|
|
56560
57248
|
if (ent.name.startsWith(".")) continue;
|
|
56561
|
-
records.push(await loadOne(
|
|
57249
|
+
records.push(await loadOne(import_node_path55.default.join(root, ent.name), ent.name));
|
|
56562
57250
|
}
|
|
56563
57251
|
records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
|
|
56564
57252
|
return records;
|
|
56565
57253
|
}
|
|
56566
57254
|
async function loadOne(dir, dirName) {
|
|
56567
|
-
const manifestPath =
|
|
57255
|
+
const manifestPath = import_node_path55.default.join(dir, "manifest.json");
|
|
56568
57256
|
let raw;
|
|
56569
57257
|
try {
|
|
56570
57258
|
raw = await import_promises9.default.readFile(manifestPath, "utf8");
|
|
@@ -56609,7 +57297,7 @@ async function loadOne(dir, dirName) {
|
|
|
56609
57297
|
|
|
56610
57298
|
// src/extension/uninstall.ts
|
|
56611
57299
|
var import_promises10 = __toESM(require("fs/promises"), 1);
|
|
56612
|
-
var
|
|
57300
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
56613
57301
|
var UninstallError = class extends Error {
|
|
56614
57302
|
constructor(code, message) {
|
|
56615
57303
|
super(message);
|
|
@@ -56618,7 +57306,7 @@ var UninstallError = class extends Error {
|
|
|
56618
57306
|
code;
|
|
56619
57307
|
};
|
|
56620
57308
|
async function uninstall(deps) {
|
|
56621
|
-
const dir =
|
|
57309
|
+
const dir = import_node_path56.default.join(deps.root, deps.extId);
|
|
56622
57310
|
try {
|
|
56623
57311
|
await import_promises10.default.access(dir);
|
|
56624
57312
|
} catch {
|
|
@@ -56673,6 +57361,11 @@ function buildMethodHandlers(deps) {
|
|
|
56673
57361
|
broadcast: deps.broadcastToOwners,
|
|
56674
57362
|
now: () => Date.now()
|
|
56675
57363
|
}),
|
|
57364
|
+
...buildContactSshHandlers({
|
|
57365
|
+
store: deps.contactStore,
|
|
57366
|
+
broadcast: deps.broadcastToOwners,
|
|
57367
|
+
sshdDir: deps.sshdDir
|
|
57368
|
+
}),
|
|
56676
57369
|
whoami: buildWhoamiHandler({
|
|
56677
57370
|
ownerDisplayName: deps.ownerDisplayName,
|
|
56678
57371
|
ownerPrincipalId: deps.ownerPrincipalId,
|
|
@@ -56719,7 +57412,7 @@ function buildMethodHandlers(deps) {
|
|
|
56719
57412
|
}
|
|
56720
57413
|
|
|
56721
57414
|
// src/app-builder/dev-server-supervisor.ts
|
|
56722
|
-
var
|
|
57415
|
+
var import_node_child_process17 = require("child_process");
|
|
56723
57416
|
var import_node_events2 = require("events");
|
|
56724
57417
|
var DEFAULT_READY_PATTERN = /Local:\s+https?:\/\/|Nest application successfully started|server listening on/i;
|
|
56725
57418
|
var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
@@ -56756,7 +57449,7 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
|
56756
57449
|
tunnelHost: args.tunnelHost,
|
|
56757
57450
|
devCommand: cmd
|
|
56758
57451
|
});
|
|
56759
|
-
const child = (0,
|
|
57452
|
+
const child = (0, import_node_child_process17.spawn)("sh", ["-c", cmd], {
|
|
56760
57453
|
cwd: args.cwd,
|
|
56761
57454
|
env,
|
|
56762
57455
|
stdio: "pipe",
|
|
@@ -57008,6 +57701,12 @@ var METHOD_GRANT_MAP = {
|
|
|
57008
57701
|
"contact:list": ADMIN_ANY,
|
|
57009
57702
|
"contact:pin": ADMIN_ANY,
|
|
57010
57703
|
"contact:remove": ADMIN_ANY,
|
|
57704
|
+
// contact:setSshAccess (owner UI 配 SSH 授权):ADMIN_ANY
|
|
57705
|
+
// contact:sshKey:issue (guest daemon 拉自己的 privkey):public — handler 内校
|
|
57706
|
+
// ctx.principal.kind==='guest' + store.get(callerId).sshAllowed
|
|
57707
|
+
// (对齐 inbox:postMessage 的"能连上=有 auth,业务在 handler 校"模式)
|
|
57708
|
+
"contact:setSshAccess": ADMIN_ANY,
|
|
57709
|
+
"contact:sshKey:issue": { kind: "public" },
|
|
57011
57710
|
// ---- visitor:* (访客名单,owner-only) ----
|
|
57012
57711
|
// owner 看完整访客名单(含没开会话的);guest 不可调(handler 内再 assertOwner 兜底)。
|
|
57013
57712
|
"visitor:list": ADMIN_ANY,
|
|
@@ -57188,8 +57887,8 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
|
|
|
57188
57887
|
}
|
|
57189
57888
|
|
|
57190
57889
|
// src/extension/runtime.ts
|
|
57191
|
-
var
|
|
57192
|
-
var
|
|
57890
|
+
var import_node_child_process18 = require("child_process");
|
|
57891
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
57193
57892
|
var import_promises11 = require("timers/promises");
|
|
57194
57893
|
|
|
57195
57894
|
// src/extension/port-allocator.ts
|
|
@@ -57290,13 +57989,13 @@ var Runtime = class {
|
|
|
57290
57989
|
/\$CLAWOS_EXT_PORT/g,
|
|
57291
57990
|
String(port)
|
|
57292
57991
|
);
|
|
57293
|
-
const dir =
|
|
57992
|
+
const dir = import_node_path57.default.join(this.root, extId);
|
|
57294
57993
|
const env = {
|
|
57295
57994
|
...process.env,
|
|
57296
57995
|
CLAWOS_EXT_PORT: String(port),
|
|
57297
57996
|
CLAWOS_EXT_ID: extId
|
|
57298
57997
|
};
|
|
57299
|
-
const child = (0,
|
|
57998
|
+
const child = (0, import_node_child_process18.spawn)("sh", ["-c", cmd], {
|
|
57300
57999
|
cwd: dir,
|
|
57301
58000
|
env,
|
|
57302
58001
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -57402,7 +58101,7 @@ ${handle.stderrTail}`
|
|
|
57402
58101
|
|
|
57403
58102
|
// src/extension/published-channels.ts
|
|
57404
58103
|
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
57405
|
-
var
|
|
58104
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
57406
58105
|
init_zod();
|
|
57407
58106
|
var PublishedChannelsError = class extends Error {
|
|
57408
58107
|
constructor(code, message) {
|
|
@@ -57501,7 +58200,7 @@ var PublishedChannelStore = class {
|
|
|
57501
58200
|
)
|
|
57502
58201
|
};
|
|
57503
58202
|
const tmp = `${this.filePath}.tmp`;
|
|
57504
|
-
await import_promises12.default.mkdir(
|
|
58203
|
+
await import_promises12.default.mkdir(import_node_path58.default.dirname(this.filePath), { recursive: true });
|
|
57505
58204
|
await import_promises12.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
57506
58205
|
await import_promises12.default.rename(tmp, this.filePath);
|
|
57507
58206
|
}
|
|
@@ -57509,7 +58208,7 @@ var PublishedChannelStore = class {
|
|
|
57509
58208
|
|
|
57510
58209
|
// src/extension/bundle-cache.ts
|
|
57511
58210
|
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
57512
|
-
var
|
|
58211
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
57513
58212
|
var BundleCache = class {
|
|
57514
58213
|
constructor(rootDir) {
|
|
57515
58214
|
this.rootDir = rootDir;
|
|
@@ -57518,14 +58217,14 @@ var BundleCache = class {
|
|
|
57518
58217
|
/** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
|
|
57519
58218
|
async write(snapshotHash, buffer) {
|
|
57520
58219
|
await import_promises13.default.mkdir(this.rootDir, { recursive: true });
|
|
57521
|
-
const file =
|
|
58220
|
+
const file = import_node_path59.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
57522
58221
|
const tmp = `${file}.tmp`;
|
|
57523
58222
|
await import_promises13.default.writeFile(tmp, buffer, { mode: 384 });
|
|
57524
58223
|
await import_promises13.default.rename(tmp, file);
|
|
57525
58224
|
}
|
|
57526
58225
|
/** Returns the bundle bytes, or null when the file doesn't exist. */
|
|
57527
58226
|
async read(snapshotHash) {
|
|
57528
|
-
const file =
|
|
58227
|
+
const file = import_node_path59.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
57529
58228
|
try {
|
|
57530
58229
|
return await import_promises13.default.readFile(file);
|
|
57531
58230
|
} catch (e) {
|
|
@@ -57535,7 +58234,7 @@ var BundleCache = class {
|
|
|
57535
58234
|
}
|
|
57536
58235
|
/** Idempotent — missing file is not an error. */
|
|
57537
58236
|
async delete(snapshotHash) {
|
|
57538
|
-
const file =
|
|
58237
|
+
const file = import_node_path59.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
57539
58238
|
await import_promises13.default.rm(file, { force: true });
|
|
57540
58239
|
}
|
|
57541
58240
|
};
|
|
@@ -57560,16 +58259,16 @@ async function startDaemon(config) {
|
|
|
57560
58259
|
});
|
|
57561
58260
|
const logger = createLogger({
|
|
57562
58261
|
level: config.logLevel,
|
|
57563
|
-
file:
|
|
58262
|
+
file: import_node_path60.default.join(config.dataDir, "clawd.log"),
|
|
57564
58263
|
logClient
|
|
57565
58264
|
});
|
|
57566
58265
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
57567
58266
|
const screenIdleProbeLogger = createFileOnlyLogger({
|
|
57568
|
-
file:
|
|
58267
|
+
file: import_node_path60.default.join(config.dataDir, "screen-idle-probe.log"),
|
|
57569
58268
|
level: "debug"
|
|
57570
58269
|
});
|
|
57571
58270
|
logger.info("screen-idle probe logger enabled", {
|
|
57572
|
-
file:
|
|
58271
|
+
file: import_node_path60.default.join(config.dataDir, "screen-idle-probe.log")
|
|
57573
58272
|
});
|
|
57574
58273
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
57575
58274
|
const pre = stateMgr.preflight();
|
|
@@ -57707,8 +58406,8 @@ async function startDaemon(config) {
|
|
|
57707
58406
|
const agents = new AgentsScanner();
|
|
57708
58407
|
const history = new ClaudeHistoryReader();
|
|
57709
58408
|
let transport = null;
|
|
57710
|
-
const personaStore = new PersonaStore(
|
|
57711
|
-
const usersRoot =
|
|
58409
|
+
const personaStore = new PersonaStore(import_node_path60.default.join(config.dataDir, "personas"));
|
|
58410
|
+
const usersRoot = import_node_path60.default.join(config.dataDir, "users");
|
|
57712
58411
|
const defaultsRoot = findDefaultsRoot(logger);
|
|
57713
58412
|
if (defaultsRoot) {
|
|
57714
58413
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -57728,17 +58427,17 @@ async function startDaemon(config) {
|
|
|
57728
58427
|
migrateCodexSandbox({ store: personaStore, logger });
|
|
57729
58428
|
const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
|
|
57730
58429
|
const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
|
|
57731
|
-
const here = typeof __dirname === "string" ? __dirname :
|
|
58430
|
+
const here = typeof __dirname === "string" ? __dirname : import_node_path60.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
|
|
57732
58431
|
const dispatchServerCandidates = [
|
|
57733
|
-
|
|
58432
|
+
import_node_path60.default.join(here, "dispatch", "mcp-server.cjs"),
|
|
57734
58433
|
// 生产 dist/index → dist/dispatch/mcp-server.cjs
|
|
57735
|
-
|
|
58434
|
+
import_node_path60.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
|
|
57736
58435
|
// dev tsx src/index → ../dist/dispatch/mcp-server.cjs
|
|
57737
58436
|
];
|
|
57738
|
-
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) =>
|
|
58437
|
+
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
|
|
57739
58438
|
let dispatchMcpConfigPath2;
|
|
57740
58439
|
if (dispatchServerScriptPath) {
|
|
57741
|
-
const dispatchLogPath =
|
|
58440
|
+
const dispatchLogPath = import_node_path60.default.join(config.dataDir, "dispatch-mcp-server.log");
|
|
57742
58441
|
dispatchMcpConfigPath2 = writeDispatchMcpConfig({
|
|
57743
58442
|
dataDir: config.dataDir,
|
|
57744
58443
|
serverScriptPath: dispatchServerScriptPath,
|
|
@@ -57755,15 +58454,15 @@ async function startDaemon(config) {
|
|
|
57755
58454
|
});
|
|
57756
58455
|
}
|
|
57757
58456
|
const ticketServerCandidates = [
|
|
57758
|
-
|
|
57759
|
-
|
|
58457
|
+
import_node_path60.default.join(here, "ticket", "mcp-server.cjs"),
|
|
58458
|
+
import_node_path60.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
|
|
57760
58459
|
];
|
|
57761
|
-
const ticketServerScriptPath = ticketServerCandidates.find((p2) =>
|
|
58460
|
+
const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
|
|
57762
58461
|
const ticketOwnerUnionId = feishuIdentity?.identity.unionId ?? "";
|
|
57763
58462
|
const ticketOwnerName = feishuIdentity?.identity.displayName ?? "";
|
|
57764
58463
|
let ticketMcpConfigPath2;
|
|
57765
58464
|
if (ticketServerScriptPath && ticketOwnerUnionId) {
|
|
57766
|
-
const ticketLogPath =
|
|
58465
|
+
const ticketLogPath = import_node_path60.default.join(config.dataDir, "ticket-mcp-server.log");
|
|
57767
58466
|
ticketMcpConfigPath2 = writeTicketMcpConfig({
|
|
57768
58467
|
dataDir: config.dataDir,
|
|
57769
58468
|
serverScriptPath: ticketServerScriptPath,
|
|
@@ -57784,13 +58483,13 @@ async function startDaemon(config) {
|
|
|
57784
58483
|
});
|
|
57785
58484
|
}
|
|
57786
58485
|
const shiftServerCandidates = [
|
|
57787
|
-
|
|
57788
|
-
|
|
58486
|
+
import_node_path60.default.join(here, "shift", "mcp-server.cjs"),
|
|
58487
|
+
import_node_path60.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
|
|
57789
58488
|
];
|
|
57790
|
-
const shiftServerScriptPath = shiftServerCandidates.find((p2) =>
|
|
58489
|
+
const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
|
|
57791
58490
|
let shiftMcpConfigPath2;
|
|
57792
58491
|
if (shiftServerScriptPath) {
|
|
57793
|
-
const shiftLogPath =
|
|
58492
|
+
const shiftLogPath = import_node_path60.default.join(config.dataDir, "shift-mcp-server.log");
|
|
57794
58493
|
shiftMcpConfigPath2 = await writeShiftMcpConfig({
|
|
57795
58494
|
dataDir: config.dataDir,
|
|
57796
58495
|
serverScriptPath: shiftServerScriptPath,
|
|
@@ -57808,13 +58507,13 @@ async function startDaemon(config) {
|
|
|
57808
58507
|
);
|
|
57809
58508
|
}
|
|
57810
58509
|
const inboxServerCandidates = [
|
|
57811
|
-
|
|
57812
|
-
|
|
58510
|
+
import_node_path60.default.join(here, "inbox", "mcp-server.cjs"),
|
|
58511
|
+
import_node_path60.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
|
|
57813
58512
|
];
|
|
57814
|
-
const inboxServerScriptPath = inboxServerCandidates.find((p2) =>
|
|
58513
|
+
const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
|
|
57815
58514
|
let inboxMcpConfigPath2;
|
|
57816
58515
|
if (inboxServerScriptPath) {
|
|
57817
|
-
const inboxLogPath =
|
|
58516
|
+
const inboxLogPath = import_node_path60.default.join(config.dataDir, "inbox-mcp-server.log");
|
|
57818
58517
|
inboxMcpConfigPath2 = await writeInboxMcpConfig({
|
|
57819
58518
|
dataDir: config.dataDir,
|
|
57820
58519
|
serverScriptPath: inboxServerScriptPath,
|
|
@@ -57832,7 +58531,7 @@ async function startDaemon(config) {
|
|
|
57832
58531
|
);
|
|
57833
58532
|
}
|
|
57834
58533
|
const shiftStore = createShiftStore({
|
|
57835
|
-
filePath:
|
|
58534
|
+
filePath: import_node_path60.default.join(config.dataDir, "shift.json"),
|
|
57836
58535
|
ownerIdProvider: () => ownerPrincipalId,
|
|
57837
58536
|
now: () => Date.now()
|
|
57838
58537
|
});
|
|
@@ -57854,7 +58553,7 @@ async function startDaemon(config) {
|
|
|
57854
58553
|
getAdapter,
|
|
57855
58554
|
historyReader: history,
|
|
57856
58555
|
dataDir: config.dataDir,
|
|
57857
|
-
personaRoot:
|
|
58556
|
+
personaRoot: import_node_path60.default.join(config.dataDir, "personas"),
|
|
57858
58557
|
usersRoot,
|
|
57859
58558
|
personaStore,
|
|
57860
58559
|
ownerDisplayName,
|
|
@@ -57897,10 +58596,10 @@ async function startDaemon(config) {
|
|
|
57897
58596
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
57898
58597
|
attachmentGroup: {
|
|
57899
58598
|
onFileEdit: (input) => {
|
|
57900
|
-
const absPath =
|
|
58599
|
+
const absPath = import_node_path60.default.isAbsolute(input.relPath) ? input.relPath : import_node_path60.default.join(input.cwd, input.relPath);
|
|
57901
58600
|
let size = 0;
|
|
57902
58601
|
try {
|
|
57903
|
-
size =
|
|
58602
|
+
size = import_node_fs47.default.statSync(absPath).size;
|
|
57904
58603
|
} catch (err) {
|
|
57905
58604
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
57906
58605
|
sessionId: input.sessionId,
|
|
@@ -58098,11 +58797,11 @@ async function startDaemon(config) {
|
|
|
58098
58797
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
58099
58798
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
58100
58799
|
// guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
|
|
58101
|
-
personaRoot:
|
|
58800
|
+
personaRoot: import_node_path60.default.join(config.dataDir, "personas"),
|
|
58102
58801
|
usersRoot
|
|
58103
58802
|
},
|
|
58104
58803
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
58105
|
-
personaRoot:
|
|
58804
|
+
personaRoot: import_node_path60.default.join(config.dataDir, "personas"),
|
|
58106
58805
|
// v2 多人 persona 隔离:handler 派生 guest user-dir 放行
|
|
58107
58806
|
usersRoot,
|
|
58108
58807
|
// capability:list / delete handler 依赖
|
|
@@ -58122,6 +58821,9 @@ async function startDaemon(config) {
|
|
|
58122
58821
|
inboxStore,
|
|
58123
58822
|
// 联系人列表 store(device:connect / 自动反向落同一 store)
|
|
58124
58823
|
contactStore,
|
|
58824
|
+
// <dataDir>/sshd 绝对路径 —— contact-ssh handlers 用它拼 authorized_keys / keys/ 子路径
|
|
58825
|
+
// Task 10 会加 SshdManager 起 sshd;handlers wire 提前挂 sshdDir 让 typecheck 过
|
|
58826
|
+
sshdDir: import_node_path60.default.join(config.dataDir, "sshd"),
|
|
58125
58827
|
// inbox:sendDm 用:sessionId → session 出身(复用 attachment 同款 findOwnedSessionScope)
|
|
58126
58828
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
58127
58829
|
// contact:removed broadcast;复用 capability:tokenIssued 同款通路
|
|
@@ -58211,11 +58913,11 @@ async function startDaemon(config) {
|
|
|
58211
58913
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2):
|
|
58212
58914
|
// appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
|
|
58213
58915
|
// dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
|
|
58214
|
-
appBuilderPersonaRoot:
|
|
58916
|
+
appBuilderPersonaRoot: import_node_path60.default.join(config.dataDir, "personas", "persona-app-builder"),
|
|
58215
58917
|
// 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
|
|
58216
|
-
deployKitRoot:
|
|
58918
|
+
deployKitRoot: import_node_path60.default.join(config.dataDir, "deploy-kit"),
|
|
58217
58919
|
// scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
|
|
58218
|
-
resolvePersonaRoot: (personaId) =>
|
|
58920
|
+
resolvePersonaRoot: (personaId) => import_node_path60.default.join(config.dataDir, "personas", personaId),
|
|
58219
58921
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
|
|
58220
58922
|
// 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
|
|
58221
58923
|
// 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
|
|
@@ -58258,7 +58960,7 @@ async function startDaemon(config) {
|
|
|
58258
58960
|
}
|
|
58259
58961
|
let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
|
|
58260
58962
|
if (sourceFile && sourceFile.toolSessionId) {
|
|
58261
|
-
sourceJsonlPath =
|
|
58963
|
+
sourceJsonlPath = import_node_path60.default.join(
|
|
58262
58964
|
import_node_os21.default.homedir(),
|
|
58263
58965
|
".claude",
|
|
58264
58966
|
"projects",
|
|
@@ -58558,8 +59260,8 @@ async function startDaemon(config) {
|
|
|
58558
59260
|
const lines = [
|
|
58559
59261
|
`Tunnel: ${r.url}`,
|
|
58560
59262
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
58561
|
-
`Frpc config: ${
|
|
58562
|
-
`Frpc log: ${
|
|
59263
|
+
`Frpc config: ${import_node_path60.default.join(config.dataDir, "frpc.toml")}`,
|
|
59264
|
+
`Frpc log: ${import_node_path60.default.join(config.dataDir, "frpc.log")}`
|
|
58563
59265
|
];
|
|
58564
59266
|
const width = Math.max(...lines.map((l) => l.length));
|
|
58565
59267
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -58572,8 +59274,8 @@ ${bar}
|
|
|
58572
59274
|
|
|
58573
59275
|
`);
|
|
58574
59276
|
try {
|
|
58575
|
-
const connectPath =
|
|
58576
|
-
|
|
59277
|
+
const connectPath = import_node_path60.default.join(config.dataDir, "connect.txt");
|
|
59278
|
+
import_node_fs47.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
58577
59279
|
} catch {
|
|
58578
59280
|
}
|
|
58579
59281
|
} catch (err) {
|
|
@@ -58598,6 +59300,22 @@ ${bar}
|
|
|
58598
59300
|
logger.warn("tunnel unavailable, degraded to local mode", { reason: tunnelError });
|
|
58599
59301
|
}
|
|
58600
59302
|
}
|
|
59303
|
+
const sshdMgr = new SshdManager({
|
|
59304
|
+
dataDir: config.dataDir,
|
|
59305
|
+
port: config.sshdPort,
|
|
59306
|
+
logger,
|
|
59307
|
+
installProcessExitHandlers: true,
|
|
59308
|
+
onSshdExit: (info) => logger.warn("sshd exited unexpectedly", info)
|
|
59309
|
+
});
|
|
59310
|
+
try {
|
|
59311
|
+
await sshdMgr.start();
|
|
59312
|
+
rebuildAuthorizedKeys(contactStore, import_node_path60.default.join(config.dataDir, "sshd"));
|
|
59313
|
+
logger.info("sshd: contact-ssh sandbox ready", { port: config.sshdPort });
|
|
59314
|
+
} catch (err) {
|
|
59315
|
+
logger.warn("sshd start failed; contact SSH grant will not work until fixed", {
|
|
59316
|
+
err: err.message
|
|
59317
|
+
});
|
|
59318
|
+
}
|
|
58601
59319
|
void reportDevice();
|
|
58602
59320
|
void fetchServerKey();
|
|
58603
59321
|
const tickAttachmentGc = () => {
|
|
@@ -58632,6 +59350,11 @@ ${bar}
|
|
|
58632
59350
|
if (tunnelMgr) {
|
|
58633
59351
|
await tunnelMgr.stop();
|
|
58634
59352
|
}
|
|
59353
|
+
await sshdMgr.stop().catch((err) => {
|
|
59354
|
+
logger.warn("shutdown.sshd-stop-failed", {
|
|
59355
|
+
error: err instanceof Error ? err.message : String(err)
|
|
59356
|
+
});
|
|
59357
|
+
});
|
|
58635
59358
|
await wss.stop();
|
|
58636
59359
|
stateMgr.delete();
|
|
58637
59360
|
if (logClient) await logClient.dispose();
|
|
@@ -58645,9 +59368,9 @@ ${bar}
|
|
|
58645
59368
|
};
|
|
58646
59369
|
}
|
|
58647
59370
|
function migrateDropPersonsDir(dataDir) {
|
|
58648
|
-
const dir =
|
|
59371
|
+
const dir = import_node_path60.default.join(dataDir, "persons");
|
|
58649
59372
|
try {
|
|
58650
|
-
|
|
59373
|
+
import_node_fs47.default.rmSync(dir, { recursive: true, force: true });
|
|
58651
59374
|
} catch {
|
|
58652
59375
|
}
|
|
58653
59376
|
}
|