@clawos-dev/clawd 0.2.64-beta.106.42f5a70 → 0.2.65-beta.107.ac81439

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.
Files changed (2) hide show
  1. package/dist/cli.cjs +409 -657
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -110,17 +110,12 @@ var init_methods = __esm({
110
110
  // 触发频率低(仅 window resize),response 也是 ack。
111
111
  "session:pty:input",
112
112
  "session:pty:resize",
113
- // ---- attachment.* file-sharing(spec §5;详见 attachment-schemas.ts) ----
113
+ // ---- attachment.* file-sharing(详见 attachment-schemas.ts) ----
114
114
  // 命名警告:这里的 `attachment.*` RPC 与 CC v2.x 上行 `type:"attachment"` 系统行
115
115
  // (attachment-skills / attachment-deferred-tools / attachment_memories)是不同概念。
116
116
  // 全部管理类 RPC handler 入口 requireOwner(personal token 调任意一个都 403);
117
- // 实际文件传输走 HTTP 路由(GET/POST,详见 spec §5),不在白名单内。
118
- "attachment.outboxCreate",
119
- "attachment.outboxRevoke",
120
- "attachment.outboxList",
121
- "attachment.mountAdd",
122
- "attachment.mountRemove",
123
- "attachment.mountList",
117
+ // 实际文件传输走 HTTP 路由(GET /files?p=&e=&s=,签名验证),不在白名单内。
118
+ "attachment.signUrl",
124
119
  "attachment.groupAdd",
125
120
  "attachment.groupRemove",
126
121
  "attachment.groupList",
@@ -617,8 +612,8 @@ var init_parseUtil = __esm({
617
612
  init_errors2();
618
613
  init_en();
619
614
  makeIssue = (params) => {
620
- const { data, path: path33, errorMaps, issueData } = params;
621
- const fullPath = [...path33, ...issueData.path || []];
615
+ const { data, path: path31, errorMaps, issueData } = params;
616
+ const fullPath = [...path31, ...issueData.path || []];
622
617
  const fullIssue = {
623
618
  ...issueData,
624
619
  path: fullPath
@@ -929,11 +924,11 @@ var init_types = __esm({
929
924
  init_parseUtil();
930
925
  init_util();
931
926
  ParseInputLazyPath = class {
932
- constructor(parent, value, path33, key) {
927
+ constructor(parent, value, path31, key) {
933
928
  this._cachedPath = [];
934
929
  this.parent = parent;
935
930
  this.data = value;
936
- this._path = path33;
931
+ this._path = path31;
937
932
  this._key = key;
938
933
  }
939
934
  get path() {
@@ -4317,116 +4312,47 @@ var init_zod = __esm({
4317
4312
  });
4318
4313
 
4319
4314
  // ../protocol/src/attachment-schemas.ts
4320
- var TOKEN_ROLES, GROUP_FILE_SOURCES, MOUNT_MODES, GroupFileEntrySchema, MountEntrySchema, OutboxScopeSchema, OutboxCapEntrySchema, AttachmentOutboxCreateArgs, AttachmentOutboxCreateResponseSchema, AttachmentOutboxRevokeArgs, AttachmentOutboxRevokeResponseSchema, AttachmentOutboxListArgs, AttachmentOutboxListResponseSchema, AttachmentMountAddArgs, AttachmentMountAddResponseSchema, AttachmentMountRemoveArgs, AttachmentMountRemoveResponseSchema, AttachmentMountListArgs, AttachmentMountListResponseSchema, AttachmentGroupAddArgs, AttachmentGroupAddResponseSchema, AttachmentGroupRemoveArgs, AttachmentGroupRemoveResponseSchema, AttachmentGroupListArgs, AttachmentGroupListResponseSchema, AttachmentGroupListPersonaArgs, AttachmentGroupListPersonaResponseSchema;
4315
+ var TOKEN_ROLES, GROUP_FILE_SOURCES, GroupFileEntrySchema, AttachmentSignUrlArgs, AttachmentSignUrlResponseSchema, AttachmentGroupAddArgs, AttachmentGroupAddResponseSchema, AttachmentGroupRemoveArgs, AttachmentGroupRemoveResponseSchema, AttachmentGroupListArgs, AttachmentGroupListResponseSchema, AttachmentGroupListPersonaArgs, AttachmentGroupListPersonaResponseSchema;
4321
4316
  var init_attachment_schemas = __esm({
4322
4317
  "../protocol/src/attachment-schemas.ts"() {
4323
4318
  "use strict";
4324
4319
  init_zod();
4325
4320
  TOKEN_ROLES = ["owner", "personal"];
4326
4321
  GROUP_FILE_SOURCES = ["agent", "owner"];
4327
- MOUNT_MODES = ["link", "copy"];
4328
4322
  GroupFileEntrySchema = external_exports.object({
4329
4323
  /** daemon 派发的稳定 id(用于 RPC remove / UI key) */
4330
4324
  id: external_exports.string().min(1),
4331
4325
  /** 相对 personaDir / sessionCwd 的路径 */
4332
4326
  relPath: external_exports.string().min(1),
4333
4327
  from: external_exports.enum(GROUP_FILE_SOURCES),
4334
- /** owner 手动加群时的可选备注 */
4328
+ /** owner 手动加入时的可选备注 */
4335
4329
  label: external_exports.string().optional(),
4336
4330
  /** 文件字节数(stat 时拍) */
4337
4331
  size: external_exports.number().int().nonnegative(),
4338
4332
  mime: external_exports.string().min(1),
4339
- /** 入群时间戳(ms) */
4333
+ /** 加入清单时间戳(ms) */
4340
4334
  addedAt: external_exports.number().int().nonnegative(),
4341
4335
  /** 最近一次 agent Write/Edit 时间戳(agent 反复改同 path 时更新) */
4342
4336
  lastEditedAt: external_exports.number().int().nonnegative().optional(),
4343
4337
  /** agent rm / 文件不见了时打标;UI 灰显,不能再 share */
4344
4338
  stale: external_exports.boolean().optional()
4345
4339
  });
4346
- MountEntrySchema = external_exports.object({
4347
- /** personaDir 下的 basename(link 目标 / copy 目的名) */
4348
- basename: external_exports.string().min(1),
4349
- /** 原始绝对路径(link 模式下 realpath 后用于 sandbox.allowRead 派生) */
4340
+ AttachmentSignUrlArgs = external_exports.object({
4341
+ /** 要分享的绝对路径;签名只关心 absPath,不区分 persona/session */
4350
4342
  absPath: external_exports.string().min(1),
4351
- mode: external_exports.enum(MOUNT_MODES)
4343
+ /** TTL 秒数;缺省 24h;'never' 走 null 不带 exp 字段(永久有效) */
4344
+ ttlSeconds: external_exports.number().int().positive().nullable().optional()
4352
4345
  });
4353
- OutboxScopeSchema = external_exports.discriminatedUnion("kind", [
4354
- external_exports.object({ kind: external_exports.literal("public") }),
4355
- external_exports.object({ kind: external_exports.literal("personal"), personalId: external_exports.string().min(1) })
4356
- ]);
4357
- OutboxCapEntrySchema = external_exports.object({
4358
- capToken: external_exports.string().min(1),
4359
- /** 归属:persona 域 cap 带 personaId;direct 会话 cap 带 sessionId */
4360
- scopeRef: external_exports.union([
4361
- external_exports.object({ kind: external_exports.literal("persona"), personaId: external_exports.string().min(1) }),
4362
- external_exports.object({ kind: external_exports.literal("session"), sessionId: external_exports.string().min(1) })
4363
- ]),
4364
- absPath: external_exports.string().min(1),
4365
- /** display name(UI 列表显示 + Share dialog 标题),通常等于 basename */
4366
- name: external_exports.string().min(1),
4367
- expiresAt: external_exports.number().int().nonnegative().nullable(),
4368
- oneShot: external_exports.boolean(),
4369
- scope: OutboxScopeSchema,
4370
- hits: external_exports.number().int().nonnegative(),
4371
- revoked: external_exports.boolean().optional(),
4372
- createdAt: external_exports.number().int().nonnegative()
4373
- });
4374
- AttachmentOutboxCreateArgs = external_exports.object({
4375
- /** persona 域:传 personaId;direct 会话:传 sessionId(二选一) */
4376
- personaId: external_exports.string().min(1).optional(),
4377
- sessionId: external_exports.string().min(1).optional(),
4378
- absPath: external_exports.string().min(1),
4379
- /** TTL 秒数;缺省 24h;'never' 走 null */
4380
- ttlSeconds: external_exports.number().int().positive().nullable().optional(),
4381
- oneShot: external_exports.boolean().optional(),
4382
- scope: OutboxScopeSchema.optional()
4383
- });
4384
- AttachmentOutboxCreateResponseSchema = external_exports.object({
4385
- capToken: external_exports.string().min(1),
4386
- /** 完整 URL(含 httpBaseUrl 前缀),UI 直接复制到剪贴板 */
4346
+ AttachmentSignUrlResponseSchema = external_exports.object({
4347
+ /** 完整 URL(含 httpBaseUrl 前缀),UI 直接 window.open / 复制到剪贴板 */
4387
4348
  url: external_exports.string().min(1),
4349
+ /** 失效时间戳(ms);null = 永久 */
4388
4350
  expiresAt: external_exports.number().int().nonnegative().nullable()
4389
4351
  });
4390
- AttachmentOutboxRevokeArgs = external_exports.object({
4391
- capToken: external_exports.string().min(1)
4392
- });
4393
- AttachmentOutboxRevokeResponseSchema = external_exports.object({
4394
- revoked: external_exports.literal(true)
4395
- });
4396
- AttachmentOutboxListArgs = external_exports.object({
4397
- /** 缺省 = 全部(含 direct 会话);传 personaId 过滤到该 persona */
4398
- personaId: external_exports.string().min(1).optional()
4399
- });
4400
- AttachmentOutboxListResponseSchema = external_exports.object({
4401
- entries: external_exports.array(OutboxCapEntrySchema)
4402
- });
4403
- AttachmentMountAddArgs = external_exports.object({
4404
- personaId: external_exports.string().min(1),
4405
- absPath: external_exports.string().min(1),
4406
- mode: external_exports.enum(MOUNT_MODES)
4407
- });
4408
- AttachmentMountAddResponseSchema = external_exports.object({
4409
- entry: MountEntrySchema,
4410
- /** sandbox.allowRead 派生后是否需要重启 session 才能让 CC 子进程跟 symlink */
4411
- sandboxRestartNeeded: external_exports.boolean()
4412
- });
4413
- AttachmentMountRemoveArgs = external_exports.object({
4414
- personaId: external_exports.string().min(1),
4415
- basename: external_exports.string().min(1)
4416
- });
4417
- AttachmentMountRemoveResponseSchema = external_exports.object({
4418
- removed: external_exports.literal(true)
4419
- });
4420
- AttachmentMountListArgs = external_exports.object({
4421
- personaId: external_exports.string().min(1)
4422
- });
4423
- AttachmentMountListResponseSchema = external_exports.object({
4424
- entries: external_exports.array(MountEntrySchema)
4425
- });
4426
4352
  AttachmentGroupAddArgs = external_exports.object({
4427
4353
  sessionId: external_exports.string().min(1),
4428
4354
  relPath: external_exports.string().min(1),
4429
- /** owner 手动加群时可选备注;agent 自动入清单不走此 RPC */
4355
+ /** owner 手动加入时可选备注;agent 自动入清单不走此 RPC */
4430
4356
  label: external_exports.string().optional()
4431
4357
  });
4432
4358
  AttachmentGroupAddResponseSchema = external_exports.object({
@@ -5543,8 +5469,8 @@ var require_req = __commonJS({
5543
5469
  if (req.originalUrl) {
5544
5470
  _req.url = req.originalUrl;
5545
5471
  } else {
5546
- const path33 = req.path;
5547
- _req.url = typeof path33 === "string" ? path33 : req.url ? req.url.path || req.url : void 0;
5472
+ const path31 = req.path;
5473
+ _req.url = typeof path31 === "string" ? path31 : req.url ? req.url.path || req.url : void 0;
5548
5474
  }
5549
5475
  if (req.query) {
5550
5476
  _req.query = req.query;
@@ -5709,14 +5635,14 @@ var require_redact = __commonJS({
5709
5635
  }
5710
5636
  return obj;
5711
5637
  }
5712
- function parsePath(path33) {
5638
+ function parsePath(path31) {
5713
5639
  const parts = [];
5714
5640
  let current = "";
5715
5641
  let inBrackets = false;
5716
5642
  let inQuotes = false;
5717
5643
  let quoteChar = "";
5718
- for (let i = 0; i < path33.length; i++) {
5719
- const char = path33[i];
5644
+ for (let i = 0; i < path31.length; i++) {
5645
+ const char = path31[i];
5720
5646
  if (!inBrackets && char === ".") {
5721
5647
  if (current) {
5722
5648
  parts.push(current);
@@ -5847,10 +5773,10 @@ var require_redact = __commonJS({
5847
5773
  return current;
5848
5774
  }
5849
5775
  function redactPaths(obj, paths, censor, remove = false) {
5850
- for (const path33 of paths) {
5851
- const parts = parsePath(path33);
5776
+ for (const path31 of paths) {
5777
+ const parts = parsePath(path31);
5852
5778
  if (parts.includes("*")) {
5853
- redactWildcardPath(obj, parts, censor, path33, remove);
5779
+ redactWildcardPath(obj, parts, censor, path31, remove);
5854
5780
  } else {
5855
5781
  if (remove) {
5856
5782
  removeKey(obj, parts);
@@ -5935,8 +5861,8 @@ var require_redact = __commonJS({
5935
5861
  }
5936
5862
  } else {
5937
5863
  if (afterWildcard.includes("*")) {
5938
- const wrappedCensor = typeof censor === "function" ? (value, path33) => {
5939
- const fullPath = [...pathArray.slice(0, pathLength), ...path33];
5864
+ const wrappedCensor = typeof censor === "function" ? (value, path31) => {
5865
+ const fullPath = [...pathArray.slice(0, pathLength), ...path31];
5940
5866
  return censor(value, fullPath);
5941
5867
  } : censor;
5942
5868
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -5971,8 +5897,8 @@ var require_redact = __commonJS({
5971
5897
  return null;
5972
5898
  }
5973
5899
  const pathStructure = /* @__PURE__ */ new Map();
5974
- for (const path33 of pathsToClone) {
5975
- const parts = parsePath(path33);
5900
+ for (const path31 of pathsToClone) {
5901
+ const parts = parsePath(path31);
5976
5902
  let current = pathStructure;
5977
5903
  for (let i = 0; i < parts.length; i++) {
5978
5904
  const part = parts[i];
@@ -6024,24 +5950,24 @@ var require_redact = __commonJS({
6024
5950
  }
6025
5951
  return cloneSelectively(obj, pathStructure);
6026
5952
  }
6027
- function validatePath(path33) {
6028
- if (typeof path33 !== "string") {
5953
+ function validatePath(path31) {
5954
+ if (typeof path31 !== "string") {
6029
5955
  throw new Error("Paths must be (non-empty) strings");
6030
5956
  }
6031
- if (path33 === "") {
5957
+ if (path31 === "") {
6032
5958
  throw new Error("Invalid redaction path ()");
6033
5959
  }
6034
- if (path33.includes("..")) {
6035
- throw new Error(`Invalid redaction path (${path33})`);
5960
+ if (path31.includes("..")) {
5961
+ throw new Error(`Invalid redaction path (${path31})`);
6036
5962
  }
6037
- if (path33.includes(",")) {
6038
- throw new Error(`Invalid redaction path (${path33})`);
5963
+ if (path31.includes(",")) {
5964
+ throw new Error(`Invalid redaction path (${path31})`);
6039
5965
  }
6040
5966
  let bracketCount = 0;
6041
5967
  let inQuotes = false;
6042
5968
  let quoteChar = "";
6043
- for (let i = 0; i < path33.length; i++) {
6044
- const char = path33[i];
5969
+ for (let i = 0; i < path31.length; i++) {
5970
+ const char = path31[i];
6045
5971
  if ((char === '"' || char === "'") && bracketCount > 0) {
6046
5972
  if (!inQuotes) {
6047
5973
  inQuotes = true;
@@ -6055,20 +5981,20 @@ var require_redact = __commonJS({
6055
5981
  } else if (char === "]" && !inQuotes) {
6056
5982
  bracketCount--;
6057
5983
  if (bracketCount < 0) {
6058
- throw new Error(`Invalid redaction path (${path33})`);
5984
+ throw new Error(`Invalid redaction path (${path31})`);
6059
5985
  }
6060
5986
  }
6061
5987
  }
6062
5988
  if (bracketCount !== 0) {
6063
- throw new Error(`Invalid redaction path (${path33})`);
5989
+ throw new Error(`Invalid redaction path (${path31})`);
6064
5990
  }
6065
5991
  }
6066
5992
  function validatePaths(paths) {
6067
5993
  if (!Array.isArray(paths)) {
6068
5994
  throw new TypeError("paths must be an array");
6069
5995
  }
6070
- for (const path33 of paths) {
6071
- validatePath(path33);
5996
+ for (const path31 of paths) {
5997
+ validatePath(path31);
6072
5998
  }
6073
5999
  }
6074
6000
  function slowRedact(options = {}) {
@@ -6236,8 +6162,8 @@ var require_redaction = __commonJS({
6236
6162
  if (shape[k2] === null) {
6237
6163
  o[k2] = (value) => topCensor(value, [k2]);
6238
6164
  } else {
6239
- const wrappedCensor = typeof censor === "function" ? (value, path33) => {
6240
- return censor(value, [k2, ...path33]);
6165
+ const wrappedCensor = typeof censor === "function" ? (value, path31) => {
6166
+ return censor(value, [k2, ...path31]);
6241
6167
  } : censor;
6242
6168
  o[k2] = Redact({
6243
6169
  paths: shape[k2],
@@ -6455,10 +6381,10 @@ var require_atomic_sleep = __commonJS({
6455
6381
  var require_sonic_boom = __commonJS({
6456
6382
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
6457
6383
  "use strict";
6458
- var fs30 = require("fs");
6384
+ var fs28 = require("fs");
6459
6385
  var EventEmitter2 = require("events");
6460
6386
  var inherits = require("util").inherits;
6461
- var path33 = require("path");
6387
+ var path31 = require("path");
6462
6388
  var sleep = require_atomic_sleep();
6463
6389
  var assert = require("assert");
6464
6390
  var BUSY_WRITE_TIMEOUT = 100;
@@ -6512,20 +6438,20 @@ var require_sonic_boom = __commonJS({
6512
6438
  const mode = sonic.mode;
6513
6439
  if (sonic.sync) {
6514
6440
  try {
6515
- if (sonic.mkdir) fs30.mkdirSync(path33.dirname(file), { recursive: true });
6516
- const fd = fs30.openSync(file, flags, mode);
6441
+ if (sonic.mkdir) fs28.mkdirSync(path31.dirname(file), { recursive: true });
6442
+ const fd = fs28.openSync(file, flags, mode);
6517
6443
  fileOpened(null, fd);
6518
6444
  } catch (err) {
6519
6445
  fileOpened(err);
6520
6446
  throw err;
6521
6447
  }
6522
6448
  } else if (sonic.mkdir) {
6523
- fs30.mkdir(path33.dirname(file), { recursive: true }, (err) => {
6449
+ fs28.mkdir(path31.dirname(file), { recursive: true }, (err) => {
6524
6450
  if (err) return fileOpened(err);
6525
- fs30.open(file, flags, mode, fileOpened);
6451
+ fs28.open(file, flags, mode, fileOpened);
6526
6452
  });
6527
6453
  } else {
6528
- fs30.open(file, flags, mode, fileOpened);
6454
+ fs28.open(file, flags, mode, fileOpened);
6529
6455
  }
6530
6456
  }
6531
6457
  function SonicBoom(opts) {
@@ -6566,8 +6492,8 @@ var require_sonic_boom = __commonJS({
6566
6492
  this.flush = flushBuffer;
6567
6493
  this.flushSync = flushBufferSync;
6568
6494
  this._actualWrite = actualWriteBuffer;
6569
- fsWriteSync = () => fs30.writeSync(this.fd, this._writingBuf);
6570
- fsWrite = () => fs30.write(this.fd, this._writingBuf, this.release);
6495
+ fsWriteSync = () => fs28.writeSync(this.fd, this._writingBuf);
6496
+ fsWrite = () => fs28.write(this.fd, this._writingBuf, this.release);
6571
6497
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
6572
6498
  this._writingBuf = "";
6573
6499
  this.write = write;
@@ -6576,15 +6502,15 @@ var require_sonic_boom = __commonJS({
6576
6502
  this._actualWrite = actualWrite;
6577
6503
  fsWriteSync = () => {
6578
6504
  if (Buffer.isBuffer(this._writingBuf)) {
6579
- return fs30.writeSync(this.fd, this._writingBuf);
6505
+ return fs28.writeSync(this.fd, this._writingBuf);
6580
6506
  }
6581
- return fs30.writeSync(this.fd, this._writingBuf, "utf8");
6507
+ return fs28.writeSync(this.fd, this._writingBuf, "utf8");
6582
6508
  };
6583
6509
  fsWrite = () => {
6584
6510
  if (Buffer.isBuffer(this._writingBuf)) {
6585
- return fs30.write(this.fd, this._writingBuf, this.release);
6511
+ return fs28.write(this.fd, this._writingBuf, this.release);
6586
6512
  }
6587
- return fs30.write(this.fd, this._writingBuf, "utf8", this.release);
6513
+ return fs28.write(this.fd, this._writingBuf, "utf8", this.release);
6588
6514
  };
6589
6515
  } else {
6590
6516
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -6641,7 +6567,7 @@ var require_sonic_boom = __commonJS({
6641
6567
  }
6642
6568
  }
6643
6569
  if (this._fsync) {
6644
- fs30.fsyncSync(this.fd);
6570
+ fs28.fsyncSync(this.fd);
6645
6571
  }
6646
6572
  const len = this._len;
6647
6573
  if (this._reopening) {
@@ -6755,7 +6681,7 @@ var require_sonic_boom = __commonJS({
6755
6681
  const onDrain = () => {
6756
6682
  if (!this._fsync) {
6757
6683
  try {
6758
- fs30.fsync(this.fd, (err) => {
6684
+ fs28.fsync(this.fd, (err) => {
6759
6685
  this._flushPending = false;
6760
6686
  cb(err);
6761
6687
  });
@@ -6857,7 +6783,7 @@ var require_sonic_boom = __commonJS({
6857
6783
  const fd = this.fd;
6858
6784
  this.once("ready", () => {
6859
6785
  if (fd !== this.fd) {
6860
- fs30.close(fd, (err) => {
6786
+ fs28.close(fd, (err) => {
6861
6787
  if (err) {
6862
6788
  return this.emit("error", err);
6863
6789
  }
@@ -6906,7 +6832,7 @@ var require_sonic_boom = __commonJS({
6906
6832
  buf = this._bufs[0];
6907
6833
  }
6908
6834
  try {
6909
- const n = Buffer.isBuffer(buf) ? fs30.writeSync(this.fd, buf) : fs30.writeSync(this.fd, buf, "utf8");
6835
+ const n = Buffer.isBuffer(buf) ? fs28.writeSync(this.fd, buf) : fs28.writeSync(this.fd, buf, "utf8");
6910
6836
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
6911
6837
  buf = releasedBufObj.writingBuf;
6912
6838
  this._len = releasedBufObj.len;
@@ -6922,7 +6848,7 @@ var require_sonic_boom = __commonJS({
6922
6848
  }
6923
6849
  }
6924
6850
  try {
6925
- fs30.fsyncSync(this.fd);
6851
+ fs28.fsyncSync(this.fd);
6926
6852
  } catch {
6927
6853
  }
6928
6854
  }
@@ -6943,7 +6869,7 @@ var require_sonic_boom = __commonJS({
6943
6869
  buf = mergeBuf(this._bufs[0], this._lens[0]);
6944
6870
  }
6945
6871
  try {
6946
- const n = fs30.writeSync(this.fd, buf);
6872
+ const n = fs28.writeSync(this.fd, buf);
6947
6873
  buf = buf.subarray(n);
6948
6874
  this._len = Math.max(this._len - n, 0);
6949
6875
  if (buf.length <= 0) {
@@ -6971,13 +6897,13 @@ var require_sonic_boom = __commonJS({
6971
6897
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
6972
6898
  if (this.sync) {
6973
6899
  try {
6974
- const written = Buffer.isBuffer(this._writingBuf) ? fs30.writeSync(this.fd, this._writingBuf) : fs30.writeSync(this.fd, this._writingBuf, "utf8");
6900
+ const written = Buffer.isBuffer(this._writingBuf) ? fs28.writeSync(this.fd, this._writingBuf) : fs28.writeSync(this.fd, this._writingBuf, "utf8");
6975
6901
  release(null, written);
6976
6902
  } catch (err) {
6977
6903
  release(err);
6978
6904
  }
6979
6905
  } else {
6980
- fs30.write(this.fd, this._writingBuf, release);
6906
+ fs28.write(this.fd, this._writingBuf, release);
6981
6907
  }
6982
6908
  }
6983
6909
  function actualWriteBuffer() {
@@ -6986,7 +6912,7 @@ var require_sonic_boom = __commonJS({
6986
6912
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
6987
6913
  if (this.sync) {
6988
6914
  try {
6989
- const written = fs30.writeSync(this.fd, this._writingBuf);
6915
+ const written = fs28.writeSync(this.fd, this._writingBuf);
6990
6916
  release(null, written);
6991
6917
  } catch (err) {
6992
6918
  release(err);
@@ -6995,7 +6921,7 @@ var require_sonic_boom = __commonJS({
6995
6921
  if (kCopyBuffer) {
6996
6922
  this._writingBuf = Buffer.from(this._writingBuf);
6997
6923
  }
6998
- fs30.write(this.fd, this._writingBuf, release);
6924
+ fs28.write(this.fd, this._writingBuf, release);
6999
6925
  }
7000
6926
  }
7001
6927
  function actualClose(sonic) {
@@ -7011,12 +6937,12 @@ var require_sonic_boom = __commonJS({
7011
6937
  sonic._lens = [];
7012
6938
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
7013
6939
  try {
7014
- fs30.fsync(sonic.fd, closeWrapped);
6940
+ fs28.fsync(sonic.fd, closeWrapped);
7015
6941
  } catch {
7016
6942
  }
7017
6943
  function closeWrapped() {
7018
6944
  if (sonic.fd !== 1 && sonic.fd !== 2) {
7019
- fs30.close(sonic.fd, done);
6945
+ fs28.close(sonic.fd, done);
7020
6946
  } else {
7021
6947
  done();
7022
6948
  }
@@ -10151,11 +10077,11 @@ var init_lib = __esm({
10151
10077
  }
10152
10078
  }
10153
10079
  },
10154
- addToPath: function addToPath(path33, added, removed, oldPosInc, options) {
10155
- var last = path33.lastComponent;
10080
+ addToPath: function addToPath(path31, added, removed, oldPosInc, options) {
10081
+ var last = path31.lastComponent;
10156
10082
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
10157
10083
  return {
10158
- oldPos: path33.oldPos + oldPosInc,
10084
+ oldPos: path31.oldPos + oldPosInc,
10159
10085
  lastComponent: {
10160
10086
  count: last.count + 1,
10161
10087
  added,
@@ -10165,7 +10091,7 @@ var init_lib = __esm({
10165
10091
  };
10166
10092
  } else {
10167
10093
  return {
10168
- oldPos: path33.oldPos + oldPosInc,
10094
+ oldPos: path31.oldPos + oldPosInc,
10169
10095
  lastComponent: {
10170
10096
  count: 1,
10171
10097
  added,
@@ -10596,10 +10522,10 @@ function attachmentToHistoryMessage(o, ts) {
10596
10522
  const memories = raw.map((m2) => {
10597
10523
  if (!m2 || typeof m2 !== "object") return null;
10598
10524
  const rec = m2;
10599
- const path33 = typeof rec.path === "string" ? rec.path : null;
10525
+ const path31 = typeof rec.path === "string" ? rec.path : null;
10600
10526
  const content = typeof rec.content === "string" ? rec.content : null;
10601
- if (!path33 || content == null) return null;
10602
- const entry = { path: path33, content };
10527
+ if (!path31 || content == null) return null;
10528
+ const entry = { path: path31, content };
10603
10529
  if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
10604
10530
  return entry;
10605
10531
  }).filter((m2) => m2 !== null);
@@ -11403,10 +11329,10 @@ function parseAttachment(obj) {
11403
11329
  const memories = raw.map((m2) => {
11404
11330
  if (!m2 || typeof m2 !== "object") return null;
11405
11331
  const rec = m2;
11406
- const path33 = typeof rec.path === "string" ? rec.path : null;
11332
+ const path31 = typeof rec.path === "string" ? rec.path : null;
11407
11333
  const content = typeof rec.content === "string" ? rec.content : null;
11408
- if (!path33 || content == null) return null;
11409
- const out = { path: path33, content };
11334
+ if (!path31 || content == null) return null;
11335
+ const out = { path: path31, content };
11410
11336
  if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
11411
11337
  return out;
11412
11338
  }).filter((m2) => m2 !== null);
@@ -20320,7 +20246,7 @@ var require_websocket_server = __commonJS({
20320
20246
  // src/run-case/recorder.ts
20321
20247
  function startRunCaseRecorder(opts) {
20322
20248
  const now = opts.now ?? Date.now;
20323
- const dir = import_node_path29.default.dirname(opts.recordPath);
20249
+ const dir = import_node_path27.default.dirname(opts.recordPath);
20324
20250
  let stream = null;
20325
20251
  let closing = false;
20326
20252
  let closedSettled = false;
@@ -20334,8 +20260,8 @@ function startRunCaseRecorder(opts) {
20334
20260
  });
20335
20261
  const ensureStream = () => {
20336
20262
  if (stream) return stream;
20337
- import_node_fs27.default.mkdirSync(dir, { recursive: true });
20338
- stream = import_node_fs27.default.createWriteStream(opts.recordPath, { flags: "a" });
20263
+ import_node_fs25.default.mkdirSync(dir, { recursive: true });
20264
+ stream = import_node_fs25.default.createWriteStream(opts.recordPath, { flags: "a" });
20339
20265
  stream.on("close", () => closedResolve());
20340
20266
  return stream;
20341
20267
  };
@@ -20360,12 +20286,12 @@ function startRunCaseRecorder(opts) {
20360
20286
  };
20361
20287
  return { tap, close, closed };
20362
20288
  }
20363
- var import_node_fs27, import_node_path29;
20289
+ var import_node_fs25, import_node_path27;
20364
20290
  var init_recorder = __esm({
20365
20291
  "src/run-case/recorder.ts"() {
20366
20292
  "use strict";
20367
- import_node_fs27 = __toESM(require("fs"), 1);
20368
- import_node_path29 = __toESM(require("path"), 1);
20293
+ import_node_fs25 = __toESM(require("fs"), 1);
20294
+ import_node_path27 = __toESM(require("path"), 1);
20369
20295
  }
20370
20296
  });
20371
20297
 
@@ -20408,7 +20334,7 @@ var init_wire = __esm({
20408
20334
  // src/run-case/controller.ts
20409
20335
  async function runController(opts) {
20410
20336
  const now = opts.now ?? Date.now;
20411
- const cwd = opts.cwd ?? (0, import_node_fs28.mkdtempSync)(import_node_path30.default.join(import_node_os15.default.tmpdir(), "clawd-runcase-"));
20337
+ const cwd = opts.cwd ?? (0, import_node_fs26.mkdtempSync)(import_node_path28.default.join(import_node_os15.default.tmpdir(), "clawd-runcase-"));
20412
20338
  const ownsCwd = opts.cwd === void 0;
20413
20339
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
20414
20340
  const spawnCtx = { cwd };
@@ -20569,19 +20495,19 @@ async function runController(opts) {
20569
20495
  if (sigintHandler) process.off("SIGINT", sigintHandler);
20570
20496
  if (ownsCwd) {
20571
20497
  try {
20572
- (0, import_node_fs28.rmSync)(cwd, { recursive: true, force: true });
20498
+ (0, import_node_fs26.rmSync)(cwd, { recursive: true, force: true });
20573
20499
  } catch {
20574
20500
  }
20575
20501
  }
20576
20502
  return exitCode ?? 0;
20577
20503
  }
20578
- var import_node_fs28, import_node_os15, import_node_path30;
20504
+ var import_node_fs26, import_node_os15, import_node_path28;
20579
20505
  var init_controller = __esm({
20580
20506
  "src/run-case/controller.ts"() {
20581
20507
  "use strict";
20582
- import_node_fs28 = require("fs");
20508
+ import_node_fs26 = require("fs");
20583
20509
  import_node_os15 = __toESM(require("os"), 1);
20584
- import_node_path30 = __toESM(require("path"), 1);
20510
+ import_node_path28 = __toESM(require("path"), 1);
20585
20511
  init_claude();
20586
20512
  init_stdout_splitter();
20587
20513
  init_permission_stdio();
@@ -20813,8 +20739,8 @@ Env (advanced):
20813
20739
  `;
20814
20740
 
20815
20741
  // src/index.ts
20816
- var import_node_path28 = __toESM(require("path"), 1);
20817
- var import_node_fs26 = __toESM(require("fs"), 1);
20742
+ var import_node_path26 = __toESM(require("path"), 1);
20743
+ var import_node_fs24 = __toESM(require("fs"), 1);
20818
20744
 
20819
20745
  // src/logger.ts
20820
20746
  var import_node_fs2 = __toESM(require("fs"), 1);
@@ -26540,40 +26466,178 @@ function safeRealpath(p2) {
26540
26466
 
26541
26467
  // src/attachment/mime.ts
26542
26468
  var import_node_path15 = __toESM(require("path"), 1);
26543
- var EXT_TO_MIME = {
26544
- ".md": "text/markdown",
26545
- ".markdown": "text/markdown",
26546
- ".txt": "text/plain",
26547
- ".log": "text/plain",
26548
- ".json": "application/json",
26549
- ".html": "text/html",
26550
- ".htm": "text/html",
26551
- ".css": "text/css",
26552
- ".js": "application/javascript",
26553
- ".ts": "application/typescript",
26554
- ".tsx": "application/typescript",
26469
+ var TEXT_PLAIN = "text/plain; charset=utf-8";
26470
+ var EXT_TO_NATIVE_MIME = {
26471
+ // 图片
26555
26472
  ".png": "image/png",
26556
26473
  ".jpg": "image/jpeg",
26557
26474
  ".jpeg": "image/jpeg",
26558
26475
  ".gif": "image/gif",
26559
26476
  ".webp": "image/webp",
26560
26477
  ".svg": "image/svg+xml",
26478
+ ".bmp": "image/bmp",
26479
+ ".ico": "image/x-icon",
26480
+ ".avif": "image/avif",
26481
+ // 文档 / 富文本
26561
26482
  ".pdf": "application/pdf",
26483
+ ".html": "text/html; charset=utf-8",
26484
+ ".htm": "text/html; charset=utf-8",
26485
+ // 视频 / 音频
26562
26486
  ".mp4": "video/mp4",
26563
26487
  ".webm": "video/webm",
26488
+ ".mov": "video/quicktime",
26564
26489
  ".mp3": "audio/mpeg",
26565
26490
  ".wav": "audio/wav",
26491
+ ".ogg": "audio/ogg",
26492
+ ".flac": "audio/flac",
26493
+ // 真二进制(让浏览器下载而不是错把它当文本明文)
26566
26494
  ".zip": "application/zip",
26567
26495
  ".gz": "application/gzip",
26568
26496
  ".tar": "application/x-tar",
26569
- ".csv": "text/csv",
26570
- ".xml": "application/xml",
26571
- ".yaml": "application/yaml",
26572
- ".yml": "application/yaml"
26497
+ ".7z": "application/x-7z-compressed",
26498
+ ".rar": "application/x-rar-compressed"
26573
26499
  };
26500
+ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
26501
+ // 文档
26502
+ ".md",
26503
+ ".markdown",
26504
+ ".rst",
26505
+ ".adoc",
26506
+ ".txt",
26507
+ ".log",
26508
+ // 通用结构化
26509
+ ".json",
26510
+ ".jsonc",
26511
+ ".json5",
26512
+ ".yaml",
26513
+ ".yml",
26514
+ ".toml",
26515
+ ".xml",
26516
+ ".csv",
26517
+ ".tsv",
26518
+ ".ini",
26519
+ ".conf",
26520
+ ".cfg",
26521
+ ".env",
26522
+ // 前端
26523
+ ".js",
26524
+ ".jsx",
26525
+ ".mjs",
26526
+ ".cjs",
26527
+ ".ts",
26528
+ ".tsx",
26529
+ ".css",
26530
+ ".scss",
26531
+ ".sass",
26532
+ ".less",
26533
+ ".vue",
26534
+ ".svelte",
26535
+ ".graphql",
26536
+ ".gql",
26537
+ // 后端 / 各语言
26538
+ ".py",
26539
+ ".rb",
26540
+ ".go",
26541
+ ".rs",
26542
+ ".java",
26543
+ ".kt",
26544
+ ".kts",
26545
+ ".scala",
26546
+ ".c",
26547
+ ".h",
26548
+ ".cpp",
26549
+ ".hpp",
26550
+ ".cc",
26551
+ ".hh",
26552
+ ".cs",
26553
+ ".php",
26554
+ ".swift",
26555
+ ".m",
26556
+ ".mm",
26557
+ ".dart",
26558
+ ".lua",
26559
+ ".pl",
26560
+ ".r",
26561
+ ".sh",
26562
+ ".bash",
26563
+ ".zsh",
26564
+ ".fish",
26565
+ // 其他
26566
+ ".sql",
26567
+ ".proto",
26568
+ ".dockerfile",
26569
+ ".diff",
26570
+ ".patch",
26571
+ ".makefile",
26572
+ ".mk"
26573
+ ]);
26574
26574
  function lookupMime(filePathOrName) {
26575
26575
  const ext = import_node_path15.default.extname(filePathOrName).toLowerCase();
26576
- return EXT_TO_MIME[ext] ?? "application/octet-stream";
26576
+ if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
26577
+ if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
26578
+ return "application/octet-stream";
26579
+ }
26580
+
26581
+ // src/attachment/sign-url.ts
26582
+ var import_node_crypto5 = __toESM(require("crypto"), 1);
26583
+ var HMAC_ALGO = "sha256";
26584
+ function base64urlEncode(buf) {
26585
+ const b2 = typeof buf === "string" ? Buffer.from(buf, "utf8") : buf;
26586
+ return b2.toString("base64url");
26587
+ }
26588
+ function base64urlDecodeBuf(s) {
26589
+ return Buffer.from(s, "base64url");
26590
+ }
26591
+ function encodeAbsPathForUrl(absPath) {
26592
+ return absPath.split("/").map(encodeURIComponent).join("/");
26593
+ }
26594
+ function decodeAbsPathFromUrl(encoded) {
26595
+ return encoded.split("/").map(decodeURIComponent).join("/");
26596
+ }
26597
+ function computeSig(secret, absPath, e) {
26598
+ const msg = e === null ? absPath : `${absPath}|${e}`;
26599
+ return import_node_crypto5.default.createHmac(HMAC_ALGO, secret).update(msg).digest();
26600
+ }
26601
+ function signUrlParts(secret, absPath, ttlSeconds, now = Date.now) {
26602
+ const e = ttlSeconds === null ? null : Math.floor(now() / 1e3) + ttlSeconds;
26603
+ const s = base64urlEncode(computeSig(secret, absPath, e));
26604
+ return { absPath, e, s };
26605
+ }
26606
+ function buildSignedFileUrl(httpBaseUrl, parts) {
26607
+ const encodedPath = encodeAbsPathForUrl(parts.absPath);
26608
+ const query = parts.e === null ? `s=${encodeURIComponent(parts.s)}` : `e=${parts.e}&s=${encodeURIComponent(parts.s)}`;
26609
+ const base = httpBaseUrl.endsWith("/") ? httpBaseUrl.slice(0, -1) : httpBaseUrl;
26610
+ return `${base}/files${encodedPath}?${query}`;
26611
+ }
26612
+ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
26613
+ let e;
26614
+ if (eRaw === null || eRaw === void 0 || eRaw === "") {
26615
+ e = null;
26616
+ } else {
26617
+ const parsed = Number.parseInt(eRaw, 10);
26618
+ if (!Number.isFinite(parsed) || parsed < 0) {
26619
+ return { ok: false, code: "MALFORMED" };
26620
+ }
26621
+ e = parsed;
26622
+ }
26623
+ if (!absPath || !s) return { ok: false, code: "MALFORMED" };
26624
+ const expected = computeSig(secret, absPath, e);
26625
+ let provided;
26626
+ try {
26627
+ provided = base64urlDecodeBuf(s);
26628
+ } catch {
26629
+ return { ok: false, code: "MALFORMED" };
26630
+ }
26631
+ if (provided.length !== expected.length) {
26632
+ return { ok: false, code: "BAD_SIG" };
26633
+ }
26634
+ if (!import_node_crypto5.default.timingSafeEqual(provided, expected)) {
26635
+ return { ok: false, code: "BAD_SIG" };
26636
+ }
26637
+ if (e !== null && now() / 1e3 > e) {
26638
+ return { ok: false, code: "EXPIRED" };
26639
+ }
26640
+ return { ok: true, absPath };
26577
26641
  }
26578
26642
 
26579
26643
  // src/transport/http-router.ts
@@ -26588,43 +26652,37 @@ function createHttpRouter(deps) {
26588
26652
  sendJson(res, 200, { ok: true, version: deps.daemonVersion });
26589
26653
  return true;
26590
26654
  }
26591
- if (!url.pathname.startsWith("/persona/") && !url.pathname.startsWith("/session/") && url.pathname !== "/outbox" && !url.pathname.startsWith("/outbox/")) {
26655
+ if (!url.pathname.startsWith("/persona/") && !url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/")) {
26592
26656
  return false;
26593
26657
  }
26594
- {
26595
- const directMatch = url.pathname.match(/^\/outbox\/([^/]+)$/);
26596
- const personaMatch = url.pathname.match(/^\/persona\/([^/]+)\/outbox\/([^/]+)$/);
26597
- if (directMatch || personaMatch) {
26598
- if (!deps.outboxStore) {
26599
- sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "outbox store not wired" });
26600
- return true;
26601
- }
26602
- const capToken = directMatch ? directMatch[1] : personaMatch[2];
26603
- const lookup = deps.outboxStore.lookup(capToken);
26604
- if (!lookup.ok) {
26605
- const statusByCode = {
26606
- NOT_FOUND: 404,
26607
- EXPIRED: 410,
26608
- REVOKED: 410
26609
- };
26610
- sendJson(res, statusByCode[lookup.code], { code: lookup.code, message: "outbox cap not usable" });
26611
- return true;
26612
- }
26613
- const { entry } = lookup;
26614
- if (entry.scope.kind === "personal") {
26615
- const ctx2 = deps.authResolver.resolveFromHeader(
26616
- req.headers.authorization,
26617
- req.socket.remoteAddress ?? void 0
26618
- );
26619
- if (!ctx2 || ctx2.role !== "personal" || ctx2.personaId !== entry.scope.personalId) {
26620
- sendJson(res, 403, { code: "FORBIDDEN", message: "personal-scoped cap requires matching token" });
26621
- return true;
26622
- }
26623
- }
26624
- const outboxStore = deps.outboxStore;
26625
- streamFile(res, entry.absPath, deps.logger, () => outboxStore.consume(capToken));
26658
+ if (url.pathname.startsWith("/files/") && req.method === "GET") {
26659
+ const secret = deps.getSignSecret?.();
26660
+ if (!secret) {
26661
+ sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "signed URL secret unavailable (noAuth?)" });
26662
+ return true;
26663
+ }
26664
+ const encodedPath = url.pathname.slice("/files".length);
26665
+ let absPath;
26666
+ try {
26667
+ absPath = decodeAbsPathFromUrl(encodedPath);
26668
+ } catch {
26669
+ sendJson(res, 400, { code: "MALFORMED", message: "invalid path encoding" });
26670
+ return true;
26671
+ }
26672
+ const e = url.searchParams.get("e");
26673
+ const s = url.searchParams.get("s") ?? "";
26674
+ const r = verifySignedUrl(secret, absPath, e, s);
26675
+ if (!r.ok) {
26676
+ const statusByCode = {
26677
+ BAD_SIG: 403,
26678
+ EXPIRED: 410,
26679
+ MALFORMED: 400
26680
+ };
26681
+ sendJson(res, statusByCode[r.code], { code: r.code, message: "signed URL invalid" });
26626
26682
  return true;
26627
26683
  }
26684
+ streamFile(res, r.absPath, deps.logger);
26685
+ return true;
26628
26686
  }
26629
26687
  const ctx = deps.authResolver.resolveFromHeader(
26630
26688
  req.headers.authorization,
@@ -26694,10 +26752,6 @@ function createHttpRouter(deps) {
26694
26752
  streamFile(res, absPath, deps.logger);
26695
26753
  return true;
26696
26754
  }
26697
- if (/^\/persona\/[^/]+\/attachment-meta$/.test(url.pathname) && req.method === "GET") {
26698
- sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "attachment-meta \u2014 PR 6" }));
26699
- return true;
26700
- }
26701
26755
  sendJson(res, 404, { code: "NOT_FOUND", message: `no route for ${req.method} ${url.pathname}` });
26702
26756
  return true;
26703
26757
  };
@@ -26723,7 +26777,7 @@ function isContainedIn(abs, root) {
26723
26777
  if (normalized === normalizedRoot) return true;
26724
26778
  return normalized.startsWith(normalizedRoot + import_node_path16.default.sep);
26725
26779
  }
26726
- function streamFile(res, absPath, logger, onComplete) {
26780
+ function streamFile(res, absPath, logger) {
26727
26781
  let stat;
26728
26782
  try {
26729
26783
  stat = import_node_fs14.default.statSync(absPath);
@@ -26741,10 +26795,12 @@ function streamFile(res, absPath, logger, onComplete) {
26741
26795
  return;
26742
26796
  }
26743
26797
  const mime = lookupMime(absPath);
26798
+ const basename = import_node_path16.default.basename(absPath);
26744
26799
  res.writeHead(200, {
26745
26800
  "Content-Type": mime,
26746
26801
  "Content-Length": String(stat.size),
26747
- // 防止浏览器把任意 mime 当 html 渲染(spec §11 #8 安全心智)
26802
+ "Content-Disposition": `inline; filename*=UTF-8''${encodeURIComponent(basename)}`,
26803
+ // 防止浏览器把任意 mime 当 html 渲染
26748
26804
  "X-Content-Type-Options": "nosniff"
26749
26805
  });
26750
26806
  const stream = import_node_fs14.default.createReadStream(absPath);
@@ -26752,228 +26808,14 @@ function streamFile(res, absPath, logger, onComplete) {
26752
26808
  logger?.warn("streamFile read error", { absPath, err: err.message });
26753
26809
  res.destroy();
26754
26810
  });
26755
- if (onComplete) {
26756
- res.once("finish", onComplete);
26757
- }
26758
26811
  stream.pipe(res);
26759
26812
  }
26760
26813
 
26761
- // src/attachment/outbox.ts
26814
+ // src/discovery/state-file.ts
26762
26815
  var import_node_fs15 = __toESM(require("fs"), 1);
26763
26816
  var import_node_path17 = __toESM(require("path"), 1);
26764
- var import_node_crypto5 = __toESM(require("crypto"), 1);
26765
- init_protocol();
26766
- var FILE_NAME = "outbox-caps.json";
26767
- var OutboxStore = class {
26768
- file;
26769
- now;
26770
- logger;
26771
- cache = [];
26772
- constructor(opts) {
26773
- this.file = import_node_path17.default.join(opts.dataDir, FILE_NAME);
26774
- this.now = opts.now ?? Date.now;
26775
- this.logger = opts.logger;
26776
- this.reload();
26777
- }
26778
- reload() {
26779
- try {
26780
- const raw = import_node_fs15.default.readFileSync(this.file, "utf8");
26781
- const parsed = JSON.parse(raw);
26782
- if (!Array.isArray(parsed)) {
26783
- this.logger?.warn("OutboxStore.reload: outbox-caps.json is not an array; resetting", {
26784
- file: this.file
26785
- });
26786
- this.cache = [];
26787
- return;
26788
- }
26789
- const out = [];
26790
- for (const item of parsed) {
26791
- const r = OutboxCapEntrySchema.safeParse(item);
26792
- if (r.success) out.push(r.data);
26793
- }
26794
- this.cache = out;
26795
- } catch (err) {
26796
- const code = err?.code;
26797
- if (code !== "ENOENT") {
26798
- this.logger?.warn("OutboxStore.reload failed; outbox-caps.json may be corrupt", {
26799
- file: this.file,
26800
- err: err.message
26801
- });
26802
- }
26803
- this.cache = [];
26804
- }
26805
- }
26806
- persist() {
26807
- import_node_fs15.default.mkdirSync(import_node_path17.default.dirname(this.file), { recursive: true });
26808
- const tmp = `${this.file}.tmp-${process.pid}-${Date.now()}`;
26809
- import_node_fs15.default.writeFileSync(tmp, JSON.stringify(this.cache, null, 2), { mode: 384 });
26810
- import_node_fs15.default.renameSync(tmp, this.file);
26811
- }
26812
- /** 列出当前缓存(含 revoked / 过期,UI Drawer 显灰;过滤靠调用方) */
26813
- list() {
26814
- return this.cache.slice();
26815
- }
26816
- /** 按 personaId / sessionId 过滤;为 outbox Drawer 服务 */
26817
- listByPersona(personaId) {
26818
- return this.cache.filter(
26819
- (c) => c.scopeRef.kind === "persona" && c.scopeRef.personaId === personaId
26820
- );
26821
- }
26822
- /**
26823
- * Mint a new cap. capToken = 32B 随机 base64url(256-bit entropy),spec §11 #8 防剪贴板/微信
26824
- * 截图泄露通过短码的暴力面。返回完整 entry 让 caller 拼 URL。
26825
- *
26826
- * @param ttlSeconds null = 永久;undefined = 默认 24h(spec §12)
26827
- */
26828
- mint(input) {
26829
- const ts = this.now();
26830
- const ttl = input.ttlSeconds === null ? null : input.ttlSeconds ?? 24 * 3600;
26831
- const entry = {
26832
- capToken: import_node_crypto5.default.randomBytes(32).toString("base64url"),
26833
- scopeRef: input.scopeRef,
26834
- absPath: input.absPath,
26835
- name: input.name,
26836
- expiresAt: ttl === null ? null : ts + ttl * 1e3,
26837
- oneShot: input.oneShot ?? false,
26838
- scope: input.scope ?? { kind: "public" },
26839
- hits: 0,
26840
- createdAt: ts
26841
- };
26842
- this.cache.push(entry);
26843
- this.persist();
26844
- return entry;
26845
- }
26846
- /** Mark revoked;不删除条目(便于审计),上层 lookup 后返回 410 */
26847
- revoke(capToken) {
26848
- const idx = this.cache.findIndex((c) => c.capToken === capToken);
26849
- if (idx < 0) return false;
26850
- if (this.cache[idx].revoked) return false;
26851
- this.cache[idx] = { ...this.cache[idx], revoked: true };
26852
- this.persist();
26853
- return true;
26854
- }
26855
- /**
26856
- * HTTP 层用:根据 capToken 找到 entry 并判定能不能用。
26857
- * 三种结果:
26858
- * - { ok: true, entry } 可用,调用方流文件
26859
- * - { ok: false, code: 'EXPIRED' | 'REVOKED' | 'NOT_FOUND' }
26860
- *
26861
- * 命中 + oneShot=true 时由调用方调 consume() 把 cache 中 hits 自增并立即 revoke
26862
- * (consume 应当在 response 'finish' 后才触发,避免中途断流误算消费)。
26863
- */
26864
- lookup(capToken) {
26865
- const entry = this.cache.find((c) => c.capToken === capToken);
26866
- if (!entry) return { ok: false, code: "NOT_FOUND" };
26867
- if (entry.revoked) return { ok: false, code: "REVOKED" };
26868
- if (entry.expiresAt !== null && this.now() > entry.expiresAt) {
26869
- return { ok: false, code: "EXPIRED" };
26870
- }
26871
- return { ok: true, entry };
26872
- }
26873
- /** 命中后真正"消费"一次:hits++ + oneShot 时 revoke。daemon HTTP 层 stream 成功后再调 */
26874
- consume(capToken) {
26875
- const idx = this.cache.findIndex((c) => c.capToken === capToken);
26876
- if (idx < 0) return;
26877
- const prev = this.cache[idx];
26878
- this.cache[idx] = {
26879
- ...prev,
26880
- hits: prev.hits + 1,
26881
- revoked: prev.oneShot ? true : prev.revoked
26882
- };
26883
- this.persist();
26884
- }
26885
- };
26886
-
26887
- // src/attachment/mount.ts
26888
- var import_node_fs16 = __toESM(require("fs"), 1);
26889
- var import_node_path18 = __toESM(require("path"), 1);
26890
- init_protocol();
26891
- var MountStore = class {
26892
- constructor(opts) {
26893
- this.opts = opts;
26894
- }
26895
- opts;
26896
- filePath(personaId) {
26897
- return import_node_path18.default.join(this.opts.personaDirPath(personaId), ".clawd", "shared-files.json");
26898
- }
26899
- list(personaId) {
26900
- try {
26901
- const raw = import_node_fs16.default.readFileSync(this.filePath(personaId), "utf8");
26902
- const parsed = JSON.parse(raw);
26903
- if (!Array.isArray(parsed)) return [];
26904
- const out = [];
26905
- for (const item of parsed) {
26906
- const r = MountEntrySchema.safeParse(item);
26907
- if (r.success) out.push(r.data);
26908
- }
26909
- return out;
26910
- } catch {
26911
- return [];
26912
- }
26913
- }
26914
- /**
26915
- * Add a mount。在 personaDir 创建 link/copy + 写 manifest。冲突 basename 抛错(caller
26916
- * 决定是覆盖还是改名)。
26917
- */
26918
- add(personaId, input) {
26919
- const personaDir = this.opts.personaDirPath(personaId);
26920
- if (!import_node_fs16.default.existsSync(personaDir)) {
26921
- throw new Error(`personaDir not found: ${personaDir}`);
26922
- }
26923
- const source = import_node_path18.default.resolve(input.absPath);
26924
- if (!import_node_fs16.default.existsSync(source)) {
26925
- throw new Error(`source path not found: ${source}`);
26926
- }
26927
- const basename = import_node_path18.default.basename(source);
26928
- const dest = import_node_path18.default.join(personaDir, basename);
26929
- if (import_node_fs16.default.existsSync(dest)) {
26930
- throw new Error(`destination already exists: ${dest}`);
26931
- }
26932
- if (input.mode === "link") {
26933
- import_node_fs16.default.symlinkSync(source, dest);
26934
- } else {
26935
- import_node_fs16.default.copyFileSync(source, dest);
26936
- }
26937
- const entry = {
26938
- basename,
26939
- absPath: source,
26940
- mode: input.mode
26941
- };
26942
- const next = this.list(personaId).concat([entry]);
26943
- this.writeManifest(personaId, next);
26944
- return entry;
26945
- }
26946
- remove(personaId, basename) {
26947
- const entries = this.list(personaId);
26948
- const idx = entries.findIndex((e) => e.basename === basename);
26949
- if (idx < 0) return false;
26950
- const entry = entries[idx];
26951
- const personaDir = this.opts.personaDirPath(personaId);
26952
- const dest = import_node_path18.default.join(personaDir, basename);
26953
- try {
26954
- import_node_fs16.default.unlinkSync(dest);
26955
- } catch {
26956
- }
26957
- const next = entries.slice();
26958
- next.splice(idx, 1);
26959
- this.writeManifest(personaId, next);
26960
- void entry;
26961
- return true;
26962
- }
26963
- writeManifest(personaId, entries) {
26964
- const file = this.filePath(personaId);
26965
- import_node_fs16.default.mkdirSync(import_node_path18.default.dirname(file), { recursive: true });
26966
- const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
26967
- import_node_fs16.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
26968
- import_node_fs16.default.renameSync(tmp, file);
26969
- }
26970
- };
26971
-
26972
- // src/discovery/state-file.ts
26973
- var import_node_fs17 = __toESM(require("fs"), 1);
26974
- var import_node_path19 = __toESM(require("path"), 1);
26975
26817
  function defaultStateFilePath(dataDir) {
26976
- return import_node_path19.default.join(dataDir, "state.json");
26818
+ return import_node_path17.default.join(dataDir, "state.json");
26977
26819
  }
26978
26820
  function isPidAlive(pid) {
26979
26821
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -26995,7 +26837,7 @@ var StateFileManager = class {
26995
26837
  }
26996
26838
  read() {
26997
26839
  try {
26998
- const raw = import_node_fs17.default.readFileSync(this.file, "utf8");
26840
+ const raw = import_node_fs15.default.readFileSync(this.file, "utf8");
26999
26841
  const parsed = JSON.parse(raw);
27000
26842
  return parsed;
27001
26843
  } catch {
@@ -27009,34 +26851,34 @@ var StateFileManager = class {
27009
26851
  return { status: "stale", existing };
27010
26852
  }
27011
26853
  write(state) {
27012
- import_node_fs17.default.mkdirSync(import_node_path19.default.dirname(this.file), { recursive: true });
26854
+ import_node_fs15.default.mkdirSync(import_node_path17.default.dirname(this.file), { recursive: true });
27013
26855
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
27014
- import_node_fs17.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
27015
- import_node_fs17.default.renameSync(tmp, this.file);
26856
+ import_node_fs15.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
26857
+ import_node_fs15.default.renameSync(tmp, this.file);
27016
26858
  if (process.platform !== "win32") {
27017
26859
  try {
27018
- import_node_fs17.default.chmodSync(this.file, 384);
26860
+ import_node_fs15.default.chmodSync(this.file, 384);
27019
26861
  } catch {
27020
26862
  }
27021
26863
  }
27022
26864
  }
27023
26865
  delete() {
27024
26866
  try {
27025
- import_node_fs17.default.unlinkSync(this.file);
26867
+ import_node_fs15.default.unlinkSync(this.file);
27026
26868
  } catch {
27027
26869
  }
27028
26870
  }
27029
26871
  };
27030
26872
 
27031
26873
  // src/tunnel/tunnel-manager.ts
27032
- var import_node_fs21 = __toESM(require("fs"), 1);
27033
- var import_node_path23 = __toESM(require("path"), 1);
26874
+ var import_node_fs19 = __toESM(require("fs"), 1);
26875
+ var import_node_path21 = __toESM(require("path"), 1);
27034
26876
  var import_node_crypto6 = __toESM(require("crypto"), 1);
27035
26877
  var import_node_child_process5 = require("child_process");
27036
26878
 
27037
26879
  // src/tunnel/tunnel-store.ts
27038
- var import_node_fs18 = __toESM(require("fs"), 1);
27039
- var import_node_path20 = __toESM(require("path"), 1);
26880
+ var import_node_fs16 = __toESM(require("fs"), 1);
26881
+ var import_node_path18 = __toESM(require("path"), 1);
27040
26882
  var TunnelStore = class {
27041
26883
  constructor(filePath) {
27042
26884
  this.filePath = filePath;
@@ -27044,7 +26886,7 @@ var TunnelStore = class {
27044
26886
  filePath;
27045
26887
  async get() {
27046
26888
  try {
27047
- const raw = await import_node_fs18.default.promises.readFile(this.filePath, "utf8");
26889
+ const raw = await import_node_fs16.default.promises.readFile(this.filePath, "utf8");
27048
26890
  const obj = JSON.parse(raw);
27049
26891
  if (!isPersistedTunnel(obj)) return null;
27050
26892
  return obj;
@@ -27055,22 +26897,22 @@ var TunnelStore = class {
27055
26897
  }
27056
26898
  }
27057
26899
  async set(v2) {
27058
- const dir = import_node_path20.default.dirname(this.filePath);
27059
- await import_node_fs18.default.promises.mkdir(dir, { recursive: true });
26900
+ const dir = import_node_path18.default.dirname(this.filePath);
26901
+ await import_node_fs16.default.promises.mkdir(dir, { recursive: true });
27060
26902
  const data = JSON.stringify(v2, null, 2);
27061
26903
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
27062
- await import_node_fs18.default.promises.writeFile(tmp, data, { mode: 384 });
26904
+ await import_node_fs16.default.promises.writeFile(tmp, data, { mode: 384 });
27063
26905
  if (process.platform !== "win32") {
27064
26906
  try {
27065
- await import_node_fs18.default.promises.chmod(tmp, 384);
26907
+ await import_node_fs16.default.promises.chmod(tmp, 384);
27066
26908
  } catch {
27067
26909
  }
27068
26910
  }
27069
- await import_node_fs18.default.promises.rename(tmp, this.filePath);
26911
+ await import_node_fs16.default.promises.rename(tmp, this.filePath);
27070
26912
  }
27071
26913
  async clear() {
27072
26914
  try {
27073
- await import_node_fs18.default.promises.unlink(this.filePath);
26915
+ await import_node_fs16.default.promises.unlink(this.filePath);
27074
26916
  } catch (err) {
27075
26917
  const code = err?.code;
27076
26918
  if (code !== "ENOENT") throw err;
@@ -27165,9 +27007,9 @@ function escape(v2) {
27165
27007
  }
27166
27008
 
27167
27009
  // src/tunnel/frpc-binary.ts
27168
- var import_node_fs19 = __toESM(require("fs"), 1);
27010
+ var import_node_fs17 = __toESM(require("fs"), 1);
27169
27011
  var import_node_os9 = __toESM(require("os"), 1);
27170
- var import_node_path21 = __toESM(require("path"), 1);
27012
+ var import_node_path19 = __toESM(require("path"), 1);
27171
27013
  var import_node_child_process3 = require("child_process");
27172
27014
  var import_node_stream2 = require("stream");
27173
27015
  var import_promises = require("stream/promises");
@@ -27199,20 +27041,20 @@ function frpcDownloadUrl(version2, p2) {
27199
27041
  }
27200
27042
  async function ensureFrpcBinary(opts) {
27201
27043
  if (opts.override) {
27202
- if (!import_node_fs19.default.existsSync(opts.override)) {
27044
+ if (!import_node_fs17.default.existsSync(opts.override)) {
27203
27045
  throw new Error(`frpc binary not found at override path: ${opts.override}`);
27204
27046
  }
27205
27047
  return opts.override;
27206
27048
  }
27207
27049
  const version2 = opts.version ?? FRPC_VERSION;
27208
27050
  const platform = opts.platform ?? detectPlatform();
27209
- const binDir = import_node_path21.default.join(opts.dataDir, "bin");
27210
- import_node_fs19.default.mkdirSync(binDir, { recursive: true });
27051
+ const binDir = import_node_path19.default.join(opts.dataDir, "bin");
27052
+ import_node_fs17.default.mkdirSync(binDir, { recursive: true });
27211
27053
  cleanupStaleArtifacts(binDir);
27212
- const stableBin = import_node_path21.default.join(binDir, "frpc");
27213
- if (import_node_fs19.default.existsSync(stableBin)) return stableBin;
27054
+ const stableBin = import_node_path19.default.join(binDir, "frpc");
27055
+ if (import_node_fs17.default.existsSync(stableBin)) return stableBin;
27214
27056
  const partialBin = `${stableBin}.partial`;
27215
- const tarballPath = import_node_path21.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
27057
+ const tarballPath = import_node_path19.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
27216
27058
  try {
27217
27059
  const url = frpcDownloadUrl(version2, platform);
27218
27060
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -27221,8 +27063,8 @@ async function ensureFrpcBinary(opts) {
27221
27063
  } else {
27222
27064
  await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
27223
27065
  }
27224
- import_node_fs19.default.chmodSync(partialBin, 493);
27225
- import_node_fs19.default.renameSync(partialBin, stableBin);
27066
+ import_node_fs17.default.chmodSync(partialBin, 493);
27067
+ import_node_fs17.default.renameSync(partialBin, stableBin);
27226
27068
  } finally {
27227
27069
  safeUnlink(tarballPath);
27228
27070
  safeUnlink(partialBin);
@@ -27232,15 +27074,15 @@ async function ensureFrpcBinary(opts) {
27232
27074
  function cleanupStaleArtifacts(binDir) {
27233
27075
  let entries;
27234
27076
  try {
27235
- entries = import_node_fs19.default.readdirSync(binDir);
27077
+ entries = import_node_fs17.default.readdirSync(binDir);
27236
27078
  } catch {
27237
27079
  return;
27238
27080
  }
27239
27081
  for (const name of entries) {
27240
27082
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
27241
- const full = import_node_path21.default.join(binDir, name);
27083
+ const full = import_node_path19.default.join(binDir, name);
27242
27084
  try {
27243
- import_node_fs19.default.rmSync(full, { recursive: true, force: true });
27085
+ import_node_fs17.default.rmSync(full, { recursive: true, force: true });
27244
27086
  } catch {
27245
27087
  }
27246
27088
  }
@@ -27248,7 +27090,7 @@ function cleanupStaleArtifacts(binDir) {
27248
27090
  }
27249
27091
  function safeUnlink(p2) {
27250
27092
  try {
27251
- import_node_fs19.default.unlinkSync(p2);
27093
+ import_node_fs17.default.unlinkSync(p2);
27252
27094
  } catch {
27253
27095
  }
27254
27096
  }
@@ -27259,13 +27101,13 @@ async function downloadToFile(url, dest, fetchImpl) {
27259
27101
  if (!res.ok || !res.body) {
27260
27102
  throw new Error(`download failed: ${res.status} ${res.statusText}`);
27261
27103
  }
27262
- const out = import_node_fs19.default.createWriteStream(dest);
27104
+ const out = import_node_fs17.default.createWriteStream(dest);
27263
27105
  const nodeStream = import_node_stream2.Readable.fromWeb(res.body);
27264
27106
  await (0, import_promises.pipeline)(nodeStream, out);
27265
27107
  }
27266
27108
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
27267
- const work = import_node_path21.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
27268
- import_node_fs19.default.mkdirSync(work, { recursive: true });
27109
+ const work = import_node_path19.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
27110
+ import_node_fs17.default.mkdirSync(work, { recursive: true });
27269
27111
  try {
27270
27112
  await new Promise((resolve2, reject) => {
27271
27113
  const proc = (0, import_node_child_process3.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
@@ -27273,32 +27115,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
27273
27115
  proc.on("exit", (code) => code === 0 ? resolve2() : reject(new Error(`tar exited ${code}`)));
27274
27116
  });
27275
27117
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
27276
- const src = import_node_path21.default.join(work, dirName, "frpc");
27277
- if (!import_node_fs19.default.existsSync(src)) {
27118
+ const src = import_node_path19.default.join(work, dirName, "frpc");
27119
+ if (!import_node_fs17.default.existsSync(src)) {
27278
27120
  throw new Error(`frpc not found inside tarball at ${src}`);
27279
27121
  }
27280
- import_node_fs19.default.copyFileSync(src, destBin);
27122
+ import_node_fs17.default.copyFileSync(src, destBin);
27281
27123
  } finally {
27282
- import_node_fs19.default.rmSync(work, { recursive: true, force: true });
27124
+ import_node_fs17.default.rmSync(work, { recursive: true, force: true });
27283
27125
  }
27284
27126
  }
27285
27127
 
27286
27128
  // src/tunnel/frpc-process.ts
27287
- var import_node_fs20 = __toESM(require("fs"), 1);
27288
- var import_node_path22 = __toESM(require("path"), 1);
27129
+ var import_node_fs18 = __toESM(require("fs"), 1);
27130
+ var import_node_path20 = __toESM(require("path"), 1);
27289
27131
  var import_node_child_process4 = require("child_process");
27290
27132
  function frpcPidFilePath(dataDir) {
27291
- return import_node_path22.default.join(dataDir, "frpc.pid");
27133
+ return import_node_path20.default.join(dataDir, "frpc.pid");
27292
27134
  }
27293
27135
  function writeFrpcPid(dataDir, pid) {
27294
27136
  try {
27295
- import_node_fs20.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
27137
+ import_node_fs18.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
27296
27138
  } catch {
27297
27139
  }
27298
27140
  }
27299
27141
  function clearFrpcPid(dataDir) {
27300
27142
  try {
27301
- import_node_fs20.default.unlinkSync(frpcPidFilePath(dataDir));
27143
+ import_node_fs18.default.unlinkSync(frpcPidFilePath(dataDir));
27302
27144
  } catch {
27303
27145
  }
27304
27146
  }
@@ -27314,7 +27156,7 @@ function defaultIsPidAlive(pid) {
27314
27156
  }
27315
27157
  function defaultReadPidFile(file) {
27316
27158
  try {
27317
- return import_node_fs20.default.readFileSync(file, "utf8");
27159
+ return import_node_fs18.default.readFileSync(file, "utf8");
27318
27160
  } catch {
27319
27161
  return null;
27320
27162
  }
@@ -27330,7 +27172,7 @@ function defaultSleep(ms) {
27330
27172
  }
27331
27173
  async function killStaleFrpc(deps) {
27332
27174
  const pidFile = frpcPidFilePath(deps.dataDir);
27333
- const tomlPath = import_node_path22.default.join(deps.dataDir, "frpc.toml");
27175
+ const tomlPath = import_node_path20.default.join(deps.dataDir, "frpc.toml");
27334
27176
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
27335
27177
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
27336
27178
  const killPid = deps.killPidImpl ?? defaultKillPid;
@@ -27354,7 +27196,7 @@ async function killStaleFrpc(deps) {
27354
27196
  }
27355
27197
  if (victims.size === 0) {
27356
27198
  try {
27357
- import_node_fs20.default.unlinkSync(pidFile);
27199
+ import_node_fs18.default.unlinkSync(pidFile);
27358
27200
  } catch {
27359
27201
  }
27360
27202
  return;
@@ -27365,7 +27207,7 @@ async function killStaleFrpc(deps) {
27365
27207
  }
27366
27208
  await sleep(deps.reapWaitMs ?? 300);
27367
27209
  try {
27368
- import_node_fs20.default.unlinkSync(pidFile);
27210
+ import_node_fs18.default.unlinkSync(pidFile);
27369
27211
  } catch {
27370
27212
  }
27371
27213
  }
@@ -27402,7 +27244,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
27402
27244
  var TunnelManager = class {
27403
27245
  constructor(deps) {
27404
27246
  this.deps = deps;
27405
- this.store = deps.store ?? new TunnelStore(import_node_path23.default.join(deps.dataDir, "tunnel.json"));
27247
+ this.store = deps.store ?? new TunnelStore(import_node_path21.default.join(deps.dataDir, "tunnel.json"));
27406
27248
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
27407
27249
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
27408
27250
  }
@@ -27529,7 +27371,7 @@ var TunnelManager = class {
27529
27371
  dataDir: this.deps.dataDir,
27530
27372
  override: this.deps.frpcBinaryOverride ?? void 0
27531
27373
  });
27532
- const tomlPath = import_node_path23.default.join(this.deps.dataDir, "frpc.toml");
27374
+ const tomlPath = import_node_path21.default.join(this.deps.dataDir, "frpc.toml");
27533
27375
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto6.default.randomBytes(3).toString("hex")}`;
27534
27376
  const toml = buildFrpcToml({
27535
27377
  serverAddr: t.frpsHost,
@@ -27540,12 +27382,12 @@ var TunnelManager = class {
27540
27382
  localPort,
27541
27383
  logLevel: "info"
27542
27384
  });
27543
- await import_node_fs21.default.promises.writeFile(tomlPath, toml, { mode: 384 });
27385
+ await import_node_fs19.default.promises.writeFile(tomlPath, toml, { mode: 384 });
27544
27386
  const proc = (this.deps.spawnImpl ?? import_node_child_process5.spawn)(frpcBin, ["-c", tomlPath], {
27545
27387
  stdio: ["ignore", "pipe", "pipe"]
27546
27388
  });
27547
- const logFilePath = import_node_path23.default.join(this.deps.dataDir, "frpc.log");
27548
- const logStream = import_node_fs21.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
27389
+ const logFilePath = import_node_path21.default.join(this.deps.dataDir, "frpc.log");
27390
+ const logStream = import_node_fs19.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
27549
27391
  logStream.on("error", () => {
27550
27392
  });
27551
27393
  const tee = (chunk) => {
@@ -27638,12 +27480,12 @@ function deriveStableDeviceKey(opts = {}) {
27638
27480
  }
27639
27481
 
27640
27482
  // src/auth-store.ts
27641
- var import_node_fs22 = __toESM(require("fs"), 1);
27642
- var import_node_path24 = __toESM(require("path"), 1);
27483
+ var import_node_fs20 = __toESM(require("fs"), 1);
27484
+ var import_node_path22 = __toESM(require("path"), 1);
27643
27485
  var import_node_crypto8 = __toESM(require("crypto"), 1);
27644
27486
  var AUTH_FILE_NAME = "auth.json";
27645
27487
  function authFilePath(dataDir) {
27646
- return import_node_path24.default.join(dataDir, AUTH_FILE_NAME);
27488
+ return import_node_path22.default.join(dataDir, AUTH_FILE_NAME);
27647
27489
  }
27648
27490
  function loadOrCreateAuthToken(opts) {
27649
27491
  const file = authFilePath(opts.dataDir);
@@ -27659,7 +27501,7 @@ function defaultGenerate() {
27659
27501
  }
27660
27502
  function readAuthFile(file) {
27661
27503
  try {
27662
- const raw = import_node_fs22.default.readFileSync(file, "utf8");
27504
+ const raw = import_node_fs20.default.readFileSync(file, "utf8");
27663
27505
  const parsed = JSON.parse(raw);
27664
27506
  if (typeof parsed?.token === "string" && parsed.token.length > 0) {
27665
27507
  return {
@@ -27675,25 +27517,25 @@ function readAuthFile(file) {
27675
27517
  }
27676
27518
  }
27677
27519
  function writeAuthFile(file, content) {
27678
- import_node_fs22.default.mkdirSync(import_node_path24.default.dirname(file), { recursive: true });
27679
- import_node_fs22.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
27520
+ import_node_fs20.default.mkdirSync(import_node_path22.default.dirname(file), { recursive: true });
27521
+ import_node_fs20.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
27680
27522
  try {
27681
- import_node_fs22.default.chmodSync(file, 384);
27523
+ import_node_fs20.default.chmodSync(file, 384);
27682
27524
  } catch {
27683
27525
  }
27684
27526
  }
27685
27527
 
27686
27528
  // src/owner-profile.ts
27687
- var import_node_fs23 = __toESM(require("fs"), 1);
27529
+ var import_node_fs21 = __toESM(require("fs"), 1);
27688
27530
  var import_node_os11 = __toESM(require("os"), 1);
27689
- var import_node_path25 = __toESM(require("path"), 1);
27531
+ var import_node_path23 = __toESM(require("path"), 1);
27690
27532
  var PROFILE_FILENAME = "profile.json";
27691
27533
  function loadOwnerDisplayName(dataDir) {
27692
27534
  const fallback = import_node_os11.default.userInfo().username;
27693
- const profilePath = import_node_path25.default.join(dataDir, PROFILE_FILENAME);
27535
+ const profilePath = import_node_path23.default.join(dataDir, PROFILE_FILENAME);
27694
27536
  let raw;
27695
27537
  try {
27696
- raw = import_node_fs23.default.readFileSync(profilePath, "utf8");
27538
+ raw = import_node_fs21.default.readFileSync(profilePath, "utf8");
27697
27539
  } catch {
27698
27540
  return fallback;
27699
27541
  }
@@ -27722,12 +27564,12 @@ init_protocol();
27722
27564
  init_protocol();
27723
27565
 
27724
27566
  // src/session/fork.ts
27725
- var import_node_fs24 = __toESM(require("fs"), 1);
27567
+ var import_node_fs22 = __toESM(require("fs"), 1);
27726
27568
  var import_node_os12 = __toESM(require("os"), 1);
27727
- var import_node_path26 = __toESM(require("path"), 1);
27569
+ var import_node_path24 = __toESM(require("path"), 1);
27728
27570
  init_claude_history();
27729
27571
  function readJsonlEntries(file) {
27730
- const raw = import_node_fs24.default.readFileSync(file, "utf8");
27572
+ const raw = import_node_fs22.default.readFileSync(file, "utf8");
27731
27573
  const out = [];
27732
27574
  for (const line of raw.split("\n")) {
27733
27575
  const t = line.trim();
@@ -27740,10 +27582,10 @@ function readJsonlEntries(file) {
27740
27582
  return out;
27741
27583
  }
27742
27584
  function forkSession(input) {
27743
- const baseDir = input.baseDir ?? import_node_path26.default.join(import_node_os12.default.homedir(), ".claude");
27744
- const projectDir = import_node_path26.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
27745
- const sourceFile = import_node_path26.default.join(projectDir, `${input.toolSessionId}.jsonl`);
27746
- if (!import_node_fs24.default.existsSync(sourceFile)) {
27585
+ const baseDir = input.baseDir ?? import_node_path24.default.join(import_node_os12.default.homedir(), ".claude");
27586
+ const projectDir = import_node_path24.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
27587
+ const sourceFile = import_node_path24.default.join(projectDir, `${input.toolSessionId}.jsonl`);
27588
+ if (!import_node_fs22.default.existsSync(sourceFile)) {
27747
27589
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
27748
27590
  }
27749
27591
  const entries = readJsonlEntries(sourceFile);
@@ -27773,9 +27615,9 @@ function forkSession(input) {
27773
27615
  }
27774
27616
  forkedLines.push(JSON.stringify(forked));
27775
27617
  }
27776
- const forkedFilePath = import_node_path26.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
27777
- import_node_fs24.default.mkdirSync(projectDir, { recursive: true });
27778
- import_node_fs24.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
27618
+ const forkedFilePath = import_node_path24.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
27619
+ import_node_fs22.default.mkdirSync(projectDir, { recursive: true });
27620
+ import_node_fs22.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
27779
27621
  return { forkedToolSessionId, forkedFilePath };
27780
27622
  }
27781
27623
 
@@ -28096,9 +27938,9 @@ init_protocol();
28096
27938
 
28097
27939
  // src/workspace/git.ts
28098
27940
  var import_node_child_process6 = require("child_process");
28099
- var import_node_fs25 = __toESM(require("fs"), 1);
27941
+ var import_node_fs23 = __toESM(require("fs"), 1);
28100
27942
  var import_node_os13 = __toESM(require("os"), 1);
28101
- var import_node_path27 = __toESM(require("path"), 1);
27943
+ var import_node_path25 = __toESM(require("path"), 1);
28102
27944
  var import_node_util = require("util");
28103
27945
  var pexec = (0, import_node_util.promisify)(import_node_child_process6.execFile);
28104
27946
  function formatChildProcessError(err) {
@@ -28113,9 +27955,9 @@ function formatChildProcessError(err) {
28113
27955
  return e.message ?? "unknown error";
28114
27956
  }
28115
27957
  function normalizePath(p2) {
28116
- const resolved = import_node_path27.default.resolve(p2);
27958
+ const resolved = import_node_path25.default.resolve(p2);
28117
27959
  try {
28118
- return import_node_fs25.default.realpathSync(resolved);
27960
+ return import_node_fs23.default.realpathSync(resolved);
28119
27961
  } catch {
28120
27962
  return resolved;
28121
27963
  }
@@ -28216,13 +28058,13 @@ function flattenToDirName(branch) {
28216
28058
  }
28217
28059
  function encodeClaudeProjectDir(absPath) {
28218
28060
  if (!absPath || typeof absPath !== "string") return "";
28219
- let canonical = import_node_path27.default.resolve(absPath);
28061
+ let canonical = import_node_path25.default.resolve(absPath);
28220
28062
  try {
28221
- canonical = import_node_fs25.default.realpathSync(canonical);
28063
+ canonical = import_node_fs23.default.realpathSync(canonical);
28222
28064
  } catch {
28223
28065
  try {
28224
- const parent = import_node_fs25.default.realpathSync(import_node_path27.default.dirname(canonical));
28225
- canonical = import_node_path27.default.join(parent, import_node_path27.default.basename(canonical));
28066
+ const parent = import_node_fs23.default.realpathSync(import_node_path25.default.dirname(canonical));
28067
+ canonical = import_node_path25.default.join(parent, import_node_path25.default.basename(canonical));
28226
28068
  } catch {
28227
28069
  }
28228
28070
  }
@@ -28246,11 +28088,11 @@ async function createWorktree(input) {
28246
28088
  if (!isGitRoot) {
28247
28089
  throw new Error(`\u76EE\u5F55 ${cwd} \u4E0D\u662F git repo \u6839`);
28248
28090
  }
28249
- const parent = import_node_path27.default.dirname(import_node_path27.default.resolve(cwd));
28250
- if (parent === "/" || parent === import_node_path27.default.resolve(cwd)) {
28091
+ const parent = import_node_path25.default.dirname(import_node_path25.default.resolve(cwd));
28092
+ if (parent === "/" || parent === import_node_path25.default.resolve(cwd)) {
28251
28093
  throw new Error("repo \u5728\u78C1\u76D8\u6839\u76EE\u5F55\uFF0C\u65E0\u6CD5\u5728\u540C\u7EA7\u521B\u5EFA worktree");
28252
28094
  }
28253
- const worktreeRoot = import_node_path27.default.join(parent, dirName);
28095
+ const worktreeRoot = import_node_path25.default.join(parent, dirName);
28254
28096
  try {
28255
28097
  await pexec("git", ["-C", cwd, "fetch", "origin", baseBranch, "--no-tags"], {
28256
28098
  timeout: 3e4
@@ -28269,7 +28111,7 @@ async function createWorktree(input) {
28269
28111
  const msg = err.message;
28270
28112
  if (msg.startsWith("\u5206\u652F ")) throw err;
28271
28113
  }
28272
- if (import_node_fs25.default.existsSync(worktreeRoot)) {
28114
+ if (import_node_fs23.default.existsSync(worktreeRoot)) {
28273
28115
  throw new Error(`\u76EE\u5F55 ${worktreeRoot} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u6362\u4E00\u4E2A label \u6216\u6E05\u7406\u540E\u91CD\u8BD5`);
28274
28116
  }
28275
28117
  try {
@@ -28297,8 +28139,8 @@ async function removeWorktree(input) {
28297
28139
  );
28298
28140
  const gitCommonDir = stdout.trim();
28299
28141
  if (!gitCommonDir) throw new Error("empty git-common-dir");
28300
- const absGitCommon = import_node_path27.default.isAbsolute(gitCommonDir) ? gitCommonDir : import_node_path27.default.resolve(worktreeRoot, gitCommonDir);
28301
- repoRoot = import_node_path27.default.dirname(absGitCommon);
28142
+ const absGitCommon = import_node_path25.default.isAbsolute(gitCommonDir) ? gitCommonDir : import_node_path25.default.resolve(worktreeRoot, gitCommonDir);
28143
+ repoRoot = import_node_path25.default.dirname(absGitCommon);
28302
28144
  } catch {
28303
28145
  repoRoot = null;
28304
28146
  }
@@ -28310,7 +28152,7 @@ async function removeWorktree(input) {
28310
28152
  } catch (err) {
28311
28153
  const stderr = err.stderr ?? "";
28312
28154
  const lower = stderr.toLowerCase();
28313
- const vanished = lower.includes("not a working tree") || lower.includes("is not a working tree") || !import_node_fs25.default.existsSync(worktreeRoot);
28155
+ const vanished = lower.includes("not a working tree") || lower.includes("is not a working tree") || !import_node_fs23.default.existsSync(worktreeRoot);
28314
28156
  if (!vanished) {
28315
28157
  throw new Error(`\u6E05\u7406 worktree \u5931\u8D25\uFF1A${formatChildProcessError(err)}`);
28316
28158
  }
@@ -28329,10 +28171,10 @@ async function removeWorktree(input) {
28329
28171
  try {
28330
28172
  const encoded = encodeClaudeProjectDir(worktreeRoot);
28331
28173
  if (encoded) {
28332
- const projectsRoot = import_node_path27.default.join(import_node_os13.default.homedir(), ".claude", "projects");
28333
- const target = import_node_path27.default.resolve(projectsRoot, encoded);
28334
- if (target.startsWith(projectsRoot + import_node_path27.default.sep) && target !== projectsRoot) {
28335
- import_node_fs25.default.rmSync(target, { recursive: true, force: true });
28174
+ const projectsRoot = import_node_path25.default.join(import_node_os13.default.homedir(), ".claude", "projects");
28175
+ const target = import_node_path25.default.resolve(projectsRoot, encoded);
28176
+ if (target.startsWith(projectsRoot + import_node_path25.default.sep) && target !== projectsRoot) {
28177
+ import_node_fs23.default.rmSync(target, { recursive: true, force: true });
28336
28178
  }
28337
28179
  }
28338
28180
  } catch {
@@ -28554,115 +28396,39 @@ function buildPersonaHandlers(deps) {
28554
28396
  // src/handlers/attachment.ts
28555
28397
  init_protocol();
28556
28398
  init_protocol();
28399
+ var DEFAULT_TTL_SECONDS = 24 * 3600;
28557
28400
  function buildAttachmentHandlers(deps) {
28558
- const outboxCreate = async (frame) => {
28559
- const parsed = AttachmentOutboxCreateArgs.safeParse(frame);
28401
+ const signUrl = async (frame) => {
28402
+ const parsed = AttachmentSignUrlArgs.safeParse(frame);
28560
28403
  if (!parsed.success) {
28561
28404
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, parsed.error.message);
28562
28405
  }
28563
28406
  const args = parsed.data;
28564
- if (args.personaId && args.sessionId || !args.personaId && !args.sessionId) {
28407
+ const secret = deps.getSignSecret();
28408
+ if (!secret) {
28565
28409
  throw new ClawdError(
28566
- ERROR_CODES.VALIDATION_ERROR,
28567
- "outboxCreate requires exactly one of personaId / sessionId"
28410
+ ERROR_CODES.METHOD_NOT_IMPLEMENTED,
28411
+ "signUrl requires an owner token (daemon noAuth mode disables share URLs)"
28568
28412
  );
28569
28413
  }
28570
- const scopeRef = deps.getPersonaScopeForRequest(args);
28571
- if (!scopeRef) {
28572
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "invalid scope");
28573
- }
28574
- const name = args.absPath.split("/").pop() || args.absPath;
28575
- const entry = deps.outboxStore.mint({
28576
- scopeRef,
28577
- absPath: args.absPath,
28578
- name,
28579
- ttlSeconds: args.ttlSeconds,
28580
- oneShot: args.oneShot,
28581
- scope: args.scope
28582
- });
28583
28414
  const httpBaseUrl = deps.getHttpBaseUrl();
28584
- const urlPath = scopeRef.kind === "persona" ? `/persona/${encodeURIComponent(scopeRef.personaId)}/outbox/${entry.capToken}` : `/outbox/${entry.capToken}`;
28585
- const url = httpBaseUrl ? `${httpBaseUrl}${urlPath}` : urlPath;
28415
+ if (!httpBaseUrl) {
28416
+ throw new ClawdError(
28417
+ ERROR_CODES.METHOD_NOT_IMPLEMENTED,
28418
+ "httpBaseUrl unavailable (daemon HTTP not ready)"
28419
+ );
28420
+ }
28421
+ const ttl = args.ttlSeconds === null ? null : args.ttlSeconds ?? DEFAULT_TTL_SECONDS;
28422
+ const parts = signUrlParts(secret, args.absPath, ttl);
28423
+ const url = buildSignedFileUrl(httpBaseUrl, parts);
28586
28424
  return {
28587
28425
  response: {
28588
- type: "attachment.outboxCreate",
28589
- capToken: entry.capToken,
28426
+ type: "attachment.signUrl",
28590
28427
  url,
28591
- expiresAt: entry.expiresAt
28428
+ expiresAt: parts.e === null ? null : parts.e * 1e3
28592
28429
  }
28593
28430
  };
28594
28431
  };
28595
- const outboxRevoke = async (frame) => {
28596
- const parsed = AttachmentOutboxRevokeArgs.safeParse(frame);
28597
- if (!parsed.success) {
28598
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, parsed.error.message);
28599
- }
28600
- const ok = deps.outboxStore.revoke(parsed.data.capToken);
28601
- if (!ok) {
28602
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `capToken not found / already revoked`);
28603
- }
28604
- return { response: { type: "attachment.outboxRevoke", revoked: true } };
28605
- };
28606
- const outboxList = async (frame) => {
28607
- const parsed = AttachmentOutboxListArgs.safeParse(frame);
28608
- if (!parsed.success) {
28609
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, parsed.error.message);
28610
- }
28611
- const entries = parsed.data.personaId ? deps.outboxStore.listByPersona(parsed.data.personaId) : deps.outboxStore.list();
28612
- return {
28613
- response: { type: "attachment.outboxList", entries }
28614
- };
28615
- };
28616
- const mountAdd = async (frame) => {
28617
- if (!deps.mountStore) {
28618
- throw new ClawdError(ERROR_CODES.METHOD_NOT_IMPLEMENTED, "mountStore not wired");
28619
- }
28620
- const parsed = AttachmentMountAddArgs.safeParse(frame);
28621
- if (!parsed.success) {
28622
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, parsed.error.message);
28623
- }
28624
- const args = parsed.data;
28625
- try {
28626
- const entry = deps.mountStore.add(args.personaId, {
28627
- absPath: args.absPath,
28628
- mode: args.mode
28629
- });
28630
- return {
28631
- response: {
28632
- type: "attachment.mountAdd",
28633
- entry,
28634
- sandboxRestartNeeded: true
28635
- }
28636
- };
28637
- } catch (err) {
28638
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, err.message);
28639
- }
28640
- };
28641
- const mountRemove = async (frame) => {
28642
- if (!deps.mountStore) {
28643
- throw new ClawdError(ERROR_CODES.METHOD_NOT_IMPLEMENTED, "mountStore not wired");
28644
- }
28645
- const parsed = AttachmentMountRemoveArgs.safeParse(frame);
28646
- if (!parsed.success) {
28647
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, parsed.error.message);
28648
- }
28649
- const ok = deps.mountStore.remove(parsed.data.personaId, parsed.data.basename);
28650
- if (!ok) {
28651
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "mount not found");
28652
- }
28653
- return { response: { type: "attachment.mountRemove", removed: true } };
28654
- };
28655
- const mountList = async (frame) => {
28656
- if (!deps.mountStore) {
28657
- throw new ClawdError(ERROR_CODES.METHOD_NOT_IMPLEMENTED, "mountStore not wired");
28658
- }
28659
- const parsed = AttachmentMountListArgs.safeParse(frame);
28660
- if (!parsed.success) {
28661
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, parsed.error.message);
28662
- }
28663
- const entries = deps.mountStore.list(parsed.data.personaId);
28664
- return { response: { type: "attachment.mountList", entries } };
28665
- };
28666
28432
  const groupAdd = async (frame) => {
28667
28433
  if (!deps.groupFileStore || !deps.getSessionScope) {
28668
28434
  throw new ClawdError(ERROR_CODES.METHOD_NOT_IMPLEMENTED, "groupFileStore not wired");
@@ -28732,12 +28498,7 @@ function buildAttachmentHandlers(deps) {
28732
28498
  return { response: { type: "attachment.groupListPersona", perSession } };
28733
28499
  };
28734
28500
  return {
28735
- "attachment.outboxCreate": outboxCreate,
28736
- "attachment.outboxRevoke": outboxRevoke,
28737
- "attachment.outboxList": outboxList,
28738
- "attachment.mountAdd": mountAdd,
28739
- "attachment.mountRemove": mountRemove,
28740
- "attachment.mountList": mountList,
28501
+ "attachment.signUrl": signUrl,
28741
28502
  "attachment.groupAdd": groupAdd,
28742
28503
  "attachment.groupRemove": groupRemove,
28743
28504
  "attachment.groupList": groupList,
@@ -28769,7 +28530,7 @@ function buildMethodHandlers(deps) {
28769
28530
  async function startDaemon(config) {
28770
28531
  const logger = createLogger({
28771
28532
  level: config.logLevel,
28772
- file: import_node_path28.default.join(config.dataDir, "clawd.log")
28533
+ file: import_node_path26.default.join(config.dataDir, "clawd.log")
28773
28534
  });
28774
28535
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
28775
28536
  const stateMgr = new StateFileManager({ dataDir: config.dataDir });
@@ -28801,7 +28562,7 @@ async function startDaemon(config) {
28801
28562
  const agents = new AgentsScanner();
28802
28563
  const history = new ClaudeHistoryReader();
28803
28564
  let transport = null;
28804
- const personaStore = new PersonaStore(import_node_path28.default.join(config.dataDir, "personas"));
28565
+ const personaStore = new PersonaStore(import_node_path26.default.join(config.dataDir, "personas"));
28805
28566
  const defaultsRoot = findDefaultsRoot();
28806
28567
  if (defaultsRoot) {
28807
28568
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -28810,17 +28571,13 @@ async function startDaemon(config) {
28810
28571
  }
28811
28572
  const ownerDisplayName = loadOwnerDisplayName(config.dataDir);
28812
28573
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
28813
- const outboxStore = new OutboxStore({ dataDir: config.dataDir, logger });
28814
- const mountStore = new MountStore({
28815
- personaDirPath: (pid) => personaStore.personaDirPath(pid)
28816
- });
28817
28574
  const manager = new SessionManager({
28818
28575
  store,
28819
28576
  logger,
28820
28577
  getAdapter,
28821
28578
  historyReader: history,
28822
28579
  dataDir: config.dataDir,
28823
- personaRoot: import_node_path28.default.join(config.dataDir, "personas"),
28580
+ personaRoot: import_node_path26.default.join(config.dataDir, "personas"),
28824
28581
  personaStore,
28825
28582
  ownerDisplayName,
28826
28583
  mode: config.mode,
@@ -28843,10 +28600,10 @@ async function startDaemon(config) {
28843
28600
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
28844
28601
  attachmentGroup: {
28845
28602
  onFileEdit: (input) => {
28846
- const absPath = import_node_path28.default.isAbsolute(input.relPath) ? input.relPath : import_node_path28.default.join(input.cwd, input.relPath);
28603
+ const absPath = import_node_path26.default.isAbsolute(input.relPath) ? input.relPath : import_node_path26.default.join(input.cwd, input.relPath);
28847
28604
  let size = 0;
28848
28605
  try {
28849
- size = import_node_fs26.default.statSync(absPath).size;
28606
+ size = import_node_fs24.default.statSync(absPath).size;
28850
28607
  } catch (err) {
28851
28608
  logger.warn("attachment.onFileEdit stat failed", {
28852
28609
  sessionId: input.sessionId,
@@ -28951,22 +28708,16 @@ async function startDaemon(config) {
28951
28708
  // 时禁用文件 GET/POST,保持 1.0 行为兼容)。
28952
28709
  getHttpBaseUrl,
28953
28710
  httpToken: resolvedAuthToken,
28954
- // file-sharing attachment.* RPC(spec §5 PR 6+)。getPersonaScopeForRequest RPC
28955
- // args 里的 personaId/sessionId 翻译成 scopeRef 给 OutboxStore;mountAdd/groupAdd
28956
- // 后续 PR 复用同一份装配。
28711
+ // file-sharing attachment.* RPC。signUrl owner token HMAC secret;group RPC
28712
+ // 根据 sessionId 反查 scope 写盘。
28957
28713
  attachment: {
28958
- outboxStore,
28959
- mountStore,
28960
28714
  groupFileStore,
28961
- personaDirPath: (pid) => personaStore.personaDirPath(pid),
28962
28715
  getHttpBaseUrl,
28963
- getPersonaScopeForRequest: (args) => {
28964
- if (args.personaId) return { kind: "persona", personaId: args.personaId };
28965
- if (args.sessionId) return { kind: "session", sessionId: args.sessionId };
28966
- return null;
28967
- },
28716
+ // HMAC sign secret:复用 ~/.clawd/auth.json owner token(持久跨重启)。
28717
+ // noAuth 模式 resolvedAuthToken null handler 自己返 NOT_IMPLEMENTED。
28718
+ getSignSecret: () => resolvedAuthToken ?? "",
28968
28719
  // group RPC:根据 sessionId 反查 scope;owner-mode persona session 走
28969
- // 'persona/<pid>/owner',default 走 'default'。PR 9 UI Drawer 用。
28720
+ // 'persona/<pid>/owner',default 走 'default'。
28970
28721
  getSessionScope: (sessionId) => {
28971
28722
  const file = store.read(sessionId);
28972
28723
  if (!file) return null;
@@ -28988,7 +28739,8 @@ async function startDaemon(config) {
28988
28739
  personaStore,
28989
28740
  groupFileStore,
28990
28741
  sessionStore: store,
28991
- outboxStore
28742
+ // /files HMAC verify 用同一份 owner token 做 secret(与 attachment.signUrl 同源)
28743
+ getSignSecret: () => resolvedAuthToken ?? null
28992
28744
  });
28993
28745
  wsServer = new LocalWsServer({
28994
28746
  host: config.host,
@@ -29127,8 +28879,8 @@ async function startDaemon(config) {
29127
28879
  const lines = [
29128
28880
  `Tunnel: ${r.url}`,
29129
28881
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
29130
- `Frpc config: ${import_node_path28.default.join(config.dataDir, "frpc.toml")}`,
29131
- `Frpc log: ${import_node_path28.default.join(config.dataDir, "frpc.log")}`
28882
+ `Frpc config: ${import_node_path26.default.join(config.dataDir, "frpc.toml")}`,
28883
+ `Frpc log: ${import_node_path26.default.join(config.dataDir, "frpc.log")}`
29132
28884
  ];
29133
28885
  const width = Math.max(...lines.map((l) => l.length));
29134
28886
  const bar = "\u2550".repeat(width + 4);
@@ -29141,8 +28893,8 @@ ${bar}
29141
28893
 
29142
28894
  `);
29143
28895
  try {
29144
- const connectPath = import_node_path28.default.join(config.dataDir, "connect.txt");
29145
- import_node_fs26.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
28896
+ const connectPath = import_node_path26.default.join(config.dataDir, "connect.txt");
28897
+ import_node_fs24.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
29146
28898
  } catch {
29147
28899
  }
29148
28900
  } catch (err) {