@wrenai/genbi 0.0.2 → 0.0.3
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/README.md
CHANGED
|
@@ -25,7 +25,7 @@ the rest:
|
|
|
25
25
|
|
|
26
26
|
## Developer preview
|
|
27
27
|
|
|
28
|
-
`@wrenai/genbi` is published at `0.0.
|
|
28
|
+
`@wrenai/genbi` is published at `0.0.3`. Nothing here is under a compatibility
|
|
29
29
|
commitment yet — commands, environment variables and interfaces move without
|
|
30
30
|
notice. The Codex runtime is supported in code but has not been exercised end
|
|
31
31
|
to end; Claude is the one that is. See
|
|
@@ -95,6 +95,17 @@ export interface NativeVendorReadiness {
|
|
|
95
95
|
export interface NativeProducerReadiness {
|
|
96
96
|
readonly available: boolean;
|
|
97
97
|
readonly category?: "native_session_producer_incompatible";
|
|
98
|
+
/**
|
|
99
|
+
* Why the preflight rejected the producer. Safe to send: every field is a
|
|
100
|
+
* fixed string plus a content digest of the resolved binary — the phase name,
|
|
101
|
+
* a `result=` token, and (for a Wren runtime rejection) one of that resolver's
|
|
102
|
+
* fixed sentences. None of them interpolate a path or process output.
|
|
103
|
+
*
|
|
104
|
+
* It is here because the category alone named a state without a cause, so an
|
|
105
|
+
* operator reading the Sessions view could not tell a missing binary from an
|
|
106
|
+
* incompatible one, and neither could a bug report.
|
|
107
|
+
*/
|
|
108
|
+
readonly diagnostic?: string;
|
|
98
109
|
}
|
|
99
110
|
export interface NativePurposeReadiness {
|
|
100
111
|
readonly scopeKind: "bootstrap" | "bound_project";
|
|
@@ -196,13 +207,30 @@ export interface NativeProducerIdentity {
|
|
|
196
207
|
readonly executable: string;
|
|
197
208
|
readonly identity: string;
|
|
198
209
|
}
|
|
210
|
+
/** One vendor's own preflight outcome. A vendor nobody selected must not speak for one that was. */
|
|
211
|
+
export interface NativeProducerVendorProbe {
|
|
212
|
+
readonly available: boolean;
|
|
213
|
+
/** Browser-safe: a fixed sentence, never a path or process output. */
|
|
214
|
+
readonly reason?: string;
|
|
215
|
+
/** Fixed fields plus a content digest; never a command path or process output. */
|
|
216
|
+
readonly diagnostic: string;
|
|
217
|
+
}
|
|
199
218
|
export interface NativeProducerPreflightResult {
|
|
219
|
+
/** True when at least one vendor is usable — never a verdict on the vendor in use. */
|
|
200
220
|
readonly available: boolean;
|
|
201
221
|
readonly category?: typeof NATIVE_PRODUCER_CATEGORY;
|
|
202
222
|
/** Server-only resolved executable and content identity for the eventual launch. */
|
|
203
223
|
readonly producer?: NativeProducerIdentity;
|
|
204
224
|
/** Server-only: fixed fields plus a content digest; never a command path or process output. */
|
|
205
225
|
readonly diagnostic: string;
|
|
226
|
+
/**
|
|
227
|
+
* Per-vendor outcomes. The preflight probes every vendor so that readiness can
|
|
228
|
+
* answer for the one actually configured: the Codex leg requires a native Wren
|
|
229
|
+
* runtime the resolver can trace to the server's own repository, which an
|
|
230
|
+
* installed package can never satisfy, and a single boolean over both vendors
|
|
231
|
+
* let that failure take Claude down with it.
|
|
232
|
+
*/
|
|
233
|
+
readonly vendors: Readonly<Record<NativeVendor, NativeProducerVendorProbe>>;
|
|
206
234
|
}
|
|
207
235
|
/**
|
|
208
236
|
* Exercises the exact, file-only native dispatch boundary against the
|
|
@@ -310,6 +310,24 @@ function configuredIrPath(irPath) {
|
|
|
310
310
|
return false;
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Why a vendor's preflight rejected the producer. Each is a fixed sentence
|
|
315
|
+
* chosen here — none is built from a path, a command, or process output — so
|
|
316
|
+
* `nativeSessionLaunchFailure` may pass them through unchanged.
|
|
317
|
+
*/
|
|
318
|
+
const NATIVE_PRODUCER_REASONS = new Set([
|
|
319
|
+
"the Warble binary could not be resolved",
|
|
320
|
+
"the Warble binary could not be identified",
|
|
321
|
+
"the Warble binary could not be started",
|
|
322
|
+
"the Warble binary did not respond",
|
|
323
|
+
"the Warble binary rejected the dispatch probe",
|
|
324
|
+
"the Warble binary does not support the native session flags this build requires",
|
|
325
|
+
"the Warble binary could not be started for this vendor",
|
|
326
|
+
"the Warble binary did not respond for this vendor",
|
|
327
|
+
"Warble could not dispatch this profile for this vendor",
|
|
328
|
+
"Warble emitted a launch spec this build cannot run",
|
|
329
|
+
"the preflight could not create its temporary workspace",
|
|
330
|
+
]);
|
|
313
331
|
/** A deliberately small public classification; never expose process or filesystem detail. */
|
|
314
332
|
export function nativeSessionLaunchFailure(error) {
|
|
315
333
|
const message = error instanceof Error ? error.message : "";
|
|
@@ -319,8 +337,15 @@ export function nativeSessionLaunchFailure(error) {
|
|
|
319
337
|
return message;
|
|
320
338
|
if (message === "native session materialization prerequisites are unavailable")
|
|
321
339
|
return message;
|
|
322
|
-
|
|
323
|
-
|
|
340
|
+
// Every NativeWrenRuntimeError message is `native Wren runtime is unavailable: `
|
|
341
|
+
// followed by one of that resolver's fixed literals; not one of them
|
|
342
|
+
// interpolates a path or process output, so the whole family may pass through.
|
|
343
|
+
// Matching the bare sentence, as this did, stopped matching anything once the
|
|
344
|
+
// resolver started naming which link in the chain broke.
|
|
345
|
+
if (message.startsWith("native Wren runtime is unavailable"))
|
|
346
|
+
return message;
|
|
347
|
+
if (NATIVE_PRODUCER_REASONS.has(message))
|
|
348
|
+
return message;
|
|
324
349
|
if (message === "native session producer is incompatible")
|
|
325
350
|
return message;
|
|
326
351
|
if (message === "native session launch artifacts are unavailable" || message === "native session launch specification is invalid" || message === "native session launch specification is incompatible")
|
|
@@ -348,12 +373,25 @@ const NATIVE_HELP_MARKERS = [
|
|
|
348
373
|
"--native-mcp",
|
|
349
374
|
"--out",
|
|
350
375
|
];
|
|
351
|
-
function
|
|
376
|
+
function producerDiagnostic(identity, phase, detail) {
|
|
377
|
+
return `identity=${identity ?? "unavailable"} phase=${phase} ${detail}`;
|
|
378
|
+
}
|
|
379
|
+
/** A failure before any vendor-specific work rules out every vendor, for the same reason. */
|
|
380
|
+
function everyVendorUnavailable(probe) {
|
|
381
|
+
const vendors = {};
|
|
382
|
+
for (const vendor of NATIVE_PROBE_TARGETS)
|
|
383
|
+
vendors[vendor] = probe;
|
|
384
|
+
return vendors;
|
|
385
|
+
}
|
|
386
|
+
/** A shared-phase outcome: it decided nothing vendor-specific, so it speaks for all of them. */
|
|
387
|
+
function nativeProducerResult(available, identity, phase, detail, reason, producer) {
|
|
388
|
+
const diagnostic = producerDiagnostic(identity, phase, detail);
|
|
352
389
|
return {
|
|
353
390
|
available,
|
|
354
391
|
...(available ? {} : { category: NATIVE_PRODUCER_CATEGORY }),
|
|
355
|
-
...(
|
|
356
|
-
diagnostic
|
|
392
|
+
...(producer ? { producer } : {}),
|
|
393
|
+
diagnostic,
|
|
394
|
+
vendors: everyVendorUnavailable(available ? { available: true, diagnostic } : { available: false, reason, diagnostic }),
|
|
357
395
|
};
|
|
358
396
|
}
|
|
359
397
|
function executableIdentity(executable) {
|
|
@@ -439,26 +477,34 @@ function runNativeProbe(command, args) {
|
|
|
439
477
|
export async function probeNativeSessionProducer(input) {
|
|
440
478
|
const executable = localExecutablePath(input.warbleBin);
|
|
441
479
|
if (!executable)
|
|
442
|
-
return nativeProducerResult(false, undefined, "resolve", "result=binary_not_found");
|
|
480
|
+
return nativeProducerResult(false, undefined, "resolve", "result=binary_not_found", "the Warble binary could not be resolved");
|
|
443
481
|
const identity = executableIdentity(executable);
|
|
444
482
|
if (!identity)
|
|
445
|
-
return nativeProducerResult(false, undefined, "identity", "result=identity_unavailable");
|
|
483
|
+
return nativeProducerResult(false, undefined, "identity", "result=identity_unavailable", "the Warble binary could not be identified");
|
|
446
484
|
const producer = { executable, identity };
|
|
447
485
|
const help = await runNativeProbe(executable, ["dispatch", "--help"]);
|
|
448
486
|
if (help.spawnFailed)
|
|
449
|
-
return nativeProducerResult(false, identity, "dispatch_help", "result=spawn_failed");
|
|
487
|
+
return nativeProducerResult(false, identity, "dispatch_help", "result=spawn_failed", "the Warble binary could not be started");
|
|
450
488
|
if (help.timedOut)
|
|
451
|
-
return nativeProducerResult(false, identity, "dispatch_help", "result=timed_out");
|
|
489
|
+
return nativeProducerResult(false, identity, "dispatch_help", "result=timed_out", "the Warble binary did not respond");
|
|
452
490
|
if (help.exitCode !== 0)
|
|
453
|
-
return nativeProducerResult(false, identity, "dispatch_help", `result=exit_code_${help.exitCode ?? "signal"}
|
|
491
|
+
return nativeProducerResult(false, identity, "dispatch_help", `result=exit_code_${help.exitCode ?? "signal"}`, "the Warble binary rejected the dispatch probe");
|
|
454
492
|
const missingMarkers = NATIVE_HELP_MARKERS.filter((marker) => !help.output.includes(marker));
|
|
455
493
|
if (missingMarkers.length > 0)
|
|
456
|
-
return nativeProducerResult(false, identity, "dispatch_help", `result=missing_markers_${missingMarkers.join(",")}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
494
|
+
return nativeProducerResult(false, identity, "dispatch_help", `result=missing_markers_${missingMarkers.join(",")}`, "the Warble binary does not support the native session flags this build requires");
|
|
495
|
+
// Vendor is the outer loop on purpose. It used to be the inner one, and the
|
|
496
|
+
// first failing pair returned for the whole preflight — so a Codex rejection
|
|
497
|
+
// reported the producer incompatible even for a Claude-only install, which is
|
|
498
|
+
// exactly what an installed package produces.
|
|
499
|
+
const vendors = {};
|
|
500
|
+
for (const vendor of NATIVE_PROBE_TARGETS) {
|
|
501
|
+
let outcome = { available: true, diagnostic: producerDiagnostic(identity, `complete_${vendor}`, "result=compatible") };
|
|
502
|
+
for (const purpose of NATIVE_PURPOSES) {
|
|
503
|
+
const irPath = input.irPaths[purpose];
|
|
504
|
+
if (!irPath || !configuredIrPath(irPath))
|
|
505
|
+
continue;
|
|
506
|
+
const phase = `dispatch_${purpose}_${vendor}`;
|
|
507
|
+
const fail = (detail, reason) => ({ available: false, reason, diagnostic: producerDiagnostic(identity, phase, detail) });
|
|
462
508
|
let root;
|
|
463
509
|
try {
|
|
464
510
|
root = mkdtempSync(path.join(os.tmpdir(), "genbi-native-preflight-"));
|
|
@@ -478,32 +524,54 @@ export async function probeNativeSessionProducer(input) {
|
|
|
478
524
|
writeFileSync(scopePath, JSON.stringify({ version: "3", kind: NATIVE_DISPATCH_REGISTRY[purpose].scopeKind, scope_id: scopeId, cwd: outputRoot, entry: nativeEntryFor(purpose, vendor), ...(purpose === "setup" ? { bootstrap_root: bootstrapRoot } : {}), ...(wrenRuntime ? { wren_runtime: wrenRuntime } : {}), ...(binding ? { binding } : {}) }), { encoding: "utf8", mode: 0o600, flag: "wx" });
|
|
479
525
|
writeFileSync(mcpPath, JSON.stringify({ version: "1", url: "http://127.0.0.1:0/api/native-sessions/mcp", credential: "native-preflight-nonsecret" }), { encoding: "utf8", mode: 0o600, flag: "wx" });
|
|
480
526
|
const dispatched = await runNativeProbe(executable, ["dispatch", irPath, "--target", targetForProvider(vendor), "--purpose", purpose, "--native-scope", scopePath, "--native-mcp", mcpPath, "--out", outputRoot]);
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
527
|
+
if (dispatched.spawnFailed) {
|
|
528
|
+
outcome = fail("result=spawn_failed", "the Warble binary could not be started for this vendor");
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
if (dispatched.timedOut) {
|
|
532
|
+
outcome = fail("result=timed_out", "the Warble binary did not respond for this vendor");
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
if (dispatched.exitCode !== 0) {
|
|
536
|
+
outcome = fail(`result=exit_code_${dispatched.exitCode ?? "signal"}`, "Warble could not dispatch this profile for this vendor");
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
488
539
|
try {
|
|
489
540
|
readNativeLaunchSpec(outputRoot, purpose, vendor, scopeId, undefined, { version: "1", url: "http://127.0.0.1:0/api/native-sessions/mcp", credential: "native-preflight-nonsecret" });
|
|
490
541
|
}
|
|
491
542
|
catch {
|
|
492
|
-
|
|
543
|
+
outcome = fail("result=launch_spec_incompatible", "Warble emitted a launch spec this build cannot run");
|
|
544
|
+
break;
|
|
493
545
|
}
|
|
494
546
|
}
|
|
495
547
|
catch (error) {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
548
|
+
outcome = error instanceof NativeWrenRuntimeError
|
|
549
|
+
? fail(`result=wren_runtime_unavailable (${error.message})`, error.message)
|
|
550
|
+
: fail("result=preflight_workspace_unavailable", "the preflight could not create its temporary workspace");
|
|
551
|
+
break;
|
|
499
552
|
}
|
|
500
553
|
finally {
|
|
501
554
|
if (root)
|
|
502
555
|
rmSync(root, { recursive: true, force: true });
|
|
503
556
|
}
|
|
504
557
|
}
|
|
505
|
-
|
|
506
|
-
|
|
558
|
+
vendors[vendor] = outcome;
|
|
559
|
+
}
|
|
560
|
+
const usable = NATIVE_PROBE_TARGETS.filter((vendor) => vendors[vendor].available);
|
|
561
|
+
const available = usable.length > 0;
|
|
562
|
+
// When nothing is usable the summary must still carry each vendor's own cause;
|
|
563
|
+
// collapsing them to "no_compatible_vendor" would lose the only line that says
|
|
564
|
+
// what actually broke.
|
|
565
|
+
const vendorResults = NATIVE_PROBE_TARGETS
|
|
566
|
+
.map((vendor) => `${vendor}=${vendors[vendor].diagnostic.split(" result=")[1] ?? "unknown"}`)
|
|
567
|
+
.join(" ");
|
|
568
|
+
return {
|
|
569
|
+
available,
|
|
570
|
+
...(available ? {} : { category: NATIVE_PRODUCER_CATEGORY }),
|
|
571
|
+
producer,
|
|
572
|
+
diagnostic: producerDiagnostic(identity, "complete", available ? `result=compatible_${usable.join("+")}` : `result=no_compatible_vendor ${vendorResults}`),
|
|
573
|
+
vendors,
|
|
574
|
+
};
|
|
507
575
|
}
|
|
508
576
|
function parseNativeSetupRecoveryReport(value) {
|
|
509
577
|
const report = record(value);
|
|
@@ -1139,6 +1207,10 @@ export class NativeSessionService {
|
|
|
1139
1207
|
? nativeSessionStateBaseAvailableForProject(this.materializationState(), configuredRoot)
|
|
1140
1208
|
: purpose === "setup" && projectAvailable && nativeSessionStateBaseAvailable(this.materializationState());
|
|
1141
1209
|
const workspaceUnavailable = !projectAvailable || !stateAvailable;
|
|
1210
|
+
// Only the configured vendor's own probe may block this purpose. Probing
|
|
1211
|
+
// both and collapsing them into one boolean let an unselected vendor veto
|
|
1212
|
+
// the selected one.
|
|
1213
|
+
const selectedProducer = producer && dispatch ? producer.vendors[dispatch.provider] : undefined;
|
|
1142
1214
|
const reason = runtimeCorrection
|
|
1143
1215
|
?? (!dispatch && !legacyFixture
|
|
1144
1216
|
? "native sessions require a saved Runtime & authentication binding"
|
|
@@ -1146,8 +1218,8 @@ export class NativeSessionService {
|
|
|
1146
1218
|
? purpose === "setup" ? "native setup sessions require a workspace root" : "native sessions require a current bound project"
|
|
1147
1219
|
: workspaceUnavailable
|
|
1148
1220
|
? "native session workspace is unavailable"
|
|
1149
|
-
:
|
|
1150
|
-
? "native session producer is incompatible"
|
|
1221
|
+
: selectedProducer && !selectedProducer.available
|
|
1222
|
+
? selectedProducer.reason ?? "native session producer is incompatible"
|
|
1151
1223
|
: !irPath || !(this.options.dispatch !== undefined || configuredIrPath(irPath)) || !legacyProducerAvailable()
|
|
1152
1224
|
? "native session materialization prerequisites are unavailable"
|
|
1153
1225
|
: mcpReadiness && !mcpReadiness.available
|
|
@@ -1160,6 +1232,17 @@ export class NativeSessionService {
|
|
|
1160
1232
|
? `the ${executable} interactive CLI is not available on this machine`
|
|
1161
1233
|
: undefined);
|
|
1162
1234
|
const vendorReadiness = {};
|
|
1235
|
+
// Production readiness now reports every vendor, so the UI can say which
|
|
1236
|
+
// one is unusable and why instead of only that "the producer" is.
|
|
1237
|
+
if (!legacyFixture && producer)
|
|
1238
|
+
for (const vendor of NATIVE_VENDORS) {
|
|
1239
|
+
const probe = producer.vendors[vendor];
|
|
1240
|
+
vendorReadiness[vendor] = probe?.available === false
|
|
1241
|
+
? { available: false, reason: probe.reason ?? "native session producer is incompatible" }
|
|
1242
|
+
: !executableAvailable(vendor)
|
|
1243
|
+
? { available: false, reason: `the ${vendor === "claude" ? "claude" : "codex"} interactive CLI is not available on this machine` }
|
|
1244
|
+
: { available: true };
|
|
1245
|
+
}
|
|
1163
1246
|
if (legacyFixture)
|
|
1164
1247
|
for (const vendor of NATIVE_VENDORS) {
|
|
1165
1248
|
const legacyExecutable = vendor === "claude" ? "claude" : "codex";
|
|
@@ -1174,7 +1257,16 @@ export class NativeSessionService {
|
|
|
1174
1257
|
scopeKind,
|
|
1175
1258
|
profile: definition.profile,
|
|
1176
1259
|
...(dispatch ? { target: dispatch.target, targetLabel: dispatch.targetLabel } : {}),
|
|
1177
|
-
|
|
1260
|
+
// With a vendor selected this answers for that vendor; with none selected
|
|
1261
|
+
// it falls back to "is any vendor usable", rather than claiming health
|
|
1262
|
+
// no vendor could back.
|
|
1263
|
+
...(producer
|
|
1264
|
+
? {
|
|
1265
|
+
producer: (selectedProducer ? selectedProducer.available : producer.available)
|
|
1266
|
+
? { available: true, diagnostic: (selectedProducer ?? producer).diagnostic }
|
|
1267
|
+
: { available: false, category: NATIVE_PRODUCER_CATEGORY, diagnostic: (selectedProducer ?? producer).diagnostic },
|
|
1268
|
+
}
|
|
1269
|
+
: {}),
|
|
1178
1270
|
available: legacyAvailable,
|
|
1179
1271
|
...(unavailable ? { reason: unavailable } : {}),
|
|
1180
1272
|
vendors: vendorReadiness,
|
|
@@ -1222,8 +1314,10 @@ export class NativeSessionService {
|
|
|
1222
1314
|
? configuredIr
|
|
1223
1315
|
: await this.options.resolveDispatchIr(purpose, binding);
|
|
1224
1316
|
const producer = this.legacyFixtureMode() ? undefined : await this.productionProducerPreflight();
|
|
1225
|
-
|
|
1226
|
-
|
|
1317
|
+
const launchProducer = producer ? producer.vendors[dispatchDefinition.provider] : undefined;
|
|
1318
|
+
if (producer && (!launchProducer?.available || !producer.producer)) {
|
|
1319
|
+
throw new InteractiveLaunchError(launchProducer?.reason ?? "native session producer is incompatible");
|
|
1320
|
+
}
|
|
1227
1321
|
if (producer?.producer)
|
|
1228
1322
|
assertNativeProducerIdentity(producer.producer);
|
|
1229
1323
|
const scopeId = resumed?.row.scopeId ?? newId("native-scope");
|