@clawos-dev/clawd 0.2.86-beta.161.3250922 → 0.2.86-beta.162.77b3c16
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 +546 -665
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -65,6 +65,14 @@ 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",
|
|
68
76
|
"permission:respond",
|
|
69
77
|
// AskUserQuestion 表单回写:UI 答完所有 question 后调用,daemon 把答案合并进 updated_input
|
|
70
78
|
// 写一条 control_response 到 CC stdin(详见 events.ts session:question JSDoc)
|
|
@@ -643,8 +651,8 @@ var init_parseUtil = __esm({
|
|
|
643
651
|
init_errors2();
|
|
644
652
|
init_en();
|
|
645
653
|
makeIssue = (params) => {
|
|
646
|
-
const { data, path:
|
|
647
|
-
const fullPath = [...
|
|
654
|
+
const { data, path: path44, errorMaps, issueData } = params;
|
|
655
|
+
const fullPath = [...path44, ...issueData.path || []];
|
|
648
656
|
const fullIssue = {
|
|
649
657
|
...issueData,
|
|
650
658
|
path: fullPath
|
|
@@ -955,11 +963,11 @@ var init_types = __esm({
|
|
|
955
963
|
init_parseUtil();
|
|
956
964
|
init_util();
|
|
957
965
|
ParseInputLazyPath = class {
|
|
958
|
-
constructor(parent, value,
|
|
966
|
+
constructor(parent, value, path44, key) {
|
|
959
967
|
this._cachedPath = [];
|
|
960
968
|
this.parent = parent;
|
|
961
969
|
this.data = value;
|
|
962
|
-
this._path =
|
|
970
|
+
this._path = path44;
|
|
963
971
|
this._key = key;
|
|
964
972
|
}
|
|
965
973
|
get path() {
|
|
@@ -4343,13 +4351,13 @@ var init_zod = __esm({
|
|
|
4343
4351
|
});
|
|
4344
4352
|
|
|
4345
4353
|
// ../protocol/src/attachment-schemas.ts
|
|
4346
|
-
var TOKEN_ROLES, GROUP_FILE_SOURCES, GroupFileEntrySchema, AttachmentSignUrlArgs, AttachmentSignUrlResponseSchema, AttachmentGroupAddArgs, AttachmentGroupAddResponseSchema, AttachmentGroupRemoveArgs, AttachmentGroupRemoveResponseSchema, AttachmentGroupListArgs, AttachmentGroupListResponseSchema
|
|
4354
|
+
var TOKEN_ROLES, GROUP_FILE_SOURCES, GroupFileEntrySchema, AttachmentSignUrlArgs, AttachmentSignUrlResponseSchema, AttachmentGroupAddArgs, AttachmentGroupAddResponseSchema, AttachmentGroupRemoveArgs, AttachmentGroupRemoveResponseSchema, AttachmentGroupListArgs, AttachmentGroupListResponseSchema;
|
|
4347
4355
|
var init_attachment_schemas = __esm({
|
|
4348
4356
|
"../protocol/src/attachment-schemas.ts"() {
|
|
4349
4357
|
"use strict";
|
|
4350
4358
|
init_zod();
|
|
4351
4359
|
TOKEN_ROLES = ["owner"];
|
|
4352
|
-
GROUP_FILE_SOURCES = ["agent", "owner"
|
|
4360
|
+
GROUP_FILE_SOURCES = ["agent", "owner"];
|
|
4353
4361
|
GroupFileEntrySchema = external_exports.object({
|
|
4354
4362
|
/** daemon 派发的稳定 id(用于 RPC remove / UI key) */
|
|
4355
4363
|
id: external_exports.string().min(1),
|
|
@@ -4404,16 +4412,6 @@ var init_attachment_schemas = __esm({
|
|
|
4404
4412
|
AttachmentGroupListResponseSchema = external_exports.object({
|
|
4405
4413
|
entries: external_exports.array(GroupFileEntrySchema)
|
|
4406
4414
|
});
|
|
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
|
-
});
|
|
4417
4415
|
}
|
|
4418
4416
|
});
|
|
4419
4417
|
|
|
@@ -4493,7 +4491,7 @@ var init_persona_schemas = __esm({
|
|
|
4493
4491
|
});
|
|
4494
4492
|
|
|
4495
4493
|
// ../protocol/src/schemas.ts
|
|
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;
|
|
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, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, HistoryRecentDirsArgs, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, AuthRequestFrameSchema, AuthOkFrameSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema;
|
|
4497
4495
|
var init_schemas = __esm({
|
|
4498
4496
|
"../protocol/src/schemas.ts"() {
|
|
4499
4497
|
"use strict";
|
|
@@ -4621,6 +4619,35 @@ var init_schemas = __esm({
|
|
|
4621
4619
|
* 当前为 caller 自报,daemon 不密码学验证真伪;强身份验签是独立工作(B 方案)。
|
|
4622
4620
|
*/
|
|
4623
4621
|
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(),
|
|
4624
4651
|
createdAt: external_exports.string().min(1),
|
|
4625
4652
|
updatedAt: external_exports.string().min(1)
|
|
4626
4653
|
});
|
|
@@ -4976,6 +5003,14 @@ var init_schemas = __esm({
|
|
|
4976
5003
|
sessionId: external_exports.string().min(1),
|
|
4977
5004
|
pinned: external_exports.boolean()
|
|
4978
5005
|
});
|
|
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
|
+
});
|
|
4979
5014
|
SessionReorderPinsArgs = external_exports.object({
|
|
4980
5015
|
orderedIds: external_exports.array(external_exports.string().min(1)).min(1)
|
|
4981
5016
|
});
|
|
@@ -5590,8 +5625,8 @@ var require_req = __commonJS({
|
|
|
5590
5625
|
if (req.originalUrl) {
|
|
5591
5626
|
_req.url = req.originalUrl;
|
|
5592
5627
|
} else {
|
|
5593
|
-
const
|
|
5594
|
-
_req.url = typeof
|
|
5628
|
+
const path44 = req.path;
|
|
5629
|
+
_req.url = typeof path44 === "string" ? path44 : req.url ? req.url.path || req.url : void 0;
|
|
5595
5630
|
}
|
|
5596
5631
|
if (req.query) {
|
|
5597
5632
|
_req.query = req.query;
|
|
@@ -5756,14 +5791,14 @@ var require_redact = __commonJS({
|
|
|
5756
5791
|
}
|
|
5757
5792
|
return obj;
|
|
5758
5793
|
}
|
|
5759
|
-
function parsePath(
|
|
5794
|
+
function parsePath(path44) {
|
|
5760
5795
|
const parts = [];
|
|
5761
5796
|
let current = "";
|
|
5762
5797
|
let inBrackets = false;
|
|
5763
5798
|
let inQuotes = false;
|
|
5764
5799
|
let quoteChar = "";
|
|
5765
|
-
for (let i = 0; i <
|
|
5766
|
-
const char =
|
|
5800
|
+
for (let i = 0; i < path44.length; i++) {
|
|
5801
|
+
const char = path44[i];
|
|
5767
5802
|
if (!inBrackets && char === ".") {
|
|
5768
5803
|
if (current) {
|
|
5769
5804
|
parts.push(current);
|
|
@@ -5894,10 +5929,10 @@ var require_redact = __commonJS({
|
|
|
5894
5929
|
return current;
|
|
5895
5930
|
}
|
|
5896
5931
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
5897
|
-
for (const
|
|
5898
|
-
const parts = parsePath(
|
|
5932
|
+
for (const path44 of paths) {
|
|
5933
|
+
const parts = parsePath(path44);
|
|
5899
5934
|
if (parts.includes("*")) {
|
|
5900
|
-
redactWildcardPath(obj, parts, censor,
|
|
5935
|
+
redactWildcardPath(obj, parts, censor, path44, remove);
|
|
5901
5936
|
} else {
|
|
5902
5937
|
if (remove) {
|
|
5903
5938
|
removeKey(obj, parts);
|
|
@@ -5982,8 +6017,8 @@ var require_redact = __commonJS({
|
|
|
5982
6017
|
}
|
|
5983
6018
|
} else {
|
|
5984
6019
|
if (afterWildcard.includes("*")) {
|
|
5985
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
5986
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
6020
|
+
const wrappedCensor = typeof censor === "function" ? (value, path44) => {
|
|
6021
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path44];
|
|
5987
6022
|
return censor(value, fullPath);
|
|
5988
6023
|
} : censor;
|
|
5989
6024
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -6018,8 +6053,8 @@ var require_redact = __commonJS({
|
|
|
6018
6053
|
return null;
|
|
6019
6054
|
}
|
|
6020
6055
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
6021
|
-
for (const
|
|
6022
|
-
const parts = parsePath(
|
|
6056
|
+
for (const path44 of pathsToClone) {
|
|
6057
|
+
const parts = parsePath(path44);
|
|
6023
6058
|
let current = pathStructure;
|
|
6024
6059
|
for (let i = 0; i < parts.length; i++) {
|
|
6025
6060
|
const part = parts[i];
|
|
@@ -6071,24 +6106,24 @@ var require_redact = __commonJS({
|
|
|
6071
6106
|
}
|
|
6072
6107
|
return cloneSelectively(obj, pathStructure);
|
|
6073
6108
|
}
|
|
6074
|
-
function validatePath(
|
|
6075
|
-
if (typeof
|
|
6109
|
+
function validatePath(path44) {
|
|
6110
|
+
if (typeof path44 !== "string") {
|
|
6076
6111
|
throw new Error("Paths must be (non-empty) strings");
|
|
6077
6112
|
}
|
|
6078
|
-
if (
|
|
6113
|
+
if (path44 === "") {
|
|
6079
6114
|
throw new Error("Invalid redaction path ()");
|
|
6080
6115
|
}
|
|
6081
|
-
if (
|
|
6082
|
-
throw new Error(`Invalid redaction path (${
|
|
6116
|
+
if (path44.includes("..")) {
|
|
6117
|
+
throw new Error(`Invalid redaction path (${path44})`);
|
|
6083
6118
|
}
|
|
6084
|
-
if (
|
|
6085
|
-
throw new Error(`Invalid redaction path (${
|
|
6119
|
+
if (path44.includes(",")) {
|
|
6120
|
+
throw new Error(`Invalid redaction path (${path44})`);
|
|
6086
6121
|
}
|
|
6087
6122
|
let bracketCount = 0;
|
|
6088
6123
|
let inQuotes = false;
|
|
6089
6124
|
let quoteChar = "";
|
|
6090
|
-
for (let i = 0; i <
|
|
6091
|
-
const char =
|
|
6125
|
+
for (let i = 0; i < path44.length; i++) {
|
|
6126
|
+
const char = path44[i];
|
|
6092
6127
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
6093
6128
|
if (!inQuotes) {
|
|
6094
6129
|
inQuotes = true;
|
|
@@ -6102,20 +6137,20 @@ var require_redact = __commonJS({
|
|
|
6102
6137
|
} else if (char === "]" && !inQuotes) {
|
|
6103
6138
|
bracketCount--;
|
|
6104
6139
|
if (bracketCount < 0) {
|
|
6105
|
-
throw new Error(`Invalid redaction path (${
|
|
6140
|
+
throw new Error(`Invalid redaction path (${path44})`);
|
|
6106
6141
|
}
|
|
6107
6142
|
}
|
|
6108
6143
|
}
|
|
6109
6144
|
if (bracketCount !== 0) {
|
|
6110
|
-
throw new Error(`Invalid redaction path (${
|
|
6145
|
+
throw new Error(`Invalid redaction path (${path44})`);
|
|
6111
6146
|
}
|
|
6112
6147
|
}
|
|
6113
6148
|
function validatePaths(paths) {
|
|
6114
6149
|
if (!Array.isArray(paths)) {
|
|
6115
6150
|
throw new TypeError("paths must be an array");
|
|
6116
6151
|
}
|
|
6117
|
-
for (const
|
|
6118
|
-
validatePath(
|
|
6152
|
+
for (const path44 of paths) {
|
|
6153
|
+
validatePath(path44);
|
|
6119
6154
|
}
|
|
6120
6155
|
}
|
|
6121
6156
|
function slowRedact(options = {}) {
|
|
@@ -6283,8 +6318,8 @@ var require_redaction = __commonJS({
|
|
|
6283
6318
|
if (shape[k2] === null) {
|
|
6284
6319
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
6285
6320
|
} else {
|
|
6286
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6287
|
-
return censor(value, [k2, ...
|
|
6321
|
+
const wrappedCensor = typeof censor === "function" ? (value, path44) => {
|
|
6322
|
+
return censor(value, [k2, ...path44]);
|
|
6288
6323
|
} : censor;
|
|
6289
6324
|
o[k2] = Redact({
|
|
6290
6325
|
paths: shape[k2],
|
|
@@ -6502,10 +6537,10 @@ var require_atomic_sleep = __commonJS({
|
|
|
6502
6537
|
var require_sonic_boom = __commonJS({
|
|
6503
6538
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
6504
6539
|
"use strict";
|
|
6505
|
-
var
|
|
6540
|
+
var fs35 = require("fs");
|
|
6506
6541
|
var EventEmitter2 = require("events");
|
|
6507
6542
|
var inherits = require("util").inherits;
|
|
6508
|
-
var
|
|
6543
|
+
var path44 = require("path");
|
|
6509
6544
|
var sleep = require_atomic_sleep();
|
|
6510
6545
|
var assert = require("assert");
|
|
6511
6546
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -6559,20 +6594,20 @@ var require_sonic_boom = __commonJS({
|
|
|
6559
6594
|
const mode = sonic.mode;
|
|
6560
6595
|
if (sonic.sync) {
|
|
6561
6596
|
try {
|
|
6562
|
-
if (sonic.mkdir)
|
|
6563
|
-
const fd =
|
|
6597
|
+
if (sonic.mkdir) fs35.mkdirSync(path44.dirname(file), { recursive: true });
|
|
6598
|
+
const fd = fs35.openSync(file, flags, mode);
|
|
6564
6599
|
fileOpened(null, fd);
|
|
6565
6600
|
} catch (err) {
|
|
6566
6601
|
fileOpened(err);
|
|
6567
6602
|
throw err;
|
|
6568
6603
|
}
|
|
6569
6604
|
} else if (sonic.mkdir) {
|
|
6570
|
-
|
|
6605
|
+
fs35.mkdir(path44.dirname(file), { recursive: true }, (err) => {
|
|
6571
6606
|
if (err) return fileOpened(err);
|
|
6572
|
-
|
|
6607
|
+
fs35.open(file, flags, mode, fileOpened);
|
|
6573
6608
|
});
|
|
6574
6609
|
} else {
|
|
6575
|
-
|
|
6610
|
+
fs35.open(file, flags, mode, fileOpened);
|
|
6576
6611
|
}
|
|
6577
6612
|
}
|
|
6578
6613
|
function SonicBoom(opts) {
|
|
@@ -6613,8 +6648,8 @@ var require_sonic_boom = __commonJS({
|
|
|
6613
6648
|
this.flush = flushBuffer;
|
|
6614
6649
|
this.flushSync = flushBufferSync;
|
|
6615
6650
|
this._actualWrite = actualWriteBuffer;
|
|
6616
|
-
fsWriteSync = () =>
|
|
6617
|
-
fsWrite = () =>
|
|
6651
|
+
fsWriteSync = () => fs35.writeSync(this.fd, this._writingBuf);
|
|
6652
|
+
fsWrite = () => fs35.write(this.fd, this._writingBuf, this.release);
|
|
6618
6653
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
6619
6654
|
this._writingBuf = "";
|
|
6620
6655
|
this.write = write;
|
|
@@ -6623,15 +6658,15 @@ var require_sonic_boom = __commonJS({
|
|
|
6623
6658
|
this._actualWrite = actualWrite;
|
|
6624
6659
|
fsWriteSync = () => {
|
|
6625
6660
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
6626
|
-
return
|
|
6661
|
+
return fs35.writeSync(this.fd, this._writingBuf);
|
|
6627
6662
|
}
|
|
6628
|
-
return
|
|
6663
|
+
return fs35.writeSync(this.fd, this._writingBuf, "utf8");
|
|
6629
6664
|
};
|
|
6630
6665
|
fsWrite = () => {
|
|
6631
6666
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
6632
|
-
return
|
|
6667
|
+
return fs35.write(this.fd, this._writingBuf, this.release);
|
|
6633
6668
|
}
|
|
6634
|
-
return
|
|
6669
|
+
return fs35.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
6635
6670
|
};
|
|
6636
6671
|
} else {
|
|
6637
6672
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -6688,7 +6723,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6688
6723
|
}
|
|
6689
6724
|
}
|
|
6690
6725
|
if (this._fsync) {
|
|
6691
|
-
|
|
6726
|
+
fs35.fsyncSync(this.fd);
|
|
6692
6727
|
}
|
|
6693
6728
|
const len = this._len;
|
|
6694
6729
|
if (this._reopening) {
|
|
@@ -6802,7 +6837,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6802
6837
|
const onDrain = () => {
|
|
6803
6838
|
if (!this._fsync) {
|
|
6804
6839
|
try {
|
|
6805
|
-
|
|
6840
|
+
fs35.fsync(this.fd, (err) => {
|
|
6806
6841
|
this._flushPending = false;
|
|
6807
6842
|
cb(err);
|
|
6808
6843
|
});
|
|
@@ -6904,7 +6939,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6904
6939
|
const fd = this.fd;
|
|
6905
6940
|
this.once("ready", () => {
|
|
6906
6941
|
if (fd !== this.fd) {
|
|
6907
|
-
|
|
6942
|
+
fs35.close(fd, (err) => {
|
|
6908
6943
|
if (err) {
|
|
6909
6944
|
return this.emit("error", err);
|
|
6910
6945
|
}
|
|
@@ -6953,7 +6988,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6953
6988
|
buf = this._bufs[0];
|
|
6954
6989
|
}
|
|
6955
6990
|
try {
|
|
6956
|
-
const n = Buffer.isBuffer(buf) ?
|
|
6991
|
+
const n = Buffer.isBuffer(buf) ? fs35.writeSync(this.fd, buf) : fs35.writeSync(this.fd, buf, "utf8");
|
|
6957
6992
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
6958
6993
|
buf = releasedBufObj.writingBuf;
|
|
6959
6994
|
this._len = releasedBufObj.len;
|
|
@@ -6969,7 +7004,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6969
7004
|
}
|
|
6970
7005
|
}
|
|
6971
7006
|
try {
|
|
6972
|
-
|
|
7007
|
+
fs35.fsyncSync(this.fd);
|
|
6973
7008
|
} catch {
|
|
6974
7009
|
}
|
|
6975
7010
|
}
|
|
@@ -6990,7 +7025,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6990
7025
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
6991
7026
|
}
|
|
6992
7027
|
try {
|
|
6993
|
-
const n =
|
|
7028
|
+
const n = fs35.writeSync(this.fd, buf);
|
|
6994
7029
|
buf = buf.subarray(n);
|
|
6995
7030
|
this._len = Math.max(this._len - n, 0);
|
|
6996
7031
|
if (buf.length <= 0) {
|
|
@@ -7018,13 +7053,13 @@ var require_sonic_boom = __commonJS({
|
|
|
7018
7053
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
7019
7054
|
if (this.sync) {
|
|
7020
7055
|
try {
|
|
7021
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
7056
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs35.writeSync(this.fd, this._writingBuf) : fs35.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7022
7057
|
release(null, written);
|
|
7023
7058
|
} catch (err) {
|
|
7024
7059
|
release(err);
|
|
7025
7060
|
}
|
|
7026
7061
|
} else {
|
|
7027
|
-
|
|
7062
|
+
fs35.write(this.fd, this._writingBuf, release);
|
|
7028
7063
|
}
|
|
7029
7064
|
}
|
|
7030
7065
|
function actualWriteBuffer() {
|
|
@@ -7033,7 +7068,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7033
7068
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
7034
7069
|
if (this.sync) {
|
|
7035
7070
|
try {
|
|
7036
|
-
const written =
|
|
7071
|
+
const written = fs35.writeSync(this.fd, this._writingBuf);
|
|
7037
7072
|
release(null, written);
|
|
7038
7073
|
} catch (err) {
|
|
7039
7074
|
release(err);
|
|
@@ -7042,7 +7077,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7042
7077
|
if (kCopyBuffer) {
|
|
7043
7078
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
7044
7079
|
}
|
|
7045
|
-
|
|
7080
|
+
fs35.write(this.fd, this._writingBuf, release);
|
|
7046
7081
|
}
|
|
7047
7082
|
}
|
|
7048
7083
|
function actualClose(sonic) {
|
|
@@ -7058,12 +7093,12 @@ var require_sonic_boom = __commonJS({
|
|
|
7058
7093
|
sonic._lens = [];
|
|
7059
7094
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
7060
7095
|
try {
|
|
7061
|
-
|
|
7096
|
+
fs35.fsync(sonic.fd, closeWrapped);
|
|
7062
7097
|
} catch {
|
|
7063
7098
|
}
|
|
7064
7099
|
function closeWrapped() {
|
|
7065
7100
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
7066
|
-
|
|
7101
|
+
fs35.close(sonic.fd, done);
|
|
7067
7102
|
} else {
|
|
7068
7103
|
done();
|
|
7069
7104
|
}
|
|
@@ -7794,7 +7829,7 @@ var require_transport = __commonJS({
|
|
|
7794
7829
|
stream.flushSync();
|
|
7795
7830
|
}
|
|
7796
7831
|
function transport(fullOptions) {
|
|
7797
|
-
const { pipeline:
|
|
7832
|
+
const { pipeline: pipeline2, targets, levels, dedupe, worker = {}, caller = getCallers(), sync = false } = fullOptions;
|
|
7798
7833
|
const options = {
|
|
7799
7834
|
...fullOptions.options
|
|
7800
7835
|
};
|
|
@@ -7822,9 +7857,9 @@ var require_transport = __commonJS({
|
|
|
7822
7857
|
};
|
|
7823
7858
|
});
|
|
7824
7859
|
});
|
|
7825
|
-
} else if (
|
|
7860
|
+
} else if (pipeline2) {
|
|
7826
7861
|
target = bundlerOverrides["pino-worker"] || join11(__dirname, "worker.js");
|
|
7827
|
-
options.pipelines = [
|
|
7862
|
+
options.pipelines = [pipeline2.map((dest) => {
|
|
7828
7863
|
return {
|
|
7829
7864
|
...dest,
|
|
7830
7865
|
target: fixTarget(dest.target)
|
|
@@ -9427,7 +9462,7 @@ var require_multistream = __commonJS({
|
|
|
9427
9462
|
var require_pino = __commonJS({
|
|
9428
9463
|
"../node_modules/.pnpm/pino@9.14.0/node_modules/pino/pino.js"(exports2, module2) {
|
|
9429
9464
|
"use strict";
|
|
9430
|
-
var
|
|
9465
|
+
var os17 = require("os");
|
|
9431
9466
|
var stdSerializers = require_pino_std_serializers();
|
|
9432
9467
|
var caller = require_caller();
|
|
9433
9468
|
var redaction = require_redaction();
|
|
@@ -9474,7 +9509,7 @@ var require_pino = __commonJS({
|
|
|
9474
9509
|
} = symbols;
|
|
9475
9510
|
var { epochTime, nullTime } = time;
|
|
9476
9511
|
var { pid } = process;
|
|
9477
|
-
var hostname =
|
|
9512
|
+
var hostname = os17.hostname();
|
|
9478
9513
|
var defaultErrorSerializer = stdSerializers.err;
|
|
9479
9514
|
var defaultOptions = {
|
|
9480
9515
|
level: "info",
|
|
@@ -10198,11 +10233,11 @@ var init_lib = __esm({
|
|
|
10198
10233
|
}
|
|
10199
10234
|
}
|
|
10200
10235
|
},
|
|
10201
|
-
addToPath: function addToPath(
|
|
10202
|
-
var last =
|
|
10236
|
+
addToPath: function addToPath(path44, added, removed, oldPosInc, options) {
|
|
10237
|
+
var last = path44.lastComponent;
|
|
10203
10238
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
10204
10239
|
return {
|
|
10205
|
-
oldPos:
|
|
10240
|
+
oldPos: path44.oldPos + oldPosInc,
|
|
10206
10241
|
lastComponent: {
|
|
10207
10242
|
count: last.count + 1,
|
|
10208
10243
|
added,
|
|
@@ -10212,7 +10247,7 @@ var init_lib = __esm({
|
|
|
10212
10247
|
};
|
|
10213
10248
|
} else {
|
|
10214
10249
|
return {
|
|
10215
|
-
oldPos:
|
|
10250
|
+
oldPos: path44.oldPos + oldPosInc,
|
|
10216
10251
|
lastComponent: {
|
|
10217
10252
|
count: 1,
|
|
10218
10253
|
added,
|
|
@@ -10643,10 +10678,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
10643
10678
|
const memories = raw.map((m2) => {
|
|
10644
10679
|
if (!m2 || typeof m2 !== "object") return null;
|
|
10645
10680
|
const rec = m2;
|
|
10646
|
-
const
|
|
10681
|
+
const path44 = typeof rec.path === "string" ? rec.path : null;
|
|
10647
10682
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
10648
|
-
if (!
|
|
10649
|
-
const entry = { path:
|
|
10683
|
+
if (!path44 || content == null) return null;
|
|
10684
|
+
const entry = { path: path44, content };
|
|
10650
10685
|
if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
|
|
10651
10686
|
return entry;
|
|
10652
10687
|
}).filter((m2) => m2 !== null);
|
|
@@ -11472,10 +11507,10 @@ function parseAttachment(obj) {
|
|
|
11472
11507
|
const memories = raw.map((m2) => {
|
|
11473
11508
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11474
11509
|
const rec = m2;
|
|
11475
|
-
const
|
|
11510
|
+
const path44 = typeof rec.path === "string" ? rec.path : null;
|
|
11476
11511
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
11477
|
-
if (!
|
|
11478
|
-
const out = { path:
|
|
11512
|
+
if (!path44 || content == null) return null;
|
|
11513
|
+
const out = { path: path44, content };
|
|
11479
11514
|
if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
|
|
11480
11515
|
return out;
|
|
11481
11516
|
}).filter((m2) => m2 !== null);
|
|
@@ -20390,7 +20425,7 @@ var require_websocket_server = __commonJS({
|
|
|
20390
20425
|
// src/run-case/recorder.ts
|
|
20391
20426
|
function startRunCaseRecorder(opts) {
|
|
20392
20427
|
const now = opts.now ?? Date.now;
|
|
20393
|
-
const dir =
|
|
20428
|
+
const dir = import_node_path30.default.dirname(opts.recordPath);
|
|
20394
20429
|
let stream = null;
|
|
20395
20430
|
let closing = false;
|
|
20396
20431
|
let closedSettled = false;
|
|
@@ -20404,8 +20439,8 @@ function startRunCaseRecorder(opts) {
|
|
|
20404
20439
|
});
|
|
20405
20440
|
const ensureStream = () => {
|
|
20406
20441
|
if (stream) return stream;
|
|
20407
|
-
|
|
20408
|
-
stream =
|
|
20442
|
+
import_node_fs27.default.mkdirSync(dir, { recursive: true });
|
|
20443
|
+
stream = import_node_fs27.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
20409
20444
|
stream.on("close", () => closedResolve());
|
|
20410
20445
|
return stream;
|
|
20411
20446
|
};
|
|
@@ -20430,12 +20465,12 @@ function startRunCaseRecorder(opts) {
|
|
|
20430
20465
|
};
|
|
20431
20466
|
return { tap, close, closed };
|
|
20432
20467
|
}
|
|
20433
|
-
var
|
|
20468
|
+
var import_node_fs27, import_node_path30;
|
|
20434
20469
|
var init_recorder = __esm({
|
|
20435
20470
|
"src/run-case/recorder.ts"() {
|
|
20436
20471
|
"use strict";
|
|
20437
|
-
|
|
20438
|
-
|
|
20472
|
+
import_node_fs27 = __toESM(require("fs"), 1);
|
|
20473
|
+
import_node_path30 = __toESM(require("path"), 1);
|
|
20439
20474
|
}
|
|
20440
20475
|
});
|
|
20441
20476
|
|
|
@@ -20478,7 +20513,7 @@ var init_wire = __esm({
|
|
|
20478
20513
|
// src/run-case/controller.ts
|
|
20479
20514
|
async function runController(opts) {
|
|
20480
20515
|
const now = opts.now ?? Date.now;
|
|
20481
|
-
const cwd = opts.cwd ?? (0,
|
|
20516
|
+
const cwd = opts.cwd ?? (0, import_node_fs28.mkdtempSync)(import_node_path31.default.join(import_node_os15.default.tmpdir(), "clawd-runcase-"));
|
|
20482
20517
|
const ownsCwd = opts.cwd === void 0;
|
|
20483
20518
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
20484
20519
|
const spawnCtx = { cwd };
|
|
@@ -20639,19 +20674,19 @@ async function runController(opts) {
|
|
|
20639
20674
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
20640
20675
|
if (ownsCwd) {
|
|
20641
20676
|
try {
|
|
20642
|
-
(0,
|
|
20677
|
+
(0, import_node_fs28.rmSync)(cwd, { recursive: true, force: true });
|
|
20643
20678
|
} catch {
|
|
20644
20679
|
}
|
|
20645
20680
|
}
|
|
20646
20681
|
return exitCode ?? 0;
|
|
20647
20682
|
}
|
|
20648
|
-
var
|
|
20683
|
+
var import_node_fs28, import_node_os15, import_node_path31;
|
|
20649
20684
|
var init_controller = __esm({
|
|
20650
20685
|
"src/run-case/controller.ts"() {
|
|
20651
20686
|
"use strict";
|
|
20652
|
-
|
|
20653
|
-
|
|
20654
|
-
|
|
20687
|
+
import_node_fs28 = require("fs");
|
|
20688
|
+
import_node_os15 = __toESM(require("os"), 1);
|
|
20689
|
+
import_node_path31 = __toESM(require("path"), 1);
|
|
20655
20690
|
init_claude();
|
|
20656
20691
|
init_stdout_splitter();
|
|
20657
20692
|
init_permission_stdio();
|
|
@@ -20883,8 +20918,8 @@ Env (advanced):
|
|
|
20883
20918
|
`;
|
|
20884
20919
|
|
|
20885
20920
|
// src/index.ts
|
|
20886
|
-
var
|
|
20887
|
-
var
|
|
20921
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
20922
|
+
var import_node_fs26 = __toESM(require("fs"), 1);
|
|
20888
20923
|
|
|
20889
20924
|
// src/logger.ts
|
|
20890
20925
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
@@ -22619,18 +22654,6 @@ var SessionManager = class {
|
|
|
22619
22654
|
(a, b2) => a.updatedAt > b2.updatedAt ? -1 : a.updatedAt < b2.updatedAt ? 1 : 0
|
|
22620
22655
|
);
|
|
22621
22656
|
}
|
|
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
|
-
}
|
|
22634
22657
|
// 写回 SessionFile,自动根据 ownerPersonaId / creatorPrincipalId 派生写入 scope.
|
|
22635
22658
|
// 调用方原先都是 `deps.store.write(updated)`,全部走这里收口路由.
|
|
22636
22659
|
writeOwned(file) {
|
|
@@ -22884,6 +22907,25 @@ var SessionManager = class {
|
|
|
22884
22907
|
const file = this.getFile(args.sessionId);
|
|
22885
22908
|
return { response: file, broadcast: [] };
|
|
22886
22909
|
}
|
|
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
|
+
}
|
|
22887
22929
|
// observer 上报"jsonl 写入了一条 user 行"时调用:在 buffer 里找到与之对应的、
|
|
22888
22930
|
// 还没建立映射的最早一条 synth user_text,记下 synth → real 映射。
|
|
22889
22931
|
// 找不到匹配的 synth(runner 不存在 / buffer 已被 /clear 清空 / 真 uuid 来自纯 history
|
|
@@ -25531,11 +25573,18 @@ var AgentsScanner = class {
|
|
|
25531
25573
|
};
|
|
25532
25574
|
|
|
25533
25575
|
// 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
|
|
25534
25582
|
var import_node_fs12 = __toESM(require("fs"), 1);
|
|
25535
25583
|
var import_node_os8 = __toESM(require("os"), 1);
|
|
25536
25584
|
var import_node_path13 = __toESM(require("path"), 1);
|
|
25537
25585
|
init_claude_history();
|
|
25538
|
-
var
|
|
25586
|
+
var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
|
|
25587
|
+
var SubagentMetaObserver = class {
|
|
25539
25588
|
constructor(opts) {
|
|
25540
25589
|
this.opts = opts;
|
|
25541
25590
|
this.home = opts.home ?? import_node_os8.default.homedir();
|
|
@@ -25543,16 +25592,132 @@ var SessionObserver = class {
|
|
|
25543
25592
|
opts;
|
|
25544
25593
|
home;
|
|
25545
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
|
+
var SessionObserver = class {
|
|
25699
|
+
constructor(opts) {
|
|
25700
|
+
this.opts = opts;
|
|
25701
|
+
this.home = opts.home ?? import_node_os9.default.homedir();
|
|
25702
|
+
this.metaObserver = opts.enableSubagentMetaObserver ? new SubagentMetaObserver({ home: this.home, onEvent: opts.onEvent }) : null;
|
|
25703
|
+
}
|
|
25704
|
+
opts;
|
|
25705
|
+
home;
|
|
25706
|
+
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;
|
|
25546
25711
|
resolveJsonlPath(cwd, toolSessionId, override) {
|
|
25547
25712
|
if (override) return override;
|
|
25548
|
-
return
|
|
25713
|
+
return import_node_path14.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
|
|
25549
25714
|
}
|
|
25550
25715
|
start(args) {
|
|
25551
25716
|
this.stop(args.sessionId);
|
|
25552
25717
|
const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
|
|
25553
25718
|
let size = 0;
|
|
25554
25719
|
try {
|
|
25555
|
-
size =
|
|
25720
|
+
size = import_node_fs13.default.statSync(filePath).size;
|
|
25556
25721
|
} catch {
|
|
25557
25722
|
}
|
|
25558
25723
|
const w2 = {
|
|
@@ -25565,10 +25730,10 @@ var SessionObserver = class {
|
|
|
25565
25730
|
adapter: args.adapter
|
|
25566
25731
|
};
|
|
25567
25732
|
try {
|
|
25568
|
-
|
|
25733
|
+
import_node_fs13.default.mkdirSync(import_node_path14.default.dirname(filePath), { recursive: true });
|
|
25569
25734
|
} catch {
|
|
25570
25735
|
}
|
|
25571
|
-
w2.watcher =
|
|
25736
|
+
w2.watcher = import_node_fs13.default.watch(import_node_path14.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
|
|
25572
25737
|
if (!changedName || !filePath.endsWith(changedName)) return;
|
|
25573
25738
|
this.poll(w2);
|
|
25574
25739
|
});
|
|
@@ -25577,6 +25742,11 @@ var SessionObserver = class {
|
|
|
25577
25742
|
this.opts.onStatus?.(args.sessionId, "observing");
|
|
25578
25743
|
this.hydrateMetaTail(w2);
|
|
25579
25744
|
this.poll(w2);
|
|
25745
|
+
this.metaObserver?.start({
|
|
25746
|
+
sessionId: args.sessionId,
|
|
25747
|
+
cwd: args.cwd,
|
|
25748
|
+
toolSessionId: args.toolSessionId
|
|
25749
|
+
});
|
|
25580
25750
|
return { filePath };
|
|
25581
25751
|
}
|
|
25582
25752
|
// 读 JSONL 文件尾部 N 行,把每行的 meta_update 字段**合并成一个 patch** 喂给 reducer。
|
|
@@ -25586,7 +25756,7 @@ var SessionObserver = class {
|
|
|
25586
25756
|
// 异常静默吞,不阻塞 watcher 启动
|
|
25587
25757
|
hydrateMetaTail(w2, maxLines = 200) {
|
|
25588
25758
|
try {
|
|
25589
|
-
const raw =
|
|
25759
|
+
const raw = import_node_fs13.default.readFileSync(w2.filePath, "utf8");
|
|
25590
25760
|
if (!raw) return;
|
|
25591
25761
|
const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
25592
25762
|
if (allLines.length === 0) return;
|
|
@@ -25610,7 +25780,7 @@ var SessionObserver = class {
|
|
|
25610
25780
|
poll(w2) {
|
|
25611
25781
|
let size = 0;
|
|
25612
25782
|
try {
|
|
25613
|
-
size =
|
|
25783
|
+
size = import_node_fs13.default.statSync(w2.filePath).size;
|
|
25614
25784
|
} catch {
|
|
25615
25785
|
return;
|
|
25616
25786
|
}
|
|
@@ -25619,11 +25789,11 @@ var SessionObserver = class {
|
|
|
25619
25789
|
w2.buf = "";
|
|
25620
25790
|
}
|
|
25621
25791
|
if (size === w2.lastSize) return;
|
|
25622
|
-
const fd =
|
|
25792
|
+
const fd = import_node_fs13.default.openSync(w2.filePath, "r");
|
|
25623
25793
|
try {
|
|
25624
25794
|
const len = size - w2.lastSize;
|
|
25625
25795
|
const buf = Buffer.alloc(len);
|
|
25626
|
-
|
|
25796
|
+
import_node_fs13.default.readSync(fd, buf, 0, len, w2.lastSize);
|
|
25627
25797
|
w2.lastSize = size;
|
|
25628
25798
|
w2.buf += buf.toString("utf8");
|
|
25629
25799
|
let newlineIndex;
|
|
@@ -25637,7 +25807,7 @@ var SessionObserver = class {
|
|
|
25637
25807
|
}
|
|
25638
25808
|
}
|
|
25639
25809
|
} finally {
|
|
25640
|
-
|
|
25810
|
+
import_node_fs13.default.closeSync(fd);
|
|
25641
25811
|
}
|
|
25642
25812
|
}
|
|
25643
25813
|
// 解析 JSONL 单行:仅当是主链 user 文本行(非 sidechain / 非 sub-agent / message.role='user'
|
|
@@ -25690,11 +25860,13 @@ var SessionObserver = class {
|
|
|
25690
25860
|
}
|
|
25691
25861
|
if (w2.pollTimer) clearInterval(w2.pollTimer);
|
|
25692
25862
|
this.watches.delete(sessionId);
|
|
25863
|
+
this.metaObserver?.stop(sessionId);
|
|
25693
25864
|
this.opts.onStatus?.(sessionId, "stopped");
|
|
25694
25865
|
return true;
|
|
25695
25866
|
}
|
|
25696
25867
|
stopAll() {
|
|
25697
25868
|
for (const id of [...this.watches.keys()]) this.stop(id);
|
|
25869
|
+
this.metaObserver?.stopAll();
|
|
25698
25870
|
}
|
|
25699
25871
|
};
|
|
25700
25872
|
|
|
@@ -26295,14 +26467,14 @@ function authenticate(token, deps) {
|
|
|
26295
26467
|
}
|
|
26296
26468
|
|
|
26297
26469
|
// src/permission/capability-store.ts
|
|
26298
|
-
var
|
|
26299
|
-
var
|
|
26470
|
+
var fs16 = __toESM(require("fs"), 1);
|
|
26471
|
+
var path19 = __toESM(require("path"), 1);
|
|
26300
26472
|
var CAPABILITIES_FILE_NAME = "capabilities.json";
|
|
26301
26473
|
var FILE_VERSION = 1;
|
|
26302
26474
|
var CapabilityStore = class {
|
|
26303
26475
|
constructor(dataDir) {
|
|
26304
26476
|
this.dataDir = dataDir;
|
|
26305
|
-
|
|
26477
|
+
fs16.mkdirSync(dataDir, { recursive: true });
|
|
26306
26478
|
this.cache = this.readFromDisk();
|
|
26307
26479
|
}
|
|
26308
26480
|
dataDir;
|
|
@@ -26326,13 +26498,13 @@ var CapabilityStore = class {
|
|
|
26326
26498
|
this.flush();
|
|
26327
26499
|
}
|
|
26328
26500
|
filePath() {
|
|
26329
|
-
return
|
|
26501
|
+
return path19.join(this.dataDir, CAPABILITIES_FILE_NAME);
|
|
26330
26502
|
}
|
|
26331
26503
|
readFromDisk() {
|
|
26332
26504
|
const file = this.filePath();
|
|
26333
26505
|
let raw;
|
|
26334
26506
|
try {
|
|
26335
|
-
raw =
|
|
26507
|
+
raw = fs16.readFileSync(file, "utf8");
|
|
26336
26508
|
} catch (err) {
|
|
26337
26509
|
if (err?.code === "ENOENT") return [];
|
|
26338
26510
|
return [];
|
|
@@ -26360,10 +26532,10 @@ var CapabilityStore = class {
|
|
|
26360
26532
|
}
|
|
26361
26533
|
atomicWrite(file, content) {
|
|
26362
26534
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
26363
|
-
|
|
26364
|
-
|
|
26535
|
+
fs16.writeFileSync(tmp, content, { mode: 384 });
|
|
26536
|
+
fs16.renameSync(tmp, file);
|
|
26365
26537
|
try {
|
|
26366
|
-
|
|
26538
|
+
fs16.chmodSync(file, 384);
|
|
26367
26539
|
} catch {
|
|
26368
26540
|
}
|
|
26369
26541
|
}
|
|
@@ -26456,14 +26628,14 @@ var CapabilityManager = class {
|
|
|
26456
26628
|
};
|
|
26457
26629
|
|
|
26458
26630
|
// src/permission/cleanup.ts
|
|
26459
|
-
var
|
|
26631
|
+
var fs17 = __toESM(require("fs"), 1);
|
|
26460
26632
|
function cleanupGuestSessionsForCapability(cap, factory) {
|
|
26461
26633
|
const removed = [];
|
|
26462
26634
|
for (const g2 of cap.grants) {
|
|
26463
26635
|
if (g2.resource.type !== "persona") continue;
|
|
26464
26636
|
const dir = factory.vmGuestRoot(g2.resource.id, cap.id);
|
|
26465
26637
|
try {
|
|
26466
|
-
|
|
26638
|
+
fs17.rmSync(dir, { recursive: true, force: true });
|
|
26467
26639
|
removed.push(dir);
|
|
26468
26640
|
} catch {
|
|
26469
26641
|
}
|
|
@@ -26472,13 +26644,13 @@ function cleanupGuestSessionsForCapability(cap, factory) {
|
|
|
26472
26644
|
}
|
|
26473
26645
|
|
|
26474
26646
|
// src/permission/personal-capability.ts
|
|
26475
|
-
var
|
|
26476
|
-
var
|
|
26647
|
+
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
26648
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
26477
26649
|
var import_node_crypto4 = __toESM(require("crypto"), 1);
|
|
26478
26650
|
var PERSONAL_CAP_FILE_NAME = "personal-capability.json";
|
|
26479
26651
|
var PERSONAL_CAP_DISPLAY_NAME = "personal";
|
|
26480
26652
|
function personalCapFilePath(dataDir) {
|
|
26481
|
-
return
|
|
26653
|
+
return import_node_path15.default.join(dataDir, PERSONAL_CAP_FILE_NAME);
|
|
26482
26654
|
}
|
|
26483
26655
|
function loadOrCreatePersonalCapability(opts) {
|
|
26484
26656
|
const file = personalCapFilePath(opts.dataDir);
|
|
@@ -26508,7 +26680,7 @@ function loadOrCreatePersonalCapability(opts) {
|
|
|
26508
26680
|
function readFile(file) {
|
|
26509
26681
|
let raw;
|
|
26510
26682
|
try {
|
|
26511
|
-
raw =
|
|
26683
|
+
raw = import_node_fs14.default.readFileSync(file, "utf8");
|
|
26512
26684
|
} catch (err) {
|
|
26513
26685
|
const code = err?.code;
|
|
26514
26686
|
if (code === "ENOENT") return null;
|
|
@@ -26529,10 +26701,10 @@ function readFile(file) {
|
|
|
26529
26701
|
return { token: obj.token, capability: capParsed.data };
|
|
26530
26702
|
}
|
|
26531
26703
|
function writeFile(file, content) {
|
|
26532
|
-
|
|
26533
|
-
|
|
26704
|
+
import_node_fs14.default.mkdirSync(import_node_path15.default.dirname(file), { recursive: true });
|
|
26705
|
+
import_node_fs14.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
26534
26706
|
try {
|
|
26535
|
-
|
|
26707
|
+
import_node_fs14.default.chmodSync(file, 384);
|
|
26536
26708
|
} catch {
|
|
26537
26709
|
}
|
|
26538
26710
|
}
|
|
@@ -26547,13 +26719,13 @@ function sha256Hex2(s) {
|
|
|
26547
26719
|
}
|
|
26548
26720
|
|
|
26549
26721
|
// src/inbox/inbox-store.ts
|
|
26550
|
-
var
|
|
26551
|
-
var
|
|
26722
|
+
var fs19 = __toESM(require("fs"), 1);
|
|
26723
|
+
var path21 = __toESM(require("path"), 1);
|
|
26552
26724
|
var INBOX_SUBDIR = "inbox";
|
|
26553
26725
|
var InboxStore = class {
|
|
26554
26726
|
constructor(dataDir) {
|
|
26555
26727
|
this.dataDir = dataDir;
|
|
26556
|
-
|
|
26728
|
+
fs19.mkdirSync(this.dirPath(), { recursive: true });
|
|
26557
26729
|
}
|
|
26558
26730
|
dataDir;
|
|
26559
26731
|
/**
|
|
@@ -26565,7 +26737,7 @@ var InboxStore = class {
|
|
|
26565
26737
|
const file = this.filePath(peerOwnerId);
|
|
26566
26738
|
let raw;
|
|
26567
26739
|
try {
|
|
26568
|
-
raw =
|
|
26740
|
+
raw = fs19.readFileSync(file, "utf8");
|
|
26569
26741
|
} catch (err) {
|
|
26570
26742
|
if (err?.code === "ENOENT") return [];
|
|
26571
26743
|
return [];
|
|
@@ -26581,7 +26753,7 @@ var InboxStore = class {
|
|
|
26581
26753
|
const dir = this.dirPath();
|
|
26582
26754
|
let entries;
|
|
26583
26755
|
try {
|
|
26584
|
-
entries =
|
|
26756
|
+
entries = fs19.readdirSync(dir);
|
|
26585
26757
|
} catch (err) {
|
|
26586
26758
|
if (err?.code === "ENOENT") return [];
|
|
26587
26759
|
return [];
|
|
@@ -26597,9 +26769,9 @@ var InboxStore = class {
|
|
|
26597
26769
|
if (existing.some((m2) => m2.id === message.id)) return;
|
|
26598
26770
|
const file = this.filePath(message.peerOwnerId);
|
|
26599
26771
|
const line = JSON.stringify(message) + "\n";
|
|
26600
|
-
|
|
26772
|
+
fs19.appendFileSync(file, line, { mode: 384 });
|
|
26601
26773
|
try {
|
|
26602
|
-
|
|
26774
|
+
fs19.chmodSync(file, 384);
|
|
26603
26775
|
} catch {
|
|
26604
26776
|
}
|
|
26605
26777
|
}
|
|
@@ -26629,7 +26801,7 @@ var InboxStore = class {
|
|
|
26629
26801
|
removeByPeerOwnerId(peerOwnerId) {
|
|
26630
26802
|
const file = this.filePath(peerOwnerId);
|
|
26631
26803
|
try {
|
|
26632
|
-
|
|
26804
|
+
fs19.unlinkSync(file);
|
|
26633
26805
|
} catch (err) {
|
|
26634
26806
|
if (err?.code === "ENOENT") return;
|
|
26635
26807
|
}
|
|
@@ -26638,18 +26810,18 @@ var InboxStore = class {
|
|
|
26638
26810
|
const file = this.filePath(peerOwnerId);
|
|
26639
26811
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
26640
26812
|
const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
|
|
26641
|
-
|
|
26642
|
-
|
|
26813
|
+
fs19.writeFileSync(tmp, content, { mode: 384 });
|
|
26814
|
+
fs19.renameSync(tmp, file);
|
|
26643
26815
|
try {
|
|
26644
|
-
|
|
26816
|
+
fs19.chmodSync(file, 384);
|
|
26645
26817
|
} catch {
|
|
26646
26818
|
}
|
|
26647
26819
|
}
|
|
26648
26820
|
dirPath() {
|
|
26649
|
-
return
|
|
26821
|
+
return path21.join(this.dataDir, INBOX_SUBDIR);
|
|
26650
26822
|
}
|
|
26651
26823
|
filePath(peerOwnerId) {
|
|
26652
|
-
return
|
|
26824
|
+
return path21.join(this.dirPath(), `${peerOwnerId}.jsonl`);
|
|
26653
26825
|
}
|
|
26654
26826
|
};
|
|
26655
26827
|
function parseAllLines(raw) {
|
|
@@ -26736,8 +26908,8 @@ var InboxManager = class {
|
|
|
26736
26908
|
};
|
|
26737
26909
|
|
|
26738
26910
|
// src/state/received-capability-store.ts
|
|
26739
|
-
var
|
|
26740
|
-
var
|
|
26911
|
+
var fs20 = __toESM(require("fs"), 1);
|
|
26912
|
+
var path22 = __toESM(require("path"), 1);
|
|
26741
26913
|
var FILE_NAME = "received-capabilities.json";
|
|
26742
26914
|
var ReceivedCapabilityStore = class {
|
|
26743
26915
|
constructor(dataDir) {
|
|
@@ -26747,10 +26919,10 @@ var ReceivedCapabilityStore = class {
|
|
|
26747
26919
|
caps = /* @__PURE__ */ new Map();
|
|
26748
26920
|
load() {
|
|
26749
26921
|
this.caps.clear();
|
|
26750
|
-
const file =
|
|
26922
|
+
const file = path22.join(this.dataDir, FILE_NAME);
|
|
26751
26923
|
let raw;
|
|
26752
26924
|
try {
|
|
26753
|
-
raw =
|
|
26925
|
+
raw = fs20.readFileSync(file, "utf8");
|
|
26754
26926
|
} catch (err) {
|
|
26755
26927
|
if (err?.code !== "ENOENT") this.renameBak(file);
|
|
26756
26928
|
return;
|
|
@@ -26787,20 +26959,20 @@ var ReceivedCapabilityStore = class {
|
|
|
26787
26959
|
this.flush();
|
|
26788
26960
|
}
|
|
26789
26961
|
flush() {
|
|
26790
|
-
const file =
|
|
26962
|
+
const file = path22.join(this.dataDir, FILE_NAME);
|
|
26791
26963
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
26792
26964
|
const content = JSON.stringify(
|
|
26793
26965
|
{ receivedCaps: Array.from(this.caps.values()) },
|
|
26794
26966
|
null,
|
|
26795
26967
|
2
|
|
26796
26968
|
);
|
|
26797
|
-
|
|
26798
|
-
|
|
26799
|
-
|
|
26969
|
+
fs20.mkdirSync(this.dataDir, { recursive: true });
|
|
26970
|
+
fs20.writeFileSync(tmp, content, { mode: 384 });
|
|
26971
|
+
fs20.renameSync(tmp, file);
|
|
26800
26972
|
}
|
|
26801
26973
|
renameBak(file) {
|
|
26802
26974
|
try {
|
|
26803
|
-
|
|
26975
|
+
fs20.renameSync(file, `${file}.bak`);
|
|
26804
26976
|
} catch {
|
|
26805
26977
|
}
|
|
26806
26978
|
}
|
|
@@ -26898,61 +27070,61 @@ async function connectRemote(args) {
|
|
|
26898
27070
|
}
|
|
26899
27071
|
|
|
26900
27072
|
// src/migrations/2026-05-20-flatten-sessions.ts
|
|
26901
|
-
var
|
|
26902
|
-
var
|
|
27073
|
+
var fs21 = __toESM(require("fs"), 1);
|
|
27074
|
+
var path23 = __toESM(require("path"), 1);
|
|
26903
27075
|
var MIGRATION_FLAG_NAME = ".migration.v1.done";
|
|
26904
27076
|
function migrateFlattenSessions(opts) {
|
|
26905
27077
|
const dataDir = opts.dataDir;
|
|
26906
27078
|
const now = opts.now ?? Date.now;
|
|
26907
|
-
const sessionsDir =
|
|
26908
|
-
const flagPath =
|
|
27079
|
+
const sessionsDir = path23.join(dataDir, "sessions");
|
|
27080
|
+
const flagPath = path23.join(sessionsDir, MIGRATION_FLAG_NAME);
|
|
26909
27081
|
if (existsSync3(flagPath)) {
|
|
26910
27082
|
return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
|
|
26911
27083
|
}
|
|
26912
27084
|
let movedBare = 0;
|
|
26913
27085
|
let movedVmOwner = 0;
|
|
26914
27086
|
let archivedListener = 0;
|
|
26915
|
-
const defaultDir =
|
|
27087
|
+
const defaultDir = path23.join(sessionsDir, "default");
|
|
26916
27088
|
if (existsSync3(defaultDir)) {
|
|
26917
27089
|
for (const entry of readdirSafe(defaultDir)) {
|
|
26918
27090
|
if (!entry.endsWith(".json")) continue;
|
|
26919
|
-
const src =
|
|
26920
|
-
const dst =
|
|
26921
|
-
|
|
27091
|
+
const src = path23.join(defaultDir, entry);
|
|
27092
|
+
const dst = path23.join(sessionsDir, entry);
|
|
27093
|
+
fs21.renameSync(src, dst);
|
|
26922
27094
|
movedBare += 1;
|
|
26923
27095
|
}
|
|
26924
27096
|
rmdirIfEmpty(defaultDir);
|
|
26925
27097
|
}
|
|
26926
27098
|
for (const pid of readdirSafe(sessionsDir)) {
|
|
26927
|
-
const personaDir =
|
|
27099
|
+
const personaDir = path23.join(sessionsDir, pid);
|
|
26928
27100
|
if (!isDir(personaDir)) continue;
|
|
26929
27101
|
if (pid === "default") continue;
|
|
26930
|
-
const ownerSrc =
|
|
27102
|
+
const ownerSrc = path23.join(personaDir, "owner");
|
|
26931
27103
|
if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
|
|
26932
|
-
const ownerDst =
|
|
26933
|
-
|
|
27104
|
+
const ownerDst = path23.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
|
|
27105
|
+
fs21.mkdirSync(ownerDst, { recursive: true });
|
|
26934
27106
|
for (const file of readdirSafe(ownerSrc)) {
|
|
26935
27107
|
if (!file.endsWith(".json")) continue;
|
|
26936
|
-
|
|
27108
|
+
fs21.renameSync(path23.join(ownerSrc, file), path23.join(ownerDst, file));
|
|
26937
27109
|
movedVmOwner += 1;
|
|
26938
27110
|
}
|
|
26939
27111
|
rmdirIfEmpty(ownerSrc);
|
|
26940
27112
|
}
|
|
26941
|
-
const listenerSrc =
|
|
27113
|
+
const listenerSrc = path23.join(personaDir, "listener");
|
|
26942
27114
|
if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
|
|
26943
|
-
const archiveDst =
|
|
26944
|
-
|
|
27115
|
+
const archiveDst = path23.join(dataDir, ".legacy", `listener-${pid}`);
|
|
27116
|
+
fs21.mkdirSync(archiveDst, { recursive: true });
|
|
26945
27117
|
for (const file of readdirSafe(listenerSrc)) {
|
|
26946
27118
|
if (!file.endsWith(".json")) continue;
|
|
26947
|
-
|
|
27119
|
+
fs21.renameSync(path23.join(listenerSrc, file), path23.join(archiveDst, file));
|
|
26948
27120
|
archivedListener += 1;
|
|
26949
27121
|
}
|
|
26950
27122
|
rmdirIfEmpty(listenerSrc);
|
|
26951
27123
|
}
|
|
26952
27124
|
rmdirIfEmpty(personaDir);
|
|
26953
27125
|
}
|
|
26954
|
-
|
|
26955
|
-
|
|
27126
|
+
fs21.mkdirSync(sessionsDir, { recursive: true });
|
|
27127
|
+
fs21.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
|
|
26956
27128
|
return {
|
|
26957
27129
|
skipped: false,
|
|
26958
27130
|
flagWritten: true,
|
|
@@ -26963,7 +27135,7 @@ function migrateFlattenSessions(opts) {
|
|
|
26963
27135
|
}
|
|
26964
27136
|
function existsSync3(p2) {
|
|
26965
27137
|
try {
|
|
26966
|
-
|
|
27138
|
+
fs21.statSync(p2);
|
|
26967
27139
|
return true;
|
|
26968
27140
|
} catch {
|
|
26969
27141
|
return false;
|
|
@@ -26971,21 +27143,21 @@ function existsSync3(p2) {
|
|
|
26971
27143
|
}
|
|
26972
27144
|
function isDir(p2) {
|
|
26973
27145
|
try {
|
|
26974
|
-
return
|
|
27146
|
+
return fs21.statSync(p2).isDirectory();
|
|
26975
27147
|
} catch {
|
|
26976
27148
|
return false;
|
|
26977
27149
|
}
|
|
26978
27150
|
}
|
|
26979
27151
|
function readdirSafe(p2) {
|
|
26980
27152
|
try {
|
|
26981
|
-
return
|
|
27153
|
+
return fs21.readdirSync(p2);
|
|
26982
27154
|
} catch {
|
|
26983
27155
|
return [];
|
|
26984
27156
|
}
|
|
26985
27157
|
}
|
|
26986
27158
|
function rmdirIfEmpty(p2) {
|
|
26987
27159
|
try {
|
|
26988
|
-
|
|
27160
|
+
fs21.rmdirSync(p2);
|
|
26989
27161
|
} catch {
|
|
26990
27162
|
}
|
|
26991
27163
|
}
|
|
@@ -26995,7 +27167,7 @@ var import_node_fs15 = __toESM(require("fs"), 1);
|
|
|
26995
27167
|
var import_node_path17 = __toESM(require("path"), 1);
|
|
26996
27168
|
|
|
26997
27169
|
// src/attachment/mime.ts
|
|
26998
|
-
var
|
|
27170
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
26999
27171
|
var TEXT_PLAIN = "text/plain; charset=utf-8";
|
|
27000
27172
|
var EXT_TO_NATIVE_MIME = {
|
|
27001
27173
|
// 图片
|
|
@@ -27102,7 +27274,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
27102
27274
|
".mk"
|
|
27103
27275
|
]);
|
|
27104
27276
|
function lookupMime(filePathOrName) {
|
|
27105
|
-
const ext =
|
|
27277
|
+
const ext = import_node_path16.default.extname(filePathOrName).toLowerCase();
|
|
27106
27278
|
if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
|
|
27107
27279
|
if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
|
|
27108
27280
|
return "application/octet-stream";
|
|
@@ -27170,131 +27342,7 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
|
|
|
27170
27342
|
return { ok: true, absPath };
|
|
27171
27343
|
}
|
|
27172
27344
|
|
|
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
|
-
|
|
27277
27345
|
// src/transport/http-router.ts
|
|
27278
|
-
var UPLOAD_MIME_ALLOW = /* @__PURE__ */ new Set([
|
|
27279
|
-
"image/png",
|
|
27280
|
-
"image/jpeg",
|
|
27281
|
-
"image/gif",
|
|
27282
|
-
"image/webp",
|
|
27283
|
-
"application/pdf"
|
|
27284
|
-
]);
|
|
27285
|
-
var UPLOAD_MIME_ALLOW_PREFIX = ["text/"];
|
|
27286
|
-
var UPLOAD_MAX_BYTES = 25 * 1024 * 1024;
|
|
27287
|
-
function isUploadMimeAllowed(mime) {
|
|
27288
|
-
if (UPLOAD_MIME_ALLOW.has(mime)) return true;
|
|
27289
|
-
return UPLOAD_MIME_ALLOW_PREFIX.some((p2) => mime.startsWith(p2));
|
|
27290
|
-
}
|
|
27291
|
-
function isValidUploadFileName(fileName) {
|
|
27292
|
-
if (fileName.length === 0) return false;
|
|
27293
|
-
if (fileName === "." || fileName === "..") return false;
|
|
27294
|
-
if (fileName.startsWith(".")) return false;
|
|
27295
|
-
if (fileName.includes("/") || fileName.includes("\\")) return false;
|
|
27296
|
-
return fileName === import_node_path17.default.basename(fileName);
|
|
27297
|
-
}
|
|
27298
27346
|
function createHttpRouter(deps) {
|
|
27299
27347
|
return async (req, res) => {
|
|
27300
27348
|
const url = parseUrl(req.url);
|
|
@@ -27308,7 +27356,7 @@ function createHttpRouter(deps) {
|
|
|
27308
27356
|
});
|
|
27309
27357
|
return true;
|
|
27310
27358
|
}
|
|
27311
|
-
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/")
|
|
27359
|
+
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/")) {
|
|
27312
27360
|
return false;
|
|
27313
27361
|
}
|
|
27314
27362
|
if (url.pathname.startsWith("/files/") && req.method === "GET") {
|
|
@@ -27348,106 +27396,6 @@ function createHttpRouter(deps) {
|
|
|
27348
27396
|
sendJson(res, 401, { code: "UNAUTHORIZED", message: "missing or invalid bearer token" });
|
|
27349
27397
|
return true;
|
|
27350
27398
|
}
|
|
27351
|
-
if (url.pathname === "/attachments/upload" && req.method === "POST") {
|
|
27352
|
-
if (ctx.role !== "owner") {
|
|
27353
|
-
sendJson(res, 403, { code: "FORBIDDEN", message: "owner only" });
|
|
27354
|
-
return true;
|
|
27355
|
-
}
|
|
27356
|
-
const up = deps.attachmentUpload;
|
|
27357
|
-
if (!up) {
|
|
27358
|
-
sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "attachment upload not wired" });
|
|
27359
|
-
return true;
|
|
27360
|
-
}
|
|
27361
|
-
const sessionId = url.searchParams.get("sessionId");
|
|
27362
|
-
const fileName = url.searchParams.get("fileName");
|
|
27363
|
-
const mimeType = url.searchParams.get("mimeType");
|
|
27364
|
-
const contentLengthRaw = req.headers["content-length"];
|
|
27365
|
-
if (!sessionId || !fileName || !mimeType || !contentLengthRaw) {
|
|
27366
|
-
sendJson(res, 400, {
|
|
27367
|
-
code: "INVALID_PARAM",
|
|
27368
|
-
message: "missing sessionId/fileName/mimeType/Content-Length"
|
|
27369
|
-
});
|
|
27370
|
-
return true;
|
|
27371
|
-
}
|
|
27372
|
-
if (!isValidUploadFileName(fileName)) {
|
|
27373
|
-
sendJson(res, 400, {
|
|
27374
|
-
code: "INVALID_FILENAME",
|
|
27375
|
-
message: `fileName must be a plain basename (no path separators, no dotfiles): ${fileName}`
|
|
27376
|
-
});
|
|
27377
|
-
return true;
|
|
27378
|
-
}
|
|
27379
|
-
const contentLength = Number.parseInt(contentLengthRaw, 10);
|
|
27380
|
-
if (!Number.isFinite(contentLength) || contentLength < 0) {
|
|
27381
|
-
sendJson(res, 400, { code: "INVALID_PARAM", message: "malformed Content-Length" });
|
|
27382
|
-
return true;
|
|
27383
|
-
}
|
|
27384
|
-
if (contentLength > UPLOAD_MAX_BYTES) {
|
|
27385
|
-
sendJson(res, 413, {
|
|
27386
|
-
code: "FILE_TOO_LARGE",
|
|
27387
|
-
message: `max ${UPLOAD_MAX_BYTES} bytes`
|
|
27388
|
-
});
|
|
27389
|
-
return true;
|
|
27390
|
-
}
|
|
27391
|
-
if (!isUploadMimeAllowed(mimeType)) {
|
|
27392
|
-
sendJson(res, 400, {
|
|
27393
|
-
code: "UNSUPPORTED_MIME",
|
|
27394
|
-
message: `mime ${mimeType} not allowed`
|
|
27395
|
-
});
|
|
27396
|
-
return true;
|
|
27397
|
-
}
|
|
27398
|
-
const sessionDir = up.getSessionDir(sessionId);
|
|
27399
|
-
const scope = up.getSessionScope(sessionId);
|
|
27400
|
-
if (!sessionDir || !scope) {
|
|
27401
|
-
sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sessionId} not found` });
|
|
27402
|
-
return true;
|
|
27403
|
-
}
|
|
27404
|
-
const httpBaseUrl = up.getHttpBaseUrl();
|
|
27405
|
-
if (!httpBaseUrl) {
|
|
27406
|
-
sendJson(res, 503, {
|
|
27407
|
-
code: "TUNNEL_NOT_READY",
|
|
27408
|
-
message: "httpBaseUrl unavailable"
|
|
27409
|
-
});
|
|
27410
|
-
return true;
|
|
27411
|
-
}
|
|
27412
|
-
const secret = deps.getSignSecret?.();
|
|
27413
|
-
if (!secret) {
|
|
27414
|
-
sendJson(res, 501, {
|
|
27415
|
-
code: "NOT_IMPLEMENTED",
|
|
27416
|
-
message: "sign secret unavailable (noAuth?)"
|
|
27417
|
-
});
|
|
27418
|
-
return true;
|
|
27419
|
-
}
|
|
27420
|
-
try {
|
|
27421
|
-
const result = await writeUploadedAttachment({
|
|
27422
|
-
sessionDir,
|
|
27423
|
-
sessionId,
|
|
27424
|
-
scope,
|
|
27425
|
-
fileName,
|
|
27426
|
-
mimeType,
|
|
27427
|
-
contentLength,
|
|
27428
|
-
body: req,
|
|
27429
|
-
groupFileStore: up.groupFileStore
|
|
27430
|
-
});
|
|
27431
|
-
const parts = signUrlParts(secret, result.absPath, null);
|
|
27432
|
-
const fullUrl = buildSignedFileUrl(httpBaseUrl, parts);
|
|
27433
|
-
sendJson(res, 200, {
|
|
27434
|
-
attachmentId: result.attachmentId,
|
|
27435
|
-
url: fullUrl,
|
|
27436
|
-
relPath: result.relPath,
|
|
27437
|
-
sizeBytes: result.sizeBytes,
|
|
27438
|
-
mimeType: result.mimeType
|
|
27439
|
-
});
|
|
27440
|
-
} catch (err) {
|
|
27441
|
-
if (err instanceof UploadError) {
|
|
27442
|
-
const status = err.code === "SIZE_MISMATCH" ? 400 : 500;
|
|
27443
|
-
sendJson(res, status, { code: err.code, message: err.message });
|
|
27444
|
-
} else {
|
|
27445
|
-
deps.logger?.warn("attachment upload failed", { err: err.message });
|
|
27446
|
-
sendJson(res, 500, { code: "STORAGE_ERROR", message: err.message });
|
|
27447
|
-
}
|
|
27448
|
-
}
|
|
27449
|
-
return true;
|
|
27450
|
-
}
|
|
27451
27399
|
const sessionFilesMatch = url.pathname.match(/^\/session\/([^/]+)\/files$/);
|
|
27452
27400
|
if (sessionFilesMatch && req.method === "GET") {
|
|
27453
27401
|
const sid = sessionFilesMatch[1];
|
|
@@ -27529,78 +27477,10 @@ function streamFile(res, absPath, logger) {
|
|
|
27529
27477
|
stream.pipe(res);
|
|
27530
27478
|
}
|
|
27531
27479
|
|
|
27532
|
-
// src/attachment/
|
|
27480
|
+
// src/attachment/group.ts
|
|
27533
27481
|
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
27534
27482
|
var import_node_path18 = __toESM(require("path"), 1);
|
|
27535
|
-
var
|
|
27536
|
-
function runAttachmentGc(args) {
|
|
27537
|
-
const now = (args.now ?? Date.now)();
|
|
27538
|
-
const ttlMs = args.ttlMs ?? DEFAULT_TTL_MS;
|
|
27539
|
-
const liveAbs = /* @__PURE__ */ new Set();
|
|
27540
|
-
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27541
|
-
for (const entry of args.groupFileStore.list(scope, sessionId)) {
|
|
27542
|
-
if (entry.stale) continue;
|
|
27543
|
-
if (import_node_path18.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
|
|
27544
|
-
}
|
|
27545
|
-
}
|
|
27546
|
-
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27547
|
-
const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path18.default.join(
|
|
27548
|
-
args.dataDir,
|
|
27549
|
-
"sessions",
|
|
27550
|
-
...scopeSubPath(scope).map(safeFileName),
|
|
27551
|
-
safeFileName(sessionId)
|
|
27552
|
-
);
|
|
27553
|
-
const attRoot = import_node_path18.default.join(sessionDir, ".attachments");
|
|
27554
|
-
let hashDirs;
|
|
27555
|
-
try {
|
|
27556
|
-
hashDirs = import_node_fs16.default.readdirSync(attRoot);
|
|
27557
|
-
} catch (err) {
|
|
27558
|
-
if (err.code === "ENOENT") continue;
|
|
27559
|
-
args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
|
|
27560
|
-
continue;
|
|
27561
|
-
}
|
|
27562
|
-
for (const hashDir of hashDirs) {
|
|
27563
|
-
const hashDirAbs = import_node_path18.default.join(attRoot, hashDir);
|
|
27564
|
-
let files;
|
|
27565
|
-
try {
|
|
27566
|
-
files = import_node_fs16.default.readdirSync(hashDirAbs);
|
|
27567
|
-
} catch {
|
|
27568
|
-
continue;
|
|
27569
|
-
}
|
|
27570
|
-
for (const name of files) {
|
|
27571
|
-
const file = import_node_path18.default.join(hashDirAbs, name);
|
|
27572
|
-
let stat;
|
|
27573
|
-
try {
|
|
27574
|
-
stat = import_node_fs16.default.statSync(file);
|
|
27575
|
-
} catch {
|
|
27576
|
-
continue;
|
|
27577
|
-
}
|
|
27578
|
-
if (!stat.isFile()) continue;
|
|
27579
|
-
const age = now - stat.mtimeMs;
|
|
27580
|
-
if (age < ttlMs) continue;
|
|
27581
|
-
if (liveAbs.has(file)) continue;
|
|
27582
|
-
try {
|
|
27583
|
-
import_node_fs16.default.unlinkSync(file);
|
|
27584
|
-
} catch (err) {
|
|
27585
|
-
args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
|
|
27586
|
-
}
|
|
27587
|
-
}
|
|
27588
|
-
try {
|
|
27589
|
-
if (import_node_fs16.default.readdirSync(hashDirAbs).length === 0) import_node_fs16.default.rmdirSync(hashDirAbs);
|
|
27590
|
-
} catch {
|
|
27591
|
-
}
|
|
27592
|
-
}
|
|
27593
|
-
try {
|
|
27594
|
-
if (import_node_fs16.default.readdirSync(attRoot).length === 0) import_node_fs16.default.rmdirSync(attRoot);
|
|
27595
|
-
} catch {
|
|
27596
|
-
}
|
|
27597
|
-
}
|
|
27598
|
-
}
|
|
27599
|
-
|
|
27600
|
-
// src/attachment/group.ts
|
|
27601
|
-
var import_node_fs17 = __toESM(require("fs"), 1);
|
|
27602
|
-
var import_node_path19 = __toESM(require("path"), 1);
|
|
27603
|
-
var import_node_crypto7 = __toESM(require("crypto"), 1);
|
|
27483
|
+
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
27604
27484
|
init_protocol();
|
|
27605
27485
|
var GroupFileStore = class {
|
|
27606
27486
|
dataDir;
|
|
@@ -27611,11 +27491,11 @@ var GroupFileStore = class {
|
|
|
27611
27491
|
this.logger = opts.logger;
|
|
27612
27492
|
}
|
|
27613
27493
|
rootForScope(scope) {
|
|
27614
|
-
return
|
|
27494
|
+
return import_node_path18.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
|
|
27615
27495
|
}
|
|
27616
27496
|
/** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
|
|
27617
27497
|
filePath(scope, sessionId) {
|
|
27618
|
-
return
|
|
27498
|
+
return import_node_path18.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
|
|
27619
27499
|
}
|
|
27620
27500
|
cacheKey(scope, sessionId) {
|
|
27621
27501
|
return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
|
|
@@ -27624,7 +27504,7 @@ var GroupFileStore = class {
|
|
|
27624
27504
|
readFile(scope, sessionId) {
|
|
27625
27505
|
const file = this.filePath(scope, sessionId);
|
|
27626
27506
|
try {
|
|
27627
|
-
const raw =
|
|
27507
|
+
const raw = import_node_fs16.default.readFileSync(file, "utf8");
|
|
27628
27508
|
const parsed = JSON.parse(raw);
|
|
27629
27509
|
if (!Array.isArray(parsed)) {
|
|
27630
27510
|
this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
|
|
@@ -27650,10 +27530,10 @@ var GroupFileStore = class {
|
|
|
27650
27530
|
}
|
|
27651
27531
|
writeFile(scope, sessionId, entries) {
|
|
27652
27532
|
const file = this.filePath(scope, sessionId);
|
|
27653
|
-
|
|
27533
|
+
import_node_fs16.default.mkdirSync(import_node_path18.default.dirname(file), { recursive: true });
|
|
27654
27534
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
27655
|
-
|
|
27656
|
-
|
|
27535
|
+
import_node_fs16.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
|
|
27536
|
+
import_node_fs16.default.renameSync(tmp, file);
|
|
27657
27537
|
}
|
|
27658
27538
|
/** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
|
|
27659
27539
|
list(scope, sessionId) {
|
|
@@ -27689,7 +27569,7 @@ var GroupFileStore = class {
|
|
|
27689
27569
|
entries[idx] = next;
|
|
27690
27570
|
} else {
|
|
27691
27571
|
next = {
|
|
27692
|
-
id: `gf-${
|
|
27572
|
+
id: `gf-${import_node_crypto6.default.randomBytes(6).toString("base64url")}`,
|
|
27693
27573
|
relPath: input.relPath,
|
|
27694
27574
|
from: input.from,
|
|
27695
27575
|
label: input.label,
|
|
@@ -27739,10 +27619,10 @@ var GroupFileStore = class {
|
|
|
27739
27619
|
};
|
|
27740
27620
|
|
|
27741
27621
|
// src/discovery/state-file.ts
|
|
27742
|
-
var
|
|
27743
|
-
var
|
|
27622
|
+
var import_node_fs17 = __toESM(require("fs"), 1);
|
|
27623
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
27744
27624
|
function defaultStateFilePath(dataDir) {
|
|
27745
|
-
return
|
|
27625
|
+
return import_node_path19.default.join(dataDir, "state.json");
|
|
27746
27626
|
}
|
|
27747
27627
|
function isPidAlive(pid) {
|
|
27748
27628
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
@@ -27764,7 +27644,7 @@ var StateFileManager = class {
|
|
|
27764
27644
|
}
|
|
27765
27645
|
read() {
|
|
27766
27646
|
try {
|
|
27767
|
-
const raw =
|
|
27647
|
+
const raw = import_node_fs17.default.readFileSync(this.file, "utf8");
|
|
27768
27648
|
const parsed = JSON.parse(raw);
|
|
27769
27649
|
return parsed;
|
|
27770
27650
|
} catch {
|
|
@@ -27778,34 +27658,34 @@ var StateFileManager = class {
|
|
|
27778
27658
|
return { status: "stale", existing };
|
|
27779
27659
|
}
|
|
27780
27660
|
write(state) {
|
|
27781
|
-
|
|
27661
|
+
import_node_fs17.default.mkdirSync(import_node_path19.default.dirname(this.file), { recursive: true });
|
|
27782
27662
|
const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
|
|
27783
|
-
|
|
27784
|
-
|
|
27663
|
+
import_node_fs17.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
|
|
27664
|
+
import_node_fs17.default.renameSync(tmp, this.file);
|
|
27785
27665
|
if (process.platform !== "win32") {
|
|
27786
27666
|
try {
|
|
27787
|
-
|
|
27667
|
+
import_node_fs17.default.chmodSync(this.file, 384);
|
|
27788
27668
|
} catch {
|
|
27789
27669
|
}
|
|
27790
27670
|
}
|
|
27791
27671
|
}
|
|
27792
27672
|
delete() {
|
|
27793
27673
|
try {
|
|
27794
|
-
|
|
27674
|
+
import_node_fs17.default.unlinkSync(this.file);
|
|
27795
27675
|
} catch {
|
|
27796
27676
|
}
|
|
27797
27677
|
}
|
|
27798
27678
|
};
|
|
27799
27679
|
|
|
27800
27680
|
// src/tunnel/tunnel-manager.ts
|
|
27801
|
-
var
|
|
27802
|
-
var
|
|
27803
|
-
var
|
|
27681
|
+
var import_node_fs21 = __toESM(require("fs"), 1);
|
|
27682
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
27683
|
+
var import_node_crypto7 = __toESM(require("crypto"), 1);
|
|
27804
27684
|
var import_node_child_process5 = require("child_process");
|
|
27805
27685
|
|
|
27806
27686
|
// src/tunnel/tunnel-store.ts
|
|
27807
|
-
var
|
|
27808
|
-
var
|
|
27687
|
+
var import_node_fs18 = __toESM(require("fs"), 1);
|
|
27688
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
27809
27689
|
var TunnelStore = class {
|
|
27810
27690
|
constructor(filePath) {
|
|
27811
27691
|
this.filePath = filePath;
|
|
@@ -27813,7 +27693,7 @@ var TunnelStore = class {
|
|
|
27813
27693
|
filePath;
|
|
27814
27694
|
async get() {
|
|
27815
27695
|
try {
|
|
27816
|
-
const raw = await
|
|
27696
|
+
const raw = await import_node_fs18.default.promises.readFile(this.filePath, "utf8");
|
|
27817
27697
|
const obj = JSON.parse(raw);
|
|
27818
27698
|
if (!isPersistedTunnel(obj)) return null;
|
|
27819
27699
|
return obj;
|
|
@@ -27824,22 +27704,22 @@ var TunnelStore = class {
|
|
|
27824
27704
|
}
|
|
27825
27705
|
}
|
|
27826
27706
|
async set(v2) {
|
|
27827
|
-
const dir =
|
|
27828
|
-
await
|
|
27707
|
+
const dir = import_node_path20.default.dirname(this.filePath);
|
|
27708
|
+
await import_node_fs18.default.promises.mkdir(dir, { recursive: true });
|
|
27829
27709
|
const data = JSON.stringify(v2, null, 2);
|
|
27830
27710
|
const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
27831
|
-
await
|
|
27711
|
+
await import_node_fs18.default.promises.writeFile(tmp, data, { mode: 384 });
|
|
27832
27712
|
if (process.platform !== "win32") {
|
|
27833
27713
|
try {
|
|
27834
|
-
await
|
|
27714
|
+
await import_node_fs18.default.promises.chmod(tmp, 384);
|
|
27835
27715
|
} catch {
|
|
27836
27716
|
}
|
|
27837
27717
|
}
|
|
27838
|
-
await
|
|
27718
|
+
await import_node_fs18.default.promises.rename(tmp, this.filePath);
|
|
27839
27719
|
}
|
|
27840
27720
|
async clear() {
|
|
27841
27721
|
try {
|
|
27842
|
-
await
|
|
27722
|
+
await import_node_fs18.default.promises.unlink(this.filePath);
|
|
27843
27723
|
} catch (err) {
|
|
27844
27724
|
const code = err?.code;
|
|
27845
27725
|
if (code !== "ENOENT") throw err;
|
|
@@ -27934,12 +27814,12 @@ function escape(v2) {
|
|
|
27934
27814
|
}
|
|
27935
27815
|
|
|
27936
27816
|
// src/tunnel/frpc-binary.ts
|
|
27937
|
-
var
|
|
27938
|
-
var
|
|
27939
|
-
var
|
|
27817
|
+
var import_node_fs19 = __toESM(require("fs"), 1);
|
|
27818
|
+
var import_node_os10 = __toESM(require("os"), 1);
|
|
27819
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
27940
27820
|
var import_node_child_process3 = require("child_process");
|
|
27941
27821
|
var import_node_stream2 = require("stream");
|
|
27942
|
-
var
|
|
27822
|
+
var import_promises = require("stream/promises");
|
|
27943
27823
|
var FRPC_VERSION = "0.68.0";
|
|
27944
27824
|
var UnsupportedPlatformError = class extends Error {
|
|
27945
27825
|
constructor(platform, arch) {
|
|
@@ -27968,20 +27848,20 @@ function frpcDownloadUrl(version2, p2) {
|
|
|
27968
27848
|
}
|
|
27969
27849
|
async function ensureFrpcBinary(opts) {
|
|
27970
27850
|
if (opts.override) {
|
|
27971
|
-
if (!
|
|
27851
|
+
if (!import_node_fs19.default.existsSync(opts.override)) {
|
|
27972
27852
|
throw new Error(`frpc binary not found at override path: ${opts.override}`);
|
|
27973
27853
|
}
|
|
27974
27854
|
return opts.override;
|
|
27975
27855
|
}
|
|
27976
27856
|
const version2 = opts.version ?? FRPC_VERSION;
|
|
27977
27857
|
const platform = opts.platform ?? detectPlatform();
|
|
27978
|
-
const binDir =
|
|
27979
|
-
|
|
27858
|
+
const binDir = import_node_path21.default.join(opts.dataDir, "bin");
|
|
27859
|
+
import_node_fs19.default.mkdirSync(binDir, { recursive: true });
|
|
27980
27860
|
cleanupStaleArtifacts(binDir);
|
|
27981
|
-
const stableBin =
|
|
27982
|
-
if (
|
|
27861
|
+
const stableBin = import_node_path21.default.join(binDir, "frpc");
|
|
27862
|
+
if (import_node_fs19.default.existsSync(stableBin)) return stableBin;
|
|
27983
27863
|
const partialBin = `${stableBin}.partial`;
|
|
27984
|
-
const tarballPath =
|
|
27864
|
+
const tarballPath = import_node_path21.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
|
|
27985
27865
|
try {
|
|
27986
27866
|
const url = frpcDownloadUrl(version2, platform);
|
|
27987
27867
|
await downloadToFile(url, tarballPath, opts.fetchImpl);
|
|
@@ -27990,8 +27870,8 @@ async function ensureFrpcBinary(opts) {
|
|
|
27990
27870
|
} else {
|
|
27991
27871
|
await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
|
|
27992
27872
|
}
|
|
27993
|
-
|
|
27994
|
-
|
|
27873
|
+
import_node_fs19.default.chmodSync(partialBin, 493);
|
|
27874
|
+
import_node_fs19.default.renameSync(partialBin, stableBin);
|
|
27995
27875
|
} finally {
|
|
27996
27876
|
safeUnlink(tarballPath);
|
|
27997
27877
|
safeUnlink(partialBin);
|
|
@@ -28001,15 +27881,15 @@ async function ensureFrpcBinary(opts) {
|
|
|
28001
27881
|
function cleanupStaleArtifacts(binDir) {
|
|
28002
27882
|
let entries;
|
|
28003
27883
|
try {
|
|
28004
|
-
entries =
|
|
27884
|
+
entries = import_node_fs19.default.readdirSync(binDir);
|
|
28005
27885
|
} catch {
|
|
28006
27886
|
return;
|
|
28007
27887
|
}
|
|
28008
27888
|
for (const name of entries) {
|
|
28009
27889
|
if (name.endsWith(".partial") || name.startsWith("extract-")) {
|
|
28010
|
-
const full =
|
|
27890
|
+
const full = import_node_path21.default.join(binDir, name);
|
|
28011
27891
|
try {
|
|
28012
|
-
|
|
27892
|
+
import_node_fs19.default.rmSync(full, { recursive: true, force: true });
|
|
28013
27893
|
} catch {
|
|
28014
27894
|
}
|
|
28015
27895
|
}
|
|
@@ -28017,7 +27897,7 @@ function cleanupStaleArtifacts(binDir) {
|
|
|
28017
27897
|
}
|
|
28018
27898
|
function safeUnlink(p2) {
|
|
28019
27899
|
try {
|
|
28020
|
-
|
|
27900
|
+
import_node_fs19.default.unlinkSync(p2);
|
|
28021
27901
|
} catch {
|
|
28022
27902
|
}
|
|
28023
27903
|
}
|
|
@@ -28028,13 +27908,13 @@ async function downloadToFile(url, dest, fetchImpl) {
|
|
|
28028
27908
|
if (!res.ok || !res.body) {
|
|
28029
27909
|
throw new Error(`download failed: ${res.status} ${res.statusText}`);
|
|
28030
27910
|
}
|
|
28031
|
-
const out =
|
|
27911
|
+
const out = import_node_fs19.default.createWriteStream(dest);
|
|
28032
27912
|
const nodeStream = import_node_stream2.Readable.fromWeb(res.body);
|
|
28033
|
-
await (0,
|
|
27913
|
+
await (0, import_promises.pipeline)(nodeStream, out);
|
|
28034
27914
|
}
|
|
28035
27915
|
async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
|
|
28036
|
-
const work =
|
|
28037
|
-
|
|
27916
|
+
const work = import_node_path21.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
|
|
27917
|
+
import_node_fs19.default.mkdirSync(work, { recursive: true });
|
|
28038
27918
|
try {
|
|
28039
27919
|
await new Promise((resolve6, reject) => {
|
|
28040
27920
|
const proc = (0, import_node_child_process3.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
|
|
@@ -28042,32 +27922,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
|
|
|
28042
27922
|
proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
|
|
28043
27923
|
});
|
|
28044
27924
|
const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
|
|
28045
|
-
const src =
|
|
28046
|
-
if (!
|
|
27925
|
+
const src = import_node_path21.default.join(work, dirName, "frpc");
|
|
27926
|
+
if (!import_node_fs19.default.existsSync(src)) {
|
|
28047
27927
|
throw new Error(`frpc not found inside tarball at ${src}`);
|
|
28048
27928
|
}
|
|
28049
|
-
|
|
27929
|
+
import_node_fs19.default.copyFileSync(src, destBin);
|
|
28050
27930
|
} finally {
|
|
28051
|
-
|
|
27931
|
+
import_node_fs19.default.rmSync(work, { recursive: true, force: true });
|
|
28052
27932
|
}
|
|
28053
27933
|
}
|
|
28054
27934
|
|
|
28055
27935
|
// src/tunnel/frpc-process.ts
|
|
28056
|
-
var
|
|
28057
|
-
var
|
|
27936
|
+
var import_node_fs20 = __toESM(require("fs"), 1);
|
|
27937
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
28058
27938
|
var import_node_child_process4 = require("child_process");
|
|
28059
27939
|
function frpcPidFilePath(dataDir) {
|
|
28060
|
-
return
|
|
27940
|
+
return import_node_path22.default.join(dataDir, "frpc.pid");
|
|
28061
27941
|
}
|
|
28062
27942
|
function writeFrpcPid(dataDir, pid) {
|
|
28063
27943
|
try {
|
|
28064
|
-
|
|
27944
|
+
import_node_fs20.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
|
|
28065
27945
|
} catch {
|
|
28066
27946
|
}
|
|
28067
27947
|
}
|
|
28068
27948
|
function clearFrpcPid(dataDir) {
|
|
28069
27949
|
try {
|
|
28070
|
-
|
|
27950
|
+
import_node_fs20.default.unlinkSync(frpcPidFilePath(dataDir));
|
|
28071
27951
|
} catch {
|
|
28072
27952
|
}
|
|
28073
27953
|
}
|
|
@@ -28083,7 +27963,7 @@ function defaultIsPidAlive(pid) {
|
|
|
28083
27963
|
}
|
|
28084
27964
|
function defaultReadPidFile(file) {
|
|
28085
27965
|
try {
|
|
28086
|
-
return
|
|
27966
|
+
return import_node_fs20.default.readFileSync(file, "utf8");
|
|
28087
27967
|
} catch {
|
|
28088
27968
|
return null;
|
|
28089
27969
|
}
|
|
@@ -28099,7 +27979,7 @@ function defaultSleep(ms) {
|
|
|
28099
27979
|
}
|
|
28100
27980
|
async function killStaleFrpc(deps) {
|
|
28101
27981
|
const pidFile = frpcPidFilePath(deps.dataDir);
|
|
28102
|
-
const tomlPath =
|
|
27982
|
+
const tomlPath = import_node_path22.default.join(deps.dataDir, "frpc.toml");
|
|
28103
27983
|
const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
|
|
28104
27984
|
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
|
|
28105
27985
|
const killPid = deps.killPidImpl ?? defaultKillPid;
|
|
@@ -28123,7 +28003,7 @@ async function killStaleFrpc(deps) {
|
|
|
28123
28003
|
}
|
|
28124
28004
|
if (victims.size === 0) {
|
|
28125
28005
|
try {
|
|
28126
|
-
|
|
28006
|
+
import_node_fs20.default.unlinkSync(pidFile);
|
|
28127
28007
|
} catch {
|
|
28128
28008
|
}
|
|
28129
28009
|
return;
|
|
@@ -28134,7 +28014,7 @@ async function killStaleFrpc(deps) {
|
|
|
28134
28014
|
}
|
|
28135
28015
|
await sleep(deps.reapWaitMs ?? 300);
|
|
28136
28016
|
try {
|
|
28137
|
-
|
|
28017
|
+
import_node_fs20.default.unlinkSync(pidFile);
|
|
28138
28018
|
} catch {
|
|
28139
28019
|
}
|
|
28140
28020
|
}
|
|
@@ -28171,7 +28051,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
28171
28051
|
var TunnelManager = class {
|
|
28172
28052
|
constructor(deps) {
|
|
28173
28053
|
this.deps = deps;
|
|
28174
|
-
this.store = deps.store ?? new TunnelStore(
|
|
28054
|
+
this.store = deps.store ?? new TunnelStore(import_node_path23.default.join(deps.dataDir, "tunnel.json"));
|
|
28175
28055
|
this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
|
|
28176
28056
|
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
28177
28057
|
}
|
|
@@ -28298,8 +28178,8 @@ var TunnelManager = class {
|
|
|
28298
28178
|
dataDir: this.deps.dataDir,
|
|
28299
28179
|
override: this.deps.frpcBinaryOverride ?? void 0
|
|
28300
28180
|
});
|
|
28301
|
-
const tomlPath =
|
|
28302
|
-
const proxyName = `clawd-${t.subdomain}-${localPort}-${
|
|
28181
|
+
const tomlPath = import_node_path23.default.join(this.deps.dataDir, "frpc.toml");
|
|
28182
|
+
const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto7.default.randomBytes(3).toString("hex")}`;
|
|
28303
28183
|
const toml = buildFrpcToml({
|
|
28304
28184
|
serverAddr: t.frpsHost,
|
|
28305
28185
|
serverPort: t.frpsPort,
|
|
@@ -28309,12 +28189,12 @@ var TunnelManager = class {
|
|
|
28309
28189
|
localPort,
|
|
28310
28190
|
logLevel: "info"
|
|
28311
28191
|
});
|
|
28312
|
-
await
|
|
28192
|
+
await import_node_fs21.default.promises.writeFile(tomlPath, toml, { mode: 384 });
|
|
28313
28193
|
const proc = (this.deps.spawnImpl ?? import_node_child_process5.spawn)(frpcBin, ["-c", tomlPath], {
|
|
28314
28194
|
stdio: ["ignore", "pipe", "pipe"]
|
|
28315
28195
|
});
|
|
28316
|
-
const logFilePath =
|
|
28317
|
-
const logStream =
|
|
28196
|
+
const logFilePath = import_node_path23.default.join(this.deps.dataDir, "frpc.log");
|
|
28197
|
+
const logStream = import_node_fs21.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
|
|
28318
28198
|
logStream.on("error", () => {
|
|
28319
28199
|
});
|
|
28320
28200
|
const tee = (chunk) => {
|
|
@@ -28396,23 +28276,23 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
28396
28276
|
}
|
|
28397
28277
|
|
|
28398
28278
|
// src/tunnel/device-key.ts
|
|
28399
|
-
var
|
|
28400
|
-
var
|
|
28279
|
+
var import_node_os11 = __toESM(require("os"), 1);
|
|
28280
|
+
var import_node_crypto8 = __toESM(require("crypto"), 1);
|
|
28401
28281
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
28402
28282
|
function deriveStableDeviceKey(opts = {}) {
|
|
28403
|
-
const hostname = opts.hostname ??
|
|
28404
|
-
const uid = opts.uid ?? (typeof
|
|
28283
|
+
const hostname = opts.hostname ?? import_node_os11.default.hostname();
|
|
28284
|
+
const uid = opts.uid ?? (typeof import_node_os11.default.userInfo === "function" ? import_node_os11.default.userInfo().uid : 0);
|
|
28405
28285
|
const input = `${hostname}::${uid}`;
|
|
28406
|
-
return
|
|
28286
|
+
return import_node_crypto8.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
28407
28287
|
}
|
|
28408
28288
|
|
|
28409
28289
|
// src/auth-store.ts
|
|
28410
|
-
var
|
|
28411
|
-
var
|
|
28412
|
-
var
|
|
28290
|
+
var import_node_fs22 = __toESM(require("fs"), 1);
|
|
28291
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
28292
|
+
var import_node_crypto9 = __toESM(require("crypto"), 1);
|
|
28413
28293
|
var AUTH_FILE_NAME = "auth.json";
|
|
28414
28294
|
function authFilePath(dataDir) {
|
|
28415
|
-
return
|
|
28295
|
+
return import_node_path24.default.join(dataDir, AUTH_FILE_NAME);
|
|
28416
28296
|
}
|
|
28417
28297
|
function loadOrCreateAuthFile(opts) {
|
|
28418
28298
|
const file = authFilePath(opts.dataDir);
|
|
@@ -28437,14 +28317,14 @@ function loadOrCreateAuthFile(opts) {
|
|
|
28437
28317
|
return next;
|
|
28438
28318
|
}
|
|
28439
28319
|
function defaultGenerateToken2() {
|
|
28440
|
-
return
|
|
28320
|
+
return import_node_crypto9.default.randomBytes(32).toString("base64url");
|
|
28441
28321
|
}
|
|
28442
28322
|
function defaultGenerateOwnerPrincipalId() {
|
|
28443
|
-
return `owner-${
|
|
28323
|
+
return `owner-${import_node_crypto9.default.randomUUID()}`;
|
|
28444
28324
|
}
|
|
28445
28325
|
function readAuthFile(file) {
|
|
28446
28326
|
try {
|
|
28447
|
-
const raw =
|
|
28327
|
+
const raw = import_node_fs22.default.readFileSync(file, "utf8");
|
|
28448
28328
|
const parsed = JSON.parse(raw);
|
|
28449
28329
|
if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
|
|
28450
28330
|
return null;
|
|
@@ -28462,25 +28342,25 @@ function readAuthFile(file) {
|
|
|
28462
28342
|
}
|
|
28463
28343
|
}
|
|
28464
28344
|
function writeAuthFile(file, content) {
|
|
28465
|
-
|
|
28466
|
-
|
|
28345
|
+
import_node_fs22.default.mkdirSync(import_node_path24.default.dirname(file), { recursive: true });
|
|
28346
|
+
import_node_fs22.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
28467
28347
|
try {
|
|
28468
|
-
|
|
28348
|
+
import_node_fs22.default.chmodSync(file, 384);
|
|
28469
28349
|
} catch {
|
|
28470
28350
|
}
|
|
28471
28351
|
}
|
|
28472
28352
|
|
|
28473
28353
|
// src/owner-profile.ts
|
|
28474
|
-
var
|
|
28475
|
-
var
|
|
28476
|
-
var
|
|
28354
|
+
var import_node_fs23 = __toESM(require("fs"), 1);
|
|
28355
|
+
var import_node_os12 = __toESM(require("os"), 1);
|
|
28356
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
28477
28357
|
var PROFILE_FILENAME = "profile.json";
|
|
28478
28358
|
function loadOwnerDisplayName(dataDir) {
|
|
28479
|
-
const fallback =
|
|
28480
|
-
const profilePath =
|
|
28359
|
+
const fallback = import_node_os12.default.userInfo().username;
|
|
28360
|
+
const profilePath = import_node_path25.default.join(dataDir, PROFILE_FILENAME);
|
|
28481
28361
|
let raw;
|
|
28482
28362
|
try {
|
|
28483
|
-
raw =
|
|
28363
|
+
raw = import_node_fs23.default.readFileSync(profilePath, "utf8");
|
|
28484
28364
|
} catch {
|
|
28485
28365
|
return fallback;
|
|
28486
28366
|
}
|
|
@@ -28509,12 +28389,12 @@ init_protocol();
|
|
|
28509
28389
|
init_protocol();
|
|
28510
28390
|
|
|
28511
28391
|
// src/session/fork.ts
|
|
28512
|
-
var
|
|
28513
|
-
var
|
|
28514
|
-
var
|
|
28392
|
+
var import_node_fs24 = __toESM(require("fs"), 1);
|
|
28393
|
+
var import_node_os13 = __toESM(require("os"), 1);
|
|
28394
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
28515
28395
|
init_claude_history();
|
|
28516
28396
|
function readJsonlEntries(file) {
|
|
28517
|
-
const raw =
|
|
28397
|
+
const raw = import_node_fs24.default.readFileSync(file, "utf8");
|
|
28518
28398
|
const out = [];
|
|
28519
28399
|
for (const line of raw.split("\n")) {
|
|
28520
28400
|
const t = line.trim();
|
|
@@ -28527,10 +28407,10 @@ function readJsonlEntries(file) {
|
|
|
28527
28407
|
return out;
|
|
28528
28408
|
}
|
|
28529
28409
|
function forkSession(input) {
|
|
28530
|
-
const baseDir = input.baseDir ??
|
|
28531
|
-
const projectDir =
|
|
28532
|
-
const sourceFile =
|
|
28533
|
-
if (!
|
|
28410
|
+
const baseDir = input.baseDir ?? import_node_path26.default.join(import_node_os13.default.homedir(), ".claude");
|
|
28411
|
+
const projectDir = import_node_path26.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
28412
|
+
const sourceFile = import_node_path26.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
28413
|
+
if (!import_node_fs24.default.existsSync(sourceFile)) {
|
|
28534
28414
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
28535
28415
|
}
|
|
28536
28416
|
const entries = readJsonlEntries(sourceFile);
|
|
@@ -28560,9 +28440,9 @@ function forkSession(input) {
|
|
|
28560
28440
|
}
|
|
28561
28441
|
forkedLines.push(JSON.stringify(forked));
|
|
28562
28442
|
}
|
|
28563
|
-
const forkedFilePath =
|
|
28564
|
-
|
|
28565
|
-
|
|
28443
|
+
const forkedFilePath = import_node_path26.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
28444
|
+
import_node_fs24.default.mkdirSync(projectDir, { recursive: true });
|
|
28445
|
+
import_node_fs24.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
28566
28446
|
return { forkedToolSessionId, forkedFilePath };
|
|
28567
28447
|
}
|
|
28568
28448
|
|
|
@@ -28850,6 +28730,28 @@ function buildSessionHandlers(deps) {
|
|
|
28850
28730
|
}
|
|
28851
28731
|
};
|
|
28852
28732
|
};
|
|
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
|
+
};
|
|
28853
28755
|
return {
|
|
28854
28756
|
"session:create": create,
|
|
28855
28757
|
"session:list": list,
|
|
@@ -28875,7 +28777,9 @@ function buildSessionHandlers(deps) {
|
|
|
28875
28777
|
"session:cancelQuestion": cancelQuestion,
|
|
28876
28778
|
"session:pty:input": ptyInput,
|
|
28877
28779
|
"session:pty:resize": ptyResize,
|
|
28878
|
-
"session:pty:snapshot": ptySnapshot
|
|
28780
|
+
"session:pty:snapshot": ptySnapshot,
|
|
28781
|
+
"peerSession:upsert": peerSessionUpsert,
|
|
28782
|
+
"peerSession:remove": peerSessionRemove
|
|
28879
28783
|
};
|
|
28880
28784
|
}
|
|
28881
28785
|
|
|
@@ -28894,7 +28798,7 @@ function buildPermissionHandlers(deps) {
|
|
|
28894
28798
|
}
|
|
28895
28799
|
|
|
28896
28800
|
// src/handlers/history.ts
|
|
28897
|
-
var
|
|
28801
|
+
var path36 = __toESM(require("path"), 1);
|
|
28898
28802
|
init_protocol();
|
|
28899
28803
|
|
|
28900
28804
|
// src/session/recent-dirs.ts
|
|
@@ -28912,7 +28816,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
28912
28816
|
}
|
|
28913
28817
|
|
|
28914
28818
|
// src/permission/persona-paths.ts
|
|
28915
|
-
var
|
|
28819
|
+
var path35 = __toESM(require("path"), 1);
|
|
28916
28820
|
function getAllowedPersonaIds(grants, action) {
|
|
28917
28821
|
const ids = /* @__PURE__ */ new Set();
|
|
28918
28822
|
for (const g2 of grants) {
|
|
@@ -28925,26 +28829,26 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
28925
28829
|
return ids;
|
|
28926
28830
|
}
|
|
28927
28831
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read") {
|
|
28928
|
-
const root =
|
|
28929
|
-
const target =
|
|
28930
|
-
const sep3 = root.endsWith(
|
|
28832
|
+
const root = path35.resolve(personaRoot);
|
|
28833
|
+
const target = path35.resolve(absPath);
|
|
28834
|
+
const sep3 = root.endsWith(path35.sep) ? "" : path35.sep;
|
|
28931
28835
|
if (!target.startsWith(root + sep3)) return false;
|
|
28932
|
-
const rel =
|
|
28836
|
+
const rel = path35.relative(root, target);
|
|
28933
28837
|
if (!rel || rel.startsWith("..")) return false;
|
|
28934
|
-
const personaId = rel.split(
|
|
28838
|
+
const personaId = rel.split(path35.sep)[0];
|
|
28935
28839
|
if (!personaId) return false;
|
|
28936
28840
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
28937
28841
|
if (allowed === "*") return true;
|
|
28938
28842
|
return allowed.has(personaId);
|
|
28939
28843
|
}
|
|
28940
28844
|
function personaIdFromPath(absPath, personaRoot) {
|
|
28941
|
-
const root =
|
|
28942
|
-
const target =
|
|
28943
|
-
const sep3 = root.endsWith(
|
|
28845
|
+
const root = path35.resolve(personaRoot);
|
|
28846
|
+
const target = path35.resolve(absPath);
|
|
28847
|
+
const sep3 = root.endsWith(path35.sep) ? "" : path35.sep;
|
|
28944
28848
|
if (!target.startsWith(root + sep3)) return null;
|
|
28945
|
-
const rel =
|
|
28849
|
+
const rel = path35.relative(root, target);
|
|
28946
28850
|
if (!rel || rel.startsWith("..")) return null;
|
|
28947
|
-
const id = rel.split(
|
|
28851
|
+
const id = rel.split(path35.sep)[0];
|
|
28948
28852
|
return id || null;
|
|
28949
28853
|
}
|
|
28950
28854
|
|
|
@@ -28969,7 +28873,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28969
28873
|
if (!pid) return false;
|
|
28970
28874
|
return isGuestPathAllowed(
|
|
28971
28875
|
ctx.grants,
|
|
28972
|
-
|
|
28876
|
+
path36.join(personaRoot, pid),
|
|
28973
28877
|
personaRoot,
|
|
28974
28878
|
"read"
|
|
28975
28879
|
);
|
|
@@ -28980,7 +28884,7 @@ function buildHistoryHandlers(deps) {
|
|
|
28980
28884
|
};
|
|
28981
28885
|
const list = async (frame, _client, ctx) => {
|
|
28982
28886
|
const args = HistoryListArgs.parse(frame);
|
|
28983
|
-
assertGuestPath(ctx,
|
|
28887
|
+
assertGuestPath(ctx, path36.resolve(args.projectPath), personaRoot, "history:list");
|
|
28984
28888
|
const sessions = await history.listSessions(args);
|
|
28985
28889
|
return { response: { type: "history:list", sessions } };
|
|
28986
28890
|
};
|
|
@@ -29012,13 +28916,13 @@ function buildHistoryHandlers(deps) {
|
|
|
29012
28916
|
};
|
|
29013
28917
|
const subagents = async (frame, _client, ctx) => {
|
|
29014
28918
|
const args = HistorySubagentsArgs.parse(frame);
|
|
29015
|
-
assertGuestPath(ctx,
|
|
28919
|
+
assertGuestPath(ctx, path36.resolve(args.cwd), personaRoot, "history:subagents");
|
|
29016
28920
|
const subs = await history.listSubagents(args);
|
|
29017
28921
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
29018
28922
|
};
|
|
29019
28923
|
const subagentRead = async (frame, _client, ctx) => {
|
|
29020
28924
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
29021
|
-
assertGuestPath(ctx,
|
|
28925
|
+
assertGuestPath(ctx, path36.resolve(args.cwd), personaRoot, "history:subagent-read");
|
|
29022
28926
|
const res = await history.readSubagent(args);
|
|
29023
28927
|
return { response: { type: "history:subagent-read", ...res } };
|
|
29024
28928
|
};
|
|
@@ -29026,7 +28930,7 @@ function buildHistoryHandlers(deps) {
|
|
|
29026
28930
|
const dirs = listRecentDirs(store);
|
|
29027
28931
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
29028
28932
|
const filtered = dirs.filter(
|
|
29029
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
28933
|
+
(d) => isGuestPathAllowed(ctx.grants, path36.resolve(d.cwd), personaRoot, "read")
|
|
29030
28934
|
);
|
|
29031
28935
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
29032
28936
|
}
|
|
@@ -29043,8 +28947,8 @@ function buildHistoryHandlers(deps) {
|
|
|
29043
28947
|
}
|
|
29044
28948
|
|
|
29045
28949
|
// src/handlers/workspace.ts
|
|
29046
|
-
var
|
|
29047
|
-
var
|
|
28950
|
+
var path37 = __toESM(require("path"), 1);
|
|
28951
|
+
var os14 = __toESM(require("os"), 1);
|
|
29048
28952
|
init_protocol();
|
|
29049
28953
|
init_protocol();
|
|
29050
28954
|
function buildEnabledPluginNames(personaManager, personaId) {
|
|
@@ -29083,23 +28987,23 @@ function buildWorkspaceHandlers(deps) {
|
|
|
29083
28987
|
const list = async (frame, _client, ctx) => {
|
|
29084
28988
|
const args = WorkspaceListArgs.parse(frame);
|
|
29085
28989
|
const isGuest = ctx?.principal.kind === "guest";
|
|
29086
|
-
const fallbackCwd = isGuest && personaRoot ? personaRoot :
|
|
29087
|
-
const resolvedCwd =
|
|
29088
|
-
const target = args.path ?
|
|
28990
|
+
const fallbackCwd = isGuest && personaRoot ? personaRoot : os14.homedir();
|
|
28991
|
+
const resolvedCwd = path37.resolve(args.cwd ?? fallbackCwd);
|
|
28992
|
+
const target = args.path ? path37.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
29089
28993
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list");
|
|
29090
28994
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
29091
28995
|
return { response: { type: "workspace:list", ...res } };
|
|
29092
28996
|
};
|
|
29093
28997
|
const read = async (frame, _client, ctx) => {
|
|
29094
28998
|
const args = WorkspaceReadArgs.parse(frame);
|
|
29095
|
-
const target =
|
|
28999
|
+
const target = path37.isAbsolute(args.path) ? path37.resolve(args.path) : path37.resolve(args.cwd, args.path);
|
|
29096
29000
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read");
|
|
29097
29001
|
const res = workspace.read(args);
|
|
29098
29002
|
return { response: { type: "workspace:read", ...res } };
|
|
29099
29003
|
};
|
|
29100
29004
|
const skillsList = async (frame, _client, ctx) => {
|
|
29101
29005
|
const args = SkillsListArgs.parse(frame);
|
|
29102
|
-
const cwdAbs =
|
|
29006
|
+
const cwdAbs = path37.resolve(args.cwd);
|
|
29103
29007
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list");
|
|
29104
29008
|
const list2 = skills.list(args);
|
|
29105
29009
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -29111,7 +29015,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
29111
29015
|
};
|
|
29112
29016
|
const agentsList = async (frame, _client, ctx) => {
|
|
29113
29017
|
const args = AgentsListArgs.parse(frame);
|
|
29114
|
-
const cwdAbs =
|
|
29018
|
+
const cwdAbs = path37.resolve(args.cwd);
|
|
29115
29019
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list");
|
|
29116
29020
|
const list2 = agents.list(args);
|
|
29117
29021
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -29130,20 +29034,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
29130
29034
|
}
|
|
29131
29035
|
|
|
29132
29036
|
// src/handlers/git.ts
|
|
29133
|
-
var
|
|
29037
|
+
var path39 = __toESM(require("path"), 1);
|
|
29134
29038
|
init_protocol();
|
|
29135
29039
|
init_protocol();
|
|
29136
29040
|
|
|
29137
29041
|
// src/workspace/git.ts
|
|
29138
29042
|
var import_node_child_process6 = require("child_process");
|
|
29139
|
-
var
|
|
29140
|
-
var
|
|
29043
|
+
var import_node_fs25 = __toESM(require("fs"), 1);
|
|
29044
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
29141
29045
|
var import_node_util = require("util");
|
|
29142
29046
|
var pexec = (0, import_node_util.promisify)(import_node_child_process6.execFile);
|
|
29143
29047
|
function normalizePath(p2) {
|
|
29144
|
-
const resolved =
|
|
29048
|
+
const resolved = import_node_path27.default.resolve(p2);
|
|
29145
29049
|
try {
|
|
29146
|
-
return
|
|
29050
|
+
return import_node_fs25.default.realpathSync(resolved);
|
|
29147
29051
|
} catch {
|
|
29148
29052
|
return resolved;
|
|
29149
29053
|
}
|
|
@@ -29216,7 +29120,7 @@ async function listGitBranches(cwd) {
|
|
|
29216
29120
|
// src/handlers/git.ts
|
|
29217
29121
|
function assertGuestCwd(ctx, cwd, personaRoot, method) {
|
|
29218
29122
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
29219
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
29123
|
+
if (!isGuestPathAllowed(ctx.grants, path39.resolve(cwd), personaRoot, "read")) {
|
|
29220
29124
|
throw new ClawdError(
|
|
29221
29125
|
ERROR_CODES.UNAUTHORIZED,
|
|
29222
29126
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -29579,7 +29483,7 @@ function buildWhoamiHandler(deps) {
|
|
|
29579
29483
|
}
|
|
29580
29484
|
|
|
29581
29485
|
// src/handlers/meta.ts
|
|
29582
|
-
var
|
|
29486
|
+
var import_node_os14 = __toESM(require("os"), 1);
|
|
29583
29487
|
init_protocol();
|
|
29584
29488
|
|
|
29585
29489
|
// src/version.ts
|
|
@@ -29609,7 +29513,7 @@ function buildReadyFrame(deps, client) {
|
|
|
29609
29513
|
return {
|
|
29610
29514
|
version,
|
|
29611
29515
|
protocolVersion: PROTOCOL_VERSION,
|
|
29612
|
-
hostname:
|
|
29516
|
+
hostname: import_node_os14.default.hostname(),
|
|
29613
29517
|
os: process.platform,
|
|
29614
29518
|
tools,
|
|
29615
29519
|
runningSessions: info.runningSessions,
|
|
@@ -29703,7 +29607,7 @@ function buildPersonaHandlers(deps) {
|
|
|
29703
29607
|
}
|
|
29704
29608
|
|
|
29705
29609
|
// src/handlers/attachment.ts
|
|
29706
|
-
var
|
|
29610
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
29707
29611
|
init_protocol();
|
|
29708
29612
|
init_protocol();
|
|
29709
29613
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -29757,12 +29661,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
29757
29661
|
`session ${args.sessionId} scope unresolved`
|
|
29758
29662
|
);
|
|
29759
29663
|
}
|
|
29760
|
-
const cwdAbs =
|
|
29761
|
-
const candidateAbs =
|
|
29664
|
+
const cwdAbs = import_node_path28.default.resolve(sessionFile.cwd);
|
|
29665
|
+
const candidateAbs = import_node_path28.default.isAbsolute(args.relPath) ? import_node_path28.default.resolve(args.relPath) : import_node_path28.default.resolve(cwdAbs, args.relPath);
|
|
29762
29666
|
assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl");
|
|
29763
29667
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
29764
29668
|
const entry = entries.find((e) => {
|
|
29765
|
-
const storedAbs =
|
|
29669
|
+
const storedAbs = import_node_path28.default.isAbsolute(e.relPath) ? import_node_path28.default.resolve(e.relPath) : import_node_path28.default.resolve(cwdAbs, e.relPath);
|
|
29766
29670
|
return storedAbs === candidateAbs && !e.stale;
|
|
29767
29671
|
});
|
|
29768
29672
|
if (!entry) {
|
|
@@ -29786,7 +29690,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29786
29690
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
29787
29691
|
const f = deps.sessionStore.read(sessionId);
|
|
29788
29692
|
if (!f) return;
|
|
29789
|
-
assertGuestAttachmentPath(ctx,
|
|
29693
|
+
assertGuestAttachmentPath(ctx, import_node_path28.default.resolve(f.cwd), deps.personaRoot, method);
|
|
29790
29694
|
}
|
|
29791
29695
|
const groupAdd = async (frame, _client, ctx) => {
|
|
29792
29696
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -29827,7 +29731,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29827
29731
|
}
|
|
29828
29732
|
const entries = deps.groupFileStore.list(scope, parsed.data.sessionId);
|
|
29829
29733
|
const target = entries.find((e) => e.relPath === parsed.data.relPath);
|
|
29830
|
-
if (target?.from === "owner"
|
|
29734
|
+
if (target?.from === "owner") {
|
|
29831
29735
|
deps.groupFileStore.remove(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
29832
29736
|
} else {
|
|
29833
29737
|
deps.groupFileStore.markStale(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
@@ -29953,6 +29857,11 @@ var METHOD_GRANT_MAP = {
|
|
|
29953
29857
|
"session:pin": CAPABILITY_SCOPED,
|
|
29954
29858
|
"session:reorderPins": ADMIN_ANY,
|
|
29955
29859
|
// 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,
|
|
29956
29865
|
"permission:respond": CAPABILITY_SCOPED,
|
|
29957
29866
|
"session:answerQuestion": CAPABILITY_SCOPED,
|
|
29958
29867
|
"session:cancelQuestion": CAPABILITY_SCOPED,
|
|
@@ -30023,7 +29932,7 @@ function computeGrantForFrame(method, frame) {
|
|
|
30023
29932
|
async function startDaemon(config) {
|
|
30024
29933
|
const logger = createLogger({
|
|
30025
29934
|
level: config.logLevel,
|
|
30026
|
-
file:
|
|
29935
|
+
file: import_node_path29.default.join(config.dataDir, "clawd.log")
|
|
30027
29936
|
});
|
|
30028
29937
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
30029
29938
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
@@ -30110,7 +30019,7 @@ async function startDaemon(config) {
|
|
|
30110
30019
|
const agents = new AgentsScanner();
|
|
30111
30020
|
const history = new ClaudeHistoryReader();
|
|
30112
30021
|
let transport = null;
|
|
30113
|
-
const personaStore = new PersonaStore(
|
|
30022
|
+
const personaStore = new PersonaStore(import_node_path29.default.join(config.dataDir, "personas"));
|
|
30114
30023
|
const defaultsRoot = findDefaultsRoot();
|
|
30115
30024
|
if (defaultsRoot) {
|
|
30116
30025
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -30127,7 +30036,7 @@ async function startDaemon(config) {
|
|
|
30127
30036
|
getAdapter,
|
|
30128
30037
|
historyReader: history,
|
|
30129
30038
|
dataDir: config.dataDir,
|
|
30130
|
-
personaRoot:
|
|
30039
|
+
personaRoot: import_node_path29.default.join(config.dataDir, "personas"),
|
|
30131
30040
|
personaStore,
|
|
30132
30041
|
ownerDisplayName,
|
|
30133
30042
|
ownerPrincipalId,
|
|
@@ -30151,10 +30060,10 @@ async function startDaemon(config) {
|
|
|
30151
30060
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
30152
30061
|
attachmentGroup: {
|
|
30153
30062
|
onFileEdit: (input) => {
|
|
30154
|
-
const absPath =
|
|
30063
|
+
const absPath = import_node_path29.default.isAbsolute(input.relPath) ? input.relPath : import_node_path29.default.join(input.cwd, input.relPath);
|
|
30155
30064
|
let size = 0;
|
|
30156
30065
|
try {
|
|
30157
|
-
size =
|
|
30066
|
+
size = import_node_fs26.default.statSync(absPath).size;
|
|
30158
30067
|
} catch (err) {
|
|
30159
30068
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
30160
30069
|
sessionId: input.sessionId,
|
|
@@ -30180,6 +30089,11 @@ async function startDaemon(config) {
|
|
|
30180
30089
|
}
|
|
30181
30090
|
});
|
|
30182
30091
|
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",
|
|
30183
30097
|
onEvent: ({ sessionId, events }) => {
|
|
30184
30098
|
manager.feedObserverEvents(sessionId, events);
|
|
30185
30099
|
},
|
|
@@ -30267,10 +30181,10 @@ async function startDaemon(config) {
|
|
|
30267
30181
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
30268
30182
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30269
30183
|
// guest path guard:candidate 必须在 personaRoot 子树下
|
|
30270
|
-
personaRoot:
|
|
30184
|
+
personaRoot: import_node_path29.default.join(config.dataDir, "personas")
|
|
30271
30185
|
},
|
|
30272
30186
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
30273
|
-
personaRoot:
|
|
30187
|
+
personaRoot: import_node_path29.default.join(config.dataDir, "personas"),
|
|
30274
30188
|
// capability:list / delete handler 依赖
|
|
30275
30189
|
capabilityManager,
|
|
30276
30190
|
// personal-cap:get 返回的 shareBaseUrl 用此 base URL:
|
|
@@ -30306,19 +30220,7 @@ async function startDaemon(config) {
|
|
|
30306
30220
|
sessionStore: store,
|
|
30307
30221
|
// /files HMAC verify 用 auth.json 的 signSecret 字段(与 attachment.signUrl 同源)。
|
|
30308
30222
|
// --auth-token CLI 模式没 signSecret → 路由返 501,sign URL 功能整体禁用。
|
|
30309
|
-
getSignSecret: () => authFile?.signSecret ?? null
|
|
30310
|
-
// chatinput 附件上传(POST /attachments/upload,spec §6.1)。upload pipeline
|
|
30311
|
-
// 在 session.cwd 下的 .attachments/ 落盘;signSecret 与 /files HMAC 同源;
|
|
30312
|
-
// tunnel 未启时返 503 TUNNEL_NOT_READY 让前端显式提示。
|
|
30313
|
-
attachmentUpload: {
|
|
30314
|
-
groupFileStore,
|
|
30315
|
-
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30316
|
-
getSessionDir: (sid) => {
|
|
30317
|
-
const f = manager.findOwnedSession(sid);
|
|
30318
|
-
return f ? f.cwd : null;
|
|
30319
|
-
},
|
|
30320
|
-
getHttpBaseUrl: () => currentTunnelUrl ? getHttpBaseUrl() : null
|
|
30321
|
-
}
|
|
30223
|
+
getSignSecret: () => authFile?.signSecret ?? null
|
|
30322
30224
|
});
|
|
30323
30225
|
wsServer = new LocalWsServer({
|
|
30324
30226
|
host: config.host,
|
|
@@ -30483,8 +30385,8 @@ async function startDaemon(config) {
|
|
|
30483
30385
|
const lines = [
|
|
30484
30386
|
`Tunnel: ${r.url}`,
|
|
30485
30387
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
30486
|
-
`Frpc config: ${
|
|
30487
|
-
`Frpc log: ${
|
|
30388
|
+
`Frpc config: ${import_node_path29.default.join(config.dataDir, "frpc.toml")}`,
|
|
30389
|
+
`Frpc log: ${import_node_path29.default.join(config.dataDir, "frpc.log")}`
|
|
30488
30390
|
];
|
|
30489
30391
|
const width = Math.max(...lines.map((l) => l.length));
|
|
30490
30392
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -30497,8 +30399,8 @@ ${bar}
|
|
|
30497
30399
|
|
|
30498
30400
|
`);
|
|
30499
30401
|
try {
|
|
30500
|
-
const connectPath =
|
|
30501
|
-
|
|
30402
|
+
const connectPath = import_node_path29.default.join(config.dataDir, "connect.txt");
|
|
30403
|
+
import_node_fs26.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
30502
30404
|
} catch {
|
|
30503
30405
|
}
|
|
30504
30406
|
} catch (err) {
|
|
@@ -30523,29 +30425,8 @@ ${bar}
|
|
|
30523
30425
|
logger.warn("tunnel unavailable, degraded to local mode", { reason: tunnelError });
|
|
30524
30426
|
}
|
|
30525
30427
|
}
|
|
30526
|
-
const tickAttachmentGc = () => {
|
|
30527
|
-
try {
|
|
30528
|
-
runAttachmentGc({
|
|
30529
|
-
dataDir: config.dataDir,
|
|
30530
|
-
groupFileStore,
|
|
30531
|
-
sessionScopes: manager.listOwnedSessionScopes().map(({ scope, sessionId }) => ({
|
|
30532
|
-
scope,
|
|
30533
|
-
sessionId
|
|
30534
|
-
})),
|
|
30535
|
-
// 注入 cwd 让 GC 直接定位 .attachments 目录(与 upload 落盘点一致)
|
|
30536
|
-
getSessionCwd: (sid) => manager.findOwnedSession(sid)?.cwd ?? null,
|
|
30537
|
-
logger
|
|
30538
|
-
});
|
|
30539
|
-
} catch (err) {
|
|
30540
|
-
logger.warn("attachment gc tick failed", { err: err.message });
|
|
30541
|
-
}
|
|
30542
|
-
};
|
|
30543
|
-
setImmediate(tickAttachmentGc);
|
|
30544
|
-
const attachmentGcInterval = setInterval(tickAttachmentGc, 24 * 3600 * 1e3);
|
|
30545
|
-
attachmentGcInterval.unref();
|
|
30546
30428
|
const shutdown = async () => {
|
|
30547
30429
|
logger.info("stopping clawd");
|
|
30548
|
-
clearInterval(attachmentGcInterval);
|
|
30549
30430
|
observer.stopAll();
|
|
30550
30431
|
manager.stopAll();
|
|
30551
30432
|
if (tunnelMgr) {
|
|
@@ -30563,9 +30444,9 @@ ${bar}
|
|
|
30563
30444
|
};
|
|
30564
30445
|
}
|
|
30565
30446
|
function migrateDropPersonsDir(dataDir) {
|
|
30566
|
-
const dir =
|
|
30447
|
+
const dir = import_node_path29.default.join(dataDir, "persons");
|
|
30567
30448
|
try {
|
|
30568
|
-
|
|
30449
|
+
import_node_fs26.default.rmSync(dir, { recursive: true, force: true });
|
|
30569
30450
|
} catch {
|
|
30570
30451
|
}
|
|
30571
30452
|
}
|