@clawos-dev/clawd 0.2.89-beta.165.fab5549 → 0.2.89-beta.166.ca76654
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 +595 -236
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -651,8 +651,8 @@ var init_parseUtil = __esm({
|
|
|
651
651
|
init_errors2();
|
|
652
652
|
init_en();
|
|
653
653
|
makeIssue = (params) => {
|
|
654
|
-
const { data, path:
|
|
655
|
-
const fullPath = [...
|
|
654
|
+
const { data, path: path46, errorMaps, issueData } = params;
|
|
655
|
+
const fullPath = [...path46, ...issueData.path || []];
|
|
656
656
|
const fullIssue = {
|
|
657
657
|
...issueData,
|
|
658
658
|
path: fullPath
|
|
@@ -963,11 +963,11 @@ var init_types = __esm({
|
|
|
963
963
|
init_parseUtil();
|
|
964
964
|
init_util();
|
|
965
965
|
ParseInputLazyPath = class {
|
|
966
|
-
constructor(parent, value,
|
|
966
|
+
constructor(parent, value, path46, key) {
|
|
967
967
|
this._cachedPath = [];
|
|
968
968
|
this.parent = parent;
|
|
969
969
|
this.data = value;
|
|
970
|
-
this._path =
|
|
970
|
+
this._path = path46;
|
|
971
971
|
this._key = key;
|
|
972
972
|
}
|
|
973
973
|
get path() {
|
|
@@ -4351,13 +4351,13 @@ var init_zod = __esm({
|
|
|
4351
4351
|
});
|
|
4352
4352
|
|
|
4353
4353
|
// ../protocol/src/attachment-schemas.ts
|
|
4354
|
-
var TOKEN_ROLES, GROUP_FILE_SOURCES, GroupFileEntrySchema, AttachmentSignUrlArgs, AttachmentSignUrlResponseSchema, AttachmentGroupAddArgs, AttachmentGroupAddResponseSchema, AttachmentGroupRemoveArgs, AttachmentGroupRemoveResponseSchema, AttachmentGroupListArgs, AttachmentGroupListResponseSchema;
|
|
4354
|
+
var TOKEN_ROLES, GROUP_FILE_SOURCES, GroupFileEntrySchema, AttachmentSignUrlArgs, AttachmentSignUrlResponseSchema, AttachmentGroupAddArgs, AttachmentGroupAddResponseSchema, AttachmentGroupRemoveArgs, AttachmentGroupRemoveResponseSchema, AttachmentGroupListArgs, AttachmentGroupListResponseSchema, AttachmentUploadResponseSchema;
|
|
4355
4355
|
var init_attachment_schemas = __esm({
|
|
4356
4356
|
"../protocol/src/attachment-schemas.ts"() {
|
|
4357
4357
|
"use strict";
|
|
4358
4358
|
init_zod();
|
|
4359
4359
|
TOKEN_ROLES = ["owner"];
|
|
4360
|
-
GROUP_FILE_SOURCES = ["agent", "owner"];
|
|
4360
|
+
GROUP_FILE_SOURCES = ["agent", "owner", "user-upload"];
|
|
4361
4361
|
GroupFileEntrySchema = external_exports.object({
|
|
4362
4362
|
/** daemon 派发的稳定 id(用于 RPC remove / UI key) */
|
|
4363
4363
|
id: external_exports.string().min(1),
|
|
@@ -4412,6 +4412,16 @@ var init_attachment_schemas = __esm({
|
|
|
4412
4412
|
AttachmentGroupListResponseSchema = external_exports.object({
|
|
4413
4413
|
entries: external_exports.array(GroupFileEntrySchema)
|
|
4414
4414
|
});
|
|
4415
|
+
AttachmentUploadResponseSchema = external_exports.object({
|
|
4416
|
+
/** sha256(bytes) 前 16 位,前端持有的稳定 ref */
|
|
4417
|
+
attachmentId: external_exports.string().min(1),
|
|
4418
|
+
/** 完整 tunnel signed URL,前端拼 marker / 直接送 Anthropic 都用它 */
|
|
4419
|
+
url: external_exports.string().min(1),
|
|
4420
|
+
/** 落在 session 目录下的相对路径(后续 group RPC / drawer 展示用) */
|
|
4421
|
+
relPath: external_exports.string().min(1),
|
|
4422
|
+
sizeBytes: external_exports.number().int().nonnegative(),
|
|
4423
|
+
mimeType: external_exports.string().min(1)
|
|
4424
|
+
});
|
|
4415
4425
|
}
|
|
4416
4426
|
});
|
|
4417
4427
|
|
|
@@ -5625,8 +5635,8 @@ var require_req = __commonJS({
|
|
|
5625
5635
|
if (req.originalUrl) {
|
|
5626
5636
|
_req.url = req.originalUrl;
|
|
5627
5637
|
} else {
|
|
5628
|
-
const
|
|
5629
|
-
_req.url = typeof
|
|
5638
|
+
const path46 = req.path;
|
|
5639
|
+
_req.url = typeof path46 === "string" ? path46 : req.url ? req.url.path || req.url : void 0;
|
|
5630
5640
|
}
|
|
5631
5641
|
if (req.query) {
|
|
5632
5642
|
_req.query = req.query;
|
|
@@ -5791,14 +5801,14 @@ var require_redact = __commonJS({
|
|
|
5791
5801
|
}
|
|
5792
5802
|
return obj;
|
|
5793
5803
|
}
|
|
5794
|
-
function parsePath(
|
|
5804
|
+
function parsePath(path46) {
|
|
5795
5805
|
const parts = [];
|
|
5796
5806
|
let current = "";
|
|
5797
5807
|
let inBrackets = false;
|
|
5798
5808
|
let inQuotes = false;
|
|
5799
5809
|
let quoteChar = "";
|
|
5800
|
-
for (let i = 0; i <
|
|
5801
|
-
const char =
|
|
5810
|
+
for (let i = 0; i < path46.length; i++) {
|
|
5811
|
+
const char = path46[i];
|
|
5802
5812
|
if (!inBrackets && char === ".") {
|
|
5803
5813
|
if (current) {
|
|
5804
5814
|
parts.push(current);
|
|
@@ -5929,10 +5939,10 @@ var require_redact = __commonJS({
|
|
|
5929
5939
|
return current;
|
|
5930
5940
|
}
|
|
5931
5941
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
5932
|
-
for (const
|
|
5933
|
-
const parts = parsePath(
|
|
5942
|
+
for (const path46 of paths) {
|
|
5943
|
+
const parts = parsePath(path46);
|
|
5934
5944
|
if (parts.includes("*")) {
|
|
5935
|
-
redactWildcardPath(obj, parts, censor,
|
|
5945
|
+
redactWildcardPath(obj, parts, censor, path46, remove);
|
|
5936
5946
|
} else {
|
|
5937
5947
|
if (remove) {
|
|
5938
5948
|
removeKey(obj, parts);
|
|
@@ -6017,8 +6027,8 @@ var require_redact = __commonJS({
|
|
|
6017
6027
|
}
|
|
6018
6028
|
} else {
|
|
6019
6029
|
if (afterWildcard.includes("*")) {
|
|
6020
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6021
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
6030
|
+
const wrappedCensor = typeof censor === "function" ? (value, path46) => {
|
|
6031
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path46];
|
|
6022
6032
|
return censor(value, fullPath);
|
|
6023
6033
|
} : censor;
|
|
6024
6034
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -6053,8 +6063,8 @@ var require_redact = __commonJS({
|
|
|
6053
6063
|
return null;
|
|
6054
6064
|
}
|
|
6055
6065
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
6056
|
-
for (const
|
|
6057
|
-
const parts = parsePath(
|
|
6066
|
+
for (const path46 of pathsToClone) {
|
|
6067
|
+
const parts = parsePath(path46);
|
|
6058
6068
|
let current = pathStructure;
|
|
6059
6069
|
for (let i = 0; i < parts.length; i++) {
|
|
6060
6070
|
const part = parts[i];
|
|
@@ -6106,24 +6116,24 @@ var require_redact = __commonJS({
|
|
|
6106
6116
|
}
|
|
6107
6117
|
return cloneSelectively(obj, pathStructure);
|
|
6108
6118
|
}
|
|
6109
|
-
function validatePath(
|
|
6110
|
-
if (typeof
|
|
6119
|
+
function validatePath(path46) {
|
|
6120
|
+
if (typeof path46 !== "string") {
|
|
6111
6121
|
throw new Error("Paths must be (non-empty) strings");
|
|
6112
6122
|
}
|
|
6113
|
-
if (
|
|
6123
|
+
if (path46 === "") {
|
|
6114
6124
|
throw new Error("Invalid redaction path ()");
|
|
6115
6125
|
}
|
|
6116
|
-
if (
|
|
6117
|
-
throw new Error(`Invalid redaction path (${
|
|
6126
|
+
if (path46.includes("..")) {
|
|
6127
|
+
throw new Error(`Invalid redaction path (${path46})`);
|
|
6118
6128
|
}
|
|
6119
|
-
if (
|
|
6120
|
-
throw new Error(`Invalid redaction path (${
|
|
6129
|
+
if (path46.includes(",")) {
|
|
6130
|
+
throw new Error(`Invalid redaction path (${path46})`);
|
|
6121
6131
|
}
|
|
6122
6132
|
let bracketCount = 0;
|
|
6123
6133
|
let inQuotes = false;
|
|
6124
6134
|
let quoteChar = "";
|
|
6125
|
-
for (let i = 0; i <
|
|
6126
|
-
const char =
|
|
6135
|
+
for (let i = 0; i < path46.length; i++) {
|
|
6136
|
+
const char = path46[i];
|
|
6127
6137
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
6128
6138
|
if (!inQuotes) {
|
|
6129
6139
|
inQuotes = true;
|
|
@@ -6137,20 +6147,20 @@ var require_redact = __commonJS({
|
|
|
6137
6147
|
} else if (char === "]" && !inQuotes) {
|
|
6138
6148
|
bracketCount--;
|
|
6139
6149
|
if (bracketCount < 0) {
|
|
6140
|
-
throw new Error(`Invalid redaction path (${
|
|
6150
|
+
throw new Error(`Invalid redaction path (${path46})`);
|
|
6141
6151
|
}
|
|
6142
6152
|
}
|
|
6143
6153
|
}
|
|
6144
6154
|
if (bracketCount !== 0) {
|
|
6145
|
-
throw new Error(`Invalid redaction path (${
|
|
6155
|
+
throw new Error(`Invalid redaction path (${path46})`);
|
|
6146
6156
|
}
|
|
6147
6157
|
}
|
|
6148
6158
|
function validatePaths(paths) {
|
|
6149
6159
|
if (!Array.isArray(paths)) {
|
|
6150
6160
|
throw new TypeError("paths must be an array");
|
|
6151
6161
|
}
|
|
6152
|
-
for (const
|
|
6153
|
-
validatePath(
|
|
6162
|
+
for (const path46 of paths) {
|
|
6163
|
+
validatePath(path46);
|
|
6154
6164
|
}
|
|
6155
6165
|
}
|
|
6156
6166
|
function slowRedact(options = {}) {
|
|
@@ -6318,8 +6328,8 @@ var require_redaction = __commonJS({
|
|
|
6318
6328
|
if (shape[k2] === null) {
|
|
6319
6329
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
6320
6330
|
} else {
|
|
6321
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6322
|
-
return censor(value, [k2, ...
|
|
6331
|
+
const wrappedCensor = typeof censor === "function" ? (value, path46) => {
|
|
6332
|
+
return censor(value, [k2, ...path46]);
|
|
6323
6333
|
} : censor;
|
|
6324
6334
|
o[k2] = Redact({
|
|
6325
6335
|
paths: shape[k2],
|
|
@@ -6537,10 +6547,10 @@ var require_atomic_sleep = __commonJS({
|
|
|
6537
6547
|
var require_sonic_boom = __commonJS({
|
|
6538
6548
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
6539
6549
|
"use strict";
|
|
6540
|
-
var
|
|
6550
|
+
var fs37 = require("fs");
|
|
6541
6551
|
var EventEmitter2 = require("events");
|
|
6542
6552
|
var inherits = require("util").inherits;
|
|
6543
|
-
var
|
|
6553
|
+
var path46 = require("path");
|
|
6544
6554
|
var sleep = require_atomic_sleep();
|
|
6545
6555
|
var assert = require("assert");
|
|
6546
6556
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -6594,20 +6604,20 @@ var require_sonic_boom = __commonJS({
|
|
|
6594
6604
|
const mode = sonic.mode;
|
|
6595
6605
|
if (sonic.sync) {
|
|
6596
6606
|
try {
|
|
6597
|
-
if (sonic.mkdir)
|
|
6598
|
-
const fd =
|
|
6607
|
+
if (sonic.mkdir) fs37.mkdirSync(path46.dirname(file), { recursive: true });
|
|
6608
|
+
const fd = fs37.openSync(file, flags, mode);
|
|
6599
6609
|
fileOpened(null, fd);
|
|
6600
6610
|
} catch (err) {
|
|
6601
6611
|
fileOpened(err);
|
|
6602
6612
|
throw err;
|
|
6603
6613
|
}
|
|
6604
6614
|
} else if (sonic.mkdir) {
|
|
6605
|
-
|
|
6615
|
+
fs37.mkdir(path46.dirname(file), { recursive: true }, (err) => {
|
|
6606
6616
|
if (err) return fileOpened(err);
|
|
6607
|
-
|
|
6617
|
+
fs37.open(file, flags, mode, fileOpened);
|
|
6608
6618
|
});
|
|
6609
6619
|
} else {
|
|
6610
|
-
|
|
6620
|
+
fs37.open(file, flags, mode, fileOpened);
|
|
6611
6621
|
}
|
|
6612
6622
|
}
|
|
6613
6623
|
function SonicBoom(opts) {
|
|
@@ -6648,8 +6658,8 @@ var require_sonic_boom = __commonJS({
|
|
|
6648
6658
|
this.flush = flushBuffer;
|
|
6649
6659
|
this.flushSync = flushBufferSync;
|
|
6650
6660
|
this._actualWrite = actualWriteBuffer;
|
|
6651
|
-
fsWriteSync = () =>
|
|
6652
|
-
fsWrite = () =>
|
|
6661
|
+
fsWriteSync = () => fs37.writeSync(this.fd, this._writingBuf);
|
|
6662
|
+
fsWrite = () => fs37.write(this.fd, this._writingBuf, this.release);
|
|
6653
6663
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
6654
6664
|
this._writingBuf = "";
|
|
6655
6665
|
this.write = write;
|
|
@@ -6658,15 +6668,15 @@ var require_sonic_boom = __commonJS({
|
|
|
6658
6668
|
this._actualWrite = actualWrite;
|
|
6659
6669
|
fsWriteSync = () => {
|
|
6660
6670
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
6661
|
-
return
|
|
6671
|
+
return fs37.writeSync(this.fd, this._writingBuf);
|
|
6662
6672
|
}
|
|
6663
|
-
return
|
|
6673
|
+
return fs37.writeSync(this.fd, this._writingBuf, "utf8");
|
|
6664
6674
|
};
|
|
6665
6675
|
fsWrite = () => {
|
|
6666
6676
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
6667
|
-
return
|
|
6677
|
+
return fs37.write(this.fd, this._writingBuf, this.release);
|
|
6668
6678
|
}
|
|
6669
|
-
return
|
|
6679
|
+
return fs37.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
6670
6680
|
};
|
|
6671
6681
|
} else {
|
|
6672
6682
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -6723,7 +6733,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6723
6733
|
}
|
|
6724
6734
|
}
|
|
6725
6735
|
if (this._fsync) {
|
|
6726
|
-
|
|
6736
|
+
fs37.fsyncSync(this.fd);
|
|
6727
6737
|
}
|
|
6728
6738
|
const len = this._len;
|
|
6729
6739
|
if (this._reopening) {
|
|
@@ -6837,7 +6847,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6837
6847
|
const onDrain = () => {
|
|
6838
6848
|
if (!this._fsync) {
|
|
6839
6849
|
try {
|
|
6840
|
-
|
|
6850
|
+
fs37.fsync(this.fd, (err) => {
|
|
6841
6851
|
this._flushPending = false;
|
|
6842
6852
|
cb(err);
|
|
6843
6853
|
});
|
|
@@ -6939,7 +6949,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6939
6949
|
const fd = this.fd;
|
|
6940
6950
|
this.once("ready", () => {
|
|
6941
6951
|
if (fd !== this.fd) {
|
|
6942
|
-
|
|
6952
|
+
fs37.close(fd, (err) => {
|
|
6943
6953
|
if (err) {
|
|
6944
6954
|
return this.emit("error", err);
|
|
6945
6955
|
}
|
|
@@ -6988,7 +6998,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6988
6998
|
buf = this._bufs[0];
|
|
6989
6999
|
}
|
|
6990
7000
|
try {
|
|
6991
|
-
const n = Buffer.isBuffer(buf) ?
|
|
7001
|
+
const n = Buffer.isBuffer(buf) ? fs37.writeSync(this.fd, buf) : fs37.writeSync(this.fd, buf, "utf8");
|
|
6992
7002
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
6993
7003
|
buf = releasedBufObj.writingBuf;
|
|
6994
7004
|
this._len = releasedBufObj.len;
|
|
@@ -7004,7 +7014,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7004
7014
|
}
|
|
7005
7015
|
}
|
|
7006
7016
|
try {
|
|
7007
|
-
|
|
7017
|
+
fs37.fsyncSync(this.fd);
|
|
7008
7018
|
} catch {
|
|
7009
7019
|
}
|
|
7010
7020
|
}
|
|
@@ -7025,7 +7035,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7025
7035
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
7026
7036
|
}
|
|
7027
7037
|
try {
|
|
7028
|
-
const n =
|
|
7038
|
+
const n = fs37.writeSync(this.fd, buf);
|
|
7029
7039
|
buf = buf.subarray(n);
|
|
7030
7040
|
this._len = Math.max(this._len - n, 0);
|
|
7031
7041
|
if (buf.length <= 0) {
|
|
@@ -7053,13 +7063,13 @@ var require_sonic_boom = __commonJS({
|
|
|
7053
7063
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
7054
7064
|
if (this.sync) {
|
|
7055
7065
|
try {
|
|
7056
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
7066
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs37.writeSync(this.fd, this._writingBuf) : fs37.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7057
7067
|
release(null, written);
|
|
7058
7068
|
} catch (err) {
|
|
7059
7069
|
release(err);
|
|
7060
7070
|
}
|
|
7061
7071
|
} else {
|
|
7062
|
-
|
|
7072
|
+
fs37.write(this.fd, this._writingBuf, release);
|
|
7063
7073
|
}
|
|
7064
7074
|
}
|
|
7065
7075
|
function actualWriteBuffer() {
|
|
@@ -7068,7 +7078,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7068
7078
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
7069
7079
|
if (this.sync) {
|
|
7070
7080
|
try {
|
|
7071
|
-
const written =
|
|
7081
|
+
const written = fs37.writeSync(this.fd, this._writingBuf);
|
|
7072
7082
|
release(null, written);
|
|
7073
7083
|
} catch (err) {
|
|
7074
7084
|
release(err);
|
|
@@ -7077,7 +7087,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7077
7087
|
if (kCopyBuffer) {
|
|
7078
7088
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
7079
7089
|
}
|
|
7080
|
-
|
|
7090
|
+
fs37.write(this.fd, this._writingBuf, release);
|
|
7081
7091
|
}
|
|
7082
7092
|
}
|
|
7083
7093
|
function actualClose(sonic) {
|
|
@@ -7093,12 +7103,12 @@ var require_sonic_boom = __commonJS({
|
|
|
7093
7103
|
sonic._lens = [];
|
|
7094
7104
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
7095
7105
|
try {
|
|
7096
|
-
|
|
7106
|
+
fs37.fsync(sonic.fd, closeWrapped);
|
|
7097
7107
|
} catch {
|
|
7098
7108
|
}
|
|
7099
7109
|
function closeWrapped() {
|
|
7100
7110
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
7101
|
-
|
|
7111
|
+
fs37.close(sonic.fd, done);
|
|
7102
7112
|
} else {
|
|
7103
7113
|
done();
|
|
7104
7114
|
}
|
|
@@ -7829,7 +7839,7 @@ var require_transport = __commonJS({
|
|
|
7829
7839
|
stream.flushSync();
|
|
7830
7840
|
}
|
|
7831
7841
|
function transport(fullOptions) {
|
|
7832
|
-
const { pipeline:
|
|
7842
|
+
const { pipeline: pipeline3, targets, levels, dedupe, worker = {}, caller = getCallers(), sync = false } = fullOptions;
|
|
7833
7843
|
const options = {
|
|
7834
7844
|
...fullOptions.options
|
|
7835
7845
|
};
|
|
@@ -7857,9 +7867,9 @@ var require_transport = __commonJS({
|
|
|
7857
7867
|
};
|
|
7858
7868
|
});
|
|
7859
7869
|
});
|
|
7860
|
-
} else if (
|
|
7870
|
+
} else if (pipeline3) {
|
|
7861
7871
|
target = bundlerOverrides["pino-worker"] || join11(__dirname, "worker.js");
|
|
7862
|
-
options.pipelines = [
|
|
7872
|
+
options.pipelines = [pipeline3.map((dest) => {
|
|
7863
7873
|
return {
|
|
7864
7874
|
...dest,
|
|
7865
7875
|
target: fixTarget(dest.target)
|
|
@@ -10233,11 +10243,11 @@ var init_lib = __esm({
|
|
|
10233
10243
|
}
|
|
10234
10244
|
}
|
|
10235
10245
|
},
|
|
10236
|
-
addToPath: function addToPath(
|
|
10237
|
-
var last =
|
|
10246
|
+
addToPath: function addToPath(path46, added, removed, oldPosInc, options) {
|
|
10247
|
+
var last = path46.lastComponent;
|
|
10238
10248
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
10239
10249
|
return {
|
|
10240
|
-
oldPos:
|
|
10250
|
+
oldPos: path46.oldPos + oldPosInc,
|
|
10241
10251
|
lastComponent: {
|
|
10242
10252
|
count: last.count + 1,
|
|
10243
10253
|
added,
|
|
@@ -10247,7 +10257,7 @@ var init_lib = __esm({
|
|
|
10247
10257
|
};
|
|
10248
10258
|
} else {
|
|
10249
10259
|
return {
|
|
10250
|
-
oldPos:
|
|
10260
|
+
oldPos: path46.oldPos + oldPosInc,
|
|
10251
10261
|
lastComponent: {
|
|
10252
10262
|
count: 1,
|
|
10253
10263
|
added,
|
|
@@ -10678,10 +10688,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
10678
10688
|
const memories = raw.map((m2) => {
|
|
10679
10689
|
if (!m2 || typeof m2 !== "object") return null;
|
|
10680
10690
|
const rec = m2;
|
|
10681
|
-
const
|
|
10691
|
+
const path46 = typeof rec.path === "string" ? rec.path : null;
|
|
10682
10692
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
10683
|
-
if (!
|
|
10684
|
-
const entry = { path:
|
|
10693
|
+
if (!path46 || content == null) return null;
|
|
10694
|
+
const entry = { path: path46, content };
|
|
10685
10695
|
if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
|
|
10686
10696
|
return entry;
|
|
10687
10697
|
}).filter((m2) => m2 !== null);
|
|
@@ -11507,10 +11517,10 @@ function parseAttachment(obj) {
|
|
|
11507
11517
|
const memories = raw.map((m2) => {
|
|
11508
11518
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11509
11519
|
const rec = m2;
|
|
11510
|
-
const
|
|
11520
|
+
const path46 = typeof rec.path === "string" ? rec.path : null;
|
|
11511
11521
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
11512
|
-
if (!
|
|
11513
|
-
const out = { path:
|
|
11522
|
+
if (!path46 || content == null) return null;
|
|
11523
|
+
const out = { path: path46, content };
|
|
11514
11524
|
if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
|
|
11515
11525
|
return out;
|
|
11516
11526
|
}).filter((m2) => m2 !== null);
|
|
@@ -20425,7 +20435,7 @@ var require_websocket_server = __commonJS({
|
|
|
20425
20435
|
// src/run-case/recorder.ts
|
|
20426
20436
|
function startRunCaseRecorder(opts) {
|
|
20427
20437
|
const now = opts.now ?? Date.now;
|
|
20428
|
-
const dir =
|
|
20438
|
+
const dir = import_node_path32.default.dirname(opts.recordPath);
|
|
20429
20439
|
let stream = null;
|
|
20430
20440
|
let closing = false;
|
|
20431
20441
|
let closedSettled = false;
|
|
@@ -20439,8 +20449,8 @@ function startRunCaseRecorder(opts) {
|
|
|
20439
20449
|
});
|
|
20440
20450
|
const ensureStream = () => {
|
|
20441
20451
|
if (stream) return stream;
|
|
20442
|
-
|
|
20443
|
-
stream =
|
|
20452
|
+
import_node_fs29.default.mkdirSync(dir, { recursive: true });
|
|
20453
|
+
stream = import_node_fs29.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
20444
20454
|
stream.on("close", () => closedResolve());
|
|
20445
20455
|
return stream;
|
|
20446
20456
|
};
|
|
@@ -20465,12 +20475,12 @@ function startRunCaseRecorder(opts) {
|
|
|
20465
20475
|
};
|
|
20466
20476
|
return { tap, close, closed };
|
|
20467
20477
|
}
|
|
20468
|
-
var
|
|
20478
|
+
var import_node_fs29, import_node_path32;
|
|
20469
20479
|
var init_recorder = __esm({
|
|
20470
20480
|
"src/run-case/recorder.ts"() {
|
|
20471
20481
|
"use strict";
|
|
20472
|
-
|
|
20473
|
-
|
|
20482
|
+
import_node_fs29 = __toESM(require("fs"), 1);
|
|
20483
|
+
import_node_path32 = __toESM(require("path"), 1);
|
|
20474
20484
|
}
|
|
20475
20485
|
});
|
|
20476
20486
|
|
|
@@ -20513,7 +20523,7 @@ var init_wire = __esm({
|
|
|
20513
20523
|
// src/run-case/controller.ts
|
|
20514
20524
|
async function runController(opts) {
|
|
20515
20525
|
const now = opts.now ?? Date.now;
|
|
20516
|
-
const cwd = opts.cwd ?? (0,
|
|
20526
|
+
const cwd = opts.cwd ?? (0, import_node_fs30.mkdtempSync)(import_node_path33.default.join(import_node_os15.default.tmpdir(), "clawd-runcase-"));
|
|
20517
20527
|
const ownsCwd = opts.cwd === void 0;
|
|
20518
20528
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
20519
20529
|
const spawnCtx = { cwd };
|
|
@@ -20674,19 +20684,19 @@ async function runController(opts) {
|
|
|
20674
20684
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
20675
20685
|
if (ownsCwd) {
|
|
20676
20686
|
try {
|
|
20677
|
-
(0,
|
|
20687
|
+
(0, import_node_fs30.rmSync)(cwd, { recursive: true, force: true });
|
|
20678
20688
|
} catch {
|
|
20679
20689
|
}
|
|
20680
20690
|
}
|
|
20681
20691
|
return exitCode ?? 0;
|
|
20682
20692
|
}
|
|
20683
|
-
var
|
|
20693
|
+
var import_node_fs30, import_node_os15, import_node_path33;
|
|
20684
20694
|
var init_controller = __esm({
|
|
20685
20695
|
"src/run-case/controller.ts"() {
|
|
20686
20696
|
"use strict";
|
|
20687
|
-
|
|
20697
|
+
import_node_fs30 = require("fs");
|
|
20688
20698
|
import_node_os15 = __toESM(require("os"), 1);
|
|
20689
|
-
|
|
20699
|
+
import_node_path33 = __toESM(require("path"), 1);
|
|
20690
20700
|
init_claude();
|
|
20691
20701
|
init_stdout_splitter();
|
|
20692
20702
|
init_permission_stdio();
|
|
@@ -20918,8 +20928,8 @@ Env (advanced):
|
|
|
20918
20928
|
`;
|
|
20919
20929
|
|
|
20920
20930
|
// src/index.ts
|
|
20921
|
-
var
|
|
20922
|
-
var
|
|
20931
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
20932
|
+
var import_node_fs28 = __toESM(require("fs"), 1);
|
|
20923
20933
|
|
|
20924
20934
|
// src/logger.ts
|
|
20925
20935
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
@@ -22654,6 +22664,18 @@ var SessionManager = class {
|
|
|
22654
22664
|
(a, b2) => a.updatedAt > b2.updatedAt ? -1 : a.updatedAt < b2.updatedAt ? 1 : 0
|
|
22655
22665
|
);
|
|
22656
22666
|
}
|
|
22667
|
+
/**
|
|
22668
|
+
* 跨 scope 列出所有 owner 视角的 session(default + persona owner + persona guest),
|
|
22669
|
+
* 附带派生的 SessionScope。attachment GC tick 用 —— 需要遍历所有活跃 session 的
|
|
22670
|
+
* .attachments 目录 + 群文件清单求 liveSet。
|
|
22671
|
+
*/
|
|
22672
|
+
listOwnedSessionScopes() {
|
|
22673
|
+
return this.listAllOwned().map((file) => ({
|
|
22674
|
+
scope: this.scopeForFile(file),
|
|
22675
|
+
sessionId: file.sessionId,
|
|
22676
|
+
cwd: file.cwd
|
|
22677
|
+
}));
|
|
22678
|
+
}
|
|
22657
22679
|
// 写回 SessionFile,自动根据 ownerPersonaId / creatorPrincipalId 派生写入 scope.
|
|
22658
22680
|
// 调用方原先都是 `deps.store.write(updated)`,全部走这里收口路由.
|
|
22659
22681
|
writeOwned(file) {
|
|
@@ -27170,8 +27192,8 @@ function rmdirIfEmpty(p2) {
|
|
|
27170
27192
|
}
|
|
27171
27193
|
|
|
27172
27194
|
// src/transport/http-router.ts
|
|
27173
|
-
var
|
|
27174
|
-
var
|
|
27195
|
+
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
27196
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
27175
27197
|
|
|
27176
27198
|
// src/attachment/mime.ts
|
|
27177
27199
|
var import_node_path16 = __toESM(require("path"), 1);
|
|
@@ -27349,7 +27371,137 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
|
|
|
27349
27371
|
return { ok: true, absPath };
|
|
27350
27372
|
}
|
|
27351
27373
|
|
|
27374
|
+
// src/attachment/upload.ts
|
|
27375
|
+
var import_node_fs15 = __toESM(require("fs"), 1);
|
|
27376
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
27377
|
+
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
27378
|
+
var import_promises = require("stream/promises");
|
|
27379
|
+
var UploadError = class extends Error {
|
|
27380
|
+
constructor(code, message) {
|
|
27381
|
+
super(message);
|
|
27382
|
+
this.code = code;
|
|
27383
|
+
this.name = "UploadError";
|
|
27384
|
+
}
|
|
27385
|
+
code;
|
|
27386
|
+
};
|
|
27387
|
+
function assertValidFileName(fileName) {
|
|
27388
|
+
if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path17.default.basename(fileName)) {
|
|
27389
|
+
throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
|
|
27390
|
+
}
|
|
27391
|
+
}
|
|
27392
|
+
var HASH_PREFIX_LEN = 16;
|
|
27393
|
+
async function writeUploadedAttachment(args) {
|
|
27394
|
+
assertValidFileName(args.fileName);
|
|
27395
|
+
const attachmentsRoot = import_node_path17.default.join(args.sessionDir, ".attachments");
|
|
27396
|
+
try {
|
|
27397
|
+
import_node_fs15.default.mkdirSync(attachmentsRoot, { recursive: true });
|
|
27398
|
+
} catch (err) {
|
|
27399
|
+
throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
|
|
27400
|
+
}
|
|
27401
|
+
const hasher = import_node_crypto6.default.createHash("sha256");
|
|
27402
|
+
let actualSize = 0;
|
|
27403
|
+
const tmpPath = import_node_path17.default.join(
|
|
27404
|
+
attachmentsRoot,
|
|
27405
|
+
`.upload-${process.pid}-${Date.now()}-${import_node_crypto6.default.randomBytes(4).toString("hex")}`
|
|
27406
|
+
);
|
|
27407
|
+
try {
|
|
27408
|
+
await (0, import_promises.pipeline)(
|
|
27409
|
+
args.body,
|
|
27410
|
+
async function* (source) {
|
|
27411
|
+
for await (const chunk of source) {
|
|
27412
|
+
const buf = chunk;
|
|
27413
|
+
actualSize += buf.length;
|
|
27414
|
+
hasher.update(buf);
|
|
27415
|
+
yield buf;
|
|
27416
|
+
}
|
|
27417
|
+
},
|
|
27418
|
+
import_node_fs15.default.createWriteStream(tmpPath, { mode: 384 })
|
|
27419
|
+
);
|
|
27420
|
+
} catch (err) {
|
|
27421
|
+
try {
|
|
27422
|
+
import_node_fs15.default.unlinkSync(tmpPath);
|
|
27423
|
+
} catch {
|
|
27424
|
+
}
|
|
27425
|
+
throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
|
|
27426
|
+
}
|
|
27427
|
+
if (actualSize !== args.contentLength) {
|
|
27428
|
+
try {
|
|
27429
|
+
import_node_fs15.default.unlinkSync(tmpPath);
|
|
27430
|
+
} catch {
|
|
27431
|
+
}
|
|
27432
|
+
throw new UploadError(
|
|
27433
|
+
"SIZE_MISMATCH",
|
|
27434
|
+
`expected ${args.contentLength} bytes, got ${actualSize}`
|
|
27435
|
+
);
|
|
27436
|
+
}
|
|
27437
|
+
const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
|
|
27438
|
+
const hashDir = import_node_path17.default.join(attachmentsRoot, attachmentId);
|
|
27439
|
+
let finalFileName;
|
|
27440
|
+
let hashDirExists = false;
|
|
27441
|
+
try {
|
|
27442
|
+
hashDirExists = import_node_fs15.default.statSync(hashDir).isDirectory();
|
|
27443
|
+
} catch {
|
|
27444
|
+
}
|
|
27445
|
+
if (hashDirExists) {
|
|
27446
|
+
const existing = import_node_fs15.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
|
|
27447
|
+
finalFileName = existing[0] ?? args.fileName;
|
|
27448
|
+
try {
|
|
27449
|
+
import_node_fs15.default.unlinkSync(tmpPath);
|
|
27450
|
+
} catch {
|
|
27451
|
+
}
|
|
27452
|
+
} else {
|
|
27453
|
+
try {
|
|
27454
|
+
import_node_fs15.default.mkdirSync(hashDir, { recursive: true });
|
|
27455
|
+
finalFileName = args.fileName;
|
|
27456
|
+
import_node_fs15.default.renameSync(tmpPath, import_node_path17.default.join(hashDir, finalFileName));
|
|
27457
|
+
} catch (err) {
|
|
27458
|
+
try {
|
|
27459
|
+
import_node_fs15.default.unlinkSync(tmpPath);
|
|
27460
|
+
} catch {
|
|
27461
|
+
}
|
|
27462
|
+
throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
|
|
27463
|
+
}
|
|
27464
|
+
}
|
|
27465
|
+
const absPath = import_node_path17.default.join(hashDir, finalFileName);
|
|
27466
|
+
const relPath = import_node_path17.default.relative(args.sessionDir, absPath);
|
|
27467
|
+
args.groupFileStore.upsert(args.scope, args.sessionId, {
|
|
27468
|
+
relPath: absPath,
|
|
27469
|
+
// 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
|
|
27470
|
+
from: "user-upload",
|
|
27471
|
+
label: args.fileName,
|
|
27472
|
+
size: actualSize,
|
|
27473
|
+
mime: args.mimeType
|
|
27474
|
+
});
|
|
27475
|
+
return { attachmentId, absPath, relPath, sizeBytes: actualSize, mimeType: args.mimeType };
|
|
27476
|
+
}
|
|
27477
|
+
|
|
27352
27478
|
// src/transport/http-router.ts
|
|
27479
|
+
var UPLOAD_MIME_ALLOW = /* @__PURE__ */ new Set([
|
|
27480
|
+
"image/png",
|
|
27481
|
+
"image/jpeg",
|
|
27482
|
+
"image/gif",
|
|
27483
|
+
"image/webp",
|
|
27484
|
+
"application/pdf"
|
|
27485
|
+
]);
|
|
27486
|
+
var UPLOAD_MIME_ALLOW_PREFIX = ["text/"];
|
|
27487
|
+
var UPLOAD_MAX_BYTES = 25 * 1024 * 1024;
|
|
27488
|
+
var UPLOAD_CORS_HEADERS = {
|
|
27489
|
+
"Access-Control-Allow-Origin": "*",
|
|
27490
|
+
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
|
27491
|
+
"Access-Control-Allow-Headers": "Authorization, Content-Type",
|
|
27492
|
+
"Access-Control-Max-Age": "86400"
|
|
27493
|
+
};
|
|
27494
|
+
function isUploadMimeAllowed(mime) {
|
|
27495
|
+
if (UPLOAD_MIME_ALLOW.has(mime)) return true;
|
|
27496
|
+
return UPLOAD_MIME_ALLOW_PREFIX.some((p2) => mime.startsWith(p2));
|
|
27497
|
+
}
|
|
27498
|
+
function isValidUploadFileName(fileName) {
|
|
27499
|
+
if (fileName.length === 0) return false;
|
|
27500
|
+
if (fileName === "." || fileName === "..") return false;
|
|
27501
|
+
if (fileName.startsWith(".")) return false;
|
|
27502
|
+
if (fileName.includes("/") || fileName.includes("\\")) return false;
|
|
27503
|
+
return fileName === import_node_path18.default.basename(fileName);
|
|
27504
|
+
}
|
|
27353
27505
|
function createHttpRouter(deps) {
|
|
27354
27506
|
return async (req, res) => {
|
|
27355
27507
|
const url = parseUrl(req.url);
|
|
@@ -27363,9 +27515,14 @@ function createHttpRouter(deps) {
|
|
|
27363
27515
|
});
|
|
27364
27516
|
return true;
|
|
27365
27517
|
}
|
|
27366
|
-
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/")) {
|
|
27518
|
+
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/") && url.pathname !== "/attachments/upload") {
|
|
27367
27519
|
return false;
|
|
27368
27520
|
}
|
|
27521
|
+
if (url.pathname === "/attachments/upload" && req.method === "OPTIONS") {
|
|
27522
|
+
res.writeHead(204, UPLOAD_CORS_HEADERS);
|
|
27523
|
+
res.end();
|
|
27524
|
+
return true;
|
|
27525
|
+
}
|
|
27369
27526
|
if (url.pathname.startsWith("/files/") && req.method === "GET") {
|
|
27370
27527
|
const secret = deps.getSignSecret?.();
|
|
27371
27528
|
if (!secret) {
|
|
@@ -27403,6 +27560,107 @@ function createHttpRouter(deps) {
|
|
|
27403
27560
|
sendJson(res, 401, { code: "UNAUTHORIZED", message: "missing or invalid bearer token" });
|
|
27404
27561
|
return true;
|
|
27405
27562
|
}
|
|
27563
|
+
if (url.pathname === "/attachments/upload" && req.method === "POST") {
|
|
27564
|
+
const sendUploadJson = (status, body) => sendJson(res, status, body, UPLOAD_CORS_HEADERS);
|
|
27565
|
+
if (ctx.role !== "owner") {
|
|
27566
|
+
sendUploadJson(403, { code: "FORBIDDEN", message: "owner only" });
|
|
27567
|
+
return true;
|
|
27568
|
+
}
|
|
27569
|
+
const up = deps.attachmentUpload;
|
|
27570
|
+
if (!up) {
|
|
27571
|
+
sendUploadJson(501, { code: "NOT_IMPLEMENTED", message: "attachment upload not wired" });
|
|
27572
|
+
return true;
|
|
27573
|
+
}
|
|
27574
|
+
const sessionId = url.searchParams.get("sessionId");
|
|
27575
|
+
const fileName = url.searchParams.get("fileName");
|
|
27576
|
+
const mimeType = url.searchParams.get("mimeType");
|
|
27577
|
+
const contentLengthRaw = req.headers["content-length"];
|
|
27578
|
+
if (!sessionId || !fileName || !mimeType || !contentLengthRaw) {
|
|
27579
|
+
sendUploadJson(400, {
|
|
27580
|
+
code: "INVALID_PARAM",
|
|
27581
|
+
message: "missing sessionId/fileName/mimeType/Content-Length"
|
|
27582
|
+
});
|
|
27583
|
+
return true;
|
|
27584
|
+
}
|
|
27585
|
+
if (!isValidUploadFileName(fileName)) {
|
|
27586
|
+
sendUploadJson(400, {
|
|
27587
|
+
code: "INVALID_FILENAME",
|
|
27588
|
+
message: `fileName must be a plain basename (no path separators, no dotfiles): ${fileName}`
|
|
27589
|
+
});
|
|
27590
|
+
return true;
|
|
27591
|
+
}
|
|
27592
|
+
const contentLength = Number.parseInt(contentLengthRaw, 10);
|
|
27593
|
+
if (!Number.isFinite(contentLength) || contentLength < 0) {
|
|
27594
|
+
sendUploadJson(400, { code: "INVALID_PARAM", message: "malformed Content-Length" });
|
|
27595
|
+
return true;
|
|
27596
|
+
}
|
|
27597
|
+
if (contentLength > UPLOAD_MAX_BYTES) {
|
|
27598
|
+
sendUploadJson(413, {
|
|
27599
|
+
code: "FILE_TOO_LARGE",
|
|
27600
|
+
message: `max ${UPLOAD_MAX_BYTES} bytes`
|
|
27601
|
+
});
|
|
27602
|
+
return true;
|
|
27603
|
+
}
|
|
27604
|
+
if (!isUploadMimeAllowed(mimeType)) {
|
|
27605
|
+
sendUploadJson(400, {
|
|
27606
|
+
code: "UNSUPPORTED_MIME",
|
|
27607
|
+
message: `mime ${mimeType} not allowed`
|
|
27608
|
+
});
|
|
27609
|
+
return true;
|
|
27610
|
+
}
|
|
27611
|
+
const sessionDir = up.getSessionDir(sessionId);
|
|
27612
|
+
const scope = up.getSessionScope(sessionId);
|
|
27613
|
+
if (!sessionDir || !scope) {
|
|
27614
|
+
sendUploadJson(404, { code: "NOT_FOUND", message: `session ${sessionId} not found` });
|
|
27615
|
+
return true;
|
|
27616
|
+
}
|
|
27617
|
+
const httpBaseUrl = up.getHttpBaseUrl();
|
|
27618
|
+
if (!httpBaseUrl) {
|
|
27619
|
+
sendUploadJson(503, {
|
|
27620
|
+
code: "TUNNEL_NOT_READY",
|
|
27621
|
+
message: "httpBaseUrl unavailable"
|
|
27622
|
+
});
|
|
27623
|
+
return true;
|
|
27624
|
+
}
|
|
27625
|
+
const secret = deps.getSignSecret?.();
|
|
27626
|
+
if (!secret) {
|
|
27627
|
+
sendUploadJson(501, {
|
|
27628
|
+
code: "NOT_IMPLEMENTED",
|
|
27629
|
+
message: "sign secret unavailable (noAuth?)"
|
|
27630
|
+
});
|
|
27631
|
+
return true;
|
|
27632
|
+
}
|
|
27633
|
+
try {
|
|
27634
|
+
const result = await writeUploadedAttachment({
|
|
27635
|
+
sessionDir,
|
|
27636
|
+
sessionId,
|
|
27637
|
+
scope,
|
|
27638
|
+
fileName,
|
|
27639
|
+
mimeType,
|
|
27640
|
+
contentLength,
|
|
27641
|
+
body: req,
|
|
27642
|
+
groupFileStore: up.groupFileStore
|
|
27643
|
+
});
|
|
27644
|
+
const parts = signUrlParts(secret, result.absPath, null);
|
|
27645
|
+
const fullUrl = buildSignedFileUrl(httpBaseUrl, parts);
|
|
27646
|
+
sendUploadJson(200, {
|
|
27647
|
+
attachmentId: result.attachmentId,
|
|
27648
|
+
url: fullUrl,
|
|
27649
|
+
relPath: result.relPath,
|
|
27650
|
+
sizeBytes: result.sizeBytes,
|
|
27651
|
+
mimeType: result.mimeType
|
|
27652
|
+
});
|
|
27653
|
+
} catch (err) {
|
|
27654
|
+
if (err instanceof UploadError) {
|
|
27655
|
+
const status = err.code === "SIZE_MISMATCH" ? 400 : 500;
|
|
27656
|
+
sendUploadJson(status, { code: err.code, message: err.message });
|
|
27657
|
+
} else {
|
|
27658
|
+
deps.logger?.warn("attachment upload failed", { err: err.message });
|
|
27659
|
+
sendUploadJson(500, { code: "STORAGE_ERROR", message: err.message });
|
|
27660
|
+
}
|
|
27661
|
+
}
|
|
27662
|
+
return true;
|
|
27663
|
+
}
|
|
27406
27664
|
const sessionFilesMatch = url.pathname.match(/^\/session\/([^/]+)\/files$/);
|
|
27407
27665
|
if (sessionFilesMatch && req.method === "GET") {
|
|
27408
27666
|
const sid = sessionFilesMatch[1];
|
|
@@ -27412,7 +27670,7 @@ function createHttpRouter(deps) {
|
|
|
27412
27670
|
return true;
|
|
27413
27671
|
}
|
|
27414
27672
|
let absPath;
|
|
27415
|
-
if (
|
|
27673
|
+
if (import_node_path18.default.isAbsolute(pathParam)) {
|
|
27416
27674
|
absPath = pathParam;
|
|
27417
27675
|
} else if (deps.sessionStore) {
|
|
27418
27676
|
const file = deps.sessionStore.read(sid);
|
|
@@ -27420,7 +27678,7 @@ function createHttpRouter(deps) {
|
|
|
27420
27678
|
sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
|
|
27421
27679
|
return true;
|
|
27422
27680
|
}
|
|
27423
|
-
absPath =
|
|
27681
|
+
absPath = import_node_path18.default.join(file.cwd, pathParam);
|
|
27424
27682
|
} else {
|
|
27425
27683
|
sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
|
|
27426
27684
|
return true;
|
|
@@ -27453,7 +27711,7 @@ function withCtx(ctx, body) {
|
|
|
27453
27711
|
function streamFile(res, absPath, logger) {
|
|
27454
27712
|
let stat;
|
|
27455
27713
|
try {
|
|
27456
|
-
stat =
|
|
27714
|
+
stat = import_node_fs16.default.statSync(absPath);
|
|
27457
27715
|
} catch (err) {
|
|
27458
27716
|
const code = err?.code;
|
|
27459
27717
|
if (code === "ENOENT") {
|
|
@@ -27468,7 +27726,7 @@ function streamFile(res, absPath, logger) {
|
|
|
27468
27726
|
return;
|
|
27469
27727
|
}
|
|
27470
27728
|
const mime = lookupMime(absPath);
|
|
27471
|
-
const basename =
|
|
27729
|
+
const basename = import_node_path18.default.basename(absPath);
|
|
27472
27730
|
res.writeHead(200, {
|
|
27473
27731
|
"Content-Type": mime,
|
|
27474
27732
|
"Content-Length": String(stat.size),
|
|
@@ -27476,7 +27734,7 @@ function streamFile(res, absPath, logger) {
|
|
|
27476
27734
|
// 防止浏览器把任意 mime 当 html 渲染
|
|
27477
27735
|
"X-Content-Type-Options": "nosniff"
|
|
27478
27736
|
});
|
|
27479
|
-
const stream =
|
|
27737
|
+
const stream = import_node_fs16.default.createReadStream(absPath);
|
|
27480
27738
|
stream.on("error", (err) => {
|
|
27481
27739
|
logger?.warn("streamFile read error", { absPath, err: err.message });
|
|
27482
27740
|
res.destroy();
|
|
@@ -27484,10 +27742,78 @@ function streamFile(res, absPath, logger) {
|
|
|
27484
27742
|
stream.pipe(res);
|
|
27485
27743
|
}
|
|
27486
27744
|
|
|
27745
|
+
// src/attachment/gc.ts
|
|
27746
|
+
var import_node_fs17 = __toESM(require("fs"), 1);
|
|
27747
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
27748
|
+
var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
|
|
27749
|
+
function runAttachmentGc(args) {
|
|
27750
|
+
const now = (args.now ?? Date.now)();
|
|
27751
|
+
const ttlMs = args.ttlMs ?? DEFAULT_TTL_MS;
|
|
27752
|
+
const liveAbs = /* @__PURE__ */ new Set();
|
|
27753
|
+
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27754
|
+
for (const entry of args.groupFileStore.list(scope, sessionId)) {
|
|
27755
|
+
if (entry.stale) continue;
|
|
27756
|
+
if (import_node_path19.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
|
|
27757
|
+
}
|
|
27758
|
+
}
|
|
27759
|
+
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27760
|
+
const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path19.default.join(
|
|
27761
|
+
args.dataDir,
|
|
27762
|
+
"sessions",
|
|
27763
|
+
...scopeSubPath(scope).map(safeFileName),
|
|
27764
|
+
safeFileName(sessionId)
|
|
27765
|
+
);
|
|
27766
|
+
const attRoot = import_node_path19.default.join(sessionDir, ".attachments");
|
|
27767
|
+
let hashDirs;
|
|
27768
|
+
try {
|
|
27769
|
+
hashDirs = import_node_fs17.default.readdirSync(attRoot);
|
|
27770
|
+
} catch (err) {
|
|
27771
|
+
if (err.code === "ENOENT") continue;
|
|
27772
|
+
args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
|
|
27773
|
+
continue;
|
|
27774
|
+
}
|
|
27775
|
+
for (const hashDir of hashDirs) {
|
|
27776
|
+
const hashDirAbs = import_node_path19.default.join(attRoot, hashDir);
|
|
27777
|
+
let files;
|
|
27778
|
+
try {
|
|
27779
|
+
files = import_node_fs17.default.readdirSync(hashDirAbs);
|
|
27780
|
+
} catch {
|
|
27781
|
+
continue;
|
|
27782
|
+
}
|
|
27783
|
+
for (const name of files) {
|
|
27784
|
+
const file = import_node_path19.default.join(hashDirAbs, name);
|
|
27785
|
+
let stat;
|
|
27786
|
+
try {
|
|
27787
|
+
stat = import_node_fs17.default.statSync(file);
|
|
27788
|
+
} catch {
|
|
27789
|
+
continue;
|
|
27790
|
+
}
|
|
27791
|
+
if (!stat.isFile()) continue;
|
|
27792
|
+
const age = now - stat.mtimeMs;
|
|
27793
|
+
if (age < ttlMs) continue;
|
|
27794
|
+
if (liveAbs.has(file)) continue;
|
|
27795
|
+
try {
|
|
27796
|
+
import_node_fs17.default.unlinkSync(file);
|
|
27797
|
+
} catch (err) {
|
|
27798
|
+
args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
|
|
27799
|
+
}
|
|
27800
|
+
}
|
|
27801
|
+
try {
|
|
27802
|
+
if (import_node_fs17.default.readdirSync(hashDirAbs).length === 0) import_node_fs17.default.rmdirSync(hashDirAbs);
|
|
27803
|
+
} catch {
|
|
27804
|
+
}
|
|
27805
|
+
}
|
|
27806
|
+
try {
|
|
27807
|
+
if (import_node_fs17.default.readdirSync(attRoot).length === 0) import_node_fs17.default.rmdirSync(attRoot);
|
|
27808
|
+
} catch {
|
|
27809
|
+
}
|
|
27810
|
+
}
|
|
27811
|
+
}
|
|
27812
|
+
|
|
27487
27813
|
// src/attachment/group.ts
|
|
27488
|
-
var
|
|
27489
|
-
var
|
|
27490
|
-
var
|
|
27814
|
+
var import_node_fs18 = __toESM(require("fs"), 1);
|
|
27815
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
27816
|
+
var import_node_crypto7 = __toESM(require("crypto"), 1);
|
|
27491
27817
|
init_protocol();
|
|
27492
27818
|
var GroupFileStore = class {
|
|
27493
27819
|
dataDir;
|
|
@@ -27498,11 +27824,11 @@ var GroupFileStore = class {
|
|
|
27498
27824
|
this.logger = opts.logger;
|
|
27499
27825
|
}
|
|
27500
27826
|
rootForScope(scope) {
|
|
27501
|
-
return
|
|
27827
|
+
return import_node_path20.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
|
|
27502
27828
|
}
|
|
27503
27829
|
/** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
|
|
27504
27830
|
filePath(scope, sessionId) {
|
|
27505
|
-
return
|
|
27831
|
+
return import_node_path20.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
|
|
27506
27832
|
}
|
|
27507
27833
|
cacheKey(scope, sessionId) {
|
|
27508
27834
|
return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
|
|
@@ -27511,7 +27837,7 @@ var GroupFileStore = class {
|
|
|
27511
27837
|
readFile(scope, sessionId) {
|
|
27512
27838
|
const file = this.filePath(scope, sessionId);
|
|
27513
27839
|
try {
|
|
27514
|
-
const raw =
|
|
27840
|
+
const raw = import_node_fs18.default.readFileSync(file, "utf8");
|
|
27515
27841
|
const parsed = JSON.parse(raw);
|
|
27516
27842
|
if (!Array.isArray(parsed)) {
|
|
27517
27843
|
this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
|
|
@@ -27537,10 +27863,10 @@ var GroupFileStore = class {
|
|
|
27537
27863
|
}
|
|
27538
27864
|
writeFile(scope, sessionId, entries) {
|
|
27539
27865
|
const file = this.filePath(scope, sessionId);
|
|
27540
|
-
|
|
27866
|
+
import_node_fs18.default.mkdirSync(import_node_path20.default.dirname(file), { recursive: true });
|
|
27541
27867
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
27542
|
-
|
|
27543
|
-
|
|
27868
|
+
import_node_fs18.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
|
|
27869
|
+
import_node_fs18.default.renameSync(tmp, file);
|
|
27544
27870
|
}
|
|
27545
27871
|
/** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
|
|
27546
27872
|
list(scope, sessionId) {
|
|
@@ -27576,7 +27902,7 @@ var GroupFileStore = class {
|
|
|
27576
27902
|
entries[idx] = next;
|
|
27577
27903
|
} else {
|
|
27578
27904
|
next = {
|
|
27579
|
-
id: `gf-${
|
|
27905
|
+
id: `gf-${import_node_crypto7.default.randomBytes(6).toString("base64url")}`,
|
|
27580
27906
|
relPath: input.relPath,
|
|
27581
27907
|
from: input.from,
|
|
27582
27908
|
label: input.label,
|
|
@@ -27626,10 +27952,10 @@ var GroupFileStore = class {
|
|
|
27626
27952
|
};
|
|
27627
27953
|
|
|
27628
27954
|
// src/discovery/state-file.ts
|
|
27629
|
-
var
|
|
27630
|
-
var
|
|
27955
|
+
var import_node_fs19 = __toESM(require("fs"), 1);
|
|
27956
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
27631
27957
|
function defaultStateFilePath(dataDir) {
|
|
27632
|
-
return
|
|
27958
|
+
return import_node_path21.default.join(dataDir, "state.json");
|
|
27633
27959
|
}
|
|
27634
27960
|
function isPidAlive(pid) {
|
|
27635
27961
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
@@ -27651,7 +27977,7 @@ var StateFileManager = class {
|
|
|
27651
27977
|
}
|
|
27652
27978
|
read() {
|
|
27653
27979
|
try {
|
|
27654
|
-
const raw =
|
|
27980
|
+
const raw = import_node_fs19.default.readFileSync(this.file, "utf8");
|
|
27655
27981
|
const parsed = JSON.parse(raw);
|
|
27656
27982
|
return parsed;
|
|
27657
27983
|
} catch {
|
|
@@ -27665,34 +27991,34 @@ var StateFileManager = class {
|
|
|
27665
27991
|
return { status: "stale", existing };
|
|
27666
27992
|
}
|
|
27667
27993
|
write(state) {
|
|
27668
|
-
|
|
27994
|
+
import_node_fs19.default.mkdirSync(import_node_path21.default.dirname(this.file), { recursive: true });
|
|
27669
27995
|
const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
|
|
27670
|
-
|
|
27671
|
-
|
|
27996
|
+
import_node_fs19.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
|
|
27997
|
+
import_node_fs19.default.renameSync(tmp, this.file);
|
|
27672
27998
|
if (process.platform !== "win32") {
|
|
27673
27999
|
try {
|
|
27674
|
-
|
|
28000
|
+
import_node_fs19.default.chmodSync(this.file, 384);
|
|
27675
28001
|
} catch {
|
|
27676
28002
|
}
|
|
27677
28003
|
}
|
|
27678
28004
|
}
|
|
27679
28005
|
delete() {
|
|
27680
28006
|
try {
|
|
27681
|
-
|
|
28007
|
+
import_node_fs19.default.unlinkSync(this.file);
|
|
27682
28008
|
} catch {
|
|
27683
28009
|
}
|
|
27684
28010
|
}
|
|
27685
28011
|
};
|
|
27686
28012
|
|
|
27687
28013
|
// src/tunnel/tunnel-manager.ts
|
|
27688
|
-
var
|
|
27689
|
-
var
|
|
27690
|
-
var
|
|
28014
|
+
var import_node_fs23 = __toESM(require("fs"), 1);
|
|
28015
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
28016
|
+
var import_node_crypto8 = __toESM(require("crypto"), 1);
|
|
27691
28017
|
var import_node_child_process5 = require("child_process");
|
|
27692
28018
|
|
|
27693
28019
|
// src/tunnel/tunnel-store.ts
|
|
27694
|
-
var
|
|
27695
|
-
var
|
|
28020
|
+
var import_node_fs20 = __toESM(require("fs"), 1);
|
|
28021
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
27696
28022
|
var TunnelStore = class {
|
|
27697
28023
|
constructor(filePath) {
|
|
27698
28024
|
this.filePath = filePath;
|
|
@@ -27700,7 +28026,7 @@ var TunnelStore = class {
|
|
|
27700
28026
|
filePath;
|
|
27701
28027
|
async get() {
|
|
27702
28028
|
try {
|
|
27703
|
-
const raw = await
|
|
28029
|
+
const raw = await import_node_fs20.default.promises.readFile(this.filePath, "utf8");
|
|
27704
28030
|
const obj = JSON.parse(raw);
|
|
27705
28031
|
if (!isPersistedTunnel(obj)) return null;
|
|
27706
28032
|
return obj;
|
|
@@ -27711,22 +28037,22 @@ var TunnelStore = class {
|
|
|
27711
28037
|
}
|
|
27712
28038
|
}
|
|
27713
28039
|
async set(v2) {
|
|
27714
|
-
const dir =
|
|
27715
|
-
await
|
|
28040
|
+
const dir = import_node_path22.default.dirname(this.filePath);
|
|
28041
|
+
await import_node_fs20.default.promises.mkdir(dir, { recursive: true });
|
|
27716
28042
|
const data = JSON.stringify(v2, null, 2);
|
|
27717
28043
|
const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
27718
|
-
await
|
|
28044
|
+
await import_node_fs20.default.promises.writeFile(tmp, data, { mode: 384 });
|
|
27719
28045
|
if (process.platform !== "win32") {
|
|
27720
28046
|
try {
|
|
27721
|
-
await
|
|
28047
|
+
await import_node_fs20.default.promises.chmod(tmp, 384);
|
|
27722
28048
|
} catch {
|
|
27723
28049
|
}
|
|
27724
28050
|
}
|
|
27725
|
-
await
|
|
28051
|
+
await import_node_fs20.default.promises.rename(tmp, this.filePath);
|
|
27726
28052
|
}
|
|
27727
28053
|
async clear() {
|
|
27728
28054
|
try {
|
|
27729
|
-
await
|
|
28055
|
+
await import_node_fs20.default.promises.unlink(this.filePath);
|
|
27730
28056
|
} catch (err) {
|
|
27731
28057
|
const code = err?.code;
|
|
27732
28058
|
if (code !== "ENOENT") throw err;
|
|
@@ -27821,12 +28147,12 @@ function escape(v2) {
|
|
|
27821
28147
|
}
|
|
27822
28148
|
|
|
27823
28149
|
// src/tunnel/frpc-binary.ts
|
|
27824
|
-
var
|
|
28150
|
+
var import_node_fs21 = __toESM(require("fs"), 1);
|
|
27825
28151
|
var import_node_os10 = __toESM(require("os"), 1);
|
|
27826
|
-
var
|
|
28152
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
27827
28153
|
var import_node_child_process3 = require("child_process");
|
|
27828
28154
|
var import_node_stream2 = require("stream");
|
|
27829
|
-
var
|
|
28155
|
+
var import_promises2 = require("stream/promises");
|
|
27830
28156
|
var FRPC_VERSION = "0.68.0";
|
|
27831
28157
|
var UnsupportedPlatformError = class extends Error {
|
|
27832
28158
|
constructor(platform, arch) {
|
|
@@ -27855,20 +28181,20 @@ function frpcDownloadUrl(version2, p2) {
|
|
|
27855
28181
|
}
|
|
27856
28182
|
async function ensureFrpcBinary(opts) {
|
|
27857
28183
|
if (opts.override) {
|
|
27858
|
-
if (!
|
|
28184
|
+
if (!import_node_fs21.default.existsSync(opts.override)) {
|
|
27859
28185
|
throw new Error(`frpc binary not found at override path: ${opts.override}`);
|
|
27860
28186
|
}
|
|
27861
28187
|
return opts.override;
|
|
27862
28188
|
}
|
|
27863
28189
|
const version2 = opts.version ?? FRPC_VERSION;
|
|
27864
28190
|
const platform = opts.platform ?? detectPlatform();
|
|
27865
|
-
const binDir =
|
|
27866
|
-
|
|
28191
|
+
const binDir = import_node_path23.default.join(opts.dataDir, "bin");
|
|
28192
|
+
import_node_fs21.default.mkdirSync(binDir, { recursive: true });
|
|
27867
28193
|
cleanupStaleArtifacts(binDir);
|
|
27868
|
-
const stableBin =
|
|
27869
|
-
if (
|
|
28194
|
+
const stableBin = import_node_path23.default.join(binDir, "frpc");
|
|
28195
|
+
if (import_node_fs21.default.existsSync(stableBin)) return stableBin;
|
|
27870
28196
|
const partialBin = `${stableBin}.partial`;
|
|
27871
|
-
const tarballPath =
|
|
28197
|
+
const tarballPath = import_node_path23.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
|
|
27872
28198
|
try {
|
|
27873
28199
|
const url = frpcDownloadUrl(version2, platform);
|
|
27874
28200
|
await downloadToFile(url, tarballPath, opts.fetchImpl);
|
|
@@ -27877,8 +28203,8 @@ async function ensureFrpcBinary(opts) {
|
|
|
27877
28203
|
} else {
|
|
27878
28204
|
await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
|
|
27879
28205
|
}
|
|
27880
|
-
|
|
27881
|
-
|
|
28206
|
+
import_node_fs21.default.chmodSync(partialBin, 493);
|
|
28207
|
+
import_node_fs21.default.renameSync(partialBin, stableBin);
|
|
27882
28208
|
} finally {
|
|
27883
28209
|
safeUnlink(tarballPath);
|
|
27884
28210
|
safeUnlink(partialBin);
|
|
@@ -27888,15 +28214,15 @@ async function ensureFrpcBinary(opts) {
|
|
|
27888
28214
|
function cleanupStaleArtifacts(binDir) {
|
|
27889
28215
|
let entries;
|
|
27890
28216
|
try {
|
|
27891
|
-
entries =
|
|
28217
|
+
entries = import_node_fs21.default.readdirSync(binDir);
|
|
27892
28218
|
} catch {
|
|
27893
28219
|
return;
|
|
27894
28220
|
}
|
|
27895
28221
|
for (const name of entries) {
|
|
27896
28222
|
if (name.endsWith(".partial") || name.startsWith("extract-")) {
|
|
27897
|
-
const full =
|
|
28223
|
+
const full = import_node_path23.default.join(binDir, name);
|
|
27898
28224
|
try {
|
|
27899
|
-
|
|
28225
|
+
import_node_fs21.default.rmSync(full, { recursive: true, force: true });
|
|
27900
28226
|
} catch {
|
|
27901
28227
|
}
|
|
27902
28228
|
}
|
|
@@ -27904,7 +28230,7 @@ function cleanupStaleArtifacts(binDir) {
|
|
|
27904
28230
|
}
|
|
27905
28231
|
function safeUnlink(p2) {
|
|
27906
28232
|
try {
|
|
27907
|
-
|
|
28233
|
+
import_node_fs21.default.unlinkSync(p2);
|
|
27908
28234
|
} catch {
|
|
27909
28235
|
}
|
|
27910
28236
|
}
|
|
@@ -27915,13 +28241,13 @@ async function downloadToFile(url, dest, fetchImpl) {
|
|
|
27915
28241
|
if (!res.ok || !res.body) {
|
|
27916
28242
|
throw new Error(`download failed: ${res.status} ${res.statusText}`);
|
|
27917
28243
|
}
|
|
27918
|
-
const out =
|
|
28244
|
+
const out = import_node_fs21.default.createWriteStream(dest);
|
|
27919
28245
|
const nodeStream = import_node_stream2.Readable.fromWeb(res.body);
|
|
27920
|
-
await (0,
|
|
28246
|
+
await (0, import_promises2.pipeline)(nodeStream, out);
|
|
27921
28247
|
}
|
|
27922
28248
|
async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
|
|
27923
|
-
const work =
|
|
27924
|
-
|
|
28249
|
+
const work = import_node_path23.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
|
|
28250
|
+
import_node_fs21.default.mkdirSync(work, { recursive: true });
|
|
27925
28251
|
try {
|
|
27926
28252
|
await new Promise((resolve6, reject) => {
|
|
27927
28253
|
const proc = (0, import_node_child_process3.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
|
|
@@ -27929,32 +28255,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
|
|
|
27929
28255
|
proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
|
|
27930
28256
|
});
|
|
27931
28257
|
const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
|
|
27932
|
-
const src =
|
|
27933
|
-
if (!
|
|
28258
|
+
const src = import_node_path23.default.join(work, dirName, "frpc");
|
|
28259
|
+
if (!import_node_fs21.default.existsSync(src)) {
|
|
27934
28260
|
throw new Error(`frpc not found inside tarball at ${src}`);
|
|
27935
28261
|
}
|
|
27936
|
-
|
|
28262
|
+
import_node_fs21.default.copyFileSync(src, destBin);
|
|
27937
28263
|
} finally {
|
|
27938
|
-
|
|
28264
|
+
import_node_fs21.default.rmSync(work, { recursive: true, force: true });
|
|
27939
28265
|
}
|
|
27940
28266
|
}
|
|
27941
28267
|
|
|
27942
28268
|
// src/tunnel/frpc-process.ts
|
|
27943
|
-
var
|
|
27944
|
-
var
|
|
28269
|
+
var import_node_fs22 = __toESM(require("fs"), 1);
|
|
28270
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
27945
28271
|
var import_node_child_process4 = require("child_process");
|
|
27946
28272
|
function frpcPidFilePath(dataDir) {
|
|
27947
|
-
return
|
|
28273
|
+
return import_node_path24.default.join(dataDir, "frpc.pid");
|
|
27948
28274
|
}
|
|
27949
28275
|
function writeFrpcPid(dataDir, pid) {
|
|
27950
28276
|
try {
|
|
27951
|
-
|
|
28277
|
+
import_node_fs22.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
|
|
27952
28278
|
} catch {
|
|
27953
28279
|
}
|
|
27954
28280
|
}
|
|
27955
28281
|
function clearFrpcPid(dataDir) {
|
|
27956
28282
|
try {
|
|
27957
|
-
|
|
28283
|
+
import_node_fs22.default.unlinkSync(frpcPidFilePath(dataDir));
|
|
27958
28284
|
} catch {
|
|
27959
28285
|
}
|
|
27960
28286
|
}
|
|
@@ -27970,7 +28296,7 @@ function defaultIsPidAlive(pid) {
|
|
|
27970
28296
|
}
|
|
27971
28297
|
function defaultReadPidFile(file) {
|
|
27972
28298
|
try {
|
|
27973
|
-
return
|
|
28299
|
+
return import_node_fs22.default.readFileSync(file, "utf8");
|
|
27974
28300
|
} catch {
|
|
27975
28301
|
return null;
|
|
27976
28302
|
}
|
|
@@ -27986,7 +28312,7 @@ function defaultSleep(ms) {
|
|
|
27986
28312
|
}
|
|
27987
28313
|
async function killStaleFrpc(deps) {
|
|
27988
28314
|
const pidFile = frpcPidFilePath(deps.dataDir);
|
|
27989
|
-
const tomlPath =
|
|
28315
|
+
const tomlPath = import_node_path24.default.join(deps.dataDir, "frpc.toml");
|
|
27990
28316
|
const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
|
|
27991
28317
|
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
|
|
27992
28318
|
const killPid = deps.killPidImpl ?? defaultKillPid;
|
|
@@ -28010,7 +28336,7 @@ async function killStaleFrpc(deps) {
|
|
|
28010
28336
|
}
|
|
28011
28337
|
if (victims.size === 0) {
|
|
28012
28338
|
try {
|
|
28013
|
-
|
|
28339
|
+
import_node_fs22.default.unlinkSync(pidFile);
|
|
28014
28340
|
} catch {
|
|
28015
28341
|
}
|
|
28016
28342
|
return;
|
|
@@ -28021,7 +28347,7 @@ async function killStaleFrpc(deps) {
|
|
|
28021
28347
|
}
|
|
28022
28348
|
await sleep(deps.reapWaitMs ?? 300);
|
|
28023
28349
|
try {
|
|
28024
|
-
|
|
28350
|
+
import_node_fs22.default.unlinkSync(pidFile);
|
|
28025
28351
|
} catch {
|
|
28026
28352
|
}
|
|
28027
28353
|
}
|
|
@@ -28058,7 +28384,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
28058
28384
|
var TunnelManager = class {
|
|
28059
28385
|
constructor(deps) {
|
|
28060
28386
|
this.deps = deps;
|
|
28061
|
-
this.store = deps.store ?? new TunnelStore(
|
|
28387
|
+
this.store = deps.store ?? new TunnelStore(import_node_path25.default.join(deps.dataDir, "tunnel.json"));
|
|
28062
28388
|
this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
|
|
28063
28389
|
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
28064
28390
|
}
|
|
@@ -28185,8 +28511,8 @@ var TunnelManager = class {
|
|
|
28185
28511
|
dataDir: this.deps.dataDir,
|
|
28186
28512
|
override: this.deps.frpcBinaryOverride ?? void 0
|
|
28187
28513
|
});
|
|
28188
|
-
const tomlPath =
|
|
28189
|
-
const proxyName = `clawd-${t.subdomain}-${localPort}-${
|
|
28514
|
+
const tomlPath = import_node_path25.default.join(this.deps.dataDir, "frpc.toml");
|
|
28515
|
+
const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto8.default.randomBytes(3).toString("hex")}`;
|
|
28190
28516
|
const toml = buildFrpcToml({
|
|
28191
28517
|
serverAddr: t.frpsHost,
|
|
28192
28518
|
serverPort: t.frpsPort,
|
|
@@ -28196,12 +28522,12 @@ var TunnelManager = class {
|
|
|
28196
28522
|
localPort,
|
|
28197
28523
|
logLevel: "info"
|
|
28198
28524
|
});
|
|
28199
|
-
await
|
|
28525
|
+
await import_node_fs23.default.promises.writeFile(tomlPath, toml, { mode: 384 });
|
|
28200
28526
|
const proc = (this.deps.spawnImpl ?? import_node_child_process5.spawn)(frpcBin, ["-c", tomlPath], {
|
|
28201
28527
|
stdio: ["ignore", "pipe", "pipe"]
|
|
28202
28528
|
});
|
|
28203
|
-
const logFilePath =
|
|
28204
|
-
const logStream =
|
|
28529
|
+
const logFilePath = import_node_path25.default.join(this.deps.dataDir, "frpc.log");
|
|
28530
|
+
const logStream = import_node_fs23.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
|
|
28205
28531
|
logStream.on("error", () => {
|
|
28206
28532
|
});
|
|
28207
28533
|
const tee = (chunk) => {
|
|
@@ -28284,22 +28610,22 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
28284
28610
|
|
|
28285
28611
|
// src/tunnel/device-key.ts
|
|
28286
28612
|
var import_node_os11 = __toESM(require("os"), 1);
|
|
28287
|
-
var
|
|
28613
|
+
var import_node_crypto9 = __toESM(require("crypto"), 1);
|
|
28288
28614
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
28289
28615
|
function deriveStableDeviceKey(opts = {}) {
|
|
28290
28616
|
const hostname = opts.hostname ?? import_node_os11.default.hostname();
|
|
28291
28617
|
const uid = opts.uid ?? (typeof import_node_os11.default.userInfo === "function" ? import_node_os11.default.userInfo().uid : 0);
|
|
28292
28618
|
const input = `${hostname}::${uid}`;
|
|
28293
|
-
return
|
|
28619
|
+
return import_node_crypto9.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
28294
28620
|
}
|
|
28295
28621
|
|
|
28296
28622
|
// src/auth-store.ts
|
|
28297
|
-
var
|
|
28298
|
-
var
|
|
28299
|
-
var
|
|
28623
|
+
var import_node_fs24 = __toESM(require("fs"), 1);
|
|
28624
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
28625
|
+
var import_node_crypto10 = __toESM(require("crypto"), 1);
|
|
28300
28626
|
var AUTH_FILE_NAME = "auth.json";
|
|
28301
28627
|
function authFilePath(dataDir) {
|
|
28302
|
-
return
|
|
28628
|
+
return import_node_path26.default.join(dataDir, AUTH_FILE_NAME);
|
|
28303
28629
|
}
|
|
28304
28630
|
function loadOrCreateAuthFile(opts) {
|
|
28305
28631
|
const file = authFilePath(opts.dataDir);
|
|
@@ -28324,14 +28650,14 @@ function loadOrCreateAuthFile(opts) {
|
|
|
28324
28650
|
return next;
|
|
28325
28651
|
}
|
|
28326
28652
|
function defaultGenerateToken2() {
|
|
28327
|
-
return
|
|
28653
|
+
return import_node_crypto10.default.randomBytes(32).toString("base64url");
|
|
28328
28654
|
}
|
|
28329
28655
|
function defaultGenerateOwnerPrincipalId() {
|
|
28330
|
-
return `owner-${
|
|
28656
|
+
return `owner-${import_node_crypto10.default.randomUUID()}`;
|
|
28331
28657
|
}
|
|
28332
28658
|
function readAuthFile(file) {
|
|
28333
28659
|
try {
|
|
28334
|
-
const raw =
|
|
28660
|
+
const raw = import_node_fs24.default.readFileSync(file, "utf8");
|
|
28335
28661
|
const parsed = JSON.parse(raw);
|
|
28336
28662
|
if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
|
|
28337
28663
|
return null;
|
|
@@ -28349,25 +28675,25 @@ function readAuthFile(file) {
|
|
|
28349
28675
|
}
|
|
28350
28676
|
}
|
|
28351
28677
|
function writeAuthFile(file, content) {
|
|
28352
|
-
|
|
28353
|
-
|
|
28678
|
+
import_node_fs24.default.mkdirSync(import_node_path26.default.dirname(file), { recursive: true });
|
|
28679
|
+
import_node_fs24.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
28354
28680
|
try {
|
|
28355
|
-
|
|
28681
|
+
import_node_fs24.default.chmodSync(file, 384);
|
|
28356
28682
|
} catch {
|
|
28357
28683
|
}
|
|
28358
28684
|
}
|
|
28359
28685
|
|
|
28360
28686
|
// src/owner-profile.ts
|
|
28361
|
-
var
|
|
28687
|
+
var import_node_fs25 = __toESM(require("fs"), 1);
|
|
28362
28688
|
var import_node_os12 = __toESM(require("os"), 1);
|
|
28363
|
-
var
|
|
28689
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
28364
28690
|
var PROFILE_FILENAME = "profile.json";
|
|
28365
28691
|
function loadOwnerDisplayName(dataDir) {
|
|
28366
28692
|
const fallback = import_node_os12.default.userInfo().username;
|
|
28367
|
-
const profilePath =
|
|
28693
|
+
const profilePath = import_node_path27.default.join(dataDir, PROFILE_FILENAME);
|
|
28368
28694
|
let raw;
|
|
28369
28695
|
try {
|
|
28370
|
-
raw =
|
|
28696
|
+
raw = import_node_fs25.default.readFileSync(profilePath, "utf8");
|
|
28371
28697
|
} catch {
|
|
28372
28698
|
return fallback;
|
|
28373
28699
|
}
|
|
@@ -28396,12 +28722,12 @@ init_protocol();
|
|
|
28396
28722
|
init_protocol();
|
|
28397
28723
|
|
|
28398
28724
|
// src/session/fork.ts
|
|
28399
|
-
var
|
|
28725
|
+
var import_node_fs26 = __toESM(require("fs"), 1);
|
|
28400
28726
|
var import_node_os13 = __toESM(require("os"), 1);
|
|
28401
|
-
var
|
|
28727
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
28402
28728
|
init_claude_history();
|
|
28403
28729
|
function readJsonlEntries(file) {
|
|
28404
|
-
const raw =
|
|
28730
|
+
const raw = import_node_fs26.default.readFileSync(file, "utf8");
|
|
28405
28731
|
const out = [];
|
|
28406
28732
|
for (const line of raw.split("\n")) {
|
|
28407
28733
|
const t = line.trim();
|
|
@@ -28414,10 +28740,10 @@ function readJsonlEntries(file) {
|
|
|
28414
28740
|
return out;
|
|
28415
28741
|
}
|
|
28416
28742
|
function forkSession(input) {
|
|
28417
|
-
const baseDir = input.baseDir ??
|
|
28418
|
-
const projectDir =
|
|
28419
|
-
const sourceFile =
|
|
28420
|
-
if (!
|
|
28743
|
+
const baseDir = input.baseDir ?? import_node_path28.default.join(import_node_os13.default.homedir(), ".claude");
|
|
28744
|
+
const projectDir = import_node_path28.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
28745
|
+
const sourceFile = import_node_path28.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
28746
|
+
if (!import_node_fs26.default.existsSync(sourceFile)) {
|
|
28421
28747
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
28422
28748
|
}
|
|
28423
28749
|
const entries = readJsonlEntries(sourceFile);
|
|
@@ -28447,9 +28773,9 @@ function forkSession(input) {
|
|
|
28447
28773
|
}
|
|
28448
28774
|
forkedLines.push(JSON.stringify(forked));
|
|
28449
28775
|
}
|
|
28450
|
-
const forkedFilePath =
|
|
28451
|
-
|
|
28452
|
-
|
|
28776
|
+
const forkedFilePath = import_node_path28.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
28777
|
+
import_node_fs26.default.mkdirSync(projectDir, { recursive: true });
|
|
28778
|
+
import_node_fs26.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
28453
28779
|
return { forkedToolSessionId, forkedFilePath };
|
|
28454
28780
|
}
|
|
28455
28781
|
|
|
@@ -28805,7 +29131,7 @@ function buildPermissionHandlers(deps) {
|
|
|
28805
29131
|
}
|
|
28806
29132
|
|
|
28807
29133
|
// src/handlers/history.ts
|
|
28808
|
-
var
|
|
29134
|
+
var path38 = __toESM(require("path"), 1);
|
|
28809
29135
|
init_protocol();
|
|
28810
29136
|
|
|
28811
29137
|
// src/session/recent-dirs.ts
|
|
@@ -28823,7 +29149,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
28823
29149
|
}
|
|
28824
29150
|
|
|
28825
29151
|
// src/permission/persona-paths.ts
|
|
28826
|
-
var
|
|
29152
|
+
var path37 = __toESM(require("path"), 1);
|
|
28827
29153
|
function getAllowedPersonaIds(grants, action) {
|
|
28828
29154
|
const ids = /* @__PURE__ */ new Set();
|
|
28829
29155
|
for (const g2 of grants) {
|
|
@@ -28836,26 +29162,26 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
28836
29162
|
return ids;
|
|
28837
29163
|
}
|
|
28838
29164
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read") {
|
|
28839
|
-
const root =
|
|
28840
|
-
const target =
|
|
28841
|
-
const sep3 = root.endsWith(
|
|
29165
|
+
const root = path37.resolve(personaRoot);
|
|
29166
|
+
const target = path37.resolve(absPath);
|
|
29167
|
+
const sep3 = root.endsWith(path37.sep) ? "" : path37.sep;
|
|
28842
29168
|
if (!target.startsWith(root + sep3)) return false;
|
|
28843
|
-
const rel =
|
|
29169
|
+
const rel = path37.relative(root, target);
|
|
28844
29170
|
if (!rel || rel.startsWith("..")) return false;
|
|
28845
|
-
const personaId = rel.split(
|
|
29171
|
+
const personaId = rel.split(path37.sep)[0];
|
|
28846
29172
|
if (!personaId) return false;
|
|
28847
29173
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
28848
29174
|
if (allowed === "*") return true;
|
|
28849
29175
|
return allowed.has(personaId);
|
|
28850
29176
|
}
|
|
28851
29177
|
function personaIdFromPath(absPath, personaRoot) {
|
|
28852
|
-
const root =
|
|
28853
|
-
const target =
|
|
28854
|
-
const sep3 = root.endsWith(
|
|
29178
|
+
const root = path37.resolve(personaRoot);
|
|
29179
|
+
const target = path37.resolve(absPath);
|
|
29180
|
+
const sep3 = root.endsWith(path37.sep) ? "" : path37.sep;
|
|
28855
29181
|
if (!target.startsWith(root + sep3)) return null;
|
|
28856
|
-
const rel =
|
|
29182
|
+
const rel = path37.relative(root, target);
|
|
28857
29183
|
if (!rel || rel.startsWith("..")) return null;
|
|
28858
|
-
const id = rel.split(
|
|
29184
|
+
const id = rel.split(path37.sep)[0];
|
|
28859
29185
|
return id || null;
|
|
28860
29186
|
}
|
|
28861
29187
|
|
|
@@ -28880,7 +29206,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28880
29206
|
if (!pid) return false;
|
|
28881
29207
|
return isGuestPathAllowed(
|
|
28882
29208
|
ctx.grants,
|
|
28883
|
-
|
|
29209
|
+
path38.join(personaRoot, pid),
|
|
28884
29210
|
personaRoot,
|
|
28885
29211
|
"read"
|
|
28886
29212
|
);
|
|
@@ -28891,7 +29217,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28891
29217
|
};
|
|
28892
29218
|
const list = async (frame, _client, ctx) => {
|
|
28893
29219
|
const args = HistoryListArgs.parse(frame);
|
|
28894
|
-
assertGuestPath(ctx,
|
|
29220
|
+
assertGuestPath(ctx, path38.resolve(args.projectPath), personaRoot, "history:list");
|
|
28895
29221
|
const sessions = await history.listSessions(args);
|
|
28896
29222
|
return { response: { type: "history:list", sessions } };
|
|
28897
29223
|
};
|
|
@@ -28923,13 +29249,13 @@ function buildHistoryHandlers(deps) {
|
|
|
28923
29249
|
};
|
|
28924
29250
|
const subagents = async (frame, _client, ctx) => {
|
|
28925
29251
|
const args = HistorySubagentsArgs.parse(frame);
|
|
28926
|
-
assertGuestPath(ctx,
|
|
29252
|
+
assertGuestPath(ctx, path38.resolve(args.cwd), personaRoot, "history:subagents");
|
|
28927
29253
|
const subs = await history.listSubagents(args);
|
|
28928
29254
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
28929
29255
|
};
|
|
28930
29256
|
const subagentRead = async (frame, _client, ctx) => {
|
|
28931
29257
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
28932
|
-
assertGuestPath(ctx,
|
|
29258
|
+
assertGuestPath(ctx, path38.resolve(args.cwd), personaRoot, "history:subagent-read");
|
|
28933
29259
|
const res = await history.readSubagent(args);
|
|
28934
29260
|
return { response: { type: "history:subagent-read", ...res } };
|
|
28935
29261
|
};
|
|
@@ -28937,7 +29263,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28937
29263
|
const dirs = listRecentDirs(store);
|
|
28938
29264
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
28939
29265
|
const filtered = dirs.filter(
|
|
28940
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
29266
|
+
(d) => isGuestPathAllowed(ctx.grants, path38.resolve(d.cwd), personaRoot, "read")
|
|
28941
29267
|
);
|
|
28942
29268
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
28943
29269
|
}
|
|
@@ -28954,7 +29280,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28954
29280
|
}
|
|
28955
29281
|
|
|
28956
29282
|
// src/handlers/workspace.ts
|
|
28957
|
-
var
|
|
29283
|
+
var path39 = __toESM(require("path"), 1);
|
|
28958
29284
|
var os14 = __toESM(require("os"), 1);
|
|
28959
29285
|
init_protocol();
|
|
28960
29286
|
init_protocol();
|
|
@@ -28995,22 +29321,22 @@ function buildWorkspaceHandlers(deps) {
|
|
|
28995
29321
|
const args = WorkspaceListArgs.parse(frame);
|
|
28996
29322
|
const isGuest = ctx?.principal.kind === "guest";
|
|
28997
29323
|
const fallbackCwd = isGuest && personaRoot ? personaRoot : os14.homedir();
|
|
28998
|
-
const resolvedCwd =
|
|
28999
|
-
const target = args.path ?
|
|
29324
|
+
const resolvedCwd = path39.resolve(args.cwd ?? fallbackCwd);
|
|
29325
|
+
const target = args.path ? path39.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
29000
29326
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list");
|
|
29001
29327
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
29002
29328
|
return { response: { type: "workspace:list", ...res } };
|
|
29003
29329
|
};
|
|
29004
29330
|
const read = async (frame, _client, ctx) => {
|
|
29005
29331
|
const args = WorkspaceReadArgs.parse(frame);
|
|
29006
|
-
const target =
|
|
29332
|
+
const target = path39.isAbsolute(args.path) ? path39.resolve(args.path) : path39.resolve(args.cwd, args.path);
|
|
29007
29333
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read");
|
|
29008
29334
|
const res = workspace.read(args);
|
|
29009
29335
|
return { response: { type: "workspace:read", ...res } };
|
|
29010
29336
|
};
|
|
29011
29337
|
const skillsList = async (frame, _client, ctx) => {
|
|
29012
29338
|
const args = SkillsListArgs.parse(frame);
|
|
29013
|
-
const cwdAbs =
|
|
29339
|
+
const cwdAbs = path39.resolve(args.cwd);
|
|
29014
29340
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list");
|
|
29015
29341
|
const list2 = skills.list(args);
|
|
29016
29342
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -29022,7 +29348,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
29022
29348
|
};
|
|
29023
29349
|
const agentsList = async (frame, _client, ctx) => {
|
|
29024
29350
|
const args = AgentsListArgs.parse(frame);
|
|
29025
|
-
const cwdAbs =
|
|
29351
|
+
const cwdAbs = path39.resolve(args.cwd);
|
|
29026
29352
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list");
|
|
29027
29353
|
const list2 = agents.list(args);
|
|
29028
29354
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -29041,20 +29367,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
29041
29367
|
}
|
|
29042
29368
|
|
|
29043
29369
|
// src/handlers/git.ts
|
|
29044
|
-
var
|
|
29370
|
+
var path41 = __toESM(require("path"), 1);
|
|
29045
29371
|
init_protocol();
|
|
29046
29372
|
init_protocol();
|
|
29047
29373
|
|
|
29048
29374
|
// src/workspace/git.ts
|
|
29049
29375
|
var import_node_child_process6 = require("child_process");
|
|
29050
|
-
var
|
|
29051
|
-
var
|
|
29376
|
+
var import_node_fs27 = __toESM(require("fs"), 1);
|
|
29377
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
29052
29378
|
var import_node_util = require("util");
|
|
29053
29379
|
var pexec = (0, import_node_util.promisify)(import_node_child_process6.execFile);
|
|
29054
29380
|
function normalizePath(p2) {
|
|
29055
|
-
const resolved =
|
|
29381
|
+
const resolved = import_node_path29.default.resolve(p2);
|
|
29056
29382
|
try {
|
|
29057
|
-
return
|
|
29383
|
+
return import_node_fs27.default.realpathSync(resolved);
|
|
29058
29384
|
} catch {
|
|
29059
29385
|
return resolved;
|
|
29060
29386
|
}
|
|
@@ -29127,7 +29453,7 @@ async function listGitBranches(cwd) {
|
|
|
29127
29453
|
// src/handlers/git.ts
|
|
29128
29454
|
function assertGuestCwd(ctx, cwd, personaRoot, method) {
|
|
29129
29455
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
29130
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
29456
|
+
if (!isGuestPathAllowed(ctx.grants, path41.resolve(cwd), personaRoot, "read")) {
|
|
29131
29457
|
throw new ClawdError(
|
|
29132
29458
|
ERROR_CODES.UNAUTHORIZED,
|
|
29133
29459
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -29614,7 +29940,7 @@ function buildPersonaHandlers(deps) {
|
|
|
29614
29940
|
}
|
|
29615
29941
|
|
|
29616
29942
|
// src/handlers/attachment.ts
|
|
29617
|
-
var
|
|
29943
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
29618
29944
|
init_protocol();
|
|
29619
29945
|
init_protocol();
|
|
29620
29946
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -29668,12 +29994,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
29668
29994
|
`session ${args.sessionId} scope unresolved`
|
|
29669
29995
|
);
|
|
29670
29996
|
}
|
|
29671
|
-
const cwdAbs =
|
|
29672
|
-
const candidateAbs =
|
|
29997
|
+
const cwdAbs = import_node_path30.default.resolve(sessionFile.cwd);
|
|
29998
|
+
const candidateAbs = import_node_path30.default.isAbsolute(args.relPath) ? import_node_path30.default.resolve(args.relPath) : import_node_path30.default.resolve(cwdAbs, args.relPath);
|
|
29673
29999
|
assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl");
|
|
29674
30000
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
29675
30001
|
const entry = entries.find((e) => {
|
|
29676
|
-
const storedAbs =
|
|
30002
|
+
const storedAbs = import_node_path30.default.isAbsolute(e.relPath) ? import_node_path30.default.resolve(e.relPath) : import_node_path30.default.resolve(cwdAbs, e.relPath);
|
|
29677
30003
|
return storedAbs === candidateAbs && !e.stale;
|
|
29678
30004
|
});
|
|
29679
30005
|
if (!entry) {
|
|
@@ -29697,7 +30023,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29697
30023
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
29698
30024
|
const f = deps.sessionStore.read(sessionId);
|
|
29699
30025
|
if (!f) return;
|
|
29700
|
-
assertGuestAttachmentPath(ctx,
|
|
30026
|
+
assertGuestAttachmentPath(ctx, import_node_path30.default.resolve(f.cwd), deps.personaRoot, method);
|
|
29701
30027
|
}
|
|
29702
30028
|
const groupAdd = async (frame, _client, ctx) => {
|
|
29703
30029
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -29738,7 +30064,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29738
30064
|
}
|
|
29739
30065
|
const entries = deps.groupFileStore.list(scope, parsed.data.sessionId);
|
|
29740
30066
|
const target = entries.find((e) => e.relPath === parsed.data.relPath);
|
|
29741
|
-
if (target?.from === "owner") {
|
|
30067
|
+
if (target?.from === "owner" || target?.from === "user-upload") {
|
|
29742
30068
|
deps.groupFileStore.remove(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
29743
30069
|
} else {
|
|
29744
30070
|
deps.groupFileStore.markStale(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
@@ -29939,7 +30265,7 @@ function computeGrantForFrame(method, frame) {
|
|
|
29939
30265
|
async function startDaemon(config) {
|
|
29940
30266
|
const logger = createLogger({
|
|
29941
30267
|
level: config.logLevel,
|
|
29942
|
-
file:
|
|
30268
|
+
file: import_node_path31.default.join(config.dataDir, "clawd.log")
|
|
29943
30269
|
});
|
|
29944
30270
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
29945
30271
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
@@ -30026,7 +30352,7 @@ async function startDaemon(config) {
|
|
|
30026
30352
|
const agents = new AgentsScanner();
|
|
30027
30353
|
const history = new ClaudeHistoryReader();
|
|
30028
30354
|
let transport = null;
|
|
30029
|
-
const personaStore = new PersonaStore(
|
|
30355
|
+
const personaStore = new PersonaStore(import_node_path31.default.join(config.dataDir, "personas"));
|
|
30030
30356
|
const defaultsRoot = findDefaultsRoot();
|
|
30031
30357
|
if (defaultsRoot) {
|
|
30032
30358
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -30043,7 +30369,7 @@ async function startDaemon(config) {
|
|
|
30043
30369
|
getAdapter,
|
|
30044
30370
|
historyReader: history,
|
|
30045
30371
|
dataDir: config.dataDir,
|
|
30046
|
-
personaRoot:
|
|
30372
|
+
personaRoot: import_node_path31.default.join(config.dataDir, "personas"),
|
|
30047
30373
|
personaStore,
|
|
30048
30374
|
ownerDisplayName,
|
|
30049
30375
|
ownerPrincipalId,
|
|
@@ -30067,10 +30393,10 @@ async function startDaemon(config) {
|
|
|
30067
30393
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
30068
30394
|
attachmentGroup: {
|
|
30069
30395
|
onFileEdit: (input) => {
|
|
30070
|
-
const absPath =
|
|
30396
|
+
const absPath = import_node_path31.default.isAbsolute(input.relPath) ? input.relPath : import_node_path31.default.join(input.cwd, input.relPath);
|
|
30071
30397
|
let size = 0;
|
|
30072
30398
|
try {
|
|
30073
|
-
size =
|
|
30399
|
+
size = import_node_fs28.default.statSync(absPath).size;
|
|
30074
30400
|
} catch (err) {
|
|
30075
30401
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
30076
30402
|
sessionId: input.sessionId,
|
|
@@ -30188,10 +30514,10 @@ async function startDaemon(config) {
|
|
|
30188
30514
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
30189
30515
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30190
30516
|
// guest path guard:candidate 必须在 personaRoot 子树下
|
|
30191
|
-
personaRoot:
|
|
30517
|
+
personaRoot: import_node_path31.default.join(config.dataDir, "personas")
|
|
30192
30518
|
},
|
|
30193
30519
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
30194
|
-
personaRoot:
|
|
30520
|
+
personaRoot: import_node_path31.default.join(config.dataDir, "personas"),
|
|
30195
30521
|
// capability:list / delete handler 依赖
|
|
30196
30522
|
capabilityManager,
|
|
30197
30523
|
// personal-cap:get 返回的 shareBaseUrl 用此 base URL:
|
|
@@ -30227,7 +30553,19 @@ async function startDaemon(config) {
|
|
|
30227
30553
|
sessionStore: store,
|
|
30228
30554
|
// /files HMAC verify 用 auth.json 的 signSecret 字段(与 attachment.signUrl 同源)。
|
|
30229
30555
|
// --auth-token CLI 模式没 signSecret → 路由返 501,sign URL 功能整体禁用。
|
|
30230
|
-
getSignSecret: () => authFile?.signSecret ?? null
|
|
30556
|
+
getSignSecret: () => authFile?.signSecret ?? null,
|
|
30557
|
+
// chatinput 附件上传(POST /attachments/upload,spec §6.1)。upload pipeline
|
|
30558
|
+
// 在 session.cwd 下的 .attachments/ 落盘;signSecret 与 /files HMAC 同源;
|
|
30559
|
+
// tunnel 未启时返 503 TUNNEL_NOT_READY 让前端显式提示。
|
|
30560
|
+
attachmentUpload: {
|
|
30561
|
+
groupFileStore,
|
|
30562
|
+
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30563
|
+
getSessionDir: (sid) => {
|
|
30564
|
+
const f = manager.findOwnedSession(sid);
|
|
30565
|
+
return f ? f.cwd : null;
|
|
30566
|
+
},
|
|
30567
|
+
getHttpBaseUrl: () => currentTunnelUrl ? getHttpBaseUrl() : null
|
|
30568
|
+
}
|
|
30231
30569
|
});
|
|
30232
30570
|
wsServer = new LocalWsServer({
|
|
30233
30571
|
host: config.host,
|
|
@@ -30392,8 +30730,8 @@ async function startDaemon(config) {
|
|
|
30392
30730
|
const lines = [
|
|
30393
30731
|
`Tunnel: ${r.url}`,
|
|
30394
30732
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
30395
|
-
`Frpc config: ${
|
|
30396
|
-
`Frpc log: ${
|
|
30733
|
+
`Frpc config: ${import_node_path31.default.join(config.dataDir, "frpc.toml")}`,
|
|
30734
|
+
`Frpc log: ${import_node_path31.default.join(config.dataDir, "frpc.log")}`
|
|
30397
30735
|
];
|
|
30398
30736
|
const width = Math.max(...lines.map((l) => l.length));
|
|
30399
30737
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -30406,8 +30744,8 @@ ${bar}
|
|
|
30406
30744
|
|
|
30407
30745
|
`);
|
|
30408
30746
|
try {
|
|
30409
|
-
const connectPath =
|
|
30410
|
-
|
|
30747
|
+
const connectPath = import_node_path31.default.join(config.dataDir, "connect.txt");
|
|
30748
|
+
import_node_fs28.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
30411
30749
|
} catch {
|
|
30412
30750
|
}
|
|
30413
30751
|
} catch (err) {
|
|
@@ -30432,8 +30770,29 @@ ${bar}
|
|
|
30432
30770
|
logger.warn("tunnel unavailable, degraded to local mode", { reason: tunnelError });
|
|
30433
30771
|
}
|
|
30434
30772
|
}
|
|
30773
|
+
const tickAttachmentGc = () => {
|
|
30774
|
+
try {
|
|
30775
|
+
runAttachmentGc({
|
|
30776
|
+
dataDir: config.dataDir,
|
|
30777
|
+
groupFileStore,
|
|
30778
|
+
sessionScopes: manager.listOwnedSessionScopes().map(({ scope, sessionId }) => ({
|
|
30779
|
+
scope,
|
|
30780
|
+
sessionId
|
|
30781
|
+
})),
|
|
30782
|
+
// 注入 cwd 让 GC 直接定位 .attachments 目录(与 upload 落盘点一致)
|
|
30783
|
+
getSessionCwd: (sid) => manager.findOwnedSession(sid)?.cwd ?? null,
|
|
30784
|
+
logger
|
|
30785
|
+
});
|
|
30786
|
+
} catch (err) {
|
|
30787
|
+
logger.warn("attachment gc tick failed", { err: err.message });
|
|
30788
|
+
}
|
|
30789
|
+
};
|
|
30790
|
+
setImmediate(tickAttachmentGc);
|
|
30791
|
+
const attachmentGcInterval = setInterval(tickAttachmentGc, 24 * 3600 * 1e3);
|
|
30792
|
+
attachmentGcInterval.unref();
|
|
30435
30793
|
const shutdown = async () => {
|
|
30436
30794
|
logger.info("stopping clawd");
|
|
30795
|
+
clearInterval(attachmentGcInterval);
|
|
30437
30796
|
observer.stopAll();
|
|
30438
30797
|
manager.stopAll();
|
|
30439
30798
|
if (tunnelMgr) {
|
|
@@ -30451,9 +30810,9 @@ ${bar}
|
|
|
30451
30810
|
};
|
|
30452
30811
|
}
|
|
30453
30812
|
function migrateDropPersonsDir(dataDir) {
|
|
30454
|
-
const dir =
|
|
30813
|
+
const dir = import_node_path31.default.join(dataDir, "persons");
|
|
30455
30814
|
try {
|
|
30456
|
-
|
|
30815
|
+
import_node_fs28.default.rmSync(dir, { recursive: true, force: true });
|
|
30457
30816
|
} catch {
|
|
30458
30817
|
}
|
|
30459
30818
|
}
|