@clawos-dev/clawd 0.2.98 → 0.2.99-beta.190.29dcdb5
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 +934 -144
- 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:
|
|
664
|
-
const fullPath = [...
|
|
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,
|
|
987
|
+
constructor(parent, value, path58, key) {
|
|
976
988
|
this._cachedPath = [];
|
|
977
989
|
this.parent = parent;
|
|
978
990
|
this.data = value;
|
|
979
|
-
this._path =
|
|
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
|
-
|
|
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
|
|
5707
|
-
_req.url = typeof
|
|
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(
|
|
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 <
|
|
5879
|
-
const char =
|
|
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
|
|
6011
|
-
const parts = parsePath(
|
|
6099
|
+
for (const path58 of paths) {
|
|
6100
|
+
const parts = parsePath(path58);
|
|
6012
6101
|
if (parts.includes("*")) {
|
|
6013
|
-
redactWildcardPath(obj, parts, censor,
|
|
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,
|
|
6099
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
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
|
|
6135
|
-
const parts = parsePath(
|
|
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(
|
|
6188
|
-
if (typeof
|
|
6276
|
+
function validatePath(path58) {
|
|
6277
|
+
if (typeof path58 !== "string") {
|
|
6189
6278
|
throw new Error("Paths must be (non-empty) strings");
|
|
6190
6279
|
}
|
|
6191
|
-
if (
|
|
6280
|
+
if (path58 === "") {
|
|
6192
6281
|
throw new Error("Invalid redaction path ()");
|
|
6193
6282
|
}
|
|
6194
|
-
if (
|
|
6195
|
-
throw new Error(`Invalid redaction path (${
|
|
6283
|
+
if (path58.includes("..")) {
|
|
6284
|
+
throw new Error(`Invalid redaction path (${path58})`);
|
|
6196
6285
|
}
|
|
6197
|
-
if (
|
|
6198
|
-
throw new Error(`Invalid redaction path (${
|
|
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 <
|
|
6204
|
-
const char =
|
|
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 (${
|
|
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 (${
|
|
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
|
|
6231
|
-
validatePath(
|
|
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,
|
|
6400
|
-
return censor(value, [k2, ...
|
|
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
|
|
6707
|
+
var fs46 = require("fs");
|
|
6619
6708
|
var EventEmitter2 = require("events");
|
|
6620
6709
|
var inherits = require("util").inherits;
|
|
6621
|
-
var
|
|
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)
|
|
6676
|
-
const fd =
|
|
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
|
-
|
|
6772
|
+
fs46.mkdir(path58.dirname(file), { recursive: true }, (err) => {
|
|
6684
6773
|
if (err) return fileOpened(err);
|
|
6685
|
-
|
|
6774
|
+
fs46.open(file, flags, mode, fileOpened);
|
|
6686
6775
|
});
|
|
6687
6776
|
} else {
|
|
6688
|
-
|
|
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 = () =>
|
|
6730
|
-
fsWrite = () =>
|
|
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
|
|
6828
|
+
return fs46.writeSync(this.fd, this._writingBuf);
|
|
6740
6829
|
}
|
|
6741
|
-
return
|
|
6830
|
+
return fs46.writeSync(this.fd, this._writingBuf, "utf8");
|
|
6742
6831
|
};
|
|
6743
6832
|
fsWrite = () => {
|
|
6744
6833
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
6745
|
-
return
|
|
6834
|
+
return fs46.write(this.fd, this._writingBuf, this.release);
|
|
6746
6835
|
}
|
|
6747
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) ?
|
|
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
|
-
|
|
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 =
|
|
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) ?
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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(
|
|
10315
|
-
var last =
|
|
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:
|
|
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:
|
|
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
|
|
10863
|
+
const path58 = typeof rec.path === "string" ? rec.path : null;
|
|
10775
10864
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
10776
|
-
if (!
|
|
10777
|
-
const entry = { path:
|
|
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
|
|
11697
|
+
const path58 = typeof rec.path === "string" ? rec.path : null;
|
|
11609
11698
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
11610
|
-
if (!
|
|
11611
|
-
const out = { path:
|
|
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(
|
|
23401
|
-
var parts =
|
|
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(
|
|
29255
|
-
if (
|
|
29256
|
-
|
|
29343
|
+
var parentFolder = function(path58) {
|
|
29344
|
+
if (path58.slice(-1) === "/") {
|
|
29345
|
+
path58 = path58.substring(0, path58.length - 1);
|
|
29257
29346
|
}
|
|
29258
|
-
var lastSlash =
|
|
29259
|
-
return lastSlash > 0 ?
|
|
29347
|
+
var lastSlash = path58.lastIndexOf("/");
|
|
29348
|
+
return lastSlash > 0 ? path58.substring(0, lastSlash) : "";
|
|
29260
29349
|
};
|
|
29261
|
-
var forceTrailingSlash = function(
|
|
29262
|
-
if (
|
|
29263
|
-
|
|
29350
|
+
var forceTrailingSlash = function(path58) {
|
|
29351
|
+
if (path58.slice(-1) !== "/") {
|
|
29352
|
+
path58 += "/";
|
|
29264
29353
|
}
|
|
29265
|
-
return
|
|
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
|
|
30232
|
-
if (!(this instanceof
|
|
30233
|
-
return new
|
|
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
|
|
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
|
-
|
|
30252
|
-
|
|
30253
|
-
|
|
30254
|
-
|
|
30255
|
-
|
|
30256
|
-
|
|
30257
|
-
return new
|
|
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
|
-
|
|
30260
|
-
module2.exports =
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
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)(
|
|
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,
|
|
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
|
-
|
|
30528
|
-
|
|
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
|
|
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
|
|
@@ -35581,7 +35670,7 @@ function scanAgentsDir(dir, source, seen, out) {
|
|
|
35581
35670
|
}
|
|
35582
35671
|
}
|
|
35583
35672
|
function scanPluginAgentsTree(root, pluginName, seen, out) {
|
|
35584
|
-
function
|
|
35673
|
+
function walk2(dir, namespaces) {
|
|
35585
35674
|
let entries;
|
|
35586
35675
|
try {
|
|
35587
35676
|
entries = import_node_fs12.default.readdirSync(dir, { withFileTypes: true });
|
|
@@ -35591,7 +35680,7 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
|
|
|
35591
35680
|
for (const ent of entries) {
|
|
35592
35681
|
const childPath = import_node_path13.default.join(dir, ent.name);
|
|
35593
35682
|
if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
|
|
35594
|
-
|
|
35683
|
+
walk2(childPath, [...namespaces, ent.name]);
|
|
35595
35684
|
continue;
|
|
35596
35685
|
}
|
|
35597
35686
|
if (!ent.name.endsWith(".md")) continue;
|
|
@@ -35611,7 +35700,7 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
|
|
|
35611
35700
|
});
|
|
35612
35701
|
}
|
|
35613
35702
|
}
|
|
35614
|
-
|
|
35703
|
+
walk2(root, []);
|
|
35615
35704
|
}
|
|
35616
35705
|
function readInstalledPlugins2(home) {
|
|
35617
35706
|
const pluginsDir = import_node_path13.default.join(home, ".claude", "plugins");
|
|
@@ -36350,7 +36439,8 @@ var LocalWsServer = class {
|
|
|
36350
36439
|
if (verdict !== "pass") {
|
|
36351
36440
|
if (!wasAuthed && authGate.isAuthed(client.id)) {
|
|
36352
36441
|
try {
|
|
36353
|
-
const
|
|
36442
|
+
const principalKind = this.clients.get(client.id)?.ctx?.principal.kind;
|
|
36443
|
+
const full = this.opts.readyFrameBuilder({ remoteAddress, principalKind });
|
|
36354
36444
|
this.safeSend(this.clients.get(client.id).ws, { type: "ready", ...full });
|
|
36355
36445
|
} catch (err) {
|
|
36356
36446
|
this.logger?.warn("post-auth ready frame build failed", { err: err.message });
|
|
@@ -40357,7 +40447,430 @@ function buildAttachmentHandlers(deps) {
|
|
|
40357
40447
|
}
|
|
40358
40448
|
|
|
40359
40449
|
// src/handlers/extension.ts
|
|
40450
|
+
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
40451
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
40360
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
|
+
}
|
|
40361
40874
|
function assertOwner(ctx) {
|
|
40362
40875
|
if (!ctx || ctx.principal.kind !== "owner") {
|
|
40363
40876
|
throw new ClawdError(ERROR_CODES.FORBIDDEN, "owner only");
|
|
@@ -40412,21 +40925,183 @@ function buildExtensionHandlers(deps) {
|
|
|
40412
40925
|
await deps.uninstall({ root: deps.root, extId, runtime: deps.runtime });
|
|
40413
40926
|
return { response: { ok: true } };
|
|
40414
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
|
+
};
|
|
40415
41083
|
return {
|
|
40416
41084
|
"extension.list": list,
|
|
40417
41085
|
"extension.open": open,
|
|
40418
41086
|
"extension.close": close,
|
|
40419
|
-
"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
|
|
40420
41095
|
};
|
|
40421
41096
|
}
|
|
40422
41097
|
|
|
40423
41098
|
// src/extension/registry.ts
|
|
40424
|
-
var
|
|
40425
|
-
var
|
|
41099
|
+
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
41100
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
40426
41101
|
async function loadAll(root) {
|
|
40427
41102
|
let entries;
|
|
40428
41103
|
try {
|
|
40429
|
-
entries = await
|
|
41104
|
+
entries = await import_promises8.default.readdir(root, { withFileTypes: true });
|
|
40430
41105
|
} catch (e) {
|
|
40431
41106
|
if (e.code === "ENOENT") return [];
|
|
40432
41107
|
throw e;
|
|
@@ -40435,16 +41110,16 @@ async function loadAll(root) {
|
|
|
40435
41110
|
for (const ent of entries) {
|
|
40436
41111
|
if (!ent.isDirectory()) continue;
|
|
40437
41112
|
if (ent.name.startsWith(".")) continue;
|
|
40438
|
-
records.push(await loadOne(
|
|
41113
|
+
records.push(await loadOne(import_node_path39.default.join(root, ent.name), ent.name));
|
|
40439
41114
|
}
|
|
40440
41115
|
records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
|
|
40441
41116
|
return records;
|
|
40442
41117
|
}
|
|
40443
41118
|
async function loadOne(dir, dirName) {
|
|
40444
|
-
const manifestPath =
|
|
41119
|
+
const manifestPath = import_node_path39.default.join(dir, "manifest.json");
|
|
40445
41120
|
let raw;
|
|
40446
41121
|
try {
|
|
40447
|
-
raw = await
|
|
41122
|
+
raw = await import_promises8.default.readFile(manifestPath, "utf8");
|
|
40448
41123
|
} catch {
|
|
40449
41124
|
return {
|
|
40450
41125
|
extId: dirName,
|
|
@@ -40485,8 +41160,8 @@ async function loadOne(dir, dirName) {
|
|
|
40485
41160
|
}
|
|
40486
41161
|
|
|
40487
41162
|
// src/extension/uninstall.ts
|
|
40488
|
-
var
|
|
40489
|
-
var
|
|
41163
|
+
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
41164
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
40490
41165
|
var UninstallError = class extends Error {
|
|
40491
41166
|
constructor(code, message) {
|
|
40492
41167
|
super(message);
|
|
@@ -40495,22 +41170,14 @@ var UninstallError = class extends Error {
|
|
|
40495
41170
|
code;
|
|
40496
41171
|
};
|
|
40497
41172
|
async function uninstall(deps) {
|
|
40498
|
-
const dir =
|
|
41173
|
+
const dir = import_node_path40.default.join(deps.root, deps.extId);
|
|
40499
41174
|
try {
|
|
40500
|
-
await
|
|
41175
|
+
await import_promises9.default.access(dir);
|
|
40501
41176
|
} catch {
|
|
40502
41177
|
throw new UninstallError("NOT_FOUND", `extension ${deps.extId} not installed`);
|
|
40503
41178
|
}
|
|
40504
41179
|
await deps.runtime.close(deps.extId);
|
|
40505
|
-
await
|
|
40506
|
-
}
|
|
40507
|
-
|
|
40508
|
-
// src/extension/paths.ts
|
|
40509
|
-
var import_node_os16 = __toESM(require("os"), 1);
|
|
40510
|
-
var import_node_path36 = __toESM(require("path"), 1);
|
|
40511
|
-
function extensionsRoot(override) {
|
|
40512
|
-
const home = override ?? process.env.CLAWD_HOME ?? import_node_path36.default.join(import_node_os16.default.homedir(), ".clawd");
|
|
40513
|
-
return import_node_path36.default.join(home, "extensions");
|
|
41180
|
+
await import_promises9.default.rm(dir, { recursive: true, force: true });
|
|
40514
41181
|
}
|
|
40515
41182
|
|
|
40516
41183
|
// src/handlers/index.ts
|
|
@@ -40554,7 +41221,8 @@ function buildMethodHandlers(deps) {
|
|
|
40554
41221
|
loadAll,
|
|
40555
41222
|
runtime: deps.extensionRuntime,
|
|
40556
41223
|
uninstall,
|
|
40557
|
-
root: extensionsRoot()
|
|
41224
|
+
root: extensionsRoot(),
|
|
41225
|
+
publishedChannelStore: deps.publishedChannelStore
|
|
40558
41226
|
})
|
|
40559
41227
|
};
|
|
40560
41228
|
}
|
|
@@ -40677,7 +41345,18 @@ var METHOD_GRANT_MAP = {
|
|
|
40677
41345
|
"extension.list": ADMIN_ANY,
|
|
40678
41346
|
"extension.open": ADMIN_ANY,
|
|
40679
41347
|
"extension.close": ADMIN_ANY,
|
|
40680
|
-
"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
|
|
40681
41360
|
};
|
|
40682
41361
|
function computeGrantForFrame(method, frame) {
|
|
40683
41362
|
const rule = METHOD_GRANT_MAP[method];
|
|
@@ -40694,8 +41373,8 @@ function computeGrantForFrame(method, frame) {
|
|
|
40694
41373
|
|
|
40695
41374
|
// src/extension/runtime.ts
|
|
40696
41375
|
var import_node_child_process7 = require("child_process");
|
|
40697
|
-
var
|
|
40698
|
-
var
|
|
41376
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
41377
|
+
var import_promises10 = require("timers/promises");
|
|
40699
41378
|
|
|
40700
41379
|
// src/extension/port-allocator.ts
|
|
40701
41380
|
var import_node_net = __toESM(require("net"), 1);
|
|
@@ -40791,7 +41470,7 @@ var Runtime = class {
|
|
|
40791
41470
|
/\$CLAWOS_EXT_PORT/g,
|
|
40792
41471
|
String(port)
|
|
40793
41472
|
);
|
|
40794
|
-
const dir =
|
|
41473
|
+
const dir = import_node_path41.default.join(this.root, extId);
|
|
40795
41474
|
const env = {
|
|
40796
41475
|
...process.env,
|
|
40797
41476
|
CLAWOS_EXT_PORT: String(port),
|
|
@@ -40867,7 +41546,7 @@ ${handle.stderrTail}`
|
|
|
40867
41546
|
if (res.ok) return;
|
|
40868
41547
|
} catch {
|
|
40869
41548
|
}
|
|
40870
|
-
await (0,
|
|
41549
|
+
await (0, import_promises10.setTimeout)(this.healthzIntervalMs);
|
|
40871
41550
|
}
|
|
40872
41551
|
throw new RuntimeError(
|
|
40873
41552
|
"HEALTHZ_FAILED",
|
|
@@ -40892,7 +41571,7 @@ ${handle.stderrTail}`
|
|
|
40892
41571
|
};
|
|
40893
41572
|
killGroup("SIGTERM");
|
|
40894
41573
|
const exited = new Promise((resolve6) => child.once("exit", () => resolve6()));
|
|
40895
|
-
const timed = (0,
|
|
41574
|
+
const timed = (0, import_promises10.setTimeout)(5e3).then(() => "timeout");
|
|
40896
41575
|
const winner = await Promise.race([exited.then(() => "exited"), timed]);
|
|
40897
41576
|
if (winner === "timeout" && child.exitCode === null) {
|
|
40898
41577
|
killGroup("SIGKILL");
|
|
@@ -40901,11 +41580,118 @@ ${handle.stderrTail}`
|
|
|
40901
41580
|
}
|
|
40902
41581
|
};
|
|
40903
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
|
+
|
|
40904
41690
|
// src/index.ts
|
|
40905
41691
|
async function startDaemon(config) {
|
|
40906
41692
|
const logger = createLogger({
|
|
40907
41693
|
level: config.logLevel,
|
|
40908
|
-
file:
|
|
41694
|
+
file: import_node_path43.default.join(config.dataDir, "clawd.log")
|
|
40909
41695
|
});
|
|
40910
41696
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
40911
41697
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
@@ -40992,7 +41778,7 @@ async function startDaemon(config) {
|
|
|
40992
41778
|
const agents = new AgentsScanner();
|
|
40993
41779
|
const history = new ClaudeHistoryReader();
|
|
40994
41780
|
let transport = null;
|
|
40995
|
-
const personaStore = new PersonaStore(
|
|
41781
|
+
const personaStore = new PersonaStore(import_node_path43.default.join(config.dataDir, "personas"));
|
|
40996
41782
|
const defaultsRoot = findDefaultsRoot();
|
|
40997
41783
|
if (defaultsRoot) {
|
|
40998
41784
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -41009,7 +41795,7 @@ async function startDaemon(config) {
|
|
|
41009
41795
|
getAdapter,
|
|
41010
41796
|
historyReader: history,
|
|
41011
41797
|
dataDir: config.dataDir,
|
|
41012
|
-
personaRoot:
|
|
41798
|
+
personaRoot: import_node_path43.default.join(config.dataDir, "personas"),
|
|
41013
41799
|
personaStore,
|
|
41014
41800
|
ownerDisplayName,
|
|
41015
41801
|
ownerPrincipalId,
|
|
@@ -41033,7 +41819,7 @@ async function startDaemon(config) {
|
|
|
41033
41819
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
41034
41820
|
attachmentGroup: {
|
|
41035
41821
|
onFileEdit: (input) => {
|
|
41036
|
-
const absPath =
|
|
41822
|
+
const absPath = import_node_path43.default.isAbsolute(input.relPath) ? input.relPath : import_node_path43.default.join(input.cwd, input.relPath);
|
|
41037
41823
|
let size = 0;
|
|
41038
41824
|
try {
|
|
41039
41825
|
size = import_node_fs29.default.statSync(absPath).size;
|
|
@@ -41120,6 +41906,8 @@ async function startDaemon(config) {
|
|
|
41120
41906
|
return `http://${config.host}:${config.port}`;
|
|
41121
41907
|
};
|
|
41122
41908
|
const extensionRuntime = new Runtime({ root: extensionsRoot() });
|
|
41909
|
+
const publishedChannelStore = new PublishedChannelStore(publishedChannelsFile());
|
|
41910
|
+
await publishedChannelStore.load();
|
|
41123
41911
|
const handlers = buildMethodHandlers({
|
|
41124
41912
|
manager,
|
|
41125
41913
|
workspace,
|
|
@@ -41159,10 +41947,10 @@ async function startDaemon(config) {
|
|
|
41159
41947
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
41160
41948
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
41161
41949
|
// guest path guard:candidate 必须在 personaRoot 子树下
|
|
41162
|
-
personaRoot:
|
|
41950
|
+
personaRoot: import_node_path43.default.join(config.dataDir, "personas")
|
|
41163
41951
|
},
|
|
41164
41952
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
41165
|
-
personaRoot:
|
|
41953
|
+
personaRoot: import_node_path43.default.join(config.dataDir, "personas"),
|
|
41166
41954
|
// capability:list / delete handler 依赖
|
|
41167
41955
|
capabilityManager,
|
|
41168
41956
|
// personal-cap:get 返回的 shareBaseUrl 用此 base URL:
|
|
@@ -41189,7 +41977,9 @@ async function startDaemon(config) {
|
|
|
41189
41977
|
connectRemote,
|
|
41190
41978
|
// extension runtime (v1: local-mode only). Instance owned by daemon top
|
|
41191
41979
|
// level so stopAll() runs in the shutdown hook below.
|
|
41192
|
-
extensionRuntime
|
|
41980
|
+
extensionRuntime,
|
|
41981
|
+
// extension sharing v1 — owner-side published-channels store.
|
|
41982
|
+
publishedChannelStore
|
|
41193
41983
|
});
|
|
41194
41984
|
const authResolver = new AuthContextResolver({
|
|
41195
41985
|
ownerToken: resolvedAuthToken
|
|
@@ -41374,8 +42164,8 @@ async function startDaemon(config) {
|
|
|
41374
42164
|
const lines = [
|
|
41375
42165
|
`Tunnel: ${r.url}`,
|
|
41376
42166
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
41377
|
-
`Frpc config: ${
|
|
41378
|
-
`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")}`
|
|
41379
42169
|
];
|
|
41380
42170
|
const width = Math.max(...lines.map((l) => l.length));
|
|
41381
42171
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -41388,7 +42178,7 @@ ${bar}
|
|
|
41388
42178
|
|
|
41389
42179
|
`);
|
|
41390
42180
|
try {
|
|
41391
|
-
const connectPath =
|
|
42181
|
+
const connectPath = import_node_path43.default.join(config.dataDir, "connect.txt");
|
|
41392
42182
|
import_node_fs29.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
41393
42183
|
} catch {
|
|
41394
42184
|
}
|
|
@@ -41455,7 +42245,7 @@ ${bar}
|
|
|
41455
42245
|
};
|
|
41456
42246
|
}
|
|
41457
42247
|
function migrateDropPersonsDir(dataDir) {
|
|
41458
|
-
const dir =
|
|
42248
|
+
const dir = import_node_path43.default.join(dataDir, "persons");
|
|
41459
42249
|
try {
|
|
41460
42250
|
import_node_fs29.default.rmSync(dir, { recursive: true, force: true });
|
|
41461
42251
|
} catch {
|