@clawos-dev/clawd 0.2.105-beta.199.6386646 → 0.2.105-beta.201.e3d63e9
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 +208 -132
- 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,11 +32580,12 @@ 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
|
-
// ClaudeTuiAdapter spawn 时通过
|
|
32551
|
-
//
|
|
32583
|
+
// TUI 模式:toolSessionId → @xterm/headless TerminalSurface(单一虚拟终端,popup / ready
|
|
32584
|
+
// observer 都从这一份 buffer 派生状态)。ClaudeTuiAdapter spawn 时通过 onSurfaceRegister
|
|
32585
|
+
// callback 写入;exit 时 onSurfaceUnregister 清理。
|
|
32586
|
+
// manager.resizePty 用此映射把 UI 的 session:pty:resize 同步到 surface 内部 buffer,
|
|
32552
32587
|
// 保证 snapshot serialize 时 cursor positioning 与 UI xterm 实际尺寸一致。
|
|
32553
|
-
|
|
32588
|
+
surfacesByToolSid = /* @__PURE__ */ new Map();
|
|
32554
32589
|
// 仅 mode='tui' 需要:index.ts 装配阶段在 observer 构造后注入。
|
|
32555
32590
|
// observer.onEvent 要回灌 manager.feedObserverEvents,所以 observer 必须晚于 manager
|
|
32556
32591
|
// 构造;反过来 manager 又要在 mode='tui' 下 spawn session 时 attach observer。setter 解循环
|
|
@@ -33824,18 +33859,18 @@ var SessionManager = class {
|
|
|
33824
33859
|
if (!tsid) return void 0;
|
|
33825
33860
|
return this.ptyTransports.get(tsid);
|
|
33826
33861
|
}
|
|
33827
|
-
/** ClaudeTuiAdapter spawn 时注册
|
|
33828
|
-
|
|
33829
|
-
this.
|
|
33862
|
+
/** ClaudeTuiAdapter spawn 时注册 surface ref;同一 tsid 重 spawn 会覆盖 */
|
|
33863
|
+
registerSurface(toolSessionId, surface) {
|
|
33864
|
+
this.surfacesByToolSid.set(toolSessionId, surface);
|
|
33830
33865
|
}
|
|
33831
33866
|
/** spawn 进程 exit 时清理 */
|
|
33832
|
-
|
|
33833
|
-
this.
|
|
33867
|
+
unregisterSurface(toolSessionId) {
|
|
33868
|
+
this.surfacesByToolSid.delete(toolSessionId);
|
|
33834
33869
|
}
|
|
33835
33870
|
/**
|
|
33836
|
-
* 同时 resize pty 子进程和
|
|
33837
|
-
* 顺序:pty.resize 在前,
|
|
33838
|
-
*
|
|
33871
|
+
* 同时 resize pty 子进程和 surface buffer:UI session:pty:resize 入口走这个。
|
|
33872
|
+
* 顺序:pty.resize 在前,surface.resize 在后 —— pty 是真正的 CC 子进程视图,必须先成功;
|
|
33873
|
+
* surface 只是 daemon 端镜像,任一步失败都返 false。
|
|
33839
33874
|
* 找不到 runner / toolSessionId / pty 任一缺失 → 返 false,handler 静默给 UI 报 ok=false 不抛错。
|
|
33840
33875
|
*/
|
|
33841
33876
|
resizePty(sessionId, cols, rows) {
|
|
@@ -33850,8 +33885,8 @@ var SessionManager = class {
|
|
|
33850
33885
|
} catch {
|
|
33851
33886
|
return false;
|
|
33852
33887
|
}
|
|
33853
|
-
const
|
|
33854
|
-
if (
|
|
33888
|
+
const surface = this.surfacesByToolSid.get(tsid);
|
|
33889
|
+
if (surface) surface.resize(cols, rows);
|
|
33855
33890
|
return true;
|
|
33856
33891
|
}
|
|
33857
33892
|
/**
|
|
@@ -35149,59 +35184,30 @@ var POPUP_FOOTER_PATTERNS = [
|
|
|
35149
35184
|
{ kind: "permission", regex: /Tab to amend|Do you want to proceed/i },
|
|
35150
35185
|
{ kind: "question", regex: /Enter to select|↑\/↓ to navigate/i }
|
|
35151
35186
|
];
|
|
35152
|
-
function
|
|
35187
|
+
function createTerminalSurface(opts) {
|
|
35153
35188
|
const term = new Terminal({
|
|
35154
|
-
cols: opts
|
|
35155
|
-
rows: opts
|
|
35156
|
-
scrollback: 1e3,
|
|
35189
|
+
cols: opts?.cols ?? 120,
|
|
35190
|
+
rows: opts?.rows ?? 40,
|
|
35191
|
+
scrollback: opts?.scrollback ?? 1e3,
|
|
35157
35192
|
allowProposedApi: true
|
|
35158
35193
|
});
|
|
35159
35194
|
const serializeAddon = new D();
|
|
35160
35195
|
term.loadAddon(serializeAddon);
|
|
35161
|
-
let visible = null;
|
|
35162
|
-
let pendingClear = null;
|
|
35163
35196
|
let parseSeq = 0;
|
|
35164
35197
|
let pendingParse = 0;
|
|
35165
35198
|
let drainWaiters = [];
|
|
35166
35199
|
let disposed = false;
|
|
35167
|
-
const
|
|
35200
|
+
const observers = [];
|
|
35201
|
+
const collectLines = () => {
|
|
35168
35202
|
const buf = term.buffer.active;
|
|
35203
|
+
const lines = new Array(buf.length);
|
|
35169
35204
|
for (let i = 0; i < buf.length; i++) {
|
|
35170
35205
|
const line = buf.getLine(i);
|
|
35171
|
-
|
|
35172
|
-
const text = line.translateToString(true);
|
|
35173
|
-
if (!text) continue;
|
|
35174
|
-
for (const p2 of POPUP_FOOTER_PATTERNS) {
|
|
35175
|
-
if (p2.regex.test(text)) return p2.kind;
|
|
35176
|
-
}
|
|
35177
|
-
}
|
|
35178
|
-
return null;
|
|
35179
|
-
};
|
|
35180
|
-
const tick = () => {
|
|
35181
|
-
const matched = scanFooter();
|
|
35182
|
-
if (matched && visible === null) {
|
|
35183
|
-
visible = matched;
|
|
35184
|
-
opts.onTransition(matched, true);
|
|
35185
|
-
} else if (matched && visible !== null) {
|
|
35186
|
-
if (pendingClear) {
|
|
35187
|
-
clearTimeout(pendingClear);
|
|
35188
|
-
pendingClear = null;
|
|
35189
|
-
}
|
|
35190
|
-
} else if (!matched && visible !== null && !pendingClear) {
|
|
35191
|
-
const toClear = visible;
|
|
35192
|
-
pendingClear = setTimeout(() => {
|
|
35193
|
-
pendingClear = null;
|
|
35194
|
-
if (visible === toClear) {
|
|
35195
|
-
visible = null;
|
|
35196
|
-
opts.onTransition(toClear, false);
|
|
35197
|
-
}
|
|
35198
|
-
}, opts.clearQuietMs);
|
|
35206
|
+
lines[i] = line ? line.translateToString(true) : "";
|
|
35199
35207
|
}
|
|
35208
|
+
return lines;
|
|
35200
35209
|
};
|
|
35201
35210
|
return {
|
|
35202
|
-
get visibleKind() {
|
|
35203
|
-
return visible;
|
|
35204
|
-
},
|
|
35205
35211
|
get parseSeq() {
|
|
35206
35212
|
return parseSeq;
|
|
35207
35213
|
},
|
|
@@ -35215,7 +35221,10 @@ function createPopupDetector(opts) {
|
|
|
35215
35221
|
}
|
|
35216
35222
|
if (typeof seq === "number" && seq > parseSeq) parseSeq = seq;
|
|
35217
35223
|
pendingParse--;
|
|
35218
|
-
|
|
35224
|
+
if (observers.length > 0) {
|
|
35225
|
+
const lines = collectLines();
|
|
35226
|
+
for (const cb of [...observers]) cb(lines);
|
|
35227
|
+
}
|
|
35219
35228
|
if (pendingParse === 0 && drainWaiters.length > 0) {
|
|
35220
35229
|
const waiters = drainWaiters;
|
|
35221
35230
|
drainWaiters = [];
|
|
@@ -35237,42 +35246,93 @@ function createPopupDetector(opts) {
|
|
|
35237
35246
|
} catch {
|
|
35238
35247
|
}
|
|
35239
35248
|
},
|
|
35249
|
+
serialize() {
|
|
35250
|
+
return serializeAddon.serialize({ scrollback: 1e3 });
|
|
35251
|
+
},
|
|
35240
35252
|
dispose() {
|
|
35241
35253
|
disposed = true;
|
|
35242
|
-
|
|
35243
|
-
clearTimeout(pendingClear);
|
|
35244
|
-
pendingClear = null;
|
|
35245
|
-
}
|
|
35254
|
+
pendingParse = 0;
|
|
35246
35255
|
const waiters = drainWaiters;
|
|
35247
35256
|
drainWaiters = [];
|
|
35248
35257
|
for (const w2 of waiters) w2();
|
|
35258
|
+
observers.length = 0;
|
|
35249
35259
|
serializeAddon.dispose();
|
|
35250
35260
|
term.dispose();
|
|
35251
35261
|
},
|
|
35252
|
-
|
|
35253
|
-
|
|
35262
|
+
onTick(cb) {
|
|
35263
|
+
observers.push(cb);
|
|
35264
|
+
return () => {
|
|
35265
|
+
const i = observers.indexOf(cb);
|
|
35266
|
+
if (i >= 0) observers.splice(i, 1);
|
|
35267
|
+
};
|
|
35254
35268
|
}
|
|
35255
35269
|
};
|
|
35256
35270
|
}
|
|
35257
|
-
function
|
|
35258
|
-
|
|
35259
|
-
|
|
35260
|
-
|
|
35261
|
-
|
|
35262
|
-
|
|
35271
|
+
function observePopup(surface, opts) {
|
|
35272
|
+
let visible = null;
|
|
35273
|
+
let pendingClear = null;
|
|
35274
|
+
const scanFooter = (lines) => {
|
|
35275
|
+
for (const text of lines) {
|
|
35276
|
+
if (!text) continue;
|
|
35277
|
+
for (const p2 of POPUP_FOOTER_PATTERNS) {
|
|
35278
|
+
if (p2.regex.test(text)) return p2.kind;
|
|
35279
|
+
}
|
|
35280
|
+
}
|
|
35281
|
+
return null;
|
|
35282
|
+
};
|
|
35283
|
+
const unsubscribe = surface.onTick((lines) => {
|
|
35284
|
+
const matched = scanFooter(lines);
|
|
35285
|
+
const inputFrameVisible = opts.getInputFrameVisible();
|
|
35286
|
+
if (visible === null) {
|
|
35287
|
+
if (matched && !inputFrameVisible) {
|
|
35288
|
+
visible = matched;
|
|
35289
|
+
opts.onTransition(matched, true);
|
|
35290
|
+
}
|
|
35291
|
+
return;
|
|
35292
|
+
}
|
|
35293
|
+
if (!inputFrameVisible) {
|
|
35294
|
+
if (pendingClear) {
|
|
35295
|
+
clearTimeout(pendingClear);
|
|
35296
|
+
pendingClear = null;
|
|
35297
|
+
}
|
|
35298
|
+
return;
|
|
35299
|
+
}
|
|
35300
|
+
if (!pendingClear) {
|
|
35301
|
+
const toClear = visible;
|
|
35302
|
+
pendingClear = setTimeout(() => {
|
|
35303
|
+
pendingClear = null;
|
|
35304
|
+
if (visible === toClear) {
|
|
35305
|
+
visible = null;
|
|
35306
|
+
opts.onTransition(toClear, false);
|
|
35307
|
+
}
|
|
35308
|
+
}, opts.clearQuietMs);
|
|
35309
|
+
}
|
|
35263
35310
|
});
|
|
35311
|
+
return {
|
|
35312
|
+
get visibleKind() {
|
|
35313
|
+
return visible;
|
|
35314
|
+
},
|
|
35315
|
+
dispose() {
|
|
35316
|
+
unsubscribe();
|
|
35317
|
+
if (pendingClear) {
|
|
35318
|
+
clearTimeout(pendingClear);
|
|
35319
|
+
pendingClear = null;
|
|
35320
|
+
}
|
|
35321
|
+
}
|
|
35322
|
+
};
|
|
35323
|
+
}
|
|
35324
|
+
function observeReady(surface, opts) {
|
|
35325
|
+
let prevReady = false;
|
|
35264
35326
|
let quietTimer = null;
|
|
35265
|
-
let
|
|
35266
|
-
|
|
35267
|
-
|
|
35327
|
+
let unsubscribed = false;
|
|
35328
|
+
let latestLines = [];
|
|
35329
|
+
const scanReadyFrame = (lines) => {
|
|
35268
35330
|
let prevHasDivider = false;
|
|
35269
|
-
for (
|
|
35270
|
-
|
|
35271
|
-
if (!line) {
|
|
35331
|
+
for (const text of lines) {
|
|
35332
|
+
if (!text) {
|
|
35272
35333
|
prevHasDivider = false;
|
|
35273
35334
|
continue;
|
|
35274
35335
|
}
|
|
35275
|
-
const text = line.translateToString(true);
|
|
35276
35336
|
const hasPrompt = /^❯/.test(text);
|
|
35277
35337
|
const hasDivider = /─{20,}/.test(text);
|
|
35278
35338
|
if (hasPrompt && prevHasDivider) return true;
|
|
@@ -35295,18 +35355,18 @@ function createReadyDetector(opts) {
|
|
|
35295
35355
|
};
|
|
35296
35356
|
const quietFire = () => {
|
|
35297
35357
|
quietTimer = null;
|
|
35298
|
-
if (
|
|
35299
|
-
if (!scanReadyFrame()) {
|
|
35358
|
+
if (unsubscribed) return;
|
|
35359
|
+
if (!scanReadyFrame(latestLines)) {
|
|
35300
35360
|
dbg({ event: "quiet-fire-but-not-ready-anymore" });
|
|
35301
35361
|
return;
|
|
35302
35362
|
}
|
|
35303
35363
|
dbg({ event: "quiet-fire \u2192 onReady" });
|
|
35304
35364
|
opts.onReady();
|
|
35305
35365
|
};
|
|
35306
|
-
|
|
35307
|
-
|
|
35308
|
-
|
|
35309
|
-
const ready = scanReadyFrame();
|
|
35366
|
+
const unsubscribe = surface.onTick((lines) => {
|
|
35367
|
+
if (unsubscribed) return;
|
|
35368
|
+
latestLines = lines;
|
|
35369
|
+
const ready = scanReadyFrame(lines);
|
|
35310
35370
|
if (!ready) {
|
|
35311
35371
|
clearQuiet();
|
|
35312
35372
|
if (prevReady) {
|
|
@@ -35321,19 +35381,15 @@ function createReadyDetector(opts) {
|
|
|
35321
35381
|
}
|
|
35322
35382
|
clearQuiet();
|
|
35323
35383
|
quietTimer = setTimeout(quietFire, opts.quietMs);
|
|
35324
|
-
};
|
|
35384
|
+
});
|
|
35325
35385
|
return {
|
|
35326
|
-
|
|
35327
|
-
|
|
35328
|
-
term.write(data, () => {
|
|
35329
|
-
if (disposed) return;
|
|
35330
|
-
tick();
|
|
35331
|
-
});
|
|
35386
|
+
get inputFrameVisible() {
|
|
35387
|
+
return prevReady;
|
|
35332
35388
|
},
|
|
35333
35389
|
dispose() {
|
|
35334
|
-
|
|
35390
|
+
unsubscribed = true;
|
|
35391
|
+
unsubscribe();
|
|
35335
35392
|
clearQuiet();
|
|
35336
|
-
term.dispose();
|
|
35337
35393
|
}
|
|
35338
35394
|
};
|
|
35339
35395
|
}
|
|
@@ -35415,7 +35471,18 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35415
35471
|
tag: ctx.toolSessionId ?? "no-tsid"
|
|
35416
35472
|
});
|
|
35417
35473
|
const bootGate = createBootGate(pty, this.tuiLogger);
|
|
35418
|
-
const
|
|
35474
|
+
const surface = createTerminalSurface();
|
|
35475
|
+
const readyObserver = observeReady(surface, {
|
|
35476
|
+
quietMs: 500,
|
|
35477
|
+
onReady: () => {
|
|
35478
|
+
if (!ctx.toolSessionId || !this.tuiOpts.onReady) return;
|
|
35479
|
+
this.tuiLogger?.debug("ready-detected", { toolSessionId: ctx.toolSessionId });
|
|
35480
|
+
this.tuiOpts.onReady(ctx.toolSessionId);
|
|
35481
|
+
},
|
|
35482
|
+
// [RG-DBG] 排查 ReadyGate 偶现卡输入用;定位完跟所有 [RG] 日志一起删
|
|
35483
|
+
logger: this.tuiLogger
|
|
35484
|
+
});
|
|
35485
|
+
const popupObserver = observePopup(surface, {
|
|
35419
35486
|
clearQuietMs: 200,
|
|
35420
35487
|
onTransition: (kind, visible) => {
|
|
35421
35488
|
if (!ctx.toolSessionId || !this.tuiOpts.onPopupTransition) return;
|
|
@@ -35425,29 +35492,21 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35425
35492
|
visible
|
|
35426
35493
|
});
|
|
35427
35494
|
this.tuiOpts.onPopupTransition(ctx.toolSessionId, kind, visible);
|
|
35428
|
-
}
|
|
35429
|
-
});
|
|
35430
|
-
const readyDetector = createReadyDetector({
|
|
35431
|
-
quietMs: 500,
|
|
35432
|
-
onReady: () => {
|
|
35433
|
-
if (!ctx.toolSessionId || !this.tuiOpts.onReady) return;
|
|
35434
|
-
this.tuiLogger?.debug("ready-detected", { toolSessionId: ctx.toolSessionId });
|
|
35435
|
-
this.tuiOpts.onReady(ctx.toolSessionId);
|
|
35436
35495
|
},
|
|
35437
|
-
//
|
|
35438
|
-
|
|
35496
|
+
// popup_close 信号源:ready observer 扫到 ❯ + ─{20,} 输入框结构 → 输入框可见 → popup 已关
|
|
35497
|
+
getInputFrameVisible: () => readyObserver.inputFrameVisible
|
|
35439
35498
|
});
|
|
35440
|
-
if (ctx.toolSessionId && this.tuiOpts.
|
|
35441
|
-
this.tuiOpts.
|
|
35499
|
+
if (ctx.toolSessionId && this.tuiOpts.onSurfaceRegister) {
|
|
35500
|
+
this.tuiOpts.onSurfaceRegister(ctx.toolSessionId, surface);
|
|
35442
35501
|
}
|
|
35443
35502
|
let chunkSeq = 0;
|
|
35444
35503
|
if (ctx.toolSessionId && this.tuiOpts.onPtyReplayRegister) {
|
|
35445
35504
|
this.tuiOpts.onPtyReplayRegister(ctx.toolSessionId, async () => {
|
|
35446
|
-
await
|
|
35505
|
+
await surface.drain();
|
|
35447
35506
|
return {
|
|
35448
|
-
data:
|
|
35449
|
-
popupKind:
|
|
35450
|
-
atSeq:
|
|
35507
|
+
data: surface.serialize(),
|
|
35508
|
+
popupKind: popupObserver.visibleKind,
|
|
35509
|
+
atSeq: surface.parseSeq
|
|
35451
35510
|
};
|
|
35452
35511
|
});
|
|
35453
35512
|
}
|
|
@@ -35464,8 +35523,7 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35464
35523
|
this.tuiOpts.onPtyData(ctx.toolSessionId, merged.toString("base64"), seq);
|
|
35465
35524
|
}
|
|
35466
35525
|
bootGate.feedBytes(merged);
|
|
35467
|
-
void
|
|
35468
|
-
readyDetector.writeBytes(merged.toString("utf8"));
|
|
35526
|
+
void surface.writeBytes(merged.toString("utf8"), seq);
|
|
35469
35527
|
if (ctx.toolSessionId) {
|
|
35470
35528
|
probePtyDataThrottled(ctx.toolSessionId, merged.length, merged);
|
|
35471
35529
|
}
|
|
@@ -35483,14 +35541,15 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35483
35541
|
flushCoalesced();
|
|
35484
35542
|
}
|
|
35485
35543
|
bootGate.dispose();
|
|
35486
|
-
|
|
35487
|
-
|
|
35488
|
-
|
|
35544
|
+
readyObserver.dispose();
|
|
35545
|
+
popupObserver.dispose();
|
|
35546
|
+
if (ctx.toolSessionId && this.tuiOpts.onSurfaceUnregister) {
|
|
35547
|
+
this.tuiOpts.onSurfaceUnregister(ctx.toolSessionId);
|
|
35489
35548
|
}
|
|
35490
35549
|
if (ctx.toolSessionId && this.tuiOpts.onPtyUnregister) {
|
|
35491
35550
|
this.tuiOpts.onPtyUnregister(ctx.toolSessionId);
|
|
35492
35551
|
}
|
|
35493
|
-
|
|
35552
|
+
surface.dispose();
|
|
35494
35553
|
});
|
|
35495
35554
|
return ptyChild;
|
|
35496
35555
|
}
|
|
@@ -37766,12 +37825,6 @@ async function importZip(buf, root) {
|
|
|
37766
37825
|
} catch (e) {
|
|
37767
37826
|
throw new ImportError("INVALID_MANIFEST", `manifest.json: ${e.message}`);
|
|
37768
37827
|
}
|
|
37769
|
-
if (manifestJson && typeof manifestJson === "object" && "entry" in manifestJson && manifestJson.entry?.url !== void 0) {
|
|
37770
|
-
throw new ImportError(
|
|
37771
|
-
"REMOTE_NOT_SUPPORTED",
|
|
37772
|
-
"entry.url (remote mode) is not supported in v1"
|
|
37773
|
-
);
|
|
37774
|
-
}
|
|
37775
37828
|
const result = ExtensionManifestSchema.safeParse(manifestJson);
|
|
37776
37829
|
if (!result.success) {
|
|
37777
37830
|
throw new ImportError(
|
|
@@ -37780,6 +37833,16 @@ async function importZip(buf, root) {
|
|
|
37780
37833
|
);
|
|
37781
37834
|
}
|
|
37782
37835
|
const manifest = result.data;
|
|
37836
|
+
if (manifestMode(manifest) === "hosted") {
|
|
37837
|
+
const filenames = Object.keys(zip.files).filter((n) => !zip.files[n].dir);
|
|
37838
|
+
const extras = filenames.filter((n) => n !== "manifest.json");
|
|
37839
|
+
if (extras.length > 0) {
|
|
37840
|
+
throw new ImportError(
|
|
37841
|
+
"HOSTED_ZIP_EXTRA_FILES",
|
|
37842
|
+
`hosted extension may only contain manifest.json (extra: ${extras.join(", ")})`
|
|
37843
|
+
);
|
|
37844
|
+
}
|
|
37845
|
+
}
|
|
37783
37846
|
const destDir = import_node_path19.default.join(root, manifest.id);
|
|
37784
37847
|
let destExists = false;
|
|
37785
37848
|
try {
|
|
@@ -40240,6 +40303,7 @@ function buildReadyFrame(deps, client) {
|
|
|
40240
40303
|
fileSharing.httpBaseUrl = httpBaseUrl;
|
|
40241
40304
|
if (deps.httpToken) fileSharing.httpToken = deps.httpToken;
|
|
40242
40305
|
}
|
|
40306
|
+
const daemonSource = process.env.CLAWD_DAEMON_SOURCE === "ota" ? "ota" : "packaged";
|
|
40243
40307
|
return {
|
|
40244
40308
|
version,
|
|
40245
40309
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -40249,6 +40313,7 @@ function buildReadyFrame(deps, client) {
|
|
|
40249
40313
|
runningSessions: info.runningSessions,
|
|
40250
40314
|
tunnelUrl,
|
|
40251
40315
|
mode: deps.mode,
|
|
40316
|
+
daemonSource,
|
|
40252
40317
|
...fileSharing
|
|
40253
40318
|
};
|
|
40254
40319
|
}
|
|
@@ -40941,10 +41006,17 @@ function pickExtId(frame) {
|
|
|
40941
41006
|
}
|
|
40942
41007
|
function composeState(rec, running, crashed, getPort) {
|
|
40943
41008
|
if (rec.state === "invalid") return rec;
|
|
41009
|
+
if (manifestMode(rec.manifest) === "hosted") {
|
|
41010
|
+
return {
|
|
41011
|
+
...rec,
|
|
41012
|
+
state: "running",
|
|
41013
|
+
target: { kind: "hosted", url: rec.manifest.entry.url }
|
|
41014
|
+
};
|
|
41015
|
+
}
|
|
40944
41016
|
if (running.has(rec.extId)) {
|
|
40945
41017
|
const port = getPort(rec.extId);
|
|
40946
41018
|
if (port == null) return rec;
|
|
40947
|
-
return { ...rec, state: "running", port };
|
|
41019
|
+
return { ...rec, state: "running", target: { kind: "local", port } };
|
|
40948
41020
|
}
|
|
40949
41021
|
if (crashed.has(rec.extId)) return { ...rec, state: "crashed" };
|
|
40950
41022
|
return rec;
|
|
@@ -40966,8 +41038,8 @@ function buildExtensionHandlers(deps) {
|
|
|
40966
41038
|
const open = async (frame, _client, ctx) => {
|
|
40967
41039
|
assertOwner(ctx);
|
|
40968
41040
|
const extId = pickExtId(frame);
|
|
40969
|
-
const
|
|
40970
|
-
return { response:
|
|
41041
|
+
const target = await deps.runtime.open(extId);
|
|
41042
|
+
return { response: target };
|
|
40971
41043
|
};
|
|
40972
41044
|
const close = async (frame, _client, ctx) => {
|
|
40973
41045
|
assertOwner(ctx);
|
|
@@ -41526,16 +41598,20 @@ var Runtime = class {
|
|
|
41526
41598
|
}
|
|
41527
41599
|
async open(extId) {
|
|
41528
41600
|
const existing = this.handles.get(extId);
|
|
41529
|
-
if (existing) return { port: existing.port };
|
|
41601
|
+
if (existing) return { kind: "local", port: existing.port };
|
|
41530
41602
|
const records = await loadAll(this.root);
|
|
41531
41603
|
const rec = records.find((r) => r.extId === extId);
|
|
41532
41604
|
if (!rec) throw new RuntimeError("NOT_FOUND", `extension ${extId} not installed`);
|
|
41533
41605
|
if (rec.state === "invalid")
|
|
41534
41606
|
throw new RuntimeError("INVALID_MANIFEST", rec.invalidReason);
|
|
41607
|
+
if (manifestMode(rec.manifest) === "hosted") {
|
|
41608
|
+
return { kind: "hosted", url: rec.manifest.entry.url };
|
|
41609
|
+
}
|
|
41535
41610
|
const port = await this.allocator.allocate().catch(() => {
|
|
41536
41611
|
throw new RuntimeError("PORT_EXHAUSTED", "no free port in configured range");
|
|
41537
41612
|
});
|
|
41538
|
-
const
|
|
41613
|
+
const startCommand = rec.manifest.runtime.startCommand;
|
|
41614
|
+
const cmd = startCommand.replace(
|
|
41539
41615
|
/\$CLAWOS_EXT_PORT/g,
|
|
41540
41616
|
String(port)
|
|
41541
41617
|
);
|
|
@@ -41573,7 +41649,7 @@ var Runtime = class {
|
|
|
41573
41649
|
try {
|
|
41574
41650
|
await this.waitForHealthz(port, () => earlyExit, handle);
|
|
41575
41651
|
this.crashed.delete(extId);
|
|
41576
|
-
return { port };
|
|
41652
|
+
return { kind: "local", port };
|
|
41577
41653
|
} catch (e) {
|
|
41578
41654
|
if (this.handles.get(extId) === handle) {
|
|
41579
41655
|
this.handles.delete(extId);
|
|
@@ -41988,8 +42064,8 @@ async function startDaemon(config) {
|
|
|
41988
42064
|
onPtyData: (tsid, b64, seq) => manager.emitPtyChunk(tsid, b64, seq),
|
|
41989
42065
|
onPopupTransition: (tsid, kind, visible) => manager.emitPopupTransition(tsid, kind, visible),
|
|
41990
42066
|
onPtyReplayRegister: (tsid, replay) => manager.registerPtyReplay(tsid, replay),
|
|
41991
|
-
|
|
41992
|
-
|
|
42067
|
+
onSurfaceRegister: (tsid, surface) => manager.registerSurface(tsid, surface),
|
|
42068
|
+
onSurfaceUnregister: (tsid) => manager.unregisterSurface(tsid),
|
|
41993
42069
|
// ReadyGate v2:ReadyDetector emit ready 时投递 reducer 'ready-detected' input
|
|
41994
42070
|
onReady: (tsid) => manager.dispatchReadyDetected(tsid)
|
|
41995
42071
|
}) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
|
package/package.json
CHANGED