@clawos-dev/clawd 0.2.86-beta.162.77b3c16 → 0.2.87-beta.163.de7e74a
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 +677 -546
- 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)
|
|
@@ -9462,7 +9427,7 @@ var require_multistream = __commonJS({
|
|
|
9462
9427
|
var require_pino = __commonJS({
|
|
9463
9428
|
"../node_modules/.pnpm/pino@9.14.0/node_modules/pino/pino.js"(exports2, module2) {
|
|
9464
9429
|
"use strict";
|
|
9465
|
-
var
|
|
9430
|
+
var os16 = require("os");
|
|
9466
9431
|
var stdSerializers = require_pino_std_serializers();
|
|
9467
9432
|
var caller = require_caller();
|
|
9468
9433
|
var redaction = require_redaction();
|
|
@@ -9509,7 +9474,7 @@ var require_pino = __commonJS({
|
|
|
9509
9474
|
} = symbols;
|
|
9510
9475
|
var { epochTime, nullTime } = time;
|
|
9511
9476
|
var { pid } = process;
|
|
9512
|
-
var hostname =
|
|
9477
|
+
var hostname = os16.hostname();
|
|
9513
9478
|
var defaultErrorSerializer = stdSerializers.err;
|
|
9514
9479
|
var defaultOptions = {
|
|
9515
9480
|
level: "info",
|
|
@@ -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
|
-
|
|
20688
|
-
|
|
20689
|
-
|
|
20652
|
+
import_node_fs29 = require("fs");
|
|
20653
|
+
import_node_os14 = __toESM(require("os"), 1);
|
|
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
|
|
@@ -25573,151 +25531,28 @@ var AgentsScanner = class {
|
|
|
25573
25531
|
};
|
|
25574
25532
|
|
|
25575
25533
|
// src/observer/session-observer.ts
|
|
25576
|
-
var import_node_fs13 = __toESM(require("fs"), 1);
|
|
25577
|
-
var import_node_os9 = __toESM(require("os"), 1);
|
|
25578
|
-
var import_node_path14 = __toESM(require("path"), 1);
|
|
25579
|
-
init_claude_history();
|
|
25580
|
-
|
|
25581
|
-
// src/observer/subagent-meta-observer.ts
|
|
25582
25534
|
var import_node_fs12 = __toESM(require("fs"), 1);
|
|
25583
25535
|
var import_node_os8 = __toESM(require("os"), 1);
|
|
25584
25536
|
var import_node_path13 = __toESM(require("path"), 1);
|
|
25585
25537
|
init_claude_history();
|
|
25586
|
-
var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
|
|
25587
|
-
var SubagentMetaObserver = class {
|
|
25588
|
-
constructor(opts) {
|
|
25589
|
-
this.opts = opts;
|
|
25590
|
-
this.home = opts.home ?? import_node_os8.default.homedir();
|
|
25591
|
-
}
|
|
25592
|
-
opts;
|
|
25593
|
-
home;
|
|
25594
|
-
watches = /* @__PURE__ */ new Map();
|
|
25595
|
-
// public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
|
|
25596
|
-
resolveSubagentDir(cwd, toolSessionId) {
|
|
25597
|
-
return import_node_path13.default.join(
|
|
25598
|
-
this.home,
|
|
25599
|
-
".claude",
|
|
25600
|
-
"projects",
|
|
25601
|
-
cwdToHashDir(cwd),
|
|
25602
|
-
toolSessionId,
|
|
25603
|
-
"subagents"
|
|
25604
|
-
);
|
|
25605
|
-
}
|
|
25606
|
-
start(args) {
|
|
25607
|
-
this.stop(args.sessionId);
|
|
25608
|
-
const dirPath = this.resolveSubagentDir(args.cwd, args.toolSessionId);
|
|
25609
|
-
const w2 = {
|
|
25610
|
-
sessionId: args.sessionId,
|
|
25611
|
-
dirPath,
|
|
25612
|
-
watcher: null,
|
|
25613
|
-
pollTimer: null,
|
|
25614
|
-
emitted: /* @__PURE__ */ new Set()
|
|
25615
|
-
};
|
|
25616
|
-
this.watches.set(args.sessionId, w2);
|
|
25617
|
-
this.scan(w2);
|
|
25618
|
-
this.attachWatcher(w2);
|
|
25619
|
-
w2.pollTimer = setInterval(() => this.scan(w2), 200);
|
|
25620
|
-
return { dirPath };
|
|
25621
|
-
}
|
|
25622
|
-
// fs.watch 必须在目录存在后调用;首次启动时 subagents/ 通常不存在 —— attachWatcher
|
|
25623
|
-
// 失败就交给 pollTimer 200ms 兜底,目录一出现就被下次 poll 捡起来,那时也尝试 attach
|
|
25624
|
-
attachWatcher(w2) {
|
|
25625
|
-
if (w2.watcher) return;
|
|
25626
|
-
try {
|
|
25627
|
-
w2.watcher = import_node_fs12.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
|
|
25628
|
-
if (!name) return;
|
|
25629
|
-
const m2 = META_RE.exec(String(name));
|
|
25630
|
-
if (!m2) return;
|
|
25631
|
-
this.scanOne(w2, String(name));
|
|
25632
|
-
});
|
|
25633
|
-
} catch {
|
|
25634
|
-
}
|
|
25635
|
-
}
|
|
25636
|
-
scan(w2) {
|
|
25637
|
-
let entries;
|
|
25638
|
-
try {
|
|
25639
|
-
entries = import_node_fs12.default.readdirSync(w2.dirPath);
|
|
25640
|
-
} catch {
|
|
25641
|
-
return;
|
|
25642
|
-
}
|
|
25643
|
-
if (!w2.watcher) this.attachWatcher(w2);
|
|
25644
|
-
for (const name of entries) {
|
|
25645
|
-
this.scanOne(w2, name);
|
|
25646
|
-
}
|
|
25647
|
-
}
|
|
25648
|
-
scanOne(w2, name) {
|
|
25649
|
-
const m2 = META_RE.exec(name);
|
|
25650
|
-
if (!m2) return;
|
|
25651
|
-
const agentId = m2[1];
|
|
25652
|
-
if (w2.emitted.has(agentId)) return;
|
|
25653
|
-
const file = import_node_path13.default.join(w2.dirPath, name);
|
|
25654
|
-
let raw;
|
|
25655
|
-
try {
|
|
25656
|
-
raw = import_node_fs12.default.readFileSync(file, "utf8");
|
|
25657
|
-
} catch {
|
|
25658
|
-
return;
|
|
25659
|
-
}
|
|
25660
|
-
let obj;
|
|
25661
|
-
try {
|
|
25662
|
-
obj = JSON.parse(raw);
|
|
25663
|
-
} catch {
|
|
25664
|
-
return;
|
|
25665
|
-
}
|
|
25666
|
-
const toolUseId = typeof obj.toolUseId === "string" ? obj.toolUseId : "";
|
|
25667
|
-
if (!toolUseId) return;
|
|
25668
|
-
w2.emitted.add(agentId);
|
|
25669
|
-
const ev = {
|
|
25670
|
-
kind: "subagent_progress",
|
|
25671
|
-
toolUseId,
|
|
25672
|
-
agentId,
|
|
25673
|
-
status: "started"
|
|
25674
|
-
};
|
|
25675
|
-
if (typeof obj.description === "string" && obj.description) ev.description = obj.description;
|
|
25676
|
-
this.opts.onEvent({ sessionId: w2.sessionId, events: [ev] });
|
|
25677
|
-
}
|
|
25678
|
-
isObserving(sessionId) {
|
|
25679
|
-
return this.watches.has(sessionId);
|
|
25680
|
-
}
|
|
25681
|
-
stop(sessionId) {
|
|
25682
|
-
const w2 = this.watches.get(sessionId);
|
|
25683
|
-
if (!w2) return false;
|
|
25684
|
-
try {
|
|
25685
|
-
w2.watcher?.close();
|
|
25686
|
-
} catch {
|
|
25687
|
-
}
|
|
25688
|
-
if (w2.pollTimer) clearInterval(w2.pollTimer);
|
|
25689
|
-
this.watches.delete(sessionId);
|
|
25690
|
-
return true;
|
|
25691
|
-
}
|
|
25692
|
-
stopAll() {
|
|
25693
|
-
for (const id of [...this.watches.keys()]) this.stop(id);
|
|
25694
|
-
}
|
|
25695
|
-
};
|
|
25696
|
-
|
|
25697
|
-
// src/observer/session-observer.ts
|
|
25698
25538
|
var SessionObserver = class {
|
|
25699
25539
|
constructor(opts) {
|
|
25700
25540
|
this.opts = opts;
|
|
25701
|
-
this.home = opts.home ??
|
|
25702
|
-
this.metaObserver = opts.enableSubagentMetaObserver ? new SubagentMetaObserver({ home: this.home, onEvent: opts.onEvent }) : null;
|
|
25541
|
+
this.home = opts.home ?? import_node_os8.default.homedir();
|
|
25703
25542
|
}
|
|
25704
25543
|
opts;
|
|
25705
25544
|
home;
|
|
25706
25545
|
watches = /* @__PURE__ */ new Map();
|
|
25707
|
-
// 子 observer:监听 <projectsRoot>/<hashDir>/<toolSessionId>/subagents/ 目录的 meta.json,
|
|
25708
|
-
// 仅 TUI 模式启用——SDK 模式靠 `system.task_started` 帧(claude.ts:466)emit subagent_progress
|
|
25709
|
-
// 已经能解锁 AgentCallLine 抽屉入口,再跑 meta watcher 会让 ring buffer 留双帧
|
|
25710
|
-
metaObserver;
|
|
25711
25546
|
resolveJsonlPath(cwd, toolSessionId, override) {
|
|
25712
25547
|
if (override) return override;
|
|
25713
|
-
return
|
|
25548
|
+
return import_node_path13.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
|
|
25714
25549
|
}
|
|
25715
25550
|
start(args) {
|
|
25716
25551
|
this.stop(args.sessionId);
|
|
25717
25552
|
const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
|
|
25718
25553
|
let size = 0;
|
|
25719
25554
|
try {
|
|
25720
|
-
size =
|
|
25555
|
+
size = import_node_fs12.default.statSync(filePath).size;
|
|
25721
25556
|
} catch {
|
|
25722
25557
|
}
|
|
25723
25558
|
const w2 = {
|
|
@@ -25730,10 +25565,10 @@ var SessionObserver = class {
|
|
|
25730
25565
|
adapter: args.adapter
|
|
25731
25566
|
};
|
|
25732
25567
|
try {
|
|
25733
|
-
|
|
25568
|
+
import_node_fs12.default.mkdirSync(import_node_path13.default.dirname(filePath), { recursive: true });
|
|
25734
25569
|
} catch {
|
|
25735
25570
|
}
|
|
25736
|
-
w2.watcher =
|
|
25571
|
+
w2.watcher = import_node_fs12.default.watch(import_node_path13.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
|
|
25737
25572
|
if (!changedName || !filePath.endsWith(changedName)) return;
|
|
25738
25573
|
this.poll(w2);
|
|
25739
25574
|
});
|
|
@@ -25742,11 +25577,6 @@ var SessionObserver = class {
|
|
|
25742
25577
|
this.opts.onStatus?.(args.sessionId, "observing");
|
|
25743
25578
|
this.hydrateMetaTail(w2);
|
|
25744
25579
|
this.poll(w2);
|
|
25745
|
-
this.metaObserver?.start({
|
|
25746
|
-
sessionId: args.sessionId,
|
|
25747
|
-
cwd: args.cwd,
|
|
25748
|
-
toolSessionId: args.toolSessionId
|
|
25749
|
-
});
|
|
25750
25580
|
return { filePath };
|
|
25751
25581
|
}
|
|
25752
25582
|
// 读 JSONL 文件尾部 N 行,把每行的 meta_update 字段**合并成一个 patch** 喂给 reducer。
|
|
@@ -25756,7 +25586,7 @@ var SessionObserver = class {
|
|
|
25756
25586
|
// 异常静默吞,不阻塞 watcher 启动
|
|
25757
25587
|
hydrateMetaTail(w2, maxLines = 200) {
|
|
25758
25588
|
try {
|
|
25759
|
-
const raw =
|
|
25589
|
+
const raw = import_node_fs12.default.readFileSync(w2.filePath, "utf8");
|
|
25760
25590
|
if (!raw) return;
|
|
25761
25591
|
const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
25762
25592
|
if (allLines.length === 0) return;
|
|
@@ -25780,7 +25610,7 @@ var SessionObserver = class {
|
|
|
25780
25610
|
poll(w2) {
|
|
25781
25611
|
let size = 0;
|
|
25782
25612
|
try {
|
|
25783
|
-
size =
|
|
25613
|
+
size = import_node_fs12.default.statSync(w2.filePath).size;
|
|
25784
25614
|
} catch {
|
|
25785
25615
|
return;
|
|
25786
25616
|
}
|
|
@@ -25789,11 +25619,11 @@ var SessionObserver = class {
|
|
|
25789
25619
|
w2.buf = "";
|
|
25790
25620
|
}
|
|
25791
25621
|
if (size === w2.lastSize) return;
|
|
25792
|
-
const fd =
|
|
25622
|
+
const fd = import_node_fs12.default.openSync(w2.filePath, "r");
|
|
25793
25623
|
try {
|
|
25794
25624
|
const len = size - w2.lastSize;
|
|
25795
25625
|
const buf = Buffer.alloc(len);
|
|
25796
|
-
|
|
25626
|
+
import_node_fs12.default.readSync(fd, buf, 0, len, w2.lastSize);
|
|
25797
25627
|
w2.lastSize = size;
|
|
25798
25628
|
w2.buf += buf.toString("utf8");
|
|
25799
25629
|
let newlineIndex;
|
|
@@ -25807,7 +25637,7 @@ var SessionObserver = class {
|
|
|
25807
25637
|
}
|
|
25808
25638
|
}
|
|
25809
25639
|
} finally {
|
|
25810
|
-
|
|
25640
|
+
import_node_fs12.default.closeSync(fd);
|
|
25811
25641
|
}
|
|
25812
25642
|
}
|
|
25813
25643
|
// 解析 JSONL 单行:仅当是主链 user 文本行(非 sidechain / 非 sub-agent / message.role='user'
|
|
@@ -25860,13 +25690,11 @@ var SessionObserver = class {
|
|
|
25860
25690
|
}
|
|
25861
25691
|
if (w2.pollTimer) clearInterval(w2.pollTimer);
|
|
25862
25692
|
this.watches.delete(sessionId);
|
|
25863
|
-
this.metaObserver?.stop(sessionId);
|
|
25864
25693
|
this.opts.onStatus?.(sessionId, "stopped");
|
|
25865
25694
|
return true;
|
|
25866
25695
|
}
|
|
25867
25696
|
stopAll() {
|
|
25868
25697
|
for (const id of [...this.watches.keys()]) this.stop(id);
|
|
25869
|
-
this.metaObserver?.stopAll();
|
|
25870
25698
|
}
|
|
25871
25699
|
};
|
|
25872
25700
|
|
|
@@ -26467,14 +26295,14 @@ function authenticate(token, deps) {
|
|
|
26467
26295
|
}
|
|
26468
26296
|
|
|
26469
26297
|
// src/permission/capability-store.ts
|
|
26470
|
-
var
|
|
26471
|
-
var
|
|
26298
|
+
var fs15 = __toESM(require("fs"), 1);
|
|
26299
|
+
var path18 = __toESM(require("path"), 1);
|
|
26472
26300
|
var CAPABILITIES_FILE_NAME = "capabilities.json";
|
|
26473
26301
|
var FILE_VERSION = 1;
|
|
26474
26302
|
var CapabilityStore = class {
|
|
26475
26303
|
constructor(dataDir) {
|
|
26476
26304
|
this.dataDir = dataDir;
|
|
26477
|
-
|
|
26305
|
+
fs15.mkdirSync(dataDir, { recursive: true });
|
|
26478
26306
|
this.cache = this.readFromDisk();
|
|
26479
26307
|
}
|
|
26480
26308
|
dataDir;
|
|
@@ -26498,13 +26326,13 @@ var CapabilityStore = class {
|
|
|
26498
26326
|
this.flush();
|
|
26499
26327
|
}
|
|
26500
26328
|
filePath() {
|
|
26501
|
-
return
|
|
26329
|
+
return path18.join(this.dataDir, CAPABILITIES_FILE_NAME);
|
|
26502
26330
|
}
|
|
26503
26331
|
readFromDisk() {
|
|
26504
26332
|
const file = this.filePath();
|
|
26505
26333
|
let raw;
|
|
26506
26334
|
try {
|
|
26507
|
-
raw =
|
|
26335
|
+
raw = fs15.readFileSync(file, "utf8");
|
|
26508
26336
|
} catch (err) {
|
|
26509
26337
|
if (err?.code === "ENOENT") return [];
|
|
26510
26338
|
return [];
|
|
@@ -26532,10 +26360,10 @@ var CapabilityStore = class {
|
|
|
26532
26360
|
}
|
|
26533
26361
|
atomicWrite(file, content) {
|
|
26534
26362
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
26535
|
-
|
|
26536
|
-
|
|
26363
|
+
fs15.writeFileSync(tmp, content, { mode: 384 });
|
|
26364
|
+
fs15.renameSync(tmp, file);
|
|
26537
26365
|
try {
|
|
26538
|
-
|
|
26366
|
+
fs15.chmodSync(file, 384);
|
|
26539
26367
|
} catch {
|
|
26540
26368
|
}
|
|
26541
26369
|
}
|
|
@@ -26628,14 +26456,14 @@ var CapabilityManager = class {
|
|
|
26628
26456
|
};
|
|
26629
26457
|
|
|
26630
26458
|
// src/permission/cleanup.ts
|
|
26631
|
-
var
|
|
26459
|
+
var fs16 = __toESM(require("fs"), 1);
|
|
26632
26460
|
function cleanupGuestSessionsForCapability(cap, factory) {
|
|
26633
26461
|
const removed = [];
|
|
26634
26462
|
for (const g2 of cap.grants) {
|
|
26635
26463
|
if (g2.resource.type !== "persona") continue;
|
|
26636
26464
|
const dir = factory.vmGuestRoot(g2.resource.id, cap.id);
|
|
26637
26465
|
try {
|
|
26638
|
-
|
|
26466
|
+
fs16.rmSync(dir, { recursive: true, force: true });
|
|
26639
26467
|
removed.push(dir);
|
|
26640
26468
|
} catch {
|
|
26641
26469
|
}
|
|
@@ -26644,13 +26472,13 @@ function cleanupGuestSessionsForCapability(cap, factory) {
|
|
|
26644
26472
|
}
|
|
26645
26473
|
|
|
26646
26474
|
// src/permission/personal-capability.ts
|
|
26647
|
-
var
|
|
26648
|
-
var
|
|
26475
|
+
var import_node_fs13 = __toESM(require("fs"), 1);
|
|
26476
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
26649
26477
|
var import_node_crypto4 = __toESM(require("crypto"), 1);
|
|
26650
26478
|
var PERSONAL_CAP_FILE_NAME = "personal-capability.json";
|
|
26651
26479
|
var PERSONAL_CAP_DISPLAY_NAME = "personal";
|
|
26652
26480
|
function personalCapFilePath(dataDir) {
|
|
26653
|
-
return
|
|
26481
|
+
return import_node_path14.default.join(dataDir, PERSONAL_CAP_FILE_NAME);
|
|
26654
26482
|
}
|
|
26655
26483
|
function loadOrCreatePersonalCapability(opts) {
|
|
26656
26484
|
const file = personalCapFilePath(opts.dataDir);
|
|
@@ -26680,7 +26508,7 @@ function loadOrCreatePersonalCapability(opts) {
|
|
|
26680
26508
|
function readFile(file) {
|
|
26681
26509
|
let raw;
|
|
26682
26510
|
try {
|
|
26683
|
-
raw =
|
|
26511
|
+
raw = import_node_fs13.default.readFileSync(file, "utf8");
|
|
26684
26512
|
} catch (err) {
|
|
26685
26513
|
const code = err?.code;
|
|
26686
26514
|
if (code === "ENOENT") return null;
|
|
@@ -26701,10 +26529,10 @@ function readFile(file) {
|
|
|
26701
26529
|
return { token: obj.token, capability: capParsed.data };
|
|
26702
26530
|
}
|
|
26703
26531
|
function writeFile(file, content) {
|
|
26704
|
-
|
|
26705
|
-
|
|
26532
|
+
import_node_fs13.default.mkdirSync(import_node_path14.default.dirname(file), { recursive: true });
|
|
26533
|
+
import_node_fs13.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
26706
26534
|
try {
|
|
26707
|
-
|
|
26535
|
+
import_node_fs13.default.chmodSync(file, 384);
|
|
26708
26536
|
} catch {
|
|
26709
26537
|
}
|
|
26710
26538
|
}
|
|
@@ -26719,13 +26547,13 @@ function sha256Hex2(s) {
|
|
|
26719
26547
|
}
|
|
26720
26548
|
|
|
26721
26549
|
// src/inbox/inbox-store.ts
|
|
26722
|
-
var
|
|
26723
|
-
var
|
|
26550
|
+
var fs18 = __toESM(require("fs"), 1);
|
|
26551
|
+
var path20 = __toESM(require("path"), 1);
|
|
26724
26552
|
var INBOX_SUBDIR = "inbox";
|
|
26725
26553
|
var InboxStore = class {
|
|
26726
26554
|
constructor(dataDir) {
|
|
26727
26555
|
this.dataDir = dataDir;
|
|
26728
|
-
|
|
26556
|
+
fs18.mkdirSync(this.dirPath(), { recursive: true });
|
|
26729
26557
|
}
|
|
26730
26558
|
dataDir;
|
|
26731
26559
|
/**
|
|
@@ -26737,7 +26565,7 @@ var InboxStore = class {
|
|
|
26737
26565
|
const file = this.filePath(peerOwnerId);
|
|
26738
26566
|
let raw;
|
|
26739
26567
|
try {
|
|
26740
|
-
raw =
|
|
26568
|
+
raw = fs18.readFileSync(file, "utf8");
|
|
26741
26569
|
} catch (err) {
|
|
26742
26570
|
if (err?.code === "ENOENT") return [];
|
|
26743
26571
|
return [];
|
|
@@ -26753,7 +26581,7 @@ var InboxStore = class {
|
|
|
26753
26581
|
const dir = this.dirPath();
|
|
26754
26582
|
let entries;
|
|
26755
26583
|
try {
|
|
26756
|
-
entries =
|
|
26584
|
+
entries = fs18.readdirSync(dir);
|
|
26757
26585
|
} catch (err) {
|
|
26758
26586
|
if (err?.code === "ENOENT") return [];
|
|
26759
26587
|
return [];
|
|
@@ -26769,9 +26597,9 @@ var InboxStore = class {
|
|
|
26769
26597
|
if (existing.some((m2) => m2.id === message.id)) return;
|
|
26770
26598
|
const file = this.filePath(message.peerOwnerId);
|
|
26771
26599
|
const line = JSON.stringify(message) + "\n";
|
|
26772
|
-
|
|
26600
|
+
fs18.appendFileSync(file, line, { mode: 384 });
|
|
26773
26601
|
try {
|
|
26774
|
-
|
|
26602
|
+
fs18.chmodSync(file, 384);
|
|
26775
26603
|
} catch {
|
|
26776
26604
|
}
|
|
26777
26605
|
}
|
|
@@ -26801,7 +26629,7 @@ var InboxStore = class {
|
|
|
26801
26629
|
removeByPeerOwnerId(peerOwnerId) {
|
|
26802
26630
|
const file = this.filePath(peerOwnerId);
|
|
26803
26631
|
try {
|
|
26804
|
-
|
|
26632
|
+
fs18.unlinkSync(file);
|
|
26805
26633
|
} catch (err) {
|
|
26806
26634
|
if (err?.code === "ENOENT") return;
|
|
26807
26635
|
}
|
|
@@ -26810,18 +26638,18 @@ var InboxStore = class {
|
|
|
26810
26638
|
const file = this.filePath(peerOwnerId);
|
|
26811
26639
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
26812
26640
|
const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
|
|
26813
|
-
|
|
26814
|
-
|
|
26641
|
+
fs18.writeFileSync(tmp, content, { mode: 384 });
|
|
26642
|
+
fs18.renameSync(tmp, file);
|
|
26815
26643
|
try {
|
|
26816
|
-
|
|
26644
|
+
fs18.chmodSync(file, 384);
|
|
26817
26645
|
} catch {
|
|
26818
26646
|
}
|
|
26819
26647
|
}
|
|
26820
26648
|
dirPath() {
|
|
26821
|
-
return
|
|
26649
|
+
return path20.join(this.dataDir, INBOX_SUBDIR);
|
|
26822
26650
|
}
|
|
26823
26651
|
filePath(peerOwnerId) {
|
|
26824
|
-
return
|
|
26652
|
+
return path20.join(this.dirPath(), `${peerOwnerId}.jsonl`);
|
|
26825
26653
|
}
|
|
26826
26654
|
};
|
|
26827
26655
|
function parseAllLines(raw) {
|
|
@@ -26908,8 +26736,8 @@ var InboxManager = class {
|
|
|
26908
26736
|
};
|
|
26909
26737
|
|
|
26910
26738
|
// src/state/received-capability-store.ts
|
|
26911
|
-
var
|
|
26912
|
-
var
|
|
26739
|
+
var fs19 = __toESM(require("fs"), 1);
|
|
26740
|
+
var path21 = __toESM(require("path"), 1);
|
|
26913
26741
|
var FILE_NAME = "received-capabilities.json";
|
|
26914
26742
|
var ReceivedCapabilityStore = class {
|
|
26915
26743
|
constructor(dataDir) {
|
|
@@ -26919,10 +26747,10 @@ var ReceivedCapabilityStore = class {
|
|
|
26919
26747
|
caps = /* @__PURE__ */ new Map();
|
|
26920
26748
|
load() {
|
|
26921
26749
|
this.caps.clear();
|
|
26922
|
-
const file =
|
|
26750
|
+
const file = path21.join(this.dataDir, FILE_NAME);
|
|
26923
26751
|
let raw;
|
|
26924
26752
|
try {
|
|
26925
|
-
raw =
|
|
26753
|
+
raw = fs19.readFileSync(file, "utf8");
|
|
26926
26754
|
} catch (err) {
|
|
26927
26755
|
if (err?.code !== "ENOENT") this.renameBak(file);
|
|
26928
26756
|
return;
|
|
@@ -26959,20 +26787,20 @@ var ReceivedCapabilityStore = class {
|
|
|
26959
26787
|
this.flush();
|
|
26960
26788
|
}
|
|
26961
26789
|
flush() {
|
|
26962
|
-
const file =
|
|
26790
|
+
const file = path21.join(this.dataDir, FILE_NAME);
|
|
26963
26791
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
26964
26792
|
const content = JSON.stringify(
|
|
26965
26793
|
{ receivedCaps: Array.from(this.caps.values()) },
|
|
26966
26794
|
null,
|
|
26967
26795
|
2
|
|
26968
26796
|
);
|
|
26969
|
-
|
|
26970
|
-
|
|
26971
|
-
|
|
26797
|
+
fs19.mkdirSync(this.dataDir, { recursive: true });
|
|
26798
|
+
fs19.writeFileSync(tmp, content, { mode: 384 });
|
|
26799
|
+
fs19.renameSync(tmp, file);
|
|
26972
26800
|
}
|
|
26973
26801
|
renameBak(file) {
|
|
26974
26802
|
try {
|
|
26975
|
-
|
|
26803
|
+
fs19.renameSync(file, `${file}.bak`);
|
|
26976
26804
|
} catch {
|
|
26977
26805
|
}
|
|
26978
26806
|
}
|
|
@@ -27070,61 +26898,61 @@ async function connectRemote(args) {
|
|
|
27070
26898
|
}
|
|
27071
26899
|
|
|
27072
26900
|
// src/migrations/2026-05-20-flatten-sessions.ts
|
|
27073
|
-
var
|
|
27074
|
-
var
|
|
26901
|
+
var fs20 = __toESM(require("fs"), 1);
|
|
26902
|
+
var path22 = __toESM(require("path"), 1);
|
|
27075
26903
|
var MIGRATION_FLAG_NAME = ".migration.v1.done";
|
|
27076
26904
|
function migrateFlattenSessions(opts) {
|
|
27077
26905
|
const dataDir = opts.dataDir;
|
|
27078
26906
|
const now = opts.now ?? Date.now;
|
|
27079
|
-
const sessionsDir =
|
|
27080
|
-
const flagPath =
|
|
26907
|
+
const sessionsDir = path22.join(dataDir, "sessions");
|
|
26908
|
+
const flagPath = path22.join(sessionsDir, MIGRATION_FLAG_NAME);
|
|
27081
26909
|
if (existsSync3(flagPath)) {
|
|
27082
26910
|
return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
|
|
27083
26911
|
}
|
|
27084
26912
|
let movedBare = 0;
|
|
27085
26913
|
let movedVmOwner = 0;
|
|
27086
26914
|
let archivedListener = 0;
|
|
27087
|
-
const defaultDir =
|
|
26915
|
+
const defaultDir = path22.join(sessionsDir, "default");
|
|
27088
26916
|
if (existsSync3(defaultDir)) {
|
|
27089
26917
|
for (const entry of readdirSafe(defaultDir)) {
|
|
27090
26918
|
if (!entry.endsWith(".json")) continue;
|
|
27091
|
-
const src =
|
|
27092
|
-
const dst =
|
|
27093
|
-
|
|
26919
|
+
const src = path22.join(defaultDir, entry);
|
|
26920
|
+
const dst = path22.join(sessionsDir, entry);
|
|
26921
|
+
fs20.renameSync(src, dst);
|
|
27094
26922
|
movedBare += 1;
|
|
27095
26923
|
}
|
|
27096
26924
|
rmdirIfEmpty(defaultDir);
|
|
27097
26925
|
}
|
|
27098
26926
|
for (const pid of readdirSafe(sessionsDir)) {
|
|
27099
|
-
const personaDir =
|
|
26927
|
+
const personaDir = path22.join(sessionsDir, pid);
|
|
27100
26928
|
if (!isDir(personaDir)) continue;
|
|
27101
26929
|
if (pid === "default") continue;
|
|
27102
|
-
const ownerSrc =
|
|
26930
|
+
const ownerSrc = path22.join(personaDir, "owner");
|
|
27103
26931
|
if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
|
|
27104
|
-
const ownerDst =
|
|
27105
|
-
|
|
26932
|
+
const ownerDst = path22.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
|
|
26933
|
+
fs20.mkdirSync(ownerDst, { recursive: true });
|
|
27106
26934
|
for (const file of readdirSafe(ownerSrc)) {
|
|
27107
26935
|
if (!file.endsWith(".json")) continue;
|
|
27108
|
-
|
|
26936
|
+
fs20.renameSync(path22.join(ownerSrc, file), path22.join(ownerDst, file));
|
|
27109
26937
|
movedVmOwner += 1;
|
|
27110
26938
|
}
|
|
27111
26939
|
rmdirIfEmpty(ownerSrc);
|
|
27112
26940
|
}
|
|
27113
|
-
const listenerSrc =
|
|
26941
|
+
const listenerSrc = path22.join(personaDir, "listener");
|
|
27114
26942
|
if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
|
|
27115
|
-
const archiveDst =
|
|
27116
|
-
|
|
26943
|
+
const archiveDst = path22.join(dataDir, ".legacy", `listener-${pid}`);
|
|
26944
|
+
fs20.mkdirSync(archiveDst, { recursive: true });
|
|
27117
26945
|
for (const file of readdirSafe(listenerSrc)) {
|
|
27118
26946
|
if (!file.endsWith(".json")) continue;
|
|
27119
|
-
|
|
26947
|
+
fs20.renameSync(path22.join(listenerSrc, file), path22.join(archiveDst, file));
|
|
27120
26948
|
archivedListener += 1;
|
|
27121
26949
|
}
|
|
27122
26950
|
rmdirIfEmpty(listenerSrc);
|
|
27123
26951
|
}
|
|
27124
26952
|
rmdirIfEmpty(personaDir);
|
|
27125
26953
|
}
|
|
27126
|
-
|
|
27127
|
-
|
|
26954
|
+
fs20.mkdirSync(sessionsDir, { recursive: true });
|
|
26955
|
+
fs20.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
|
|
27128
26956
|
return {
|
|
27129
26957
|
skipped: false,
|
|
27130
26958
|
flagWritten: true,
|
|
@@ -27135,7 +26963,7 @@ function migrateFlattenSessions(opts) {
|
|
|
27135
26963
|
}
|
|
27136
26964
|
function existsSync3(p2) {
|
|
27137
26965
|
try {
|
|
27138
|
-
|
|
26966
|
+
fs20.statSync(p2);
|
|
27139
26967
|
return true;
|
|
27140
26968
|
} catch {
|
|
27141
26969
|
return false;
|
|
@@ -27143,21 +26971,21 @@ function existsSync3(p2) {
|
|
|
27143
26971
|
}
|
|
27144
26972
|
function isDir(p2) {
|
|
27145
26973
|
try {
|
|
27146
|
-
return
|
|
26974
|
+
return fs20.statSync(p2).isDirectory();
|
|
27147
26975
|
} catch {
|
|
27148
26976
|
return false;
|
|
27149
26977
|
}
|
|
27150
26978
|
}
|
|
27151
26979
|
function readdirSafe(p2) {
|
|
27152
26980
|
try {
|
|
27153
|
-
return
|
|
26981
|
+
return fs20.readdirSync(p2);
|
|
27154
26982
|
} catch {
|
|
27155
26983
|
return [];
|
|
27156
26984
|
}
|
|
27157
26985
|
}
|
|
27158
26986
|
function rmdirIfEmpty(p2) {
|
|
27159
26987
|
try {
|
|
27160
|
-
|
|
26988
|
+
fs20.rmdirSync(p2);
|
|
27161
26989
|
} catch {
|
|
27162
26990
|
}
|
|
27163
26991
|
}
|
|
@@ -27167,7 +26995,7 @@ var import_node_fs15 = __toESM(require("fs"), 1);
|
|
|
27167
26995
|
var import_node_path17 = __toESM(require("path"), 1);
|
|
27168
26996
|
|
|
27169
26997
|
// src/attachment/mime.ts
|
|
27170
|
-
var
|
|
26998
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
27171
26999
|
var TEXT_PLAIN = "text/plain; charset=utf-8";
|
|
27172
27000
|
var EXT_TO_NATIVE_MIME = {
|
|
27173
27001
|
// 图片
|
|
@@ -27274,7 +27102,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
27274
27102
|
".mk"
|
|
27275
27103
|
]);
|
|
27276
27104
|
function lookupMime(filePathOrName) {
|
|
27277
|
-
const ext =
|
|
27105
|
+
const ext = import_node_path15.default.extname(filePathOrName).toLowerCase();
|
|
27278
27106
|
if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
|
|
27279
27107
|
if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
|
|
27280
27108
|
return "application/octet-stream";
|
|
@@ -27342,7 +27170,137 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
|
|
|
27342
27170
|
return { ok: true, absPath };
|
|
27343
27171
|
}
|
|
27344
27172
|
|
|
27173
|
+
// src/attachment/upload.ts
|
|
27174
|
+
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
27175
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
27176
|
+
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
27177
|
+
var import_promises = require("stream/promises");
|
|
27178
|
+
var UploadError = class extends Error {
|
|
27179
|
+
constructor(code, message) {
|
|
27180
|
+
super(message);
|
|
27181
|
+
this.code = code;
|
|
27182
|
+
this.name = "UploadError";
|
|
27183
|
+
}
|
|
27184
|
+
code;
|
|
27185
|
+
};
|
|
27186
|
+
function assertValidFileName(fileName) {
|
|
27187
|
+
if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path16.default.basename(fileName)) {
|
|
27188
|
+
throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
|
|
27189
|
+
}
|
|
27190
|
+
}
|
|
27191
|
+
var HASH_PREFIX_LEN = 16;
|
|
27192
|
+
async function writeUploadedAttachment(args) {
|
|
27193
|
+
assertValidFileName(args.fileName);
|
|
27194
|
+
const attachmentsRoot = import_node_path16.default.join(args.sessionDir, ".attachments");
|
|
27195
|
+
try {
|
|
27196
|
+
import_node_fs14.default.mkdirSync(attachmentsRoot, { recursive: true });
|
|
27197
|
+
} catch (err) {
|
|
27198
|
+
throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
|
|
27199
|
+
}
|
|
27200
|
+
const hasher = import_node_crypto6.default.createHash("sha256");
|
|
27201
|
+
let actualSize = 0;
|
|
27202
|
+
const tmpPath = import_node_path16.default.join(
|
|
27203
|
+
attachmentsRoot,
|
|
27204
|
+
`.upload-${process.pid}-${Date.now()}-${import_node_crypto6.default.randomBytes(4).toString("hex")}`
|
|
27205
|
+
);
|
|
27206
|
+
try {
|
|
27207
|
+
await (0, import_promises.pipeline)(
|
|
27208
|
+
args.body,
|
|
27209
|
+
async function* (source) {
|
|
27210
|
+
for await (const chunk of source) {
|
|
27211
|
+
const buf = chunk;
|
|
27212
|
+
actualSize += buf.length;
|
|
27213
|
+
hasher.update(buf);
|
|
27214
|
+
yield buf;
|
|
27215
|
+
}
|
|
27216
|
+
},
|
|
27217
|
+
import_node_fs14.default.createWriteStream(tmpPath, { mode: 384 })
|
|
27218
|
+
);
|
|
27219
|
+
} catch (err) {
|
|
27220
|
+
try {
|
|
27221
|
+
import_node_fs14.default.unlinkSync(tmpPath);
|
|
27222
|
+
} catch {
|
|
27223
|
+
}
|
|
27224
|
+
throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
|
|
27225
|
+
}
|
|
27226
|
+
if (actualSize !== args.contentLength) {
|
|
27227
|
+
try {
|
|
27228
|
+
import_node_fs14.default.unlinkSync(tmpPath);
|
|
27229
|
+
} catch {
|
|
27230
|
+
}
|
|
27231
|
+
throw new UploadError(
|
|
27232
|
+
"SIZE_MISMATCH",
|
|
27233
|
+
`expected ${args.contentLength} bytes, got ${actualSize}`
|
|
27234
|
+
);
|
|
27235
|
+
}
|
|
27236
|
+
const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
|
|
27237
|
+
const hashDir = import_node_path16.default.join(attachmentsRoot, attachmentId);
|
|
27238
|
+
let finalFileName;
|
|
27239
|
+
let hashDirExists = false;
|
|
27240
|
+
try {
|
|
27241
|
+
hashDirExists = import_node_fs14.default.statSync(hashDir).isDirectory();
|
|
27242
|
+
} catch {
|
|
27243
|
+
}
|
|
27244
|
+
if (hashDirExists) {
|
|
27245
|
+
const existing = import_node_fs14.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
|
|
27246
|
+
finalFileName = existing[0] ?? args.fileName;
|
|
27247
|
+
try {
|
|
27248
|
+
import_node_fs14.default.unlinkSync(tmpPath);
|
|
27249
|
+
} catch {
|
|
27250
|
+
}
|
|
27251
|
+
} else {
|
|
27252
|
+
try {
|
|
27253
|
+
import_node_fs14.default.mkdirSync(hashDir, { recursive: true });
|
|
27254
|
+
finalFileName = args.fileName;
|
|
27255
|
+
import_node_fs14.default.renameSync(tmpPath, import_node_path16.default.join(hashDir, finalFileName));
|
|
27256
|
+
} catch (err) {
|
|
27257
|
+
try {
|
|
27258
|
+
import_node_fs14.default.unlinkSync(tmpPath);
|
|
27259
|
+
} catch {
|
|
27260
|
+
}
|
|
27261
|
+
throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
|
|
27262
|
+
}
|
|
27263
|
+
}
|
|
27264
|
+
const absPath = import_node_path16.default.join(hashDir, finalFileName);
|
|
27265
|
+
const relPath = import_node_path16.default.relative(args.sessionDir, absPath);
|
|
27266
|
+
args.groupFileStore.upsert(args.scope, args.sessionId, {
|
|
27267
|
+
relPath: absPath,
|
|
27268
|
+
// 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
|
|
27269
|
+
from: "user-upload",
|
|
27270
|
+
label: args.fileName,
|
|
27271
|
+
size: actualSize,
|
|
27272
|
+
mime: args.mimeType
|
|
27273
|
+
});
|
|
27274
|
+
return { attachmentId, absPath, relPath, sizeBytes: actualSize, mimeType: args.mimeType };
|
|
27275
|
+
}
|
|
27276
|
+
|
|
27345
27277
|
// src/transport/http-router.ts
|
|
27278
|
+
var UPLOAD_MIME_ALLOW = /* @__PURE__ */ new Set([
|
|
27279
|
+
"image/png",
|
|
27280
|
+
"image/jpeg",
|
|
27281
|
+
"image/gif",
|
|
27282
|
+
"image/webp",
|
|
27283
|
+
"application/pdf"
|
|
27284
|
+
]);
|
|
27285
|
+
var UPLOAD_MIME_ALLOW_PREFIX = ["text/"];
|
|
27286
|
+
var UPLOAD_MAX_BYTES = 25 * 1024 * 1024;
|
|
27287
|
+
var UPLOAD_CORS_HEADERS = {
|
|
27288
|
+
"Access-Control-Allow-Origin": "*",
|
|
27289
|
+
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
|
27290
|
+
"Access-Control-Allow-Headers": "Authorization, Content-Type",
|
|
27291
|
+
"Access-Control-Max-Age": "86400"
|
|
27292
|
+
};
|
|
27293
|
+
function isUploadMimeAllowed(mime) {
|
|
27294
|
+
if (UPLOAD_MIME_ALLOW.has(mime)) return true;
|
|
27295
|
+
return UPLOAD_MIME_ALLOW_PREFIX.some((p2) => mime.startsWith(p2));
|
|
27296
|
+
}
|
|
27297
|
+
function isValidUploadFileName(fileName) {
|
|
27298
|
+
if (fileName.length === 0) return false;
|
|
27299
|
+
if (fileName === "." || fileName === "..") return false;
|
|
27300
|
+
if (fileName.startsWith(".")) return false;
|
|
27301
|
+
if (fileName.includes("/") || fileName.includes("\\")) return false;
|
|
27302
|
+
return fileName === import_node_path17.default.basename(fileName);
|
|
27303
|
+
}
|
|
27346
27304
|
function createHttpRouter(deps) {
|
|
27347
27305
|
return async (req, res) => {
|
|
27348
27306
|
const url = parseUrl(req.url);
|
|
@@ -27356,9 +27314,14 @@ function createHttpRouter(deps) {
|
|
|
27356
27314
|
});
|
|
27357
27315
|
return true;
|
|
27358
27316
|
}
|
|
27359
|
-
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/")) {
|
|
27317
|
+
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/") && url.pathname !== "/attachments/upload") {
|
|
27360
27318
|
return false;
|
|
27361
27319
|
}
|
|
27320
|
+
if (url.pathname === "/attachments/upload" && req.method === "OPTIONS") {
|
|
27321
|
+
res.writeHead(204, UPLOAD_CORS_HEADERS);
|
|
27322
|
+
res.end();
|
|
27323
|
+
return true;
|
|
27324
|
+
}
|
|
27362
27325
|
if (url.pathname.startsWith("/files/") && req.method === "GET") {
|
|
27363
27326
|
const secret = deps.getSignSecret?.();
|
|
27364
27327
|
if (!secret) {
|
|
@@ -27396,6 +27359,107 @@ function createHttpRouter(deps) {
|
|
|
27396
27359
|
sendJson(res, 401, { code: "UNAUTHORIZED", message: "missing or invalid bearer token" });
|
|
27397
27360
|
return true;
|
|
27398
27361
|
}
|
|
27362
|
+
if (url.pathname === "/attachments/upload" && req.method === "POST") {
|
|
27363
|
+
const sendUploadJson = (status, body) => sendJson(res, status, body, UPLOAD_CORS_HEADERS);
|
|
27364
|
+
if (ctx.role !== "owner") {
|
|
27365
|
+
sendUploadJson(403, { code: "FORBIDDEN", message: "owner only" });
|
|
27366
|
+
return true;
|
|
27367
|
+
}
|
|
27368
|
+
const up = deps.attachmentUpload;
|
|
27369
|
+
if (!up) {
|
|
27370
|
+
sendUploadJson(501, { code: "NOT_IMPLEMENTED", message: "attachment upload not wired" });
|
|
27371
|
+
return true;
|
|
27372
|
+
}
|
|
27373
|
+
const sessionId = url.searchParams.get("sessionId");
|
|
27374
|
+
const fileName = url.searchParams.get("fileName");
|
|
27375
|
+
const mimeType = url.searchParams.get("mimeType");
|
|
27376
|
+
const contentLengthRaw = req.headers["content-length"];
|
|
27377
|
+
if (!sessionId || !fileName || !mimeType || !contentLengthRaw) {
|
|
27378
|
+
sendUploadJson(400, {
|
|
27379
|
+
code: "INVALID_PARAM",
|
|
27380
|
+
message: "missing sessionId/fileName/mimeType/Content-Length"
|
|
27381
|
+
});
|
|
27382
|
+
return true;
|
|
27383
|
+
}
|
|
27384
|
+
if (!isValidUploadFileName(fileName)) {
|
|
27385
|
+
sendUploadJson(400, {
|
|
27386
|
+
code: "INVALID_FILENAME",
|
|
27387
|
+
message: `fileName must be a plain basename (no path separators, no dotfiles): ${fileName}`
|
|
27388
|
+
});
|
|
27389
|
+
return true;
|
|
27390
|
+
}
|
|
27391
|
+
const contentLength = Number.parseInt(contentLengthRaw, 10);
|
|
27392
|
+
if (!Number.isFinite(contentLength) || contentLength < 0) {
|
|
27393
|
+
sendUploadJson(400, { code: "INVALID_PARAM", message: "malformed Content-Length" });
|
|
27394
|
+
return true;
|
|
27395
|
+
}
|
|
27396
|
+
if (contentLength > UPLOAD_MAX_BYTES) {
|
|
27397
|
+
sendUploadJson(413, {
|
|
27398
|
+
code: "FILE_TOO_LARGE",
|
|
27399
|
+
message: `max ${UPLOAD_MAX_BYTES} bytes`
|
|
27400
|
+
});
|
|
27401
|
+
return true;
|
|
27402
|
+
}
|
|
27403
|
+
if (!isUploadMimeAllowed(mimeType)) {
|
|
27404
|
+
sendUploadJson(400, {
|
|
27405
|
+
code: "UNSUPPORTED_MIME",
|
|
27406
|
+
message: `mime ${mimeType} not allowed`
|
|
27407
|
+
});
|
|
27408
|
+
return true;
|
|
27409
|
+
}
|
|
27410
|
+
const sessionDir = up.getSessionDir(sessionId);
|
|
27411
|
+
const scope = up.getSessionScope(sessionId);
|
|
27412
|
+
if (!sessionDir || !scope) {
|
|
27413
|
+
sendUploadJson(404, { code: "NOT_FOUND", message: `session ${sessionId} not found` });
|
|
27414
|
+
return true;
|
|
27415
|
+
}
|
|
27416
|
+
const httpBaseUrl = up.getHttpBaseUrl();
|
|
27417
|
+
if (!httpBaseUrl) {
|
|
27418
|
+
sendUploadJson(503, {
|
|
27419
|
+
code: "TUNNEL_NOT_READY",
|
|
27420
|
+
message: "httpBaseUrl unavailable"
|
|
27421
|
+
});
|
|
27422
|
+
return true;
|
|
27423
|
+
}
|
|
27424
|
+
const secret = deps.getSignSecret?.();
|
|
27425
|
+
if (!secret) {
|
|
27426
|
+
sendUploadJson(501, {
|
|
27427
|
+
code: "NOT_IMPLEMENTED",
|
|
27428
|
+
message: "sign secret unavailable (noAuth?)"
|
|
27429
|
+
});
|
|
27430
|
+
return true;
|
|
27431
|
+
}
|
|
27432
|
+
try {
|
|
27433
|
+
const result = await writeUploadedAttachment({
|
|
27434
|
+
sessionDir,
|
|
27435
|
+
sessionId,
|
|
27436
|
+
scope,
|
|
27437
|
+
fileName,
|
|
27438
|
+
mimeType,
|
|
27439
|
+
contentLength,
|
|
27440
|
+
body: req,
|
|
27441
|
+
groupFileStore: up.groupFileStore
|
|
27442
|
+
});
|
|
27443
|
+
const parts = signUrlParts(secret, result.absPath, null);
|
|
27444
|
+
const fullUrl = buildSignedFileUrl(httpBaseUrl, parts);
|
|
27445
|
+
sendUploadJson(200, {
|
|
27446
|
+
attachmentId: result.attachmentId,
|
|
27447
|
+
url: fullUrl,
|
|
27448
|
+
relPath: result.relPath,
|
|
27449
|
+
sizeBytes: result.sizeBytes,
|
|
27450
|
+
mimeType: result.mimeType
|
|
27451
|
+
});
|
|
27452
|
+
} catch (err) {
|
|
27453
|
+
if (err instanceof UploadError) {
|
|
27454
|
+
const status = err.code === "SIZE_MISMATCH" ? 400 : 500;
|
|
27455
|
+
sendUploadJson(status, { code: err.code, message: err.message });
|
|
27456
|
+
} else {
|
|
27457
|
+
deps.logger?.warn("attachment upload failed", { err: err.message });
|
|
27458
|
+
sendUploadJson(500, { code: "STORAGE_ERROR", message: err.message });
|
|
27459
|
+
}
|
|
27460
|
+
}
|
|
27461
|
+
return true;
|
|
27462
|
+
}
|
|
27399
27463
|
const sessionFilesMatch = url.pathname.match(/^\/session\/([^/]+)\/files$/);
|
|
27400
27464
|
if (sessionFilesMatch && req.method === "GET") {
|
|
27401
27465
|
const sid = sessionFilesMatch[1];
|
|
@@ -27477,10 +27541,78 @@ function streamFile(res, absPath, logger) {
|
|
|
27477
27541
|
stream.pipe(res);
|
|
27478
27542
|
}
|
|
27479
27543
|
|
|
27480
|
-
// src/attachment/
|
|
27544
|
+
// src/attachment/gc.ts
|
|
27481
27545
|
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
27482
27546
|
var import_node_path18 = __toESM(require("path"), 1);
|
|
27483
|
-
var
|
|
27547
|
+
var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
|
|
27548
|
+
function runAttachmentGc(args) {
|
|
27549
|
+
const now = (args.now ?? Date.now)();
|
|
27550
|
+
const ttlMs = args.ttlMs ?? DEFAULT_TTL_MS;
|
|
27551
|
+
const liveAbs = /* @__PURE__ */ new Set();
|
|
27552
|
+
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27553
|
+
for (const entry of args.groupFileStore.list(scope, sessionId)) {
|
|
27554
|
+
if (entry.stale) continue;
|
|
27555
|
+
if (import_node_path18.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
|
|
27556
|
+
}
|
|
27557
|
+
}
|
|
27558
|
+
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27559
|
+
const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path18.default.join(
|
|
27560
|
+
args.dataDir,
|
|
27561
|
+
"sessions",
|
|
27562
|
+
...scopeSubPath(scope).map(safeFileName),
|
|
27563
|
+
safeFileName(sessionId)
|
|
27564
|
+
);
|
|
27565
|
+
const attRoot = import_node_path18.default.join(sessionDir, ".attachments");
|
|
27566
|
+
let hashDirs;
|
|
27567
|
+
try {
|
|
27568
|
+
hashDirs = import_node_fs16.default.readdirSync(attRoot);
|
|
27569
|
+
} catch (err) {
|
|
27570
|
+
if (err.code === "ENOENT") continue;
|
|
27571
|
+
args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
|
|
27572
|
+
continue;
|
|
27573
|
+
}
|
|
27574
|
+
for (const hashDir of hashDirs) {
|
|
27575
|
+
const hashDirAbs = import_node_path18.default.join(attRoot, hashDir);
|
|
27576
|
+
let files;
|
|
27577
|
+
try {
|
|
27578
|
+
files = import_node_fs16.default.readdirSync(hashDirAbs);
|
|
27579
|
+
} catch {
|
|
27580
|
+
continue;
|
|
27581
|
+
}
|
|
27582
|
+
for (const name of files) {
|
|
27583
|
+
const file = import_node_path18.default.join(hashDirAbs, name);
|
|
27584
|
+
let stat;
|
|
27585
|
+
try {
|
|
27586
|
+
stat = import_node_fs16.default.statSync(file);
|
|
27587
|
+
} catch {
|
|
27588
|
+
continue;
|
|
27589
|
+
}
|
|
27590
|
+
if (!stat.isFile()) continue;
|
|
27591
|
+
const age = now - stat.mtimeMs;
|
|
27592
|
+
if (age < ttlMs) continue;
|
|
27593
|
+
if (liveAbs.has(file)) continue;
|
|
27594
|
+
try {
|
|
27595
|
+
import_node_fs16.default.unlinkSync(file);
|
|
27596
|
+
} catch (err) {
|
|
27597
|
+
args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
|
|
27598
|
+
}
|
|
27599
|
+
}
|
|
27600
|
+
try {
|
|
27601
|
+
if (import_node_fs16.default.readdirSync(hashDirAbs).length === 0) import_node_fs16.default.rmdirSync(hashDirAbs);
|
|
27602
|
+
} catch {
|
|
27603
|
+
}
|
|
27604
|
+
}
|
|
27605
|
+
try {
|
|
27606
|
+
if (import_node_fs16.default.readdirSync(attRoot).length === 0) import_node_fs16.default.rmdirSync(attRoot);
|
|
27607
|
+
} catch {
|
|
27608
|
+
}
|
|
27609
|
+
}
|
|
27610
|
+
}
|
|
27611
|
+
|
|
27612
|
+
// src/attachment/group.ts
|
|
27613
|
+
var import_node_fs17 = __toESM(require("fs"), 1);
|
|
27614
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
27615
|
+
var import_node_crypto7 = __toESM(require("crypto"), 1);
|
|
27484
27616
|
init_protocol();
|
|
27485
27617
|
var GroupFileStore = class {
|
|
27486
27618
|
dataDir;
|
|
@@ -27491,11 +27623,11 @@ var GroupFileStore = class {
|
|
|
27491
27623
|
this.logger = opts.logger;
|
|
27492
27624
|
}
|
|
27493
27625
|
rootForScope(scope) {
|
|
27494
|
-
return
|
|
27626
|
+
return import_node_path19.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
|
|
27495
27627
|
}
|
|
27496
27628
|
/** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
|
|
27497
27629
|
filePath(scope, sessionId) {
|
|
27498
|
-
return
|
|
27630
|
+
return import_node_path19.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
|
|
27499
27631
|
}
|
|
27500
27632
|
cacheKey(scope, sessionId) {
|
|
27501
27633
|
return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
|
|
@@ -27504,7 +27636,7 @@ var GroupFileStore = class {
|
|
|
27504
27636
|
readFile(scope, sessionId) {
|
|
27505
27637
|
const file = this.filePath(scope, sessionId);
|
|
27506
27638
|
try {
|
|
27507
|
-
const raw =
|
|
27639
|
+
const raw = import_node_fs17.default.readFileSync(file, "utf8");
|
|
27508
27640
|
const parsed = JSON.parse(raw);
|
|
27509
27641
|
if (!Array.isArray(parsed)) {
|
|
27510
27642
|
this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
|
|
@@ -27530,10 +27662,10 @@ var GroupFileStore = class {
|
|
|
27530
27662
|
}
|
|
27531
27663
|
writeFile(scope, sessionId, entries) {
|
|
27532
27664
|
const file = this.filePath(scope, sessionId);
|
|
27533
|
-
|
|
27665
|
+
import_node_fs17.default.mkdirSync(import_node_path19.default.dirname(file), { recursive: true });
|
|
27534
27666
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
27535
|
-
|
|
27536
|
-
|
|
27667
|
+
import_node_fs17.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
|
|
27668
|
+
import_node_fs17.default.renameSync(tmp, file);
|
|
27537
27669
|
}
|
|
27538
27670
|
/** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
|
|
27539
27671
|
list(scope, sessionId) {
|
|
@@ -27569,7 +27701,7 @@ var GroupFileStore = class {
|
|
|
27569
27701
|
entries[idx] = next;
|
|
27570
27702
|
} else {
|
|
27571
27703
|
next = {
|
|
27572
|
-
id: `gf-${
|
|
27704
|
+
id: `gf-${import_node_crypto7.default.randomBytes(6).toString("base64url")}`,
|
|
27573
27705
|
relPath: input.relPath,
|
|
27574
27706
|
from: input.from,
|
|
27575
27707
|
label: input.label,
|
|
@@ -27619,10 +27751,10 @@ var GroupFileStore = class {
|
|
|
27619
27751
|
};
|
|
27620
27752
|
|
|
27621
27753
|
// src/discovery/state-file.ts
|
|
27622
|
-
var
|
|
27623
|
-
var
|
|
27754
|
+
var import_node_fs18 = __toESM(require("fs"), 1);
|
|
27755
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
27624
27756
|
function defaultStateFilePath(dataDir) {
|
|
27625
|
-
return
|
|
27757
|
+
return import_node_path20.default.join(dataDir, "state.json");
|
|
27626
27758
|
}
|
|
27627
27759
|
function isPidAlive(pid) {
|
|
27628
27760
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
@@ -27644,7 +27776,7 @@ var StateFileManager = class {
|
|
|
27644
27776
|
}
|
|
27645
27777
|
read() {
|
|
27646
27778
|
try {
|
|
27647
|
-
const raw =
|
|
27779
|
+
const raw = import_node_fs18.default.readFileSync(this.file, "utf8");
|
|
27648
27780
|
const parsed = JSON.parse(raw);
|
|
27649
27781
|
return parsed;
|
|
27650
27782
|
} catch {
|
|
@@ -27658,34 +27790,34 @@ var StateFileManager = class {
|
|
|
27658
27790
|
return { status: "stale", existing };
|
|
27659
27791
|
}
|
|
27660
27792
|
write(state) {
|
|
27661
|
-
|
|
27793
|
+
import_node_fs18.default.mkdirSync(import_node_path20.default.dirname(this.file), { recursive: true });
|
|
27662
27794
|
const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
|
|
27663
|
-
|
|
27664
|
-
|
|
27795
|
+
import_node_fs18.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
|
|
27796
|
+
import_node_fs18.default.renameSync(tmp, this.file);
|
|
27665
27797
|
if (process.platform !== "win32") {
|
|
27666
27798
|
try {
|
|
27667
|
-
|
|
27799
|
+
import_node_fs18.default.chmodSync(this.file, 384);
|
|
27668
27800
|
} catch {
|
|
27669
27801
|
}
|
|
27670
27802
|
}
|
|
27671
27803
|
}
|
|
27672
27804
|
delete() {
|
|
27673
27805
|
try {
|
|
27674
|
-
|
|
27806
|
+
import_node_fs18.default.unlinkSync(this.file);
|
|
27675
27807
|
} catch {
|
|
27676
27808
|
}
|
|
27677
27809
|
}
|
|
27678
27810
|
};
|
|
27679
27811
|
|
|
27680
27812
|
// src/tunnel/tunnel-manager.ts
|
|
27681
|
-
var
|
|
27682
|
-
var
|
|
27683
|
-
var
|
|
27813
|
+
var import_node_fs22 = __toESM(require("fs"), 1);
|
|
27814
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
27815
|
+
var import_node_crypto8 = __toESM(require("crypto"), 1);
|
|
27684
27816
|
var import_node_child_process5 = require("child_process");
|
|
27685
27817
|
|
|
27686
27818
|
// src/tunnel/tunnel-store.ts
|
|
27687
|
-
var
|
|
27688
|
-
var
|
|
27819
|
+
var import_node_fs19 = __toESM(require("fs"), 1);
|
|
27820
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
27689
27821
|
var TunnelStore = class {
|
|
27690
27822
|
constructor(filePath) {
|
|
27691
27823
|
this.filePath = filePath;
|
|
@@ -27693,7 +27825,7 @@ var TunnelStore = class {
|
|
|
27693
27825
|
filePath;
|
|
27694
27826
|
async get() {
|
|
27695
27827
|
try {
|
|
27696
|
-
const raw = await
|
|
27828
|
+
const raw = await import_node_fs19.default.promises.readFile(this.filePath, "utf8");
|
|
27697
27829
|
const obj = JSON.parse(raw);
|
|
27698
27830
|
if (!isPersistedTunnel(obj)) return null;
|
|
27699
27831
|
return obj;
|
|
@@ -27704,22 +27836,22 @@ var TunnelStore = class {
|
|
|
27704
27836
|
}
|
|
27705
27837
|
}
|
|
27706
27838
|
async set(v2) {
|
|
27707
|
-
const dir =
|
|
27708
|
-
await
|
|
27839
|
+
const dir = import_node_path21.default.dirname(this.filePath);
|
|
27840
|
+
await import_node_fs19.default.promises.mkdir(dir, { recursive: true });
|
|
27709
27841
|
const data = JSON.stringify(v2, null, 2);
|
|
27710
27842
|
const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
27711
|
-
await
|
|
27843
|
+
await import_node_fs19.default.promises.writeFile(tmp, data, { mode: 384 });
|
|
27712
27844
|
if (process.platform !== "win32") {
|
|
27713
27845
|
try {
|
|
27714
|
-
await
|
|
27846
|
+
await import_node_fs19.default.promises.chmod(tmp, 384);
|
|
27715
27847
|
} catch {
|
|
27716
27848
|
}
|
|
27717
27849
|
}
|
|
27718
|
-
await
|
|
27850
|
+
await import_node_fs19.default.promises.rename(tmp, this.filePath);
|
|
27719
27851
|
}
|
|
27720
27852
|
async clear() {
|
|
27721
27853
|
try {
|
|
27722
|
-
await
|
|
27854
|
+
await import_node_fs19.default.promises.unlink(this.filePath);
|
|
27723
27855
|
} catch (err) {
|
|
27724
27856
|
const code = err?.code;
|
|
27725
27857
|
if (code !== "ENOENT") throw err;
|
|
@@ -27814,12 +27946,12 @@ function escape(v2) {
|
|
|
27814
27946
|
}
|
|
27815
27947
|
|
|
27816
27948
|
// src/tunnel/frpc-binary.ts
|
|
27817
|
-
var
|
|
27818
|
-
var
|
|
27819
|
-
var
|
|
27949
|
+
var import_node_fs20 = __toESM(require("fs"), 1);
|
|
27950
|
+
var import_node_os9 = __toESM(require("os"), 1);
|
|
27951
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
27820
27952
|
var import_node_child_process3 = require("child_process");
|
|
27821
27953
|
var import_node_stream2 = require("stream");
|
|
27822
|
-
var
|
|
27954
|
+
var import_promises2 = require("stream/promises");
|
|
27823
27955
|
var FRPC_VERSION = "0.68.0";
|
|
27824
27956
|
var UnsupportedPlatformError = class extends Error {
|
|
27825
27957
|
constructor(platform, arch) {
|
|
@@ -27848,20 +27980,20 @@ function frpcDownloadUrl(version2, p2) {
|
|
|
27848
27980
|
}
|
|
27849
27981
|
async function ensureFrpcBinary(opts) {
|
|
27850
27982
|
if (opts.override) {
|
|
27851
|
-
if (!
|
|
27983
|
+
if (!import_node_fs20.default.existsSync(opts.override)) {
|
|
27852
27984
|
throw new Error(`frpc binary not found at override path: ${opts.override}`);
|
|
27853
27985
|
}
|
|
27854
27986
|
return opts.override;
|
|
27855
27987
|
}
|
|
27856
27988
|
const version2 = opts.version ?? FRPC_VERSION;
|
|
27857
27989
|
const platform = opts.platform ?? detectPlatform();
|
|
27858
|
-
const binDir =
|
|
27859
|
-
|
|
27990
|
+
const binDir = import_node_path22.default.join(opts.dataDir, "bin");
|
|
27991
|
+
import_node_fs20.default.mkdirSync(binDir, { recursive: true });
|
|
27860
27992
|
cleanupStaleArtifacts(binDir);
|
|
27861
|
-
const stableBin =
|
|
27862
|
-
if (
|
|
27993
|
+
const stableBin = import_node_path22.default.join(binDir, "frpc");
|
|
27994
|
+
if (import_node_fs20.default.existsSync(stableBin)) return stableBin;
|
|
27863
27995
|
const partialBin = `${stableBin}.partial`;
|
|
27864
|
-
const tarballPath =
|
|
27996
|
+
const tarballPath = import_node_path22.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
|
|
27865
27997
|
try {
|
|
27866
27998
|
const url = frpcDownloadUrl(version2, platform);
|
|
27867
27999
|
await downloadToFile(url, tarballPath, opts.fetchImpl);
|
|
@@ -27870,8 +28002,8 @@ async function ensureFrpcBinary(opts) {
|
|
|
27870
28002
|
} else {
|
|
27871
28003
|
await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
|
|
27872
28004
|
}
|
|
27873
|
-
|
|
27874
|
-
|
|
28005
|
+
import_node_fs20.default.chmodSync(partialBin, 493);
|
|
28006
|
+
import_node_fs20.default.renameSync(partialBin, stableBin);
|
|
27875
28007
|
} finally {
|
|
27876
28008
|
safeUnlink(tarballPath);
|
|
27877
28009
|
safeUnlink(partialBin);
|
|
@@ -27881,15 +28013,15 @@ async function ensureFrpcBinary(opts) {
|
|
|
27881
28013
|
function cleanupStaleArtifacts(binDir) {
|
|
27882
28014
|
let entries;
|
|
27883
28015
|
try {
|
|
27884
|
-
entries =
|
|
28016
|
+
entries = import_node_fs20.default.readdirSync(binDir);
|
|
27885
28017
|
} catch {
|
|
27886
28018
|
return;
|
|
27887
28019
|
}
|
|
27888
28020
|
for (const name of entries) {
|
|
27889
28021
|
if (name.endsWith(".partial") || name.startsWith("extract-")) {
|
|
27890
|
-
const full =
|
|
28022
|
+
const full = import_node_path22.default.join(binDir, name);
|
|
27891
28023
|
try {
|
|
27892
|
-
|
|
28024
|
+
import_node_fs20.default.rmSync(full, { recursive: true, force: true });
|
|
27893
28025
|
} catch {
|
|
27894
28026
|
}
|
|
27895
28027
|
}
|
|
@@ -27897,7 +28029,7 @@ function cleanupStaleArtifacts(binDir) {
|
|
|
27897
28029
|
}
|
|
27898
28030
|
function safeUnlink(p2) {
|
|
27899
28031
|
try {
|
|
27900
|
-
|
|
28032
|
+
import_node_fs20.default.unlinkSync(p2);
|
|
27901
28033
|
} catch {
|
|
27902
28034
|
}
|
|
27903
28035
|
}
|
|
@@ -27908,13 +28040,13 @@ async function downloadToFile(url, dest, fetchImpl) {
|
|
|
27908
28040
|
if (!res.ok || !res.body) {
|
|
27909
28041
|
throw new Error(`download failed: ${res.status} ${res.statusText}`);
|
|
27910
28042
|
}
|
|
27911
|
-
const out =
|
|
28043
|
+
const out = import_node_fs20.default.createWriteStream(dest);
|
|
27912
28044
|
const nodeStream = import_node_stream2.Readable.fromWeb(res.body);
|
|
27913
|
-
await (0,
|
|
28045
|
+
await (0, import_promises2.pipeline)(nodeStream, out);
|
|
27914
28046
|
}
|
|
27915
28047
|
async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
|
|
27916
|
-
const work =
|
|
27917
|
-
|
|
28048
|
+
const work = import_node_path22.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
|
|
28049
|
+
import_node_fs20.default.mkdirSync(work, { recursive: true });
|
|
27918
28050
|
try {
|
|
27919
28051
|
await new Promise((resolve6, reject) => {
|
|
27920
28052
|
const proc = (0, import_node_child_process3.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
|
|
@@ -27922,32 +28054,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
|
|
|
27922
28054
|
proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
|
|
27923
28055
|
});
|
|
27924
28056
|
const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
|
|
27925
|
-
const src =
|
|
27926
|
-
if (!
|
|
28057
|
+
const src = import_node_path22.default.join(work, dirName, "frpc");
|
|
28058
|
+
if (!import_node_fs20.default.existsSync(src)) {
|
|
27927
28059
|
throw new Error(`frpc not found inside tarball at ${src}`);
|
|
27928
28060
|
}
|
|
27929
|
-
|
|
28061
|
+
import_node_fs20.default.copyFileSync(src, destBin);
|
|
27930
28062
|
} finally {
|
|
27931
|
-
|
|
28063
|
+
import_node_fs20.default.rmSync(work, { recursive: true, force: true });
|
|
27932
28064
|
}
|
|
27933
28065
|
}
|
|
27934
28066
|
|
|
27935
28067
|
// src/tunnel/frpc-process.ts
|
|
27936
|
-
var
|
|
27937
|
-
var
|
|
28068
|
+
var import_node_fs21 = __toESM(require("fs"), 1);
|
|
28069
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
27938
28070
|
var import_node_child_process4 = require("child_process");
|
|
27939
28071
|
function frpcPidFilePath(dataDir) {
|
|
27940
|
-
return
|
|
28072
|
+
return import_node_path23.default.join(dataDir, "frpc.pid");
|
|
27941
28073
|
}
|
|
27942
28074
|
function writeFrpcPid(dataDir, pid) {
|
|
27943
28075
|
try {
|
|
27944
|
-
|
|
28076
|
+
import_node_fs21.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
|
|
27945
28077
|
} catch {
|
|
27946
28078
|
}
|
|
27947
28079
|
}
|
|
27948
28080
|
function clearFrpcPid(dataDir) {
|
|
27949
28081
|
try {
|
|
27950
|
-
|
|
28082
|
+
import_node_fs21.default.unlinkSync(frpcPidFilePath(dataDir));
|
|
27951
28083
|
} catch {
|
|
27952
28084
|
}
|
|
27953
28085
|
}
|
|
@@ -27963,7 +28095,7 @@ function defaultIsPidAlive(pid) {
|
|
|
27963
28095
|
}
|
|
27964
28096
|
function defaultReadPidFile(file) {
|
|
27965
28097
|
try {
|
|
27966
|
-
return
|
|
28098
|
+
return import_node_fs21.default.readFileSync(file, "utf8");
|
|
27967
28099
|
} catch {
|
|
27968
28100
|
return null;
|
|
27969
28101
|
}
|
|
@@ -27979,7 +28111,7 @@ function defaultSleep(ms) {
|
|
|
27979
28111
|
}
|
|
27980
28112
|
async function killStaleFrpc(deps) {
|
|
27981
28113
|
const pidFile = frpcPidFilePath(deps.dataDir);
|
|
27982
|
-
const tomlPath =
|
|
28114
|
+
const tomlPath = import_node_path23.default.join(deps.dataDir, "frpc.toml");
|
|
27983
28115
|
const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
|
|
27984
28116
|
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
|
|
27985
28117
|
const killPid = deps.killPidImpl ?? defaultKillPid;
|
|
@@ -28003,7 +28135,7 @@ async function killStaleFrpc(deps) {
|
|
|
28003
28135
|
}
|
|
28004
28136
|
if (victims.size === 0) {
|
|
28005
28137
|
try {
|
|
28006
|
-
|
|
28138
|
+
import_node_fs21.default.unlinkSync(pidFile);
|
|
28007
28139
|
} catch {
|
|
28008
28140
|
}
|
|
28009
28141
|
return;
|
|
@@ -28014,7 +28146,7 @@ async function killStaleFrpc(deps) {
|
|
|
28014
28146
|
}
|
|
28015
28147
|
await sleep(deps.reapWaitMs ?? 300);
|
|
28016
28148
|
try {
|
|
28017
|
-
|
|
28149
|
+
import_node_fs21.default.unlinkSync(pidFile);
|
|
28018
28150
|
} catch {
|
|
28019
28151
|
}
|
|
28020
28152
|
}
|
|
@@ -28051,7 +28183,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
28051
28183
|
var TunnelManager = class {
|
|
28052
28184
|
constructor(deps) {
|
|
28053
28185
|
this.deps = deps;
|
|
28054
|
-
this.store = deps.store ?? new TunnelStore(
|
|
28186
|
+
this.store = deps.store ?? new TunnelStore(import_node_path24.default.join(deps.dataDir, "tunnel.json"));
|
|
28055
28187
|
this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
|
|
28056
28188
|
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
28057
28189
|
}
|
|
@@ -28178,8 +28310,8 @@ var TunnelManager = class {
|
|
|
28178
28310
|
dataDir: this.deps.dataDir,
|
|
28179
28311
|
override: this.deps.frpcBinaryOverride ?? void 0
|
|
28180
28312
|
});
|
|
28181
|
-
const tomlPath =
|
|
28182
|
-
const proxyName = `clawd-${t.subdomain}-${localPort}-${
|
|
28313
|
+
const tomlPath = import_node_path24.default.join(this.deps.dataDir, "frpc.toml");
|
|
28314
|
+
const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto8.default.randomBytes(3).toString("hex")}`;
|
|
28183
28315
|
const toml = buildFrpcToml({
|
|
28184
28316
|
serverAddr: t.frpsHost,
|
|
28185
28317
|
serverPort: t.frpsPort,
|
|
@@ -28189,12 +28321,12 @@ var TunnelManager = class {
|
|
|
28189
28321
|
localPort,
|
|
28190
28322
|
logLevel: "info"
|
|
28191
28323
|
});
|
|
28192
|
-
await
|
|
28324
|
+
await import_node_fs22.default.promises.writeFile(tomlPath, toml, { mode: 384 });
|
|
28193
28325
|
const proc = (this.deps.spawnImpl ?? import_node_child_process5.spawn)(frpcBin, ["-c", tomlPath], {
|
|
28194
28326
|
stdio: ["ignore", "pipe", "pipe"]
|
|
28195
28327
|
});
|
|
28196
|
-
const logFilePath =
|
|
28197
|
-
const logStream =
|
|
28328
|
+
const logFilePath = import_node_path24.default.join(this.deps.dataDir, "frpc.log");
|
|
28329
|
+
const logStream = import_node_fs22.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
|
|
28198
28330
|
logStream.on("error", () => {
|
|
28199
28331
|
});
|
|
28200
28332
|
const tee = (chunk) => {
|
|
@@ -28276,23 +28408,23 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
28276
28408
|
}
|
|
28277
28409
|
|
|
28278
28410
|
// src/tunnel/device-key.ts
|
|
28279
|
-
var
|
|
28280
|
-
var
|
|
28411
|
+
var import_node_os10 = __toESM(require("os"), 1);
|
|
28412
|
+
var import_node_crypto9 = __toESM(require("crypto"), 1);
|
|
28281
28413
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
28282
28414
|
function deriveStableDeviceKey(opts = {}) {
|
|
28283
|
-
const hostname = opts.hostname ??
|
|
28284
|
-
const uid = opts.uid ?? (typeof
|
|
28415
|
+
const hostname = opts.hostname ?? import_node_os10.default.hostname();
|
|
28416
|
+
const uid = opts.uid ?? (typeof import_node_os10.default.userInfo === "function" ? import_node_os10.default.userInfo().uid : 0);
|
|
28285
28417
|
const input = `${hostname}::${uid}`;
|
|
28286
|
-
return
|
|
28418
|
+
return import_node_crypto9.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
28287
28419
|
}
|
|
28288
28420
|
|
|
28289
28421
|
// src/auth-store.ts
|
|
28290
|
-
var
|
|
28291
|
-
var
|
|
28292
|
-
var
|
|
28422
|
+
var import_node_fs23 = __toESM(require("fs"), 1);
|
|
28423
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
28424
|
+
var import_node_crypto10 = __toESM(require("crypto"), 1);
|
|
28293
28425
|
var AUTH_FILE_NAME = "auth.json";
|
|
28294
28426
|
function authFilePath(dataDir) {
|
|
28295
|
-
return
|
|
28427
|
+
return import_node_path25.default.join(dataDir, AUTH_FILE_NAME);
|
|
28296
28428
|
}
|
|
28297
28429
|
function loadOrCreateAuthFile(opts) {
|
|
28298
28430
|
const file = authFilePath(opts.dataDir);
|
|
@@ -28317,14 +28449,14 @@ function loadOrCreateAuthFile(opts) {
|
|
|
28317
28449
|
return next;
|
|
28318
28450
|
}
|
|
28319
28451
|
function defaultGenerateToken2() {
|
|
28320
|
-
return
|
|
28452
|
+
return import_node_crypto10.default.randomBytes(32).toString("base64url");
|
|
28321
28453
|
}
|
|
28322
28454
|
function defaultGenerateOwnerPrincipalId() {
|
|
28323
|
-
return `owner-${
|
|
28455
|
+
return `owner-${import_node_crypto10.default.randomUUID()}`;
|
|
28324
28456
|
}
|
|
28325
28457
|
function readAuthFile(file) {
|
|
28326
28458
|
try {
|
|
28327
|
-
const raw =
|
|
28459
|
+
const raw = import_node_fs23.default.readFileSync(file, "utf8");
|
|
28328
28460
|
const parsed = JSON.parse(raw);
|
|
28329
28461
|
if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
|
|
28330
28462
|
return null;
|
|
@@ -28342,25 +28474,25 @@ function readAuthFile(file) {
|
|
|
28342
28474
|
}
|
|
28343
28475
|
}
|
|
28344
28476
|
function writeAuthFile(file, content) {
|
|
28345
|
-
|
|
28346
|
-
|
|
28477
|
+
import_node_fs23.default.mkdirSync(import_node_path25.default.dirname(file), { recursive: true });
|
|
28478
|
+
import_node_fs23.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
28347
28479
|
try {
|
|
28348
|
-
|
|
28480
|
+
import_node_fs23.default.chmodSync(file, 384);
|
|
28349
28481
|
} catch {
|
|
28350
28482
|
}
|
|
28351
28483
|
}
|
|
28352
28484
|
|
|
28353
28485
|
// src/owner-profile.ts
|
|
28354
|
-
var
|
|
28355
|
-
var
|
|
28356
|
-
var
|
|
28486
|
+
var import_node_fs24 = __toESM(require("fs"), 1);
|
|
28487
|
+
var import_node_os11 = __toESM(require("os"), 1);
|
|
28488
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
28357
28489
|
var PROFILE_FILENAME = "profile.json";
|
|
28358
28490
|
function loadOwnerDisplayName(dataDir) {
|
|
28359
|
-
const fallback =
|
|
28360
|
-
const profilePath =
|
|
28491
|
+
const fallback = import_node_os11.default.userInfo().username;
|
|
28492
|
+
const profilePath = import_node_path26.default.join(dataDir, PROFILE_FILENAME);
|
|
28361
28493
|
let raw;
|
|
28362
28494
|
try {
|
|
28363
|
-
raw =
|
|
28495
|
+
raw = import_node_fs24.default.readFileSync(profilePath, "utf8");
|
|
28364
28496
|
} catch {
|
|
28365
28497
|
return fallback;
|
|
28366
28498
|
}
|
|
@@ -28389,12 +28521,12 @@ init_protocol();
|
|
|
28389
28521
|
init_protocol();
|
|
28390
28522
|
|
|
28391
28523
|
// src/session/fork.ts
|
|
28392
|
-
var
|
|
28393
|
-
var
|
|
28394
|
-
var
|
|
28524
|
+
var import_node_fs25 = __toESM(require("fs"), 1);
|
|
28525
|
+
var import_node_os12 = __toESM(require("os"), 1);
|
|
28526
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
28395
28527
|
init_claude_history();
|
|
28396
28528
|
function readJsonlEntries(file) {
|
|
28397
|
-
const raw =
|
|
28529
|
+
const raw = import_node_fs25.default.readFileSync(file, "utf8");
|
|
28398
28530
|
const out = [];
|
|
28399
28531
|
for (const line of raw.split("\n")) {
|
|
28400
28532
|
const t = line.trim();
|
|
@@ -28407,10 +28539,10 @@ function readJsonlEntries(file) {
|
|
|
28407
28539
|
return out;
|
|
28408
28540
|
}
|
|
28409
28541
|
function forkSession(input) {
|
|
28410
|
-
const baseDir = input.baseDir ??
|
|
28411
|
-
const projectDir =
|
|
28412
|
-
const sourceFile =
|
|
28413
|
-
if (!
|
|
28542
|
+
const baseDir = input.baseDir ?? import_node_path27.default.join(import_node_os12.default.homedir(), ".claude");
|
|
28543
|
+
const projectDir = import_node_path27.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
28544
|
+
const sourceFile = import_node_path27.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
28545
|
+
if (!import_node_fs25.default.existsSync(sourceFile)) {
|
|
28414
28546
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
28415
28547
|
}
|
|
28416
28548
|
const entries = readJsonlEntries(sourceFile);
|
|
@@ -28440,9 +28572,9 @@ function forkSession(input) {
|
|
|
28440
28572
|
}
|
|
28441
28573
|
forkedLines.push(JSON.stringify(forked));
|
|
28442
28574
|
}
|
|
28443
|
-
const forkedFilePath =
|
|
28444
|
-
|
|
28445
|
-
|
|
28575
|
+
const forkedFilePath = import_node_path27.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
28576
|
+
import_node_fs25.default.mkdirSync(projectDir, { recursive: true });
|
|
28577
|
+
import_node_fs25.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
28446
28578
|
return { forkedToolSessionId, forkedFilePath };
|
|
28447
28579
|
}
|
|
28448
28580
|
|
|
@@ -28730,28 +28862,6 @@ function buildSessionHandlers(deps) {
|
|
|
28730
28862
|
}
|
|
28731
28863
|
};
|
|
28732
28864
|
};
|
|
28733
|
-
const peerSessionUpsert = async (frame, _client, ctx) => {
|
|
28734
|
-
if (ctx && ctx.principal.kind !== "owner") {
|
|
28735
|
-
throw new ClawdError(
|
|
28736
|
-
ERROR_CODES.UNAUTHORIZED,
|
|
28737
|
-
`principal ${ctx.principal.kind}:${ctx.principal.id} cannot peerSession:upsert`
|
|
28738
|
-
);
|
|
28739
|
-
}
|
|
28740
|
-
const file = PeerSessionUpsertArgs.parse(frame);
|
|
28741
|
-
manager.mirrorUpsert(file);
|
|
28742
|
-
return { response: { type: "peerSession:upsert:ok" }, broadcast: [] };
|
|
28743
|
-
};
|
|
28744
|
-
const peerSessionRemove = async (frame, _client, ctx) => {
|
|
28745
|
-
if (ctx && ctx.principal.kind !== "owner") {
|
|
28746
|
-
throw new ClawdError(
|
|
28747
|
-
ERROR_CODES.UNAUTHORIZED,
|
|
28748
|
-
`principal ${ctx.principal.kind}:${ctx.principal.id} cannot peerSession:remove`
|
|
28749
|
-
);
|
|
28750
|
-
}
|
|
28751
|
-
const args = PeerSessionRemoveArgs.parse(frame);
|
|
28752
|
-
manager.mirrorRemove(args.sessionId);
|
|
28753
|
-
return { response: { type: "peerSession:remove:ok" }, broadcast: [] };
|
|
28754
|
-
};
|
|
28755
28865
|
return {
|
|
28756
28866
|
"session:create": create,
|
|
28757
28867
|
"session:list": list,
|
|
@@ -28777,9 +28887,7 @@ function buildSessionHandlers(deps) {
|
|
|
28777
28887
|
"session:cancelQuestion": cancelQuestion,
|
|
28778
28888
|
"session:pty:input": ptyInput,
|
|
28779
28889
|
"session:pty:resize": ptyResize,
|
|
28780
|
-
"session:pty:snapshot": ptySnapshot
|
|
28781
|
-
"peerSession:upsert": peerSessionUpsert,
|
|
28782
|
-
"peerSession:remove": peerSessionRemove
|
|
28890
|
+
"session:pty:snapshot": ptySnapshot
|
|
28783
28891
|
};
|
|
28784
28892
|
}
|
|
28785
28893
|
|
|
@@ -28798,7 +28906,7 @@ function buildPermissionHandlers(deps) {
|
|
|
28798
28906
|
}
|
|
28799
28907
|
|
|
28800
28908
|
// src/handlers/history.ts
|
|
28801
|
-
var
|
|
28909
|
+
var path37 = __toESM(require("path"), 1);
|
|
28802
28910
|
init_protocol();
|
|
28803
28911
|
|
|
28804
28912
|
// src/session/recent-dirs.ts
|
|
@@ -28816,7 +28924,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
28816
28924
|
}
|
|
28817
28925
|
|
|
28818
28926
|
// src/permission/persona-paths.ts
|
|
28819
|
-
var
|
|
28927
|
+
var path36 = __toESM(require("path"), 1);
|
|
28820
28928
|
function getAllowedPersonaIds(grants, action) {
|
|
28821
28929
|
const ids = /* @__PURE__ */ new Set();
|
|
28822
28930
|
for (const g2 of grants) {
|
|
@@ -28829,26 +28937,26 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
28829
28937
|
return ids;
|
|
28830
28938
|
}
|
|
28831
28939
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read") {
|
|
28832
|
-
const root =
|
|
28833
|
-
const target =
|
|
28834
|
-
const sep3 = root.endsWith(
|
|
28940
|
+
const root = path36.resolve(personaRoot);
|
|
28941
|
+
const target = path36.resolve(absPath);
|
|
28942
|
+
const sep3 = root.endsWith(path36.sep) ? "" : path36.sep;
|
|
28835
28943
|
if (!target.startsWith(root + sep3)) return false;
|
|
28836
|
-
const rel =
|
|
28944
|
+
const rel = path36.relative(root, target);
|
|
28837
28945
|
if (!rel || rel.startsWith("..")) return false;
|
|
28838
|
-
const personaId = rel.split(
|
|
28946
|
+
const personaId = rel.split(path36.sep)[0];
|
|
28839
28947
|
if (!personaId) return false;
|
|
28840
28948
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
28841
28949
|
if (allowed === "*") return true;
|
|
28842
28950
|
return allowed.has(personaId);
|
|
28843
28951
|
}
|
|
28844
28952
|
function personaIdFromPath(absPath, personaRoot) {
|
|
28845
|
-
const root =
|
|
28846
|
-
const target =
|
|
28847
|
-
const sep3 = root.endsWith(
|
|
28953
|
+
const root = path36.resolve(personaRoot);
|
|
28954
|
+
const target = path36.resolve(absPath);
|
|
28955
|
+
const sep3 = root.endsWith(path36.sep) ? "" : path36.sep;
|
|
28848
28956
|
if (!target.startsWith(root + sep3)) return null;
|
|
28849
|
-
const rel =
|
|
28957
|
+
const rel = path36.relative(root, target);
|
|
28850
28958
|
if (!rel || rel.startsWith("..")) return null;
|
|
28851
|
-
const id = rel.split(
|
|
28959
|
+
const id = rel.split(path36.sep)[0];
|
|
28852
28960
|
return id || null;
|
|
28853
28961
|
}
|
|
28854
28962
|
|
|
@@ -28873,7 +28981,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28873
28981
|
if (!pid) return false;
|
|
28874
28982
|
return isGuestPathAllowed(
|
|
28875
28983
|
ctx.grants,
|
|
28876
|
-
|
|
28984
|
+
path37.join(personaRoot, pid),
|
|
28877
28985
|
personaRoot,
|
|
28878
28986
|
"read"
|
|
28879
28987
|
);
|
|
@@ -28884,7 +28992,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28884
28992
|
};
|
|
28885
28993
|
const list = async (frame, _client, ctx) => {
|
|
28886
28994
|
const args = HistoryListArgs.parse(frame);
|
|
28887
|
-
assertGuestPath(ctx,
|
|
28995
|
+
assertGuestPath(ctx, path37.resolve(args.projectPath), personaRoot, "history:list");
|
|
28888
28996
|
const sessions = await history.listSessions(args);
|
|
28889
28997
|
return { response: { type: "history:list", sessions } };
|
|
28890
28998
|
};
|
|
@@ -28916,13 +29024,13 @@ function buildHistoryHandlers(deps) {
|
|
|
28916
29024
|
};
|
|
28917
29025
|
const subagents = async (frame, _client, ctx) => {
|
|
28918
29026
|
const args = HistorySubagentsArgs.parse(frame);
|
|
28919
|
-
assertGuestPath(ctx,
|
|
29027
|
+
assertGuestPath(ctx, path37.resolve(args.cwd), personaRoot, "history:subagents");
|
|
28920
29028
|
const subs = await history.listSubagents(args);
|
|
28921
29029
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
28922
29030
|
};
|
|
28923
29031
|
const subagentRead = async (frame, _client, ctx) => {
|
|
28924
29032
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
28925
|
-
assertGuestPath(ctx,
|
|
29033
|
+
assertGuestPath(ctx, path37.resolve(args.cwd), personaRoot, "history:subagent-read");
|
|
28926
29034
|
const res = await history.readSubagent(args);
|
|
28927
29035
|
return { response: { type: "history:subagent-read", ...res } };
|
|
28928
29036
|
};
|
|
@@ -28930,7 +29038,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28930
29038
|
const dirs = listRecentDirs(store);
|
|
28931
29039
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
28932
29040
|
const filtered = dirs.filter(
|
|
28933
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
29041
|
+
(d) => isGuestPathAllowed(ctx.grants, path37.resolve(d.cwd), personaRoot, "read")
|
|
28934
29042
|
);
|
|
28935
29043
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
28936
29044
|
}
|
|
@@ -28947,8 +29055,8 @@ function buildHistoryHandlers(deps) {
|
|
|
28947
29055
|
}
|
|
28948
29056
|
|
|
28949
29057
|
// src/handlers/workspace.ts
|
|
28950
|
-
var
|
|
28951
|
-
var
|
|
29058
|
+
var path38 = __toESM(require("path"), 1);
|
|
29059
|
+
var os13 = __toESM(require("os"), 1);
|
|
28952
29060
|
init_protocol();
|
|
28953
29061
|
init_protocol();
|
|
28954
29062
|
function buildEnabledPluginNames(personaManager, personaId) {
|
|
@@ -28987,23 +29095,23 @@ function buildWorkspaceHandlers(deps) {
|
|
|
28987
29095
|
const list = async (frame, _client, ctx) => {
|
|
28988
29096
|
const args = WorkspaceListArgs.parse(frame);
|
|
28989
29097
|
const isGuest = ctx?.principal.kind === "guest";
|
|
28990
|
-
const fallbackCwd = isGuest && personaRoot ? personaRoot :
|
|
28991
|
-
const resolvedCwd =
|
|
28992
|
-
const target = args.path ?
|
|
29098
|
+
const fallbackCwd = isGuest && personaRoot ? personaRoot : os13.homedir();
|
|
29099
|
+
const resolvedCwd = path38.resolve(args.cwd ?? fallbackCwd);
|
|
29100
|
+
const target = args.path ? path38.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
28993
29101
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list");
|
|
28994
29102
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
28995
29103
|
return { response: { type: "workspace:list", ...res } };
|
|
28996
29104
|
};
|
|
28997
29105
|
const read = async (frame, _client, ctx) => {
|
|
28998
29106
|
const args = WorkspaceReadArgs.parse(frame);
|
|
28999
|
-
const target =
|
|
29107
|
+
const target = path38.isAbsolute(args.path) ? path38.resolve(args.path) : path38.resolve(args.cwd, args.path);
|
|
29000
29108
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read");
|
|
29001
29109
|
const res = workspace.read(args);
|
|
29002
29110
|
return { response: { type: "workspace:read", ...res } };
|
|
29003
29111
|
};
|
|
29004
29112
|
const skillsList = async (frame, _client, ctx) => {
|
|
29005
29113
|
const args = SkillsListArgs.parse(frame);
|
|
29006
|
-
const cwdAbs =
|
|
29114
|
+
const cwdAbs = path38.resolve(args.cwd);
|
|
29007
29115
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list");
|
|
29008
29116
|
const list2 = skills.list(args);
|
|
29009
29117
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -29015,7 +29123,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
29015
29123
|
};
|
|
29016
29124
|
const agentsList = async (frame, _client, ctx) => {
|
|
29017
29125
|
const args = AgentsListArgs.parse(frame);
|
|
29018
|
-
const cwdAbs =
|
|
29126
|
+
const cwdAbs = path38.resolve(args.cwd);
|
|
29019
29127
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list");
|
|
29020
29128
|
const list2 = agents.list(args);
|
|
29021
29129
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -29034,20 +29142,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
29034
29142
|
}
|
|
29035
29143
|
|
|
29036
29144
|
// src/handlers/git.ts
|
|
29037
|
-
var
|
|
29145
|
+
var path40 = __toESM(require("path"), 1);
|
|
29038
29146
|
init_protocol();
|
|
29039
29147
|
init_protocol();
|
|
29040
29148
|
|
|
29041
29149
|
// src/workspace/git.ts
|
|
29042
29150
|
var import_node_child_process6 = require("child_process");
|
|
29043
|
-
var
|
|
29044
|
-
var
|
|
29151
|
+
var import_node_fs26 = __toESM(require("fs"), 1);
|
|
29152
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
29045
29153
|
var import_node_util = require("util");
|
|
29046
29154
|
var pexec = (0, import_node_util.promisify)(import_node_child_process6.execFile);
|
|
29047
29155
|
function normalizePath(p2) {
|
|
29048
|
-
const resolved =
|
|
29156
|
+
const resolved = import_node_path28.default.resolve(p2);
|
|
29049
29157
|
try {
|
|
29050
|
-
return
|
|
29158
|
+
return import_node_fs26.default.realpathSync(resolved);
|
|
29051
29159
|
} catch {
|
|
29052
29160
|
return resolved;
|
|
29053
29161
|
}
|
|
@@ -29120,7 +29228,7 @@ async function listGitBranches(cwd) {
|
|
|
29120
29228
|
// src/handlers/git.ts
|
|
29121
29229
|
function assertGuestCwd(ctx, cwd, personaRoot, method) {
|
|
29122
29230
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
29123
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
29231
|
+
if (!isGuestPathAllowed(ctx.grants, path40.resolve(cwd), personaRoot, "read")) {
|
|
29124
29232
|
throw new ClawdError(
|
|
29125
29233
|
ERROR_CODES.UNAUTHORIZED,
|
|
29126
29234
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -29483,7 +29591,7 @@ function buildWhoamiHandler(deps) {
|
|
|
29483
29591
|
}
|
|
29484
29592
|
|
|
29485
29593
|
// src/handlers/meta.ts
|
|
29486
|
-
var
|
|
29594
|
+
var import_node_os13 = __toESM(require("os"), 1);
|
|
29487
29595
|
init_protocol();
|
|
29488
29596
|
|
|
29489
29597
|
// src/version.ts
|
|
@@ -29513,7 +29621,7 @@ function buildReadyFrame(deps, client) {
|
|
|
29513
29621
|
return {
|
|
29514
29622
|
version,
|
|
29515
29623
|
protocolVersion: PROTOCOL_VERSION,
|
|
29516
|
-
hostname:
|
|
29624
|
+
hostname: import_node_os13.default.hostname(),
|
|
29517
29625
|
os: process.platform,
|
|
29518
29626
|
tools,
|
|
29519
29627
|
runningSessions: info.runningSessions,
|
|
@@ -29607,7 +29715,7 @@ function buildPersonaHandlers(deps) {
|
|
|
29607
29715
|
}
|
|
29608
29716
|
|
|
29609
29717
|
// src/handlers/attachment.ts
|
|
29610
|
-
var
|
|
29718
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
29611
29719
|
init_protocol();
|
|
29612
29720
|
init_protocol();
|
|
29613
29721
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -29661,12 +29769,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
29661
29769
|
`session ${args.sessionId} scope unresolved`
|
|
29662
29770
|
);
|
|
29663
29771
|
}
|
|
29664
|
-
const cwdAbs =
|
|
29665
|
-
const candidateAbs =
|
|
29772
|
+
const cwdAbs = import_node_path29.default.resolve(sessionFile.cwd);
|
|
29773
|
+
const candidateAbs = import_node_path29.default.isAbsolute(args.relPath) ? import_node_path29.default.resolve(args.relPath) : import_node_path29.default.resolve(cwdAbs, args.relPath);
|
|
29666
29774
|
assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl");
|
|
29667
29775
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
29668
29776
|
const entry = entries.find((e) => {
|
|
29669
|
-
const storedAbs =
|
|
29777
|
+
const storedAbs = import_node_path29.default.isAbsolute(e.relPath) ? import_node_path29.default.resolve(e.relPath) : import_node_path29.default.resolve(cwdAbs, e.relPath);
|
|
29670
29778
|
return storedAbs === candidateAbs && !e.stale;
|
|
29671
29779
|
});
|
|
29672
29780
|
if (!entry) {
|
|
@@ -29690,7 +29798,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29690
29798
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
29691
29799
|
const f = deps.sessionStore.read(sessionId);
|
|
29692
29800
|
if (!f) return;
|
|
29693
|
-
assertGuestAttachmentPath(ctx,
|
|
29801
|
+
assertGuestAttachmentPath(ctx, import_node_path29.default.resolve(f.cwd), deps.personaRoot, method);
|
|
29694
29802
|
}
|
|
29695
29803
|
const groupAdd = async (frame, _client, ctx) => {
|
|
29696
29804
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -29731,7 +29839,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29731
29839
|
}
|
|
29732
29840
|
const entries = deps.groupFileStore.list(scope, parsed.data.sessionId);
|
|
29733
29841
|
const target = entries.find((e) => e.relPath === parsed.data.relPath);
|
|
29734
|
-
if (target?.from === "owner") {
|
|
29842
|
+
if (target?.from === "owner" || target?.from === "user-upload") {
|
|
29735
29843
|
deps.groupFileStore.remove(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
29736
29844
|
} else {
|
|
29737
29845
|
deps.groupFileStore.markStale(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
@@ -29857,11 +29965,6 @@ var METHOD_GRANT_MAP = {
|
|
|
29857
29965
|
"session:pin": CAPABILITY_SCOPED,
|
|
29858
29966
|
"session:reorderPins": ADMIN_ANY,
|
|
29859
29967
|
// owner 全局操作,无 personaId 维度
|
|
29860
|
-
// mirror peer sessions (2026-05-26):owner-only — guest 不应能写/删本机 mirror SessionFile
|
|
29861
|
-
// (会绕过 #739 隔离规则把伪造身份的 SessionFile 写进 default store)。handler 端内部 ctx
|
|
29862
|
-
// 校验,dispatcher 层一刀切 admin 也兜底。
|
|
29863
|
-
"peerSession:upsert": ADMIN_ANY,
|
|
29864
|
-
"peerSession:remove": ADMIN_ANY,
|
|
29865
29968
|
"permission:respond": CAPABILITY_SCOPED,
|
|
29866
29969
|
"session:answerQuestion": CAPABILITY_SCOPED,
|
|
29867
29970
|
"session:cancelQuestion": CAPABILITY_SCOPED,
|
|
@@ -29932,7 +30035,7 @@ function computeGrantForFrame(method, frame) {
|
|
|
29932
30035
|
async function startDaemon(config) {
|
|
29933
30036
|
const logger = createLogger({
|
|
29934
30037
|
level: config.logLevel,
|
|
29935
|
-
file:
|
|
30038
|
+
file: import_node_path30.default.join(config.dataDir, "clawd.log")
|
|
29936
30039
|
});
|
|
29937
30040
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
29938
30041
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
@@ -30019,7 +30122,7 @@ async function startDaemon(config) {
|
|
|
30019
30122
|
const agents = new AgentsScanner();
|
|
30020
30123
|
const history = new ClaudeHistoryReader();
|
|
30021
30124
|
let transport = null;
|
|
30022
|
-
const personaStore = new PersonaStore(
|
|
30125
|
+
const personaStore = new PersonaStore(import_node_path30.default.join(config.dataDir, "personas"));
|
|
30023
30126
|
const defaultsRoot = findDefaultsRoot();
|
|
30024
30127
|
if (defaultsRoot) {
|
|
30025
30128
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -30036,7 +30139,7 @@ async function startDaemon(config) {
|
|
|
30036
30139
|
getAdapter,
|
|
30037
30140
|
historyReader: history,
|
|
30038
30141
|
dataDir: config.dataDir,
|
|
30039
|
-
personaRoot:
|
|
30142
|
+
personaRoot: import_node_path30.default.join(config.dataDir, "personas"),
|
|
30040
30143
|
personaStore,
|
|
30041
30144
|
ownerDisplayName,
|
|
30042
30145
|
ownerPrincipalId,
|
|
@@ -30060,10 +30163,10 @@ async function startDaemon(config) {
|
|
|
30060
30163
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
30061
30164
|
attachmentGroup: {
|
|
30062
30165
|
onFileEdit: (input) => {
|
|
30063
|
-
const absPath =
|
|
30166
|
+
const absPath = import_node_path30.default.isAbsolute(input.relPath) ? input.relPath : import_node_path30.default.join(input.cwd, input.relPath);
|
|
30064
30167
|
let size = 0;
|
|
30065
30168
|
try {
|
|
30066
|
-
size =
|
|
30169
|
+
size = import_node_fs27.default.statSync(absPath).size;
|
|
30067
30170
|
} catch (err) {
|
|
30068
30171
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
30069
30172
|
sessionId: input.sessionId,
|
|
@@ -30089,11 +30192,6 @@ async function startDaemon(config) {
|
|
|
30089
30192
|
}
|
|
30090
30193
|
});
|
|
30091
30194
|
const observer = new SessionObserver({
|
|
30092
|
-
// TUI 模式 pty 跑 CC 不发 SDK system.task_started 帧 → UI pending 期 progress.agentId
|
|
30093
|
-
// 永远 undefined,子 agent 调用过程没有抽屉入口。开 meta observer 后从 subagents 目录
|
|
30094
|
-
// 新出现的 agent-*.meta.json 派生 subagent_progress(started) 补这块缺口。SDK 模式仍走
|
|
30095
|
-
// task_started 路径,避免双 emit 让 ring buffer 双帧。
|
|
30096
|
-
enableSubagentMetaObserver: config.mode === "tui",
|
|
30097
30195
|
onEvent: ({ sessionId, events }) => {
|
|
30098
30196
|
manager.feedObserverEvents(sessionId, events);
|
|
30099
30197
|
},
|
|
@@ -30181,10 +30279,10 @@ async function startDaemon(config) {
|
|
|
30181
30279
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
30182
30280
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30183
30281
|
// guest path guard:candidate 必须在 personaRoot 子树下
|
|
30184
|
-
personaRoot:
|
|
30282
|
+
personaRoot: import_node_path30.default.join(config.dataDir, "personas")
|
|
30185
30283
|
},
|
|
30186
30284
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
30187
|
-
personaRoot:
|
|
30285
|
+
personaRoot: import_node_path30.default.join(config.dataDir, "personas"),
|
|
30188
30286
|
// capability:list / delete handler 依赖
|
|
30189
30287
|
capabilityManager,
|
|
30190
30288
|
// personal-cap:get 返回的 shareBaseUrl 用此 base URL:
|
|
@@ -30220,7 +30318,19 @@ async function startDaemon(config) {
|
|
|
30220
30318
|
sessionStore: store,
|
|
30221
30319
|
// /files HMAC verify 用 auth.json 的 signSecret 字段(与 attachment.signUrl 同源)。
|
|
30222
30320
|
// --auth-token CLI 模式没 signSecret → 路由返 501,sign URL 功能整体禁用。
|
|
30223
|
-
getSignSecret: () => authFile?.signSecret ?? null
|
|
30321
|
+
getSignSecret: () => authFile?.signSecret ?? null,
|
|
30322
|
+
// chatinput 附件上传(POST /attachments/upload,spec §6.1)。upload pipeline
|
|
30323
|
+
// 在 session.cwd 下的 .attachments/ 落盘;signSecret 与 /files HMAC 同源;
|
|
30324
|
+
// tunnel 未启时返 503 TUNNEL_NOT_READY 让前端显式提示。
|
|
30325
|
+
attachmentUpload: {
|
|
30326
|
+
groupFileStore,
|
|
30327
|
+
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30328
|
+
getSessionDir: (sid) => {
|
|
30329
|
+
const f = manager.findOwnedSession(sid);
|
|
30330
|
+
return f ? f.cwd : null;
|
|
30331
|
+
},
|
|
30332
|
+
getHttpBaseUrl: () => currentTunnelUrl ? getHttpBaseUrl() : null
|
|
30333
|
+
}
|
|
30224
30334
|
});
|
|
30225
30335
|
wsServer = new LocalWsServer({
|
|
30226
30336
|
host: config.host,
|
|
@@ -30385,8 +30495,8 @@ async function startDaemon(config) {
|
|
|
30385
30495
|
const lines = [
|
|
30386
30496
|
`Tunnel: ${r.url}`,
|
|
30387
30497
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
30388
|
-
`Frpc config: ${
|
|
30389
|
-
`Frpc log: ${
|
|
30498
|
+
`Frpc config: ${import_node_path30.default.join(config.dataDir, "frpc.toml")}`,
|
|
30499
|
+
`Frpc log: ${import_node_path30.default.join(config.dataDir, "frpc.log")}`
|
|
30390
30500
|
];
|
|
30391
30501
|
const width = Math.max(...lines.map((l) => l.length));
|
|
30392
30502
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -30399,8 +30509,8 @@ ${bar}
|
|
|
30399
30509
|
|
|
30400
30510
|
`);
|
|
30401
30511
|
try {
|
|
30402
|
-
const connectPath =
|
|
30403
|
-
|
|
30512
|
+
const connectPath = import_node_path30.default.join(config.dataDir, "connect.txt");
|
|
30513
|
+
import_node_fs27.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
30404
30514
|
} catch {
|
|
30405
30515
|
}
|
|
30406
30516
|
} catch (err) {
|
|
@@ -30425,8 +30535,29 @@ ${bar}
|
|
|
30425
30535
|
logger.warn("tunnel unavailable, degraded to local mode", { reason: tunnelError });
|
|
30426
30536
|
}
|
|
30427
30537
|
}
|
|
30538
|
+
const tickAttachmentGc = () => {
|
|
30539
|
+
try {
|
|
30540
|
+
runAttachmentGc({
|
|
30541
|
+
dataDir: config.dataDir,
|
|
30542
|
+
groupFileStore,
|
|
30543
|
+
sessionScopes: manager.listOwnedSessionScopes().map(({ scope, sessionId }) => ({
|
|
30544
|
+
scope,
|
|
30545
|
+
sessionId
|
|
30546
|
+
})),
|
|
30547
|
+
// 注入 cwd 让 GC 直接定位 .attachments 目录(与 upload 落盘点一致)
|
|
30548
|
+
getSessionCwd: (sid) => manager.findOwnedSession(sid)?.cwd ?? null,
|
|
30549
|
+
logger
|
|
30550
|
+
});
|
|
30551
|
+
} catch (err) {
|
|
30552
|
+
logger.warn("attachment gc tick failed", { err: err.message });
|
|
30553
|
+
}
|
|
30554
|
+
};
|
|
30555
|
+
setImmediate(tickAttachmentGc);
|
|
30556
|
+
const attachmentGcInterval = setInterval(tickAttachmentGc, 24 * 3600 * 1e3);
|
|
30557
|
+
attachmentGcInterval.unref();
|
|
30428
30558
|
const shutdown = async () => {
|
|
30429
30559
|
logger.info("stopping clawd");
|
|
30560
|
+
clearInterval(attachmentGcInterval);
|
|
30430
30561
|
observer.stopAll();
|
|
30431
30562
|
manager.stopAll();
|
|
30432
30563
|
if (tunnelMgr) {
|
|
@@ -30444,9 +30575,9 @@ ${bar}
|
|
|
30444
30575
|
};
|
|
30445
30576
|
}
|
|
30446
30577
|
function migrateDropPersonsDir(dataDir) {
|
|
30447
|
-
const dir =
|
|
30578
|
+
const dir = import_node_path30.default.join(dataDir, "persons");
|
|
30448
30579
|
try {
|
|
30449
|
-
|
|
30580
|
+
import_node_fs27.default.rmSync(dir, { recursive: true, force: true });
|
|
30450
30581
|
} catch {
|
|
30451
30582
|
}
|
|
30452
30583
|
}
|