@clawos-dev/clawd 0.2.85-beta.158.934b6ca → 0.2.85-beta.159.97c16b3
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 +565 -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: path45, errorMaps, issueData } = params;
|
|
647
|
+
const fullPath = [...path45, ...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, path45, key) {
|
|
967
959
|
this._cachedPath = [];
|
|
968
960
|
this.parent = parent;
|
|
969
961
|
this.data = value;
|
|
970
|
-
this._path =
|
|
962
|
+
this._path = path45;
|
|
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 path45 = req.path;
|
|
5594
|
+
_req.url = typeof path45 === "string" ? path45 : 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(path45) {
|
|
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 < path45.length; i++) {
|
|
5766
|
+
const char = path45[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 path45 of paths) {
|
|
5898
|
+
const parts = parsePath(path45);
|
|
5934
5899
|
if (parts.includes("*")) {
|
|
5935
|
-
redactWildcardPath(obj, parts, censor,
|
|
5900
|
+
redactWildcardPath(obj, parts, censor, path45, 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, path45) => {
|
|
5986
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path45];
|
|
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 path45 of pathsToClone) {
|
|
6022
|
+
const parts = parsePath(path45);
|
|
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(path45) {
|
|
6075
|
+
if (typeof path45 !== "string") {
|
|
6111
6076
|
throw new Error("Paths must be (non-empty) strings");
|
|
6112
6077
|
}
|
|
6113
|
-
if (
|
|
6078
|
+
if (path45 === "") {
|
|
6114
6079
|
throw new Error("Invalid redaction path ()");
|
|
6115
6080
|
}
|
|
6116
|
-
if (
|
|
6117
|
-
throw new Error(`Invalid redaction path (${
|
|
6081
|
+
if (path45.includes("..")) {
|
|
6082
|
+
throw new Error(`Invalid redaction path (${path45})`);
|
|
6118
6083
|
}
|
|
6119
|
-
if (
|
|
6120
|
-
throw new Error(`Invalid redaction path (${
|
|
6084
|
+
if (path45.includes(",")) {
|
|
6085
|
+
throw new Error(`Invalid redaction path (${path45})`);
|
|
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 < path45.length; i++) {
|
|
6091
|
+
const char = path45[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 (${path45})`);
|
|
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 (${path45})`);
|
|
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 path45 of paths) {
|
|
6118
|
+
validatePath(path45);
|
|
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, path45) => {
|
|
6287
|
+
return censor(value, [k2, ...path45]);
|
|
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 fs36 = require("fs");
|
|
6541
6506
|
var EventEmitter2 = require("events");
|
|
6542
6507
|
var inherits = require("util").inherits;
|
|
6543
|
-
var
|
|
6508
|
+
var path45 = 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) fs36.mkdirSync(path45.dirname(file), { recursive: true });
|
|
6563
|
+
const fd = fs36.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
|
+
fs36.mkdir(path45.dirname(file), { recursive: true }, (err) => {
|
|
6606
6571
|
if (err) return fileOpened(err);
|
|
6607
|
-
|
|
6572
|
+
fs36.open(file, flags, mode, fileOpened);
|
|
6608
6573
|
});
|
|
6609
6574
|
} else {
|
|
6610
|
-
|
|
6575
|
+
fs36.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 = () => fs36.writeSync(this.fd, this._writingBuf);
|
|
6617
|
+
fsWrite = () => fs36.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 fs36.writeSync(this.fd, this._writingBuf);
|
|
6662
6627
|
}
|
|
6663
|
-
return
|
|
6628
|
+
return fs36.writeSync(this.fd, this._writingBuf, "utf8");
|
|
6664
6629
|
};
|
|
6665
6630
|
fsWrite = () => {
|
|
6666
6631
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
6667
|
-
return
|
|
6632
|
+
return fs36.write(this.fd, this._writingBuf, this.release);
|
|
6668
6633
|
}
|
|
6669
|
-
return
|
|
6634
|
+
return fs36.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
|
+
fs36.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
|
+
fs36.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
|
+
fs36.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) ? fs36.writeSync(this.fd, buf) : fs36.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
|
+
fs36.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 = fs36.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) ? fs36.writeSync(this.fd, this._writingBuf) : fs36.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
|
+
fs36.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 = fs36.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
|
+
fs36.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
|
+
fs36.fsync(sonic.fd, closeWrapped);
|
|
7097
7062
|
} catch {
|
|
7098
7063
|
}
|
|
7099
7064
|
function closeWrapped() {
|
|
7100
7065
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
7101
|
-
|
|
7066
|
+
fs36.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(path45, added, removed, oldPosInc, options) {
|
|
10202
|
+
var last = path45.lastComponent;
|
|
10238
10203
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
10239
10204
|
return {
|
|
10240
|
-
oldPos:
|
|
10205
|
+
oldPos: path45.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: path45.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 path45 = 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 (!path45 || content == null) return null;
|
|
10649
|
+
const entry = { path: path45, 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 path45 = 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 (!path45 || content == null) return null;
|
|
11478
|
+
const out = { path: path45, 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_path31.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_fs28.default.mkdirSync(dir, { recursive: true });
|
|
20408
|
+
stream = import_node_fs28.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_fs28, import_node_path31;
|
|
20469
20434
|
var init_recorder = __esm({
|
|
20470
20435
|
"src/run-case/recorder.ts"() {
|
|
20471
20436
|
"use strict";
|
|
20472
|
-
|
|
20473
|
-
|
|
20437
|
+
import_node_fs28 = __toESM(require("fs"), 1);
|
|
20438
|
+
import_node_path31 = __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_fs29.mkdtempSync)(import_node_path32.default.join(import_node_os14.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_fs29.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_fs29, import_node_os14, import_node_path32;
|
|
20684
20649
|
var init_controller = __esm({
|
|
20685
20650
|
"src/run-case/controller.ts"() {
|
|
20686
20651
|
"use strict";
|
|
20687
|
-
|
|
20652
|
+
import_node_fs29 = require("fs");
|
|
20688
20653
|
import_node_os14 = __toESM(require("os"), 1);
|
|
20689
|
-
|
|
20654
|
+
import_node_path32 = __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_path30 = __toESM(require("path"), 1);
|
|
20887
|
+
var import_node_fs27 = __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
|
|
@@ -27033,8 +26991,8 @@ function rmdirIfEmpty(p2) {
|
|
|
27033
26991
|
}
|
|
27034
26992
|
|
|
27035
26993
|
// src/transport/http-router.ts
|
|
27036
|
-
var
|
|
27037
|
-
var
|
|
26994
|
+
var import_node_fs15 = __toESM(require("fs"), 1);
|
|
26995
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
27038
26996
|
|
|
27039
26997
|
// src/attachment/mime.ts
|
|
27040
26998
|
var import_node_path15 = __toESM(require("path"), 1);
|
|
@@ -27212,7 +27170,118 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
|
|
|
27212
27170
|
return { ok: true, absPath };
|
|
27213
27171
|
}
|
|
27214
27172
|
|
|
27173
|
+
// src/attachment/upload.ts
|
|
27174
|
+
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
27175
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
27176
|
+
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
27177
|
+
var import_promises = require("stream/promises");
|
|
27178
|
+
var UploadError = class extends Error {
|
|
27179
|
+
constructor(code, message) {
|
|
27180
|
+
super(message);
|
|
27181
|
+
this.code = code;
|
|
27182
|
+
this.name = "UploadError";
|
|
27183
|
+
}
|
|
27184
|
+
code;
|
|
27185
|
+
};
|
|
27186
|
+
var HASH_PREFIX_LEN = 16;
|
|
27187
|
+
async function writeUploadedAttachment(args) {
|
|
27188
|
+
const attachmentsRoot = import_node_path16.default.join(args.sessionDir, ".attachments");
|
|
27189
|
+
try {
|
|
27190
|
+
import_node_fs14.default.mkdirSync(attachmentsRoot, { recursive: true });
|
|
27191
|
+
} catch (err) {
|
|
27192
|
+
throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
|
|
27193
|
+
}
|
|
27194
|
+
const hasher = import_node_crypto6.default.createHash("sha256");
|
|
27195
|
+
let actualSize = 0;
|
|
27196
|
+
const tmpPath = import_node_path16.default.join(
|
|
27197
|
+
attachmentsRoot,
|
|
27198
|
+
`.upload-${process.pid}-${Date.now()}-${import_node_crypto6.default.randomBytes(4).toString("hex")}`
|
|
27199
|
+
);
|
|
27200
|
+
try {
|
|
27201
|
+
await (0, import_promises.pipeline)(
|
|
27202
|
+
args.body,
|
|
27203
|
+
async function* (source) {
|
|
27204
|
+
for await (const chunk of source) {
|
|
27205
|
+
const buf = chunk;
|
|
27206
|
+
actualSize += buf.length;
|
|
27207
|
+
hasher.update(buf);
|
|
27208
|
+
yield buf;
|
|
27209
|
+
}
|
|
27210
|
+
},
|
|
27211
|
+
import_node_fs14.default.createWriteStream(tmpPath, { mode: 384 })
|
|
27212
|
+
);
|
|
27213
|
+
} catch (err) {
|
|
27214
|
+
try {
|
|
27215
|
+
import_node_fs14.default.unlinkSync(tmpPath);
|
|
27216
|
+
} catch {
|
|
27217
|
+
}
|
|
27218
|
+
throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
|
|
27219
|
+
}
|
|
27220
|
+
if (actualSize !== args.contentLength) {
|
|
27221
|
+
try {
|
|
27222
|
+
import_node_fs14.default.unlinkSync(tmpPath);
|
|
27223
|
+
} catch {
|
|
27224
|
+
}
|
|
27225
|
+
throw new UploadError(
|
|
27226
|
+
"SIZE_MISMATCH",
|
|
27227
|
+
`expected ${args.contentLength} bytes, got ${actualSize}`
|
|
27228
|
+
);
|
|
27229
|
+
}
|
|
27230
|
+
const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
|
|
27231
|
+
const hashDir = import_node_path16.default.join(attachmentsRoot, attachmentId);
|
|
27232
|
+
let finalFileName;
|
|
27233
|
+
let hashDirExists = false;
|
|
27234
|
+
try {
|
|
27235
|
+
hashDirExists = import_node_fs14.default.statSync(hashDir).isDirectory();
|
|
27236
|
+
} catch {
|
|
27237
|
+
}
|
|
27238
|
+
if (hashDirExists) {
|
|
27239
|
+
const existing = import_node_fs14.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
|
|
27240
|
+
finalFileName = existing[0] ?? args.fileName;
|
|
27241
|
+
try {
|
|
27242
|
+
import_node_fs14.default.unlinkSync(tmpPath);
|
|
27243
|
+
} catch {
|
|
27244
|
+
}
|
|
27245
|
+
} else {
|
|
27246
|
+
try {
|
|
27247
|
+
import_node_fs14.default.mkdirSync(hashDir, { recursive: true });
|
|
27248
|
+
finalFileName = args.fileName;
|
|
27249
|
+
import_node_fs14.default.renameSync(tmpPath, import_node_path16.default.join(hashDir, finalFileName));
|
|
27250
|
+
} catch (err) {
|
|
27251
|
+
try {
|
|
27252
|
+
import_node_fs14.default.unlinkSync(tmpPath);
|
|
27253
|
+
} catch {
|
|
27254
|
+
}
|
|
27255
|
+
throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
|
|
27256
|
+
}
|
|
27257
|
+
}
|
|
27258
|
+
const absPath = import_node_path16.default.join(hashDir, finalFileName);
|
|
27259
|
+
const relPath = import_node_path16.default.relative(args.sessionDir, absPath);
|
|
27260
|
+
args.groupFileStore.upsert(args.scope, args.sessionId, {
|
|
27261
|
+
relPath: absPath,
|
|
27262
|
+
// 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
|
|
27263
|
+
from: "user-upload",
|
|
27264
|
+
label: args.fileName,
|
|
27265
|
+
size: actualSize,
|
|
27266
|
+
mime: args.mimeType
|
|
27267
|
+
});
|
|
27268
|
+
return { attachmentId, absPath, relPath, sizeBytes: actualSize, mimeType: args.mimeType };
|
|
27269
|
+
}
|
|
27270
|
+
|
|
27215
27271
|
// src/transport/http-router.ts
|
|
27272
|
+
var UPLOAD_MIME_ALLOW = /* @__PURE__ */ new Set([
|
|
27273
|
+
"image/png",
|
|
27274
|
+
"image/jpeg",
|
|
27275
|
+
"image/gif",
|
|
27276
|
+
"image/webp",
|
|
27277
|
+
"application/pdf"
|
|
27278
|
+
]);
|
|
27279
|
+
var UPLOAD_MIME_ALLOW_PREFIX = ["text/"];
|
|
27280
|
+
var UPLOAD_MAX_BYTES = 25 * 1024 * 1024;
|
|
27281
|
+
function isUploadMimeAllowed(mime) {
|
|
27282
|
+
if (UPLOAD_MIME_ALLOW.has(mime)) return true;
|
|
27283
|
+
return UPLOAD_MIME_ALLOW_PREFIX.some((p2) => mime.startsWith(p2));
|
|
27284
|
+
}
|
|
27216
27285
|
function createHttpRouter(deps) {
|
|
27217
27286
|
return async (req, res) => {
|
|
27218
27287
|
const url = parseUrl(req.url);
|
|
@@ -27226,7 +27295,7 @@ function createHttpRouter(deps) {
|
|
|
27226
27295
|
});
|
|
27227
27296
|
return true;
|
|
27228
27297
|
}
|
|
27229
|
-
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/")) {
|
|
27298
|
+
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/") && url.pathname !== "/attachments/upload") {
|
|
27230
27299
|
return false;
|
|
27231
27300
|
}
|
|
27232
27301
|
if (url.pathname.startsWith("/files/") && req.method === "GET") {
|
|
@@ -27266,6 +27335,99 @@ function createHttpRouter(deps) {
|
|
|
27266
27335
|
sendJson(res, 401, { code: "UNAUTHORIZED", message: "missing or invalid bearer token" });
|
|
27267
27336
|
return true;
|
|
27268
27337
|
}
|
|
27338
|
+
if (url.pathname === "/attachments/upload" && req.method === "POST") {
|
|
27339
|
+
if (ctx.role !== "owner") {
|
|
27340
|
+
sendJson(res, 403, { code: "FORBIDDEN", message: "owner only" });
|
|
27341
|
+
return true;
|
|
27342
|
+
}
|
|
27343
|
+
const up = deps.attachmentUpload;
|
|
27344
|
+
if (!up) {
|
|
27345
|
+
sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "attachment upload not wired" });
|
|
27346
|
+
return true;
|
|
27347
|
+
}
|
|
27348
|
+
const sessionId = url.searchParams.get("sessionId");
|
|
27349
|
+
const fileName = url.searchParams.get("fileName");
|
|
27350
|
+
const mimeType = url.searchParams.get("mimeType");
|
|
27351
|
+
const contentLengthRaw = req.headers["content-length"];
|
|
27352
|
+
if (!sessionId || !fileName || !mimeType || !contentLengthRaw) {
|
|
27353
|
+
sendJson(res, 400, {
|
|
27354
|
+
code: "INVALID_PARAM",
|
|
27355
|
+
message: "missing sessionId/fileName/mimeType/Content-Length"
|
|
27356
|
+
});
|
|
27357
|
+
return true;
|
|
27358
|
+
}
|
|
27359
|
+
const contentLength = Number.parseInt(contentLengthRaw, 10);
|
|
27360
|
+
if (!Number.isFinite(contentLength) || contentLength < 0) {
|
|
27361
|
+
sendJson(res, 400, { code: "INVALID_PARAM", message: "malformed Content-Length" });
|
|
27362
|
+
return true;
|
|
27363
|
+
}
|
|
27364
|
+
if (contentLength > UPLOAD_MAX_BYTES) {
|
|
27365
|
+
sendJson(res, 413, {
|
|
27366
|
+
code: "FILE_TOO_LARGE",
|
|
27367
|
+
message: `max ${UPLOAD_MAX_BYTES} bytes`
|
|
27368
|
+
});
|
|
27369
|
+
return true;
|
|
27370
|
+
}
|
|
27371
|
+
if (!isUploadMimeAllowed(mimeType)) {
|
|
27372
|
+
sendJson(res, 400, {
|
|
27373
|
+
code: "UNSUPPORTED_MIME",
|
|
27374
|
+
message: `mime ${mimeType} not allowed`
|
|
27375
|
+
});
|
|
27376
|
+
return true;
|
|
27377
|
+
}
|
|
27378
|
+
const sessionDir = up.getSessionDir(sessionId);
|
|
27379
|
+
const scope = up.getSessionScope(sessionId);
|
|
27380
|
+
if (!sessionDir || !scope) {
|
|
27381
|
+
sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sessionId} not found` });
|
|
27382
|
+
return true;
|
|
27383
|
+
}
|
|
27384
|
+
const httpBaseUrl = up.getHttpBaseUrl();
|
|
27385
|
+
if (!httpBaseUrl) {
|
|
27386
|
+
sendJson(res, 503, {
|
|
27387
|
+
code: "TUNNEL_NOT_READY",
|
|
27388
|
+
message: "httpBaseUrl unavailable"
|
|
27389
|
+
});
|
|
27390
|
+
return true;
|
|
27391
|
+
}
|
|
27392
|
+
const secret = deps.getSignSecret?.();
|
|
27393
|
+
if (!secret) {
|
|
27394
|
+
sendJson(res, 501, {
|
|
27395
|
+
code: "NOT_IMPLEMENTED",
|
|
27396
|
+
message: "sign secret unavailable (noAuth?)"
|
|
27397
|
+
});
|
|
27398
|
+
return true;
|
|
27399
|
+
}
|
|
27400
|
+
try {
|
|
27401
|
+
const result = await writeUploadedAttachment({
|
|
27402
|
+
sessionDir,
|
|
27403
|
+
sessionId,
|
|
27404
|
+
scope,
|
|
27405
|
+
fileName,
|
|
27406
|
+
mimeType,
|
|
27407
|
+
contentLength,
|
|
27408
|
+
body: req,
|
|
27409
|
+
groupFileStore: up.groupFileStore
|
|
27410
|
+
});
|
|
27411
|
+
const parts = signUrlParts(secret, result.absPath, null);
|
|
27412
|
+
const fullUrl = buildSignedFileUrl(httpBaseUrl, parts);
|
|
27413
|
+
sendJson(res, 200, {
|
|
27414
|
+
attachmentId: result.attachmentId,
|
|
27415
|
+
url: fullUrl,
|
|
27416
|
+
relPath: result.relPath,
|
|
27417
|
+
sizeBytes: result.sizeBytes,
|
|
27418
|
+
mimeType: result.mimeType
|
|
27419
|
+
});
|
|
27420
|
+
} catch (err) {
|
|
27421
|
+
if (err instanceof UploadError) {
|
|
27422
|
+
const status = err.code === "SIZE_MISMATCH" ? 400 : 500;
|
|
27423
|
+
sendJson(res, status, { code: err.code, message: err.message });
|
|
27424
|
+
} else {
|
|
27425
|
+
deps.logger?.warn("attachment upload failed", { err: err.message });
|
|
27426
|
+
sendJson(res, 500, { code: "STORAGE_ERROR", message: err.message });
|
|
27427
|
+
}
|
|
27428
|
+
}
|
|
27429
|
+
return true;
|
|
27430
|
+
}
|
|
27269
27431
|
const sessionFilesMatch = url.pathname.match(/^\/session\/([^/]+)\/files$/);
|
|
27270
27432
|
if (sessionFilesMatch && req.method === "GET") {
|
|
27271
27433
|
const sid = sessionFilesMatch[1];
|
|
@@ -27275,7 +27437,7 @@ function createHttpRouter(deps) {
|
|
|
27275
27437
|
return true;
|
|
27276
27438
|
}
|
|
27277
27439
|
let absPath;
|
|
27278
|
-
if (
|
|
27440
|
+
if (import_node_path17.default.isAbsolute(pathParam)) {
|
|
27279
27441
|
absPath = pathParam;
|
|
27280
27442
|
} else if (deps.sessionStore) {
|
|
27281
27443
|
const file = deps.sessionStore.read(sid);
|
|
@@ -27283,7 +27445,7 @@ function createHttpRouter(deps) {
|
|
|
27283
27445
|
sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
|
|
27284
27446
|
return true;
|
|
27285
27447
|
}
|
|
27286
|
-
absPath =
|
|
27448
|
+
absPath = import_node_path17.default.join(file.cwd, pathParam);
|
|
27287
27449
|
} else {
|
|
27288
27450
|
sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
|
|
27289
27451
|
return true;
|
|
@@ -27316,7 +27478,7 @@ function withCtx(ctx, body) {
|
|
|
27316
27478
|
function streamFile(res, absPath, logger) {
|
|
27317
27479
|
let stat;
|
|
27318
27480
|
try {
|
|
27319
|
-
stat =
|
|
27481
|
+
stat = import_node_fs15.default.statSync(absPath);
|
|
27320
27482
|
} catch (err) {
|
|
27321
27483
|
const code = err?.code;
|
|
27322
27484
|
if (code === "ENOENT") {
|
|
@@ -27331,7 +27493,7 @@ function streamFile(res, absPath, logger) {
|
|
|
27331
27493
|
return;
|
|
27332
27494
|
}
|
|
27333
27495
|
const mime = lookupMime(absPath);
|
|
27334
|
-
const basename =
|
|
27496
|
+
const basename = import_node_path17.default.basename(absPath);
|
|
27335
27497
|
res.writeHead(200, {
|
|
27336
27498
|
"Content-Type": mime,
|
|
27337
27499
|
"Content-Length": String(stat.size),
|
|
@@ -27339,7 +27501,7 @@ function streamFile(res, absPath, logger) {
|
|
|
27339
27501
|
// 防止浏览器把任意 mime 当 html 渲染
|
|
27340
27502
|
"X-Content-Type-Options": "nosniff"
|
|
27341
27503
|
});
|
|
27342
|
-
const stream =
|
|
27504
|
+
const stream = import_node_fs15.default.createReadStream(absPath);
|
|
27343
27505
|
stream.on("error", (err) => {
|
|
27344
27506
|
logger?.warn("streamFile read error", { absPath, err: err.message });
|
|
27345
27507
|
res.destroy();
|
|
@@ -27347,10 +27509,78 @@ function streamFile(res, absPath, logger) {
|
|
|
27347
27509
|
stream.pipe(res);
|
|
27348
27510
|
}
|
|
27349
27511
|
|
|
27512
|
+
// src/attachment/gc.ts
|
|
27513
|
+
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
27514
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
27515
|
+
var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
|
|
27516
|
+
function runAttachmentGc(args) {
|
|
27517
|
+
const now = (args.now ?? Date.now)();
|
|
27518
|
+
const ttlMs = args.ttlMs ?? DEFAULT_TTL_MS;
|
|
27519
|
+
const liveAbs = /* @__PURE__ */ new Set();
|
|
27520
|
+
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27521
|
+
for (const entry of args.groupFileStore.list(scope, sessionId)) {
|
|
27522
|
+
if (entry.stale) continue;
|
|
27523
|
+
if (import_node_path18.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
|
|
27524
|
+
}
|
|
27525
|
+
}
|
|
27526
|
+
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27527
|
+
const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path18.default.join(
|
|
27528
|
+
args.dataDir,
|
|
27529
|
+
"sessions",
|
|
27530
|
+
...scopeSubPath(scope).map(safeFileName),
|
|
27531
|
+
safeFileName(sessionId)
|
|
27532
|
+
);
|
|
27533
|
+
const attRoot = import_node_path18.default.join(sessionDir, ".attachments");
|
|
27534
|
+
let hashDirs;
|
|
27535
|
+
try {
|
|
27536
|
+
hashDirs = import_node_fs16.default.readdirSync(attRoot);
|
|
27537
|
+
} catch (err) {
|
|
27538
|
+
if (err.code === "ENOENT") continue;
|
|
27539
|
+
args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
|
|
27540
|
+
continue;
|
|
27541
|
+
}
|
|
27542
|
+
for (const hashDir of hashDirs) {
|
|
27543
|
+
const hashDirAbs = import_node_path18.default.join(attRoot, hashDir);
|
|
27544
|
+
let files;
|
|
27545
|
+
try {
|
|
27546
|
+
files = import_node_fs16.default.readdirSync(hashDirAbs);
|
|
27547
|
+
} catch {
|
|
27548
|
+
continue;
|
|
27549
|
+
}
|
|
27550
|
+
for (const name of files) {
|
|
27551
|
+
const file = import_node_path18.default.join(hashDirAbs, name);
|
|
27552
|
+
let stat;
|
|
27553
|
+
try {
|
|
27554
|
+
stat = import_node_fs16.default.statSync(file);
|
|
27555
|
+
} catch {
|
|
27556
|
+
continue;
|
|
27557
|
+
}
|
|
27558
|
+
if (!stat.isFile()) continue;
|
|
27559
|
+
const age = now - stat.mtimeMs;
|
|
27560
|
+
if (age < ttlMs) continue;
|
|
27561
|
+
if (liveAbs.has(file)) continue;
|
|
27562
|
+
try {
|
|
27563
|
+
import_node_fs16.default.unlinkSync(file);
|
|
27564
|
+
} catch (err) {
|
|
27565
|
+
args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
|
|
27566
|
+
}
|
|
27567
|
+
}
|
|
27568
|
+
try {
|
|
27569
|
+
if (import_node_fs16.default.readdirSync(hashDirAbs).length === 0) import_node_fs16.default.rmdirSync(hashDirAbs);
|
|
27570
|
+
} catch {
|
|
27571
|
+
}
|
|
27572
|
+
}
|
|
27573
|
+
try {
|
|
27574
|
+
if (import_node_fs16.default.readdirSync(attRoot).length === 0) import_node_fs16.default.rmdirSync(attRoot);
|
|
27575
|
+
} catch {
|
|
27576
|
+
}
|
|
27577
|
+
}
|
|
27578
|
+
}
|
|
27579
|
+
|
|
27350
27580
|
// src/attachment/group.ts
|
|
27351
|
-
var
|
|
27352
|
-
var
|
|
27353
|
-
var
|
|
27581
|
+
var import_node_fs17 = __toESM(require("fs"), 1);
|
|
27582
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
27583
|
+
var import_node_crypto7 = __toESM(require("crypto"), 1);
|
|
27354
27584
|
init_protocol();
|
|
27355
27585
|
var GroupFileStore = class {
|
|
27356
27586
|
dataDir;
|
|
@@ -27361,11 +27591,11 @@ var GroupFileStore = class {
|
|
|
27361
27591
|
this.logger = opts.logger;
|
|
27362
27592
|
}
|
|
27363
27593
|
rootForScope(scope) {
|
|
27364
|
-
return
|
|
27594
|
+
return import_node_path19.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
|
|
27365
27595
|
}
|
|
27366
27596
|
/** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
|
|
27367
27597
|
filePath(scope, sessionId) {
|
|
27368
|
-
return
|
|
27598
|
+
return import_node_path19.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
|
|
27369
27599
|
}
|
|
27370
27600
|
cacheKey(scope, sessionId) {
|
|
27371
27601
|
return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
|
|
@@ -27374,7 +27604,7 @@ var GroupFileStore = class {
|
|
|
27374
27604
|
readFile(scope, sessionId) {
|
|
27375
27605
|
const file = this.filePath(scope, sessionId);
|
|
27376
27606
|
try {
|
|
27377
|
-
const raw =
|
|
27607
|
+
const raw = import_node_fs17.default.readFileSync(file, "utf8");
|
|
27378
27608
|
const parsed = JSON.parse(raw);
|
|
27379
27609
|
if (!Array.isArray(parsed)) {
|
|
27380
27610
|
this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
|
|
@@ -27400,10 +27630,10 @@ var GroupFileStore = class {
|
|
|
27400
27630
|
}
|
|
27401
27631
|
writeFile(scope, sessionId, entries) {
|
|
27402
27632
|
const file = this.filePath(scope, sessionId);
|
|
27403
|
-
|
|
27633
|
+
import_node_fs17.default.mkdirSync(import_node_path19.default.dirname(file), { recursive: true });
|
|
27404
27634
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
27405
|
-
|
|
27406
|
-
|
|
27635
|
+
import_node_fs17.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
|
|
27636
|
+
import_node_fs17.default.renameSync(tmp, file);
|
|
27407
27637
|
}
|
|
27408
27638
|
/** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
|
|
27409
27639
|
list(scope, sessionId) {
|
|
@@ -27439,7 +27669,7 @@ var GroupFileStore = class {
|
|
|
27439
27669
|
entries[idx] = next;
|
|
27440
27670
|
} else {
|
|
27441
27671
|
next = {
|
|
27442
|
-
id: `gf-${
|
|
27672
|
+
id: `gf-${import_node_crypto7.default.randomBytes(6).toString("base64url")}`,
|
|
27443
27673
|
relPath: input.relPath,
|
|
27444
27674
|
from: input.from,
|
|
27445
27675
|
label: input.label,
|
|
@@ -27489,10 +27719,10 @@ var GroupFileStore = class {
|
|
|
27489
27719
|
};
|
|
27490
27720
|
|
|
27491
27721
|
// src/discovery/state-file.ts
|
|
27492
|
-
var
|
|
27493
|
-
var
|
|
27722
|
+
var import_node_fs18 = __toESM(require("fs"), 1);
|
|
27723
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
27494
27724
|
function defaultStateFilePath(dataDir) {
|
|
27495
|
-
return
|
|
27725
|
+
return import_node_path20.default.join(dataDir, "state.json");
|
|
27496
27726
|
}
|
|
27497
27727
|
function isPidAlive(pid) {
|
|
27498
27728
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
@@ -27514,7 +27744,7 @@ var StateFileManager = class {
|
|
|
27514
27744
|
}
|
|
27515
27745
|
read() {
|
|
27516
27746
|
try {
|
|
27517
|
-
const raw =
|
|
27747
|
+
const raw = import_node_fs18.default.readFileSync(this.file, "utf8");
|
|
27518
27748
|
const parsed = JSON.parse(raw);
|
|
27519
27749
|
return parsed;
|
|
27520
27750
|
} catch {
|
|
@@ -27528,34 +27758,34 @@ var StateFileManager = class {
|
|
|
27528
27758
|
return { status: "stale", existing };
|
|
27529
27759
|
}
|
|
27530
27760
|
write(state) {
|
|
27531
|
-
|
|
27761
|
+
import_node_fs18.default.mkdirSync(import_node_path20.default.dirname(this.file), { recursive: true });
|
|
27532
27762
|
const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
|
|
27533
|
-
|
|
27534
|
-
|
|
27763
|
+
import_node_fs18.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
|
|
27764
|
+
import_node_fs18.default.renameSync(tmp, this.file);
|
|
27535
27765
|
if (process.platform !== "win32") {
|
|
27536
27766
|
try {
|
|
27537
|
-
|
|
27767
|
+
import_node_fs18.default.chmodSync(this.file, 384);
|
|
27538
27768
|
} catch {
|
|
27539
27769
|
}
|
|
27540
27770
|
}
|
|
27541
27771
|
}
|
|
27542
27772
|
delete() {
|
|
27543
27773
|
try {
|
|
27544
|
-
|
|
27774
|
+
import_node_fs18.default.unlinkSync(this.file);
|
|
27545
27775
|
} catch {
|
|
27546
27776
|
}
|
|
27547
27777
|
}
|
|
27548
27778
|
};
|
|
27549
27779
|
|
|
27550
27780
|
// src/tunnel/tunnel-manager.ts
|
|
27551
|
-
var
|
|
27552
|
-
var
|
|
27553
|
-
var
|
|
27781
|
+
var import_node_fs22 = __toESM(require("fs"), 1);
|
|
27782
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
27783
|
+
var import_node_crypto8 = __toESM(require("crypto"), 1);
|
|
27554
27784
|
var import_node_child_process5 = require("child_process");
|
|
27555
27785
|
|
|
27556
27786
|
// src/tunnel/tunnel-store.ts
|
|
27557
|
-
var
|
|
27558
|
-
var
|
|
27787
|
+
var import_node_fs19 = __toESM(require("fs"), 1);
|
|
27788
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
27559
27789
|
var TunnelStore = class {
|
|
27560
27790
|
constructor(filePath) {
|
|
27561
27791
|
this.filePath = filePath;
|
|
@@ -27563,7 +27793,7 @@ var TunnelStore = class {
|
|
|
27563
27793
|
filePath;
|
|
27564
27794
|
async get() {
|
|
27565
27795
|
try {
|
|
27566
|
-
const raw = await
|
|
27796
|
+
const raw = await import_node_fs19.default.promises.readFile(this.filePath, "utf8");
|
|
27567
27797
|
const obj = JSON.parse(raw);
|
|
27568
27798
|
if (!isPersistedTunnel(obj)) return null;
|
|
27569
27799
|
return obj;
|
|
@@ -27574,22 +27804,22 @@ var TunnelStore = class {
|
|
|
27574
27804
|
}
|
|
27575
27805
|
}
|
|
27576
27806
|
async set(v2) {
|
|
27577
|
-
const dir =
|
|
27578
|
-
await
|
|
27807
|
+
const dir = import_node_path21.default.dirname(this.filePath);
|
|
27808
|
+
await import_node_fs19.default.promises.mkdir(dir, { recursive: true });
|
|
27579
27809
|
const data = JSON.stringify(v2, null, 2);
|
|
27580
27810
|
const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
27581
|
-
await
|
|
27811
|
+
await import_node_fs19.default.promises.writeFile(tmp, data, { mode: 384 });
|
|
27582
27812
|
if (process.platform !== "win32") {
|
|
27583
27813
|
try {
|
|
27584
|
-
await
|
|
27814
|
+
await import_node_fs19.default.promises.chmod(tmp, 384);
|
|
27585
27815
|
} catch {
|
|
27586
27816
|
}
|
|
27587
27817
|
}
|
|
27588
|
-
await
|
|
27818
|
+
await import_node_fs19.default.promises.rename(tmp, this.filePath);
|
|
27589
27819
|
}
|
|
27590
27820
|
async clear() {
|
|
27591
27821
|
try {
|
|
27592
|
-
await
|
|
27822
|
+
await import_node_fs19.default.promises.unlink(this.filePath);
|
|
27593
27823
|
} catch (err) {
|
|
27594
27824
|
const code = err?.code;
|
|
27595
27825
|
if (code !== "ENOENT") throw err;
|
|
@@ -27684,12 +27914,12 @@ function escape(v2) {
|
|
|
27684
27914
|
}
|
|
27685
27915
|
|
|
27686
27916
|
// src/tunnel/frpc-binary.ts
|
|
27687
|
-
var
|
|
27917
|
+
var import_node_fs20 = __toESM(require("fs"), 1);
|
|
27688
27918
|
var import_node_os9 = __toESM(require("os"), 1);
|
|
27689
|
-
var
|
|
27919
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
27690
27920
|
var import_node_child_process3 = require("child_process");
|
|
27691
27921
|
var import_node_stream2 = require("stream");
|
|
27692
|
-
var
|
|
27922
|
+
var import_promises2 = require("stream/promises");
|
|
27693
27923
|
var FRPC_VERSION = "0.68.0";
|
|
27694
27924
|
var UnsupportedPlatformError = class extends Error {
|
|
27695
27925
|
constructor(platform, arch) {
|
|
@@ -27718,20 +27948,20 @@ function frpcDownloadUrl(version2, p2) {
|
|
|
27718
27948
|
}
|
|
27719
27949
|
async function ensureFrpcBinary(opts) {
|
|
27720
27950
|
if (opts.override) {
|
|
27721
|
-
if (!
|
|
27951
|
+
if (!import_node_fs20.default.existsSync(opts.override)) {
|
|
27722
27952
|
throw new Error(`frpc binary not found at override path: ${opts.override}`);
|
|
27723
27953
|
}
|
|
27724
27954
|
return opts.override;
|
|
27725
27955
|
}
|
|
27726
27956
|
const version2 = opts.version ?? FRPC_VERSION;
|
|
27727
27957
|
const platform = opts.platform ?? detectPlatform();
|
|
27728
|
-
const binDir =
|
|
27729
|
-
|
|
27958
|
+
const binDir = import_node_path22.default.join(opts.dataDir, "bin");
|
|
27959
|
+
import_node_fs20.default.mkdirSync(binDir, { recursive: true });
|
|
27730
27960
|
cleanupStaleArtifacts(binDir);
|
|
27731
|
-
const stableBin =
|
|
27732
|
-
if (
|
|
27961
|
+
const stableBin = import_node_path22.default.join(binDir, "frpc");
|
|
27962
|
+
if (import_node_fs20.default.existsSync(stableBin)) return stableBin;
|
|
27733
27963
|
const partialBin = `${stableBin}.partial`;
|
|
27734
|
-
const tarballPath =
|
|
27964
|
+
const tarballPath = import_node_path22.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
|
|
27735
27965
|
try {
|
|
27736
27966
|
const url = frpcDownloadUrl(version2, platform);
|
|
27737
27967
|
await downloadToFile(url, tarballPath, opts.fetchImpl);
|
|
@@ -27740,8 +27970,8 @@ async function ensureFrpcBinary(opts) {
|
|
|
27740
27970
|
} else {
|
|
27741
27971
|
await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
|
|
27742
27972
|
}
|
|
27743
|
-
|
|
27744
|
-
|
|
27973
|
+
import_node_fs20.default.chmodSync(partialBin, 493);
|
|
27974
|
+
import_node_fs20.default.renameSync(partialBin, stableBin);
|
|
27745
27975
|
} finally {
|
|
27746
27976
|
safeUnlink(tarballPath);
|
|
27747
27977
|
safeUnlink(partialBin);
|
|
@@ -27751,15 +27981,15 @@ async function ensureFrpcBinary(opts) {
|
|
|
27751
27981
|
function cleanupStaleArtifacts(binDir) {
|
|
27752
27982
|
let entries;
|
|
27753
27983
|
try {
|
|
27754
|
-
entries =
|
|
27984
|
+
entries = import_node_fs20.default.readdirSync(binDir);
|
|
27755
27985
|
} catch {
|
|
27756
27986
|
return;
|
|
27757
27987
|
}
|
|
27758
27988
|
for (const name of entries) {
|
|
27759
27989
|
if (name.endsWith(".partial") || name.startsWith("extract-")) {
|
|
27760
|
-
const full =
|
|
27990
|
+
const full = import_node_path22.default.join(binDir, name);
|
|
27761
27991
|
try {
|
|
27762
|
-
|
|
27992
|
+
import_node_fs20.default.rmSync(full, { recursive: true, force: true });
|
|
27763
27993
|
} catch {
|
|
27764
27994
|
}
|
|
27765
27995
|
}
|
|
@@ -27767,7 +27997,7 @@ function cleanupStaleArtifacts(binDir) {
|
|
|
27767
27997
|
}
|
|
27768
27998
|
function safeUnlink(p2) {
|
|
27769
27999
|
try {
|
|
27770
|
-
|
|
28000
|
+
import_node_fs20.default.unlinkSync(p2);
|
|
27771
28001
|
} catch {
|
|
27772
28002
|
}
|
|
27773
28003
|
}
|
|
@@ -27778,13 +28008,13 @@ async function downloadToFile(url, dest, fetchImpl) {
|
|
|
27778
28008
|
if (!res.ok || !res.body) {
|
|
27779
28009
|
throw new Error(`download failed: ${res.status} ${res.statusText}`);
|
|
27780
28010
|
}
|
|
27781
|
-
const out =
|
|
28011
|
+
const out = import_node_fs20.default.createWriteStream(dest);
|
|
27782
28012
|
const nodeStream = import_node_stream2.Readable.fromWeb(res.body);
|
|
27783
|
-
await (0,
|
|
28013
|
+
await (0, import_promises2.pipeline)(nodeStream, out);
|
|
27784
28014
|
}
|
|
27785
28015
|
async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
|
|
27786
|
-
const work =
|
|
27787
|
-
|
|
28016
|
+
const work = import_node_path22.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
|
|
28017
|
+
import_node_fs20.default.mkdirSync(work, { recursive: true });
|
|
27788
28018
|
try {
|
|
27789
28019
|
await new Promise((resolve6, reject) => {
|
|
27790
28020
|
const proc = (0, import_node_child_process3.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
|
|
@@ -27792,32 +28022,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
|
|
|
27792
28022
|
proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
|
|
27793
28023
|
});
|
|
27794
28024
|
const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
|
|
27795
|
-
const src =
|
|
27796
|
-
if (!
|
|
28025
|
+
const src = import_node_path22.default.join(work, dirName, "frpc");
|
|
28026
|
+
if (!import_node_fs20.default.existsSync(src)) {
|
|
27797
28027
|
throw new Error(`frpc not found inside tarball at ${src}`);
|
|
27798
28028
|
}
|
|
27799
|
-
|
|
28029
|
+
import_node_fs20.default.copyFileSync(src, destBin);
|
|
27800
28030
|
} finally {
|
|
27801
|
-
|
|
28031
|
+
import_node_fs20.default.rmSync(work, { recursive: true, force: true });
|
|
27802
28032
|
}
|
|
27803
28033
|
}
|
|
27804
28034
|
|
|
27805
28035
|
// src/tunnel/frpc-process.ts
|
|
27806
|
-
var
|
|
27807
|
-
var
|
|
28036
|
+
var import_node_fs21 = __toESM(require("fs"), 1);
|
|
28037
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
27808
28038
|
var import_node_child_process4 = require("child_process");
|
|
27809
28039
|
function frpcPidFilePath(dataDir) {
|
|
27810
|
-
return
|
|
28040
|
+
return import_node_path23.default.join(dataDir, "frpc.pid");
|
|
27811
28041
|
}
|
|
27812
28042
|
function writeFrpcPid(dataDir, pid) {
|
|
27813
28043
|
try {
|
|
27814
|
-
|
|
28044
|
+
import_node_fs21.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
|
|
27815
28045
|
} catch {
|
|
27816
28046
|
}
|
|
27817
28047
|
}
|
|
27818
28048
|
function clearFrpcPid(dataDir) {
|
|
27819
28049
|
try {
|
|
27820
|
-
|
|
28050
|
+
import_node_fs21.default.unlinkSync(frpcPidFilePath(dataDir));
|
|
27821
28051
|
} catch {
|
|
27822
28052
|
}
|
|
27823
28053
|
}
|
|
@@ -27833,7 +28063,7 @@ function defaultIsPidAlive(pid) {
|
|
|
27833
28063
|
}
|
|
27834
28064
|
function defaultReadPidFile(file) {
|
|
27835
28065
|
try {
|
|
27836
|
-
return
|
|
28066
|
+
return import_node_fs21.default.readFileSync(file, "utf8");
|
|
27837
28067
|
} catch {
|
|
27838
28068
|
return null;
|
|
27839
28069
|
}
|
|
@@ -27849,7 +28079,7 @@ function defaultSleep(ms) {
|
|
|
27849
28079
|
}
|
|
27850
28080
|
async function killStaleFrpc(deps) {
|
|
27851
28081
|
const pidFile = frpcPidFilePath(deps.dataDir);
|
|
27852
|
-
const tomlPath =
|
|
28082
|
+
const tomlPath = import_node_path23.default.join(deps.dataDir, "frpc.toml");
|
|
27853
28083
|
const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
|
|
27854
28084
|
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
|
|
27855
28085
|
const killPid = deps.killPidImpl ?? defaultKillPid;
|
|
@@ -27873,7 +28103,7 @@ async function killStaleFrpc(deps) {
|
|
|
27873
28103
|
}
|
|
27874
28104
|
if (victims.size === 0) {
|
|
27875
28105
|
try {
|
|
27876
|
-
|
|
28106
|
+
import_node_fs21.default.unlinkSync(pidFile);
|
|
27877
28107
|
} catch {
|
|
27878
28108
|
}
|
|
27879
28109
|
return;
|
|
@@ -27884,7 +28114,7 @@ async function killStaleFrpc(deps) {
|
|
|
27884
28114
|
}
|
|
27885
28115
|
await sleep(deps.reapWaitMs ?? 300);
|
|
27886
28116
|
try {
|
|
27887
|
-
|
|
28117
|
+
import_node_fs21.default.unlinkSync(pidFile);
|
|
27888
28118
|
} catch {
|
|
27889
28119
|
}
|
|
27890
28120
|
}
|
|
@@ -27921,7 +28151,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
27921
28151
|
var TunnelManager = class {
|
|
27922
28152
|
constructor(deps) {
|
|
27923
28153
|
this.deps = deps;
|
|
27924
|
-
this.store = deps.store ?? new TunnelStore(
|
|
28154
|
+
this.store = deps.store ?? new TunnelStore(import_node_path24.default.join(deps.dataDir, "tunnel.json"));
|
|
27925
28155
|
this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
|
|
27926
28156
|
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
27927
28157
|
}
|
|
@@ -28048,8 +28278,8 @@ var TunnelManager = class {
|
|
|
28048
28278
|
dataDir: this.deps.dataDir,
|
|
28049
28279
|
override: this.deps.frpcBinaryOverride ?? void 0
|
|
28050
28280
|
});
|
|
28051
|
-
const tomlPath =
|
|
28052
|
-
const proxyName = `clawd-${t.subdomain}-${localPort}-${
|
|
28281
|
+
const tomlPath = import_node_path24.default.join(this.deps.dataDir, "frpc.toml");
|
|
28282
|
+
const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto8.default.randomBytes(3).toString("hex")}`;
|
|
28053
28283
|
const toml = buildFrpcToml({
|
|
28054
28284
|
serverAddr: t.frpsHost,
|
|
28055
28285
|
serverPort: t.frpsPort,
|
|
@@ -28059,12 +28289,12 @@ var TunnelManager = class {
|
|
|
28059
28289
|
localPort,
|
|
28060
28290
|
logLevel: "info"
|
|
28061
28291
|
});
|
|
28062
|
-
await
|
|
28292
|
+
await import_node_fs22.default.promises.writeFile(tomlPath, toml, { mode: 384 });
|
|
28063
28293
|
const proc = (this.deps.spawnImpl ?? import_node_child_process5.spawn)(frpcBin, ["-c", tomlPath], {
|
|
28064
28294
|
stdio: ["ignore", "pipe", "pipe"]
|
|
28065
28295
|
});
|
|
28066
|
-
const logFilePath =
|
|
28067
|
-
const logStream =
|
|
28296
|
+
const logFilePath = import_node_path24.default.join(this.deps.dataDir, "frpc.log");
|
|
28297
|
+
const logStream = import_node_fs22.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
|
|
28068
28298
|
logStream.on("error", () => {
|
|
28069
28299
|
});
|
|
28070
28300
|
const tee = (chunk) => {
|
|
@@ -28147,22 +28377,22 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
28147
28377
|
|
|
28148
28378
|
// src/tunnel/device-key.ts
|
|
28149
28379
|
var import_node_os10 = __toESM(require("os"), 1);
|
|
28150
|
-
var
|
|
28380
|
+
var import_node_crypto9 = __toESM(require("crypto"), 1);
|
|
28151
28381
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
28152
28382
|
function deriveStableDeviceKey(opts = {}) {
|
|
28153
28383
|
const hostname = opts.hostname ?? import_node_os10.default.hostname();
|
|
28154
28384
|
const uid = opts.uid ?? (typeof import_node_os10.default.userInfo === "function" ? import_node_os10.default.userInfo().uid : 0);
|
|
28155
28385
|
const input = `${hostname}::${uid}`;
|
|
28156
|
-
return
|
|
28386
|
+
return import_node_crypto9.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
28157
28387
|
}
|
|
28158
28388
|
|
|
28159
28389
|
// src/auth-store.ts
|
|
28160
|
-
var
|
|
28161
|
-
var
|
|
28162
|
-
var
|
|
28390
|
+
var import_node_fs23 = __toESM(require("fs"), 1);
|
|
28391
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
28392
|
+
var import_node_crypto10 = __toESM(require("crypto"), 1);
|
|
28163
28393
|
var AUTH_FILE_NAME = "auth.json";
|
|
28164
28394
|
function authFilePath(dataDir) {
|
|
28165
|
-
return
|
|
28395
|
+
return import_node_path25.default.join(dataDir, AUTH_FILE_NAME);
|
|
28166
28396
|
}
|
|
28167
28397
|
function loadOrCreateAuthFile(opts) {
|
|
28168
28398
|
const file = authFilePath(opts.dataDir);
|
|
@@ -28187,14 +28417,14 @@ function loadOrCreateAuthFile(opts) {
|
|
|
28187
28417
|
return next;
|
|
28188
28418
|
}
|
|
28189
28419
|
function defaultGenerateToken2() {
|
|
28190
|
-
return
|
|
28420
|
+
return import_node_crypto10.default.randomBytes(32).toString("base64url");
|
|
28191
28421
|
}
|
|
28192
28422
|
function defaultGenerateOwnerPrincipalId() {
|
|
28193
|
-
return `owner-${
|
|
28423
|
+
return `owner-${import_node_crypto10.default.randomUUID()}`;
|
|
28194
28424
|
}
|
|
28195
28425
|
function readAuthFile(file) {
|
|
28196
28426
|
try {
|
|
28197
|
-
const raw =
|
|
28427
|
+
const raw = import_node_fs23.default.readFileSync(file, "utf8");
|
|
28198
28428
|
const parsed = JSON.parse(raw);
|
|
28199
28429
|
if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
|
|
28200
28430
|
return null;
|
|
@@ -28212,25 +28442,25 @@ function readAuthFile(file) {
|
|
|
28212
28442
|
}
|
|
28213
28443
|
}
|
|
28214
28444
|
function writeAuthFile(file, content) {
|
|
28215
|
-
|
|
28216
|
-
|
|
28445
|
+
import_node_fs23.default.mkdirSync(import_node_path25.default.dirname(file), { recursive: true });
|
|
28446
|
+
import_node_fs23.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
28217
28447
|
try {
|
|
28218
|
-
|
|
28448
|
+
import_node_fs23.default.chmodSync(file, 384);
|
|
28219
28449
|
} catch {
|
|
28220
28450
|
}
|
|
28221
28451
|
}
|
|
28222
28452
|
|
|
28223
28453
|
// src/owner-profile.ts
|
|
28224
|
-
var
|
|
28454
|
+
var import_node_fs24 = __toESM(require("fs"), 1);
|
|
28225
28455
|
var import_node_os11 = __toESM(require("os"), 1);
|
|
28226
|
-
var
|
|
28456
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
28227
28457
|
var PROFILE_FILENAME = "profile.json";
|
|
28228
28458
|
function loadOwnerDisplayName(dataDir) {
|
|
28229
28459
|
const fallback = import_node_os11.default.userInfo().username;
|
|
28230
|
-
const profilePath =
|
|
28460
|
+
const profilePath = import_node_path26.default.join(dataDir, PROFILE_FILENAME);
|
|
28231
28461
|
let raw;
|
|
28232
28462
|
try {
|
|
28233
|
-
raw =
|
|
28463
|
+
raw = import_node_fs24.default.readFileSync(profilePath, "utf8");
|
|
28234
28464
|
} catch {
|
|
28235
28465
|
return fallback;
|
|
28236
28466
|
}
|
|
@@ -28259,12 +28489,12 @@ init_protocol();
|
|
|
28259
28489
|
init_protocol();
|
|
28260
28490
|
|
|
28261
28491
|
// src/session/fork.ts
|
|
28262
|
-
var
|
|
28492
|
+
var import_node_fs25 = __toESM(require("fs"), 1);
|
|
28263
28493
|
var import_node_os12 = __toESM(require("os"), 1);
|
|
28264
|
-
var
|
|
28494
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
28265
28495
|
init_claude_history();
|
|
28266
28496
|
function readJsonlEntries(file) {
|
|
28267
|
-
const raw =
|
|
28497
|
+
const raw = import_node_fs25.default.readFileSync(file, "utf8");
|
|
28268
28498
|
const out = [];
|
|
28269
28499
|
for (const line of raw.split("\n")) {
|
|
28270
28500
|
const t = line.trim();
|
|
@@ -28277,10 +28507,10 @@ function readJsonlEntries(file) {
|
|
|
28277
28507
|
return out;
|
|
28278
28508
|
}
|
|
28279
28509
|
function forkSession(input) {
|
|
28280
|
-
const baseDir = input.baseDir ??
|
|
28281
|
-
const projectDir =
|
|
28282
|
-
const sourceFile =
|
|
28283
|
-
if (!
|
|
28510
|
+
const baseDir = input.baseDir ?? import_node_path27.default.join(import_node_os12.default.homedir(), ".claude");
|
|
28511
|
+
const projectDir = import_node_path27.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
28512
|
+
const sourceFile = import_node_path27.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
28513
|
+
if (!import_node_fs25.default.existsSync(sourceFile)) {
|
|
28284
28514
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
28285
28515
|
}
|
|
28286
28516
|
const entries = readJsonlEntries(sourceFile);
|
|
@@ -28310,9 +28540,9 @@ function forkSession(input) {
|
|
|
28310
28540
|
}
|
|
28311
28541
|
forkedLines.push(JSON.stringify(forked));
|
|
28312
28542
|
}
|
|
28313
|
-
const forkedFilePath =
|
|
28314
|
-
|
|
28315
|
-
|
|
28543
|
+
const forkedFilePath = import_node_path27.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
28544
|
+
import_node_fs25.default.mkdirSync(projectDir, { recursive: true });
|
|
28545
|
+
import_node_fs25.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
28316
28546
|
return { forkedToolSessionId, forkedFilePath };
|
|
28317
28547
|
}
|
|
28318
28548
|
|
|
@@ -28600,28 +28830,6 @@ function buildSessionHandlers(deps) {
|
|
|
28600
28830
|
}
|
|
28601
28831
|
};
|
|
28602
28832
|
};
|
|
28603
|
-
const peerSessionUpsert = async (frame, _client, ctx) => {
|
|
28604
|
-
if (ctx && ctx.principal.kind !== "owner") {
|
|
28605
|
-
throw new ClawdError(
|
|
28606
|
-
ERROR_CODES.UNAUTHORIZED,
|
|
28607
|
-
`principal ${ctx.principal.kind}:${ctx.principal.id} cannot peerSession:upsert`
|
|
28608
|
-
);
|
|
28609
|
-
}
|
|
28610
|
-
const file = PeerSessionUpsertArgs.parse(frame);
|
|
28611
|
-
manager.mirrorUpsert(file);
|
|
28612
|
-
return { response: { type: "peerSession:upsert:ok" }, broadcast: [] };
|
|
28613
|
-
};
|
|
28614
|
-
const peerSessionRemove = async (frame, _client, ctx) => {
|
|
28615
|
-
if (ctx && ctx.principal.kind !== "owner") {
|
|
28616
|
-
throw new ClawdError(
|
|
28617
|
-
ERROR_CODES.UNAUTHORIZED,
|
|
28618
|
-
`principal ${ctx.principal.kind}:${ctx.principal.id} cannot peerSession:remove`
|
|
28619
|
-
);
|
|
28620
|
-
}
|
|
28621
|
-
const args = PeerSessionRemoveArgs.parse(frame);
|
|
28622
|
-
manager.mirrorRemove(args.sessionId);
|
|
28623
|
-
return { response: { type: "peerSession:remove:ok" }, broadcast: [] };
|
|
28624
|
-
};
|
|
28625
28833
|
return {
|
|
28626
28834
|
"session:create": create,
|
|
28627
28835
|
"session:list": list,
|
|
@@ -28647,9 +28855,7 @@ function buildSessionHandlers(deps) {
|
|
|
28647
28855
|
"session:cancelQuestion": cancelQuestion,
|
|
28648
28856
|
"session:pty:input": ptyInput,
|
|
28649
28857
|
"session:pty:resize": ptyResize,
|
|
28650
|
-
"session:pty:snapshot": ptySnapshot
|
|
28651
|
-
"peerSession:upsert": peerSessionUpsert,
|
|
28652
|
-
"peerSession:remove": peerSessionRemove
|
|
28858
|
+
"session:pty:snapshot": ptySnapshot
|
|
28653
28859
|
};
|
|
28654
28860
|
}
|
|
28655
28861
|
|
|
@@ -28668,7 +28874,7 @@ function buildPermissionHandlers(deps) {
|
|
|
28668
28874
|
}
|
|
28669
28875
|
|
|
28670
28876
|
// src/handlers/history.ts
|
|
28671
|
-
var
|
|
28877
|
+
var path37 = __toESM(require("path"), 1);
|
|
28672
28878
|
init_protocol();
|
|
28673
28879
|
|
|
28674
28880
|
// src/session/recent-dirs.ts
|
|
@@ -28686,7 +28892,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
28686
28892
|
}
|
|
28687
28893
|
|
|
28688
28894
|
// src/permission/persona-paths.ts
|
|
28689
|
-
var
|
|
28895
|
+
var path36 = __toESM(require("path"), 1);
|
|
28690
28896
|
function getAllowedPersonaIds(grants, action) {
|
|
28691
28897
|
const ids = /* @__PURE__ */ new Set();
|
|
28692
28898
|
for (const g2 of grants) {
|
|
@@ -28699,26 +28905,26 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
28699
28905
|
return ids;
|
|
28700
28906
|
}
|
|
28701
28907
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read") {
|
|
28702
|
-
const root =
|
|
28703
|
-
const target =
|
|
28704
|
-
const sep3 = root.endsWith(
|
|
28908
|
+
const root = path36.resolve(personaRoot);
|
|
28909
|
+
const target = path36.resolve(absPath);
|
|
28910
|
+
const sep3 = root.endsWith(path36.sep) ? "" : path36.sep;
|
|
28705
28911
|
if (!target.startsWith(root + sep3)) return false;
|
|
28706
|
-
const rel =
|
|
28912
|
+
const rel = path36.relative(root, target);
|
|
28707
28913
|
if (!rel || rel.startsWith("..")) return false;
|
|
28708
|
-
const personaId = rel.split(
|
|
28914
|
+
const personaId = rel.split(path36.sep)[0];
|
|
28709
28915
|
if (!personaId) return false;
|
|
28710
28916
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
28711
28917
|
if (allowed === "*") return true;
|
|
28712
28918
|
return allowed.has(personaId);
|
|
28713
28919
|
}
|
|
28714
28920
|
function personaIdFromPath(absPath, personaRoot) {
|
|
28715
|
-
const root =
|
|
28716
|
-
const target =
|
|
28717
|
-
const sep3 = root.endsWith(
|
|
28921
|
+
const root = path36.resolve(personaRoot);
|
|
28922
|
+
const target = path36.resolve(absPath);
|
|
28923
|
+
const sep3 = root.endsWith(path36.sep) ? "" : path36.sep;
|
|
28718
28924
|
if (!target.startsWith(root + sep3)) return null;
|
|
28719
|
-
const rel =
|
|
28925
|
+
const rel = path36.relative(root, target);
|
|
28720
28926
|
if (!rel || rel.startsWith("..")) return null;
|
|
28721
|
-
const id = rel.split(
|
|
28927
|
+
const id = rel.split(path36.sep)[0];
|
|
28722
28928
|
return id || null;
|
|
28723
28929
|
}
|
|
28724
28930
|
|
|
@@ -28743,7 +28949,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28743
28949
|
if (!pid) return false;
|
|
28744
28950
|
return isGuestPathAllowed(
|
|
28745
28951
|
ctx.grants,
|
|
28746
|
-
|
|
28952
|
+
path37.join(personaRoot, pid),
|
|
28747
28953
|
personaRoot,
|
|
28748
28954
|
"read"
|
|
28749
28955
|
);
|
|
@@ -28754,7 +28960,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28754
28960
|
};
|
|
28755
28961
|
const list = async (frame, _client, ctx) => {
|
|
28756
28962
|
const args = HistoryListArgs.parse(frame);
|
|
28757
|
-
assertGuestPath(ctx,
|
|
28963
|
+
assertGuestPath(ctx, path37.resolve(args.projectPath), personaRoot, "history:list");
|
|
28758
28964
|
const sessions = await history.listSessions(args);
|
|
28759
28965
|
return { response: { type: "history:list", sessions } };
|
|
28760
28966
|
};
|
|
@@ -28786,13 +28992,13 @@ function buildHistoryHandlers(deps) {
|
|
|
28786
28992
|
};
|
|
28787
28993
|
const subagents = async (frame, _client, ctx) => {
|
|
28788
28994
|
const args = HistorySubagentsArgs.parse(frame);
|
|
28789
|
-
assertGuestPath(ctx,
|
|
28995
|
+
assertGuestPath(ctx, path37.resolve(args.cwd), personaRoot, "history:subagents");
|
|
28790
28996
|
const subs = await history.listSubagents(args);
|
|
28791
28997
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
28792
28998
|
};
|
|
28793
28999
|
const subagentRead = async (frame, _client, ctx) => {
|
|
28794
29000
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
28795
|
-
assertGuestPath(ctx,
|
|
29001
|
+
assertGuestPath(ctx, path37.resolve(args.cwd), personaRoot, "history:subagent-read");
|
|
28796
29002
|
const res = await history.readSubagent(args);
|
|
28797
29003
|
return { response: { type: "history:subagent-read", ...res } };
|
|
28798
29004
|
};
|
|
@@ -28800,7 +29006,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28800
29006
|
const dirs = listRecentDirs(store);
|
|
28801
29007
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
28802
29008
|
const filtered = dirs.filter(
|
|
28803
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
29009
|
+
(d) => isGuestPathAllowed(ctx.grants, path37.resolve(d.cwd), personaRoot, "read")
|
|
28804
29010
|
);
|
|
28805
29011
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
28806
29012
|
}
|
|
@@ -28817,7 +29023,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28817
29023
|
}
|
|
28818
29024
|
|
|
28819
29025
|
// src/handlers/workspace.ts
|
|
28820
|
-
var
|
|
29026
|
+
var path38 = __toESM(require("path"), 1);
|
|
28821
29027
|
var os13 = __toESM(require("os"), 1);
|
|
28822
29028
|
init_protocol();
|
|
28823
29029
|
init_protocol();
|
|
@@ -28858,22 +29064,22 @@ function buildWorkspaceHandlers(deps) {
|
|
|
28858
29064
|
const args = WorkspaceListArgs.parse(frame);
|
|
28859
29065
|
const isGuest = ctx?.principal.kind === "guest";
|
|
28860
29066
|
const fallbackCwd = isGuest && personaRoot ? personaRoot : os13.homedir();
|
|
28861
|
-
const resolvedCwd =
|
|
28862
|
-
const target = args.path ?
|
|
29067
|
+
const resolvedCwd = path38.resolve(args.cwd ?? fallbackCwd);
|
|
29068
|
+
const target = args.path ? path38.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
28863
29069
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list");
|
|
28864
29070
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
28865
29071
|
return { response: { type: "workspace:list", ...res } };
|
|
28866
29072
|
};
|
|
28867
29073
|
const read = async (frame, _client, ctx) => {
|
|
28868
29074
|
const args = WorkspaceReadArgs.parse(frame);
|
|
28869
|
-
const target =
|
|
29075
|
+
const target = path38.isAbsolute(args.path) ? path38.resolve(args.path) : path38.resolve(args.cwd, args.path);
|
|
28870
29076
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read");
|
|
28871
29077
|
const res = workspace.read(args);
|
|
28872
29078
|
return { response: { type: "workspace:read", ...res } };
|
|
28873
29079
|
};
|
|
28874
29080
|
const skillsList = async (frame, _client, ctx) => {
|
|
28875
29081
|
const args = SkillsListArgs.parse(frame);
|
|
28876
|
-
const cwdAbs =
|
|
29082
|
+
const cwdAbs = path38.resolve(args.cwd);
|
|
28877
29083
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list");
|
|
28878
29084
|
const list2 = skills.list(args);
|
|
28879
29085
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -28885,7 +29091,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
28885
29091
|
};
|
|
28886
29092
|
const agentsList = async (frame, _client, ctx) => {
|
|
28887
29093
|
const args = AgentsListArgs.parse(frame);
|
|
28888
|
-
const cwdAbs =
|
|
29094
|
+
const cwdAbs = path38.resolve(args.cwd);
|
|
28889
29095
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list");
|
|
28890
29096
|
const list2 = agents.list(args);
|
|
28891
29097
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -28904,20 +29110,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
28904
29110
|
}
|
|
28905
29111
|
|
|
28906
29112
|
// src/handlers/git.ts
|
|
28907
|
-
var
|
|
29113
|
+
var path40 = __toESM(require("path"), 1);
|
|
28908
29114
|
init_protocol();
|
|
28909
29115
|
init_protocol();
|
|
28910
29116
|
|
|
28911
29117
|
// src/workspace/git.ts
|
|
28912
29118
|
var import_node_child_process6 = require("child_process");
|
|
28913
|
-
var
|
|
28914
|
-
var
|
|
29119
|
+
var import_node_fs26 = __toESM(require("fs"), 1);
|
|
29120
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
28915
29121
|
var import_node_util = require("util");
|
|
28916
29122
|
var pexec = (0, import_node_util.promisify)(import_node_child_process6.execFile);
|
|
28917
29123
|
function normalizePath(p2) {
|
|
28918
|
-
const resolved =
|
|
29124
|
+
const resolved = import_node_path28.default.resolve(p2);
|
|
28919
29125
|
try {
|
|
28920
|
-
return
|
|
29126
|
+
return import_node_fs26.default.realpathSync(resolved);
|
|
28921
29127
|
} catch {
|
|
28922
29128
|
return resolved;
|
|
28923
29129
|
}
|
|
@@ -28990,7 +29196,7 @@ async function listGitBranches(cwd) {
|
|
|
28990
29196
|
// src/handlers/git.ts
|
|
28991
29197
|
function assertGuestCwd(ctx, cwd, personaRoot, method) {
|
|
28992
29198
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
28993
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
29199
|
+
if (!isGuestPathAllowed(ctx.grants, path40.resolve(cwd), personaRoot, "read")) {
|
|
28994
29200
|
throw new ClawdError(
|
|
28995
29201
|
ERROR_CODES.UNAUTHORIZED,
|
|
28996
29202
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -29477,7 +29683,7 @@ function buildPersonaHandlers(deps) {
|
|
|
29477
29683
|
}
|
|
29478
29684
|
|
|
29479
29685
|
// src/handlers/attachment.ts
|
|
29480
|
-
var
|
|
29686
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
29481
29687
|
init_protocol();
|
|
29482
29688
|
init_protocol();
|
|
29483
29689
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -29531,12 +29737,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
29531
29737
|
`session ${args.sessionId} scope unresolved`
|
|
29532
29738
|
);
|
|
29533
29739
|
}
|
|
29534
|
-
const cwdAbs =
|
|
29535
|
-
const candidateAbs =
|
|
29740
|
+
const cwdAbs = import_node_path29.default.resolve(sessionFile.cwd);
|
|
29741
|
+
const candidateAbs = import_node_path29.default.isAbsolute(args.relPath) ? import_node_path29.default.resolve(args.relPath) : import_node_path29.default.resolve(cwdAbs, args.relPath);
|
|
29536
29742
|
assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl");
|
|
29537
29743
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
29538
29744
|
const entry = entries.find((e) => {
|
|
29539
|
-
const storedAbs =
|
|
29745
|
+
const storedAbs = import_node_path29.default.isAbsolute(e.relPath) ? import_node_path29.default.resolve(e.relPath) : import_node_path29.default.resolve(cwdAbs, e.relPath);
|
|
29540
29746
|
return storedAbs === candidateAbs && !e.stale;
|
|
29541
29747
|
});
|
|
29542
29748
|
if (!entry) {
|
|
@@ -29560,7 +29766,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29560
29766
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
29561
29767
|
const f = deps.sessionStore.read(sessionId);
|
|
29562
29768
|
if (!f) return;
|
|
29563
|
-
assertGuestAttachmentPath(ctx,
|
|
29769
|
+
assertGuestAttachmentPath(ctx, import_node_path29.default.resolve(f.cwd), deps.personaRoot, method);
|
|
29564
29770
|
}
|
|
29565
29771
|
const groupAdd = async (frame, _client, ctx) => {
|
|
29566
29772
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -29601,7 +29807,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29601
29807
|
}
|
|
29602
29808
|
const entries = deps.groupFileStore.list(scope, parsed.data.sessionId);
|
|
29603
29809
|
const target = entries.find((e) => e.relPath === parsed.data.relPath);
|
|
29604
|
-
if (target?.from === "owner") {
|
|
29810
|
+
if (target?.from === "owner" || target?.from === "user-upload") {
|
|
29605
29811
|
deps.groupFileStore.remove(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
29606
29812
|
} else {
|
|
29607
29813
|
deps.groupFileStore.markStale(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
@@ -29727,11 +29933,6 @@ var METHOD_GRANT_MAP = {
|
|
|
29727
29933
|
"session:pin": CAPABILITY_SCOPED,
|
|
29728
29934
|
"session:reorderPins": ADMIN_ANY,
|
|
29729
29935
|
// owner 全局操作,无 personaId 维度
|
|
29730
|
-
// mirror peer sessions (2026-05-26):owner-only — guest 不应能写/删本机 mirror SessionFile
|
|
29731
|
-
// (会绕过 #739 隔离规则把伪造身份的 SessionFile 写进 default store)。handler 端内部 ctx
|
|
29732
|
-
// 校验,dispatcher 层一刀切 admin 也兜底。
|
|
29733
|
-
"peerSession:upsert": ADMIN_ANY,
|
|
29734
|
-
"peerSession:remove": ADMIN_ANY,
|
|
29735
29936
|
"permission:respond": CAPABILITY_SCOPED,
|
|
29736
29937
|
"session:answerQuestion": CAPABILITY_SCOPED,
|
|
29737
29938
|
"session:cancelQuestion": CAPABILITY_SCOPED,
|
|
@@ -29802,7 +30003,7 @@ function computeGrantForFrame(method, frame) {
|
|
|
29802
30003
|
async function startDaemon(config) {
|
|
29803
30004
|
const logger = createLogger({
|
|
29804
30005
|
level: config.logLevel,
|
|
29805
|
-
file:
|
|
30006
|
+
file: import_node_path30.default.join(config.dataDir, "clawd.log")
|
|
29806
30007
|
});
|
|
29807
30008
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
29808
30009
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
@@ -29889,7 +30090,7 @@ async function startDaemon(config) {
|
|
|
29889
30090
|
const agents = new AgentsScanner();
|
|
29890
30091
|
const history = new ClaudeHistoryReader();
|
|
29891
30092
|
let transport = null;
|
|
29892
|
-
const personaStore = new PersonaStore(
|
|
30093
|
+
const personaStore = new PersonaStore(import_node_path30.default.join(config.dataDir, "personas"));
|
|
29893
30094
|
const defaultsRoot = findDefaultsRoot();
|
|
29894
30095
|
if (defaultsRoot) {
|
|
29895
30096
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -29906,7 +30107,7 @@ async function startDaemon(config) {
|
|
|
29906
30107
|
getAdapter,
|
|
29907
30108
|
historyReader: history,
|
|
29908
30109
|
dataDir: config.dataDir,
|
|
29909
|
-
personaRoot:
|
|
30110
|
+
personaRoot: import_node_path30.default.join(config.dataDir, "personas"),
|
|
29910
30111
|
personaStore,
|
|
29911
30112
|
ownerDisplayName,
|
|
29912
30113
|
ownerPrincipalId,
|
|
@@ -29930,10 +30131,10 @@ async function startDaemon(config) {
|
|
|
29930
30131
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
29931
30132
|
attachmentGroup: {
|
|
29932
30133
|
onFileEdit: (input) => {
|
|
29933
|
-
const absPath =
|
|
30134
|
+
const absPath = import_node_path30.default.isAbsolute(input.relPath) ? input.relPath : import_node_path30.default.join(input.cwd, input.relPath);
|
|
29934
30135
|
let size = 0;
|
|
29935
30136
|
try {
|
|
29936
|
-
size =
|
|
30137
|
+
size = import_node_fs27.default.statSync(absPath).size;
|
|
29937
30138
|
} catch (err) {
|
|
29938
30139
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
29939
30140
|
sessionId: input.sessionId,
|
|
@@ -30046,10 +30247,10 @@ async function startDaemon(config) {
|
|
|
30046
30247
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
30047
30248
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30048
30249
|
// guest path guard:candidate 必须在 personaRoot 子树下
|
|
30049
|
-
personaRoot:
|
|
30250
|
+
personaRoot: import_node_path30.default.join(config.dataDir, "personas")
|
|
30050
30251
|
},
|
|
30051
30252
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
30052
|
-
personaRoot:
|
|
30253
|
+
personaRoot: import_node_path30.default.join(config.dataDir, "personas"),
|
|
30053
30254
|
// capability:list / delete handler 依赖
|
|
30054
30255
|
capabilityManager,
|
|
30055
30256
|
// personal-cap:get 返回的 shareBaseUrl 用此 base URL:
|
|
@@ -30085,7 +30286,19 @@ async function startDaemon(config) {
|
|
|
30085
30286
|
sessionStore: store,
|
|
30086
30287
|
// /files HMAC verify 用 auth.json 的 signSecret 字段(与 attachment.signUrl 同源)。
|
|
30087
30288
|
// --auth-token CLI 模式没 signSecret → 路由返 501,sign URL 功能整体禁用。
|
|
30088
|
-
getSignSecret: () => authFile?.signSecret ?? null
|
|
30289
|
+
getSignSecret: () => authFile?.signSecret ?? null,
|
|
30290
|
+
// chatinput 附件上传(POST /attachments/upload,spec §6.1)。upload pipeline
|
|
30291
|
+
// 在 session.cwd 下的 .attachments/ 落盘;signSecret 与 /files HMAC 同源;
|
|
30292
|
+
// tunnel 未启时返 503 TUNNEL_NOT_READY 让前端显式提示。
|
|
30293
|
+
attachmentUpload: {
|
|
30294
|
+
groupFileStore,
|
|
30295
|
+
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30296
|
+
getSessionDir: (sid) => {
|
|
30297
|
+
const f = manager.findOwnedSession(sid);
|
|
30298
|
+
return f ? f.cwd : null;
|
|
30299
|
+
},
|
|
30300
|
+
getHttpBaseUrl: () => currentTunnelUrl ? getHttpBaseUrl() : null
|
|
30301
|
+
}
|
|
30089
30302
|
});
|
|
30090
30303
|
wsServer = new LocalWsServer({
|
|
30091
30304
|
host: config.host,
|
|
@@ -30250,8 +30463,8 @@ async function startDaemon(config) {
|
|
|
30250
30463
|
const lines = [
|
|
30251
30464
|
`Tunnel: ${r.url}`,
|
|
30252
30465
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
30253
|
-
`Frpc config: ${
|
|
30254
|
-
`Frpc log: ${
|
|
30466
|
+
`Frpc config: ${import_node_path30.default.join(config.dataDir, "frpc.toml")}`,
|
|
30467
|
+
`Frpc log: ${import_node_path30.default.join(config.dataDir, "frpc.log")}`
|
|
30255
30468
|
];
|
|
30256
30469
|
const width = Math.max(...lines.map((l) => l.length));
|
|
30257
30470
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -30264,8 +30477,8 @@ ${bar}
|
|
|
30264
30477
|
|
|
30265
30478
|
`);
|
|
30266
30479
|
try {
|
|
30267
|
-
const connectPath =
|
|
30268
|
-
|
|
30480
|
+
const connectPath = import_node_path30.default.join(config.dataDir, "connect.txt");
|
|
30481
|
+
import_node_fs27.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
30269
30482
|
} catch {
|
|
30270
30483
|
}
|
|
30271
30484
|
} catch (err) {
|
|
@@ -30290,8 +30503,29 @@ ${bar}
|
|
|
30290
30503
|
logger.warn("tunnel unavailable, degraded to local mode", { reason: tunnelError });
|
|
30291
30504
|
}
|
|
30292
30505
|
}
|
|
30506
|
+
const tickAttachmentGc = () => {
|
|
30507
|
+
try {
|
|
30508
|
+
runAttachmentGc({
|
|
30509
|
+
dataDir: config.dataDir,
|
|
30510
|
+
groupFileStore,
|
|
30511
|
+
sessionScopes: manager.listOwnedSessionScopes().map(({ scope, sessionId }) => ({
|
|
30512
|
+
scope,
|
|
30513
|
+
sessionId
|
|
30514
|
+
})),
|
|
30515
|
+
// 注入 cwd 让 GC 直接定位 .attachments 目录(与 upload 落盘点一致)
|
|
30516
|
+
getSessionCwd: (sid) => manager.findOwnedSession(sid)?.cwd ?? null,
|
|
30517
|
+
logger
|
|
30518
|
+
});
|
|
30519
|
+
} catch (err) {
|
|
30520
|
+
logger.warn("attachment gc tick failed", { err: err.message });
|
|
30521
|
+
}
|
|
30522
|
+
};
|
|
30523
|
+
setImmediate(tickAttachmentGc);
|
|
30524
|
+
const attachmentGcInterval = setInterval(tickAttachmentGc, 24 * 3600 * 1e3);
|
|
30525
|
+
attachmentGcInterval.unref();
|
|
30293
30526
|
const shutdown = async () => {
|
|
30294
30527
|
logger.info("stopping clawd");
|
|
30528
|
+
clearInterval(attachmentGcInterval);
|
|
30295
30529
|
observer.stopAll();
|
|
30296
30530
|
manager.stopAll();
|
|
30297
30531
|
if (tunnelMgr) {
|
|
@@ -30309,9 +30543,9 @@ ${bar}
|
|
|
30309
30543
|
};
|
|
30310
30544
|
}
|
|
30311
30545
|
function migrateDropPersonsDir(dataDir) {
|
|
30312
|
-
const dir =
|
|
30546
|
+
const dir = import_node_path30.default.join(dataDir, "persons");
|
|
30313
30547
|
try {
|
|
30314
|
-
|
|
30548
|
+
import_node_fs27.default.rmSync(dir, { recursive: true, force: true });
|
|
30315
30549
|
} catch {
|
|
30316
30550
|
}
|
|
30317
30551
|
}
|