@clawos-dev/clawd 0.2.102 → 0.2.103-beta.198.c756b33
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 +181 -157
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -5367,6 +5367,20 @@ var init_received_capability = __esm({
|
|
|
5367
5367
|
});
|
|
5368
5368
|
|
|
5369
5369
|
// ../protocol/src/extension.ts
|
|
5370
|
+
function isAllowedHostedUrl(raw) {
|
|
5371
|
+
let u;
|
|
5372
|
+
try {
|
|
5373
|
+
u = new URL(raw);
|
|
5374
|
+
} catch {
|
|
5375
|
+
return false;
|
|
5376
|
+
}
|
|
5377
|
+
if (u.protocol === "https:") return true;
|
|
5378
|
+
if (u.protocol === "http:" && LOCALHOST_HOSTS.has(u.hostname)) return true;
|
|
5379
|
+
return false;
|
|
5380
|
+
}
|
|
5381
|
+
function manifestMode(m2) {
|
|
5382
|
+
return m2.entry?.url ? "hosted" : "local";
|
|
5383
|
+
}
|
|
5370
5384
|
function ownerNamespace(ownerPrincipalId) {
|
|
5371
5385
|
let h = 2166136261;
|
|
5372
5386
|
for (let i = 0; i < ownerPrincipalId.length; i++) {
|
|
@@ -5408,7 +5422,7 @@ function parseOrThrow(v2) {
|
|
|
5408
5422
|
const [a, b2, c] = v2.split(".").map((p2) => parseInt(p2, 10));
|
|
5409
5423
|
return [a, b2, c];
|
|
5410
5424
|
}
|
|
5411
|
-
var EXT_ID_REGEX, ExtensionStateSchema, ExtensionManifestSchema, ExtensionRecordSchema, PublishedSnapshotSchema, ChannelRefSchema, SourceRefSchema, PublishedExtensionRecordSchema, PublishCheckSchema, PublishStatusResultSchema;
|
|
5425
|
+
var EXT_ID_REGEX, ExtensionStateSchema, LOCALHOST_HOSTS, ExtensionManifestSchema, ExtensionTargetSchema, ExtensionRecordSchema, PublishedSnapshotSchema, ChannelRefSchema, SourceRefSchema, PublishedExtensionRecordSchema, PublishCheckSchema, PublishStatusResultSchema;
|
|
5412
5426
|
var init_extension = __esm({
|
|
5413
5427
|
"../protocol/src/extension.ts"() {
|
|
5414
5428
|
"use strict";
|
|
@@ -5421,18 +5435,38 @@ var init_extension = __esm({
|
|
|
5421
5435
|
"crashed",
|
|
5422
5436
|
"invalid"
|
|
5423
5437
|
]);
|
|
5438
|
+
LOCALHOST_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1"]);
|
|
5424
5439
|
ExtensionManifestSchema = external_exports.object({
|
|
5425
5440
|
id: external_exports.string().regex(EXT_ID_REGEX),
|
|
5426
5441
|
name: external_exports.string().min(1),
|
|
5427
5442
|
version: external_exports.string().min(1),
|
|
5428
5443
|
apiVersion: external_exports.literal("1"),
|
|
5444
|
+
runtime: external_exports.object({ startCommand: external_exports.string().min(1) }).passthrough().optional(),
|
|
5429
5445
|
entry: external_exports.object({
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5446
|
+
url: external_exports.string().url().refine(isAllowedHostedUrl, {
|
|
5447
|
+
message: "INVALID_URL_SCHEME: only https or http://localhost is allowed"
|
|
5448
|
+
})
|
|
5449
|
+
}).strict().optional()
|
|
5450
|
+
}).passthrough().superRefine((m2, ctx) => {
|
|
5451
|
+
const hasRuntime = !!m2.runtime;
|
|
5452
|
+
const hasUrl = !!m2.entry?.url;
|
|
5453
|
+
if (hasRuntime && hasUrl) {
|
|
5454
|
+
ctx.addIssue({
|
|
5455
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5456
|
+
message: "INVALID_MANIFEST: entry.url and runtime cannot coexist"
|
|
5457
|
+
});
|
|
5458
|
+
}
|
|
5459
|
+
if (!hasRuntime && !hasUrl) {
|
|
5460
|
+
ctx.addIssue({
|
|
5461
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5462
|
+
message: "INVALID_MANIFEST: must have either entry.url or runtime"
|
|
5463
|
+
});
|
|
5464
|
+
}
|
|
5465
|
+
});
|
|
5466
|
+
ExtensionTargetSchema = external_exports.discriminatedUnion("kind", [
|
|
5467
|
+
external_exports.object({ kind: external_exports.literal("local"), port: external_exports.number().int().min(1).max(65535) }),
|
|
5468
|
+
external_exports.object({ kind: external_exports.literal("hosted"), url: external_exports.string().url() })
|
|
5469
|
+
]);
|
|
5436
5470
|
ExtensionRecordSchema = external_exports.discriminatedUnion("state", [
|
|
5437
5471
|
external_exports.object({
|
|
5438
5472
|
extId: external_exports.string(),
|
|
@@ -5448,7 +5482,7 @@ var init_extension = __esm({
|
|
|
5448
5482
|
extId: external_exports.string(),
|
|
5449
5483
|
manifest: ExtensionManifestSchema,
|
|
5450
5484
|
state: external_exports.literal("running"),
|
|
5451
|
-
|
|
5485
|
+
target: ExtensionTargetSchema
|
|
5452
5486
|
}),
|
|
5453
5487
|
external_exports.object({
|
|
5454
5488
|
extId: external_exports.string(),
|
|
@@ -32546,12 +32580,11 @@ var SessionManager = class {
|
|
|
32546
32580
|
// 新 client 通过 session:subscribe 接入时由 onSubscribe hook 取 getPtyReplay(sessionId)
|
|
32547
32581
|
// 把屏幕快照定向 emit,解决"刷新页面 / 第二个 tab 接入时 xterm 白屏"
|
|
32548
32582
|
ptyReplaysByToolSid = /* @__PURE__ */ new Map();
|
|
32549
|
-
// TUI 模式:toolSessionId → @xterm/headless
|
|
32550
|
-
//
|
|
32551
|
-
//
|
|
32552
|
-
// manager.resizePty 用此映射把 UI 的 session:pty:resize 同步到 surface 内部 buffer,
|
|
32583
|
+
// TUI 模式:toolSessionId → @xterm/headless PopupDetector。
|
|
32584
|
+
// ClaudeTuiAdapter spawn 时通过 onDetectorRegister callback 写入;exit 时 onDetectorUnregister 清理。
|
|
32585
|
+
// manager.resizePty 用此映射把 UI 的 session:pty:resize 同步到 detector 内部 buffer,
|
|
32553
32586
|
// 保证 snapshot serialize 时 cursor positioning 与 UI xterm 实际尺寸一致。
|
|
32554
|
-
|
|
32587
|
+
detectorsByToolSid = /* @__PURE__ */ new Map();
|
|
32555
32588
|
// 仅 mode='tui' 需要:index.ts 装配阶段在 observer 构造后注入。
|
|
32556
32589
|
// observer.onEvent 要回灌 manager.feedObserverEvents,所以 observer 必须晚于 manager
|
|
32557
32590
|
// 构造;反过来 manager 又要在 mode='tui' 下 spawn session 时 attach observer。setter 解循环
|
|
@@ -33825,18 +33858,18 @@ var SessionManager = class {
|
|
|
33825
33858
|
if (!tsid) return void 0;
|
|
33826
33859
|
return this.ptyTransports.get(tsid);
|
|
33827
33860
|
}
|
|
33828
|
-
/** ClaudeTuiAdapter spawn 时注册
|
|
33829
|
-
|
|
33830
|
-
this.
|
|
33861
|
+
/** ClaudeTuiAdapter spawn 时注册 detector ref;同一 tsid 重 spawn 会覆盖 */
|
|
33862
|
+
registerDetector(toolSessionId, detector) {
|
|
33863
|
+
this.detectorsByToolSid.set(toolSessionId, detector);
|
|
33831
33864
|
}
|
|
33832
33865
|
/** spawn 进程 exit 时清理 */
|
|
33833
|
-
|
|
33834
|
-
this.
|
|
33866
|
+
unregisterDetector(toolSessionId) {
|
|
33867
|
+
this.detectorsByToolSid.delete(toolSessionId);
|
|
33835
33868
|
}
|
|
33836
33869
|
/**
|
|
33837
|
-
* 同时 resize pty 子进程和
|
|
33838
|
-
* 顺序:pty.resize 在前,
|
|
33839
|
-
*
|
|
33870
|
+
* 同时 resize pty 子进程和 detector buffer:UI session:pty:resize 入口走这个。
|
|
33871
|
+
* 顺序:pty.resize 在前,detector.resize 在后 —— pty 是真正的 CC 子进程视图,必须先成功;
|
|
33872
|
+
* detector 只是 daemon 端镜像,任一步失败都返 false。
|
|
33840
33873
|
* 找不到 runner / toolSessionId / pty 任一缺失 → 返 false,handler 静默给 UI 报 ok=false 不抛错。
|
|
33841
33874
|
*/
|
|
33842
33875
|
resizePty(sessionId, cols, rows) {
|
|
@@ -33851,8 +33884,8 @@ var SessionManager = class {
|
|
|
33851
33884
|
} catch {
|
|
33852
33885
|
return false;
|
|
33853
33886
|
}
|
|
33854
|
-
const
|
|
33855
|
-
if (
|
|
33887
|
+
const detector = this.detectorsByToolSid.get(tsid);
|
|
33888
|
+
if (detector) detector.resize(cols, rows);
|
|
33856
33889
|
return true;
|
|
33857
33890
|
}
|
|
33858
33891
|
/**
|
|
@@ -35150,30 +35183,59 @@ var POPUP_FOOTER_PATTERNS = [
|
|
|
35150
35183
|
{ kind: "permission", regex: /Tab to amend|Do you want to proceed/i },
|
|
35151
35184
|
{ kind: "question", regex: /Enter to select|↑\/↓ to navigate/i }
|
|
35152
35185
|
];
|
|
35153
|
-
function
|
|
35186
|
+
function createPopupDetector(opts) {
|
|
35154
35187
|
const term = new Terminal({
|
|
35155
|
-
cols: opts
|
|
35156
|
-
rows: opts
|
|
35157
|
-
scrollback:
|
|
35188
|
+
cols: opts.cols ?? 120,
|
|
35189
|
+
rows: opts.rows ?? 40,
|
|
35190
|
+
scrollback: 1e3,
|
|
35158
35191
|
allowProposedApi: true
|
|
35159
35192
|
});
|
|
35160
35193
|
const serializeAddon = new D();
|
|
35161
35194
|
term.loadAddon(serializeAddon);
|
|
35195
|
+
let visible = null;
|
|
35196
|
+
let pendingClear = null;
|
|
35162
35197
|
let parseSeq = 0;
|
|
35163
35198
|
let pendingParse = 0;
|
|
35164
35199
|
let drainWaiters = [];
|
|
35165
35200
|
let disposed = false;
|
|
35166
|
-
const
|
|
35167
|
-
const collectLines = () => {
|
|
35201
|
+
const scanFooter = () => {
|
|
35168
35202
|
const buf = term.buffer.active;
|
|
35169
|
-
const lines = new Array(buf.length);
|
|
35170
35203
|
for (let i = 0; i < buf.length; i++) {
|
|
35171
35204
|
const line = buf.getLine(i);
|
|
35172
|
-
|
|
35205
|
+
if (!line) continue;
|
|
35206
|
+
const text = line.translateToString(true);
|
|
35207
|
+
if (!text) continue;
|
|
35208
|
+
for (const p2 of POPUP_FOOTER_PATTERNS) {
|
|
35209
|
+
if (p2.regex.test(text)) return p2.kind;
|
|
35210
|
+
}
|
|
35211
|
+
}
|
|
35212
|
+
return null;
|
|
35213
|
+
};
|
|
35214
|
+
const tick = () => {
|
|
35215
|
+
const matched = scanFooter();
|
|
35216
|
+
if (matched && visible === null) {
|
|
35217
|
+
visible = matched;
|
|
35218
|
+
opts.onTransition(matched, true);
|
|
35219
|
+
} else if (matched && visible !== null) {
|
|
35220
|
+
if (pendingClear) {
|
|
35221
|
+
clearTimeout(pendingClear);
|
|
35222
|
+
pendingClear = null;
|
|
35223
|
+
}
|
|
35224
|
+
} else if (!matched && visible !== null && !pendingClear) {
|
|
35225
|
+
const toClear = visible;
|
|
35226
|
+
pendingClear = setTimeout(() => {
|
|
35227
|
+
pendingClear = null;
|
|
35228
|
+
if (visible === toClear) {
|
|
35229
|
+
visible = null;
|
|
35230
|
+
opts.onTransition(toClear, false);
|
|
35231
|
+
}
|
|
35232
|
+
}, opts.clearQuietMs);
|
|
35173
35233
|
}
|
|
35174
|
-
return lines;
|
|
35175
35234
|
};
|
|
35176
35235
|
return {
|
|
35236
|
+
get visibleKind() {
|
|
35237
|
+
return visible;
|
|
35238
|
+
},
|
|
35177
35239
|
get parseSeq() {
|
|
35178
35240
|
return parseSeq;
|
|
35179
35241
|
},
|
|
@@ -35187,10 +35249,7 @@ function createTerminalSurface(opts) {
|
|
|
35187
35249
|
}
|
|
35188
35250
|
if (typeof seq === "number" && seq > parseSeq) parseSeq = seq;
|
|
35189
35251
|
pendingParse--;
|
|
35190
|
-
|
|
35191
|
-
const lines = collectLines();
|
|
35192
|
-
for (const cb of [...observers]) cb(lines);
|
|
35193
|
-
}
|
|
35252
|
+
tick();
|
|
35194
35253
|
if (pendingParse === 0 && drainWaiters.length > 0) {
|
|
35195
35254
|
const waiters = drainWaiters;
|
|
35196
35255
|
drainWaiters = [];
|
|
@@ -35212,93 +35271,42 @@ function createTerminalSurface(opts) {
|
|
|
35212
35271
|
} catch {
|
|
35213
35272
|
}
|
|
35214
35273
|
},
|
|
35215
|
-
serialize() {
|
|
35216
|
-
return serializeAddon.serialize({ scrollback: 1e3 });
|
|
35217
|
-
},
|
|
35218
35274
|
dispose() {
|
|
35219
35275
|
disposed = true;
|
|
35220
|
-
|
|
35276
|
+
if (pendingClear) {
|
|
35277
|
+
clearTimeout(pendingClear);
|
|
35278
|
+
pendingClear = null;
|
|
35279
|
+
}
|
|
35221
35280
|
const waiters = drainWaiters;
|
|
35222
35281
|
drainWaiters = [];
|
|
35223
35282
|
for (const w2 of waiters) w2();
|
|
35224
|
-
observers.length = 0;
|
|
35225
35283
|
serializeAddon.dispose();
|
|
35226
35284
|
term.dispose();
|
|
35227
35285
|
},
|
|
35228
|
-
|
|
35229
|
-
|
|
35230
|
-
return () => {
|
|
35231
|
-
const i = observers.indexOf(cb);
|
|
35232
|
-
if (i >= 0) observers.splice(i, 1);
|
|
35233
|
-
};
|
|
35286
|
+
serialize() {
|
|
35287
|
+
return serializeAddon.serialize({ scrollback: 1e3 });
|
|
35234
35288
|
}
|
|
35235
35289
|
};
|
|
35236
35290
|
}
|
|
35237
|
-
function
|
|
35238
|
-
|
|
35239
|
-
|
|
35240
|
-
|
|
35241
|
-
|
|
35242
|
-
|
|
35243
|
-
for (const p2 of POPUP_FOOTER_PATTERNS) {
|
|
35244
|
-
if (p2.regex.test(text)) return p2.kind;
|
|
35245
|
-
}
|
|
35246
|
-
}
|
|
35247
|
-
return null;
|
|
35248
|
-
};
|
|
35249
|
-
const unsubscribe = surface.onTick((lines) => {
|
|
35250
|
-
const matched = scanFooter(lines);
|
|
35251
|
-
const inputFrameVisible = opts.getInputFrameVisible();
|
|
35252
|
-
if (visible === null) {
|
|
35253
|
-
if (matched && !inputFrameVisible) {
|
|
35254
|
-
visible = matched;
|
|
35255
|
-
opts.onTransition(matched, true);
|
|
35256
|
-
}
|
|
35257
|
-
return;
|
|
35258
|
-
}
|
|
35259
|
-
if (!inputFrameVisible) {
|
|
35260
|
-
if (pendingClear) {
|
|
35261
|
-
clearTimeout(pendingClear);
|
|
35262
|
-
pendingClear = null;
|
|
35263
|
-
}
|
|
35264
|
-
return;
|
|
35265
|
-
}
|
|
35266
|
-
if (!pendingClear) {
|
|
35267
|
-
const toClear = visible;
|
|
35268
|
-
pendingClear = setTimeout(() => {
|
|
35269
|
-
pendingClear = null;
|
|
35270
|
-
if (visible === toClear) {
|
|
35271
|
-
visible = null;
|
|
35272
|
-
opts.onTransition(toClear, false);
|
|
35273
|
-
}
|
|
35274
|
-
}, opts.clearQuietMs);
|
|
35275
|
-
}
|
|
35291
|
+
function createReadyDetector(opts) {
|
|
35292
|
+
const term = new Terminal({
|
|
35293
|
+
cols: opts.cols ?? 120,
|
|
35294
|
+
rows: opts.rows ?? 40,
|
|
35295
|
+
scrollback: 200,
|
|
35296
|
+
allowProposedApi: true
|
|
35276
35297
|
});
|
|
35277
|
-
return {
|
|
35278
|
-
get visibleKind() {
|
|
35279
|
-
return visible;
|
|
35280
|
-
},
|
|
35281
|
-
dispose() {
|
|
35282
|
-
unsubscribe();
|
|
35283
|
-
if (pendingClear) {
|
|
35284
|
-
clearTimeout(pendingClear);
|
|
35285
|
-
pendingClear = null;
|
|
35286
|
-
}
|
|
35287
|
-
}
|
|
35288
|
-
};
|
|
35289
|
-
}
|
|
35290
|
-
function observeReady(surface, opts) {
|
|
35291
|
-
let prevReady = false;
|
|
35292
35298
|
let quietTimer = null;
|
|
35293
|
-
let
|
|
35294
|
-
|
|
35295
|
-
|
|
35299
|
+
let disposed = false;
|
|
35300
|
+
const scanReadyFrame = () => {
|
|
35301
|
+
const buf = term.buffer.active;
|
|
35296
35302
|
let prevHasDivider = false;
|
|
35297
|
-
for (
|
|
35298
|
-
|
|
35303
|
+
for (let i = 0; i < buf.length; i++) {
|
|
35304
|
+
const line = buf.getLine(i);
|
|
35305
|
+
if (!line) {
|
|
35299
35306
|
prevHasDivider = false;
|
|
35300
35307
|
continue;
|
|
35301
35308
|
}
|
|
35309
|
+
const text = line.translateToString(true);
|
|
35302
35310
|
const hasPrompt = /^❯/.test(text);
|
|
35303
35311
|
const hasDivider = /─{20,}/.test(text);
|
|
35304
35312
|
if (hasPrompt && prevHasDivider) return true;
|
|
@@ -35321,18 +35329,18 @@ function observeReady(surface, opts) {
|
|
|
35321
35329
|
};
|
|
35322
35330
|
const quietFire = () => {
|
|
35323
35331
|
quietTimer = null;
|
|
35324
|
-
if (
|
|
35325
|
-
if (!scanReadyFrame(
|
|
35332
|
+
if (disposed) return;
|
|
35333
|
+
if (!scanReadyFrame()) {
|
|
35326
35334
|
dbg({ event: "quiet-fire-but-not-ready-anymore" });
|
|
35327
35335
|
return;
|
|
35328
35336
|
}
|
|
35329
35337
|
dbg({ event: "quiet-fire \u2192 onReady" });
|
|
35330
35338
|
opts.onReady();
|
|
35331
35339
|
};
|
|
35332
|
-
|
|
35333
|
-
|
|
35334
|
-
|
|
35335
|
-
const ready = scanReadyFrame(
|
|
35340
|
+
let prevReady = false;
|
|
35341
|
+
const tick = () => {
|
|
35342
|
+
if (disposed) return;
|
|
35343
|
+
const ready = scanReadyFrame();
|
|
35336
35344
|
if (!ready) {
|
|
35337
35345
|
clearQuiet();
|
|
35338
35346
|
if (prevReady) {
|
|
@@ -35347,15 +35355,19 @@ function observeReady(surface, opts) {
|
|
|
35347
35355
|
}
|
|
35348
35356
|
clearQuiet();
|
|
35349
35357
|
quietTimer = setTimeout(quietFire, opts.quietMs);
|
|
35350
|
-
}
|
|
35358
|
+
};
|
|
35351
35359
|
return {
|
|
35352
|
-
|
|
35353
|
-
return
|
|
35360
|
+
writeBytes(data) {
|
|
35361
|
+
if (disposed) return;
|
|
35362
|
+
term.write(data, () => {
|
|
35363
|
+
if (disposed) return;
|
|
35364
|
+
tick();
|
|
35365
|
+
});
|
|
35354
35366
|
},
|
|
35355
35367
|
dispose() {
|
|
35356
|
-
|
|
35357
|
-
unsubscribe();
|
|
35368
|
+
disposed = true;
|
|
35358
35369
|
clearQuiet();
|
|
35370
|
+
term.dispose();
|
|
35359
35371
|
}
|
|
35360
35372
|
};
|
|
35361
35373
|
}
|
|
@@ -35437,18 +35449,7 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35437
35449
|
tag: ctx.toolSessionId ?? "no-tsid"
|
|
35438
35450
|
});
|
|
35439
35451
|
const bootGate = createBootGate(pty, this.tuiLogger);
|
|
35440
|
-
const
|
|
35441
|
-
const readyObserver = observeReady(surface, {
|
|
35442
|
-
quietMs: 500,
|
|
35443
|
-
onReady: () => {
|
|
35444
|
-
if (!ctx.toolSessionId || !this.tuiOpts.onReady) return;
|
|
35445
|
-
this.tuiLogger?.debug("ready-detected", { toolSessionId: ctx.toolSessionId });
|
|
35446
|
-
this.tuiOpts.onReady(ctx.toolSessionId);
|
|
35447
|
-
},
|
|
35448
|
-
// [RG-DBG] 排查 ReadyGate 偶现卡输入用;定位完跟所有 [RG] 日志一起删
|
|
35449
|
-
logger: this.tuiLogger
|
|
35450
|
-
});
|
|
35451
|
-
const popupObserver = observePopup(surface, {
|
|
35452
|
+
const detector = createPopupDetector({
|
|
35452
35453
|
clearQuietMs: 200,
|
|
35453
35454
|
onTransition: (kind, visible) => {
|
|
35454
35455
|
if (!ctx.toolSessionId || !this.tuiOpts.onPopupTransition) return;
|
|
@@ -35458,21 +35459,29 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35458
35459
|
visible
|
|
35459
35460
|
});
|
|
35460
35461
|
this.tuiOpts.onPopupTransition(ctx.toolSessionId, kind, visible);
|
|
35462
|
+
}
|
|
35463
|
+
});
|
|
35464
|
+
const readyDetector = createReadyDetector({
|
|
35465
|
+
quietMs: 500,
|
|
35466
|
+
onReady: () => {
|
|
35467
|
+
if (!ctx.toolSessionId || !this.tuiOpts.onReady) return;
|
|
35468
|
+
this.tuiLogger?.debug("ready-detected", { toolSessionId: ctx.toolSessionId });
|
|
35469
|
+
this.tuiOpts.onReady(ctx.toolSessionId);
|
|
35461
35470
|
},
|
|
35462
|
-
//
|
|
35463
|
-
|
|
35471
|
+
// [RG-DBG] 排查 ReadyGate 偶现卡输入用;定位完跟所有 [RG] 日志一起删
|
|
35472
|
+
logger: this.tuiLogger
|
|
35464
35473
|
});
|
|
35465
|
-
if (ctx.toolSessionId && this.tuiOpts.
|
|
35466
|
-
this.tuiOpts.
|
|
35474
|
+
if (ctx.toolSessionId && this.tuiOpts.onDetectorRegister) {
|
|
35475
|
+
this.tuiOpts.onDetectorRegister(ctx.toolSessionId, detector);
|
|
35467
35476
|
}
|
|
35468
35477
|
let chunkSeq = 0;
|
|
35469
35478
|
if (ctx.toolSessionId && this.tuiOpts.onPtyReplayRegister) {
|
|
35470
35479
|
this.tuiOpts.onPtyReplayRegister(ctx.toolSessionId, async () => {
|
|
35471
|
-
await
|
|
35480
|
+
await detector.drain();
|
|
35472
35481
|
return {
|
|
35473
|
-
data:
|
|
35474
|
-
popupKind:
|
|
35475
|
-
atSeq:
|
|
35482
|
+
data: detector.serialize(),
|
|
35483
|
+
popupKind: detector.visibleKind,
|
|
35484
|
+
atSeq: detector.parseSeq
|
|
35476
35485
|
};
|
|
35477
35486
|
});
|
|
35478
35487
|
}
|
|
@@ -35489,7 +35498,8 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35489
35498
|
this.tuiOpts.onPtyData(ctx.toolSessionId, merged.toString("base64"), seq);
|
|
35490
35499
|
}
|
|
35491
35500
|
bootGate.feedBytes(merged);
|
|
35492
|
-
void
|
|
35501
|
+
void detector.writeBytes(merged.toString("utf8"), seq);
|
|
35502
|
+
readyDetector.writeBytes(merged.toString("utf8"));
|
|
35493
35503
|
if (ctx.toolSessionId) {
|
|
35494
35504
|
probePtyDataThrottled(ctx.toolSessionId, merged.length, merged);
|
|
35495
35505
|
}
|
|
@@ -35507,15 +35517,14 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35507
35517
|
flushCoalesced();
|
|
35508
35518
|
}
|
|
35509
35519
|
bootGate.dispose();
|
|
35510
|
-
|
|
35511
|
-
|
|
35512
|
-
|
|
35513
|
-
this.tuiOpts.onSurfaceUnregister(ctx.toolSessionId);
|
|
35520
|
+
readyDetector.dispose();
|
|
35521
|
+
if (ctx.toolSessionId && this.tuiOpts.onDetectorUnregister) {
|
|
35522
|
+
this.tuiOpts.onDetectorUnregister(ctx.toolSessionId);
|
|
35514
35523
|
}
|
|
35515
35524
|
if (ctx.toolSessionId && this.tuiOpts.onPtyUnregister) {
|
|
35516
35525
|
this.tuiOpts.onPtyUnregister(ctx.toolSessionId);
|
|
35517
35526
|
}
|
|
35518
|
-
|
|
35527
|
+
detector.dispose();
|
|
35519
35528
|
});
|
|
35520
35529
|
return ptyChild;
|
|
35521
35530
|
}
|
|
@@ -37791,12 +37800,6 @@ async function importZip(buf, root) {
|
|
|
37791
37800
|
} catch (e) {
|
|
37792
37801
|
throw new ImportError("INVALID_MANIFEST", `manifest.json: ${e.message}`);
|
|
37793
37802
|
}
|
|
37794
|
-
if (manifestJson && typeof manifestJson === "object" && "entry" in manifestJson && manifestJson.entry?.url !== void 0) {
|
|
37795
|
-
throw new ImportError(
|
|
37796
|
-
"REMOTE_NOT_SUPPORTED",
|
|
37797
|
-
"entry.url (remote mode) is not supported in v1"
|
|
37798
|
-
);
|
|
37799
|
-
}
|
|
37800
37803
|
const result = ExtensionManifestSchema.safeParse(manifestJson);
|
|
37801
37804
|
if (!result.success) {
|
|
37802
37805
|
throw new ImportError(
|
|
@@ -37805,6 +37808,16 @@ async function importZip(buf, root) {
|
|
|
37805
37808
|
);
|
|
37806
37809
|
}
|
|
37807
37810
|
const manifest = result.data;
|
|
37811
|
+
if (manifestMode(manifest) === "hosted") {
|
|
37812
|
+
const filenames = Object.keys(zip.files).filter((n) => !zip.files[n].dir);
|
|
37813
|
+
const extras = filenames.filter((n) => n !== "manifest.json");
|
|
37814
|
+
if (extras.length > 0) {
|
|
37815
|
+
throw new ImportError(
|
|
37816
|
+
"HOSTED_ZIP_EXTRA_FILES",
|
|
37817
|
+
`hosted extension may only contain manifest.json (extra: ${extras.join(", ")})`
|
|
37818
|
+
);
|
|
37819
|
+
}
|
|
37820
|
+
}
|
|
37808
37821
|
const destDir = import_node_path19.default.join(root, manifest.id);
|
|
37809
37822
|
let destExists = false;
|
|
37810
37823
|
try {
|
|
@@ -40963,10 +40976,17 @@ function pickExtId(frame) {
|
|
|
40963
40976
|
}
|
|
40964
40977
|
function composeState(rec, running, crashed, getPort) {
|
|
40965
40978
|
if (rec.state === "invalid") return rec;
|
|
40979
|
+
if (manifestMode(rec.manifest) === "hosted") {
|
|
40980
|
+
return {
|
|
40981
|
+
...rec,
|
|
40982
|
+
state: "running",
|
|
40983
|
+
target: { kind: "hosted", url: rec.manifest.entry.url }
|
|
40984
|
+
};
|
|
40985
|
+
}
|
|
40966
40986
|
if (running.has(rec.extId)) {
|
|
40967
40987
|
const port = getPort(rec.extId);
|
|
40968
40988
|
if (port == null) return rec;
|
|
40969
|
-
return { ...rec, state: "running", port };
|
|
40989
|
+
return { ...rec, state: "running", target: { kind: "local", port } };
|
|
40970
40990
|
}
|
|
40971
40991
|
if (crashed.has(rec.extId)) return { ...rec, state: "crashed" };
|
|
40972
40992
|
return rec;
|
|
@@ -40988,8 +41008,8 @@ function buildExtensionHandlers(deps) {
|
|
|
40988
41008
|
const open = async (frame, _client, ctx) => {
|
|
40989
41009
|
assertOwner(ctx);
|
|
40990
41010
|
const extId = pickExtId(frame);
|
|
40991
|
-
const
|
|
40992
|
-
return { response:
|
|
41011
|
+
const target = await deps.runtime.open(extId);
|
|
41012
|
+
return { response: target };
|
|
40993
41013
|
};
|
|
40994
41014
|
const close = async (frame, _client, ctx) => {
|
|
40995
41015
|
assertOwner(ctx);
|
|
@@ -41539,16 +41559,20 @@ var Runtime = class {
|
|
|
41539
41559
|
}
|
|
41540
41560
|
async open(extId) {
|
|
41541
41561
|
const existing = this.handles.get(extId);
|
|
41542
|
-
if (existing) return { port: existing.port };
|
|
41562
|
+
if (existing) return { kind: "local", port: existing.port };
|
|
41543
41563
|
const records = await loadAll(this.root);
|
|
41544
41564
|
const rec = records.find((r) => r.extId === extId);
|
|
41545
41565
|
if (!rec) throw new RuntimeError("NOT_FOUND", `extension ${extId} not installed`);
|
|
41546
41566
|
if (rec.state === "invalid")
|
|
41547
41567
|
throw new RuntimeError("INVALID_MANIFEST", rec.invalidReason);
|
|
41568
|
+
if (manifestMode(rec.manifest) === "hosted") {
|
|
41569
|
+
return { kind: "hosted", url: rec.manifest.entry.url };
|
|
41570
|
+
}
|
|
41548
41571
|
const port = await this.allocator.allocate().catch(() => {
|
|
41549
41572
|
throw new RuntimeError("PORT_EXHAUSTED", "no free port in configured range");
|
|
41550
41573
|
});
|
|
41551
|
-
const
|
|
41574
|
+
const startCommand = rec.manifest.runtime.startCommand;
|
|
41575
|
+
const cmd = startCommand.replace(
|
|
41552
41576
|
/\$CLAWOS_EXT_PORT/g,
|
|
41553
41577
|
String(port)
|
|
41554
41578
|
);
|
|
@@ -41586,7 +41610,7 @@ var Runtime = class {
|
|
|
41586
41610
|
try {
|
|
41587
41611
|
await this.waitForHealthz(port, () => earlyExit, handle);
|
|
41588
41612
|
this.crashed.delete(extId);
|
|
41589
|
-
return { port };
|
|
41613
|
+
return { kind: "local", port };
|
|
41590
41614
|
} catch (e) {
|
|
41591
41615
|
if (this.handles.get(extId) === handle) {
|
|
41592
41616
|
this.handles.delete(extId);
|
|
@@ -41968,8 +41992,8 @@ async function startDaemon(config) {
|
|
|
41968
41992
|
onPtyData: (tsid, b64, seq) => manager.emitPtyChunk(tsid, b64, seq),
|
|
41969
41993
|
onPopupTransition: (tsid, kind, visible) => manager.emitPopupTransition(tsid, kind, visible),
|
|
41970
41994
|
onPtyReplayRegister: (tsid, replay) => manager.registerPtyReplay(tsid, replay),
|
|
41971
|
-
|
|
41972
|
-
|
|
41995
|
+
onDetectorRegister: (tsid, detector) => manager.registerDetector(tsid, detector),
|
|
41996
|
+
onDetectorUnregister: (tsid) => manager.unregisterDetector(tsid),
|
|
41973
41997
|
// ReadyGate v2:ReadyDetector emit ready 时投递 reducer 'ready-detected' input
|
|
41974
41998
|
onReady: (tsid) => manager.dispatchReadyDetected(tsid)
|
|
41975
41999
|
}) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
|
package/package.json
CHANGED