@clawos-dev/clawd 0.2.85-beta.160.958069f → 0.2.86-beta.161.3250922
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 +663 -451
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -643,8 +643,8 @@ var init_parseUtil = __esm({
|
|
|
643
643
|
init_errors2();
|
|
644
644
|
init_en();
|
|
645
645
|
makeIssue = (params) => {
|
|
646
|
-
const { data, path:
|
|
647
|
-
const fullPath = [...
|
|
646
|
+
const { data, path: path45, errorMaps, issueData } = params;
|
|
647
|
+
const fullPath = [...path45, ...issueData.path || []];
|
|
648
648
|
const fullIssue = {
|
|
649
649
|
...issueData,
|
|
650
650
|
path: fullPath
|
|
@@ -955,11 +955,11 @@ var init_types = __esm({
|
|
|
955
955
|
init_parseUtil();
|
|
956
956
|
init_util();
|
|
957
957
|
ParseInputLazyPath = class {
|
|
958
|
-
constructor(parent, value,
|
|
958
|
+
constructor(parent, value, path45, key) {
|
|
959
959
|
this._cachedPath = [];
|
|
960
960
|
this.parent = parent;
|
|
961
961
|
this.data = value;
|
|
962
|
-
this._path =
|
|
962
|
+
this._path = path45;
|
|
963
963
|
this._key = key;
|
|
964
964
|
}
|
|
965
965
|
get path() {
|
|
@@ -4343,13 +4343,13 @@ var init_zod = __esm({
|
|
|
4343
4343
|
});
|
|
4344
4344
|
|
|
4345
4345
|
// ../protocol/src/attachment-schemas.ts
|
|
4346
|
-
var TOKEN_ROLES, GROUP_FILE_SOURCES, GroupFileEntrySchema, AttachmentSignUrlArgs, AttachmentSignUrlResponseSchema, AttachmentGroupAddArgs, AttachmentGroupAddResponseSchema, AttachmentGroupRemoveArgs, AttachmentGroupRemoveResponseSchema, AttachmentGroupListArgs, AttachmentGroupListResponseSchema;
|
|
4346
|
+
var TOKEN_ROLES, GROUP_FILE_SOURCES, GroupFileEntrySchema, AttachmentSignUrlArgs, AttachmentSignUrlResponseSchema, AttachmentGroupAddArgs, AttachmentGroupAddResponseSchema, AttachmentGroupRemoveArgs, AttachmentGroupRemoveResponseSchema, AttachmentGroupListArgs, AttachmentGroupListResponseSchema, AttachmentUploadResponseSchema;
|
|
4347
4347
|
var init_attachment_schemas = __esm({
|
|
4348
4348
|
"../protocol/src/attachment-schemas.ts"() {
|
|
4349
4349
|
"use strict";
|
|
4350
4350
|
init_zod();
|
|
4351
4351
|
TOKEN_ROLES = ["owner"];
|
|
4352
|
-
GROUP_FILE_SOURCES = ["agent", "owner"];
|
|
4352
|
+
GROUP_FILE_SOURCES = ["agent", "owner", "user-upload"];
|
|
4353
4353
|
GroupFileEntrySchema = external_exports.object({
|
|
4354
4354
|
/** daemon 派发的稳定 id(用于 RPC remove / UI key) */
|
|
4355
4355
|
id: external_exports.string().min(1),
|
|
@@ -4404,6 +4404,16 @@ var init_attachment_schemas = __esm({
|
|
|
4404
4404
|
AttachmentGroupListResponseSchema = external_exports.object({
|
|
4405
4405
|
entries: external_exports.array(GroupFileEntrySchema)
|
|
4406
4406
|
});
|
|
4407
|
+
AttachmentUploadResponseSchema = external_exports.object({
|
|
4408
|
+
/** sha256(bytes) 前 16 位,前端持有的稳定 ref */
|
|
4409
|
+
attachmentId: external_exports.string().min(1),
|
|
4410
|
+
/** 完整 tunnel signed URL,前端拼 marker / 直接送 Anthropic 都用它 */
|
|
4411
|
+
url: external_exports.string().min(1),
|
|
4412
|
+
/** 落在 session 目录下的相对路径(后续 group RPC / drawer 展示用) */
|
|
4413
|
+
relPath: external_exports.string().min(1),
|
|
4414
|
+
sizeBytes: external_exports.number().int().nonnegative(),
|
|
4415
|
+
mimeType: external_exports.string().min(1)
|
|
4416
|
+
});
|
|
4407
4417
|
}
|
|
4408
4418
|
});
|
|
4409
4419
|
|
|
@@ -5580,8 +5590,8 @@ var require_req = __commonJS({
|
|
|
5580
5590
|
if (req.originalUrl) {
|
|
5581
5591
|
_req.url = req.originalUrl;
|
|
5582
5592
|
} else {
|
|
5583
|
-
const
|
|
5584
|
-
_req.url = typeof
|
|
5593
|
+
const path45 = req.path;
|
|
5594
|
+
_req.url = typeof path45 === "string" ? path45 : req.url ? req.url.path || req.url : void 0;
|
|
5585
5595
|
}
|
|
5586
5596
|
if (req.query) {
|
|
5587
5597
|
_req.query = req.query;
|
|
@@ -5746,14 +5756,14 @@ var require_redact = __commonJS({
|
|
|
5746
5756
|
}
|
|
5747
5757
|
return obj;
|
|
5748
5758
|
}
|
|
5749
|
-
function parsePath(
|
|
5759
|
+
function parsePath(path45) {
|
|
5750
5760
|
const parts = [];
|
|
5751
5761
|
let current = "";
|
|
5752
5762
|
let inBrackets = false;
|
|
5753
5763
|
let inQuotes = false;
|
|
5754
5764
|
let quoteChar = "";
|
|
5755
|
-
for (let i = 0; i <
|
|
5756
|
-
const char =
|
|
5765
|
+
for (let i = 0; i < path45.length; i++) {
|
|
5766
|
+
const char = path45[i];
|
|
5757
5767
|
if (!inBrackets && char === ".") {
|
|
5758
5768
|
if (current) {
|
|
5759
5769
|
parts.push(current);
|
|
@@ -5884,10 +5894,10 @@ var require_redact = __commonJS({
|
|
|
5884
5894
|
return current;
|
|
5885
5895
|
}
|
|
5886
5896
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
5887
|
-
for (const
|
|
5888
|
-
const parts = parsePath(
|
|
5897
|
+
for (const path45 of paths) {
|
|
5898
|
+
const parts = parsePath(path45);
|
|
5889
5899
|
if (parts.includes("*")) {
|
|
5890
|
-
redactWildcardPath(obj, parts, censor,
|
|
5900
|
+
redactWildcardPath(obj, parts, censor, path45, remove);
|
|
5891
5901
|
} else {
|
|
5892
5902
|
if (remove) {
|
|
5893
5903
|
removeKey(obj, parts);
|
|
@@ -5972,8 +5982,8 @@ var require_redact = __commonJS({
|
|
|
5972
5982
|
}
|
|
5973
5983
|
} else {
|
|
5974
5984
|
if (afterWildcard.includes("*")) {
|
|
5975
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
5976
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
5985
|
+
const wrappedCensor = typeof censor === "function" ? (value, path45) => {
|
|
5986
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path45];
|
|
5977
5987
|
return censor(value, fullPath);
|
|
5978
5988
|
} : censor;
|
|
5979
5989
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -6008,8 +6018,8 @@ var require_redact = __commonJS({
|
|
|
6008
6018
|
return null;
|
|
6009
6019
|
}
|
|
6010
6020
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
6011
|
-
for (const
|
|
6012
|
-
const parts = parsePath(
|
|
6021
|
+
for (const path45 of pathsToClone) {
|
|
6022
|
+
const parts = parsePath(path45);
|
|
6013
6023
|
let current = pathStructure;
|
|
6014
6024
|
for (let i = 0; i < parts.length; i++) {
|
|
6015
6025
|
const part = parts[i];
|
|
@@ -6061,24 +6071,24 @@ var require_redact = __commonJS({
|
|
|
6061
6071
|
}
|
|
6062
6072
|
return cloneSelectively(obj, pathStructure);
|
|
6063
6073
|
}
|
|
6064
|
-
function validatePath(
|
|
6065
|
-
if (typeof
|
|
6074
|
+
function validatePath(path45) {
|
|
6075
|
+
if (typeof path45 !== "string") {
|
|
6066
6076
|
throw new Error("Paths must be (non-empty) strings");
|
|
6067
6077
|
}
|
|
6068
|
-
if (
|
|
6078
|
+
if (path45 === "") {
|
|
6069
6079
|
throw new Error("Invalid redaction path ()");
|
|
6070
6080
|
}
|
|
6071
|
-
if (
|
|
6072
|
-
throw new Error(`Invalid redaction path (${
|
|
6081
|
+
if (path45.includes("..")) {
|
|
6082
|
+
throw new Error(`Invalid redaction path (${path45})`);
|
|
6073
6083
|
}
|
|
6074
|
-
if (
|
|
6075
|
-
throw new Error(`Invalid redaction path (${
|
|
6084
|
+
if (path45.includes(",")) {
|
|
6085
|
+
throw new Error(`Invalid redaction path (${path45})`);
|
|
6076
6086
|
}
|
|
6077
6087
|
let bracketCount = 0;
|
|
6078
6088
|
let inQuotes = false;
|
|
6079
6089
|
let quoteChar = "";
|
|
6080
|
-
for (let i = 0; i <
|
|
6081
|
-
const char =
|
|
6090
|
+
for (let i = 0; i < path45.length; i++) {
|
|
6091
|
+
const char = path45[i];
|
|
6082
6092
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
6083
6093
|
if (!inQuotes) {
|
|
6084
6094
|
inQuotes = true;
|
|
@@ -6092,20 +6102,20 @@ var require_redact = __commonJS({
|
|
|
6092
6102
|
} else if (char === "]" && !inQuotes) {
|
|
6093
6103
|
bracketCount--;
|
|
6094
6104
|
if (bracketCount < 0) {
|
|
6095
|
-
throw new Error(`Invalid redaction path (${
|
|
6105
|
+
throw new Error(`Invalid redaction path (${path45})`);
|
|
6096
6106
|
}
|
|
6097
6107
|
}
|
|
6098
6108
|
}
|
|
6099
6109
|
if (bracketCount !== 0) {
|
|
6100
|
-
throw new Error(`Invalid redaction path (${
|
|
6110
|
+
throw new Error(`Invalid redaction path (${path45})`);
|
|
6101
6111
|
}
|
|
6102
6112
|
}
|
|
6103
6113
|
function validatePaths(paths) {
|
|
6104
6114
|
if (!Array.isArray(paths)) {
|
|
6105
6115
|
throw new TypeError("paths must be an array");
|
|
6106
6116
|
}
|
|
6107
|
-
for (const
|
|
6108
|
-
validatePath(
|
|
6117
|
+
for (const path45 of paths) {
|
|
6118
|
+
validatePath(path45);
|
|
6109
6119
|
}
|
|
6110
6120
|
}
|
|
6111
6121
|
function slowRedact(options = {}) {
|
|
@@ -6273,8 +6283,8 @@ var require_redaction = __commonJS({
|
|
|
6273
6283
|
if (shape[k2] === null) {
|
|
6274
6284
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
6275
6285
|
} else {
|
|
6276
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6277
|
-
return censor(value, [k2, ...
|
|
6286
|
+
const wrappedCensor = typeof censor === "function" ? (value, path45) => {
|
|
6287
|
+
return censor(value, [k2, ...path45]);
|
|
6278
6288
|
} : censor;
|
|
6279
6289
|
o[k2] = Redact({
|
|
6280
6290
|
paths: shape[k2],
|
|
@@ -6492,10 +6502,10 @@ var require_atomic_sleep = __commonJS({
|
|
|
6492
6502
|
var require_sonic_boom = __commonJS({
|
|
6493
6503
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
6494
6504
|
"use strict";
|
|
6495
|
-
var
|
|
6505
|
+
var fs36 = require("fs");
|
|
6496
6506
|
var EventEmitter2 = require("events");
|
|
6497
6507
|
var inherits = require("util").inherits;
|
|
6498
|
-
var
|
|
6508
|
+
var path45 = require("path");
|
|
6499
6509
|
var sleep = require_atomic_sleep();
|
|
6500
6510
|
var assert = require("assert");
|
|
6501
6511
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -6549,20 +6559,20 @@ var require_sonic_boom = __commonJS({
|
|
|
6549
6559
|
const mode = sonic.mode;
|
|
6550
6560
|
if (sonic.sync) {
|
|
6551
6561
|
try {
|
|
6552
|
-
if (sonic.mkdir)
|
|
6553
|
-
const fd =
|
|
6562
|
+
if (sonic.mkdir) fs36.mkdirSync(path45.dirname(file), { recursive: true });
|
|
6563
|
+
const fd = fs36.openSync(file, flags, mode);
|
|
6554
6564
|
fileOpened(null, fd);
|
|
6555
6565
|
} catch (err) {
|
|
6556
6566
|
fileOpened(err);
|
|
6557
6567
|
throw err;
|
|
6558
6568
|
}
|
|
6559
6569
|
} else if (sonic.mkdir) {
|
|
6560
|
-
|
|
6570
|
+
fs36.mkdir(path45.dirname(file), { recursive: true }, (err) => {
|
|
6561
6571
|
if (err) return fileOpened(err);
|
|
6562
|
-
|
|
6572
|
+
fs36.open(file, flags, mode, fileOpened);
|
|
6563
6573
|
});
|
|
6564
6574
|
} else {
|
|
6565
|
-
|
|
6575
|
+
fs36.open(file, flags, mode, fileOpened);
|
|
6566
6576
|
}
|
|
6567
6577
|
}
|
|
6568
6578
|
function SonicBoom(opts) {
|
|
@@ -6603,8 +6613,8 @@ var require_sonic_boom = __commonJS({
|
|
|
6603
6613
|
this.flush = flushBuffer;
|
|
6604
6614
|
this.flushSync = flushBufferSync;
|
|
6605
6615
|
this._actualWrite = actualWriteBuffer;
|
|
6606
|
-
fsWriteSync = () =>
|
|
6607
|
-
fsWrite = () =>
|
|
6616
|
+
fsWriteSync = () => fs36.writeSync(this.fd, this._writingBuf);
|
|
6617
|
+
fsWrite = () => fs36.write(this.fd, this._writingBuf, this.release);
|
|
6608
6618
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
6609
6619
|
this._writingBuf = "";
|
|
6610
6620
|
this.write = write;
|
|
@@ -6613,15 +6623,15 @@ var require_sonic_boom = __commonJS({
|
|
|
6613
6623
|
this._actualWrite = actualWrite;
|
|
6614
6624
|
fsWriteSync = () => {
|
|
6615
6625
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
6616
|
-
return
|
|
6626
|
+
return fs36.writeSync(this.fd, this._writingBuf);
|
|
6617
6627
|
}
|
|
6618
|
-
return
|
|
6628
|
+
return fs36.writeSync(this.fd, this._writingBuf, "utf8");
|
|
6619
6629
|
};
|
|
6620
6630
|
fsWrite = () => {
|
|
6621
6631
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
6622
|
-
return
|
|
6632
|
+
return fs36.write(this.fd, this._writingBuf, this.release);
|
|
6623
6633
|
}
|
|
6624
|
-
return
|
|
6634
|
+
return fs36.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
6625
6635
|
};
|
|
6626
6636
|
} else {
|
|
6627
6637
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -6678,7 +6688,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6678
6688
|
}
|
|
6679
6689
|
}
|
|
6680
6690
|
if (this._fsync) {
|
|
6681
|
-
|
|
6691
|
+
fs36.fsyncSync(this.fd);
|
|
6682
6692
|
}
|
|
6683
6693
|
const len = this._len;
|
|
6684
6694
|
if (this._reopening) {
|
|
@@ -6792,7 +6802,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6792
6802
|
const onDrain = () => {
|
|
6793
6803
|
if (!this._fsync) {
|
|
6794
6804
|
try {
|
|
6795
|
-
|
|
6805
|
+
fs36.fsync(this.fd, (err) => {
|
|
6796
6806
|
this._flushPending = false;
|
|
6797
6807
|
cb(err);
|
|
6798
6808
|
});
|
|
@@ -6894,7 +6904,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6894
6904
|
const fd = this.fd;
|
|
6895
6905
|
this.once("ready", () => {
|
|
6896
6906
|
if (fd !== this.fd) {
|
|
6897
|
-
|
|
6907
|
+
fs36.close(fd, (err) => {
|
|
6898
6908
|
if (err) {
|
|
6899
6909
|
return this.emit("error", err);
|
|
6900
6910
|
}
|
|
@@ -6943,7 +6953,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6943
6953
|
buf = this._bufs[0];
|
|
6944
6954
|
}
|
|
6945
6955
|
try {
|
|
6946
|
-
const n = Buffer.isBuffer(buf) ?
|
|
6956
|
+
const n = Buffer.isBuffer(buf) ? fs36.writeSync(this.fd, buf) : fs36.writeSync(this.fd, buf, "utf8");
|
|
6947
6957
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
6948
6958
|
buf = releasedBufObj.writingBuf;
|
|
6949
6959
|
this._len = releasedBufObj.len;
|
|
@@ -6959,7 +6969,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6959
6969
|
}
|
|
6960
6970
|
}
|
|
6961
6971
|
try {
|
|
6962
|
-
|
|
6972
|
+
fs36.fsyncSync(this.fd);
|
|
6963
6973
|
} catch {
|
|
6964
6974
|
}
|
|
6965
6975
|
}
|
|
@@ -6980,7 +6990,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6980
6990
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
6981
6991
|
}
|
|
6982
6992
|
try {
|
|
6983
|
-
const n =
|
|
6993
|
+
const n = fs36.writeSync(this.fd, buf);
|
|
6984
6994
|
buf = buf.subarray(n);
|
|
6985
6995
|
this._len = Math.max(this._len - n, 0);
|
|
6986
6996
|
if (buf.length <= 0) {
|
|
@@ -7008,13 +7018,13 @@ var require_sonic_boom = __commonJS({
|
|
|
7008
7018
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
7009
7019
|
if (this.sync) {
|
|
7010
7020
|
try {
|
|
7011
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
7021
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs36.writeSync(this.fd, this._writingBuf) : fs36.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7012
7022
|
release(null, written);
|
|
7013
7023
|
} catch (err) {
|
|
7014
7024
|
release(err);
|
|
7015
7025
|
}
|
|
7016
7026
|
} else {
|
|
7017
|
-
|
|
7027
|
+
fs36.write(this.fd, this._writingBuf, release);
|
|
7018
7028
|
}
|
|
7019
7029
|
}
|
|
7020
7030
|
function actualWriteBuffer() {
|
|
@@ -7023,7 +7033,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7023
7033
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
7024
7034
|
if (this.sync) {
|
|
7025
7035
|
try {
|
|
7026
|
-
const written =
|
|
7036
|
+
const written = fs36.writeSync(this.fd, this._writingBuf);
|
|
7027
7037
|
release(null, written);
|
|
7028
7038
|
} catch (err) {
|
|
7029
7039
|
release(err);
|
|
@@ -7032,7 +7042,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7032
7042
|
if (kCopyBuffer) {
|
|
7033
7043
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
7034
7044
|
}
|
|
7035
|
-
|
|
7045
|
+
fs36.write(this.fd, this._writingBuf, release);
|
|
7036
7046
|
}
|
|
7037
7047
|
}
|
|
7038
7048
|
function actualClose(sonic) {
|
|
@@ -7048,12 +7058,12 @@ var require_sonic_boom = __commonJS({
|
|
|
7048
7058
|
sonic._lens = [];
|
|
7049
7059
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
7050
7060
|
try {
|
|
7051
|
-
|
|
7061
|
+
fs36.fsync(sonic.fd, closeWrapped);
|
|
7052
7062
|
} catch {
|
|
7053
7063
|
}
|
|
7054
7064
|
function closeWrapped() {
|
|
7055
7065
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
7056
|
-
|
|
7066
|
+
fs36.close(sonic.fd, done);
|
|
7057
7067
|
} else {
|
|
7058
7068
|
done();
|
|
7059
7069
|
}
|
|
@@ -7784,7 +7794,7 @@ var require_transport = __commonJS({
|
|
|
7784
7794
|
stream.flushSync();
|
|
7785
7795
|
}
|
|
7786
7796
|
function transport(fullOptions) {
|
|
7787
|
-
const { pipeline:
|
|
7797
|
+
const { pipeline: pipeline3, targets, levels, dedupe, worker = {}, caller = getCallers(), sync = false } = fullOptions;
|
|
7788
7798
|
const options = {
|
|
7789
7799
|
...fullOptions.options
|
|
7790
7800
|
};
|
|
@@ -7812,9 +7822,9 @@ var require_transport = __commonJS({
|
|
|
7812
7822
|
};
|
|
7813
7823
|
});
|
|
7814
7824
|
});
|
|
7815
|
-
} else if (
|
|
7825
|
+
} else if (pipeline3) {
|
|
7816
7826
|
target = bundlerOverrides["pino-worker"] || join11(__dirname, "worker.js");
|
|
7817
|
-
options.pipelines = [
|
|
7827
|
+
options.pipelines = [pipeline3.map((dest) => {
|
|
7818
7828
|
return {
|
|
7819
7829
|
...dest,
|
|
7820
7830
|
target: fixTarget(dest.target)
|
|
@@ -9417,7 +9427,7 @@ var require_multistream = __commonJS({
|
|
|
9417
9427
|
var require_pino = __commonJS({
|
|
9418
9428
|
"../node_modules/.pnpm/pino@9.14.0/node_modules/pino/pino.js"(exports2, module2) {
|
|
9419
9429
|
"use strict";
|
|
9420
|
-
var
|
|
9430
|
+
var os16 = require("os");
|
|
9421
9431
|
var stdSerializers = require_pino_std_serializers();
|
|
9422
9432
|
var caller = require_caller();
|
|
9423
9433
|
var redaction = require_redaction();
|
|
@@ -9464,7 +9474,7 @@ var require_pino = __commonJS({
|
|
|
9464
9474
|
} = symbols;
|
|
9465
9475
|
var { epochTime, nullTime } = time;
|
|
9466
9476
|
var { pid } = process;
|
|
9467
|
-
var hostname =
|
|
9477
|
+
var hostname = os16.hostname();
|
|
9468
9478
|
var defaultErrorSerializer = stdSerializers.err;
|
|
9469
9479
|
var defaultOptions = {
|
|
9470
9480
|
level: "info",
|
|
@@ -10188,11 +10198,11 @@ var init_lib = __esm({
|
|
|
10188
10198
|
}
|
|
10189
10199
|
}
|
|
10190
10200
|
},
|
|
10191
|
-
addToPath: function addToPath(
|
|
10192
|
-
var last =
|
|
10201
|
+
addToPath: function addToPath(path45, added, removed, oldPosInc, options) {
|
|
10202
|
+
var last = path45.lastComponent;
|
|
10193
10203
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
10194
10204
|
return {
|
|
10195
|
-
oldPos:
|
|
10205
|
+
oldPos: path45.oldPos + oldPosInc,
|
|
10196
10206
|
lastComponent: {
|
|
10197
10207
|
count: last.count + 1,
|
|
10198
10208
|
added,
|
|
@@ -10202,7 +10212,7 @@ var init_lib = __esm({
|
|
|
10202
10212
|
};
|
|
10203
10213
|
} else {
|
|
10204
10214
|
return {
|
|
10205
|
-
oldPos:
|
|
10215
|
+
oldPos: path45.oldPos + oldPosInc,
|
|
10206
10216
|
lastComponent: {
|
|
10207
10217
|
count: 1,
|
|
10208
10218
|
added,
|
|
@@ -10633,10 +10643,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
10633
10643
|
const memories = raw.map((m2) => {
|
|
10634
10644
|
if (!m2 || typeof m2 !== "object") return null;
|
|
10635
10645
|
const rec = m2;
|
|
10636
|
-
const
|
|
10646
|
+
const path45 = typeof rec.path === "string" ? rec.path : null;
|
|
10637
10647
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
10638
|
-
if (!
|
|
10639
|
-
const entry = { path:
|
|
10648
|
+
if (!path45 || content == null) return null;
|
|
10649
|
+
const entry = { path: path45, content };
|
|
10640
10650
|
if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
|
|
10641
10651
|
return entry;
|
|
10642
10652
|
}).filter((m2) => m2 !== null);
|
|
@@ -11462,10 +11472,10 @@ function parseAttachment(obj) {
|
|
|
11462
11472
|
const memories = raw.map((m2) => {
|
|
11463
11473
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11464
11474
|
const rec = m2;
|
|
11465
|
-
const
|
|
11475
|
+
const path45 = typeof rec.path === "string" ? rec.path : null;
|
|
11466
11476
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
11467
|
-
if (!
|
|
11468
|
-
const out = { path:
|
|
11477
|
+
if (!path45 || content == null) return null;
|
|
11478
|
+
const out = { path: path45, content };
|
|
11469
11479
|
if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
|
|
11470
11480
|
return out;
|
|
11471
11481
|
}).filter((m2) => m2 !== null);
|
|
@@ -20380,7 +20390,7 @@ var require_websocket_server = __commonJS({
|
|
|
20380
20390
|
// src/run-case/recorder.ts
|
|
20381
20391
|
function startRunCaseRecorder(opts) {
|
|
20382
20392
|
const now = opts.now ?? Date.now;
|
|
20383
|
-
const dir =
|
|
20393
|
+
const dir = import_node_path31.default.dirname(opts.recordPath);
|
|
20384
20394
|
let stream = null;
|
|
20385
20395
|
let closing = false;
|
|
20386
20396
|
let closedSettled = false;
|
|
@@ -20394,8 +20404,8 @@ function startRunCaseRecorder(opts) {
|
|
|
20394
20404
|
});
|
|
20395
20405
|
const ensureStream = () => {
|
|
20396
20406
|
if (stream) return stream;
|
|
20397
|
-
|
|
20398
|
-
stream =
|
|
20407
|
+
import_node_fs28.default.mkdirSync(dir, { recursive: true });
|
|
20408
|
+
stream = import_node_fs28.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
20399
20409
|
stream.on("close", () => closedResolve());
|
|
20400
20410
|
return stream;
|
|
20401
20411
|
};
|
|
@@ -20420,12 +20430,12 @@ function startRunCaseRecorder(opts) {
|
|
|
20420
20430
|
};
|
|
20421
20431
|
return { tap, close, closed };
|
|
20422
20432
|
}
|
|
20423
|
-
var
|
|
20433
|
+
var import_node_fs28, import_node_path31;
|
|
20424
20434
|
var init_recorder = __esm({
|
|
20425
20435
|
"src/run-case/recorder.ts"() {
|
|
20426
20436
|
"use strict";
|
|
20427
|
-
|
|
20428
|
-
|
|
20437
|
+
import_node_fs28 = __toESM(require("fs"), 1);
|
|
20438
|
+
import_node_path31 = __toESM(require("path"), 1);
|
|
20429
20439
|
}
|
|
20430
20440
|
});
|
|
20431
20441
|
|
|
@@ -20468,7 +20478,7 @@ var init_wire = __esm({
|
|
|
20468
20478
|
// src/run-case/controller.ts
|
|
20469
20479
|
async function runController(opts) {
|
|
20470
20480
|
const now = opts.now ?? Date.now;
|
|
20471
|
-
const cwd = opts.cwd ?? (0,
|
|
20481
|
+
const cwd = opts.cwd ?? (0, import_node_fs29.mkdtempSync)(import_node_path32.default.join(import_node_os14.default.tmpdir(), "clawd-runcase-"));
|
|
20472
20482
|
const ownsCwd = opts.cwd === void 0;
|
|
20473
20483
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
20474
20484
|
const spawnCtx = { cwd };
|
|
@@ -20629,19 +20639,19 @@ async function runController(opts) {
|
|
|
20629
20639
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
20630
20640
|
if (ownsCwd) {
|
|
20631
20641
|
try {
|
|
20632
|
-
(0,
|
|
20642
|
+
(0, import_node_fs29.rmSync)(cwd, { recursive: true, force: true });
|
|
20633
20643
|
} catch {
|
|
20634
20644
|
}
|
|
20635
20645
|
}
|
|
20636
20646
|
return exitCode ?? 0;
|
|
20637
20647
|
}
|
|
20638
|
-
var
|
|
20648
|
+
var import_node_fs29, import_node_os14, import_node_path32;
|
|
20639
20649
|
var init_controller = __esm({
|
|
20640
20650
|
"src/run-case/controller.ts"() {
|
|
20641
20651
|
"use strict";
|
|
20642
|
-
|
|
20643
|
-
|
|
20644
|
-
|
|
20652
|
+
import_node_fs29 = require("fs");
|
|
20653
|
+
import_node_os14 = __toESM(require("os"), 1);
|
|
20654
|
+
import_node_path32 = __toESM(require("path"), 1);
|
|
20645
20655
|
init_claude();
|
|
20646
20656
|
init_stdout_splitter();
|
|
20647
20657
|
init_permission_stdio();
|
|
@@ -20873,8 +20883,8 @@ Env (advanced):
|
|
|
20873
20883
|
`;
|
|
20874
20884
|
|
|
20875
20885
|
// src/index.ts
|
|
20876
|
-
var
|
|
20877
|
-
var
|
|
20886
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
20887
|
+
var import_node_fs27 = __toESM(require("fs"), 1);
|
|
20878
20888
|
|
|
20879
20889
|
// src/logger.ts
|
|
20880
20890
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
@@ -22609,6 +22619,18 @@ var SessionManager = class {
|
|
|
22609
22619
|
(a, b2) => a.updatedAt > b2.updatedAt ? -1 : a.updatedAt < b2.updatedAt ? 1 : 0
|
|
22610
22620
|
);
|
|
22611
22621
|
}
|
|
22622
|
+
/**
|
|
22623
|
+
* 跨 scope 列出所有 owner 视角的 session(default + persona owner + persona guest),
|
|
22624
|
+
* 附带派生的 SessionScope。attachment GC tick 用 —— 需要遍历所有活跃 session 的
|
|
22625
|
+
* .attachments 目录 + 群文件清单求 liveSet。
|
|
22626
|
+
*/
|
|
22627
|
+
listOwnedSessionScopes() {
|
|
22628
|
+
return this.listAllOwned().map((file) => ({
|
|
22629
|
+
scope: this.scopeForFile(file),
|
|
22630
|
+
sessionId: file.sessionId,
|
|
22631
|
+
cwd: file.cwd
|
|
22632
|
+
}));
|
|
22633
|
+
}
|
|
22612
22634
|
// 写回 SessionFile,自动根据 ownerPersonaId / creatorPrincipalId 派生写入 scope.
|
|
22613
22635
|
// 调用方原先都是 `deps.store.write(updated)`,全部走这里收口路由.
|
|
22614
22636
|
writeOwned(file) {
|
|
@@ -25509,151 +25531,28 @@ var AgentsScanner = class {
|
|
|
25509
25531
|
};
|
|
25510
25532
|
|
|
25511
25533
|
// src/observer/session-observer.ts
|
|
25512
|
-
var import_node_fs13 = __toESM(require("fs"), 1);
|
|
25513
|
-
var import_node_os9 = __toESM(require("os"), 1);
|
|
25514
|
-
var import_node_path14 = __toESM(require("path"), 1);
|
|
25515
|
-
init_claude_history();
|
|
25516
|
-
|
|
25517
|
-
// src/observer/subagent-meta-observer.ts
|
|
25518
25534
|
var import_node_fs12 = __toESM(require("fs"), 1);
|
|
25519
25535
|
var import_node_os8 = __toESM(require("os"), 1);
|
|
25520
25536
|
var import_node_path13 = __toESM(require("path"), 1);
|
|
25521
25537
|
init_claude_history();
|
|
25522
|
-
var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
|
|
25523
|
-
var SubagentMetaObserver = class {
|
|
25524
|
-
constructor(opts) {
|
|
25525
|
-
this.opts = opts;
|
|
25526
|
-
this.home = opts.home ?? import_node_os8.default.homedir();
|
|
25527
|
-
}
|
|
25528
|
-
opts;
|
|
25529
|
-
home;
|
|
25530
|
-
watches = /* @__PURE__ */ new Map();
|
|
25531
|
-
// public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
|
|
25532
|
-
resolveSubagentDir(cwd, toolSessionId) {
|
|
25533
|
-
return import_node_path13.default.join(
|
|
25534
|
-
this.home,
|
|
25535
|
-
".claude",
|
|
25536
|
-
"projects",
|
|
25537
|
-
cwdToHashDir(cwd),
|
|
25538
|
-
toolSessionId,
|
|
25539
|
-
"subagents"
|
|
25540
|
-
);
|
|
25541
|
-
}
|
|
25542
|
-
start(args) {
|
|
25543
|
-
this.stop(args.sessionId);
|
|
25544
|
-
const dirPath = this.resolveSubagentDir(args.cwd, args.toolSessionId);
|
|
25545
|
-
const w2 = {
|
|
25546
|
-
sessionId: args.sessionId,
|
|
25547
|
-
dirPath,
|
|
25548
|
-
watcher: null,
|
|
25549
|
-
pollTimer: null,
|
|
25550
|
-
emitted: /* @__PURE__ */ new Set()
|
|
25551
|
-
};
|
|
25552
|
-
this.watches.set(args.sessionId, w2);
|
|
25553
|
-
this.scan(w2);
|
|
25554
|
-
this.attachWatcher(w2);
|
|
25555
|
-
w2.pollTimer = setInterval(() => this.scan(w2), 200);
|
|
25556
|
-
return { dirPath };
|
|
25557
|
-
}
|
|
25558
|
-
// fs.watch 必须在目录存在后调用;首次启动时 subagents/ 通常不存在 —— attachWatcher
|
|
25559
|
-
// 失败就交给 pollTimer 200ms 兜底,目录一出现就被下次 poll 捡起来,那时也尝试 attach
|
|
25560
|
-
attachWatcher(w2) {
|
|
25561
|
-
if (w2.watcher) return;
|
|
25562
|
-
try {
|
|
25563
|
-
w2.watcher = import_node_fs12.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
|
|
25564
|
-
if (!name) return;
|
|
25565
|
-
const m2 = META_RE.exec(String(name));
|
|
25566
|
-
if (!m2) return;
|
|
25567
|
-
this.scanOne(w2, String(name));
|
|
25568
|
-
});
|
|
25569
|
-
} catch {
|
|
25570
|
-
}
|
|
25571
|
-
}
|
|
25572
|
-
scan(w2) {
|
|
25573
|
-
let entries;
|
|
25574
|
-
try {
|
|
25575
|
-
entries = import_node_fs12.default.readdirSync(w2.dirPath);
|
|
25576
|
-
} catch {
|
|
25577
|
-
return;
|
|
25578
|
-
}
|
|
25579
|
-
if (!w2.watcher) this.attachWatcher(w2);
|
|
25580
|
-
for (const name of entries) {
|
|
25581
|
-
this.scanOne(w2, name);
|
|
25582
|
-
}
|
|
25583
|
-
}
|
|
25584
|
-
scanOne(w2, name) {
|
|
25585
|
-
const m2 = META_RE.exec(name);
|
|
25586
|
-
if (!m2) return;
|
|
25587
|
-
const agentId = m2[1];
|
|
25588
|
-
if (w2.emitted.has(agentId)) return;
|
|
25589
|
-
const file = import_node_path13.default.join(w2.dirPath, name);
|
|
25590
|
-
let raw;
|
|
25591
|
-
try {
|
|
25592
|
-
raw = import_node_fs12.default.readFileSync(file, "utf8");
|
|
25593
|
-
} catch {
|
|
25594
|
-
return;
|
|
25595
|
-
}
|
|
25596
|
-
let obj;
|
|
25597
|
-
try {
|
|
25598
|
-
obj = JSON.parse(raw);
|
|
25599
|
-
} catch {
|
|
25600
|
-
return;
|
|
25601
|
-
}
|
|
25602
|
-
const toolUseId = typeof obj.toolUseId === "string" ? obj.toolUseId : "";
|
|
25603
|
-
if (!toolUseId) return;
|
|
25604
|
-
w2.emitted.add(agentId);
|
|
25605
|
-
const ev = {
|
|
25606
|
-
kind: "subagent_progress",
|
|
25607
|
-
toolUseId,
|
|
25608
|
-
agentId,
|
|
25609
|
-
status: "started"
|
|
25610
|
-
};
|
|
25611
|
-
if (typeof obj.description === "string" && obj.description) ev.description = obj.description;
|
|
25612
|
-
this.opts.onEvent({ sessionId: w2.sessionId, events: [ev] });
|
|
25613
|
-
}
|
|
25614
|
-
isObserving(sessionId) {
|
|
25615
|
-
return this.watches.has(sessionId);
|
|
25616
|
-
}
|
|
25617
|
-
stop(sessionId) {
|
|
25618
|
-
const w2 = this.watches.get(sessionId);
|
|
25619
|
-
if (!w2) return false;
|
|
25620
|
-
try {
|
|
25621
|
-
w2.watcher?.close();
|
|
25622
|
-
} catch {
|
|
25623
|
-
}
|
|
25624
|
-
if (w2.pollTimer) clearInterval(w2.pollTimer);
|
|
25625
|
-
this.watches.delete(sessionId);
|
|
25626
|
-
return true;
|
|
25627
|
-
}
|
|
25628
|
-
stopAll() {
|
|
25629
|
-
for (const id of [...this.watches.keys()]) this.stop(id);
|
|
25630
|
-
}
|
|
25631
|
-
};
|
|
25632
|
-
|
|
25633
|
-
// src/observer/session-observer.ts
|
|
25634
25538
|
var SessionObserver = class {
|
|
25635
25539
|
constructor(opts) {
|
|
25636
25540
|
this.opts = opts;
|
|
25637
|
-
this.home = opts.home ??
|
|
25638
|
-
this.metaObserver = opts.enableSubagentMetaObserver ? new SubagentMetaObserver({ home: this.home, onEvent: opts.onEvent }) : null;
|
|
25541
|
+
this.home = opts.home ?? import_node_os8.default.homedir();
|
|
25639
25542
|
}
|
|
25640
25543
|
opts;
|
|
25641
25544
|
home;
|
|
25642
25545
|
watches = /* @__PURE__ */ new Map();
|
|
25643
|
-
// 子 observer:监听 <projectsRoot>/<hashDir>/<toolSessionId>/subagents/ 目录的 meta.json,
|
|
25644
|
-
// 仅 TUI 模式启用——SDK 模式靠 `system.task_started` 帧(claude.ts:466)emit subagent_progress
|
|
25645
|
-
// 已经能解锁 AgentCallLine 抽屉入口,再跑 meta watcher 会让 ring buffer 留双帧
|
|
25646
|
-
metaObserver;
|
|
25647
25546
|
resolveJsonlPath(cwd, toolSessionId, override) {
|
|
25648
25547
|
if (override) return override;
|
|
25649
|
-
return
|
|
25548
|
+
return import_node_path13.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
|
|
25650
25549
|
}
|
|
25651
25550
|
start(args) {
|
|
25652
25551
|
this.stop(args.sessionId);
|
|
25653
25552
|
const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
|
|
25654
25553
|
let size = 0;
|
|
25655
25554
|
try {
|
|
25656
|
-
size =
|
|
25555
|
+
size = import_node_fs12.default.statSync(filePath).size;
|
|
25657
25556
|
} catch {
|
|
25658
25557
|
}
|
|
25659
25558
|
const w2 = {
|
|
@@ -25666,10 +25565,10 @@ var SessionObserver = class {
|
|
|
25666
25565
|
adapter: args.adapter
|
|
25667
25566
|
};
|
|
25668
25567
|
try {
|
|
25669
|
-
|
|
25568
|
+
import_node_fs12.default.mkdirSync(import_node_path13.default.dirname(filePath), { recursive: true });
|
|
25670
25569
|
} catch {
|
|
25671
25570
|
}
|
|
25672
|
-
w2.watcher =
|
|
25571
|
+
w2.watcher = import_node_fs12.default.watch(import_node_path13.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
|
|
25673
25572
|
if (!changedName || !filePath.endsWith(changedName)) return;
|
|
25674
25573
|
this.poll(w2);
|
|
25675
25574
|
});
|
|
@@ -25678,11 +25577,6 @@ var SessionObserver = class {
|
|
|
25678
25577
|
this.opts.onStatus?.(args.sessionId, "observing");
|
|
25679
25578
|
this.hydrateMetaTail(w2);
|
|
25680
25579
|
this.poll(w2);
|
|
25681
|
-
this.metaObserver?.start({
|
|
25682
|
-
sessionId: args.sessionId,
|
|
25683
|
-
cwd: args.cwd,
|
|
25684
|
-
toolSessionId: args.toolSessionId
|
|
25685
|
-
});
|
|
25686
25580
|
return { filePath };
|
|
25687
25581
|
}
|
|
25688
25582
|
// 读 JSONL 文件尾部 N 行,把每行的 meta_update 字段**合并成一个 patch** 喂给 reducer。
|
|
@@ -25692,7 +25586,7 @@ var SessionObserver = class {
|
|
|
25692
25586
|
// 异常静默吞,不阻塞 watcher 启动
|
|
25693
25587
|
hydrateMetaTail(w2, maxLines = 200) {
|
|
25694
25588
|
try {
|
|
25695
|
-
const raw =
|
|
25589
|
+
const raw = import_node_fs12.default.readFileSync(w2.filePath, "utf8");
|
|
25696
25590
|
if (!raw) return;
|
|
25697
25591
|
const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
25698
25592
|
if (allLines.length === 0) return;
|
|
@@ -25716,7 +25610,7 @@ var SessionObserver = class {
|
|
|
25716
25610
|
poll(w2) {
|
|
25717
25611
|
let size = 0;
|
|
25718
25612
|
try {
|
|
25719
|
-
size =
|
|
25613
|
+
size = import_node_fs12.default.statSync(w2.filePath).size;
|
|
25720
25614
|
} catch {
|
|
25721
25615
|
return;
|
|
25722
25616
|
}
|
|
@@ -25725,11 +25619,11 @@ var SessionObserver = class {
|
|
|
25725
25619
|
w2.buf = "";
|
|
25726
25620
|
}
|
|
25727
25621
|
if (size === w2.lastSize) return;
|
|
25728
|
-
const fd =
|
|
25622
|
+
const fd = import_node_fs12.default.openSync(w2.filePath, "r");
|
|
25729
25623
|
try {
|
|
25730
25624
|
const len = size - w2.lastSize;
|
|
25731
25625
|
const buf = Buffer.alloc(len);
|
|
25732
|
-
|
|
25626
|
+
import_node_fs12.default.readSync(fd, buf, 0, len, w2.lastSize);
|
|
25733
25627
|
w2.lastSize = size;
|
|
25734
25628
|
w2.buf += buf.toString("utf8");
|
|
25735
25629
|
let newlineIndex;
|
|
@@ -25743,7 +25637,7 @@ var SessionObserver = class {
|
|
|
25743
25637
|
}
|
|
25744
25638
|
}
|
|
25745
25639
|
} finally {
|
|
25746
|
-
|
|
25640
|
+
import_node_fs12.default.closeSync(fd);
|
|
25747
25641
|
}
|
|
25748
25642
|
}
|
|
25749
25643
|
// 解析 JSONL 单行:仅当是主链 user 文本行(非 sidechain / 非 sub-agent / message.role='user'
|
|
@@ -25796,13 +25690,11 @@ var SessionObserver = class {
|
|
|
25796
25690
|
}
|
|
25797
25691
|
if (w2.pollTimer) clearInterval(w2.pollTimer);
|
|
25798
25692
|
this.watches.delete(sessionId);
|
|
25799
|
-
this.metaObserver?.stop(sessionId);
|
|
25800
25693
|
this.opts.onStatus?.(sessionId, "stopped");
|
|
25801
25694
|
return true;
|
|
25802
25695
|
}
|
|
25803
25696
|
stopAll() {
|
|
25804
25697
|
for (const id of [...this.watches.keys()]) this.stop(id);
|
|
25805
|
-
this.metaObserver?.stopAll();
|
|
25806
25698
|
}
|
|
25807
25699
|
};
|
|
25808
25700
|
|
|
@@ -26403,14 +26295,14 @@ function authenticate(token, deps) {
|
|
|
26403
26295
|
}
|
|
26404
26296
|
|
|
26405
26297
|
// src/permission/capability-store.ts
|
|
26406
|
-
var
|
|
26407
|
-
var
|
|
26298
|
+
var fs15 = __toESM(require("fs"), 1);
|
|
26299
|
+
var path18 = __toESM(require("path"), 1);
|
|
26408
26300
|
var CAPABILITIES_FILE_NAME = "capabilities.json";
|
|
26409
26301
|
var FILE_VERSION = 1;
|
|
26410
26302
|
var CapabilityStore = class {
|
|
26411
26303
|
constructor(dataDir) {
|
|
26412
26304
|
this.dataDir = dataDir;
|
|
26413
|
-
|
|
26305
|
+
fs15.mkdirSync(dataDir, { recursive: true });
|
|
26414
26306
|
this.cache = this.readFromDisk();
|
|
26415
26307
|
}
|
|
26416
26308
|
dataDir;
|
|
@@ -26434,13 +26326,13 @@ var CapabilityStore = class {
|
|
|
26434
26326
|
this.flush();
|
|
26435
26327
|
}
|
|
26436
26328
|
filePath() {
|
|
26437
|
-
return
|
|
26329
|
+
return path18.join(this.dataDir, CAPABILITIES_FILE_NAME);
|
|
26438
26330
|
}
|
|
26439
26331
|
readFromDisk() {
|
|
26440
26332
|
const file = this.filePath();
|
|
26441
26333
|
let raw;
|
|
26442
26334
|
try {
|
|
26443
|
-
raw =
|
|
26335
|
+
raw = fs15.readFileSync(file, "utf8");
|
|
26444
26336
|
} catch (err) {
|
|
26445
26337
|
if (err?.code === "ENOENT") return [];
|
|
26446
26338
|
return [];
|
|
@@ -26468,10 +26360,10 @@ var CapabilityStore = class {
|
|
|
26468
26360
|
}
|
|
26469
26361
|
atomicWrite(file, content) {
|
|
26470
26362
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
26471
|
-
|
|
26472
|
-
|
|
26363
|
+
fs15.writeFileSync(tmp, content, { mode: 384 });
|
|
26364
|
+
fs15.renameSync(tmp, file);
|
|
26473
26365
|
try {
|
|
26474
|
-
|
|
26366
|
+
fs15.chmodSync(file, 384);
|
|
26475
26367
|
} catch {
|
|
26476
26368
|
}
|
|
26477
26369
|
}
|
|
@@ -26564,14 +26456,14 @@ var CapabilityManager = class {
|
|
|
26564
26456
|
};
|
|
26565
26457
|
|
|
26566
26458
|
// src/permission/cleanup.ts
|
|
26567
|
-
var
|
|
26459
|
+
var fs16 = __toESM(require("fs"), 1);
|
|
26568
26460
|
function cleanupGuestSessionsForCapability(cap, factory) {
|
|
26569
26461
|
const removed = [];
|
|
26570
26462
|
for (const g2 of cap.grants) {
|
|
26571
26463
|
if (g2.resource.type !== "persona") continue;
|
|
26572
26464
|
const dir = factory.vmGuestRoot(g2.resource.id, cap.id);
|
|
26573
26465
|
try {
|
|
26574
|
-
|
|
26466
|
+
fs16.rmSync(dir, { recursive: true, force: true });
|
|
26575
26467
|
removed.push(dir);
|
|
26576
26468
|
} catch {
|
|
26577
26469
|
}
|
|
@@ -26580,13 +26472,13 @@ function cleanupGuestSessionsForCapability(cap, factory) {
|
|
|
26580
26472
|
}
|
|
26581
26473
|
|
|
26582
26474
|
// src/permission/personal-capability.ts
|
|
26583
|
-
var
|
|
26584
|
-
var
|
|
26475
|
+
var import_node_fs13 = __toESM(require("fs"), 1);
|
|
26476
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
26585
26477
|
var import_node_crypto4 = __toESM(require("crypto"), 1);
|
|
26586
26478
|
var PERSONAL_CAP_FILE_NAME = "personal-capability.json";
|
|
26587
26479
|
var PERSONAL_CAP_DISPLAY_NAME = "personal";
|
|
26588
26480
|
function personalCapFilePath(dataDir) {
|
|
26589
|
-
return
|
|
26481
|
+
return import_node_path14.default.join(dataDir, PERSONAL_CAP_FILE_NAME);
|
|
26590
26482
|
}
|
|
26591
26483
|
function loadOrCreatePersonalCapability(opts) {
|
|
26592
26484
|
const file = personalCapFilePath(opts.dataDir);
|
|
@@ -26616,7 +26508,7 @@ function loadOrCreatePersonalCapability(opts) {
|
|
|
26616
26508
|
function readFile(file) {
|
|
26617
26509
|
let raw;
|
|
26618
26510
|
try {
|
|
26619
|
-
raw =
|
|
26511
|
+
raw = import_node_fs13.default.readFileSync(file, "utf8");
|
|
26620
26512
|
} catch (err) {
|
|
26621
26513
|
const code = err?.code;
|
|
26622
26514
|
if (code === "ENOENT") return null;
|
|
@@ -26637,10 +26529,10 @@ function readFile(file) {
|
|
|
26637
26529
|
return { token: obj.token, capability: capParsed.data };
|
|
26638
26530
|
}
|
|
26639
26531
|
function writeFile(file, content) {
|
|
26640
|
-
|
|
26641
|
-
|
|
26532
|
+
import_node_fs13.default.mkdirSync(import_node_path14.default.dirname(file), { recursive: true });
|
|
26533
|
+
import_node_fs13.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
26642
26534
|
try {
|
|
26643
|
-
|
|
26535
|
+
import_node_fs13.default.chmodSync(file, 384);
|
|
26644
26536
|
} catch {
|
|
26645
26537
|
}
|
|
26646
26538
|
}
|
|
@@ -26655,13 +26547,13 @@ function sha256Hex2(s) {
|
|
|
26655
26547
|
}
|
|
26656
26548
|
|
|
26657
26549
|
// src/inbox/inbox-store.ts
|
|
26658
|
-
var
|
|
26659
|
-
var
|
|
26550
|
+
var fs18 = __toESM(require("fs"), 1);
|
|
26551
|
+
var path20 = __toESM(require("path"), 1);
|
|
26660
26552
|
var INBOX_SUBDIR = "inbox";
|
|
26661
26553
|
var InboxStore = class {
|
|
26662
26554
|
constructor(dataDir) {
|
|
26663
26555
|
this.dataDir = dataDir;
|
|
26664
|
-
|
|
26556
|
+
fs18.mkdirSync(this.dirPath(), { recursive: true });
|
|
26665
26557
|
}
|
|
26666
26558
|
dataDir;
|
|
26667
26559
|
/**
|
|
@@ -26673,7 +26565,7 @@ var InboxStore = class {
|
|
|
26673
26565
|
const file = this.filePath(peerOwnerId);
|
|
26674
26566
|
let raw;
|
|
26675
26567
|
try {
|
|
26676
|
-
raw =
|
|
26568
|
+
raw = fs18.readFileSync(file, "utf8");
|
|
26677
26569
|
} catch (err) {
|
|
26678
26570
|
if (err?.code === "ENOENT") return [];
|
|
26679
26571
|
return [];
|
|
@@ -26689,7 +26581,7 @@ var InboxStore = class {
|
|
|
26689
26581
|
const dir = this.dirPath();
|
|
26690
26582
|
let entries;
|
|
26691
26583
|
try {
|
|
26692
|
-
entries =
|
|
26584
|
+
entries = fs18.readdirSync(dir);
|
|
26693
26585
|
} catch (err) {
|
|
26694
26586
|
if (err?.code === "ENOENT") return [];
|
|
26695
26587
|
return [];
|
|
@@ -26705,9 +26597,9 @@ var InboxStore = class {
|
|
|
26705
26597
|
if (existing.some((m2) => m2.id === message.id)) return;
|
|
26706
26598
|
const file = this.filePath(message.peerOwnerId);
|
|
26707
26599
|
const line = JSON.stringify(message) + "\n";
|
|
26708
|
-
|
|
26600
|
+
fs18.appendFileSync(file, line, { mode: 384 });
|
|
26709
26601
|
try {
|
|
26710
|
-
|
|
26602
|
+
fs18.chmodSync(file, 384);
|
|
26711
26603
|
} catch {
|
|
26712
26604
|
}
|
|
26713
26605
|
}
|
|
@@ -26737,7 +26629,7 @@ var InboxStore = class {
|
|
|
26737
26629
|
removeByPeerOwnerId(peerOwnerId) {
|
|
26738
26630
|
const file = this.filePath(peerOwnerId);
|
|
26739
26631
|
try {
|
|
26740
|
-
|
|
26632
|
+
fs18.unlinkSync(file);
|
|
26741
26633
|
} catch (err) {
|
|
26742
26634
|
if (err?.code === "ENOENT") return;
|
|
26743
26635
|
}
|
|
@@ -26746,18 +26638,18 @@ var InboxStore = class {
|
|
|
26746
26638
|
const file = this.filePath(peerOwnerId);
|
|
26747
26639
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
26748
26640
|
const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
|
|
26749
|
-
|
|
26750
|
-
|
|
26641
|
+
fs18.writeFileSync(tmp, content, { mode: 384 });
|
|
26642
|
+
fs18.renameSync(tmp, file);
|
|
26751
26643
|
try {
|
|
26752
|
-
|
|
26644
|
+
fs18.chmodSync(file, 384);
|
|
26753
26645
|
} catch {
|
|
26754
26646
|
}
|
|
26755
26647
|
}
|
|
26756
26648
|
dirPath() {
|
|
26757
|
-
return
|
|
26649
|
+
return path20.join(this.dataDir, INBOX_SUBDIR);
|
|
26758
26650
|
}
|
|
26759
26651
|
filePath(peerOwnerId) {
|
|
26760
|
-
return
|
|
26652
|
+
return path20.join(this.dirPath(), `${peerOwnerId}.jsonl`);
|
|
26761
26653
|
}
|
|
26762
26654
|
};
|
|
26763
26655
|
function parseAllLines(raw) {
|
|
@@ -26844,8 +26736,8 @@ var InboxManager = class {
|
|
|
26844
26736
|
};
|
|
26845
26737
|
|
|
26846
26738
|
// src/state/received-capability-store.ts
|
|
26847
|
-
var
|
|
26848
|
-
var
|
|
26739
|
+
var fs19 = __toESM(require("fs"), 1);
|
|
26740
|
+
var path21 = __toESM(require("path"), 1);
|
|
26849
26741
|
var FILE_NAME = "received-capabilities.json";
|
|
26850
26742
|
var ReceivedCapabilityStore = class {
|
|
26851
26743
|
constructor(dataDir) {
|
|
@@ -26855,10 +26747,10 @@ var ReceivedCapabilityStore = class {
|
|
|
26855
26747
|
caps = /* @__PURE__ */ new Map();
|
|
26856
26748
|
load() {
|
|
26857
26749
|
this.caps.clear();
|
|
26858
|
-
const file =
|
|
26750
|
+
const file = path21.join(this.dataDir, FILE_NAME);
|
|
26859
26751
|
let raw;
|
|
26860
26752
|
try {
|
|
26861
|
-
raw =
|
|
26753
|
+
raw = fs19.readFileSync(file, "utf8");
|
|
26862
26754
|
} catch (err) {
|
|
26863
26755
|
if (err?.code !== "ENOENT") this.renameBak(file);
|
|
26864
26756
|
return;
|
|
@@ -26895,20 +26787,20 @@ var ReceivedCapabilityStore = class {
|
|
|
26895
26787
|
this.flush();
|
|
26896
26788
|
}
|
|
26897
26789
|
flush() {
|
|
26898
|
-
const file =
|
|
26790
|
+
const file = path21.join(this.dataDir, FILE_NAME);
|
|
26899
26791
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
26900
26792
|
const content = JSON.stringify(
|
|
26901
26793
|
{ receivedCaps: Array.from(this.caps.values()) },
|
|
26902
26794
|
null,
|
|
26903
26795
|
2
|
|
26904
26796
|
);
|
|
26905
|
-
|
|
26906
|
-
|
|
26907
|
-
|
|
26797
|
+
fs19.mkdirSync(this.dataDir, { recursive: true });
|
|
26798
|
+
fs19.writeFileSync(tmp, content, { mode: 384 });
|
|
26799
|
+
fs19.renameSync(tmp, file);
|
|
26908
26800
|
}
|
|
26909
26801
|
renameBak(file) {
|
|
26910
26802
|
try {
|
|
26911
|
-
|
|
26803
|
+
fs19.renameSync(file, `${file}.bak`);
|
|
26912
26804
|
} catch {
|
|
26913
26805
|
}
|
|
26914
26806
|
}
|
|
@@ -27006,61 +26898,61 @@ async function connectRemote(args) {
|
|
|
27006
26898
|
}
|
|
27007
26899
|
|
|
27008
26900
|
// src/migrations/2026-05-20-flatten-sessions.ts
|
|
27009
|
-
var
|
|
27010
|
-
var
|
|
26901
|
+
var fs20 = __toESM(require("fs"), 1);
|
|
26902
|
+
var path22 = __toESM(require("path"), 1);
|
|
27011
26903
|
var MIGRATION_FLAG_NAME = ".migration.v1.done";
|
|
27012
26904
|
function migrateFlattenSessions(opts) {
|
|
27013
26905
|
const dataDir = opts.dataDir;
|
|
27014
26906
|
const now = opts.now ?? Date.now;
|
|
27015
|
-
const sessionsDir =
|
|
27016
|
-
const flagPath =
|
|
26907
|
+
const sessionsDir = path22.join(dataDir, "sessions");
|
|
26908
|
+
const flagPath = path22.join(sessionsDir, MIGRATION_FLAG_NAME);
|
|
27017
26909
|
if (existsSync3(flagPath)) {
|
|
27018
26910
|
return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
|
|
27019
26911
|
}
|
|
27020
26912
|
let movedBare = 0;
|
|
27021
26913
|
let movedVmOwner = 0;
|
|
27022
26914
|
let archivedListener = 0;
|
|
27023
|
-
const defaultDir =
|
|
26915
|
+
const defaultDir = path22.join(sessionsDir, "default");
|
|
27024
26916
|
if (existsSync3(defaultDir)) {
|
|
27025
26917
|
for (const entry of readdirSafe(defaultDir)) {
|
|
27026
26918
|
if (!entry.endsWith(".json")) continue;
|
|
27027
|
-
const src =
|
|
27028
|
-
const dst =
|
|
27029
|
-
|
|
26919
|
+
const src = path22.join(defaultDir, entry);
|
|
26920
|
+
const dst = path22.join(sessionsDir, entry);
|
|
26921
|
+
fs20.renameSync(src, dst);
|
|
27030
26922
|
movedBare += 1;
|
|
27031
26923
|
}
|
|
27032
26924
|
rmdirIfEmpty(defaultDir);
|
|
27033
26925
|
}
|
|
27034
26926
|
for (const pid of readdirSafe(sessionsDir)) {
|
|
27035
|
-
const personaDir =
|
|
26927
|
+
const personaDir = path22.join(sessionsDir, pid);
|
|
27036
26928
|
if (!isDir(personaDir)) continue;
|
|
27037
26929
|
if (pid === "default") continue;
|
|
27038
|
-
const ownerSrc =
|
|
26930
|
+
const ownerSrc = path22.join(personaDir, "owner");
|
|
27039
26931
|
if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
|
|
27040
|
-
const ownerDst =
|
|
27041
|
-
|
|
26932
|
+
const ownerDst = path22.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
|
|
26933
|
+
fs20.mkdirSync(ownerDst, { recursive: true });
|
|
27042
26934
|
for (const file of readdirSafe(ownerSrc)) {
|
|
27043
26935
|
if (!file.endsWith(".json")) continue;
|
|
27044
|
-
|
|
26936
|
+
fs20.renameSync(path22.join(ownerSrc, file), path22.join(ownerDst, file));
|
|
27045
26937
|
movedVmOwner += 1;
|
|
27046
26938
|
}
|
|
27047
26939
|
rmdirIfEmpty(ownerSrc);
|
|
27048
26940
|
}
|
|
27049
|
-
const listenerSrc =
|
|
26941
|
+
const listenerSrc = path22.join(personaDir, "listener");
|
|
27050
26942
|
if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
|
|
27051
|
-
const archiveDst =
|
|
27052
|
-
|
|
26943
|
+
const archiveDst = path22.join(dataDir, ".legacy", `listener-${pid}`);
|
|
26944
|
+
fs20.mkdirSync(archiveDst, { recursive: true });
|
|
27053
26945
|
for (const file of readdirSafe(listenerSrc)) {
|
|
27054
26946
|
if (!file.endsWith(".json")) continue;
|
|
27055
|
-
|
|
26947
|
+
fs20.renameSync(path22.join(listenerSrc, file), path22.join(archiveDst, file));
|
|
27056
26948
|
archivedListener += 1;
|
|
27057
26949
|
}
|
|
27058
26950
|
rmdirIfEmpty(listenerSrc);
|
|
27059
26951
|
}
|
|
27060
26952
|
rmdirIfEmpty(personaDir);
|
|
27061
26953
|
}
|
|
27062
|
-
|
|
27063
|
-
|
|
26954
|
+
fs20.mkdirSync(sessionsDir, { recursive: true });
|
|
26955
|
+
fs20.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
|
|
27064
26956
|
return {
|
|
27065
26957
|
skipped: false,
|
|
27066
26958
|
flagWritten: true,
|
|
@@ -27071,7 +26963,7 @@ function migrateFlattenSessions(opts) {
|
|
|
27071
26963
|
}
|
|
27072
26964
|
function existsSync3(p2) {
|
|
27073
26965
|
try {
|
|
27074
|
-
|
|
26966
|
+
fs20.statSync(p2);
|
|
27075
26967
|
return true;
|
|
27076
26968
|
} catch {
|
|
27077
26969
|
return false;
|
|
@@ -27079,21 +26971,21 @@ function existsSync3(p2) {
|
|
|
27079
26971
|
}
|
|
27080
26972
|
function isDir(p2) {
|
|
27081
26973
|
try {
|
|
27082
|
-
return
|
|
26974
|
+
return fs20.statSync(p2).isDirectory();
|
|
27083
26975
|
} catch {
|
|
27084
26976
|
return false;
|
|
27085
26977
|
}
|
|
27086
26978
|
}
|
|
27087
26979
|
function readdirSafe(p2) {
|
|
27088
26980
|
try {
|
|
27089
|
-
return
|
|
26981
|
+
return fs20.readdirSync(p2);
|
|
27090
26982
|
} catch {
|
|
27091
26983
|
return [];
|
|
27092
26984
|
}
|
|
27093
26985
|
}
|
|
27094
26986
|
function rmdirIfEmpty(p2) {
|
|
27095
26987
|
try {
|
|
27096
|
-
|
|
26988
|
+
fs20.rmdirSync(p2);
|
|
27097
26989
|
} catch {
|
|
27098
26990
|
}
|
|
27099
26991
|
}
|
|
@@ -27103,7 +26995,7 @@ var import_node_fs15 = __toESM(require("fs"), 1);
|
|
|
27103
26995
|
var import_node_path17 = __toESM(require("path"), 1);
|
|
27104
26996
|
|
|
27105
26997
|
// src/attachment/mime.ts
|
|
27106
|
-
var
|
|
26998
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
27107
26999
|
var TEXT_PLAIN = "text/plain; charset=utf-8";
|
|
27108
27000
|
var EXT_TO_NATIVE_MIME = {
|
|
27109
27001
|
// 图片
|
|
@@ -27210,7 +27102,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
27210
27102
|
".mk"
|
|
27211
27103
|
]);
|
|
27212
27104
|
function lookupMime(filePathOrName) {
|
|
27213
|
-
const ext =
|
|
27105
|
+
const ext = import_node_path15.default.extname(filePathOrName).toLowerCase();
|
|
27214
27106
|
if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
|
|
27215
27107
|
if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
|
|
27216
27108
|
return "application/octet-stream";
|
|
@@ -27278,7 +27170,131 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
|
|
|
27278
27170
|
return { ok: true, absPath };
|
|
27279
27171
|
}
|
|
27280
27172
|
|
|
27173
|
+
// src/attachment/upload.ts
|
|
27174
|
+
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
27175
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
27176
|
+
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
27177
|
+
var import_promises = require("stream/promises");
|
|
27178
|
+
var UploadError = class extends Error {
|
|
27179
|
+
constructor(code, message) {
|
|
27180
|
+
super(message);
|
|
27181
|
+
this.code = code;
|
|
27182
|
+
this.name = "UploadError";
|
|
27183
|
+
}
|
|
27184
|
+
code;
|
|
27185
|
+
};
|
|
27186
|
+
function assertValidFileName(fileName) {
|
|
27187
|
+
if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path16.default.basename(fileName)) {
|
|
27188
|
+
throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
|
|
27189
|
+
}
|
|
27190
|
+
}
|
|
27191
|
+
var HASH_PREFIX_LEN = 16;
|
|
27192
|
+
async function writeUploadedAttachment(args) {
|
|
27193
|
+
assertValidFileName(args.fileName);
|
|
27194
|
+
const attachmentsRoot = import_node_path16.default.join(args.sessionDir, ".attachments");
|
|
27195
|
+
try {
|
|
27196
|
+
import_node_fs14.default.mkdirSync(attachmentsRoot, { recursive: true });
|
|
27197
|
+
} catch (err) {
|
|
27198
|
+
throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
|
|
27199
|
+
}
|
|
27200
|
+
const hasher = import_node_crypto6.default.createHash("sha256");
|
|
27201
|
+
let actualSize = 0;
|
|
27202
|
+
const tmpPath = import_node_path16.default.join(
|
|
27203
|
+
attachmentsRoot,
|
|
27204
|
+
`.upload-${process.pid}-${Date.now()}-${import_node_crypto6.default.randomBytes(4).toString("hex")}`
|
|
27205
|
+
);
|
|
27206
|
+
try {
|
|
27207
|
+
await (0, import_promises.pipeline)(
|
|
27208
|
+
args.body,
|
|
27209
|
+
async function* (source) {
|
|
27210
|
+
for await (const chunk of source) {
|
|
27211
|
+
const buf = chunk;
|
|
27212
|
+
actualSize += buf.length;
|
|
27213
|
+
hasher.update(buf);
|
|
27214
|
+
yield buf;
|
|
27215
|
+
}
|
|
27216
|
+
},
|
|
27217
|
+
import_node_fs14.default.createWriteStream(tmpPath, { mode: 384 })
|
|
27218
|
+
);
|
|
27219
|
+
} catch (err) {
|
|
27220
|
+
try {
|
|
27221
|
+
import_node_fs14.default.unlinkSync(tmpPath);
|
|
27222
|
+
} catch {
|
|
27223
|
+
}
|
|
27224
|
+
throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
|
|
27225
|
+
}
|
|
27226
|
+
if (actualSize !== args.contentLength) {
|
|
27227
|
+
try {
|
|
27228
|
+
import_node_fs14.default.unlinkSync(tmpPath);
|
|
27229
|
+
} catch {
|
|
27230
|
+
}
|
|
27231
|
+
throw new UploadError(
|
|
27232
|
+
"SIZE_MISMATCH",
|
|
27233
|
+
`expected ${args.contentLength} bytes, got ${actualSize}`
|
|
27234
|
+
);
|
|
27235
|
+
}
|
|
27236
|
+
const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
|
|
27237
|
+
const hashDir = import_node_path16.default.join(attachmentsRoot, attachmentId);
|
|
27238
|
+
let finalFileName;
|
|
27239
|
+
let hashDirExists = false;
|
|
27240
|
+
try {
|
|
27241
|
+
hashDirExists = import_node_fs14.default.statSync(hashDir).isDirectory();
|
|
27242
|
+
} catch {
|
|
27243
|
+
}
|
|
27244
|
+
if (hashDirExists) {
|
|
27245
|
+
const existing = import_node_fs14.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
|
|
27246
|
+
finalFileName = existing[0] ?? args.fileName;
|
|
27247
|
+
try {
|
|
27248
|
+
import_node_fs14.default.unlinkSync(tmpPath);
|
|
27249
|
+
} catch {
|
|
27250
|
+
}
|
|
27251
|
+
} else {
|
|
27252
|
+
try {
|
|
27253
|
+
import_node_fs14.default.mkdirSync(hashDir, { recursive: true });
|
|
27254
|
+
finalFileName = args.fileName;
|
|
27255
|
+
import_node_fs14.default.renameSync(tmpPath, import_node_path16.default.join(hashDir, finalFileName));
|
|
27256
|
+
} catch (err) {
|
|
27257
|
+
try {
|
|
27258
|
+
import_node_fs14.default.unlinkSync(tmpPath);
|
|
27259
|
+
} catch {
|
|
27260
|
+
}
|
|
27261
|
+
throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
|
|
27262
|
+
}
|
|
27263
|
+
}
|
|
27264
|
+
const absPath = import_node_path16.default.join(hashDir, finalFileName);
|
|
27265
|
+
const relPath = import_node_path16.default.relative(args.sessionDir, absPath);
|
|
27266
|
+
args.groupFileStore.upsert(args.scope, args.sessionId, {
|
|
27267
|
+
relPath: absPath,
|
|
27268
|
+
// 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
|
|
27269
|
+
from: "user-upload",
|
|
27270
|
+
label: args.fileName,
|
|
27271
|
+
size: actualSize,
|
|
27272
|
+
mime: args.mimeType
|
|
27273
|
+
});
|
|
27274
|
+
return { attachmentId, absPath, relPath, sizeBytes: actualSize, mimeType: args.mimeType };
|
|
27275
|
+
}
|
|
27276
|
+
|
|
27281
27277
|
// src/transport/http-router.ts
|
|
27278
|
+
var UPLOAD_MIME_ALLOW = /* @__PURE__ */ new Set([
|
|
27279
|
+
"image/png",
|
|
27280
|
+
"image/jpeg",
|
|
27281
|
+
"image/gif",
|
|
27282
|
+
"image/webp",
|
|
27283
|
+
"application/pdf"
|
|
27284
|
+
]);
|
|
27285
|
+
var UPLOAD_MIME_ALLOW_PREFIX = ["text/"];
|
|
27286
|
+
var UPLOAD_MAX_BYTES = 25 * 1024 * 1024;
|
|
27287
|
+
function isUploadMimeAllowed(mime) {
|
|
27288
|
+
if (UPLOAD_MIME_ALLOW.has(mime)) return true;
|
|
27289
|
+
return UPLOAD_MIME_ALLOW_PREFIX.some((p2) => mime.startsWith(p2));
|
|
27290
|
+
}
|
|
27291
|
+
function isValidUploadFileName(fileName) {
|
|
27292
|
+
if (fileName.length === 0) return false;
|
|
27293
|
+
if (fileName === "." || fileName === "..") return false;
|
|
27294
|
+
if (fileName.startsWith(".")) return false;
|
|
27295
|
+
if (fileName.includes("/") || fileName.includes("\\")) return false;
|
|
27296
|
+
return fileName === import_node_path17.default.basename(fileName);
|
|
27297
|
+
}
|
|
27282
27298
|
function createHttpRouter(deps) {
|
|
27283
27299
|
return async (req, res) => {
|
|
27284
27300
|
const url = parseUrl(req.url);
|
|
@@ -27292,7 +27308,7 @@ function createHttpRouter(deps) {
|
|
|
27292
27308
|
});
|
|
27293
27309
|
return true;
|
|
27294
27310
|
}
|
|
27295
|
-
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/")) {
|
|
27311
|
+
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/") && url.pathname !== "/attachments/upload") {
|
|
27296
27312
|
return false;
|
|
27297
27313
|
}
|
|
27298
27314
|
if (url.pathname.startsWith("/files/") && req.method === "GET") {
|
|
@@ -27332,6 +27348,106 @@ function createHttpRouter(deps) {
|
|
|
27332
27348
|
sendJson(res, 401, { code: "UNAUTHORIZED", message: "missing or invalid bearer token" });
|
|
27333
27349
|
return true;
|
|
27334
27350
|
}
|
|
27351
|
+
if (url.pathname === "/attachments/upload" && req.method === "POST") {
|
|
27352
|
+
if (ctx.role !== "owner") {
|
|
27353
|
+
sendJson(res, 403, { code: "FORBIDDEN", message: "owner only" });
|
|
27354
|
+
return true;
|
|
27355
|
+
}
|
|
27356
|
+
const up = deps.attachmentUpload;
|
|
27357
|
+
if (!up) {
|
|
27358
|
+
sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "attachment upload not wired" });
|
|
27359
|
+
return true;
|
|
27360
|
+
}
|
|
27361
|
+
const sessionId = url.searchParams.get("sessionId");
|
|
27362
|
+
const fileName = url.searchParams.get("fileName");
|
|
27363
|
+
const mimeType = url.searchParams.get("mimeType");
|
|
27364
|
+
const contentLengthRaw = req.headers["content-length"];
|
|
27365
|
+
if (!sessionId || !fileName || !mimeType || !contentLengthRaw) {
|
|
27366
|
+
sendJson(res, 400, {
|
|
27367
|
+
code: "INVALID_PARAM",
|
|
27368
|
+
message: "missing sessionId/fileName/mimeType/Content-Length"
|
|
27369
|
+
});
|
|
27370
|
+
return true;
|
|
27371
|
+
}
|
|
27372
|
+
if (!isValidUploadFileName(fileName)) {
|
|
27373
|
+
sendJson(res, 400, {
|
|
27374
|
+
code: "INVALID_FILENAME",
|
|
27375
|
+
message: `fileName must be a plain basename (no path separators, no dotfiles): ${fileName}`
|
|
27376
|
+
});
|
|
27377
|
+
return true;
|
|
27378
|
+
}
|
|
27379
|
+
const contentLength = Number.parseInt(contentLengthRaw, 10);
|
|
27380
|
+
if (!Number.isFinite(contentLength) || contentLength < 0) {
|
|
27381
|
+
sendJson(res, 400, { code: "INVALID_PARAM", message: "malformed Content-Length" });
|
|
27382
|
+
return true;
|
|
27383
|
+
}
|
|
27384
|
+
if (contentLength > UPLOAD_MAX_BYTES) {
|
|
27385
|
+
sendJson(res, 413, {
|
|
27386
|
+
code: "FILE_TOO_LARGE",
|
|
27387
|
+
message: `max ${UPLOAD_MAX_BYTES} bytes`
|
|
27388
|
+
});
|
|
27389
|
+
return true;
|
|
27390
|
+
}
|
|
27391
|
+
if (!isUploadMimeAllowed(mimeType)) {
|
|
27392
|
+
sendJson(res, 400, {
|
|
27393
|
+
code: "UNSUPPORTED_MIME",
|
|
27394
|
+
message: `mime ${mimeType} not allowed`
|
|
27395
|
+
});
|
|
27396
|
+
return true;
|
|
27397
|
+
}
|
|
27398
|
+
const sessionDir = up.getSessionDir(sessionId);
|
|
27399
|
+
const scope = up.getSessionScope(sessionId);
|
|
27400
|
+
if (!sessionDir || !scope) {
|
|
27401
|
+
sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sessionId} not found` });
|
|
27402
|
+
return true;
|
|
27403
|
+
}
|
|
27404
|
+
const httpBaseUrl = up.getHttpBaseUrl();
|
|
27405
|
+
if (!httpBaseUrl) {
|
|
27406
|
+
sendJson(res, 503, {
|
|
27407
|
+
code: "TUNNEL_NOT_READY",
|
|
27408
|
+
message: "httpBaseUrl unavailable"
|
|
27409
|
+
});
|
|
27410
|
+
return true;
|
|
27411
|
+
}
|
|
27412
|
+
const secret = deps.getSignSecret?.();
|
|
27413
|
+
if (!secret) {
|
|
27414
|
+
sendJson(res, 501, {
|
|
27415
|
+
code: "NOT_IMPLEMENTED",
|
|
27416
|
+
message: "sign secret unavailable (noAuth?)"
|
|
27417
|
+
});
|
|
27418
|
+
return true;
|
|
27419
|
+
}
|
|
27420
|
+
try {
|
|
27421
|
+
const result = await writeUploadedAttachment({
|
|
27422
|
+
sessionDir,
|
|
27423
|
+
sessionId,
|
|
27424
|
+
scope,
|
|
27425
|
+
fileName,
|
|
27426
|
+
mimeType,
|
|
27427
|
+
contentLength,
|
|
27428
|
+
body: req,
|
|
27429
|
+
groupFileStore: up.groupFileStore
|
|
27430
|
+
});
|
|
27431
|
+
const parts = signUrlParts(secret, result.absPath, null);
|
|
27432
|
+
const fullUrl = buildSignedFileUrl(httpBaseUrl, parts);
|
|
27433
|
+
sendJson(res, 200, {
|
|
27434
|
+
attachmentId: result.attachmentId,
|
|
27435
|
+
url: fullUrl,
|
|
27436
|
+
relPath: result.relPath,
|
|
27437
|
+
sizeBytes: result.sizeBytes,
|
|
27438
|
+
mimeType: result.mimeType
|
|
27439
|
+
});
|
|
27440
|
+
} catch (err) {
|
|
27441
|
+
if (err instanceof UploadError) {
|
|
27442
|
+
const status = err.code === "SIZE_MISMATCH" ? 400 : 500;
|
|
27443
|
+
sendJson(res, status, { code: err.code, message: err.message });
|
|
27444
|
+
} else {
|
|
27445
|
+
deps.logger?.warn("attachment upload failed", { err: err.message });
|
|
27446
|
+
sendJson(res, 500, { code: "STORAGE_ERROR", message: err.message });
|
|
27447
|
+
}
|
|
27448
|
+
}
|
|
27449
|
+
return true;
|
|
27450
|
+
}
|
|
27335
27451
|
const sessionFilesMatch = url.pathname.match(/^\/session\/([^/]+)\/files$/);
|
|
27336
27452
|
if (sessionFilesMatch && req.method === "GET") {
|
|
27337
27453
|
const sid = sessionFilesMatch[1];
|
|
@@ -27413,10 +27529,78 @@ function streamFile(res, absPath, logger) {
|
|
|
27413
27529
|
stream.pipe(res);
|
|
27414
27530
|
}
|
|
27415
27531
|
|
|
27416
|
-
// src/attachment/
|
|
27532
|
+
// src/attachment/gc.ts
|
|
27417
27533
|
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
27418
27534
|
var import_node_path18 = __toESM(require("path"), 1);
|
|
27419
|
-
var
|
|
27535
|
+
var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
|
|
27536
|
+
function runAttachmentGc(args) {
|
|
27537
|
+
const now = (args.now ?? Date.now)();
|
|
27538
|
+
const ttlMs = args.ttlMs ?? DEFAULT_TTL_MS;
|
|
27539
|
+
const liveAbs = /* @__PURE__ */ new Set();
|
|
27540
|
+
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27541
|
+
for (const entry of args.groupFileStore.list(scope, sessionId)) {
|
|
27542
|
+
if (entry.stale) continue;
|
|
27543
|
+
if (import_node_path18.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
|
|
27544
|
+
}
|
|
27545
|
+
}
|
|
27546
|
+
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27547
|
+
const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path18.default.join(
|
|
27548
|
+
args.dataDir,
|
|
27549
|
+
"sessions",
|
|
27550
|
+
...scopeSubPath(scope).map(safeFileName),
|
|
27551
|
+
safeFileName(sessionId)
|
|
27552
|
+
);
|
|
27553
|
+
const attRoot = import_node_path18.default.join(sessionDir, ".attachments");
|
|
27554
|
+
let hashDirs;
|
|
27555
|
+
try {
|
|
27556
|
+
hashDirs = import_node_fs16.default.readdirSync(attRoot);
|
|
27557
|
+
} catch (err) {
|
|
27558
|
+
if (err.code === "ENOENT") continue;
|
|
27559
|
+
args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
|
|
27560
|
+
continue;
|
|
27561
|
+
}
|
|
27562
|
+
for (const hashDir of hashDirs) {
|
|
27563
|
+
const hashDirAbs = import_node_path18.default.join(attRoot, hashDir);
|
|
27564
|
+
let files;
|
|
27565
|
+
try {
|
|
27566
|
+
files = import_node_fs16.default.readdirSync(hashDirAbs);
|
|
27567
|
+
} catch {
|
|
27568
|
+
continue;
|
|
27569
|
+
}
|
|
27570
|
+
for (const name of files) {
|
|
27571
|
+
const file = import_node_path18.default.join(hashDirAbs, name);
|
|
27572
|
+
let stat;
|
|
27573
|
+
try {
|
|
27574
|
+
stat = import_node_fs16.default.statSync(file);
|
|
27575
|
+
} catch {
|
|
27576
|
+
continue;
|
|
27577
|
+
}
|
|
27578
|
+
if (!stat.isFile()) continue;
|
|
27579
|
+
const age = now - stat.mtimeMs;
|
|
27580
|
+
if (age < ttlMs) continue;
|
|
27581
|
+
if (liveAbs.has(file)) continue;
|
|
27582
|
+
try {
|
|
27583
|
+
import_node_fs16.default.unlinkSync(file);
|
|
27584
|
+
} catch (err) {
|
|
27585
|
+
args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
|
|
27586
|
+
}
|
|
27587
|
+
}
|
|
27588
|
+
try {
|
|
27589
|
+
if (import_node_fs16.default.readdirSync(hashDirAbs).length === 0) import_node_fs16.default.rmdirSync(hashDirAbs);
|
|
27590
|
+
} catch {
|
|
27591
|
+
}
|
|
27592
|
+
}
|
|
27593
|
+
try {
|
|
27594
|
+
if (import_node_fs16.default.readdirSync(attRoot).length === 0) import_node_fs16.default.rmdirSync(attRoot);
|
|
27595
|
+
} catch {
|
|
27596
|
+
}
|
|
27597
|
+
}
|
|
27598
|
+
}
|
|
27599
|
+
|
|
27600
|
+
// src/attachment/group.ts
|
|
27601
|
+
var import_node_fs17 = __toESM(require("fs"), 1);
|
|
27602
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
27603
|
+
var import_node_crypto7 = __toESM(require("crypto"), 1);
|
|
27420
27604
|
init_protocol();
|
|
27421
27605
|
var GroupFileStore = class {
|
|
27422
27606
|
dataDir;
|
|
@@ -27427,11 +27611,11 @@ var GroupFileStore = class {
|
|
|
27427
27611
|
this.logger = opts.logger;
|
|
27428
27612
|
}
|
|
27429
27613
|
rootForScope(scope) {
|
|
27430
|
-
return
|
|
27614
|
+
return import_node_path19.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
|
|
27431
27615
|
}
|
|
27432
27616
|
/** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
|
|
27433
27617
|
filePath(scope, sessionId) {
|
|
27434
|
-
return
|
|
27618
|
+
return import_node_path19.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
|
|
27435
27619
|
}
|
|
27436
27620
|
cacheKey(scope, sessionId) {
|
|
27437
27621
|
return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
|
|
@@ -27440,7 +27624,7 @@ var GroupFileStore = class {
|
|
|
27440
27624
|
readFile(scope, sessionId) {
|
|
27441
27625
|
const file = this.filePath(scope, sessionId);
|
|
27442
27626
|
try {
|
|
27443
|
-
const raw =
|
|
27627
|
+
const raw = import_node_fs17.default.readFileSync(file, "utf8");
|
|
27444
27628
|
const parsed = JSON.parse(raw);
|
|
27445
27629
|
if (!Array.isArray(parsed)) {
|
|
27446
27630
|
this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
|
|
@@ -27466,10 +27650,10 @@ var GroupFileStore = class {
|
|
|
27466
27650
|
}
|
|
27467
27651
|
writeFile(scope, sessionId, entries) {
|
|
27468
27652
|
const file = this.filePath(scope, sessionId);
|
|
27469
|
-
|
|
27653
|
+
import_node_fs17.default.mkdirSync(import_node_path19.default.dirname(file), { recursive: true });
|
|
27470
27654
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
27471
|
-
|
|
27472
|
-
|
|
27655
|
+
import_node_fs17.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
|
|
27656
|
+
import_node_fs17.default.renameSync(tmp, file);
|
|
27473
27657
|
}
|
|
27474
27658
|
/** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
|
|
27475
27659
|
list(scope, sessionId) {
|
|
@@ -27505,7 +27689,7 @@ var GroupFileStore = class {
|
|
|
27505
27689
|
entries[idx] = next;
|
|
27506
27690
|
} else {
|
|
27507
27691
|
next = {
|
|
27508
|
-
id: `gf-${
|
|
27692
|
+
id: `gf-${import_node_crypto7.default.randomBytes(6).toString("base64url")}`,
|
|
27509
27693
|
relPath: input.relPath,
|
|
27510
27694
|
from: input.from,
|
|
27511
27695
|
label: input.label,
|
|
@@ -27555,10 +27739,10 @@ var GroupFileStore = class {
|
|
|
27555
27739
|
};
|
|
27556
27740
|
|
|
27557
27741
|
// src/discovery/state-file.ts
|
|
27558
|
-
var
|
|
27559
|
-
var
|
|
27742
|
+
var import_node_fs18 = __toESM(require("fs"), 1);
|
|
27743
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
27560
27744
|
function defaultStateFilePath(dataDir) {
|
|
27561
|
-
return
|
|
27745
|
+
return import_node_path20.default.join(dataDir, "state.json");
|
|
27562
27746
|
}
|
|
27563
27747
|
function isPidAlive(pid) {
|
|
27564
27748
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
@@ -27580,7 +27764,7 @@ var StateFileManager = class {
|
|
|
27580
27764
|
}
|
|
27581
27765
|
read() {
|
|
27582
27766
|
try {
|
|
27583
|
-
const raw =
|
|
27767
|
+
const raw = import_node_fs18.default.readFileSync(this.file, "utf8");
|
|
27584
27768
|
const parsed = JSON.parse(raw);
|
|
27585
27769
|
return parsed;
|
|
27586
27770
|
} catch {
|
|
@@ -27594,34 +27778,34 @@ var StateFileManager = class {
|
|
|
27594
27778
|
return { status: "stale", existing };
|
|
27595
27779
|
}
|
|
27596
27780
|
write(state) {
|
|
27597
|
-
|
|
27781
|
+
import_node_fs18.default.mkdirSync(import_node_path20.default.dirname(this.file), { recursive: true });
|
|
27598
27782
|
const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
|
|
27599
|
-
|
|
27600
|
-
|
|
27783
|
+
import_node_fs18.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
|
|
27784
|
+
import_node_fs18.default.renameSync(tmp, this.file);
|
|
27601
27785
|
if (process.platform !== "win32") {
|
|
27602
27786
|
try {
|
|
27603
|
-
|
|
27787
|
+
import_node_fs18.default.chmodSync(this.file, 384);
|
|
27604
27788
|
} catch {
|
|
27605
27789
|
}
|
|
27606
27790
|
}
|
|
27607
27791
|
}
|
|
27608
27792
|
delete() {
|
|
27609
27793
|
try {
|
|
27610
|
-
|
|
27794
|
+
import_node_fs18.default.unlinkSync(this.file);
|
|
27611
27795
|
} catch {
|
|
27612
27796
|
}
|
|
27613
27797
|
}
|
|
27614
27798
|
};
|
|
27615
27799
|
|
|
27616
27800
|
// src/tunnel/tunnel-manager.ts
|
|
27617
|
-
var
|
|
27618
|
-
var
|
|
27619
|
-
var
|
|
27801
|
+
var import_node_fs22 = __toESM(require("fs"), 1);
|
|
27802
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
27803
|
+
var import_node_crypto8 = __toESM(require("crypto"), 1);
|
|
27620
27804
|
var import_node_child_process5 = require("child_process");
|
|
27621
27805
|
|
|
27622
27806
|
// src/tunnel/tunnel-store.ts
|
|
27623
|
-
var
|
|
27624
|
-
var
|
|
27807
|
+
var import_node_fs19 = __toESM(require("fs"), 1);
|
|
27808
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
27625
27809
|
var TunnelStore = class {
|
|
27626
27810
|
constructor(filePath) {
|
|
27627
27811
|
this.filePath = filePath;
|
|
@@ -27629,7 +27813,7 @@ var TunnelStore = class {
|
|
|
27629
27813
|
filePath;
|
|
27630
27814
|
async get() {
|
|
27631
27815
|
try {
|
|
27632
|
-
const raw = await
|
|
27816
|
+
const raw = await import_node_fs19.default.promises.readFile(this.filePath, "utf8");
|
|
27633
27817
|
const obj = JSON.parse(raw);
|
|
27634
27818
|
if (!isPersistedTunnel(obj)) return null;
|
|
27635
27819
|
return obj;
|
|
@@ -27640,22 +27824,22 @@ var TunnelStore = class {
|
|
|
27640
27824
|
}
|
|
27641
27825
|
}
|
|
27642
27826
|
async set(v2) {
|
|
27643
|
-
const dir =
|
|
27644
|
-
await
|
|
27827
|
+
const dir = import_node_path21.default.dirname(this.filePath);
|
|
27828
|
+
await import_node_fs19.default.promises.mkdir(dir, { recursive: true });
|
|
27645
27829
|
const data = JSON.stringify(v2, null, 2);
|
|
27646
27830
|
const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
27647
|
-
await
|
|
27831
|
+
await import_node_fs19.default.promises.writeFile(tmp, data, { mode: 384 });
|
|
27648
27832
|
if (process.platform !== "win32") {
|
|
27649
27833
|
try {
|
|
27650
|
-
await
|
|
27834
|
+
await import_node_fs19.default.promises.chmod(tmp, 384);
|
|
27651
27835
|
} catch {
|
|
27652
27836
|
}
|
|
27653
27837
|
}
|
|
27654
|
-
await
|
|
27838
|
+
await import_node_fs19.default.promises.rename(tmp, this.filePath);
|
|
27655
27839
|
}
|
|
27656
27840
|
async clear() {
|
|
27657
27841
|
try {
|
|
27658
|
-
await
|
|
27842
|
+
await import_node_fs19.default.promises.unlink(this.filePath);
|
|
27659
27843
|
} catch (err) {
|
|
27660
27844
|
const code = err?.code;
|
|
27661
27845
|
if (code !== "ENOENT") throw err;
|
|
@@ -27750,12 +27934,12 @@ function escape(v2) {
|
|
|
27750
27934
|
}
|
|
27751
27935
|
|
|
27752
27936
|
// src/tunnel/frpc-binary.ts
|
|
27753
|
-
var
|
|
27754
|
-
var
|
|
27755
|
-
var
|
|
27937
|
+
var import_node_fs20 = __toESM(require("fs"), 1);
|
|
27938
|
+
var import_node_os9 = __toESM(require("os"), 1);
|
|
27939
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
27756
27940
|
var import_node_child_process3 = require("child_process");
|
|
27757
27941
|
var import_node_stream2 = require("stream");
|
|
27758
|
-
var
|
|
27942
|
+
var import_promises2 = require("stream/promises");
|
|
27759
27943
|
var FRPC_VERSION = "0.68.0";
|
|
27760
27944
|
var UnsupportedPlatformError = class extends Error {
|
|
27761
27945
|
constructor(platform, arch) {
|
|
@@ -27784,20 +27968,20 @@ function frpcDownloadUrl(version2, p2) {
|
|
|
27784
27968
|
}
|
|
27785
27969
|
async function ensureFrpcBinary(opts) {
|
|
27786
27970
|
if (opts.override) {
|
|
27787
|
-
if (!
|
|
27971
|
+
if (!import_node_fs20.default.existsSync(opts.override)) {
|
|
27788
27972
|
throw new Error(`frpc binary not found at override path: ${opts.override}`);
|
|
27789
27973
|
}
|
|
27790
27974
|
return opts.override;
|
|
27791
27975
|
}
|
|
27792
27976
|
const version2 = opts.version ?? FRPC_VERSION;
|
|
27793
27977
|
const platform = opts.platform ?? detectPlatform();
|
|
27794
|
-
const binDir =
|
|
27795
|
-
|
|
27978
|
+
const binDir = import_node_path22.default.join(opts.dataDir, "bin");
|
|
27979
|
+
import_node_fs20.default.mkdirSync(binDir, { recursive: true });
|
|
27796
27980
|
cleanupStaleArtifacts(binDir);
|
|
27797
|
-
const stableBin =
|
|
27798
|
-
if (
|
|
27981
|
+
const stableBin = import_node_path22.default.join(binDir, "frpc");
|
|
27982
|
+
if (import_node_fs20.default.existsSync(stableBin)) return stableBin;
|
|
27799
27983
|
const partialBin = `${stableBin}.partial`;
|
|
27800
|
-
const tarballPath =
|
|
27984
|
+
const tarballPath = import_node_path22.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
|
|
27801
27985
|
try {
|
|
27802
27986
|
const url = frpcDownloadUrl(version2, platform);
|
|
27803
27987
|
await downloadToFile(url, tarballPath, opts.fetchImpl);
|
|
@@ -27806,8 +27990,8 @@ async function ensureFrpcBinary(opts) {
|
|
|
27806
27990
|
} else {
|
|
27807
27991
|
await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
|
|
27808
27992
|
}
|
|
27809
|
-
|
|
27810
|
-
|
|
27993
|
+
import_node_fs20.default.chmodSync(partialBin, 493);
|
|
27994
|
+
import_node_fs20.default.renameSync(partialBin, stableBin);
|
|
27811
27995
|
} finally {
|
|
27812
27996
|
safeUnlink(tarballPath);
|
|
27813
27997
|
safeUnlink(partialBin);
|
|
@@ -27817,15 +28001,15 @@ async function ensureFrpcBinary(opts) {
|
|
|
27817
28001
|
function cleanupStaleArtifacts(binDir) {
|
|
27818
28002
|
let entries;
|
|
27819
28003
|
try {
|
|
27820
|
-
entries =
|
|
28004
|
+
entries = import_node_fs20.default.readdirSync(binDir);
|
|
27821
28005
|
} catch {
|
|
27822
28006
|
return;
|
|
27823
28007
|
}
|
|
27824
28008
|
for (const name of entries) {
|
|
27825
28009
|
if (name.endsWith(".partial") || name.startsWith("extract-")) {
|
|
27826
|
-
const full =
|
|
28010
|
+
const full = import_node_path22.default.join(binDir, name);
|
|
27827
28011
|
try {
|
|
27828
|
-
|
|
28012
|
+
import_node_fs20.default.rmSync(full, { recursive: true, force: true });
|
|
27829
28013
|
} catch {
|
|
27830
28014
|
}
|
|
27831
28015
|
}
|
|
@@ -27833,7 +28017,7 @@ function cleanupStaleArtifacts(binDir) {
|
|
|
27833
28017
|
}
|
|
27834
28018
|
function safeUnlink(p2) {
|
|
27835
28019
|
try {
|
|
27836
|
-
|
|
28020
|
+
import_node_fs20.default.unlinkSync(p2);
|
|
27837
28021
|
} catch {
|
|
27838
28022
|
}
|
|
27839
28023
|
}
|
|
@@ -27844,13 +28028,13 @@ async function downloadToFile(url, dest, fetchImpl) {
|
|
|
27844
28028
|
if (!res.ok || !res.body) {
|
|
27845
28029
|
throw new Error(`download failed: ${res.status} ${res.statusText}`);
|
|
27846
28030
|
}
|
|
27847
|
-
const out =
|
|
28031
|
+
const out = import_node_fs20.default.createWriteStream(dest);
|
|
27848
28032
|
const nodeStream = import_node_stream2.Readable.fromWeb(res.body);
|
|
27849
|
-
await (0,
|
|
28033
|
+
await (0, import_promises2.pipeline)(nodeStream, out);
|
|
27850
28034
|
}
|
|
27851
28035
|
async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
|
|
27852
|
-
const work =
|
|
27853
|
-
|
|
28036
|
+
const work = import_node_path22.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
|
|
28037
|
+
import_node_fs20.default.mkdirSync(work, { recursive: true });
|
|
27854
28038
|
try {
|
|
27855
28039
|
await new Promise((resolve6, reject) => {
|
|
27856
28040
|
const proc = (0, import_node_child_process3.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
|
|
@@ -27858,32 +28042,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
|
|
|
27858
28042
|
proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
|
|
27859
28043
|
});
|
|
27860
28044
|
const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
|
|
27861
|
-
const src =
|
|
27862
|
-
if (!
|
|
28045
|
+
const src = import_node_path22.default.join(work, dirName, "frpc");
|
|
28046
|
+
if (!import_node_fs20.default.existsSync(src)) {
|
|
27863
28047
|
throw new Error(`frpc not found inside tarball at ${src}`);
|
|
27864
28048
|
}
|
|
27865
|
-
|
|
28049
|
+
import_node_fs20.default.copyFileSync(src, destBin);
|
|
27866
28050
|
} finally {
|
|
27867
|
-
|
|
28051
|
+
import_node_fs20.default.rmSync(work, { recursive: true, force: true });
|
|
27868
28052
|
}
|
|
27869
28053
|
}
|
|
27870
28054
|
|
|
27871
28055
|
// src/tunnel/frpc-process.ts
|
|
27872
|
-
var
|
|
27873
|
-
var
|
|
28056
|
+
var import_node_fs21 = __toESM(require("fs"), 1);
|
|
28057
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
27874
28058
|
var import_node_child_process4 = require("child_process");
|
|
27875
28059
|
function frpcPidFilePath(dataDir) {
|
|
27876
|
-
return
|
|
28060
|
+
return import_node_path23.default.join(dataDir, "frpc.pid");
|
|
27877
28061
|
}
|
|
27878
28062
|
function writeFrpcPid(dataDir, pid) {
|
|
27879
28063
|
try {
|
|
27880
|
-
|
|
28064
|
+
import_node_fs21.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
|
|
27881
28065
|
} catch {
|
|
27882
28066
|
}
|
|
27883
28067
|
}
|
|
27884
28068
|
function clearFrpcPid(dataDir) {
|
|
27885
28069
|
try {
|
|
27886
|
-
|
|
28070
|
+
import_node_fs21.default.unlinkSync(frpcPidFilePath(dataDir));
|
|
27887
28071
|
} catch {
|
|
27888
28072
|
}
|
|
27889
28073
|
}
|
|
@@ -27899,7 +28083,7 @@ function defaultIsPidAlive(pid) {
|
|
|
27899
28083
|
}
|
|
27900
28084
|
function defaultReadPidFile(file) {
|
|
27901
28085
|
try {
|
|
27902
|
-
return
|
|
28086
|
+
return import_node_fs21.default.readFileSync(file, "utf8");
|
|
27903
28087
|
} catch {
|
|
27904
28088
|
return null;
|
|
27905
28089
|
}
|
|
@@ -27915,7 +28099,7 @@ function defaultSleep(ms) {
|
|
|
27915
28099
|
}
|
|
27916
28100
|
async function killStaleFrpc(deps) {
|
|
27917
28101
|
const pidFile = frpcPidFilePath(deps.dataDir);
|
|
27918
|
-
const tomlPath =
|
|
28102
|
+
const tomlPath = import_node_path23.default.join(deps.dataDir, "frpc.toml");
|
|
27919
28103
|
const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
|
|
27920
28104
|
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
|
|
27921
28105
|
const killPid = deps.killPidImpl ?? defaultKillPid;
|
|
@@ -27939,7 +28123,7 @@ async function killStaleFrpc(deps) {
|
|
|
27939
28123
|
}
|
|
27940
28124
|
if (victims.size === 0) {
|
|
27941
28125
|
try {
|
|
27942
|
-
|
|
28126
|
+
import_node_fs21.default.unlinkSync(pidFile);
|
|
27943
28127
|
} catch {
|
|
27944
28128
|
}
|
|
27945
28129
|
return;
|
|
@@ -27950,7 +28134,7 @@ async function killStaleFrpc(deps) {
|
|
|
27950
28134
|
}
|
|
27951
28135
|
await sleep(deps.reapWaitMs ?? 300);
|
|
27952
28136
|
try {
|
|
27953
|
-
|
|
28137
|
+
import_node_fs21.default.unlinkSync(pidFile);
|
|
27954
28138
|
} catch {
|
|
27955
28139
|
}
|
|
27956
28140
|
}
|
|
@@ -27987,7 +28171,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
27987
28171
|
var TunnelManager = class {
|
|
27988
28172
|
constructor(deps) {
|
|
27989
28173
|
this.deps = deps;
|
|
27990
|
-
this.store = deps.store ?? new TunnelStore(
|
|
28174
|
+
this.store = deps.store ?? new TunnelStore(import_node_path24.default.join(deps.dataDir, "tunnel.json"));
|
|
27991
28175
|
this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
|
|
27992
28176
|
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
27993
28177
|
}
|
|
@@ -28114,8 +28298,8 @@ var TunnelManager = class {
|
|
|
28114
28298
|
dataDir: this.deps.dataDir,
|
|
28115
28299
|
override: this.deps.frpcBinaryOverride ?? void 0
|
|
28116
28300
|
});
|
|
28117
|
-
const tomlPath =
|
|
28118
|
-
const proxyName = `clawd-${t.subdomain}-${localPort}-${
|
|
28301
|
+
const tomlPath = import_node_path24.default.join(this.deps.dataDir, "frpc.toml");
|
|
28302
|
+
const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto8.default.randomBytes(3).toString("hex")}`;
|
|
28119
28303
|
const toml = buildFrpcToml({
|
|
28120
28304
|
serverAddr: t.frpsHost,
|
|
28121
28305
|
serverPort: t.frpsPort,
|
|
@@ -28125,12 +28309,12 @@ var TunnelManager = class {
|
|
|
28125
28309
|
localPort,
|
|
28126
28310
|
logLevel: "info"
|
|
28127
28311
|
});
|
|
28128
|
-
await
|
|
28312
|
+
await import_node_fs22.default.promises.writeFile(tomlPath, toml, { mode: 384 });
|
|
28129
28313
|
const proc = (this.deps.spawnImpl ?? import_node_child_process5.spawn)(frpcBin, ["-c", tomlPath], {
|
|
28130
28314
|
stdio: ["ignore", "pipe", "pipe"]
|
|
28131
28315
|
});
|
|
28132
|
-
const logFilePath =
|
|
28133
|
-
const logStream =
|
|
28316
|
+
const logFilePath = import_node_path24.default.join(this.deps.dataDir, "frpc.log");
|
|
28317
|
+
const logStream = import_node_fs22.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
|
|
28134
28318
|
logStream.on("error", () => {
|
|
28135
28319
|
});
|
|
28136
28320
|
const tee = (chunk) => {
|
|
@@ -28212,23 +28396,23 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
28212
28396
|
}
|
|
28213
28397
|
|
|
28214
28398
|
// src/tunnel/device-key.ts
|
|
28215
|
-
var
|
|
28216
|
-
var
|
|
28399
|
+
var import_node_os10 = __toESM(require("os"), 1);
|
|
28400
|
+
var import_node_crypto9 = __toESM(require("crypto"), 1);
|
|
28217
28401
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
28218
28402
|
function deriveStableDeviceKey(opts = {}) {
|
|
28219
|
-
const hostname = opts.hostname ??
|
|
28220
|
-
const uid = opts.uid ?? (typeof
|
|
28403
|
+
const hostname = opts.hostname ?? import_node_os10.default.hostname();
|
|
28404
|
+
const uid = opts.uid ?? (typeof import_node_os10.default.userInfo === "function" ? import_node_os10.default.userInfo().uid : 0);
|
|
28221
28405
|
const input = `${hostname}::${uid}`;
|
|
28222
|
-
return
|
|
28406
|
+
return import_node_crypto9.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
28223
28407
|
}
|
|
28224
28408
|
|
|
28225
28409
|
// src/auth-store.ts
|
|
28226
|
-
var
|
|
28227
|
-
var
|
|
28228
|
-
var
|
|
28410
|
+
var import_node_fs23 = __toESM(require("fs"), 1);
|
|
28411
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
28412
|
+
var import_node_crypto10 = __toESM(require("crypto"), 1);
|
|
28229
28413
|
var AUTH_FILE_NAME = "auth.json";
|
|
28230
28414
|
function authFilePath(dataDir) {
|
|
28231
|
-
return
|
|
28415
|
+
return import_node_path25.default.join(dataDir, AUTH_FILE_NAME);
|
|
28232
28416
|
}
|
|
28233
28417
|
function loadOrCreateAuthFile(opts) {
|
|
28234
28418
|
const file = authFilePath(opts.dataDir);
|
|
@@ -28253,14 +28437,14 @@ function loadOrCreateAuthFile(opts) {
|
|
|
28253
28437
|
return next;
|
|
28254
28438
|
}
|
|
28255
28439
|
function defaultGenerateToken2() {
|
|
28256
|
-
return
|
|
28440
|
+
return import_node_crypto10.default.randomBytes(32).toString("base64url");
|
|
28257
28441
|
}
|
|
28258
28442
|
function defaultGenerateOwnerPrincipalId() {
|
|
28259
|
-
return `owner-${
|
|
28443
|
+
return `owner-${import_node_crypto10.default.randomUUID()}`;
|
|
28260
28444
|
}
|
|
28261
28445
|
function readAuthFile(file) {
|
|
28262
28446
|
try {
|
|
28263
|
-
const raw =
|
|
28447
|
+
const raw = import_node_fs23.default.readFileSync(file, "utf8");
|
|
28264
28448
|
const parsed = JSON.parse(raw);
|
|
28265
28449
|
if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
|
|
28266
28450
|
return null;
|
|
@@ -28278,25 +28462,25 @@ function readAuthFile(file) {
|
|
|
28278
28462
|
}
|
|
28279
28463
|
}
|
|
28280
28464
|
function writeAuthFile(file, content) {
|
|
28281
|
-
|
|
28282
|
-
|
|
28465
|
+
import_node_fs23.default.mkdirSync(import_node_path25.default.dirname(file), { recursive: true });
|
|
28466
|
+
import_node_fs23.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
28283
28467
|
try {
|
|
28284
|
-
|
|
28468
|
+
import_node_fs23.default.chmodSync(file, 384);
|
|
28285
28469
|
} catch {
|
|
28286
28470
|
}
|
|
28287
28471
|
}
|
|
28288
28472
|
|
|
28289
28473
|
// src/owner-profile.ts
|
|
28290
|
-
var
|
|
28291
|
-
var
|
|
28292
|
-
var
|
|
28474
|
+
var import_node_fs24 = __toESM(require("fs"), 1);
|
|
28475
|
+
var import_node_os11 = __toESM(require("os"), 1);
|
|
28476
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
28293
28477
|
var PROFILE_FILENAME = "profile.json";
|
|
28294
28478
|
function loadOwnerDisplayName(dataDir) {
|
|
28295
|
-
const fallback =
|
|
28296
|
-
const profilePath =
|
|
28479
|
+
const fallback = import_node_os11.default.userInfo().username;
|
|
28480
|
+
const profilePath = import_node_path26.default.join(dataDir, PROFILE_FILENAME);
|
|
28297
28481
|
let raw;
|
|
28298
28482
|
try {
|
|
28299
|
-
raw =
|
|
28483
|
+
raw = import_node_fs24.default.readFileSync(profilePath, "utf8");
|
|
28300
28484
|
} catch {
|
|
28301
28485
|
return fallback;
|
|
28302
28486
|
}
|
|
@@ -28325,12 +28509,12 @@ init_protocol();
|
|
|
28325
28509
|
init_protocol();
|
|
28326
28510
|
|
|
28327
28511
|
// src/session/fork.ts
|
|
28328
|
-
var
|
|
28329
|
-
var
|
|
28330
|
-
var
|
|
28512
|
+
var import_node_fs25 = __toESM(require("fs"), 1);
|
|
28513
|
+
var import_node_os12 = __toESM(require("os"), 1);
|
|
28514
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
28331
28515
|
init_claude_history();
|
|
28332
28516
|
function readJsonlEntries(file) {
|
|
28333
|
-
const raw =
|
|
28517
|
+
const raw = import_node_fs25.default.readFileSync(file, "utf8");
|
|
28334
28518
|
const out = [];
|
|
28335
28519
|
for (const line of raw.split("\n")) {
|
|
28336
28520
|
const t = line.trim();
|
|
@@ -28343,10 +28527,10 @@ function readJsonlEntries(file) {
|
|
|
28343
28527
|
return out;
|
|
28344
28528
|
}
|
|
28345
28529
|
function forkSession(input) {
|
|
28346
|
-
const baseDir = input.baseDir ??
|
|
28347
|
-
const projectDir =
|
|
28348
|
-
const sourceFile =
|
|
28349
|
-
if (!
|
|
28530
|
+
const baseDir = input.baseDir ?? import_node_path27.default.join(import_node_os12.default.homedir(), ".claude");
|
|
28531
|
+
const projectDir = import_node_path27.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
28532
|
+
const sourceFile = import_node_path27.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
28533
|
+
if (!import_node_fs25.default.existsSync(sourceFile)) {
|
|
28350
28534
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
28351
28535
|
}
|
|
28352
28536
|
const entries = readJsonlEntries(sourceFile);
|
|
@@ -28376,9 +28560,9 @@ function forkSession(input) {
|
|
|
28376
28560
|
}
|
|
28377
28561
|
forkedLines.push(JSON.stringify(forked));
|
|
28378
28562
|
}
|
|
28379
|
-
const forkedFilePath =
|
|
28380
|
-
|
|
28381
|
-
|
|
28563
|
+
const forkedFilePath = import_node_path27.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
28564
|
+
import_node_fs25.default.mkdirSync(projectDir, { recursive: true });
|
|
28565
|
+
import_node_fs25.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
28382
28566
|
return { forkedToolSessionId, forkedFilePath };
|
|
28383
28567
|
}
|
|
28384
28568
|
|
|
@@ -28710,7 +28894,7 @@ function buildPermissionHandlers(deps) {
|
|
|
28710
28894
|
}
|
|
28711
28895
|
|
|
28712
28896
|
// src/handlers/history.ts
|
|
28713
|
-
var
|
|
28897
|
+
var path37 = __toESM(require("path"), 1);
|
|
28714
28898
|
init_protocol();
|
|
28715
28899
|
|
|
28716
28900
|
// src/session/recent-dirs.ts
|
|
@@ -28728,7 +28912,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
28728
28912
|
}
|
|
28729
28913
|
|
|
28730
28914
|
// src/permission/persona-paths.ts
|
|
28731
|
-
var
|
|
28915
|
+
var path36 = __toESM(require("path"), 1);
|
|
28732
28916
|
function getAllowedPersonaIds(grants, action) {
|
|
28733
28917
|
const ids = /* @__PURE__ */ new Set();
|
|
28734
28918
|
for (const g2 of grants) {
|
|
@@ -28741,26 +28925,26 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
28741
28925
|
return ids;
|
|
28742
28926
|
}
|
|
28743
28927
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read") {
|
|
28744
|
-
const root =
|
|
28745
|
-
const target =
|
|
28746
|
-
const sep3 = root.endsWith(
|
|
28928
|
+
const root = path36.resolve(personaRoot);
|
|
28929
|
+
const target = path36.resolve(absPath);
|
|
28930
|
+
const sep3 = root.endsWith(path36.sep) ? "" : path36.sep;
|
|
28747
28931
|
if (!target.startsWith(root + sep3)) return false;
|
|
28748
|
-
const rel =
|
|
28932
|
+
const rel = path36.relative(root, target);
|
|
28749
28933
|
if (!rel || rel.startsWith("..")) return false;
|
|
28750
|
-
const personaId = rel.split(
|
|
28934
|
+
const personaId = rel.split(path36.sep)[0];
|
|
28751
28935
|
if (!personaId) return false;
|
|
28752
28936
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
28753
28937
|
if (allowed === "*") return true;
|
|
28754
28938
|
return allowed.has(personaId);
|
|
28755
28939
|
}
|
|
28756
28940
|
function personaIdFromPath(absPath, personaRoot) {
|
|
28757
|
-
const root =
|
|
28758
|
-
const target =
|
|
28759
|
-
const sep3 = root.endsWith(
|
|
28941
|
+
const root = path36.resolve(personaRoot);
|
|
28942
|
+
const target = path36.resolve(absPath);
|
|
28943
|
+
const sep3 = root.endsWith(path36.sep) ? "" : path36.sep;
|
|
28760
28944
|
if (!target.startsWith(root + sep3)) return null;
|
|
28761
|
-
const rel =
|
|
28945
|
+
const rel = path36.relative(root, target);
|
|
28762
28946
|
if (!rel || rel.startsWith("..")) return null;
|
|
28763
|
-
const id = rel.split(
|
|
28947
|
+
const id = rel.split(path36.sep)[0];
|
|
28764
28948
|
return id || null;
|
|
28765
28949
|
}
|
|
28766
28950
|
|
|
@@ -28785,7 +28969,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28785
28969
|
if (!pid) return false;
|
|
28786
28970
|
return isGuestPathAllowed(
|
|
28787
28971
|
ctx.grants,
|
|
28788
|
-
|
|
28972
|
+
path37.join(personaRoot, pid),
|
|
28789
28973
|
personaRoot,
|
|
28790
28974
|
"read"
|
|
28791
28975
|
);
|
|
@@ -28796,7 +28980,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28796
28980
|
};
|
|
28797
28981
|
const list = async (frame, _client, ctx) => {
|
|
28798
28982
|
const args = HistoryListArgs.parse(frame);
|
|
28799
|
-
assertGuestPath(ctx,
|
|
28983
|
+
assertGuestPath(ctx, path37.resolve(args.projectPath), personaRoot, "history:list");
|
|
28800
28984
|
const sessions = await history.listSessions(args);
|
|
28801
28985
|
return { response: { type: "history:list", sessions } };
|
|
28802
28986
|
};
|
|
@@ -28828,13 +29012,13 @@ function buildHistoryHandlers(deps) {
|
|
|
28828
29012
|
};
|
|
28829
29013
|
const subagents = async (frame, _client, ctx) => {
|
|
28830
29014
|
const args = HistorySubagentsArgs.parse(frame);
|
|
28831
|
-
assertGuestPath(ctx,
|
|
29015
|
+
assertGuestPath(ctx, path37.resolve(args.cwd), personaRoot, "history:subagents");
|
|
28832
29016
|
const subs = await history.listSubagents(args);
|
|
28833
29017
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
28834
29018
|
};
|
|
28835
29019
|
const subagentRead = async (frame, _client, ctx) => {
|
|
28836
29020
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
28837
|
-
assertGuestPath(ctx,
|
|
29021
|
+
assertGuestPath(ctx, path37.resolve(args.cwd), personaRoot, "history:subagent-read");
|
|
28838
29022
|
const res = await history.readSubagent(args);
|
|
28839
29023
|
return { response: { type: "history:subagent-read", ...res } };
|
|
28840
29024
|
};
|
|
@@ -28842,7 +29026,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28842
29026
|
const dirs = listRecentDirs(store);
|
|
28843
29027
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
28844
29028
|
const filtered = dirs.filter(
|
|
28845
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
29029
|
+
(d) => isGuestPathAllowed(ctx.grants, path37.resolve(d.cwd), personaRoot, "read")
|
|
28846
29030
|
);
|
|
28847
29031
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
28848
29032
|
}
|
|
@@ -28859,8 +29043,8 @@ function buildHistoryHandlers(deps) {
|
|
|
28859
29043
|
}
|
|
28860
29044
|
|
|
28861
29045
|
// src/handlers/workspace.ts
|
|
28862
|
-
var
|
|
28863
|
-
var
|
|
29046
|
+
var path38 = __toESM(require("path"), 1);
|
|
29047
|
+
var os13 = __toESM(require("os"), 1);
|
|
28864
29048
|
init_protocol();
|
|
28865
29049
|
init_protocol();
|
|
28866
29050
|
function buildEnabledPluginNames(personaManager, personaId) {
|
|
@@ -28899,23 +29083,23 @@ function buildWorkspaceHandlers(deps) {
|
|
|
28899
29083
|
const list = async (frame, _client, ctx) => {
|
|
28900
29084
|
const args = WorkspaceListArgs.parse(frame);
|
|
28901
29085
|
const isGuest = ctx?.principal.kind === "guest";
|
|
28902
|
-
const fallbackCwd = isGuest && personaRoot ? personaRoot :
|
|
28903
|
-
const resolvedCwd =
|
|
28904
|
-
const target = args.path ?
|
|
29086
|
+
const fallbackCwd = isGuest && personaRoot ? personaRoot : os13.homedir();
|
|
29087
|
+
const resolvedCwd = path38.resolve(args.cwd ?? fallbackCwd);
|
|
29088
|
+
const target = args.path ? path38.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
28905
29089
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list");
|
|
28906
29090
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
28907
29091
|
return { response: { type: "workspace:list", ...res } };
|
|
28908
29092
|
};
|
|
28909
29093
|
const read = async (frame, _client, ctx) => {
|
|
28910
29094
|
const args = WorkspaceReadArgs.parse(frame);
|
|
28911
|
-
const target =
|
|
29095
|
+
const target = path38.isAbsolute(args.path) ? path38.resolve(args.path) : path38.resolve(args.cwd, args.path);
|
|
28912
29096
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read");
|
|
28913
29097
|
const res = workspace.read(args);
|
|
28914
29098
|
return { response: { type: "workspace:read", ...res } };
|
|
28915
29099
|
};
|
|
28916
29100
|
const skillsList = async (frame, _client, ctx) => {
|
|
28917
29101
|
const args = SkillsListArgs.parse(frame);
|
|
28918
|
-
const cwdAbs =
|
|
29102
|
+
const cwdAbs = path38.resolve(args.cwd);
|
|
28919
29103
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list");
|
|
28920
29104
|
const list2 = skills.list(args);
|
|
28921
29105
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -28927,7 +29111,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
28927
29111
|
};
|
|
28928
29112
|
const agentsList = async (frame, _client, ctx) => {
|
|
28929
29113
|
const args = AgentsListArgs.parse(frame);
|
|
28930
|
-
const cwdAbs =
|
|
29114
|
+
const cwdAbs = path38.resolve(args.cwd);
|
|
28931
29115
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list");
|
|
28932
29116
|
const list2 = agents.list(args);
|
|
28933
29117
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -28946,20 +29130,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
28946
29130
|
}
|
|
28947
29131
|
|
|
28948
29132
|
// src/handlers/git.ts
|
|
28949
|
-
var
|
|
29133
|
+
var path40 = __toESM(require("path"), 1);
|
|
28950
29134
|
init_protocol();
|
|
28951
29135
|
init_protocol();
|
|
28952
29136
|
|
|
28953
29137
|
// src/workspace/git.ts
|
|
28954
29138
|
var import_node_child_process6 = require("child_process");
|
|
28955
|
-
var
|
|
28956
|
-
var
|
|
29139
|
+
var import_node_fs26 = __toESM(require("fs"), 1);
|
|
29140
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
28957
29141
|
var import_node_util = require("util");
|
|
28958
29142
|
var pexec = (0, import_node_util.promisify)(import_node_child_process6.execFile);
|
|
28959
29143
|
function normalizePath(p2) {
|
|
28960
|
-
const resolved =
|
|
29144
|
+
const resolved = import_node_path28.default.resolve(p2);
|
|
28961
29145
|
try {
|
|
28962
|
-
return
|
|
29146
|
+
return import_node_fs26.default.realpathSync(resolved);
|
|
28963
29147
|
} catch {
|
|
28964
29148
|
return resolved;
|
|
28965
29149
|
}
|
|
@@ -29032,7 +29216,7 @@ async function listGitBranches(cwd) {
|
|
|
29032
29216
|
// src/handlers/git.ts
|
|
29033
29217
|
function assertGuestCwd(ctx, cwd, personaRoot, method) {
|
|
29034
29218
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
29035
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
29219
|
+
if (!isGuestPathAllowed(ctx.grants, path40.resolve(cwd), personaRoot, "read")) {
|
|
29036
29220
|
throw new ClawdError(
|
|
29037
29221
|
ERROR_CODES.UNAUTHORIZED,
|
|
29038
29222
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -29395,7 +29579,7 @@ function buildWhoamiHandler(deps) {
|
|
|
29395
29579
|
}
|
|
29396
29580
|
|
|
29397
29581
|
// src/handlers/meta.ts
|
|
29398
|
-
var
|
|
29582
|
+
var import_node_os13 = __toESM(require("os"), 1);
|
|
29399
29583
|
init_protocol();
|
|
29400
29584
|
|
|
29401
29585
|
// src/version.ts
|
|
@@ -29425,7 +29609,7 @@ function buildReadyFrame(deps, client) {
|
|
|
29425
29609
|
return {
|
|
29426
29610
|
version,
|
|
29427
29611
|
protocolVersion: PROTOCOL_VERSION,
|
|
29428
|
-
hostname:
|
|
29612
|
+
hostname: import_node_os13.default.hostname(),
|
|
29429
29613
|
os: process.platform,
|
|
29430
29614
|
tools,
|
|
29431
29615
|
runningSessions: info.runningSessions,
|
|
@@ -29519,7 +29703,7 @@ function buildPersonaHandlers(deps) {
|
|
|
29519
29703
|
}
|
|
29520
29704
|
|
|
29521
29705
|
// src/handlers/attachment.ts
|
|
29522
|
-
var
|
|
29706
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
29523
29707
|
init_protocol();
|
|
29524
29708
|
init_protocol();
|
|
29525
29709
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -29573,12 +29757,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
29573
29757
|
`session ${args.sessionId} scope unresolved`
|
|
29574
29758
|
);
|
|
29575
29759
|
}
|
|
29576
|
-
const cwdAbs =
|
|
29577
|
-
const candidateAbs =
|
|
29760
|
+
const cwdAbs = import_node_path29.default.resolve(sessionFile.cwd);
|
|
29761
|
+
const candidateAbs = import_node_path29.default.isAbsolute(args.relPath) ? import_node_path29.default.resolve(args.relPath) : import_node_path29.default.resolve(cwdAbs, args.relPath);
|
|
29578
29762
|
assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl");
|
|
29579
29763
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
29580
29764
|
const entry = entries.find((e) => {
|
|
29581
|
-
const storedAbs =
|
|
29765
|
+
const storedAbs = import_node_path29.default.isAbsolute(e.relPath) ? import_node_path29.default.resolve(e.relPath) : import_node_path29.default.resolve(cwdAbs, e.relPath);
|
|
29582
29766
|
return storedAbs === candidateAbs && !e.stale;
|
|
29583
29767
|
});
|
|
29584
29768
|
if (!entry) {
|
|
@@ -29602,7 +29786,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29602
29786
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
29603
29787
|
const f = deps.sessionStore.read(sessionId);
|
|
29604
29788
|
if (!f) return;
|
|
29605
|
-
assertGuestAttachmentPath(ctx,
|
|
29789
|
+
assertGuestAttachmentPath(ctx, import_node_path29.default.resolve(f.cwd), deps.personaRoot, method);
|
|
29606
29790
|
}
|
|
29607
29791
|
const groupAdd = async (frame, _client, ctx) => {
|
|
29608
29792
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -29643,7 +29827,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29643
29827
|
}
|
|
29644
29828
|
const entries = deps.groupFileStore.list(scope, parsed.data.sessionId);
|
|
29645
29829
|
const target = entries.find((e) => e.relPath === parsed.data.relPath);
|
|
29646
|
-
if (target?.from === "owner") {
|
|
29830
|
+
if (target?.from === "owner" || target?.from === "user-upload") {
|
|
29647
29831
|
deps.groupFileStore.remove(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
29648
29832
|
} else {
|
|
29649
29833
|
deps.groupFileStore.markStale(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
@@ -29839,7 +30023,7 @@ function computeGrantForFrame(method, frame) {
|
|
|
29839
30023
|
async function startDaemon(config) {
|
|
29840
30024
|
const logger = createLogger({
|
|
29841
30025
|
level: config.logLevel,
|
|
29842
|
-
file:
|
|
30026
|
+
file: import_node_path30.default.join(config.dataDir, "clawd.log")
|
|
29843
30027
|
});
|
|
29844
30028
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
29845
30029
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
@@ -29926,7 +30110,7 @@ async function startDaemon(config) {
|
|
|
29926
30110
|
const agents = new AgentsScanner();
|
|
29927
30111
|
const history = new ClaudeHistoryReader();
|
|
29928
30112
|
let transport = null;
|
|
29929
|
-
const personaStore = new PersonaStore(
|
|
30113
|
+
const personaStore = new PersonaStore(import_node_path30.default.join(config.dataDir, "personas"));
|
|
29930
30114
|
const defaultsRoot = findDefaultsRoot();
|
|
29931
30115
|
if (defaultsRoot) {
|
|
29932
30116
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -29943,7 +30127,7 @@ async function startDaemon(config) {
|
|
|
29943
30127
|
getAdapter,
|
|
29944
30128
|
historyReader: history,
|
|
29945
30129
|
dataDir: config.dataDir,
|
|
29946
|
-
personaRoot:
|
|
30130
|
+
personaRoot: import_node_path30.default.join(config.dataDir, "personas"),
|
|
29947
30131
|
personaStore,
|
|
29948
30132
|
ownerDisplayName,
|
|
29949
30133
|
ownerPrincipalId,
|
|
@@ -29967,10 +30151,10 @@ async function startDaemon(config) {
|
|
|
29967
30151
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
29968
30152
|
attachmentGroup: {
|
|
29969
30153
|
onFileEdit: (input) => {
|
|
29970
|
-
const absPath =
|
|
30154
|
+
const absPath = import_node_path30.default.isAbsolute(input.relPath) ? input.relPath : import_node_path30.default.join(input.cwd, input.relPath);
|
|
29971
30155
|
let size = 0;
|
|
29972
30156
|
try {
|
|
29973
|
-
size =
|
|
30157
|
+
size = import_node_fs27.default.statSync(absPath).size;
|
|
29974
30158
|
} catch (err) {
|
|
29975
30159
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
29976
30160
|
sessionId: input.sessionId,
|
|
@@ -29996,11 +30180,6 @@ async function startDaemon(config) {
|
|
|
29996
30180
|
}
|
|
29997
30181
|
});
|
|
29998
30182
|
const observer = new SessionObserver({
|
|
29999
|
-
// TUI 模式 pty 跑 CC 不发 SDK system.task_started 帧 → UI pending 期 progress.agentId
|
|
30000
|
-
// 永远 undefined,子 agent 调用过程没有抽屉入口。开 meta observer 后从 subagents 目录
|
|
30001
|
-
// 新出现的 agent-*.meta.json 派生 subagent_progress(started) 补这块缺口。SDK 模式仍走
|
|
30002
|
-
// task_started 路径,避免双 emit 让 ring buffer 双帧。
|
|
30003
|
-
enableSubagentMetaObserver: config.mode === "tui",
|
|
30004
30183
|
onEvent: ({ sessionId, events }) => {
|
|
30005
30184
|
manager.feedObserverEvents(sessionId, events);
|
|
30006
30185
|
},
|
|
@@ -30088,10 +30267,10 @@ async function startDaemon(config) {
|
|
|
30088
30267
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
30089
30268
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30090
30269
|
// guest path guard:candidate 必须在 personaRoot 子树下
|
|
30091
|
-
personaRoot:
|
|
30270
|
+
personaRoot: import_node_path30.default.join(config.dataDir, "personas")
|
|
30092
30271
|
},
|
|
30093
30272
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
30094
|
-
personaRoot:
|
|
30273
|
+
personaRoot: import_node_path30.default.join(config.dataDir, "personas"),
|
|
30095
30274
|
// capability:list / delete handler 依赖
|
|
30096
30275
|
capabilityManager,
|
|
30097
30276
|
// personal-cap:get 返回的 shareBaseUrl 用此 base URL:
|
|
@@ -30127,7 +30306,19 @@ async function startDaemon(config) {
|
|
|
30127
30306
|
sessionStore: store,
|
|
30128
30307
|
// /files HMAC verify 用 auth.json 的 signSecret 字段(与 attachment.signUrl 同源)。
|
|
30129
30308
|
// --auth-token CLI 模式没 signSecret → 路由返 501,sign URL 功能整体禁用。
|
|
30130
|
-
getSignSecret: () => authFile?.signSecret ?? null
|
|
30309
|
+
getSignSecret: () => authFile?.signSecret ?? null,
|
|
30310
|
+
// chatinput 附件上传(POST /attachments/upload,spec §6.1)。upload pipeline
|
|
30311
|
+
// 在 session.cwd 下的 .attachments/ 落盘;signSecret 与 /files HMAC 同源;
|
|
30312
|
+
// tunnel 未启时返 503 TUNNEL_NOT_READY 让前端显式提示。
|
|
30313
|
+
attachmentUpload: {
|
|
30314
|
+
groupFileStore,
|
|
30315
|
+
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30316
|
+
getSessionDir: (sid) => {
|
|
30317
|
+
const f = manager.findOwnedSession(sid);
|
|
30318
|
+
return f ? f.cwd : null;
|
|
30319
|
+
},
|
|
30320
|
+
getHttpBaseUrl: () => currentTunnelUrl ? getHttpBaseUrl() : null
|
|
30321
|
+
}
|
|
30131
30322
|
});
|
|
30132
30323
|
wsServer = new LocalWsServer({
|
|
30133
30324
|
host: config.host,
|
|
@@ -30292,8 +30483,8 @@ async function startDaemon(config) {
|
|
|
30292
30483
|
const lines = [
|
|
30293
30484
|
`Tunnel: ${r.url}`,
|
|
30294
30485
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
30295
|
-
`Frpc config: ${
|
|
30296
|
-
`Frpc log: ${
|
|
30486
|
+
`Frpc config: ${import_node_path30.default.join(config.dataDir, "frpc.toml")}`,
|
|
30487
|
+
`Frpc log: ${import_node_path30.default.join(config.dataDir, "frpc.log")}`
|
|
30297
30488
|
];
|
|
30298
30489
|
const width = Math.max(...lines.map((l) => l.length));
|
|
30299
30490
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -30306,8 +30497,8 @@ ${bar}
|
|
|
30306
30497
|
|
|
30307
30498
|
`);
|
|
30308
30499
|
try {
|
|
30309
|
-
const connectPath =
|
|
30310
|
-
|
|
30500
|
+
const connectPath = import_node_path30.default.join(config.dataDir, "connect.txt");
|
|
30501
|
+
import_node_fs27.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
30311
30502
|
} catch {
|
|
30312
30503
|
}
|
|
30313
30504
|
} catch (err) {
|
|
@@ -30332,8 +30523,29 @@ ${bar}
|
|
|
30332
30523
|
logger.warn("tunnel unavailable, degraded to local mode", { reason: tunnelError });
|
|
30333
30524
|
}
|
|
30334
30525
|
}
|
|
30526
|
+
const tickAttachmentGc = () => {
|
|
30527
|
+
try {
|
|
30528
|
+
runAttachmentGc({
|
|
30529
|
+
dataDir: config.dataDir,
|
|
30530
|
+
groupFileStore,
|
|
30531
|
+
sessionScopes: manager.listOwnedSessionScopes().map(({ scope, sessionId }) => ({
|
|
30532
|
+
scope,
|
|
30533
|
+
sessionId
|
|
30534
|
+
})),
|
|
30535
|
+
// 注入 cwd 让 GC 直接定位 .attachments 目录(与 upload 落盘点一致)
|
|
30536
|
+
getSessionCwd: (sid) => manager.findOwnedSession(sid)?.cwd ?? null,
|
|
30537
|
+
logger
|
|
30538
|
+
});
|
|
30539
|
+
} catch (err) {
|
|
30540
|
+
logger.warn("attachment gc tick failed", { err: err.message });
|
|
30541
|
+
}
|
|
30542
|
+
};
|
|
30543
|
+
setImmediate(tickAttachmentGc);
|
|
30544
|
+
const attachmentGcInterval = setInterval(tickAttachmentGc, 24 * 3600 * 1e3);
|
|
30545
|
+
attachmentGcInterval.unref();
|
|
30335
30546
|
const shutdown = async () => {
|
|
30336
30547
|
logger.info("stopping clawd");
|
|
30548
|
+
clearInterval(attachmentGcInterval);
|
|
30337
30549
|
observer.stopAll();
|
|
30338
30550
|
manager.stopAll();
|
|
30339
30551
|
if (tunnelMgr) {
|
|
@@ -30351,9 +30563,9 @@ ${bar}
|
|
|
30351
30563
|
};
|
|
30352
30564
|
}
|
|
30353
30565
|
function migrateDropPersonsDir(dataDir) {
|
|
30354
|
-
const dir =
|
|
30566
|
+
const dir = import_node_path30.default.join(dataDir, "persons");
|
|
30355
30567
|
try {
|
|
30356
|
-
|
|
30568
|
+
import_node_fs27.default.rmSync(dir, { recursive: true, force: true });
|
|
30357
30569
|
} catch {
|
|
30358
30570
|
}
|
|
30359
30571
|
}
|