@clawos-dev/clawd 0.2.199-beta.400.ba99f40 → 0.2.199
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 +575 -1143
- package/dist/share-md-viewer-error.html +1 -1
- package/dist/share-md-viewer.html +1 -1
- package/dist/share-ui/assets/{guest-B1dInTij.js → guest-BtMmEdS6.js} +27 -27
- package/dist/share-ui/assets/guest-CCfI4zdC.css +32 -0
- package/dist/share-ui/guest.html +2 -2
- package/package.json +1 -1
- package/dist/share-ui/assets/guest-B5ChN9T3.css +0 -32
package/dist/cli.cjs
CHANGED
|
@@ -158,11 +158,6 @@ 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",
|
|
166
161
|
// ---- visitor:* (web 访客 · persona web 分享,spec 2026-06-24-persona-web-share-design) ----
|
|
167
162
|
// owner-only:列出登录访问过本机 public persona 的 web 访客(零安装、飞书登录、daemon 自签
|
|
168
163
|
// visitor token)。存储 ~/.clawd/visitors.json(VisitorStore),登录(exchange)即 upsert;
|
|
@@ -742,8 +737,8 @@ var init_parseUtil = __esm({
|
|
|
742
737
|
init_errors2();
|
|
743
738
|
init_en();
|
|
744
739
|
makeIssue = (params) => {
|
|
745
|
-
const { data, path:
|
|
746
|
-
const fullPath = [...
|
|
740
|
+
const { data, path: path68, errorMaps, issueData } = params;
|
|
741
|
+
const fullPath = [...path68, ...issueData.path || []];
|
|
747
742
|
const fullIssue = {
|
|
748
743
|
...issueData,
|
|
749
744
|
path: fullPath
|
|
@@ -1054,11 +1049,11 @@ var init_types = __esm({
|
|
|
1054
1049
|
init_parseUtil();
|
|
1055
1050
|
init_util();
|
|
1056
1051
|
ParseInputLazyPath = class {
|
|
1057
|
-
constructor(parent, value,
|
|
1052
|
+
constructor(parent, value, path68, key) {
|
|
1058
1053
|
this._cachedPath = [];
|
|
1059
1054
|
this.parent = parent;
|
|
1060
1055
|
this.data = value;
|
|
1061
|
-
this._path =
|
|
1056
|
+
this._path = path68;
|
|
1062
1057
|
this._key = key;
|
|
1063
1058
|
}
|
|
1064
1059
|
get path() {
|
|
@@ -5683,19 +5678,7 @@ var init_contact = __esm({
|
|
|
5683
5678
|
* 老 contacts.json 缺此字段 → zod default 补 null(无破坏性升级;不 orphan)。
|
|
5684
5679
|
* 对齐 SessionFile.pinnedAt 语义(daemon 侧持久化,contact:pin RPC 更新 → contact:pinned push)。
|
|
5685
5680
|
*/
|
|
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([])
|
|
5681
|
+
pinnedAt: external_exports.number().int().nullable().default(null)
|
|
5699
5682
|
}).strict();
|
|
5700
5683
|
ContactWireSchema = ContactSchema;
|
|
5701
5684
|
ContactRemoveArgsSchema = external_exports.object({
|
|
@@ -5734,40 +5717,6 @@ var init_contact = __esm({
|
|
|
5734
5717
|
}
|
|
5735
5718
|
});
|
|
5736
5719
|
|
|
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
|
-
|
|
5771
5720
|
// ../protocol/src/extension.ts
|
|
5772
5721
|
function isAllowedHostedUrl(raw) {
|
|
5773
5722
|
let u;
|
|
@@ -6169,7 +6118,6 @@ var init_runtime = __esm({
|
|
|
6169
6118
|
init_capability();
|
|
6170
6119
|
init_inbox();
|
|
6171
6120
|
init_contact();
|
|
6172
|
-
init_contact_ssh();
|
|
6173
6121
|
init_extension();
|
|
6174
6122
|
init_feishu_auth();
|
|
6175
6123
|
init_dispatch();
|
|
@@ -6449,8 +6397,8 @@ var require_req = __commonJS({
|
|
|
6449
6397
|
if (req.originalUrl) {
|
|
6450
6398
|
_req.url = req.originalUrl;
|
|
6451
6399
|
} else {
|
|
6452
|
-
const
|
|
6453
|
-
_req.url = typeof
|
|
6400
|
+
const path68 = req.path;
|
|
6401
|
+
_req.url = typeof path68 === "string" ? path68 : req.url ? req.url.path || req.url : void 0;
|
|
6454
6402
|
}
|
|
6455
6403
|
if (req.query) {
|
|
6456
6404
|
_req.query = req.query;
|
|
@@ -6615,14 +6563,14 @@ var require_redact = __commonJS({
|
|
|
6615
6563
|
}
|
|
6616
6564
|
return obj;
|
|
6617
6565
|
}
|
|
6618
|
-
function parsePath(
|
|
6566
|
+
function parsePath(path68) {
|
|
6619
6567
|
const parts = [];
|
|
6620
6568
|
let current = "";
|
|
6621
6569
|
let inBrackets = false;
|
|
6622
6570
|
let inQuotes = false;
|
|
6623
6571
|
let quoteChar = "";
|
|
6624
|
-
for (let i = 0; i <
|
|
6625
|
-
const char =
|
|
6572
|
+
for (let i = 0; i < path68.length; i++) {
|
|
6573
|
+
const char = path68[i];
|
|
6626
6574
|
if (!inBrackets && char === ".") {
|
|
6627
6575
|
if (current) {
|
|
6628
6576
|
parts.push(current);
|
|
@@ -6753,10 +6701,10 @@ var require_redact = __commonJS({
|
|
|
6753
6701
|
return current;
|
|
6754
6702
|
}
|
|
6755
6703
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
6756
|
-
for (const
|
|
6757
|
-
const parts = parsePath(
|
|
6704
|
+
for (const path68 of paths) {
|
|
6705
|
+
const parts = parsePath(path68);
|
|
6758
6706
|
if (parts.includes("*")) {
|
|
6759
|
-
redactWildcardPath(obj, parts, censor,
|
|
6707
|
+
redactWildcardPath(obj, parts, censor, path68, remove);
|
|
6760
6708
|
} else {
|
|
6761
6709
|
if (remove) {
|
|
6762
6710
|
removeKey(obj, parts);
|
|
@@ -6841,8 +6789,8 @@ var require_redact = __commonJS({
|
|
|
6841
6789
|
}
|
|
6842
6790
|
} else {
|
|
6843
6791
|
if (afterWildcard.includes("*")) {
|
|
6844
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6845
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
6792
|
+
const wrappedCensor = typeof censor === "function" ? (value, path68) => {
|
|
6793
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path68];
|
|
6846
6794
|
return censor(value, fullPath);
|
|
6847
6795
|
} : censor;
|
|
6848
6796
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -6877,8 +6825,8 @@ var require_redact = __commonJS({
|
|
|
6877
6825
|
return null;
|
|
6878
6826
|
}
|
|
6879
6827
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
6880
|
-
for (const
|
|
6881
|
-
const parts = parsePath(
|
|
6828
|
+
for (const path68 of pathsToClone) {
|
|
6829
|
+
const parts = parsePath(path68);
|
|
6882
6830
|
let current = pathStructure;
|
|
6883
6831
|
for (let i = 0; i < parts.length; i++) {
|
|
6884
6832
|
const part = parts[i];
|
|
@@ -6930,24 +6878,24 @@ var require_redact = __commonJS({
|
|
|
6930
6878
|
}
|
|
6931
6879
|
return cloneSelectively(obj, pathStructure);
|
|
6932
6880
|
}
|
|
6933
|
-
function validatePath(
|
|
6934
|
-
if (typeof
|
|
6881
|
+
function validatePath(path68) {
|
|
6882
|
+
if (typeof path68 !== "string") {
|
|
6935
6883
|
throw new Error("Paths must be (non-empty) strings");
|
|
6936
6884
|
}
|
|
6937
|
-
if (
|
|
6885
|
+
if (path68 === "") {
|
|
6938
6886
|
throw new Error("Invalid redaction path ()");
|
|
6939
6887
|
}
|
|
6940
|
-
if (
|
|
6941
|
-
throw new Error(`Invalid redaction path (${
|
|
6888
|
+
if (path68.includes("..")) {
|
|
6889
|
+
throw new Error(`Invalid redaction path (${path68})`);
|
|
6942
6890
|
}
|
|
6943
|
-
if (
|
|
6944
|
-
throw new Error(`Invalid redaction path (${
|
|
6891
|
+
if (path68.includes(",")) {
|
|
6892
|
+
throw new Error(`Invalid redaction path (${path68})`);
|
|
6945
6893
|
}
|
|
6946
6894
|
let bracketCount = 0;
|
|
6947
6895
|
let inQuotes = false;
|
|
6948
6896
|
let quoteChar = "";
|
|
6949
|
-
for (let i = 0; i <
|
|
6950
|
-
const char =
|
|
6897
|
+
for (let i = 0; i < path68.length; i++) {
|
|
6898
|
+
const char = path68[i];
|
|
6951
6899
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
6952
6900
|
if (!inQuotes) {
|
|
6953
6901
|
inQuotes = true;
|
|
@@ -6961,20 +6909,20 @@ var require_redact = __commonJS({
|
|
|
6961
6909
|
} else if (char === "]" && !inQuotes) {
|
|
6962
6910
|
bracketCount--;
|
|
6963
6911
|
if (bracketCount < 0) {
|
|
6964
|
-
throw new Error(`Invalid redaction path (${
|
|
6912
|
+
throw new Error(`Invalid redaction path (${path68})`);
|
|
6965
6913
|
}
|
|
6966
6914
|
}
|
|
6967
6915
|
}
|
|
6968
6916
|
if (bracketCount !== 0) {
|
|
6969
|
-
throw new Error(`Invalid redaction path (${
|
|
6917
|
+
throw new Error(`Invalid redaction path (${path68})`);
|
|
6970
6918
|
}
|
|
6971
6919
|
}
|
|
6972
6920
|
function validatePaths(paths) {
|
|
6973
6921
|
if (!Array.isArray(paths)) {
|
|
6974
6922
|
throw new TypeError("paths must be an array");
|
|
6975
6923
|
}
|
|
6976
|
-
for (const
|
|
6977
|
-
validatePath(
|
|
6924
|
+
for (const path68 of paths) {
|
|
6925
|
+
validatePath(path68);
|
|
6978
6926
|
}
|
|
6979
6927
|
}
|
|
6980
6928
|
function slowRedact(options = {}) {
|
|
@@ -7142,8 +7090,8 @@ var require_redaction = __commonJS({
|
|
|
7142
7090
|
if (shape[k2] === null) {
|
|
7143
7091
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
7144
7092
|
} else {
|
|
7145
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
7146
|
-
return censor(value, [k2, ...
|
|
7093
|
+
const wrappedCensor = typeof censor === "function" ? (value, path68) => {
|
|
7094
|
+
return censor(value, [k2, ...path68]);
|
|
7147
7095
|
} : censor;
|
|
7148
7096
|
o[k2] = Redact({
|
|
7149
7097
|
paths: shape[k2],
|
|
@@ -7361,10 +7309,10 @@ var require_atomic_sleep = __commonJS({
|
|
|
7361
7309
|
var require_sonic_boom = __commonJS({
|
|
7362
7310
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
7363
7311
|
"use strict";
|
|
7364
|
-
var
|
|
7312
|
+
var fs61 = require("fs");
|
|
7365
7313
|
var EventEmitter3 = require("events");
|
|
7366
7314
|
var inherits = require("util").inherits;
|
|
7367
|
-
var
|
|
7315
|
+
var path68 = require("path");
|
|
7368
7316
|
var sleep2 = require_atomic_sleep();
|
|
7369
7317
|
var assert = require("assert");
|
|
7370
7318
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -7418,20 +7366,20 @@ var require_sonic_boom = __commonJS({
|
|
|
7418
7366
|
const mode = sonic.mode;
|
|
7419
7367
|
if (sonic.sync) {
|
|
7420
7368
|
try {
|
|
7421
|
-
if (sonic.mkdir)
|
|
7422
|
-
const fd =
|
|
7369
|
+
if (sonic.mkdir) fs61.mkdirSync(path68.dirname(file), { recursive: true });
|
|
7370
|
+
const fd = fs61.openSync(file, flags, mode);
|
|
7423
7371
|
fileOpened(null, fd);
|
|
7424
7372
|
} catch (err) {
|
|
7425
7373
|
fileOpened(err);
|
|
7426
7374
|
throw err;
|
|
7427
7375
|
}
|
|
7428
7376
|
} else if (sonic.mkdir) {
|
|
7429
|
-
|
|
7377
|
+
fs61.mkdir(path68.dirname(file), { recursive: true }, (err) => {
|
|
7430
7378
|
if (err) return fileOpened(err);
|
|
7431
|
-
|
|
7379
|
+
fs61.open(file, flags, mode, fileOpened);
|
|
7432
7380
|
});
|
|
7433
7381
|
} else {
|
|
7434
|
-
|
|
7382
|
+
fs61.open(file, flags, mode, fileOpened);
|
|
7435
7383
|
}
|
|
7436
7384
|
}
|
|
7437
7385
|
function SonicBoom(opts) {
|
|
@@ -7472,8 +7420,8 @@ var require_sonic_boom = __commonJS({
|
|
|
7472
7420
|
this.flush = flushBuffer;
|
|
7473
7421
|
this.flushSync = flushBufferSync;
|
|
7474
7422
|
this._actualWrite = actualWriteBuffer;
|
|
7475
|
-
fsWriteSync = () =>
|
|
7476
|
-
fsWrite = () =>
|
|
7423
|
+
fsWriteSync = () => fs61.writeSync(this.fd, this._writingBuf);
|
|
7424
|
+
fsWrite = () => fs61.write(this.fd, this._writingBuf, this.release);
|
|
7477
7425
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
7478
7426
|
this._writingBuf = "";
|
|
7479
7427
|
this.write = write;
|
|
@@ -7482,15 +7430,15 @@ var require_sonic_boom = __commonJS({
|
|
|
7482
7430
|
this._actualWrite = actualWrite;
|
|
7483
7431
|
fsWriteSync = () => {
|
|
7484
7432
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7485
|
-
return
|
|
7433
|
+
return fs61.writeSync(this.fd, this._writingBuf);
|
|
7486
7434
|
}
|
|
7487
|
-
return
|
|
7435
|
+
return fs61.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7488
7436
|
};
|
|
7489
7437
|
fsWrite = () => {
|
|
7490
7438
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7491
|
-
return
|
|
7439
|
+
return fs61.write(this.fd, this._writingBuf, this.release);
|
|
7492
7440
|
}
|
|
7493
|
-
return
|
|
7441
|
+
return fs61.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
7494
7442
|
};
|
|
7495
7443
|
} else {
|
|
7496
7444
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -7547,7 +7495,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7547
7495
|
}
|
|
7548
7496
|
}
|
|
7549
7497
|
if (this._fsync) {
|
|
7550
|
-
|
|
7498
|
+
fs61.fsyncSync(this.fd);
|
|
7551
7499
|
}
|
|
7552
7500
|
const len = this._len;
|
|
7553
7501
|
if (this._reopening) {
|
|
@@ -7661,7 +7609,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7661
7609
|
const onDrain = () => {
|
|
7662
7610
|
if (!this._fsync) {
|
|
7663
7611
|
try {
|
|
7664
|
-
|
|
7612
|
+
fs61.fsync(this.fd, (err) => {
|
|
7665
7613
|
this._flushPending = false;
|
|
7666
7614
|
cb(err);
|
|
7667
7615
|
});
|
|
@@ -7763,7 +7711,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7763
7711
|
const fd = this.fd;
|
|
7764
7712
|
this.once("ready", () => {
|
|
7765
7713
|
if (fd !== this.fd) {
|
|
7766
|
-
|
|
7714
|
+
fs61.close(fd, (err) => {
|
|
7767
7715
|
if (err) {
|
|
7768
7716
|
return this.emit("error", err);
|
|
7769
7717
|
}
|
|
@@ -7812,7 +7760,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7812
7760
|
buf = this._bufs[0];
|
|
7813
7761
|
}
|
|
7814
7762
|
try {
|
|
7815
|
-
const n = Buffer.isBuffer(buf) ?
|
|
7763
|
+
const n = Buffer.isBuffer(buf) ? fs61.writeSync(this.fd, buf) : fs61.writeSync(this.fd, buf, "utf8");
|
|
7816
7764
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
7817
7765
|
buf = releasedBufObj.writingBuf;
|
|
7818
7766
|
this._len = releasedBufObj.len;
|
|
@@ -7828,7 +7776,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7828
7776
|
}
|
|
7829
7777
|
}
|
|
7830
7778
|
try {
|
|
7831
|
-
|
|
7779
|
+
fs61.fsyncSync(this.fd);
|
|
7832
7780
|
} catch {
|
|
7833
7781
|
}
|
|
7834
7782
|
}
|
|
@@ -7849,7 +7797,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7849
7797
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
7850
7798
|
}
|
|
7851
7799
|
try {
|
|
7852
|
-
const n =
|
|
7800
|
+
const n = fs61.writeSync(this.fd, buf);
|
|
7853
7801
|
buf = buf.subarray(n);
|
|
7854
7802
|
this._len = Math.max(this._len - n, 0);
|
|
7855
7803
|
if (buf.length <= 0) {
|
|
@@ -7877,13 +7825,13 @@ var require_sonic_boom = __commonJS({
|
|
|
7877
7825
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
7878
7826
|
if (this.sync) {
|
|
7879
7827
|
try {
|
|
7880
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
7828
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs61.writeSync(this.fd, this._writingBuf) : fs61.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7881
7829
|
release(null, written);
|
|
7882
7830
|
} catch (err) {
|
|
7883
7831
|
release(err);
|
|
7884
7832
|
}
|
|
7885
7833
|
} else {
|
|
7886
|
-
|
|
7834
|
+
fs61.write(this.fd, this._writingBuf, release);
|
|
7887
7835
|
}
|
|
7888
7836
|
}
|
|
7889
7837
|
function actualWriteBuffer() {
|
|
@@ -7892,7 +7840,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7892
7840
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
7893
7841
|
if (this.sync) {
|
|
7894
7842
|
try {
|
|
7895
|
-
const written =
|
|
7843
|
+
const written = fs61.writeSync(this.fd, this._writingBuf);
|
|
7896
7844
|
release(null, written);
|
|
7897
7845
|
} catch (err) {
|
|
7898
7846
|
release(err);
|
|
@@ -7901,7 +7849,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7901
7849
|
if (kCopyBuffer) {
|
|
7902
7850
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
7903
7851
|
}
|
|
7904
|
-
|
|
7852
|
+
fs61.write(this.fd, this._writingBuf, release);
|
|
7905
7853
|
}
|
|
7906
7854
|
}
|
|
7907
7855
|
function actualClose(sonic) {
|
|
@@ -7917,12 +7865,12 @@ var require_sonic_boom = __commonJS({
|
|
|
7917
7865
|
sonic._lens = [];
|
|
7918
7866
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
7919
7867
|
try {
|
|
7920
|
-
|
|
7868
|
+
fs61.fsync(sonic.fd, closeWrapped);
|
|
7921
7869
|
} catch {
|
|
7922
7870
|
}
|
|
7923
7871
|
function closeWrapped() {
|
|
7924
7872
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
7925
|
-
|
|
7873
|
+
fs61.close(sonic.fd, done);
|
|
7926
7874
|
} else {
|
|
7927
7875
|
done();
|
|
7928
7876
|
}
|
|
@@ -11057,11 +11005,11 @@ var init_lib = __esm({
|
|
|
11057
11005
|
}
|
|
11058
11006
|
}
|
|
11059
11007
|
},
|
|
11060
|
-
addToPath: function addToPath(
|
|
11061
|
-
var last =
|
|
11008
|
+
addToPath: function addToPath(path68, added, removed, oldPosInc, options) {
|
|
11009
|
+
var last = path68.lastComponent;
|
|
11062
11010
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
11063
11011
|
return {
|
|
11064
|
-
oldPos:
|
|
11012
|
+
oldPos: path68.oldPos + oldPosInc,
|
|
11065
11013
|
lastComponent: {
|
|
11066
11014
|
count: last.count + 1,
|
|
11067
11015
|
added,
|
|
@@ -11071,7 +11019,7 @@ var init_lib = __esm({
|
|
|
11071
11019
|
};
|
|
11072
11020
|
} else {
|
|
11073
11021
|
return {
|
|
11074
|
-
oldPos:
|
|
11022
|
+
oldPos: path68.oldPos + oldPosInc,
|
|
11075
11023
|
lastComponent: {
|
|
11076
11024
|
count: 1,
|
|
11077
11025
|
added,
|
|
@@ -11537,10 +11485,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
11537
11485
|
const memories = raw.map((m2) => {
|
|
11538
11486
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11539
11487
|
const rec3 = m2;
|
|
11540
|
-
const
|
|
11488
|
+
const path68 = typeof rec3.path === "string" ? rec3.path : null;
|
|
11541
11489
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
11542
|
-
if (!
|
|
11543
|
-
const entry = { path:
|
|
11490
|
+
if (!path68 || content == null) return null;
|
|
11491
|
+
const entry = { path: path68, content };
|
|
11544
11492
|
if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
|
|
11545
11493
|
return entry;
|
|
11546
11494
|
}).filter((m2) => m2 !== null);
|
|
@@ -12352,10 +12300,10 @@ function parseAttachment(obj) {
|
|
|
12352
12300
|
const memories = raw.map((m2) => {
|
|
12353
12301
|
if (!m2 || typeof m2 !== "object") return null;
|
|
12354
12302
|
const rec3 = m2;
|
|
12355
|
-
const
|
|
12303
|
+
const path68 = typeof rec3.path === "string" ? rec3.path : null;
|
|
12356
12304
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
12357
|
-
if (!
|
|
12358
|
-
const out = { path:
|
|
12305
|
+
if (!path68 || content == null) return null;
|
|
12306
|
+
const out = { path: path68, content };
|
|
12359
12307
|
if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
|
|
12360
12308
|
return out;
|
|
12361
12309
|
}).filter((m2) => m2 !== null);
|
|
@@ -29030,7 +28978,7 @@ var require_websocket = __commonJS({
|
|
|
29030
28978
|
var http3 = require("http");
|
|
29031
28979
|
var net3 = require("net");
|
|
29032
28980
|
var tls = require("tls");
|
|
29033
|
-
var { randomBytes, createHash:
|
|
28981
|
+
var { randomBytes, createHash: createHash3 } = require("crypto");
|
|
29034
28982
|
var { Duplex, Readable: Readable3 } = require("stream");
|
|
29035
28983
|
var { URL: URL2 } = require("url");
|
|
29036
28984
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -29690,7 +29638,7 @@ var require_websocket = __commonJS({
|
|
|
29690
29638
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
29691
29639
|
return;
|
|
29692
29640
|
}
|
|
29693
|
-
const digest =
|
|
29641
|
+
const digest = createHash3("sha1").update(key + GUID).digest("base64");
|
|
29694
29642
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
29695
29643
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
29696
29644
|
return;
|
|
@@ -30057,7 +30005,7 @@ var require_websocket_server = __commonJS({
|
|
|
30057
30005
|
var EventEmitter3 = require("events");
|
|
30058
30006
|
var http3 = require("http");
|
|
30059
30007
|
var { Duplex } = require("stream");
|
|
30060
|
-
var { createHash:
|
|
30008
|
+
var { createHash: createHash3 } = require("crypto");
|
|
30061
30009
|
var extension2 = require_extension();
|
|
30062
30010
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
30063
30011
|
var subprotocol2 = require_subprotocol();
|
|
@@ -30358,7 +30306,7 @@ var require_websocket_server = __commonJS({
|
|
|
30358
30306
|
);
|
|
30359
30307
|
}
|
|
30360
30308
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
30361
|
-
const digest =
|
|
30309
|
+
const digest = createHash3("sha1").update(key + GUID).digest("base64");
|
|
30362
30310
|
const headers = [
|
|
30363
30311
|
"HTTP/1.1 101 Switching Protocols",
|
|
30364
30312
|
"Upgrade: websocket",
|
|
@@ -33320,8 +33268,8 @@ var require_utils = __commonJS({
|
|
|
33320
33268
|
var result = transform[inputType][outputType](input);
|
|
33321
33269
|
return result;
|
|
33322
33270
|
};
|
|
33323
|
-
exports2.resolve = function(
|
|
33324
|
-
var parts =
|
|
33271
|
+
exports2.resolve = function(path68) {
|
|
33272
|
+
var parts = path68.split("/");
|
|
33325
33273
|
var result = [];
|
|
33326
33274
|
for (var index = 0; index < parts.length; index++) {
|
|
33327
33275
|
var part = parts[index];
|
|
@@ -39174,18 +39122,18 @@ var require_object = __commonJS({
|
|
|
39174
39122
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
39175
39123
|
this.files[name] = object;
|
|
39176
39124
|
};
|
|
39177
|
-
var parentFolder = function(
|
|
39178
|
-
if (
|
|
39179
|
-
|
|
39125
|
+
var parentFolder = function(path68) {
|
|
39126
|
+
if (path68.slice(-1) === "/") {
|
|
39127
|
+
path68 = path68.substring(0, path68.length - 1);
|
|
39180
39128
|
}
|
|
39181
|
-
var lastSlash =
|
|
39182
|
-
return lastSlash > 0 ?
|
|
39129
|
+
var lastSlash = path68.lastIndexOf("/");
|
|
39130
|
+
return lastSlash > 0 ? path68.substring(0, lastSlash) : "";
|
|
39183
39131
|
};
|
|
39184
|
-
var forceTrailingSlash = function(
|
|
39185
|
-
if (
|
|
39186
|
-
|
|
39132
|
+
var forceTrailingSlash = function(path68) {
|
|
39133
|
+
if (path68.slice(-1) !== "/") {
|
|
39134
|
+
path68 += "/";
|
|
39187
39135
|
}
|
|
39188
|
-
return
|
|
39136
|
+
return path68;
|
|
39189
39137
|
};
|
|
39190
39138
|
var folderAdd = function(name, createFolders) {
|
|
39191
39139
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -40187,7 +40135,7 @@ var require_lib3 = __commonJS({
|
|
|
40187
40135
|
// src/run-case/recorder.ts
|
|
40188
40136
|
function startRunCaseRecorder(opts) {
|
|
40189
40137
|
const now = opts.now ?? Date.now;
|
|
40190
|
-
const dir =
|
|
40138
|
+
const dir = import_node_path56.default.dirname(opts.recordPath);
|
|
40191
40139
|
let stream = null;
|
|
40192
40140
|
let closing = false;
|
|
40193
40141
|
let closedSettled = false;
|
|
@@ -40201,8 +40149,8 @@ function startRunCaseRecorder(opts) {
|
|
|
40201
40149
|
});
|
|
40202
40150
|
const ensureStream = () => {
|
|
40203
40151
|
if (stream) return stream;
|
|
40204
|
-
|
|
40205
|
-
stream =
|
|
40152
|
+
import_node_fs43.default.mkdirSync(dir, { recursive: true });
|
|
40153
|
+
stream = import_node_fs43.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
40206
40154
|
stream.on("close", () => closedResolve());
|
|
40207
40155
|
return stream;
|
|
40208
40156
|
};
|
|
@@ -40227,12 +40175,12 @@ function startRunCaseRecorder(opts) {
|
|
|
40227
40175
|
};
|
|
40228
40176
|
return { tap, close, closed };
|
|
40229
40177
|
}
|
|
40230
|
-
var
|
|
40178
|
+
var import_node_fs43, import_node_path56;
|
|
40231
40179
|
var init_recorder = __esm({
|
|
40232
40180
|
"src/run-case/recorder.ts"() {
|
|
40233
40181
|
"use strict";
|
|
40234
|
-
|
|
40235
|
-
|
|
40182
|
+
import_node_fs43 = __toESM(require("fs"), 1);
|
|
40183
|
+
import_node_path56 = __toESM(require("path"), 1);
|
|
40236
40184
|
}
|
|
40237
40185
|
});
|
|
40238
40186
|
|
|
@@ -40275,7 +40223,7 @@ var init_wire = __esm({
|
|
|
40275
40223
|
// src/run-case/controller.ts
|
|
40276
40224
|
async function runController(opts) {
|
|
40277
40225
|
const now = opts.now ?? Date.now;
|
|
40278
|
-
const cwd = opts.cwd ?? (0,
|
|
40226
|
+
const cwd = opts.cwd ?? (0, import_node_fs44.mkdtempSync)(import_node_path57.default.join(import_node_os22.default.tmpdir(), "clawd-runcase-"));
|
|
40279
40227
|
const ownsCwd = opts.cwd === void 0;
|
|
40280
40228
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
40281
40229
|
const spawnCtx = { cwd };
|
|
@@ -40436,19 +40384,19 @@ async function runController(opts) {
|
|
|
40436
40384
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
40437
40385
|
if (ownsCwd) {
|
|
40438
40386
|
try {
|
|
40439
|
-
(0,
|
|
40387
|
+
(0, import_node_fs44.rmSync)(cwd, { recursive: true, force: true });
|
|
40440
40388
|
} catch {
|
|
40441
40389
|
}
|
|
40442
40390
|
}
|
|
40443
40391
|
return exitCode ?? 0;
|
|
40444
40392
|
}
|
|
40445
|
-
var
|
|
40393
|
+
var import_node_fs44, import_node_os22, import_node_path57;
|
|
40446
40394
|
var init_controller = __esm({
|
|
40447
40395
|
"src/run-case/controller.ts"() {
|
|
40448
40396
|
"use strict";
|
|
40449
|
-
|
|
40397
|
+
import_node_fs44 = require("fs");
|
|
40450
40398
|
import_node_os22 = __toESM(require("os"), 1);
|
|
40451
|
-
|
|
40399
|
+
import_node_path57 = __toESM(require("path"), 1);
|
|
40452
40400
|
init_claude();
|
|
40453
40401
|
init_stdout_splitter();
|
|
40454
40402
|
init_permission_stdio();
|
|
@@ -40551,7 +40499,6 @@ var import_node_path = __toESM(require("path"), 1);
|
|
|
40551
40499
|
init_protocol();
|
|
40552
40500
|
var DEFAULT_PORT = 18790;
|
|
40553
40501
|
var DEFAULT_HOST = "127.0.0.1";
|
|
40554
|
-
var DEFAULT_SSHD_PORT = 22422;
|
|
40555
40502
|
var DEFAULT_CLAWOS_API = "https://api.clawos.chat";
|
|
40556
40503
|
var DEFAULT_LOG_ENDPOINT = "https://clawd-prod.cn-hangzhou.log.aliyuncs.com/logstores/app-logs/track";
|
|
40557
40504
|
function resolveLogShipping(raw, cliNoShipping) {
|
|
@@ -40619,14 +40566,6 @@ function parseArgs(argv) {
|
|
|
40619
40566
|
case "--no-log-shipping":
|
|
40620
40567
|
out.noLogShipping = true;
|
|
40621
40568
|
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
|
-
}
|
|
40630
40569
|
default:
|
|
40631
40570
|
if (a.startsWith("--")) throw new Error(`unknown flag: ${a}`);
|
|
40632
40571
|
break;
|
|
@@ -40676,7 +40615,6 @@ function resolveConfig(opts) {
|
|
|
40676
40615
|
fileCfg.logShipping,
|
|
40677
40616
|
args.noLogShipping ?? false
|
|
40678
40617
|
);
|
|
40679
|
-
const sshdPort = args.sshdPort ?? (typeof fileCfg.sshdPort === "number" ? fileCfg.sshdPort : DEFAULT_SSHD_PORT);
|
|
40680
40618
|
return {
|
|
40681
40619
|
port,
|
|
40682
40620
|
host,
|
|
@@ -40690,8 +40628,7 @@ function resolveConfig(opts) {
|
|
|
40690
40628
|
frpcBinary,
|
|
40691
40629
|
mode,
|
|
40692
40630
|
previewPorts,
|
|
40693
|
-
logShipping
|
|
40694
|
-
sshdPort
|
|
40631
|
+
logShipping
|
|
40695
40632
|
};
|
|
40696
40633
|
}
|
|
40697
40634
|
var HELP_TEXT = `clawd [options]
|
|
@@ -40703,7 +40640,6 @@ var HELP_TEXT = `clawd [options]
|
|
|
40703
40640
|
--clawos-api <url> tunnel register \u63A5\u53E3\u7684 base url\uFF08\u9ED8\u8BA4 https://api.clawos.chat\uFF09
|
|
40704
40641
|
--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
|
|
40705
40642
|
--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
|
|
40707
40643
|
--help / -h \u663E\u793A\u5E2E\u52A9
|
|
40708
40644
|
--version / -v \u663E\u793A\u7248\u672C
|
|
40709
40645
|
|
|
@@ -40722,8 +40658,8 @@ Env (advanced):
|
|
|
40722
40658
|
`;
|
|
40723
40659
|
|
|
40724
40660
|
// src/index.ts
|
|
40725
|
-
var
|
|
40726
|
-
var
|
|
40661
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
40662
|
+
var import_node_fs42 = __toESM(require("fs"), 1);
|
|
40727
40663
|
var import_node_os21 = __toESM(require("os"), 1);
|
|
40728
40664
|
|
|
40729
40665
|
// ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
|
|
@@ -40843,6 +40779,18 @@ function createLogger(opts = {}) {
|
|
|
40843
40779
|
);
|
|
40844
40780
|
return wrap(base);
|
|
40845
40781
|
}
|
|
40782
|
+
function createFileOnlyLogger(opts) {
|
|
40783
|
+
const level = opts.level ?? "debug";
|
|
40784
|
+
try {
|
|
40785
|
+
import_node_fs2.default.mkdirSync(import_node_path2.default.dirname(opts.file), { recursive: true });
|
|
40786
|
+
} catch {
|
|
40787
|
+
}
|
|
40788
|
+
const base = (0, import_pino.default)(
|
|
40789
|
+
{ level, base: {} },
|
|
40790
|
+
import_pino.default.destination({ dest: opts.file, mkdir: true, sync: true })
|
|
40791
|
+
);
|
|
40792
|
+
return wrap(base);
|
|
40793
|
+
}
|
|
40846
40794
|
function pinoLevelToString(n) {
|
|
40847
40795
|
if (typeof n !== "number") return null;
|
|
40848
40796
|
if (n >= 50) return "error";
|
|
@@ -43008,9 +42956,18 @@ var SessionManager = class {
|
|
|
43008
42956
|
// 由 observer 监听 jsonl user 行后调 recordRealUserUuid 建立映射;rewind 系列 RPC 在
|
|
43009
42957
|
// 入参 / 出参做转译,保证 UI 看到的 uuid 始终是 events 流里的 synth uuid
|
|
43010
42958
|
realUuidBySynth = /* @__PURE__ */ new Map();
|
|
43011
|
-
//
|
|
43012
|
-
//
|
|
43013
|
-
|
|
42959
|
+
// observer 收到 `turn_duration` 信号但屏幕还没稳定 5s → 挂进 pending,等 `notifyScreenIdle`
|
|
42960
|
+
// (屏幕 armed=true 触发点)flush 成真正的 turn_end 进 reducer。
|
|
42961
|
+
//
|
|
42962
|
+
// 语义澄清:`turn_duration` 是 CC 报的原始信号("本轮 API 调用完了"),**不代表 turn 真的结束**
|
|
42963
|
+
//(背景 agent 可能还在跑)。屏幕 5s 稳定 + 无 popup 才是"确认信号"。
|
|
42964
|
+
// 两者 AND 后 daemon 才产生真正的 `turn_end` 事件送给 reducer。
|
|
42965
|
+
//
|
|
42966
|
+
// pending 不设截止时间:屏幕稳定的时机由 CC UI 决定,可能几秒也可能几分钟。观察者以
|
|
42967
|
+
// "屏幕真的稳定"作为触发点,signal-driven 而非 timer-driven。
|
|
42968
|
+
//
|
|
42969
|
+
// 清理点:newSession / stop / session-delete / stopAll。
|
|
42970
|
+
pendingTurnDurationSignals = /* @__PURE__ */ new Set();
|
|
43014
42971
|
// SessionStore 按 scope 派生(root = <dataDir>/sessions/<scopeSubPath>/)。
|
|
43015
42972
|
// default scope 直接复用 deps.store;persona scope(owner / listener)第一次访问时按需创建并缓存。
|
|
43016
42973
|
// 取代旧的 storesByAgent —— agentId 概念由 SessionScope 取代,路径即身份,
|
|
@@ -43350,6 +43307,14 @@ var SessionManager = class {
|
|
|
43350
43307
|
routeFromRunner(frame, target) {
|
|
43351
43308
|
const compressed = compressFrameForWire(frame);
|
|
43352
43309
|
if (!compressed) return;
|
|
43310
|
+
if (compressed.type === "session:status") {
|
|
43311
|
+
const s = compressed;
|
|
43312
|
+
this.deps.screenIdleProbeLogger?.info("session:status wire emit", {
|
|
43313
|
+
sessionId: s.sessionId,
|
|
43314
|
+
status: s.status,
|
|
43315
|
+
target
|
|
43316
|
+
});
|
|
43317
|
+
}
|
|
43353
43318
|
if (compressed.type === "session:event" || compressed.type === "session:status") {
|
|
43354
43319
|
const sid = compressed.sessionId;
|
|
43355
43320
|
if (sid) {
|
|
@@ -43623,7 +43588,7 @@ var SessionManager = class {
|
|
|
43623
43588
|
this.runners.delete(args.sessionId);
|
|
43624
43589
|
this.realUuidBySynth.delete(args.sessionId);
|
|
43625
43590
|
this.lastUiSizeBySessionId.delete(args.sessionId);
|
|
43626
|
-
this.
|
|
43591
|
+
this.clearPendingTurnEnd(args.sessionId);
|
|
43627
43592
|
return { response: { sessionId: args.sessionId }, broadcast };
|
|
43628
43593
|
}
|
|
43629
43594
|
this.deleteOwned(args.sessionId);
|
|
@@ -43653,6 +43618,7 @@ var SessionManager = class {
|
|
|
43653
43618
|
async stop(args) {
|
|
43654
43619
|
const runner = this.runners.get(args.sessionId);
|
|
43655
43620
|
if (!runner) return { response: { ok: true }, broadcast: [] };
|
|
43621
|
+
this.clearPendingTurnEnd(args.sessionId);
|
|
43656
43622
|
const { broadcast } = this.withCollector(() => {
|
|
43657
43623
|
runner.input({ kind: "command", command: { kind: "stop" } });
|
|
43658
43624
|
});
|
|
@@ -43786,6 +43752,7 @@ var SessionManager = class {
|
|
|
43786
43752
|
newSession(args) {
|
|
43787
43753
|
const existingFile = this.getFile(args.sessionId);
|
|
43788
43754
|
const nextToolSessionId = this.deps.mode === "tui" && (existingFile.tool ?? "claude") === "claude" ? v4_default() : void 0;
|
|
43755
|
+
this.clearPendingTurnEnd(args.sessionId);
|
|
43789
43756
|
const runner = this.runners.get(args.sessionId);
|
|
43790
43757
|
if (runner) {
|
|
43791
43758
|
const { value, broadcast } = this.withCollector(() => {
|
|
@@ -43886,6 +43853,7 @@ var SessionManager = class {
|
|
|
43886
43853
|
for (const r of this.runners.values()) {
|
|
43887
43854
|
r.input({ kind: "command", command: { kind: "stop" } });
|
|
43888
43855
|
}
|
|
43856
|
+
this.pendingTurnDurationSignals.clear();
|
|
43889
43857
|
}
|
|
43890
43858
|
// 给 observer 用:拿已存在的 runner
|
|
43891
43859
|
getActive(sessionId) {
|
|
@@ -44102,7 +44070,7 @@ var SessionManager = class {
|
|
|
44102
44070
|
this.runners.delete(args.sessionId);
|
|
44103
44071
|
this.realUuidBySynth.delete(args.sessionId);
|
|
44104
44072
|
this.lastUiSizeBySessionId.delete(args.sessionId);
|
|
44105
|
-
this.
|
|
44073
|
+
this.clearPendingTurnEnd(args.sessionId);
|
|
44106
44074
|
return { response: { sessionId: args.sessionId }, broadcast };
|
|
44107
44075
|
}
|
|
44108
44076
|
this.storeFor(args.scope).delete(args.sessionId);
|
|
@@ -44348,23 +44316,93 @@ var SessionManager = class {
|
|
|
44348
44316
|
return;
|
|
44349
44317
|
}
|
|
44350
44318
|
}
|
|
44351
|
-
this.lastObserverEventAt.set(sessionId, (this.deps.now ?? Date.now)());
|
|
44352
44319
|
let feedEvents = outEvents;
|
|
44353
44320
|
if (outEvents.some((e) => e.kind === "turn_end")) {
|
|
44354
|
-
const
|
|
44355
|
-
|
|
44321
|
+
const runnerState = runner.getState();
|
|
44322
|
+
const toolSessionId = runnerState.file.toolSessionId;
|
|
44323
|
+
const adapter = this.deps.getAdapter(runnerState.file.tool ?? "claude");
|
|
44324
|
+
const gateOpen = !adapter.canAcceptTurnEnd || !toolSessionId ? true : adapter.canAcceptTurnEnd(toolSessionId);
|
|
44325
|
+
this.deps.screenIdleProbeLogger?.info("turn_duration signal received", {
|
|
44326
|
+
sessionId,
|
|
44327
|
+
toolSessionId,
|
|
44328
|
+
batchKinds: outEvents.map((e) => e.kind),
|
|
44329
|
+
gateOpen,
|
|
44330
|
+
hasCanAcceptGate: !!adapter.canAcceptTurnEnd
|
|
44331
|
+
});
|
|
44332
|
+
if (gateOpen) {
|
|
44333
|
+
this.deps.screenIdleProbeLogger?.info(
|
|
44334
|
+
"turn_duration \u2192 turn_end confirmed (gate open) \u2192 fed to reducer",
|
|
44335
|
+
{ sessionId, toolSessionId }
|
|
44336
|
+
);
|
|
44337
|
+
} else {
|
|
44356
44338
|
feedEvents = outEvents.filter((e) => e.kind !== "turn_end");
|
|
44357
|
-
this.
|
|
44358
|
-
|
|
44359
|
-
|
|
44360
|
-
|
|
44361
|
-
|
|
44362
|
-
}
|
|
44339
|
+
if (this.pendingTurnDurationSignals.has(sessionId)) {
|
|
44340
|
+
this.deps.screenIdleProbeLogger?.info(
|
|
44341
|
+
"turn_duration dedup: pending already set (repeated observer signal)",
|
|
44342
|
+
{ sessionId, toolSessionId }
|
|
44343
|
+
);
|
|
44344
|
+
} else {
|
|
44345
|
+
this.pendingTurnDurationSignals.add(sessionId);
|
|
44346
|
+
this.deps.screenIdleProbeLogger?.info(
|
|
44347
|
+
"turn_duration pending: gate closed \u2192 waiting for screen-idle signal",
|
|
44348
|
+
{ sessionId, toolSessionId }
|
|
44349
|
+
);
|
|
44350
|
+
}
|
|
44363
44351
|
}
|
|
44364
44352
|
}
|
|
44365
44353
|
if (feedEvents.length === 0) return;
|
|
44366
44354
|
runner.feedObserverEvents(feedEvents);
|
|
44367
44355
|
}
|
|
44356
|
+
/**
|
|
44357
|
+
* `ClaudeTuiAdapter.observeScreenIdle` fire triggered → armed=true 时调用(一次性触发点)。
|
|
44358
|
+
*
|
|
44359
|
+
* 语义:屏幕真的稳定了 5s。查有没有 pending 的 turn_duration 信号:
|
|
44360
|
+
* - 有 → 之前收到的 turn_duration 被屏幕稳定**确认**了,flush 成 turn_end 进 reducer
|
|
44361
|
+
* - 无 → 屏幕稳定但从没收到 turn_duration → noop(不生成 turn_end;补偿路径的错误做法)
|
|
44362
|
+
*
|
|
44363
|
+
* pending 集合是**必要前提**:turn_end 只能来自"observer 收 turn_duration + 屏幕后续稳定"
|
|
44364
|
+
* 双源确认,任何一个缺少都不能 emit。这跟 PR #962 拆掉的 dispatchTurnIdle "屏幕静止就补
|
|
44365
|
+
* turn_end" 语义不同。
|
|
44366
|
+
*
|
|
44367
|
+
* 仅 TUI 模式;SDK / codex 没有屏幕信号也就不会触发本方法。
|
|
44368
|
+
*/
|
|
44369
|
+
notifyScreenIdle(toolSessionId) {
|
|
44370
|
+
if (this.deps.mode !== "tui") return;
|
|
44371
|
+
const sid = this.sessionIdByToolSid(toolSessionId);
|
|
44372
|
+
if (!sid) {
|
|
44373
|
+
this.deps.screenIdleProbeLogger?.warn("notifyScreenIdle: no session for toolSessionId", {
|
|
44374
|
+
toolSessionId
|
|
44375
|
+
});
|
|
44376
|
+
return;
|
|
44377
|
+
}
|
|
44378
|
+
if (!this.pendingTurnDurationSignals.has(sid)) {
|
|
44379
|
+
this.deps.screenIdleProbeLogger?.info(
|
|
44380
|
+
"notifyScreenIdle: no pending turn_duration \u2192 noop",
|
|
44381
|
+
{ sessionId: sid, toolSessionId }
|
|
44382
|
+
);
|
|
44383
|
+
return;
|
|
44384
|
+
}
|
|
44385
|
+
const runner = this.runners.get(sid);
|
|
44386
|
+
if (!runner) {
|
|
44387
|
+
this.pendingTurnDurationSignals.delete(sid);
|
|
44388
|
+
this.deps.screenIdleProbeLogger?.warn(
|
|
44389
|
+
"notifyScreenIdle: pending but no runner \u2192 cleared without inject",
|
|
44390
|
+
{ sessionId: sid, toolSessionId }
|
|
44391
|
+
);
|
|
44392
|
+
return;
|
|
44393
|
+
}
|
|
44394
|
+
this.pendingTurnDurationSignals.delete(sid);
|
|
44395
|
+
this.deps.screenIdleProbeLogger?.info(
|
|
44396
|
+
"notifyScreenIdle: pending turn_duration + screen idle confirmed \u2192 inject turn_end",
|
|
44397
|
+
{ sessionId: sid, toolSessionId }
|
|
44398
|
+
);
|
|
44399
|
+
runner.input({ kind: "inject-events", events: [{ kind: "turn_end" }] });
|
|
44400
|
+
}
|
|
44401
|
+
clearPendingTurnEnd(sessionId) {
|
|
44402
|
+
if (this.pendingTurnDurationSignals.delete(sessionId)) {
|
|
44403
|
+
this.deps.screenIdleProbeLogger?.info("pending turn_duration cleared", { sessionId });
|
|
44404
|
+
}
|
|
44405
|
+
}
|
|
44368
44406
|
// AskUserQuestion 表单回写(plan: clawd-ask-user-question):UI 答完所有 question 后调用。
|
|
44369
44407
|
// - session 不存在 / 无 runner → noop 幂等返回 ok(first-decider-wins)
|
|
44370
44408
|
// - reducer noop(toolUseId 不存在或已答过)也保持幂等返回,handler 不抛
|
|
@@ -44623,70 +44661,6 @@ var SessionManager = class {
|
|
|
44623
44661
|
if (!runner) return;
|
|
44624
44662
|
runner.input({ kind: "ready-detected" });
|
|
44625
44663
|
}
|
|
44626
|
-
/**
|
|
44627
|
-
* ClaudeTuiAdapter onTurnIdle callback:屏幕内容静止时**复发**本轮已出现过的权威 turn_end。
|
|
44628
|
-
* 本意:turn_duration 写盘早于尾段正文 → observer 把尾随 text 推进 buffer 盖掉 lastEventKind →
|
|
44629
|
-
* spinner 不熄;屏幕静止时再补一条 turn_end 排到尾随 text 之后。
|
|
44630
|
-
*
|
|
44631
|
-
* Fix A(修 bug1 "UI 还在变却显示结束" / bug2 "发消息后无 spinner"):补偿**只复发不 originate**——
|
|
44632
|
-
* 仅当本轮已出现过 turn_end(turnEndSeenThisTurn,即真有过尾随 text 覆盖场景)才补。turnEndSeenThisTurn
|
|
44633
|
-
* ===false 说明本轮 CC 从未结束过(仍在工作 / 刚发消息没产出 / 漏检弹框等用户),屏幕静止 ≠ turn 结束,
|
|
44634
|
-
* 凭空 inject turn_end 会误灭 spinner——故跳过,让真正的 turn_duration 来时再正常收。
|
|
44635
|
-
* 仅 tui 模式;runner 缺失 noop。turn_end 无 uuid 不参与 dedup。
|
|
44636
|
-
*/
|
|
44637
|
-
dispatchTurnIdle(toolSessionId) {
|
|
44638
|
-
if (this.deps.mode !== "tui") return;
|
|
44639
|
-
const sid = this.sessionIdByToolSid(toolSessionId);
|
|
44640
|
-
const runner = sid ? this.runners.get(sid) : void 0;
|
|
44641
|
-
if (!runner) return;
|
|
44642
|
-
const ev = this.peekTurnEvidence(runner);
|
|
44643
|
-
const willInject = ev.turnEndSeenThisTurn;
|
|
44644
|
-
this.deps.logger?.info("[TE-PROBE] screen-idle compensation", {
|
|
44645
|
-
sessionId: sid,
|
|
44646
|
-
src: "screen-idle",
|
|
44647
|
-
willInject,
|
|
44648
|
-
...ev
|
|
44649
|
-
});
|
|
44650
|
-
if (!willInject) return;
|
|
44651
|
-
runner.input({ kind: "inject-events", events: [{ kind: "turn_end" }] });
|
|
44652
|
-
}
|
|
44653
|
-
/**
|
|
44654
|
-
* 读 runner 当前 turn 态快照,供两个 turn_end 注入源(屏幕静止补偿 / observer turn_duration)
|
|
44655
|
-
* 判断误发 + 打 [TE-PROBE] 日志。
|
|
44656
|
-
* turnEndSeenThisTurn:从 buffer 末尾回扫到最近 user_text 期间是否已出现过 turn_end
|
|
44657
|
-
* (已出现=本轮真结束过、合法尾随;未出现=本轮还没结束过)→ Fix A 复发闸。
|
|
44658
|
-
* turnHasContent:末条是否 assistant 产出(非 user_text/turn_end/空)→ Fix B 空 turn 守卫闸。
|
|
44659
|
-
*/
|
|
44660
|
-
peekTurnEvidence(runner) {
|
|
44661
|
-
const st = runner.getState();
|
|
44662
|
-
const buf = st.buffer;
|
|
44663
|
-
const lastEventKindBefore = buf.length > 0 ? buf[buf.length - 1].event.kind : null;
|
|
44664
|
-
let turnEndSeenThisTurn = false;
|
|
44665
|
-
for (let i = buf.length - 1; i >= 0; i--) {
|
|
44666
|
-
const k2 = buf[i].event.kind;
|
|
44667
|
-
if (k2 === "user_text") break;
|
|
44668
|
-
if (k2 === "turn_end") {
|
|
44669
|
-
turnEndSeenThisTurn = true;
|
|
44670
|
-
break;
|
|
44671
|
-
}
|
|
44672
|
-
}
|
|
44673
|
-
const turnHasContent = lastEventKindBefore !== null && lastEventKindBefore !== "user_text" && lastEventKindBefore !== "turn_end";
|
|
44674
|
-
return { turnOpenBefore: st.turnOpen, lastEventKindBefore, turnEndSeenThisTurn, turnHasContent };
|
|
44675
|
-
}
|
|
44676
|
-
/**
|
|
44677
|
-
* observer 还需静止多久(ms)才满 idleMs,0 = 已满。observeScreenIdle 复合条件闸:屏幕静止后
|
|
44678
|
-
* 精确等这段剩余再补 turn_end —— turn_duration 写盘早于尾段正文,observer 把尾随 text poll 落盘
|
|
44679
|
-
* 期间屏幕可能已静止,仅看屏幕会早 fire(补的 turn_end 盖不到尾随 text 之后)。
|
|
44680
|
-
* 找不到 runner / 从无事件 → 0(不阻塞 fire)。idleMs 由装配处传 SCREEN_IDLE_MS。
|
|
44681
|
-
*/
|
|
44682
|
-
observerIdleWaitMs(toolSessionId, idleMs) {
|
|
44683
|
-
const sid = this.sessionIdByToolSid(toolSessionId);
|
|
44684
|
-
if (!sid) return 0;
|
|
44685
|
-
const last = this.lastObserverEventAt.get(sid);
|
|
44686
|
-
if (last === void 0) return 0;
|
|
44687
|
-
const elapsed = (this.deps.now ?? Date.now)() - last;
|
|
44688
|
-
return Math.max(0, idleMs - elapsed);
|
|
44689
|
-
}
|
|
44690
44664
|
/** toolSessionId → sessionId 反查(遍历 runners);session 数典型 < 10,O(n) 可接受 */
|
|
44691
44665
|
sessionIdByToolSid(toolSessionId) {
|
|
44692
44666
|
for (const [sid, runner] of this.runners) {
|
|
@@ -46196,8 +46170,8 @@ function turnStartInput(text) {
|
|
|
46196
46170
|
const items = [];
|
|
46197
46171
|
let leftover = text;
|
|
46198
46172
|
for (const m2 of text.matchAll(SKILL_RE)) {
|
|
46199
|
-
const [marker, name,
|
|
46200
|
-
items.push({ type: "skill", name, path:
|
|
46173
|
+
const [marker, name, path68] = m2;
|
|
46174
|
+
items.push({ type: "skill", name, path: path68 });
|
|
46201
46175
|
leftover = leftover.replace(marker, "");
|
|
46202
46176
|
}
|
|
46203
46177
|
for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
|
|
@@ -46429,6 +46403,7 @@ var CodexAdapter = class {
|
|
|
46429
46403
|
};
|
|
46430
46404
|
|
|
46431
46405
|
// src/tools/claude-tui.ts
|
|
46406
|
+
var import_node_crypto5 = require("crypto");
|
|
46432
46407
|
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
46433
46408
|
var import_node_os7 = __toESM(require("os"), 1);
|
|
46434
46409
|
var import_node_path14 = __toESM(require("path"), 1);
|
|
@@ -47239,22 +47214,56 @@ function observeScreenIdle(surface, opts) {
|
|
|
47239
47214
|
timer = null;
|
|
47240
47215
|
if (disposed) return;
|
|
47241
47216
|
if (opts.getPopupVisible()) {
|
|
47217
|
+
opts.probeLogger?.info("screen-idle fire suppressed: popup visible", {
|
|
47218
|
+
label: opts.probeLabel
|
|
47219
|
+
});
|
|
47242
47220
|
timer = setTimeout(fire, opts.idleMs);
|
|
47243
47221
|
return;
|
|
47244
47222
|
}
|
|
47245
47223
|
const obsWait = opts.getObserverWaitMs?.() ?? 0;
|
|
47246
47224
|
if (obsWait > 0) {
|
|
47225
|
+
opts.probeLogger?.info("screen-idle fire suppressed: observer not idle", {
|
|
47226
|
+
label: opts.probeLabel,
|
|
47227
|
+
obsWait
|
|
47228
|
+
});
|
|
47247
47229
|
timer = setTimeout(fire, Math.max(obsWait, REWAIT_MIN_MS));
|
|
47248
47230
|
return;
|
|
47249
47231
|
}
|
|
47250
|
-
if (armed)
|
|
47232
|
+
if (armed) {
|
|
47233
|
+
opts.probeLogger?.debug("screen-idle fire noop: already armed", {
|
|
47234
|
+
label: opts.probeLabel
|
|
47235
|
+
});
|
|
47236
|
+
return;
|
|
47237
|
+
}
|
|
47251
47238
|
armed = true;
|
|
47239
|
+
opts.probeLogger?.info("screen-idle fire triggered \u2192 armed=true, calling onIdle", {
|
|
47240
|
+
label: opts.probeLabel
|
|
47241
|
+
});
|
|
47252
47242
|
opts.onIdle();
|
|
47253
47243
|
};
|
|
47254
47244
|
const unsub = surface.onTick((lines) => {
|
|
47255
47245
|
if (disposed) return;
|
|
47256
47246
|
const snap = snapOf(lines);
|
|
47257
47247
|
if (snap === lastSnap) return;
|
|
47248
|
+
if (opts.probeLogger) {
|
|
47249
|
+
const prev = lastSnap;
|
|
47250
|
+
const meta = {
|
|
47251
|
+
label: opts.probeLabel,
|
|
47252
|
+
prevHash: prev === null ? null : shortHash(prev),
|
|
47253
|
+
nextHash: shortHash(snap),
|
|
47254
|
+
prevLen: prev?.length ?? 0,
|
|
47255
|
+
nextLen: snap.length
|
|
47256
|
+
};
|
|
47257
|
+
if (prev !== null) {
|
|
47258
|
+
const diff2 = firstLineDiff(prev, snap);
|
|
47259
|
+
if (diff2) {
|
|
47260
|
+
meta.diffRow = diff2.row;
|
|
47261
|
+
meta.prevRow = diff2.prev;
|
|
47262
|
+
meta.nextRow = diff2.next;
|
|
47263
|
+
}
|
|
47264
|
+
}
|
|
47265
|
+
opts.probeLogger.info("screen-idle tick snap changed", meta);
|
|
47266
|
+
}
|
|
47258
47267
|
lastSnap = snap;
|
|
47259
47268
|
armed = false;
|
|
47260
47269
|
clear();
|
|
@@ -47265,9 +47274,38 @@ function observeScreenIdle(surface, opts) {
|
|
|
47265
47274
|
disposed = true;
|
|
47266
47275
|
unsub();
|
|
47267
47276
|
clear();
|
|
47277
|
+
},
|
|
47278
|
+
isIdle() {
|
|
47279
|
+
const popupVisible = opts.getPopupVisible();
|
|
47280
|
+
const idle = armed && !popupVisible;
|
|
47281
|
+
if (opts.probeLogger) {
|
|
47282
|
+
opts.probeLogger.info("screen-idle isIdle check", {
|
|
47283
|
+
label: opts.probeLabel,
|
|
47284
|
+
idle,
|
|
47285
|
+
armed,
|
|
47286
|
+
popupVisible
|
|
47287
|
+
});
|
|
47288
|
+
}
|
|
47289
|
+
return idle;
|
|
47268
47290
|
}
|
|
47269
47291
|
};
|
|
47270
47292
|
}
|
|
47293
|
+
function shortHash(s) {
|
|
47294
|
+
return (0, import_node_crypto5.createHash)("sha1").update(s).digest("hex").slice(0, 8);
|
|
47295
|
+
}
|
|
47296
|
+
function firstLineDiff(prev, next) {
|
|
47297
|
+
const p2 = prev.split("\n");
|
|
47298
|
+
const n = next.split("\n");
|
|
47299
|
+
const rows = Math.max(p2.length, n.length);
|
|
47300
|
+
for (let i = 0; i < rows; i++) {
|
|
47301
|
+
const pl = p2[i] ?? "";
|
|
47302
|
+
const nl = n[i] ?? "";
|
|
47303
|
+
if (pl !== nl) {
|
|
47304
|
+
return { row: i, prev: pl.slice(0, 60), next: nl.slice(0, 60) };
|
|
47305
|
+
}
|
|
47306
|
+
}
|
|
47307
|
+
return null;
|
|
47308
|
+
}
|
|
47271
47309
|
var BYPASS_SETTLE_MS = 300;
|
|
47272
47310
|
var SCREEN_IDLE_MS = 5e3;
|
|
47273
47311
|
function createBootGate(pty, logger) {
|
|
@@ -47342,11 +47380,42 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
47342
47380
|
// 用于 spawn / PtyChildProcess 链路打日志
|
|
47343
47381
|
tuiLogger;
|
|
47344
47382
|
tuiOpts;
|
|
47383
|
+
/**
|
|
47384
|
+
* per-toolSessionId 的 tui 观察者句柄,仅用于 turn_end gate 查询(`canAcceptTurnEnd`)。
|
|
47385
|
+
* onIdle / onPopupTransition 等回调仍走原有闭包(不复用这份 map),本 map 只承担
|
|
47386
|
+
* "manager 需要跨模块查屏幕/弹框状态"这单一职责。
|
|
47387
|
+
*/
|
|
47388
|
+
tuiStates = /* @__PURE__ */ new Map();
|
|
47345
47389
|
constructor(opts = {}) {
|
|
47346
47390
|
super(opts);
|
|
47347
47391
|
this.tuiLogger = opts.logger;
|
|
47348
47392
|
this.tuiOpts = opts;
|
|
47349
47393
|
}
|
|
47394
|
+
/**
|
|
47395
|
+
* TUI adapter 的 turn_end 权威判定:屏幕已 idle 且非弹框态才放行。
|
|
47396
|
+
*
|
|
47397
|
+
* `feedObserverEvents` 收到 observer 回灌 `turn_end` 时调用。屏幕仍在变(如后台 agent 在跑)
|
|
47398
|
+
* 时 drop 掉 turn_end,避免 `system/turn_duration` JSONL 帧误触发 running-idle 状态转换。
|
|
47399
|
+
*
|
|
47400
|
+
* 未跟踪的 toolSessionId(spawn 前 / spawn 失败 / 已 dispose)视为 pass —— gate 只 drop
|
|
47401
|
+
* "有证据判定为伪信号"的场景,不做 unknown → block。
|
|
47402
|
+
*/
|
|
47403
|
+
canAcceptTurnEnd(toolSessionId) {
|
|
47404
|
+
const state = this.tuiStates.get(toolSessionId);
|
|
47405
|
+
if (!state) {
|
|
47406
|
+
this.tuiOpts.screenIdleProbeLogger?.info(
|
|
47407
|
+
"canAcceptTurnEnd: no tuiState \u2192 pass (\u672A\u8DDF\u8E2A)",
|
|
47408
|
+
{ toolSessionId }
|
|
47409
|
+
);
|
|
47410
|
+
return true;
|
|
47411
|
+
}
|
|
47412
|
+
const result = state.screenIdle.isIdle();
|
|
47413
|
+
this.tuiOpts.screenIdleProbeLogger?.info("canAcceptTurnEnd", {
|
|
47414
|
+
toolSessionId,
|
|
47415
|
+
result
|
|
47416
|
+
});
|
|
47417
|
+
return result;
|
|
47418
|
+
}
|
|
47350
47419
|
spawn(ctx) {
|
|
47351
47420
|
const args = buildTuiSpawnArgs(ctx, jsonlExistsForCtx(ctx));
|
|
47352
47421
|
const cmd = process.env.CLAUDE_BIN ?? "claude";
|
|
@@ -47404,18 +47473,26 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
47404
47473
|
const screenIdleObserver = observeScreenIdle(surface, {
|
|
47405
47474
|
idleMs: SCREEN_IDLE_MS,
|
|
47406
47475
|
onIdle: () => {
|
|
47407
|
-
if (!ctx.toolSessionId || !this.tuiOpts.
|
|
47408
|
-
this.tuiLogger?.debug("screen-idle \u2192
|
|
47409
|
-
this.tuiOpts.
|
|
47476
|
+
if (!ctx.toolSessionId || !this.tuiOpts.onScreenIdle) return;
|
|
47477
|
+
this.tuiLogger?.debug("screen-idle \u2192 notifyScreenIdle", { toolSessionId: ctx.toolSessionId });
|
|
47478
|
+
this.tuiOpts.onScreenIdle(ctx.toolSessionId);
|
|
47410
47479
|
},
|
|
47411
47480
|
getPopupVisible: () => popupObserver.visibleKind !== null,
|
|
47412
|
-
//
|
|
47413
|
-
|
|
47414
|
-
|
|
47481
|
+
// 取证 probe(可选,装配处传独立 file-only logger,跟主 daemon.log 解耦)
|
|
47482
|
+
...this.tuiOpts.screenIdleProbeLogger ? {
|
|
47483
|
+
probeLogger: this.tuiOpts.screenIdleProbeLogger,
|
|
47484
|
+
probeLabel: ctx.toolSessionId ?? "<no-tsid>"
|
|
47485
|
+
} : {}
|
|
47415
47486
|
});
|
|
47416
47487
|
if (ctx.toolSessionId && this.tuiOpts.onSurfaceRegister) {
|
|
47417
47488
|
this.tuiOpts.onSurfaceRegister(ctx.toolSessionId, surface);
|
|
47418
47489
|
}
|
|
47490
|
+
if (ctx.toolSessionId) {
|
|
47491
|
+
this.tuiStates.set(ctx.toolSessionId, {
|
|
47492
|
+
screenIdle: screenIdleObserver,
|
|
47493
|
+
popup: popupObserver
|
|
47494
|
+
});
|
|
47495
|
+
}
|
|
47419
47496
|
let chunkSeq = 0;
|
|
47420
47497
|
if (ctx.toolSessionId && this.tuiOpts.onPtyReplayRegister) {
|
|
47421
47498
|
this.tuiOpts.onPtyReplayRegister(ctx.toolSessionId, async () => {
|
|
@@ -47461,6 +47538,9 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
47461
47538
|
readyObserver.dispose();
|
|
47462
47539
|
popupObserver.dispose();
|
|
47463
47540
|
screenIdleObserver.dispose();
|
|
47541
|
+
if (ctx.toolSessionId) {
|
|
47542
|
+
this.tuiStates.delete(ctx.toolSessionId);
|
|
47543
|
+
}
|
|
47464
47544
|
if (ctx.toolSessionId && this.tuiOpts.onSurfaceUnregister) {
|
|
47465
47545
|
this.tuiOpts.onSurfaceUnregister(ctx.toolSessionId);
|
|
47466
47546
|
}
|
|
@@ -47743,7 +47823,7 @@ async function writeInboxMcpConfig(args) {
|
|
|
47743
47823
|
// src/shift/store.ts
|
|
47744
47824
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
47745
47825
|
var import_node_path19 = __toESM(require("path"), 1);
|
|
47746
|
-
var
|
|
47826
|
+
var import_node_crypto6 = require("crypto");
|
|
47747
47827
|
|
|
47748
47828
|
// src/shift/constants.ts
|
|
47749
47829
|
var MAX_RUNS_PER_SHIFT = 30;
|
|
@@ -47839,7 +47919,7 @@ function createShiftStore(deps) {
|
|
|
47839
47919
|
const nextRunAtMs = computeNextRunAtMs(input.schedule, now) ?? void 0;
|
|
47840
47920
|
const shift = {
|
|
47841
47921
|
...input,
|
|
47842
|
-
id: (0,
|
|
47922
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
47843
47923
|
createdAtMs: now,
|
|
47844
47924
|
updatedAtMs: now,
|
|
47845
47925
|
state: { nextRunAtMs },
|
|
@@ -48615,13 +48695,13 @@ function mapSkillsListResponse(res) {
|
|
|
48615
48695
|
const r = s ?? {};
|
|
48616
48696
|
const name = str3(r.name);
|
|
48617
48697
|
if (!name) continue;
|
|
48618
|
-
const
|
|
48698
|
+
const path68 = str3(r.path);
|
|
48619
48699
|
const description = str3(r.description);
|
|
48620
48700
|
const isPlugin = name.includes(":");
|
|
48621
48701
|
out.push({
|
|
48622
48702
|
name,
|
|
48623
48703
|
source: isPlugin ? "plugin" : "project",
|
|
48624
|
-
...
|
|
48704
|
+
...path68 ? { path: path68 } : {},
|
|
48625
48705
|
...description ? { description } : {},
|
|
48626
48706
|
...isPlugin ? { plugin: name.split(":")[0] } : {}
|
|
48627
48707
|
});
|
|
@@ -50415,23 +50495,6 @@ var ContactStore = class {
|
|
|
50415
50495
|
this.flush();
|
|
50416
50496
|
return true;
|
|
50417
50497
|
}
|
|
50418
|
-
/**
|
|
50419
|
-
* 更新单条 contact 的 SSH 授权(PR: contact-ssh-sandbox)。对齐 setPin pattern:
|
|
50420
|
-
* store 只做原始 mutation,不做业务校验(如"sshAllowed=false 时清空 exposedDirs")——
|
|
50421
|
-
* 那是 handler / UI 的责任。数组语义是完全替换(不 append)。
|
|
50422
|
-
* @returns 是否命中:deviceId 不存在返 false;命中即 flush.
|
|
50423
|
-
*/
|
|
50424
|
-
setSshAccess(deviceId, opts) {
|
|
50425
|
-
const existing = this.contacts.get(deviceId);
|
|
50426
|
-
if (!existing) return false;
|
|
50427
|
-
this.contacts.set(deviceId, {
|
|
50428
|
-
...existing,
|
|
50429
|
-
sshAllowed: opts.sshAllowed,
|
|
50430
|
-
exposedDirs: opts.exposedDirs
|
|
50431
|
-
});
|
|
50432
|
-
this.flush();
|
|
50433
|
-
return true;
|
|
50434
|
-
}
|
|
50435
50498
|
flush() {
|
|
50436
50499
|
const file = path30.join(this.dataDir, FILE_NAME);
|
|
50437
50500
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
@@ -50579,9 +50642,7 @@ async function autoReverseContact(args) {
|
|
|
50579
50642
|
connectToken: "",
|
|
50580
50643
|
grants,
|
|
50581
50644
|
addedAt: now(),
|
|
50582
|
-
pinnedAt: null
|
|
50583
|
-
sshAllowed: false,
|
|
50584
|
-
exposedDirs: []
|
|
50645
|
+
pinnedAt: null
|
|
50585
50646
|
};
|
|
50586
50647
|
args.store.upsert(base);
|
|
50587
50648
|
args.broadcast({ type: "contact:added", contact: base });
|
|
@@ -50810,7 +50871,7 @@ function lookupMime(filePathOrName) {
|
|
|
50810
50871
|
}
|
|
50811
50872
|
|
|
50812
50873
|
// src/attachment/sign-url.ts
|
|
50813
|
-
var
|
|
50874
|
+
var import_node_crypto7 = __toESM(require("crypto"), 1);
|
|
50814
50875
|
var HMAC_ALGO = "sha256";
|
|
50815
50876
|
function base64urlEncode(buf) {
|
|
50816
50877
|
const b2 = typeof buf === "string" ? Buffer.from(buf, "utf8") : buf;
|
|
@@ -50827,7 +50888,7 @@ function decodeAbsPathFromUrl(encoded) {
|
|
|
50827
50888
|
}
|
|
50828
50889
|
function computeSig(secret, absPath, e) {
|
|
50829
50890
|
const msg = e === null ? absPath : `${absPath}|${e}`;
|
|
50830
|
-
return
|
|
50891
|
+
return import_node_crypto7.default.createHmac(HMAC_ALGO, secret).update(msg).digest();
|
|
50831
50892
|
}
|
|
50832
50893
|
function signUrlParts(secret, absPath, ttlSeconds, now = Date.now) {
|
|
50833
50894
|
const e = ttlSeconds === null ? null : Math.floor(now() / 1e3) + ttlSeconds;
|
|
@@ -50862,7 +50923,7 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
|
|
|
50862
50923
|
if (provided.length !== expected.length) {
|
|
50863
50924
|
return { ok: false, code: "BAD_SIG" };
|
|
50864
50925
|
}
|
|
50865
|
-
if (!
|
|
50926
|
+
if (!import_node_crypto7.default.timingSafeEqual(provided, expected)) {
|
|
50866
50927
|
return { ok: false, code: "BAD_SIG" };
|
|
50867
50928
|
}
|
|
50868
50929
|
if (e !== null && now() / 1e3 > e) {
|
|
@@ -50874,7 +50935,7 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
|
|
|
50874
50935
|
// src/attachment/upload.ts
|
|
50875
50936
|
var import_node_fs25 = __toESM(require("fs"), 1);
|
|
50876
50937
|
var import_node_path25 = __toESM(require("path"), 1);
|
|
50877
|
-
var
|
|
50938
|
+
var import_node_crypto8 = __toESM(require("crypto"), 1);
|
|
50878
50939
|
var import_promises2 = require("stream/promises");
|
|
50879
50940
|
var UploadError = class extends Error {
|
|
50880
50941
|
constructor(code, message) {
|
|
@@ -50898,11 +50959,11 @@ async function writeUploadedAttachment(args) {
|
|
|
50898
50959
|
} catch (err) {
|
|
50899
50960
|
throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
|
|
50900
50961
|
}
|
|
50901
|
-
const hasher =
|
|
50962
|
+
const hasher = import_node_crypto8.default.createHash("sha256");
|
|
50902
50963
|
let actualSize = 0;
|
|
50903
50964
|
const tmpPath = import_node_path25.default.join(
|
|
50904
50965
|
attachmentsRoot,
|
|
50905
|
-
`.upload-${process.pid}-${Date.now()}-${
|
|
50966
|
+
`.upload-${process.pid}-${Date.now()}-${import_node_crypto8.default.randomBytes(4).toString("hex")}`
|
|
50906
50967
|
);
|
|
50907
50968
|
try {
|
|
50908
50969
|
await (0, import_promises2.pipeline)(
|
|
@@ -51778,7 +51839,7 @@ function runAttachmentGc(args) {
|
|
|
51778
51839
|
// src/attachment/group.ts
|
|
51779
51840
|
var import_node_fs28 = __toESM(require("fs"), 1);
|
|
51780
51841
|
var import_node_path29 = __toESM(require("path"), 1);
|
|
51781
|
-
var
|
|
51842
|
+
var import_node_crypto9 = __toESM(require("crypto"), 1);
|
|
51782
51843
|
init_protocol();
|
|
51783
51844
|
var GroupFileStore = class {
|
|
51784
51845
|
dataDir;
|
|
@@ -51867,7 +51928,7 @@ var GroupFileStore = class {
|
|
|
51867
51928
|
entries[idx] = next;
|
|
51868
51929
|
} else {
|
|
51869
51930
|
next = {
|
|
51870
|
-
id: `gf-${
|
|
51931
|
+
id: `gf-${import_node_crypto9.default.randomBytes(6).toString("base64url")}`,
|
|
51871
51932
|
relPath: input.relPath,
|
|
51872
51933
|
from: input.from,
|
|
51873
51934
|
label: input.label,
|
|
@@ -51986,7 +52047,7 @@ function readDaemonSourceFromEnv(env = process.env) {
|
|
|
51986
52047
|
// src/tunnel/tunnel-manager.ts
|
|
51987
52048
|
var import_node_fs33 = __toESM(require("fs"), 1);
|
|
51988
52049
|
var import_node_path34 = __toESM(require("path"), 1);
|
|
51989
|
-
var
|
|
52050
|
+
var import_node_crypto10 = __toESM(require("crypto"), 1);
|
|
51990
52051
|
var import_node_child_process9 = require("child_process");
|
|
51991
52052
|
|
|
51992
52053
|
// src/tunnel/tunnel-store.ts
|
|
@@ -52485,7 +52546,7 @@ var TunnelManager = class {
|
|
|
52485
52546
|
override: this.deps.frpcBinaryOverride ?? void 0
|
|
52486
52547
|
});
|
|
52487
52548
|
const tomlPath = import_node_path34.default.join(this.deps.dataDir, "frpc.toml");
|
|
52488
|
-
const proxyName = `clawd-${t.subdomain}-${localPort}-${
|
|
52549
|
+
const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto10.default.randomBytes(3).toString("hex")}`;
|
|
52489
52550
|
const toml = buildFrpcToml({
|
|
52490
52551
|
serverAddr: t.frpsHost,
|
|
52491
52552
|
serverPort: t.frpsPort,
|
|
@@ -52581,510 +52642,29 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
52581
52642
|
});
|
|
52582
52643
|
}
|
|
52583
52644
|
|
|
52584
|
-
// src/sshd/sshd-manager.ts
|
|
52585
|
-
var import_node_fs36 = __toESM(require("fs"), 1);
|
|
52586
|
-
var import_node_path37 = __toESM(require("path"), 1);
|
|
52587
|
-
var import_node_child_process11 = require("child_process");
|
|
52588
|
-
|
|
52589
|
-
// src/sshd/sshd-config.ts
|
|
52590
|
-
function buildSshdConfig(input) {
|
|
52591
|
-
const lines = [
|
|
52592
|
-
`ListenAddress ${input.listenAddress}`,
|
|
52593
|
-
`Port ${input.port}`,
|
|
52594
|
-
`HostKey ${input.hostKeyPath}`,
|
|
52595
|
-
`PidFile ${input.pidFilePath}`,
|
|
52596
|
-
`AuthorizedKeysFile ${input.authorizedKeysFile}`,
|
|
52597
|
-
`PubkeyAuthentication yes`,
|
|
52598
|
-
`PasswordAuthentication no`,
|
|
52599
|
-
`ChallengeResponseAuthentication no`,
|
|
52600
|
-
`KbdInteractiveAuthentication no`,
|
|
52601
|
-
`PermitRootLogin no`,
|
|
52602
|
-
`StrictModes no`,
|
|
52603
|
-
`UsePAM no`,
|
|
52604
|
-
`LogLevel INFO`,
|
|
52605
|
-
`Subsystem sftp internal-sftp`
|
|
52606
|
-
];
|
|
52607
|
-
return lines.join("\n") + "\n";
|
|
52608
|
-
}
|
|
52609
|
-
|
|
52610
|
-
// src/sshd/sshd-process.ts
|
|
52611
|
-
var import_node_fs34 = __toESM(require("fs"), 1);
|
|
52612
|
-
var import_node_path35 = __toESM(require("path"), 1);
|
|
52613
|
-
var import_node_child_process10 = require("child_process");
|
|
52614
|
-
function sshdPidFilePath(dataDir) {
|
|
52615
|
-
return import_node_path35.default.join(dataDir, "sshd", "sshd.pid");
|
|
52616
|
-
}
|
|
52617
|
-
function writeSshdPid(dataDir, pid) {
|
|
52618
|
-
try {
|
|
52619
|
-
const p2 = sshdPidFilePath(dataDir);
|
|
52620
|
-
import_node_fs34.default.mkdirSync(import_node_path35.default.dirname(p2), { recursive: true, mode: 448 });
|
|
52621
|
-
import_node_fs34.default.writeFileSync(p2, String(pid), { mode: 384 });
|
|
52622
|
-
} catch {
|
|
52623
|
-
}
|
|
52624
|
-
}
|
|
52625
|
-
function clearSshdPid(dataDir) {
|
|
52626
|
-
try {
|
|
52627
|
-
import_node_fs34.default.unlinkSync(sshdPidFilePath(dataDir));
|
|
52628
|
-
} catch {
|
|
52629
|
-
}
|
|
52630
|
-
}
|
|
52631
|
-
function defaultIsPidAlive2(pid) {
|
|
52632
|
-
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
52633
|
-
try {
|
|
52634
|
-
process.kill(pid, 0);
|
|
52635
|
-
return true;
|
|
52636
|
-
} catch (err) {
|
|
52637
|
-
const code = err.code;
|
|
52638
|
-
return code === "EPERM";
|
|
52639
|
-
}
|
|
52640
|
-
}
|
|
52641
|
-
function defaultReadPidFile2(file) {
|
|
52642
|
-
try {
|
|
52643
|
-
return import_node_fs34.default.readFileSync(file, "utf8");
|
|
52644
|
-
} catch {
|
|
52645
|
-
return null;
|
|
52646
|
-
}
|
|
52647
|
-
}
|
|
52648
|
-
function defaultKillPid2(pid, signal) {
|
|
52649
|
-
try {
|
|
52650
|
-
process.kill(pid, signal);
|
|
52651
|
-
} catch {
|
|
52652
|
-
}
|
|
52653
|
-
}
|
|
52654
|
-
function defaultSleep2(ms) {
|
|
52655
|
-
return new Promise((r) => setTimeout(r, ms));
|
|
52656
|
-
}
|
|
52657
|
-
async function killStaleSshd(deps) {
|
|
52658
|
-
const pidFile = sshdPidFilePath(deps.dataDir);
|
|
52659
|
-
const configPath = import_node_path35.default.join(deps.dataDir, "sshd", "sshd_config");
|
|
52660
|
-
const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile2;
|
|
52661
|
-
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive2;
|
|
52662
|
-
const killPid = deps.killPidImpl ?? defaultKillPid2;
|
|
52663
|
-
const scanPids = deps.scanSshdPidsImpl ?? ((cp) => defaultScanSshdPidsByCmdline(cp, deps.logger));
|
|
52664
|
-
const sleep2 = deps.sleepImpl ?? defaultSleep2;
|
|
52665
|
-
const victims = /* @__PURE__ */ new Set();
|
|
52666
|
-
const raw = readPidFile(pidFile);
|
|
52667
|
-
if (raw) {
|
|
52668
|
-
const pid = parseInt(raw.trim(), 10);
|
|
52669
|
-
if (Number.isFinite(pid) && pid > 0 && pid !== deps.ownPid && isAlive(pid)) {
|
|
52670
|
-
victims.add(pid);
|
|
52671
|
-
}
|
|
52672
|
-
}
|
|
52673
|
-
try {
|
|
52674
|
-
const scanned = await scanPids(configPath);
|
|
52675
|
-
for (const pid of scanned) {
|
|
52676
|
-
if (pid > 0 && pid !== deps.ownPid && isAlive(pid)) victims.add(pid);
|
|
52677
|
-
}
|
|
52678
|
-
} catch (e) {
|
|
52679
|
-
deps.logger?.warn("sshd: stale-sshd cmdline scan failed", { err: e.message });
|
|
52680
|
-
}
|
|
52681
|
-
if (victims.size === 0) {
|
|
52682
|
-
try {
|
|
52683
|
-
import_node_fs34.default.unlinkSync(pidFile);
|
|
52684
|
-
} catch {
|
|
52685
|
-
}
|
|
52686
|
-
return;
|
|
52687
|
-
}
|
|
52688
|
-
for (const pid of victims) {
|
|
52689
|
-
deps.logger?.warn("sshd: killing stale sshd before respawn", { pid });
|
|
52690
|
-
killPid(pid, "SIGKILL");
|
|
52691
|
-
}
|
|
52692
|
-
await sleep2(deps.reapWaitMs ?? 300);
|
|
52693
|
-
try {
|
|
52694
|
-
import_node_fs34.default.unlinkSync(pidFile);
|
|
52695
|
-
} catch {
|
|
52696
|
-
}
|
|
52697
|
-
}
|
|
52698
|
-
async function defaultScanSshdPidsByCmdline(configPath, logger) {
|
|
52699
|
-
if (process.platform === "win32") return [];
|
|
52700
|
-
return new Promise((resolve6) => {
|
|
52701
|
-
const ps = (0, import_node_child_process10.spawn)("ps", ["-axo", "pid=,command="], { stdio: ["ignore", "pipe", "ignore"] });
|
|
52702
|
-
let buf = "";
|
|
52703
|
-
ps.stdout.on("data", (c) => {
|
|
52704
|
-
buf += c.toString();
|
|
52705
|
-
});
|
|
52706
|
-
ps.on("exit", () => {
|
|
52707
|
-
const pids = [];
|
|
52708
|
-
for (const line of buf.split("\n")) {
|
|
52709
|
-
const m2 = /^\s*(\d+)\s+(.*)$/.exec(line);
|
|
52710
|
-
if (!m2) continue;
|
|
52711
|
-
const cmd = m2[2];
|
|
52712
|
-
if (!/\bsshd\b/.test(cmd)) continue;
|
|
52713
|
-
if (!cmd.includes(configPath)) continue;
|
|
52714
|
-
const pid = parseInt(m2[1], 10);
|
|
52715
|
-
if (Number.isFinite(pid) && pid > 0) pids.push(pid);
|
|
52716
|
-
}
|
|
52717
|
-
resolve6(pids);
|
|
52718
|
-
});
|
|
52719
|
-
ps.on("error", (e) => {
|
|
52720
|
-
logger?.warn("sshd: ps scan failed", { err: e.message });
|
|
52721
|
-
resolve6([]);
|
|
52722
|
-
});
|
|
52723
|
-
});
|
|
52724
|
-
}
|
|
52725
|
-
|
|
52726
|
-
// src/sshd/jail-script.ts
|
|
52727
|
-
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
52728
|
-
var import_node_path36 = __toESM(require("path"), 1);
|
|
52729
|
-
var CLAWD_SSH_JAIL_SCRIPT = String.raw`#!/usr/bin/env bash
|
|
52730
|
-
# clawd-ssh-jail — SSH reverse access sandbox wrapper (managed by clawd; do not edit)
|
|
52731
|
-
#
|
|
52732
|
-
# 由 sshd authorized_keys 的 command= 强制入口调用。
|
|
52733
|
-
# 用法: sshd 会以 \`clawd-ssh-jail <deviceId>\` 起本脚本;$SSH_ORIGINAL_COMMAND = client
|
|
52734
|
-
# 真实请求(interactive shell 时为空)。
|
|
52735
|
-
#
|
|
52736
|
-
# 职责:
|
|
52737
|
-
# 1. 读 ~/.clawd/contacts.json 找 contact.exposedDirs
|
|
52738
|
-
# 2. macOS 用 sandbox-exec + sbpl; Linux 用 bwrap
|
|
52739
|
-
# 3. exec 沙箱 shell
|
|
52740
|
-
|
|
52741
|
-
set -euo pipefail
|
|
52742
|
-
|
|
52743
|
-
DEVICE_ID="\${1:-}"
|
|
52744
|
-
if [ -z "$DEVICE_ID" ]; then
|
|
52745
|
-
echo "clawd-ssh-jail: missing deviceId" >&2
|
|
52746
|
-
exit 1
|
|
52747
|
-
fi
|
|
52748
|
-
|
|
52749
|
-
CONTACTS="\${HOME}/.clawd/contacts.json"
|
|
52750
|
-
if [ ! -f "$CONTACTS" ]; then
|
|
52751
|
-
echo "clawd-ssh-jail: contacts.json missing" >&2
|
|
52752
|
-
exit 1
|
|
52753
|
-
fi
|
|
52754
|
-
|
|
52755
|
-
# 读 contact 的 exposedDirs (mac/linux 都自带 python3)
|
|
52756
|
-
EXPOSED_JSON=$(python3 -c "
|
|
52757
|
-
import json, sys
|
|
52758
|
-
with open('$CONTACTS') as f:
|
|
52759
|
-
data = json.load(f)
|
|
52760
|
-
for c in data.get('contacts', []):
|
|
52761
|
-
if c.get('deviceId') == '$DEVICE_ID':
|
|
52762
|
-
if not c.get('sshAllowed'):
|
|
52763
|
-
print('DENIED', file=sys.stderr); sys.exit(2)
|
|
52764
|
-
for d in c.get('exposedDirs', []):
|
|
52765
|
-
print(d)
|
|
52766
|
-
sys.exit(0)
|
|
52767
|
-
sys.exit(3)
|
|
52768
|
-
")
|
|
52769
|
-
|
|
52770
|
-
if [ -z "$EXPOSED_JSON" ]; then
|
|
52771
|
-
echo "clawd-ssh-jail: contact not found or no exposed dirs" >&2
|
|
52772
|
-
exit 1
|
|
52773
|
-
fi
|
|
52774
|
-
|
|
52775
|
-
# 校验路径安全(bash 侧二次防御)
|
|
52776
|
-
while IFS= read -r line; do
|
|
52777
|
-
case "$line" in
|
|
52778
|
-
/*) : ;;
|
|
52779
|
-
*) echo "clawd-ssh-jail: bad path: $line" >&2; exit 1 ;;
|
|
52780
|
-
esac
|
|
52781
|
-
case "$line" in
|
|
52782
|
-
*[\"\'\`\$\;\|\&\(\)\{\}\[\]\<\>\*\?]*)
|
|
52783
|
-
echo "clawd-ssh-jail: unsafe path: $line" >&2; exit 1 ;;
|
|
52784
|
-
esac
|
|
52785
|
-
done <<< "$EXPOSED_JSON"
|
|
52786
|
-
|
|
52787
|
-
CMD="\${SSH_ORIGINAL_COMMAND:-}"
|
|
52788
|
-
if [ -z "$CMD" ]; then
|
|
52789
|
-
SHELL_CMD=(bash --login)
|
|
52790
|
-
else
|
|
52791
|
-
SHELL_CMD=(bash -c "$CMD")
|
|
52792
|
-
fi
|
|
52793
|
-
|
|
52794
|
-
case "$(uname -s)" in
|
|
52795
|
-
Darwin)
|
|
52796
|
-
POLICY="(version 1)
|
|
52797
|
-
(deny default)
|
|
52798
|
-
(allow process*)
|
|
52799
|
-
(allow signal (target self))
|
|
52800
|
-
(allow sysctl-read)
|
|
52801
|
-
(allow mach-lookup)
|
|
52802
|
-
(allow file-read-metadata)
|
|
52803
|
-
(allow network*)
|
|
52804
|
-
(allow file-read* (subpath \"/usr\"))
|
|
52805
|
-
(allow file-read* (subpath \"/bin\"))
|
|
52806
|
-
(allow file-read* (subpath \"/sbin\"))
|
|
52807
|
-
(allow file-read* (subpath \"/System\"))
|
|
52808
|
-
(allow file-read* (subpath \"/Library\"))
|
|
52809
|
-
(allow file-read* (subpath \"/etc\"))
|
|
52810
|
-
(allow file-read* (subpath \"/private/etc\"))"
|
|
52811
|
-
while IFS= read -r d; do
|
|
52812
|
-
POLICY+="
|
|
52813
|
-
(allow file-read* file-write* (subpath \"$d\"))"
|
|
52814
|
-
done <<< "$EXPOSED_JSON"
|
|
52815
|
-
exec sandbox-exec -p "$POLICY" "\${SHELL_CMD[@]}"
|
|
52816
|
-
;;
|
|
52817
|
-
Linux)
|
|
52818
|
-
BWRAP_ARGS=(
|
|
52819
|
-
--unshare-user --unshare-ipc --unshare-pid --unshare-uts
|
|
52820
|
-
--die-with-parent
|
|
52821
|
-
--proc /proc --dev /dev --tmpfs /tmp
|
|
52822
|
-
--ro-bind /usr /usr --ro-bind /bin /bin --ro-bind /sbin /sbin
|
|
52823
|
-
--ro-bind /lib /lib --ro-bind /etc /etc
|
|
52824
|
-
)
|
|
52825
|
-
if [ -d /lib64 ]; then BWRAP_ARGS+=(--ro-bind /lib64 /lib64); fi
|
|
52826
|
-
while IFS= read -r d; do
|
|
52827
|
-
BWRAP_ARGS+=(--bind "$d" "$d")
|
|
52828
|
-
done <<< "$EXPOSED_JSON"
|
|
52829
|
-
exec bwrap "\${BWRAP_ARGS[@]}" "\${SHELL_CMD[@]}"
|
|
52830
|
-
;;
|
|
52831
|
-
*)
|
|
52832
|
-
echo "clawd-ssh-jail: unsupported OS $(uname -s)" >&2
|
|
52833
|
-
exit 1
|
|
52834
|
-
;;
|
|
52835
|
-
esac
|
|
52836
|
-
`;
|
|
52837
|
-
function ensureJailScript(dataDir) {
|
|
52838
|
-
const binDir = import_node_path36.default.join(dataDir, "bin");
|
|
52839
|
-
import_node_fs35.default.mkdirSync(binDir, { recursive: true, mode: 493 });
|
|
52840
|
-
const target = import_node_path36.default.join(binDir, "clawd-ssh-jail");
|
|
52841
|
-
import_node_fs35.default.writeFileSync(target, CLAWD_SSH_JAIL_SCRIPT, { mode: 493 });
|
|
52842
|
-
return target;
|
|
52843
|
-
}
|
|
52844
|
-
|
|
52845
|
-
// src/sshd/sshd-manager.ts
|
|
52846
|
-
var SshdManager = class {
|
|
52847
|
-
constructor(deps) {
|
|
52848
|
-
this.deps = deps;
|
|
52849
|
-
this.sshdDir = import_node_path37.default.join(deps.dataDir, "sshd");
|
|
52850
|
-
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
52851
|
-
}
|
|
52852
|
-
deps;
|
|
52853
|
-
proc = null;
|
|
52854
|
-
sshdDir;
|
|
52855
|
-
stopping = false;
|
|
52856
|
-
exitHookInstalled = false;
|
|
52857
|
-
startupTimeoutMs;
|
|
52858
|
-
get port() {
|
|
52859
|
-
return this.deps.port;
|
|
52860
|
-
}
|
|
52861
|
-
async start() {
|
|
52862
|
-
const { logger } = this.deps;
|
|
52863
|
-
await (this.deps.killStaleImpl ?? killStaleSshd)({
|
|
52864
|
-
dataDir: this.deps.dataDir,
|
|
52865
|
-
ownPid: process.pid,
|
|
52866
|
-
logger
|
|
52867
|
-
});
|
|
52868
|
-
import_node_fs36.default.mkdirSync(this.sshdDir, { recursive: true, mode: 448 });
|
|
52869
|
-
import_node_fs36.default.mkdirSync(import_node_path37.default.join(this.sshdDir, "authorized_keys.d"), { recursive: true, mode: 448 });
|
|
52870
|
-
ensureJailScript(this.deps.dataDir);
|
|
52871
|
-
const hostKeyPath = import_node_path37.default.join(this.sshdDir, "host_key");
|
|
52872
|
-
if (!import_node_fs36.default.existsSync(hostKeyPath)) {
|
|
52873
|
-
await this.generateHostKey(hostKeyPath);
|
|
52874
|
-
}
|
|
52875
|
-
const akFile = import_node_path37.default.join(this.sshdDir, "authorized_keys.d", "clawd-contacts");
|
|
52876
|
-
if (!import_node_fs36.default.existsSync(akFile)) {
|
|
52877
|
-
import_node_fs36.default.writeFileSync(akFile, "", { mode: 384 });
|
|
52878
|
-
}
|
|
52879
|
-
const configPath = import_node_path37.default.join(this.sshdDir, "sshd_config");
|
|
52880
|
-
const config = buildSshdConfig({
|
|
52881
|
-
listenAddress: "127.0.0.1",
|
|
52882
|
-
port: this.deps.port,
|
|
52883
|
-
hostKeyPath,
|
|
52884
|
-
authorizedKeysFile: akFile,
|
|
52885
|
-
pidFilePath: import_node_path37.default.join(this.sshdDir, "sshd.pid")
|
|
52886
|
-
});
|
|
52887
|
-
import_node_fs36.default.writeFileSync(configPath, config, { mode: 384 });
|
|
52888
|
-
const sshdBin = this.deps.sshdBin ?? "/usr/sbin/sshd";
|
|
52889
|
-
const proc = (this.deps.spawnImpl ?? import_node_child_process11.spawn)(sshdBin, ["-D", "-e", "-f", configPath], {
|
|
52890
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
52891
|
-
});
|
|
52892
|
-
const logStream = import_node_fs36.default.createWriteStream(import_node_path37.default.join(this.sshdDir, "sshd.log"), {
|
|
52893
|
-
flags: "a",
|
|
52894
|
-
mode: 384
|
|
52895
|
-
});
|
|
52896
|
-
logStream.on("error", () => {
|
|
52897
|
-
});
|
|
52898
|
-
const tee = (c) => {
|
|
52899
|
-
logStream.write(String(c));
|
|
52900
|
-
};
|
|
52901
|
-
proc.stdout?.on("data", tee);
|
|
52902
|
-
proc.stderr?.on("data", tee);
|
|
52903
|
-
proc.once("exit", () => logStream.end());
|
|
52904
|
-
const ready = await waitForSshdReady(proc, this.startupTimeoutMs);
|
|
52905
|
-
if (!ready.ok) {
|
|
52906
|
-
try {
|
|
52907
|
-
proc.kill("SIGTERM");
|
|
52908
|
-
} catch {
|
|
52909
|
-
}
|
|
52910
|
-
const tail = ready.output.slice(-500);
|
|
52911
|
-
const msg = tail ? `${ready.error}
|
|
52912
|
-
${tail}` : ready.error;
|
|
52913
|
-
throw new Error(msg);
|
|
52914
|
-
}
|
|
52915
|
-
if (typeof proc.pid === "number") writeSshdPid(this.deps.dataDir, proc.pid);
|
|
52916
|
-
this.proc = proc;
|
|
52917
|
-
this.installProcessExitHandlersIfNeeded();
|
|
52918
|
-
this.attachExitListener(proc);
|
|
52919
|
-
logger?.info("sshd: up", { port: this.deps.port, pid: proc.pid ?? null });
|
|
52920
|
-
return { port: this.deps.port };
|
|
52921
|
-
}
|
|
52922
|
-
async stop() {
|
|
52923
|
-
this.stopping = true;
|
|
52924
|
-
const proc = this.proc;
|
|
52925
|
-
this.proc = null;
|
|
52926
|
-
if (!proc) {
|
|
52927
|
-
clearSshdPid(this.deps.dataDir);
|
|
52928
|
-
return;
|
|
52929
|
-
}
|
|
52930
|
-
proc.kill("SIGTERM");
|
|
52931
|
-
await new Promise((resolve6) => {
|
|
52932
|
-
const t = setTimeout(() => {
|
|
52933
|
-
try {
|
|
52934
|
-
proc.kill("SIGKILL");
|
|
52935
|
-
} catch {
|
|
52936
|
-
}
|
|
52937
|
-
resolve6();
|
|
52938
|
-
}, 5e3);
|
|
52939
|
-
proc.once("exit", () => {
|
|
52940
|
-
clearTimeout(t);
|
|
52941
|
-
resolve6();
|
|
52942
|
-
});
|
|
52943
|
-
});
|
|
52944
|
-
clearSshdPid(this.deps.dataDir);
|
|
52945
|
-
}
|
|
52946
|
-
killSync() {
|
|
52947
|
-
const proc = this.proc;
|
|
52948
|
-
this.proc = null;
|
|
52949
|
-
clearSshdPid(this.deps.dataDir);
|
|
52950
|
-
if (!proc) return;
|
|
52951
|
-
try {
|
|
52952
|
-
proc.kill("SIGTERM");
|
|
52953
|
-
} catch {
|
|
52954
|
-
}
|
|
52955
|
-
}
|
|
52956
|
-
attachExitListener(proc) {
|
|
52957
|
-
proc.on("exit", (code) => {
|
|
52958
|
-
this.deps.logger?.warn("sshd exited", { code });
|
|
52959
|
-
if (this.stopping) return;
|
|
52960
|
-
this.proc = null;
|
|
52961
|
-
this.deps.onSshdExit?.({ code });
|
|
52962
|
-
});
|
|
52963
|
-
}
|
|
52964
|
-
installProcessExitHandlersIfNeeded() {
|
|
52965
|
-
if (this.exitHookInstalled) return;
|
|
52966
|
-
if (this.deps.installProcessExitHandlers !== true) return;
|
|
52967
|
-
this.exitHookInstalled = true;
|
|
52968
|
-
const sync = () => this.killSync();
|
|
52969
|
-
process.once("exit", sync);
|
|
52970
|
-
process.once("SIGHUP", sync);
|
|
52971
|
-
process.once("uncaughtException", sync);
|
|
52972
|
-
}
|
|
52973
|
-
async generateHostKey(hostKeyPath) {
|
|
52974
|
-
const keygenBin = this.deps.keygenBin ?? "/usr/bin/ssh-keygen";
|
|
52975
|
-
await new Promise((resolve6, reject) => {
|
|
52976
|
-
const p2 = (this.deps.spawnImpl ?? import_node_child_process11.spawn)(
|
|
52977
|
-
keygenBin,
|
|
52978
|
-
["-t", "ed25519", "-f", hostKeyPath, "-N", "", "-q"],
|
|
52979
|
-
{ stdio: "ignore" }
|
|
52980
|
-
);
|
|
52981
|
-
p2.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`ssh-keygen exit ${code}`)));
|
|
52982
|
-
p2.on("error", reject);
|
|
52983
|
-
});
|
|
52984
|
-
try {
|
|
52985
|
-
import_node_fs36.default.chmodSync(hostKeyPath, 384);
|
|
52986
|
-
} catch {
|
|
52987
|
-
}
|
|
52988
|
-
}
|
|
52989
|
-
};
|
|
52990
|
-
async function waitForSshdReady(proc, timeoutMs) {
|
|
52991
|
-
return new Promise((resolve6) => {
|
|
52992
|
-
let settled = false;
|
|
52993
|
-
let buf = "";
|
|
52994
|
-
const finish = (r) => {
|
|
52995
|
-
if (settled) return;
|
|
52996
|
-
settled = true;
|
|
52997
|
-
cleanup();
|
|
52998
|
-
resolve6(r);
|
|
52999
|
-
};
|
|
53000
|
-
const onData = (chunk) => {
|
|
53001
|
-
buf += String(chunk);
|
|
53002
|
-
if (/Server listening on/i.test(buf)) finish({ ok: true });
|
|
53003
|
-
if (/fatal:/i.test(buf) || /error: Bind to port/i.test(buf)) {
|
|
53004
|
-
finish({ ok: false, error: "sshd startup failed", output: buf });
|
|
53005
|
-
}
|
|
53006
|
-
};
|
|
53007
|
-
const onExit = (code) => finish({ ok: false, error: `sshd exited before ready (code=${code})`, output: buf });
|
|
53008
|
-
const onErr = (err) => finish({ ok: false, error: `sshd spawn error: ${err.message}`, output: buf });
|
|
53009
|
-
const cleanup = () => {
|
|
53010
|
-
proc.stdout?.off("data", onData);
|
|
53011
|
-
proc.stderr?.off("data", onData);
|
|
53012
|
-
proc.off("exit", onExit);
|
|
53013
|
-
proc.off("error", onErr);
|
|
53014
|
-
clearTimeout(timer);
|
|
53015
|
-
};
|
|
53016
|
-
proc.stdout?.on("data", onData);
|
|
53017
|
-
proc.stderr?.on("data", onData);
|
|
53018
|
-
proc.on("exit", onExit);
|
|
53019
|
-
proc.on("error", onErr);
|
|
53020
|
-
const timer = setTimeout(
|
|
53021
|
-
() => finish({ ok: false, error: `sshd startup timeout after ${timeoutMs}ms`, output: buf }),
|
|
53022
|
-
timeoutMs
|
|
53023
|
-
);
|
|
53024
|
-
});
|
|
53025
|
-
}
|
|
53026
|
-
|
|
53027
|
-
// src/sshd/authorized-keys.ts
|
|
53028
|
-
var import_node_fs37 = __toESM(require("fs"), 1);
|
|
53029
|
-
var import_node_path38 = __toESM(require("path"), 1);
|
|
53030
|
-
var JAIL_BIN_PATH_ENV = "CLAWD_JAIL_BIN_PATH";
|
|
53031
|
-
var AUTHORIZED_KEYS_FILE = "clawd-contacts";
|
|
53032
|
-
function jailBinPath() {
|
|
53033
|
-
return process.env[JAIL_BIN_PATH_ENV] ?? import_node_path38.default.join(process.env.HOME ?? "", ".clawd", "bin", "clawd-ssh-jail");
|
|
53034
|
-
}
|
|
53035
|
-
function rebuildAuthorizedKeys(store, sshdDir) {
|
|
53036
|
-
const akDir = import_node_path38.default.join(sshdDir, "authorized_keys.d");
|
|
53037
|
-
const target = import_node_path38.default.join(akDir, AUTHORIZED_KEYS_FILE);
|
|
53038
|
-
import_node_fs37.default.mkdirSync(akDir, { recursive: true, mode: 448 });
|
|
53039
|
-
const lines = ["# managed by clawd; do not edit", ""];
|
|
53040
|
-
for (const c of store.list()) {
|
|
53041
|
-
if (!c.sshAllowed) continue;
|
|
53042
|
-
const safe = /^[A-Za-z0-9_.-]+$/.test(c.deviceId);
|
|
53043
|
-
if (!safe) continue;
|
|
53044
|
-
const pubkey = readIssuedPubkey(sshdDir, c.deviceId);
|
|
53045
|
-
if (!pubkey) continue;
|
|
53046
|
-
const bin = jailBinPath();
|
|
53047
|
-
lines.push(`command="${bin} ${c.deviceId}",restrict ${pubkey.trim()}`);
|
|
53048
|
-
lines.push(`# contact:${c.deviceId}`);
|
|
53049
|
-
}
|
|
53050
|
-
const body = lines.join("\n") + "\n";
|
|
53051
|
-
const tmp = `${target}.tmp-${process.pid}-${Date.now()}`;
|
|
53052
|
-
import_node_fs37.default.writeFileSync(tmp, body, { mode: 384 });
|
|
53053
|
-
import_node_fs37.default.renameSync(tmp, target);
|
|
53054
|
-
}
|
|
53055
|
-
function readIssuedPubkey(sshdDir, deviceId) {
|
|
53056
|
-
const safeId = deviceId.replace(/[\/\\]/g, "_");
|
|
53057
|
-
const p2 = import_node_path38.default.join(sshdDir, "keys", `${safeId}.ed25519.pub`);
|
|
53058
|
-
try {
|
|
53059
|
-
return import_node_fs37.default.readFileSync(p2, "utf8");
|
|
53060
|
-
} catch {
|
|
53061
|
-
return null;
|
|
53062
|
-
}
|
|
53063
|
-
}
|
|
53064
|
-
|
|
53065
52645
|
// src/tunnel/device-key.ts
|
|
53066
52646
|
var import_node_os14 = __toESM(require("os"), 1);
|
|
53067
|
-
var
|
|
53068
|
-
var
|
|
52647
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
52648
|
+
var import_node_crypto11 = __toESM(require("crypto"), 1);
|
|
53069
52649
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
53070
52650
|
function deriveStableDeviceKey(opts = {}) {
|
|
53071
52651
|
const hostname = opts.hostname ?? import_node_os14.default.hostname();
|
|
53072
52652
|
const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
|
|
53073
52653
|
const home = opts.home ?? import_node_os14.default.homedir();
|
|
53074
|
-
const defaultDataDir =
|
|
53075
|
-
const normalizedDataDir = opts.dataDir ?
|
|
52654
|
+
const defaultDataDir = import_node_path35.default.resolve(import_node_path35.default.join(home, ".clawd"));
|
|
52655
|
+
const normalizedDataDir = opts.dataDir ? import_node_path35.default.resolve(opts.dataDir) : null;
|
|
53076
52656
|
const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
|
|
53077
52657
|
const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
|
|
53078
|
-
return
|
|
52658
|
+
return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
53079
52659
|
}
|
|
53080
52660
|
|
|
53081
52661
|
// src/auth-store.ts
|
|
53082
|
-
var
|
|
53083
|
-
var
|
|
53084
|
-
var
|
|
52662
|
+
var import_node_fs34 = __toESM(require("fs"), 1);
|
|
52663
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
52664
|
+
var import_node_crypto12 = __toESM(require("crypto"), 1);
|
|
53085
52665
|
var AUTH_FILE_NAME = "auth.json";
|
|
53086
52666
|
function authFilePath(dataDir) {
|
|
53087
|
-
return
|
|
52667
|
+
return import_node_path36.default.join(dataDir, AUTH_FILE_NAME);
|
|
53088
52668
|
}
|
|
53089
52669
|
function loadOrCreateAuthFile(opts) {
|
|
53090
52670
|
const file = authFilePath(opts.dataDir);
|
|
@@ -53113,14 +52693,14 @@ function loadOrCreateAuthFile(opts) {
|
|
|
53113
52693
|
return next;
|
|
53114
52694
|
}
|
|
53115
52695
|
function defaultGenerateToken() {
|
|
53116
|
-
return
|
|
52696
|
+
return import_node_crypto12.default.randomBytes(32).toString("base64url");
|
|
53117
52697
|
}
|
|
53118
52698
|
function defaultGenerateOwnerPrincipalId() {
|
|
53119
|
-
return `owner-${
|
|
52699
|
+
return `owner-${import_node_crypto12.default.randomUUID()}`;
|
|
53120
52700
|
}
|
|
53121
52701
|
function readAuthFile(file) {
|
|
53122
52702
|
try {
|
|
53123
|
-
const raw =
|
|
52703
|
+
const raw = import_node_fs34.default.readFileSync(file, "utf8");
|
|
53124
52704
|
const parsed = JSON.parse(raw);
|
|
53125
52705
|
if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
|
|
53126
52706
|
return null;
|
|
@@ -53140,25 +52720,25 @@ function readAuthFile(file) {
|
|
|
53140
52720
|
}
|
|
53141
52721
|
}
|
|
53142
52722
|
function writeAuthFile(file, content) {
|
|
53143
|
-
|
|
53144
|
-
|
|
52723
|
+
import_node_fs34.default.mkdirSync(import_node_path36.default.dirname(file), { recursive: true });
|
|
52724
|
+
import_node_fs34.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
53145
52725
|
try {
|
|
53146
|
-
|
|
52726
|
+
import_node_fs34.default.chmodSync(file, 384);
|
|
53147
52727
|
} catch {
|
|
53148
52728
|
}
|
|
53149
52729
|
}
|
|
53150
52730
|
|
|
53151
52731
|
// src/owner-profile.ts
|
|
53152
|
-
var
|
|
52732
|
+
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
53153
52733
|
var import_node_os15 = __toESM(require("os"), 1);
|
|
53154
|
-
var
|
|
52734
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
53155
52735
|
var PROFILE_FILENAME = "profile.json";
|
|
53156
52736
|
function loadOwnerDisplayName(dataDir) {
|
|
53157
52737
|
const fallback = import_node_os15.default.userInfo().username;
|
|
53158
|
-
const profilePath =
|
|
52738
|
+
const profilePath = import_node_path37.default.join(dataDir, PROFILE_FILENAME);
|
|
53159
52739
|
let raw;
|
|
53160
52740
|
try {
|
|
53161
|
-
raw =
|
|
52741
|
+
raw = import_node_fs35.default.readFileSync(profilePath, "utf8");
|
|
53162
52742
|
} catch {
|
|
53163
52743
|
return fallback;
|
|
53164
52744
|
}
|
|
@@ -53181,18 +52761,18 @@ function loadOwnerDisplayName(dataDir) {
|
|
|
53181
52761
|
}
|
|
53182
52762
|
|
|
53183
52763
|
// src/feishu-auth/owner-identity-store.ts
|
|
53184
|
-
var
|
|
53185
|
-
var
|
|
52764
|
+
var import_node_fs36 = __toESM(require("fs"), 1);
|
|
52765
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
53186
52766
|
var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
|
|
53187
52767
|
var OwnerIdentityStore = class {
|
|
53188
52768
|
file;
|
|
53189
52769
|
constructor(dataDir) {
|
|
53190
|
-
this.file =
|
|
52770
|
+
this.file = import_node_path38.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
|
|
53191
52771
|
}
|
|
53192
52772
|
read() {
|
|
53193
52773
|
let raw;
|
|
53194
52774
|
try {
|
|
53195
|
-
raw =
|
|
52775
|
+
raw = import_node_fs36.default.readFileSync(this.file, "utf8");
|
|
53196
52776
|
} catch {
|
|
53197
52777
|
return null;
|
|
53198
52778
|
}
|
|
@@ -53220,16 +52800,16 @@ var OwnerIdentityStore = class {
|
|
|
53220
52800
|
};
|
|
53221
52801
|
}
|
|
53222
52802
|
write(record) {
|
|
53223
|
-
|
|
53224
|
-
|
|
52803
|
+
import_node_fs36.default.mkdirSync(import_node_path38.default.dirname(this.file), { recursive: true });
|
|
52804
|
+
import_node_fs36.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
|
|
53225
52805
|
try {
|
|
53226
|
-
|
|
52806
|
+
import_node_fs36.default.chmodSync(this.file, 384);
|
|
53227
52807
|
} catch {
|
|
53228
52808
|
}
|
|
53229
52809
|
}
|
|
53230
52810
|
clear() {
|
|
53231
52811
|
try {
|
|
53232
|
-
|
|
52812
|
+
import_node_fs36.default.unlinkSync(this.file);
|
|
53233
52813
|
} catch (err) {
|
|
53234
52814
|
const code = err?.code;
|
|
53235
52815
|
if (code !== "ENOENT") throw err;
|
|
@@ -53238,7 +52818,7 @@ var OwnerIdentityStore = class {
|
|
|
53238
52818
|
};
|
|
53239
52819
|
|
|
53240
52820
|
// src/feishu-auth/login-flow.ts
|
|
53241
|
-
var
|
|
52821
|
+
var import_node_crypto13 = __toESM(require("crypto"), 1);
|
|
53242
52822
|
var STATE_TTL_MS = 5 * 60 * 1e3;
|
|
53243
52823
|
var LoginFlow = class {
|
|
53244
52824
|
constructor(deps) {
|
|
@@ -53247,7 +52827,7 @@ var LoginFlow = class {
|
|
|
53247
52827
|
deps;
|
|
53248
52828
|
pendingStates = /* @__PURE__ */ new Map();
|
|
53249
52829
|
start() {
|
|
53250
|
-
const state =
|
|
52830
|
+
const state = import_node_crypto13.default.randomBytes(16).toString("base64url");
|
|
53251
52831
|
const now = (this.deps.now ?? Date.now)();
|
|
53252
52832
|
this.pendingStates.set(state, now);
|
|
53253
52833
|
this.gcExpired(now);
|
|
@@ -53350,9 +52930,9 @@ var CentralClientError = class extends Error {
|
|
|
53350
52930
|
code;
|
|
53351
52931
|
cause;
|
|
53352
52932
|
};
|
|
53353
|
-
async function centralRequest(opts,
|
|
52933
|
+
async function centralRequest(opts, path68, init) {
|
|
53354
52934
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
53355
|
-
const url = `${opts.api.replace(/\/+$/, "")}${
|
|
52935
|
+
const url = `${opts.api.replace(/\/+$/, "")}${path68}`;
|
|
53356
52936
|
const ctrl = new AbortController();
|
|
53357
52937
|
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
53358
52938
|
let res;
|
|
@@ -53494,8 +53074,8 @@ function verifyConnectToken(args) {
|
|
|
53494
53074
|
}
|
|
53495
53075
|
|
|
53496
53076
|
// src/feishu-auth/server-key.ts
|
|
53497
|
-
var
|
|
53498
|
-
var
|
|
53077
|
+
var fs46 = __toESM(require("fs"), 1);
|
|
53078
|
+
var path47 = __toESM(require("path"), 1);
|
|
53499
53079
|
var FILE_NAME2 = "server-signing-key.json";
|
|
53500
53080
|
var ServerKeyStore = class {
|
|
53501
53081
|
constructor(dataDir) {
|
|
@@ -53503,12 +53083,12 @@ var ServerKeyStore = class {
|
|
|
53503
53083
|
}
|
|
53504
53084
|
dataDir;
|
|
53505
53085
|
filePath() {
|
|
53506
|
-
return
|
|
53086
|
+
return path47.join(this.dataDir, FILE_NAME2);
|
|
53507
53087
|
}
|
|
53508
53088
|
/** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
|
|
53509
53089
|
read() {
|
|
53510
53090
|
try {
|
|
53511
|
-
const raw =
|
|
53091
|
+
const raw = fs46.readFileSync(this.filePath(), "utf8");
|
|
53512
53092
|
const parsed = JSON.parse(raw);
|
|
53513
53093
|
if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
|
|
53514
53094
|
return parsed.publicKeyPem;
|
|
@@ -53523,12 +53103,12 @@ var ServerKeyStore = class {
|
|
|
53523
53103
|
publicKeyPem,
|
|
53524
53104
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
53525
53105
|
};
|
|
53526
|
-
|
|
53527
|
-
|
|
53106
|
+
fs46.mkdirSync(this.dataDir, { recursive: true });
|
|
53107
|
+
fs46.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
|
|
53528
53108
|
}
|
|
53529
53109
|
clear() {
|
|
53530
53110
|
try {
|
|
53531
|
-
|
|
53111
|
+
fs46.unlinkSync(this.filePath());
|
|
53532
53112
|
} catch {
|
|
53533
53113
|
}
|
|
53534
53114
|
}
|
|
@@ -53541,12 +53121,12 @@ init_protocol();
|
|
|
53541
53121
|
init_protocol();
|
|
53542
53122
|
|
|
53543
53123
|
// src/session/fork.ts
|
|
53544
|
-
var
|
|
53124
|
+
var import_node_fs37 = __toESM(require("fs"), 1);
|
|
53545
53125
|
var import_node_os16 = __toESM(require("os"), 1);
|
|
53546
|
-
var
|
|
53126
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
53547
53127
|
init_claude_history();
|
|
53548
53128
|
function readJsonlEntries(file) {
|
|
53549
|
-
const raw =
|
|
53129
|
+
const raw = import_node_fs37.default.readFileSync(file, "utf8");
|
|
53550
53130
|
const out = [];
|
|
53551
53131
|
for (const line of raw.split("\n")) {
|
|
53552
53132
|
const t = line.trim();
|
|
@@ -53559,10 +53139,10 @@ function readJsonlEntries(file) {
|
|
|
53559
53139
|
return out;
|
|
53560
53140
|
}
|
|
53561
53141
|
function forkSession(input) {
|
|
53562
|
-
const baseDir = input.baseDir ??
|
|
53563
|
-
const projectDir =
|
|
53564
|
-
const sourceFile =
|
|
53565
|
-
if (!
|
|
53142
|
+
const baseDir = input.baseDir ?? import_node_path39.default.join(import_node_os16.default.homedir(), ".claude");
|
|
53143
|
+
const projectDir = import_node_path39.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
53144
|
+
const sourceFile = import_node_path39.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
53145
|
+
if (!import_node_fs37.default.existsSync(sourceFile)) {
|
|
53566
53146
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
53567
53147
|
}
|
|
53568
53148
|
const entries = readJsonlEntries(sourceFile);
|
|
@@ -53592,9 +53172,9 @@ function forkSession(input) {
|
|
|
53592
53172
|
}
|
|
53593
53173
|
forkedLines.push(JSON.stringify(forked));
|
|
53594
53174
|
}
|
|
53595
|
-
const forkedFilePath =
|
|
53596
|
-
|
|
53597
|
-
|
|
53175
|
+
const forkedFilePath = import_node_path39.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
53176
|
+
import_node_fs37.default.mkdirSync(projectDir, { recursive: true });
|
|
53177
|
+
import_node_fs37.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
53598
53178
|
return { forkedToolSessionId, forkedFilePath };
|
|
53599
53179
|
}
|
|
53600
53180
|
|
|
@@ -53946,7 +53526,7 @@ function buildPermissionHandlers(deps) {
|
|
|
53946
53526
|
}
|
|
53947
53527
|
|
|
53948
53528
|
// src/handlers/history.ts
|
|
53949
|
-
var
|
|
53529
|
+
var path50 = __toESM(require("path"), 1);
|
|
53950
53530
|
init_protocol();
|
|
53951
53531
|
|
|
53952
53532
|
// src/session/recent-dirs.ts
|
|
@@ -53964,7 +53544,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
53964
53544
|
}
|
|
53965
53545
|
|
|
53966
53546
|
// src/permission/persona-paths.ts
|
|
53967
|
-
var
|
|
53547
|
+
var path49 = __toESM(require("path"), 1);
|
|
53968
53548
|
function getAllowedPersonaIds(grants, action) {
|
|
53969
53549
|
const ids = /* @__PURE__ */ new Set();
|
|
53970
53550
|
for (const g2 of grants) {
|
|
@@ -53977,42 +53557,42 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
53977
53557
|
return ids;
|
|
53978
53558
|
}
|
|
53979
53559
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
|
|
53980
|
-
const target =
|
|
53560
|
+
const target = path49.resolve(absPath);
|
|
53981
53561
|
if (userWorkDir) {
|
|
53982
|
-
const u =
|
|
53983
|
-
const usep = u.endsWith(
|
|
53562
|
+
const u = path49.resolve(userWorkDir);
|
|
53563
|
+
const usep = u.endsWith(path49.sep) ? "" : path49.sep;
|
|
53984
53564
|
if (target === u || target.startsWith(u + usep)) return true;
|
|
53985
53565
|
}
|
|
53986
|
-
const root =
|
|
53987
|
-
const sep3 = root.endsWith(
|
|
53566
|
+
const root = path49.resolve(personaRoot);
|
|
53567
|
+
const sep3 = root.endsWith(path49.sep) ? "" : path49.sep;
|
|
53988
53568
|
if (!target.startsWith(root + sep3)) return false;
|
|
53989
|
-
const rel =
|
|
53569
|
+
const rel = path49.relative(root, target);
|
|
53990
53570
|
if (!rel || rel.startsWith("..")) return false;
|
|
53991
|
-
const personaId = rel.split(
|
|
53571
|
+
const personaId = rel.split(path49.sep)[0];
|
|
53992
53572
|
if (!personaId) return false;
|
|
53993
53573
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
53994
53574
|
if (allowed === "*") return true;
|
|
53995
53575
|
return allowed.has(personaId);
|
|
53996
53576
|
}
|
|
53997
53577
|
function personaIdFromPath(absPath, personaRoot) {
|
|
53998
|
-
const root =
|
|
53999
|
-
const target =
|
|
54000
|
-
const sep3 = root.endsWith(
|
|
53578
|
+
const root = path49.resolve(personaRoot);
|
|
53579
|
+
const target = path49.resolve(absPath);
|
|
53580
|
+
const sep3 = root.endsWith(path49.sep) ? "" : path49.sep;
|
|
54001
53581
|
if (!target.startsWith(root + sep3)) return null;
|
|
54002
|
-
const rel =
|
|
53582
|
+
const rel = path49.relative(root, target);
|
|
54003
53583
|
if (!rel || rel.startsWith("..")) return null;
|
|
54004
|
-
const id = rel.split(
|
|
53584
|
+
const id = rel.split(path49.sep)[0];
|
|
54005
53585
|
return id || null;
|
|
54006
53586
|
}
|
|
54007
53587
|
function isPathWithin(dir, absPath) {
|
|
54008
|
-
const d =
|
|
54009
|
-
const t =
|
|
54010
|
-
const sep3 = d.endsWith(
|
|
53588
|
+
const d = path49.resolve(dir);
|
|
53589
|
+
const t = path49.resolve(absPath);
|
|
53590
|
+
const sep3 = d.endsWith(path49.sep) ? "" : path49.sep;
|
|
54011
53591
|
return t === d || t.startsWith(d + sep3);
|
|
54012
53592
|
}
|
|
54013
53593
|
function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
|
|
54014
53594
|
if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
|
|
54015
|
-
return personaIdFromPath(
|
|
53595
|
+
return personaIdFromPath(path49.resolve(absPath), personaRoot) === personaId;
|
|
54016
53596
|
}
|
|
54017
53597
|
|
|
54018
53598
|
// src/handlers/history.ts
|
|
@@ -54038,7 +53618,7 @@ function buildHistoryHandlers(deps) {
|
|
|
54038
53618
|
if (!pid) return false;
|
|
54039
53619
|
return isGuestPathAllowed(
|
|
54040
53620
|
ctx.grants,
|
|
54041
|
-
|
|
53621
|
+
path50.join(personaRoot, pid),
|
|
54042
53622
|
personaRoot,
|
|
54043
53623
|
"read",
|
|
54044
53624
|
userWorkDir
|
|
@@ -54050,7 +53630,7 @@ function buildHistoryHandlers(deps) {
|
|
|
54050
53630
|
};
|
|
54051
53631
|
const list = async (frame, _client, ctx) => {
|
|
54052
53632
|
const args = HistoryListArgs.parse(frame);
|
|
54053
|
-
assertGuestPath(ctx,
|
|
53633
|
+
assertGuestPath(ctx, path50.resolve(args.projectPath), personaRoot, "history:list");
|
|
54054
53634
|
const sessions = await history.listSessions(args);
|
|
54055
53635
|
return { response: { type: "history:list", sessions } };
|
|
54056
53636
|
};
|
|
@@ -54082,13 +53662,13 @@ function buildHistoryHandlers(deps) {
|
|
|
54082
53662
|
};
|
|
54083
53663
|
const subagents = async (frame, _client, ctx) => {
|
|
54084
53664
|
const args = HistorySubagentsArgs.parse(frame);
|
|
54085
|
-
assertGuestPath(ctx,
|
|
53665
|
+
assertGuestPath(ctx, path50.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
|
|
54086
53666
|
const subs = await history.listSubagents(args);
|
|
54087
53667
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
54088
53668
|
};
|
|
54089
53669
|
const subagentRead = async (frame, _client, ctx) => {
|
|
54090
53670
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
54091
|
-
assertGuestPath(ctx,
|
|
53671
|
+
assertGuestPath(ctx, path50.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
|
|
54092
53672
|
const res = await history.readSubagent(args);
|
|
54093
53673
|
return { response: { type: "history:subagent-read", ...res } };
|
|
54094
53674
|
};
|
|
@@ -54097,7 +53677,7 @@ function buildHistoryHandlers(deps) {
|
|
|
54097
53677
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
54098
53678
|
const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
|
|
54099
53679
|
const filtered = dirs.filter(
|
|
54100
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
53680
|
+
(d) => isGuestPathAllowed(ctx.grants, path50.resolve(d.cwd), personaRoot, "read", userWorkDir)
|
|
54101
53681
|
);
|
|
54102
53682
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
54103
53683
|
}
|
|
@@ -54114,7 +53694,7 @@ function buildHistoryHandlers(deps) {
|
|
|
54114
53694
|
}
|
|
54115
53695
|
|
|
54116
53696
|
// src/handlers/workspace.ts
|
|
54117
|
-
var
|
|
53697
|
+
var path51 = __toESM(require("path"), 1);
|
|
54118
53698
|
var os16 = __toESM(require("os"), 1);
|
|
54119
53699
|
init_protocol();
|
|
54120
53700
|
init_protocol();
|
|
@@ -54156,22 +53736,22 @@ function buildWorkspaceHandlers(deps) {
|
|
|
54156
53736
|
const args = WorkspaceListArgs.parse(frame);
|
|
54157
53737
|
const isGuest = ctx?.principal.kind === "guest";
|
|
54158
53738
|
const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
|
|
54159
|
-
const resolvedCwd =
|
|
54160
|
-
const target = args.path ?
|
|
53739
|
+
const resolvedCwd = path51.resolve(args.cwd ?? fallbackCwd);
|
|
53740
|
+
const target = args.path ? path51.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
54161
53741
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
|
|
54162
53742
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
54163
53743
|
return { response: { type: "workspace:list", ...res } };
|
|
54164
53744
|
};
|
|
54165
53745
|
const read = async (frame, _client, ctx) => {
|
|
54166
53746
|
const args = WorkspaceReadArgs.parse(frame);
|
|
54167
|
-
const target =
|
|
53747
|
+
const target = path51.isAbsolute(args.path) ? path51.resolve(args.path) : path51.resolve(args.cwd, args.path);
|
|
54168
53748
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
|
|
54169
53749
|
const res = workspace.read(args);
|
|
54170
53750
|
return { response: { type: "workspace:read", ...res } };
|
|
54171
53751
|
};
|
|
54172
53752
|
const skillsList = async (frame, _client, ctx) => {
|
|
54173
53753
|
const args = SkillsListArgs.parse(frame);
|
|
54174
|
-
const cwdAbs =
|
|
53754
|
+
const cwdAbs = path51.resolve(args.cwd);
|
|
54175
53755
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
|
|
54176
53756
|
const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
|
|
54177
53757
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -54183,7 +53763,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
54183
53763
|
};
|
|
54184
53764
|
const agentsList = async (frame, _client, ctx) => {
|
|
54185
53765
|
const args = AgentsListArgs.parse(frame);
|
|
54186
|
-
const cwdAbs =
|
|
53766
|
+
const cwdAbs = path51.resolve(args.cwd);
|
|
54187
53767
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
|
|
54188
53768
|
if (args.tool === "codex") {
|
|
54189
53769
|
return { response: { type: "agents:list", agents: [] } };
|
|
@@ -54205,20 +53785,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
54205
53785
|
}
|
|
54206
53786
|
|
|
54207
53787
|
// src/handlers/git.ts
|
|
54208
|
-
var
|
|
53788
|
+
var path53 = __toESM(require("path"), 1);
|
|
54209
53789
|
init_protocol();
|
|
54210
53790
|
init_protocol();
|
|
54211
53791
|
|
|
54212
53792
|
// src/workspace/git.ts
|
|
54213
|
-
var
|
|
54214
|
-
var
|
|
54215
|
-
var
|
|
53793
|
+
var import_node_child_process10 = require("child_process");
|
|
53794
|
+
var import_node_fs38 = __toESM(require("fs"), 1);
|
|
53795
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
54216
53796
|
var import_node_util = require("util");
|
|
54217
|
-
var pexec = (0, import_node_util.promisify)(
|
|
53797
|
+
var pexec = (0, import_node_util.promisify)(import_node_child_process10.execFile);
|
|
54218
53798
|
function normalizePath(p2) {
|
|
54219
|
-
const resolved =
|
|
53799
|
+
const resolved = import_node_path40.default.resolve(p2);
|
|
54220
53800
|
try {
|
|
54221
|
-
return
|
|
53801
|
+
return import_node_fs38.default.realpathSync(resolved);
|
|
54222
53802
|
} catch {
|
|
54223
53803
|
return resolved;
|
|
54224
53804
|
}
|
|
@@ -54292,7 +53872,7 @@ async function listGitBranches(cwd) {
|
|
|
54292
53872
|
function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
|
|
54293
53873
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
54294
53874
|
const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
|
|
54295
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
53875
|
+
if (!isGuestPathAllowed(ctx.grants, path53.resolve(cwd), personaRoot, "read", userWorkDir)) {
|
|
54296
53876
|
throw new ClawdError(
|
|
54297
53877
|
ERROR_CODES.UNAUTHORIZED,
|
|
54298
53878
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -54625,128 +54205,6 @@ function buildContactHandlers(deps) {
|
|
|
54625
54205
|
};
|
|
54626
54206
|
}
|
|
54627
54207
|
|
|
54628
|
-
// src/handlers/contact-ssh.ts
|
|
54629
|
-
init_protocol();
|
|
54630
|
-
|
|
54631
|
-
// src/sshd/key-issue.ts
|
|
54632
|
-
var import_node_fs43 = __toESM(require("fs"), 1);
|
|
54633
|
-
var import_node_path45 = __toESM(require("path"), 1);
|
|
54634
|
-
var import_node_child_process13 = require("child_process");
|
|
54635
|
-
function safeDeviceId(deviceId) {
|
|
54636
|
-
return deviceId.replace(/[\/\\]/g, "_");
|
|
54637
|
-
}
|
|
54638
|
-
async function issueContactSshKey(deviceId, sshdDir, opts = {}) {
|
|
54639
|
-
const safeId = safeDeviceId(deviceId);
|
|
54640
|
-
const keysDir = import_node_path45.default.join(sshdDir, "keys");
|
|
54641
|
-
import_node_fs43.default.mkdirSync(keysDir, { recursive: true, mode: 448 });
|
|
54642
|
-
const privPath = import_node_path45.default.join(keysDir, `${safeId}.ed25519`);
|
|
54643
|
-
const pubPath = `${privPath}.pub`;
|
|
54644
|
-
if (import_node_fs43.default.existsSync(privPath) && import_node_fs43.default.existsSync(pubPath)) {
|
|
54645
|
-
return {
|
|
54646
|
-
privateKeyPem: import_node_fs43.default.readFileSync(privPath, "utf8"),
|
|
54647
|
-
publicKeyLine: import_node_fs43.default.readFileSync(pubPath, "utf8").trim()
|
|
54648
|
-
};
|
|
54649
|
-
}
|
|
54650
|
-
const bin = opts.keygenBin ?? "/usr/bin/ssh-keygen";
|
|
54651
|
-
await new Promise((resolve6, reject) => {
|
|
54652
|
-
const p2 = (opts.spawnImpl ?? import_node_child_process13.spawn)(
|
|
54653
|
-
bin,
|
|
54654
|
-
["-t", "ed25519", "-f", privPath, "-N", "", "-q", "-C", `clawd-contact-${safeId}`],
|
|
54655
|
-
{ stdio: "ignore" }
|
|
54656
|
-
);
|
|
54657
|
-
p2.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`ssh-keygen exit ${code}`)));
|
|
54658
|
-
p2.on("error", reject);
|
|
54659
|
-
});
|
|
54660
|
-
try {
|
|
54661
|
-
import_node_fs43.default.chmodSync(privPath, 384);
|
|
54662
|
-
} catch {
|
|
54663
|
-
}
|
|
54664
|
-
try {
|
|
54665
|
-
import_node_fs43.default.chmodSync(pubPath, 420);
|
|
54666
|
-
} catch {
|
|
54667
|
-
}
|
|
54668
|
-
return {
|
|
54669
|
-
privateKeyPem: import_node_fs43.default.readFileSync(privPath, "utf8"),
|
|
54670
|
-
publicKeyLine: import_node_fs43.default.readFileSync(pubPath, "utf8").trim()
|
|
54671
|
-
};
|
|
54672
|
-
}
|
|
54673
|
-
|
|
54674
|
-
// src/handlers/contact-ssh.ts
|
|
54675
|
-
function ensureOwner2(ctx) {
|
|
54676
|
-
if (!ctx || ctx.principal.kind !== "owner") {
|
|
54677
|
-
throw new ClawdError(
|
|
54678
|
-
ERROR_CODES.UNAUTHORIZED,
|
|
54679
|
-
"UNAUTHORIZED: contact:setSshAccess requires owner ctx"
|
|
54680
|
-
);
|
|
54681
|
-
}
|
|
54682
|
-
}
|
|
54683
|
-
function ensureGuest(ctx) {
|
|
54684
|
-
if (!ctx || ctx.principal.kind !== "guest") {
|
|
54685
|
-
throw new ClawdError(
|
|
54686
|
-
ERROR_CODES.UNAUTHORIZED,
|
|
54687
|
-
"UNAUTHORIZED: contact:sshKey:issue requires guest ctx"
|
|
54688
|
-
);
|
|
54689
|
-
}
|
|
54690
|
-
return ctx.principal.id;
|
|
54691
|
-
}
|
|
54692
|
-
function buildContactSshHandlers(deps) {
|
|
54693
|
-
const setSshAccess = async (frame, _client, ctx) => {
|
|
54694
|
-
ensureOwner2(ctx);
|
|
54695
|
-
const { type: _t, requestId: _r, ...rest } = frame;
|
|
54696
|
-
const args = ContactSetSshAccessArgsSchema.parse(rest);
|
|
54697
|
-
const hit = deps.store.setSshAccess(args.deviceId, {
|
|
54698
|
-
sshAllowed: args.sshAllowed,
|
|
54699
|
-
exposedDirs: args.exposedDirs
|
|
54700
|
-
});
|
|
54701
|
-
if (!hit) {
|
|
54702
|
-
throw new ClawdError(
|
|
54703
|
-
ERROR_CODES.CONTACT_NOT_FOUND,
|
|
54704
|
-
`CONTACT_NOT_FOUND: contact ${args.deviceId} not in store`
|
|
54705
|
-
);
|
|
54706
|
-
}
|
|
54707
|
-
rebuildAuthorizedKeys(deps.store, deps.sshdDir);
|
|
54708
|
-
deps.broadcast({
|
|
54709
|
-
type: "contact:ssh-access-updated",
|
|
54710
|
-
deviceId: args.deviceId,
|
|
54711
|
-
sshAllowed: args.sshAllowed,
|
|
54712
|
-
exposedDirs: args.exposedDirs
|
|
54713
|
-
});
|
|
54714
|
-
return {
|
|
54715
|
-
response: {
|
|
54716
|
-
type: "contact:setSshAccess:ok",
|
|
54717
|
-
deviceId: args.deviceId,
|
|
54718
|
-
sshAllowed: args.sshAllowed,
|
|
54719
|
-
exposedDirs: args.exposedDirs
|
|
54720
|
-
}
|
|
54721
|
-
};
|
|
54722
|
-
};
|
|
54723
|
-
const sshKeyIssue = async (frame, _client, ctx) => {
|
|
54724
|
-
const callerDeviceId = ensureGuest(ctx);
|
|
54725
|
-
const { type: _t, requestId: _r, ...rest } = frame;
|
|
54726
|
-
ContactSshKeyIssueArgsSchema.parse(rest);
|
|
54727
|
-
const contact = deps.store.get(callerDeviceId);
|
|
54728
|
-
if (!contact || !contact.sshAllowed) {
|
|
54729
|
-
throw new ClawdError(
|
|
54730
|
-
ERROR_CODES.UNAUTHORIZED,
|
|
54731
|
-
`UNAUTHORIZED: contact ${callerDeviceId} not authorized for SSH`
|
|
54732
|
-
);
|
|
54733
|
-
}
|
|
54734
|
-
const { privateKeyPem, publicKeyLine } = await issueContactSshKey(callerDeviceId, deps.sshdDir);
|
|
54735
|
-
rebuildAuthorizedKeys(deps.store, deps.sshdDir);
|
|
54736
|
-
return {
|
|
54737
|
-
response: {
|
|
54738
|
-
type: "contact:sshKey:issue:ok",
|
|
54739
|
-
privateKeyPem,
|
|
54740
|
-
publicKeyLine
|
|
54741
|
-
}
|
|
54742
|
-
};
|
|
54743
|
-
};
|
|
54744
|
-
return {
|
|
54745
|
-
"contact:setSshAccess": setSshAccess,
|
|
54746
|
-
"contact:sshKey:issue": sshKeyIssue
|
|
54747
|
-
};
|
|
54748
|
-
}
|
|
54749
|
-
|
|
54750
54208
|
// src/handlers/whoami.ts
|
|
54751
54209
|
init_protocol();
|
|
54752
54210
|
function buildWhoamiHandler(deps) {
|
|
@@ -54842,7 +54300,7 @@ function buildFeishuAuthHandlers(deps) {
|
|
|
54842
54300
|
|
|
54843
54301
|
// src/handlers/device.ts
|
|
54844
54302
|
init_protocol();
|
|
54845
|
-
function
|
|
54303
|
+
function ensureOwner2(ctx) {
|
|
54846
54304
|
if (!ctx || ctx.principal.kind !== "owner") {
|
|
54847
54305
|
throw new ClawdError(ERROR_CODES.UNAUTHORIZED, "UNAUTHORIZED: device:* requires owner ctx");
|
|
54848
54306
|
}
|
|
@@ -54850,14 +54308,14 @@ function ensureOwner3(ctx) {
|
|
|
54850
54308
|
function buildDeviceHandlers(deps) {
|
|
54851
54309
|
const now = deps.now ?? Date.now;
|
|
54852
54310
|
const list = async (_frame, _client, ctx) => {
|
|
54853
|
-
|
|
54311
|
+
ensureOwner2(ctx);
|
|
54854
54312
|
const devices = await deps.listDevices();
|
|
54855
54313
|
return {
|
|
54856
54314
|
response: { type: "device:list:ok", devices }
|
|
54857
54315
|
};
|
|
54858
54316
|
};
|
|
54859
54317
|
const connect = async (frame, _client, ctx) => {
|
|
54860
|
-
|
|
54318
|
+
ensureOwner2(ctx);
|
|
54861
54319
|
const { type: _t, requestId: _r, ...rest } = frame;
|
|
54862
54320
|
const args = DeviceConnectArgsSchema.parse(rest);
|
|
54863
54321
|
const exchanged = await deps.exchange(args.deviceId);
|
|
@@ -54900,9 +54358,7 @@ function buildDeviceHandlers(deps) {
|
|
|
54900
54358
|
connectToken: exchanged.token,
|
|
54901
54359
|
grants: wh.grants,
|
|
54902
54360
|
addedAt: now(),
|
|
54903
|
-
pinnedAt: null
|
|
54904
|
-
sshAllowed: false,
|
|
54905
|
-
exposedDirs: []
|
|
54361
|
+
pinnedAt: null
|
|
54906
54362
|
};
|
|
54907
54363
|
deps.store.upsert(contact);
|
|
54908
54364
|
deps.broadcast({ type: "contact:added", contact });
|
|
@@ -55058,7 +54514,7 @@ function buildPersonaHandlers(deps) {
|
|
|
55058
54514
|
}
|
|
55059
54515
|
|
|
55060
54516
|
// src/handlers/attachment.ts
|
|
55061
|
-
var
|
|
54517
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
55062
54518
|
init_protocol();
|
|
55063
54519
|
init_protocol();
|
|
55064
54520
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -55138,12 +54594,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
55138
54594
|
`session ${args.sessionId} scope unresolved`
|
|
55139
54595
|
);
|
|
55140
54596
|
}
|
|
55141
|
-
const cwdAbs =
|
|
55142
|
-
const candidateAbs =
|
|
54597
|
+
const cwdAbs = import_node_path41.default.resolve(sessionFile.cwd);
|
|
54598
|
+
const candidateAbs = import_node_path41.default.isAbsolute(args.relPath) ? import_node_path41.default.resolve(args.relPath) : import_node_path41.default.resolve(cwdAbs, args.relPath);
|
|
55143
54599
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
|
|
55144
54600
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
55145
54601
|
const entry = entries.find((e) => {
|
|
55146
|
-
const storedAbs =
|
|
54602
|
+
const storedAbs = import_node_path41.default.isAbsolute(e.relPath) ? import_node_path41.default.resolve(e.relPath) : import_node_path41.default.resolve(cwdAbs, e.relPath);
|
|
55147
54603
|
return storedAbs === candidateAbs && !e.stale;
|
|
55148
54604
|
});
|
|
55149
54605
|
if (!entry) {
|
|
@@ -55168,7 +54624,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
55168
54624
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
55169
54625
|
const f = deps.sessionStore.read(sessionId);
|
|
55170
54626
|
if (!f) return;
|
|
55171
|
-
assertGuestAttachmentPath(ctx,
|
|
54627
|
+
assertGuestAttachmentPath(ctx, import_node_path41.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
|
|
55172
54628
|
}
|
|
55173
54629
|
const groupAdd = async (frame, _client, ctx) => {
|
|
55174
54630
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -55183,8 +54639,8 @@ function buildAttachmentHandlers(deps) {
|
|
|
55183
54639
|
if (!scope) {
|
|
55184
54640
|
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
|
|
55185
54641
|
}
|
|
55186
|
-
const cwdAbs =
|
|
55187
|
-
const candidateAbs =
|
|
54642
|
+
const cwdAbs = import_node_path41.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
|
|
54643
|
+
const candidateAbs = import_node_path41.default.isAbsolute(args.relPath) ? import_node_path41.default.resolve(args.relPath) : import_node_path41.default.resolve(cwdAbs, args.relPath);
|
|
55188
54644
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
|
|
55189
54645
|
const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
|
|
55190
54646
|
const size = 0;
|
|
@@ -55243,19 +54699,19 @@ function buildAttachmentHandlers(deps) {
|
|
|
55243
54699
|
|
|
55244
54700
|
// src/handlers/extension.ts
|
|
55245
54701
|
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
55246
|
-
var
|
|
54702
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
55247
54703
|
init_protocol();
|
|
55248
54704
|
|
|
55249
54705
|
// src/extension/bundle-zip.ts
|
|
55250
54706
|
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
55251
|
-
var
|
|
55252
|
-
var
|
|
54707
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
54708
|
+
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
55253
54709
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
55254
54710
|
async function bundleExtensionDir(dir) {
|
|
55255
54711
|
const entries = await listFilesSorted(dir);
|
|
55256
54712
|
const zip = new import_jszip2.default();
|
|
55257
54713
|
for (const rel of entries) {
|
|
55258
|
-
const abs =
|
|
54714
|
+
const abs = import_node_path42.default.join(dir, rel);
|
|
55259
54715
|
const content = await import_promises5.default.readFile(abs);
|
|
55260
54716
|
zip.file(rel, content, { date: FIXED_DATE });
|
|
55261
54717
|
}
|
|
@@ -55264,7 +54720,7 @@ async function bundleExtensionDir(dir) {
|
|
|
55264
54720
|
compression: "DEFLATE",
|
|
55265
54721
|
compressionOptions: { level: 6 }
|
|
55266
54722
|
});
|
|
55267
|
-
const sha256 =
|
|
54723
|
+
const sha256 = import_node_crypto14.default.createHash("sha256").update(buffer).digest("hex");
|
|
55268
54724
|
return { buffer, sha256 };
|
|
55269
54725
|
}
|
|
55270
54726
|
var FIXED_DATE = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
|
|
@@ -55276,7 +54732,7 @@ async function listFilesSorted(rootDir) {
|
|
|
55276
54732
|
return out;
|
|
55277
54733
|
}
|
|
55278
54734
|
async function walk(absRoot, relPrefix, out) {
|
|
55279
|
-
const dirAbs =
|
|
54735
|
+
const dirAbs = import_node_path42.default.join(absRoot, relPrefix);
|
|
55280
54736
|
const entries = await import_promises5.default.readdir(dirAbs, { withFileTypes: true });
|
|
55281
54737
|
for (const e of entries) {
|
|
55282
54738
|
if (IGNORE_BASENAMES.has(e.name)) continue;
|
|
@@ -55330,25 +54786,25 @@ function computePublishCheck(args) {
|
|
|
55330
54786
|
|
|
55331
54787
|
// src/extension/install-flow.ts
|
|
55332
54788
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
55333
|
-
var
|
|
54789
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
55334
54790
|
var import_node_os19 = __toESM(require("os"), 1);
|
|
55335
|
-
var
|
|
54791
|
+
var import_node_crypto15 = __toESM(require("crypto"), 1);
|
|
55336
54792
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
55337
54793
|
|
|
55338
54794
|
// src/extension/paths.ts
|
|
55339
54795
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
55340
|
-
var
|
|
54796
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
55341
54797
|
function clawdHomeRoot(override) {
|
|
55342
|
-
return override ?? process.env.CLAWD_HOME ??
|
|
54798
|
+
return override ?? process.env.CLAWD_HOME ?? import_node_path43.default.join(import_node_os18.default.homedir(), ".clawd");
|
|
55343
54799
|
}
|
|
55344
54800
|
function extensionsRoot(override) {
|
|
55345
|
-
return
|
|
54801
|
+
return import_node_path43.default.join(clawdHomeRoot(override), "extensions");
|
|
55346
54802
|
}
|
|
55347
54803
|
function publishedChannelsFile(override) {
|
|
55348
|
-
return
|
|
54804
|
+
return import_node_path43.default.join(clawdHomeRoot(override), "extensions-published.json");
|
|
55349
54805
|
}
|
|
55350
54806
|
function bundleCacheRoot(override) {
|
|
55351
|
-
return
|
|
54807
|
+
return import_node_path43.default.join(clawdHomeRoot(override), "extension-bundles");
|
|
55352
54808
|
}
|
|
55353
54809
|
|
|
55354
54810
|
// src/extension/install-flow.ts
|
|
@@ -55361,7 +54817,7 @@ var InstallError = class extends Error {
|
|
|
55361
54817
|
};
|
|
55362
54818
|
async function installFromChannel(args, deps) {
|
|
55363
54819
|
const { channelRef, snapshotHash, bundleZip } = args;
|
|
55364
|
-
const computed =
|
|
54820
|
+
const computed = import_node_crypto15.default.createHash("sha256").update(bundleZip).digest("hex");
|
|
55365
54821
|
if (computed !== snapshotHash) {
|
|
55366
54822
|
throw new InstallError(
|
|
55367
54823
|
"HASH_MISMATCH",
|
|
@@ -55375,7 +54831,7 @@ async function installFromChannel(args, deps) {
|
|
|
55375
54831
|
throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
55376
54832
|
}
|
|
55377
54833
|
for (const name of Object.keys(zip.files)) {
|
|
55378
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
54834
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path44.default.isAbsolute(name)) {
|
|
55379
54835
|
throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
55380
54836
|
}
|
|
55381
54837
|
}
|
|
@@ -55407,7 +54863,7 @@ async function installFromChannel(args, deps) {
|
|
|
55407
54863
|
);
|
|
55408
54864
|
}
|
|
55409
54865
|
const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
|
|
55410
|
-
const destDir =
|
|
54866
|
+
const destDir = import_node_path44.default.join(deps.extensionsRoot, localExtId);
|
|
55411
54867
|
let destExists = false;
|
|
55412
54868
|
try {
|
|
55413
54869
|
await import_promises6.default.access(destDir);
|
|
@@ -55421,16 +54877,16 @@ async function installFromChannel(args, deps) {
|
|
|
55421
54877
|
);
|
|
55422
54878
|
}
|
|
55423
54879
|
const stage = await import_promises6.default.mkdtemp(
|
|
55424
|
-
|
|
54880
|
+
import_node_path44.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
|
|
55425
54881
|
);
|
|
55426
54882
|
try {
|
|
55427
54883
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
55428
|
-
const dest =
|
|
54884
|
+
const dest = import_node_path44.default.join(stage, name);
|
|
55429
54885
|
if (entry.dir) {
|
|
55430
54886
|
await import_promises6.default.mkdir(dest, { recursive: true });
|
|
55431
54887
|
continue;
|
|
55432
54888
|
}
|
|
55433
|
-
await import_promises6.default.mkdir(
|
|
54889
|
+
await import_promises6.default.mkdir(import_node_path44.default.dirname(dest), { recursive: true });
|
|
55434
54890
|
if (name === "manifest.json") {
|
|
55435
54891
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
55436
54892
|
await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -55451,9 +54907,9 @@ async function installFromChannel(args, deps) {
|
|
|
55451
54907
|
|
|
55452
54908
|
// src/extension/update-flow.ts
|
|
55453
54909
|
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
55454
|
-
var
|
|
54910
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
55455
54911
|
var import_node_os20 = __toESM(require("os"), 1);
|
|
55456
|
-
var
|
|
54912
|
+
var import_node_crypto16 = __toESM(require("crypto"), 1);
|
|
55457
54913
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
55458
54914
|
var UpdateError = class extends Error {
|
|
55459
54915
|
constructor(code, message) {
|
|
@@ -55468,11 +54924,11 @@ async function updateFromChannel(args, deps) {
|
|
|
55468
54924
|
channelRef.extId,
|
|
55469
54925
|
channelRef.ownerPrincipalId
|
|
55470
54926
|
);
|
|
55471
|
-
const liveDir =
|
|
54927
|
+
const liveDir = import_node_path45.default.join(deps.extensionsRoot, localExtId);
|
|
55472
54928
|
const prevDir = `${liveDir}.prev`;
|
|
55473
54929
|
let existingVersion;
|
|
55474
54930
|
try {
|
|
55475
|
-
const raw = await import_promises7.default.readFile(
|
|
54931
|
+
const raw = await import_promises7.default.readFile(import_node_path45.default.join(liveDir, "manifest.json"), "utf8");
|
|
55476
54932
|
const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
|
|
55477
54933
|
if (!parsed2.success) {
|
|
55478
54934
|
throw new UpdateError(
|
|
@@ -55491,7 +54947,7 @@ async function updateFromChannel(args, deps) {
|
|
|
55491
54947
|
if (e instanceof UpdateError) throw e;
|
|
55492
54948
|
throw e;
|
|
55493
54949
|
}
|
|
55494
|
-
const computed =
|
|
54950
|
+
const computed = import_node_crypto16.default.createHash("sha256").update(bundleZip).digest("hex");
|
|
55495
54951
|
if (computed !== snapshotHash) {
|
|
55496
54952
|
throw new UpdateError(
|
|
55497
54953
|
"HASH_MISMATCH",
|
|
@@ -55505,7 +54961,7 @@ async function updateFromChannel(args, deps) {
|
|
|
55505
54961
|
throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
55506
54962
|
}
|
|
55507
54963
|
for (const name of Object.keys(zip.files)) {
|
|
55508
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
54964
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path45.default.isAbsolute(name)) {
|
|
55509
54965
|
throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
55510
54966
|
}
|
|
55511
54967
|
}
|
|
@@ -55540,16 +54996,16 @@ async function updateFromChannel(args, deps) {
|
|
|
55540
54996
|
await import_promises7.default.rm(prevDir, { recursive: true, force: true });
|
|
55541
54997
|
await import_promises7.default.rename(liveDir, prevDir);
|
|
55542
54998
|
const stage = await import_promises7.default.mkdtemp(
|
|
55543
|
-
|
|
54999
|
+
import_node_path45.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
|
|
55544
55000
|
);
|
|
55545
55001
|
try {
|
|
55546
55002
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
55547
|
-
const dest =
|
|
55003
|
+
const dest = import_node_path45.default.join(stage, name);
|
|
55548
55004
|
if (entry.dir) {
|
|
55549
55005
|
await import_promises7.default.mkdir(dest, { recursive: true });
|
|
55550
55006
|
continue;
|
|
55551
55007
|
}
|
|
55552
|
-
await import_promises7.default.mkdir(
|
|
55008
|
+
await import_promises7.default.mkdir(import_node_path45.default.dirname(dest), { recursive: true });
|
|
55553
55009
|
if (name === "manifest.json") {
|
|
55554
55010
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
55555
55011
|
await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -55642,7 +55098,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
55642
55098
|
);
|
|
55643
55099
|
}
|
|
55644
55100
|
}
|
|
55645
|
-
const manifestPath =
|
|
55101
|
+
const manifestPath = import_node_path46.default.join(root, extId, "manifest.json");
|
|
55646
55102
|
const manifest = await readManifest(root, extId);
|
|
55647
55103
|
const next = { ...manifest, version: newVersion };
|
|
55648
55104
|
const tmp = `${manifestPath}.tmp`;
|
|
@@ -55650,7 +55106,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
55650
55106
|
await import_promises8.default.rename(tmp, manifestPath);
|
|
55651
55107
|
}
|
|
55652
55108
|
async function readManifest(root, extId) {
|
|
55653
|
-
const file =
|
|
55109
|
+
const file = import_node_path46.default.join(root, extId, "manifest.json");
|
|
55654
55110
|
let raw;
|
|
55655
55111
|
try {
|
|
55656
55112
|
raw = await import_promises8.default.readFile(file, "utf8");
|
|
@@ -55741,7 +55197,7 @@ function buildExtensionHandlers(deps) {
|
|
|
55741
55197
|
};
|
|
55742
55198
|
async function buildSnapshotMeta(extId) {
|
|
55743
55199
|
const manifest = await readManifest(deps.root, extId);
|
|
55744
|
-
const { sha256, buffer } = await bundleExtensionDir(
|
|
55200
|
+
const { sha256, buffer } = await bundleExtensionDir(import_node_path46.default.join(deps.root, extId));
|
|
55745
55201
|
return { manifest, contentHash: sha256, buffer };
|
|
55746
55202
|
}
|
|
55747
55203
|
const publish = async (frame, _client, ctx) => {
|
|
@@ -55922,9 +55378,9 @@ function buildExtensionHandlers(deps) {
|
|
|
55922
55378
|
}
|
|
55923
55379
|
|
|
55924
55380
|
// src/app-builder/project-store.ts
|
|
55925
|
-
var
|
|
55926
|
-
var
|
|
55927
|
-
var
|
|
55381
|
+
var import_node_fs39 = require("fs");
|
|
55382
|
+
var import_node_child_process11 = require("child_process");
|
|
55383
|
+
var import_node_path47 = require("path");
|
|
55928
55384
|
init_protocol();
|
|
55929
55385
|
var PROJECTS_DIR = "projects";
|
|
55930
55386
|
var META_FILE = ".clawd-project.json";
|
|
@@ -55938,19 +55394,19 @@ var ProjectStore = class {
|
|
|
55938
55394
|
root;
|
|
55939
55395
|
/** projects/<name>/.clawd-project.json 路径 */
|
|
55940
55396
|
metaPath(name) {
|
|
55941
|
-
return (0,
|
|
55397
|
+
return (0, import_node_path47.join)(this.projectsRoot(), name, META_FILE);
|
|
55942
55398
|
}
|
|
55943
55399
|
/** projects/<name>/ 目录路径(cwd 用) */
|
|
55944
55400
|
projectDir(name) {
|
|
55945
|
-
return (0,
|
|
55401
|
+
return (0, import_node_path47.join)(this.projectsRoot(), name);
|
|
55946
55402
|
}
|
|
55947
55403
|
projectsRoot() {
|
|
55948
|
-
return (0,
|
|
55404
|
+
return (0, import_node_path47.join)(this.root, PROJECTS_DIR);
|
|
55949
55405
|
}
|
|
55950
55406
|
async list() {
|
|
55951
55407
|
let entries;
|
|
55952
55408
|
try {
|
|
55953
|
-
entries = await
|
|
55409
|
+
entries = await import_node_fs39.promises.readdir(this.projectsRoot());
|
|
55954
55410
|
} catch (err) {
|
|
55955
55411
|
if (err.code === "ENOENT") return [];
|
|
55956
55412
|
throw err;
|
|
@@ -55958,7 +55414,7 @@ var ProjectStore = class {
|
|
|
55958
55414
|
const out = [];
|
|
55959
55415
|
for (const name of entries) {
|
|
55960
55416
|
try {
|
|
55961
|
-
const raw = await
|
|
55417
|
+
const raw = await import_node_fs39.promises.readFile(this.metaPath(name), "utf8");
|
|
55962
55418
|
const json = JSON.parse(raw);
|
|
55963
55419
|
let migrated = false;
|
|
55964
55420
|
if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
|
|
@@ -55969,7 +55425,7 @@ var ProjectStore = class {
|
|
|
55969
55425
|
if (parsed.success) {
|
|
55970
55426
|
out.push(parsed.data);
|
|
55971
55427
|
if (migrated) {
|
|
55972
|
-
void
|
|
55428
|
+
void import_node_fs39.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
|
|
55973
55429
|
});
|
|
55974
55430
|
}
|
|
55975
55431
|
}
|
|
@@ -56013,8 +55469,8 @@ var ProjectStore = class {
|
|
|
56013
55469
|
throw new Error(`invalid name "${name}": ${validated.error.message}`);
|
|
56014
55470
|
}
|
|
56015
55471
|
const dir = this.projectDir(name);
|
|
56016
|
-
await
|
|
56017
|
-
await
|
|
55472
|
+
await import_node_fs39.promises.mkdir(dir, { recursive: true });
|
|
55473
|
+
await import_node_fs39.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
|
|
56018
55474
|
return meta;
|
|
56019
55475
|
}
|
|
56020
55476
|
/**
|
|
@@ -56028,7 +55484,7 @@ var ProjectStore = class {
|
|
|
56028
55484
|
async scaffold(name, templateSrcDir, scaffoldScriptPath) {
|
|
56029
55485
|
const destDir = this.projectDir(name);
|
|
56030
55486
|
return await new Promise((resolve6, reject) => {
|
|
56031
|
-
const child = (0,
|
|
55487
|
+
const child = (0, import_node_child_process11.spawn)("bash", [scaffoldScriptPath, name, templateSrcDir, destDir], {
|
|
56032
55488
|
env: { ...process.env, PATH: process.env.PATH ?? "" },
|
|
56033
55489
|
stdio: ["ignore", "pipe", "pipe"]
|
|
56034
55490
|
});
|
|
@@ -56057,7 +55513,7 @@ var ProjectStore = class {
|
|
|
56057
55513
|
}
|
|
56058
55514
|
async delete(name) {
|
|
56059
55515
|
const dir = this.projectDir(name);
|
|
56060
|
-
await
|
|
55516
|
+
await import_node_fs39.promises.rm(dir, { recursive: true, force: true });
|
|
56061
55517
|
}
|
|
56062
55518
|
async updatePort(name, newPort) {
|
|
56063
55519
|
if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
|
|
@@ -56073,7 +55529,7 @@ var ProjectStore = class {
|
|
|
56073
55529
|
throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
|
|
56074
55530
|
}
|
|
56075
55531
|
const updated = { ...target, port: newPort };
|
|
56076
|
-
await
|
|
55532
|
+
await import_node_fs39.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
56077
55533
|
return updated;
|
|
56078
55534
|
}
|
|
56079
55535
|
/**
|
|
@@ -56090,7 +55546,7 @@ var ProjectStore = class {
|
|
|
56090
55546
|
if (!validated.success) {
|
|
56091
55547
|
throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
|
|
56092
55548
|
}
|
|
56093
|
-
await
|
|
55549
|
+
await import_node_fs39.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
56094
55550
|
return validated.data;
|
|
56095
55551
|
}
|
|
56096
55552
|
/**
|
|
@@ -56111,7 +55567,7 @@ var ProjectStore = class {
|
|
|
56111
55567
|
if (!validated.success) {
|
|
56112
55568
|
throw new Error(`invalid publishJob: ${validated.error.message}`);
|
|
56113
55569
|
}
|
|
56114
|
-
await
|
|
55570
|
+
await import_node_fs39.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
56115
55571
|
return validated.data;
|
|
56116
55572
|
}
|
|
56117
55573
|
/** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
|
|
@@ -56126,13 +55582,13 @@ var ProjectStore = class {
|
|
|
56126
55582
|
if (!validated.success) {
|
|
56127
55583
|
throw new Error(`failed to clear publishJob: ${validated.error.message}`);
|
|
56128
55584
|
}
|
|
56129
|
-
await
|
|
55585
|
+
await import_node_fs39.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
56130
55586
|
return validated.data;
|
|
56131
55587
|
}
|
|
56132
55588
|
};
|
|
56133
55589
|
|
|
56134
55590
|
// src/app-builder/kill-port.ts
|
|
56135
|
-
var
|
|
55591
|
+
var import_node_child_process12 = require("child_process");
|
|
56136
55592
|
async function killPortOccupants(port, ownedPids, logger) {
|
|
56137
55593
|
let pids;
|
|
56138
55594
|
try {
|
|
@@ -56174,7 +55630,7 @@ async function killPortOccupants(port, ownedPids, logger) {
|
|
|
56174
55630
|
}
|
|
56175
55631
|
function listPidsOnPort(port) {
|
|
56176
55632
|
return new Promise((resolve6, reject) => {
|
|
56177
|
-
(0,
|
|
55633
|
+
(0, import_node_child_process12.execFile)(
|
|
56178
55634
|
"lsof",
|
|
56179
55635
|
["-ti", `:${port}`],
|
|
56180
55636
|
{ timeout: 3e3 },
|
|
@@ -56196,7 +55652,7 @@ function listPidsOnPort(port) {
|
|
|
56196
55652
|
}
|
|
56197
55653
|
|
|
56198
55654
|
// src/app-builder/publish-registry.ts
|
|
56199
|
-
var
|
|
55655
|
+
var import_node_crypto17 = require("crypto");
|
|
56200
55656
|
var PublishJobRegistry = class {
|
|
56201
55657
|
jobs = /* @__PURE__ */ new Map();
|
|
56202
55658
|
has(name) {
|
|
@@ -56213,7 +55669,7 @@ var PublishJobRegistry = class {
|
|
|
56213
55669
|
if (this.jobs.has(args.name)) {
|
|
56214
55670
|
throw new Error(`already publishing: ${args.name}`);
|
|
56215
55671
|
}
|
|
56216
|
-
const jobId = args.jobId ?? `job-${(0,
|
|
55672
|
+
const jobId = args.jobId ?? `job-${(0, import_node_crypto17.randomUUID)()}`;
|
|
56217
55673
|
this.jobs.set(args.name, {
|
|
56218
55674
|
jobId,
|
|
56219
55675
|
name: args.name,
|
|
@@ -56246,9 +55702,9 @@ var PublishJobRegistry = class {
|
|
|
56246
55702
|
};
|
|
56247
55703
|
|
|
56248
55704
|
// src/app-builder/publish-job-runner.ts
|
|
56249
|
-
var
|
|
56250
|
-
var
|
|
56251
|
-
var
|
|
55705
|
+
var import_node_child_process13 = require("child_process");
|
|
55706
|
+
var import_node_fs40 = require("fs");
|
|
55707
|
+
var import_node_path48 = require("path");
|
|
56252
55708
|
|
|
56253
55709
|
// src/app-builder/publish-stage-parser.ts
|
|
56254
55710
|
var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
|
|
@@ -56275,19 +55731,19 @@ function tailStderrLines(buf, n) {
|
|
|
56275
55731
|
// src/app-builder/publish-job-runner.ts
|
|
56276
55732
|
async function startPublishJob(deps, args) {
|
|
56277
55733
|
const { registry: registry2, projectDir } = deps;
|
|
56278
|
-
const
|
|
55734
|
+
const spawn12 = deps.spawnImpl ?? import_node_child_process13.spawn;
|
|
56279
55735
|
if (registry2.has(args.name)) {
|
|
56280
55736
|
return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
|
|
56281
55737
|
}
|
|
56282
55738
|
const projDir = projectDir(args.name);
|
|
56283
|
-
const logPath = (0,
|
|
55739
|
+
const logPath = (0, import_node_path48.join)(projDir, ".publish.log");
|
|
56284
55740
|
let logStream = null;
|
|
56285
55741
|
try {
|
|
56286
|
-
logStream = (0,
|
|
55742
|
+
logStream = (0, import_node_fs40.createWriteStream)(logPath, { flags: "w" });
|
|
56287
55743
|
} catch {
|
|
56288
55744
|
logStream = null;
|
|
56289
55745
|
}
|
|
56290
|
-
const child =
|
|
55746
|
+
const child = spawn12("bash", [args.scriptPath, projDir, args.personaRoot ?? ""], {
|
|
56291
55747
|
cwd: projDir,
|
|
56292
55748
|
env: process.env,
|
|
56293
55749
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -56540,8 +55996,8 @@ async function recoverInterruptedJobs(deps) {
|
|
|
56540
55996
|
|
|
56541
55997
|
// src/handlers/app-builder.ts
|
|
56542
55998
|
init_protocol();
|
|
56543
|
-
var
|
|
56544
|
-
var
|
|
55999
|
+
var import_node_path49 = require("path");
|
|
56000
|
+
var import_node_fs41 = require("fs");
|
|
56545
56001
|
var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
|
|
56546
56002
|
var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
|
|
56547
56003
|
async function recoverInterruptedPublishJobs(store, logger) {
|
|
@@ -56622,7 +56078,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
56622
56078
|
async function listAllUsersProjects() {
|
|
56623
56079
|
if (!deps.usersRoot || !deps.getStore) return [];
|
|
56624
56080
|
const getStore = deps.getStore;
|
|
56625
|
-
const userIds = await
|
|
56081
|
+
const userIds = await import_node_fs41.promises.readdir(deps.usersRoot).catch(() => []);
|
|
56626
56082
|
const perUser = await Promise.all(
|
|
56627
56083
|
userIds.map((uid) => getStore(uid).list().catch(() => []))
|
|
56628
56084
|
);
|
|
@@ -56698,8 +56154,8 @@ function buildAppBuilderHandlers(deps) {
|
|
|
56698
56154
|
const project = await userStore.create(f.name, reservedPorts);
|
|
56699
56155
|
try {
|
|
56700
56156
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
|
|
56701
|
-
const templateSrcDir = (0,
|
|
56702
|
-
const scaffoldScript = (0,
|
|
56157
|
+
const templateSrcDir = (0, import_node_path49.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
|
|
56158
|
+
const scaffoldScript = (0, import_node_path49.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
|
|
56703
56159
|
const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
|
|
56704
56160
|
deps.logger?.info("app-builder.scaffold.done", {
|
|
56705
56161
|
name: project.name,
|
|
@@ -56920,7 +56376,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
56920
56376
|
await userStore.clearPublishJob(args.name);
|
|
56921
56377
|
}
|
|
56922
56378
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
|
|
56923
|
-
const scriptPath = (0,
|
|
56379
|
+
const scriptPath = (0, import_node_path49.join)(deps.deployKitRoot, "scripts", "publish.sh");
|
|
56924
56380
|
deps.logger?.info("app-builder.publish.start", {
|
|
56925
56381
|
name: args.name,
|
|
56926
56382
|
sessionId: boundSession.sessionId,
|
|
@@ -57064,7 +56520,7 @@ function buildShiftHandlers(deps) {
|
|
|
57064
56520
|
|
|
57065
56521
|
// src/handlers/visitor.ts
|
|
57066
56522
|
init_protocol();
|
|
57067
|
-
function
|
|
56523
|
+
function ensureOwner3(ctx) {
|
|
57068
56524
|
if (!ctx || ctx.principal.kind !== "owner") {
|
|
57069
56525
|
throw new ClawdError(
|
|
57070
56526
|
ERROR_CODES.UNAUTHORIZED,
|
|
@@ -57074,7 +56530,7 @@ function ensureOwner4(ctx) {
|
|
|
57074
56530
|
}
|
|
57075
56531
|
function buildVisitorHandlers(deps) {
|
|
57076
56532
|
const list = async (_frame, _client, ctx) => {
|
|
57077
|
-
|
|
56533
|
+
ensureOwner3(ctx);
|
|
57078
56534
|
return {
|
|
57079
56535
|
response: {
|
|
57080
56536
|
type: "visitor:list",
|
|
@@ -57089,7 +56545,7 @@ function buildVisitorHandlers(deps) {
|
|
|
57089
56545
|
|
|
57090
56546
|
// src/extension/registry.ts
|
|
57091
56547
|
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
57092
|
-
var
|
|
56548
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
57093
56549
|
async function loadAll(root) {
|
|
57094
56550
|
let entries;
|
|
57095
56551
|
try {
|
|
@@ -57102,13 +56558,13 @@ async function loadAll(root) {
|
|
|
57102
56558
|
for (const ent of entries) {
|
|
57103
56559
|
if (!ent.isDirectory()) continue;
|
|
57104
56560
|
if (ent.name.startsWith(".")) continue;
|
|
57105
|
-
records.push(await loadOne(
|
|
56561
|
+
records.push(await loadOne(import_node_path50.default.join(root, ent.name), ent.name));
|
|
57106
56562
|
}
|
|
57107
56563
|
records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
|
|
57108
56564
|
return records;
|
|
57109
56565
|
}
|
|
57110
56566
|
async function loadOne(dir, dirName) {
|
|
57111
|
-
const manifestPath =
|
|
56567
|
+
const manifestPath = import_node_path50.default.join(dir, "manifest.json");
|
|
57112
56568
|
let raw;
|
|
57113
56569
|
try {
|
|
57114
56570
|
raw = await import_promises9.default.readFile(manifestPath, "utf8");
|
|
@@ -57153,7 +56609,7 @@ async function loadOne(dir, dirName) {
|
|
|
57153
56609
|
|
|
57154
56610
|
// src/extension/uninstall.ts
|
|
57155
56611
|
var import_promises10 = __toESM(require("fs/promises"), 1);
|
|
57156
|
-
var
|
|
56612
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
57157
56613
|
var UninstallError = class extends Error {
|
|
57158
56614
|
constructor(code, message) {
|
|
57159
56615
|
super(message);
|
|
@@ -57162,7 +56618,7 @@ var UninstallError = class extends Error {
|
|
|
57162
56618
|
code;
|
|
57163
56619
|
};
|
|
57164
56620
|
async function uninstall(deps) {
|
|
57165
|
-
const dir =
|
|
56621
|
+
const dir = import_node_path51.default.join(deps.root, deps.extId);
|
|
57166
56622
|
try {
|
|
57167
56623
|
await import_promises10.default.access(dir);
|
|
57168
56624
|
} catch {
|
|
@@ -57173,7 +56629,7 @@ async function uninstall(deps) {
|
|
|
57173
56629
|
}
|
|
57174
56630
|
|
|
57175
56631
|
// src/handlers/index.ts
|
|
57176
|
-
var
|
|
56632
|
+
var import_node_crypto18 = require("crypto");
|
|
57177
56633
|
function buildMethodHandlers(deps) {
|
|
57178
56634
|
return {
|
|
57179
56635
|
...buildSessionHandlers({
|
|
@@ -57206,7 +56662,7 @@ function buildMethodHandlers(deps) {
|
|
|
57206
56662
|
const c = deps.contactStore.get(deviceId);
|
|
57207
56663
|
return c ? { deviceId: c.deviceId, remoteUrl: c.remoteUrl, connectToken: c.connectToken } : null;
|
|
57208
56664
|
},
|
|
57209
|
-
genId: () => (0,
|
|
56665
|
+
genId: () => (0, import_node_crypto18.randomUUID)(),
|
|
57210
56666
|
now: () => Date.now(),
|
|
57211
56667
|
forwardInboxPostToPeer,
|
|
57212
56668
|
logger: deps.logger
|
|
@@ -57217,11 +56673,6 @@ function buildMethodHandlers(deps) {
|
|
|
57217
56673
|
broadcast: deps.broadcastToOwners,
|
|
57218
56674
|
now: () => Date.now()
|
|
57219
56675
|
}),
|
|
57220
|
-
...buildContactSshHandlers({
|
|
57221
|
-
store: deps.contactStore,
|
|
57222
|
-
broadcast: deps.broadcastToOwners,
|
|
57223
|
-
sshdDir: deps.sshdDir
|
|
57224
|
-
}),
|
|
57225
56676
|
whoami: buildWhoamiHandler({
|
|
57226
56677
|
ownerDisplayName: deps.ownerDisplayName,
|
|
57227
56678
|
ownerPrincipalId: deps.ownerPrincipalId,
|
|
@@ -57268,7 +56719,7 @@ function buildMethodHandlers(deps) {
|
|
|
57268
56719
|
}
|
|
57269
56720
|
|
|
57270
56721
|
// src/app-builder/dev-server-supervisor.ts
|
|
57271
|
-
var
|
|
56722
|
+
var import_node_child_process14 = require("child_process");
|
|
57272
56723
|
var import_node_events2 = require("events");
|
|
57273
56724
|
var DEFAULT_READY_PATTERN = /Local:\s+https?:\/\/|Nest application successfully started|server listening on/i;
|
|
57274
56725
|
var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
@@ -57305,7 +56756,7 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
|
57305
56756
|
tunnelHost: args.tunnelHost,
|
|
57306
56757
|
devCommand: cmd
|
|
57307
56758
|
});
|
|
57308
|
-
const child = (0,
|
|
56759
|
+
const child = (0, import_node_child_process14.spawn)("sh", ["-c", cmd], {
|
|
57309
56760
|
cwd: args.cwd,
|
|
57310
56761
|
env,
|
|
57311
56762
|
stdio: "pipe",
|
|
@@ -57557,12 +57008,6 @@ var METHOD_GRANT_MAP = {
|
|
|
57557
57008
|
"contact:list": ADMIN_ANY,
|
|
57558
57009
|
"contact:pin": ADMIN_ANY,
|
|
57559
57010
|
"contact:remove": ADMIN_ANY,
|
|
57560
|
-
// contact:setSshAccess (owner UI 配 SSH 授权):ADMIN_ANY
|
|
57561
|
-
// contact:sshKey:issue (guest daemon 拉自己的 privkey):public — handler 内校
|
|
57562
|
-
// ctx.principal.kind==='guest' + store.get(callerId).sshAllowed
|
|
57563
|
-
// (对齐 inbox:postMessage 的"能连上=有 auth,业务在 handler 校"模式)
|
|
57564
|
-
"contact:setSshAccess": ADMIN_ANY,
|
|
57565
|
-
"contact:sshKey:issue": { kind: "public" },
|
|
57566
57011
|
// ---- visitor:* (访客名单,owner-only) ----
|
|
57567
57012
|
// owner 看完整访客名单(含没开会话的);guest 不可调(handler 内再 assertOwner 兜底)。
|
|
57568
57013
|
"visitor:list": ADMIN_ANY,
|
|
@@ -57743,8 +57188,8 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
|
|
|
57743
57188
|
}
|
|
57744
57189
|
|
|
57745
57190
|
// src/extension/runtime.ts
|
|
57746
|
-
var
|
|
57747
|
-
var
|
|
57191
|
+
var import_node_child_process15 = require("child_process");
|
|
57192
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
57748
57193
|
var import_promises11 = require("timers/promises");
|
|
57749
57194
|
|
|
57750
57195
|
// src/extension/port-allocator.ts
|
|
@@ -57845,13 +57290,13 @@ var Runtime = class {
|
|
|
57845
57290
|
/\$CLAWOS_EXT_PORT/g,
|
|
57846
57291
|
String(port)
|
|
57847
57292
|
);
|
|
57848
|
-
const dir =
|
|
57293
|
+
const dir = import_node_path52.default.join(this.root, extId);
|
|
57849
57294
|
const env = {
|
|
57850
57295
|
...process.env,
|
|
57851
57296
|
CLAWOS_EXT_PORT: String(port),
|
|
57852
57297
|
CLAWOS_EXT_ID: extId
|
|
57853
57298
|
};
|
|
57854
|
-
const child = (0,
|
|
57299
|
+
const child = (0, import_node_child_process15.spawn)("sh", ["-c", cmd], {
|
|
57855
57300
|
cwd: dir,
|
|
57856
57301
|
env,
|
|
57857
57302
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -57957,7 +57402,7 @@ ${handle.stderrTail}`
|
|
|
57957
57402
|
|
|
57958
57403
|
// src/extension/published-channels.ts
|
|
57959
57404
|
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
57960
|
-
var
|
|
57405
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
57961
57406
|
init_zod();
|
|
57962
57407
|
var PublishedChannelsError = class extends Error {
|
|
57963
57408
|
constructor(code, message) {
|
|
@@ -58056,7 +57501,7 @@ var PublishedChannelStore = class {
|
|
|
58056
57501
|
)
|
|
58057
57502
|
};
|
|
58058
57503
|
const tmp = `${this.filePath}.tmp`;
|
|
58059
|
-
await import_promises12.default.mkdir(
|
|
57504
|
+
await import_promises12.default.mkdir(import_node_path53.default.dirname(this.filePath), { recursive: true });
|
|
58060
57505
|
await import_promises12.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
58061
57506
|
await import_promises12.default.rename(tmp, this.filePath);
|
|
58062
57507
|
}
|
|
@@ -58064,7 +57509,7 @@ var PublishedChannelStore = class {
|
|
|
58064
57509
|
|
|
58065
57510
|
// src/extension/bundle-cache.ts
|
|
58066
57511
|
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
58067
|
-
var
|
|
57512
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
58068
57513
|
var BundleCache = class {
|
|
58069
57514
|
constructor(rootDir) {
|
|
58070
57515
|
this.rootDir = rootDir;
|
|
@@ -58073,14 +57518,14 @@ var BundleCache = class {
|
|
|
58073
57518
|
/** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
|
|
58074
57519
|
async write(snapshotHash, buffer) {
|
|
58075
57520
|
await import_promises13.default.mkdir(this.rootDir, { recursive: true });
|
|
58076
|
-
const file =
|
|
57521
|
+
const file = import_node_path54.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
58077
57522
|
const tmp = `${file}.tmp`;
|
|
58078
57523
|
await import_promises13.default.writeFile(tmp, buffer, { mode: 384 });
|
|
58079
57524
|
await import_promises13.default.rename(tmp, file);
|
|
58080
57525
|
}
|
|
58081
57526
|
/** Returns the bundle bytes, or null when the file doesn't exist. */
|
|
58082
57527
|
async read(snapshotHash) {
|
|
58083
|
-
const file =
|
|
57528
|
+
const file = import_node_path54.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
58084
57529
|
try {
|
|
58085
57530
|
return await import_promises13.default.readFile(file);
|
|
58086
57531
|
} catch (e) {
|
|
@@ -58090,7 +57535,7 @@ var BundleCache = class {
|
|
|
58090
57535
|
}
|
|
58091
57536
|
/** Idempotent — missing file is not an error. */
|
|
58092
57537
|
async delete(snapshotHash) {
|
|
58093
|
-
const file =
|
|
57538
|
+
const file = import_node_path54.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
58094
57539
|
await import_promises13.default.rm(file, { force: true });
|
|
58095
57540
|
}
|
|
58096
57541
|
};
|
|
@@ -58115,10 +57560,17 @@ async function startDaemon(config) {
|
|
|
58115
57560
|
});
|
|
58116
57561
|
const logger = createLogger({
|
|
58117
57562
|
level: config.logLevel,
|
|
58118
|
-
file:
|
|
57563
|
+
file: import_node_path55.default.join(config.dataDir, "clawd.log"),
|
|
58119
57564
|
logClient
|
|
58120
57565
|
});
|
|
58121
57566
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
57567
|
+
const screenIdleProbeLogger = createFileOnlyLogger({
|
|
57568
|
+
file: import_node_path55.default.join(config.dataDir, "screen-idle-probe.log"),
|
|
57569
|
+
level: "debug"
|
|
57570
|
+
});
|
|
57571
|
+
logger.info("screen-idle probe logger enabled", {
|
|
57572
|
+
file: import_node_path55.default.join(config.dataDir, "screen-idle-probe.log")
|
|
57573
|
+
});
|
|
58122
57574
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
58123
57575
|
const pre = stateMgr.preflight();
|
|
58124
57576
|
if (pre.status === "active") {
|
|
@@ -58255,8 +57707,8 @@ async function startDaemon(config) {
|
|
|
58255
57707
|
const agents = new AgentsScanner();
|
|
58256
57708
|
const history = new ClaudeHistoryReader();
|
|
58257
57709
|
let transport = null;
|
|
58258
|
-
const personaStore = new PersonaStore(
|
|
58259
|
-
const usersRoot =
|
|
57710
|
+
const personaStore = new PersonaStore(import_node_path55.default.join(config.dataDir, "personas"));
|
|
57711
|
+
const usersRoot = import_node_path55.default.join(config.dataDir, "users");
|
|
58260
57712
|
const defaultsRoot = findDefaultsRoot(logger);
|
|
58261
57713
|
if (defaultsRoot) {
|
|
58262
57714
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -58276,17 +57728,17 @@ async function startDaemon(config) {
|
|
|
58276
57728
|
migrateCodexSandbox({ store: personaStore, logger });
|
|
58277
57729
|
const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
|
|
58278
57730
|
const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
|
|
58279
|
-
const here = typeof __dirname === "string" ? __dirname :
|
|
57731
|
+
const here = typeof __dirname === "string" ? __dirname : import_node_path55.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
|
|
58280
57732
|
const dispatchServerCandidates = [
|
|
58281
|
-
|
|
57733
|
+
import_node_path55.default.join(here, "dispatch", "mcp-server.cjs"),
|
|
58282
57734
|
// 生产 dist/index → dist/dispatch/mcp-server.cjs
|
|
58283
|
-
|
|
57735
|
+
import_node_path55.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
|
|
58284
57736
|
// dev tsx src/index → ../dist/dispatch/mcp-server.cjs
|
|
58285
57737
|
];
|
|
58286
|
-
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) =>
|
|
57738
|
+
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs42.default.existsSync(p2));
|
|
58287
57739
|
let dispatchMcpConfigPath2;
|
|
58288
57740
|
if (dispatchServerScriptPath) {
|
|
58289
|
-
const dispatchLogPath =
|
|
57741
|
+
const dispatchLogPath = import_node_path55.default.join(config.dataDir, "dispatch-mcp-server.log");
|
|
58290
57742
|
dispatchMcpConfigPath2 = writeDispatchMcpConfig({
|
|
58291
57743
|
dataDir: config.dataDir,
|
|
58292
57744
|
serverScriptPath: dispatchServerScriptPath,
|
|
@@ -58303,15 +57755,15 @@ async function startDaemon(config) {
|
|
|
58303
57755
|
});
|
|
58304
57756
|
}
|
|
58305
57757
|
const ticketServerCandidates = [
|
|
58306
|
-
|
|
58307
|
-
|
|
57758
|
+
import_node_path55.default.join(here, "ticket", "mcp-server.cjs"),
|
|
57759
|
+
import_node_path55.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
|
|
58308
57760
|
];
|
|
58309
|
-
const ticketServerScriptPath = ticketServerCandidates.find((p2) =>
|
|
57761
|
+
const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs42.default.existsSync(p2));
|
|
58310
57762
|
const ticketOwnerUnionId = feishuIdentity?.identity.unionId ?? "";
|
|
58311
57763
|
const ticketOwnerName = feishuIdentity?.identity.displayName ?? "";
|
|
58312
57764
|
let ticketMcpConfigPath2;
|
|
58313
57765
|
if (ticketServerScriptPath && ticketOwnerUnionId) {
|
|
58314
|
-
const ticketLogPath =
|
|
57766
|
+
const ticketLogPath = import_node_path55.default.join(config.dataDir, "ticket-mcp-server.log");
|
|
58315
57767
|
ticketMcpConfigPath2 = writeTicketMcpConfig({
|
|
58316
57768
|
dataDir: config.dataDir,
|
|
58317
57769
|
serverScriptPath: ticketServerScriptPath,
|
|
@@ -58332,13 +57784,13 @@ async function startDaemon(config) {
|
|
|
58332
57784
|
});
|
|
58333
57785
|
}
|
|
58334
57786
|
const shiftServerCandidates = [
|
|
58335
|
-
|
|
58336
|
-
|
|
57787
|
+
import_node_path55.default.join(here, "shift", "mcp-server.cjs"),
|
|
57788
|
+
import_node_path55.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
|
|
58337
57789
|
];
|
|
58338
|
-
const shiftServerScriptPath = shiftServerCandidates.find((p2) =>
|
|
57790
|
+
const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs42.default.existsSync(p2));
|
|
58339
57791
|
let shiftMcpConfigPath2;
|
|
58340
57792
|
if (shiftServerScriptPath) {
|
|
58341
|
-
const shiftLogPath =
|
|
57793
|
+
const shiftLogPath = import_node_path55.default.join(config.dataDir, "shift-mcp-server.log");
|
|
58342
57794
|
shiftMcpConfigPath2 = await writeShiftMcpConfig({
|
|
58343
57795
|
dataDir: config.dataDir,
|
|
58344
57796
|
serverScriptPath: shiftServerScriptPath,
|
|
@@ -58356,13 +57808,13 @@ async function startDaemon(config) {
|
|
|
58356
57808
|
);
|
|
58357
57809
|
}
|
|
58358
57810
|
const inboxServerCandidates = [
|
|
58359
|
-
|
|
58360
|
-
|
|
57811
|
+
import_node_path55.default.join(here, "inbox", "mcp-server.cjs"),
|
|
57812
|
+
import_node_path55.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
|
|
58361
57813
|
];
|
|
58362
|
-
const inboxServerScriptPath = inboxServerCandidates.find((p2) =>
|
|
57814
|
+
const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs42.default.existsSync(p2));
|
|
58363
57815
|
let inboxMcpConfigPath2;
|
|
58364
57816
|
if (inboxServerScriptPath) {
|
|
58365
|
-
const inboxLogPath =
|
|
57817
|
+
const inboxLogPath = import_node_path55.default.join(config.dataDir, "inbox-mcp-server.log");
|
|
58366
57818
|
inboxMcpConfigPath2 = await writeInboxMcpConfig({
|
|
58367
57819
|
dataDir: config.dataDir,
|
|
58368
57820
|
serverScriptPath: inboxServerScriptPath,
|
|
@@ -58380,7 +57832,7 @@ async function startDaemon(config) {
|
|
|
58380
57832
|
);
|
|
58381
57833
|
}
|
|
58382
57834
|
const shiftStore = createShiftStore({
|
|
58383
|
-
filePath:
|
|
57835
|
+
filePath: import_node_path55.default.join(config.dataDir, "shift.json"),
|
|
58384
57836
|
ownerIdProvider: () => ownerPrincipalId,
|
|
58385
57837
|
now: () => Date.now()
|
|
58386
57838
|
});
|
|
@@ -58395,10 +57847,14 @@ async function startDaemon(config) {
|
|
|
58395
57847
|
// 新布局派生 (sessions/* + personas/<pid>/.clawd/sessions/owner/*)
|
|
58396
57848
|
storeFactory: sessionStoreFactory,
|
|
58397
57849
|
logger,
|
|
57850
|
+
// 取证 probe(可选,CLAWD_SCREEN_IDLE_PROBE=1 时启用):manager turn_end 判定链
|
|
57851
|
+
// 的所有决策点打到独立文件,跟 adapter 的 observeScreenIdle probe 共用同一份 file logger,
|
|
57852
|
+
// 便于 grep sessionId 时 tui 层 + manager 层交叉时序都在同一文件里
|
|
57853
|
+
...screenIdleProbeLogger ? { screenIdleProbeLogger } : {},
|
|
58398
57854
|
getAdapter,
|
|
58399
57855
|
historyReader: history,
|
|
58400
57856
|
dataDir: config.dataDir,
|
|
58401
|
-
personaRoot:
|
|
57857
|
+
personaRoot: import_node_path55.default.join(config.dataDir, "personas"),
|
|
58402
57858
|
usersRoot,
|
|
58403
57859
|
personaStore,
|
|
58404
57860
|
ownerDisplayName,
|
|
@@ -58441,10 +57897,10 @@ async function startDaemon(config) {
|
|
|
58441
57897
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
58442
57898
|
attachmentGroup: {
|
|
58443
57899
|
onFileEdit: (input) => {
|
|
58444
|
-
const absPath =
|
|
57900
|
+
const absPath = import_node_path55.default.isAbsolute(input.relPath) ? input.relPath : import_node_path55.default.join(input.cwd, input.relPath);
|
|
58445
57901
|
let size = 0;
|
|
58446
57902
|
try {
|
|
58447
|
-
size =
|
|
57903
|
+
size = import_node_fs42.default.statSync(absPath).size;
|
|
58448
57904
|
} catch (err) {
|
|
58449
57905
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
58450
57906
|
sessionId: input.sessionId,
|
|
@@ -58512,10 +57968,10 @@ async function startDaemon(config) {
|
|
|
58512
57968
|
onSurfaceUnregister: (tsid) => manager.unregisterSurface(tsid),
|
|
58513
57969
|
// ReadyGate v2:ReadyDetector emit ready 时投递 reducer 'ready-detected' input
|
|
58514
57970
|
onReady: (tsid) => manager.dispatchReadyDetected(tsid),
|
|
58515
|
-
//
|
|
58516
|
-
|
|
58517
|
-
//
|
|
58518
|
-
|
|
57971
|
+
// 屏幕真稳定 5s 的一次性信号 → manager 查 pending turn_duration 并 flush 成 turn_end
|
|
57972
|
+
onScreenIdle: (tsid) => manager.notifyScreenIdle(tsid),
|
|
57973
|
+
// 取证 probe(默认无条件启用;见 createFileOnlyLogger)
|
|
57974
|
+
screenIdleProbeLogger
|
|
58519
57975
|
}) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
|
|
58520
57976
|
registerAdapter("claude", claudeAdapter);
|
|
58521
57977
|
registerAdapter("codex", new CodexAdapter({ logger, historyReader: new CodexHistoryReader() }));
|
|
@@ -58642,11 +58098,11 @@ async function startDaemon(config) {
|
|
|
58642
58098
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
58643
58099
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
58644
58100
|
// guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
|
|
58645
|
-
personaRoot:
|
|
58101
|
+
personaRoot: import_node_path55.default.join(config.dataDir, "personas"),
|
|
58646
58102
|
usersRoot
|
|
58647
58103
|
},
|
|
58648
58104
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
58649
|
-
personaRoot:
|
|
58105
|
+
personaRoot: import_node_path55.default.join(config.dataDir, "personas"),
|
|
58650
58106
|
// v2 多人 persona 隔离:handler 派生 guest user-dir 放行
|
|
58651
58107
|
usersRoot,
|
|
58652
58108
|
// capability:list / delete handler 依赖
|
|
@@ -58666,9 +58122,6 @@ async function startDaemon(config) {
|
|
|
58666
58122
|
inboxStore,
|
|
58667
58123
|
// 联系人列表 store(device:connect / 自动反向落同一 store)
|
|
58668
58124
|
contactStore,
|
|
58669
|
-
// <dataDir>/sshd 绝对路径 —— contact-ssh handlers 用它拼 authorized_keys / keys/ 子路径
|
|
58670
|
-
// Task 10 会加 SshdManager 起 sshd;handlers wire 提前挂 sshdDir 让 typecheck 过
|
|
58671
|
-
sshdDir: import_node_path60.default.join(config.dataDir, "sshd"),
|
|
58672
58125
|
// inbox:sendDm 用:sessionId → session 出身(复用 attachment 同款 findOwnedSessionScope)
|
|
58673
58126
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
58674
58127
|
// contact:removed broadcast;复用 capability:tokenIssued 同款通路
|
|
@@ -58758,11 +58211,11 @@ async function startDaemon(config) {
|
|
|
58758
58211
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2):
|
|
58759
58212
|
// appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
|
|
58760
58213
|
// dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
|
|
58761
|
-
appBuilderPersonaRoot:
|
|
58214
|
+
appBuilderPersonaRoot: import_node_path55.default.join(config.dataDir, "personas", "persona-app-builder"),
|
|
58762
58215
|
// 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
|
|
58763
|
-
deployKitRoot:
|
|
58216
|
+
deployKitRoot: import_node_path55.default.join(config.dataDir, "deploy-kit"),
|
|
58764
58217
|
// scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
|
|
58765
|
-
resolvePersonaRoot: (personaId) =>
|
|
58218
|
+
resolvePersonaRoot: (personaId) => import_node_path55.default.join(config.dataDir, "personas", personaId),
|
|
58766
58219
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
|
|
58767
58220
|
// 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
|
|
58768
58221
|
// 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
|
|
@@ -58805,7 +58258,7 @@ async function startDaemon(config) {
|
|
|
58805
58258
|
}
|
|
58806
58259
|
let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
|
|
58807
58260
|
if (sourceFile && sourceFile.toolSessionId) {
|
|
58808
|
-
sourceJsonlPath =
|
|
58261
|
+
sourceJsonlPath = import_node_path55.default.join(
|
|
58809
58262
|
import_node_os21.default.homedir(),
|
|
58810
58263
|
".claude",
|
|
58811
58264
|
"projects",
|
|
@@ -59105,8 +58558,8 @@ async function startDaemon(config) {
|
|
|
59105
58558
|
const lines = [
|
|
59106
58559
|
`Tunnel: ${r.url}`,
|
|
59107
58560
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
59108
|
-
`Frpc config: ${
|
|
59109
|
-
`Frpc log: ${
|
|
58561
|
+
`Frpc config: ${import_node_path55.default.join(config.dataDir, "frpc.toml")}`,
|
|
58562
|
+
`Frpc log: ${import_node_path55.default.join(config.dataDir, "frpc.log")}`
|
|
59110
58563
|
];
|
|
59111
58564
|
const width = Math.max(...lines.map((l) => l.length));
|
|
59112
58565
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -59119,8 +58572,8 @@ ${bar}
|
|
|
59119
58572
|
|
|
59120
58573
|
`);
|
|
59121
58574
|
try {
|
|
59122
|
-
const connectPath =
|
|
59123
|
-
|
|
58575
|
+
const connectPath = import_node_path55.default.join(config.dataDir, "connect.txt");
|
|
58576
|
+
import_node_fs42.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
59124
58577
|
} catch {
|
|
59125
58578
|
}
|
|
59126
58579
|
} catch (err) {
|
|
@@ -59145,22 +58598,6 @@ ${bar}
|
|
|
59145
58598
|
logger.warn("tunnel unavailable, degraded to local mode", { reason: tunnelError });
|
|
59146
58599
|
}
|
|
59147
58600
|
}
|
|
59148
|
-
const sshdMgr = new SshdManager({
|
|
59149
|
-
dataDir: config.dataDir,
|
|
59150
|
-
port: config.sshdPort,
|
|
59151
|
-
logger,
|
|
59152
|
-
installProcessExitHandlers: true,
|
|
59153
|
-
onSshdExit: (info) => logger.warn("sshd exited unexpectedly", info)
|
|
59154
|
-
});
|
|
59155
|
-
try {
|
|
59156
|
-
await sshdMgr.start();
|
|
59157
|
-
rebuildAuthorizedKeys(contactStore, import_node_path60.default.join(config.dataDir, "sshd"));
|
|
59158
|
-
logger.info("sshd: contact-ssh sandbox ready", { port: config.sshdPort });
|
|
59159
|
-
} catch (err) {
|
|
59160
|
-
logger.warn("sshd start failed; contact SSH grant will not work until fixed", {
|
|
59161
|
-
err: err.message
|
|
59162
|
-
});
|
|
59163
|
-
}
|
|
59164
58601
|
void reportDevice();
|
|
59165
58602
|
void fetchServerKey();
|
|
59166
58603
|
const tickAttachmentGc = () => {
|
|
@@ -59195,11 +58632,6 @@ ${bar}
|
|
|
59195
58632
|
if (tunnelMgr) {
|
|
59196
58633
|
await tunnelMgr.stop();
|
|
59197
58634
|
}
|
|
59198
|
-
await sshdMgr.stop().catch((err) => {
|
|
59199
|
-
logger.warn("shutdown.sshd-stop-failed", {
|
|
59200
|
-
error: err instanceof Error ? err.message : String(err)
|
|
59201
|
-
});
|
|
59202
|
-
});
|
|
59203
58635
|
await wss.stop();
|
|
59204
58636
|
stateMgr.delete();
|
|
59205
58637
|
if (logClient) await logClient.dispose();
|
|
@@ -59213,9 +58645,9 @@ ${bar}
|
|
|
59213
58645
|
};
|
|
59214
58646
|
}
|
|
59215
58647
|
function migrateDropPersonsDir(dataDir) {
|
|
59216
|
-
const dir =
|
|
58648
|
+
const dir = import_node_path55.default.join(dataDir, "persons");
|
|
59217
58649
|
try {
|
|
59218
|
-
|
|
58650
|
+
import_node_fs42.default.rmSync(dir, { recursive: true, force: true });
|
|
59219
58651
|
} catch {
|
|
59220
58652
|
}
|
|
59221
58653
|
}
|