@clawos-dev/clawd 0.2.87 → 0.2.89-beta.165.fab5549
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 +338 -597
- 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)
|
|
@@ -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
|
-
|
|
20687
|
+
import_node_fs28 = require("fs");
|
|
20653
20688
|
import_node_os15 = __toESM(require("os"), 1);
|
|
20654
|
-
|
|
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,32 @@ 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 物理落 default 即可。
|
|
22915
|
+
// 远端 creator / origin 字段原值保留(不被本机 ctx 派生覆写)。
|
|
22916
|
+
//
|
|
22917
|
+
// strip 远端 personaId:mirror SessionFile 上的 ownerPersonaId 是朋友 daemon 的 persona
|
|
22918
|
+
// 目录 id,本机不持有对应 persona。留着会让按 ownerPersonaId 反查的本机消费者
|
|
22919
|
+
// (MyPersonasSection 等)把 mirror 误聚到同 id persona 下。写入前清掉。
|
|
22920
|
+
//
|
|
22921
|
+
// 与 manager.create / manager.update 区别:
|
|
22922
|
+
// - 不走 newRunner / attachObserver / cwd 校验(mirror 是纯 metadata 层)
|
|
22923
|
+
// - 不发 session:info 帧(manager.create / .update 通过 reducer/wireCollector 发 push)
|
|
22924
|
+
mirrorUpsert(file) {
|
|
22925
|
+
const { ownerPersonaId: _stripped, ...rest } = file;
|
|
22926
|
+
return this.deps.store.write(rest);
|
|
22927
|
+
}
|
|
22928
|
+
// mirror peer sessions:删本机 mirror SessionFile。**跟 listAllOwned 对称扫所有 scope**
|
|
22929
|
+
// (default + persona/owner + persona/guest)。光删 deps.store 会有 store.delete 返回
|
|
22930
|
+
// false 但 handler 仍 ack `peerSession:remove:ok` 的假象(文件落到 persona scope 时
|
|
22931
|
+
// listAllOwned 还能扫出来 → sidebar 刷新页面 mirror 复活)。deleteOwned 跟 listAllOwned
|
|
22932
|
+
// 一对,对账齐全。idempotent:找不到不 throw。**不广播 session:deleted**。
|
|
22933
|
+
mirrorRemove(sessionId) {
|
|
22934
|
+
this.deleteOwned(sessionId);
|
|
22935
|
+
}
|
|
22887
22936
|
// observer 上报"jsonl 写入了一条 user 行"时调用:在 buffer 里找到与之对应的、
|
|
22888
22937
|
// 还没建立映射的最早一条 synth user_text,记下 synth → real 映射。
|
|
22889
22938
|
// 找不到匹配的 synth(runner 不存在 / buffer 已被 /clear 清空 / 真 uuid 来自纯 history
|
|
@@ -27121,8 +27170,8 @@ function rmdirIfEmpty(p2) {
|
|
|
27121
27170
|
}
|
|
27122
27171
|
|
|
27123
27172
|
// src/transport/http-router.ts
|
|
27124
|
-
var
|
|
27125
|
-
var
|
|
27173
|
+
var import_node_fs15 = __toESM(require("fs"), 1);
|
|
27174
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
27126
27175
|
|
|
27127
27176
|
// src/attachment/mime.ts
|
|
27128
27177
|
var import_node_path16 = __toESM(require("path"), 1);
|
|
@@ -27300,137 +27349,7 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
|
|
|
27300
27349
|
return { ok: true, absPath };
|
|
27301
27350
|
}
|
|
27302
27351
|
|
|
27303
|
-
// src/attachment/upload.ts
|
|
27304
|
-
var import_node_fs15 = __toESM(require("fs"), 1);
|
|
27305
|
-
var import_node_path17 = __toESM(require("path"), 1);
|
|
27306
|
-
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
27307
|
-
var import_promises = require("stream/promises");
|
|
27308
|
-
var UploadError = class extends Error {
|
|
27309
|
-
constructor(code, message) {
|
|
27310
|
-
super(message);
|
|
27311
|
-
this.code = code;
|
|
27312
|
-
this.name = "UploadError";
|
|
27313
|
-
}
|
|
27314
|
-
code;
|
|
27315
|
-
};
|
|
27316
|
-
function assertValidFileName(fileName) {
|
|
27317
|
-
if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path17.default.basename(fileName)) {
|
|
27318
|
-
throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
|
|
27319
|
-
}
|
|
27320
|
-
}
|
|
27321
|
-
var HASH_PREFIX_LEN = 16;
|
|
27322
|
-
async function writeUploadedAttachment(args) {
|
|
27323
|
-
assertValidFileName(args.fileName);
|
|
27324
|
-
const attachmentsRoot = import_node_path17.default.join(args.sessionDir, ".attachments");
|
|
27325
|
-
try {
|
|
27326
|
-
import_node_fs15.default.mkdirSync(attachmentsRoot, { recursive: true });
|
|
27327
|
-
} catch (err) {
|
|
27328
|
-
throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
|
|
27329
|
-
}
|
|
27330
|
-
const hasher = import_node_crypto6.default.createHash("sha256");
|
|
27331
|
-
let actualSize = 0;
|
|
27332
|
-
const tmpPath = import_node_path17.default.join(
|
|
27333
|
-
attachmentsRoot,
|
|
27334
|
-
`.upload-${process.pid}-${Date.now()}-${import_node_crypto6.default.randomBytes(4).toString("hex")}`
|
|
27335
|
-
);
|
|
27336
|
-
try {
|
|
27337
|
-
await (0, import_promises.pipeline)(
|
|
27338
|
-
args.body,
|
|
27339
|
-
async function* (source) {
|
|
27340
|
-
for await (const chunk of source) {
|
|
27341
|
-
const buf = chunk;
|
|
27342
|
-
actualSize += buf.length;
|
|
27343
|
-
hasher.update(buf);
|
|
27344
|
-
yield buf;
|
|
27345
|
-
}
|
|
27346
|
-
},
|
|
27347
|
-
import_node_fs15.default.createWriteStream(tmpPath, { mode: 384 })
|
|
27348
|
-
);
|
|
27349
|
-
} catch (err) {
|
|
27350
|
-
try {
|
|
27351
|
-
import_node_fs15.default.unlinkSync(tmpPath);
|
|
27352
|
-
} catch {
|
|
27353
|
-
}
|
|
27354
|
-
throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
|
|
27355
|
-
}
|
|
27356
|
-
if (actualSize !== args.contentLength) {
|
|
27357
|
-
try {
|
|
27358
|
-
import_node_fs15.default.unlinkSync(tmpPath);
|
|
27359
|
-
} catch {
|
|
27360
|
-
}
|
|
27361
|
-
throw new UploadError(
|
|
27362
|
-
"SIZE_MISMATCH",
|
|
27363
|
-
`expected ${args.contentLength} bytes, got ${actualSize}`
|
|
27364
|
-
);
|
|
27365
|
-
}
|
|
27366
|
-
const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
|
|
27367
|
-
const hashDir = import_node_path17.default.join(attachmentsRoot, attachmentId);
|
|
27368
|
-
let finalFileName;
|
|
27369
|
-
let hashDirExists = false;
|
|
27370
|
-
try {
|
|
27371
|
-
hashDirExists = import_node_fs15.default.statSync(hashDir).isDirectory();
|
|
27372
|
-
} catch {
|
|
27373
|
-
}
|
|
27374
|
-
if (hashDirExists) {
|
|
27375
|
-
const existing = import_node_fs15.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
|
|
27376
|
-
finalFileName = existing[0] ?? args.fileName;
|
|
27377
|
-
try {
|
|
27378
|
-
import_node_fs15.default.unlinkSync(tmpPath);
|
|
27379
|
-
} catch {
|
|
27380
|
-
}
|
|
27381
|
-
} else {
|
|
27382
|
-
try {
|
|
27383
|
-
import_node_fs15.default.mkdirSync(hashDir, { recursive: true });
|
|
27384
|
-
finalFileName = args.fileName;
|
|
27385
|
-
import_node_fs15.default.renameSync(tmpPath, import_node_path17.default.join(hashDir, finalFileName));
|
|
27386
|
-
} catch (err) {
|
|
27387
|
-
try {
|
|
27388
|
-
import_node_fs15.default.unlinkSync(tmpPath);
|
|
27389
|
-
} catch {
|
|
27390
|
-
}
|
|
27391
|
-
throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
|
|
27392
|
-
}
|
|
27393
|
-
}
|
|
27394
|
-
const absPath = import_node_path17.default.join(hashDir, finalFileName);
|
|
27395
|
-
const relPath = import_node_path17.default.relative(args.sessionDir, absPath);
|
|
27396
|
-
args.groupFileStore.upsert(args.scope, args.sessionId, {
|
|
27397
|
-
relPath: absPath,
|
|
27398
|
-
// 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
|
|
27399
|
-
from: "user-upload",
|
|
27400
|
-
label: args.fileName,
|
|
27401
|
-
size: actualSize,
|
|
27402
|
-
mime: args.mimeType
|
|
27403
|
-
});
|
|
27404
|
-
return { attachmentId, absPath, relPath, sizeBytes: actualSize, mimeType: args.mimeType };
|
|
27405
|
-
}
|
|
27406
|
-
|
|
27407
27352
|
// src/transport/http-router.ts
|
|
27408
|
-
var UPLOAD_MIME_ALLOW = /* @__PURE__ */ new Set([
|
|
27409
|
-
"image/png",
|
|
27410
|
-
"image/jpeg",
|
|
27411
|
-
"image/gif",
|
|
27412
|
-
"image/webp",
|
|
27413
|
-
"application/pdf"
|
|
27414
|
-
]);
|
|
27415
|
-
var UPLOAD_MIME_ALLOW_PREFIX = ["text/"];
|
|
27416
|
-
var UPLOAD_MAX_BYTES = 25 * 1024 * 1024;
|
|
27417
|
-
var UPLOAD_CORS_HEADERS = {
|
|
27418
|
-
"Access-Control-Allow-Origin": "*",
|
|
27419
|
-
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
|
27420
|
-
"Access-Control-Allow-Headers": "Authorization, Content-Type",
|
|
27421
|
-
"Access-Control-Max-Age": "86400"
|
|
27422
|
-
};
|
|
27423
|
-
function isUploadMimeAllowed(mime) {
|
|
27424
|
-
if (UPLOAD_MIME_ALLOW.has(mime)) return true;
|
|
27425
|
-
return UPLOAD_MIME_ALLOW_PREFIX.some((p2) => mime.startsWith(p2));
|
|
27426
|
-
}
|
|
27427
|
-
function isValidUploadFileName(fileName) {
|
|
27428
|
-
if (fileName.length === 0) return false;
|
|
27429
|
-
if (fileName === "." || fileName === "..") return false;
|
|
27430
|
-
if (fileName.startsWith(".")) return false;
|
|
27431
|
-
if (fileName.includes("/") || fileName.includes("\\")) return false;
|
|
27432
|
-
return fileName === import_node_path18.default.basename(fileName);
|
|
27433
|
-
}
|
|
27434
27353
|
function createHttpRouter(deps) {
|
|
27435
27354
|
return async (req, res) => {
|
|
27436
27355
|
const url = parseUrl(req.url);
|
|
@@ -27444,14 +27363,9 @@ function createHttpRouter(deps) {
|
|
|
27444
27363
|
});
|
|
27445
27364
|
return true;
|
|
27446
27365
|
}
|
|
27447
|
-
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/")
|
|
27366
|
+
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/")) {
|
|
27448
27367
|
return false;
|
|
27449
27368
|
}
|
|
27450
|
-
if (url.pathname === "/attachments/upload" && req.method === "OPTIONS") {
|
|
27451
|
-
res.writeHead(204, UPLOAD_CORS_HEADERS);
|
|
27452
|
-
res.end();
|
|
27453
|
-
return true;
|
|
27454
|
-
}
|
|
27455
27369
|
if (url.pathname.startsWith("/files/") && req.method === "GET") {
|
|
27456
27370
|
const secret = deps.getSignSecret?.();
|
|
27457
27371
|
if (!secret) {
|
|
@@ -27489,107 +27403,6 @@ function createHttpRouter(deps) {
|
|
|
27489
27403
|
sendJson(res, 401, { code: "UNAUTHORIZED", message: "missing or invalid bearer token" });
|
|
27490
27404
|
return true;
|
|
27491
27405
|
}
|
|
27492
|
-
if (url.pathname === "/attachments/upload" && req.method === "POST") {
|
|
27493
|
-
const sendUploadJson = (status, body) => sendJson(res, status, body, UPLOAD_CORS_HEADERS);
|
|
27494
|
-
if (ctx.role !== "owner") {
|
|
27495
|
-
sendUploadJson(403, { code: "FORBIDDEN", message: "owner only" });
|
|
27496
|
-
return true;
|
|
27497
|
-
}
|
|
27498
|
-
const up = deps.attachmentUpload;
|
|
27499
|
-
if (!up) {
|
|
27500
|
-
sendUploadJson(501, { code: "NOT_IMPLEMENTED", message: "attachment upload not wired" });
|
|
27501
|
-
return true;
|
|
27502
|
-
}
|
|
27503
|
-
const sessionId = url.searchParams.get("sessionId");
|
|
27504
|
-
const fileName = url.searchParams.get("fileName");
|
|
27505
|
-
const mimeType = url.searchParams.get("mimeType");
|
|
27506
|
-
const contentLengthRaw = req.headers["content-length"];
|
|
27507
|
-
if (!sessionId || !fileName || !mimeType || !contentLengthRaw) {
|
|
27508
|
-
sendUploadJson(400, {
|
|
27509
|
-
code: "INVALID_PARAM",
|
|
27510
|
-
message: "missing sessionId/fileName/mimeType/Content-Length"
|
|
27511
|
-
});
|
|
27512
|
-
return true;
|
|
27513
|
-
}
|
|
27514
|
-
if (!isValidUploadFileName(fileName)) {
|
|
27515
|
-
sendUploadJson(400, {
|
|
27516
|
-
code: "INVALID_FILENAME",
|
|
27517
|
-
message: `fileName must be a plain basename (no path separators, no dotfiles): ${fileName}`
|
|
27518
|
-
});
|
|
27519
|
-
return true;
|
|
27520
|
-
}
|
|
27521
|
-
const contentLength = Number.parseInt(contentLengthRaw, 10);
|
|
27522
|
-
if (!Number.isFinite(contentLength) || contentLength < 0) {
|
|
27523
|
-
sendUploadJson(400, { code: "INVALID_PARAM", message: "malformed Content-Length" });
|
|
27524
|
-
return true;
|
|
27525
|
-
}
|
|
27526
|
-
if (contentLength > UPLOAD_MAX_BYTES) {
|
|
27527
|
-
sendUploadJson(413, {
|
|
27528
|
-
code: "FILE_TOO_LARGE",
|
|
27529
|
-
message: `max ${UPLOAD_MAX_BYTES} bytes`
|
|
27530
|
-
});
|
|
27531
|
-
return true;
|
|
27532
|
-
}
|
|
27533
|
-
if (!isUploadMimeAllowed(mimeType)) {
|
|
27534
|
-
sendUploadJson(400, {
|
|
27535
|
-
code: "UNSUPPORTED_MIME",
|
|
27536
|
-
message: `mime ${mimeType} not allowed`
|
|
27537
|
-
});
|
|
27538
|
-
return true;
|
|
27539
|
-
}
|
|
27540
|
-
const sessionDir = up.getSessionDir(sessionId);
|
|
27541
|
-
const scope = up.getSessionScope(sessionId);
|
|
27542
|
-
if (!sessionDir || !scope) {
|
|
27543
|
-
sendUploadJson(404, { code: "NOT_FOUND", message: `session ${sessionId} not found` });
|
|
27544
|
-
return true;
|
|
27545
|
-
}
|
|
27546
|
-
const httpBaseUrl = up.getHttpBaseUrl();
|
|
27547
|
-
if (!httpBaseUrl) {
|
|
27548
|
-
sendUploadJson(503, {
|
|
27549
|
-
code: "TUNNEL_NOT_READY",
|
|
27550
|
-
message: "httpBaseUrl unavailable"
|
|
27551
|
-
});
|
|
27552
|
-
return true;
|
|
27553
|
-
}
|
|
27554
|
-
const secret = deps.getSignSecret?.();
|
|
27555
|
-
if (!secret) {
|
|
27556
|
-
sendUploadJson(501, {
|
|
27557
|
-
code: "NOT_IMPLEMENTED",
|
|
27558
|
-
message: "sign secret unavailable (noAuth?)"
|
|
27559
|
-
});
|
|
27560
|
-
return true;
|
|
27561
|
-
}
|
|
27562
|
-
try {
|
|
27563
|
-
const result = await writeUploadedAttachment({
|
|
27564
|
-
sessionDir,
|
|
27565
|
-
sessionId,
|
|
27566
|
-
scope,
|
|
27567
|
-
fileName,
|
|
27568
|
-
mimeType,
|
|
27569
|
-
contentLength,
|
|
27570
|
-
body: req,
|
|
27571
|
-
groupFileStore: up.groupFileStore
|
|
27572
|
-
});
|
|
27573
|
-
const parts = signUrlParts(secret, result.absPath, null);
|
|
27574
|
-
const fullUrl = buildSignedFileUrl(httpBaseUrl, parts);
|
|
27575
|
-
sendUploadJson(200, {
|
|
27576
|
-
attachmentId: result.attachmentId,
|
|
27577
|
-
url: fullUrl,
|
|
27578
|
-
relPath: result.relPath,
|
|
27579
|
-
sizeBytes: result.sizeBytes,
|
|
27580
|
-
mimeType: result.mimeType
|
|
27581
|
-
});
|
|
27582
|
-
} catch (err) {
|
|
27583
|
-
if (err instanceof UploadError) {
|
|
27584
|
-
const status = err.code === "SIZE_MISMATCH" ? 400 : 500;
|
|
27585
|
-
sendUploadJson(status, { code: err.code, message: err.message });
|
|
27586
|
-
} else {
|
|
27587
|
-
deps.logger?.warn("attachment upload failed", { err: err.message });
|
|
27588
|
-
sendUploadJson(500, { code: "STORAGE_ERROR", message: err.message });
|
|
27589
|
-
}
|
|
27590
|
-
}
|
|
27591
|
-
return true;
|
|
27592
|
-
}
|
|
27593
27406
|
const sessionFilesMatch = url.pathname.match(/^\/session\/([^/]+)\/files$/);
|
|
27594
27407
|
if (sessionFilesMatch && req.method === "GET") {
|
|
27595
27408
|
const sid = sessionFilesMatch[1];
|
|
@@ -27599,7 +27412,7 @@ function createHttpRouter(deps) {
|
|
|
27599
27412
|
return true;
|
|
27600
27413
|
}
|
|
27601
27414
|
let absPath;
|
|
27602
|
-
if (
|
|
27415
|
+
if (import_node_path17.default.isAbsolute(pathParam)) {
|
|
27603
27416
|
absPath = pathParam;
|
|
27604
27417
|
} else if (deps.sessionStore) {
|
|
27605
27418
|
const file = deps.sessionStore.read(sid);
|
|
@@ -27607,7 +27420,7 @@ function createHttpRouter(deps) {
|
|
|
27607
27420
|
sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
|
|
27608
27421
|
return true;
|
|
27609
27422
|
}
|
|
27610
|
-
absPath =
|
|
27423
|
+
absPath = import_node_path17.default.join(file.cwd, pathParam);
|
|
27611
27424
|
} else {
|
|
27612
27425
|
sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
|
|
27613
27426
|
return true;
|
|
@@ -27640,7 +27453,7 @@ function withCtx(ctx, body) {
|
|
|
27640
27453
|
function streamFile(res, absPath, logger) {
|
|
27641
27454
|
let stat;
|
|
27642
27455
|
try {
|
|
27643
|
-
stat =
|
|
27456
|
+
stat = import_node_fs15.default.statSync(absPath);
|
|
27644
27457
|
} catch (err) {
|
|
27645
27458
|
const code = err?.code;
|
|
27646
27459
|
if (code === "ENOENT") {
|
|
@@ -27655,7 +27468,7 @@ function streamFile(res, absPath, logger) {
|
|
|
27655
27468
|
return;
|
|
27656
27469
|
}
|
|
27657
27470
|
const mime = lookupMime(absPath);
|
|
27658
|
-
const basename =
|
|
27471
|
+
const basename = import_node_path17.default.basename(absPath);
|
|
27659
27472
|
res.writeHead(200, {
|
|
27660
27473
|
"Content-Type": mime,
|
|
27661
27474
|
"Content-Length": String(stat.size),
|
|
@@ -27663,7 +27476,7 @@ function streamFile(res, absPath, logger) {
|
|
|
27663
27476
|
// 防止浏览器把任意 mime 当 html 渲染
|
|
27664
27477
|
"X-Content-Type-Options": "nosniff"
|
|
27665
27478
|
});
|
|
27666
|
-
const stream =
|
|
27479
|
+
const stream = import_node_fs15.default.createReadStream(absPath);
|
|
27667
27480
|
stream.on("error", (err) => {
|
|
27668
27481
|
logger?.warn("streamFile read error", { absPath, err: err.message });
|
|
27669
27482
|
res.destroy();
|
|
@@ -27671,78 +27484,10 @@ function streamFile(res, absPath, logger) {
|
|
|
27671
27484
|
stream.pipe(res);
|
|
27672
27485
|
}
|
|
27673
27486
|
|
|
27674
|
-
// src/attachment/gc.ts
|
|
27675
|
-
var import_node_fs17 = __toESM(require("fs"), 1);
|
|
27676
|
-
var import_node_path19 = __toESM(require("path"), 1);
|
|
27677
|
-
var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
|
|
27678
|
-
function runAttachmentGc(args) {
|
|
27679
|
-
const now = (args.now ?? Date.now)();
|
|
27680
|
-
const ttlMs = args.ttlMs ?? DEFAULT_TTL_MS;
|
|
27681
|
-
const liveAbs = /* @__PURE__ */ new Set();
|
|
27682
|
-
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27683
|
-
for (const entry of args.groupFileStore.list(scope, sessionId)) {
|
|
27684
|
-
if (entry.stale) continue;
|
|
27685
|
-
if (import_node_path19.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
|
|
27686
|
-
}
|
|
27687
|
-
}
|
|
27688
|
-
for (const { scope, sessionId } of args.sessionScopes) {
|
|
27689
|
-
const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path19.default.join(
|
|
27690
|
-
args.dataDir,
|
|
27691
|
-
"sessions",
|
|
27692
|
-
...scopeSubPath(scope).map(safeFileName),
|
|
27693
|
-
safeFileName(sessionId)
|
|
27694
|
-
);
|
|
27695
|
-
const attRoot = import_node_path19.default.join(sessionDir, ".attachments");
|
|
27696
|
-
let hashDirs;
|
|
27697
|
-
try {
|
|
27698
|
-
hashDirs = import_node_fs17.default.readdirSync(attRoot);
|
|
27699
|
-
} catch (err) {
|
|
27700
|
-
if (err.code === "ENOENT") continue;
|
|
27701
|
-
args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
|
|
27702
|
-
continue;
|
|
27703
|
-
}
|
|
27704
|
-
for (const hashDir of hashDirs) {
|
|
27705
|
-
const hashDirAbs = import_node_path19.default.join(attRoot, hashDir);
|
|
27706
|
-
let files;
|
|
27707
|
-
try {
|
|
27708
|
-
files = import_node_fs17.default.readdirSync(hashDirAbs);
|
|
27709
|
-
} catch {
|
|
27710
|
-
continue;
|
|
27711
|
-
}
|
|
27712
|
-
for (const name of files) {
|
|
27713
|
-
const file = import_node_path19.default.join(hashDirAbs, name);
|
|
27714
|
-
let stat;
|
|
27715
|
-
try {
|
|
27716
|
-
stat = import_node_fs17.default.statSync(file);
|
|
27717
|
-
} catch {
|
|
27718
|
-
continue;
|
|
27719
|
-
}
|
|
27720
|
-
if (!stat.isFile()) continue;
|
|
27721
|
-
const age = now - stat.mtimeMs;
|
|
27722
|
-
if (age < ttlMs) continue;
|
|
27723
|
-
if (liveAbs.has(file)) continue;
|
|
27724
|
-
try {
|
|
27725
|
-
import_node_fs17.default.unlinkSync(file);
|
|
27726
|
-
} catch (err) {
|
|
27727
|
-
args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
|
|
27728
|
-
}
|
|
27729
|
-
}
|
|
27730
|
-
try {
|
|
27731
|
-
if (import_node_fs17.default.readdirSync(hashDirAbs).length === 0) import_node_fs17.default.rmdirSync(hashDirAbs);
|
|
27732
|
-
} catch {
|
|
27733
|
-
}
|
|
27734
|
-
}
|
|
27735
|
-
try {
|
|
27736
|
-
if (import_node_fs17.default.readdirSync(attRoot).length === 0) import_node_fs17.default.rmdirSync(attRoot);
|
|
27737
|
-
} catch {
|
|
27738
|
-
}
|
|
27739
|
-
}
|
|
27740
|
-
}
|
|
27741
|
-
|
|
27742
27487
|
// src/attachment/group.ts
|
|
27743
|
-
var
|
|
27744
|
-
var
|
|
27745
|
-
var
|
|
27488
|
+
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
27489
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
27490
|
+
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
27746
27491
|
init_protocol();
|
|
27747
27492
|
var GroupFileStore = class {
|
|
27748
27493
|
dataDir;
|
|
@@ -27753,11 +27498,11 @@ var GroupFileStore = class {
|
|
|
27753
27498
|
this.logger = opts.logger;
|
|
27754
27499
|
}
|
|
27755
27500
|
rootForScope(scope) {
|
|
27756
|
-
return
|
|
27501
|
+
return import_node_path18.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
|
|
27757
27502
|
}
|
|
27758
27503
|
/** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
|
|
27759
27504
|
filePath(scope, sessionId) {
|
|
27760
|
-
return
|
|
27505
|
+
return import_node_path18.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
|
|
27761
27506
|
}
|
|
27762
27507
|
cacheKey(scope, sessionId) {
|
|
27763
27508
|
return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
|
|
@@ -27766,7 +27511,7 @@ var GroupFileStore = class {
|
|
|
27766
27511
|
readFile(scope, sessionId) {
|
|
27767
27512
|
const file = this.filePath(scope, sessionId);
|
|
27768
27513
|
try {
|
|
27769
|
-
const raw =
|
|
27514
|
+
const raw = import_node_fs16.default.readFileSync(file, "utf8");
|
|
27770
27515
|
const parsed = JSON.parse(raw);
|
|
27771
27516
|
if (!Array.isArray(parsed)) {
|
|
27772
27517
|
this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
|
|
@@ -27792,10 +27537,10 @@ var GroupFileStore = class {
|
|
|
27792
27537
|
}
|
|
27793
27538
|
writeFile(scope, sessionId, entries) {
|
|
27794
27539
|
const file = this.filePath(scope, sessionId);
|
|
27795
|
-
|
|
27540
|
+
import_node_fs16.default.mkdirSync(import_node_path18.default.dirname(file), { recursive: true });
|
|
27796
27541
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
27797
|
-
|
|
27798
|
-
|
|
27542
|
+
import_node_fs16.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
|
|
27543
|
+
import_node_fs16.default.renameSync(tmp, file);
|
|
27799
27544
|
}
|
|
27800
27545
|
/** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
|
|
27801
27546
|
list(scope, sessionId) {
|
|
@@ -27831,7 +27576,7 @@ var GroupFileStore = class {
|
|
|
27831
27576
|
entries[idx] = next;
|
|
27832
27577
|
} else {
|
|
27833
27578
|
next = {
|
|
27834
|
-
id: `gf-${
|
|
27579
|
+
id: `gf-${import_node_crypto6.default.randomBytes(6).toString("base64url")}`,
|
|
27835
27580
|
relPath: input.relPath,
|
|
27836
27581
|
from: input.from,
|
|
27837
27582
|
label: input.label,
|
|
@@ -27881,10 +27626,10 @@ var GroupFileStore = class {
|
|
|
27881
27626
|
};
|
|
27882
27627
|
|
|
27883
27628
|
// src/discovery/state-file.ts
|
|
27884
|
-
var
|
|
27885
|
-
var
|
|
27629
|
+
var import_node_fs17 = __toESM(require("fs"), 1);
|
|
27630
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
27886
27631
|
function defaultStateFilePath(dataDir) {
|
|
27887
|
-
return
|
|
27632
|
+
return import_node_path19.default.join(dataDir, "state.json");
|
|
27888
27633
|
}
|
|
27889
27634
|
function isPidAlive(pid) {
|
|
27890
27635
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
@@ -27906,7 +27651,7 @@ var StateFileManager = class {
|
|
|
27906
27651
|
}
|
|
27907
27652
|
read() {
|
|
27908
27653
|
try {
|
|
27909
|
-
const raw =
|
|
27654
|
+
const raw = import_node_fs17.default.readFileSync(this.file, "utf8");
|
|
27910
27655
|
const parsed = JSON.parse(raw);
|
|
27911
27656
|
return parsed;
|
|
27912
27657
|
} catch {
|
|
@@ -27920,34 +27665,34 @@ var StateFileManager = class {
|
|
|
27920
27665
|
return { status: "stale", existing };
|
|
27921
27666
|
}
|
|
27922
27667
|
write(state) {
|
|
27923
|
-
|
|
27668
|
+
import_node_fs17.default.mkdirSync(import_node_path19.default.dirname(this.file), { recursive: true });
|
|
27924
27669
|
const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
|
|
27925
|
-
|
|
27926
|
-
|
|
27670
|
+
import_node_fs17.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
|
|
27671
|
+
import_node_fs17.default.renameSync(tmp, this.file);
|
|
27927
27672
|
if (process.platform !== "win32") {
|
|
27928
27673
|
try {
|
|
27929
|
-
|
|
27674
|
+
import_node_fs17.default.chmodSync(this.file, 384);
|
|
27930
27675
|
} catch {
|
|
27931
27676
|
}
|
|
27932
27677
|
}
|
|
27933
27678
|
}
|
|
27934
27679
|
delete() {
|
|
27935
27680
|
try {
|
|
27936
|
-
|
|
27681
|
+
import_node_fs17.default.unlinkSync(this.file);
|
|
27937
27682
|
} catch {
|
|
27938
27683
|
}
|
|
27939
27684
|
}
|
|
27940
27685
|
};
|
|
27941
27686
|
|
|
27942
27687
|
// src/tunnel/tunnel-manager.ts
|
|
27943
|
-
var
|
|
27944
|
-
var
|
|
27945
|
-
var
|
|
27688
|
+
var import_node_fs21 = __toESM(require("fs"), 1);
|
|
27689
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
27690
|
+
var import_node_crypto7 = __toESM(require("crypto"), 1);
|
|
27946
27691
|
var import_node_child_process5 = require("child_process");
|
|
27947
27692
|
|
|
27948
27693
|
// src/tunnel/tunnel-store.ts
|
|
27949
|
-
var
|
|
27950
|
-
var
|
|
27694
|
+
var import_node_fs18 = __toESM(require("fs"), 1);
|
|
27695
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
27951
27696
|
var TunnelStore = class {
|
|
27952
27697
|
constructor(filePath) {
|
|
27953
27698
|
this.filePath = filePath;
|
|
@@ -27955,7 +27700,7 @@ var TunnelStore = class {
|
|
|
27955
27700
|
filePath;
|
|
27956
27701
|
async get() {
|
|
27957
27702
|
try {
|
|
27958
|
-
const raw = await
|
|
27703
|
+
const raw = await import_node_fs18.default.promises.readFile(this.filePath, "utf8");
|
|
27959
27704
|
const obj = JSON.parse(raw);
|
|
27960
27705
|
if (!isPersistedTunnel(obj)) return null;
|
|
27961
27706
|
return obj;
|
|
@@ -27966,22 +27711,22 @@ var TunnelStore = class {
|
|
|
27966
27711
|
}
|
|
27967
27712
|
}
|
|
27968
27713
|
async set(v2) {
|
|
27969
|
-
const dir =
|
|
27970
|
-
await
|
|
27714
|
+
const dir = import_node_path20.default.dirname(this.filePath);
|
|
27715
|
+
await import_node_fs18.default.promises.mkdir(dir, { recursive: true });
|
|
27971
27716
|
const data = JSON.stringify(v2, null, 2);
|
|
27972
27717
|
const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
27973
|
-
await
|
|
27718
|
+
await import_node_fs18.default.promises.writeFile(tmp, data, { mode: 384 });
|
|
27974
27719
|
if (process.platform !== "win32") {
|
|
27975
27720
|
try {
|
|
27976
|
-
await
|
|
27721
|
+
await import_node_fs18.default.promises.chmod(tmp, 384);
|
|
27977
27722
|
} catch {
|
|
27978
27723
|
}
|
|
27979
27724
|
}
|
|
27980
|
-
await
|
|
27725
|
+
await import_node_fs18.default.promises.rename(tmp, this.filePath);
|
|
27981
27726
|
}
|
|
27982
27727
|
async clear() {
|
|
27983
27728
|
try {
|
|
27984
|
-
await
|
|
27729
|
+
await import_node_fs18.default.promises.unlink(this.filePath);
|
|
27985
27730
|
} catch (err) {
|
|
27986
27731
|
const code = err?.code;
|
|
27987
27732
|
if (code !== "ENOENT") throw err;
|
|
@@ -28076,12 +27821,12 @@ function escape(v2) {
|
|
|
28076
27821
|
}
|
|
28077
27822
|
|
|
28078
27823
|
// src/tunnel/frpc-binary.ts
|
|
28079
|
-
var
|
|
27824
|
+
var import_node_fs19 = __toESM(require("fs"), 1);
|
|
28080
27825
|
var import_node_os10 = __toESM(require("os"), 1);
|
|
28081
|
-
var
|
|
27826
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
28082
27827
|
var import_node_child_process3 = require("child_process");
|
|
28083
27828
|
var import_node_stream2 = require("stream");
|
|
28084
|
-
var
|
|
27829
|
+
var import_promises = require("stream/promises");
|
|
28085
27830
|
var FRPC_VERSION = "0.68.0";
|
|
28086
27831
|
var UnsupportedPlatformError = class extends Error {
|
|
28087
27832
|
constructor(platform, arch) {
|
|
@@ -28110,20 +27855,20 @@ function frpcDownloadUrl(version2, p2) {
|
|
|
28110
27855
|
}
|
|
28111
27856
|
async function ensureFrpcBinary(opts) {
|
|
28112
27857
|
if (opts.override) {
|
|
28113
|
-
if (!
|
|
27858
|
+
if (!import_node_fs19.default.existsSync(opts.override)) {
|
|
28114
27859
|
throw new Error(`frpc binary not found at override path: ${opts.override}`);
|
|
28115
27860
|
}
|
|
28116
27861
|
return opts.override;
|
|
28117
27862
|
}
|
|
28118
27863
|
const version2 = opts.version ?? FRPC_VERSION;
|
|
28119
27864
|
const platform = opts.platform ?? detectPlatform();
|
|
28120
|
-
const binDir =
|
|
28121
|
-
|
|
27865
|
+
const binDir = import_node_path21.default.join(opts.dataDir, "bin");
|
|
27866
|
+
import_node_fs19.default.mkdirSync(binDir, { recursive: true });
|
|
28122
27867
|
cleanupStaleArtifacts(binDir);
|
|
28123
|
-
const stableBin =
|
|
28124
|
-
if (
|
|
27868
|
+
const stableBin = import_node_path21.default.join(binDir, "frpc");
|
|
27869
|
+
if (import_node_fs19.default.existsSync(stableBin)) return stableBin;
|
|
28125
27870
|
const partialBin = `${stableBin}.partial`;
|
|
28126
|
-
const tarballPath =
|
|
27871
|
+
const tarballPath = import_node_path21.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
|
|
28127
27872
|
try {
|
|
28128
27873
|
const url = frpcDownloadUrl(version2, platform);
|
|
28129
27874
|
await downloadToFile(url, tarballPath, opts.fetchImpl);
|
|
@@ -28132,8 +27877,8 @@ async function ensureFrpcBinary(opts) {
|
|
|
28132
27877
|
} else {
|
|
28133
27878
|
await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
|
|
28134
27879
|
}
|
|
28135
|
-
|
|
28136
|
-
|
|
27880
|
+
import_node_fs19.default.chmodSync(partialBin, 493);
|
|
27881
|
+
import_node_fs19.default.renameSync(partialBin, stableBin);
|
|
28137
27882
|
} finally {
|
|
28138
27883
|
safeUnlink(tarballPath);
|
|
28139
27884
|
safeUnlink(partialBin);
|
|
@@ -28143,15 +27888,15 @@ async function ensureFrpcBinary(opts) {
|
|
|
28143
27888
|
function cleanupStaleArtifacts(binDir) {
|
|
28144
27889
|
let entries;
|
|
28145
27890
|
try {
|
|
28146
|
-
entries =
|
|
27891
|
+
entries = import_node_fs19.default.readdirSync(binDir);
|
|
28147
27892
|
} catch {
|
|
28148
27893
|
return;
|
|
28149
27894
|
}
|
|
28150
27895
|
for (const name of entries) {
|
|
28151
27896
|
if (name.endsWith(".partial") || name.startsWith("extract-")) {
|
|
28152
|
-
const full =
|
|
27897
|
+
const full = import_node_path21.default.join(binDir, name);
|
|
28153
27898
|
try {
|
|
28154
|
-
|
|
27899
|
+
import_node_fs19.default.rmSync(full, { recursive: true, force: true });
|
|
28155
27900
|
} catch {
|
|
28156
27901
|
}
|
|
28157
27902
|
}
|
|
@@ -28159,7 +27904,7 @@ function cleanupStaleArtifacts(binDir) {
|
|
|
28159
27904
|
}
|
|
28160
27905
|
function safeUnlink(p2) {
|
|
28161
27906
|
try {
|
|
28162
|
-
|
|
27907
|
+
import_node_fs19.default.unlinkSync(p2);
|
|
28163
27908
|
} catch {
|
|
28164
27909
|
}
|
|
28165
27910
|
}
|
|
@@ -28170,13 +27915,13 @@ async function downloadToFile(url, dest, fetchImpl) {
|
|
|
28170
27915
|
if (!res.ok || !res.body) {
|
|
28171
27916
|
throw new Error(`download failed: ${res.status} ${res.statusText}`);
|
|
28172
27917
|
}
|
|
28173
|
-
const out =
|
|
27918
|
+
const out = import_node_fs19.default.createWriteStream(dest);
|
|
28174
27919
|
const nodeStream = import_node_stream2.Readable.fromWeb(res.body);
|
|
28175
|
-
await (0,
|
|
27920
|
+
await (0, import_promises.pipeline)(nodeStream, out);
|
|
28176
27921
|
}
|
|
28177
27922
|
async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
|
|
28178
|
-
const work =
|
|
28179
|
-
|
|
27923
|
+
const work = import_node_path21.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
|
|
27924
|
+
import_node_fs19.default.mkdirSync(work, { recursive: true });
|
|
28180
27925
|
try {
|
|
28181
27926
|
await new Promise((resolve6, reject) => {
|
|
28182
27927
|
const proc = (0, import_node_child_process3.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
|
|
@@ -28184,32 +27929,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
|
|
|
28184
27929
|
proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
|
|
28185
27930
|
});
|
|
28186
27931
|
const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
|
|
28187
|
-
const src =
|
|
28188
|
-
if (!
|
|
27932
|
+
const src = import_node_path21.default.join(work, dirName, "frpc");
|
|
27933
|
+
if (!import_node_fs19.default.existsSync(src)) {
|
|
28189
27934
|
throw new Error(`frpc not found inside tarball at ${src}`);
|
|
28190
27935
|
}
|
|
28191
|
-
|
|
27936
|
+
import_node_fs19.default.copyFileSync(src, destBin);
|
|
28192
27937
|
} finally {
|
|
28193
|
-
|
|
27938
|
+
import_node_fs19.default.rmSync(work, { recursive: true, force: true });
|
|
28194
27939
|
}
|
|
28195
27940
|
}
|
|
28196
27941
|
|
|
28197
27942
|
// src/tunnel/frpc-process.ts
|
|
28198
|
-
var
|
|
28199
|
-
var
|
|
27943
|
+
var import_node_fs20 = __toESM(require("fs"), 1);
|
|
27944
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
28200
27945
|
var import_node_child_process4 = require("child_process");
|
|
28201
27946
|
function frpcPidFilePath(dataDir) {
|
|
28202
|
-
return
|
|
27947
|
+
return import_node_path22.default.join(dataDir, "frpc.pid");
|
|
28203
27948
|
}
|
|
28204
27949
|
function writeFrpcPid(dataDir, pid) {
|
|
28205
27950
|
try {
|
|
28206
|
-
|
|
27951
|
+
import_node_fs20.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
|
|
28207
27952
|
} catch {
|
|
28208
27953
|
}
|
|
28209
27954
|
}
|
|
28210
27955
|
function clearFrpcPid(dataDir) {
|
|
28211
27956
|
try {
|
|
28212
|
-
|
|
27957
|
+
import_node_fs20.default.unlinkSync(frpcPidFilePath(dataDir));
|
|
28213
27958
|
} catch {
|
|
28214
27959
|
}
|
|
28215
27960
|
}
|
|
@@ -28225,7 +27970,7 @@ function defaultIsPidAlive(pid) {
|
|
|
28225
27970
|
}
|
|
28226
27971
|
function defaultReadPidFile(file) {
|
|
28227
27972
|
try {
|
|
28228
|
-
return
|
|
27973
|
+
return import_node_fs20.default.readFileSync(file, "utf8");
|
|
28229
27974
|
} catch {
|
|
28230
27975
|
return null;
|
|
28231
27976
|
}
|
|
@@ -28241,7 +27986,7 @@ function defaultSleep(ms) {
|
|
|
28241
27986
|
}
|
|
28242
27987
|
async function killStaleFrpc(deps) {
|
|
28243
27988
|
const pidFile = frpcPidFilePath(deps.dataDir);
|
|
28244
|
-
const tomlPath =
|
|
27989
|
+
const tomlPath = import_node_path22.default.join(deps.dataDir, "frpc.toml");
|
|
28245
27990
|
const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
|
|
28246
27991
|
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
|
|
28247
27992
|
const killPid = deps.killPidImpl ?? defaultKillPid;
|
|
@@ -28265,7 +28010,7 @@ async function killStaleFrpc(deps) {
|
|
|
28265
28010
|
}
|
|
28266
28011
|
if (victims.size === 0) {
|
|
28267
28012
|
try {
|
|
28268
|
-
|
|
28013
|
+
import_node_fs20.default.unlinkSync(pidFile);
|
|
28269
28014
|
} catch {
|
|
28270
28015
|
}
|
|
28271
28016
|
return;
|
|
@@ -28276,7 +28021,7 @@ async function killStaleFrpc(deps) {
|
|
|
28276
28021
|
}
|
|
28277
28022
|
await sleep(deps.reapWaitMs ?? 300);
|
|
28278
28023
|
try {
|
|
28279
|
-
|
|
28024
|
+
import_node_fs20.default.unlinkSync(pidFile);
|
|
28280
28025
|
} catch {
|
|
28281
28026
|
}
|
|
28282
28027
|
}
|
|
@@ -28313,7 +28058,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
28313
28058
|
var TunnelManager = class {
|
|
28314
28059
|
constructor(deps) {
|
|
28315
28060
|
this.deps = deps;
|
|
28316
|
-
this.store = deps.store ?? new TunnelStore(
|
|
28061
|
+
this.store = deps.store ?? new TunnelStore(import_node_path23.default.join(deps.dataDir, "tunnel.json"));
|
|
28317
28062
|
this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
|
|
28318
28063
|
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
28319
28064
|
}
|
|
@@ -28440,8 +28185,8 @@ var TunnelManager = class {
|
|
|
28440
28185
|
dataDir: this.deps.dataDir,
|
|
28441
28186
|
override: this.deps.frpcBinaryOverride ?? void 0
|
|
28442
28187
|
});
|
|
28443
|
-
const tomlPath =
|
|
28444
|
-
const proxyName = `clawd-${t.subdomain}-${localPort}-${
|
|
28188
|
+
const tomlPath = import_node_path23.default.join(this.deps.dataDir, "frpc.toml");
|
|
28189
|
+
const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto7.default.randomBytes(3).toString("hex")}`;
|
|
28445
28190
|
const toml = buildFrpcToml({
|
|
28446
28191
|
serverAddr: t.frpsHost,
|
|
28447
28192
|
serverPort: t.frpsPort,
|
|
@@ -28451,12 +28196,12 @@ var TunnelManager = class {
|
|
|
28451
28196
|
localPort,
|
|
28452
28197
|
logLevel: "info"
|
|
28453
28198
|
});
|
|
28454
|
-
await
|
|
28199
|
+
await import_node_fs21.default.promises.writeFile(tomlPath, toml, { mode: 384 });
|
|
28455
28200
|
const proc = (this.deps.spawnImpl ?? import_node_child_process5.spawn)(frpcBin, ["-c", tomlPath], {
|
|
28456
28201
|
stdio: ["ignore", "pipe", "pipe"]
|
|
28457
28202
|
});
|
|
28458
|
-
const logFilePath =
|
|
28459
|
-
const logStream =
|
|
28203
|
+
const logFilePath = import_node_path23.default.join(this.deps.dataDir, "frpc.log");
|
|
28204
|
+
const logStream = import_node_fs21.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
|
|
28460
28205
|
logStream.on("error", () => {
|
|
28461
28206
|
});
|
|
28462
28207
|
const tee = (chunk) => {
|
|
@@ -28539,22 +28284,22 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
28539
28284
|
|
|
28540
28285
|
// src/tunnel/device-key.ts
|
|
28541
28286
|
var import_node_os11 = __toESM(require("os"), 1);
|
|
28542
|
-
var
|
|
28287
|
+
var import_node_crypto8 = __toESM(require("crypto"), 1);
|
|
28543
28288
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
28544
28289
|
function deriveStableDeviceKey(opts = {}) {
|
|
28545
28290
|
const hostname = opts.hostname ?? import_node_os11.default.hostname();
|
|
28546
28291
|
const uid = opts.uid ?? (typeof import_node_os11.default.userInfo === "function" ? import_node_os11.default.userInfo().uid : 0);
|
|
28547
28292
|
const input = `${hostname}::${uid}`;
|
|
28548
|
-
return
|
|
28293
|
+
return import_node_crypto8.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
28549
28294
|
}
|
|
28550
28295
|
|
|
28551
28296
|
// src/auth-store.ts
|
|
28552
|
-
var
|
|
28553
|
-
var
|
|
28554
|
-
var
|
|
28297
|
+
var import_node_fs22 = __toESM(require("fs"), 1);
|
|
28298
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
28299
|
+
var import_node_crypto9 = __toESM(require("crypto"), 1);
|
|
28555
28300
|
var AUTH_FILE_NAME = "auth.json";
|
|
28556
28301
|
function authFilePath(dataDir) {
|
|
28557
|
-
return
|
|
28302
|
+
return import_node_path24.default.join(dataDir, AUTH_FILE_NAME);
|
|
28558
28303
|
}
|
|
28559
28304
|
function loadOrCreateAuthFile(opts) {
|
|
28560
28305
|
const file = authFilePath(opts.dataDir);
|
|
@@ -28579,14 +28324,14 @@ function loadOrCreateAuthFile(opts) {
|
|
|
28579
28324
|
return next;
|
|
28580
28325
|
}
|
|
28581
28326
|
function defaultGenerateToken2() {
|
|
28582
|
-
return
|
|
28327
|
+
return import_node_crypto9.default.randomBytes(32).toString("base64url");
|
|
28583
28328
|
}
|
|
28584
28329
|
function defaultGenerateOwnerPrincipalId() {
|
|
28585
|
-
return `owner-${
|
|
28330
|
+
return `owner-${import_node_crypto9.default.randomUUID()}`;
|
|
28586
28331
|
}
|
|
28587
28332
|
function readAuthFile(file) {
|
|
28588
28333
|
try {
|
|
28589
|
-
const raw =
|
|
28334
|
+
const raw = import_node_fs22.default.readFileSync(file, "utf8");
|
|
28590
28335
|
const parsed = JSON.parse(raw);
|
|
28591
28336
|
if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
|
|
28592
28337
|
return null;
|
|
@@ -28604,25 +28349,25 @@ function readAuthFile(file) {
|
|
|
28604
28349
|
}
|
|
28605
28350
|
}
|
|
28606
28351
|
function writeAuthFile(file, content) {
|
|
28607
|
-
|
|
28608
|
-
|
|
28352
|
+
import_node_fs22.default.mkdirSync(import_node_path24.default.dirname(file), { recursive: true });
|
|
28353
|
+
import_node_fs22.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
28609
28354
|
try {
|
|
28610
|
-
|
|
28355
|
+
import_node_fs22.default.chmodSync(file, 384);
|
|
28611
28356
|
} catch {
|
|
28612
28357
|
}
|
|
28613
28358
|
}
|
|
28614
28359
|
|
|
28615
28360
|
// src/owner-profile.ts
|
|
28616
|
-
var
|
|
28361
|
+
var import_node_fs23 = __toESM(require("fs"), 1);
|
|
28617
28362
|
var import_node_os12 = __toESM(require("os"), 1);
|
|
28618
|
-
var
|
|
28363
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
28619
28364
|
var PROFILE_FILENAME = "profile.json";
|
|
28620
28365
|
function loadOwnerDisplayName(dataDir) {
|
|
28621
28366
|
const fallback = import_node_os12.default.userInfo().username;
|
|
28622
|
-
const profilePath =
|
|
28367
|
+
const profilePath = import_node_path25.default.join(dataDir, PROFILE_FILENAME);
|
|
28623
28368
|
let raw;
|
|
28624
28369
|
try {
|
|
28625
|
-
raw =
|
|
28370
|
+
raw = import_node_fs23.default.readFileSync(profilePath, "utf8");
|
|
28626
28371
|
} catch {
|
|
28627
28372
|
return fallback;
|
|
28628
28373
|
}
|
|
@@ -28651,12 +28396,12 @@ init_protocol();
|
|
|
28651
28396
|
init_protocol();
|
|
28652
28397
|
|
|
28653
28398
|
// src/session/fork.ts
|
|
28654
|
-
var
|
|
28399
|
+
var import_node_fs24 = __toESM(require("fs"), 1);
|
|
28655
28400
|
var import_node_os13 = __toESM(require("os"), 1);
|
|
28656
|
-
var
|
|
28401
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
28657
28402
|
init_claude_history();
|
|
28658
28403
|
function readJsonlEntries(file) {
|
|
28659
|
-
const raw =
|
|
28404
|
+
const raw = import_node_fs24.default.readFileSync(file, "utf8");
|
|
28660
28405
|
const out = [];
|
|
28661
28406
|
for (const line of raw.split("\n")) {
|
|
28662
28407
|
const t = line.trim();
|
|
@@ -28669,10 +28414,10 @@ function readJsonlEntries(file) {
|
|
|
28669
28414
|
return out;
|
|
28670
28415
|
}
|
|
28671
28416
|
function forkSession(input) {
|
|
28672
|
-
const baseDir = input.baseDir ??
|
|
28673
|
-
const projectDir =
|
|
28674
|
-
const sourceFile =
|
|
28675
|
-
if (!
|
|
28417
|
+
const baseDir = input.baseDir ?? import_node_path26.default.join(import_node_os13.default.homedir(), ".claude");
|
|
28418
|
+
const projectDir = import_node_path26.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
28419
|
+
const sourceFile = import_node_path26.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
28420
|
+
if (!import_node_fs24.default.existsSync(sourceFile)) {
|
|
28676
28421
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
28677
28422
|
}
|
|
28678
28423
|
const entries = readJsonlEntries(sourceFile);
|
|
@@ -28702,9 +28447,9 @@ function forkSession(input) {
|
|
|
28702
28447
|
}
|
|
28703
28448
|
forkedLines.push(JSON.stringify(forked));
|
|
28704
28449
|
}
|
|
28705
|
-
const forkedFilePath =
|
|
28706
|
-
|
|
28707
|
-
|
|
28450
|
+
const forkedFilePath = import_node_path26.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
28451
|
+
import_node_fs24.default.mkdirSync(projectDir, { recursive: true });
|
|
28452
|
+
import_node_fs24.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
28708
28453
|
return { forkedToolSessionId, forkedFilePath };
|
|
28709
28454
|
}
|
|
28710
28455
|
|
|
@@ -28992,6 +28737,28 @@ function buildSessionHandlers(deps) {
|
|
|
28992
28737
|
}
|
|
28993
28738
|
};
|
|
28994
28739
|
};
|
|
28740
|
+
const peerSessionUpsert = async (frame, _client, ctx) => {
|
|
28741
|
+
if (ctx && ctx.principal.kind !== "owner") {
|
|
28742
|
+
throw new ClawdError(
|
|
28743
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
28744
|
+
`principal ${ctx.principal.kind}:${ctx.principal.id} cannot peerSession:upsert`
|
|
28745
|
+
);
|
|
28746
|
+
}
|
|
28747
|
+
const file = PeerSessionUpsertArgs.parse(frame);
|
|
28748
|
+
manager.mirrorUpsert(file);
|
|
28749
|
+
return { response: { type: "peerSession:upsert:ok" }, broadcast: [] };
|
|
28750
|
+
};
|
|
28751
|
+
const peerSessionRemove = async (frame, _client, ctx) => {
|
|
28752
|
+
if (ctx && ctx.principal.kind !== "owner") {
|
|
28753
|
+
throw new ClawdError(
|
|
28754
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
28755
|
+
`principal ${ctx.principal.kind}:${ctx.principal.id} cannot peerSession:remove`
|
|
28756
|
+
);
|
|
28757
|
+
}
|
|
28758
|
+
const args = PeerSessionRemoveArgs.parse(frame);
|
|
28759
|
+
manager.mirrorRemove(args.sessionId);
|
|
28760
|
+
return { response: { type: "peerSession:remove:ok" }, broadcast: [] };
|
|
28761
|
+
};
|
|
28995
28762
|
return {
|
|
28996
28763
|
"session:create": create,
|
|
28997
28764
|
"session:list": list,
|
|
@@ -29017,7 +28784,9 @@ function buildSessionHandlers(deps) {
|
|
|
29017
28784
|
"session:cancelQuestion": cancelQuestion,
|
|
29018
28785
|
"session:pty:input": ptyInput,
|
|
29019
28786
|
"session:pty:resize": ptyResize,
|
|
29020
|
-
"session:pty:snapshot": ptySnapshot
|
|
28787
|
+
"session:pty:snapshot": ptySnapshot,
|
|
28788
|
+
"peerSession:upsert": peerSessionUpsert,
|
|
28789
|
+
"peerSession:remove": peerSessionRemove
|
|
29021
28790
|
};
|
|
29022
28791
|
}
|
|
29023
28792
|
|
|
@@ -29036,7 +28805,7 @@ function buildPermissionHandlers(deps) {
|
|
|
29036
28805
|
}
|
|
29037
28806
|
|
|
29038
28807
|
// src/handlers/history.ts
|
|
29039
|
-
var
|
|
28808
|
+
var path36 = __toESM(require("path"), 1);
|
|
29040
28809
|
init_protocol();
|
|
29041
28810
|
|
|
29042
28811
|
// src/session/recent-dirs.ts
|
|
@@ -29054,7 +28823,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
29054
28823
|
}
|
|
29055
28824
|
|
|
29056
28825
|
// src/permission/persona-paths.ts
|
|
29057
|
-
var
|
|
28826
|
+
var path35 = __toESM(require("path"), 1);
|
|
29058
28827
|
function getAllowedPersonaIds(grants, action) {
|
|
29059
28828
|
const ids = /* @__PURE__ */ new Set();
|
|
29060
28829
|
for (const g2 of grants) {
|
|
@@ -29067,26 +28836,26 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
29067
28836
|
return ids;
|
|
29068
28837
|
}
|
|
29069
28838
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read") {
|
|
29070
|
-
const root =
|
|
29071
|
-
const target =
|
|
29072
|
-
const sep3 = root.endsWith(
|
|
28839
|
+
const root = path35.resolve(personaRoot);
|
|
28840
|
+
const target = path35.resolve(absPath);
|
|
28841
|
+
const sep3 = root.endsWith(path35.sep) ? "" : path35.sep;
|
|
29073
28842
|
if (!target.startsWith(root + sep3)) return false;
|
|
29074
|
-
const rel =
|
|
28843
|
+
const rel = path35.relative(root, target);
|
|
29075
28844
|
if (!rel || rel.startsWith("..")) return false;
|
|
29076
|
-
const personaId = rel.split(
|
|
28845
|
+
const personaId = rel.split(path35.sep)[0];
|
|
29077
28846
|
if (!personaId) return false;
|
|
29078
28847
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
29079
28848
|
if (allowed === "*") return true;
|
|
29080
28849
|
return allowed.has(personaId);
|
|
29081
28850
|
}
|
|
29082
28851
|
function personaIdFromPath(absPath, personaRoot) {
|
|
29083
|
-
const root =
|
|
29084
|
-
const target =
|
|
29085
|
-
const sep3 = root.endsWith(
|
|
28852
|
+
const root = path35.resolve(personaRoot);
|
|
28853
|
+
const target = path35.resolve(absPath);
|
|
28854
|
+
const sep3 = root.endsWith(path35.sep) ? "" : path35.sep;
|
|
29086
28855
|
if (!target.startsWith(root + sep3)) return null;
|
|
29087
|
-
const rel =
|
|
28856
|
+
const rel = path35.relative(root, target);
|
|
29088
28857
|
if (!rel || rel.startsWith("..")) return null;
|
|
29089
|
-
const id = rel.split(
|
|
28858
|
+
const id = rel.split(path35.sep)[0];
|
|
29090
28859
|
return id || null;
|
|
29091
28860
|
}
|
|
29092
28861
|
|
|
@@ -29111,7 +28880,7 @@ function buildHistoryHandlers(deps) {
|
|
|
29111
28880
|
if (!pid) return false;
|
|
29112
28881
|
return isGuestPathAllowed(
|
|
29113
28882
|
ctx.grants,
|
|
29114
|
-
|
|
28883
|
+
path36.join(personaRoot, pid),
|
|
29115
28884
|
personaRoot,
|
|
29116
28885
|
"read"
|
|
29117
28886
|
);
|
|
@@ -29122,7 +28891,7 @@ function buildHistoryHandlers(deps) {
|
|
|
29122
28891
|
};
|
|
29123
28892
|
const list = async (frame, _client, ctx) => {
|
|
29124
28893
|
const args = HistoryListArgs.parse(frame);
|
|
29125
|
-
assertGuestPath(ctx,
|
|
28894
|
+
assertGuestPath(ctx, path36.resolve(args.projectPath), personaRoot, "history:list");
|
|
29126
28895
|
const sessions = await history.listSessions(args);
|
|
29127
28896
|
return { response: { type: "history:list", sessions } };
|
|
29128
28897
|
};
|
|
@@ -29154,13 +28923,13 @@ function buildHistoryHandlers(deps) {
|
|
|
29154
28923
|
};
|
|
29155
28924
|
const subagents = async (frame, _client, ctx) => {
|
|
29156
28925
|
const args = HistorySubagentsArgs.parse(frame);
|
|
29157
|
-
assertGuestPath(ctx,
|
|
28926
|
+
assertGuestPath(ctx, path36.resolve(args.cwd), personaRoot, "history:subagents");
|
|
29158
28927
|
const subs = await history.listSubagents(args);
|
|
29159
28928
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
29160
28929
|
};
|
|
29161
28930
|
const subagentRead = async (frame, _client, ctx) => {
|
|
29162
28931
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
29163
|
-
assertGuestPath(ctx,
|
|
28932
|
+
assertGuestPath(ctx, path36.resolve(args.cwd), personaRoot, "history:subagent-read");
|
|
29164
28933
|
const res = await history.readSubagent(args);
|
|
29165
28934
|
return { response: { type: "history:subagent-read", ...res } };
|
|
29166
28935
|
};
|
|
@@ -29168,7 +28937,7 @@ function buildHistoryHandlers(deps) {
|
|
|
29168
28937
|
const dirs = listRecentDirs(store);
|
|
29169
28938
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
29170
28939
|
const filtered = dirs.filter(
|
|
29171
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
28940
|
+
(d) => isGuestPathAllowed(ctx.grants, path36.resolve(d.cwd), personaRoot, "read")
|
|
29172
28941
|
);
|
|
29173
28942
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
29174
28943
|
}
|
|
@@ -29185,7 +28954,7 @@ function buildHistoryHandlers(deps) {
|
|
|
29185
28954
|
}
|
|
29186
28955
|
|
|
29187
28956
|
// src/handlers/workspace.ts
|
|
29188
|
-
var
|
|
28957
|
+
var path37 = __toESM(require("path"), 1);
|
|
29189
28958
|
var os14 = __toESM(require("os"), 1);
|
|
29190
28959
|
init_protocol();
|
|
29191
28960
|
init_protocol();
|
|
@@ -29226,22 +28995,22 @@ function buildWorkspaceHandlers(deps) {
|
|
|
29226
28995
|
const args = WorkspaceListArgs.parse(frame);
|
|
29227
28996
|
const isGuest = ctx?.principal.kind === "guest";
|
|
29228
28997
|
const fallbackCwd = isGuest && personaRoot ? personaRoot : os14.homedir();
|
|
29229
|
-
const resolvedCwd =
|
|
29230
|
-
const target = args.path ?
|
|
28998
|
+
const resolvedCwd = path37.resolve(args.cwd ?? fallbackCwd);
|
|
28999
|
+
const target = args.path ? path37.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
29231
29000
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list");
|
|
29232
29001
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
29233
29002
|
return { response: { type: "workspace:list", ...res } };
|
|
29234
29003
|
};
|
|
29235
29004
|
const read = async (frame, _client, ctx) => {
|
|
29236
29005
|
const args = WorkspaceReadArgs.parse(frame);
|
|
29237
|
-
const target =
|
|
29006
|
+
const target = path37.isAbsolute(args.path) ? path37.resolve(args.path) : path37.resolve(args.cwd, args.path);
|
|
29238
29007
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read");
|
|
29239
29008
|
const res = workspace.read(args);
|
|
29240
29009
|
return { response: { type: "workspace:read", ...res } };
|
|
29241
29010
|
};
|
|
29242
29011
|
const skillsList = async (frame, _client, ctx) => {
|
|
29243
29012
|
const args = SkillsListArgs.parse(frame);
|
|
29244
|
-
const cwdAbs =
|
|
29013
|
+
const cwdAbs = path37.resolve(args.cwd);
|
|
29245
29014
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list");
|
|
29246
29015
|
const list2 = skills.list(args);
|
|
29247
29016
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -29253,7 +29022,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
29253
29022
|
};
|
|
29254
29023
|
const agentsList = async (frame, _client, ctx) => {
|
|
29255
29024
|
const args = AgentsListArgs.parse(frame);
|
|
29256
|
-
const cwdAbs =
|
|
29025
|
+
const cwdAbs = path37.resolve(args.cwd);
|
|
29257
29026
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list");
|
|
29258
29027
|
const list2 = agents.list(args);
|
|
29259
29028
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -29272,20 +29041,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
29272
29041
|
}
|
|
29273
29042
|
|
|
29274
29043
|
// src/handlers/git.ts
|
|
29275
|
-
var
|
|
29044
|
+
var path39 = __toESM(require("path"), 1);
|
|
29276
29045
|
init_protocol();
|
|
29277
29046
|
init_protocol();
|
|
29278
29047
|
|
|
29279
29048
|
// src/workspace/git.ts
|
|
29280
29049
|
var import_node_child_process6 = require("child_process");
|
|
29281
|
-
var
|
|
29282
|
-
var
|
|
29050
|
+
var import_node_fs25 = __toESM(require("fs"), 1);
|
|
29051
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
29283
29052
|
var import_node_util = require("util");
|
|
29284
29053
|
var pexec = (0, import_node_util.promisify)(import_node_child_process6.execFile);
|
|
29285
29054
|
function normalizePath(p2) {
|
|
29286
|
-
const resolved =
|
|
29055
|
+
const resolved = import_node_path27.default.resolve(p2);
|
|
29287
29056
|
try {
|
|
29288
|
-
return
|
|
29057
|
+
return import_node_fs25.default.realpathSync(resolved);
|
|
29289
29058
|
} catch {
|
|
29290
29059
|
return resolved;
|
|
29291
29060
|
}
|
|
@@ -29358,7 +29127,7 @@ async function listGitBranches(cwd) {
|
|
|
29358
29127
|
// src/handlers/git.ts
|
|
29359
29128
|
function assertGuestCwd(ctx, cwd, personaRoot, method) {
|
|
29360
29129
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
29361
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
29130
|
+
if (!isGuestPathAllowed(ctx.grants, path39.resolve(cwd), personaRoot, "read")) {
|
|
29362
29131
|
throw new ClawdError(
|
|
29363
29132
|
ERROR_CODES.UNAUTHORIZED,
|
|
29364
29133
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -29845,7 +29614,7 @@ function buildPersonaHandlers(deps) {
|
|
|
29845
29614
|
}
|
|
29846
29615
|
|
|
29847
29616
|
// src/handlers/attachment.ts
|
|
29848
|
-
var
|
|
29617
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
29849
29618
|
init_protocol();
|
|
29850
29619
|
init_protocol();
|
|
29851
29620
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -29899,12 +29668,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
29899
29668
|
`session ${args.sessionId} scope unresolved`
|
|
29900
29669
|
);
|
|
29901
29670
|
}
|
|
29902
|
-
const cwdAbs =
|
|
29903
|
-
const candidateAbs =
|
|
29671
|
+
const cwdAbs = import_node_path28.default.resolve(sessionFile.cwd);
|
|
29672
|
+
const candidateAbs = import_node_path28.default.isAbsolute(args.relPath) ? import_node_path28.default.resolve(args.relPath) : import_node_path28.default.resolve(cwdAbs, args.relPath);
|
|
29904
29673
|
assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl");
|
|
29905
29674
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
29906
29675
|
const entry = entries.find((e) => {
|
|
29907
|
-
const storedAbs =
|
|
29676
|
+
const storedAbs = import_node_path28.default.isAbsolute(e.relPath) ? import_node_path28.default.resolve(e.relPath) : import_node_path28.default.resolve(cwdAbs, e.relPath);
|
|
29908
29677
|
return storedAbs === candidateAbs && !e.stale;
|
|
29909
29678
|
});
|
|
29910
29679
|
if (!entry) {
|
|
@@ -29928,7 +29697,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29928
29697
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
29929
29698
|
const f = deps.sessionStore.read(sessionId);
|
|
29930
29699
|
if (!f) return;
|
|
29931
|
-
assertGuestAttachmentPath(ctx,
|
|
29700
|
+
assertGuestAttachmentPath(ctx, import_node_path28.default.resolve(f.cwd), deps.personaRoot, method);
|
|
29932
29701
|
}
|
|
29933
29702
|
const groupAdd = async (frame, _client, ctx) => {
|
|
29934
29703
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -29969,7 +29738,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
29969
29738
|
}
|
|
29970
29739
|
const entries = deps.groupFileStore.list(scope, parsed.data.sessionId);
|
|
29971
29740
|
const target = entries.find((e) => e.relPath === parsed.data.relPath);
|
|
29972
|
-
if (target?.from === "owner"
|
|
29741
|
+
if (target?.from === "owner") {
|
|
29973
29742
|
deps.groupFileStore.remove(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
29974
29743
|
} else {
|
|
29975
29744
|
deps.groupFileStore.markStale(scope, parsed.data.sessionId, parsed.data.relPath);
|
|
@@ -30095,6 +29864,11 @@ var METHOD_GRANT_MAP = {
|
|
|
30095
29864
|
"session:pin": CAPABILITY_SCOPED,
|
|
30096
29865
|
"session:reorderPins": ADMIN_ANY,
|
|
30097
29866
|
// owner 全局操作,无 personaId 维度
|
|
29867
|
+
// mirror peer sessions (2026-05-26):owner-only — guest 不应能写/删本机 mirror SessionFile
|
|
29868
|
+
// (会绕过 #739 隔离规则把伪造身份的 SessionFile 写进 default store)。handler 端内部 ctx
|
|
29869
|
+
// 校验,dispatcher 层一刀切 admin 也兜底。
|
|
29870
|
+
"peerSession:upsert": ADMIN_ANY,
|
|
29871
|
+
"peerSession:remove": ADMIN_ANY,
|
|
30098
29872
|
"permission:respond": CAPABILITY_SCOPED,
|
|
30099
29873
|
"session:answerQuestion": CAPABILITY_SCOPED,
|
|
30100
29874
|
"session:cancelQuestion": CAPABILITY_SCOPED,
|
|
@@ -30165,7 +29939,7 @@ function computeGrantForFrame(method, frame) {
|
|
|
30165
29939
|
async function startDaemon(config) {
|
|
30166
29940
|
const logger = createLogger({
|
|
30167
29941
|
level: config.logLevel,
|
|
30168
|
-
file:
|
|
29942
|
+
file: import_node_path29.default.join(config.dataDir, "clawd.log")
|
|
30169
29943
|
});
|
|
30170
29944
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
30171
29945
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
@@ -30252,7 +30026,7 @@ async function startDaemon(config) {
|
|
|
30252
30026
|
const agents = new AgentsScanner();
|
|
30253
30027
|
const history = new ClaudeHistoryReader();
|
|
30254
30028
|
let transport = null;
|
|
30255
|
-
const personaStore = new PersonaStore(
|
|
30029
|
+
const personaStore = new PersonaStore(import_node_path29.default.join(config.dataDir, "personas"));
|
|
30256
30030
|
const defaultsRoot = findDefaultsRoot();
|
|
30257
30031
|
if (defaultsRoot) {
|
|
30258
30032
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -30269,7 +30043,7 @@ async function startDaemon(config) {
|
|
|
30269
30043
|
getAdapter,
|
|
30270
30044
|
historyReader: history,
|
|
30271
30045
|
dataDir: config.dataDir,
|
|
30272
|
-
personaRoot:
|
|
30046
|
+
personaRoot: import_node_path29.default.join(config.dataDir, "personas"),
|
|
30273
30047
|
personaStore,
|
|
30274
30048
|
ownerDisplayName,
|
|
30275
30049
|
ownerPrincipalId,
|
|
@@ -30293,10 +30067,10 @@ async function startDaemon(config) {
|
|
|
30293
30067
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
30294
30068
|
attachmentGroup: {
|
|
30295
30069
|
onFileEdit: (input) => {
|
|
30296
|
-
const absPath =
|
|
30070
|
+
const absPath = import_node_path29.default.isAbsolute(input.relPath) ? input.relPath : import_node_path29.default.join(input.cwd, input.relPath);
|
|
30297
30071
|
let size = 0;
|
|
30298
30072
|
try {
|
|
30299
|
-
size =
|
|
30073
|
+
size = import_node_fs26.default.statSync(absPath).size;
|
|
30300
30074
|
} catch (err) {
|
|
30301
30075
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
30302
30076
|
sessionId: input.sessionId,
|
|
@@ -30414,10 +30188,10 @@ async function startDaemon(config) {
|
|
|
30414
30188
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
30415
30189
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30416
30190
|
// guest path guard:candidate 必须在 personaRoot 子树下
|
|
30417
|
-
personaRoot:
|
|
30191
|
+
personaRoot: import_node_path29.default.join(config.dataDir, "personas")
|
|
30418
30192
|
},
|
|
30419
30193
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
30420
|
-
personaRoot:
|
|
30194
|
+
personaRoot: import_node_path29.default.join(config.dataDir, "personas"),
|
|
30421
30195
|
// capability:list / delete handler 依赖
|
|
30422
30196
|
capabilityManager,
|
|
30423
30197
|
// personal-cap:get 返回的 shareBaseUrl 用此 base URL:
|
|
@@ -30453,19 +30227,7 @@ async function startDaemon(config) {
|
|
|
30453
30227
|
sessionStore: store,
|
|
30454
30228
|
// /files HMAC verify 用 auth.json 的 signSecret 字段(与 attachment.signUrl 同源)。
|
|
30455
30229
|
// --auth-token CLI 模式没 signSecret → 路由返 501,sign URL 功能整体禁用。
|
|
30456
|
-
getSignSecret: () => authFile?.signSecret ?? null
|
|
30457
|
-
// chatinput 附件上传(POST /attachments/upload,spec §6.1)。upload pipeline
|
|
30458
|
-
// 在 session.cwd 下的 .attachments/ 落盘;signSecret 与 /files HMAC 同源;
|
|
30459
|
-
// tunnel 未启时返 503 TUNNEL_NOT_READY 让前端显式提示。
|
|
30460
|
-
attachmentUpload: {
|
|
30461
|
-
groupFileStore,
|
|
30462
|
-
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
30463
|
-
getSessionDir: (sid) => {
|
|
30464
|
-
const f = manager.findOwnedSession(sid);
|
|
30465
|
-
return f ? f.cwd : null;
|
|
30466
|
-
},
|
|
30467
|
-
getHttpBaseUrl: () => currentTunnelUrl ? getHttpBaseUrl() : null
|
|
30468
|
-
}
|
|
30230
|
+
getSignSecret: () => authFile?.signSecret ?? null
|
|
30469
30231
|
});
|
|
30470
30232
|
wsServer = new LocalWsServer({
|
|
30471
30233
|
host: config.host,
|
|
@@ -30630,8 +30392,8 @@ async function startDaemon(config) {
|
|
|
30630
30392
|
const lines = [
|
|
30631
30393
|
`Tunnel: ${r.url}`,
|
|
30632
30394
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
30633
|
-
`Frpc config: ${
|
|
30634
|
-
`Frpc log: ${
|
|
30395
|
+
`Frpc config: ${import_node_path29.default.join(config.dataDir, "frpc.toml")}`,
|
|
30396
|
+
`Frpc log: ${import_node_path29.default.join(config.dataDir, "frpc.log")}`
|
|
30635
30397
|
];
|
|
30636
30398
|
const width = Math.max(...lines.map((l) => l.length));
|
|
30637
30399
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -30644,8 +30406,8 @@ ${bar}
|
|
|
30644
30406
|
|
|
30645
30407
|
`);
|
|
30646
30408
|
try {
|
|
30647
|
-
const connectPath =
|
|
30648
|
-
|
|
30409
|
+
const connectPath = import_node_path29.default.join(config.dataDir, "connect.txt");
|
|
30410
|
+
import_node_fs26.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
30649
30411
|
} catch {
|
|
30650
30412
|
}
|
|
30651
30413
|
} catch (err) {
|
|
@@ -30670,29 +30432,8 @@ ${bar}
|
|
|
30670
30432
|
logger.warn("tunnel unavailable, degraded to local mode", { reason: tunnelError });
|
|
30671
30433
|
}
|
|
30672
30434
|
}
|
|
30673
|
-
const tickAttachmentGc = () => {
|
|
30674
|
-
try {
|
|
30675
|
-
runAttachmentGc({
|
|
30676
|
-
dataDir: config.dataDir,
|
|
30677
|
-
groupFileStore,
|
|
30678
|
-
sessionScopes: manager.listOwnedSessionScopes().map(({ scope, sessionId }) => ({
|
|
30679
|
-
scope,
|
|
30680
|
-
sessionId
|
|
30681
|
-
})),
|
|
30682
|
-
// 注入 cwd 让 GC 直接定位 .attachments 目录(与 upload 落盘点一致)
|
|
30683
|
-
getSessionCwd: (sid) => manager.findOwnedSession(sid)?.cwd ?? null,
|
|
30684
|
-
logger
|
|
30685
|
-
});
|
|
30686
|
-
} catch (err) {
|
|
30687
|
-
logger.warn("attachment gc tick failed", { err: err.message });
|
|
30688
|
-
}
|
|
30689
|
-
};
|
|
30690
|
-
setImmediate(tickAttachmentGc);
|
|
30691
|
-
const attachmentGcInterval = setInterval(tickAttachmentGc, 24 * 3600 * 1e3);
|
|
30692
|
-
attachmentGcInterval.unref();
|
|
30693
30435
|
const shutdown = async () => {
|
|
30694
30436
|
logger.info("stopping clawd");
|
|
30695
|
-
clearInterval(attachmentGcInterval);
|
|
30696
30437
|
observer.stopAll();
|
|
30697
30438
|
manager.stopAll();
|
|
30698
30439
|
if (tunnelMgr) {
|
|
@@ -30710,9 +30451,9 @@ ${bar}
|
|
|
30710
30451
|
};
|
|
30711
30452
|
}
|
|
30712
30453
|
function migrateDropPersonsDir(dataDir) {
|
|
30713
|
-
const dir =
|
|
30454
|
+
const dir = import_node_path29.default.join(dataDir, "persons");
|
|
30714
30455
|
try {
|
|
30715
|
-
|
|
30456
|
+
import_node_fs26.default.rmSync(dir, { recursive: true, force: true });
|
|
30716
30457
|
} catch {
|
|
30717
30458
|
}
|
|
30718
30459
|
}
|