@clawos-dev/clawd 0.2.86-beta.162.77b3c16 → 0.2.86
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 +585 -331
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -65,14 +65,6 @@ var init_methods = __esm({
|
|
|
65
65
|
"session:unsubscribe",
|
|
66
66
|
"session:pin",
|
|
67
67
|
"session:reorderPins",
|
|
68
|
-
// mirror peer sessions (2026-05-26): owner-only RPC,写/删本机 mirror SessionFile —
|
|
69
|
-
// mirror 是「**我**在朋友 daemon 上自己创建的 session」的本机持久化副本(不是朋友的会话),
|
|
70
|
-
// 方便 sidebar 跨设备聚合。daemon dumb 写删,**不广播任何 push 帧** (不发 session:info /
|
|
71
|
-
// session:deleted);UI 是写入唯一源,双写完成后通过 cache.setData('sessions', ...) 同步
|
|
72
|
-
// 本地 sidebar state。native session:* handler 完全不感知 mirror (UI 操作 mirror 不复用
|
|
73
|
-
// native 路径)。
|
|
74
|
-
"peerSession:upsert",
|
|
75
|
-
"peerSession:remove",
|
|
76
68
|
"permission:respond",
|
|
77
69
|
// AskUserQuestion 表单回写:UI 答完所有 question 后调用,daemon 把答案合并进 updated_input
|
|
78
70
|
// 写一条 control_response 到 CC stdin(详见 events.ts session:question JSDoc)
|
|
@@ -651,8 +643,8 @@ var init_parseUtil = __esm({
|
|
|
651
643
|
init_errors2();
|
|
652
644
|
init_en();
|
|
653
645
|
makeIssue = (params) => {
|
|
654
|
-
const { data, path:
|
|
655
|
-
const fullPath = [...
|
|
646
|
+
const { data, path: path46, errorMaps, issueData } = params;
|
|
647
|
+
const fullPath = [...path46, ...issueData.path || []];
|
|
656
648
|
const fullIssue = {
|
|
657
649
|
...issueData,
|
|
658
650
|
path: fullPath
|
|
@@ -963,11 +955,11 @@ var init_types = __esm({
|
|
|
963
955
|
init_parseUtil();
|
|
964
956
|
init_util();
|
|
965
957
|
ParseInputLazyPath = class {
|
|
966
|
-
constructor(parent, value,
|
|
958
|
+
constructor(parent, value, path46, key) {
|
|
967
959
|
this._cachedPath = [];
|
|
968
960
|
this.parent = parent;
|
|
969
961
|
this.data = value;
|
|
970
|
-
this._path =
|
|
962
|
+
this._path = path46;
|
|
971
963
|
this._key = key;
|
|
972
964
|
}
|
|
973
965
|
get path() {
|
|
@@ -4351,13 +4343,13 @@ var init_zod = __esm({
|
|
|
4351
4343
|
});
|
|
4352
4344
|
|
|
4353
4345
|
// ../protocol/src/attachment-schemas.ts
|
|
4354
|
-
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;
|
|
4355
4347
|
var init_attachment_schemas = __esm({
|
|
4356
4348
|
"../protocol/src/attachment-schemas.ts"() {
|
|
4357
4349
|
"use strict";
|
|
4358
4350
|
init_zod();
|
|
4359
4351
|
TOKEN_ROLES = ["owner"];
|
|
4360
|
-
GROUP_FILE_SOURCES = ["agent", "owner"];
|
|
4352
|
+
GROUP_FILE_SOURCES = ["agent", "owner", "user-upload"];
|
|
4361
4353
|
GroupFileEntrySchema = external_exports.object({
|
|
4362
4354
|
/** daemon 派发的稳定 id(用于 RPC remove / UI key) */
|
|
4363
4355
|
id: external_exports.string().min(1),
|
|
@@ -4412,6 +4404,16 @@ var init_attachment_schemas = __esm({
|
|
|
4412
4404
|
AttachmentGroupListResponseSchema = external_exports.object({
|
|
4413
4405
|
entries: external_exports.array(GroupFileEntrySchema)
|
|
4414
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
|
+
});
|
|
4415
4417
|
}
|
|
4416
4418
|
});
|
|
4417
4419
|
|
|
@@ -4491,7 +4493,7 @@ var init_persona_schemas = __esm({
|
|
|
4491
4493
|
});
|
|
4492
4494
|
|
|
4493
4495
|
// ../protocol/src/schemas.ts
|
|
4494
|
-
var SessionStatusSchema, UsageSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, ParsedEventSchema, SessionCreateArgs, SessionIdArgs, SessionUpdateArgs, SessionSendArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsArgs, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionSubscribeArgs, SessionPinArgs,
|
|
4496
|
+
var SessionStatusSchema, UsageSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, ParsedEventSchema, SessionCreateArgs, SessionIdArgs, SessionUpdateArgs, SessionSendArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsArgs, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionSubscribeArgs, SessionPinArgs, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, HistoryRecentDirsArgs, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, AuthRequestFrameSchema, AuthOkFrameSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema;
|
|
4495
4497
|
var init_schemas = __esm({
|
|
4496
4498
|
"../protocol/src/schemas.ts"() {
|
|
4497
4499
|
"use strict";
|
|
@@ -4619,35 +4621,6 @@ var init_schemas = __esm({
|
|
|
4619
4621
|
* 当前为 caller 自报,daemon 不密码学验证真伪;强身份验签是独立工作(B 方案)。
|
|
4620
4622
|
*/
|
|
4621
4623
|
creatorOwnerPrincipalId: external_exports.string().min(1).optional(),
|
|
4622
|
-
/**
|
|
4623
|
-
* Mirror peer sessions (2026-05-26): mirror 标识。**我在朋友 daemon 上以 guest ctx
|
|
4624
|
-
* 创建的 session**,远端是 source of truth,本机是该 session 的持久化副本,方便
|
|
4625
|
-
* sidebar 跨设备聚合。字段值是远端 daemon 的 ownerPrincipalId(即我接入的那位朋友)。
|
|
4626
|
-
*
|
|
4627
|
-
* 语义关键澄清:mirror **不是朋友创建给我的会话**——它是我自己的会话,只是在朋友
|
|
4628
|
-
* 的机器上跑。sidebar chip 显示 `@<朋友>` 暗示「位置」,不是「来源」。
|
|
4629
|
-
*
|
|
4630
|
-
* - undefined / 缺省 → 本机 native session(在我自己机器上跑)
|
|
4631
|
-
* - 非空 → 我的会话在该 ownerPrincipalId 朋友的 daemon 上跑,本机为持久化副本
|
|
4632
|
-
*
|
|
4633
|
-
* 与 `creatorPrincipalId` / `creatorOwnerPrincipalId` 在四类 session 下取值矩阵:
|
|
4634
|
-
* | 形态 | creatorPrincipalId | creatorOwnerPrincipalId | originOwnerPrincipalId |
|
|
4635
|
-
* |-------------------------------------|--------------------------|-------------------------|------------------------|
|
|
4636
|
-
* | 本机 owner 自创 | 本机 ownerPrincipalId | 本机 ownerPrincipalId | undefined |
|
|
4637
|
-
* | 本机被别人通过 personal token 接入创建 | 远端接入者 cap.id | 远端接入者 ownerPrincipalId | undefined |
|
|
4638
|
-
* | 我在朋友 daemon 上以 guest ctx 创建(mirror 回本机) | 朋友 daemon personal cap.id | 我自己 ownerPrincipalId | 朋友 ownerPrincipalId |
|
|
4639
|
-
*
|
|
4640
|
-
* UI 用法:
|
|
4641
|
-
* - sidebar Active/Pinned/Recents 过滤:`origin 非空 → 一律视作 self`(绕过 #739 隔离)
|
|
4642
|
-
* - 行内显示 `@<朋友>` chip 暗示位置
|
|
4643
|
-
* - 进 ChatPanel 时按 origin 路由到 `pool.ensureRemoteForOwner(origin)` 走远端
|
|
4644
|
-
*
|
|
4645
|
-
* daemon 用法:
|
|
4646
|
-
* - `peerSession:upsert` / `peerSession:remove` 一族 RPC 专管 mirror 写删;
|
|
4647
|
-
* daemon dumb write/delete,**不广播任何 push 帧**,UI 是写入唯一源
|
|
4648
|
-
* - native `session:*` handler 完全不感知 mirror(UI 操作 mirror 不走 native 路径)
|
|
4649
|
-
*/
|
|
4650
|
-
originOwnerPrincipalId: external_exports.string().min(1).optional(),
|
|
4651
4624
|
createdAt: external_exports.string().min(1),
|
|
4652
4625
|
updatedAt: external_exports.string().min(1)
|
|
4653
4626
|
});
|
|
@@ -5003,14 +4976,6 @@ var init_schemas = __esm({
|
|
|
5003
4976
|
sessionId: external_exports.string().min(1),
|
|
5004
4977
|
pinned: external_exports.boolean()
|
|
5005
4978
|
});
|
|
5006
|
-
PeerSessionUpsertArgs = SessionFileSchema;
|
|
5007
|
-
PeerSessionUpsertResponseSchema = external_exports.object({
|
|
5008
|
-
type: external_exports.literal("peerSession:upsert:ok")
|
|
5009
|
-
});
|
|
5010
|
-
PeerSessionRemoveArgs = external_exports.object({ sessionId: external_exports.string().min(1) });
|
|
5011
|
-
PeerSessionRemoveResponseSchema = external_exports.object({
|
|
5012
|
-
type: external_exports.literal("peerSession:remove:ok")
|
|
5013
|
-
});
|
|
5014
4979
|
SessionReorderPinsArgs = external_exports.object({
|
|
5015
4980
|
orderedIds: external_exports.array(external_exports.string().min(1)).min(1)
|
|
5016
4981
|
});
|
|
@@ -5625,8 +5590,8 @@ var require_req = __commonJS({
|
|
|
5625
5590
|
if (req.originalUrl) {
|
|
5626
5591
|
_req.url = req.originalUrl;
|
|
5627
5592
|
} else {
|
|
5628
|
-
const
|
|
5629
|
-
_req.url = typeof
|
|
5593
|
+
const path46 = req.path;
|
|
5594
|
+
_req.url = typeof path46 === "string" ? path46 : req.url ? req.url.path || req.url : void 0;
|
|
5630
5595
|
}
|
|
5631
5596
|
if (req.query) {
|
|
5632
5597
|
_req.query = req.query;
|
|
@@ -5791,14 +5756,14 @@ var require_redact = __commonJS({
|
|
|
5791
5756
|
}
|
|
5792
5757
|
return obj;
|
|
5793
5758
|
}
|
|
5794
|
-
function parsePath(
|
|
5759
|
+
function parsePath(path46) {
|
|
5795
5760
|
const parts = [];
|
|
5796
5761
|
let current = "";
|
|
5797
5762
|
let inBrackets = false;
|
|
5798
5763
|
let inQuotes = false;
|
|
5799
5764
|
let quoteChar = "";
|
|
5800
|
-
for (let i = 0; i <
|
|
5801
|
-
const char =
|
|
5765
|
+
for (let i = 0; i < path46.length; i++) {
|
|
5766
|
+
const char = path46[i];
|
|
5802
5767
|
if (!inBrackets && char === ".") {
|
|
5803
5768
|
if (current) {
|
|
5804
5769
|
parts.push(current);
|
|
@@ -5929,10 +5894,10 @@ var require_redact = __commonJS({
|
|
|
5929
5894
|
return current;
|
|
5930
5895
|
}
|
|
5931
5896
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
5932
|
-
for (const
|
|
5933
|
-
const parts = parsePath(
|
|
5897
|
+
for (const path46 of paths) {
|
|
5898
|
+
const parts = parsePath(path46);
|
|
5934
5899
|
if (parts.includes("*")) {
|
|
5935
|
-
redactWildcardPath(obj, parts, censor,
|
|
5900
|
+
redactWildcardPath(obj, parts, censor, path46, remove);
|
|
5936
5901
|
} else {
|
|
5937
5902
|
if (remove) {
|
|
5938
5903
|
removeKey(obj, parts);
|
|
@@ -6017,8 +5982,8 @@ var require_redact = __commonJS({
|
|
|
6017
5982
|
}
|
|
6018
5983
|
} else {
|
|
6019
5984
|
if (afterWildcard.includes("*")) {
|
|
6020
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6021
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
5985
|
+
const wrappedCensor = typeof censor === "function" ? (value, path46) => {
|
|
5986
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path46];
|
|
6022
5987
|
return censor(value, fullPath);
|
|
6023
5988
|
} : censor;
|
|
6024
5989
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -6053,8 +6018,8 @@ var require_redact = __commonJS({
|
|
|
6053
6018
|
return null;
|
|
6054
6019
|
}
|
|
6055
6020
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
6056
|
-
for (const
|
|
6057
|
-
const parts = parsePath(
|
|
6021
|
+
for (const path46 of pathsToClone) {
|
|
6022
|
+
const parts = parsePath(path46);
|
|
6058
6023
|
let current = pathStructure;
|
|
6059
6024
|
for (let i = 0; i < parts.length; i++) {
|
|
6060
6025
|
const part = parts[i];
|
|
@@ -6106,24 +6071,24 @@ var require_redact = __commonJS({
|
|
|
6106
6071
|
}
|
|
6107
6072
|
return cloneSelectively(obj, pathStructure);
|
|
6108
6073
|
}
|
|
6109
|
-
function validatePath(
|
|
6110
|
-
if (typeof
|
|
6074
|
+
function validatePath(path46) {
|
|
6075
|
+
if (typeof path46 !== "string") {
|
|
6111
6076
|
throw new Error("Paths must be (non-empty) strings");
|
|
6112
6077
|
}
|
|
6113
|
-
if (
|
|
6078
|
+
if (path46 === "") {
|
|
6114
6079
|
throw new Error("Invalid redaction path ()");
|
|
6115
6080
|
}
|
|
6116
|
-
if (
|
|
6117
|
-
throw new Error(`Invalid redaction path (${
|
|
6081
|
+
if (path46.includes("..")) {
|
|
6082
|
+
throw new Error(`Invalid redaction path (${path46})`);
|
|
6118
6083
|
}
|
|
6119
|
-
if (
|
|
6120
|
-
throw new Error(`Invalid redaction path (${
|
|
6084
|
+
if (path46.includes(",")) {
|
|
6085
|
+
throw new Error(`Invalid redaction path (${path46})`);
|
|
6121
6086
|
}
|
|
6122
6087
|
let bracketCount = 0;
|
|
6123
6088
|
let inQuotes = false;
|
|
6124
6089
|
let quoteChar = "";
|
|
6125
|
-
for (let i = 0; i <
|
|
6126
|
-
const char =
|
|
6090
|
+
for (let i = 0; i < path46.length; i++) {
|
|
6091
|
+
const char = path46[i];
|
|
6127
6092
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
6128
6093
|
if (!inQuotes) {
|
|
6129
6094
|
inQuotes = true;
|
|
@@ -6137,20 +6102,20 @@ var require_redact = __commonJS({
|
|
|
6137
6102
|
} else if (char === "]" && !inQuotes) {
|
|
6138
6103
|
bracketCount--;
|
|
6139
6104
|
if (bracketCount < 0) {
|
|
6140
|
-
throw new Error(`Invalid redaction path (${
|
|
6105
|
+
throw new Error(`Invalid redaction path (${path46})`);
|
|
6141
6106
|
}
|
|
6142
6107
|
}
|
|
6143
6108
|
}
|
|
6144
6109
|
if (bracketCount !== 0) {
|
|
6145
|
-
throw new Error(`Invalid redaction path (${
|
|
6110
|
+
throw new Error(`Invalid redaction path (${path46})`);
|
|
6146
6111
|
}
|
|
6147
6112
|
}
|
|
6148
6113
|
function validatePaths(paths) {
|
|
6149
6114
|
if (!Array.isArray(paths)) {
|
|
6150
6115
|
throw new TypeError("paths must be an array");
|
|
6151
6116
|
}
|
|
6152
|
-
for (const
|
|
6153
|
-
validatePath(
|
|
6117
|
+
for (const path46 of paths) {
|
|
6118
|
+
validatePath(path46);
|
|
6154
6119
|
}
|
|
6155
6120
|
}
|
|
6156
6121
|
function slowRedact(options = {}) {
|
|
@@ -6318,8 +6283,8 @@ var require_redaction = __commonJS({
|
|
|
6318
6283
|
if (shape[k2] === null) {
|
|
6319
6284
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
6320
6285
|
} else {
|
|
6321
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6322
|
-
return censor(value, [k2, ...
|
|
6286
|
+
const wrappedCensor = typeof censor === "function" ? (value, path46) => {
|
|
6287
|
+
return censor(value, [k2, ...path46]);
|
|
6323
6288
|
} : censor;
|
|
6324
6289
|
o[k2] = Redact({
|
|
6325
6290
|
paths: shape[k2],
|
|
@@ -6537,10 +6502,10 @@ var require_atomic_sleep = __commonJS({
|
|
|
6537
6502
|
var require_sonic_boom = __commonJS({
|
|
6538
6503
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
6539
6504
|
"use strict";
|
|
6540
|
-
var
|
|
6505
|
+
var fs37 = require("fs");
|
|
6541
6506
|
var EventEmitter2 = require("events");
|
|
6542
6507
|
var inherits = require("util").inherits;
|
|
6543
|
-
var
|
|
6508
|
+
var path46 = require("path");
|
|
6544
6509
|
var sleep = require_atomic_sleep();
|
|
6545
6510
|
var assert = require("assert");
|
|
6546
6511
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -6594,20 +6559,20 @@ var require_sonic_boom = __commonJS({
|
|
|
6594
6559
|
const mode = sonic.mode;
|
|
6595
6560
|
if (sonic.sync) {
|
|
6596
6561
|
try {
|
|
6597
|
-
if (sonic.mkdir)
|
|
6598
|
-
const fd =
|
|
6562
|
+
if (sonic.mkdir) fs37.mkdirSync(path46.dirname(file), { recursive: true });
|
|
6563
|
+
const fd = fs37.openSync(file, flags, mode);
|
|
6599
6564
|
fileOpened(null, fd);
|
|
6600
6565
|
} catch (err) {
|
|
6601
6566
|
fileOpened(err);
|
|
6602
6567
|
throw err;
|
|
6603
6568
|
}
|
|
6604
6569
|
} else if (sonic.mkdir) {
|
|
6605
|
-
|
|
6570
|
+
fs37.mkdir(path46.dirname(file), { recursive: true }, (err) => {
|
|
6606
6571
|
if (err) return fileOpened(err);
|
|
6607
|
-
|
|
6572
|
+
fs37.open(file, flags, mode, fileOpened);
|
|
6608
6573
|
});
|
|
6609
6574
|
} else {
|
|
6610
|
-
|
|
6575
|
+
fs37.open(file, flags, mode, fileOpened);
|
|
6611
6576
|
}
|
|
6612
6577
|
}
|
|
6613
6578
|
function SonicBoom(opts) {
|
|
@@ -6648,8 +6613,8 @@ var require_sonic_boom = __commonJS({
|
|
|
6648
6613
|
this.flush = flushBuffer;
|
|
6649
6614
|
this.flushSync = flushBufferSync;
|
|
6650
6615
|
this._actualWrite = actualWriteBuffer;
|
|
6651
|
-
fsWriteSync = () =>
|
|
6652
|
-
fsWrite = () =>
|
|
6616
|
+
fsWriteSync = () => fs37.writeSync(this.fd, this._writingBuf);
|
|
6617
|
+
fsWrite = () => fs37.write(this.fd, this._writingBuf, this.release);
|
|
6653
6618
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
6654
6619
|
this._writingBuf = "";
|
|
6655
6620
|
this.write = write;
|
|
@@ -6658,15 +6623,15 @@ var require_sonic_boom = __commonJS({
|
|
|
6658
6623
|
this._actualWrite = actualWrite;
|
|
6659
6624
|
fsWriteSync = () => {
|
|
6660
6625
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
6661
|
-
return
|
|
6626
|
+
return fs37.writeSync(this.fd, this._writingBuf);
|
|
6662
6627
|
}
|
|
6663
|
-
return
|
|
6628
|
+
return fs37.writeSync(this.fd, this._writingBuf, "utf8");
|
|
6664
6629
|
};
|
|
6665
6630
|
fsWrite = () => {
|
|
6666
6631
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
6667
|
-
return
|
|
6632
|
+
return fs37.write(this.fd, this._writingBuf, this.release);
|
|
6668
6633
|
}
|
|
6669
|
-
return
|
|
6634
|
+
return fs37.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
6670
6635
|
};
|
|
6671
6636
|
} else {
|
|
6672
6637
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -6723,7 +6688,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6723
6688
|
}
|
|
6724
6689
|
}
|
|
6725
6690
|
if (this._fsync) {
|
|
6726
|
-
|
|
6691
|
+
fs37.fsyncSync(this.fd);
|
|
6727
6692
|
}
|
|
6728
6693
|
const len = this._len;
|
|
6729
6694
|
if (this._reopening) {
|
|
@@ -6837,7 +6802,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6837
6802
|
const onDrain = () => {
|
|
6838
6803
|
if (!this._fsync) {
|
|
6839
6804
|
try {
|
|
6840
|
-
|
|
6805
|
+
fs37.fsync(this.fd, (err) => {
|
|
6841
6806
|
this._flushPending = false;
|
|
6842
6807
|
cb(err);
|
|
6843
6808
|
});
|
|
@@ -6939,7 +6904,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6939
6904
|
const fd = this.fd;
|
|
6940
6905
|
this.once("ready", () => {
|
|
6941
6906
|
if (fd !== this.fd) {
|
|
6942
|
-
|
|
6907
|
+
fs37.close(fd, (err) => {
|
|
6943
6908
|
if (err) {
|
|
6944
6909
|
return this.emit("error", err);
|
|
6945
6910
|
}
|
|
@@ -6988,7 +6953,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6988
6953
|
buf = this._bufs[0];
|
|
6989
6954
|
}
|
|
6990
6955
|
try {
|
|
6991
|
-
const n = Buffer.isBuffer(buf) ?
|
|
6956
|
+
const n = Buffer.isBuffer(buf) ? fs37.writeSync(this.fd, buf) : fs37.writeSync(this.fd, buf, "utf8");
|
|
6992
6957
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
6993
6958
|
buf = releasedBufObj.writingBuf;
|
|
6994
6959
|
this._len = releasedBufObj.len;
|
|
@@ -7004,7 +6969,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7004
6969
|
}
|
|
7005
6970
|
}
|
|
7006
6971
|
try {
|
|
7007
|
-
|
|
6972
|
+
fs37.fsyncSync(this.fd);
|
|
7008
6973
|
} catch {
|
|
7009
6974
|
}
|
|
7010
6975
|
}
|
|
@@ -7025,7 +6990,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7025
6990
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
7026
6991
|
}
|
|
7027
6992
|
try {
|
|
7028
|
-
const n =
|
|
6993
|
+
const n = fs37.writeSync(this.fd, buf);
|
|
7029
6994
|
buf = buf.subarray(n);
|
|
7030
6995
|
this._len = Math.max(this._len - n, 0);
|
|
7031
6996
|
if (buf.length <= 0) {
|
|
@@ -7053,13 +7018,13 @@ var require_sonic_boom = __commonJS({
|
|
|
7053
7018
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
7054
7019
|
if (this.sync) {
|
|
7055
7020
|
try {
|
|
7056
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
7021
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs37.writeSync(this.fd, this._writingBuf) : fs37.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7057
7022
|
release(null, written);
|
|
7058
7023
|
} catch (err) {
|
|
7059
7024
|
release(err);
|
|
7060
7025
|
}
|
|
7061
7026
|
} else {
|
|
7062
|
-
|
|
7027
|
+
fs37.write(this.fd, this._writingBuf, release);
|
|
7063
7028
|
}
|
|
7064
7029
|
}
|
|
7065
7030
|
function actualWriteBuffer() {
|
|
@@ -7068,7 +7033,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7068
7033
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
7069
7034
|
if (this.sync) {
|
|
7070
7035
|
try {
|
|
7071
|
-
const written =
|
|
7036
|
+
const written = fs37.writeSync(this.fd, this._writingBuf);
|
|
7072
7037
|
release(null, written);
|
|
7073
7038
|
} catch (err) {
|
|
7074
7039
|
release(err);
|
|
@@ -7077,7 +7042,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7077
7042
|
if (kCopyBuffer) {
|
|
7078
7043
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
7079
7044
|
}
|
|
7080
|
-
|
|
7045
|
+
fs37.write(this.fd, this._writingBuf, release);
|
|
7081
7046
|
}
|
|
7082
7047
|
}
|
|
7083
7048
|
function actualClose(sonic) {
|
|
@@ -7093,12 +7058,12 @@ var require_sonic_boom = __commonJS({
|
|
|
7093
7058
|
sonic._lens = [];
|
|
7094
7059
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
7095
7060
|
try {
|
|
7096
|
-
|
|
7061
|
+
fs37.fsync(sonic.fd, closeWrapped);
|
|
7097
7062
|
} catch {
|
|
7098
7063
|
}
|
|
7099
7064
|
function closeWrapped() {
|
|
7100
7065
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
7101
|
-
|
|
7066
|
+
fs37.close(sonic.fd, done);
|
|
7102
7067
|
} else {
|
|
7103
7068
|
done();
|
|
7104
7069
|
}
|
|
@@ -7829,7 +7794,7 @@ var require_transport = __commonJS({
|
|
|
7829
7794
|
stream.flushSync();
|
|
7830
7795
|
}
|
|
7831
7796
|
function transport(fullOptions) {
|
|
7832
|
-
const { pipeline:
|
|
7797
|
+
const { pipeline: pipeline3, targets, levels, dedupe, worker = {}, caller = getCallers(), sync = false } = fullOptions;
|
|
7833
7798
|
const options = {
|
|
7834
7799
|
...fullOptions.options
|
|
7835
7800
|
};
|
|
@@ -7857,9 +7822,9 @@ var require_transport = __commonJS({
|
|
|
7857
7822
|
};
|
|
7858
7823
|
});
|
|
7859
7824
|
});
|
|
7860
|
-
} else if (
|
|
7825
|
+
} else if (pipeline3) {
|
|
7861
7826
|
target = bundlerOverrides["pino-worker"] || join11(__dirname, "worker.js");
|
|
7862
|
-
options.pipelines = [
|
|
7827
|
+
options.pipelines = [pipeline3.map((dest) => {
|
|
7863
7828
|
return {
|
|
7864
7829
|
...dest,
|
|
7865
7830
|
target: fixTarget(dest.target)
|
|
@@ -10233,11 +10198,11 @@ var init_lib = __esm({
|
|
|
10233
10198
|
}
|
|
10234
10199
|
}
|
|
10235
10200
|
},
|
|
10236
|
-
addToPath: function addToPath(
|
|
10237
|
-
var last =
|
|
10201
|
+
addToPath: function addToPath(path46, added, removed, oldPosInc, options) {
|
|
10202
|
+
var last = path46.lastComponent;
|
|
10238
10203
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
10239
10204
|
return {
|
|
10240
|
-
oldPos:
|
|
10205
|
+
oldPos: path46.oldPos + oldPosInc,
|
|
10241
10206
|
lastComponent: {
|
|
10242
10207
|
count: last.count + 1,
|
|
10243
10208
|
added,
|
|
@@ -10247,7 +10212,7 @@ var init_lib = __esm({
|
|
|
10247
10212
|
};
|
|
10248
10213
|
} else {
|
|
10249
10214
|
return {
|
|
10250
|
-
oldPos:
|
|
10215
|
+
oldPos: path46.oldPos + oldPosInc,
|
|
10251
10216
|
lastComponent: {
|
|
10252
10217
|
count: 1,
|
|
10253
10218
|
added,
|
|
@@ -10678,10 +10643,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
10678
10643
|
const memories = raw.map((m2) => {
|
|
10679
10644
|
if (!m2 || typeof m2 !== "object") return null;
|
|
10680
10645
|
const rec = m2;
|
|
10681
|
-
const
|
|
10646
|
+
const path46 = typeof rec.path === "string" ? rec.path : null;
|
|
10682
10647
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
10683
|
-
if (!
|
|
10684
|
-
const entry = { path:
|
|
10648
|
+
if (!path46 || content == null) return null;
|
|
10649
|
+
const entry = { path: path46, content };
|
|
10685
10650
|
if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
|
|
10686
10651
|
return entry;
|
|
10687
10652
|
}).filter((m2) => m2 !== null);
|
|
@@ -11507,10 +11472,10 @@ function parseAttachment(obj) {
|
|
|
11507
11472
|
const memories = raw.map((m2) => {
|
|
11508
11473
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11509
11474
|
const rec = m2;
|
|
11510
|
-
const
|
|
11475
|
+
const path46 = typeof rec.path === "string" ? rec.path : null;
|
|
11511
11476
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
11512
|
-
if (!
|
|
11513
|
-
const out = { path:
|
|
11477
|
+
if (!path46 || content == null) return null;
|
|
11478
|
+
const out = { path: path46, content };
|
|
11514
11479
|
if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
|
|
11515
11480
|
return out;
|
|
11516
11481
|
}).filter((m2) => m2 !== null);
|
|
@@ -20425,7 +20390,7 @@ var require_websocket_server = __commonJS({
|
|
|
20425
20390
|
// src/run-case/recorder.ts
|
|
20426
20391
|
function startRunCaseRecorder(opts) {
|
|
20427
20392
|
const now = opts.now ?? Date.now;
|
|
20428
|
-
const dir =
|
|
20393
|
+
const dir = import_node_path32.default.dirname(opts.recordPath);
|
|
20429
20394
|
let stream = null;
|
|
20430
20395
|
let closing = false;
|
|
20431
20396
|
let closedSettled = false;
|
|
@@ -20439,8 +20404,8 @@ function startRunCaseRecorder(opts) {
|
|
|
20439
20404
|
});
|
|
20440
20405
|
const ensureStream = () => {
|
|
20441
20406
|
if (stream) return stream;
|
|
20442
|
-
|
|
20443
|
-
stream =
|
|
20407
|
+
import_node_fs29.default.mkdirSync(dir, { recursive: true });
|
|
20408
|
+
stream = import_node_fs29.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
20444
20409
|
stream.on("close", () => closedResolve());
|
|
20445
20410
|
return stream;
|
|
20446
20411
|
};
|
|
@@ -20465,12 +20430,12 @@ function startRunCaseRecorder(opts) {
|
|
|
20465
20430
|
};
|
|
20466
20431
|
return { tap, close, closed };
|
|
20467
20432
|
}
|
|
20468
|
-
var
|
|
20433
|
+
var import_node_fs29, import_node_path32;
|
|
20469
20434
|
var init_recorder = __esm({
|
|
20470
20435
|
"src/run-case/recorder.ts"() {
|
|
20471
20436
|
"use strict";
|
|
20472
|
-
|
|
20473
|
-
|
|
20437
|
+
import_node_fs29 = __toESM(require("fs"), 1);
|
|
20438
|
+
import_node_path32 = __toESM(require("path"), 1);
|
|
20474
20439
|
}
|
|
20475
20440
|
});
|
|
20476
20441
|
|
|
@@ -20513,7 +20478,7 @@ var init_wire = __esm({
|
|
|
20513
20478
|
// src/run-case/controller.ts
|
|
20514
20479
|
async function runController(opts) {
|
|
20515
20480
|
const now = opts.now ?? Date.now;
|
|
20516
|
-
const cwd = opts.cwd ?? (0,
|
|
20481
|
+
const cwd = opts.cwd ?? (0, import_node_fs30.mkdtempSync)(import_node_path33.default.join(import_node_os15.default.tmpdir(), "clawd-runcase-"));
|
|
20517
20482
|
const ownsCwd = opts.cwd === void 0;
|
|
20518
20483
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
20519
20484
|
const spawnCtx = { cwd };
|
|
@@ -20674,19 +20639,19 @@ async function runController(opts) {
|
|
|
20674
20639
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
20675
20640
|
if (ownsCwd) {
|
|
20676
20641
|
try {
|
|
20677
|
-
(0,
|
|
20642
|
+
(0, import_node_fs30.rmSync)(cwd, { recursive: true, force: true });
|
|
20678
20643
|
} catch {
|
|
20679
20644
|
}
|
|
20680
20645
|
}
|
|
20681
20646
|
return exitCode ?? 0;
|
|
20682
20647
|
}
|
|
20683
|
-
var
|
|
20648
|
+
var import_node_fs30, import_node_os15, import_node_path33;
|
|
20684
20649
|
var init_controller = __esm({
|
|
20685
20650
|
"src/run-case/controller.ts"() {
|
|
20686
20651
|
"use strict";
|
|
20687
|
-
|
|
20652
|
+
import_node_fs30 = require("fs");
|
|
20688
20653
|
import_node_os15 = __toESM(require("os"), 1);
|
|
20689
|
-
|
|
20654
|
+
import_node_path33 = __toESM(require("path"), 1);
|
|
20690
20655
|
init_claude();
|
|
20691
20656
|
init_stdout_splitter();
|
|
20692
20657
|
init_permission_stdio();
|
|
@@ -20918,8 +20883,8 @@ Env (advanced):
|
|
|
20918
20883
|
`;
|
|
20919
20884
|
|
|
20920
20885
|
// src/index.ts
|
|
20921
|
-
var
|
|
20922
|
-
var
|
|
20886
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
20887
|
+
var import_node_fs28 = __toESM(require("fs"), 1);
|
|
20923
20888
|
|
|
20924
20889
|
// src/logger.ts
|
|
20925
20890
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
@@ -22654,6 +22619,18 @@ var SessionManager = class {
|
|
|
22654
22619
|
(a, b2) => a.updatedAt > b2.updatedAt ? -1 : a.updatedAt < b2.updatedAt ? 1 : 0
|
|
22655
22620
|
);
|
|
22656
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
|
+
}
|
|
22657
22634
|
// 写回 SessionFile,自动根据 ownerPersonaId / creatorPrincipalId 派生写入 scope.
|
|
22658
22635
|
// 调用方原先都是 `deps.store.write(updated)`,全部走这里收口路由.
|
|
22659
22636
|
writeOwned(file) {
|
|
@@ -22907,25 +22884,6 @@ var SessionManager = class {
|
|
|
22907
22884
|
const file = this.getFile(args.sessionId);
|
|
22908
22885
|
return { response: file, broadcast: [] };
|
|
22909
22886
|
}
|
|
22910
|
-
// mirror peer sessions (2026-05-26):把来自朋友 daemon 的 SessionFile(originOwnerPrincipalId
|
|
22911
|
-
// 非空)持久化到本机 default store。dumb write,**不广播 push 帧**——UI 是写入唯一源,
|
|
22912
|
-
// 双写完成后通过 cache.setData 同步 sidebar 本地 state。
|
|
22913
|
-
//
|
|
22914
|
-
// 强制走 deps.store(即 default scope),不走 scopeForFile:即使 mirror SessionFile 带
|
|
22915
|
-
// 远端 ownerPersonaId(远端 persona session),本机也落 default,不创建本机 persona 目录。
|
|
22916
|
-
// 远端 creator / origin 字段原值保留(不被本机 ctx 派生覆写)。
|
|
22917
|
-
//
|
|
22918
|
-
// 与 manager.create / manager.update 区别:
|
|
22919
|
-
// - 不走 newRunner / attachObserver / cwd 校验(mirror 是纯 metadata 层)
|
|
22920
|
-
// - 不发 session:info 帧(manager.create / .update 通过 reducer/wireCollector 发 push)
|
|
22921
|
-
mirrorUpsert(file) {
|
|
22922
|
-
return this.deps.store.write(file);
|
|
22923
|
-
}
|
|
22924
|
-
// mirror peer sessions:删本机 mirror SessionFile。强制 default store。
|
|
22925
|
-
// idempotent:找不到不 throw(UI 反复点删容错)。**不广播 session:deleted**。
|
|
22926
|
-
mirrorRemove(sessionId) {
|
|
22927
|
-
this.deps.store.delete(sessionId);
|
|
22928
|
-
}
|
|
22929
22887
|
// observer 上报"jsonl 写入了一条 user 行"时调用:在 buffer 里找到与之对应的、
|
|
22930
22888
|
// 还没建立映射的最早一条 synth user_text,记下 synth → real 映射。
|
|
22931
22889
|
// 找不到匹配的 synth(runner 不存在 / buffer 已被 /clear 清空 / 真 uuid 来自纯 history
|
|
@@ -27163,8 +27121,8 @@ function rmdirIfEmpty(p2) {
|
|
|
27163
27121
|
}
|
|
27164
27122
|
|
|
27165
27123
|
// src/transport/http-router.ts
|
|
27166
|
-
var
|
|
27167
|
-
var
|
|
27124
|
+
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
27125
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
27168
27126
|
|
|
27169
27127
|
// src/attachment/mime.ts
|
|
27170
27128
|
var import_node_path16 = __toESM(require("path"), 1);
|
|
@@ -27342,7 +27300,131 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
|
|
|
27342
27300
|
return { ok: true, absPath };
|
|
27343
27301
|
}
|
|
27344
27302
|
|
|
27303
|
+
// src/attachment/upload.ts
|
|
27304
|
+
var import_node_fs15 = __toESM(require("fs"), 1);
|
|
27305
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
27306
|
+
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
27307
|
+
var import_promises = require("stream/promises");
|
|
27308
|
+
var UploadError = class extends Error {
|
|
27309
|
+
constructor(code, message) {
|
|
27310
|
+
super(message);
|
|
27311
|
+
this.code = code;
|
|
27312
|
+
this.name = "UploadError";
|
|
27313
|
+
}
|
|
27314
|
+
code;
|
|
27315
|
+
};
|
|
27316
|
+
function assertValidFileName(fileName) {
|
|
27317
|
+
if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path17.default.basename(fileName)) {
|
|
27318
|
+
throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
|
|
27319
|
+
}
|
|
27320
|
+
}
|
|
27321
|
+
var HASH_PREFIX_LEN = 16;
|
|
27322
|
+
async function writeUploadedAttachment(args) {
|
|
27323
|
+
assertValidFileName(args.fileName);
|
|
27324
|
+
const attachmentsRoot = import_node_path17.default.join(args.sessionDir, ".attachments");
|
|
27325
|
+
try {
|
|
27326
|
+
import_node_fs15.default.mkdirSync(attachmentsRoot, { recursive: true });
|
|
27327
|
+
} catch (err) {
|
|
27328
|
+
throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
|
|
27329
|
+
}
|
|
27330
|
+
const hasher = import_node_crypto6.default.createHash("sha256");
|
|
27331
|
+
let actualSize = 0;
|
|
27332
|
+
const tmpPath = import_node_path17.default.join(
|
|
27333
|
+
attachmentsRoot,
|
|
27334
|
+
`.upload-${process.pid}-${Date.now()}-${import_node_crypto6.default.randomBytes(4).toString("hex")}`
|
|
27335
|
+
);
|
|
27336
|
+
try {
|
|
27337
|
+
await (0, import_promises.pipeline)(
|
|
27338
|
+
args.body,
|
|
27339
|
+
async function* (source) {
|
|
27340
|
+
for await (const chunk of source) {
|
|
27341
|
+
const buf = chunk;
|
|
27342
|
+
actualSize += buf.length;
|
|
27343
|
+
hasher.update(buf);
|
|
27344
|
+
yield buf;
|
|
27345
|
+
}
|
|
27346
|
+
},
|
|
27347
|
+
import_node_fs15.default.createWriteStream(tmpPath, { mode: 384 })
|
|
27348
|
+
);
|
|
27349
|
+
} catch (err) {
|
|
27350
|
+
try {
|
|
27351
|
+
import_node_fs15.default.unlinkSync(tmpPath);
|
|
27352
|
+
} catch {
|
|
27353
|
+
}
|
|
27354
|
+
throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
|
|
27355
|
+
}
|
|
27356
|
+
if (actualSize !== args.contentLength) {
|
|
27357
|
+
try {
|
|
27358
|
+
import_node_fs15.default.unlinkSync(tmpPath);
|
|
27359
|
+
} catch {
|
|
27360
|
+
}
|
|
27361
|
+
throw new UploadError(
|
|
27362
|
+
"SIZE_MISMATCH",
|
|
27363
|
+
`expected ${args.contentLength} bytes, got ${actualSize}`
|
|
27364
|
+
);
|
|
27365
|
+
}
|
|
27366
|
+
const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
|
|
27367
|
+
const hashDir = import_node_path17.default.join(attachmentsRoot, attachmentId);
|
|
27368
|
+
let finalFileName;
|
|
27369
|
+
let hashDirExists = false;
|
|
27370
|
+
try {
|
|
27371
|
+
hashDirExists = import_node_fs15.default.statSync(hashDir).isDirectory();
|
|
27372
|
+
} catch {
|
|
27373
|
+
}
|
|
27374
|
+
if (hashDirExists) {
|
|
27375
|
+
const existing = import_node_fs15.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
|
|
27376
|
+
finalFileName = existing[0] ?? args.fileName;
|
|
27377
|
+
try {
|
|
27378
|
+
import_node_fs15.default.unlinkSync(tmpPath);
|
|
27379
|
+
} catch {
|
|
27380
|
+
}
|
|
27381
|
+
} else {
|
|
27382
|
+
try {
|
|
27383
|
+
import_node_fs15.default.mkdirSync(hashDir, { recursive: true });
|
|
27384
|
+
finalFileName = args.fileName;
|
|
27385
|
+
import_node_fs15.default.renameSync(tmpPath, import_node_path17.default.join(hashDir, finalFileName));
|
|
27386
|
+
} catch (err) {
|
|
27387
|
+
try {
|
|
27388
|
+
import_node_fs15.default.unlinkSync(tmpPath);
|
|
27389
|
+
} catch {
|
|
27390
|
+
}
|
|
27391
|
+
throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
|
|
27392
|
+
}
|
|
27393
|
+
}
|
|
27394
|
+
const absPath = import_node_path17.default.join(hashDir, finalFileName);
|
|
27395
|
+
const relPath = import_node_path17.default.relative(args.sessionDir, absPath);
|
|
27396
|
+
args.groupFileStore.upsert(args.scope, args.sessionId, {
|
|
27397
|
+
relPath: absPath,
|
|
27398
|
+
// 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
|
|
27399
|
+
from: "user-upload",
|
|
27400
|
+
label: args.fileName,
|
|
27401
|
+
size: actualSize,
|
|
27402
|
+
mime: args.mimeType
|
|
27403
|
+
});
|
|
27404
|
+
return { attachmentId, absPath, relPath, sizeBytes: actualSize, mimeType: args.mimeType };
|
|
27405
|
+
}
|
|
27406
|
+
|
|
27345
27407
|
// src/transport/http-router.ts
|
|
27408
|
+
var UPLOAD_MIME_ALLOW = /* @__PURE__ */ new Set([
|
|
27409
|
+
"image/png",
|
|
27410
|
+
"image/jpeg",
|
|
27411
|
+
"image/gif",
|
|
27412
|
+
"image/webp",
|
|
27413
|
+
"application/pdf"
|
|
27414
|
+
]);
|
|
27415
|
+
var UPLOAD_MIME_ALLOW_PREFIX = ["text/"];
|
|
27416
|
+
var UPLOAD_MAX_BYTES = 25 * 1024 * 1024;
|
|
27417
|
+
function isUploadMimeAllowed(mime) {
|
|
27418
|
+
if (UPLOAD_MIME_ALLOW.has(mime)) return true;
|
|
27419
|
+
return UPLOAD_MIME_ALLOW_PREFIX.some((p2) => mime.startsWith(p2));
|
|
27420
|
+
}
|
|
27421
|
+
function isValidUploadFileName(fileName) {
|
|
27422
|
+
if (fileName.length === 0) return false;
|
|
27423
|
+
if (fileName === "." || fileName === "..") return false;
|
|
27424
|
+
if (fileName.startsWith(".")) return false;
|
|
27425
|
+
if (fileName.includes("/") || fileName.includes("\\")) return false;
|
|
27426
|
+
return fileName === import_node_path18.default.basename(fileName);
|
|
27427
|
+
}
|
|
27346
27428
|
function createHttpRouter(deps) {
|
|
27347
27429
|
return async (req, res) => {
|
|
27348
27430
|
const url = parseUrl(req.url);
|
|
@@ -27356,7 +27438,7 @@ function createHttpRouter(deps) {
|
|
|
27356
27438
|
});
|
|
27357
27439
|
return true;
|
|
27358
27440
|
}
|
|
27359
|
-
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/")) {
|
|
27441
|
+
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/") && url.pathname !== "/attachments/upload") {
|
|
27360
27442
|
return false;
|
|
27361
27443
|
}
|
|
27362
27444
|
if (url.pathname.startsWith("/files/") && req.method === "GET") {
|
|
@@ -27396,6 +27478,106 @@ function createHttpRouter(deps) {
|
|
|
27396
27478
|
sendJson(res, 401, { code: "UNAUTHORIZED", message: "missing or invalid bearer token" });
|
|
27397
27479
|
return true;
|
|
27398
27480
|
}
|
|
27481
|
+
if (url.pathname === "/attachments/upload" && req.method === "POST") {
|
|
27482
|
+
if (ctx.role !== "owner") {
|
|
27483
|
+
sendJson(res, 403, { code: "FORBIDDEN", message: "owner only" });
|
|
27484
|
+
return true;
|
|
27485
|
+
}
|
|
27486
|
+
const up = deps.attachmentUpload;
|
|
27487
|
+
if (!up) {
|
|
27488
|
+
sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "attachment upload not wired" });
|
|
27489
|
+
return true;
|
|
27490
|
+
}
|
|
27491
|
+
const sessionId = url.searchParams.get("sessionId");
|
|
27492
|
+
const fileName = url.searchParams.get("fileName");
|
|
27493
|
+
const mimeType = url.searchParams.get("mimeType");
|
|
27494
|
+
const contentLengthRaw = req.headers["content-length"];
|
|
27495
|
+
if (!sessionId || !fileName || !mimeType || !contentLengthRaw) {
|
|
27496
|
+
sendJson(res, 400, {
|
|
27497
|
+
code: "INVALID_PARAM",
|
|
27498
|
+
message: "missing sessionId/fileName/mimeType/Content-Length"
|
|
27499
|
+
});
|
|
27500
|
+
return true;
|
|
27501
|
+
}
|
|
27502
|
+
if (!isValidUploadFileName(fileName)) {
|
|
27503
|
+
sendJson(res, 400, {
|
|
27504
|
+
code: "INVALID_FILENAME",
|
|
27505
|
+
message: `fileName must be a plain basename (no path separators, no dotfiles): ${fileName}`
|
|
27506
|
+
});
|
|
27507
|
+
return true;
|
|
27508
|
+
}
|
|
27509
|
+
const contentLength = Number.parseInt(contentLengthRaw, 10);
|
|
27510
|
+
if (!Number.isFinite(contentLength) || contentLength < 0) {
|
|
27511
|
+
sendJson(res, 400, { code: "INVALID_PARAM", message: "malformed Content-Length" });
|
|
27512
|
+
return true;
|
|
27513
|
+
}
|
|
27514
|
+
if (contentLength > UPLOAD_MAX_BYTES) {
|
|
27515
|
+
sendJson(res, 413, {
|
|
27516
|
+
code: "FILE_TOO_LARGE",
|
|
27517
|
+
message: `max ${UPLOAD_MAX_BYTES} bytes`
|
|
27518
|
+
});
|
|
27519
|
+
return true;
|
|
27520
|
+
}
|
|
27521
|
+
if (!isUploadMimeAllowed(mimeType)) {
|
|
27522
|
+
sendJson(res, 400, {
|
|
27523
|
+
code: "UNSUPPORTED_MIME",
|
|
27524
|
+
message: `mime ${mimeType} not allowed`
|
|
27525
|
+
});
|
|
27526
|
+
return true;
|
|
27527
|
+
}
|
|
27528
|
+
const sessionDir = up.getSessionDir(sessionId);
|
|
27529
|
+
const scope = up.getSessionScope(sessionId);
|
|
27530
|
+
if (!sessionDir || !scope) {
|
|
27531
|
+
sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sessionId} not found` });
|
|
27532
|
+
return true;
|
|
27533
|
+
}
|
|
27534
|
+
const httpBaseUrl = up.getHttpBaseUrl();
|
|
27535
|
+
if (!httpBaseUrl) {
|
|
27536
|
+
sendJson(res, 503, {
|
|
27537
|
+
code: "TUNNEL_NOT_READY",
|
|
27538
|
+
message: "httpBaseUrl unavailable"
|
|
27539
|
+
});
|
|
27540
|
+
return true;
|
|
27541
|
+
}
|
|
27542
|
+
const secret = deps.getSignSecret?.();
|
|
27543
|
+
if (!secret) {
|
|
27544
|
+
sendJson(res, 501, {
|
|
27545
|
+
code: "NOT_IMPLEMENTED",
|
|
27546
|
+
message: "sign secret unavailable (noAuth?)"
|
|
27547
|
+
});
|
|
27548
|
+
return true;
|
|
27549
|
+
}
|
|
27550
|
+
try {
|
|
27551
|
+
const result = await writeUploadedAttachment({
|
|
27552
|
+
sessionDir,
|
|
27553
|
+
sessionId,
|
|
27554
|
+
scope,
|
|
27555
|
+
fileName,
|
|
27556
|
+
mimeType,
|
|
27557
|
+
contentLength,
|
|
27558
|
+
body: req,
|
|
27559
|
+
groupFileStore: up.groupFileStore
|
|
27560
|
+
});
|
|
27561
|
+
const parts = signUrlParts(secret, result.absPath, null);
|
|
27562
|
+
const fullUrl = buildSignedFileUrl(httpBaseUrl, parts);
|
|
27563
|
+
sendJson(res, 200, {
|
|
27564
|
+
attachmentId: result.attachmentId,
|
|
27565
|
+
url: fullUrl,
|
|
27566
|
+
relPath: result.relPath,
|
|
27567
|
+
sizeBytes: result.sizeBytes,
|
|
27568
|
+
mimeType: result.mimeType
|
|
27569
|
+
});
|
|
27570
|
+
} catch (err) {
|
|
27571
|
+
if (err instanceof UploadError) {
|
|
27572
|
+
const status = err.code === "SIZE_MISMATCH" ? 400 : 500;
|
|
27573
|
+
sendJson(res, status, { code: err.code, message: err.message });
|
|
27574
|
+
} else {
|
|
27575
|
+
deps.logger?.warn("attachment upload failed", { err: err.message });
|
|
27576
|
+
sendJson(res, 500, { code: "STORAGE_ERROR", message: err.message });
|
|
27577
|
+
}
|
|
27578
|
+
}
|
|
27579
|
+
return true;
|
|
27580
|
+
}
|
|
27399
27581
|
const sessionFilesMatch = url.pathname.match(/^\/session\/([^/]+)\/files$/);
|
|
27400
27582
|
if (sessionFilesMatch && req.method === "GET") {
|
|
27401
27583
|
const sid = sessionFilesMatch[1];
|
|
@@ -27405,7 +27587,7 @@ function createHttpRouter(deps) {
|
|
|
27405
27587
|
return true;
|
|
27406
27588
|
}
|
|
27407
27589
|
let absPath;
|
|
27408
|
-
if (
|
|
27590
|
+
if (import_node_path18.default.isAbsolute(pathParam)) {
|
|
27409
27591
|
absPath = pathParam;
|
|
27410
27592
|
} else if (deps.sessionStore) {
|
|
27411
27593
|
const file = deps.sessionStore.read(sid);
|
|
@@ -27413,7 +27595,7 @@ function createHttpRouter(deps) {
|
|
|
27413
27595
|
sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
|
|
27414
27596
|
return true;
|
|
27415
27597
|
}
|
|
27416
|
-
absPath =
|
|
27598
|
+
absPath = import_node_path18.default.join(file.cwd, pathParam);
|
|
27417
27599
|
} else {
|
|
27418
27600
|
sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
|
|
27419
27601
|
return true;
|
|
@@ -27446,7 +27628,7 @@ function withCtx(ctx, body) {
|
|
|
27446
27628
|
function streamFile(res, absPath, logger) {
|
|
27447
27629
|
let stat;
|
|
27448
27630
|
try {
|
|
27449
|
-
stat =
|
|
27631
|
+
stat = import_node_fs16.default.statSync(absPath);
|
|
27450
27632
|
} catch (err) {
|
|
27451
27633
|
const code = err?.code;
|
|
27452
27634
|
if (code === "ENOENT") {
|
|
@@ -27461,7 +27643,7 @@ function streamFile(res, absPath, logger) {
|
|
|
27461
27643
|
return;
|
|
27462
27644
|
}
|
|
27463
27645
|
const mime = lookupMime(absPath);
|
|
27464
|
-
const basename =
|
|
27646
|
+
const basename = import_node_path18.default.basename(absPath);
|
|
27465
27647
|
res.writeHead(200, {
|
|
27466
27648
|
"Content-Type": mime,
|
|
27467
27649
|
"Content-Length": String(stat.size),
|
|
@@ -27469,7 +27651,7 @@ function streamFile(res, absPath, logger) {
|
|
|
27469
27651
|
// 防止浏览器把任意 mime 当 html 渲染
|
|
27470
27652
|
"X-Content-Type-Options": "nosniff"
|
|
27471
27653
|
});
|
|
27472
|
-
const stream =
|
|
27654
|
+
const stream = import_node_fs16.default.createReadStream(absPath);
|
|
27473
27655
|
stream.on("error", (err) => {
|
|
27474
27656
|
logger?.warn("streamFile read error", { absPath, err: err.message });
|
|
27475
27657
|
res.destroy();
|
|
@@ -27477,10 +27659,78 @@ function streamFile(res, absPath, logger) {
|
|
|
27477
27659
|
stream.pipe(res);
|
|
27478
27660
|
}
|
|
27479
27661
|
|
|
27662
|
+
// src/attachment/gc.ts
|
|
27663
|
+
var import_node_fs17 = __toESM(require("fs"), 1);
|
|
27664
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
27665
|
+
var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
|
|
27666
|
+
function runAttachmentGc(args) {
|
|
27667
|
+
const now = (args.now ?? Date.now)();
|
|
27668
|
+
const ttlMs = args.ttlMs ?? DEFAULT_TTL_MS;
|
|
27669
|
+
const liveAbs = /* @__PURE__ */ new Set();
|
|
27670
|
+
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27671
|
+
for (const entry of args.groupFileStore.list(scope, sessionId)) {
|
|
27672
|
+
if (entry.stale) continue;
|
|
27673
|
+
if (import_node_path19.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
|
|
27674
|
+
}
|
|
27675
|
+
}
|
|
27676
|
+
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27677
|
+
const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path19.default.join(
|
|
27678
|
+
args.dataDir,
|
|
27679
|
+
"sessions",
|
|
27680
|
+
...scopeSubPath(scope).map(safeFileName),
|
|
27681
|
+
safeFileName(sessionId)
|
|
27682
|
+
);
|
|
27683
|
+
const attRoot = import_node_path19.default.join(sessionDir, ".attachments");
|
|
27684
|
+
let hashDirs;
|
|
27685
|
+
try {
|
|
27686
|
+
hashDirs = import_node_fs17.default.readdirSync(attRoot);
|
|
27687
|
+
} catch (err) {
|
|
27688
|
+
if (err.code === "ENOENT") continue;
|
|
27689
|
+
args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
|
|
27690
|
+
continue;
|
|
27691
|
+
}
|
|
27692
|
+
for (const hashDir of hashDirs) {
|
|
27693
|
+
const hashDirAbs = import_node_path19.default.join(attRoot, hashDir);
|
|
27694
|
+
let files;
|
|
27695
|
+
try {
|
|
27696
|
+
files = import_node_fs17.default.readdirSync(hashDirAbs);
|
|
27697
|
+
} catch {
|
|
27698
|
+
continue;
|
|
27699
|
+
}
|
|
27700
|
+
for (const name of files) {
|
|
27701
|
+
const file = import_node_path19.default.join(hashDirAbs, name);
|
|
27702
|
+
let stat;
|
|
27703
|
+
try {
|
|
27704
|
+
stat = import_node_fs17.default.statSync(file);
|
|
27705
|
+
} catch {
|
|
27706
|
+
continue;
|
|
27707
|
+
}
|
|
27708
|
+
if (!stat.isFile()) continue;
|
|
27709
|
+
const age = now - stat.mtimeMs;
|
|
27710
|
+
if (age < ttlMs) continue;
|
|
27711
|
+
if (liveAbs.has(file)) continue;
|
|
27712
|
+
try {
|
|
27713
|
+
import_node_fs17.default.unlinkSync(file);
|
|
27714
|
+
} catch (err) {
|
|
27715
|
+
args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
|
|
27716
|
+
}
|
|
27717
|
+
}
|
|
27718
|
+
try {
|
|
27719
|
+
if (import_node_fs17.default.readdirSync(hashDirAbs).length === 0) import_node_fs17.default.rmdirSync(hashDirAbs);
|
|
27720
|
+
} catch {
|
|
27721
|
+
}
|
|
27722
|
+
}
|
|
27723
|
+
try {
|
|
27724
|
+
if (import_node_fs17.default.readdirSync(attRoot).length === 0) import_node_fs17.default.rmdirSync(attRoot);
|
|
27725
|
+
} catch {
|
|
27726
|
+
}
|
|
27727
|
+
}
|
|
27728
|
+
}
|
|
27729
|
+
|
|
27480
27730
|
// src/attachment/group.ts
|
|
27481
|
-
var
|
|
27482
|
-
var
|
|
27483
|
-
var
|
|
27731
|
+
var import_node_fs18 = __toESM(require("fs"), 1);
|
|
27732
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
27733
|
+
var import_node_crypto7 = __toESM(require("crypto"), 1);
|
|
27484
27734
|
init_protocol();
|
|
27485
27735
|
var GroupFileStore = class {
|
|
27486
27736
|
dataDir;
|
|
@@ -27491,11 +27741,11 @@ var GroupFileStore = class {
|
|
|
27491
27741
|
this.logger = opts.logger;
|
|
27492
27742
|
}
|
|
27493
27743
|
rootForScope(scope) {
|
|
27494
|
-
return
|
|
27744
|
+
return import_node_path20.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
|
|
27495
27745
|
}
|
|
27496
27746
|
/** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
|
|
27497
27747
|
filePath(scope, sessionId) {
|
|
27498
|
-
return
|
|
27748
|
+
return import_node_path20.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
|
|
27499
27749
|
}
|
|
27500
27750
|
cacheKey(scope, sessionId) {
|
|
27501
27751
|
return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
|
|
@@ -27504,7 +27754,7 @@ var GroupFileStore = class {
|
|
|
27504
27754
|
readFile(scope, sessionId) {
|
|
27505
27755
|
const file = this.filePath(scope, sessionId);
|
|
27506
27756
|
try {
|
|
27507
|
-
const raw =
|
|
27757
|
+
const raw = import_node_fs18.default.readFileSync(file, "utf8");
|
|
27508
27758
|
const parsed = JSON.parse(raw);
|
|
27509
27759
|
if (!Array.isArray(parsed)) {
|
|
27510
27760
|
this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
|
|
@@ -27530,10 +27780,10 @@ var GroupFileStore = class {
|
|
|
27530
27780
|
}
|
|
27531
27781
|
writeFile(scope, sessionId, entries) {
|
|
27532
27782
|
const file = this.filePath(scope, sessionId);
|
|
27533
|
-
|
|
27783
|
+
import_node_fs18.default.mkdirSync(import_node_path20.default.dirname(file), { recursive: true });
|
|
27534
27784
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
27535
|
-
|
|
27536
|
-
|
|
27785
|
+
import_node_fs18.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
|
|
27786
|
+
import_node_fs18.default.renameSync(tmp, file);
|
|
27537
27787
|
}
|
|
27538
27788
|
/** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
|
|
27539
27789
|
list(scope, sessionId) {
|
|
@@ -27569,7 +27819,7 @@ var GroupFileStore = class {
|
|
|
27569
27819
|
entries[idx] = next;
|
|
27570
27820
|
} else {
|
|
27571
27821
|
next = {
|
|
27572
|
-
id: `gf-${
|
|
27822
|
+
id: `gf-${import_node_crypto7.default.randomBytes(6).toString("base64url")}`,
|
|
27573
27823
|
relPath: input.relPath,
|
|
27574
27824
|
from: input.from,
|
|
27575
27825
|
label: input.label,
|
|
@@ -27619,10 +27869,10 @@ var GroupFileStore = class {
|
|
|
27619
27869
|
};
|
|
27620
27870
|
|
|
27621
27871
|
// src/discovery/state-file.ts
|
|
27622
|
-
var
|
|
27623
|
-
var
|
|
27872
|
+
var import_node_fs19 = __toESM(require("fs"), 1);
|
|
27873
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
27624
27874
|
function defaultStateFilePath(dataDir) {
|
|
27625
|
-
return
|
|
27875
|
+
return import_node_path21.default.join(dataDir, "state.json");
|
|
27626
27876
|
}
|
|
27627
27877
|
function isPidAlive(pid) {
|
|
27628
27878
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
@@ -27644,7 +27894,7 @@ var StateFileManager = class {
|
|
|
27644
27894
|
}
|
|
27645
27895
|
read() {
|
|
27646
27896
|
try {
|
|
27647
|
-
const raw =
|
|
27897
|
+
const raw = import_node_fs19.default.readFileSync(this.file, "utf8");
|
|
27648
27898
|
const parsed = JSON.parse(raw);
|
|
27649
27899
|
return parsed;
|
|
27650
27900
|
} catch {
|
|
@@ -27658,34 +27908,34 @@ var StateFileManager = class {
|
|
|
27658
27908
|
return { status: "stale", existing };
|
|
27659
27909
|
}
|
|
27660
27910
|
write(state) {
|
|
27661
|
-
|
|
27911
|
+
import_node_fs19.default.mkdirSync(import_node_path21.default.dirname(this.file), { recursive: true });
|
|
27662
27912
|
const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
|
|
27663
|
-
|
|
27664
|
-
|
|
27913
|
+
import_node_fs19.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
|
|
27914
|
+
import_node_fs19.default.renameSync(tmp, this.file);
|
|
27665
27915
|
if (process.platform !== "win32") {
|
|
27666
27916
|
try {
|
|
27667
|
-
|
|
27917
|
+
import_node_fs19.default.chmodSync(this.file, 384);
|
|
27668
27918
|
} catch {
|
|
27669
27919
|
}
|
|
27670
27920
|
}
|
|
27671
27921
|
}
|
|
27672
27922
|
delete() {
|
|
27673
27923
|
try {
|
|
27674
|
-
|
|
27924
|
+
import_node_fs19.default.unlinkSync(this.file);
|
|
27675
27925
|
} catch {
|
|
27676
27926
|
}
|
|
27677
27927
|
}
|
|
27678
27928
|
};
|
|
27679
27929
|
|
|
27680
27930
|
// src/tunnel/tunnel-manager.ts
|
|
27681
|
-
var
|
|
27682
|
-
var
|
|
27683
|
-
var
|
|
27931
|
+
var import_node_fs23 = __toESM(require("fs"), 1);
|
|
27932
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
27933
|
+
var import_node_crypto8 = __toESM(require("crypto"), 1);
|
|
27684
27934
|
var import_node_child_process5 = require("child_process");
|
|
27685
27935
|
|
|
27686
27936
|
// src/tunnel/tunnel-store.ts
|
|
27687
|
-
var
|
|
27688
|
-
var
|
|
27937
|
+
var import_node_fs20 = __toESM(require("fs"), 1);
|
|
27938
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
27689
27939
|
var TunnelStore = class {
|
|
27690
27940
|
constructor(filePath) {
|
|
27691
27941
|
this.filePath = filePath;
|
|
@@ -27693,7 +27943,7 @@ var TunnelStore = class {
|
|
|
27693
27943
|
filePath;
|
|
27694
27944
|
async get() {
|
|
27695
27945
|
try {
|
|
27696
|
-
const raw = await
|
|
27946
|
+
const raw = await import_node_fs20.default.promises.readFile(this.filePath, "utf8");
|
|
27697
27947
|
const obj = JSON.parse(raw);
|
|
27698
27948
|
if (!isPersistedTunnel(obj)) return null;
|
|
27699
27949
|
return obj;
|
|
@@ -27704,22 +27954,22 @@ var TunnelStore = class {
|
|
|
27704
27954
|
}
|
|
27705
27955
|
}
|
|
27706
27956
|
async set(v2) {
|
|
27707
|
-
const dir =
|
|
27708
|
-
await
|
|
27957
|
+
const dir = import_node_path22.default.dirname(this.filePath);
|
|
27958
|
+
await import_node_fs20.default.promises.mkdir(dir, { recursive: true });
|
|
27709
27959
|
const data = JSON.stringify(v2, null, 2);
|
|
27710
27960
|
const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
27711
|
-
await
|
|
27961
|
+
await import_node_fs20.default.promises.writeFile(tmp, data, { mode: 384 });
|
|
27712
27962
|
if (process.platform !== "win32") {
|
|
27713
27963
|
try {
|
|
27714
|
-
await
|
|
27964
|
+
await import_node_fs20.default.promises.chmod(tmp, 384);
|
|
27715
27965
|
} catch {
|
|
27716
27966
|
}
|
|
27717
27967
|
}
|
|
27718
|
-
await
|
|
27968
|
+
await import_node_fs20.default.promises.rename(tmp, this.filePath);
|
|
27719
27969
|
}
|
|
27720
27970
|
async clear() {
|
|
27721
27971
|
try {
|
|
27722
|
-
await
|
|
27972
|
+
await import_node_fs20.default.promises.unlink(this.filePath);
|
|
27723
27973
|
} catch (err) {
|
|
27724
27974
|
const code = err?.code;
|
|
27725
27975
|
if (code !== "ENOENT") throw err;
|
|
@@ -27814,12 +28064,12 @@ function escape(v2) {
|
|
|
27814
28064
|
}
|
|
27815
28065
|
|
|
27816
28066
|
// src/tunnel/frpc-binary.ts
|
|
27817
|
-
var
|
|
28067
|
+
var import_node_fs21 = __toESM(require("fs"), 1);
|
|
27818
28068
|
var import_node_os10 = __toESM(require("os"), 1);
|
|
27819
|
-
var
|
|
28069
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
27820
28070
|
var import_node_child_process3 = require("child_process");
|
|
27821
28071
|
var import_node_stream2 = require("stream");
|
|
27822
|
-
var
|
|
28072
|
+
var import_promises2 = require("stream/promises");
|
|
27823
28073
|
var FRPC_VERSION = "0.68.0";
|
|
27824
28074
|
var UnsupportedPlatformError = class extends Error {
|
|
27825
28075
|
constructor(platform, arch) {
|
|
@@ -27848,20 +28098,20 @@ function frpcDownloadUrl(version2, p2) {
|
|
|
27848
28098
|
}
|
|
27849
28099
|
async function ensureFrpcBinary(opts) {
|
|
27850
28100
|
if (opts.override) {
|
|
27851
|
-
if (!
|
|
28101
|
+
if (!import_node_fs21.default.existsSync(opts.override)) {
|
|
27852
28102
|
throw new Error(`frpc binary not found at override path: ${opts.override}`);
|
|
27853
28103
|
}
|
|
27854
28104
|
return opts.override;
|
|
27855
28105
|
}
|
|
27856
28106
|
const version2 = opts.version ?? FRPC_VERSION;
|
|
27857
28107
|
const platform = opts.platform ?? detectPlatform();
|
|
27858
|
-
const binDir =
|
|
27859
|
-
|
|
28108
|
+
const binDir = import_node_path23.default.join(opts.dataDir, "bin");
|
|
28109
|
+
import_node_fs21.default.mkdirSync(binDir, { recursive: true });
|
|
27860
28110
|
cleanupStaleArtifacts(binDir);
|
|
27861
|
-
const stableBin =
|
|
27862
|
-
if (
|
|
28111
|
+
const stableBin = import_node_path23.default.join(binDir, "frpc");
|
|
28112
|
+
if (import_node_fs21.default.existsSync(stableBin)) return stableBin;
|
|
27863
28113
|
const partialBin = `${stableBin}.partial`;
|
|
27864
|
-
const tarballPath =
|
|
28114
|
+
const tarballPath = import_node_path23.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
|
|
27865
28115
|
try {
|
|
27866
28116
|
const url = frpcDownloadUrl(version2, platform);
|
|
27867
28117
|
await downloadToFile(url, tarballPath, opts.fetchImpl);
|
|
@@ -27870,8 +28120,8 @@ async function ensureFrpcBinary(opts) {
|
|
|
27870
28120
|
} else {
|
|
27871
28121
|
await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
|
|
27872
28122
|
}
|
|
27873
|
-
|
|
27874
|
-
|
|
28123
|
+
import_node_fs21.default.chmodSync(partialBin, 493);
|
|
28124
|
+
import_node_fs21.default.renameSync(partialBin, stableBin);
|
|
27875
28125
|
} finally {
|
|
27876
28126
|
safeUnlink(tarballPath);
|
|
27877
28127
|
safeUnlink(partialBin);
|
|
@@ -27881,15 +28131,15 @@ async function ensureFrpcBinary(opts) {
|
|
|
27881
28131
|
function cleanupStaleArtifacts(binDir) {
|
|
27882
28132
|
let entries;
|
|
27883
28133
|
try {
|
|
27884
|
-
entries =
|
|
28134
|
+
entries = import_node_fs21.default.readdirSync(binDir);
|
|
27885
28135
|
} catch {
|
|
27886
28136
|
return;
|
|
27887
28137
|
}
|
|
27888
28138
|
for (const name of entries) {
|
|
27889
28139
|
if (name.endsWith(".partial") || name.startsWith("extract-")) {
|
|
27890
|
-
const full =
|
|
28140
|
+
const full = import_node_path23.default.join(binDir, name);
|
|
27891
28141
|
try {
|
|
27892
|
-
|
|
28142
|
+
import_node_fs21.default.rmSync(full, { recursive: true, force: true });
|
|
27893
28143
|
} catch {
|
|
27894
28144
|
}
|
|
27895
28145
|
}
|
|
@@ -27897,7 +28147,7 @@ function cleanupStaleArtifacts(binDir) {
|
|
|
27897
28147
|
}
|
|
27898
28148
|
function safeUnlink(p2) {
|
|
27899
28149
|
try {
|
|
27900
|
-
|
|
28150
|
+
import_node_fs21.default.unlinkSync(p2);
|
|
27901
28151
|
} catch {
|
|
27902
28152
|
}
|
|
27903
28153
|
}
|
|
@@ -27908,13 +28158,13 @@ async function downloadToFile(url, dest, fetchImpl) {
|
|
|
27908
28158
|
if (!res.ok || !res.body) {
|
|
27909
28159
|
throw new Error(`download failed: ${res.status} ${res.statusText}`);
|
|
27910
28160
|
}
|
|
27911
|
-
const out =
|
|
28161
|
+
const out = import_node_fs21.default.createWriteStream(dest);
|
|
27912
28162
|
const nodeStream = import_node_stream2.Readable.fromWeb(res.body);
|
|
27913
|
-
await (0,
|
|
28163
|
+
await (0, import_promises2.pipeline)(nodeStream, out);
|
|
27914
28164
|
}
|
|
27915
28165
|
async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
|
|
27916
|
-
const work =
|
|
27917
|
-
|
|
28166
|
+
const work = import_node_path23.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
|
|
28167
|
+
import_node_fs21.default.mkdirSync(work, { recursive: true });
|
|
27918
28168
|
try {
|
|
27919
28169
|
await new Promise((resolve6, reject) => {
|
|
27920
28170
|
const proc = (0, import_node_child_process3.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
|
|
@@ -27922,32 +28172,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
|
|
|
27922
28172
|
proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
|
|
27923
28173
|
});
|
|
27924
28174
|
const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
|
|
27925
|
-
const src =
|
|
27926
|
-
if (!
|
|
28175
|
+
const src = import_node_path23.default.join(work, dirName, "frpc");
|
|
28176
|
+
if (!import_node_fs21.default.existsSync(src)) {
|
|
27927
28177
|
throw new Error(`frpc not found inside tarball at ${src}`);
|
|
27928
28178
|
}
|
|
27929
|
-
|
|
28179
|
+
import_node_fs21.default.copyFileSync(src, destBin);
|
|
27930
28180
|
} finally {
|
|
27931
|
-
|
|
28181
|
+
import_node_fs21.default.rmSync(work, { recursive: true, force: true });
|
|
27932
28182
|
}
|
|
27933
28183
|
}
|
|
27934
28184
|
|
|
27935
28185
|
// src/tunnel/frpc-process.ts
|
|
27936
|
-
var
|
|
27937
|
-
var
|
|
28186
|
+
var import_node_fs22 = __toESM(require("fs"), 1);
|
|
28187
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
27938
28188
|
var import_node_child_process4 = require("child_process");
|
|
27939
28189
|
function frpcPidFilePath(dataDir) {
|
|
27940
|
-
return
|
|
28190
|
+
return import_node_path24.default.join(dataDir, "frpc.pid");
|
|
27941
28191
|
}
|
|
27942
28192
|
function writeFrpcPid(dataDir, pid) {
|
|
27943
28193
|
try {
|
|
27944
|
-
|
|
28194
|
+
import_node_fs22.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
|
|
27945
28195
|
} catch {
|
|
27946
28196
|
}
|
|
27947
28197
|
}
|
|
27948
28198
|
function clearFrpcPid(dataDir) {
|
|
27949
28199
|
try {
|
|
27950
|
-
|
|
28200
|
+
import_node_fs22.default.unlinkSync(frpcPidFilePath(dataDir));
|
|
27951
28201
|
} catch {
|
|
27952
28202
|
}
|
|
27953
28203
|
}
|
|
@@ -27963,7 +28213,7 @@ function defaultIsPidAlive(pid) {
|
|
|
27963
28213
|
}
|
|
27964
28214
|
function defaultReadPidFile(file) {
|
|
27965
28215
|
try {
|
|
27966
|
-
return
|
|
28216
|
+
return import_node_fs22.default.readFileSync(file, "utf8");
|
|
27967
28217
|
} catch {
|
|
27968
28218
|
return null;
|
|
27969
28219
|
}
|
|
@@ -27979,7 +28229,7 @@ function defaultSleep(ms) {
|
|
|
27979
28229
|
}
|
|
27980
28230
|
async function killStaleFrpc(deps) {
|
|
27981
28231
|
const pidFile = frpcPidFilePath(deps.dataDir);
|
|
27982
|
-
const tomlPath =
|
|
28232
|
+
const tomlPath = import_node_path24.default.join(deps.dataDir, "frpc.toml");
|
|
27983
28233
|
const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
|
|
27984
28234
|
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
|
|
27985
28235
|
const killPid = deps.killPidImpl ?? defaultKillPid;
|
|
@@ -28003,7 +28253,7 @@ async function killStaleFrpc(deps) {
|
|
|
28003
28253
|
}
|
|
28004
28254
|
if (victims.size === 0) {
|
|
28005
28255
|
try {
|
|
28006
|
-
|
|
28256
|
+
import_node_fs22.default.unlinkSync(pidFile);
|
|
28007
28257
|
} catch {
|
|
28008
28258
|
}
|
|
28009
28259
|
return;
|
|
@@ -28014,7 +28264,7 @@ async function killStaleFrpc(deps) {
|
|
|
28014
28264
|
}
|
|
28015
28265
|
await sleep(deps.reapWaitMs ?? 300);
|
|
28016
28266
|
try {
|
|
28017
|
-
|
|
28267
|
+
import_node_fs22.default.unlinkSync(pidFile);
|
|
28018
28268
|
} catch {
|
|
28019
28269
|
}
|
|
28020
28270
|
}
|
|
@@ -28051,7 +28301,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
28051
28301
|
var TunnelManager = class {
|
|
28052
28302
|
constructor(deps) {
|
|
28053
28303
|
this.deps = deps;
|
|
28054
|
-
this.store = deps.store ?? new TunnelStore(
|
|
28304
|
+
this.store = deps.store ?? new TunnelStore(import_node_path25.default.join(deps.dataDir, "tunnel.json"));
|
|
28055
28305
|
this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
|
|
28056
28306
|
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
28057
28307
|
}
|
|
@@ -28178,8 +28428,8 @@ var TunnelManager = class {
|
|
|
28178
28428
|
dataDir: this.deps.dataDir,
|
|
28179
28429
|
override: this.deps.frpcBinaryOverride ?? void 0
|
|
28180
28430
|
});
|
|
28181
|
-
const tomlPath =
|
|
28182
|
-
const proxyName = `clawd-${t.subdomain}-${localPort}-${
|
|
28431
|
+
const tomlPath = import_node_path25.default.join(this.deps.dataDir, "frpc.toml");
|
|
28432
|
+
const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto8.default.randomBytes(3).toString("hex")}`;
|
|
28183
28433
|
const toml = buildFrpcToml({
|
|
28184
28434
|
serverAddr: t.frpsHost,
|
|
28185
28435
|
serverPort: t.frpsPort,
|
|
@@ -28189,12 +28439,12 @@ var TunnelManager = class {
|
|
|
28189
28439
|
localPort,
|
|
28190
28440
|
logLevel: "info"
|
|
28191
28441
|
});
|
|
28192
|
-
await
|
|
28442
|
+
await import_node_fs23.default.promises.writeFile(tomlPath, toml, { mode: 384 });
|
|
28193
28443
|
const proc = (this.deps.spawnImpl ?? import_node_child_process5.spawn)(frpcBin, ["-c", tomlPath], {
|
|
28194
28444
|
stdio: ["ignore", "pipe", "pipe"]
|
|
28195
28445
|
});
|
|
28196
|
-
const logFilePath =
|
|
28197
|
-
const logStream =
|
|
28446
|
+
const logFilePath = import_node_path25.default.join(this.deps.dataDir, "frpc.log");
|
|
28447
|
+
const logStream = import_node_fs23.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
|
|
28198
28448
|
logStream.on("error", () => {
|
|
28199
28449
|
});
|
|
28200
28450
|
const tee = (chunk) => {
|
|
@@ -28277,22 +28527,22 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
28277
28527
|
|
|
28278
28528
|
// src/tunnel/device-key.ts
|
|
28279
28529
|
var import_node_os11 = __toESM(require("os"), 1);
|
|
28280
|
-
var
|
|
28530
|
+
var import_node_crypto9 = __toESM(require("crypto"), 1);
|
|
28281
28531
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
28282
28532
|
function deriveStableDeviceKey(opts = {}) {
|
|
28283
28533
|
const hostname = opts.hostname ?? import_node_os11.default.hostname();
|
|
28284
28534
|
const uid = opts.uid ?? (typeof import_node_os11.default.userInfo === "function" ? import_node_os11.default.userInfo().uid : 0);
|
|
28285
28535
|
const input = `${hostname}::${uid}`;
|
|
28286
|
-
return
|
|
28536
|
+
return import_node_crypto9.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
28287
28537
|
}
|
|
28288
28538
|
|
|
28289
28539
|
// src/auth-store.ts
|
|
28290
|
-
var
|
|
28291
|
-
var
|
|
28292
|
-
var
|
|
28540
|
+
var import_node_fs24 = __toESM(require("fs"), 1);
|
|
28541
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
28542
|
+
var import_node_crypto10 = __toESM(require("crypto"), 1);
|
|
28293
28543
|
var AUTH_FILE_NAME = "auth.json";
|
|
28294
28544
|
function authFilePath(dataDir) {
|
|
28295
|
-
return
|
|
28545
|
+
return import_node_path26.default.join(dataDir, AUTH_FILE_NAME);
|
|
28296
28546
|
}
|
|
28297
28547
|
function loadOrCreateAuthFile(opts) {
|
|
28298
28548
|
const file = authFilePath(opts.dataDir);
|
|
@@ -28317,14 +28567,14 @@ function loadOrCreateAuthFile(opts) {
|
|
|
28317
28567
|
return next;
|
|
28318
28568
|
}
|
|
28319
28569
|
function defaultGenerateToken2() {
|
|
28320
|
-
return
|
|
28570
|
+
return import_node_crypto10.default.randomBytes(32).toString("base64url");
|
|
28321
28571
|
}
|
|
28322
28572
|
function defaultGenerateOwnerPrincipalId() {
|
|
28323
|
-
return `owner-${
|
|
28573
|
+
return `owner-${import_node_crypto10.default.randomUUID()}`;
|
|
28324
28574
|
}
|
|
28325
28575
|
function readAuthFile(file) {
|
|
28326
28576
|
try {
|
|
28327
|
-
const raw =
|
|
28577
|
+
const raw = import_node_fs24.default.readFileSync(file, "utf8");
|
|
28328
28578
|
const parsed = JSON.parse(raw);
|
|
28329
28579
|
if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
|
|
28330
28580
|
return null;
|
|
@@ -28342,25 +28592,25 @@ function readAuthFile(file) {
|
|
|
28342
28592
|
}
|
|
28343
28593
|
}
|
|
28344
28594
|
function writeAuthFile(file, content) {
|
|
28345
|
-
|
|
28346
|
-
|
|
28595
|
+
import_node_fs24.default.mkdirSync(import_node_path26.default.dirname(file), { recursive: true });
|
|
28596
|
+
import_node_fs24.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
28347
28597
|
try {
|
|
28348
|
-
|
|
28598
|
+
import_node_fs24.default.chmodSync(file, 384);
|
|
28349
28599
|
} catch {
|
|
28350
28600
|
}
|
|
28351
28601
|
}
|
|
28352
28602
|
|
|
28353
28603
|
// src/owner-profile.ts
|
|
28354
|
-
var
|
|
28604
|
+
var import_node_fs25 = __toESM(require("fs"), 1);
|
|
28355
28605
|
var import_node_os12 = __toESM(require("os"), 1);
|
|
28356
|
-
var
|
|
28606
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
28357
28607
|
var PROFILE_FILENAME = "profile.json";
|
|
28358
28608
|
function loadOwnerDisplayName(dataDir) {
|
|
28359
28609
|
const fallback = import_node_os12.default.userInfo().username;
|
|
28360
|
-
const profilePath =
|
|
28610
|
+
const profilePath = import_node_path27.default.join(dataDir, PROFILE_FILENAME);
|
|
28361
28611
|
let raw;
|
|
28362
28612
|
try {
|
|
28363
|
-
raw =
|
|
28613
|
+
raw = import_node_fs25.default.readFileSync(profilePath, "utf8");
|
|
28364
28614
|
} catch {
|
|
28365
28615
|
return fallback;
|
|
28366
28616
|
}
|
|
@@ -28389,12 +28639,12 @@ init_protocol();
|
|
|
28389
28639
|
init_protocol();
|
|
28390
28640
|
|
|
28391
28641
|
// src/session/fork.ts
|
|
28392
|
-
var
|
|
28642
|
+
var import_node_fs26 = __toESM(require("fs"), 1);
|
|
28393
28643
|
var import_node_os13 = __toESM(require("os"), 1);
|
|
28394
|
-
var
|
|
28644
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
28395
28645
|
init_claude_history();
|
|
28396
28646
|
function readJsonlEntries(file) {
|
|
28397
|
-
const raw =
|
|
28647
|
+
const raw = import_node_fs26.default.readFileSync(file, "utf8");
|
|
28398
28648
|
const out = [];
|
|
28399
28649
|
for (const line of raw.split("\n")) {
|
|
28400
28650
|
const t = line.trim();
|
|
@@ -28407,10 +28657,10 @@ function readJsonlEntries(file) {
|
|
|
28407
28657
|
return out;
|
|
28408
28658
|
}
|
|
28409
28659
|
function forkSession(input) {
|
|
28410
|
-
const baseDir = input.baseDir ??
|
|
28411
|
-
const projectDir =
|
|
28412
|
-
const sourceFile =
|
|
28413
|
-
if (!
|
|
28660
|
+
const baseDir = input.baseDir ?? import_node_path28.default.join(import_node_os13.default.homedir(), ".claude");
|
|
28661
|
+
const projectDir = import_node_path28.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
28662
|
+
const sourceFile = import_node_path28.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
28663
|
+
if (!import_node_fs26.default.existsSync(sourceFile)) {
|
|
28414
28664
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
28415
28665
|
}
|
|
28416
28666
|
const entries = readJsonlEntries(sourceFile);
|
|
@@ -28440,9 +28690,9 @@ function forkSession(input) {
|
|
|
28440
28690
|
}
|
|
28441
28691
|
forkedLines.push(JSON.stringify(forked));
|
|
28442
28692
|
}
|
|
28443
|
-
const forkedFilePath =
|
|
28444
|
-
|
|
28445
|
-
|
|
28693
|
+
const forkedFilePath = import_node_path28.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
28694
|
+
import_node_fs26.default.mkdirSync(projectDir, { recursive: true });
|
|
28695
|
+
import_node_fs26.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
28446
28696
|
return { forkedToolSessionId, forkedFilePath };
|
|
28447
28697
|
}
|
|
28448
28698
|
|
|
@@ -28730,28 +28980,6 @@ function buildSessionHandlers(deps) {
|
|
|
28730
28980
|
}
|
|
28731
28981
|
};
|
|
28732
28982
|
};
|
|
28733
|
-
const peerSessionUpsert = async (frame, _client, ctx) => {
|
|
28734
|
-
if (ctx && ctx.principal.kind !== "owner") {
|
|
28735
|
-
throw new ClawdError(
|
|
28736
|
-
ERROR_CODES.UNAUTHORIZED,
|
|
28737
|
-
`principal ${ctx.principal.kind}:${ctx.principal.id} cannot peerSession:upsert`
|
|
28738
|
-
);
|
|
28739
|
-
}
|
|
28740
|
-
const file = PeerSessionUpsertArgs.parse(frame);
|
|
28741
|
-
manager.mirrorUpsert(file);
|
|
28742
|
-
return { response: { type: "peerSession:upsert:ok" }, broadcast: [] };
|
|
28743
|
-
};
|
|
28744
|
-
const peerSessionRemove = async (frame, _client, ctx) => {
|
|
28745
|
-
if (ctx && ctx.principal.kind !== "owner") {
|
|
28746
|
-
throw new ClawdError(
|
|
28747
|
-
ERROR_CODES.UNAUTHORIZED,
|
|
28748
|
-
`principal ${ctx.principal.kind}:${ctx.principal.id} cannot peerSession:remove`
|
|
28749
|
-
);
|
|
28750
|
-
}
|
|
28751
|
-
const args = PeerSessionRemoveArgs.parse(frame);
|
|
28752
|
-
manager.mirrorRemove(args.sessionId);
|
|
28753
|
-
return { response: { type: "peerSession:remove:ok" }, broadcast: [] };
|
|
28754
|
-
};
|
|
28755
28983
|
return {
|
|
28756
28984
|
"session:create": create,
|
|
28757
28985
|
"session:list": list,
|
|
@@ -28777,9 +29005,7 @@ function buildSessionHandlers(deps) {
|
|
|
28777
29005
|
"session:cancelQuestion": cancelQuestion,
|
|
28778
29006
|
"session:pty:input": ptyInput,
|
|
28779
29007
|
"session:pty:resize": ptyResize,
|
|
28780
|
-
"session:pty:snapshot": ptySnapshot
|
|
28781
|
-
"peerSession:upsert": peerSessionUpsert,
|
|
28782
|
-
"peerSession:remove": peerSessionRemove
|
|
29008
|
+
"session:pty:snapshot": ptySnapshot
|
|
28783
29009
|
};
|
|
28784
29010
|
}
|
|
28785
29011
|
|
|
@@ -28798,7 +29024,7 @@ function buildPermissionHandlers(deps) {
|
|
|
28798
29024
|
}
|
|
28799
29025
|
|
|
28800
29026
|
// src/handlers/history.ts
|
|
28801
|
-
var
|
|
29027
|
+
var path38 = __toESM(require("path"), 1);
|
|
28802
29028
|
init_protocol();
|
|
28803
29029
|
|
|
28804
29030
|
// src/session/recent-dirs.ts
|
|
@@ -28816,7 +29042,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
28816
29042
|
}
|
|
28817
29043
|
|
|
28818
29044
|
// src/permission/persona-paths.ts
|
|
28819
|
-
var
|
|
29045
|
+
var path37 = __toESM(require("path"), 1);
|
|
28820
29046
|
function getAllowedPersonaIds(grants, action) {
|
|
28821
29047
|
const ids = /* @__PURE__ */ new Set();
|
|
28822
29048
|
for (const g2 of grants) {
|
|
@@ -28829,26 +29055,26 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
28829
29055
|
return ids;
|
|
28830
29056
|
}
|
|
28831
29057
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read") {
|
|
28832
|
-
const root =
|
|
28833
|
-
const target =
|
|
28834
|
-
const sep3 = root.endsWith(
|
|
29058
|
+
const root = path37.resolve(personaRoot);
|
|
29059
|
+
const target = path37.resolve(absPath);
|
|
29060
|
+
const sep3 = root.endsWith(path37.sep) ? "" : path37.sep;
|
|
28835
29061
|
if (!target.startsWith(root + sep3)) return false;
|
|
28836
|
-
const rel =
|
|
29062
|
+
const rel = path37.relative(root, target);
|
|
28837
29063
|
if (!rel || rel.startsWith("..")) return false;
|
|
28838
|
-
const personaId = rel.split(
|
|
29064
|
+
const personaId = rel.split(path37.sep)[0];
|
|
28839
29065
|
if (!personaId) return false;
|
|
28840
29066
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
28841
29067
|
if (allowed === "*") return true;
|
|
28842
29068
|
return allowed.has(personaId);
|
|
28843
29069
|
}
|
|
28844
29070
|
function personaIdFromPath(absPath, personaRoot) {
|
|
28845
|
-
const root =
|
|
28846
|
-
const target =
|
|
28847
|
-
const sep3 = root.endsWith(
|
|
29071
|
+
const root = path37.resolve(personaRoot);
|
|
29072
|
+
const target = path37.resolve(absPath);
|
|
29073
|
+
const sep3 = root.endsWith(path37.sep) ? "" : path37.sep;
|
|
28848
29074
|
if (!target.startsWith(root + sep3)) return null;
|
|
28849
|
-
const rel =
|
|
29075
|
+
const rel = path37.relative(root, target);
|
|
28850
29076
|
if (!rel || rel.startsWith("..")) return null;
|
|
28851
|
-
const id = rel.split(
|
|
29077
|
+
const id = rel.split(path37.sep)[0];
|
|
28852
29078
|
return id || null;
|
|
28853
29079
|
}
|
|
28854
29080
|
|
|
@@ -28873,7 +29099,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28873
29099
|
if (!pid) return false;
|
|
28874
29100
|
return isGuestPathAllowed(
|
|
28875
29101
|
ctx.grants,
|
|
28876
|
-
|
|
29102
|
+
path38.join(personaRoot, pid),
|
|
28877
29103
|
personaRoot,
|
|
28878
29104
|
"read"
|
|
28879
29105
|
);
|
|
@@ -28884,7 +29110,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28884
29110
|
};
|
|
28885
29111
|
const list = async (frame, _client, ctx) => {
|
|
28886
29112
|
const args = HistoryListArgs.parse(frame);
|
|
28887
|
-
assertGuestPath(ctx,
|
|
29113
|
+
assertGuestPath(ctx, path38.resolve(args.projectPath), personaRoot, "history:list");
|
|
28888
29114
|
const sessions = await history.listSessions(args);
|
|
28889
29115
|
return { response: { type: "history:list", sessions } };
|
|
28890
29116
|
};
|
|
@@ -28916,13 +29142,13 @@ function buildHistoryHandlers(deps) {
|
|
|
28916
29142
|
};
|
|
28917
29143
|
const subagents = async (frame, _client, ctx) => {
|
|
28918
29144
|
const args = HistorySubagentsArgs.parse(frame);
|
|
28919
|
-
assertGuestPath(ctx,
|
|
29145
|
+
assertGuestPath(ctx, path38.resolve(args.cwd), personaRoot, "history:subagents");
|
|
28920
29146
|
const subs = await history.listSubagents(args);
|
|
28921
29147
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
28922
29148
|
};
|
|
28923
29149
|
const subagentRead = async (frame, _client, ctx) => {
|
|
28924
29150
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
28925
|
-
assertGuestPath(ctx,
|
|
29151
|
+
assertGuestPath(ctx, path38.resolve(args.cwd), personaRoot, "history:subagent-read");
|
|
28926
29152
|
const res = await history.readSubagent(args);
|
|
28927
29153
|
return { response: { type: "history:subagent-read", ...res } };
|
|
28928
29154
|
};
|
|
@@ -28930,7 +29156,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28930
29156
|
const dirs = listRecentDirs(store);
|
|
28931
29157
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
28932
29158
|
const filtered = dirs.filter(
|
|
28933
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
29159
|
+
(d) => isGuestPathAllowed(ctx.grants, path38.resolve(d.cwd), personaRoot, "read")
|
|
28934
29160
|
);
|
|
28935
29161
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
28936
29162
|
}
|
|
@@ -28947,7 +29173,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28947
29173
|
}
|
|
28948
29174
|
|
|
28949
29175
|
// src/handlers/workspace.ts
|
|
28950
|
-
var
|
|
29176
|
+
var path39 = __toESM(require("path"), 1);
|
|
28951
29177
|
var os14 = __toESM(require("os"), 1);
|
|
28952
29178
|
init_protocol();
|
|
28953
29179
|
init_protocol();
|
|
@@ -28988,22 +29214,22 @@ function buildWorkspaceHandlers(deps) {
|
|
|
28988
29214
|
const args = WorkspaceListArgs.parse(frame);
|
|
28989
29215
|
const isGuest = ctx?.principal.kind === "guest";
|
|
28990
29216
|
const fallbackCwd = isGuest && personaRoot ? personaRoot : os14.homedir();
|
|
28991
|
-
const resolvedCwd =
|
|
28992
|
-
const target = args.path ?
|
|
29217
|
+
const resolvedCwd = path39.resolve(args.cwd ?? fallbackCwd);
|
|
29218
|
+
const target = args.path ? path39.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
28993
29219
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list");
|
|
28994
29220
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
28995
29221
|
return { response: { type: "workspace:list", ...res } };
|
|
28996
29222
|
};
|
|
28997
29223
|
const read = async (frame, _client, ctx) => {
|
|
28998
29224
|
const args = WorkspaceReadArgs.parse(frame);
|
|
28999
|
-
const target =
|
|
29225
|
+
const target = path39.isAbsolute(args.path) ? path39.resolve(args.path) : path39.resolve(args.cwd, args.path);
|
|
29000
29226
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read");
|
|
29001
29227
|
const res = workspace.read(args);
|
|
29002
29228
|
return { response: { type: "workspace:read", ...res } };
|
|
29003
29229
|
};
|
|
29004
29230
|
const skillsList = async (frame, _client, ctx) => {
|
|
29005
29231
|
const args = SkillsListArgs.parse(frame);
|
|
29006
|
-
const cwdAbs =
|
|
29232
|
+
const cwdAbs = path39.resolve(args.cwd);
|
|
29007
29233
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list");
|
|
29008
29234
|
const list2 = skills.list(args);
|
|
29009
29235
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -29015,7 +29241,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
29015
29241
|
};
|
|
29016
29242
|
const agentsList = async (frame, _client, ctx) => {
|
|
29017
29243
|
const args = AgentsListArgs.parse(frame);
|
|
29018
|
-
const cwdAbs =
|
|
29244
|
+
const cwdAbs = path39.resolve(args.cwd);
|
|
29019
29245
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list");
|
|
29020
29246
|
const list2 = agents.list(args);
|
|
29021
29247
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -29034,20 +29260,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
29034
29260
|
}
|
|
29035
29261
|
|
|
29036
29262
|
// src/handlers/git.ts
|
|
29037
|
-
var
|
|
29263
|
+
var path41 = __toESM(require("path"), 1);
|
|
29038
29264
|
init_protocol();
|
|
29039
29265
|
init_protocol();
|
|
29040
29266
|
|
|
29041
29267
|
// src/workspace/git.ts
|
|
29042
29268
|
var import_node_child_process6 = require("child_process");
|
|
29043
|
-
var
|
|
29044
|
-
var
|
|
29269
|
+
var import_node_fs27 = __toESM(require("fs"), 1);
|
|
29270
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
29045
29271
|
var import_node_util = require("util");
|
|
29046
29272
|
var pexec = (0, import_node_util.promisify)(import_node_child_process6.execFile);
|
|
29047
29273
|
function normalizePath(p2) {
|
|
29048
|
-
const resolved =
|
|
29274
|
+
const resolved = import_node_path29.default.resolve(p2);
|
|
29049
29275
|
try {
|
|
29050
|
-
return
|
|
29276
|
+
return import_node_fs27.default.realpathSync(resolved);
|
|
29051
29277
|
} catch {
|
|
29052
29278
|
return resolved;
|
|
29053
29279
|
}
|
|
@@ -29120,7 +29346,7 @@ async function listGitBranches(cwd) {
|
|
|
29120
29346
|
// src/handlers/git.ts
|
|
29121
29347
|
function assertGuestCwd(ctx, cwd, personaRoot, method) {
|
|
29122
29348
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
29123
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
29349
|
+
if (!isGuestPathAllowed(ctx.grants, path41.resolve(cwd), personaRoot, "read")) {
|
|
29124
29350
|
throw new ClawdError(
|
|
29125
29351
|
ERROR_CODES.UNAUTHORIZED,
|
|
29126
29352
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -29607,7 +29833,7 @@ function buildPersonaHandlers(deps) {
|
|
|
29607
29833
|
}
|
|
29608
29834
|
|
|
29609
29835
|
// src/handlers/attachment.ts
|
|
29610
|
-
var
|
|
29836
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
29611
29837
|
init_protocol();
|
|
29612
29838
|
init_protocol();
|
|
29613
29839
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -29661,12 +29887,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
29661
29887
|
`session ${args.sessionId} scope unresolved`
|
|
29662
29888
|
);
|
|
29663
29889
|
}
|
|
29664
|
-
const cwdAbs =
|
|
29665
|
-
const candidateAbs =
|
|
29890
|
+
const cwdAbs = import_node_path30.default.resolve(sessionFile.cwd);
|
|
29891
|
+
const candidateAbs = import_node_path30.default.isAbsolute(args.relPath) ? import_node_path30.default.resolve(args.relPath) : import_node_path30.default.resolve(cwdAbs, args.relPath);
|
|
29666
29892
|
assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl");
|
|
29667
29893
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
29668
29894
|
const entry = entries.find((e) => {
|
|
29669
|
-
const storedAbs =
|
|
29895
|
+
const storedAbs = import_node_path30.default.isAbsolute(e.relPath) ? import_node_path30.default.resolve(e.relPath) : import_node_path30.default.resolve(cwdAbs, e.relPath);
|
|
29670
29896
|
return storedAbs === candidateAbs && !e.stale;
|
|
29671
29897
|
});
|
|
29672
29898
|
if (!entry) {
|
|
@@ -29690,7 +29916,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29690
29916
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
29691
29917
|
const f = deps.sessionStore.read(sessionId);
|
|
29692
29918
|
if (!f) return;
|
|
29693
|
-
assertGuestAttachmentPath(ctx,
|
|
29919
|
+
assertGuestAttachmentPath(ctx, import_node_path30.default.resolve(f.cwd), deps.personaRoot, method);
|
|
29694
29920
|
}
|
|
29695
29921
|
const groupAdd = async (frame, _client, ctx) => {
|
|
29696
29922
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -29731,7 +29957,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29731
29957
|
}
|
|
29732
29958
|
const entries = deps.groupFileStore.list(scope, parsed.data.sessionId);
|
|
29733
29959
|
const target = entries.find((e) => e.relPath === parsed.data.relPath);
|
|
29734
|
-
if (target?.from === "owner") {
|
|
29960
|
+
if (target?.from === "owner" || target?.from === "user-upload") {
|
|
29735
29961
|
deps.groupFileStore.remove(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
29736
29962
|
} else {
|
|
29737
29963
|
deps.groupFileStore.markStale(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
@@ -29857,11 +30083,6 @@ var METHOD_GRANT_MAP = {
|
|
|
29857
30083
|
"session:pin": CAPABILITY_SCOPED,
|
|
29858
30084
|
"session:reorderPins": ADMIN_ANY,
|
|
29859
30085
|
// owner 全局操作,无 personaId 维度
|
|
29860
|
-
// mirror peer sessions (2026-05-26):owner-only — guest 不应能写/删本机 mirror SessionFile
|
|
29861
|
-
// (会绕过 #739 隔离规则把伪造身份的 SessionFile 写进 default store)。handler 端内部 ctx
|
|
29862
|
-
// 校验,dispatcher 层一刀切 admin 也兜底。
|
|
29863
|
-
"peerSession:upsert": ADMIN_ANY,
|
|
29864
|
-
"peerSession:remove": ADMIN_ANY,
|
|
29865
30086
|
"permission:respond": CAPABILITY_SCOPED,
|
|
29866
30087
|
"session:answerQuestion": CAPABILITY_SCOPED,
|
|
29867
30088
|
"session:cancelQuestion": CAPABILITY_SCOPED,
|
|
@@ -29932,7 +30153,7 @@ function computeGrantForFrame(method, frame) {
|
|
|
29932
30153
|
async function startDaemon(config) {
|
|
29933
30154
|
const logger = createLogger({
|
|
29934
30155
|
level: config.logLevel,
|
|
29935
|
-
file:
|
|
30156
|
+
file: import_node_path31.default.join(config.dataDir, "clawd.log")
|
|
29936
30157
|
});
|
|
29937
30158
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
29938
30159
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
@@ -30019,7 +30240,7 @@ async function startDaemon(config) {
|
|
|
30019
30240
|
const agents = new AgentsScanner();
|
|
30020
30241
|
const history = new ClaudeHistoryReader();
|
|
30021
30242
|
let transport = null;
|
|
30022
|
-
const personaStore = new PersonaStore(
|
|
30243
|
+
const personaStore = new PersonaStore(import_node_path31.default.join(config.dataDir, "personas"));
|
|
30023
30244
|
const defaultsRoot = findDefaultsRoot();
|
|
30024
30245
|
if (defaultsRoot) {
|
|
30025
30246
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -30036,7 +30257,7 @@ async function startDaemon(config) {
|
|
|
30036
30257
|
getAdapter,
|
|
30037
30258
|
historyReader: history,
|
|
30038
30259
|
dataDir: config.dataDir,
|
|
30039
|
-
personaRoot:
|
|
30260
|
+
personaRoot: import_node_path31.default.join(config.dataDir, "personas"),
|
|
30040
30261
|
personaStore,
|
|
30041
30262
|
ownerDisplayName,
|
|
30042
30263
|
ownerPrincipalId,
|
|
@@ -30060,10 +30281,10 @@ async function startDaemon(config) {
|
|
|
30060
30281
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
30061
30282
|
attachmentGroup: {
|
|
30062
30283
|
onFileEdit: (input) => {
|
|
30063
|
-
const absPath =
|
|
30284
|
+
const absPath = import_node_path31.default.isAbsolute(input.relPath) ? input.relPath : import_node_path31.default.join(input.cwd, input.relPath);
|
|
30064
30285
|
let size = 0;
|
|
30065
30286
|
try {
|
|
30066
|
-
size =
|
|
30287
|
+
size = import_node_fs28.default.statSync(absPath).size;
|
|
30067
30288
|
} catch (err) {
|
|
30068
30289
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
30069
30290
|
sessionId: input.sessionId,
|
|
@@ -30181,10 +30402,10 @@ async function startDaemon(config) {
|
|
|
30181
30402
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
30182
30403
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30183
30404
|
// guest path guard:candidate 必须在 personaRoot 子树下
|
|
30184
|
-
personaRoot:
|
|
30405
|
+
personaRoot: import_node_path31.default.join(config.dataDir, "personas")
|
|
30185
30406
|
},
|
|
30186
30407
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
30187
|
-
personaRoot:
|
|
30408
|
+
personaRoot: import_node_path31.default.join(config.dataDir, "personas"),
|
|
30188
30409
|
// capability:list / delete handler 依赖
|
|
30189
30410
|
capabilityManager,
|
|
30190
30411
|
// personal-cap:get 返回的 shareBaseUrl 用此 base URL:
|
|
@@ -30220,7 +30441,19 @@ async function startDaemon(config) {
|
|
|
30220
30441
|
sessionStore: store,
|
|
30221
30442
|
// /files HMAC verify 用 auth.json 的 signSecret 字段(与 attachment.signUrl 同源)。
|
|
30222
30443
|
// --auth-token CLI 模式没 signSecret → 路由返 501,sign URL 功能整体禁用。
|
|
30223
|
-
getSignSecret: () => authFile?.signSecret ?? null
|
|
30444
|
+
getSignSecret: () => authFile?.signSecret ?? null,
|
|
30445
|
+
// chatinput 附件上传(POST /attachments/upload,spec §6.1)。upload pipeline
|
|
30446
|
+
// 在 session.cwd 下的 .attachments/ 落盘;signSecret 与 /files HMAC 同源;
|
|
30447
|
+
// tunnel 未启时返 503 TUNNEL_NOT_READY 让前端显式提示。
|
|
30448
|
+
attachmentUpload: {
|
|
30449
|
+
groupFileStore,
|
|
30450
|
+
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30451
|
+
getSessionDir: (sid) => {
|
|
30452
|
+
const f = manager.findOwnedSession(sid);
|
|
30453
|
+
return f ? f.cwd : null;
|
|
30454
|
+
},
|
|
30455
|
+
getHttpBaseUrl: () => currentTunnelUrl ? getHttpBaseUrl() : null
|
|
30456
|
+
}
|
|
30224
30457
|
});
|
|
30225
30458
|
wsServer = new LocalWsServer({
|
|
30226
30459
|
host: config.host,
|
|
@@ -30385,8 +30618,8 @@ async function startDaemon(config) {
|
|
|
30385
30618
|
const lines = [
|
|
30386
30619
|
`Tunnel: ${r.url}`,
|
|
30387
30620
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
30388
|
-
`Frpc config: ${
|
|
30389
|
-
`Frpc log: ${
|
|
30621
|
+
`Frpc config: ${import_node_path31.default.join(config.dataDir, "frpc.toml")}`,
|
|
30622
|
+
`Frpc log: ${import_node_path31.default.join(config.dataDir, "frpc.log")}`
|
|
30390
30623
|
];
|
|
30391
30624
|
const width = Math.max(...lines.map((l) => l.length));
|
|
30392
30625
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -30399,8 +30632,8 @@ ${bar}
|
|
|
30399
30632
|
|
|
30400
30633
|
`);
|
|
30401
30634
|
try {
|
|
30402
|
-
const connectPath =
|
|
30403
|
-
|
|
30635
|
+
const connectPath = import_node_path31.default.join(config.dataDir, "connect.txt");
|
|
30636
|
+
import_node_fs28.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
30404
30637
|
} catch {
|
|
30405
30638
|
}
|
|
30406
30639
|
} catch (err) {
|
|
@@ -30425,8 +30658,29 @@ ${bar}
|
|
|
30425
30658
|
logger.warn("tunnel unavailable, degraded to local mode", { reason: tunnelError });
|
|
30426
30659
|
}
|
|
30427
30660
|
}
|
|
30661
|
+
const tickAttachmentGc = () => {
|
|
30662
|
+
try {
|
|
30663
|
+
runAttachmentGc({
|
|
30664
|
+
dataDir: config.dataDir,
|
|
30665
|
+
groupFileStore,
|
|
30666
|
+
sessionScopes: manager.listOwnedSessionScopes().map(({ scope, sessionId }) => ({
|
|
30667
|
+
scope,
|
|
30668
|
+
sessionId
|
|
30669
|
+
})),
|
|
30670
|
+
// 注入 cwd 让 GC 直接定位 .attachments 目录(与 upload 落盘点一致)
|
|
30671
|
+
getSessionCwd: (sid) => manager.findOwnedSession(sid)?.cwd ?? null,
|
|
30672
|
+
logger
|
|
30673
|
+
});
|
|
30674
|
+
} catch (err) {
|
|
30675
|
+
logger.warn("attachment gc tick failed", { err: err.message });
|
|
30676
|
+
}
|
|
30677
|
+
};
|
|
30678
|
+
setImmediate(tickAttachmentGc);
|
|
30679
|
+
const attachmentGcInterval = setInterval(tickAttachmentGc, 24 * 3600 * 1e3);
|
|
30680
|
+
attachmentGcInterval.unref();
|
|
30428
30681
|
const shutdown = async () => {
|
|
30429
30682
|
logger.info("stopping clawd");
|
|
30683
|
+
clearInterval(attachmentGcInterval);
|
|
30430
30684
|
observer.stopAll();
|
|
30431
30685
|
manager.stopAll();
|
|
30432
30686
|
if (tunnelMgr) {
|
|
@@ -30444,9 +30698,9 @@ ${bar}
|
|
|
30444
30698
|
};
|
|
30445
30699
|
}
|
|
30446
30700
|
function migrateDropPersonsDir(dataDir) {
|
|
30447
|
-
const dir =
|
|
30701
|
+
const dir = import_node_path31.default.join(dataDir, "persons");
|
|
30448
30702
|
try {
|
|
30449
|
-
|
|
30703
|
+
import_node_fs28.default.rmSync(dir, { recursive: true, force: true });
|
|
30450
30704
|
} catch {
|
|
30451
30705
|
}
|
|
30452
30706
|
}
|