@clawos-dev/clawd 0.2.99-beta.187.c87213f → 0.2.99-beta.191.6c0b433

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.cjs +953 -177
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -168,7 +168,19 @@ var init_methods = __esm({
168
168
  "extension.list",
169
169
  "extension.open",
170
170
  "extension.close",
171
- "extension.uninstall"
171
+ "extension.uninstall",
172
+ // ---- extension sharing v1 (spec 2026-05-28-clawd-extension-sharing-design) ----
173
+ // owner-side: 公开 / 撤回 / 查 publish 状态(owner-only ADMIN_ANY)
174
+ "extension.publish",
175
+ "extension.unpublish",
176
+ "extension.publish:status",
177
+ // cross-tunnel: guest 从 owner 拉 published list + bundle(CAPABILITY_SCOPED)
178
+ "extension.list-published",
179
+ "extension.fetchBundle",
180
+ // guest-self: guest 在自己 daemon 上做安装与更新(ADMIN_ANY,guest 是 owner of self daemon).
181
+ // 无 subscription 持久化:guest 端身份完全来自文件系统 + reconciled publishedExtensions.
182
+ "extension.install-from-channel",
183
+ "extension.update-from-channel"
172
184
  ];
173
185
  }
174
186
  });
@@ -660,8 +672,8 @@ var init_parseUtil = __esm({
660
672
  init_errors2();
661
673
  init_en();
662
674
  makeIssue = (params) => {
663
- const { data, path: path53, errorMaps, issueData } = params;
664
- const fullPath = [...path53, ...issueData.path || []];
675
+ const { data, path: path58, errorMaps, issueData } = params;
676
+ const fullPath = [...path58, ...issueData.path || []];
665
677
  const fullIssue = {
666
678
  ...issueData,
667
679
  path: fullPath
@@ -972,11 +984,11 @@ var init_types = __esm({
972
984
  init_parseUtil();
973
985
  init_util();
974
986
  ParseInputLazyPath = class {
975
- constructor(parent, value, path53, key) {
987
+ constructor(parent, value, path58, key) {
976
988
  this._cachedPath = [];
977
989
  this.parent = parent;
978
990
  this.data = value;
979
- this._path = path53;
991
+ this._path = path58;
980
992
  this._key = key;
981
993
  }
982
994
  get path() {
@@ -5355,7 +5367,18 @@ var init_received_capability = __esm({
5355
5367
  });
5356
5368
 
5357
5369
  // ../protocol/src/extension.ts
5358
- var EXT_ID_REGEX, ExtensionStateSchema, ExtensionManifestSchema, ExtensionRecordSchema;
5370
+ function ownerNamespace(ownerPrincipalId) {
5371
+ let h = 2166136261;
5372
+ for (let i = 0; i < ownerPrincipalId.length; i++) {
5373
+ h ^= ownerPrincipalId.charCodeAt(i);
5374
+ h = Math.imul(h, 16777619) >>> 0;
5375
+ }
5376
+ return (h >>> 0).toString(16).padStart(8, "0");
5377
+ }
5378
+ function namespacedExtId(slug, ownerPrincipalId) {
5379
+ return `${slug}-${ownerNamespace(ownerPrincipalId)}`;
5380
+ }
5381
+ var EXT_ID_REGEX, ExtensionStateSchema, ExtensionManifestSchema, ExtensionRecordSchema, PublishedSnapshotSchema, ChannelRefSchema, SourceRefSchema, PublishedExtensionRecordSchema, PublishCheckSchema, PublishStatusResultSchema;
5359
5382
  var init_extension = __esm({
5360
5383
  "../protocol/src/extension.ts"() {
5361
5384
  "use strict";
@@ -5409,6 +5432,72 @@ var init_extension = __esm({
5409
5432
  invalidReason: external_exports.string().min(1)
5410
5433
  })
5411
5434
  ]);
5435
+ PublishedSnapshotSchema = external_exports.object({
5436
+ snapshotHash: external_exports.string().min(1),
5437
+ version: external_exports.string().min(1),
5438
+ publishedAt: external_exports.number()
5439
+ });
5440
+ ChannelRefSchema = external_exports.discriminatedUnion("kind", [
5441
+ external_exports.object({
5442
+ kind: external_exports.literal("p2p-tunnel"),
5443
+ ownerPrincipalId: external_exports.string().min(1),
5444
+ extId: external_exports.string().min(1)
5445
+ })
5446
+ ]);
5447
+ SourceRefSchema = external_exports.discriminatedUnion("kind", [
5448
+ external_exports.object({
5449
+ kind: external_exports.literal("p2p-tunnel"),
5450
+ ownerPrincipalId: external_exports.string().min(1),
5451
+ extId: external_exports.string().min(1),
5452
+ snapshotHash: external_exports.string().min(1)
5453
+ })
5454
+ ]);
5455
+ PublishedExtensionRecordSchema = external_exports.object({
5456
+ extId: external_exports.string().min(1),
5457
+ name: external_exports.string().min(1),
5458
+ version: external_exports.string().min(1),
5459
+ contentHash: external_exports.string().min(1),
5460
+ publishedAt: external_exports.number()
5461
+ });
5462
+ PublishCheckSchema = external_exports.discriminatedUnion("kind", [
5463
+ external_exports.object({ kind: external_exports.literal("clean") }),
5464
+ external_exports.object({
5465
+ kind: external_exports.literal("ready-new-version"),
5466
+ fromVersion: external_exports.string().min(1),
5467
+ toVersion: external_exports.string().min(1)
5468
+ }),
5469
+ external_exports.object({
5470
+ kind: external_exports.literal("error-same-hash"),
5471
+ version: external_exports.string().min(1)
5472
+ }),
5473
+ external_exports.object({
5474
+ kind: external_exports.literal("error-version-not-bumped"),
5475
+ localVersion: external_exports.string().min(1),
5476
+ publishedVersion: external_exports.string().min(1)
5477
+ }),
5478
+ external_exports.object({
5479
+ kind: external_exports.literal("error-version-regression"),
5480
+ localVersion: external_exports.string().min(1),
5481
+ publishedVersion: external_exports.string().min(1)
5482
+ })
5483
+ ]);
5484
+ PublishStatusResultSchema = external_exports.object({
5485
+ localManifest: external_exports.object({
5486
+ name: external_exports.string().min(1),
5487
+ version: external_exports.string().min(1),
5488
+ contentHash: external_exports.string().min(1)
5489
+ }),
5490
+ publishState: external_exports.discriminatedUnion("kind", [
5491
+ external_exports.object({ kind: external_exports.literal("unpublished") }),
5492
+ external_exports.object({
5493
+ kind: external_exports.literal("published"),
5494
+ version: external_exports.string().min(1),
5495
+ contentHash: external_exports.string().min(1),
5496
+ publishedAt: external_exports.number()
5497
+ })
5498
+ ]),
5499
+ check: PublishCheckSchema
5500
+ });
5412
5501
  }
5413
5502
  });
5414
5503
 
@@ -5703,8 +5792,8 @@ var require_req = __commonJS({
5703
5792
  if (req.originalUrl) {
5704
5793
  _req.url = req.originalUrl;
5705
5794
  } else {
5706
- const path53 = req.path;
5707
- _req.url = typeof path53 === "string" ? path53 : req.url ? req.url.path || req.url : void 0;
5795
+ const path58 = req.path;
5796
+ _req.url = typeof path58 === "string" ? path58 : req.url ? req.url.path || req.url : void 0;
5708
5797
  }
5709
5798
  if (req.query) {
5710
5799
  _req.query = req.query;
@@ -5869,14 +5958,14 @@ var require_redact = __commonJS({
5869
5958
  }
5870
5959
  return obj;
5871
5960
  }
5872
- function parsePath(path53) {
5961
+ function parsePath(path58) {
5873
5962
  const parts = [];
5874
5963
  let current = "";
5875
5964
  let inBrackets = false;
5876
5965
  let inQuotes = false;
5877
5966
  let quoteChar = "";
5878
- for (let i = 0; i < path53.length; i++) {
5879
- const char = path53[i];
5967
+ for (let i = 0; i < path58.length; i++) {
5968
+ const char = path58[i];
5880
5969
  if (!inBrackets && char === ".") {
5881
5970
  if (current) {
5882
5971
  parts.push(current);
@@ -6007,10 +6096,10 @@ var require_redact = __commonJS({
6007
6096
  return current;
6008
6097
  }
6009
6098
  function redactPaths(obj, paths, censor, remove = false) {
6010
- for (const path53 of paths) {
6011
- const parts = parsePath(path53);
6099
+ for (const path58 of paths) {
6100
+ const parts = parsePath(path58);
6012
6101
  if (parts.includes("*")) {
6013
- redactWildcardPath(obj, parts, censor, path53, remove);
6102
+ redactWildcardPath(obj, parts, censor, path58, remove);
6014
6103
  } else {
6015
6104
  if (remove) {
6016
6105
  removeKey(obj, parts);
@@ -6095,8 +6184,8 @@ var require_redact = __commonJS({
6095
6184
  }
6096
6185
  } else {
6097
6186
  if (afterWildcard.includes("*")) {
6098
- const wrappedCensor = typeof censor === "function" ? (value, path53) => {
6099
- const fullPath = [...pathArray.slice(0, pathLength), ...path53];
6187
+ const wrappedCensor = typeof censor === "function" ? (value, path58) => {
6188
+ const fullPath = [...pathArray.slice(0, pathLength), ...path58];
6100
6189
  return censor(value, fullPath);
6101
6190
  } : censor;
6102
6191
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -6131,8 +6220,8 @@ var require_redact = __commonJS({
6131
6220
  return null;
6132
6221
  }
6133
6222
  const pathStructure = /* @__PURE__ */ new Map();
6134
- for (const path53 of pathsToClone) {
6135
- const parts = parsePath(path53);
6223
+ for (const path58 of pathsToClone) {
6224
+ const parts = parsePath(path58);
6136
6225
  let current = pathStructure;
6137
6226
  for (let i = 0; i < parts.length; i++) {
6138
6227
  const part = parts[i];
@@ -6184,24 +6273,24 @@ var require_redact = __commonJS({
6184
6273
  }
6185
6274
  return cloneSelectively(obj, pathStructure);
6186
6275
  }
6187
- function validatePath(path53) {
6188
- if (typeof path53 !== "string") {
6276
+ function validatePath(path58) {
6277
+ if (typeof path58 !== "string") {
6189
6278
  throw new Error("Paths must be (non-empty) strings");
6190
6279
  }
6191
- if (path53 === "") {
6280
+ if (path58 === "") {
6192
6281
  throw new Error("Invalid redaction path ()");
6193
6282
  }
6194
- if (path53.includes("..")) {
6195
- throw new Error(`Invalid redaction path (${path53})`);
6283
+ if (path58.includes("..")) {
6284
+ throw new Error(`Invalid redaction path (${path58})`);
6196
6285
  }
6197
- if (path53.includes(",")) {
6198
- throw new Error(`Invalid redaction path (${path53})`);
6286
+ if (path58.includes(",")) {
6287
+ throw new Error(`Invalid redaction path (${path58})`);
6199
6288
  }
6200
6289
  let bracketCount = 0;
6201
6290
  let inQuotes = false;
6202
6291
  let quoteChar = "";
6203
- for (let i = 0; i < path53.length; i++) {
6204
- const char = path53[i];
6292
+ for (let i = 0; i < path58.length; i++) {
6293
+ const char = path58[i];
6205
6294
  if ((char === '"' || char === "'") && bracketCount > 0) {
6206
6295
  if (!inQuotes) {
6207
6296
  inQuotes = true;
@@ -6215,20 +6304,20 @@ var require_redact = __commonJS({
6215
6304
  } else if (char === "]" && !inQuotes) {
6216
6305
  bracketCount--;
6217
6306
  if (bracketCount < 0) {
6218
- throw new Error(`Invalid redaction path (${path53})`);
6307
+ throw new Error(`Invalid redaction path (${path58})`);
6219
6308
  }
6220
6309
  }
6221
6310
  }
6222
6311
  if (bracketCount !== 0) {
6223
- throw new Error(`Invalid redaction path (${path53})`);
6312
+ throw new Error(`Invalid redaction path (${path58})`);
6224
6313
  }
6225
6314
  }
6226
6315
  function validatePaths(paths) {
6227
6316
  if (!Array.isArray(paths)) {
6228
6317
  throw new TypeError("paths must be an array");
6229
6318
  }
6230
- for (const path53 of paths) {
6231
- validatePath(path53);
6319
+ for (const path58 of paths) {
6320
+ validatePath(path58);
6232
6321
  }
6233
6322
  }
6234
6323
  function slowRedact(options = {}) {
@@ -6396,8 +6485,8 @@ var require_redaction = __commonJS({
6396
6485
  if (shape[k2] === null) {
6397
6486
  o[k2] = (value) => topCensor(value, [k2]);
6398
6487
  } else {
6399
- const wrappedCensor = typeof censor === "function" ? (value, path53) => {
6400
- return censor(value, [k2, ...path53]);
6488
+ const wrappedCensor = typeof censor === "function" ? (value, path58) => {
6489
+ return censor(value, [k2, ...path58]);
6401
6490
  } : censor;
6402
6491
  o[k2] = Redact({
6403
6492
  paths: shape[k2],
@@ -6615,10 +6704,10 @@ var require_atomic_sleep = __commonJS({
6615
6704
  var require_sonic_boom = __commonJS({
6616
6705
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
6617
6706
  "use strict";
6618
- var fs41 = require("fs");
6707
+ var fs46 = require("fs");
6619
6708
  var EventEmitter2 = require("events");
6620
6709
  var inherits = require("util").inherits;
6621
- var path53 = require("path");
6710
+ var path58 = require("path");
6622
6711
  var sleep = require_atomic_sleep();
6623
6712
  var assert = require("assert");
6624
6713
  var BUSY_WRITE_TIMEOUT = 100;
@@ -6672,20 +6761,20 @@ var require_sonic_boom = __commonJS({
6672
6761
  const mode = sonic.mode;
6673
6762
  if (sonic.sync) {
6674
6763
  try {
6675
- if (sonic.mkdir) fs41.mkdirSync(path53.dirname(file), { recursive: true });
6676
- const fd = fs41.openSync(file, flags, mode);
6764
+ if (sonic.mkdir) fs46.mkdirSync(path58.dirname(file), { recursive: true });
6765
+ const fd = fs46.openSync(file, flags, mode);
6677
6766
  fileOpened(null, fd);
6678
6767
  } catch (err) {
6679
6768
  fileOpened(err);
6680
6769
  throw err;
6681
6770
  }
6682
6771
  } else if (sonic.mkdir) {
6683
- fs41.mkdir(path53.dirname(file), { recursive: true }, (err) => {
6772
+ fs46.mkdir(path58.dirname(file), { recursive: true }, (err) => {
6684
6773
  if (err) return fileOpened(err);
6685
- fs41.open(file, flags, mode, fileOpened);
6774
+ fs46.open(file, flags, mode, fileOpened);
6686
6775
  });
6687
6776
  } else {
6688
- fs41.open(file, flags, mode, fileOpened);
6777
+ fs46.open(file, flags, mode, fileOpened);
6689
6778
  }
6690
6779
  }
6691
6780
  function SonicBoom(opts) {
@@ -6726,8 +6815,8 @@ var require_sonic_boom = __commonJS({
6726
6815
  this.flush = flushBuffer;
6727
6816
  this.flushSync = flushBufferSync;
6728
6817
  this._actualWrite = actualWriteBuffer;
6729
- fsWriteSync = () => fs41.writeSync(this.fd, this._writingBuf);
6730
- fsWrite = () => fs41.write(this.fd, this._writingBuf, this.release);
6818
+ fsWriteSync = () => fs46.writeSync(this.fd, this._writingBuf);
6819
+ fsWrite = () => fs46.write(this.fd, this._writingBuf, this.release);
6731
6820
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
6732
6821
  this._writingBuf = "";
6733
6822
  this.write = write;
@@ -6736,15 +6825,15 @@ var require_sonic_boom = __commonJS({
6736
6825
  this._actualWrite = actualWrite;
6737
6826
  fsWriteSync = () => {
6738
6827
  if (Buffer.isBuffer(this._writingBuf)) {
6739
- return fs41.writeSync(this.fd, this._writingBuf);
6828
+ return fs46.writeSync(this.fd, this._writingBuf);
6740
6829
  }
6741
- return fs41.writeSync(this.fd, this._writingBuf, "utf8");
6830
+ return fs46.writeSync(this.fd, this._writingBuf, "utf8");
6742
6831
  };
6743
6832
  fsWrite = () => {
6744
6833
  if (Buffer.isBuffer(this._writingBuf)) {
6745
- return fs41.write(this.fd, this._writingBuf, this.release);
6834
+ return fs46.write(this.fd, this._writingBuf, this.release);
6746
6835
  }
6747
- return fs41.write(this.fd, this._writingBuf, "utf8", this.release);
6836
+ return fs46.write(this.fd, this._writingBuf, "utf8", this.release);
6748
6837
  };
6749
6838
  } else {
6750
6839
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -6801,7 +6890,7 @@ var require_sonic_boom = __commonJS({
6801
6890
  }
6802
6891
  }
6803
6892
  if (this._fsync) {
6804
- fs41.fsyncSync(this.fd);
6893
+ fs46.fsyncSync(this.fd);
6805
6894
  }
6806
6895
  const len = this._len;
6807
6896
  if (this._reopening) {
@@ -6915,7 +7004,7 @@ var require_sonic_boom = __commonJS({
6915
7004
  const onDrain = () => {
6916
7005
  if (!this._fsync) {
6917
7006
  try {
6918
- fs41.fsync(this.fd, (err) => {
7007
+ fs46.fsync(this.fd, (err) => {
6919
7008
  this._flushPending = false;
6920
7009
  cb(err);
6921
7010
  });
@@ -7017,7 +7106,7 @@ var require_sonic_boom = __commonJS({
7017
7106
  const fd = this.fd;
7018
7107
  this.once("ready", () => {
7019
7108
  if (fd !== this.fd) {
7020
- fs41.close(fd, (err) => {
7109
+ fs46.close(fd, (err) => {
7021
7110
  if (err) {
7022
7111
  return this.emit("error", err);
7023
7112
  }
@@ -7066,7 +7155,7 @@ var require_sonic_boom = __commonJS({
7066
7155
  buf = this._bufs[0];
7067
7156
  }
7068
7157
  try {
7069
- const n = Buffer.isBuffer(buf) ? fs41.writeSync(this.fd, buf) : fs41.writeSync(this.fd, buf, "utf8");
7158
+ const n = Buffer.isBuffer(buf) ? fs46.writeSync(this.fd, buf) : fs46.writeSync(this.fd, buf, "utf8");
7070
7159
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
7071
7160
  buf = releasedBufObj.writingBuf;
7072
7161
  this._len = releasedBufObj.len;
@@ -7082,7 +7171,7 @@ var require_sonic_boom = __commonJS({
7082
7171
  }
7083
7172
  }
7084
7173
  try {
7085
- fs41.fsyncSync(this.fd);
7174
+ fs46.fsyncSync(this.fd);
7086
7175
  } catch {
7087
7176
  }
7088
7177
  }
@@ -7103,7 +7192,7 @@ var require_sonic_boom = __commonJS({
7103
7192
  buf = mergeBuf(this._bufs[0], this._lens[0]);
7104
7193
  }
7105
7194
  try {
7106
- const n = fs41.writeSync(this.fd, buf);
7195
+ const n = fs46.writeSync(this.fd, buf);
7107
7196
  buf = buf.subarray(n);
7108
7197
  this._len = Math.max(this._len - n, 0);
7109
7198
  if (buf.length <= 0) {
@@ -7131,13 +7220,13 @@ var require_sonic_boom = __commonJS({
7131
7220
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
7132
7221
  if (this.sync) {
7133
7222
  try {
7134
- const written = Buffer.isBuffer(this._writingBuf) ? fs41.writeSync(this.fd, this._writingBuf) : fs41.writeSync(this.fd, this._writingBuf, "utf8");
7223
+ const written = Buffer.isBuffer(this._writingBuf) ? fs46.writeSync(this.fd, this._writingBuf) : fs46.writeSync(this.fd, this._writingBuf, "utf8");
7135
7224
  release(null, written);
7136
7225
  } catch (err) {
7137
7226
  release(err);
7138
7227
  }
7139
7228
  } else {
7140
- fs41.write(this.fd, this._writingBuf, release);
7229
+ fs46.write(this.fd, this._writingBuf, release);
7141
7230
  }
7142
7231
  }
7143
7232
  function actualWriteBuffer() {
@@ -7146,7 +7235,7 @@ var require_sonic_boom = __commonJS({
7146
7235
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
7147
7236
  if (this.sync) {
7148
7237
  try {
7149
- const written = fs41.writeSync(this.fd, this._writingBuf);
7238
+ const written = fs46.writeSync(this.fd, this._writingBuf);
7150
7239
  release(null, written);
7151
7240
  } catch (err) {
7152
7241
  release(err);
@@ -7155,7 +7244,7 @@ var require_sonic_boom = __commonJS({
7155
7244
  if (kCopyBuffer) {
7156
7245
  this._writingBuf = Buffer.from(this._writingBuf);
7157
7246
  }
7158
- fs41.write(this.fd, this._writingBuf, release);
7247
+ fs46.write(this.fd, this._writingBuf, release);
7159
7248
  }
7160
7249
  }
7161
7250
  function actualClose(sonic) {
@@ -7171,12 +7260,12 @@ var require_sonic_boom = __commonJS({
7171
7260
  sonic._lens = [];
7172
7261
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
7173
7262
  try {
7174
- fs41.fsync(sonic.fd, closeWrapped);
7263
+ fs46.fsync(sonic.fd, closeWrapped);
7175
7264
  } catch {
7176
7265
  }
7177
7266
  function closeWrapped() {
7178
7267
  if (sonic.fd !== 1 && sonic.fd !== 2) {
7179
- fs41.close(sonic.fd, done);
7268
+ fs46.close(sonic.fd, done);
7180
7269
  } else {
7181
7270
  done();
7182
7271
  }
@@ -9540,7 +9629,7 @@ var require_multistream = __commonJS({
9540
9629
  var require_pino = __commonJS({
9541
9630
  "../node_modules/.pnpm/pino@9.14.0/node_modules/pino/pino.js"(exports2, module2) {
9542
9631
  "use strict";
9543
- var os19 = require("os");
9632
+ var os21 = require("os");
9544
9633
  var stdSerializers = require_pino_std_serializers();
9545
9634
  var caller = require_caller();
9546
9635
  var redaction = require_redaction();
@@ -9587,7 +9676,7 @@ var require_pino = __commonJS({
9587
9676
  } = symbols;
9588
9677
  var { epochTime, nullTime } = time;
9589
9678
  var { pid } = process;
9590
- var hostname = os19.hostname();
9679
+ var hostname = os21.hostname();
9591
9680
  var defaultErrorSerializer = stdSerializers.err;
9592
9681
  var defaultOptions = {
9593
9682
  level: "info",
@@ -10311,11 +10400,11 @@ var init_lib = __esm({
10311
10400
  }
10312
10401
  }
10313
10402
  },
10314
- addToPath: function addToPath(path53, added, removed, oldPosInc, options) {
10315
- var last = path53.lastComponent;
10403
+ addToPath: function addToPath(path58, added, removed, oldPosInc, options) {
10404
+ var last = path58.lastComponent;
10316
10405
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
10317
10406
  return {
10318
- oldPos: path53.oldPos + oldPosInc,
10407
+ oldPos: path58.oldPos + oldPosInc,
10319
10408
  lastComponent: {
10320
10409
  count: last.count + 1,
10321
10410
  added,
@@ -10325,7 +10414,7 @@ var init_lib = __esm({
10325
10414
  };
10326
10415
  } else {
10327
10416
  return {
10328
- oldPos: path53.oldPos + oldPosInc,
10417
+ oldPos: path58.oldPos + oldPosInc,
10329
10418
  lastComponent: {
10330
10419
  count: 1,
10331
10420
  added,
@@ -10771,10 +10860,10 @@ function attachmentToHistoryMessage(o, ts) {
10771
10860
  const memories = raw.map((m2) => {
10772
10861
  if (!m2 || typeof m2 !== "object") return null;
10773
10862
  const rec = m2;
10774
- const path53 = typeof rec.path === "string" ? rec.path : null;
10863
+ const path58 = typeof rec.path === "string" ? rec.path : null;
10775
10864
  const content = typeof rec.content === "string" ? rec.content : null;
10776
- if (!path53 || content == null) return null;
10777
- const entry = { path: path53, content };
10865
+ if (!path58 || content == null) return null;
10866
+ const entry = { path: path58, content };
10778
10867
  if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
10779
10868
  return entry;
10780
10869
  }).filter((m2) => m2 !== null);
@@ -11605,10 +11694,10 @@ function parseAttachment(obj) {
11605
11694
  const memories = raw.map((m2) => {
11606
11695
  if (!m2 || typeof m2 !== "object") return null;
11607
11696
  const rec = m2;
11608
- const path53 = typeof rec.path === "string" ? rec.path : null;
11697
+ const path58 = typeof rec.path === "string" ? rec.path : null;
11609
11698
  const content = typeof rec.content === "string" ? rec.content : null;
11610
- if (!path53 || content == null) return null;
11611
- const out = { path: path53, content };
11699
+ if (!path58 || content == null) return null;
11700
+ const out = { path: path58, content };
11612
11701
  if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
11613
11702
  return out;
11614
11703
  }).filter((m2) => m2 !== null);
@@ -23397,8 +23486,8 @@ var require_utils = __commonJS({
23397
23486
  var result = transform[inputType][outputType](input);
23398
23487
  return result;
23399
23488
  };
23400
- exports2.resolve = function(path53) {
23401
- var parts = path53.split("/");
23489
+ exports2.resolve = function(path58) {
23490
+ var parts = path58.split("/");
23402
23491
  var result = [];
23403
23492
  for (var index = 0; index < parts.length; index++) {
23404
23493
  var part = parts[index];
@@ -29251,18 +29340,18 @@ var require_object = __commonJS({
29251
29340
  var object = new ZipObject(name, zipObjectContent, o);
29252
29341
  this.files[name] = object;
29253
29342
  };
29254
- var parentFolder = function(path53) {
29255
- if (path53.slice(-1) === "/") {
29256
- path53 = path53.substring(0, path53.length - 1);
29343
+ var parentFolder = function(path58) {
29344
+ if (path58.slice(-1) === "/") {
29345
+ path58 = path58.substring(0, path58.length - 1);
29257
29346
  }
29258
- var lastSlash = path53.lastIndexOf("/");
29259
- return lastSlash > 0 ? path53.substring(0, lastSlash) : "";
29347
+ var lastSlash = path58.lastIndexOf("/");
29348
+ return lastSlash > 0 ? path58.substring(0, lastSlash) : "";
29260
29349
  };
29261
- var forceTrailingSlash = function(path53) {
29262
- if (path53.slice(-1) !== "/") {
29263
- path53 += "/";
29350
+ var forceTrailingSlash = function(path58) {
29351
+ if (path58.slice(-1) !== "/") {
29352
+ path58 += "/";
29264
29353
  }
29265
- return path53;
29354
+ return path58;
29266
29355
  };
29267
29356
  var folderAdd = function(name, createFolders) {
29268
29357
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -30228,9 +30317,9 @@ var require_load = __commonJS({
30228
30317
  var require_lib3 = __commonJS({
30229
30318
  "../node_modules/.pnpm/jszip@3.10.1/node_modules/jszip/lib/index.js"(exports2, module2) {
30230
30319
  "use strict";
30231
- function JSZip2() {
30232
- if (!(this instanceof JSZip2)) {
30233
- return new JSZip2();
30320
+ function JSZip5() {
30321
+ if (!(this instanceof JSZip5)) {
30322
+ return new JSZip5();
30234
30323
  }
30235
30324
  if (arguments.length) {
30236
30325
  throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide.");
@@ -30239,7 +30328,7 @@ var require_lib3 = __commonJS({
30239
30328
  this.comment = null;
30240
30329
  this.root = "";
30241
30330
  this.clone = function() {
30242
- var newObj = new JSZip2();
30331
+ var newObj = new JSZip5();
30243
30332
  for (var i in this) {
30244
30333
  if (typeof this[i] !== "function") {
30245
30334
  newObj[i] = this[i];
@@ -30248,23 +30337,23 @@ var require_lib3 = __commonJS({
30248
30337
  return newObj;
30249
30338
  };
30250
30339
  }
30251
- JSZip2.prototype = require_object();
30252
- JSZip2.prototype.loadAsync = require_load();
30253
- JSZip2.support = require_support();
30254
- JSZip2.defaults = require_defaults();
30255
- JSZip2.version = "3.10.1";
30256
- JSZip2.loadAsync = function(content, options) {
30257
- return new JSZip2().loadAsync(content, options);
30340
+ JSZip5.prototype = require_object();
30341
+ JSZip5.prototype.loadAsync = require_load();
30342
+ JSZip5.support = require_support();
30343
+ JSZip5.defaults = require_defaults();
30344
+ JSZip5.version = "3.10.1";
30345
+ JSZip5.loadAsync = function(content, options) {
30346
+ return new JSZip5().loadAsync(content, options);
30258
30347
  };
30259
- JSZip2.external = require_external();
30260
- module2.exports = JSZip2;
30348
+ JSZip5.external = require_external();
30349
+ module2.exports = JSZip5;
30261
30350
  }
30262
30351
  });
30263
30352
 
30264
30353
  // src/run-case/recorder.ts
30265
30354
  function startRunCaseRecorder(opts) {
30266
30355
  const now = opts.now ?? Date.now;
30267
- const dir = import_node_path39.default.dirname(opts.recordPath);
30356
+ const dir = import_node_path44.default.dirname(opts.recordPath);
30268
30357
  let stream = null;
30269
30358
  let closing = false;
30270
30359
  let closedSettled = false;
@@ -30304,12 +30393,12 @@ function startRunCaseRecorder(opts) {
30304
30393
  };
30305
30394
  return { tap, close, closed };
30306
30395
  }
30307
- var import_node_fs30, import_node_path39;
30396
+ var import_node_fs30, import_node_path44;
30308
30397
  var init_recorder = __esm({
30309
30398
  "src/run-case/recorder.ts"() {
30310
30399
  "use strict";
30311
30400
  import_node_fs30 = __toESM(require("fs"), 1);
30312
- import_node_path39 = __toESM(require("path"), 1);
30401
+ import_node_path44 = __toESM(require("path"), 1);
30313
30402
  }
30314
30403
  });
30315
30404
 
@@ -30352,7 +30441,7 @@ var init_wire = __esm({
30352
30441
  // src/run-case/controller.ts
30353
30442
  async function runController(opts) {
30354
30443
  const now = opts.now ?? Date.now;
30355
- const cwd = opts.cwd ?? (0, import_node_fs31.mkdtempSync)(import_node_path40.default.join(import_node_os17.default.tmpdir(), "clawd-runcase-"));
30444
+ const cwd = opts.cwd ?? (0, import_node_fs31.mkdtempSync)(import_node_path45.default.join(import_node_os19.default.tmpdir(), "clawd-runcase-"));
30356
30445
  const ownsCwd = opts.cwd === void 0;
30357
30446
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
30358
30447
  const spawnCtx = { cwd };
@@ -30519,13 +30608,13 @@ async function runController(opts) {
30519
30608
  }
30520
30609
  return exitCode ?? 0;
30521
30610
  }
30522
- var import_node_fs31, import_node_os17, import_node_path40;
30611
+ var import_node_fs31, import_node_os19, import_node_path45;
30523
30612
  var init_controller = __esm({
30524
30613
  "src/run-case/controller.ts"() {
30525
30614
  "use strict";
30526
30615
  import_node_fs31 = require("fs");
30527
- import_node_os17 = __toESM(require("os"), 1);
30528
- import_node_path40 = __toESM(require("path"), 1);
30616
+ import_node_os19 = __toESM(require("os"), 1);
30617
+ import_node_path45 = __toESM(require("path"), 1);
30529
30618
  init_claude();
30530
30619
  init_stdout_splitter();
30531
30620
  init_permission_stdio();
@@ -30757,7 +30846,7 @@ Env (advanced):
30757
30846
  `;
30758
30847
 
30759
30848
  // src/index.ts
30760
- var import_node_path38 = __toESM(require("path"), 1);
30849
+ var import_node_path43 = __toESM(require("path"), 1);
30761
30850
  var import_node_fs29 = __toESM(require("fs"), 1);
30762
30851
 
30763
30852
  // src/logger.ts
@@ -31112,11 +31201,7 @@ function tryFlushPending(state, deps) {
31112
31201
  const payload = deps.encodeStdin(text, { sessionId: state.file.sessionId });
31113
31202
  return [{ kind: "write-stdin", payload }];
31114
31203
  }
31115
- function rgDbg(deps, event, info) {
31116
- if (deps.logger) {
31117
- deps.logger.debug(`[RG] ${event}`, info);
31118
- return;
31119
- }
31204
+ function rgDbg(event, info) {
31120
31205
  console.debug(`[RG] ${event}`, info);
31121
31206
  }
31122
31207
  function resetReadyGate(next) {
@@ -31392,7 +31477,7 @@ function applyCommand(state, command, deps) {
31392
31477
  const payload = deps.encodeStdin(command.text, { sessionId });
31393
31478
  effects.push({ kind: "write-stdin", payload });
31394
31479
  }
31395
- rgDbg(deps, "send", {
31480
+ rgDbg("send", {
31396
31481
  sid: sessionId,
31397
31482
  mode: deps.mode ?? "sdk",
31398
31483
  procAliveBefore,
@@ -31764,13 +31849,13 @@ function reduceSession(state, input, deps) {
31764
31849
  }
31765
31850
  case "ready-detected": {
31766
31851
  if (deps.mode !== "tui") {
31767
- rgDbg(deps, "ready-input", { sid: state.file.sessionId, skipped: "non-tui-mode" });
31852
+ rgDbg("ready-input", { sid: state.file.sessionId, skipped: "non-tui-mode" });
31768
31853
  return { state, effects: [] };
31769
31854
  }
31770
31855
  const next = cloneState(state);
31771
31856
  next.readyForSend = true;
31772
31857
  const flushEffects = tryFlushPending(next, deps);
31773
- rgDbg(deps, "ready-input", {
31858
+ rgDbg("ready-input", {
31774
31859
  sid: next.file.sessionId,
31775
31860
  wroteStdin: flushEffects.length > 0,
31776
31861
  readyAfter: next.readyForSend,
@@ -31943,9 +32028,7 @@ var SessionRunner = class {
31943
32028
  ownerDisplayName: this.hooks.ownerDisplayName,
31944
32029
  personaRoot: this.hooks.personaRoot,
31945
32030
  // ReadyGate v2:透传 mode 让 reducer send / ready-detected 分支决定走暂存队列还是直写
31946
- mode: this.hooks.mode,
31947
- // [RG-DBG] 注入 logger 让 reducer rgDbg 走 pino 进 clawd.log;定位完跟 rgDbg 一起删
31948
- logger: this.hooks.logger
32031
+ mode: this.hooks.mode
31949
32032
  };
31950
32033
  const { state, effects } = reduceSession(this.state, inputMsg, deps);
31951
32034
  this.state = state;
@@ -32124,7 +32207,7 @@ var SessionRunner = class {
32124
32207
  this.doKill(effect.signal);
32125
32208
  break;
32126
32209
  case "write-stdin":
32127
- this.hooks.logger?.debug("[RG] write-stdin", {
32210
+ console.debug("[RG] write-stdin", {
32128
32211
  procAlive: !!this.proc,
32129
32212
  stdinWritable: !!this.proc?.stdin,
32130
32213
  len: effect.payload.length,
@@ -33780,12 +33863,12 @@ var SessionManager = class {
33780
33863
  */
33781
33864
  dispatchReadyDetected(toolSessionId) {
33782
33865
  if (this.deps.mode !== "tui") {
33783
- this.deps.logger?.debug("[RG] mgr-disp", { tsid: toolSessionId, skipped: "non-tui-mode" });
33866
+ console.debug("[RG] mgr-disp", { tsid: toolSessionId, skipped: "non-tui-mode" });
33784
33867
  return;
33785
33868
  }
33786
33869
  const sid = this.sessionIdByToolSid(toolSessionId);
33787
33870
  const runner = sid ? this.runners.get(sid) : void 0;
33788
- this.deps.logger?.debug("[RG] mgr-disp", {
33871
+ console.debug("[RG] mgr-disp", {
33789
33872
  tsid: toolSessionId,
33790
33873
  sid: sid ?? null,
33791
33874
  runnerFound: !!runner
@@ -34914,19 +34997,20 @@ var PtyChildProcess = class extends import_node_events.EventEmitter {
34914
34997
  if (match) {
34915
34998
  const paste = match[1];
34916
34999
  const enter = match[2];
34917
- logger?.debug("[RG] pty-paste", { tag, pasteLen: paste.length, enterLen: enter.length });
35000
+ console.debug("[RG] pty-paste", { pasteLen: paste.length, enterLen: enter.length });
34918
35001
  pty.write(paste);
34919
35002
  setTimeout(() => {
34920
35003
  try {
34921
35004
  pty.write(enter);
34922
- logger?.debug("[RG] pty-paste-enter", { tag, wrote: "\\r", disposed: false });
35005
+ console.debug("[RG] pty-paste-enter", { wrote: "\\r", disposed: false });
35006
+ logger?.debug("pty bracketed paste submit \\r delayed", { tag, pid: this.pid });
34923
35007
  } catch (err) {
34924
- logger?.debug("[RG] pty-paste-enter", { tag, error: err.message });
35008
+ console.debug("[RG] pty-paste-enter", { error: err.message });
34925
35009
  logger?.warn("pty delayed \\r failed", { tag, error: err.message });
34926
35010
  }
34927
- }, 240);
35011
+ }, 120);
34928
35012
  } else {
34929
- logger?.debug("[RG] pty-write-direct", { tag, len: data.length });
35013
+ console.debug("[RG] pty-write-direct", { len: data.length });
34930
35014
  pty.write(data);
34931
35015
  }
34932
35016
  };
@@ -35167,21 +35251,14 @@ function createReadyDetector(opts) {
35167
35251
  quietTimer = null;
35168
35252
  }
35169
35253
  };
35170
- const dbg = (info) => {
35171
- if (opts.logger) {
35172
- opts.logger.debug("[RG] det-state", info);
35173
- return;
35174
- }
35175
- console.debug("[RG] det-state", info);
35176
- };
35177
35254
  const quietFire = () => {
35178
35255
  quietTimer = null;
35179
35256
  if (disposed) return;
35180
35257
  if (!scanReadyFrame()) {
35181
- dbg({ event: "quiet-fire-but-not-ready-anymore" });
35258
+ console.debug("[RG] det-state", { event: "quiet-fire-but-not-ready-anymore" });
35182
35259
  return;
35183
35260
  }
35184
- dbg({ event: "quiet-fire \u2192 onReady" });
35261
+ console.debug("[RG] det-state", { event: "quiet-fire \u2192 onReady" });
35185
35262
  opts.onReady();
35186
35263
  };
35187
35264
  let prevReady = false;
@@ -35191,13 +35268,13 @@ function createReadyDetector(opts) {
35191
35268
  if (!ready) {
35192
35269
  clearQuiet();
35193
35270
  if (prevReady) {
35194
- dbg({ event: "ready \u2192 not-ready" });
35271
+ console.debug("[RG] det-state", { event: "ready \u2192 not-ready" });
35195
35272
  prevReady = false;
35196
35273
  }
35197
35274
  return;
35198
35275
  }
35199
35276
  if (!prevReady) {
35200
- dbg({ event: "not-ready \u2192 ready-candidate (quiet timer set)" });
35277
+ console.debug("[RG] det-state", { event: "not-ready \u2192 ready-candidate (quiet timer set)" });
35201
35278
  prevReady = true;
35202
35279
  }
35203
35280
  clearQuiet();
@@ -35314,9 +35391,7 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
35314
35391
  if (!ctx.toolSessionId || !this.tuiOpts.onReady) return;
35315
35392
  this.tuiLogger?.debug("ready-detected", { toolSessionId: ctx.toolSessionId });
35316
35393
  this.tuiOpts.onReady(ctx.toolSessionId);
35317
- },
35318
- // [RG-DBG] 排查 ReadyGate 偶现卡输入用;定位完跟所有 [RG] 日志一起删
35319
- logger: this.tuiLogger
35394
+ }
35320
35395
  });
35321
35396
  if (ctx.toolSessionId && this.tuiOpts.onDetectorRegister) {
35322
35397
  this.tuiOpts.onDetectorRegister(ctx.toolSessionId, detector);
@@ -35595,7 +35670,7 @@ function scanAgentsDir(dir, source, seen, out) {
35595
35670
  }
35596
35671
  }
35597
35672
  function scanPluginAgentsTree(root, pluginName, seen, out) {
35598
- function walk(dir, namespaces) {
35673
+ function walk2(dir, namespaces) {
35599
35674
  let entries;
35600
35675
  try {
35601
35676
  entries = import_node_fs12.default.readdirSync(dir, { withFileTypes: true });
@@ -35605,7 +35680,7 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
35605
35680
  for (const ent of entries) {
35606
35681
  const childPath = import_node_path13.default.join(dir, ent.name);
35607
35682
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
35608
- walk(childPath, [...namespaces, ent.name]);
35683
+ walk2(childPath, [...namespaces, ent.name]);
35609
35684
  continue;
35610
35685
  }
35611
35686
  if (!ent.name.endsWith(".md")) continue;
@@ -35625,7 +35700,7 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
35625
35700
  });
35626
35701
  }
35627
35702
  }
35628
- walk(root, []);
35703
+ walk2(root, []);
35629
35704
  }
35630
35705
  function readInstalledPlugins2(home) {
35631
35706
  const pluginsDir = import_node_path13.default.join(home, ".claude", "plugins");
@@ -36364,7 +36439,8 @@ var LocalWsServer = class {
36364
36439
  if (verdict !== "pass") {
36365
36440
  if (!wasAuthed && authGate.isAuthed(client.id)) {
36366
36441
  try {
36367
- const full = this.opts.readyFrameBuilder({ remoteAddress });
36442
+ const principalKind = this.clients.get(client.id)?.ctx?.principal.kind;
36443
+ const full = this.opts.readyFrameBuilder({ remoteAddress, principalKind });
36368
36444
  this.safeSend(this.clients.get(client.id).ws, { type: "ready", ...full });
36369
36445
  } catch (err) {
36370
36446
  this.logger?.warn("post-auth ready frame build failed", { err: err.message });
@@ -40371,7 +40447,430 @@ function buildAttachmentHandlers(deps) {
40371
40447
  }
40372
40448
 
40373
40449
  // src/handlers/extension.ts
40450
+ var import_promises7 = __toESM(require("fs/promises"), 1);
40451
+ var import_node_path38 = __toESM(require("path"), 1);
40374
40452
  init_protocol();
40453
+
40454
+ // src/extension/bundle-zip.ts
40455
+ var import_promises4 = __toESM(require("fs/promises"), 1);
40456
+ var import_node_path34 = __toESM(require("path"), 1);
40457
+ var import_node_crypto11 = __toESM(require("crypto"), 1);
40458
+ var import_jszip2 = __toESM(require_lib3(), 1);
40459
+ async function bundleExtensionDir(dir) {
40460
+ const entries = await listFilesSorted(dir);
40461
+ const zip = new import_jszip2.default();
40462
+ for (const rel of entries) {
40463
+ const abs = import_node_path34.default.join(dir, rel);
40464
+ const content = await import_promises4.default.readFile(abs);
40465
+ zip.file(rel, content, { date: FIXED_DATE });
40466
+ }
40467
+ const buffer = await zip.generateAsync({
40468
+ type: "nodebuffer",
40469
+ compression: "DEFLATE",
40470
+ compressionOptions: { level: 6 }
40471
+ });
40472
+ const sha256 = import_node_crypto11.default.createHash("sha256").update(buffer).digest("hex");
40473
+ return { buffer, sha256 };
40474
+ }
40475
+ var FIXED_DATE = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
40476
+ var IGNORE_BASENAMES = /* @__PURE__ */ new Set([".DS_Store"]);
40477
+ async function listFilesSorted(rootDir) {
40478
+ const out = [];
40479
+ await walk(rootDir, "", out);
40480
+ out.sort();
40481
+ return out;
40482
+ }
40483
+ async function walk(absRoot, relPrefix, out) {
40484
+ const dirAbs = import_node_path34.default.join(absRoot, relPrefix);
40485
+ const entries = await import_promises4.default.readdir(dirAbs, { withFileTypes: true });
40486
+ for (const e of entries) {
40487
+ if (IGNORE_BASENAMES.has(e.name)) continue;
40488
+ const rel = relPrefix ? `${relPrefix}/${e.name}` : e.name;
40489
+ if (e.isDirectory()) {
40490
+ await walk(absRoot, rel, out);
40491
+ } else if (e.isFile()) {
40492
+ out.push(rel);
40493
+ }
40494
+ }
40495
+ }
40496
+
40497
+ // src/extension/publish-check.ts
40498
+ function computePublishCheck(args) {
40499
+ const { localHash, localVersion, head } = args;
40500
+ if (head === null) {
40501
+ return {
40502
+ kind: "ready-new-version",
40503
+ fromVersion: "0.0.0",
40504
+ toVersion: localVersion
40505
+ };
40506
+ }
40507
+ const hashMatch = localHash === head.snapshotHash;
40508
+ if (hashMatch) {
40509
+ if (localVersion === head.version) {
40510
+ return { kind: "clean" };
40511
+ }
40512
+ return { kind: "error-same-hash", version: head.version };
40513
+ }
40514
+ const cmp = compareSemver(localVersion, head.version);
40515
+ if (cmp > 0) {
40516
+ return {
40517
+ kind: "ready-new-version",
40518
+ fromVersion: head.version,
40519
+ toVersion: localVersion
40520
+ };
40521
+ }
40522
+ if (cmp === 0) {
40523
+ return {
40524
+ kind: "error-version-not-bumped",
40525
+ localVersion,
40526
+ publishedVersion: head.version
40527
+ };
40528
+ }
40529
+ return {
40530
+ kind: "error-version-regression",
40531
+ localVersion,
40532
+ publishedVersion: head.version
40533
+ };
40534
+ }
40535
+ function compareSemver(a, b2) {
40536
+ const pa = parseSimpleSemver(a);
40537
+ const pb = parseSimpleSemver(b2);
40538
+ for (let i = 0; i < 3; i++) {
40539
+ if (pa[i] < pb[i]) return -1;
40540
+ if (pa[i] > pb[i]) return 1;
40541
+ }
40542
+ return 0;
40543
+ }
40544
+ function parseSimpleSemver(v2) {
40545
+ const parts = v2.split(".");
40546
+ if (parts.length !== 3) {
40547
+ throw new Error(`unsupported version "${v2}" \u2014 only MAJOR.MINOR.PATCH allowed`);
40548
+ }
40549
+ const nums = parts.map((p2) => {
40550
+ if (!/^\d+$/.test(p2)) {
40551
+ throw new Error(`unsupported version "${v2}" \u2014 only numeric segments allowed`);
40552
+ }
40553
+ return parseInt(p2, 10);
40554
+ });
40555
+ return nums;
40556
+ }
40557
+
40558
+ // src/extension/install-flow.ts
40559
+ var import_promises5 = __toESM(require("fs/promises"), 1);
40560
+ var import_node_path36 = __toESM(require("path"), 1);
40561
+ var import_node_os17 = __toESM(require("os"), 1);
40562
+ var import_node_crypto12 = __toESM(require("crypto"), 1);
40563
+ var import_jszip3 = __toESM(require_lib3(), 1);
40564
+
40565
+ // src/extension/paths.ts
40566
+ var import_node_os16 = __toESM(require("os"), 1);
40567
+ var import_node_path35 = __toESM(require("path"), 1);
40568
+ function clawdHomeRoot(override) {
40569
+ return override ?? process.env.CLAWD_HOME ?? import_node_path35.default.join(import_node_os16.default.homedir(), ".clawd");
40570
+ }
40571
+ function extensionsRoot(override) {
40572
+ return import_node_path35.default.join(clawdHomeRoot(override), "extensions");
40573
+ }
40574
+ function publishedChannelsFile(override) {
40575
+ return import_node_path35.default.join(clawdHomeRoot(override), "extensions-published.json");
40576
+ }
40577
+
40578
+ // src/extension/install-flow.ts
40579
+ var InstallError = class extends Error {
40580
+ constructor(code, message) {
40581
+ super(message);
40582
+ this.code = code;
40583
+ }
40584
+ code;
40585
+ };
40586
+ async function installFromChannel(args, deps) {
40587
+ const { channelRef, snapshotHash, bundleZip } = args;
40588
+ const computed = import_node_crypto12.default.createHash("sha256").update(bundleZip).digest("hex");
40589
+ if (computed !== snapshotHash) {
40590
+ throw new InstallError(
40591
+ "HASH_MISMATCH",
40592
+ `expected ${snapshotHash.slice(0, 8)}\u2026 but got ${computed.slice(0, 8)}\u2026`
40593
+ );
40594
+ }
40595
+ let zip;
40596
+ try {
40597
+ zip = await import_jszip3.default.loadAsync(bundleZip);
40598
+ } catch (e) {
40599
+ throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
40600
+ }
40601
+ for (const name of Object.keys(zip.files)) {
40602
+ if (name.includes("..") || name.startsWith("/") || import_node_path36.default.isAbsolute(name)) {
40603
+ throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
40604
+ }
40605
+ }
40606
+ const manifestEntry = zip.file("manifest.json");
40607
+ if (!manifestEntry) {
40608
+ throw new InstallError("MANIFEST_NOT_FOUND", "manifest.json missing from bundle");
40609
+ }
40610
+ let manifestJson;
40611
+ try {
40612
+ manifestJson = JSON.parse(await manifestEntry.async("string"));
40613
+ } catch (e) {
40614
+ throw new InstallError(
40615
+ "INVALID_MANIFEST",
40616
+ `manifest.json parse: ${e.message}`
40617
+ );
40618
+ }
40619
+ const parsed = ExtensionManifestSchema.safeParse(manifestJson);
40620
+ if (!parsed.success) {
40621
+ throw new InstallError(
40622
+ "INVALID_MANIFEST",
40623
+ parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
40624
+ );
40625
+ }
40626
+ const ownerSlug = parsed.data.id;
40627
+ if (ownerSlug !== channelRef.extId) {
40628
+ throw new InstallError(
40629
+ "SLUG_MISMATCH",
40630
+ `bundle manifest.id "${ownerSlug}" does not match channelRef.extId "${channelRef.extId}"`
40631
+ );
40632
+ }
40633
+ const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
40634
+ const destDir = import_node_path36.default.join(deps.extensionsRoot, localExtId);
40635
+ let destExists = false;
40636
+ try {
40637
+ await import_promises5.default.access(destDir);
40638
+ destExists = true;
40639
+ } catch {
40640
+ }
40641
+ if (destExists) {
40642
+ throw new InstallError(
40643
+ "ALREADY_EXISTS",
40644
+ `extension ${localExtId} already installed locally (from owner ${channelRef.ownerPrincipalId})`
40645
+ );
40646
+ }
40647
+ const stage = await import_promises5.default.mkdtemp(
40648
+ import_node_path36.default.join(import_node_os17.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
40649
+ );
40650
+ try {
40651
+ for (const [name, entry] of Object.entries(zip.files)) {
40652
+ const dest = import_node_path36.default.join(stage, name);
40653
+ if (entry.dir) {
40654
+ await import_promises5.default.mkdir(dest, { recursive: true });
40655
+ continue;
40656
+ }
40657
+ await import_promises5.default.mkdir(import_node_path36.default.dirname(dest), { recursive: true });
40658
+ if (name === "manifest.json") {
40659
+ const rewritten = { ...parsed.data, id: localExtId };
40660
+ await import_promises5.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
40661
+ } else {
40662
+ await import_promises5.default.writeFile(dest, await entry.async("nodebuffer"));
40663
+ }
40664
+ }
40665
+ await import_promises5.default.mkdir(deps.extensionsRoot, { recursive: true });
40666
+ await import_promises5.default.rename(stage, destDir);
40667
+ } catch (e) {
40668
+ await import_promises5.default.rm(stage, { recursive: true, force: true }).catch(() => {
40669
+ });
40670
+ throw e;
40671
+ }
40672
+ await deps.runtime.open(localExtId);
40673
+ return { extId: localExtId, installedVersion: parsed.data.version };
40674
+ }
40675
+
40676
+ // src/extension/update-flow.ts
40677
+ var import_promises6 = __toESM(require("fs/promises"), 1);
40678
+ var import_node_path37 = __toESM(require("path"), 1);
40679
+ var import_node_os18 = __toESM(require("os"), 1);
40680
+ var import_node_crypto13 = __toESM(require("crypto"), 1);
40681
+ var import_jszip4 = __toESM(require_lib3(), 1);
40682
+ var UpdateError = class extends Error {
40683
+ constructor(code, message) {
40684
+ super(message);
40685
+ this.code = code;
40686
+ }
40687
+ code;
40688
+ };
40689
+ async function updateFromChannel(args, deps) {
40690
+ const { channelRef, snapshotHash, bundleZip } = args;
40691
+ const localExtId = namespacedExtId(
40692
+ channelRef.extId,
40693
+ channelRef.ownerPrincipalId
40694
+ );
40695
+ const liveDir = import_node_path37.default.join(deps.extensionsRoot, localExtId);
40696
+ const prevDir = `${liveDir}.prev`;
40697
+ let existingVersion;
40698
+ try {
40699
+ const raw = await import_promises6.default.readFile(import_node_path37.default.join(liveDir, "manifest.json"), "utf8");
40700
+ const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
40701
+ if (!parsed2.success) {
40702
+ throw new UpdateError(
40703
+ "INVALID_MANIFEST",
40704
+ `existing manifest invalid: ${parsed2.error.issues.map((i) => i.message).join("; ")}`
40705
+ );
40706
+ }
40707
+ existingVersion = parsed2.data.version;
40708
+ } catch (e) {
40709
+ if (e.code === "ENOENT") {
40710
+ throw new UpdateError(
40711
+ "NOT_INSTALLED",
40712
+ `${localExtId} is not installed locally`
40713
+ );
40714
+ }
40715
+ if (e instanceof UpdateError) throw e;
40716
+ throw e;
40717
+ }
40718
+ const computed = import_node_crypto13.default.createHash("sha256").update(bundleZip).digest("hex");
40719
+ if (computed !== snapshotHash) {
40720
+ throw new UpdateError(
40721
+ "HASH_MISMATCH",
40722
+ `expected ${snapshotHash.slice(0, 8)}\u2026 but got ${computed.slice(0, 8)}\u2026`
40723
+ );
40724
+ }
40725
+ let zip;
40726
+ try {
40727
+ zip = await import_jszip4.default.loadAsync(bundleZip);
40728
+ } catch (e) {
40729
+ throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
40730
+ }
40731
+ for (const name of Object.keys(zip.files)) {
40732
+ if (name.includes("..") || name.startsWith("/") || import_node_path37.default.isAbsolute(name)) {
40733
+ throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
40734
+ }
40735
+ }
40736
+ const manifestEntry = zip.file("manifest.json");
40737
+ if (!manifestEntry) {
40738
+ throw new UpdateError("MANIFEST_NOT_FOUND", "manifest.json missing from bundle");
40739
+ }
40740
+ let manifestJson;
40741
+ try {
40742
+ manifestJson = JSON.parse(await manifestEntry.async("string"));
40743
+ } catch (e) {
40744
+ throw new UpdateError(
40745
+ "INVALID_MANIFEST",
40746
+ `manifest.json parse: ${e.message}`
40747
+ );
40748
+ }
40749
+ const parsed = ExtensionManifestSchema.safeParse(manifestJson);
40750
+ if (!parsed.success) {
40751
+ throw new UpdateError(
40752
+ "INVALID_MANIFEST",
40753
+ parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
40754
+ );
40755
+ }
40756
+ const ownerSlug = parsed.data.id;
40757
+ if (ownerSlug !== channelRef.extId) {
40758
+ throw new UpdateError(
40759
+ "SLUG_MISMATCH",
40760
+ `bundle manifest.id "${ownerSlug}" does not match channelRef.extId "${channelRef.extId}"`
40761
+ );
40762
+ }
40763
+ await deps.runtime.close(localExtId);
40764
+ await import_promises6.default.rm(prevDir, { recursive: true, force: true });
40765
+ await import_promises6.default.rename(liveDir, prevDir);
40766
+ const stage = await import_promises6.default.mkdtemp(
40767
+ import_node_path37.default.join(import_node_os18.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
40768
+ );
40769
+ try {
40770
+ for (const [name, entry] of Object.entries(zip.files)) {
40771
+ const dest = import_node_path37.default.join(stage, name);
40772
+ if (entry.dir) {
40773
+ await import_promises6.default.mkdir(dest, { recursive: true });
40774
+ continue;
40775
+ }
40776
+ await import_promises6.default.mkdir(import_node_path37.default.dirname(dest), { recursive: true });
40777
+ if (name === "manifest.json") {
40778
+ const rewritten = { ...parsed.data, id: localExtId };
40779
+ await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
40780
+ } else {
40781
+ await import_promises6.default.writeFile(dest, await entry.async("nodebuffer"));
40782
+ }
40783
+ }
40784
+ await import_promises6.default.rename(stage, liveDir);
40785
+ } catch (e) {
40786
+ await import_promises6.default.rm(stage, { recursive: true, force: true }).catch(() => {
40787
+ });
40788
+ await import_promises6.default.rename(prevDir, liveDir).catch(() => {
40789
+ });
40790
+ await deps.runtime.open(localExtId).catch(() => {
40791
+ });
40792
+ return {
40793
+ kind: "rolled-back",
40794
+ extId: localExtId,
40795
+ rolledBackTo: existingVersion,
40796
+ reason: `extract failed: ${e.message}`
40797
+ };
40798
+ }
40799
+ try {
40800
+ await deps.runtime.open(localExtId);
40801
+ } catch (e) {
40802
+ await rollback(deps, localExtId, liveDir, prevDir);
40803
+ return {
40804
+ kind: "rolled-back",
40805
+ extId: localExtId,
40806
+ rolledBackTo: existingVersion,
40807
+ reason: e.message
40808
+ };
40809
+ }
40810
+ await import_promises6.default.rm(prevDir, { recursive: true, force: true });
40811
+ return {
40812
+ kind: "updated",
40813
+ extId: localExtId,
40814
+ installedVersion: parsed.data.version
40815
+ };
40816
+ }
40817
+ async function rollback(deps, localExtId, liveDir, prevDir) {
40818
+ await import_promises6.default.rm(liveDir, { recursive: true, force: true }).catch(() => {
40819
+ });
40820
+ await import_promises6.default.rename(prevDir, liveDir).catch(() => {
40821
+ });
40822
+ await deps.runtime.open(localExtId).catch(() => {
40823
+ });
40824
+ }
40825
+
40826
+ // src/handlers/extension.ts
40827
+ function pickChannelRef(frame) {
40828
+ const ref = frame.channelRef;
40829
+ const parsed = ChannelRefSchema.safeParse(ref);
40830
+ if (!parsed.success) {
40831
+ throw new ClawdError(
40832
+ ERROR_CODES.INVALID_PARAM,
40833
+ `channelRef invalid: ${parsed.error.issues.map((i) => i.message).join("; ")}`
40834
+ );
40835
+ }
40836
+ return parsed.data;
40837
+ }
40838
+ function pickStringField(frame, name) {
40839
+ const v2 = frame[name];
40840
+ if (typeof v2 !== "string" || v2.length === 0) {
40841
+ throw new ClawdError(ERROR_CODES.INVALID_PARAM, `${name} required`);
40842
+ }
40843
+ return v2;
40844
+ }
40845
+ async function readManifest(root, extId) {
40846
+ const file = import_node_path38.default.join(root, extId, "manifest.json");
40847
+ let raw;
40848
+ try {
40849
+ raw = await import_promises7.default.readFile(file, "utf8");
40850
+ } catch (e) {
40851
+ if (e.code === "ENOENT") {
40852
+ throw new ClawdError(ERROR_CODES.INVALID_PARAM, `extension ${extId} not installed`);
40853
+ }
40854
+ throw e;
40855
+ }
40856
+ let json;
40857
+ try {
40858
+ json = JSON.parse(raw);
40859
+ } catch (e) {
40860
+ throw new ClawdError(
40861
+ ERROR_CODES.INVALID_PARAM,
40862
+ `manifest.json invalid: ${e.message}`
40863
+ );
40864
+ }
40865
+ const parsed = ExtensionManifestSchema.safeParse(json);
40866
+ if (!parsed.success) {
40867
+ throw new ClawdError(
40868
+ ERROR_CODES.INVALID_PARAM,
40869
+ `manifest schema mismatch: ${parsed.error.issues.map((i) => i.message).join("; ")}`
40870
+ );
40871
+ }
40872
+ return parsed.data;
40873
+ }
40375
40874
  function assertOwner(ctx) {
40376
40875
  if (!ctx || ctx.principal.kind !== "owner") {
40377
40876
  throw new ClawdError(ERROR_CODES.FORBIDDEN, "owner only");
@@ -40426,21 +40925,183 @@ function buildExtensionHandlers(deps) {
40426
40925
  await deps.uninstall({ root: deps.root, extId, runtime: deps.runtime });
40427
40926
  return { response: { ok: true } };
40428
40927
  };
40928
+ async function buildSnapshotMeta(extId) {
40929
+ const manifest = await readManifest(deps.root, extId);
40930
+ const { sha256 } = await bundleExtensionDir(import_node_path38.default.join(deps.root, extId));
40931
+ return { manifest, contentHash: sha256 };
40932
+ }
40933
+ const publish = async (frame, _client, ctx) => {
40934
+ assertOwner(ctx);
40935
+ const extId = pickExtId(frame);
40936
+ const { manifest, contentHash } = await buildSnapshotMeta(extId);
40937
+ const head = deps.publishedChannelStore.get(extId);
40938
+ const check = computePublishCheck({
40939
+ localHash: contentHash,
40940
+ localVersion: manifest.version,
40941
+ head
40942
+ });
40943
+ if (check.kind === "clean" && head) {
40944
+ return { response: { snapshotHash: head.snapshotHash, version: head.version } };
40945
+ }
40946
+ if (check.kind === "error-same-hash" && head) {
40947
+ return { response: { snapshotHash: head.snapshotHash, version: head.version } };
40948
+ }
40949
+ if (check.kind === "error-version-not-bumped") {
40950
+ throw new ClawdError(
40951
+ ERROR_CODES.VALIDATION_ERROR,
40952
+ `VERSION_NOT_BUMPED: manifest.version (${check.localVersion}) needs to be > ${check.publishedVersion}`
40953
+ );
40954
+ }
40955
+ if (check.kind === "error-version-regression") {
40956
+ throw new ClawdError(
40957
+ ERROR_CODES.VALIDATION_ERROR,
40958
+ `VERSION_REGRESSION: manifest.version (${check.localVersion}) is lower than published version (${check.publishedVersion})`
40959
+ );
40960
+ }
40961
+ const snapshot = {
40962
+ snapshotHash: contentHash,
40963
+ version: manifest.version,
40964
+ publishedAt: Date.now()
40965
+ };
40966
+ await deps.publishedChannelStore.set(extId, snapshot);
40967
+ return {
40968
+ response: { snapshotHash: snapshot.snapshotHash, version: snapshot.version }
40969
+ };
40970
+ };
40971
+ const unpublish = async (frame, _client, ctx) => {
40972
+ assertOwner(ctx);
40973
+ const extId = pickExtId(frame);
40974
+ await deps.publishedChannelStore.delete(extId);
40975
+ return { response: {} };
40976
+ };
40977
+ const publishStatus = async (frame, _client, ctx) => {
40978
+ assertOwner(ctx);
40979
+ const extId = pickExtId(frame);
40980
+ const { manifest, contentHash } = await buildSnapshotMeta(extId);
40981
+ const head = deps.publishedChannelStore.get(extId);
40982
+ const check = computePublishCheck({
40983
+ localHash: contentHash,
40984
+ localVersion: manifest.version,
40985
+ head
40986
+ });
40987
+ const result = {
40988
+ localManifest: {
40989
+ name: manifest.name,
40990
+ version: manifest.version,
40991
+ contentHash
40992
+ },
40993
+ publishState: head ? {
40994
+ kind: "published",
40995
+ version: head.version,
40996
+ contentHash: head.snapshotHash,
40997
+ publishedAt: head.publishedAt
40998
+ } : { kind: "unpublished" },
40999
+ check
41000
+ };
41001
+ return { response: result };
41002
+ };
41003
+ const listPublished = async (_frame, _client, ctx) => {
41004
+ if (!ctx || ctx.principal.kind !== "guest") {
41005
+ throw new ClawdError(ERROR_CODES.FORBIDDEN, "guest only");
41006
+ }
41007
+ const heads = deps.publishedChannelStore.list();
41008
+ const out = [];
41009
+ for (const { extId, head } of heads) {
41010
+ try {
41011
+ const manifest = await readManifest(deps.root, extId);
41012
+ out.push({
41013
+ extId,
41014
+ name: manifest.name,
41015
+ version: head.version,
41016
+ contentHash: head.snapshotHash,
41017
+ publishedAt: head.publishedAt
41018
+ });
41019
+ } catch {
41020
+ }
41021
+ }
41022
+ return { response: { extensions: out } };
41023
+ };
41024
+ const fetchBundle = async (frame, _client, ctx) => {
41025
+ if (!ctx || ctx.principal.kind !== "guest") {
41026
+ throw new ClawdError(ERROR_CODES.FORBIDDEN, "guest only");
41027
+ }
41028
+ const extId = pickExtId(frame);
41029
+ const expectedHash = pickStringField(frame, "snapshotHash");
41030
+ const { buffer, sha256 } = await bundleExtensionDir(import_node_path38.default.join(deps.root, extId));
41031
+ if (sha256 !== expectedHash) {
41032
+ throw new ClawdError(
41033
+ ERROR_CODES.VALIDATION_ERROR,
41034
+ `HASH_MISMATCH: bundle hash ${sha256.slice(0, 8)}\u2026 \u2260 requested ${expectedHash.slice(0, 8)}\u2026`
41035
+ );
41036
+ }
41037
+ return { response: { zipBase64: buffer.toString("base64") } };
41038
+ };
41039
+ const installFromChannelHandler = async (frame, _client, ctx) => {
41040
+ assertOwner(ctx);
41041
+ const channelRef = pickChannelRef(frame);
41042
+ const snapshotHash = pickStringField(frame, "snapshotHash");
41043
+ const zipBase64 = pickStringField(frame, "zipBase64");
41044
+ const bundleZip = Buffer.from(zipBase64, "base64");
41045
+ try {
41046
+ const { extId, installedVersion } = await installFromChannel(
41047
+ { channelRef, snapshotHash, bundleZip },
41048
+ {
41049
+ extensionsRoot: deps.root,
41050
+ runtime: deps.runtime
41051
+ }
41052
+ );
41053
+ return { response: { extId, installedVersion } };
41054
+ } catch (e) {
41055
+ if (e instanceof InstallError) {
41056
+ throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `${e.code}: ${e.message}`);
41057
+ }
41058
+ throw e;
41059
+ }
41060
+ };
41061
+ const updateFromChannelHandler = async (frame, _client, ctx) => {
41062
+ assertOwner(ctx);
41063
+ const channelRef = pickChannelRef(frame);
41064
+ const snapshotHash = pickStringField(frame, "snapshotHash");
41065
+ const zipBase64 = pickStringField(frame, "zipBase64");
41066
+ const bundleZip = Buffer.from(zipBase64, "base64");
41067
+ try {
41068
+ const result = await updateFromChannel(
41069
+ { channelRef, snapshotHash, bundleZip },
41070
+ {
41071
+ extensionsRoot: deps.root,
41072
+ runtime: deps.runtime
41073
+ }
41074
+ );
41075
+ return { response: result };
41076
+ } catch (e) {
41077
+ if (e instanceof UpdateError) {
41078
+ throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `${e.code}: ${e.message}`);
41079
+ }
41080
+ throw e;
41081
+ }
41082
+ };
40429
41083
  return {
40430
41084
  "extension.list": list,
40431
41085
  "extension.open": open,
40432
41086
  "extension.close": close,
40433
- "extension.uninstall": uninstall2
41087
+ "extension.uninstall": uninstall2,
41088
+ "extension.publish": publish,
41089
+ "extension.unpublish": unpublish,
41090
+ "extension.publish:status": publishStatus,
41091
+ "extension.list-published": listPublished,
41092
+ "extension.fetchBundle": fetchBundle,
41093
+ "extension.install-from-channel": installFromChannelHandler,
41094
+ "extension.update-from-channel": updateFromChannelHandler
40434
41095
  };
40435
41096
  }
40436
41097
 
40437
41098
  // src/extension/registry.ts
40438
- var import_promises4 = __toESM(require("fs/promises"), 1);
40439
- var import_node_path34 = __toESM(require("path"), 1);
41099
+ var import_promises8 = __toESM(require("fs/promises"), 1);
41100
+ var import_node_path39 = __toESM(require("path"), 1);
40440
41101
  async function loadAll(root) {
40441
41102
  let entries;
40442
41103
  try {
40443
- entries = await import_promises4.default.readdir(root, { withFileTypes: true });
41104
+ entries = await import_promises8.default.readdir(root, { withFileTypes: true });
40444
41105
  } catch (e) {
40445
41106
  if (e.code === "ENOENT") return [];
40446
41107
  throw e;
@@ -40449,16 +41110,16 @@ async function loadAll(root) {
40449
41110
  for (const ent of entries) {
40450
41111
  if (!ent.isDirectory()) continue;
40451
41112
  if (ent.name.startsWith(".")) continue;
40452
- records.push(await loadOne(import_node_path34.default.join(root, ent.name), ent.name));
41113
+ records.push(await loadOne(import_node_path39.default.join(root, ent.name), ent.name));
40453
41114
  }
40454
41115
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
40455
41116
  return records;
40456
41117
  }
40457
41118
  async function loadOne(dir, dirName) {
40458
- const manifestPath = import_node_path34.default.join(dir, "manifest.json");
41119
+ const manifestPath = import_node_path39.default.join(dir, "manifest.json");
40459
41120
  let raw;
40460
41121
  try {
40461
- raw = await import_promises4.default.readFile(manifestPath, "utf8");
41122
+ raw = await import_promises8.default.readFile(manifestPath, "utf8");
40462
41123
  } catch {
40463
41124
  return {
40464
41125
  extId: dirName,
@@ -40499,8 +41160,8 @@ async function loadOne(dir, dirName) {
40499
41160
  }
40500
41161
 
40501
41162
  // src/extension/uninstall.ts
40502
- var import_promises5 = __toESM(require("fs/promises"), 1);
40503
- var import_node_path35 = __toESM(require("path"), 1);
41163
+ var import_promises9 = __toESM(require("fs/promises"), 1);
41164
+ var import_node_path40 = __toESM(require("path"), 1);
40504
41165
  var UninstallError = class extends Error {
40505
41166
  constructor(code, message) {
40506
41167
  super(message);
@@ -40509,22 +41170,14 @@ var UninstallError = class extends Error {
40509
41170
  code;
40510
41171
  };
40511
41172
  async function uninstall(deps) {
40512
- const dir = import_node_path35.default.join(deps.root, deps.extId);
41173
+ const dir = import_node_path40.default.join(deps.root, deps.extId);
40513
41174
  try {
40514
- await import_promises5.default.access(dir);
41175
+ await import_promises9.default.access(dir);
40515
41176
  } catch {
40516
41177
  throw new UninstallError("NOT_FOUND", `extension ${deps.extId} not installed`);
40517
41178
  }
40518
41179
  await deps.runtime.close(deps.extId);
40519
- await import_promises5.default.rm(dir, { recursive: true, force: true });
40520
- }
40521
-
40522
- // src/extension/paths.ts
40523
- var import_node_os16 = __toESM(require("os"), 1);
40524
- var import_node_path36 = __toESM(require("path"), 1);
40525
- function extensionsRoot(override) {
40526
- const home = override ?? process.env.CLAWD_HOME ?? import_node_path36.default.join(import_node_os16.default.homedir(), ".clawd");
40527
- return import_node_path36.default.join(home, "extensions");
41180
+ await import_promises9.default.rm(dir, { recursive: true, force: true });
40528
41181
  }
40529
41182
 
40530
41183
  // src/handlers/index.ts
@@ -40568,7 +41221,8 @@ function buildMethodHandlers(deps) {
40568
41221
  loadAll,
40569
41222
  runtime: deps.extensionRuntime,
40570
41223
  uninstall,
40571
- root: extensionsRoot()
41224
+ root: extensionsRoot(),
41225
+ publishedChannelStore: deps.publishedChannelStore
40572
41226
  })
40573
41227
  };
40574
41228
  }
@@ -40691,7 +41345,18 @@ var METHOD_GRANT_MAP = {
40691
41345
  "extension.list": ADMIN_ANY,
40692
41346
  "extension.open": ADMIN_ANY,
40693
41347
  "extension.close": ADMIN_ANY,
40694
- "extension.uninstall": ADMIN_ANY
41348
+ "extension.uninstall": ADMIN_ANY,
41349
+ // ---- extension sharing v1(spec 2026-05-28-clawd-extension-sharing-design) ----
41350
+ // owner-side:owner 自己管理 publish 状态
41351
+ "extension.publish": ADMIN_ANY,
41352
+ "extension.unpublish": ADMIN_ANY,
41353
+ "extension.publish:status": ADMIN_ANY,
41354
+ // cross-tunnel:guest 从 owner 拉 published list + bundle
41355
+ "extension.list-published": CAPABILITY_SCOPED,
41356
+ "extension.fetchBundle": CAPABILITY_SCOPED,
41357
+ // guest-self:guest 在自己 daemon 上触发安装与更新(无持久化 subscription 概念)
41358
+ "extension.install-from-channel": ADMIN_ANY,
41359
+ "extension.update-from-channel": ADMIN_ANY
40695
41360
  };
40696
41361
  function computeGrantForFrame(method, frame) {
40697
41362
  const rule = METHOD_GRANT_MAP[method];
@@ -40708,8 +41373,8 @@ function computeGrantForFrame(method, frame) {
40708
41373
 
40709
41374
  // src/extension/runtime.ts
40710
41375
  var import_node_child_process7 = require("child_process");
40711
- var import_node_path37 = __toESM(require("path"), 1);
40712
- var import_promises6 = require("timers/promises");
41376
+ var import_node_path41 = __toESM(require("path"), 1);
41377
+ var import_promises10 = require("timers/promises");
40713
41378
 
40714
41379
  // src/extension/port-allocator.ts
40715
41380
  var import_node_net = __toESM(require("net"), 1);
@@ -40805,7 +41470,7 @@ var Runtime = class {
40805
41470
  /\$CLAWOS_EXT_PORT/g,
40806
41471
  String(port)
40807
41472
  );
40808
- const dir = import_node_path37.default.join(this.root, extId);
41473
+ const dir = import_node_path41.default.join(this.root, extId);
40809
41474
  const env = {
40810
41475
  ...process.env,
40811
41476
  CLAWOS_EXT_PORT: String(port),
@@ -40881,7 +41546,7 @@ ${handle.stderrTail}`
40881
41546
  if (res.ok) return;
40882
41547
  } catch {
40883
41548
  }
40884
- await (0, import_promises6.setTimeout)(this.healthzIntervalMs);
41549
+ await (0, import_promises10.setTimeout)(this.healthzIntervalMs);
40885
41550
  }
40886
41551
  throw new RuntimeError(
40887
41552
  "HEALTHZ_FAILED",
@@ -40906,7 +41571,7 @@ ${handle.stderrTail}`
40906
41571
  };
40907
41572
  killGroup("SIGTERM");
40908
41573
  const exited = new Promise((resolve6) => child.once("exit", () => resolve6()));
40909
- const timed = (0, import_promises6.setTimeout)(5e3).then(() => "timeout");
41574
+ const timed = (0, import_promises10.setTimeout)(5e3).then(() => "timeout");
40910
41575
  const winner = await Promise.race([exited.then(() => "exited"), timed]);
40911
41576
  if (winner === "timeout" && child.exitCode === null) {
40912
41577
  killGroup("SIGKILL");
@@ -40915,11 +41580,118 @@ ${handle.stderrTail}`
40915
41580
  }
40916
41581
  };
40917
41582
 
41583
+ // src/extension/published-channels.ts
41584
+ var import_promises11 = __toESM(require("fs/promises"), 1);
41585
+ var import_node_path42 = __toESM(require("path"), 1);
41586
+ init_zod();
41587
+ var PublishedChannelsError = class extends Error {
41588
+ constructor(code, message) {
41589
+ super(message);
41590
+ this.code = code;
41591
+ }
41592
+ code;
41593
+ };
41594
+ var FileSchema = external_exports.object({
41595
+ version: external_exports.literal(1),
41596
+ channels: external_exports.record(
41597
+ external_exports.string(),
41598
+ external_exports.object({
41599
+ extId: external_exports.string().min(1),
41600
+ head: PublishedSnapshotSchema.nullable()
41601
+ })
41602
+ )
41603
+ });
41604
+ var PublishedChannelStore = class {
41605
+ constructor(filePath) {
41606
+ this.filePath = filePath;
41607
+ }
41608
+ filePath;
41609
+ channels = /* @__PURE__ */ new Map();
41610
+ loaded = false;
41611
+ async load() {
41612
+ this.channels.clear();
41613
+ let raw;
41614
+ try {
41615
+ raw = await import_promises11.default.readFile(this.filePath, "utf8");
41616
+ } catch (e) {
41617
+ if (e.code === "ENOENT") {
41618
+ this.loaded = true;
41619
+ return;
41620
+ }
41621
+ throw e;
41622
+ }
41623
+ let json;
41624
+ try {
41625
+ json = JSON.parse(raw);
41626
+ } catch (e) {
41627
+ throw new PublishedChannelsError(
41628
+ "CORRUPTED_PUBLISHED_CHANNELS",
41629
+ `failed to parse ${this.filePath}: ${e.message}`
41630
+ );
41631
+ }
41632
+ const parsed = FileSchema.safeParse(json);
41633
+ if (!parsed.success) {
41634
+ throw new PublishedChannelsError(
41635
+ "CORRUPTED_PUBLISHED_CHANNELS",
41636
+ `schema mismatch in ${this.filePath}: ${parsed.error.issues.map((i) => i.message).join("; ")}`
41637
+ );
41638
+ }
41639
+ for (const [extId, entry] of Object.entries(parsed.data.channels)) {
41640
+ this.channels.set(extId, entry.head);
41641
+ }
41642
+ this.loaded = true;
41643
+ }
41644
+ /** Returns the head snapshot, or null when unpublished / unknown extId. */
41645
+ get(extId) {
41646
+ this.assertLoaded();
41647
+ return this.channels.get(extId) ?? null;
41648
+ }
41649
+ /** Returns all extIds with a head (i.e. currently published). */
41650
+ list() {
41651
+ this.assertLoaded();
41652
+ const out = [];
41653
+ for (const [extId, head] of this.channels.entries()) {
41654
+ if (head) out.push({ extId, head });
41655
+ }
41656
+ return out;
41657
+ }
41658
+ async set(extId, head) {
41659
+ this.assertLoaded();
41660
+ this.channels.set(extId, head);
41661
+ await this.save();
41662
+ }
41663
+ async delete(extId) {
41664
+ this.assertLoaded();
41665
+ this.channels.delete(extId);
41666
+ await this.save();
41667
+ }
41668
+ assertLoaded() {
41669
+ if (!this.loaded) {
41670
+ throw new Error("PublishedChannelStore not loaded \u2014 call load() first");
41671
+ }
41672
+ }
41673
+ async save() {
41674
+ const data = {
41675
+ version: 1,
41676
+ channels: Object.fromEntries(
41677
+ Array.from(this.channels.entries()).map(([extId, head]) => [
41678
+ extId,
41679
+ { extId, head }
41680
+ ])
41681
+ )
41682
+ };
41683
+ const tmp = `${this.filePath}.tmp`;
41684
+ await import_promises11.default.mkdir(import_node_path42.default.dirname(this.filePath), { recursive: true });
41685
+ await import_promises11.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
41686
+ await import_promises11.default.rename(tmp, this.filePath);
41687
+ }
41688
+ };
41689
+
40918
41690
  // src/index.ts
40919
41691
  async function startDaemon(config) {
40920
41692
  const logger = createLogger({
40921
41693
  level: config.logLevel,
40922
- file: import_node_path38.default.join(config.dataDir, "clawd.log")
41694
+ file: import_node_path43.default.join(config.dataDir, "clawd.log")
40923
41695
  });
40924
41696
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
40925
41697
  const stateMgr = new StateFileManager({ dataDir: config.dataDir });
@@ -41006,7 +41778,7 @@ async function startDaemon(config) {
41006
41778
  const agents = new AgentsScanner();
41007
41779
  const history = new ClaudeHistoryReader();
41008
41780
  let transport = null;
41009
- const personaStore = new PersonaStore(import_node_path38.default.join(config.dataDir, "personas"));
41781
+ const personaStore = new PersonaStore(import_node_path43.default.join(config.dataDir, "personas"));
41010
41782
  const defaultsRoot = findDefaultsRoot();
41011
41783
  if (defaultsRoot) {
41012
41784
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -41023,7 +41795,7 @@ async function startDaemon(config) {
41023
41795
  getAdapter,
41024
41796
  historyReader: history,
41025
41797
  dataDir: config.dataDir,
41026
- personaRoot: import_node_path38.default.join(config.dataDir, "personas"),
41798
+ personaRoot: import_node_path43.default.join(config.dataDir, "personas"),
41027
41799
  personaStore,
41028
41800
  ownerDisplayName,
41029
41801
  ownerPrincipalId,
@@ -41047,7 +41819,7 @@ async function startDaemon(config) {
41047
41819
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
41048
41820
  attachmentGroup: {
41049
41821
  onFileEdit: (input) => {
41050
- const absPath = import_node_path38.default.isAbsolute(input.relPath) ? input.relPath : import_node_path38.default.join(input.cwd, input.relPath);
41822
+ const absPath = import_node_path43.default.isAbsolute(input.relPath) ? input.relPath : import_node_path43.default.join(input.cwd, input.relPath);
41051
41823
  let size = 0;
41052
41824
  try {
41053
41825
  size = import_node_fs29.default.statSync(absPath).size;
@@ -41134,6 +41906,8 @@ async function startDaemon(config) {
41134
41906
  return `http://${config.host}:${config.port}`;
41135
41907
  };
41136
41908
  const extensionRuntime = new Runtime({ root: extensionsRoot() });
41909
+ const publishedChannelStore = new PublishedChannelStore(publishedChannelsFile());
41910
+ await publishedChannelStore.load();
41137
41911
  const handlers = buildMethodHandlers({
41138
41912
  manager,
41139
41913
  workspace,
@@ -41173,10 +41947,10 @@ async function startDaemon(config) {
41173
41947
  // 'persona/<pid>/owner',default 走 'default'。
41174
41948
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
41175
41949
  // guest path guard:candidate 必须在 personaRoot 子树下
41176
- personaRoot: import_node_path38.default.join(config.dataDir, "personas")
41950
+ personaRoot: import_node_path43.default.join(config.dataDir, "personas")
41177
41951
  },
41178
41952
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
41179
- personaRoot: import_node_path38.default.join(config.dataDir, "personas"),
41953
+ personaRoot: import_node_path43.default.join(config.dataDir, "personas"),
41180
41954
  // capability:list / delete handler 依赖
41181
41955
  capabilityManager,
41182
41956
  // personal-cap:get 返回的 shareBaseUrl 用此 base URL:
@@ -41203,7 +41977,9 @@ async function startDaemon(config) {
41203
41977
  connectRemote,
41204
41978
  // extension runtime (v1: local-mode only). Instance owned by daemon top
41205
41979
  // level so stopAll() runs in the shutdown hook below.
41206
- extensionRuntime
41980
+ extensionRuntime,
41981
+ // extension sharing v1 — owner-side published-channels store.
41982
+ publishedChannelStore
41207
41983
  });
41208
41984
  const authResolver = new AuthContextResolver({
41209
41985
  ownerToken: resolvedAuthToken
@@ -41388,8 +42164,8 @@ async function startDaemon(config) {
41388
42164
  const lines = [
41389
42165
  `Tunnel: ${r.url}`,
41390
42166
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
41391
- `Frpc config: ${import_node_path38.default.join(config.dataDir, "frpc.toml")}`,
41392
- `Frpc log: ${import_node_path38.default.join(config.dataDir, "frpc.log")}`
42167
+ `Frpc config: ${import_node_path43.default.join(config.dataDir, "frpc.toml")}`,
42168
+ `Frpc log: ${import_node_path43.default.join(config.dataDir, "frpc.log")}`
41393
42169
  ];
41394
42170
  const width = Math.max(...lines.map((l) => l.length));
41395
42171
  const bar = "\u2550".repeat(width + 4);
@@ -41402,7 +42178,7 @@ ${bar}
41402
42178
 
41403
42179
  `);
41404
42180
  try {
41405
- const connectPath = import_node_path38.default.join(config.dataDir, "connect.txt");
42181
+ const connectPath = import_node_path43.default.join(config.dataDir, "connect.txt");
41406
42182
  import_node_fs29.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
41407
42183
  } catch {
41408
42184
  }
@@ -41469,7 +42245,7 @@ ${bar}
41469
42245
  };
41470
42246
  }
41471
42247
  function migrateDropPersonsDir(dataDir) {
41472
- const dir = import_node_path38.default.join(dataDir, "persons");
42248
+ const dir = import_node_path43.default.join(dataDir, "persons");
41473
42249
  try {
41474
42250
  import_node_fs29.default.rmSync(dir, { recursive: true, force: true });
41475
42251
  } catch {