@clawos-dev/clawd 0.2.69-beta.120.2a41cd5 → 0.2.69-beta.121.dbda41c

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 +173 -90
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -605,8 +605,8 @@ var init_parseUtil = __esm({
605
605
  init_errors2();
606
606
  init_en();
607
607
  makeIssue = (params) => {
608
- const { data, path: path31, errorMaps, issueData } = params;
609
- const fullPath = [...path31, ...issueData.path || []];
608
+ const { data, path: path32, errorMaps, issueData } = params;
609
+ const fullPath = [...path32, ...issueData.path || []];
610
610
  const fullIssue = {
611
611
  ...issueData,
612
612
  path: fullPath
@@ -917,11 +917,11 @@ var init_types = __esm({
917
917
  init_parseUtil();
918
918
  init_util();
919
919
  ParseInputLazyPath = class {
920
- constructor(parent, value, path31, key) {
920
+ constructor(parent, value, path32, key) {
921
921
  this._cachedPath = [];
922
922
  this.parent = parent;
923
923
  this.data = value;
924
- this._path = path31;
924
+ this._path = path32;
925
925
  this._key = key;
926
926
  }
927
927
  get path() {
@@ -4331,9 +4331,11 @@ var init_attachment_schemas = __esm({
4331
4331
  stale: external_exports.boolean().optional()
4332
4332
  });
4333
4333
  AttachmentSignUrlArgs = external_exports.object({
4334
- /** 要分享的绝对路径;签名只关心 absPath,不区分 persona/session */
4335
- absPath: external_exports.string().min(1),
4336
- /** TTL 秒数;缺省 24h;'never' null 不带 exp 字段(永久有效) */
4334
+ /** 群文件所属的 session */
4335
+ sessionId: external_exports.string().min(1),
4336
+ /** 相对 session.cwd 的路径;允许传绝对路径,daemon 端会归一化(必须在 cwd 内) */
4337
+ relPath: external_exports.string().min(1),
4338
+ /** TTL 秒数;缺省 24h;null 走永久 URL(不带 exp 字段) */
4337
4339
  ttlSeconds: external_exports.number().int().positive().nullable().optional()
4338
4340
  });
4339
4341
  AttachmentSignUrlResponseSchema = external_exports.object({
@@ -5333,8 +5335,8 @@ var require_req = __commonJS({
5333
5335
  if (req.originalUrl) {
5334
5336
  _req.url = req.originalUrl;
5335
5337
  } else {
5336
- const path31 = req.path;
5337
- _req.url = typeof path31 === "string" ? path31 : req.url ? req.url.path || req.url : void 0;
5338
+ const path32 = req.path;
5339
+ _req.url = typeof path32 === "string" ? path32 : req.url ? req.url.path || req.url : void 0;
5338
5340
  }
5339
5341
  if (req.query) {
5340
5342
  _req.query = req.query;
@@ -5499,14 +5501,14 @@ var require_redact = __commonJS({
5499
5501
  }
5500
5502
  return obj;
5501
5503
  }
5502
- function parsePath(path31) {
5504
+ function parsePath(path32) {
5503
5505
  const parts = [];
5504
5506
  let current = "";
5505
5507
  let inBrackets = false;
5506
5508
  let inQuotes = false;
5507
5509
  let quoteChar = "";
5508
- for (let i = 0; i < path31.length; i++) {
5509
- const char = path31[i];
5510
+ for (let i = 0; i < path32.length; i++) {
5511
+ const char = path32[i];
5510
5512
  if (!inBrackets && char === ".") {
5511
5513
  if (current) {
5512
5514
  parts.push(current);
@@ -5637,10 +5639,10 @@ var require_redact = __commonJS({
5637
5639
  return current;
5638
5640
  }
5639
5641
  function redactPaths(obj, paths, censor, remove = false) {
5640
- for (const path31 of paths) {
5641
- const parts = parsePath(path31);
5642
+ for (const path32 of paths) {
5643
+ const parts = parsePath(path32);
5642
5644
  if (parts.includes("*")) {
5643
- redactWildcardPath(obj, parts, censor, path31, remove);
5645
+ redactWildcardPath(obj, parts, censor, path32, remove);
5644
5646
  } else {
5645
5647
  if (remove) {
5646
5648
  removeKey(obj, parts);
@@ -5725,8 +5727,8 @@ var require_redact = __commonJS({
5725
5727
  }
5726
5728
  } else {
5727
5729
  if (afterWildcard.includes("*")) {
5728
- const wrappedCensor = typeof censor === "function" ? (value, path31) => {
5729
- const fullPath = [...pathArray.slice(0, pathLength), ...path31];
5730
+ const wrappedCensor = typeof censor === "function" ? (value, path32) => {
5731
+ const fullPath = [...pathArray.slice(0, pathLength), ...path32];
5730
5732
  return censor(value, fullPath);
5731
5733
  } : censor;
5732
5734
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -5761,8 +5763,8 @@ var require_redact = __commonJS({
5761
5763
  return null;
5762
5764
  }
5763
5765
  const pathStructure = /* @__PURE__ */ new Map();
5764
- for (const path31 of pathsToClone) {
5765
- const parts = parsePath(path31);
5766
+ for (const path32 of pathsToClone) {
5767
+ const parts = parsePath(path32);
5766
5768
  let current = pathStructure;
5767
5769
  for (let i = 0; i < parts.length; i++) {
5768
5770
  const part = parts[i];
@@ -5814,24 +5816,24 @@ var require_redact = __commonJS({
5814
5816
  }
5815
5817
  return cloneSelectively(obj, pathStructure);
5816
5818
  }
5817
- function validatePath(path31) {
5818
- if (typeof path31 !== "string") {
5819
+ function validatePath(path32) {
5820
+ if (typeof path32 !== "string") {
5819
5821
  throw new Error("Paths must be (non-empty) strings");
5820
5822
  }
5821
- if (path31 === "") {
5823
+ if (path32 === "") {
5822
5824
  throw new Error("Invalid redaction path ()");
5823
5825
  }
5824
- if (path31.includes("..")) {
5825
- throw new Error(`Invalid redaction path (${path31})`);
5826
+ if (path32.includes("..")) {
5827
+ throw new Error(`Invalid redaction path (${path32})`);
5826
5828
  }
5827
- if (path31.includes(",")) {
5828
- throw new Error(`Invalid redaction path (${path31})`);
5829
+ if (path32.includes(",")) {
5830
+ throw new Error(`Invalid redaction path (${path32})`);
5829
5831
  }
5830
5832
  let bracketCount = 0;
5831
5833
  let inQuotes = false;
5832
5834
  let quoteChar = "";
5833
- for (let i = 0; i < path31.length; i++) {
5834
- const char = path31[i];
5835
+ for (let i = 0; i < path32.length; i++) {
5836
+ const char = path32[i];
5835
5837
  if ((char === '"' || char === "'") && bracketCount > 0) {
5836
5838
  if (!inQuotes) {
5837
5839
  inQuotes = true;
@@ -5845,20 +5847,20 @@ var require_redact = __commonJS({
5845
5847
  } else if (char === "]" && !inQuotes) {
5846
5848
  bracketCount--;
5847
5849
  if (bracketCount < 0) {
5848
- throw new Error(`Invalid redaction path (${path31})`);
5850
+ throw new Error(`Invalid redaction path (${path32})`);
5849
5851
  }
5850
5852
  }
5851
5853
  }
5852
5854
  if (bracketCount !== 0) {
5853
- throw new Error(`Invalid redaction path (${path31})`);
5855
+ throw new Error(`Invalid redaction path (${path32})`);
5854
5856
  }
5855
5857
  }
5856
5858
  function validatePaths(paths) {
5857
5859
  if (!Array.isArray(paths)) {
5858
5860
  throw new TypeError("paths must be an array");
5859
5861
  }
5860
- for (const path31 of paths) {
5861
- validatePath(path31);
5862
+ for (const path32 of paths) {
5863
+ validatePath(path32);
5862
5864
  }
5863
5865
  }
5864
5866
  function slowRedact(options = {}) {
@@ -6026,8 +6028,8 @@ var require_redaction = __commonJS({
6026
6028
  if (shape[k2] === null) {
6027
6029
  o[k2] = (value) => topCensor(value, [k2]);
6028
6030
  } else {
6029
- const wrappedCensor = typeof censor === "function" ? (value, path31) => {
6030
- return censor(value, [k2, ...path31]);
6031
+ const wrappedCensor = typeof censor === "function" ? (value, path32) => {
6032
+ return censor(value, [k2, ...path32]);
6031
6033
  } : censor;
6032
6034
  o[k2] = Redact({
6033
6035
  paths: shape[k2],
@@ -6248,7 +6250,7 @@ var require_sonic_boom = __commonJS({
6248
6250
  var fs28 = require("fs");
6249
6251
  var EventEmitter2 = require("events");
6250
6252
  var inherits = require("util").inherits;
6251
- var path31 = require("path");
6253
+ var path32 = require("path");
6252
6254
  var sleep = require_atomic_sleep();
6253
6255
  var assert = require("assert");
6254
6256
  var BUSY_WRITE_TIMEOUT = 100;
@@ -6302,7 +6304,7 @@ var require_sonic_boom = __commonJS({
6302
6304
  const mode = sonic.mode;
6303
6305
  if (sonic.sync) {
6304
6306
  try {
6305
- if (sonic.mkdir) fs28.mkdirSync(path31.dirname(file), { recursive: true });
6307
+ if (sonic.mkdir) fs28.mkdirSync(path32.dirname(file), { recursive: true });
6306
6308
  const fd = fs28.openSync(file, flags, mode);
6307
6309
  fileOpened(null, fd);
6308
6310
  } catch (err) {
@@ -6310,7 +6312,7 @@ var require_sonic_boom = __commonJS({
6310
6312
  throw err;
6311
6313
  }
6312
6314
  } else if (sonic.mkdir) {
6313
- fs28.mkdir(path31.dirname(file), { recursive: true }, (err) => {
6315
+ fs28.mkdir(path32.dirname(file), { recursive: true }, (err) => {
6314
6316
  if (err) return fileOpened(err);
6315
6317
  fs28.open(file, flags, mode, fileOpened);
6316
6318
  });
@@ -9941,11 +9943,11 @@ var init_lib = __esm({
9941
9943
  }
9942
9944
  }
9943
9945
  },
9944
- addToPath: function addToPath(path31, added, removed, oldPosInc, options) {
9945
- var last = path31.lastComponent;
9946
+ addToPath: function addToPath(path32, added, removed, oldPosInc, options) {
9947
+ var last = path32.lastComponent;
9946
9948
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
9947
9949
  return {
9948
- oldPos: path31.oldPos + oldPosInc,
9950
+ oldPos: path32.oldPos + oldPosInc,
9949
9951
  lastComponent: {
9950
9952
  count: last.count + 1,
9951
9953
  added,
@@ -9955,7 +9957,7 @@ var init_lib = __esm({
9955
9957
  };
9956
9958
  } else {
9957
9959
  return {
9958
- oldPos: path31.oldPos + oldPosInc,
9960
+ oldPos: path32.oldPos + oldPosInc,
9959
9961
  lastComponent: {
9960
9962
  count: 1,
9961
9963
  added,
@@ -10386,10 +10388,10 @@ function attachmentToHistoryMessage(o, ts) {
10386
10388
  const memories = raw.map((m2) => {
10387
10389
  if (!m2 || typeof m2 !== "object") return null;
10388
10390
  const rec = m2;
10389
- const path31 = typeof rec.path === "string" ? rec.path : null;
10391
+ const path32 = typeof rec.path === "string" ? rec.path : null;
10390
10392
  const content = typeof rec.content === "string" ? rec.content : null;
10391
- if (!path31 || content == null) return null;
10392
- const entry = { path: path31, content };
10393
+ if (!path32 || content == null) return null;
10394
+ const entry = { path: path32, content };
10393
10395
  if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
10394
10396
  return entry;
10395
10397
  }).filter((m2) => m2 !== null);
@@ -11193,10 +11195,10 @@ function parseAttachment(obj) {
11193
11195
  const memories = raw.map((m2) => {
11194
11196
  if (!m2 || typeof m2 !== "object") return null;
11195
11197
  const rec = m2;
11196
- const path31 = typeof rec.path === "string" ? rec.path : null;
11198
+ const path32 = typeof rec.path === "string" ? rec.path : null;
11197
11199
  const content = typeof rec.content === "string" ? rec.content : null;
11198
- if (!path31 || content == null) return null;
11199
- const out = { path: path31, content };
11200
+ if (!path32 || content == null) return null;
11201
+ const out = { path: path32, content };
11200
11202
  if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
11201
11203
  return out;
11202
11204
  }).filter((m2) => m2 !== null);
@@ -20110,7 +20112,7 @@ var require_websocket_server = __commonJS({
20110
20112
  // src/run-case/recorder.ts
20111
20113
  function startRunCaseRecorder(opts) {
20112
20114
  const now = opts.now ?? Date.now;
20113
- const dir = import_node_path27.default.dirname(opts.recordPath);
20115
+ const dir = import_node_path28.default.dirname(opts.recordPath);
20114
20116
  let stream = null;
20115
20117
  let closing = false;
20116
20118
  let closedSettled = false;
@@ -20150,12 +20152,12 @@ function startRunCaseRecorder(opts) {
20150
20152
  };
20151
20153
  return { tap, close, closed };
20152
20154
  }
20153
- var import_node_fs25, import_node_path27;
20155
+ var import_node_fs25, import_node_path28;
20154
20156
  var init_recorder = __esm({
20155
20157
  "src/run-case/recorder.ts"() {
20156
20158
  "use strict";
20157
20159
  import_node_fs25 = __toESM(require("fs"), 1);
20158
- import_node_path27 = __toESM(require("path"), 1);
20160
+ import_node_path28 = __toESM(require("path"), 1);
20159
20161
  }
20160
20162
  });
20161
20163
 
@@ -20198,7 +20200,7 @@ var init_wire = __esm({
20198
20200
  // src/run-case/controller.ts
20199
20201
  async function runController(opts) {
20200
20202
  const now = opts.now ?? Date.now;
20201
- const cwd = opts.cwd ?? (0, import_node_fs26.mkdtempSync)(import_node_path28.default.join(import_node_os14.default.tmpdir(), "clawd-runcase-"));
20203
+ const cwd = opts.cwd ?? (0, import_node_fs26.mkdtempSync)(import_node_path29.default.join(import_node_os14.default.tmpdir(), "clawd-runcase-"));
20202
20204
  const ownsCwd = opts.cwd === void 0;
20203
20205
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
20204
20206
  const spawnCtx = { cwd };
@@ -20365,13 +20367,13 @@ async function runController(opts) {
20365
20367
  }
20366
20368
  return exitCode ?? 0;
20367
20369
  }
20368
- var import_node_fs26, import_node_os14, import_node_path28;
20370
+ var import_node_fs26, import_node_os14, import_node_path29;
20369
20371
  var init_controller = __esm({
20370
20372
  "src/run-case/controller.ts"() {
20371
20373
  "use strict";
20372
20374
  import_node_fs26 = require("fs");
20373
20375
  import_node_os14 = __toESM(require("os"), 1);
20374
- import_node_path28 = __toESM(require("path"), 1);
20376
+ import_node_path29 = __toESM(require("path"), 1);
20375
20377
  init_claude();
20376
20378
  init_stdout_splitter();
20377
20379
  init_permission_stdio();
@@ -20603,7 +20605,7 @@ Env (advanced):
20603
20605
  `;
20604
20606
 
20605
20607
  // src/index.ts
20606
- var import_node_path26 = __toESM(require("path"), 1);
20608
+ var import_node_path27 = __toESM(require("path"), 1);
20607
20609
  var import_node_fs24 = __toESM(require("fs"), 1);
20608
20610
 
20609
20611
  // src/logger.ts
@@ -22104,9 +22106,19 @@ var SessionManager = class {
22104
22106
  return entries.filter((e) => e.isDirectory() && e.name !== "default").map((e) => e.name);
22105
22107
  }
22106
22108
  // owner / default 两个分类下按 sessionId 找文件——前端只传 sessionId 不带 scope,
22107
- // SessionManager 在这里扫盘定位(default 直接试,未命中再轮询所有 persona owner 目录)。
22109
+ // SessionManager 在这里定位。三层快慢路径:
22110
+ // 1) active runner(用户当前在用的 session):runner.state.file 是 SessionFile 内存权威副本,
22111
+ // 直接返回,零磁盘 I/O。覆盖 attachment / file-sharing 等"用户操作 → 紧接着 RPC"的
22112
+ // 绝大多数场景
22113
+ // 2) default scope 磁盘:inactive default session 命中
22114
+ // 3) 所有 persona owner 目录扫盘:inactive persona owner session 命中
22108
22115
  // listener sub-session 不走这条——transport 始终明确传 (personaId, sessionId)。
22116
+ //
22117
+ // 公开方法:attachment / file-sharing handler 通过 deps 闭包消费,不应直接持 SessionStore
22118
+ // (持 default-only store 是 file-sharing v1 的预存 bug 根因——见 fix #703)
22109
22119
  findOwnedSession(sessionId) {
22120
+ const runner = this.runners.get(sessionId);
22121
+ if (runner) return runner.getState().file;
22110
22122
  const dflt = this.deps.store.read(sessionId);
22111
22123
  if (dflt) return dflt;
22112
22124
  for (const personaId of this.listPersonaIdsOnDisk()) {
@@ -22116,6 +22128,13 @@ var SessionManager = class {
22116
22128
  }
22117
22129
  return null;
22118
22130
  }
22131
+ // findOwnedSession + scopeForFile 收口。把"sessionId → SessionScope"的派生
22132
+ // 集中到 manager(scope 单一来源),调用方拿 scope 不再自己拼 object 也不依赖
22133
+ // ownerPersonaId 字段语义。给 attachment handler 通过 deps.getSessionScope 闭包消费。
22134
+ findOwnedSessionScope(sessionId) {
22135
+ const file = this.findOwnedSession(sessionId);
22136
+ return file ? this.scopeForFile(file) : null;
22137
+ }
22119
22138
  // 合并 default + 所有 persona owner 的 SessionFile —— 桌面 App 主 session 列表入口。
22120
22139
  // 同样不含 listener sub-session(listener 走 persona:listSubSessions RPC 单独入口)。
22121
22140
  listAllOwned() {
@@ -26850,14 +26869,24 @@ var AUTH_FILE_NAME = "auth.json";
26850
26869
  function authFilePath(dataDir) {
26851
26870
  return import_node_path22.default.join(dataDir, AUTH_FILE_NAME);
26852
26871
  }
26853
- function loadOrCreateAuthToken(opts) {
26872
+ function loadOrCreateAuthFile(opts) {
26854
26873
  const file = authFilePath(opts.dataDir);
26874
+ const generate = opts.generate ?? defaultGenerate;
26875
+ const now = opts.now ?? (() => /* @__PURE__ */ new Date());
26855
26876
  const existing = readAuthFile(file);
26856
- if (existing && existing.token) return existing.token;
26857
- const token = (opts.generate ?? defaultGenerate)();
26858
- const now = (opts.now ?? (() => /* @__PURE__ */ new Date()))();
26859
- writeAuthFile(file, { token, createdAt: now.toISOString() });
26860
- return token;
26877
+ if (existing && existing.token && existing.signSecret) {
26878
+ return {
26879
+ token: existing.token,
26880
+ signSecret: existing.signSecret,
26881
+ createdAt: existing.createdAt ?? (/* @__PURE__ */ new Date(0)).toISOString()
26882
+ };
26883
+ }
26884
+ const token = existing?.token || generate();
26885
+ const signSecret = existing?.signSecret || generate();
26886
+ const createdAt = existing?.createdAt || now().toISOString();
26887
+ const next = { token, signSecret, createdAt };
26888
+ writeAuthFile(file, next);
26889
+ return next;
26861
26890
  }
26862
26891
  function defaultGenerate() {
26863
26892
  return import_node_crypto8.default.randomBytes(32).toString("base64url");
@@ -26866,13 +26895,14 @@ function readAuthFile(file) {
26866
26895
  try {
26867
26896
  const raw = import_node_fs20.default.readFileSync(file, "utf8");
26868
26897
  const parsed = JSON.parse(raw);
26869
- if (typeof parsed?.token === "string" && parsed.token.length > 0) {
26870
- return {
26871
- token: parsed.token,
26872
- createdAt: typeof parsed.createdAt === "string" ? parsed.createdAt : (/* @__PURE__ */ new Date(0)).toISOString()
26873
- };
26898
+ if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
26899
+ return null;
26874
26900
  }
26875
- return null;
26901
+ return {
26902
+ token: parsed.token,
26903
+ signSecret: typeof parsed.signSecret === "string" && parsed.signSecret.length > 0 ? parsed.signSecret : void 0,
26904
+ createdAt: typeof parsed.createdAt === "string" ? parsed.createdAt : void 0
26905
+ };
26876
26906
  } catch (err) {
26877
26907
  const code = err?.code;
26878
26908
  if (code === "ENOENT") return null;
@@ -27541,6 +27571,7 @@ function buildPersonaHandlers(deps) {
27541
27571
  }
27542
27572
 
27543
27573
  // src/handlers/attachment.ts
27574
+ var import_node_path26 = __toESM(require("path"), 1);
27544
27575
  init_protocol();
27545
27576
  init_protocol();
27546
27577
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -27565,8 +27596,51 @@ function buildAttachmentHandlers(deps) {
27565
27596
  "httpBaseUrl unavailable (daemon HTTP not ready)"
27566
27597
  );
27567
27598
  }
27599
+ if (!deps.sessionStore || !deps.getSessionScope || !deps.groupFileStore) {
27600
+ throw new ClawdError(
27601
+ ERROR_CODES.METHOD_NOT_IMPLEMENTED,
27602
+ "signUrl requires session/group stores"
27603
+ );
27604
+ }
27605
+ const sessionFile = deps.sessionStore.read(args.sessionId);
27606
+ if (!sessionFile) {
27607
+ throw new ClawdError(
27608
+ ERROR_CODES.VALIDATION_ERROR,
27609
+ `session ${args.sessionId} not found`
27610
+ );
27611
+ }
27612
+ const scope = deps.getSessionScope(args.sessionId);
27613
+ if (!scope) {
27614
+ throw new ClawdError(
27615
+ ERROR_CODES.VALIDATION_ERROR,
27616
+ `session ${args.sessionId} scope unresolved`
27617
+ );
27618
+ }
27619
+ const cwdAbs = import_node_path26.default.resolve(sessionFile.cwd);
27620
+ const candidateAbs = import_node_path26.default.isAbsolute(args.relPath) ? import_node_path26.default.resolve(args.relPath) : import_node_path26.default.resolve(cwdAbs, args.relPath);
27621
+ if (!isContainedIn2(candidateAbs, cwdAbs)) {
27622
+ throw new ClawdError(
27623
+ ERROR_CODES.VALIDATION_ERROR,
27624
+ "relPath escapes session cwd"
27625
+ );
27626
+ }
27627
+ const relPath = import_node_path26.default.relative(cwdAbs, candidateAbs);
27628
+ if (relPath === "" || relPath.startsWith("..")) {
27629
+ throw new ClawdError(
27630
+ ERROR_CODES.VALIDATION_ERROR,
27631
+ "relPath escapes session cwd"
27632
+ );
27633
+ }
27634
+ const entries = deps.groupFileStore.list(scope, args.sessionId);
27635
+ const entry = entries.find((e) => e.relPath === relPath && !e.stale);
27636
+ if (!entry) {
27637
+ throw new ClawdError(
27638
+ ERROR_CODES.VALIDATION_ERROR,
27639
+ `relPath not in session group files or stale: ${relPath}`
27640
+ );
27641
+ }
27568
27642
  const ttl = args.ttlSeconds === null ? null : args.ttlSeconds ?? DEFAULT_TTL_SECONDS;
27569
- const parts = signUrlParts(secret, args.absPath, ttl);
27643
+ const parts = signUrlParts(secret, candidateAbs, ttl);
27570
27644
  const url = buildSignedFileUrl(httpBaseUrl, parts);
27571
27645
  return {
27572
27646
  response: {
@@ -27652,6 +27726,12 @@ function buildAttachmentHandlers(deps) {
27652
27726
  "attachment.groupListPersona": groupListPersona
27653
27727
  };
27654
27728
  }
27729
+ function isContainedIn2(abs, root) {
27730
+ const normalized = import_node_path26.default.resolve(abs);
27731
+ const normalizedRoot = import_node_path26.default.resolve(root);
27732
+ if (normalized === normalizedRoot) return true;
27733
+ return normalized.startsWith(normalizedRoot + import_node_path26.default.sep);
27734
+ }
27655
27735
 
27656
27736
  // src/handlers/index.ts
27657
27737
  function buildMethodHandlers(deps) {
@@ -27675,7 +27755,7 @@ function buildMethodHandlers(deps) {
27675
27755
  async function startDaemon(config) {
27676
27756
  const logger = createLogger({
27677
27757
  level: config.logLevel,
27678
- file: import_node_path26.default.join(config.dataDir, "clawd.log")
27758
+ file: import_node_path27.default.join(config.dataDir, "clawd.log")
27679
27759
  });
27680
27760
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
27681
27761
  const stateMgr = new StateFileManager({ dataDir: config.dataDir });
@@ -27687,10 +27767,12 @@ async function startDaemon(config) {
27687
27767
  logger.warn("stale state file detected, overwriting", { pid: pre.existing.pid });
27688
27768
  }
27689
27769
  let resolvedAuthToken = null;
27770
+ let authFile = null;
27690
27771
  if (config.authToken && config.authToken.trim()) {
27691
27772
  resolvedAuthToken = config.authToken.trim();
27692
27773
  } else if (config.tunnel) {
27693
- resolvedAuthToken = loadOrCreateAuthToken({ dataDir: config.dataDir });
27774
+ authFile = loadOrCreateAuthFile({ dataDir: config.dataDir });
27775
+ resolvedAuthToken = authFile.token;
27694
27776
  }
27695
27777
  const authMode = resolvedAuthToken == null ? "none" : "first-message";
27696
27778
  let wsServer = null;
@@ -27707,7 +27789,7 @@ async function startDaemon(config) {
27707
27789
  const agents = new AgentsScanner();
27708
27790
  const history = new ClaudeHistoryReader();
27709
27791
  let transport = null;
27710
- const personaStore = new PersonaStore(import_node_path26.default.join(config.dataDir, "personas"));
27792
+ const personaStore = new PersonaStore(import_node_path27.default.join(config.dataDir, "personas"));
27711
27793
  const defaultsRoot = findDefaultsRoot();
27712
27794
  if (defaultsRoot) {
27713
27795
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -27722,7 +27804,7 @@ async function startDaemon(config) {
27722
27804
  getAdapter,
27723
27805
  historyReader: history,
27724
27806
  dataDir: config.dataDir,
27725
- personaRoot: import_node_path26.default.join(config.dataDir, "personas"),
27807
+ personaRoot: import_node_path27.default.join(config.dataDir, "personas"),
27726
27808
  personaStore,
27727
27809
  ownerDisplayName,
27728
27810
  mode: config.mode,
@@ -27745,7 +27827,7 @@ async function startDaemon(config) {
27745
27827
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
27746
27828
  attachmentGroup: {
27747
27829
  onFileEdit: (input) => {
27748
- const absPath = import_node_path26.default.isAbsolute(input.relPath) ? input.relPath : import_node_path26.default.join(input.cwd, input.relPath);
27830
+ const absPath = import_node_path27.default.isAbsolute(input.relPath) ? input.relPath : import_node_path27.default.join(input.cwd, input.relPath);
27749
27831
  let size = 0;
27750
27832
  try {
27751
27833
  size = import_node_fs24.default.statSync(absPath).size;
@@ -27844,22 +27926,22 @@ async function startDaemon(config) {
27844
27926
  httpToken: resolvedAuthToken,
27845
27927
  // file-sharing attachment.* RPC。signUrl 用 owner token 做 HMAC secret;group RPC
27846
27928
  // 根据 sessionId 反查 scope 写盘。
27929
+ //
27930
+ // sessionStore / getSessionScope 都走 manager 的跨 scope 公开 API(findOwnedSession /
27931
+ // findOwnedSessionScope)—— 否则 default-only SessionStore 找不到 persona owner-mode
27932
+ // session,attachment 整套 RPC 对 persona session 都会报 "session not found"。
27933
+ // 详见 fix(daemon) #703:file-sharing v1 预存 wiring bug,#701 把 desktop 默认 --tunnel
27934
+ // 后首次让 desktop 用户用上 file-sharing 才被暴露。
27847
27935
  attachment: {
27848
27936
  groupFileStore,
27937
+ sessionStore: { read: (sid) => manager.findOwnedSession(sid) },
27849
27938
  getHttpBaseUrl,
27850
- // HMAC sign secret:复用 ~/.clawd/auth.json owner token(持久跨重启)。
27851
- // noAuth 模式 resolvedAuthToken 为 null → handler 自己返 NOT_IMPLEMENTED。
27852
- getSignSecret: () => resolvedAuthToken ?? "",
27853
- // group RPC:根据 sessionId 反查 scopeowner-mode persona session 走
27939
+ // HMAC sign secret:~/.clawd/auth.json signSecret 字段(与 WS Bearer token 独立)。
27940
+ // --auth-token CLI 模式 / noAuth 模式 authFile 为 null → handler 自己返 NOT_IMPLEMENTED。
27941
+ getSignSecret: () => authFile?.signSecret ?? "",
27942
+ // group RPC + sign 都用:根据 sessionId 反查 scopeowner-mode persona session 走
27854
27943
  // 'persona/<pid>/owner',default 走 'default'。
27855
- getSessionScope: (sessionId) => {
27856
- const file = store.read(sessionId);
27857
- if (!file) return null;
27858
- if (file.ownerPersonaId) {
27859
- return { kind: "persona", personaId: file.ownerPersonaId, mode: "owner" };
27860
- }
27861
- return { kind: "default" };
27862
- }
27944
+ getSessionScope: (sid) => manager.findOwnedSessionScope(sid)
27863
27945
  }
27864
27946
  });
27865
27947
  const authResolver = new AuthContextResolver({
@@ -27873,8 +27955,9 @@ async function startDaemon(config) {
27873
27955
  personaStore,
27874
27956
  groupFileStore,
27875
27957
  sessionStore: store,
27876
- // /files HMAC verify 用同一份 owner token secret(与 attachment.signUrl 同源)
27877
- getSignSecret: () => resolvedAuthToken ?? null
27958
+ // /files HMAC verify auth.json signSecret 字段(与 attachment.signUrl 同源)。
27959
+ // --auth-token CLI 模式没 signSecret → 路由返 501,sign URL 功能整体禁用。
27960
+ getSignSecret: () => authFile?.signSecret ?? null
27878
27961
  });
27879
27962
  wsServer = new LocalWsServer({
27880
27963
  host: config.host,
@@ -28012,8 +28095,8 @@ async function startDaemon(config) {
28012
28095
  const lines = [
28013
28096
  `Tunnel: ${r.url}`,
28014
28097
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
28015
- `Frpc config: ${import_node_path26.default.join(config.dataDir, "frpc.toml")}`,
28016
- `Frpc log: ${import_node_path26.default.join(config.dataDir, "frpc.log")}`
28098
+ `Frpc config: ${import_node_path27.default.join(config.dataDir, "frpc.toml")}`,
28099
+ `Frpc log: ${import_node_path27.default.join(config.dataDir, "frpc.log")}`
28017
28100
  ];
28018
28101
  const width = Math.max(...lines.map((l) => l.length));
28019
28102
  const bar = "\u2550".repeat(width + 4);
@@ -28026,7 +28109,7 @@ ${bar}
28026
28109
 
28027
28110
  `);
28028
28111
  try {
28029
- const connectPath = import_node_path26.default.join(config.dataDir, "connect.txt");
28112
+ const connectPath = import_node_path27.default.join(config.dataDir, "connect.txt");
28030
28113
  import_node_fs24.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
28031
28114
  } catch {
28032
28115
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.69-beta.120.2a41cd5",
3
+ "version": "0.2.69-beta.121.dbda41c",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",