@clawos-dev/clawd 0.2.99-beta.187.c87213f → 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 +953 -177
- 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
|
|
@@ -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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
},
|
|
35011
|
+
}, 120);
|
|
34928
35012
|
} else {
|
|
34929
|
-
|
|
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
|
-
|
|
35258
|
+
console.debug("[RG] det-state", { event: "quiet-fire-but-not-ready-anymore" });
|
|
35182
35259
|
return;
|
|
35183
35260
|
}
|
|
35184
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
40439
|
-
var
|
|
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
|
|
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(
|
|
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 =
|
|
41119
|
+
const manifestPath = import_node_path39.default.join(dir, "manifest.json");
|
|
40459
41120
|
let raw;
|
|
40460
41121
|
try {
|
|
40461
|
-
raw = await
|
|
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
|
|
40503
|
-
var
|
|
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 =
|
|
41173
|
+
const dir = import_node_path40.default.join(deps.root, deps.extId);
|
|
40513
41174
|
try {
|
|
40514
|
-
await
|
|
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
|
|
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
|
|
40712
|
-
var
|
|
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 =
|
|
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,
|
|
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,
|
|
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:
|
|
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(
|
|
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:
|
|
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 =
|
|
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:
|
|
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:
|
|
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: ${
|
|
41392
|
-
`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 =
|
|
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 =
|
|
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 {
|