@ggui-ai/cli 0.2.0-alpha.4 → 0.4.0-rc.0
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 +38 -7
- package/dist/api-client.d.ts +42 -1
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +30 -2
- package/dist/auth-keys.d.ts +1 -1
- package/dist/auth-keys.d.ts.map +1 -1
- package/dist/auth-keys.js +22 -7
- package/dist/auth-store.d.ts +1 -1
- package/dist/auth-store.d.ts.map +1 -1
- package/dist/cli.js +112 -30
- package/dist/config-push.d.ts +79 -0
- package/dist/config-push.d.ts.map +1 -0
- package/dist/config-push.js +301 -0
- package/dist/create-app-command.d.ts +17 -0
- package/dist/create-app-command.d.ts.map +1 -0
- package/dist/create-app-command.js +58 -0
- package/dist/deploy-command.d.ts +67 -0
- package/dist/deploy-command.d.ts.map +1 -0
- package/dist/deploy-command.js +386 -0
- package/dist/dev-command.d.ts +5 -1
- package/dist/dev-command.d.ts.map +1 -1
- package/dist/dev-command.js +31 -1
- package/dist/export-pool-command.d.ts +25 -0
- package/dist/export-pool-command.d.ts.map +1 -0
- package/dist/export-pool-command.js +116 -0
- package/dist/filesystem-seed-pool-source.d.ts +13 -0
- package/dist/filesystem-seed-pool-source.d.ts.map +1 -0
- package/dist/filesystem-seed-pool-source.js +22 -0
- package/dist/generation-probe.d.ts +53 -7
- package/dist/generation-probe.d.ts.map +1 -1
- package/dist/generation-probe.js +38 -2
- package/dist/internal/artifact-install.d.ts +4 -2
- package/dist/internal/artifact-install.d.ts.map +1 -1
- package/dist/internal/artifact-install.js +11 -10
- package/dist/internal/artifact-publish.d.ts +34 -31
- package/dist/internal/artifact-publish.d.ts.map +1 -1
- package/dist/internal/artifact-publish.js +133 -241
- package/dist/internal/artifact-search.d.ts +2 -28
- package/dist/internal/artifact-search.d.ts.map +1 -1
- package/dist/internal/artifact-search.js +5 -4
- package/dist/internal/auth-strategy.d.ts +21 -31
- package/dist/internal/auth-strategy.d.ts.map +1 -1
- package/dist/internal/auth-strategy.js +15 -23
- package/dist/internal/register-author-key.d.ts +7 -1
- package/dist/internal/register-author-key.d.ts.map +1 -1
- package/dist/internal/register-author-key.js +56 -19
- package/dist/mcp-backend.d.ts +25 -25
- package/dist/mcp-backend.d.ts.map +1 -1
- package/dist/mcp-backend.js +65 -61
- package/dist/paths.d.ts +3 -3
- package/dist/paths.js +3 -3
- package/dist/persistent-secrets.js +1 -1
- package/dist/persistent-stores.d.ts +2 -2
- package/dist/persistent-stores.d.ts.map +1 -1
- package/dist/persistent-stores.js +5 -5
- package/dist/pool-artifact.d.ts +19 -0
- package/dist/pool-artifact.d.ts.map +1 -0
- package/dist/pool-artifact.js +59 -0
- package/dist/provider-key-command.d.ts +51 -0
- package/dist/provider-key-command.d.ts.map +1 -0
- package/dist/provider-key-command.js +222 -0
- package/dist/push-command.d.ts +54 -0
- package/dist/push-command.d.ts.map +1 -0
- package/dist/push-command.js +231 -0
- package/dist/serve-command.d.ts +23 -19
- package/dist/serve-command.d.ts.map +1 -1
- package/dist/serve-command.js +13 -26
- package/dist/theme-command.js +0 -2
- package/dist/tunnel-provider.d.ts +8 -7
- package/dist/tunnel-provider.d.ts.map +1 -1
- package/package.json +25 -21
- package/dist/internal/auth-cache.d.ts +0 -40
- package/dist/internal/auth-cache.d.ts.map +0 -1
- package/dist/internal/auth-cache.js +0 -133
- package/dist/persistent-short-code-index.d.ts +0 -14
- package/dist/persistent-short-code-index.d.ts.map +0 -1
- package/dist/persistent-short-code-index.js +0 -39
- package/dist/spawn-cloudflared.d.ts +0 -61
- package/dist/spawn-cloudflared.d.ts.map +0 -1
- package/dist/spawn-cloudflared.js +0 -167
package/dist/mcp-backend.js
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* What gets wired by default — see OSS-split §2 (the five server
|
|
11
11
|
* responsibilities):
|
|
12
12
|
*
|
|
13
|
-
* - `
|
|
14
|
-
* MCP Apps iframes AND the console
|
|
13
|
+
* - `renderChannel: true` — live-channel `/ws` endpoint. Required for
|
|
14
|
+
* MCP Apps iframes AND the console render viewer.
|
|
15
15
|
* - `pairing: true` — `POST /pair` + `POST /admin/pair/init` so
|
|
16
16
|
* remote clients (Portal, third-party) can pair with this server.
|
|
17
17
|
* The default `InMemoryAuthAdapter` registers pairing tokens
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* - `console: { sessionCookie: true }` — landing page at `/` +
|
|
20
20
|
* `/s/<shortCode>` viewer + same-origin HTTP-only cookie flow.
|
|
21
21
|
* - `shortCodeIndex: new InMemoryShortCodeIndex()` — required by
|
|
22
|
-
* the `sessionCookie` flow so `POST /ggui/console/
|
|
23
|
-
* can resolve a posted shortCode to the bound `{
|
|
22
|
+
* the `sessionCookie` flow so `POST /ggui/console/session-cookie`
|
|
23
|
+
* can resolve a posted shortCode to the bound `{sessionId, appId}`.
|
|
24
24
|
* In-memory is correct for the OSS first-run: render state lives
|
|
25
25
|
* in memory unless the operator opts into sqlite, so a matching
|
|
26
26
|
* index lifetime is what operators expect.
|
|
@@ -40,12 +40,11 @@ import { listThemes } from "@ggui-ai/design/themes";
|
|
|
40
40
|
import { compileUiOnDemand } from "@ggui-ai/dev-stack";
|
|
41
41
|
import { createLocalEmbeddingProvider } from "@ggui-ai/embedding-local";
|
|
42
42
|
import { RUNTIME_BUNDLE_URL_PATH } from "@ggui-ai/iframe-runtime/server";
|
|
43
|
-
import {
|
|
44
|
-
import { InMemoryAppMetadataStore, InMemoryVectorStore } from "@ggui-ai/mcp-server-core/in-memory";
|
|
43
|
+
import { createGguiServer, FileSystemCodeStore, FixedWindowRateLimiter, InMemoryAuthAdapter, InMemoryQuotaStore, InMemoryShortCodeIndex, selectEmailSenderFromEnv, } from "@ggui-ai/mcp-server";
|
|
44
|
+
import { InMemoryAppMetadataStore, InMemoryBlueprintIndex, InMemoryVectorStore, } from "@ggui-ai/mcp-server-core/in-memory";
|
|
45
45
|
import { PlaintextFileProviderKeyStore } from "@ggui-ai/mcp-server-core/plaintext";
|
|
46
|
-
import { createInstalledBlueprintsProvider, } from "@ggui-ai/mcp-server-handlers/renders";
|
|
46
|
+
import { createInstalledBlueprintsProvider, createStderrCacheTraceSink, setCacheTraceSink, } from "@ggui-ai/mcp-server-handlers/renders";
|
|
47
47
|
import { createDeterministicPreviewEmitter } from "@ggui-ai/preview-a2ui/emitters";
|
|
48
|
-
import { randomUUID } from "node:crypto";
|
|
49
48
|
import { existsSync } from "node:fs";
|
|
50
49
|
import { createServer as createNetServer } from "node:net";
|
|
51
50
|
import { join } from "node:path";
|
|
@@ -104,13 +103,6 @@ export function buildMcpServerBackend(opts) {
|
|
|
104
103
|
// derive the live-channel URL.
|
|
105
104
|
const baseUrl = opts.publicBaseUrl ?? `http://${opts.host}:${opts.port}`;
|
|
106
105
|
const wsBaseUrl = baseUrl.replace(/^http(s?):\/\//, "ws$1://");
|
|
107
|
-
// Router over the mount handler bundle. Computed once; absent
|
|
108
|
-
// (`null`) when no mounts are declared so the server falls through
|
|
109
|
-
// to agent-routed behavior.
|
|
110
|
-
const wiredActionRouter = composeWiredActionRouterFromMounts(opts.mcpMounts, () => ({
|
|
111
|
-
appId: DEFAULT_BUILDER_APP_ID,
|
|
112
|
-
requestId: randomUUID(),
|
|
113
|
-
}));
|
|
114
106
|
// Strict-auth OSS default (`devAllowAll: false`). `createGguiServer`
|
|
115
107
|
// would otherwise fall back to `InMemoryAuthAdapter({ devAllowAll:
|
|
116
108
|
// true })`, which authenticates any non-empty bearer as builder — a
|
|
@@ -161,8 +153,23 @@ export function buildMcpServerBackend(opts) {
|
|
|
161
153
|
// get `MockEmbeddingProvider` if they don't bind an embedder — that
|
|
162
154
|
// keeps the 60MB `@huggingface/transformers` install surface off
|
|
163
155
|
// the unit-test path.
|
|
156
|
+
const embeddingCacheDir = getEmbeddingCacheDir();
|
|
164
157
|
const embedding = createLocalEmbeddingProvider({
|
|
165
|
-
cacheDir:
|
|
158
|
+
cacheDir: embeddingCacheDir,
|
|
159
|
+
});
|
|
160
|
+
// One-line boot log of which embedding provider is live, so a captured
|
|
161
|
+
// serve log makes real-model-vs-mock unambiguous. The local provider
|
|
162
|
+
// loads `bge-small-en-v1.5` lazily; we then fire a best-effort warmup
|
|
163
|
+
// probe (one `embed`) so the model is downloaded/loaded before the
|
|
164
|
+
// first real lookup AND a load failure (e.g. missing
|
|
165
|
+
// `@huggingface/transformers` peer dep) is surfaced as a clear
|
|
166
|
+
// fallback warning rather than a silent first-lookup degradation.
|
|
167
|
+
// eslint-disable-next-line no-console -- one-shot boot signal, mirrors banner output
|
|
168
|
+
console.warn(`[ggui:embedding] local ${embedding.id} (cache: ${embeddingCacheDir})`);
|
|
169
|
+
embedding.embed("warmup").catch((err) => {
|
|
170
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
171
|
+
// eslint-disable-next-line no-console -- one-shot boot signal; local model unavailable
|
|
172
|
+
console.warn(`[ggui:embedding] MockEmbeddingProvider fallback — local model unavailable: ${reason}`);
|
|
166
173
|
});
|
|
167
174
|
// BYOK provider-key store — same `~/.ggui/credentials.json` that
|
|
168
175
|
// `byok-resolver.ts` reads on every `resolve()`. Threading the store
|
|
@@ -240,7 +247,7 @@ export function buildMcpServerBackend(opts) {
|
|
|
240
247
|
// themes don't have a registry id to surface here — agents pick from
|
|
241
248
|
// `listThemes()` either way; the file-mode override only affects the
|
|
242
249
|
// server's bound `theme` (rendered CSS variables), not the
|
|
243
|
-
//
|
|
250
|
+
// render/handler-side `themeId` chain.
|
|
244
251
|
const manifestThemePreset = opts.theme?.source === "preset" ? opts.theme.preset : undefined;
|
|
245
252
|
const appMetadataStore = new InMemoryAppMetadataStore({
|
|
246
253
|
...(manifestThemePreset !== undefined ? { defaultThemeId: manifestThemePreset } : {}),
|
|
@@ -253,13 +260,13 @@ export function buildMcpServerBackend(opts) {
|
|
|
253
260
|
? { defaultGadgets: opts.gadgets }
|
|
254
261
|
: {}),
|
|
255
262
|
// `ggui.json#app.publicEnv` operator-stamped values for wrapper
|
|
256
|
-
// hooks to read via `getPublicEnv()`. The
|
|
263
|
+
// hooks to read via `getPublicEnv()`. The render gate
|
|
257
264
|
// (`assertPublicEnvSatisfied`) verifies every declared wrapper's
|
|
258
265
|
// `requires` keys are present in this map BEFORE the iframe boots.
|
|
259
266
|
...(opts.publicEnv !== undefined && Object.keys(opts.publicEnv).length > 0
|
|
260
267
|
? { defaultPublicEnv: opts.publicEnv }
|
|
261
268
|
: {}),
|
|
262
|
-
// `ggui.json#app.defaultDisplayMode`. Stamped per-
|
|
269
|
+
// `ggui.json#app.defaultDisplayMode`. Stamped per-render as
|
|
263
270
|
// `_meta.ui.displayMode`; controls only host-side presentation.
|
|
264
271
|
...(opts.defaultDisplayMode !== undefined
|
|
265
272
|
? { defaultDisplayMode: opts.defaultDisplayMode }
|
|
@@ -274,6 +281,14 @@ export function buildMcpServerBackend(opts) {
|
|
|
274
281
|
// here on the absent-storage path; threaded back as `vectors` to
|
|
275
282
|
// `createGguiServer` so server + provider share state.
|
|
276
283
|
const vectorStore = storage.vectors ?? new InMemoryVectorStore();
|
|
284
|
+
// Blueprint identity index — minted here so the install bridge's
|
|
285
|
+
// provider deps and the server share ONE instance (the server's
|
|
286
|
+
// ref-equality guard enforces this, same as `vectorStore`). The index
|
|
287
|
+
// is rebuildable from vector-store metadata and self-heals at the read
|
|
288
|
+
// site, so an in-memory index is safe even alongside a persistent
|
|
289
|
+
// `storage.vectors`; a persistent index can be threaded later via the
|
|
290
|
+
// same seam.
|
|
291
|
+
const blueprintIndex = new InMemoryBlueprintIndex();
|
|
277
292
|
// Construct the marketplace-install bridge BEFORE `createGguiServer`
|
|
278
293
|
// so we can fold it into `opts.generation.installedBlueprints` below.
|
|
279
294
|
// Per design lock (project_slices_5_6_7_plan.md), the provider
|
|
@@ -319,14 +334,14 @@ export function buildMcpServerBackend(opts) {
|
|
|
319
334
|
errors: result.errors.map((m) => m.text),
|
|
320
335
|
};
|
|
321
336
|
},
|
|
322
|
-
deps: { embedding, vectorStore },
|
|
337
|
+
deps: { embedding, vectorStore, index: blueprintIndex },
|
|
323
338
|
onIssue: (issue) => {
|
|
324
339
|
process.stderr.write(`[ggui serve] installed-blueprint ${issue.id}: ${issue.kind}: ${issue.message}\n`);
|
|
325
340
|
},
|
|
326
341
|
};
|
|
327
342
|
installedBlueprintsProvider = createInstalledBlueprintsProvider(providerOptions);
|
|
328
343
|
}
|
|
329
|
-
// Fold the bridge into the generation deps so the
|
|
344
|
+
// Fold the bridge into the generation deps so the render handler +
|
|
330
345
|
// handshake negotiator both consume it via `MatchBlueprintDeps.
|
|
331
346
|
// installedBlueprints`. Two-step pattern keeps the createGguiServer
|
|
332
347
|
// call below unchanged when no provider is wired.
|
|
@@ -338,6 +353,17 @@ export function buildMcpServerBackend(opts) {
|
|
|
338
353
|
}
|
|
339
354
|
: opts.generation
|
|
340
355
|
: undefined;
|
|
356
|
+
// Env-gated blueprint-cache trace sink. When `GGUI_CACHE_TRACE_STDERR`
|
|
357
|
+
// is truthy, every `matchBlueprint` decision (and the reason it landed
|
|
358
|
+
// there) is written as a single JSON line to stderr prefixed
|
|
359
|
+
// `[ggui:cache-trace] ` — a diagnostic for seeing WHY a semantic match
|
|
360
|
+
// missed (RAG retrieved nothing / cosine below floor / judge declined)
|
|
361
|
+
// in a captured server log. Registered BEFORE `createGguiServer` so the
|
|
362
|
+
// first lookup already routes through it. Absent env = no sink (the
|
|
363
|
+
// matcher emits nothing and spends no CPU on the top-K probe).
|
|
364
|
+
if (process.env["GGUI_CACHE_TRACE_STDERR"]) {
|
|
365
|
+
setCacheTraceSink(createStderrCacheTraceSink());
|
|
366
|
+
}
|
|
341
367
|
const server = createGguiServer({
|
|
342
368
|
auth,
|
|
343
369
|
embedding,
|
|
@@ -355,7 +381,7 @@ export function buildMcpServerBackend(opts) {
|
|
|
355
381
|
// Cross-restart HMAC secrets. Present iff the caller declared a
|
|
356
382
|
// persistent dir. Both threaded so the next restart can verify
|
|
357
383
|
// tokens minted by the previous run instead of regenerating per
|
|
358
|
-
// process.
|
|
384
|
+
// process. render-signer.secret rides on the `renderSigning`
|
|
359
385
|
// discriminated union (the `false` shape disables the layer
|
|
360
386
|
// entirely; we always want it on here, so build the object form).
|
|
361
387
|
...(persistedWsTokenSecret !== undefined ? { wsTokenSecret: persistedWsTokenSecret } : {}),
|
|
@@ -381,6 +407,10 @@ export function buildMcpServerBackend(opts) {
|
|
|
381
407
|
// declaration, leave the server to mint its own internal
|
|
382
408
|
// InMemoryVectorStore that the bridge couldn't see.
|
|
383
409
|
vectors: vectorStore,
|
|
410
|
+
// Share the one minted index instance with the server so the install
|
|
411
|
+
// bridge + matcher resolve identity through the SAME index (the
|
|
412
|
+
// server's ref-equality guard enforces this).
|
|
413
|
+
index: blueprintIndex,
|
|
384
414
|
// `threads:` is an opt-in on createGguiServer (no zero-config
|
|
385
415
|
// fallback — threads without an explicit store wouldn't mount
|
|
386
416
|
// routes at all). We only pass it when the manifest declares a
|
|
@@ -397,7 +427,7 @@ export function buildMcpServerBackend(opts) {
|
|
|
397
427
|
},
|
|
398
428
|
}
|
|
399
429
|
: {}),
|
|
400
|
-
|
|
430
|
+
renderChannel: true,
|
|
401
431
|
pairing: opts.keysFile ? { persistencePath: opts.keysFile } : true,
|
|
402
432
|
// Email magic-link login. OSS first-run default mirrors
|
|
403
433
|
// `provisionalPreview` — wire a sender so magic links can be
|
|
@@ -472,7 +502,7 @@ export function buildMcpServerBackend(opts) {
|
|
|
472
502
|
// `GGUI_CODE_CACHE_DIR`). Survives `ggui serve` restart so
|
|
473
503
|
// claude.ai's iframe cache still resolves URLs minted before the
|
|
474
504
|
// restart. Operators can `rm -rf` the cache at any time —
|
|
475
|
-
// immutable URLs guarantee a fresh
|
|
505
|
+
// immutable URLs guarantee a fresh render repopulates as needed.
|
|
476
506
|
codeStore: new FileSystemCodeStore({ root: getCodeCacheDir() }),
|
|
477
507
|
// Manifest-declared blueprints. Only present when `ggui serve`
|
|
478
508
|
// found UIs in `blueprints.include`; absent = the server's
|
|
@@ -499,7 +529,7 @@ export function buildMcpServerBackend(opts) {
|
|
|
499
529
|
...(opts.themeWriter ? { themeWriter: opts.themeWriter } : {}),
|
|
500
530
|
// Live-theme wiring — `themeProvider` reads from a shared cell
|
|
501
531
|
// that `onThemeConfigChange` writes to on every console save,
|
|
502
|
-
// so the picker's "Save to ggui.json" reaches the next
|
|
532
|
+
// so the picker's "Save to ggui.json" reaches the next render's
|
|
503
533
|
// bootstrap envelope without a restart. Both threaded through
|
|
504
534
|
// when present; absent = boot-baked behaviour (legacy).
|
|
505
535
|
...(opts.themeProvider ? { themeProvider: opts.themeProvider } : {}),
|
|
@@ -508,8 +538,8 @@ export function buildMcpServerBackend(opts) {
|
|
|
508
538
|
// is the live-channel URL published on `_meta["ai.ggui/render"]`
|
|
509
539
|
// so iframes opened by an MCP Apps host (Claude Desktop, Claude
|
|
510
540
|
// Code, claude.ai) can subscribe back to this same process.
|
|
511
|
-
// First-party
|
|
512
|
-
//
|
|
541
|
+
// First-party hosted UIs consume `_meta.ui.resourceUri`
|
|
542
|
+
// instead and never hit the URL directly.
|
|
513
543
|
mcpApps: {
|
|
514
544
|
wsUrl: `${wsBaseUrl}/ws`,
|
|
515
545
|
},
|
|
@@ -543,31 +573,27 @@ export function buildMcpServerBackend(opts) {
|
|
|
543
573
|
},
|
|
544
574
|
// Generation wiring. Threaded through to
|
|
545
575
|
// `defaultHandlers.render.generation` → `createGguiRenderHandler`.
|
|
546
|
-
// Absent = placeholder mode: render mints
|
|
576
|
+
// Absent = placeholder mode: render mints sessionIds + shortCodes
|
|
547
577
|
// + preview but does NOT produce componentCode. The CLI's
|
|
548
578
|
// `runServeCommand` probes BYOK (env → credentials file) at
|
|
549
579
|
// boot and only supplies this opt on a hit. See
|
|
550
580
|
// `./generation-probe.ts`.
|
|
551
581
|
...(generationWithInstalled ? { generation: generationWithInstalled } : {}),
|
|
582
|
+
// Seed pools from `--seed-pool` artifacts. Threaded into the
|
|
583
|
+
// handshake negotiator so cross-deployment blueprints are
|
|
584
|
+
// accessible without being in the local cache.
|
|
585
|
+
...(opts.seedPools && opts.seedPools.length > 0 ? { seedPools: opts.seedPools } : {}),
|
|
552
586
|
// Mount aggregation. External SharedHandler bundles appended onto
|
|
553
587
|
// `/mcp` alongside ggui-native tools. No CLI config loader yet —
|
|
554
588
|
// programmatic hosts + integration tests compose mounts through
|
|
555
589
|
// this opt directly.
|
|
556
590
|
...(opts.mcpMounts && opts.mcpMounts.length > 0 ? { mcpMounts: opts.mcpMounts } : {}),
|
|
557
|
-
// wiredActionRouter composed from the same mount handlers. With
|
|
558
|
-
// mounts present, every mounted tool becomes wire-dispatchable
|
|
559
|
-
// from a generated UI's `actionSpec[name].dispatch.tool` (when
|
|
560
|
-
// `dispatch.kind === 'tool'`) — the "zero agent code" path.
|
|
561
|
-
// Without mounts, no router is wired and inbound actions stay on
|
|
562
|
-
// the agent-routed path.
|
|
563
|
-
...(wiredActionRouter ? { wiredActionRouter } : {}),
|
|
564
591
|
// Schema-compat mode. Defaults to `'reject'` inside
|
|
565
592
|
// `createGguiServer`; the env override is the operator's escape
|
|
566
593
|
// hatch for scenarios that intentionally ship a blueprint with a
|
|
567
|
-
// schema mismatch (e.g. contract-probe fixtures that
|
|
568
|
-
//
|
|
569
|
-
//
|
|
570
|
-
// runtime gets to emit the envelope under test).
|
|
594
|
+
// schema mismatch (e.g. contract-probe fixtures that exercise
|
|
595
|
+
// mismatch handling downstream — `'reject'` would otherwise block
|
|
596
|
+
// the render at render/try-live time before the scenario starts).
|
|
571
597
|
//
|
|
572
598
|
// Accepted values match `SchemaCompatMode`: `'reject' | 'warn' |
|
|
573
599
|
// 'off'`. Unrecognized values fall through to the server's
|
|
@@ -580,28 +606,6 @@ export function buildMcpServerBackend(opts) {
|
|
|
580
606
|
schemaCompatCheck: process.env["GGUI_SCHEMA_COMPAT_MODE"],
|
|
581
607
|
}
|
|
582
608
|
: {}),
|
|
583
|
-
// Wired-tool per-call timeout override (default 30 s in
|
|
584
|
-
// `session-channel.ts::DEFAULT_WIRED_TOOL_TIMEOUT_MS`). Mirrors the
|
|
585
|
-
// `GGUI_SCHEMA_COMPAT_MODE` escape-hatch shape: env-only; absent or
|
|
586
|
-
// unparseable falls through to the server's default. Test harnesses
|
|
587
|
-
// exercising TOOL_TIMEOUT envelopes lower this to keep per-test
|
|
588
|
-
// budgets small (the conformance fixture
|
|
589
|
-
// `wired-action-tool-timeout.json` deliberately wires a hanging
|
|
590
|
-
// tool; the default 30 s wait would dominate a Lane-1 spec budget).
|
|
591
|
-
//
|
|
592
|
-
// Parse uses `Number()` + finite + positive-integer checks; a typo
|
|
593
|
-
// (`"abc"`, `"-100"`, `"NaN"`) falls through silently to default.
|
|
594
|
-
// Operators that need a different production timeout SHOULD compose
|
|
595
|
-
// `createGguiServer` directly rather than rely on env coupling.
|
|
596
|
-
...(() => {
|
|
597
|
-
const raw = process.env["GGUI_WIRED_TIMEOUT_MS"];
|
|
598
|
-
if (raw === undefined || raw === "")
|
|
599
|
-
return {};
|
|
600
|
-
const parsed = Number(raw);
|
|
601
|
-
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
602
|
-
return {};
|
|
603
|
-
return { wiredActionTimeoutMs: Math.floor(parsed) };
|
|
604
|
-
})(),
|
|
605
609
|
});
|
|
606
610
|
return {
|
|
607
611
|
listen: async (port, host) => {
|
package/dist/paths.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ export declare function getEmbeddingCacheDir(): string;
|
|
|
51
51
|
* safe to return even when nothing has been written yet. Operators
|
|
52
52
|
* who want to reset the cache can `rm -rf` this directory at any
|
|
53
53
|
* time — every entry is content-addressable + immutable, so a
|
|
54
|
-
* re-fetch repopulates from upstream code on next
|
|
54
|
+
* re-fetch repopulates from upstream code on next render.
|
|
55
55
|
*/
|
|
56
56
|
export declare function getCodeCacheDir(): string;
|
|
57
57
|
/**
|
|
@@ -59,14 +59,14 @@ export declare function getCodeCacheDir(): string;
|
|
|
59
59
|
* short-codes.sqlite, bootstrap-secret.hex, render-signer-secret.hex,
|
|
60
60
|
* keys.json …). Survives `ggui serve` restarts so claude.ai chat-history
|
|
61
61
|
* revisits can rehydrate the iframe instead of seeing four cascading
|
|
62
|
-
* failures (HMAC + shortCode +
|
|
62
|
+
* failures (HMAC + shortCode + render + pairing).
|
|
63
63
|
*
|
|
64
64
|
* Resolution order:
|
|
65
65
|
* 1. `GGUI_PERSISTENT_DIR` env — operator override (tests, clean-room,
|
|
66
66
|
* shared mount).
|
|
67
67
|
* 2. `<projectRoot>/.ggui/persistent` — when the CLI resolved a
|
|
68
68
|
* ggui.json. Per-project isolation; different projects get
|
|
69
|
-
* separate
|
|
69
|
+
* separate renders even on the same machine.
|
|
70
70
|
* 3. `~/.ggui/persistent` — fallback when there's no manifest
|
|
71
71
|
* (`ggui serve` from a bare directory).
|
|
72
72
|
*
|
package/dist/paths.js
CHANGED
|
@@ -81,7 +81,7 @@ export function getEmbeddingCacheDir() {
|
|
|
81
81
|
* safe to return even when nothing has been written yet. Operators
|
|
82
82
|
* who want to reset the cache can `rm -rf` this directory at any
|
|
83
83
|
* time — every entry is content-addressable + immutable, so a
|
|
84
|
-
* re-fetch repopulates from upstream code on next
|
|
84
|
+
* re-fetch repopulates from upstream code on next render.
|
|
85
85
|
*/
|
|
86
86
|
export function getCodeCacheDir() {
|
|
87
87
|
const override = process.env['GGUI_CODE_CACHE_DIR'];
|
|
@@ -94,14 +94,14 @@ export function getCodeCacheDir() {
|
|
|
94
94
|
* short-codes.sqlite, bootstrap-secret.hex, render-signer-secret.hex,
|
|
95
95
|
* keys.json …). Survives `ggui serve` restarts so claude.ai chat-history
|
|
96
96
|
* revisits can rehydrate the iframe instead of seeing four cascading
|
|
97
|
-
* failures (HMAC + shortCode +
|
|
97
|
+
* failures (HMAC + shortCode + render + pairing).
|
|
98
98
|
*
|
|
99
99
|
* Resolution order:
|
|
100
100
|
* 1. `GGUI_PERSISTENT_DIR` env — operator override (tests, clean-room,
|
|
101
101
|
* shared mount).
|
|
102
102
|
* 2. `<projectRoot>/.ggui/persistent` — when the CLI resolved a
|
|
103
103
|
* ggui.json. Per-project isolation; different projects get
|
|
104
|
-
* separate
|
|
104
|
+
* separate renders even on the same machine.
|
|
105
105
|
* 3. `~/.ggui/persistent` — fallback when there's no manifest
|
|
106
106
|
* (`ggui serve` from a bare directory).
|
|
107
107
|
*
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* Security posture: these secrets are a render-token signing key, NOT
|
|
21
21
|
* a long-lived credential like an OAuth client secret. Losing them
|
|
22
22
|
* means an attacker can mint MCP Apps bootstrap tokens for arbitrary
|
|
23
|
-
* `(
|
|
23
|
+
* `(sessionId, appId)` pairs — same blast radius as before, just
|
|
24
24
|
* surviving restart. Mode 0600 on the file matches `~/.ggui/credentials
|
|
25
25
|
* .json` (BYOK keys) which has the same threat model.
|
|
26
26
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function
|
|
1
|
+
import type { GguiSessionStore, VectorStore } from '@ggui-ai/mcp-server-core';
|
|
2
|
+
export declare function createPersistentGguiSessionStore(persistentDir: string): Promise<GguiSessionStore>;
|
|
3
3
|
export declare function createPersistentVectorStore(persistentDir: string): Promise<VectorStore>;
|
|
4
4
|
/**
|
|
5
5
|
* Compute the auto-default `keysFile` path. Returns `undefined` when
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistent-stores.d.ts","sourceRoot":"","sources":["../src/persistent-stores.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EACV,
|
|
1
|
+
{"version":3,"file":"persistent-stores.d.ts","sourceRoot":"","sources":["../src/persistent-stores.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EACV,gBAAgB,EAChB,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAElC,wBAAsB,gCAAgC,CACpD,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,CAAC,CAQ3B;AAED,wBAAsB,2BAA2B,CAC/C,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,WAAW,CAAC,CAQtB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE7D"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Default-on persistent stores under `<persistentDir>/`. Used when the
|
|
3
3
|
* operator hasn't declared `storage.renders` / `storage.vectors` in
|
|
4
|
-
* `ggui.json` AND hasn't passed `--ephemeral`. Closes the
|
|
4
|
+
* `ggui.json` AND hasn't passed `--ephemeral`. Closes the GguiSessionStore
|
|
5
5
|
* leg of the rehydrate problem: without this, a restart drops every
|
|
6
6
|
* render row even though the HMAC secret + ShortCodeIndex now persist.
|
|
7
7
|
*
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { mkdirSync } from 'node:fs';
|
|
18
18
|
import { join } from 'node:path';
|
|
19
|
-
export async function
|
|
19
|
+
export async function createPersistentGguiSessionStore(persistentDir) {
|
|
20
20
|
mkdirSync(persistentDir, { recursive: true });
|
|
21
|
-
const {
|
|
22
|
-
return new
|
|
23
|
-
filename: join(persistentDir, '
|
|
21
|
+
const { SqliteGguiSessionStore } = await import('@ggui-ai/mcp-server-core/sqlite');
|
|
22
|
+
return new SqliteGguiSessionStore({
|
|
23
|
+
filename: join(persistentDir, 'sessions.sqlite'),
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
export async function createPersistentVectorStore(persistentDir) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PortableBlueprint } from '@ggui-ai/protocol';
|
|
2
|
+
/** Write records to a directory: manifest.json + codes/<hash>.tsx. */
|
|
3
|
+
export declare function writePoolArtifact(dir: string, records: readonly PortableBlueprint[]): Promise<void>;
|
|
4
|
+
export interface ReadPoolResult {
|
|
5
|
+
/**
|
|
6
|
+
* Raw, UNVALIDATED records (manifest entry + reassembled
|
|
7
|
+
* componentCode). The artifact bytes are an untrusted input; the
|
|
8
|
+
* trust boundary is `fromPortableBlueprint` at the loader
|
|
9
|
+
* (`buildSeedPool`), which validates each record and skips
|
|
10
|
+
* rejections with a log line. This reader only owns the directory
|
|
11
|
+
* IO; the layout/format is the shared codec's.
|
|
12
|
+
*/
|
|
13
|
+
readonly records: readonly unknown[];
|
|
14
|
+
/** Per-record problems (malformed entry, bad ref, missing/unreadable code body); never throws on these. */
|
|
15
|
+
readonly issues: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
/** Read + reassemble records. Throws on a missing/invalid manifest or unsupported schemaVersion (v1 included — re-export, no migration shim). */
|
|
18
|
+
export declare function readPoolArtifact(dir: string): Promise<ReadPoolResult>;
|
|
19
|
+
//# sourceMappingURL=pool-artifact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool-artifact.d.ts","sourceRoot":"","sources":["../src/pool-artifact.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAK3D,sEAAsE;AACtE,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,SAAS,iBAAiB,EAAE,GACpC,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED,MAAM,WAAW,cAAc;IAC7B;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;IACrC,2GAA2G;IAC3G,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,iJAAiJ;AACjJ,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA6B3E"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem IO for the seed-pool wrapper artifact. The byte FORMAT
|
|
3
|
+
* (schema version, layout names, codeRef shape, rejection/issue
|
|
4
|
+
* messages) is owned by `@ggui-ai/artifact-manifest`'s pool-artifact
|
|
5
|
+
* codec — this module owns only the directory IO: writing/reading the
|
|
6
|
+
* manifest + code bodies and splicing `componentCode` back in.
|
|
7
|
+
*/
|
|
8
|
+
import { mkdir, readFile, writeFile, rm } from 'node:fs/promises';
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { POOL_ARTIFACT_CODES_DIR, POOL_ARTIFACT_MANIFEST_FILENAME, buildPoolArtifactManifest, parsePoolArtifactManifest, poolArtifactCodeRef, serializePoolArtifactManifest, } from '@ggui-ai/artifact-manifest';
|
|
12
|
+
const codeHash = (code) => createHash('sha256').update(code, 'utf-8').digest('hex');
|
|
13
|
+
/** Write records to a directory: manifest.json + codes/<hash>.tsx. */
|
|
14
|
+
export async function writePoolArtifact(dir, records) {
|
|
15
|
+
// Clear ONLY the files this codec owns, never the whole dir — the caller's
|
|
16
|
+
// path (e.g. `--out .`) is passed through verbatim, so a recursive wipe of
|
|
17
|
+
// `dir` would delete unrelated files. Removing `codes/` + the manifest still
|
|
18
|
+
// guarantees no stale code bodies survive a rewrite.
|
|
19
|
+
await mkdir(dir, { recursive: true });
|
|
20
|
+
await rm(join(dir, POOL_ARTIFACT_CODES_DIR), { recursive: true, force: true });
|
|
21
|
+
await rm(join(dir, POOL_ARTIFACT_MANIFEST_FILENAME), { force: true });
|
|
22
|
+
await mkdir(join(dir, POOL_ARTIFACT_CODES_DIR), { recursive: true });
|
|
23
|
+
const entries = [];
|
|
24
|
+
for (const r of records) {
|
|
25
|
+
const codeRef = poolArtifactCodeRef(codeHash(r.componentCode));
|
|
26
|
+
await writeFile(join(dir, POOL_ARTIFACT_CODES_DIR, codeRef), r.componentCode, 'utf-8');
|
|
27
|
+
const { componentCode: _omit, ...rest } = r;
|
|
28
|
+
entries.push({ record: rest, codeRef });
|
|
29
|
+
}
|
|
30
|
+
await writeFile(join(dir, POOL_ARTIFACT_MANIFEST_FILENAME), serializePoolArtifactManifest(buildPoolArtifactManifest(entries)), 'utf-8');
|
|
31
|
+
}
|
|
32
|
+
/** Read + reassemble records. Throws on a missing/invalid manifest or unsupported schemaVersion (v1 included — re-export, no migration shim). */
|
|
33
|
+
export async function readPoolArtifact(dir) {
|
|
34
|
+
let raw;
|
|
35
|
+
try {
|
|
36
|
+
raw = await readFile(join(dir, POOL_ARTIFACT_MANIFEST_FILENAME), 'utf-8');
|
|
37
|
+
}
|
|
38
|
+
catch (cause) {
|
|
39
|
+
throw new Error(`seed pool: cannot read ${join(dir, POOL_ARTIFACT_MANIFEST_FILENAME)}: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
40
|
+
}
|
|
41
|
+
const parsed = parsePoolArtifactManifest(raw);
|
|
42
|
+
if (!parsed.ok) {
|
|
43
|
+
throw new Error(`seed pool: ${parsed.reason}`);
|
|
44
|
+
}
|
|
45
|
+
const records = [];
|
|
46
|
+
const issues = parsed.issues.map((issue) => `seed pool: ${issue}`);
|
|
47
|
+
for (const entry of parsed.manifest.blueprints) {
|
|
48
|
+
let componentCode;
|
|
49
|
+
try {
|
|
50
|
+
componentCode = await readFile(join(dir, POOL_ARTIFACT_CODES_DIR, entry.codeRef), 'utf-8');
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
issues.push(`seed pool: missing code body ${entry.codeRef}; skipping blueprint`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
records.push({ ...entry.record, componentCode });
|
|
57
|
+
}
|
|
58
|
+
return { records, issues };
|
|
59
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** Providers that accept a user-supplied plaintext API key. */
|
|
2
|
+
export declare const BYOK_PROVIDERS: readonly ["anthropic", "openai", "google", "openrouter"];
|
|
3
|
+
/** Provider name subset that supports BYOK. */
|
|
4
|
+
export type ByokProvider = (typeof BYOK_PROVIDERS)[number];
|
|
5
|
+
/**
|
|
6
|
+
* Resolve which LLM provider to push a key for.
|
|
7
|
+
*
|
|
8
|
+
* Priority:
|
|
9
|
+
* 1. `flagProvider` (explicit `--provider` flag) — validated against BYOK set.
|
|
10
|
+
* Bedrock → descriptive error. Unknown provider → error.
|
|
11
|
+
* 2. `ggui.json#generation.model` — parsed via `parseAnyLlmRoute` from
|
|
12
|
+
* `@ggui-ai/protocol`. Bedrock → error. Unparseable / absent → error.
|
|
13
|
+
*
|
|
14
|
+
* **Does NOT hand-roll a provider parser.** All model-string parsing goes
|
|
15
|
+
* through `parseAnyLlmRoute` (covers both canonical `provider:model` and
|
|
16
|
+
* LiteLLM `provider/model` forms).
|
|
17
|
+
*/
|
|
18
|
+
export declare function inferProvider(gguiJson: unknown, flagProvider: string | undefined): ByokProvider | {
|
|
19
|
+
error: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Return the primary env-var name for a BYOK provider.
|
|
23
|
+
* For google the primary is `GOOGLE_API_KEY` (with `GEMINI_API_KEY` as
|
|
24
|
+
* fallback checked in `readKeyFromEnv`). Order matches `byok-resolver.ts`
|
|
25
|
+
* so that the local-dev key and the pushed key always agree.
|
|
26
|
+
*/
|
|
27
|
+
export declare function envVarForProvider(provider: ByokProvider): string;
|
|
28
|
+
/**
|
|
29
|
+
* Read the API key for a BYOK provider from the given env.
|
|
30
|
+
*
|
|
31
|
+
* - `anthropic` → `ANTHROPIC_API_KEY`
|
|
32
|
+
* - `openai` → `OPENAI_API_KEY`
|
|
33
|
+
* - `google` → `GOOGLE_API_KEY` (primary), then `GEMINI_API_KEY` (fallback)
|
|
34
|
+
* - `openrouter` → `OPENROUTER_API_KEY`
|
|
35
|
+
*
|
|
36
|
+
* Order matches `byok-resolver.ts` so that the key the cloud receives is
|
|
37
|
+
* always the same one local generation uses.
|
|
38
|
+
*
|
|
39
|
+
* Returns `undefined` when no non-empty value is found.
|
|
40
|
+
*/
|
|
41
|
+
export declare function readKeyFromEnv(provider: ByokProvider, env?: Readonly<Record<string, string | undefined>>): string | undefined;
|
|
42
|
+
export declare const PROVIDER_KEY_HELP = "ggui provider-key \u2014 manage cloud provider API keys for a ggui app\n\nUsage:\n ggui provider-key set --app <appId> [--provider <name>]\n\nSubcommands:\n set Push a provider API key from the appropriate env var to the cloud app.\n The provider is derived from ggui.json#generation.model unless\n --provider is given explicitly.\n\n Supported providers: anthropic, openai, google, openrouter.\n (bedrock is IAM-only \u2014 no plaintext key to push.)\n\n Reads the key from env:\n ANTHROPIC_API_KEY (anthropic)\n OPENAI_API_KEY (openai)\n GOOGLE_API_KEY (google, primary); GEMINI_API_KEY (fallback)\n OPENROUTER_API_KEY (openrouter)\n\nOptions:\n --app <appId> Target app ID (required).\n --provider <name> Override provider (default: derived from ggui.json).\n --help, -h Show this help.\n";
|
|
43
|
+
/**
|
|
44
|
+
* Entry point for `ggui provider-key <subcommand> [options]`.
|
|
45
|
+
*
|
|
46
|
+
* @param args - argv slice after `provider-key` (e.g. `['set', '--app', 'app123']`)
|
|
47
|
+
* @param startDir - optional cwd override (primarily for tests)
|
|
48
|
+
* @returns exit code (0 = success, 1 = error)
|
|
49
|
+
*/
|
|
50
|
+
export declare function runProviderKeyCommand(args: readonly string[], startDir?: string): Promise<number>;
|
|
51
|
+
//# sourceMappingURL=provider-key-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-key-command.d.ts","sourceRoot":"","sources":["../src/provider-key-command.ts"],"names":[],"mappings":"AAqBA,+DAA+D;AAC/D,eAAO,MAAM,cAAc,0DAA2D,CAAC;AAEvF,+CAA+C;AAC/C,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAU3D;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,OAAO,EACjB,YAAY,EAAE,MAAM,GAAG,SAAS,GAC/B,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CA8ClC;AAMD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAWhE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,YAAY,EACtB,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,MAAM,GAAG,SAAS,CAWpB;AAMD,eAAO,MAAM,iBAAiB,o4BAuB7B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,QAAQ,GAAE,MAAsB,GAC/B,OAAO,CAAC,MAAM,CAAC,CA+EjB"}
|