@clawos-dev/clawd 0.2.285 → 0.2.287

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -450,8 +450,8 @@ var init_parseUtil = __esm({
450
450
  init_errors();
451
451
  init_en();
452
452
  makeIssue = (params) => {
453
- const { data, path: path77, errorMaps, issueData } = params;
454
- const fullPath = [...path77, ...issueData.path || []];
453
+ const { data, path: path78, errorMaps, issueData } = params;
454
+ const fullPath = [...path78, ...issueData.path || []];
455
455
  const fullIssue = {
456
456
  ...issueData,
457
457
  path: fullPath
@@ -762,11 +762,11 @@ var init_types = __esm({
762
762
  init_parseUtil();
763
763
  init_util();
764
764
  ParseInputLazyPath = class {
765
- constructor(parent, value, path77, key) {
765
+ constructor(parent, value, path78, key) {
766
766
  this._cachedPath = [];
767
767
  this.parent = parent;
768
768
  this.data = value;
769
- this._path = path77;
769
+ this._path = path78;
770
770
  this._key = key;
771
771
  }
772
772
  get path() {
@@ -6584,6 +6584,40 @@ var init_shift_internal = __esm({
6584
6584
  }
6585
6585
  });
6586
6586
 
6587
+ // ../protocol/src/lark-bot-schemas.ts
6588
+ var LarkBotPersonaArgs, LarkBotUnbindArgs, LarkBotStatusArgs, LarkBotBindManualArgs, LarkBotGroupSchema, LarkBotStatusResultSchema, LarkBotStateFrameSchema;
6589
+ var init_lark_bot_schemas = __esm({
6590
+ "../protocol/src/lark-bot-schemas.ts"() {
6591
+ "use strict";
6592
+ init_zod();
6593
+ LarkBotPersonaArgs = external_exports.object({ personaId: external_exports.string().min(1) });
6594
+ LarkBotUnbindArgs = LarkBotPersonaArgs;
6595
+ LarkBotStatusArgs = LarkBotPersonaArgs;
6596
+ LarkBotBindManualArgs = external_exports.object({
6597
+ personaId: external_exports.string().min(1),
6598
+ appId: external_exports.string().min(1),
6599
+ appSecret: external_exports.string().min(1),
6600
+ botName: external_exports.string().min(1).optional()
6601
+ });
6602
+ LarkBotGroupSchema = external_exports.object({
6603
+ chatId: external_exports.string().min(1),
6604
+ chatName: external_exports.string().optional(),
6605
+ /** epoch ms,该群 session 最近一次活跃时间 */
6606
+ lastActiveAt: external_exports.number().int().optional()
6607
+ });
6608
+ LarkBotStatusResultSchema = external_exports.object({
6609
+ state: external_exports.enum(["unbound", "bound", "broken"]),
6610
+ appId: external_exports.string().optional(),
6611
+ botName: external_exports.string().optional(),
6612
+ brokenReason: external_exports.string().optional(),
6613
+ groups: external_exports.array(LarkBotGroupSchema)
6614
+ });
6615
+ LarkBotStateFrameSchema = LarkBotStatusResultSchema.extend({
6616
+ personaId: external_exports.string().min(1)
6617
+ });
6618
+ }
6619
+ });
6620
+
6587
6621
  // ../protocol/src/persona-schemas.ts
6588
6622
  var PersonaFileSchema, PersonaSkillSummarySchema, PersonaPluginSummarySchema, PersonaSandboxSettingsSchema, PersonaInfoResponseSchema, PersonaCreateArgsSchema, PersonaIdArgsSchema, PersonaUpdateArgsSchema, CodexSandboxSettingsSchema;
6589
6623
  var init_persona_schemas = __esm({
@@ -6994,6 +7028,14 @@ var init_schemas = __esm({
6994
7028
  * mirror,留 strip 防御)。
6995
7029
  */
6996
7030
  shiftFiredFromSessionId: external_exports.string().min(1).optional(),
7031
+ /**
7032
+ * LarkBotConnector(spec 2026-08-05):该 session 是某飞书群的专属会话。
7033
+ * larkChatId = 飞书群 chat_id(oc_ 前缀),与 persona-guest scope 的 capId `lark-<chat_id>` 对应;
7034
+ * 由 daemon lark-bot-connector 模块写入,UI 只读(sidebar 按此字段隐藏,Drawer 群清单按此聚合)。
7035
+ */
7036
+ larkChatId: external_exports.string().min(1).optional(),
7037
+ /** 飞书群名快照(展示用,随事件里的群名更新,可能滞后于飞书侧改名) */
7038
+ larkChatName: external_exports.string().min(1).optional(),
6997
7039
  createdAt: external_exports.string().min(1),
6998
7040
  updatedAt: external_exports.string().min(1)
6999
7041
  });
@@ -7801,6 +7843,7 @@ var init_methods = __esm({
7801
7843
  init_inbox();
7802
7844
  init_peer_exec();
7803
7845
  init_shift_internal();
7846
+ init_lark_bot_schemas();
7804
7847
  init_persona_schemas();
7805
7848
  init_schemas();
7806
7849
  NO_ARGS = external_exports.object({});
@@ -7979,6 +8022,18 @@ var init_methods = __esm({
7979
8022
  summary: "\u67E5 shift \u7684\u6267\u884C\u5386\u53F2\uFF08\u4EC5\u521B\u5EFA\u8005\uFF09",
7980
8023
  args: ShiftInternalHistoryArgs
7981
8024
  },
8025
+ "larkBot:bindManual": {
8026
+ summary: "\u624B\u586B app_id/app_secret \u7ED1\u5B9A\u73B0\u6709\u98DE\u4E66 bot\uFF08\u552F\u4E00\u7ED1\u5B9A\u5165\u53E3\uFF09\uFF1Bowner-only",
8027
+ args: LarkBotBindManualArgs
8028
+ },
8029
+ "larkBot:unbind": {
8030
+ summary: "\u89E3\u7ED1 persona \u7684\u98DE\u4E66 bot\uFF08\u5220\u4E91\u7AEF\u7ED1\u5B9A + \u65AD\u957F\u8FDE\u63A5\uFF0C\u98DE\u4E66\u4FA7 app \u4FDD\u7559\uFF09\uFF1Bowner-only",
8031
+ args: LarkBotUnbindArgs
8032
+ },
8033
+ "larkBot:status": {
8034
+ summary: "\u67E5 persona \u7684\u98DE\u4E66 bot \u72B6\u6001\uFF08unbound/bound/broken + \u670D\u52A1\u4E2D\u7FA4\u6E05\u5355\uFF09\uFF1Bowner-only",
8035
+ args: LarkBotStatusArgs
8036
+ },
7982
8037
  "skills:list": {
7983
8038
  summary: "\u626B\u63CF\u5E76\u5217\u51FA\u8BE5\u76EE\u5F55\u53EF\u7528\u7684 skill",
7984
8039
  args: SkillsListArgs
@@ -8418,6 +8473,7 @@ var init_runtime = __esm({
8418
8473
  init_log();
8419
8474
  init_visitor_schemas();
8420
8475
  init_shift_internal();
8476
+ init_lark_bot_schemas();
8421
8477
  }
8422
8478
  });
8423
8479
 
@@ -8692,8 +8748,8 @@ var require_req = __commonJS({
8692
8748
  if (req.originalUrl) {
8693
8749
  _req.url = req.originalUrl;
8694
8750
  } else {
8695
- const path77 = req.path;
8696
- _req.url = typeof path77 === "string" ? path77 : req.url ? req.url.path || req.url : void 0;
8751
+ const path78 = req.path;
8752
+ _req.url = typeof path78 === "string" ? path78 : req.url ? req.url.path || req.url : void 0;
8697
8753
  }
8698
8754
  if (req.query) {
8699
8755
  _req.query = req.query;
@@ -8858,14 +8914,14 @@ var require_redact = __commonJS({
8858
8914
  }
8859
8915
  return obj;
8860
8916
  }
8861
- function parsePath(path77) {
8917
+ function parsePath(path78) {
8862
8918
  const parts = [];
8863
8919
  let current = "";
8864
8920
  let inBrackets = false;
8865
8921
  let inQuotes = false;
8866
8922
  let quoteChar = "";
8867
- for (let i = 0; i < path77.length; i++) {
8868
- const char = path77[i];
8923
+ for (let i = 0; i < path78.length; i++) {
8924
+ const char = path78[i];
8869
8925
  if (!inBrackets && char === ".") {
8870
8926
  if (current) {
8871
8927
  parts.push(current);
@@ -8996,10 +9052,10 @@ var require_redact = __commonJS({
8996
9052
  return current;
8997
9053
  }
8998
9054
  function redactPaths(obj, paths, censor, remove = false) {
8999
- for (const path77 of paths) {
9000
- const parts = parsePath(path77);
9055
+ for (const path78 of paths) {
9056
+ const parts = parsePath(path78);
9001
9057
  if (parts.includes("*")) {
9002
- redactWildcardPath(obj, parts, censor, path77, remove);
9058
+ redactWildcardPath(obj, parts, censor, path78, remove);
9003
9059
  } else {
9004
9060
  if (remove) {
9005
9061
  removeKey(obj, parts);
@@ -9084,8 +9140,8 @@ var require_redact = __commonJS({
9084
9140
  }
9085
9141
  } else {
9086
9142
  if (afterWildcard.includes("*")) {
9087
- const wrappedCensor = typeof censor === "function" ? (value, path77) => {
9088
- const fullPath = [...pathArray.slice(0, pathLength), ...path77];
9143
+ const wrappedCensor = typeof censor === "function" ? (value, path78) => {
9144
+ const fullPath = [...pathArray.slice(0, pathLength), ...path78];
9089
9145
  return censor(value, fullPath);
9090
9146
  } : censor;
9091
9147
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -9120,8 +9176,8 @@ var require_redact = __commonJS({
9120
9176
  return null;
9121
9177
  }
9122
9178
  const pathStructure = /* @__PURE__ */ new Map();
9123
- for (const path77 of pathsToClone) {
9124
- const parts = parsePath(path77);
9179
+ for (const path78 of pathsToClone) {
9180
+ const parts = parsePath(path78);
9125
9181
  let current = pathStructure;
9126
9182
  for (let i = 0; i < parts.length; i++) {
9127
9183
  const part = parts[i];
@@ -9173,24 +9229,24 @@ var require_redact = __commonJS({
9173
9229
  }
9174
9230
  return cloneSelectively(obj, pathStructure);
9175
9231
  }
9176
- function validatePath(path77) {
9177
- if (typeof path77 !== "string") {
9232
+ function validatePath(path78) {
9233
+ if (typeof path78 !== "string") {
9178
9234
  throw new Error("Paths must be (non-empty) strings");
9179
9235
  }
9180
- if (path77 === "") {
9236
+ if (path78 === "") {
9181
9237
  throw new Error("Invalid redaction path ()");
9182
9238
  }
9183
- if (path77.includes("..")) {
9184
- throw new Error(`Invalid redaction path (${path77})`);
9239
+ if (path78.includes("..")) {
9240
+ throw new Error(`Invalid redaction path (${path78})`);
9185
9241
  }
9186
- if (path77.includes(",")) {
9187
- throw new Error(`Invalid redaction path (${path77})`);
9242
+ if (path78.includes(",")) {
9243
+ throw new Error(`Invalid redaction path (${path78})`);
9188
9244
  }
9189
9245
  let bracketCount = 0;
9190
9246
  let inQuotes = false;
9191
9247
  let quoteChar = "";
9192
- for (let i = 0; i < path77.length; i++) {
9193
- const char = path77[i];
9248
+ for (let i = 0; i < path78.length; i++) {
9249
+ const char = path78[i];
9194
9250
  if ((char === '"' || char === "'") && bracketCount > 0) {
9195
9251
  if (!inQuotes) {
9196
9252
  inQuotes = true;
@@ -9204,20 +9260,20 @@ var require_redact = __commonJS({
9204
9260
  } else if (char === "]" && !inQuotes) {
9205
9261
  bracketCount--;
9206
9262
  if (bracketCount < 0) {
9207
- throw new Error(`Invalid redaction path (${path77})`);
9263
+ throw new Error(`Invalid redaction path (${path78})`);
9208
9264
  }
9209
9265
  }
9210
9266
  }
9211
9267
  if (bracketCount !== 0) {
9212
- throw new Error(`Invalid redaction path (${path77})`);
9268
+ throw new Error(`Invalid redaction path (${path78})`);
9213
9269
  }
9214
9270
  }
9215
9271
  function validatePaths(paths) {
9216
9272
  if (!Array.isArray(paths)) {
9217
9273
  throw new TypeError("paths must be an array");
9218
9274
  }
9219
- for (const path77 of paths) {
9220
- validatePath(path77);
9275
+ for (const path78 of paths) {
9276
+ validatePath(path78);
9221
9277
  }
9222
9278
  }
9223
9279
  function slowRedact(options = {}) {
@@ -9385,8 +9441,8 @@ var require_redaction = __commonJS({
9385
9441
  if (shape[k2] === null) {
9386
9442
  o[k2] = (value) => topCensor(value, [k2]);
9387
9443
  } else {
9388
- const wrappedCensor = typeof censor === "function" ? (value, path77) => {
9389
- return censor(value, [k2, ...path77]);
9444
+ const wrappedCensor = typeof censor === "function" ? (value, path78) => {
9445
+ return censor(value, [k2, ...path78]);
9390
9446
  } : censor;
9391
9447
  o[k2] = Redact({
9392
9448
  paths: shape[k2],
@@ -9604,10 +9660,10 @@ var require_atomic_sleep = __commonJS({
9604
9660
  var require_sonic_boom = __commonJS({
9605
9661
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
9606
9662
  "use strict";
9607
- var fs67 = require("fs");
9663
+ var fs68 = require("fs");
9608
9664
  var EventEmitter3 = require("events");
9609
9665
  var inherits = require("util").inherits;
9610
- var path77 = require("path");
9666
+ var path78 = require("path");
9611
9667
  var sleep2 = require_atomic_sleep();
9612
9668
  var assert = require("assert");
9613
9669
  var BUSY_WRITE_TIMEOUT = 100;
@@ -9661,20 +9717,20 @@ var require_sonic_boom = __commonJS({
9661
9717
  const mode = sonic.mode;
9662
9718
  if (sonic.sync) {
9663
9719
  try {
9664
- if (sonic.mkdir) fs67.mkdirSync(path77.dirname(file), { recursive: true });
9665
- const fd = fs67.openSync(file, flags, mode);
9720
+ if (sonic.mkdir) fs68.mkdirSync(path78.dirname(file), { recursive: true });
9721
+ const fd = fs68.openSync(file, flags, mode);
9666
9722
  fileOpened(null, fd);
9667
9723
  } catch (err) {
9668
9724
  fileOpened(err);
9669
9725
  throw err;
9670
9726
  }
9671
9727
  } else if (sonic.mkdir) {
9672
- fs67.mkdir(path77.dirname(file), { recursive: true }, (err) => {
9728
+ fs68.mkdir(path78.dirname(file), { recursive: true }, (err) => {
9673
9729
  if (err) return fileOpened(err);
9674
- fs67.open(file, flags, mode, fileOpened);
9730
+ fs68.open(file, flags, mode, fileOpened);
9675
9731
  });
9676
9732
  } else {
9677
- fs67.open(file, flags, mode, fileOpened);
9733
+ fs68.open(file, flags, mode, fileOpened);
9678
9734
  }
9679
9735
  }
9680
9736
  function SonicBoom(opts) {
@@ -9715,8 +9771,8 @@ var require_sonic_boom = __commonJS({
9715
9771
  this.flush = flushBuffer;
9716
9772
  this.flushSync = flushBufferSync;
9717
9773
  this._actualWrite = actualWriteBuffer;
9718
- fsWriteSync = () => fs67.writeSync(this.fd, this._writingBuf);
9719
- fsWrite = () => fs67.write(this.fd, this._writingBuf, this.release);
9774
+ fsWriteSync = () => fs68.writeSync(this.fd, this._writingBuf);
9775
+ fsWrite = () => fs68.write(this.fd, this._writingBuf, this.release);
9720
9776
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
9721
9777
  this._writingBuf = "";
9722
9778
  this.write = write;
@@ -9725,15 +9781,15 @@ var require_sonic_boom = __commonJS({
9725
9781
  this._actualWrite = actualWrite;
9726
9782
  fsWriteSync = () => {
9727
9783
  if (Buffer.isBuffer(this._writingBuf)) {
9728
- return fs67.writeSync(this.fd, this._writingBuf);
9784
+ return fs68.writeSync(this.fd, this._writingBuf);
9729
9785
  }
9730
- return fs67.writeSync(this.fd, this._writingBuf, "utf8");
9786
+ return fs68.writeSync(this.fd, this._writingBuf, "utf8");
9731
9787
  };
9732
9788
  fsWrite = () => {
9733
9789
  if (Buffer.isBuffer(this._writingBuf)) {
9734
- return fs67.write(this.fd, this._writingBuf, this.release);
9790
+ return fs68.write(this.fd, this._writingBuf, this.release);
9735
9791
  }
9736
- return fs67.write(this.fd, this._writingBuf, "utf8", this.release);
9792
+ return fs68.write(this.fd, this._writingBuf, "utf8", this.release);
9737
9793
  };
9738
9794
  } else {
9739
9795
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -9790,7 +9846,7 @@ var require_sonic_boom = __commonJS({
9790
9846
  }
9791
9847
  }
9792
9848
  if (this._fsync) {
9793
- fs67.fsyncSync(this.fd);
9849
+ fs68.fsyncSync(this.fd);
9794
9850
  }
9795
9851
  const len = this._len;
9796
9852
  if (this._reopening) {
@@ -9904,7 +9960,7 @@ var require_sonic_boom = __commonJS({
9904
9960
  const onDrain = () => {
9905
9961
  if (!this._fsync) {
9906
9962
  try {
9907
- fs67.fsync(this.fd, (err) => {
9963
+ fs68.fsync(this.fd, (err) => {
9908
9964
  this._flushPending = false;
9909
9965
  cb(err);
9910
9966
  });
@@ -10006,7 +10062,7 @@ var require_sonic_boom = __commonJS({
10006
10062
  const fd = this.fd;
10007
10063
  this.once("ready", () => {
10008
10064
  if (fd !== this.fd) {
10009
- fs67.close(fd, (err) => {
10065
+ fs68.close(fd, (err) => {
10010
10066
  if (err) {
10011
10067
  return this.emit("error", err);
10012
10068
  }
@@ -10055,7 +10111,7 @@ var require_sonic_boom = __commonJS({
10055
10111
  buf = this._bufs[0];
10056
10112
  }
10057
10113
  try {
10058
- const n = Buffer.isBuffer(buf) ? fs67.writeSync(this.fd, buf) : fs67.writeSync(this.fd, buf, "utf8");
10114
+ const n = Buffer.isBuffer(buf) ? fs68.writeSync(this.fd, buf) : fs68.writeSync(this.fd, buf, "utf8");
10059
10115
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
10060
10116
  buf = releasedBufObj.writingBuf;
10061
10117
  this._len = releasedBufObj.len;
@@ -10071,7 +10127,7 @@ var require_sonic_boom = __commonJS({
10071
10127
  }
10072
10128
  }
10073
10129
  try {
10074
- fs67.fsyncSync(this.fd);
10130
+ fs68.fsyncSync(this.fd);
10075
10131
  } catch {
10076
10132
  }
10077
10133
  }
@@ -10092,7 +10148,7 @@ var require_sonic_boom = __commonJS({
10092
10148
  buf = mergeBuf(this._bufs[0], this._lens[0]);
10093
10149
  }
10094
10150
  try {
10095
- const n = fs67.writeSync(this.fd, buf);
10151
+ const n = fs68.writeSync(this.fd, buf);
10096
10152
  buf = buf.subarray(n);
10097
10153
  this._len = Math.max(this._len - n, 0);
10098
10154
  if (buf.length <= 0) {
@@ -10120,13 +10176,13 @@ var require_sonic_boom = __commonJS({
10120
10176
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
10121
10177
  if (this.sync) {
10122
10178
  try {
10123
- const written = Buffer.isBuffer(this._writingBuf) ? fs67.writeSync(this.fd, this._writingBuf) : fs67.writeSync(this.fd, this._writingBuf, "utf8");
10179
+ const written = Buffer.isBuffer(this._writingBuf) ? fs68.writeSync(this.fd, this._writingBuf) : fs68.writeSync(this.fd, this._writingBuf, "utf8");
10124
10180
  release(null, written);
10125
10181
  } catch (err) {
10126
10182
  release(err);
10127
10183
  }
10128
10184
  } else {
10129
- fs67.write(this.fd, this._writingBuf, release);
10185
+ fs68.write(this.fd, this._writingBuf, release);
10130
10186
  }
10131
10187
  }
10132
10188
  function actualWriteBuffer() {
@@ -10135,7 +10191,7 @@ var require_sonic_boom = __commonJS({
10135
10191
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
10136
10192
  if (this.sync) {
10137
10193
  try {
10138
- const written = fs67.writeSync(this.fd, this._writingBuf);
10194
+ const written = fs68.writeSync(this.fd, this._writingBuf);
10139
10195
  release(null, written);
10140
10196
  } catch (err) {
10141
10197
  release(err);
@@ -10144,7 +10200,7 @@ var require_sonic_boom = __commonJS({
10144
10200
  if (kCopyBuffer) {
10145
10201
  this._writingBuf = Buffer.from(this._writingBuf);
10146
10202
  }
10147
- fs67.write(this.fd, this._writingBuf, release);
10203
+ fs68.write(this.fd, this._writingBuf, release);
10148
10204
  }
10149
10205
  }
10150
10206
  function actualClose(sonic) {
@@ -10160,12 +10216,12 @@ var require_sonic_boom = __commonJS({
10160
10216
  sonic._lens = [];
10161
10217
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
10162
10218
  try {
10163
- fs67.fsync(sonic.fd, closeWrapped);
10219
+ fs68.fsync(sonic.fd, closeWrapped);
10164
10220
  } catch {
10165
10221
  }
10166
10222
  function closeWrapped() {
10167
10223
  if (sonic.fd !== 1 && sonic.fd !== 2) {
10168
- fs67.close(sonic.fd, done);
10224
+ fs68.close(sonic.fd, done);
10169
10225
  } else {
10170
10226
  done();
10171
10227
  }
@@ -13300,11 +13356,11 @@ var init_lib = __esm({
13300
13356
  }
13301
13357
  }
13302
13358
  },
13303
- addToPath: function addToPath(path77, added, removed, oldPosInc, options) {
13304
- var last = path77.lastComponent;
13359
+ addToPath: function addToPath(path78, added, removed, oldPosInc, options) {
13360
+ var last = path78.lastComponent;
13305
13361
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
13306
13362
  return {
13307
- oldPos: path77.oldPos + oldPosInc,
13363
+ oldPos: path78.oldPos + oldPosInc,
13308
13364
  lastComponent: {
13309
13365
  count: last.count + 1,
13310
13366
  added,
@@ -13314,7 +13370,7 @@ var init_lib = __esm({
13314
13370
  };
13315
13371
  } else {
13316
13372
  return {
13317
- oldPos: path77.oldPos + oldPosInc,
13373
+ oldPos: path78.oldPos + oldPosInc,
13318
13374
  lastComponent: {
13319
13375
  count: 1,
13320
13376
  added,
@@ -13823,10 +13879,10 @@ function attachmentToHistoryMessage(o, ts) {
13823
13879
  const memories = raw.map((m2) => {
13824
13880
  if (!m2 || typeof m2 !== "object") return null;
13825
13881
  const rec3 = m2;
13826
- const path77 = typeof rec3.path === "string" ? rec3.path : null;
13882
+ const path78 = typeof rec3.path === "string" ? rec3.path : null;
13827
13883
  const content = typeof rec3.content === "string" ? rec3.content : null;
13828
- if (!path77 || content == null) return null;
13829
- const entry = { path: path77, content };
13884
+ if (!path78 || content == null) return null;
13885
+ const entry = { path: path78, content };
13830
13886
  if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
13831
13887
  return entry;
13832
13888
  }).filter((m2) => m2 !== null);
@@ -14629,10 +14685,10 @@ function parseAttachment(obj) {
14629
14685
  const memories = raw.map((m2) => {
14630
14686
  if (!m2 || typeof m2 !== "object") return null;
14631
14687
  const rec3 = m2;
14632
- const path77 = typeof rec3.path === "string" ? rec3.path : null;
14688
+ const path78 = typeof rec3.path === "string" ? rec3.path : null;
14633
14689
  const content = typeof rec3.content === "string" ? rec3.content : null;
14634
- if (!path77 || content == null) return null;
14635
- const out = { path: path77, content };
14690
+ if (!path78 || content == null) return null;
14691
+ const out = { path: path78, content };
14636
14692
  if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
14637
14693
  return out;
14638
14694
  }).filter((m2) => m2 !== null);
@@ -35601,8 +35657,8 @@ var require_utils = __commonJS({
35601
35657
  var result = transform[inputType][outputType](input);
35602
35658
  return result;
35603
35659
  };
35604
- exports2.resolve = function(path77) {
35605
- var parts = path77.split("/");
35660
+ exports2.resolve = function(path78) {
35661
+ var parts = path78.split("/");
35606
35662
  var result = [];
35607
35663
  for (var index = 0; index < parts.length; index++) {
35608
35664
  var part = parts[index];
@@ -41455,18 +41511,18 @@ var require_object = __commonJS({
41455
41511
  var object = new ZipObject(name, zipObjectContent, o);
41456
41512
  this.files[name] = object;
41457
41513
  };
41458
- var parentFolder = function(path77) {
41459
- if (path77.slice(-1) === "/") {
41460
- path77 = path77.substring(0, path77.length - 1);
41514
+ var parentFolder = function(path78) {
41515
+ if (path78.slice(-1) === "/") {
41516
+ path78 = path78.substring(0, path78.length - 1);
41461
41517
  }
41462
- var lastSlash = path77.lastIndexOf("/");
41463
- return lastSlash > 0 ? path77.substring(0, lastSlash) : "";
41518
+ var lastSlash = path78.lastIndexOf("/");
41519
+ return lastSlash > 0 ? path78.substring(0, lastSlash) : "";
41464
41520
  };
41465
- var forceTrailingSlash = function(path77) {
41466
- if (path77.slice(-1) !== "/") {
41467
- path77 += "/";
41521
+ var forceTrailingSlash = function(path78) {
41522
+ if (path78.slice(-1) !== "/") {
41523
+ path78 += "/";
41468
41524
  }
41469
- return path77;
41525
+ return path78;
41470
41526
  };
41471
41527
  var folderAdd = function(name, createFolders) {
41472
41528
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -42468,7 +42524,7 @@ var require_lib3 = __commonJS({
42468
42524
  // src/run-case/recorder.ts
42469
42525
  function startRunCaseRecorder(opts) {
42470
42526
  const now = opts.now ?? Date.now;
42471
- const dir = import_node_path64.default.dirname(opts.recordPath);
42527
+ const dir = import_node_path65.default.dirname(opts.recordPath);
42472
42528
  let stream = null;
42473
42529
  let closing = false;
42474
42530
  let closedSettled = false;
@@ -42482,8 +42538,8 @@ function startRunCaseRecorder(opts) {
42482
42538
  });
42483
42539
  const ensureStream = () => {
42484
42540
  if (stream) return stream;
42485
- import_node_fs48.default.mkdirSync(dir, { recursive: true });
42486
- stream = import_node_fs48.default.createWriteStream(opts.recordPath, { flags: "a" });
42541
+ import_node_fs49.default.mkdirSync(dir, { recursive: true });
42542
+ stream = import_node_fs49.default.createWriteStream(opts.recordPath, { flags: "a" });
42487
42543
  stream.on("close", () => closedResolve());
42488
42544
  return stream;
42489
42545
  };
@@ -42508,12 +42564,12 @@ function startRunCaseRecorder(opts) {
42508
42564
  };
42509
42565
  return { tap, close, closed };
42510
42566
  }
42511
- var import_node_fs48, import_node_path64;
42567
+ var import_node_fs49, import_node_path65;
42512
42568
  var init_recorder = __esm({
42513
42569
  "src/run-case/recorder.ts"() {
42514
42570
  "use strict";
42515
- import_node_fs48 = __toESM(require("fs"), 1);
42516
- import_node_path64 = __toESM(require("path"), 1);
42571
+ import_node_fs49 = __toESM(require("fs"), 1);
42572
+ import_node_path65 = __toESM(require("path"), 1);
42517
42573
  }
42518
42574
  });
42519
42575
 
@@ -42556,7 +42612,7 @@ var init_wire = __esm({
42556
42612
  // src/run-case/controller.ts
42557
42613
  async function runController(opts) {
42558
42614
  const now = opts.now ?? Date.now;
42559
- const cwd = opts.cwd ?? (0, import_node_fs49.mkdtempSync)(import_node_path65.default.join(import_node_os23.default.tmpdir(), "clawd-runcase-"));
42615
+ const cwd = opts.cwd ?? (0, import_node_fs50.mkdtempSync)(import_node_path66.default.join(import_node_os23.default.tmpdir(), "clawd-runcase-"));
42560
42616
  const ownsCwd = opts.cwd === void 0;
42561
42617
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
42562
42618
  const spawnCtx = { cwd };
@@ -42717,19 +42773,19 @@ async function runController(opts) {
42717
42773
  if (sigintHandler) process.off("SIGINT", sigintHandler);
42718
42774
  if (ownsCwd) {
42719
42775
  try {
42720
- (0, import_node_fs49.rmSync)(cwd, { recursive: true, force: true });
42776
+ (0, import_node_fs50.rmSync)(cwd, { recursive: true, force: true });
42721
42777
  } catch {
42722
42778
  }
42723
42779
  }
42724
42780
  return exitCode ?? 0;
42725
42781
  }
42726
- var import_node_fs49, import_node_os23, import_node_path65;
42782
+ var import_node_fs50, import_node_os23, import_node_path66;
42727
42783
  var init_controller = __esm({
42728
42784
  "src/run-case/controller.ts"() {
42729
42785
  "use strict";
42730
- import_node_fs49 = require("fs");
42786
+ import_node_fs50 = require("fs");
42731
42787
  import_node_os23 = __toESM(require("os"), 1);
42732
- import_node_path65 = __toESM(require("path"), 1);
42788
+ import_node_path66 = __toESM(require("path"), 1);
42733
42789
  init_claude();
42734
42790
  init_stdout_splitter();
42735
42791
  init_permission_stdio();
@@ -43004,8 +43060,8 @@ Env (advanced):
43004
43060
  `;
43005
43061
 
43006
43062
  // src/index.ts
43007
- var import_node_path63 = __toESM(require("path"), 1);
43008
- var import_node_fs47 = __toESM(require("fs"), 1);
43063
+ var import_node_path64 = __toESM(require("path"), 1);
43064
+ var import_node_fs48 = __toESM(require("fs"), 1);
43009
43065
  var import_node_os22 = __toESM(require("os"), 1);
43010
43066
 
43011
43067
  // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
@@ -43893,6 +43949,28 @@ ${r.outcome.text}${files}`;
43893
43949
  return ["\u4F60\u4E4B\u524D\u59D4\u6D3E\u51FA\u53BB\u7684\u4EFB\u52A1\u6709\u4E86\u7ED3\u679C\uFF1A", "", blocks.join("\n\n")].join("\n");
43894
43950
  }
43895
43951
 
43952
+ // src/session/lark-reply.ts
43953
+ function extractLarkReplyText(events) {
43954
+ let lastUserIdx = -1;
43955
+ for (let i = events.length - 1; i >= 0; i--) {
43956
+ if (events[i].kind === "user_text") {
43957
+ lastUserIdx = i;
43958
+ break;
43959
+ }
43960
+ }
43961
+ const tail = events.slice(lastUserIdx + 1);
43962
+ const order = [];
43963
+ const latest = /* @__PURE__ */ new Map();
43964
+ let anon = 0;
43965
+ for (const ev of tail) {
43966
+ if (ev.kind !== "text") continue;
43967
+ const key = ev.partialId ?? `__snapshot_${anon++}`;
43968
+ if (!latest.has(key)) order.push(key);
43969
+ latest.set(key, ev.text);
43970
+ }
43971
+ return order.map((k2) => latest.get(k2) ?? "").filter((t) => t.length > 0).join("\n").trim();
43972
+ }
43973
+
43896
43974
  // src/session/runner.ts
43897
43975
  var import_node_os4 = __toESM(require("os"), 1);
43898
43976
  var import_node_path7 = __toESM(require("path"), 1);
@@ -47021,6 +47099,110 @@ var SessionManager = class {
47021
47099
  });
47022
47100
  return { sessionId, ended, kill: killImpl };
47023
47101
  }
47102
+ /**
47103
+ * LarkBotConnector(spec 2026-08-05 §5.3):把一条飞书群 @ 消息作为一个 turn 驱动
47104
+ * 该群的专属 guest session,turn-idle 后从 reducer buffer 提取回复文本。
47105
+ *
47106
+ * 跟 createShiftSession 的差异:
47107
+ * - **find-or-create**:按 capId `lark-<chatId>` 找现存 session(一群一条,D3),
47108
+ * 没有才建;daemon `create` 的无 dedupe 语义不变——dedupe 是本方法私有实现
47109
+ * - guest scope + 沙箱:群成员 query 不带 owner 权限;10min guest idle-kill + resume
47110
+ * - turn-idle 后**不 kill**(session 常驻,记忆即价值);延迟 settleDelayMs 等尾随
47111
+ * text 落盘(schemas.ts turn_end 注释:CC 写 turn_end 早于尾段正文)再读 buffer
47112
+ * - 回复提取:extractLarkReplyText(最后一条 user_text 之后的 text 事件拼接)
47113
+ */
47114
+ runLarkChatTurn(args) {
47115
+ if (!this.deps.personaRoot) {
47116
+ throw new Error("runLarkChatTurn: personaRoot missing in ManagerDeps");
47117
+ }
47118
+ const capId = `lark-${args.chatId}`;
47119
+ const scope = {
47120
+ kind: "persona",
47121
+ personaId: args.personaId,
47122
+ mode: "guest",
47123
+ capId
47124
+ };
47125
+ const store = this.storeFor(scope);
47126
+ let file = store.list().find((f) => f.larkChatId === args.chatId);
47127
+ if (!file) {
47128
+ const now = (/* @__PURE__ */ new Date()).toISOString();
47129
+ file = {
47130
+ sessionId: newSessionId(),
47131
+ cwd: import_node_path9.default.join(this.deps.personaRoot, safeFileName(args.personaId)),
47132
+ tool: this.deps.getPersonaTool?.(args.personaId) ?? "claude",
47133
+ ownerPersonaId: args.personaId,
47134
+ // guest 三件套:creatorPrincipalId=capId(scopeForFile 回落防线)、
47135
+ // creatorDisplayName(reducer guest 分支硬性要求,缺则 spawn 抛)
47136
+ creatorPrincipalId: capId,
47137
+ creatorDisplayName: `\u98DE\u4E66\u7FA4\xB7${args.chatName ?? args.chatId}`,
47138
+ larkChatId: args.chatId,
47139
+ ...args.chatName ? { larkChatName: args.chatName } : {},
47140
+ createdAt: now,
47141
+ updatedAt: now
47142
+ };
47143
+ store.write(file);
47144
+ this.deps.logger?.info("larkBot.runLarkChatTurn.session-created", {
47145
+ sessionId: file.sessionId,
47146
+ personaId: args.personaId,
47147
+ chatId: args.chatId
47148
+ });
47149
+ } else if (args.chatName && file.larkChatName !== args.chatName) {
47150
+ file = { ...file, larkChatName: args.chatName, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
47151
+ store.write(file);
47152
+ }
47153
+ const sessionId = file.sessionId;
47154
+ let resolveEnded = () => {
47155
+ };
47156
+ const ended = new Promise((r) => {
47157
+ resolveEnded = r;
47158
+ });
47159
+ let unsubscribe = () => {
47160
+ };
47161
+ let settled = false;
47162
+ const settle = (info) => {
47163
+ if (settled) return;
47164
+ settled = true;
47165
+ unsubscribe();
47166
+ resolveEnded(info);
47167
+ };
47168
+ const killImpl = () => {
47169
+ const r = this.runners.get(sessionId);
47170
+ if (!r) return;
47171
+ r.input({ kind: "command", command: { kind: "stop" } });
47172
+ };
47173
+ const settleDelayMs = args.settleDelayMs ?? 500;
47174
+ unsubscribe = this.subscribe(
47175
+ sessionId,
47176
+ scope,
47177
+ makeEndFrameHandler(sessionId, (info) => {
47178
+ if (info.kind === "idle") {
47179
+ setTimeout(() => {
47180
+ try {
47181
+ const events = this.readHistoryEventsForScope(sessionId, scope);
47182
+ const replyText = extractLarkReplyText(events);
47183
+ settle(replyText ? { ok: true, replyText } : { error: "empty reply" });
47184
+ } catch (err) {
47185
+ settle({ error: err.message });
47186
+ }
47187
+ }, settleDelayMs);
47188
+ } else if (info.kind === "stopped") {
47189
+ settle({ error: "session stopped before reply" });
47190
+ } else {
47191
+ settle({ error: info.message || "session error" });
47192
+ }
47193
+ })
47194
+ );
47195
+ const runner = this.ensureRunnerForScope(file, scope);
47196
+ runner.input({
47197
+ kind: "command",
47198
+ command: {
47199
+ kind: "send",
47200
+ text: args.text,
47201
+ senderPrincipal: makeGuestSender(capId, args.senderName)
47202
+ }
47203
+ });
47204
+ return { sessionId, ended, kill: killImpl };
47205
+ }
47024
47206
  /**
47025
47207
  * 老板插话:把 'inject-owner-text' input 喂给对应 runner,
47026
47208
  * runner 不存在时按 scope 懒创建。
@@ -49024,8 +49206,8 @@ function turnStartInput(text) {
49024
49206
  const items = [];
49025
49207
  let leftover = text;
49026
49208
  for (const m2 of text.matchAll(SKILL_RE)) {
49027
- const [marker, name, path77] = m2;
49028
- items.push({ type: "skill", name, path: path77 });
49209
+ const [marker, name, path78] = m2;
49210
+ items.push({ type: "skill", name, path: path78 });
49029
49211
  leftover = leftover.replace(marker, "");
49030
49212
  }
49031
49213
  for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
@@ -51526,6 +51708,374 @@ function buildShiftInternalHandlers(deps) {
51526
51708
  };
51527
51709
  }
51528
51710
 
51711
+ // src/handlers/lark-bot.ts
51712
+ init_protocol();
51713
+ function statusFrame(personaId2, status) {
51714
+ return { type: "larkBot:state", personaId: personaId2, ...status };
51715
+ }
51716
+ function buildLarkBotHandlers(deps) {
51717
+ const now = deps.now ?? Date.now;
51718
+ const groupsOf = (personaId2) => {
51719
+ const removed = new Set(deps.store.read(personaId2)?.removedChatIds ?? []);
51720
+ return deps.listLarkSessions(personaId2).filter((g2) => !removed.has(g2.chatId));
51721
+ };
51722
+ const emit = (personaId2, status2) => {
51723
+ deps.broadcast(statusFrame(personaId2, status2));
51724
+ };
51725
+ const bindManual = async (frame) => {
51726
+ const { personaId: personaId2, appId, appSecret, botName } = parseArgs2(
51727
+ LarkBotBindManualArgs,
51728
+ frame,
51729
+ "larkBot:bindManual"
51730
+ );
51731
+ const tunnelUrl = deps.getTunnelUrl();
51732
+ if (!tunnelUrl) throw new Error("larkBot bind requires tunnel to be up");
51733
+ const ownerId = deps.getOwnerId();
51734
+ if (!ownerId) throw new Error("larkBot bind requires feishu login (owner identity)");
51735
+ const name = botName ?? deps.getPersonaName(personaId2) ?? personaId2;
51736
+ await deps.cloud.bind({ personaId: personaId2, appId, appSecret, botName: name, tunnelUrl, ownerId });
51737
+ deps.store.write(personaId2, { appId, botName: name, boundAt: now() });
51738
+ emit(personaId2, { state: "bound", appId, botName: name, groups: groupsOf(personaId2) });
51739
+ return { response: { type: "larkBot:bindManual:ok" } };
51740
+ };
51741
+ const unbind = async (frame) => {
51742
+ const { personaId: personaId2 } = parseArgs2(LarkBotUnbindArgs, frame, "larkBot:unbind");
51743
+ const meta = deps.store.read(personaId2);
51744
+ if (!meta) throw new Error(`larkBot not bound: ${personaId2}`);
51745
+ try {
51746
+ await deps.cloud.unbind(meta.appId);
51747
+ } catch (err) {
51748
+ if (err.status !== 404) throw err;
51749
+ deps.logger?.warn(`larkBot unbind: cloud binding already gone: ${meta.appId}`);
51750
+ }
51751
+ deps.store.remove(personaId2);
51752
+ emit(personaId2, { state: "unbound", groups: [] });
51753
+ return { response: { type: "larkBot:unbind:ok" } };
51754
+ };
51755
+ const status = async (frame) => {
51756
+ const { personaId: personaId2 } = parseArgs2(LarkBotStatusArgs, frame, "larkBot:status");
51757
+ const meta = deps.store.read(personaId2);
51758
+ if (!meta) {
51759
+ return {
51760
+ response: { type: "larkBot:status:ok", state: "unbound", groups: [] }
51761
+ };
51762
+ }
51763
+ let cloudBinding;
51764
+ try {
51765
+ cloudBinding = (await deps.cloud.listBindings()).find((b2) => b2.personaId === personaId2);
51766
+ } catch (err) {
51767
+ deps.logger?.warn(`larkBot status: cloud unreachable: ${err.message}`);
51768
+ }
51769
+ const broken = cloudBinding?.status === "broken";
51770
+ const result = {
51771
+ state: broken ? "broken" : "bound",
51772
+ appId: meta.appId,
51773
+ botName: meta.botName,
51774
+ ...broken ? { brokenReason: cloudBinding?.brokenReason ?? "unknown" } : {},
51775
+ groups: groupsOf(personaId2)
51776
+ };
51777
+ return { response: { type: "larkBot:status:ok", ...result } };
51778
+ };
51779
+ return {
51780
+ "larkBot:bindManual": bindManual,
51781
+ "larkBot:unbind": unbind,
51782
+ "larkBot:status": status
51783
+ };
51784
+ }
51785
+ async function refreshTunnelUrlOnStartup(deps) {
51786
+ if (deps.listBoundPersonaIds().length === 0) return;
51787
+ const tunnelUrl = deps.getTunnelUrl();
51788
+ if (!tunnelUrl) return;
51789
+ try {
51790
+ await deps.cloud.updateTunnelUrl(tunnelUrl);
51791
+ } catch (err) {
51792
+ deps.logger?.warn(`larkBot tunnel refresh failed: ${err.message}`);
51793
+ }
51794
+ }
51795
+
51796
+ // src/lark-bot-connector/binding-store.ts
51797
+ var import_node_fs26 = __toESM(require("fs"), 1);
51798
+ var import_node_path27 = __toESM(require("path"), 1);
51799
+ function createLarkBotBindingStore(personaRoot) {
51800
+ const filePath = (personaId2) => import_node_path27.default.join(personaRoot, safeFileName(personaId2), ".clawd", "lark-bot.json");
51801
+ const read = (personaId2) => {
51802
+ const file = filePath(personaId2);
51803
+ if (!import_node_fs26.default.existsSync(file)) return null;
51804
+ try {
51805
+ const raw = JSON.parse(import_node_fs26.default.readFileSync(file, "utf8"));
51806
+ if (typeof raw?.appId !== "string" || raw.appId.length === 0) return null;
51807
+ return raw;
51808
+ } catch {
51809
+ return null;
51810
+ }
51811
+ };
51812
+ const write = (personaId2, meta) => {
51813
+ const file = filePath(personaId2);
51814
+ import_node_fs26.default.mkdirSync(import_node_path27.default.dirname(file), { recursive: true });
51815
+ const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
51816
+ import_node_fs26.default.writeFileSync(tmp, JSON.stringify(meta, null, 2), { mode: 384 });
51817
+ import_node_fs26.default.renameSync(tmp, file);
51818
+ };
51819
+ return {
51820
+ read,
51821
+ write,
51822
+ remove(personaId2) {
51823
+ import_node_fs26.default.rmSync(filePath(personaId2), { force: true });
51824
+ },
51825
+ addRemovedChat(personaId2, chatId) {
51826
+ const meta = read(personaId2);
51827
+ if (!meta) return;
51828
+ const set = new Set(meta.removedChatIds ?? []);
51829
+ set.add(chatId);
51830
+ write(personaId2, { ...meta, removedChatIds: [...set] });
51831
+ }
51832
+ };
51833
+ }
51834
+
51835
+ // src/lark-bot-connector/cloud-client.ts
51836
+ var LarkBotCloudError = class extends Error {
51837
+ constructor(message, status) {
51838
+ super(message);
51839
+ this.status = status;
51840
+ this.name = "LarkBotCloudError";
51841
+ }
51842
+ status;
51843
+ };
51844
+ function createLarkBotCloudClient(opts) {
51845
+ const f = opts.fetchImpl ?? globalThis.fetch;
51846
+ if (!f) throw new Error("fetch is not available (Node >= 18 required)");
51847
+ const base = opts.api.replace(/\/+$/, "");
51848
+ async function request(method, pathname, body) {
51849
+ const ac = new AbortController();
51850
+ const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? 15e3);
51851
+ let res;
51852
+ try {
51853
+ res = await f(`${base}${pathname}`, {
51854
+ method,
51855
+ signal: ac.signal,
51856
+ headers: {
51857
+ authorization: `Bearer ${opts.apiKey}`,
51858
+ "x-did": encodeURIComponent(opts.deviceId),
51859
+ "x-name": encodeURIComponent(opts.displayName),
51860
+ "x-scene": "daemon",
51861
+ "content-type": "application/json"
51862
+ },
51863
+ body: body ? JSON.stringify(body) : void 0
51864
+ });
51865
+ } catch (err) {
51866
+ throw new LarkBotCloudError(`lark-bot cloud request failed: ${err.message}`);
51867
+ } finally {
51868
+ clearTimeout(timer);
51869
+ }
51870
+ let json = {};
51871
+ try {
51872
+ json = await res.json();
51873
+ } catch {
51874
+ json = {};
51875
+ }
51876
+ if (!res.ok) {
51877
+ throw new LarkBotCloudError(
51878
+ `lark-bot cloud ${method} ${pathname}: HTTP ${res.status} ${JSON.stringify(json).slice(0, 200)}`,
51879
+ res.status
51880
+ );
51881
+ }
51882
+ return json;
51883
+ }
51884
+ return {
51885
+ async bind(args) {
51886
+ await request("PUT", "/api/lark-bot/bind", { ...args });
51887
+ },
51888
+ async unbind(appId) {
51889
+ await request("DELETE", `/api/lark-bot/bind/${encodeURIComponent(appId)}`);
51890
+ },
51891
+ async listBindings() {
51892
+ const json = await request("GET", "/api/lark-bot/bindings");
51893
+ return json.items ?? [];
51894
+ },
51895
+ async updateTunnelUrl(tunnelUrl) {
51896
+ await request("PATCH", "/api/lark-bot/tunnel", { tunnelUrl });
51897
+ },
51898
+ async reply(args) {
51899
+ await request("POST", "/api/lark-bot/reply", { ...args });
51900
+ },
51901
+ async claimEvents() {
51902
+ const json = await request("POST", "/api/lark-bot/events/claim");
51903
+ return json.items ?? [];
51904
+ }
51905
+ };
51906
+ }
51907
+
51908
+ // src/lark-bot-connector/chat-router.ts
51909
+ var ERROR_REPLY_TEXT = "\u5904\u7406\u51FA\u9519\u4E86\uFF0C\u8BF7\u91CD\u8BD5";
51910
+ var DEFAULT_TURN_TIMEOUT_MS = 10 * 60 * 1e3;
51911
+ function createLarkChatRouter(deps) {
51912
+ const timeoutMs = deps.turnTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS;
51913
+ const replyDelays = deps.replyRetryDelaysMs ?? [2e3, 8e3, 3e4];
51914
+ const sleep2 = deps.sleepImpl ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
51915
+ const queues = /* @__PURE__ */ new Map();
51916
+ const activity = /* @__PURE__ */ new Map();
51917
+ const runOne = async (env) => {
51918
+ if (!env.messageId) {
51919
+ deps.logger?.warn(`larkBot chat-router: message envelope missing messageId, drop ${env.eventId}`);
51920
+ return;
51921
+ }
51922
+ const chatLabel = env.chatName ?? env.chatId;
51923
+ const text = `[\u98DE\u4E66\u7FA4\xB7${chatLabel}] ${env.senderName ?? env.senderOpenId ?? "\u672A\u77E5\u6210\u5458"}: ${env.text ?? ""}`;
51924
+ const turn = deps.manager.runLarkChatTurn({
51925
+ personaId: env.personaId,
51926
+ chatId: env.chatId,
51927
+ chatName: env.chatName,
51928
+ senderOpenId: env.senderOpenId ?? "",
51929
+ senderName: env.senderName ?? env.senderOpenId ?? "\u672A\u77E5\u6210\u5458",
51930
+ text
51931
+ });
51932
+ activity.set(env.chatId, {
51933
+ chatId: env.chatId,
51934
+ chatName: env.chatName,
51935
+ lastActiveAt: Date.now(),
51936
+ personaId: env.personaId
51937
+ });
51938
+ let timedOut = false;
51939
+ let timer;
51940
+ const timeout = new Promise((resolve6) => {
51941
+ timer = setTimeout(() => {
51942
+ timedOut = true;
51943
+ turn.kill();
51944
+ resolve6({ error: "turn timeout" });
51945
+ }, timeoutMs);
51946
+ });
51947
+ let result;
51948
+ try {
51949
+ result = await Promise.race([turn.ended, timeout]);
51950
+ } finally {
51951
+ clearTimeout(timer);
51952
+ }
51953
+ const replyText = "ok" in result && result.ok ? result.replyText : ERROR_REPLY_TEXT;
51954
+ if (!("ok" in result)) {
51955
+ deps.logger?.warn(
51956
+ `larkBot turn ${timedOut ? "timeout" : "error"} (${env.chatId}): ${result.error}`
51957
+ );
51958
+ }
51959
+ for (let attempt = 0; attempt <= replyDelays.length; attempt++) {
51960
+ if (attempt > 0) await sleep2(replyDelays[attempt - 1]);
51961
+ try {
51962
+ await deps.cloud.reply({
51963
+ appId: env.appId,
51964
+ chatId: env.chatId,
51965
+ replyToMessageId: env.messageId,
51966
+ text: replyText,
51967
+ eventId: env.eventId
51968
+ });
51969
+ return;
51970
+ } catch (err) {
51971
+ deps.logger?.warn(
51972
+ `larkBot reply attempt ${attempt + 1} failed (${env.chatId}): ${err.message}`
51973
+ );
51974
+ }
51975
+ }
51976
+ };
51977
+ return {
51978
+ handleEnvelope(env) {
51979
+ if (env.type === "bot_removed") {
51980
+ deps.store.addRemovedChat(env.personaId, env.chatId);
51981
+ activity.delete(env.chatId);
51982
+ queues.delete(env.chatId);
51983
+ deps.logger?.info(`larkBot removed from chat ${env.chatId} (persona ${env.personaId})`);
51984
+ return;
51985
+ }
51986
+ const prev = queues.get(env.chatId) ?? Promise.resolve();
51987
+ const next = prev.then(
51988
+ () => runOne(env).catch(
51989
+ (err) => deps.logger?.warn(`larkBot chat-router runOne failed: ${err.message}`)
51990
+ )
51991
+ );
51992
+ queues.set(env.chatId, next);
51993
+ void next.finally(() => {
51994
+ if (queues.get(env.chatId) === next) queues.delete(env.chatId);
51995
+ });
51996
+ },
51997
+ listLarkSessions(personaId2) {
51998
+ return [...activity.values()].filter((a) => a.personaId === personaId2).map((a) => ({ chatId: a.chatId, chatName: a.chatName, lastActiveAt: a.lastActiveAt }));
51999
+ }
52000
+ };
52001
+ }
52002
+
52003
+ // src/lark-bot-connector/envelope.ts
52004
+ var import_node_crypto6 = require("crypto");
52005
+ var LARK_ENVELOPE_PREFIX = "clawdlbe1";
52006
+ function verifyLarkEventEnvelope(args) {
52007
+ const { token, publicKeyPem } = args;
52008
+ const now = args.nowSeconds ?? Math.floor(Date.now() / 1e3);
52009
+ const parts = token.split(".");
52010
+ if (parts.length !== 3 || parts[0] !== LARK_ENVELOPE_PREFIX) {
52011
+ return { ok: false, reason: "BAD_FORMAT" };
52012
+ }
52013
+ const [prefix, payloadB64, sigB64] = parts;
52014
+ let envelope;
52015
+ try {
52016
+ envelope = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf8"));
52017
+ } catch {
52018
+ return { ok: false, reason: "BAD_FORMAT" };
52019
+ }
52020
+ const data = Buffer.from(`${prefix}.${payloadB64}`, "utf8");
52021
+ let sigOk = false;
52022
+ try {
52023
+ sigOk = (0, import_node_crypto6.verify)(null, data, publicKeyPem, Buffer.from(sigB64, "base64url"));
52024
+ } catch {
52025
+ sigOk = false;
52026
+ }
52027
+ if (!sigOk) return { ok: false, reason: "BAD_SIGNATURE" };
52028
+ if (typeof envelope.exp !== "number" || envelope.exp <= now) {
52029
+ return { ok: false, reason: "EXPIRED" };
52030
+ }
52031
+ return { ok: true, envelope };
52032
+ }
52033
+
52034
+ // src/lark-bot-connector/event-endpoint.ts
52035
+ var DEFAULT_DEDUPE_CAPACITY = 1024;
52036
+ function createLarkEventDedupe(capacity = DEFAULT_DEDUPE_CAPACITY) {
52037
+ const seen = /* @__PURE__ */ new Map();
52038
+ return {
52039
+ has: (id) => seen.has(id),
52040
+ add: (id) => {
52041
+ if (seen.has(id)) return;
52042
+ seen.set(id, true);
52043
+ if (seen.size > capacity) {
52044
+ const oldest = seen.keys().next().value;
52045
+ if (oldest !== void 0) seen.delete(oldest);
52046
+ }
52047
+ }
52048
+ };
52049
+ }
52050
+ function createLarkBotEventEndpoint(deps) {
52051
+ const seen = deps.dedupe ?? createLarkEventDedupe(deps.dedupeCapacity ?? DEFAULT_DEDUPE_CAPACITY);
52052
+ return (rawBody) => {
52053
+ const publicKeyPem = deps.getPublicKey();
52054
+ if (!publicKeyPem) {
52055
+ return { status: 503, body: { code: "SERVER_KEY_UNAVAILABLE", message: "server public key not cached yet" } };
52056
+ }
52057
+ const r = verifyLarkEventEnvelope({
52058
+ token: rawBody.trim(),
52059
+ publicKeyPem,
52060
+ nowSeconds: deps.nowSeconds?.()
52061
+ });
52062
+ if (!r.ok) {
52063
+ return { status: 401, body: { code: r.reason, message: `lark event envelope rejected: ${r.reason}` } };
52064
+ }
52065
+ const { envelope } = r;
52066
+ if (seen.has(envelope.eventId)) {
52067
+ return { status: 202, body: { ok: true, deduped: true } };
52068
+ }
52069
+ seen.add(envelope.eventId);
52070
+ try {
52071
+ deps.route(envelope);
52072
+ } catch (err) {
52073
+ deps.logger?.warn(`larkBot event route failed: ${err.message}`);
52074
+ }
52075
+ return { status: 202, body: { ok: true } };
52076
+ };
52077
+ }
52078
+
51529
52079
  // src/ticket/constants.ts
51530
52080
  var DEFAULT_CLAWD_TICKETS_API_KEY = "cf057f047674896978ddcc813adbc4a186551a6fcf59ad9d";
51531
52081
  function resolveClawdTicketsApiKey() {
@@ -52137,7 +52687,7 @@ var CodexHistoryReader = class {
52137
52687
  };
52138
52688
 
52139
52689
  // src/tools/codex-skills.ts
52140
- var import_node_path27 = __toESM(require("path"), 1);
52690
+ var import_node_path28 = __toESM(require("path"), 1);
52141
52691
  function str3(v2) {
52142
52692
  return typeof v2 === "string" ? v2 : void 0;
52143
52693
  }
@@ -52152,13 +52702,13 @@ function mapSkillsListResponse(res) {
52152
52702
  const r = s ?? {};
52153
52703
  const name = str3(r.name);
52154
52704
  if (!name) continue;
52155
- const path77 = str3(r.path);
52705
+ const path78 = str3(r.path);
52156
52706
  const description = str3(r.description);
52157
52707
  const isPlugin = name.includes(":");
52158
52708
  out.push({
52159
52709
  name,
52160
52710
  source: isPlugin ? "plugin" : "project",
52161
- ...path77 ? { path: path77 } : {},
52711
+ ...path78 ? { path: path78 } : {},
52162
52712
  ...description ? { description } : {},
52163
52713
  ...isPlugin ? { plugin: name.split(":")[0] } : {}
52164
52714
  });
@@ -52174,30 +52724,30 @@ async function listCodexSkills(cwd, deps = {}) {
52174
52724
  // 先写 extraRoots 再写 list(stdio 按写入顺序处理,fire-and-forget 不 await 回包;
52175
52725
  // codex-session.ts ready 序列同款,两处保持一致)。失败不阻塞 list。
52176
52726
  beforeRequest: (client) => {
52177
- void client.request("skills/extraRoots/set", { extraRoots: [import_node_path27.default.join(cwd, ".claude", "skills")] }).catch(() => void 0);
52727
+ void client.request("skills/extraRoots/set", { extraRoots: [import_node_path28.default.join(cwd, ".claude", "skills")] }).catch(() => void 0);
52178
52728
  }
52179
52729
  });
52180
52730
  return mapSkillsListResponse(res);
52181
52731
  }
52182
52732
 
52183
52733
  // src/workspace/browser.ts
52184
- var import_node_fs26 = __toESM(require("fs"), 1);
52734
+ var import_node_fs27 = __toESM(require("fs"), 1);
52185
52735
  var import_node_os8 = __toESM(require("os"), 1);
52186
- var import_node_path28 = __toESM(require("path"), 1);
52736
+ var import_node_path29 = __toESM(require("path"), 1);
52187
52737
  init_protocol();
52188
52738
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
52189
52739
  function resolveInsideCwd(cwd, subpath) {
52190
- const absCwd = import_node_path28.default.resolve(cwd);
52191
- const joined = import_node_path28.default.resolve(absCwd, subpath ?? ".");
52192
- const rel = import_node_path28.default.relative(absCwd, joined);
52193
- if (rel.startsWith("..") || import_node_path28.default.isAbsolute(rel)) {
52740
+ const absCwd = import_node_path29.default.resolve(cwd);
52741
+ const joined = import_node_path29.default.resolve(absCwd, subpath ?? ".");
52742
+ const rel = import_node_path29.default.relative(absCwd, joined);
52743
+ if (rel.startsWith("..") || import_node_path29.default.isAbsolute(rel)) {
52194
52744
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
52195
52745
  }
52196
52746
  return joined;
52197
52747
  }
52198
52748
  function ensureCwd(cwd) {
52199
52749
  try {
52200
- const stat = import_node_fs26.default.statSync(cwd);
52750
+ const stat = import_node_fs27.default.statSync(cwd);
52201
52751
  if (!stat.isDirectory()) {
52202
52752
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${cwd}`);
52203
52753
  }
@@ -52211,7 +52761,7 @@ var WorkspaceBrowser = class {
52211
52761
  const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os8.default.homedir();
52212
52762
  ensureCwd(cwd);
52213
52763
  const full = resolveInsideCwd(cwd, args.path);
52214
- const dirents = import_node_fs26.default.readdirSync(full, { withFileTypes: true });
52764
+ const dirents = import_node_fs27.default.readdirSync(full, { withFileTypes: true });
52215
52765
  const entries = [];
52216
52766
  for (const d of dirents) {
52217
52767
  if (!args.showHidden && d.name.startsWith(".")) continue;
@@ -52221,7 +52771,7 @@ var WorkspaceBrowser = class {
52221
52771
  mtime: ""
52222
52772
  };
52223
52773
  try {
52224
- const st = import_node_fs26.default.statSync(import_node_path28.default.join(full, d.name));
52774
+ const st = import_node_fs27.default.statSync(import_node_path29.default.join(full, d.name));
52225
52775
  entry.mtime = new Date(st.mtimeMs).toISOString();
52226
52776
  if (d.isFile()) entry.size = st.size;
52227
52777
  } catch {
@@ -52237,14 +52787,14 @@ var WorkspaceBrowser = class {
52237
52787
  read(args) {
52238
52788
  ensureCwd(args.cwd);
52239
52789
  const full = resolveInsideCwd(args.cwd, args.path);
52240
- const st = import_node_fs26.default.statSync(full);
52790
+ const st = import_node_fs27.default.statSync(full);
52241
52791
  if (!st.isFile()) {
52242
52792
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
52243
52793
  }
52244
52794
  if (st.size > MAX_FILE_BYTES) {
52245
52795
  throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES} bytes`);
52246
52796
  }
52247
- const buf = import_node_fs26.default.readFileSync(full);
52797
+ const buf = import_node_fs27.default.readFileSync(full);
52248
52798
  const isBinary = buf.includes(0);
52249
52799
  if (isBinary) {
52250
52800
  return {
@@ -52266,20 +52816,20 @@ var WorkspaceBrowser = class {
52266
52816
  };
52267
52817
 
52268
52818
  // src/skills/agents-scanner.ts
52269
- var import_node_fs27 = __toESM(require("fs"), 1);
52819
+ var import_node_fs28 = __toESM(require("fs"), 1);
52270
52820
  var import_node_os9 = __toESM(require("os"), 1);
52271
- var import_node_path29 = __toESM(require("path"), 1);
52821
+ var import_node_path30 = __toESM(require("path"), 1);
52272
52822
  var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
52273
52823
  function isDirLikeSync2(p2) {
52274
52824
  try {
52275
- return import_node_fs27.default.statSync(p2).isDirectory();
52825
+ return import_node_fs28.default.statSync(p2).isDirectory();
52276
52826
  } catch {
52277
52827
  return false;
52278
52828
  }
52279
52829
  }
52280
52830
  function fileExistsSync(p2) {
52281
52831
  try {
52282
- return import_node_fs27.default.statSync(p2).isFile();
52832
+ return import_node_fs28.default.statSync(p2).isFile();
52283
52833
  } catch {
52284
52834
  return false;
52285
52835
  }
@@ -52287,7 +52837,7 @@ function fileExistsSync(p2) {
52287
52837
  function parseAgentFile(filePath) {
52288
52838
  let content;
52289
52839
  try {
52290
- content = import_node_fs27.default.readFileSync(filePath, "utf8");
52840
+ content = import_node_fs28.default.readFileSync(filePath, "utf8");
52291
52841
  } catch {
52292
52842
  return {};
52293
52843
  }
@@ -52300,16 +52850,16 @@ function parseAgentFile(filePath) {
52300
52850
  function scanAgentsDir(dir, source, seen, out) {
52301
52851
  let entries;
52302
52852
  try {
52303
- entries = import_node_fs27.default.readdirSync(dir, { withFileTypes: true });
52853
+ entries = import_node_fs28.default.readdirSync(dir, { withFileTypes: true });
52304
52854
  } catch {
52305
52855
  return;
52306
52856
  }
52307
52857
  for (const ent of entries) {
52308
52858
  if (!ent.name.endsWith(".md")) continue;
52309
- if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path29.default.join(dir, ent.name)))) {
52859
+ if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path30.default.join(dir, ent.name)))) {
52310
52860
  continue;
52311
52861
  }
52312
- const filePath = import_node_path29.default.join(dir, ent.name);
52862
+ const filePath = import_node_path30.default.join(dir, ent.name);
52313
52863
  const baseName = ent.name.replace(/\.md$/, "");
52314
52864
  if (seen.has(baseName)) continue;
52315
52865
  seen.add(baseName);
@@ -52327,12 +52877,12 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
52327
52877
  function walk2(dir, namespaces) {
52328
52878
  let entries;
52329
52879
  try {
52330
- entries = import_node_fs27.default.readdirSync(dir, { withFileTypes: true });
52880
+ entries = import_node_fs28.default.readdirSync(dir, { withFileTypes: true });
52331
52881
  } catch {
52332
52882
  return;
52333
52883
  }
52334
52884
  for (const ent of entries) {
52335
- const childPath = import_node_path29.default.join(dir, ent.name);
52885
+ const childPath = import_node_path30.default.join(dir, ent.name);
52336
52886
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
52337
52887
  walk2(childPath, [...namespaces, ent.name]);
52338
52888
  continue;
@@ -52357,13 +52907,13 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
52357
52907
  walk2(root, []);
52358
52908
  }
52359
52909
  function readInstalledPlugins2(home) {
52360
- const pluginsDir = import_node_path29.default.join(home, ".claude", "plugins");
52361
- const v2 = import_node_path29.default.join(pluginsDir, "installed_plugins_v2.json");
52362
- const v1 = import_node_path29.default.join(pluginsDir, "installed_plugins.json");
52910
+ const pluginsDir = import_node_path30.default.join(home, ".claude", "plugins");
52911
+ const v2 = import_node_path30.default.join(pluginsDir, "installed_plugins_v2.json");
52912
+ const v1 = import_node_path30.default.join(pluginsDir, "installed_plugins.json");
52363
52913
  let raw = null;
52364
52914
  for (const candidate of [v2, v1]) {
52365
52915
  try {
52366
- raw = import_node_fs27.default.readFileSync(candidate, "utf8");
52916
+ raw = import_node_fs28.default.readFileSync(candidate, "utf8");
52367
52917
  break;
52368
52918
  } catch {
52369
52919
  }
@@ -52388,19 +52938,19 @@ function readInstalledPlugins2(home) {
52388
52938
  return out;
52389
52939
  }
52390
52940
  function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
52391
- let cur = import_node_path29.default.resolve(startCwd);
52392
- const fsRoot = import_node_path29.default.parse(cur).root;
52941
+ let cur = import_node_path30.default.resolve(startCwd);
52942
+ const fsRoot = import_node_path30.default.parse(cur).root;
52393
52943
  while (true) {
52394
- scanAgentsDir(import_node_path29.default.join(cur, ".claude", "agents"), "project", seen, out);
52944
+ scanAgentsDir(import_node_path30.default.join(cur, ".claude", "agents"), "project", seen, out);
52395
52945
  let hasGit = false;
52396
52946
  try {
52397
- hasGit = import_node_fs27.default.existsSync(import_node_path29.default.join(cur, ".git"));
52947
+ hasGit = import_node_fs28.default.existsSync(import_node_path30.default.join(cur, ".git"));
52398
52948
  } catch {
52399
52949
  }
52400
52950
  if (hasGit) return;
52401
52951
  if (cur === home) return;
52402
52952
  if (cur === fsRoot) return;
52403
- const parent = import_node_path29.default.dirname(cur);
52953
+ const parent = import_node_path30.default.dirname(cur);
52404
52954
  if (parent === cur) return;
52405
52955
  cur = parent;
52406
52956
  }
@@ -52435,7 +52985,7 @@ var AgentsScanner = class {
52435
52985
  }
52436
52986
  const fsBlock = [];
52437
52987
  scanAgentsDir(
52438
- import_node_path29.default.join(this.home, ".claude", "agents"),
52988
+ import_node_path30.default.join(this.home, ".claude", "agents"),
52439
52989
  "global",
52440
52990
  seen,
52441
52991
  fsBlock
@@ -52449,7 +52999,7 @@ var AgentsScanner = class {
52449
52999
  ...this.extraPluginRoots
52450
53000
  ];
52451
53001
  for (const { name, root } of plugins) {
52452
- const agentsRoot = import_node_path29.default.join(root, "agents");
53002
+ const agentsRoot = import_node_path30.default.join(root, "agents");
52453
53003
  scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
52454
53004
  }
52455
53005
  return [...builtinBlock, ...fsBlock];
@@ -52457,15 +53007,15 @@ var AgentsScanner = class {
52457
53007
  };
52458
53008
 
52459
53009
  // src/observer/session-observer.ts
52460
- var import_node_fs29 = __toESM(require("fs"), 1);
53010
+ var import_node_fs30 = __toESM(require("fs"), 1);
52461
53011
  var import_node_os11 = __toESM(require("os"), 1);
52462
- var import_node_path31 = __toESM(require("path"), 1);
53012
+ var import_node_path32 = __toESM(require("path"), 1);
52463
53013
  init_claude_history();
52464
53014
 
52465
53015
  // src/observer/subagent-meta-observer.ts
52466
- var import_node_fs28 = __toESM(require("fs"), 1);
53016
+ var import_node_fs29 = __toESM(require("fs"), 1);
52467
53017
  var import_node_os10 = __toESM(require("os"), 1);
52468
- var import_node_path30 = __toESM(require("path"), 1);
53018
+ var import_node_path31 = __toESM(require("path"), 1);
52469
53019
  init_claude_history();
52470
53020
  var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
52471
53021
  var SubagentMetaObserver = class {
@@ -52478,7 +53028,7 @@ var SubagentMetaObserver = class {
52478
53028
  watches = /* @__PURE__ */ new Map();
52479
53029
  // public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
52480
53030
  resolveSubagentDir(cwd, toolSessionId) {
52481
- return import_node_path30.default.join(
53031
+ return import_node_path31.default.join(
52482
53032
  this.home,
52483
53033
  ".claude",
52484
53034
  "projects",
@@ -52508,7 +53058,7 @@ var SubagentMetaObserver = class {
52508
53058
  attachWatcher(w2) {
52509
53059
  if (w2.watcher) return;
52510
53060
  try {
52511
- w2.watcher = import_node_fs28.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
53061
+ w2.watcher = import_node_fs29.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
52512
53062
  if (!name) return;
52513
53063
  const m2 = META_RE.exec(String(name));
52514
53064
  if (!m2) return;
@@ -52520,7 +53070,7 @@ var SubagentMetaObserver = class {
52520
53070
  scan(w2) {
52521
53071
  let entries;
52522
53072
  try {
52523
- entries = import_node_fs28.default.readdirSync(w2.dirPath);
53073
+ entries = import_node_fs29.default.readdirSync(w2.dirPath);
52524
53074
  } catch {
52525
53075
  return;
52526
53076
  }
@@ -52534,10 +53084,10 @@ var SubagentMetaObserver = class {
52534
53084
  if (!m2) return;
52535
53085
  const agentId = m2[1];
52536
53086
  if (w2.emitted.has(agentId)) return;
52537
- const file = import_node_path30.default.join(w2.dirPath, name);
53087
+ const file = import_node_path31.default.join(w2.dirPath, name);
52538
53088
  let raw;
52539
53089
  try {
52540
- raw = import_node_fs28.default.readFileSync(file, "utf8");
53090
+ raw = import_node_fs29.default.readFileSync(file, "utf8");
52541
53091
  } catch {
52542
53092
  return;
52543
53093
  }
@@ -52594,14 +53144,14 @@ var SessionObserver = class {
52594
53144
  metaObserver;
52595
53145
  resolveJsonlPath(cwd, toolSessionId, override) {
52596
53146
  if (override) return override;
52597
- return import_node_path31.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
53147
+ return import_node_path32.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
52598
53148
  }
52599
53149
  start(args) {
52600
53150
  this.stop(args.sessionId);
52601
53151
  const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
52602
53152
  let size = 0;
52603
53153
  try {
52604
- size = import_node_fs29.default.statSync(filePath).size;
53154
+ size = import_node_fs30.default.statSync(filePath).size;
52605
53155
  } catch {
52606
53156
  }
52607
53157
  const w2 = {
@@ -52614,16 +53164,16 @@ var SessionObserver = class {
52614
53164
  adapter: args.adapter,
52615
53165
  prevIsRejectSentinel: false,
52616
53166
  toolSessionId: args.toolSessionId,
52617
- sawFile: import_node_fs29.default.existsSync(filePath),
53167
+ sawFile: import_node_fs30.default.existsSync(filePath),
52618
53168
  lastRelocateScanMs: 0,
52619
53169
  pendingCwdDerive: false,
52620
53170
  lastDeriveMs: 0
52621
53171
  };
52622
53172
  try {
52623
- import_node_fs29.default.mkdirSync(import_node_path31.default.dirname(filePath), { recursive: true });
53173
+ import_node_fs30.default.mkdirSync(import_node_path32.default.dirname(filePath), { recursive: true });
52624
53174
  } catch {
52625
53175
  }
52626
- w2.watcher = import_node_fs29.default.watch(import_node_path31.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
53176
+ w2.watcher = import_node_fs30.default.watch(import_node_path32.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
52627
53177
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
52628
53178
  this.poll(w2);
52629
53179
  });
@@ -52646,7 +53196,7 @@ var SessionObserver = class {
52646
53196
  // 异常静默吞,不阻塞 watcher 启动
52647
53197
  hydrateMetaTail(w2, maxLines = 200) {
52648
53198
  try {
52649
- const raw = import_node_fs29.default.readFileSync(w2.filePath, "utf8");
53199
+ const raw = import_node_fs30.default.readFileSync(w2.filePath, "utf8");
52650
53200
  if (!raw) return;
52651
53201
  const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
52652
53202
  if (allLines.length === 0) return;
@@ -52670,7 +53220,7 @@ var SessionObserver = class {
52670
53220
  poll(w2) {
52671
53221
  let size = 0;
52672
53222
  try {
52673
- size = import_node_fs29.default.statSync(w2.filePath).size;
53223
+ size = import_node_fs30.default.statSync(w2.filePath).size;
52674
53224
  } catch {
52675
53225
  if (w2.sawFile) this.maybeRelocate(w2);
52676
53226
  return;
@@ -52681,11 +53231,11 @@ var SessionObserver = class {
52681
53231
  w2.buf = "";
52682
53232
  }
52683
53233
  if (size === w2.lastSize) return;
52684
- const fd = import_node_fs29.default.openSync(w2.filePath, "r");
53234
+ const fd = import_node_fs30.default.openSync(w2.filePath, "r");
52685
53235
  try {
52686
53236
  const len = size - w2.lastSize;
52687
53237
  const buf = Buffer.alloc(len);
52688
- import_node_fs29.default.readSync(fd, buf, 0, len, w2.lastSize);
53238
+ import_node_fs30.default.readSync(fd, buf, 0, len, w2.lastSize);
52689
53239
  w2.lastSize = size;
52690
53240
  w2.buf += buf.toString("utf8");
52691
53241
  let newlineIndex;
@@ -52708,7 +53258,7 @@ var SessionObserver = class {
52708
53258
  }
52709
53259
  }
52710
53260
  } finally {
52711
- import_node_fs29.default.closeSync(fd);
53261
+ import_node_fs30.default.closeSync(fd);
52712
53262
  }
52713
53263
  if (w2.pendingCwdDerive) this.maybeDeriveCwd(w2);
52714
53264
  }
@@ -52720,7 +53270,7 @@ var SessionObserver = class {
52720
53270
  const now = Date.now();
52721
53271
  if (now - w2.lastRelocateScanMs < 1e3) return;
52722
53272
  w2.lastRelocateScanMs = now;
52723
- const projectsRoot = import_node_path31.default.join(this.home, ".claude", "projects");
53273
+ const projectsRoot = import_node_path32.default.join(this.home, ".claude", "projects");
52724
53274
  const found = findTranscriptByToolSessionId(projectsRoot, w2.toolSessionId);
52725
53275
  if (!found || found === w2.filePath) return;
52726
53276
  try {
@@ -52729,7 +53279,7 @@ var SessionObserver = class {
52729
53279
  }
52730
53280
  w2.filePath = found;
52731
53281
  try {
52732
- w2.watcher = import_node_fs29.default.watch(import_node_path31.default.dirname(found), { persistent: false }, (_event, changedName) => {
53282
+ w2.watcher = import_node_fs30.default.watch(import_node_path32.default.dirname(found), { persistent: false }, (_event, changedName) => {
52733
53283
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
52734
53284
  this.poll(w2);
52735
53285
  });
@@ -53524,8 +54074,8 @@ async function authenticate(token, deps) {
53524
54074
  }
53525
54075
 
53526
54076
  // src/legacy-cleanup.ts
53527
- var fs34 = __toESM(require("fs/promises"), 1);
53528
- var path36 = __toESM(require("path"), 1);
54077
+ var fs35 = __toESM(require("fs/promises"), 1);
54078
+ var path37 = __toESM(require("path"), 1);
53529
54079
  var LEGACY_FILES = [
53530
54080
  "personal-capability.json",
53531
54081
  "received-capabilities.json",
@@ -53534,9 +54084,9 @@ var LEGACY_FILES = [
53534
54084
  async function cleanupLegacyCapabilityFiles(dataDir, logger) {
53535
54085
  const removed = [];
53536
54086
  for (const name of LEGACY_FILES) {
53537
- const p2 = path36.join(dataDir, name);
54087
+ const p2 = path37.join(dataDir, name);
53538
54088
  try {
53539
- await fs34.unlink(p2);
54089
+ await fs35.unlink(p2);
53540
54090
  removed.push(name);
53541
54091
  logger.debug("legacy capability file removed", { file: name });
53542
54092
  } catch (err) {
@@ -53548,13 +54098,13 @@ async function cleanupLegacyCapabilityFiles(dataDir, logger) {
53548
54098
  }
53549
54099
 
53550
54100
  // src/inbox/inbox-store.ts
53551
- var fs35 = __toESM(require("fs"), 1);
53552
- var path37 = __toESM(require("path"), 1);
54101
+ var fs36 = __toESM(require("fs"), 1);
54102
+ var path38 = __toESM(require("path"), 1);
53553
54103
  var INBOX_SUBDIR = "inbox";
53554
54104
  var InboxStore = class {
53555
54105
  constructor(dataDir) {
53556
54106
  this.dataDir = dataDir;
53557
- fs35.mkdirSync(this.dirPath(), { recursive: true });
54107
+ fs36.mkdirSync(this.dirPath(), { recursive: true });
53558
54108
  }
53559
54109
  dataDir;
53560
54110
  /**
@@ -53566,7 +54116,7 @@ var InboxStore = class {
53566
54116
  const file = this.filePath(peerDeviceId);
53567
54117
  let raw;
53568
54118
  try {
53569
- raw = fs35.readFileSync(file, "utf8");
54119
+ raw = fs36.readFileSync(file, "utf8");
53570
54120
  } catch (err) {
53571
54121
  if (err?.code === "ENOENT") return [];
53572
54122
  return [];
@@ -53582,7 +54132,7 @@ var InboxStore = class {
53582
54132
  const dir = this.dirPath();
53583
54133
  let entries;
53584
54134
  try {
53585
- entries = fs35.readdirSync(dir);
54135
+ entries = fs36.readdirSync(dir);
53586
54136
  } catch (err) {
53587
54137
  if (err?.code === "ENOENT") return [];
53588
54138
  return [];
@@ -53598,9 +54148,9 @@ var InboxStore = class {
53598
54148
  if (existing.some((m2) => m2.id === message.id)) return;
53599
54149
  const file = this.filePath(message.peerDeviceId);
53600
54150
  const line = JSON.stringify(message) + "\n";
53601
- fs35.appendFileSync(file, line, { mode: 384 });
54151
+ fs36.appendFileSync(file, line, { mode: 384 });
53602
54152
  try {
53603
- fs35.chmodSync(file, 384);
54153
+ fs36.chmodSync(file, 384);
53604
54154
  } catch {
53605
54155
  }
53606
54156
  }
@@ -53630,7 +54180,7 @@ var InboxStore = class {
53630
54180
  removeByPeerDeviceId(peerDeviceId) {
53631
54181
  const file = this.filePath(peerDeviceId);
53632
54182
  try {
53633
- fs35.unlinkSync(file);
54183
+ fs36.unlinkSync(file);
53634
54184
  } catch (err) {
53635
54185
  if (err?.code === "ENOENT") return;
53636
54186
  }
@@ -53639,18 +54189,18 @@ var InboxStore = class {
53639
54189
  const file = this.filePath(peerDeviceId);
53640
54190
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
53641
54191
  const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
53642
- fs35.writeFileSync(tmp, content, { mode: 384 });
53643
- fs35.renameSync(tmp, file);
54192
+ fs36.writeFileSync(tmp, content, { mode: 384 });
54193
+ fs36.renameSync(tmp, file);
53644
54194
  try {
53645
- fs35.chmodSync(file, 384);
54195
+ fs36.chmodSync(file, 384);
53646
54196
  } catch {
53647
54197
  }
53648
54198
  }
53649
54199
  dirPath() {
53650
- return path37.join(this.dataDir, INBOX_SUBDIR);
54200
+ return path38.join(this.dataDir, INBOX_SUBDIR);
53651
54201
  }
53652
54202
  filePath(peerDeviceId) {
53653
- return path37.join(this.dirPath(), `${peerDeviceId}.jsonl`);
54203
+ return path38.join(this.dirPath(), `${peerDeviceId}.jsonl`);
53654
54204
  }
53655
54205
  };
53656
54206
  function parseAllLines(raw) {
@@ -53738,8 +54288,8 @@ var InboxManager = class {
53738
54288
  };
53739
54289
 
53740
54290
  // src/state/contact-store.ts
53741
- var fs36 = __toESM(require("fs"), 1);
53742
- var path38 = __toESM(require("path"), 1);
54291
+ var fs37 = __toESM(require("fs"), 1);
54292
+ var path39 = __toESM(require("path"), 1);
53743
54293
  var FILE_NAME = "contacts.json";
53744
54294
  var ContactStore = class {
53745
54295
  constructor(dataDir) {
@@ -53749,10 +54299,10 @@ var ContactStore = class {
53749
54299
  contacts = /* @__PURE__ */ new Map();
53750
54300
  load() {
53751
54301
  this.contacts.clear();
53752
- const file = path38.join(this.dataDir, FILE_NAME);
54302
+ const file = path39.join(this.dataDir, FILE_NAME);
53753
54303
  let raw;
53754
54304
  try {
53755
- raw = fs36.readFileSync(file, "utf8");
54305
+ raw = fs37.readFileSync(file, "utf8");
53756
54306
  } catch (err) {
53757
54307
  if (err?.code !== "ENOENT") this.renameBak(file);
53758
54308
  return;
@@ -53821,20 +54371,20 @@ var ContactStore = class {
53821
54371
  return true;
53822
54372
  }
53823
54373
  flush() {
53824
- const file = path38.join(this.dataDir, FILE_NAME);
54374
+ const file = path39.join(this.dataDir, FILE_NAME);
53825
54375
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
53826
54376
  const content = JSON.stringify(
53827
54377
  { contacts: Array.from(this.contacts.values()) },
53828
54378
  null,
53829
54379
  2
53830
54380
  );
53831
- fs36.mkdirSync(this.dataDir, { recursive: true });
53832
- fs36.writeFileSync(tmp, content, { mode: 384 });
53833
- fs36.renameSync(tmp, file);
54381
+ fs37.mkdirSync(this.dataDir, { recursive: true });
54382
+ fs37.writeFileSync(tmp, content, { mode: 384 });
54383
+ fs37.renameSync(tmp, file);
53834
54384
  }
53835
54385
  renameBak(file) {
53836
54386
  try {
53837
- fs36.renameSync(file, `${file}.bak`);
54387
+ fs37.renameSync(file, `${file}.bak`);
53838
54388
  } catch {
53839
54389
  }
53840
54390
  }
@@ -53942,61 +54492,61 @@ async function connectRemote(args) {
53942
54492
  }
53943
54493
 
53944
54494
  // src/migrations/2026-05-20-flatten-sessions.ts
53945
- var fs37 = __toESM(require("fs"), 1);
53946
- var path39 = __toESM(require("path"), 1);
54495
+ var fs38 = __toESM(require("fs"), 1);
54496
+ var path40 = __toESM(require("path"), 1);
53947
54497
  var MIGRATION_FLAG_NAME = ".migration.v1.done";
53948
54498
  function migrateFlattenSessions(opts) {
53949
54499
  const dataDir = opts.dataDir;
53950
54500
  const now = opts.now ?? Date.now;
53951
- const sessionsDir = path39.join(dataDir, "sessions");
53952
- const flagPath = path39.join(sessionsDir, MIGRATION_FLAG_NAME);
54501
+ const sessionsDir = path40.join(dataDir, "sessions");
54502
+ const flagPath = path40.join(sessionsDir, MIGRATION_FLAG_NAME);
53953
54503
  if (existsSync3(flagPath)) {
53954
54504
  return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
53955
54505
  }
53956
54506
  let movedBare = 0;
53957
54507
  let movedVmOwner = 0;
53958
54508
  let archivedListener = 0;
53959
- const defaultDir = path39.join(sessionsDir, "default");
54509
+ const defaultDir = path40.join(sessionsDir, "default");
53960
54510
  if (existsSync3(defaultDir)) {
53961
54511
  for (const entry of readdirSafe(defaultDir)) {
53962
54512
  if (!entry.endsWith(".json")) continue;
53963
- const src = path39.join(defaultDir, entry);
53964
- const dst = path39.join(sessionsDir, entry);
53965
- fs37.renameSync(src, dst);
54513
+ const src = path40.join(defaultDir, entry);
54514
+ const dst = path40.join(sessionsDir, entry);
54515
+ fs38.renameSync(src, dst);
53966
54516
  movedBare += 1;
53967
54517
  }
53968
54518
  rmdirIfEmpty(defaultDir);
53969
54519
  }
53970
54520
  for (const pid of readdirSafe(sessionsDir)) {
53971
- const personaDir = path39.join(sessionsDir, pid);
54521
+ const personaDir = path40.join(sessionsDir, pid);
53972
54522
  if (!isDir(personaDir)) continue;
53973
54523
  if (pid === "default") continue;
53974
- const ownerSrc = path39.join(personaDir, "owner");
54524
+ const ownerSrc = path40.join(personaDir, "owner");
53975
54525
  if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
53976
- const ownerDst = path39.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
53977
- fs37.mkdirSync(ownerDst, { recursive: true });
54526
+ const ownerDst = path40.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
54527
+ fs38.mkdirSync(ownerDst, { recursive: true });
53978
54528
  for (const file of readdirSafe(ownerSrc)) {
53979
54529
  if (!file.endsWith(".json")) continue;
53980
- fs37.renameSync(path39.join(ownerSrc, file), path39.join(ownerDst, file));
54530
+ fs38.renameSync(path40.join(ownerSrc, file), path40.join(ownerDst, file));
53981
54531
  movedVmOwner += 1;
53982
54532
  }
53983
54533
  rmdirIfEmpty(ownerSrc);
53984
54534
  }
53985
- const listenerSrc = path39.join(personaDir, "listener");
54535
+ const listenerSrc = path40.join(personaDir, "listener");
53986
54536
  if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
53987
- const archiveDst = path39.join(dataDir, ".legacy", `listener-${pid}`);
53988
- fs37.mkdirSync(archiveDst, { recursive: true });
54537
+ const archiveDst = path40.join(dataDir, ".legacy", `listener-${pid}`);
54538
+ fs38.mkdirSync(archiveDst, { recursive: true });
53989
54539
  for (const file of readdirSafe(listenerSrc)) {
53990
54540
  if (!file.endsWith(".json")) continue;
53991
- fs37.renameSync(path39.join(listenerSrc, file), path39.join(archiveDst, file));
54541
+ fs38.renameSync(path40.join(listenerSrc, file), path40.join(archiveDst, file));
53992
54542
  archivedListener += 1;
53993
54543
  }
53994
54544
  rmdirIfEmpty(listenerSrc);
53995
54545
  }
53996
54546
  rmdirIfEmpty(personaDir);
53997
54547
  }
53998
- fs37.mkdirSync(sessionsDir, { recursive: true });
53999
- fs37.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
54548
+ fs38.mkdirSync(sessionsDir, { recursive: true });
54549
+ fs38.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
54000
54550
  return {
54001
54551
  skipped: false,
54002
54552
  flagWritten: true,
@@ -54007,7 +54557,7 @@ function migrateFlattenSessions(opts) {
54007
54557
  }
54008
54558
  function existsSync3(p2) {
54009
54559
  try {
54010
- fs37.statSync(p2);
54560
+ fs38.statSync(p2);
54011
54561
  return true;
54012
54562
  } catch {
54013
54563
  return false;
@@ -54015,31 +54565,31 @@ function existsSync3(p2) {
54015
54565
  }
54016
54566
  function isDir(p2) {
54017
54567
  try {
54018
- return fs37.statSync(p2).isDirectory();
54568
+ return fs38.statSync(p2).isDirectory();
54019
54569
  } catch {
54020
54570
  return false;
54021
54571
  }
54022
54572
  }
54023
54573
  function readdirSafe(p2) {
54024
54574
  try {
54025
- return fs37.readdirSync(p2);
54575
+ return fs38.readdirSync(p2);
54026
54576
  } catch {
54027
54577
  return [];
54028
54578
  }
54029
54579
  }
54030
54580
  function rmdirIfEmpty(p2) {
54031
54581
  try {
54032
- fs37.rmdirSync(p2);
54582
+ fs38.rmdirSync(p2);
54033
54583
  } catch {
54034
54584
  }
54035
54585
  }
54036
54586
 
54037
54587
  // src/transport/http-router.ts
54038
- var import_node_fs31 = __toESM(require("fs"), 1);
54039
- var import_node_path35 = __toESM(require("path"), 1);
54588
+ var import_node_fs32 = __toESM(require("fs"), 1);
54589
+ var import_node_path36 = __toESM(require("path"), 1);
54040
54590
 
54041
54591
  // src/attachment/mime.ts
54042
- var import_node_path32 = __toESM(require("path"), 1);
54592
+ var import_node_path33 = __toESM(require("path"), 1);
54043
54593
  var TEXT_PLAIN = "text/plain; charset=utf-8";
54044
54594
  var EXT_TO_NATIVE_MIME = {
54045
54595
  // 图片
@@ -54146,14 +54696,14 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
54146
54696
  ".mk"
54147
54697
  ]);
54148
54698
  function lookupMime(filePathOrName) {
54149
- const ext = import_node_path32.default.extname(filePathOrName).toLowerCase();
54699
+ const ext = import_node_path33.default.extname(filePathOrName).toLowerCase();
54150
54700
  if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
54151
54701
  if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
54152
54702
  return "application/octet-stream";
54153
54703
  }
54154
54704
 
54155
54705
  // src/attachment/sign-url.ts
54156
- var import_node_crypto6 = __toESM(require("crypto"), 1);
54706
+ var import_node_crypto7 = __toESM(require("crypto"), 1);
54157
54707
  var HMAC_ALGO = "sha256";
54158
54708
  function base64urlEncode(buf) {
54159
54709
  const b2 = typeof buf === "string" ? Buffer.from(buf, "utf8") : buf;
@@ -54171,7 +54721,7 @@ function decodeAbsPathFromUrl(encoded) {
54171
54721
  var DOMAIN_PREFIX2 = "clawd-attach-v1|";
54172
54722
  function computeSig(secret, absPath, e) {
54173
54723
  const msg = e === null ? `${DOMAIN_PREFIX2}${absPath}` : `${DOMAIN_PREFIX2}${absPath}|${e}`;
54174
- return import_node_crypto6.default.createHmac(HMAC_ALGO, secret).update(msg).digest();
54724
+ return import_node_crypto7.default.createHmac(HMAC_ALGO, secret).update(msg).digest();
54175
54725
  }
54176
54726
  function signUrlParts(secret, absPath, ttlSeconds, now = Date.now) {
54177
54727
  const e = ttlSeconds === null ? null : Math.floor(now() / 1e3) + ttlSeconds;
@@ -54206,7 +54756,7 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
54206
54756
  if (provided.length !== expected.length) {
54207
54757
  return { ok: false, code: "BAD_SIG" };
54208
54758
  }
54209
- if (!import_node_crypto6.default.timingSafeEqual(provided, expected)) {
54759
+ if (!import_node_crypto7.default.timingSafeEqual(provided, expected)) {
54210
54760
  return { ok: false, code: "BAD_SIG" };
54211
54761
  }
54212
54762
  if (e !== null && now() / 1e3 > e) {
@@ -54216,9 +54766,9 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
54216
54766
  }
54217
54767
 
54218
54768
  // src/attachment/upload.ts
54219
- var import_node_fs30 = __toESM(require("fs"), 1);
54220
- var import_node_path33 = __toESM(require("path"), 1);
54221
- var import_node_crypto7 = __toESM(require("crypto"), 1);
54769
+ var import_node_fs31 = __toESM(require("fs"), 1);
54770
+ var import_node_path34 = __toESM(require("path"), 1);
54771
+ var import_node_crypto8 = __toESM(require("crypto"), 1);
54222
54772
  var import_promises3 = require("stream/promises");
54223
54773
  var UploadError = class extends Error {
54224
54774
  constructor(code, message) {
@@ -54229,24 +54779,24 @@ var UploadError = class extends Error {
54229
54779
  code;
54230
54780
  };
54231
54781
  function assertValidFileName(fileName) {
54232
- if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path33.default.basename(fileName)) {
54782
+ if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path34.default.basename(fileName)) {
54233
54783
  throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
54234
54784
  }
54235
54785
  }
54236
54786
  var HASH_PREFIX_LEN = 16;
54237
54787
  async function writeUploadedAttachment(args) {
54238
54788
  assertValidFileName(args.fileName);
54239
- const attachmentsRoot = import_node_path33.default.join(args.sessionDir, ".attachments");
54789
+ const attachmentsRoot = import_node_path34.default.join(args.sessionDir, ".attachments");
54240
54790
  try {
54241
- import_node_fs30.default.mkdirSync(attachmentsRoot, { recursive: true });
54791
+ import_node_fs31.default.mkdirSync(attachmentsRoot, { recursive: true });
54242
54792
  } catch (err) {
54243
54793
  throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
54244
54794
  }
54245
- const hasher = import_node_crypto7.default.createHash("sha256");
54795
+ const hasher = import_node_crypto8.default.createHash("sha256");
54246
54796
  let actualSize = 0;
54247
- const tmpPath = import_node_path33.default.join(
54797
+ const tmpPath = import_node_path34.default.join(
54248
54798
  attachmentsRoot,
54249
- `.upload-${process.pid}-${Date.now()}-${import_node_crypto7.default.randomBytes(4).toString("hex")}`
54799
+ `.upload-${process.pid}-${Date.now()}-${import_node_crypto8.default.randomBytes(4).toString("hex")}`
54250
54800
  );
54251
54801
  try {
54252
54802
  await (0, import_promises3.pipeline)(
@@ -54259,18 +54809,18 @@ async function writeUploadedAttachment(args) {
54259
54809
  yield buf;
54260
54810
  }
54261
54811
  },
54262
- import_node_fs30.default.createWriteStream(tmpPath, { mode: 384 })
54812
+ import_node_fs31.default.createWriteStream(tmpPath, { mode: 384 })
54263
54813
  );
54264
54814
  } catch (err) {
54265
54815
  try {
54266
- import_node_fs30.default.unlinkSync(tmpPath);
54816
+ import_node_fs31.default.unlinkSync(tmpPath);
54267
54817
  } catch {
54268
54818
  }
54269
54819
  throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
54270
54820
  }
54271
54821
  if (actualSize !== args.contentLength) {
54272
54822
  try {
54273
- import_node_fs30.default.unlinkSync(tmpPath);
54823
+ import_node_fs31.default.unlinkSync(tmpPath);
54274
54824
  } catch {
54275
54825
  }
54276
54826
  throw new UploadError(
@@ -54279,35 +54829,35 @@ async function writeUploadedAttachment(args) {
54279
54829
  );
54280
54830
  }
54281
54831
  const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
54282
- const hashDir = import_node_path33.default.join(attachmentsRoot, attachmentId);
54832
+ const hashDir = import_node_path34.default.join(attachmentsRoot, attachmentId);
54283
54833
  let finalFileName;
54284
54834
  let hashDirExists = false;
54285
54835
  try {
54286
- hashDirExists = import_node_fs30.default.statSync(hashDir).isDirectory();
54836
+ hashDirExists = import_node_fs31.default.statSync(hashDir).isDirectory();
54287
54837
  } catch {
54288
54838
  }
54289
54839
  if (hashDirExists) {
54290
- const existing = import_node_fs30.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
54840
+ const existing = import_node_fs31.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
54291
54841
  finalFileName = existing[0] ?? args.fileName;
54292
54842
  try {
54293
- import_node_fs30.default.unlinkSync(tmpPath);
54843
+ import_node_fs31.default.unlinkSync(tmpPath);
54294
54844
  } catch {
54295
54845
  }
54296
54846
  } else {
54297
54847
  try {
54298
- import_node_fs30.default.mkdirSync(hashDir, { recursive: true });
54848
+ import_node_fs31.default.mkdirSync(hashDir, { recursive: true });
54299
54849
  finalFileName = args.fileName;
54300
- import_node_fs30.default.renameSync(tmpPath, import_node_path33.default.join(hashDir, finalFileName));
54850
+ import_node_fs31.default.renameSync(tmpPath, import_node_path34.default.join(hashDir, finalFileName));
54301
54851
  } catch (err) {
54302
54852
  try {
54303
- import_node_fs30.default.unlinkSync(tmpPath);
54853
+ import_node_fs31.default.unlinkSync(tmpPath);
54304
54854
  } catch {
54305
54855
  }
54306
54856
  throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
54307
54857
  }
54308
54858
  }
54309
- const absPath = import_node_path33.default.join(hashDir, finalFileName);
54310
- const relPath = import_node_path33.default.relative(args.sessionDir, absPath);
54859
+ const absPath = import_node_path34.default.join(hashDir, finalFileName);
54860
+ const relPath = import_node_path34.default.relative(args.sessionDir, absPath);
54311
54861
  args.groupFileStore.upsert(args.scope, args.sessionId, {
54312
54862
  relPath: absPath,
54313
54863
  // 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
@@ -54321,7 +54871,7 @@ async function writeUploadedAttachment(args) {
54321
54871
 
54322
54872
  // src/extension/import.ts
54323
54873
  var import_promises4 = __toESM(require("fs/promises"), 1);
54324
- var import_node_path34 = __toESM(require("path"), 1);
54874
+ var import_node_path35 = __toESM(require("path"), 1);
54325
54875
  var import_node_os12 = __toESM(require("os"), 1);
54326
54876
  var import_jszip = __toESM(require_lib3(), 1);
54327
54877
  var ImportError = class extends Error {
@@ -54339,7 +54889,7 @@ async function importZip(buf, root) {
54339
54889
  throw new ImportError("ZIP_INVALID", `failed to load zip: ${e.message}`);
54340
54890
  }
54341
54891
  for (const name of Object.keys(zip.files)) {
54342
- if (name.includes("..") || name.startsWith("/") || import_node_path34.default.isAbsolute(name)) {
54892
+ if (name.includes("..") || name.startsWith("/") || import_node_path35.default.isAbsolute(name)) {
54343
54893
  throw new ImportError("ZIP_INVALID", `unsafe zip entry path: ${name}`);
54344
54894
  }
54345
54895
  }
@@ -54372,7 +54922,7 @@ async function importZip(buf, root) {
54372
54922
  );
54373
54923
  }
54374
54924
  }
54375
- const destDir = import_node_path34.default.join(root, manifest.id);
54925
+ const destDir = import_node_path35.default.join(root, manifest.id);
54376
54926
  let destExists = false;
54377
54927
  try {
54378
54928
  await import_promises4.default.access(destDir);
@@ -54382,15 +54932,15 @@ async function importZip(buf, root) {
54382
54932
  if (destExists) {
54383
54933
  throw new ImportError("ALREADY_EXISTS", `extension ${manifest.id} already installed`);
54384
54934
  }
54385
- const stage = await import_promises4.default.mkdtemp(import_node_path34.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
54935
+ const stage = await import_promises4.default.mkdtemp(import_node_path35.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
54386
54936
  try {
54387
54937
  for (const [name, entry] of Object.entries(zip.files)) {
54388
- const dest = import_node_path34.default.join(stage, name);
54938
+ const dest = import_node_path35.default.join(stage, name);
54389
54939
  if (entry.dir) {
54390
54940
  await import_promises4.default.mkdir(dest, { recursive: true });
54391
54941
  continue;
54392
54942
  }
54393
- await import_promises4.default.mkdir(import_node_path34.default.dirname(dest), { recursive: true });
54943
+ await import_promises4.default.mkdir(import_node_path35.default.dirname(dest), { recursive: true });
54394
54944
  const content = await entry.async("nodebuffer");
54395
54945
  await import_promises4.default.writeFile(dest, content);
54396
54946
  }
@@ -54420,7 +54970,7 @@ var SHARE_UI_ASSET_MIME = {
54420
54970
  ".wasm": "application/wasm"
54421
54971
  };
54422
54972
  function shareUiAssetMime(filePath) {
54423
- const ext = import_node_path35.default.extname(filePath).toLowerCase();
54973
+ const ext = import_node_path36.default.extname(filePath).toLowerCase();
54424
54974
  return SHARE_UI_ASSET_MIME[ext] ?? lookupMime(filePath);
54425
54975
  }
54426
54976
  var AUTHED_RPC_ROUTE_PREFIX = "/rpc/";
@@ -54445,6 +54995,24 @@ function authedRpcErrorStatus(code) {
54445
54995
  return 400;
54446
54996
  }
54447
54997
  }
54998
+ var LARK_BOT_EVENT_MAX_BYTES = 64 * 1024;
54999
+ async function readRawBody(req, max) {
55000
+ return await new Promise((resolve6, reject) => {
55001
+ let size = 0;
55002
+ const chunks = [];
55003
+ req.on("data", (c) => {
55004
+ size += c.length;
55005
+ if (size > max) {
55006
+ reject(new Error(`body too large (> ${max} bytes)`));
55007
+ req.destroy();
55008
+ return;
55009
+ }
55010
+ chunks.push(c);
55011
+ });
55012
+ req.on("end", () => resolve6(Buffer.concat(chunks).toString("utf8")));
55013
+ req.on("error", reject);
55014
+ });
55015
+ }
54448
55016
  async function readJsonBody(req, max) {
54449
55017
  return await new Promise((resolve6, reject) => {
54450
55018
  let size = 0;
@@ -54501,7 +55069,7 @@ function isValidUploadFileName(fileName) {
54501
55069
  if (fileName === "." || fileName === "..") return false;
54502
55070
  if (fileName.startsWith(".")) return false;
54503
55071
  if (fileName.includes("/") || fileName.includes("\\")) return false;
54504
- return fileName === import_node_path35.default.basename(fileName);
55072
+ return fileName === import_node_path36.default.basename(fileName);
54505
55073
  }
54506
55074
  function createHttpRouter(deps) {
54507
55075
  return async (req, res) => {
@@ -54557,6 +55125,22 @@ function createHttpRouter(deps) {
54557
55125
  else sendJson(res, 403, { code: r.reason }, AUTHED_RPC_CORS_HEADERS);
54558
55126
  return true;
54559
55127
  }
55128
+ if (url.pathname === "/lark-bot-connector/event" && req.method === "POST") {
55129
+ if (!deps.larkBotEvent) {
55130
+ sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "lark bot event not wired" });
55131
+ return true;
55132
+ }
55133
+ let raw;
55134
+ try {
55135
+ raw = await readRawBody(req, LARK_BOT_EVENT_MAX_BYTES);
55136
+ } catch (err) {
55137
+ sendJson(res, 400, { code: "INVALID_BODY", message: err instanceof Error ? err.message : String(err) });
55138
+ return true;
55139
+ }
55140
+ const r = deps.larkBotEvent(raw);
55141
+ sendJson(res, r.status, r.body);
55142
+ return true;
55143
+ }
54560
55144
  if ((url.pathname === "/s" || url.pathname.startsWith("/s/")) && req.method === "GET") {
54561
55145
  if (!deps.shareUiAssetLoader) {
54562
55146
  sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "share-ui assets not loaded" });
@@ -54573,7 +55157,7 @@ function createHttpRouter(deps) {
54573
55157
  }
54574
55158
  let buf;
54575
55159
  try {
54576
- buf = await import_node_fs31.default.promises.readFile(abs);
55160
+ buf = await import_node_fs32.default.promises.readFile(abs);
54577
55161
  } catch {
54578
55162
  sendJson(res, 404, { code: "NOT_FOUND", message: "asset not found" });
54579
55163
  return true;
@@ -54721,7 +55305,7 @@ function createHttpRouter(deps) {
54721
55305
  sendHtml(res, statusByCode[r.code], loader.renderErrorHtml(r.code, msgByCode[r.code]));
54722
55306
  return true;
54723
55307
  }
54724
- sendHtml(res, 200, loader.renderViewerHtml(import_node_path35.default.basename(r.absPath)));
55308
+ sendHtml(res, 200, loader.renderViewerHtml(import_node_path36.default.basename(r.absPath)));
54725
55309
  return true;
54726
55310
  }
54727
55311
  const ctx = deps.authResolver.resolveFromHeader(
@@ -54842,7 +55426,7 @@ function createHttpRouter(deps) {
54842
55426
  return true;
54843
55427
  }
54844
55428
  let absPath;
54845
- if (import_node_path35.default.isAbsolute(pathParam)) {
55429
+ if (import_node_path36.default.isAbsolute(pathParam)) {
54846
55430
  absPath = pathParam;
54847
55431
  } else if (deps.sessionStore) {
54848
55432
  const file = deps.sessionStore.read(sid);
@@ -54850,7 +55434,7 @@ function createHttpRouter(deps) {
54850
55434
  sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
54851
55435
  return true;
54852
55436
  }
54853
- absPath = import_node_path35.default.join(file.cwd, pathParam);
55437
+ absPath = import_node_path36.default.join(file.cwd, pathParam);
54854
55438
  } else {
54855
55439
  sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
54856
55440
  return true;
@@ -54937,7 +55521,7 @@ function withCtx(ctx, body) {
54937
55521
  function streamFile(res, absPath, logger) {
54938
55522
  let stat;
54939
55523
  try {
54940
- stat = import_node_fs31.default.statSync(absPath);
55524
+ stat = import_node_fs32.default.statSync(absPath);
54941
55525
  } catch (err) {
54942
55526
  const code = err?.code;
54943
55527
  if (code === "ENOENT") {
@@ -54952,7 +55536,7 @@ function streamFile(res, absPath, logger) {
54952
55536
  return;
54953
55537
  }
54954
55538
  const mime = lookupMime(absPath);
54955
- const basename = import_node_path35.default.basename(absPath);
55539
+ const basename = import_node_path36.default.basename(absPath);
54956
55540
  res.writeHead(200, {
54957
55541
  "Content-Type": mime,
54958
55542
  "Content-Length": String(stat.size),
@@ -54960,7 +55544,7 @@ function streamFile(res, absPath, logger) {
54960
55544
  // 防止浏览器把任意 mime 当 html 渲染
54961
55545
  "X-Content-Type-Options": "nosniff"
54962
55546
  });
54963
- const stream = import_node_fs31.default.createReadStream(absPath);
55547
+ const stream = import_node_fs32.default.createReadStream(absPath);
54964
55548
  stream.on("error", (err) => {
54965
55549
  logger?.warn("streamFile read error", { absPath, err: err.message });
54966
55550
  res.destroy();
@@ -54969,8 +55553,8 @@ function streamFile(res, absPath, logger) {
54969
55553
  }
54970
55554
 
54971
55555
  // src/attachment/gc.ts
54972
- var import_node_fs32 = __toESM(require("fs"), 1);
54973
- var import_node_path36 = __toESM(require("path"), 1);
55556
+ var import_node_fs33 = __toESM(require("fs"), 1);
55557
+ var import_node_path37 = __toESM(require("path"), 1);
54974
55558
  var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
54975
55559
  function runAttachmentGc(args) {
54976
55560
  const now = (args.now ?? Date.now)();
@@ -54979,38 +55563,38 @@ function runAttachmentGc(args) {
54979
55563
  for (const { scope, sessionId } of args.sessionScopes) {
54980
55564
  for (const entry of args.groupFileStore.list(scope, sessionId)) {
54981
55565
  if (entry.stale) continue;
54982
- if (import_node_path36.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
55566
+ if (import_node_path37.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
54983
55567
  }
54984
55568
  }
54985
55569
  for (const { scope, sessionId } of args.sessionScopes) {
54986
- const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path36.default.join(
55570
+ const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path37.default.join(
54987
55571
  args.dataDir,
54988
55572
  "sessions",
54989
55573
  ...scopeSubPath(scope).map(safeFileName),
54990
55574
  safeFileName(sessionId)
54991
55575
  );
54992
- const attRoot = import_node_path36.default.join(sessionDir, ".attachments");
55576
+ const attRoot = import_node_path37.default.join(sessionDir, ".attachments");
54993
55577
  let hashDirs;
54994
55578
  try {
54995
- hashDirs = import_node_fs32.default.readdirSync(attRoot);
55579
+ hashDirs = import_node_fs33.default.readdirSync(attRoot);
54996
55580
  } catch (err) {
54997
55581
  if (err.code === "ENOENT") continue;
54998
55582
  args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
54999
55583
  continue;
55000
55584
  }
55001
55585
  for (const hashDir of hashDirs) {
55002
- const hashDirAbs = import_node_path36.default.join(attRoot, hashDir);
55586
+ const hashDirAbs = import_node_path37.default.join(attRoot, hashDir);
55003
55587
  let files;
55004
55588
  try {
55005
- files = import_node_fs32.default.readdirSync(hashDirAbs);
55589
+ files = import_node_fs33.default.readdirSync(hashDirAbs);
55006
55590
  } catch {
55007
55591
  continue;
55008
55592
  }
55009
55593
  for (const name of files) {
55010
- const file = import_node_path36.default.join(hashDirAbs, name);
55594
+ const file = import_node_path37.default.join(hashDirAbs, name);
55011
55595
  let stat;
55012
55596
  try {
55013
- stat = import_node_fs32.default.statSync(file);
55597
+ stat = import_node_fs33.default.statSync(file);
55014
55598
  } catch {
55015
55599
  continue;
55016
55600
  }
@@ -55019,27 +55603,27 @@ function runAttachmentGc(args) {
55019
55603
  if (age < ttlMs) continue;
55020
55604
  if (liveAbs.has(file)) continue;
55021
55605
  try {
55022
- import_node_fs32.default.unlinkSync(file);
55606
+ import_node_fs33.default.unlinkSync(file);
55023
55607
  } catch (err) {
55024
55608
  args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
55025
55609
  }
55026
55610
  }
55027
55611
  try {
55028
- if (import_node_fs32.default.readdirSync(hashDirAbs).length === 0) import_node_fs32.default.rmdirSync(hashDirAbs);
55612
+ if (import_node_fs33.default.readdirSync(hashDirAbs).length === 0) import_node_fs33.default.rmdirSync(hashDirAbs);
55029
55613
  } catch {
55030
55614
  }
55031
55615
  }
55032
55616
  try {
55033
- if (import_node_fs32.default.readdirSync(attRoot).length === 0) import_node_fs32.default.rmdirSync(attRoot);
55617
+ if (import_node_fs33.default.readdirSync(attRoot).length === 0) import_node_fs33.default.rmdirSync(attRoot);
55034
55618
  } catch {
55035
55619
  }
55036
55620
  }
55037
55621
  }
55038
55622
 
55039
55623
  // src/attachment/group.ts
55040
- var import_node_fs33 = __toESM(require("fs"), 1);
55041
- var import_node_path37 = __toESM(require("path"), 1);
55042
- var import_node_crypto8 = __toESM(require("crypto"), 1);
55624
+ var import_node_fs34 = __toESM(require("fs"), 1);
55625
+ var import_node_path38 = __toESM(require("path"), 1);
55626
+ var import_node_crypto9 = __toESM(require("crypto"), 1);
55043
55627
  init_protocol();
55044
55628
  var GroupFileStore = class {
55045
55629
  dataDir;
@@ -55050,11 +55634,11 @@ var GroupFileStore = class {
55050
55634
  this.logger = opts.logger;
55051
55635
  }
55052
55636
  rootForScope(scope) {
55053
- return import_node_path37.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
55637
+ return import_node_path38.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
55054
55638
  }
55055
55639
  /** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
55056
55640
  filePath(scope, sessionId) {
55057
- return import_node_path37.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
55641
+ return import_node_path38.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
55058
55642
  }
55059
55643
  cacheKey(scope, sessionId) {
55060
55644
  return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
@@ -55063,7 +55647,7 @@ var GroupFileStore = class {
55063
55647
  readFile(scope, sessionId) {
55064
55648
  const file = this.filePath(scope, sessionId);
55065
55649
  try {
55066
- const raw = import_node_fs33.default.readFileSync(file, "utf8");
55650
+ const raw = import_node_fs34.default.readFileSync(file, "utf8");
55067
55651
  const parsed = JSON.parse(raw);
55068
55652
  if (!Array.isArray(parsed)) {
55069
55653
  this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
@@ -55089,10 +55673,10 @@ var GroupFileStore = class {
55089
55673
  }
55090
55674
  writeFile(scope, sessionId, entries) {
55091
55675
  const file = this.filePath(scope, sessionId);
55092
- import_node_fs33.default.mkdirSync(import_node_path37.default.dirname(file), { recursive: true });
55676
+ import_node_fs34.default.mkdirSync(import_node_path38.default.dirname(file), { recursive: true });
55093
55677
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
55094
- import_node_fs33.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
55095
- import_node_fs33.default.renameSync(tmp, file);
55678
+ import_node_fs34.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
55679
+ import_node_fs34.default.renameSync(tmp, file);
55096
55680
  }
55097
55681
  /** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
55098
55682
  list(scope, sessionId) {
@@ -55128,7 +55712,7 @@ var GroupFileStore = class {
55128
55712
  entries[idx] = next;
55129
55713
  } else {
55130
55714
  next = {
55131
- id: `gf-${import_node_crypto8.default.randomBytes(6).toString("base64url")}`,
55715
+ id: `gf-${import_node_crypto9.default.randomBytes(6).toString("base64url")}`,
55132
55716
  relPath: input.relPath,
55133
55717
  from: input.from,
55134
55718
  label: input.label,
@@ -55178,10 +55762,10 @@ var GroupFileStore = class {
55178
55762
  };
55179
55763
 
55180
55764
  // src/discovery/state-file.ts
55181
- var import_node_fs34 = __toESM(require("fs"), 1);
55182
- var import_node_path38 = __toESM(require("path"), 1);
55765
+ var import_node_fs35 = __toESM(require("fs"), 1);
55766
+ var import_node_path39 = __toESM(require("path"), 1);
55183
55767
  function defaultStateFilePath(dataDir) {
55184
- return import_node_path38.default.join(dataDir, "state.json");
55768
+ return import_node_path39.default.join(dataDir, "state.json");
55185
55769
  }
55186
55770
  function isPidAlive(pid) {
55187
55771
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -55203,7 +55787,7 @@ var StateFileManager = class {
55203
55787
  }
55204
55788
  read() {
55205
55789
  try {
55206
- const raw = import_node_fs34.default.readFileSync(this.file, "utf8");
55790
+ const raw = import_node_fs35.default.readFileSync(this.file, "utf8");
55207
55791
  const parsed = JSON.parse(raw);
55208
55792
  return parsed;
55209
55793
  } catch {
@@ -55217,20 +55801,20 @@ var StateFileManager = class {
55217
55801
  return { status: "stale", existing };
55218
55802
  }
55219
55803
  write(state) {
55220
- import_node_fs34.default.mkdirSync(import_node_path38.default.dirname(this.file), { recursive: true });
55804
+ import_node_fs35.default.mkdirSync(import_node_path39.default.dirname(this.file), { recursive: true });
55221
55805
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
55222
- import_node_fs34.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
55223
- import_node_fs34.default.renameSync(tmp, this.file);
55806
+ import_node_fs35.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
55807
+ import_node_fs35.default.renameSync(tmp, this.file);
55224
55808
  if (process.platform !== "win32") {
55225
55809
  try {
55226
- import_node_fs34.default.chmodSync(this.file, 384);
55810
+ import_node_fs35.default.chmodSync(this.file, 384);
55227
55811
  } catch {
55228
55812
  }
55229
55813
  }
55230
55814
  }
55231
55815
  delete() {
55232
55816
  try {
55233
- import_node_fs34.default.unlinkSync(this.file);
55817
+ import_node_fs35.default.unlinkSync(this.file);
55234
55818
  } catch {
55235
55819
  }
55236
55820
  }
@@ -55244,14 +55828,14 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
55244
55828
  }
55245
55829
 
55246
55830
  // src/tunnel/tunnel-manager.ts
55247
- var import_node_fs38 = __toESM(require("fs"), 1);
55248
- var import_node_path42 = __toESM(require("path"), 1);
55249
- var import_node_crypto9 = __toESM(require("crypto"), 1);
55831
+ var import_node_fs39 = __toESM(require("fs"), 1);
55832
+ var import_node_path43 = __toESM(require("path"), 1);
55833
+ var import_node_crypto10 = __toESM(require("crypto"), 1);
55250
55834
  var import_node_child_process9 = require("child_process");
55251
55835
 
55252
55836
  // src/tunnel/tunnel-store.ts
55253
- var import_node_fs35 = __toESM(require("fs"), 1);
55254
- var import_node_path39 = __toESM(require("path"), 1);
55837
+ var import_node_fs36 = __toESM(require("fs"), 1);
55838
+ var import_node_path40 = __toESM(require("path"), 1);
55255
55839
  var TunnelStore = class {
55256
55840
  constructor(filePath) {
55257
55841
  this.filePath = filePath;
@@ -55259,7 +55843,7 @@ var TunnelStore = class {
55259
55843
  filePath;
55260
55844
  async get() {
55261
55845
  try {
55262
- const raw = await import_node_fs35.default.promises.readFile(this.filePath, "utf8");
55846
+ const raw = await import_node_fs36.default.promises.readFile(this.filePath, "utf8");
55263
55847
  const obj = JSON.parse(raw);
55264
55848
  if (!isPersistedTunnel(obj)) return null;
55265
55849
  return obj;
@@ -55270,22 +55854,22 @@ var TunnelStore = class {
55270
55854
  }
55271
55855
  }
55272
55856
  async set(v2) {
55273
- const dir = import_node_path39.default.dirname(this.filePath);
55274
- await import_node_fs35.default.promises.mkdir(dir, { recursive: true });
55857
+ const dir = import_node_path40.default.dirname(this.filePath);
55858
+ await import_node_fs36.default.promises.mkdir(dir, { recursive: true });
55275
55859
  const data = JSON.stringify(v2, null, 2);
55276
55860
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
55277
- await import_node_fs35.default.promises.writeFile(tmp, data, { mode: 384 });
55861
+ await import_node_fs36.default.promises.writeFile(tmp, data, { mode: 384 });
55278
55862
  if (process.platform !== "win32") {
55279
55863
  try {
55280
- await import_node_fs35.default.promises.chmod(tmp, 384);
55864
+ await import_node_fs36.default.promises.chmod(tmp, 384);
55281
55865
  } catch {
55282
55866
  }
55283
55867
  }
55284
- await import_node_fs35.default.promises.rename(tmp, this.filePath);
55868
+ await import_node_fs36.default.promises.rename(tmp, this.filePath);
55285
55869
  }
55286
55870
  async clear() {
55287
55871
  try {
55288
- await import_node_fs35.default.promises.unlink(this.filePath);
55872
+ await import_node_fs36.default.promises.unlink(this.filePath);
55289
55873
  } catch (err) {
55290
55874
  const code = err?.code;
55291
55875
  if (code !== "ENOENT") throw err;
@@ -55380,9 +55964,9 @@ function escape(v2) {
55380
55964
  }
55381
55965
 
55382
55966
  // src/tunnel/frpc-binary.ts
55383
- var import_node_fs36 = __toESM(require("fs"), 1);
55967
+ var import_node_fs37 = __toESM(require("fs"), 1);
55384
55968
  var import_node_os13 = __toESM(require("os"), 1);
55385
- var import_node_path40 = __toESM(require("path"), 1);
55969
+ var import_node_path41 = __toESM(require("path"), 1);
55386
55970
  var import_node_child_process7 = require("child_process");
55387
55971
  var import_node_stream3 = require("stream");
55388
55972
  var import_promises5 = require("stream/promises");
@@ -55414,20 +55998,20 @@ function frpcDownloadUrl(version2, p2) {
55414
55998
  }
55415
55999
  async function ensureFrpcBinary(opts) {
55416
56000
  if (opts.override) {
55417
- if (!import_node_fs36.default.existsSync(opts.override)) {
56001
+ if (!import_node_fs37.default.existsSync(opts.override)) {
55418
56002
  throw new Error(`frpc binary not found at override path: ${opts.override}`);
55419
56003
  }
55420
56004
  return opts.override;
55421
56005
  }
55422
56006
  const version2 = opts.version ?? FRPC_VERSION;
55423
56007
  const platform = opts.platform ?? detectPlatform();
55424
- const binDir = import_node_path40.default.join(opts.dataDir, "bin");
55425
- import_node_fs36.default.mkdirSync(binDir, { recursive: true });
56008
+ const binDir = import_node_path41.default.join(opts.dataDir, "bin");
56009
+ import_node_fs37.default.mkdirSync(binDir, { recursive: true });
55426
56010
  cleanupStaleArtifacts(binDir);
55427
- const stableBin = import_node_path40.default.join(binDir, "frpc");
55428
- if (import_node_fs36.default.existsSync(stableBin)) return stableBin;
56011
+ const stableBin = import_node_path41.default.join(binDir, "frpc");
56012
+ if (import_node_fs37.default.existsSync(stableBin)) return stableBin;
55429
56013
  const partialBin = `${stableBin}.partial`;
55430
- const tarballPath = import_node_path40.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
56014
+ const tarballPath = import_node_path41.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
55431
56015
  try {
55432
56016
  const url = frpcDownloadUrl(version2, platform);
55433
56017
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -55436,8 +56020,8 @@ async function ensureFrpcBinary(opts) {
55436
56020
  } else {
55437
56021
  await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
55438
56022
  }
55439
- import_node_fs36.default.chmodSync(partialBin, 493);
55440
- import_node_fs36.default.renameSync(partialBin, stableBin);
56023
+ import_node_fs37.default.chmodSync(partialBin, 493);
56024
+ import_node_fs37.default.renameSync(partialBin, stableBin);
55441
56025
  } finally {
55442
56026
  safeUnlink(tarballPath);
55443
56027
  safeUnlink(partialBin);
@@ -55447,15 +56031,15 @@ async function ensureFrpcBinary(opts) {
55447
56031
  function cleanupStaleArtifacts(binDir) {
55448
56032
  let entries;
55449
56033
  try {
55450
- entries = import_node_fs36.default.readdirSync(binDir);
56034
+ entries = import_node_fs37.default.readdirSync(binDir);
55451
56035
  } catch {
55452
56036
  return;
55453
56037
  }
55454
56038
  for (const name of entries) {
55455
56039
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
55456
- const full = import_node_path40.default.join(binDir, name);
56040
+ const full = import_node_path41.default.join(binDir, name);
55457
56041
  try {
55458
- import_node_fs36.default.rmSync(full, { recursive: true, force: true });
56042
+ import_node_fs37.default.rmSync(full, { recursive: true, force: true });
55459
56043
  } catch {
55460
56044
  }
55461
56045
  }
@@ -55463,7 +56047,7 @@ function cleanupStaleArtifacts(binDir) {
55463
56047
  }
55464
56048
  function safeUnlink(p2) {
55465
56049
  try {
55466
- import_node_fs36.default.unlinkSync(p2);
56050
+ import_node_fs37.default.unlinkSync(p2);
55467
56051
  } catch {
55468
56052
  }
55469
56053
  }
@@ -55474,13 +56058,13 @@ async function downloadToFile(url, dest, fetchImpl) {
55474
56058
  if (!res.ok || !res.body) {
55475
56059
  throw new Error(`download failed: ${res.status} ${res.statusText}`);
55476
56060
  }
55477
- const out = import_node_fs36.default.createWriteStream(dest);
56061
+ const out = import_node_fs37.default.createWriteStream(dest);
55478
56062
  const nodeStream = import_node_stream3.Readable.fromWeb(res.body);
55479
56063
  await (0, import_promises5.pipeline)(nodeStream, out);
55480
56064
  }
55481
56065
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
55482
- const work = import_node_path40.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
55483
- import_node_fs36.default.mkdirSync(work, { recursive: true });
56066
+ const work = import_node_path41.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
56067
+ import_node_fs37.default.mkdirSync(work, { recursive: true });
55484
56068
  try {
55485
56069
  await new Promise((resolve6, reject) => {
55486
56070
  const proc = (0, import_node_child_process7.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
@@ -55488,32 +56072,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
55488
56072
  proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
55489
56073
  });
55490
56074
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
55491
- const src = import_node_path40.default.join(work, dirName, "frpc");
55492
- if (!import_node_fs36.default.existsSync(src)) {
56075
+ const src = import_node_path41.default.join(work, dirName, "frpc");
56076
+ if (!import_node_fs37.default.existsSync(src)) {
55493
56077
  throw new Error(`frpc not found inside tarball at ${src}`);
55494
56078
  }
55495
- import_node_fs36.default.copyFileSync(src, destBin);
56079
+ import_node_fs37.default.copyFileSync(src, destBin);
55496
56080
  } finally {
55497
- import_node_fs36.default.rmSync(work, { recursive: true, force: true });
56081
+ import_node_fs37.default.rmSync(work, { recursive: true, force: true });
55498
56082
  }
55499
56083
  }
55500
56084
 
55501
56085
  // src/tunnel/frpc-process.ts
55502
- var import_node_fs37 = __toESM(require("fs"), 1);
55503
- var import_node_path41 = __toESM(require("path"), 1);
56086
+ var import_node_fs38 = __toESM(require("fs"), 1);
56087
+ var import_node_path42 = __toESM(require("path"), 1);
55504
56088
  var import_node_child_process8 = require("child_process");
55505
56089
  function frpcPidFilePath(dataDir) {
55506
- return import_node_path41.default.join(dataDir, "frpc.pid");
56090
+ return import_node_path42.default.join(dataDir, "frpc.pid");
55507
56091
  }
55508
56092
  function writeFrpcPid(dataDir, pid) {
55509
56093
  try {
55510
- import_node_fs37.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
56094
+ import_node_fs38.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
55511
56095
  } catch {
55512
56096
  }
55513
56097
  }
55514
56098
  function clearFrpcPid(dataDir) {
55515
56099
  try {
55516
- import_node_fs37.default.unlinkSync(frpcPidFilePath(dataDir));
56100
+ import_node_fs38.default.unlinkSync(frpcPidFilePath(dataDir));
55517
56101
  } catch {
55518
56102
  }
55519
56103
  }
@@ -55529,7 +56113,7 @@ function defaultIsPidAlive(pid) {
55529
56113
  }
55530
56114
  function defaultReadPidFile(file) {
55531
56115
  try {
55532
- return import_node_fs37.default.readFileSync(file, "utf8");
56116
+ return import_node_fs38.default.readFileSync(file, "utf8");
55533
56117
  } catch {
55534
56118
  return null;
55535
56119
  }
@@ -55545,7 +56129,7 @@ function defaultSleep2(ms) {
55545
56129
  }
55546
56130
  async function killStaleFrpc(deps) {
55547
56131
  const pidFile = frpcPidFilePath(deps.dataDir);
55548
- const tomlPath = import_node_path41.default.join(deps.dataDir, "frpc.toml");
56132
+ const tomlPath = import_node_path42.default.join(deps.dataDir, "frpc.toml");
55549
56133
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
55550
56134
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
55551
56135
  const killPid = deps.killPidImpl ?? defaultKillPid;
@@ -55569,7 +56153,7 @@ async function killStaleFrpc(deps) {
55569
56153
  }
55570
56154
  if (victims.size === 0) {
55571
56155
  try {
55572
- import_node_fs37.default.unlinkSync(pidFile);
56156
+ import_node_fs38.default.unlinkSync(pidFile);
55573
56157
  } catch {
55574
56158
  }
55575
56159
  return;
@@ -55580,7 +56164,7 @@ async function killStaleFrpc(deps) {
55580
56164
  }
55581
56165
  await sleep2(deps.reapWaitMs ?? 300);
55582
56166
  try {
55583
- import_node_fs37.default.unlinkSync(pidFile);
56167
+ import_node_fs38.default.unlinkSync(pidFile);
55584
56168
  } catch {
55585
56169
  }
55586
56170
  }
@@ -55617,7 +56201,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
55617
56201
  var TunnelManager = class {
55618
56202
  constructor(deps) {
55619
56203
  this.deps = deps;
55620
- this.store = deps.store ?? new TunnelStore(import_node_path42.default.join(deps.dataDir, "tunnel.json"));
56204
+ this.store = deps.store ?? new TunnelStore(import_node_path43.default.join(deps.dataDir, "tunnel.json"));
55621
56205
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
55622
56206
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
55623
56207
  }
@@ -55744,8 +56328,8 @@ var TunnelManager = class {
55744
56328
  dataDir: this.deps.dataDir,
55745
56329
  override: this.deps.frpcBinaryOverride ?? void 0
55746
56330
  });
55747
- const tomlPath = import_node_path42.default.join(this.deps.dataDir, "frpc.toml");
55748
- const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto9.default.randomBytes(3).toString("hex")}`;
56331
+ const tomlPath = import_node_path43.default.join(this.deps.dataDir, "frpc.toml");
56332
+ const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto10.default.randomBytes(3).toString("hex")}`;
55749
56333
  const toml = buildFrpcToml({
55750
56334
  serverAddr: t.frpsHost,
55751
56335
  serverPort: t.frpsPort,
@@ -55755,14 +56339,14 @@ var TunnelManager = class {
55755
56339
  localPort,
55756
56340
  logLevel: "info"
55757
56341
  });
55758
- await import_node_fs38.default.promises.writeFile(tomlPath, toml, { mode: 384 });
56342
+ await import_node_fs39.default.promises.writeFile(tomlPath, toml, { mode: 384 });
55759
56343
  const proc = (this.deps.spawnImpl ?? import_node_child_process9.spawn)(frpcBin, ["-c", tomlPath], {
55760
56344
  stdio: ["ignore", "pipe", "pipe"]
55761
56345
  });
55762
- const logDir = import_node_path42.default.join(this.deps.dataDir, "log");
55763
- import_node_fs38.default.mkdirSync(logDir, { recursive: true });
55764
- const logFilePath = import_node_path42.default.join(logDir, "frpc.log");
55765
- const logStream = import_node_fs38.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
56346
+ const logDir = import_node_path43.default.join(this.deps.dataDir, "log");
56347
+ import_node_fs39.default.mkdirSync(logDir, { recursive: true });
56348
+ const logFilePath = import_node_path43.default.join(logDir, "frpc.log");
56349
+ const logStream = import_node_fs39.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
55766
56350
  logStream.on("error", () => {
55767
56351
  });
55768
56352
  const tee = (chunk) => {
@@ -55845,27 +56429,27 @@ async function waitForFrpcReady(proc, timeoutMs) {
55845
56429
 
55846
56430
  // src/tunnel/device-key.ts
55847
56431
  var import_node_os14 = __toESM(require("os"), 1);
55848
- var import_node_path43 = __toESM(require("path"), 1);
55849
- var import_node_crypto10 = __toESM(require("crypto"), 1);
56432
+ var import_node_path44 = __toESM(require("path"), 1);
56433
+ var import_node_crypto11 = __toESM(require("crypto"), 1);
55850
56434
  var DERIVE_SALT = "clawd-tunnel-device-v1";
55851
56435
  function deriveStableDeviceKey(opts = {}) {
55852
56436
  const hostname = opts.hostname ?? import_node_os14.default.hostname();
55853
56437
  const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
55854
56438
  const home = opts.home ?? import_node_os14.default.homedir();
55855
- const defaultDataDir = import_node_path43.default.resolve(import_node_path43.default.join(home, ".clawd"));
55856
- const normalizedDataDir = opts.dataDir ? import_node_path43.default.resolve(opts.dataDir) : null;
56439
+ const defaultDataDir = import_node_path44.default.resolve(import_node_path44.default.join(home, ".clawd"));
56440
+ const normalizedDataDir = opts.dataDir ? import_node_path44.default.resolve(opts.dataDir) : null;
55857
56441
  const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
55858
56442
  const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
55859
- return import_node_crypto10.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
56443
+ return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
55860
56444
  }
55861
56445
 
55862
56446
  // src/auth-store.ts
55863
- var import_node_fs39 = __toESM(require("fs"), 1);
55864
- var import_node_path44 = __toESM(require("path"), 1);
55865
- var import_node_crypto11 = __toESM(require("crypto"), 1);
56447
+ var import_node_fs40 = __toESM(require("fs"), 1);
56448
+ var import_node_path45 = __toESM(require("path"), 1);
56449
+ var import_node_crypto12 = __toESM(require("crypto"), 1);
55866
56450
  var AUTH_FILE_NAME = "auth.json";
55867
56451
  function authFilePath(dataDir) {
55868
- return import_node_path44.default.join(dataDir, AUTH_FILE_NAME);
56452
+ return import_node_path45.default.join(dataDir, AUTH_FILE_NAME);
55869
56453
  }
55870
56454
  function loadOrCreateAuthFile(opts) {
55871
56455
  const file = authFilePath(opts.dataDir);
@@ -55890,14 +56474,14 @@ function loadOrCreateAuthFile(opts) {
55890
56474
  return next;
55891
56475
  }
55892
56476
  function defaultGenerateToken() {
55893
- return import_node_crypto11.default.randomBytes(32).toString("base64url");
56477
+ return import_node_crypto12.default.randomBytes(32).toString("base64url");
55894
56478
  }
55895
56479
  function defaultGenerateOwnerPrincipalId() {
55896
- return `owner-${import_node_crypto11.default.randomUUID()}`;
56480
+ return `owner-${import_node_crypto12.default.randomUUID()}`;
55897
56481
  }
55898
56482
  function readAuthFile(file) {
55899
56483
  try {
55900
- const raw = import_node_fs39.default.readFileSync(file, "utf8");
56484
+ const raw = import_node_fs40.default.readFileSync(file, "utf8");
55901
56485
  const parsed = JSON.parse(raw);
55902
56486
  if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
55903
56487
  return null;
@@ -55917,25 +56501,25 @@ function readAuthFile(file) {
55917
56501
  }
55918
56502
  }
55919
56503
  function writeAuthFile(file, content) {
55920
- import_node_fs39.default.mkdirSync(import_node_path44.default.dirname(file), { recursive: true });
55921
- import_node_fs39.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
56504
+ import_node_fs40.default.mkdirSync(import_node_path45.default.dirname(file), { recursive: true });
56505
+ import_node_fs40.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
55922
56506
  try {
55923
- import_node_fs39.default.chmodSync(file, 384);
56507
+ import_node_fs40.default.chmodSync(file, 384);
55924
56508
  } catch {
55925
56509
  }
55926
56510
  }
55927
56511
 
55928
56512
  // src/owner-profile.ts
55929
- var import_node_fs40 = __toESM(require("fs"), 1);
56513
+ var import_node_fs41 = __toESM(require("fs"), 1);
55930
56514
  var import_node_os15 = __toESM(require("os"), 1);
55931
- var import_node_path45 = __toESM(require("path"), 1);
56515
+ var import_node_path46 = __toESM(require("path"), 1);
55932
56516
  var PROFILE_FILENAME = "profile.json";
55933
56517
  function loadOwnerDisplayName(dataDir) {
55934
56518
  const fallback = import_node_os15.default.userInfo().username;
55935
- const profilePath = import_node_path45.default.join(dataDir, PROFILE_FILENAME);
56519
+ const profilePath = import_node_path46.default.join(dataDir, PROFILE_FILENAME);
55936
56520
  let raw;
55937
56521
  try {
55938
- raw = import_node_fs40.default.readFileSync(profilePath, "utf8");
56522
+ raw = import_node_fs41.default.readFileSync(profilePath, "utf8");
55939
56523
  } catch {
55940
56524
  return fallback;
55941
56525
  }
@@ -55958,18 +56542,18 @@ function loadOwnerDisplayName(dataDir) {
55958
56542
  }
55959
56543
 
55960
56544
  // src/feishu-auth/owner-identity-store.ts
55961
- var import_node_fs41 = __toESM(require("fs"), 1);
55962
- var import_node_path46 = __toESM(require("path"), 1);
56545
+ var import_node_fs42 = __toESM(require("fs"), 1);
56546
+ var import_node_path47 = __toESM(require("path"), 1);
55963
56547
  var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
55964
56548
  var OwnerIdentityStore = class {
55965
56549
  file;
55966
56550
  constructor(dataDir) {
55967
- this.file = import_node_path46.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
56551
+ this.file = import_node_path47.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
55968
56552
  }
55969
56553
  read() {
55970
56554
  let raw;
55971
56555
  try {
55972
- raw = import_node_fs41.default.readFileSync(this.file, "utf8");
56556
+ raw = import_node_fs42.default.readFileSync(this.file, "utf8");
55973
56557
  } catch {
55974
56558
  return null;
55975
56559
  }
@@ -55997,16 +56581,16 @@ var OwnerIdentityStore = class {
55997
56581
  };
55998
56582
  }
55999
56583
  write(record) {
56000
- import_node_fs41.default.mkdirSync(import_node_path46.default.dirname(this.file), { recursive: true });
56001
- import_node_fs41.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
56584
+ import_node_fs42.default.mkdirSync(import_node_path47.default.dirname(this.file), { recursive: true });
56585
+ import_node_fs42.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
56002
56586
  try {
56003
- import_node_fs41.default.chmodSync(this.file, 384);
56587
+ import_node_fs42.default.chmodSync(this.file, 384);
56004
56588
  } catch {
56005
56589
  }
56006
56590
  }
56007
56591
  clear() {
56008
56592
  try {
56009
- import_node_fs41.default.unlinkSync(this.file);
56593
+ import_node_fs42.default.unlinkSync(this.file);
56010
56594
  } catch (err) {
56011
56595
  const code = err?.code;
56012
56596
  if (code !== "ENOENT") throw err;
@@ -56015,7 +56599,7 @@ var OwnerIdentityStore = class {
56015
56599
  };
56016
56600
 
56017
56601
  // src/feishu-auth/login-flow.ts
56018
- var import_node_crypto12 = __toESM(require("crypto"), 1);
56602
+ var import_node_crypto13 = __toESM(require("crypto"), 1);
56019
56603
  var STATE_TTL_MS = 5 * 60 * 1e3;
56020
56604
  var LoginFlow = class {
56021
56605
  constructor(deps) {
@@ -56024,7 +56608,7 @@ var LoginFlow = class {
56024
56608
  deps;
56025
56609
  pendingStates = /* @__PURE__ */ new Map();
56026
56610
  start() {
56027
- const state = import_node_crypto12.default.randomBytes(16).toString("base64url");
56611
+ const state = import_node_crypto13.default.randomBytes(16).toString("base64url");
56028
56612
  const now = (this.deps.now ?? Date.now)();
56029
56613
  this.pendingStates.set(state, now);
56030
56614
  this.gcExpired(now);
@@ -56143,9 +56727,9 @@ var CentralClientError = class extends Error {
56143
56727
  code;
56144
56728
  cause;
56145
56729
  };
56146
- async function centralRequest(opts, path77, init) {
56730
+ async function centralRequest(opts, path78, init) {
56147
56731
  const f = opts.fetchImpl ?? globalThis.fetch;
56148
- const url = `${opts.api.replace(/\/+$/, "")}${path77}`;
56732
+ const url = `${opts.api.replace(/\/+$/, "")}${path78}`;
56149
56733
  const ctrl = new AbortController();
56150
56734
  const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
56151
56735
  let res;
@@ -56339,8 +56923,8 @@ function verifyConnectToken(args) {
56339
56923
  }
56340
56924
 
56341
56925
  // src/feishu-auth/server-key.ts
56342
- var fs51 = __toESM(require("fs"), 1);
56343
- var path55 = __toESM(require("path"), 1);
56926
+ var fs52 = __toESM(require("fs"), 1);
56927
+ var path56 = __toESM(require("path"), 1);
56344
56928
  var FILE_NAME2 = "server-signing-key.json";
56345
56929
  var ServerKeyStore = class {
56346
56930
  constructor(dataDir) {
@@ -56348,12 +56932,12 @@ var ServerKeyStore = class {
56348
56932
  }
56349
56933
  dataDir;
56350
56934
  filePath() {
56351
- return path55.join(this.dataDir, FILE_NAME2);
56935
+ return path56.join(this.dataDir, FILE_NAME2);
56352
56936
  }
56353
56937
  /** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
56354
56938
  read() {
56355
56939
  try {
56356
- const raw = fs51.readFileSync(this.filePath(), "utf8");
56940
+ const raw = fs52.readFileSync(this.filePath(), "utf8");
56357
56941
  const parsed = JSON.parse(raw);
56358
56942
  if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
56359
56943
  return parsed.publicKeyPem;
@@ -56368,12 +56952,12 @@ var ServerKeyStore = class {
56368
56952
  publicKeyPem,
56369
56953
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
56370
56954
  };
56371
- fs51.mkdirSync(this.dataDir, { recursive: true });
56372
- fs51.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
56955
+ fs52.mkdirSync(this.dataDir, { recursive: true });
56956
+ fs52.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
56373
56957
  }
56374
56958
  clear() {
56375
56959
  try {
56376
- fs51.unlinkSync(this.filePath());
56960
+ fs52.unlinkSync(this.filePath());
56377
56961
  } catch {
56378
56962
  }
56379
56963
  }
@@ -56386,12 +56970,12 @@ init_protocol();
56386
56970
  init_protocol();
56387
56971
 
56388
56972
  // src/session/fork.ts
56389
- var import_node_fs42 = __toESM(require("fs"), 1);
56973
+ var import_node_fs43 = __toESM(require("fs"), 1);
56390
56974
  var import_node_os16 = __toESM(require("os"), 1);
56391
- var import_node_path47 = __toESM(require("path"), 1);
56975
+ var import_node_path48 = __toESM(require("path"), 1);
56392
56976
  init_claude_history();
56393
56977
  function readJsonlEntries(file) {
56394
- const raw = import_node_fs42.default.readFileSync(file, "utf8");
56978
+ const raw = import_node_fs43.default.readFileSync(file, "utf8");
56395
56979
  const out = [];
56396
56980
  for (const line of raw.split("\n")) {
56397
56981
  const t = line.trim();
@@ -56404,19 +56988,19 @@ function readJsonlEntries(file) {
56404
56988
  return out;
56405
56989
  }
56406
56990
  function forkSession(input) {
56407
- const baseDir = input.baseDir ?? import_node_path47.default.join(import_node_os16.default.homedir(), ".claude");
56408
- let projectDir = import_node_path47.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
56409
- let sourceFile = import_node_path47.default.join(projectDir, `${input.toolSessionId}.jsonl`);
56410
- if (!import_node_fs42.default.existsSync(sourceFile)) {
56991
+ const baseDir = input.baseDir ?? import_node_path48.default.join(import_node_os16.default.homedir(), ".claude");
56992
+ let projectDir = import_node_path48.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
56993
+ let sourceFile = import_node_path48.default.join(projectDir, `${input.toolSessionId}.jsonl`);
56994
+ if (!import_node_fs43.default.existsSync(sourceFile)) {
56411
56995
  const found = findTranscriptByToolSessionId(
56412
- import_node_path47.default.join(baseDir, "projects"),
56996
+ import_node_path48.default.join(baseDir, "projects"),
56413
56997
  input.toolSessionId
56414
56998
  );
56415
56999
  if (!found) {
56416
57000
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
56417
57001
  }
56418
57002
  sourceFile = found;
56419
- projectDir = import_node_path47.default.dirname(found);
57003
+ projectDir = import_node_path48.default.dirname(found);
56420
57004
  }
56421
57005
  const entries = readJsonlEntries(sourceFile);
56422
57006
  const mainEntries = entries.filter((e) => e.isSidechain !== true);
@@ -56445,9 +57029,9 @@ function forkSession(input) {
56445
57029
  }
56446
57030
  forkedLines.push(JSON.stringify(forked));
56447
57031
  }
56448
- const forkedFilePath = import_node_path47.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
56449
- import_node_fs42.default.mkdirSync(projectDir, { recursive: true });
56450
- import_node_fs42.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
57032
+ const forkedFilePath = import_node_path48.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
57033
+ import_node_fs43.default.mkdirSync(projectDir, { recursive: true });
57034
+ import_node_fs43.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
56451
57035
  return { forkedToolSessionId, forkedFilePath };
56452
57036
  }
56453
57037
 
@@ -56890,7 +57474,7 @@ function buildPermissionHandlers(deps) {
56890
57474
  }
56891
57475
 
56892
57476
  // src/handlers/history.ts
56893
- var path58 = __toESM(require("path"), 1);
57477
+ var path59 = __toESM(require("path"), 1);
56894
57478
  init_protocol();
56895
57479
 
56896
57480
  // src/session/recent-dirs.ts
@@ -56908,7 +57492,7 @@ function listRecentDirs(store, limit = 50) {
56908
57492
  }
56909
57493
 
56910
57494
  // src/permission/persona-paths.ts
56911
- var path57 = __toESM(require("path"), 1);
57495
+ var path58 = __toESM(require("path"), 1);
56912
57496
  function getAllowedPersonaIds(grants, action) {
56913
57497
  const ids = /* @__PURE__ */ new Set();
56914
57498
  for (const g2 of grants) {
@@ -56921,42 +57505,42 @@ function getAllowedPersonaIds(grants, action) {
56921
57505
  return ids;
56922
57506
  }
56923
57507
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
56924
- const target = path57.resolve(absPath);
57508
+ const target = path58.resolve(absPath);
56925
57509
  if (userWorkDir) {
56926
- const u = path57.resolve(userWorkDir);
56927
- const usep = u.endsWith(path57.sep) ? "" : path57.sep;
57510
+ const u = path58.resolve(userWorkDir);
57511
+ const usep = u.endsWith(path58.sep) ? "" : path58.sep;
56928
57512
  if (target === u || target.startsWith(u + usep)) return true;
56929
57513
  }
56930
- const root = path57.resolve(personaRoot);
56931
- const sep3 = root.endsWith(path57.sep) ? "" : path57.sep;
57514
+ const root = path58.resolve(personaRoot);
57515
+ const sep3 = root.endsWith(path58.sep) ? "" : path58.sep;
56932
57516
  if (!target.startsWith(root + sep3)) return false;
56933
- const rel = path57.relative(root, target);
57517
+ const rel = path58.relative(root, target);
56934
57518
  if (!rel || rel.startsWith("..")) return false;
56935
- const personaId2 = rel.split(path57.sep)[0];
57519
+ const personaId2 = rel.split(path58.sep)[0];
56936
57520
  if (!personaId2) return false;
56937
57521
  const allowed = getAllowedPersonaIds(grants, action);
56938
57522
  if (allowed === "*") return true;
56939
57523
  return allowed.has(personaId2);
56940
57524
  }
56941
57525
  function personaIdFromPath(absPath, personaRoot) {
56942
- const root = path57.resolve(personaRoot);
56943
- const target = path57.resolve(absPath);
56944
- const sep3 = root.endsWith(path57.sep) ? "" : path57.sep;
57526
+ const root = path58.resolve(personaRoot);
57527
+ const target = path58.resolve(absPath);
57528
+ const sep3 = root.endsWith(path58.sep) ? "" : path58.sep;
56945
57529
  if (!target.startsWith(root + sep3)) return null;
56946
- const rel = path57.relative(root, target);
57530
+ const rel = path58.relative(root, target);
56947
57531
  if (!rel || rel.startsWith("..")) return null;
56948
- const id = rel.split(path57.sep)[0];
57532
+ const id = rel.split(path58.sep)[0];
56949
57533
  return id || null;
56950
57534
  }
56951
57535
  function isPathWithin(dir, absPath) {
56952
- const d = path57.resolve(dir);
56953
- const t = path57.resolve(absPath);
56954
- const sep3 = d.endsWith(path57.sep) ? "" : path57.sep;
57536
+ const d = path58.resolve(dir);
57537
+ const t = path58.resolve(absPath);
57538
+ const sep3 = d.endsWith(path58.sep) ? "" : path58.sep;
56955
57539
  return t === d || t.startsWith(d + sep3);
56956
57540
  }
56957
57541
  function isPathInGuestBoundary(personaRoot, personaId2, userWorkDir, absPath) {
56958
57542
  if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
56959
- return personaIdFromPath(path57.resolve(absPath), personaRoot) === personaId2;
57543
+ return personaIdFromPath(path58.resolve(absPath), personaRoot) === personaId2;
56960
57544
  }
56961
57545
 
56962
57546
  // src/handlers/history.ts
@@ -57008,7 +57592,7 @@ function buildHistoryHandlers(deps) {
57008
57592
  if (!pid) return false;
57009
57593
  return isGuestPathAllowed(
57010
57594
  ctx.grants,
57011
- path58.join(personaRoot, pid),
57595
+ path59.join(personaRoot, pid),
57012
57596
  personaRoot,
57013
57597
  "read",
57014
57598
  userWorkDir
@@ -57020,7 +57604,7 @@ function buildHistoryHandlers(deps) {
57020
57604
  };
57021
57605
  const list = async (frame, _client, ctx) => {
57022
57606
  const args = HistoryListArgs.parse(frame);
57023
- assertGuestPath(ctx, path58.resolve(args.projectPath), personaRoot, "history:list", usersRoot);
57607
+ assertGuestPath(ctx, path59.resolve(args.projectPath), personaRoot, "history:list", usersRoot);
57024
57608
  const sessions = await history.listSessions(args);
57025
57609
  return { response: { type: "history:list", sessions } };
57026
57610
  };
@@ -57053,13 +57637,13 @@ function buildHistoryHandlers(deps) {
57053
57637
  };
57054
57638
  const subagents = async (frame, _client, ctx) => {
57055
57639
  const args = HistorySubagentsArgs.parse(frame);
57056
- assertGuestPath(ctx, path58.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
57640
+ assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
57057
57641
  const subs = await history.listSubagents(args);
57058
57642
  return { response: { type: "history:subagents", subagents: subs } };
57059
57643
  };
57060
57644
  const subagentRead = async (frame, _client, ctx) => {
57061
57645
  const args = HistorySubagentReadArgs.parse(frame);
57062
- assertGuestPath(ctx, path58.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
57646
+ assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
57063
57647
  const res = await history.readSubagent(args);
57064
57648
  return { response: { type: "history:subagent-read", ...res } };
57065
57649
  };
@@ -57068,7 +57652,7 @@ function buildHistoryHandlers(deps) {
57068
57652
  if (ctx?.principal.kind === "guest" && personaRoot) {
57069
57653
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
57070
57654
  const filtered = dirs.filter(
57071
- (d) => isGuestPathAllowed(ctx.grants, path58.resolve(d.cwd), personaRoot, "read", userWorkDir)
57655
+ (d) => isGuestPathAllowed(ctx.grants, path59.resolve(d.cwd), personaRoot, "read", userWorkDir)
57072
57656
  );
57073
57657
  return { response: { type: "history:recentDirs", dirs: filtered } };
57074
57658
  }
@@ -57085,7 +57669,7 @@ function buildHistoryHandlers(deps) {
57085
57669
  }
57086
57670
 
57087
57671
  // src/handlers/workspace.ts
57088
- var path59 = __toESM(require("path"), 1);
57672
+ var path60 = __toESM(require("path"), 1);
57089
57673
  var os16 = __toESM(require("os"), 1);
57090
57674
  init_protocol();
57091
57675
  init_protocol();
@@ -57127,22 +57711,22 @@ function buildWorkspaceHandlers(deps) {
57127
57711
  const args = WorkspaceListArgs.parse(frame);
57128
57712
  const isGuest = ctx?.principal.kind === "guest";
57129
57713
  const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
57130
- const resolvedCwd = path59.resolve(args.cwd ?? fallbackCwd);
57131
- const target = args.path ? path59.resolve(resolvedCwd, args.path) : resolvedCwd;
57714
+ const resolvedCwd = path60.resolve(args.cwd ?? fallbackCwd);
57715
+ const target = args.path ? path60.resolve(resolvedCwd, args.path) : resolvedCwd;
57132
57716
  assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
57133
57717
  const res = workspace.list({ ...args, cwd: resolvedCwd });
57134
57718
  return { response: { type: "workspace:list", ...res } };
57135
57719
  };
57136
57720
  const read = async (frame, _client, ctx) => {
57137
57721
  const args = WorkspaceReadArgs.parse(frame);
57138
- const target = path59.isAbsolute(args.path) ? path59.resolve(args.path) : path59.resolve(args.cwd, args.path);
57722
+ const target = path60.isAbsolute(args.path) ? path60.resolve(args.path) : path60.resolve(args.cwd, args.path);
57139
57723
  assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
57140
57724
  const res = workspace.read(args);
57141
57725
  return { response: { type: "workspace:read", ...res } };
57142
57726
  };
57143
57727
  const skillsList = async (frame, _client, ctx) => {
57144
57728
  const args = SkillsListArgs.parse(frame);
57145
- const cwdAbs = path59.resolve(args.cwd);
57729
+ const cwdAbs = path60.resolve(args.cwd);
57146
57730
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
57147
57731
  const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
57148
57732
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -57154,7 +57738,7 @@ function buildWorkspaceHandlers(deps) {
57154
57738
  };
57155
57739
  const agentsList = async (frame, _client, ctx) => {
57156
57740
  const args = AgentsListArgs.parse(frame);
57157
- const cwdAbs = path59.resolve(args.cwd);
57741
+ const cwdAbs = path60.resolve(args.cwd);
57158
57742
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
57159
57743
  if (args.tool === "codex") {
57160
57744
  return { response: { type: "agents:list", agents: [] } };
@@ -57176,20 +57760,20 @@ function buildWorkspaceHandlers(deps) {
57176
57760
  }
57177
57761
 
57178
57762
  // src/handlers/git.ts
57179
- var path61 = __toESM(require("path"), 1);
57763
+ var path62 = __toESM(require("path"), 1);
57180
57764
  init_protocol();
57181
57765
  init_protocol();
57182
57766
 
57183
57767
  // src/workspace/git.ts
57184
57768
  var import_node_child_process10 = require("child_process");
57185
- var import_node_fs43 = __toESM(require("fs"), 1);
57186
- var import_node_path48 = __toESM(require("path"), 1);
57769
+ var import_node_fs44 = __toESM(require("fs"), 1);
57770
+ var import_node_path49 = __toESM(require("path"), 1);
57187
57771
  var import_node_util = require("util");
57188
57772
  var pexec = (0, import_node_util.promisify)(import_node_child_process10.execFile);
57189
57773
  function normalizePath(p2) {
57190
- const resolved = import_node_path48.default.resolve(p2);
57774
+ const resolved = import_node_path49.default.resolve(p2);
57191
57775
  try {
57192
- return import_node_fs43.default.realpathSync(resolved);
57776
+ return import_node_fs44.default.realpathSync(resolved);
57193
57777
  } catch {
57194
57778
  return resolved;
57195
57779
  }
@@ -57263,7 +57847,7 @@ async function listGitBranches(cwd) {
57263
57847
  function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
57264
57848
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
57265
57849
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
57266
- if (!isGuestPathAllowed(ctx.grants, path61.resolve(cwd), personaRoot, "read", userWorkDir)) {
57850
+ if (!isGuestPathAllowed(ctx.grants, path62.resolve(cwd), personaRoot, "read", userWorkDir)) {
57267
57851
  throw new ClawdError(
57268
57852
  ERROR_CODES.UNAUTHORIZED,
57269
57853
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -57993,6 +58577,10 @@ var METHOD_GRANT_MAP = {
57993
58577
  "shift-internal:cancel": ADMIN_ANY,
57994
58578
  "shift-internal:update": ADMIN_ANY,
57995
58579
  "shift-internal:history": ADMIN_ANY,
58580
+ // ---- larkBot:* (LarkBotConnector 绑定/状态,owner-only) ----
58581
+ "larkBot:bindManual": ADMIN_ANY,
58582
+ "larkBot:unbind": ADMIN_ANY,
58583
+ "larkBot:status": ADMIN_ANY,
57996
58584
  "inbox:sendDm": ADMIN_ANY,
57997
58585
  "personaDispatch:complete": ADMIN_ANY,
57998
58586
  "personaDispatch:list": ADMIN_ANY,
@@ -58124,21 +58712,21 @@ function computeMethodAccess(args) {
58124
58712
  }
58125
58713
 
58126
58714
  // src/version.ts
58127
- var version = "0.2.285".length > 0 ? "0.2.285" : "dev";
58715
+ var version = "0.2.287".length > 0 ? "0.2.287" : "dev";
58128
58716
 
58129
58717
  // src/cli-probe/probe.ts
58130
- var fs54 = __toESM(require("fs"), 1);
58131
- var path62 = __toESM(require("path"), 1);
58718
+ var fs55 = __toESM(require("fs"), 1);
58719
+ var path63 = __toESM(require("path"), 1);
58132
58720
  var PKG_NAME = "@clawos-dev/clawd";
58133
58721
  var BIN_NAME = "clawd";
58134
58722
  var MAX_PARENT_HOPS = 5;
58135
58723
  function probeGlobalCli(pathEnv) {
58136
58724
  if (!pathEnv) return null;
58137
- for (const dir of pathEnv.split(path62.delimiter)) {
58725
+ for (const dir of pathEnv.split(path63.delimiter)) {
58138
58726
  if (!dir) continue;
58139
58727
  let real;
58140
58728
  try {
58141
- real = fs54.realpathSync(path62.join(dir, BIN_NAME));
58729
+ real = fs55.realpathSync(path63.join(dir, BIN_NAME));
58142
58730
  } catch {
58143
58731
  continue;
58144
58732
  }
@@ -58148,15 +58736,15 @@ function probeGlobalCli(pathEnv) {
58148
58736
  return null;
58149
58737
  }
58150
58738
  function readOwnPackageVersion(entryPath) {
58151
- let dir = path62.dirname(entryPath);
58739
+ let dir = path63.dirname(entryPath);
58152
58740
  for (let i = 0; i < MAX_PARENT_HOPS; i++) {
58153
58741
  try {
58154
- const raw = fs54.readFileSync(path62.join(dir, "package.json"), "utf8");
58742
+ const raw = fs55.readFileSync(path63.join(dir, "package.json"), "utf8");
58155
58743
  const pkg = JSON.parse(raw);
58156
58744
  if (pkg.name !== PKG_NAME) return null;
58157
58745
  return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
58158
58746
  } catch {
58159
- const parent = path62.dirname(dir);
58747
+ const parent = path63.dirname(dir);
58160
58748
  if (parent === dir) return null;
58161
58749
  dir = parent;
58162
58750
  }
@@ -58314,7 +58902,7 @@ function buildPersonaHandlers(deps) {
58314
58902
  }
58315
58903
 
58316
58904
  // src/handlers/attachment.ts
58317
- var import_node_path49 = __toESM(require("path"), 1);
58905
+ var import_node_path50 = __toESM(require("path"), 1);
58318
58906
  init_protocol();
58319
58907
  init_protocol();
58320
58908
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -58394,12 +58982,12 @@ function buildAttachmentHandlers(deps) {
58394
58982
  `session ${args.sessionId} scope unresolved`
58395
58983
  );
58396
58984
  }
58397
- const cwdAbs = import_node_path49.default.resolve(sessionFile.cwd);
58398
- const candidateAbs = import_node_path49.default.isAbsolute(args.relPath) ? import_node_path49.default.resolve(args.relPath) : import_node_path49.default.resolve(cwdAbs, args.relPath);
58985
+ const cwdAbs = import_node_path50.default.resolve(sessionFile.cwd);
58986
+ const candidateAbs = import_node_path50.default.isAbsolute(args.relPath) ? import_node_path50.default.resolve(args.relPath) : import_node_path50.default.resolve(cwdAbs, args.relPath);
58399
58987
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
58400
58988
  const entries = deps.groupFileStore.list(scope, args.sessionId);
58401
58989
  const entry = entries.find((e) => {
58402
- const storedAbs = import_node_path49.default.isAbsolute(e.relPath) ? import_node_path49.default.resolve(e.relPath) : import_node_path49.default.resolve(cwdAbs, e.relPath);
58990
+ const storedAbs = import_node_path50.default.isAbsolute(e.relPath) ? import_node_path50.default.resolve(e.relPath) : import_node_path50.default.resolve(cwdAbs, e.relPath);
58403
58991
  return storedAbs === candidateAbs && !e.stale;
58404
58992
  });
58405
58993
  if (!entry) {
@@ -58424,7 +59012,7 @@ function buildAttachmentHandlers(deps) {
58424
59012
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
58425
59013
  const f = deps.sessionStore.read(sessionId);
58426
59014
  if (!f) return;
58427
- assertGuestAttachmentPath(ctx, import_node_path49.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
59015
+ assertGuestAttachmentPath(ctx, import_node_path50.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
58428
59016
  }
58429
59017
  const groupAdd = async (frame, _client, ctx) => {
58430
59018
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -58439,8 +59027,8 @@ function buildAttachmentHandlers(deps) {
58439
59027
  if (!scope) {
58440
59028
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
58441
59029
  }
58442
- const cwdAbs = import_node_path49.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
58443
- const candidateAbs = import_node_path49.default.isAbsolute(args.relPath) ? import_node_path49.default.resolve(args.relPath) : import_node_path49.default.resolve(cwdAbs, args.relPath);
59030
+ const cwdAbs = import_node_path50.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
59031
+ const candidateAbs = import_node_path50.default.isAbsolute(args.relPath) ? import_node_path50.default.resolve(args.relPath) : import_node_path50.default.resolve(cwdAbs, args.relPath);
58444
59032
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
58445
59033
  const from = args.origin === "agent" ? "agent" : ctx?.principal.kind === "owner" ? "owner" : "agent";
58446
59034
  const size = 0;
@@ -58499,19 +59087,19 @@ function buildAttachmentHandlers(deps) {
58499
59087
 
58500
59088
  // src/handlers/extension.ts
58501
59089
  var import_promises9 = __toESM(require("fs/promises"), 1);
58502
- var import_node_path54 = __toESM(require("path"), 1);
59090
+ var import_node_path55 = __toESM(require("path"), 1);
58503
59091
  init_protocol();
58504
59092
 
58505
59093
  // src/extension/bundle-zip.ts
58506
59094
  var import_promises6 = __toESM(require("fs/promises"), 1);
58507
- var import_node_path50 = __toESM(require("path"), 1);
58508
- var import_node_crypto13 = __toESM(require("crypto"), 1);
59095
+ var import_node_path51 = __toESM(require("path"), 1);
59096
+ var import_node_crypto14 = __toESM(require("crypto"), 1);
58509
59097
  var import_jszip2 = __toESM(require_lib3(), 1);
58510
59098
  async function bundleExtensionDir(dir) {
58511
59099
  const entries = await listFilesSorted(dir);
58512
59100
  const zip = new import_jszip2.default();
58513
59101
  for (const rel of entries) {
58514
- const abs = import_node_path50.default.join(dir, rel);
59102
+ const abs = import_node_path51.default.join(dir, rel);
58515
59103
  const content = await import_promises6.default.readFile(abs);
58516
59104
  zip.file(rel, content, { date: FIXED_DATE });
58517
59105
  }
@@ -58520,7 +59108,7 @@ async function bundleExtensionDir(dir) {
58520
59108
  compression: "DEFLATE",
58521
59109
  compressionOptions: { level: 6 }
58522
59110
  });
58523
- const sha256 = import_node_crypto13.default.createHash("sha256").update(buffer).digest("hex");
59111
+ const sha256 = import_node_crypto14.default.createHash("sha256").update(buffer).digest("hex");
58524
59112
  return { buffer, sha256 };
58525
59113
  }
58526
59114
  var FIXED_DATE = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
@@ -58532,7 +59120,7 @@ async function listFilesSorted(rootDir) {
58532
59120
  return out;
58533
59121
  }
58534
59122
  async function walk(absRoot, relPrefix, out) {
58535
- const dirAbs = import_node_path50.default.join(absRoot, relPrefix);
59123
+ const dirAbs = import_node_path51.default.join(absRoot, relPrefix);
58536
59124
  const entries = await import_promises6.default.readdir(dirAbs, { withFileTypes: true });
58537
59125
  for (const e of entries) {
58538
59126
  if (IGNORE_BASENAMES.has(e.name)) continue;
@@ -58586,25 +59174,25 @@ function computePublishCheck(args) {
58586
59174
 
58587
59175
  // src/extension/install-flow.ts
58588
59176
  var import_promises7 = __toESM(require("fs/promises"), 1);
58589
- var import_node_path52 = __toESM(require("path"), 1);
59177
+ var import_node_path53 = __toESM(require("path"), 1);
58590
59178
  var import_node_os19 = __toESM(require("os"), 1);
58591
- var import_node_crypto14 = __toESM(require("crypto"), 1);
59179
+ var import_node_crypto15 = __toESM(require("crypto"), 1);
58592
59180
  var import_jszip3 = __toESM(require_lib3(), 1);
58593
59181
 
58594
59182
  // src/extension/paths.ts
58595
59183
  var import_node_os18 = __toESM(require("os"), 1);
58596
- var import_node_path51 = __toESM(require("path"), 1);
59184
+ var import_node_path52 = __toESM(require("path"), 1);
58597
59185
  function clawdHomeRoot(override) {
58598
- return override ?? process.env.CLAWD_HOME ?? import_node_path51.default.join(import_node_os18.default.homedir(), ".clawd");
59186
+ return override ?? process.env.CLAWD_HOME ?? import_node_path52.default.join(import_node_os18.default.homedir(), ".clawd");
58599
59187
  }
58600
59188
  function extensionsRoot(override) {
58601
- return import_node_path51.default.join(clawdHomeRoot(override), "extensions");
59189
+ return import_node_path52.default.join(clawdHomeRoot(override), "extensions");
58602
59190
  }
58603
59191
  function publishedChannelsFile(override) {
58604
- return import_node_path51.default.join(clawdHomeRoot(override), "extensions-published.json");
59192
+ return import_node_path52.default.join(clawdHomeRoot(override), "extensions-published.json");
58605
59193
  }
58606
59194
  function bundleCacheRoot(override) {
58607
- return import_node_path51.default.join(clawdHomeRoot(override), "extension-bundles");
59195
+ return import_node_path52.default.join(clawdHomeRoot(override), "extension-bundles");
58608
59196
  }
58609
59197
 
58610
59198
  // src/extension/install-flow.ts
@@ -58617,7 +59205,7 @@ var InstallError = class extends Error {
58617
59205
  };
58618
59206
  async function installFromChannel(args, deps) {
58619
59207
  const { channelRef, snapshotHash, bundleZip } = args;
58620
- const computed = import_node_crypto14.default.createHash("sha256").update(bundleZip).digest("hex");
59208
+ const computed = import_node_crypto15.default.createHash("sha256").update(bundleZip).digest("hex");
58621
59209
  if (computed !== snapshotHash) {
58622
59210
  throw new InstallError(
58623
59211
  "HASH_MISMATCH",
@@ -58631,7 +59219,7 @@ async function installFromChannel(args, deps) {
58631
59219
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
58632
59220
  }
58633
59221
  for (const name of Object.keys(zip.files)) {
58634
- if (name.includes("..") || name.startsWith("/") || import_node_path52.default.isAbsolute(name)) {
59222
+ if (name.includes("..") || name.startsWith("/") || import_node_path53.default.isAbsolute(name)) {
58635
59223
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
58636
59224
  }
58637
59225
  }
@@ -58663,7 +59251,7 @@ async function installFromChannel(args, deps) {
58663
59251
  );
58664
59252
  }
58665
59253
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
58666
- const destDir = import_node_path52.default.join(deps.extensionsRoot, localExtId);
59254
+ const destDir = import_node_path53.default.join(deps.extensionsRoot, localExtId);
58667
59255
  let destExists = false;
58668
59256
  try {
58669
59257
  await import_promises7.default.access(destDir);
@@ -58677,16 +59265,16 @@ async function installFromChannel(args, deps) {
58677
59265
  );
58678
59266
  }
58679
59267
  const stage = await import_promises7.default.mkdtemp(
58680
- import_node_path52.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
59268
+ import_node_path53.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
58681
59269
  );
58682
59270
  try {
58683
59271
  for (const [name, entry] of Object.entries(zip.files)) {
58684
- const dest = import_node_path52.default.join(stage, name);
59272
+ const dest = import_node_path53.default.join(stage, name);
58685
59273
  if (entry.dir) {
58686
59274
  await import_promises7.default.mkdir(dest, { recursive: true });
58687
59275
  continue;
58688
59276
  }
58689
- await import_promises7.default.mkdir(import_node_path52.default.dirname(dest), { recursive: true });
59277
+ await import_promises7.default.mkdir(import_node_path53.default.dirname(dest), { recursive: true });
58690
59278
  if (name === "manifest.json") {
58691
59279
  const rewritten = { ...parsed.data, id: localExtId };
58692
59280
  await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -58707,9 +59295,9 @@ async function installFromChannel(args, deps) {
58707
59295
 
58708
59296
  // src/extension/update-flow.ts
58709
59297
  var import_promises8 = __toESM(require("fs/promises"), 1);
58710
- var import_node_path53 = __toESM(require("path"), 1);
59298
+ var import_node_path54 = __toESM(require("path"), 1);
58711
59299
  var import_node_os20 = __toESM(require("os"), 1);
58712
- var import_node_crypto15 = __toESM(require("crypto"), 1);
59300
+ var import_node_crypto16 = __toESM(require("crypto"), 1);
58713
59301
  var import_jszip4 = __toESM(require_lib3(), 1);
58714
59302
  var UpdateError = class extends Error {
58715
59303
  constructor(code, message) {
@@ -58724,11 +59312,11 @@ async function updateFromChannel(args, deps) {
58724
59312
  channelRef.extId,
58725
59313
  channelRef.ownerPrincipalId
58726
59314
  );
58727
- const liveDir = import_node_path53.default.join(deps.extensionsRoot, localExtId);
59315
+ const liveDir = import_node_path54.default.join(deps.extensionsRoot, localExtId);
58728
59316
  const prevDir = `${liveDir}.prev`;
58729
59317
  let existingVersion;
58730
59318
  try {
58731
- const raw = await import_promises8.default.readFile(import_node_path53.default.join(liveDir, "manifest.json"), "utf8");
59319
+ const raw = await import_promises8.default.readFile(import_node_path54.default.join(liveDir, "manifest.json"), "utf8");
58732
59320
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
58733
59321
  if (!parsed2.success) {
58734
59322
  throw new UpdateError(
@@ -58747,7 +59335,7 @@ async function updateFromChannel(args, deps) {
58747
59335
  if (e instanceof UpdateError) throw e;
58748
59336
  throw e;
58749
59337
  }
58750
- const computed = import_node_crypto15.default.createHash("sha256").update(bundleZip).digest("hex");
59338
+ const computed = import_node_crypto16.default.createHash("sha256").update(bundleZip).digest("hex");
58751
59339
  if (computed !== snapshotHash) {
58752
59340
  throw new UpdateError(
58753
59341
  "HASH_MISMATCH",
@@ -58761,7 +59349,7 @@ async function updateFromChannel(args, deps) {
58761
59349
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
58762
59350
  }
58763
59351
  for (const name of Object.keys(zip.files)) {
58764
- if (name.includes("..") || name.startsWith("/") || import_node_path53.default.isAbsolute(name)) {
59352
+ if (name.includes("..") || name.startsWith("/") || import_node_path54.default.isAbsolute(name)) {
58765
59353
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
58766
59354
  }
58767
59355
  }
@@ -58796,16 +59384,16 @@ async function updateFromChannel(args, deps) {
58796
59384
  await import_promises8.default.rm(prevDir, { recursive: true, force: true });
58797
59385
  await import_promises8.default.rename(liveDir, prevDir);
58798
59386
  const stage = await import_promises8.default.mkdtemp(
58799
- import_node_path53.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
59387
+ import_node_path54.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
58800
59388
  );
58801
59389
  try {
58802
59390
  for (const [name, entry] of Object.entries(zip.files)) {
58803
- const dest = import_node_path53.default.join(stage, name);
59391
+ const dest = import_node_path54.default.join(stage, name);
58804
59392
  if (entry.dir) {
58805
59393
  await import_promises8.default.mkdir(dest, { recursive: true });
58806
59394
  continue;
58807
59395
  }
58808
- await import_promises8.default.mkdir(import_node_path53.default.dirname(dest), { recursive: true });
59396
+ await import_promises8.default.mkdir(import_node_path54.default.dirname(dest), { recursive: true });
58809
59397
  if (name === "manifest.json") {
58810
59398
  const rewritten = { ...parsed.data, id: localExtId };
58811
59399
  await import_promises8.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -58872,7 +59460,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
58872
59460
  );
58873
59461
  }
58874
59462
  }
58875
- const manifestPath = import_node_path54.default.join(root, extId, "manifest.json");
59463
+ const manifestPath = import_node_path55.default.join(root, extId, "manifest.json");
58876
59464
  const manifest = await readManifest(root, extId);
58877
59465
  const next = { ...manifest, version: newVersion };
58878
59466
  const tmp = `${manifestPath}.tmp`;
@@ -58880,7 +59468,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
58880
59468
  await import_promises9.default.rename(tmp, manifestPath);
58881
59469
  }
58882
59470
  async function readManifest(root, extId) {
58883
- const file = import_node_path54.default.join(root, extId, "manifest.json");
59471
+ const file = import_node_path55.default.join(root, extId, "manifest.json");
58884
59472
  let raw;
58885
59473
  try {
58886
59474
  raw = await import_promises9.default.readFile(file, "utf8");
@@ -58964,7 +59552,7 @@ function buildExtensionHandlers(deps) {
58964
59552
  };
58965
59553
  async function buildSnapshotMeta(extId) {
58966
59554
  const manifest = await readManifest(deps.root, extId);
58967
- const { sha256, buffer } = await bundleExtensionDir(import_node_path54.default.join(deps.root, extId));
59555
+ const { sha256, buffer } = await bundleExtensionDir(import_node_path55.default.join(deps.root, extId));
58968
59556
  return { manifest, contentHash: sha256, buffer };
58969
59557
  }
58970
59558
  const publish = async (frame, _client, ctx) => {
@@ -59139,9 +59727,9 @@ function buildExtensionHandlers(deps) {
59139
59727
  }
59140
59728
 
59141
59729
  // src/app-builder/project-store.ts
59142
- var import_node_fs44 = require("fs");
59730
+ var import_node_fs45 = require("fs");
59143
59731
  var import_node_child_process11 = require("child_process");
59144
- var import_node_path55 = require("path");
59732
+ var import_node_path56 = require("path");
59145
59733
  init_protocol();
59146
59734
  var PROJECTS_DIR = "projects";
59147
59735
  var META_FILE = ".clawd-project.json";
@@ -59155,19 +59743,19 @@ var ProjectStore = class {
59155
59743
  root;
59156
59744
  /** projects/<name>/.clawd-project.json 路径 */
59157
59745
  metaPath(name) {
59158
- return (0, import_node_path55.join)(this.projectsRoot(), name, META_FILE);
59746
+ return (0, import_node_path56.join)(this.projectsRoot(), name, META_FILE);
59159
59747
  }
59160
59748
  /** projects/<name>/ 目录路径(cwd 用) */
59161
59749
  projectDir(name) {
59162
- return (0, import_node_path55.join)(this.projectsRoot(), name);
59750
+ return (0, import_node_path56.join)(this.projectsRoot(), name);
59163
59751
  }
59164
59752
  projectsRoot() {
59165
- return (0, import_node_path55.join)(this.root, PROJECTS_DIR);
59753
+ return (0, import_node_path56.join)(this.root, PROJECTS_DIR);
59166
59754
  }
59167
59755
  async list() {
59168
59756
  let entries;
59169
59757
  try {
59170
- entries = await import_node_fs44.promises.readdir(this.projectsRoot());
59758
+ entries = await import_node_fs45.promises.readdir(this.projectsRoot());
59171
59759
  } catch (err) {
59172
59760
  if (err.code === "ENOENT") return [];
59173
59761
  throw err;
@@ -59175,7 +59763,7 @@ var ProjectStore = class {
59175
59763
  const out = [];
59176
59764
  for (const name of entries) {
59177
59765
  try {
59178
- const raw = await import_node_fs44.promises.readFile(this.metaPath(name), "utf8");
59766
+ const raw = await import_node_fs45.promises.readFile(this.metaPath(name), "utf8");
59179
59767
  const json = JSON.parse(raw);
59180
59768
  let migrated = false;
59181
59769
  if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
@@ -59186,7 +59774,7 @@ var ProjectStore = class {
59186
59774
  if (parsed.success) {
59187
59775
  out.push(parsed.data);
59188
59776
  if (migrated) {
59189
- void import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
59777
+ void import_node_fs45.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
59190
59778
  });
59191
59779
  }
59192
59780
  }
@@ -59230,8 +59818,8 @@ var ProjectStore = class {
59230
59818
  throw new Error(`invalid name "${name}": ${validated.error.message}`);
59231
59819
  }
59232
59820
  const dir = this.projectDir(name);
59233
- await import_node_fs44.promises.mkdir(dir, { recursive: true });
59234
- await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
59821
+ await import_node_fs45.promises.mkdir(dir, { recursive: true });
59822
+ await import_node_fs45.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
59235
59823
  return meta;
59236
59824
  }
59237
59825
  /**
@@ -59274,7 +59862,7 @@ var ProjectStore = class {
59274
59862
  }
59275
59863
  async delete(name) {
59276
59864
  const dir = this.projectDir(name);
59277
- await import_node_fs44.promises.rm(dir, { recursive: true, force: true });
59865
+ await import_node_fs45.promises.rm(dir, { recursive: true, force: true });
59278
59866
  }
59279
59867
  async updatePort(name, newPort) {
59280
59868
  if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
@@ -59290,7 +59878,7 @@ var ProjectStore = class {
59290
59878
  throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
59291
59879
  }
59292
59880
  const updated = { ...target, port: newPort };
59293
- await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
59881
+ await import_node_fs45.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
59294
59882
  return updated;
59295
59883
  }
59296
59884
  /**
@@ -59307,7 +59895,7 @@ var ProjectStore = class {
59307
59895
  if (!validated.success) {
59308
59896
  throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
59309
59897
  }
59310
- await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
59898
+ await import_node_fs45.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
59311
59899
  return validated.data;
59312
59900
  }
59313
59901
  /**
@@ -59328,7 +59916,7 @@ var ProjectStore = class {
59328
59916
  if (!validated.success) {
59329
59917
  throw new Error(`invalid publishJob: ${validated.error.message}`);
59330
59918
  }
59331
- await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
59919
+ await import_node_fs45.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
59332
59920
  return validated.data;
59333
59921
  }
59334
59922
  /** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
@@ -59343,7 +59931,7 @@ var ProjectStore = class {
59343
59931
  if (!validated.success) {
59344
59932
  throw new Error(`failed to clear publishJob: ${validated.error.message}`);
59345
59933
  }
59346
- await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
59934
+ await import_node_fs45.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
59347
59935
  return validated.data;
59348
59936
  }
59349
59937
  };
@@ -59413,7 +60001,7 @@ function listPidsOnPort(port) {
59413
60001
  }
59414
60002
 
59415
60003
  // src/app-builder/publish-registry.ts
59416
- var import_node_crypto16 = require("crypto");
60004
+ var import_node_crypto17 = require("crypto");
59417
60005
  var PublishJobRegistry = class {
59418
60006
  jobs = /* @__PURE__ */ new Map();
59419
60007
  has(name) {
@@ -59430,7 +60018,7 @@ var PublishJobRegistry = class {
59430
60018
  if (this.jobs.has(args.name)) {
59431
60019
  throw new Error(`already publishing: ${args.name}`);
59432
60020
  }
59433
- const jobId = args.jobId ?? `job-${(0, import_node_crypto16.randomUUID)()}`;
60021
+ const jobId = args.jobId ?? `job-${(0, import_node_crypto17.randomUUID)()}`;
59434
60022
  this.jobs.set(args.name, {
59435
60023
  jobId,
59436
60024
  name: args.name,
@@ -59464,8 +60052,8 @@ var PublishJobRegistry = class {
59464
60052
 
59465
60053
  // src/app-builder/publish-job-runner.ts
59466
60054
  var import_node_child_process13 = require("child_process");
59467
- var import_node_fs45 = require("fs");
59468
- var import_node_path56 = require("path");
60055
+ var import_node_fs46 = require("fs");
60056
+ var import_node_path57 = require("path");
59469
60057
 
59470
60058
  // src/app-builder/publish-stage-parser.ts
59471
60059
  var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
@@ -59497,10 +60085,10 @@ async function startPublishJob(deps, args) {
59497
60085
  return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
59498
60086
  }
59499
60087
  const projDir = projectDir(args.name);
59500
- const logPath = (0, import_node_path56.join)(projDir, ".publish.log");
60088
+ const logPath = (0, import_node_path57.join)(projDir, ".publish.log");
59501
60089
  let logStream = null;
59502
60090
  try {
59503
- logStream = (0, import_node_fs45.createWriteStream)(logPath, { flags: "w" });
60091
+ logStream = (0, import_node_fs46.createWriteStream)(logPath, { flags: "w" });
59504
60092
  } catch {
59505
60093
  logStream = null;
59506
60094
  }
@@ -59757,8 +60345,8 @@ async function recoverInterruptedJobs(deps) {
59757
60345
 
59758
60346
  // src/handlers/app-builder.ts
59759
60347
  init_protocol();
59760
- var import_node_path57 = require("path");
59761
- var import_node_fs46 = require("fs");
60348
+ var import_node_path58 = require("path");
60349
+ var import_node_fs47 = require("fs");
59762
60350
  var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
59763
60351
  var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
59764
60352
  async function recoverInterruptedPublishJobs(store, logger) {
@@ -59839,7 +60427,7 @@ function buildAppBuilderHandlers(deps) {
59839
60427
  async function listAllUsersProjects() {
59840
60428
  if (!deps.usersRoot || !deps.getStore) return [];
59841
60429
  const getStore = deps.getStore;
59842
- const userIds = await import_node_fs46.promises.readdir(deps.usersRoot).catch(() => []);
60430
+ const userIds = await import_node_fs47.promises.readdir(deps.usersRoot).catch(() => []);
59843
60431
  const perUser = await Promise.all(
59844
60432
  userIds.map((uid) => getStore(uid).list().catch(() => []))
59845
60433
  );
@@ -59908,8 +60496,8 @@ function buildAppBuilderHandlers(deps) {
59908
60496
  const project = await userStore.create(f.name, reservedPorts);
59909
60497
  try {
59910
60498
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
59911
- const templateSrcDir = (0, import_node_path57.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
59912
- const scaffoldScript = (0, import_node_path57.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
60499
+ const templateSrcDir = (0, import_node_path58.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
60500
+ const scaffoldScript = (0, import_node_path58.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
59913
60501
  const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
59914
60502
  deps.logger?.info("app-builder.scaffold.done", {
59915
60503
  name: project.name,
@@ -60113,7 +60701,7 @@ function buildAppBuilderHandlers(deps) {
60113
60701
  await userStore.clearPublishJob(args.name);
60114
60702
  }
60115
60703
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
60116
- const scriptPath = (0, import_node_path57.join)(deps.deployKitRoot, "scripts", "publish.sh");
60704
+ const scriptPath = (0, import_node_path58.join)(deps.deployKitRoot, "scripts", "publish.sh");
60117
60705
  deps.logger?.info("app-builder.publish.start", {
60118
60706
  name: args.name,
60119
60707
  sessionId: boundSession.sessionId,
@@ -60289,7 +60877,7 @@ function buildVisitorHandlers(deps) {
60289
60877
 
60290
60878
  // src/extension/registry.ts
60291
60879
  var import_promises10 = __toESM(require("fs/promises"), 1);
60292
- var import_node_path58 = __toESM(require("path"), 1);
60880
+ var import_node_path59 = __toESM(require("path"), 1);
60293
60881
  async function loadAll(root) {
60294
60882
  let entries;
60295
60883
  try {
@@ -60302,13 +60890,13 @@ async function loadAll(root) {
60302
60890
  for (const ent of entries) {
60303
60891
  if (!ent.isDirectory()) continue;
60304
60892
  if (ent.name.startsWith(".")) continue;
60305
- records.push(await loadOne(import_node_path58.default.join(root, ent.name), ent.name));
60893
+ records.push(await loadOne(import_node_path59.default.join(root, ent.name), ent.name));
60306
60894
  }
60307
60895
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
60308
60896
  return records;
60309
60897
  }
60310
60898
  async function loadOne(dir, dirName) {
60311
- const manifestPath = import_node_path58.default.join(dir, "manifest.json");
60899
+ const manifestPath = import_node_path59.default.join(dir, "manifest.json");
60312
60900
  let raw;
60313
60901
  try {
60314
60902
  raw = await import_promises10.default.readFile(manifestPath, "utf8");
@@ -60353,7 +60941,7 @@ async function loadOne(dir, dirName) {
60353
60941
 
60354
60942
  // src/extension/uninstall.ts
60355
60943
  var import_promises11 = __toESM(require("fs/promises"), 1);
60356
- var import_node_path59 = __toESM(require("path"), 1);
60944
+ var import_node_path60 = __toESM(require("path"), 1);
60357
60945
  var UninstallError = class extends Error {
60358
60946
  constructor(code, message) {
60359
60947
  super(message);
@@ -60362,7 +60950,7 @@ var UninstallError = class extends Error {
60362
60950
  code;
60363
60951
  };
60364
60952
  async function uninstall(deps) {
60365
- const dir = import_node_path59.default.join(deps.root, deps.extId);
60953
+ const dir = import_node_path60.default.join(deps.root, deps.extId);
60366
60954
  try {
60367
60955
  await import_promises11.default.access(dir);
60368
60956
  } catch {
@@ -60373,7 +60961,7 @@ async function uninstall(deps) {
60373
60961
  }
60374
60962
 
60375
60963
  // src/handlers/index.ts
60376
- var import_node_crypto17 = require("crypto");
60964
+ var import_node_crypto18 = require("crypto");
60377
60965
 
60378
60966
  // src/handlers/peer-exec.ts
60379
60967
  init_protocol();
@@ -60528,7 +61116,7 @@ function buildMethodHandlers(deps) {
60528
61116
  const c = deps.contactStore.get(deviceId);
60529
61117
  return c ? { deviceId: c.deviceId, remoteUrl: c.remoteUrl, connectToken: c.connectToken } : null;
60530
61118
  },
60531
- genId: () => (0, import_node_crypto17.randomUUID)(),
61119
+ genId: () => (0, import_node_crypto18.randomUUID)(),
60532
61120
  now: () => Date.now(),
60533
61121
  forwardInboxPostToPeer,
60534
61122
  logger: deps.logger
@@ -60892,7 +61480,7 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
60892
61480
 
60893
61481
  // src/extension/runtime.ts
60894
61482
  var import_node_child_process16 = require("child_process");
60895
- var import_node_path60 = __toESM(require("path"), 1);
61483
+ var import_node_path61 = __toESM(require("path"), 1);
60896
61484
  var import_promises12 = require("timers/promises");
60897
61485
 
60898
61486
  // src/extension/port-allocator.ts
@@ -60993,7 +61581,7 @@ var Runtime = class {
60993
61581
  /\$CLAWOS_EXT_PORT/g,
60994
61582
  String(port)
60995
61583
  );
60996
- const dir = import_node_path60.default.join(this.root, extId);
61584
+ const dir = import_node_path61.default.join(this.root, extId);
60997
61585
  const env = {
60998
61586
  ...process.env,
60999
61587
  CLAWOS_EXT_PORT: String(port),
@@ -61105,7 +61693,7 @@ ${handle.stderrTail}`
61105
61693
 
61106
61694
  // src/extension/published-channels.ts
61107
61695
  var import_promises13 = __toESM(require("fs/promises"), 1);
61108
- var import_node_path61 = __toESM(require("path"), 1);
61696
+ var import_node_path62 = __toESM(require("path"), 1);
61109
61697
  init_zod();
61110
61698
  var PublishedChannelsError = class extends Error {
61111
61699
  constructor(code, message) {
@@ -61204,7 +61792,7 @@ var PublishedChannelStore = class {
61204
61792
  )
61205
61793
  };
61206
61794
  const tmp = `${this.filePath}.tmp`;
61207
- await import_promises13.default.mkdir(import_node_path61.default.dirname(this.filePath), { recursive: true });
61795
+ await import_promises13.default.mkdir(import_node_path62.default.dirname(this.filePath), { recursive: true });
61208
61796
  await import_promises13.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
61209
61797
  await import_promises13.default.rename(tmp, this.filePath);
61210
61798
  }
@@ -61212,7 +61800,7 @@ var PublishedChannelStore = class {
61212
61800
 
61213
61801
  // src/extension/bundle-cache.ts
61214
61802
  var import_promises14 = __toESM(require("fs/promises"), 1);
61215
- var import_node_path62 = __toESM(require("path"), 1);
61803
+ var import_node_path63 = __toESM(require("path"), 1);
61216
61804
  var BundleCache = class {
61217
61805
  constructor(rootDir) {
61218
61806
  this.rootDir = rootDir;
@@ -61221,14 +61809,14 @@ var BundleCache = class {
61221
61809
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
61222
61810
  async write(snapshotHash, buffer) {
61223
61811
  await import_promises14.default.mkdir(this.rootDir, { recursive: true });
61224
- const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
61812
+ const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
61225
61813
  const tmp = `${file}.tmp`;
61226
61814
  await import_promises14.default.writeFile(tmp, buffer, { mode: 384 });
61227
61815
  await import_promises14.default.rename(tmp, file);
61228
61816
  }
61229
61817
  /** Returns the bundle bytes, or null when the file doesn't exist. */
61230
61818
  async read(snapshotHash) {
61231
- const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
61819
+ const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
61232
61820
  try {
61233
61821
  return await import_promises14.default.readFile(file);
61234
61822
  } catch (e) {
@@ -61238,7 +61826,7 @@ var BundleCache = class {
61238
61826
  }
61239
61827
  /** Idempotent — missing file is not an error. */
61240
61828
  async delete(snapshotHash) {
61241
- const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
61829
+ const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
61242
61830
  await import_promises14.default.rm(file, { force: true });
61243
61831
  }
61244
61832
  };
@@ -61261,24 +61849,24 @@ async function startDaemon(config) {
61261
61849
  sampling: logShippingCfg.sampling,
61262
61850
  homeDir: import_node_os22.default.homedir()
61263
61851
  });
61264
- const logDir = import_node_path63.default.join(config.dataDir, "log");
61265
- import_node_fs47.default.mkdirSync(logDir, { recursive: true });
61852
+ const logDir = import_node_path64.default.join(config.dataDir, "log");
61853
+ import_node_fs48.default.mkdirSync(logDir, { recursive: true });
61266
61854
  const logger = createLogger({
61267
61855
  level: config.logLevel,
61268
- file: import_node_path63.default.join(logDir, "clawd.log"),
61856
+ file: import_node_path64.default.join(logDir, "clawd.log"),
61269
61857
  logClient
61270
61858
  });
61271
61859
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
61272
61860
  const screenIdleProbeLogger = createFileOnlyLogger({
61273
- file: import_node_path63.default.join(logDir, "screen-idle-probe.log"),
61861
+ file: import_node_path64.default.join(logDir, "screen-idle-probe.log"),
61274
61862
  level: "debug"
61275
61863
  });
61276
61864
  logger.info("screen-idle probe logger enabled", {
61277
- file: import_node_path63.default.join(logDir, "screen-idle-probe.log")
61865
+ file: import_node_path64.default.join(logDir, "screen-idle-probe.log")
61278
61866
  });
61279
61867
  const CAP_TARGETS = [
61280
- import_node_path63.default.join(logDir, "clawd.log"),
61281
- import_node_path63.default.join(logDir, "screen-idle-probe.log")
61868
+ import_node_path64.default.join(logDir, "clawd.log"),
61869
+ import_node_path64.default.join(logDir, "screen-idle-probe.log")
61282
61870
  ];
61283
61871
  const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
61284
61872
  const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
@@ -61374,8 +61962,8 @@ async function startDaemon(config) {
61374
61962
  const agents = new AgentsScanner();
61375
61963
  const history = new ClaudeHistoryReader();
61376
61964
  let transport = null;
61377
- const personaStore = new PersonaStore(import_node_path63.default.join(config.dataDir, "personas"));
61378
- const usersRoot = import_node_path63.default.join(config.dataDir, "users");
61965
+ const personaStore = new PersonaStore(import_node_path64.default.join(config.dataDir, "personas"));
61966
+ const usersRoot = import_node_path64.default.join(config.dataDir, "users");
61379
61967
  const defaultsRoot = findDefaultsRoot(logger);
61380
61968
  if (defaultsRoot) {
61381
61969
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -61396,7 +61984,7 @@ async function startDaemon(config) {
61396
61984
  retirePersonaBuilder({ store: personaStore, logger });
61397
61985
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
61398
61986
  const dispatchStore = createDispatchStore({
61399
- filePath: import_node_path63.default.join(config.dataDir, "dispatch.json"),
61987
+ filePath: import_node_path64.default.join(config.dataDir, "dispatch.json"),
61400
61988
  now: () => Date.now(),
61401
61989
  onFlushError: (err) => logger.error("dispatch store flush failed", {
61402
61990
  reason: err instanceof Error ? err.message : String(err)
@@ -61411,18 +61999,18 @@ async function startDaemon(config) {
61411
61999
  now: () => Date.now(),
61412
62000
  onCompleted: (sid) => deliverPendingRef?.(sid)
61413
62001
  });
61414
- const here = typeof __dirname === "string" ? __dirname : import_node_path63.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
62002
+ const here = typeof __dirname === "string" ? __dirname : import_node_path64.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
61415
62003
  const mcpConfigs = [];
61416
62004
  const mcpDaemonUrl = `http://127.0.0.1:${config.port}`;
61417
62005
  const dispatchServerCandidates = [
61418
- import_node_path63.default.join(here, "dispatch", "mcp-server.cjs"),
62006
+ import_node_path64.default.join(here, "dispatch", "mcp-server.cjs"),
61419
62007
  // 生产 dist/index → dist/dispatch/mcp-server.cjs
61420
- import_node_path63.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
62008
+ import_node_path64.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
61421
62009
  // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
61422
62010
  ];
61423
- const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
62011
+ const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs48.default.existsSync(p2));
61424
62012
  if (dispatchServerScriptPath) {
61425
- const dispatchLogPath = import_node_path63.default.join(logDir, "dispatch-mcp-server.log");
62013
+ const dispatchLogPath = import_node_path64.default.join(logDir, "dispatch-mcp-server.log");
61426
62014
  const dispatchCfgPath = writeDispatchMcpConfig({
61427
62015
  dataDir: config.dataDir,
61428
62016
  serverScriptPath: dispatchServerScriptPath,
@@ -61442,12 +62030,12 @@ async function startDaemon(config) {
61442
62030
  });
61443
62031
  }
61444
62032
  const ticketServerCandidates = [
61445
- import_node_path63.default.join(here, "ticket", "mcp-server.cjs"),
61446
- import_node_path63.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
62033
+ import_node_path64.default.join(here, "ticket", "mcp-server.cjs"),
62034
+ import_node_path64.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
61447
62035
  ];
61448
- const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
62036
+ const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs48.default.existsSync(p2));
61449
62037
  if (ticketServerScriptPath) {
61450
- const ticketLogPath = import_node_path63.default.join(logDir, "ticket-mcp-server.log");
62038
+ const ticketLogPath = import_node_path64.default.join(logDir, "ticket-mcp-server.log");
61451
62039
  const ticketCfgPath = writeTicketMcpConfig({
61452
62040
  dataDir: config.dataDir,
61453
62041
  serverScriptPath: ticketServerScriptPath,
@@ -61469,12 +62057,12 @@ async function startDaemon(config) {
61469
62057
  });
61470
62058
  }
61471
62059
  const shiftServerCandidates = [
61472
- import_node_path63.default.join(here, "shift", "mcp-server.cjs"),
61473
- import_node_path63.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
62060
+ import_node_path64.default.join(here, "shift", "mcp-server.cjs"),
62061
+ import_node_path64.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
61474
62062
  ];
61475
- const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
62063
+ const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs48.default.existsSync(p2));
61476
62064
  if (shiftServerScriptPath) {
61477
- const shiftLogPath = import_node_path63.default.join(logDir, "shift-mcp-server.log");
62065
+ const shiftLogPath = import_node_path64.default.join(logDir, "shift-mcp-server.log");
61478
62066
  const shiftCfgPath = await writeShiftMcpConfig({
61479
62067
  dataDir: config.dataDir,
61480
62068
  serverScriptPath: shiftServerScriptPath,
@@ -61495,12 +62083,12 @@ async function startDaemon(config) {
61495
62083
  );
61496
62084
  }
61497
62085
  const inboxServerCandidates = [
61498
- import_node_path63.default.join(here, "inbox", "mcp-server.cjs"),
61499
- import_node_path63.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
62086
+ import_node_path64.default.join(here, "inbox", "mcp-server.cjs"),
62087
+ import_node_path64.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
61500
62088
  ];
61501
- const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
62089
+ const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs48.default.existsSync(p2));
61502
62090
  if (inboxServerScriptPath) {
61503
- const inboxLogPath = import_node_path63.default.join(logDir, "inbox-mcp-server.log");
62091
+ const inboxLogPath = import_node_path64.default.join(logDir, "inbox-mcp-server.log");
61504
62092
  const inboxCfgPath = await writeInboxMcpConfig({
61505
62093
  dataDir: config.dataDir,
61506
62094
  serverScriptPath: inboxServerScriptPath,
@@ -61521,10 +62109,10 @@ async function startDaemon(config) {
61521
62109
  );
61522
62110
  }
61523
62111
  const peerOpsServerCandidates = [
61524
- import_node_path63.default.join(here, "peer-ops", "mcp-server.cjs"),
61525
- import_node_path63.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
62112
+ import_node_path64.default.join(here, "peer-ops", "mcp-server.cjs"),
62113
+ import_node_path64.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
61526
62114
  ];
61527
- const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
62115
+ const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs48.default.existsSync(p2));
61528
62116
  if (peerOpsServerScriptPath) {
61529
62117
  const peerOpsCfgPath = writePeerOpsMcpConfig({
61530
62118
  dataDir: config.dataDir,
@@ -61544,12 +62132,12 @@ async function startDaemon(config) {
61544
62132
  );
61545
62133
  }
61546
62134
  const rpcToolServerCandidates = [
61547
- import_node_path63.default.join(here, "rpc-tool", "mcp-server.cjs"),
61548
- import_node_path63.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
62135
+ import_node_path64.default.join(here, "rpc-tool", "mcp-server.cjs"),
62136
+ import_node_path64.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
61549
62137
  ];
61550
- const rpcToolServerScriptPath = rpcToolServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
62138
+ const rpcToolServerScriptPath = rpcToolServerCandidates.find((p2) => import_node_fs48.default.existsSync(p2));
61551
62139
  if (rpcToolServerScriptPath) {
61552
- const rpcToolLogPath = import_node_path63.default.join(logDir, "rpc-tool-mcp-server.log");
62140
+ const rpcToolLogPath = import_node_path64.default.join(logDir, "rpc-tool-mcp-server.log");
61553
62141
  const rpcToolCfgPath = writeRpcToolMcpConfig({
61554
62142
  dataDir: config.dataDir,
61555
62143
  serverScriptPath: rpcToolServerScriptPath,
@@ -61570,7 +62158,7 @@ async function startDaemon(config) {
61570
62158
  );
61571
62159
  }
61572
62160
  const shiftStore = createShiftStore({
61573
- filePath: import_node_path63.default.join(config.dataDir, "shift.json"),
62161
+ filePath: import_node_path64.default.join(config.dataDir, "shift.json"),
61574
62162
  ownerIdProvider: () => ownerPrincipalId,
61575
62163
  now: () => Date.now()
61576
62164
  });
@@ -61592,7 +62180,7 @@ async function startDaemon(config) {
61592
62180
  getAdapter,
61593
62181
  historyReader: history,
61594
62182
  dataDir: config.dataDir,
61595
- personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
62183
+ personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
61596
62184
  // dispatch / shift 自动化 spawn 按 persona 绑定 tool 起 B session(codex persona 用 codex)。
61597
62185
  // personaRegistry 声明在 manager 之后(同作用域 const,调用时机在启动完成后,无 TDZ 风险)。
61598
62186
  getPersonaTool: (personaId2) => personaRegistry.get(personaId2)?.tool,
@@ -61643,10 +62231,10 @@ async function startDaemon(config) {
61643
62231
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
61644
62232
  attachmentGroup: {
61645
62233
  onFileEdit: (input) => {
61646
- const absPath = import_node_path63.default.isAbsolute(input.relPath) ? input.relPath : import_node_path63.default.join(input.cwd, input.relPath);
62234
+ const absPath = import_node_path64.default.isAbsolute(input.relPath) ? input.relPath : import_node_path64.default.join(input.cwd, input.relPath);
61647
62235
  let size = 0;
61648
62236
  try {
61649
- size = import_node_fs47.default.statSync(absPath).size;
62237
+ size = import_node_fs48.default.statSync(absPath).size;
61650
62238
  } catch (err) {
61651
62239
  logger.warn("attachment.onFileEdit stat failed", {
61652
62240
  sessionId: input.sessionId,
@@ -61887,11 +62475,11 @@ async function startDaemon(config) {
61887
62475
  // 'persona/<pid>/owner',default 走 'default'。
61888
62476
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
61889
62477
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
61890
- personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
62478
+ personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
61891
62479
  usersRoot
61892
62480
  },
61893
62481
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
61894
- personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
62482
+ personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
61895
62483
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
61896
62484
  usersRoot,
61897
62485
  // v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
@@ -62017,11 +62605,11 @@ async function startDaemon(config) {
62017
62605
  // 发布上线脚手架化 (spec 2026-06-03 §5.2):
62018
62606
  // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
62019
62607
  // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
62020
- appBuilderPersonaRoot: import_node_path63.default.join(config.dataDir, "personas", "persona-app-builder"),
62608
+ appBuilderPersonaRoot: import_node_path64.default.join(config.dataDir, "personas", "persona-app-builder"),
62021
62609
  // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
62022
- deployKitRoot: import_node_path63.default.join(config.dataDir, "deploy-kit"),
62610
+ deployKitRoot: import_node_path64.default.join(config.dataDir, "deploy-kit"),
62023
62611
  // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
62024
- resolvePersonaRoot: (personaId2) => import_node_path63.default.join(config.dataDir, "personas", personaId2),
62612
+ resolvePersonaRoot: (personaId2) => import_node_path64.default.join(config.dataDir, "personas", personaId2),
62025
62613
  // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
62026
62614
  // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
62027
62615
  // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
@@ -62124,6 +62712,41 @@ async function startDaemon(config) {
62124
62712
  });
62125
62713
  shiftScheduler.start();
62126
62714
  logger.info("shift scheduler started", { shiftCount: shiftStore.list().length });
62715
+ const larkBotStore = createLarkBotBindingStore(import_node_path64.default.join(config.dataDir, "personas"));
62716
+ const larkBotCloud = createLarkBotCloudClient({
62717
+ api: config.clawosApi ?? DEFAULT_CLAWOS_API,
62718
+ apiKey: resolveClawdTicketsApiKey(),
62719
+ deviceId: authFile.deviceId,
62720
+ displayName: ownerDisplayName
62721
+ });
62722
+ const larkChatRouter = createLarkChatRouter({
62723
+ manager,
62724
+ cloud: larkBotCloud,
62725
+ store: larkBotStore,
62726
+ logger: {
62727
+ warn: (msg) => logger.warn(msg),
62728
+ info: (msg) => logger.info(msg)
62729
+ }
62730
+ });
62731
+ const larkEventDedupe = createLarkEventDedupe();
62732
+ const larkBotEventEndpoint = createLarkBotEventEndpoint({
62733
+ getPublicKey: () => serverKeyStore.read(),
62734
+ route: larkChatRouter.handleEnvelope,
62735
+ dedupe: larkEventDedupe,
62736
+ logger: { warn: (msg) => logger.warn(msg) }
62737
+ });
62738
+ const larkBotDeps = {
62739
+ cloud: larkBotCloud,
62740
+ store: larkBotStore,
62741
+ broadcast: (frame) => wsServer?.broadcastToOwners(frame),
62742
+ getTunnelUrl: () => currentTunnelUrl,
62743
+ getOwnerId: () => ownerIdentityStore.read()?.identity.ownerId ?? null,
62744
+ getPersonaName: (personaId2) => personaRegistry.get(personaId2)?.label ?? null,
62745
+ listLarkSessions: larkChatRouter.listLarkSessions,
62746
+ listBoundPersonaIds: () => personaStore.list().filter((pid) => larkBotStore.read(pid) != null),
62747
+ logger: { warn: (msg) => logger.warn(msg) }
62748
+ };
62749
+ Object.assign(handlers, buildLarkBotHandlers(larkBotDeps));
62127
62750
  const authResolver = new AuthContextResolver({
62128
62751
  ownerToken: resolvedAuthToken
62129
62752
  });
@@ -62175,6 +62798,8 @@ async function startDaemon(config) {
62175
62798
  // persona web 分享:访客换 token(POST /share/exchange)+ guest SPA serve(GET /s)
62176
62799
  visitorExchange: exchangeVisitorToken,
62177
62800
  ...shareUiAssetLoader ? { shareUiAssetLoader } : {},
62801
+ // LarkBotConnector 事件入口(云端签名即凭证,无 Bearer)
62802
+ larkBotEvent: larkBotEventEndpoint,
62178
62803
  // /files HMAC verify 用 auth.json token(与 attachment.signUrl 同源)。
62179
62804
  getSignSecret: () => authFile.token,
62180
62805
  // chatinput 附件上传(POST /attachments/upload,spec §6.1)。upload pipeline
@@ -62359,12 +62984,27 @@ async function startDaemon(config) {
62359
62984
  stateMgr.write(stateSnapshot);
62360
62985
  currentTunnelUrl = r.url;
62361
62986
  wss.broadcastAll({ type: "tunnel:ready", url: r.url, subdomain: r.subdomain });
62987
+ void refreshTunnelUrlOnStartup(larkBotDeps);
62988
+ void (async () => {
62989
+ if (larkBotDeps.listBoundPersonaIds().length === 0) return;
62990
+ try {
62991
+ const items = await larkBotCloud.claimEvents();
62992
+ const nowSec = Math.floor(Date.now() / 1e3);
62993
+ for (const it of items) {
62994
+ larkEventDedupe.add(it.eventId);
62995
+ larkChatRouter.handleEnvelope({ ...it, iat: nowSec, exp: nowSec + 300 });
62996
+ }
62997
+ if (items.length > 0) logger.info("larkBot pending events claimed", { count: items.length });
62998
+ } catch (err) {
62999
+ logger.warn("larkBot pending events claim failed", { err: err.message });
63000
+ }
63001
+ })();
62362
63002
  const connectUrl = resolvedAuthToken ? `${r.url}#token=${resolvedAuthToken}` : r.url;
62363
63003
  const lines = [
62364
63004
  `Tunnel: ${r.url}`,
62365
63005
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
62366
- `Frpc config: ${import_node_path63.default.join(config.dataDir, "frpc.toml")}`,
62367
- `Frpc log: ${import_node_path63.default.join(logDir, "frpc.log")}`
63006
+ `Frpc config: ${import_node_path64.default.join(config.dataDir, "frpc.toml")}`,
63007
+ `Frpc log: ${import_node_path64.default.join(logDir, "frpc.log")}`
62368
63008
  ];
62369
63009
  const width = Math.max(...lines.map((l) => l.length));
62370
63010
  const bar = "\u2550".repeat(width + 4);
@@ -62377,8 +63017,8 @@ ${bar}
62377
63017
 
62378
63018
  `);
62379
63019
  try {
62380
- const connectPath = import_node_path63.default.join(config.dataDir, "connect.txt");
62381
- import_node_fs47.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
63020
+ const connectPath = import_node_path64.default.join(config.dataDir, "connect.txt");
63021
+ import_node_fs48.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
62382
63022
  } catch {
62383
63023
  }
62384
63024
  } catch (err) {
@@ -62450,9 +63090,9 @@ ${bar}
62450
63090
  };
62451
63091
  }
62452
63092
  function migrateDropPersonsDir(dataDir) {
62453
- const dir = import_node_path63.default.join(dataDir, "persons");
63093
+ const dir = import_node_path64.default.join(dataDir, "persons");
62454
63094
  try {
62455
- import_node_fs47.default.rmSync(dir, { recursive: true, force: true });
63095
+ import_node_fs48.default.rmSync(dir, { recursive: true, force: true });
62456
63096
  } catch {
62457
63097
  }
62458
63098
  }