@ggui-ai/cli 0.1.0-rc.1 → 0.2.0-alpha.1
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 +2 -2
- package/dist/cli.js +83 -32
- package/dist/generation-probe.d.ts +28 -6
- package/dist/generation-probe.d.ts.map +1 -1
- package/dist/generation-probe.js +43 -26
- package/dist/mcp-backend.d.ts +38 -41
- package/dist/mcp-backend.d.ts.map +1 -1
- package/dist/mcp-backend.js +105 -135
- package/dist/persistent-secrets.js +8 -8
- package/dist/persistent-stores.d.ts +2 -2
- package/dist/persistent-stores.d.ts.map +1 -1
- package/dist/persistent-stores.js +8 -8
- package/dist/serve-command.d.ts +15 -15
- package/dist/serve-command.d.ts.map +1 -1
- package/dist/serve-command.js +19 -19
- package/dist/serve-fallback.d.ts +1 -1
- package/package.json +17 -17
package/README.md
CHANGED
|
@@ -44,8 +44,8 @@ ggui serve --mcp-only # skip agent supervision
|
|
|
44
44
|
**Scope today:**
|
|
45
45
|
|
|
46
46
|
- ✅ Local server, viewer, cookie-authenticated WebSocket subscribe/ack all work end-to-end.
|
|
47
|
-
- ✅ `
|
|
48
|
-
- ✅ Component-code generation is wired on the OSS path via `createUiGenerator()` from `@ggui-ai/ui-gen` — the same harness the hosted runtime uses. `
|
|
47
|
+
- ✅ `ggui_render` mints shortCodes and lands on the same-origin viewer.
|
|
48
|
+
- ✅ Component-code generation is wired on the OSS path via `createUiGenerator()` from `@ggui-ai/ui-gen` — the same harness the hosted runtime uses. `ggui_render` returns `codeReady: false` only when no BYOK credentials resolve (no `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / etc.); supply a key to get full generation locally.
|
|
49
49
|
- 🔒 Default auth is dev-mode (any bearer → `builder`). Swap in a real `AuthAdapter` via `createGguiServer({ auth })` before exposing beyond `127.0.0.1`.
|
|
50
50
|
|
|
51
51
|
### `ggui dev`
|
package/dist/cli.js
CHANGED
|
@@ -14,16 +14,16 @@ import { discoverLocalUis, discoverMcpMounts, discoverPrimitives, GguiJsonLoadEr
|
|
|
14
14
|
import { resolveAgentPlan } from './serve-fallback.js';
|
|
15
15
|
import { buildAgentRuntime, describeTunnelSession, launchBrowser, openTunnel, parseDevFlags, resolveAgentCommand, resolveHubUrl, shouldAutoOpen, } from './dev-command.js';
|
|
16
16
|
import { createNullTunnelProvider, discoverTunnelProvider, } from './tunnel-provider.js';
|
|
17
|
-
import { ManifestBlueprintProvider, resolveStorageFromConfig,
|
|
17
|
+
import { ManifestBlueprintProvider, resolveStorageFromConfig, buildNoCredentialsRender, } from '@ggui-ai/mcp-server';
|
|
18
18
|
import { InMemoryBlueprintProvider } from '@ggui-ai/mcp-server-core/in-memory';
|
|
19
19
|
import { createByokResolver } from './byok-resolver.js';
|
|
20
20
|
import { isInstalledBlueprintPath } from './internal/artifact-install.js';
|
|
21
|
-
import { describeGenerationBinding, probeGenerationBinding, } from './generation-probe.js';
|
|
21
|
+
import { DEFAULT_ROUTE_BY_PROVIDER, describeGenerationBinding, probeGenerationBinding, } from './generation-probe.js';
|
|
22
22
|
import { buildMcpServerBackend, pickFreePort } from './mcp-backend.js';
|
|
23
23
|
import { createThemeWriter } from './theme-writer.js';
|
|
24
24
|
import { createThemeFileUploader } from './theme-file-uploader.js';
|
|
25
25
|
import { getPersistentDir } from './paths.js';
|
|
26
|
-
import {
|
|
26
|
+
import { createPersistentRenderStore, createPersistentVectorStore, } from './persistent-stores.js';
|
|
27
27
|
import { parseServeFlags, runServe, SERVE_HELP, } from './serve-command.js';
|
|
28
28
|
import { runLoginCommand } from './auth-login.js';
|
|
29
29
|
import { runLogoutCommand } from './auth-logout.js';
|
|
@@ -211,7 +211,7 @@ async function runServeCommand(args) {
|
|
|
211
211
|
// Resolve `ggui.json#storage` → concrete adapters BEFORE binding.
|
|
212
212
|
// Absent storage block → empty bundle → server keeps in-memory
|
|
213
213
|
// defaults. Sqlite paths resolve relative to the manifest directory
|
|
214
|
-
// (not CWD) so `"path": "./ggui-
|
|
214
|
+
// (not CWD) so `"path": "./ggui-renders.sqlite"` lands next to
|
|
215
215
|
// `ggui.json` regardless of where the operator invoked `ggui serve`
|
|
216
216
|
// from. Failure to instantiate (e.g. better-sqlite3 peer dep
|
|
217
217
|
// missing, permission denied) propagates to the exit-1 path below.
|
|
@@ -223,7 +223,7 @@ async function runServeCommand(args) {
|
|
|
223
223
|
process.stderr.write(`ggui serve: storage: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
224
224
|
return 1;
|
|
225
225
|
}
|
|
226
|
-
// Persistent default for
|
|
226
|
+
// Persistent default for RenderStore + VectorStore — layered AFTER
|
|
227
227
|
// the manifest resolver so explicit `ggui.json#storage` declarations
|
|
228
228
|
// always win, and BEFORE the banner so `describeStorageStatus`
|
|
229
229
|
// reflects the actual wired state. Without this layer, ggui-default
|
|
@@ -235,13 +235,13 @@ async function runServeCommand(args) {
|
|
|
235
235
|
// `--ephemeral` skips the whole persistent layering. Operators who
|
|
236
236
|
// pass the flag get pure in-memory defaults across the board (HMAC
|
|
237
237
|
// secrets mint fresh per process). Without the flag, the
|
|
238
|
-
// sqlite-backed VectorStore +
|
|
238
|
+
// sqlite-backed VectorStore + RenderStore persist across restarts
|
|
239
239
|
// — note this means the install-to-cache bridge can serve stale
|
|
240
240
|
// install-provenance rows after a boot if the source TSX behind a
|
|
241
241
|
// cached row has been edited or uninstalled in the meantime.
|
|
242
242
|
if (!parsed.ephemeral) {
|
|
243
243
|
const persistentDir = getPersistentDir(plan.projectRoot);
|
|
244
|
-
const
|
|
244
|
+
const manifestDeclaresRenders = plan.manifest?.storage?.renders !== undefined;
|
|
245
245
|
const manifestDeclaresVectors = plan.manifest?.storage?.vectors !== undefined;
|
|
246
246
|
// The persistent stores are SQLite-backed (`better-sqlite3`, an
|
|
247
247
|
// optional native module). When it isn't installed — or the
|
|
@@ -251,14 +251,14 @@ async function runServeCommand(args) {
|
|
|
251
251
|
// `better-sqlite3` later upgrades them to persistence. An
|
|
252
252
|
// explicit `ggui.json#storage` declaration still hard-fails (it
|
|
253
253
|
// resolved above) — the operator asked for it by name.
|
|
254
|
-
if (!
|
|
254
|
+
if (!manifestDeclaresRenders && storage.renderStore === undefined) {
|
|
255
255
|
try {
|
|
256
|
-
const
|
|
257
|
-
storage = { ...storage,
|
|
256
|
+
const renderStore = await createPersistentRenderStore(persistentDir);
|
|
257
|
+
storage = { ...storage, renderStore };
|
|
258
258
|
}
|
|
259
259
|
catch (err) {
|
|
260
|
-
process.stderr.write(`ggui serve: persistent
|
|
261
|
-
+ `(
|
|
260
|
+
process.stderr.write(`ggui serve: persistent render store unavailable — using in-memory `
|
|
261
|
+
+ `(renders reset on restart; install \`better-sqlite3\` for persistence). `
|
|
262
262
|
+ `[${err instanceof Error ? err.message : String(err)}]\n`);
|
|
263
263
|
}
|
|
264
264
|
}
|
|
@@ -423,10 +423,10 @@ async function runServeCommand(args) {
|
|
|
423
423
|
mcpMounts = narrowMcpMounts(discovery.mounts);
|
|
424
424
|
}
|
|
425
425
|
// Resolve `--port 0` BEFORE composing the backend. `createGguiServer`
|
|
426
|
-
// captures `mcpApps.
|
|
427
|
-
// we need a concrete port to emit URLs the
|
|
428
|
-
//
|
|
429
|
-
//
|
|
426
|
+
// captures `mcpApps.wsUrl` + `runtime.url` at construction time, so
|
|
427
|
+
// we need a concrete port to emit URLs the iframe can connect to.
|
|
428
|
+
// A tiny race window exists between close + bind; surface the bind
|
|
429
|
+
// failure cleanly if it loses.
|
|
430
430
|
//
|
|
431
431
|
// Resolved BEFORE the BYOK probe (vs. previous ordering) because
|
|
432
432
|
// the no-credentials fallback card needs the absolute settings URL
|
|
@@ -447,15 +447,33 @@ async function runServeCommand(args) {
|
|
|
447
447
|
// BYOK probe — boot scan + per-request resolver. The probe ALWAYS
|
|
448
448
|
// returns a `GenerationBinding` now (post-H2): when no env / global
|
|
449
449
|
// / user-scope key resolved at boot, the binding's `resolveLlm`
|
|
450
|
-
// re-runs at every
|
|
450
|
+
// re-runs at every render with `userScope: ctx.appId` (per-user keys
|
|
451
451
|
// stored in `~/.ggui/credentials.json` flip in there); when the
|
|
452
452
|
// re-run also misses, the bound `onNoCredentials` hook produces
|
|
453
|
-
// a Connect-Claude card
|
|
454
|
-
// `/settings`. Story-path `
|
|
455
|
-
// real
|
|
453
|
+
// a Connect-Claude card render pointing at THIS server's
|
|
454
|
+
// `/settings`. Story-path `ggui_render` therefore always lands a
|
|
455
|
+
// real render — either a generated component (key found) or
|
|
456
456
|
// the card (key missing).
|
|
457
457
|
const baseUrlForCard = parsed.publicBaseUrl ?? `http://${parsed.host}:${effectivePort}`;
|
|
458
458
|
const settingsUrl = `${baseUrlForCard}/settings`;
|
|
459
|
+
// No-credentials cards live for an hour from mint — long enough for
|
|
460
|
+
// the operator to follow the Connect-Claude link, paste a key, and
|
|
461
|
+
// re-prompt before the persisted render is GC'd.
|
|
462
|
+
const NO_CREDENTIALS_CARD_TTL_MS = 60 * 60 * 1000;
|
|
463
|
+
// Resolve the operator's explicit `generation.model` route, if any.
|
|
464
|
+
// The schema-side `parseAnyLlmRoute` transform yields a typed
|
|
465
|
+
// `LlmRoute` directly — no string parsing here. Bedrock is
|
|
466
|
+
// hosted-only on the OSS path (no IAM resolver chain wired); reject
|
|
467
|
+
// explicitly with a clear error rather than silently failing at
|
|
468
|
+
// dispatch time.
|
|
469
|
+
const configuredRoute = plan.manifest?.generation?.model;
|
|
470
|
+
if (configuredRoute?.provider === 'bedrock') {
|
|
471
|
+
process.stderr.write(`ggui serve: ggui.json#generation.model declared a bedrock route ` +
|
|
472
|
+
`("${configuredRoute.model}"), but the OSS BYOK resolver chain ` +
|
|
473
|
+
`does not cover AWS IAM. Bedrock is supported via the hosted ` +
|
|
474
|
+
`runtime only. Pick an anthropic/openai/google/openrouter route.\n`);
|
|
475
|
+
return 1;
|
|
476
|
+
}
|
|
459
477
|
let generationBinding;
|
|
460
478
|
try {
|
|
461
479
|
// Use whatever blueprint provider the manifest resolved, or
|
|
@@ -465,12 +483,18 @@ async function runServeCommand(args) {
|
|
|
465
483
|
generationBinding = await probeGenerationBinding({
|
|
466
484
|
resolver: createByokResolver(),
|
|
467
485
|
blueprints: blueprintsForGen,
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
486
|
+
...(configuredRoute ? { configuredRoute } : {}),
|
|
487
|
+
onNoCredentials: (ctx, story) => {
|
|
488
|
+
const nowEpochMs = Date.parse(story.nowIso);
|
|
489
|
+
return buildNoCredentialsRender({
|
|
490
|
+
renderId: story.renderId,
|
|
491
|
+
appId: ctx.appId,
|
|
492
|
+
intent: story.intent,
|
|
493
|
+
nowEpochMs,
|
|
494
|
+
expiresAt: nowEpochMs + NO_CREDENTIALS_CARD_TTL_MS,
|
|
495
|
+
settingsUrl,
|
|
496
|
+
});
|
|
497
|
+
},
|
|
474
498
|
});
|
|
475
499
|
}
|
|
476
500
|
catch (err) {
|
|
@@ -484,8 +508,35 @@ async function runServeCommand(args) {
|
|
|
484
508
|
generationBinding = await probeGenerationBinding({
|
|
485
509
|
resolver: createByokResolver(),
|
|
486
510
|
blueprints: blueprintsForGen,
|
|
511
|
+
...(configuredRoute ? { configuredRoute } : {}),
|
|
487
512
|
});
|
|
488
513
|
}
|
|
514
|
+
// Strict-fail: a key resolved at boot, but the operator never
|
|
515
|
+
// declared `generation.model` in their manifest. Silently picking
|
|
516
|
+
// the per-provider default was the surprise that produced #22 and
|
|
517
|
+
// #42 — the typed system removed the WIRE bug class, this check
|
|
518
|
+
// removes the OPERATOR bug class. (`--mcp-only` and no-manifest
|
|
519
|
+
// paths skip the check — they have no generation surface to pin.)
|
|
520
|
+
if (plan.manifest !== null &&
|
|
521
|
+
generationBinding.bootResolved &&
|
|
522
|
+
configuredRoute === undefined) {
|
|
523
|
+
process.stderr.write(`ggui serve: boot scan resolved a ${generationBinding.provider} key but ` +
|
|
524
|
+
`ggui.json#generation.model is unset. Pick the model explicitly:\n` +
|
|
525
|
+
`\n` +
|
|
526
|
+
` {\n` +
|
|
527
|
+
` "schema": "1",\n` +
|
|
528
|
+
` "generation": {\n` +
|
|
529
|
+
` "model": "${generationBinding.provider}:${DEFAULT_ROUTE_BY_PROVIDER[generationBinding.provider].model}"\n` +
|
|
530
|
+
` }\n` +
|
|
531
|
+
` }\n` +
|
|
532
|
+
`\n` +
|
|
533
|
+
`Accepted forms:\n` +
|
|
534
|
+
` - canonical: "anthropic:claude-haiku-4-5-20251001"\n` +
|
|
535
|
+
` - LiteLLM: "anthropic/claude-haiku-4-5"\n` +
|
|
536
|
+
`\n` +
|
|
537
|
+
`See docs/principles/model-string-convention.md.\n`);
|
|
538
|
+
return 1;
|
|
539
|
+
}
|
|
489
540
|
process.stdout.write(`${describeGenerationBinding(generationBinding)}\n`);
|
|
490
541
|
try {
|
|
491
542
|
return await runServe({
|
|
@@ -612,8 +663,8 @@ async function runServeCommand(args) {
|
|
|
612
663
|
Object.keys(plan.manifest.app.publicEnv).length > 0
|
|
613
664
|
? { publicEnv: plan.manifest.app.publicEnv }
|
|
614
665
|
: {}),
|
|
615
|
-
...(plan.manifest?.app?.
|
|
616
|
-
? {
|
|
666
|
+
...(plan.manifest?.app?.defaultDisplayMode !== undefined
|
|
667
|
+
? { defaultDisplayMode: plan.manifest.app.defaultDisplayMode }
|
|
617
668
|
: {}),
|
|
618
669
|
generation: generationBinding.generation,
|
|
619
670
|
...(mcpMounts.length > 0 ? { mcpMounts } : {}),
|
|
@@ -685,10 +736,10 @@ function describeStorageStatus(config, resolved) {
|
|
|
685
736
|
if (!config)
|
|
686
737
|
return [];
|
|
687
738
|
const lines = [];
|
|
688
|
-
if (config.
|
|
689
|
-
lines.push(resolved.
|
|
690
|
-
? `storage:
|
|
691
|
-
: `storage:
|
|
739
|
+
if (config.renders) {
|
|
740
|
+
lines.push(resolved.renderStore
|
|
741
|
+
? `storage: renders → sqlite (${config.renders.driver === 'sqlite' ? config.renders.path : '—'})`
|
|
742
|
+
: `storage: renders → in-memory`);
|
|
692
743
|
}
|
|
693
744
|
if (config.vectors) {
|
|
694
745
|
lines.push(resolved.vectors
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
* server restart. Multi-tenant `ggui serve` lives off this
|
|
18
18
|
* seam: each authenticated user manages their OWN provider
|
|
19
19
|
* key via `/settings`, the resolver picks it up on the next
|
|
20
|
-
* `
|
|
20
|
+
* `ggui_render`.
|
|
21
21
|
* 3. **No-key default** — when the boot scan finds nothing, we
|
|
22
22
|
* STILL return a binding (provider=`anthropic`, model=
|
|
23
23
|
* `claude-haiku-4-5`). Generation stays wired; the
|
|
24
24
|
* no-credentials path now produces a Connect-Claude card
|
|
25
|
-
*
|
|
25
|
+
* render via {@link GenerationDeps.onNoCredentials}
|
|
26
26
|
* instead of the broken `codeReady:false` placeholder.
|
|
27
27
|
* Caller-supplied `onNoCredentials` is threaded through
|
|
28
28
|
* verbatim.
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
* can add `GGUI_GENERATION_PROVIDER` / `ggui.json#generation.model`
|
|
38
38
|
* without re-shaping this seam.
|
|
39
39
|
*/
|
|
40
|
-
import type { BlueprintProvider, GenerationDeps, HandlerContext, LlmProvider,
|
|
40
|
+
import type { BlueprintProvider, GenerationDeps, HandlerContext, LlmProvider, LlmRoute, Render } from '@ggui-ai/mcp-server';
|
|
41
41
|
import type { ByokKeyResolution, ByokResolver } from './byok-resolver.js';
|
|
42
42
|
/**
|
|
43
43
|
* Locked default model per provider for the OSS first-run path.
|
|
@@ -53,7 +53,14 @@ import type { ByokKeyResolution, ByokResolver } from './byok-resolver.js';
|
|
|
53
53
|
* binds Bedrock through its own `GenerationDeps` at the hosted
|
|
54
54
|
* surface.
|
|
55
55
|
*/
|
|
56
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Locked default route per provider, as a typed {@link LlmRoute}.
|
|
58
|
+
* Model strings are bare wire-canonical IDs (registry KEY ==
|
|
59
|
+
* what the provider's API expects on the wire) — there is no
|
|
60
|
+
* transformation step downstream. `dispatchGeneration` sends
|
|
61
|
+
* `route.model` verbatim.
|
|
62
|
+
*/
|
|
63
|
+
export declare const DEFAULT_ROUTE_BY_PROVIDER: Readonly<Record<Exclude<LlmProvider, 'bedrock'>, LlmRoute>>;
|
|
57
64
|
/**
|
|
58
65
|
* Priority order for BYOK provider probing. The first provider
|
|
59
66
|
* whose credentials resolve wins. Anthropic first is deliberate —
|
|
@@ -66,6 +73,21 @@ export interface ProbeGenerationBindingOptions {
|
|
|
66
73
|
readonly blueprints: BlueprintProvider;
|
|
67
74
|
/** Override the probe order. Tests use this to pin the winner. */
|
|
68
75
|
readonly providerOrder?: readonly Exclude<LlmProvider, 'bedrock'>[];
|
|
76
|
+
/**
|
|
77
|
+
* Explicit operator-declared route from `ggui.json#generation.model`.
|
|
78
|
+
* When set:
|
|
79
|
+
* - `configuredRoute.provider` becomes the boot-scan provider
|
|
80
|
+
* (overriding the priority chain).
|
|
81
|
+
* - `configuredRoute.model` is what flows to the dispatch path
|
|
82
|
+
* (overriding `DEFAULT_ROUTE_BY_PROVIDER[provider]`).
|
|
83
|
+
*
|
|
84
|
+
* Absent → fall back to today's behavior: walk the priority chain,
|
|
85
|
+
* use the per-provider default. The CLI hard-fails BEFORE reaching
|
|
86
|
+
* this function when a ggui.json exists, a key resolves, but
|
|
87
|
+
* `generation.model` is unset — so this seam only sees `undefined`
|
|
88
|
+
* for the `--mcp-only` / no-manifest paths.
|
|
89
|
+
*/
|
|
90
|
+
readonly configuredRoute?: LlmRoute;
|
|
69
91
|
/**
|
|
70
92
|
* Optional no-credentials fallback hook threaded straight onto
|
|
71
93
|
* {@link GenerationDeps.onNoCredentials}. The CLI builds this
|
|
@@ -76,9 +98,9 @@ export interface ProbeGenerationBindingOptions {
|
|
|
76
98
|
*/
|
|
77
99
|
readonly onNoCredentials?: (ctx: HandlerContext, story: {
|
|
78
100
|
readonly intent: string;
|
|
79
|
-
readonly
|
|
101
|
+
readonly renderId: string;
|
|
80
102
|
readonly nowIso: string;
|
|
81
|
-
}) =>
|
|
103
|
+
}) => Render | null | Promise<Render | null>;
|
|
82
104
|
}
|
|
83
105
|
/**
|
|
84
106
|
* Outcome of the boot probe. Always returned (no `null` shape) —
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generation-probe.d.ts","sourceRoot":"","sources":["../src/generation-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,WAAW,
|
|
1
|
+
{"version":3,"file":"generation-probe.d.ts","sourceRoot":"","sources":["../src/generation-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,WAAW,EACX,QAAQ,EAGR,MAAM,EACP,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EACV,iBAAiB,EACjB,YAAY,EACb,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;GAaG;AACH;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAC9C,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,QAAQ,CAAC,CAMlD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,EAC1B,CAAC;AAElD,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,kEAAkE;IAClE,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,CAAC;IACpE;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,QAAQ,CAAC;IACpC;;;;;;;OAOG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CACzB,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE;QACL,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,KACE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC7C;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,CAAC,SAAS,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACjD,qEAAqE;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,iBAAiB,CAAC,CAwF5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,iBAAiB,GACzB,MAAM,CAWR"}
|
package/dist/generation-probe.js
CHANGED
|
@@ -13,15 +13,18 @@ import { createUiGenerator } from '@ggui-ai/ui-gen';
|
|
|
13
13
|
* binds Bedrock through its own `GenerationDeps` at the hosted
|
|
14
14
|
* surface.
|
|
15
15
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Locked default route per provider, as a typed {@link LlmRoute}.
|
|
18
|
+
* Model strings are bare wire-canonical IDs (registry KEY ==
|
|
19
|
+
* what the provider's API expects on the wire) — there is no
|
|
20
|
+
* transformation step downstream. `dispatchGeneration` sends
|
|
21
|
+
* `route.model` verbatim.
|
|
22
|
+
*/
|
|
23
|
+
export const DEFAULT_ROUTE_BY_PROVIDER = {
|
|
24
|
+
anthropic: { provider: 'anthropic', model: 'claude-haiku-4-5-20251001' },
|
|
25
|
+
openai: { provider: 'openai', model: 'gpt-5.5-2026-04-23' },
|
|
26
|
+
google: { provider: 'google', model: 'gemini-3.5-flash' },
|
|
27
|
+
openrouter: { provider: 'openrouter', model: 'anthropic/claude-haiku-4.5' },
|
|
25
28
|
};
|
|
26
29
|
/**
|
|
27
30
|
* Priority order for BYOK provider probing. The first provider
|
|
@@ -42,26 +45,37 @@ export const PROVIDER_PROBE_ORDER = ['anthropic', 'openai', 'google', 'openroute
|
|
|
42
45
|
*/
|
|
43
46
|
export async function probeGenerationBinding(opts) {
|
|
44
47
|
const order = opts.providerOrder ?? PROVIDER_PROBE_ORDER;
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
+
// When the operator declared an explicit route, the boot scan only
|
|
49
|
+
// needs to resolve THAT provider's credential — the priority walk
|
|
50
|
+
// is bypassed. Otherwise fall back to the original behavior: walk
|
|
51
|
+
// the chain, first hit wins.
|
|
52
|
+
const configured = opts.configuredRoute;
|
|
48
53
|
let bootHit = null;
|
|
49
|
-
|
|
50
|
-
const resolution = await opts.resolver.resolve(provider);
|
|
54
|
+
if (configured && configured.provider !== 'bedrock') {
|
|
55
|
+
const resolution = await opts.resolver.resolve(configured.provider);
|
|
51
56
|
if (resolution) {
|
|
52
|
-
bootHit = { ...resolution, provider };
|
|
53
|
-
|
|
57
|
+
bootHit = { ...resolution, provider: configured.provider };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
for (const provider of order) {
|
|
62
|
+
const resolution = await opts.resolver.resolve(provider);
|
|
63
|
+
if (resolution) {
|
|
64
|
+
bootHit = { ...resolution, provider };
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
54
67
|
}
|
|
55
68
|
}
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
69
|
+
// Resolve the route the dispatch path will use:
|
|
70
|
+
// - explicit `configuredRoute` always wins, even on boot-miss
|
|
71
|
+
// - else use the boot-hit provider's default route
|
|
72
|
+
// - else default to anthropic / Haiku 4.5 (the OSS "Claude first"
|
|
73
|
+
// posture; matches Connect-Claude card flow)
|
|
74
|
+
const defaultProvider = configured && configured.provider !== 'bedrock'
|
|
75
|
+
? configured.provider
|
|
76
|
+
: bootHit?.provider ?? 'anthropic';
|
|
77
|
+
const defaultRoute = configured ?? DEFAULT_ROUTE_BY_PROVIDER[defaultProvider];
|
|
78
|
+
const defaultModel = defaultRoute.model;
|
|
65
79
|
// Full-harness wire-up: `createUiGenerator` runs the same
|
|
66
80
|
// multi-turn coding agent + self-check loop the bench validates;
|
|
67
81
|
// `dispatchGeneration` returns compiled JS on `componentCode`.
|
|
@@ -77,7 +91,10 @@ export async function probeGenerationBinding(opts) {
|
|
|
77
91
|
if (!resolution)
|
|
78
92
|
return null;
|
|
79
93
|
return {
|
|
80
|
-
|
|
94
|
+
// Spread the typed route — TS knows `defaultRoute` is an
|
|
95
|
+
// `LlmRoute`, so the discriminator + model alignment is
|
|
96
|
+
// preserved when assigning to `LlmSelection`.
|
|
97
|
+
selection: { ...defaultRoute },
|
|
81
98
|
providerKey: { provider: defaultProvider, key: resolution.key },
|
|
82
99
|
};
|
|
83
100
|
};
|
package/dist/mcp-backend.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type BlueprintProvider, type
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type { ServeBackend } from
|
|
1
|
+
import { type BlueprintProvider, type DiscoveredPrimitiveCatalog, type GadgetDescriptor, type GenerationDeps, type LoadedTheme, type McpServerMount, type McpUiDisplayMode, type OperatorConfig, type ResolvedStorageStores, type ShortCodeIndex, type ThemeWriter } from "@ggui-ai/mcp-server";
|
|
2
|
+
import type { UiManifest } from "@ggui-ai/project-config";
|
|
3
|
+
import type { UiRegistry } from "@ggui-ai/ui-registry";
|
|
4
|
+
import type { ServeBackend } from "./serve-command.js";
|
|
5
5
|
export interface BuildMcpServerBackendOptions {
|
|
6
6
|
/**
|
|
7
7
|
* Pre-resolved storage from `ggui.json#storage`. Threaded straight
|
|
@@ -18,19 +18,18 @@ export interface BuildMcpServerBackendOptions {
|
|
|
18
18
|
*/
|
|
19
19
|
readonly cliVersion: string;
|
|
20
20
|
/**
|
|
21
|
-
* Bind host used to compose the MCP Apps `
|
|
21
|
+
* Bind host used to compose the MCP Apps `wsUrl` and `runtime.url`.
|
|
22
22
|
* Must be the same host the CLI then passes to `listen()`; otherwise
|
|
23
|
-
*
|
|
24
|
-
* browser can't reach.
|
|
23
|
+
* those URLs will point somewhere the iframe browser can't reach.
|
|
25
24
|
*/
|
|
26
25
|
readonly host: string;
|
|
27
26
|
/**
|
|
28
27
|
* Bind port used to compose the MCP Apps URLs. MUST be a concrete
|
|
29
|
-
* port (not `0`) — the
|
|
30
|
-
* time, so deferring to an OS-assigned
|
|
31
|
-
*
|
|
32
|
-
* should pre-resolve a free port via
|
|
33
|
-
* constructing the backend.
|
|
28
|
+
* port (not `0`) — the iframe-facing URLs (`wsUrl`, `runtime.url`)
|
|
29
|
+
* are captured at composition time, so deferring to an OS-assigned
|
|
30
|
+
* port would leave them pointing at the wrong port. Callers that
|
|
31
|
+
* started with `--port 0` should pre-resolve a free port via
|
|
32
|
+
* {@link pickFreePort} before constructing the backend.
|
|
34
33
|
*/
|
|
35
34
|
readonly port: number;
|
|
36
35
|
/**
|
|
@@ -83,13 +82,13 @@ export interface BuildMcpServerBackendOptions {
|
|
|
83
82
|
readonly themeWriter?: ThemeWriter;
|
|
84
83
|
/**
|
|
85
84
|
* Live theme getter — when set, supersedes the static `theme`
|
|
86
|
-
* resolution for every `
|
|
85
|
+
* resolution for every `ggui_render` bootstrap envelope. The CLI
|
|
87
86
|
* pairs this with `onThemeConfigChange` so a console save
|
|
88
|
-
* reaches the next
|
|
87
|
+
* reaches the next render without a server restart.
|
|
89
88
|
*/
|
|
90
89
|
readonly themeProvider?: () => {
|
|
91
90
|
readonly id?: string;
|
|
92
|
-
readonly mode?:
|
|
91
|
+
readonly mode?: "light" | "dark";
|
|
93
92
|
} | undefined;
|
|
94
93
|
/**
|
|
95
94
|
* Optional change notifier — fires from
|
|
@@ -99,17 +98,17 @@ export interface BuildMcpServerBackendOptions {
|
|
|
99
98
|
*/
|
|
100
99
|
readonly onThemeConfigChange?: (next: string | {
|
|
101
100
|
preset: string;
|
|
102
|
-
mode?:
|
|
101
|
+
mode?: "light" | "dark";
|
|
103
102
|
overrides?: Record<string, string>;
|
|
104
103
|
} | {
|
|
105
104
|
file: string;
|
|
106
|
-
mode?:
|
|
105
|
+
mode?: "light" | "dark";
|
|
107
106
|
} | null) => void;
|
|
108
107
|
/**
|
|
109
|
-
* Generation wiring for `
|
|
108
|
+
* Generation wiring for `ggui_render`. When present, the OSS render
|
|
110
109
|
* handler invokes the bound `UiGenerator` on every story-path
|
|
111
|
-
* call and
|
|
112
|
-
* placeholder mode:
|
|
110
|
+
* call and commits real componentCode as a `Render`. Absent =
|
|
111
|
+
* placeholder mode: render mints renderIds + shortCodes but does
|
|
113
112
|
* not produce componentCode.
|
|
114
113
|
*
|
|
115
114
|
* The CLI resolves this from the operator's BYOK state at boot via
|
|
@@ -171,7 +170,7 @@ export interface BuildMcpServerBackendOptions {
|
|
|
171
170
|
* (every bearer authenticates as builder) but additionally:
|
|
172
171
|
* - Wires a per-remote-IP `FixedWindowRateLimiter` over an
|
|
173
172
|
* `InMemoryQuotaStore` (default 30 generations / 10 minutes per
|
|
174
|
-
* IP). The limiter binds at `
|
|
173
|
+
* IP). The limiter binds at `ggui_render` so end-user generations
|
|
175
174
|
* can't burn the operator's BYOK budget.
|
|
176
175
|
* - The CLI banner displays "PUBLIC DEMO" copy with a cost-
|
|
177
176
|
* attribution + rate-limit note (vs. "DEV ALLOW-ALL" warning).
|
|
@@ -197,11 +196,10 @@ export interface BuildMcpServerBackendOptions {
|
|
|
197
196
|
readonly multiTenant?: boolean;
|
|
198
197
|
/**
|
|
199
198
|
* Public base URL that replaces `http://<host>:<port>` when
|
|
200
|
-
* composing `mcpApps.
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* `localhost`.
|
|
199
|
+
* composing `mcpApps.wsUrl` and `runtime.url`. Set this when the
|
|
200
|
+
* server is being reached through a tunnel (cloudflare, ngrok, …)
|
|
201
|
+
* so URLs the iframe receives resolve from the MCP host's
|
|
202
|
+
* perspective rather than `localhost`.
|
|
205
203
|
*
|
|
206
204
|
* Required shape: `http://…` or `https://…`, no trailing slash.
|
|
207
205
|
* The `wsUrl` is derived by replacing the scheme with `ws`/`wss`.
|
|
@@ -244,13 +242,13 @@ export interface BuildMcpServerBackendOptions {
|
|
|
244
242
|
* Surface = `--mcp-instructions <preset>` CLI flag or
|
|
245
243
|
* `GGUI_MCP_INSTRUCTIONS` env var (CLI flag wins).
|
|
246
244
|
*/
|
|
247
|
-
readonly mcpInstructions?:
|
|
245
|
+
readonly mcpInstructions?: "default" | "aggressive" | "always" | "minimal" | "off";
|
|
248
246
|
/**
|
|
249
247
|
* Directory backing the cross-restart persistence bundle. When set,
|
|
250
|
-
* `
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
* `_meta.ggui.
|
|
248
|
+
* `wsTokenSecret` is read from / minted into a 0600-mode file there
|
|
249
|
+
* so the HMAC key survives `ggui serve` restart — claude.ai
|
|
250
|
+
* chat-history revisits keep their cached
|
|
251
|
+
* `_meta["ai.ggui/render"].wsToken` valid across reboots.
|
|
254
252
|
*
|
|
255
253
|
* Absent / undefined = the legacy ephemeral behavior (every restart
|
|
256
254
|
* mints fresh secrets; cached tokens fail HMAC verify). The CLI sets
|
|
@@ -313,23 +311,22 @@ export interface BuildMcpServerBackendOptions {
|
|
|
313
311
|
* `ggui.json#app.publicEnv`. Each key MUST match
|
|
314
312
|
* `^GGUI_PUBLIC_APP_[A-Z0-9_]+$`. The server projects only the keys
|
|
315
313
|
* that some registered wrapper's `requires` references, onto
|
|
316
|
-
* `_meta.ggui.
|
|
314
|
+
* `_meta["ai.ggui/render"].publicEnv` and ultimately
|
|
317
315
|
* `globalThis.__ggui__.publicEnv` for `getPublicEnv()` to read.
|
|
318
316
|
*
|
|
319
317
|
* Omitted ⇒ field absent on the App record (no values projected;
|
|
320
318
|
* wrappers without `requires` still mount, wrappers with `requires`
|
|
321
|
-
* fail at
|
|
319
|
+
* fail at render-gate validation).
|
|
322
320
|
*/
|
|
323
321
|
readonly publicEnv?: Readonly<Record<string, string>>;
|
|
324
322
|
/**
|
|
325
|
-
* Per-app
|
|
326
|
-
* `ggui.json#app.
|
|
327
|
-
* `
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
* the per-push-iframe legacy behavior.
|
|
323
|
+
* Per-app default display-mode hint from
|
|
324
|
+
* `ggui.json#app.defaultDisplayMode`. Stamped on every `ggui_render`
|
|
325
|
+
* via `_meta.ui.displayMode` so the host knows whether to present
|
|
326
|
+
* the iframe inline, fullscreen, or picture-in-picture. See
|
|
327
|
+
* `App.defaultDisplayMode` for the full semantics.
|
|
331
328
|
*/
|
|
332
|
-
readonly
|
|
329
|
+
readonly defaultDisplayMode?: McpUiDisplayMode;
|
|
333
330
|
}
|
|
334
331
|
/**
|
|
335
332
|
* Pick a free localhost TCP port by letting the OS assign one on a
|
|
@@ -337,7 +334,7 @@ export interface BuildMcpServerBackendOptions {
|
|
|
337
334
|
* chosen number. Exported so the CLI (`cli.ts::runServeCommand`) and
|
|
338
335
|
* the integration tests can resolve `--port 0` to a concrete port
|
|
339
336
|
* BEFORE constructing the backend — {@link buildMcpServerBackend}
|
|
340
|
-
* captures the port in the composed `mcpApps.
|
|
337
|
+
* captures the port in the composed `mcpApps.wsUrl` / `runtime.url`
|
|
341
338
|
* at construction time, so the caller must know it up-front.
|
|
342
339
|
*
|
|
343
340
|
* Tiny race window: another process could claim the port between our
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-backend.d.ts","sourceRoot":"","sources":["../src/mcp-backend.ts"],"names":[],"mappings":"AA0CA,OAAO,EAUL,KAAK,iBAAiB,EACtB,KAAK,
|
|
1
|
+
{"version":3,"file":"mcp-backend.d.ts","sourceRoot":"","sources":["../src/mcp-backend.ts"],"names":[],"mappings":"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"}
|