@ggui-ai/cli 0.27.0 → 0.29.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/dist/cli.js CHANGED
@@ -17,16 +17,17 @@ import { buildAgentRuntime, describeTunnelSession, DEV_HELP, launchBrowser, open
17
17
  import { createNullTunnelProvider, discoverTunnelProvider, } from './tunnel-provider.js';
18
18
  import { ManifestBlueprintProvider, resolveStorageFromConfig, buildNoCredentialsGguiSession, } from '@ggui-ai/mcp-server';
19
19
  import { InMemoryBlueprintProvider } from '@ggui-ai/mcp-server-core/in-memory';
20
+ import { ZERO_CONFIG_ROUTE_BY_PROVIDER } from '@ggui-ai/protocol';
20
21
  import { createByokResolver } from './byok-resolver.js';
21
22
  import { isInstalledBlueprintPath } from './internal/artifact-install.js';
22
- import { DEFAULT_ROUTE_BY_PROVIDER, GENERATION_MODEL_ENV, describeGenerationBinding, probeGenerationBinding, resolveConfiguredRoute, } from './generation-probe.js';
23
+ import { GENERATION_MODEL_ENV, describeGenerationBinding, probeGenerationBinding, resolveConfiguredRoute, } from './generation-probe.js';
23
24
  import { buildMcpServerBackend, pickFreePort } from './mcp-backend.js';
24
25
  import { FileSystemSeedPoolSource } from './filesystem-seed-pool-source.js';
25
26
  import { buildSeedPool } from '@ggui-ai/mcp-server-handlers';
26
27
  import { createThemeWriter } from './theme-writer.js';
27
28
  import { createThemeFileUploader } from './theme-file-uploader.js';
28
29
  import { getPersistentDir } from './paths.js';
29
- import { createPersistentVectorStore, layerPersistentSessionStores, } from './persistent-stores.js';
30
+ import { createPersistentVectorStores, layerPersistentSessionStores, } from './persistent-stores.js';
30
31
  import { parseServeFlags, runServe, SERVE_HELP, } from './serve-command.js';
31
32
  import { runLoginCommand } from './auth-login.js';
32
33
  import { runLogoutCommand } from './auth-logout.js';
@@ -40,6 +41,7 @@ import { runCreateCommand } from './create-app-command.js';
40
41
  import { runPushCommand } from './push-command.js';
41
42
  import { runDeployCommand } from './deploy-command.js';
42
43
  import { runProviderKeyCommand } from './provider-key-command.js';
44
+ import { localCliLoginSdkWarning } from './local-cli-login-sdk.js';
43
45
  const HELP = `ggui — open CLI for the ggui protocol
44
46
 
45
47
  Usage:
@@ -73,6 +75,8 @@ Commands:
73
75
  --port <n> Bind port (default: 6781, 0 = OS-assigned).
74
76
  --host <addr> Bind host (default: 127.0.0.1).
75
77
  --mcp-only Boot only MCP; skip agent supervision.
78
+ --no-console Serve no operator console: no admin token is
79
+ minted or printed (env GGUI_NO_CONSOLE=1).
76
80
 
77
81
  login Sign into ggui.ai (device flow). Tokens stored in
78
82
  ~/.ggui/auth.json.
@@ -310,8 +314,8 @@ async function runServeCommand(args) {
310
314
  }
311
315
  if (!manifestDeclaresVectors && storage.vectors === undefined) {
312
316
  try {
313
- const vectors = await createPersistentVectorStore(persistentDir);
314
- storage = { ...storage, vectors };
317
+ const { vectors, index } = await createPersistentVectorStores(persistentDir);
318
+ storage = { ...storage, vectors, index };
315
319
  }
316
320
  catch (err) {
317
321
  process.stderr.write(`ggui serve: persistent vector store unavailable — using in-memory `
@@ -502,6 +506,12 @@ async function runServeCommand(args) {
502
506
  // the card (key missing).
503
507
  const baseUrlForCard = parsed.publicBaseUrl ?? `http://${parsed.host}:${effectivePort}`;
504
508
  const settingsUrl = `${baseUrlForCard}/settings`;
509
+ // ggui#1167 — `--no-console` (or `GGUI_NO_CONSOLE=1` / `true`, the env twin a
510
+ // supervisor sets without touching argv): serve no operator console.
511
+ // Resolved before the key probe because the no-credentials card points
512
+ // at the console's `/settings`, which this mode does not serve.
513
+ const envNoConsole = process.env['GGUI_NO_CONSOLE']?.trim();
514
+ const noConsole = parsed.noConsole || envNoConsole === '1' || envNoConsole === 'true';
505
515
  // No-credentials cards live for an hour from mint — long enough for
506
516
  // the operator to follow the Connect-Claude link, paste a key, and
507
517
  // re-prompt before the persisted GguiSession is GC'd.
@@ -544,11 +554,12 @@ async function runServeCommand(args) {
544
554
  // fall back to an empty one so the generator has a valid
545
555
  // `blueprints` dep regardless of manifest state.
546
556
  const blueprintsForGen = blueprintProvider ?? new InMemoryBlueprintProvider();
547
- generationBinding = await probeGenerationBinding({
548
- resolver: createByokResolver({ localCliLogin: parsed.localCliLogin }),
549
- blueprints: blueprintsForGen,
550
- ...(configuredRoute ? { configuredRoute } : {}),
551
- onNoCredentials: (ctx, story) => {
557
+ // Without a console there is no `/settings` page for the card to link
558
+ // to, so under `--no-console` a key-less render fails with
559
+ // NO_CREDENTIALS instead (ggui#1167).
560
+ const onNoCredentials = noConsole
561
+ ? undefined
562
+ : (ctx, story) => {
552
563
  const nowEpochMs = Date.parse(story.nowIso);
553
564
  return buildNoCredentialsGguiSession({
554
565
  sessionId: story.sessionId,
@@ -558,7 +569,12 @@ async function runServeCommand(args) {
558
569
  expiresAt: nowEpochMs + NO_CREDENTIALS_CARD_TTL_MS,
559
570
  settingsUrl,
560
571
  });
561
- },
572
+ };
573
+ generationBinding = await probeGenerationBinding({
574
+ resolver: createByokResolver({ localCliLogin: parsed.localCliLogin }),
575
+ blueprints: blueprintsForGen,
576
+ ...(configuredRoute ? { configuredRoute } : {}),
577
+ ...(onNoCredentials ? { onNoCredentials } : {}),
562
578
  });
563
579
  }
564
580
  catch (err) {
@@ -590,13 +606,13 @@ async function runServeCommand(args) {
590
606
  ` {\n` +
591
607
  ` "schema": "1",\n` +
592
608
  ` "generation": {\n` +
593
- ` "model": "${generationBinding.provider}:${DEFAULT_ROUTE_BY_PROVIDER[generationBinding.provider].model}"\n` +
609
+ ` "model": "${generationBinding.provider}:${ZERO_CONFIG_ROUTE_BY_PROVIDER[generationBinding.provider].model}"\n` +
594
610
  ` }\n` +
595
611
  ` }\n` +
596
612
  `\n` +
597
613
  `Accepted forms:\n` +
598
- ` - canonical: "anthropic:claude-haiku-4-5-20251001"\n` +
599
- ` - LiteLLM: "anthropic/claude-haiku-4-5"\n` +
614
+ ` - canonical: "anthropic:claude-haiku-5-5"\n` +
615
+ ` - LiteLLM: "anthropic/claude-haiku-5-5"\n` +
600
616
  `\n` +
601
617
  `See docs/principles/model-string-convention.md.\n`);
602
618
  return 1;
@@ -607,6 +623,12 @@ async function runServeCommand(args) {
607
623
  if (parsed.localCliLogin && generationBinding.provider !== 'anthropic') {
608
624
  process.stderr.write(`ggui serve: --local-cli-login covers anthropic routes only; this server generates on ${generationBinding.provider}, so the flag has no effect.\n`);
609
625
  }
626
+ else if (parsed.localCliLogin) {
627
+ // ggui#1951: the login route runs on an optional peer; say so at boot.
628
+ const sdkWarning = await localCliLoginSdkWarning();
629
+ if (sdkWarning !== undefined)
630
+ process.stderr.write(sdkWarning);
631
+ }
610
632
  // Build seed pools from --seed-pool flags BEFORE composing the backend
611
633
  // factory. `buildSeedPool` is async (reads + indexes the directory
612
634
  // artifact) so it must run here in the async outer scope rather than
@@ -636,6 +658,7 @@ async function runServeCommand(args) {
636
658
  host: parsed.host,
637
659
  mcpOnly: parsed.mcpOnly,
638
660
  devAllowAll: parsed.devAllowAll,
661
+ noConsole,
639
662
  withholdResultMeta: parsed.withholdResultMeta,
640
663
  publicDemo: parsed.publicDemo,
641
664
  multiUser: parsed.multiUser,
@@ -658,6 +681,7 @@ async function runServeCommand(args) {
658
681
  publicDemo: parsed.publicDemo,
659
682
  multiUser: parsed.multiUser,
660
683
  oauth: parsed.oauth,
684
+ ...(noConsole ? { noConsole: true } : {}),
661
685
  ...(parsed.publicBaseUrl !== undefined
662
686
  ? { publicBaseUrl: parsed.publicBaseUrl }
663
687
  : {}),
@@ -10,7 +10,7 @@ import process from 'node:process';
10
10
  import { dirname } from 'node:path';
11
11
  import { appPublicEnvSchema, appThemeSchema, canonicalOverlayHash, LOOPBACK_HOST_RE, } from '@ggui-ai/protocol';
12
12
  import { loadTheme, parseGguiJson } from '@ggui-ai/project-config/node';
13
- import { z } from 'zod';
13
+ import * as z from 'zod';
14
14
  import { patchAppConfig } from './api-client.js';
15
15
  import { findGguiJson, readGadgetsFromGguiJson, readGguiJson, } from './internal/ggui-json.js';
16
16
  // ─────────────────────────────────────────────────────────────────────────────
@@ -19,8 +19,9 @@
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
- * STILL return a binding (provider=`anthropic`, model=
23
- * `claude-haiku-4-5`). Generation stays wired; the
22
+ * STILL return a binding: the Anthropic row of
23
+ * `ZERO_CONFIG_ROUTE_BY_PROVIDER` from `@ggui-ai/protocol`, which
24
+ * is the open-source default. Generation stays wired; the
24
25
  * no-credentials path now produces a Connect-Claude card
25
26
  * render via {@link GenerationDeps.onNoCredentials}
26
27
  * instead of the broken `codeReady:false` placeholder.
@@ -42,28 +43,6 @@
42
43
  */
43
44
  import type { BlueprintProvider, GenerationDeps, HandlerContext, LlmProvider, LlmRoute, GguiSession } from '@ggui-ai/mcp-server';
44
45
  import type { ByokKeyResolution, ByokResolver } from './byok-resolver.js';
45
- /**
46
- * Locked default model per provider for the OSS first-run path.
47
- *
48
- * These are the "sensible default for an operator who exported
49
- * `ANTHROPIC_API_KEY` without thinking about which model" — NOT
50
- * benchmark-tuned recommendations. A later slice wires
51
- * `ggui.json#generation.model` so operators can pick explicitly;
52
- * until then these keep the zero-config path honest.
53
- *
54
- * Notably absent: `bedrock`. AWS credentials flow through the SDK
55
- * chain, which the OSS BYOK resolver doesn't cover — the hosted runtime
56
- * binds Bedrock through its own `GenerationDeps` at the hosted
57
- * surface.
58
- */
59
- /**
60
- * Locked default route per provider, as a typed {@link LlmRoute}.
61
- * Model strings are bare wire-canonical IDs (registry KEY ==
62
- * what the provider's API expects on the wire) — there is no
63
- * transformation step downstream. `dispatchGeneration` sends
64
- * `route.model` verbatim.
65
- */
66
- export declare const DEFAULT_ROUTE_BY_PROVIDER: Readonly<Record<Exclude<LlmProvider, 'bedrock'>, LlmRoute>>;
67
46
  /**
68
47
  * Priority order for BYOK provider probing. The first provider
69
48
  * whose credentials resolve wins. Anthropic first is deliberate —
@@ -125,7 +104,7 @@ export interface ProbeGenerationBindingOptions {
125
104
  * - `configuredRoute.provider` becomes the boot-scan provider
126
105
  * (overriding the priority chain).
127
106
  * - `configuredRoute.model` is what flows to the dispatch path
128
- * (overriding `DEFAULT_ROUTE_BY_PROVIDER[provider]`).
107
+ * (overriding `ZERO_CONFIG_ROUTE_BY_PROVIDER[provider]` from `@ggui-ai/protocol`).
129
108
  *
130
109
  * Absent → fall back to today's behavior: walk the priority chain,
131
110
  * use the per-provider default. The CLI hard-fails BEFORE reaching
@@ -1 +1 @@
1
- {"version":3,"file":"generation-probe.d.ts","sourceRoot":"","sources":["../src/generation-probe.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generation-probe.d.ts","sourceRoot":"","sources":["../src/generation-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,WAAW,EACX,QAAQ,EAGR,WAAW,EACZ,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EACV,iBAAiB,EACjB,YAAY,EACb,MAAM,oBAAoB,CAAC;AAE5B;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,EAC1B,CAAC;AAElD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,0BAA0B,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,MAAM,4BAA4B,GACpC;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,UAAU,CAAA;CAAE,GACzF;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC9C,GAAG,4BAA4B,CAW/B;AAED,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,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,KACE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CACvD;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;IAC7B,0FAA0F;IAC1F,QAAQ,CAAC,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClD;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,iBAAiB,CAAC,CA2F5B;AAiBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,iBAAiB,GACzB,MAAM,CAUR"}
@@ -1,32 +1,5 @@
1
- import { parseAnyLlmRoute } from '@ggui-ai/protocol';
1
+ import { parseAnyLlmRoute, ZERO_CONFIG_ROUTE_BY_PROVIDER } from '@ggui-ai/protocol';
2
2
  import { createUiGenerator } from '@ggui-ai/ui-gen';
3
- /**
4
- * Locked default model per provider for the OSS first-run path.
5
- *
6
- * These are the "sensible default for an operator who exported
7
- * `ANTHROPIC_API_KEY` without thinking about which model" — NOT
8
- * benchmark-tuned recommendations. A later slice wires
9
- * `ggui.json#generation.model` so operators can pick explicitly;
10
- * until then these keep the zero-config path honest.
11
- *
12
- * Notably absent: `bedrock`. AWS credentials flow through the SDK
13
- * chain, which the OSS BYOK resolver doesn't cover — the hosted runtime
14
- * binds Bedrock through its own `GenerationDeps` at the hosted
15
- * surface.
16
- */
17
- /**
18
- * Locked default route per provider, as a typed {@link LlmRoute}.
19
- * Model strings are bare wire-canonical IDs (registry KEY ==
20
- * what the provider's API expects on the wire) — there is no
21
- * transformation step downstream. `dispatchGeneration` sends
22
- * `route.model` verbatim.
23
- */
24
- export const DEFAULT_ROUTE_BY_PROVIDER = {
25
- anthropic: { provider: 'anthropic', model: 'claude-haiku-4-5-20251001' },
26
- openai: { provider: 'openai', model: 'gpt-5.6-luna' },
27
- google: { provider: 'google', model: 'gemini-3.5-flash-lite' },
28
- openrouter: { provider: 'openrouter', model: 'anthropic/claude-haiku-4.5' },
29
- };
30
3
  /**
31
4
  * Priority order for BYOK provider probing. The first provider
32
5
  * whose credentials resolve wins. Anthropic first is deliberate —
@@ -110,7 +83,7 @@ export async function probeGenerationBinding(opts) {
110
83
  const defaultProvider = configured && configured.provider !== 'bedrock'
111
84
  ? configured.provider
112
85
  : bootHit?.provider ?? 'anthropic';
113
- const defaultRoute = configured ?? DEFAULT_ROUTE_BY_PROVIDER[defaultProvider];
86
+ const defaultRoute = configured ?? ZERO_CONFIG_ROUTE_BY_PROVIDER[defaultProvider];
114
87
  const defaultModel = defaultRoute.model;
115
88
  // Full-harness wire-up: `createUiGenerator` runs the same
116
89
  // multi-turn coding agent + self-check loop the bench validates;
@@ -17,7 +17,7 @@
17
17
  import { existsSync, readFileSync, writeFileSync, } from 'node:fs';
18
18
  import { dirname, join, resolve } from 'node:path';
19
19
  import { strictGadgetDescriptorSchema, } from '@ggui-ai/protocol';
20
- import { z } from 'zod';
20
+ import * as z from 'zod';
21
21
  /** Canonical filename — duplicated from `@ggui-ai/project-config` so
22
22
  * this helper stays free of that dependency. The constant lives in one
23
23
  * place per package; updates ride along on a project-config bump. */
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `--local-cli-login` generates through the Claude Agent SDK, which
3
+ * `@ggui-ai/ui-gen` declares as an optional peer (ggui#1951): its
4
+ * native binary is about 300 MB, so a plain `@ggui-ai/cli` install
5
+ * does not carry it. `ggui serve` checks at boot and warns with the
6
+ * install lines, instead of letting the first generation on the login
7
+ * route be the one that finds out.
8
+ *
9
+ * A warning, not a refusal: the login route only runs when no Anthropic
10
+ * key resolves, so a server with a key never needs the SDK.
11
+ */
12
+ import { ensureClaudeAgentSdk } from '@ggui-ai/ui-gen';
13
+ /**
14
+ * The boot warning for a missing SDK, or `undefined` when it loads.
15
+ * Any other load failure rejects unchanged. `check` is for tests.
16
+ */
17
+ export declare function localCliLoginSdkWarning(check?: typeof ensureClaudeAgentSdk): Promise<string | undefined>;
18
+ //# sourceMappingURL=local-cli-login-sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-cli-login-sdk.d.ts","sourceRoot":"","sources":["../src/local-cli-login-sdk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAA8B,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEnF;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,GAAE,OAAO,oBAA2C,GACxD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAQ7B"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * `--local-cli-login` generates through the Claude Agent SDK, which
3
+ * `@ggui-ai/ui-gen` declares as an optional peer (ggui#1951): its
4
+ * native binary is about 300 MB, so a plain `@ggui-ai/cli` install
5
+ * does not carry it. `ggui serve` checks at boot and warns with the
6
+ * install lines, instead of letting the first generation on the login
7
+ * route be the one that finds out.
8
+ *
9
+ * A warning, not a refusal: the login route only runs when no Anthropic
10
+ * key resolves, so a server with a key never needs the SDK.
11
+ */
12
+ import { ClaudeAgentSdkMissingError, ensureClaudeAgentSdk } from '@ggui-ai/ui-gen';
13
+ /**
14
+ * The boot warning for a missing SDK, or `undefined` when it loads.
15
+ * Any other load failure rejects unchanged. `check` is for tests.
16
+ */
17
+ export async function localCliLoginSdkWarning(check = ensureClaudeAgentSdk) {
18
+ try {
19
+ await check();
20
+ return undefined;
21
+ }
22
+ catch (err) {
23
+ if (!(err instanceof ClaudeAgentSdkMissingError))
24
+ throw err;
25
+ return `ggui serve: --local-cli-login generates through the Claude Agent SDK. ${err.message}\n`;
26
+ }
27
+ }
@@ -1,5 +1,5 @@
1
1
  import { type BlueprintProvider, type DiscoveredPrimitiveCatalog, type GadgetDescriptor, type GenerationDeps, type LoadedTheme, type McpServerMount, type McpUiDisplayMode, type OperatorConfig, type ResolvedStorageStores, type ShortCodeIndex, type ThemeFileUploader, type ThemeWriter } from "@ggui-ai/mcp-server";
2
- import { type BlueprintPool } from "@ggui-ai/mcp-server-handlers/renders";
2
+ import { type BlueprintPool, type InstalledBlueprintsProvider } from "@ggui-ai/mcp-server-handlers/renders";
3
3
  import type { ThemeConfig, UiManifest } from "@ggui-ai/project-config";
4
4
  import type { UiRegistry } from "@ggui-ai/ui-registry";
5
5
  import type { ServeBackend } from "./serve-command.js";
@@ -305,6 +305,13 @@ export interface BuildMcpServerBackendOptions {
305
305
  * Surface = `--admin-token <token>` CLI flag.
306
306
  */
307
307
  readonly adminToken?: string;
308
+ /**
309
+ * ggui#1167 — serve no operator console. The `console` option is not
310
+ * passed to `createGguiServer`, so no console route is mounted and no
311
+ * admin token is minted (`adminToken` is `null`). Surface =
312
+ * `--no-console` / `GGUI_NO_CONSOLE=1`.
313
+ */
314
+ readonly noConsole?: boolean;
308
315
  /**
309
316
  * Server-rendered public welcome page at `/`. Identifies who runs
310
317
  * the server (operator block, hidden when nothing configured)
@@ -352,6 +359,13 @@ export interface BuildMcpServerBackendOptions {
352
359
  */
353
360
  readonly defaultDisplayMode?: McpUiDisplayMode;
354
361
  }
362
+ /**
363
+ * Await the install walks still in flight on `provider`, for at most
364
+ * `withinMs`. Resolves either way and never rejects (`drain()` never does);
365
+ * the timer is unref'd, so it never holds the process open on its own. No
366
+ * provider (no installed blueprints) resolves at once.
367
+ */
368
+ export declare function drainWithin(provider: Pick<InstalledBlueprintsProvider, "drain"> | undefined, withinMs: number): Promise<void>;
355
369
  /**
356
370
  * Pick a free localhost TCP port by letting the OS assign one on a
357
371
  * throwaway listener. Close the listener immediately and return the
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-backend.d.ts","sourceRoot":"","sources":["../src/mcp-backend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mcp-backend.d.ts","sourceRoot":"","sources":["../src/mcp-backend.ts"],"names":[],"mappings":"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"}
@@ -40,7 +40,7 @@ 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 { createGguiServer, FileSystemCodeStore, FixedWindowRateLimiter, InMemoryAuthAdapter, InMemoryQuotaStore, InMemoryShortCodeIndex, selectEmailSenderFromEnv, } from "@ggui-ai/mcp-server";
43
+ import { createGguiServer, FileSystemCodeStore, FixedWindowRateLimiter, InMemoryAuthAdapter, InMemoryQuotaStore, InMemoryShortCodeIndex, selectEmailSenderFromEnv, DEFAULT_CLOSE_GRACE_MS, } from "@ggui-ai/mcp-server";
44
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
46
  import { createInstalledBlueprintsProvider, createStderrCacheTraceSink, installedBlueprintIntent, setCacheTraceSink, } from "@ggui-ai/mcp-server-handlers/renders";
@@ -50,6 +50,23 @@ import { createServer as createNetServer } from "node:net";
50
50
  import { join } from "node:path";
51
51
  import { getCodeCacheDir, getCredentialsFile, getEmbeddingCacheDir } from "./paths.js";
52
52
  import { readOrMintHexSecret } from "./persistent-secrets.js";
53
+ /**
54
+ * Await the install walks still in flight on `provider`, for at most
55
+ * `withinMs`. Resolves either way and never rejects (`drain()` never does);
56
+ * the timer is unref'd, so it never holds the process open on its own. No
57
+ * provider (no installed blueprints) resolves at once.
58
+ */
59
+ export async function drainWithin(provider, withinMs) {
60
+ if (provider === undefined)
61
+ return;
62
+ let timer;
63
+ const elapsed = new Promise((resolve) => {
64
+ timer = setTimeout(resolve, withinMs);
65
+ timer.unref();
66
+ });
67
+ await Promise.race([provider.drain(), elapsed]);
68
+ clearTimeout(timer);
69
+ }
53
70
  /**
54
71
  * Pick a free localhost TCP port by letting the OS assign one on a
55
72
  * throwaway listener. Close the listener immediately and return the
@@ -292,14 +309,14 @@ export function buildMcpServerBackend(opts) {
292
309
  // here on the absent-storage path; threaded back as `vectors` to
293
310
  // `createGguiServer` so server + provider share state.
294
311
  const vectorStore = storage.vectors ?? new InMemoryVectorStore();
295
- // Blueprint identity index — minted here so the install bridge's
296
- // provider deps and the server share ONE instance (the server's
297
- // ref-equality guard enforces this, same as `vectorStore`). The index
298
- // is rebuildable from vector-store metadata and self-heals at the read
299
- // site, so an in-memory index is safe even alongside a persistent
300
- // `storage.vectors`; a persistent index can be threaded later via the
301
- // same seam.
302
- const blueprintIndex = new InMemoryBlueprintIndex();
312
+ // Blueprint identity index, ONE instance for the install bridge's
313
+ // provider deps and the server (the server's ref-equality guard
314
+ // enforces this, same as `vectorStore`). It persists exactly when the
315
+ // vectors do: storage that persists vectors hands its index beside them
316
+ // (`storage.index`). An in-memory index over persistent vectors comes
317
+ // back empty after every restart, and the install walk then registers
318
+ // each installed blueprint again as a new row (ggui#1907).
319
+ const blueprintIndex = storage.index ?? new InMemoryBlueprintIndex();
303
320
  // Construct the marketplace-install bridge BEFORE `createGguiServer`
304
321
  // so we can fold it into `opts.generation.installedBlueprints` below.
305
322
  // Per design lock (project_slices_5_6_7_plan.md), the provider
@@ -472,38 +489,44 @@ export function buildMcpServerBackend(opts) {
472
489
  // field; flag flips on `oauth: true` defaults (in-memory storage,
473
490
  // built-in paste-key consent page).
474
491
  ...(opts.oauth ? { oauth: true } : {}),
475
- console: {
476
- sessionCookie: true,
477
- ...(opts.adminToken !== undefined ? { adminToken: opts.adminToken } : {}),
478
- // First-run onboarding redirect. When neither the credentials
479
- // file (`~/.ggui/credentials.json`) nor any provider env var
480
- // (ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_API_KEY /
481
- // OPENROUTER_API_KEY) is present, send `GET /` to the
482
- // **admin** onboarding flow — the operator running `ggui serve`
483
- // IS the admin on first-run, so they need the operator-key
484
- // plane (`/admin/llm-keys`) and the assistant-connection card
485
- // surfaced there. Cookie-gated end-user `/settings` is for a
486
- // *separate* user paired with the server later, not the
487
- // first-run operator. Once any key is configured, the next
488
- // visit serves the SPA normally. Scoped to the root path;
489
- // deep links bypass the redirect.
490
- landingRedirect: () => {
491
- try {
492
- const credsFileExists = existsSync(getCredentialsFile());
493
- const anyEnvKey = !!process.env.ANTHROPIC_API_KEY ||
494
- !!process.env.OPENAI_API_KEY ||
495
- !!process.env.GOOGLE_API_KEY ||
496
- !!process.env.OPENROUTER_API_KEY;
497
- if (!credsFileExists && !anyEnvKey) {
498
- return "/admin-login?next=%2Fadmin%2Fllm-keys";
499
- }
500
- return null;
501
- }
502
- catch {
503
- return null;
504
- }
505
- },
506
- },
492
+ // ggui#1167 — `--no-console` mounts no console and so mints no admin
493
+ // token: the server is an MCP endpoint only, with no operator bearer.
494
+ ...(opts.noConsole
495
+ ? {}
496
+ : {
497
+ console: {
498
+ sessionCookie: true,
499
+ ...(opts.adminToken !== undefined ? { adminToken: opts.adminToken } : {}),
500
+ // First-run onboarding redirect. When neither the credentials
501
+ // file (`~/.ggui/credentials.json`) nor any provider env var
502
+ // (ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_API_KEY /
503
+ // OPENROUTER_API_KEY) is present, send `GET /` to the
504
+ // **admin** onboarding flow — the operator running `ggui serve`
505
+ // IS the admin on first-run, so they need the operator-key
506
+ // plane (`/admin/llm-keys`) and the assistant-connection card
507
+ // surfaced there. Cookie-gated end-user `/settings` is for a
508
+ // *separate* user paired with the server later, not the
509
+ // first-run operator. Once any key is configured, the next
510
+ // visit serves the SPA normally. Scoped to the root path;
511
+ // deep links bypass the redirect.
512
+ landingRedirect: () => {
513
+ try {
514
+ const credsFileExists = existsSync(getCredentialsFile());
515
+ const anyEnvKey = !!process.env.ANTHROPIC_API_KEY ||
516
+ !!process.env.OPENAI_API_KEY ||
517
+ !!process.env.GOOGLE_API_KEY ||
518
+ !!process.env.OPENROUTER_API_KEY;
519
+ if (!credsFileExists && !anyEnvKey) {
520
+ return "/admin-login?next=%2Fadmin%2Fllm-keys";
521
+ }
522
+ return null;
523
+ }
524
+ catch {
525
+ return null;
526
+ }
527
+ },
528
+ },
529
+ }),
507
530
  // Public welcome page at `/` — operator identification + public
508
531
  // deep-link surfaces + operator-login affordance. Resolved from
509
532
  // `ggui.json#operator` + `ggui.json#app.name` by serve-command.ts;
@@ -643,7 +666,16 @@ export function buildMcpServerBackend(opts) {
643
666
  }
644
667
  return addr.port;
645
668
  },
646
- close: () => server.close(),
669
+ close: async () => {
670
+ await server.close();
671
+ // A walk can outlive the request that started it, and a persistent
672
+ // index means one cut between a blueprint's vector write and its
673
+ // index write leaves a vector with no binding. Wait for the walks
674
+ // once no request can start a new one (the server is closed), for
675
+ // at most the server's own close grace: past it, the cut is what a
676
+ // stop without a drain would have done (ggui#1907).
677
+ await drainWithin(installedBlueprintsProvider, DEFAULT_CLOSE_GRACE_MS);
678
+ },
647
679
  toolCount: server.toolCount,
648
680
  serverName: "ggui-mcp-server",
649
681
  serverVersion: opts.cliVersion,
@@ -1,4 +1,4 @@
1
- import type { GguiSessionStore, GguiSessionStreamBuffer, VectorStore } from '@ggui-ai/mcp-server-core';
1
+ import type { BlueprintIndex, GguiSessionStore, GguiSessionStreamBuffer, VectorStore } from '@ggui-ai/mcp-server-core';
2
2
  import type { ResolvedStorageStores } from '@ggui-ai/mcp-server';
3
3
  /** The persistent session store and the live channel's stream buffer, opened together. */
4
4
  export interface PersistentSessionStores {
@@ -26,7 +26,18 @@ export declare function createPersistentSessionStores(persistentDir: string): Pr
26
26
  * unavailable; the caller falls back to the in-memory defaults.
27
27
  */
28
28
  export declare function layerPersistentSessionStores(storage: ResolvedStorageStores, persistentDir: string): Promise<ResolvedStorageStores>;
29
- export declare function createPersistentVectorStore(persistentDir: string): Promise<VectorStore>;
29
+ /**
30
+ * `ggui serve`'s persistent layer for blueprints: the vector store and its
31
+ * blueprint identity index, in one `vectors.sqlite`, both or neither. An
32
+ * in-memory index beside persistent vectors comes back empty after every
33
+ * restart, and the install walk then registers each installed blueprint
34
+ * again as a new row (ggui#1907). Throws when SQLite is unavailable; the
35
+ * caller falls back to the in-memory defaults.
36
+ */
37
+ export declare function createPersistentVectorStores(persistentDir: string): Promise<{
38
+ readonly vectors: VectorStore;
39
+ readonly index: BlueprintIndex;
40
+ }>;
30
41
  /**
31
42
  * Compute the auto-default `keysFile` path. Returns `undefined` when
32
43
  * the caller wants ephemeral pairing (the operator already passed
@@ -1 +1 @@
1
- {"version":3,"file":"persistent-stores.d.ts","sourceRoot":"","sources":["../src/persistent-stores.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EACV,gBAAgB,EAChB,uBAAuB,EACvB,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE,0FAA0F;AAC1F,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,QAAQ,CAAC,YAAY,EAAE,uBAAuB,CAAC;CAChD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,6BAA6B,CACjD,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,uBAAuB,CAAC,CAalC;AAED;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,qBAAqB,EAC9B,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,qBAAqB,CAAC,CAIhC;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
+ {"version":3,"file":"persistent-stores.d.ts","sourceRoot":"","sources":["../src/persistent-stores.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE,0FAA0F;AAC1F,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,QAAQ,CAAC,YAAY,EAAE,uBAAuB,CAAC;CAChD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,6BAA6B,CACjD,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,uBAAuB,CAAC,CAalC;AAED;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,qBAAqB,EAC9B,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,qBAAqB,CAAC,CAIhC;AAED;;;;;;;GAOG;AACH,wBAAsB,4BAA4B,CAChD,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC;IAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAA;CAAE,CAAC,CAU5E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE7D"}
@@ -54,12 +54,22 @@ export async function layerPersistentSessionStores(storage, persistentDir) {
54
54
  const { renderStore, streamBuffer } = await createPersistentSessionStores(persistentDir);
55
55
  return { ...storage, renderStore, streamBuffer };
56
56
  }
57
- export async function createPersistentVectorStore(persistentDir) {
57
+ /**
58
+ * `ggui serve`'s persistent layer for blueprints: the vector store and its
59
+ * blueprint identity index, in one `vectors.sqlite`, both or neither. An
60
+ * in-memory index beside persistent vectors comes back empty after every
61
+ * restart, and the install walk then registers each installed blueprint
62
+ * again as a new row (ggui#1907). Throws when SQLite is unavailable; the
63
+ * caller falls back to the in-memory defaults.
64
+ */
65
+ export async function createPersistentVectorStores(persistentDir) {
58
66
  mkdirSync(persistentDir, { recursive: true });
59
- const { SqliteVectorStore } = await import('@ggui-ai/mcp-server-core/sqlite');
60
- return new SqliteVectorStore({
61
- filename: join(persistentDir, 'vectors.sqlite'),
62
- });
67
+ const { SqliteBlueprintIndex, SqliteVectorStore } = await import('@ggui-ai/mcp-server-core/sqlite');
68
+ const filename = join(persistentDir, 'vectors.sqlite');
69
+ return {
70
+ vectors: new SqliteVectorStore({ filename }),
71
+ index: new SqliteBlueprintIndex({ filename }),
72
+ };
63
73
  }
64
74
  /**
65
75
  * Compute the auto-default `keysFile` path. Returns `undefined` when
@@ -11,7 +11,7 @@
11
11
  /* eslint-disable no-console */
12
12
  import process from 'node:process';
13
13
  import { parseAnyLlmRoute } from '@ggui-ai/protocol';
14
- import { z } from 'zod';
14
+ import * as z from 'zod';
15
15
  import { setAppProviderKey } from './api-client.js';
16
16
  import { findGguiJson, readGguiJson } from './internal/ggui-json.js';
17
17
  // ─────────────────────────────────────────────────────────────────────────────
@@ -57,6 +57,16 @@ export interface ParsedServeFlags {
57
57
  * warning when this flag is set.
58
58
  */
59
59
  devAllowAll: boolean;
60
+ /**
61
+ * ggui#1167 — serve no operator console. No console routes are
62
+ * mounted and no console admin token is minted, so nothing prints an
63
+ * operator bearer (`admin token →`, `ADMIN_TOKEN`). For a server a
64
+ * tool launches as an MCP endpoint only, where nobody logs in to a
65
+ * console. Env twin: `GGUI_NO_CONSOLE=1`, resolved by the CLI.
66
+ * Independent of `--mcp-only`, which skips agent supervision and
67
+ * still serves the console.
68
+ */
69
+ noConsole: boolean;
60
70
  /**
61
71
  * Override the public base URL used to compose `mcpApps.wsUrl`
62
72
  * and `runtime.url`. Without this, the URLs derive from
@@ -356,6 +366,13 @@ export interface ServeBannerInputs {
356
366
  * returned null (no env var + no credentials-file entry).
357
367
  */
358
368
  readonly noLlmKey?: boolean;
369
+ /**
370
+ * ggui#1167 — the server serves no console (`--no-console`). The
371
+ * banner then advertises no landing page, and a key-less boot names
372
+ * only the env vars to export: there is no `/settings` page and no
373
+ * admin token to point at.
374
+ */
375
+ readonly noConsole?: boolean;
359
376
  /**
360
377
  * Embedding model id surfaced for the local RAG layer. When set,
361
378
  * the banner shows a `rag` line so operators see which model is
@@ -537,5 +554,5 @@ export interface RunServeOptions {
537
554
  * {@link describeServeBanner} / {@link describeAgentStatus}.
538
555
  */
539
556
  export declare function runServe(opts: RunServeOptions): Promise<number>;
540
- export declare const SERVE_HELP = "ggui serve \u2014 run the open self-hosted personal-mode app\n\nBoots the @ggui-ai/mcp-server binding with persistent defaults AND\n(by default) supervises your agent alongside it. The current MCP\nhandler surface is the blueprint-read family (search / list_featured\n/ render); more tools come online as @ggui-ai/mcp-server-handlers\ngrows.\n\nFirst-run bundle (all on by default):\n - Landing page at / \u2014 operator opens a browser and sees\n server identity + pair-code card.\n - Render viewer at /r/<code> \u2014 same-origin viewer for renders\n minted by ggui_render; same-origin\n HTTP-only cookie authenticates the\n live-channel /ws upgrade.\n - POST /pair + POST /admin/pair/init \u2014 pairing endpoint for MCP\n hosts and third-party clients.\n - Live-channel /ws \u2014 live render plane for MCP Apps\n iframes and the console.\n\nEmbedding hosts that want a different shape (no landing page, no\npairing, programmatic control) should compose createGguiServer()\ndirectly in code rather than invoking `ggui serve`.\n\nUsage:\n ggui serve [options]\n\nOptions:\n --port <n> Bind port (default: 6781, 0 = OS-assigned).\n --host <addr> Bind host (default: 127.0.0.1).\n --mcp-only Run just the MCP server; do not supervise an agent.\n --dev-allow-all Accept any non-empty bearer (incl. no bearer) as\n builder. Local-dev / tunnel smoke ONLY \u2014 never\n expose to the open internet.\n --public-demo Public-facing demo posture: same any-bearer auth\n as --dev-allow-all, plus per-IP rate limit on\n ggui_render (30 generations / 10 min) and a\n \"PUBLIC DEMO \u2014 operator pays\" banner. Mutually\n exclusive with --dev-allow-all.\n --multi-user Multi-user posture: switches the /settings LLM-\n keys gate from admin-token to auth-adapter so\n each authenticated end-user manages their OWN\n key plane (scope = userId/appId). kind:'builder'\n identities are rejected at the gate. Strict-auth\n only; mutually exclusive with --dev-allow-all\n and --public-demo. (Generation-time BYOK still\n resolves at the operator 'global' scope \u2014 a\n follow-up will thread per-user BYOK through\n ggui_render.)\n --local-cli-login Generate with the Claude login already on this\n machine (the one `claude` uses) when no\n ANTHROPIC_API_KEY is set, instead of an API key.\n Anthropic routes only; a real key always wins. Your\n Claude plan's terms and usage limits apply, and\n generation is about 2x slower (2.2x at the median)\n than with an API key. For your own use: refused with\n --multi-user and --public-demo.\n --public-base-url <u> Override the public base URL used to compose\n iframe-runtime + shortCode URLs. Set this to a\n tunnel URL (https://<random>.trycloudflare.com)\n when testing against a remote MCP host so URLs\n resolve from the host's perspective. Without\n this, URLs derive from --host:--port and only\n work from the same machine.\n --browser-origin <o> Allow pages at this origin past MCP-wire Origin\n validation (repeatable; env GGUI_BROWSER_ORIGINS,\n comma-separated). Pages on localhost need no\n entry. NOT authentication \u2014 /mcp still requires\n a bearer. Non-browser clients are unaffected.\n --oauth Mount OAuth 2.1 + PKCE + Dynamic Client\n Registration routes (.well-known/oauth-* +\n /oauth/{authorize,token,register}). Required\n for MCP custom-connector hosts (claude.ai,\n ChatGPT) whose Add Connector form has no field\n for a pre-shared bearer; OAuth lets them do\n a discovery \u2192 DCR \u2192 consent dance ending in\n the same paired bearer the strict-auth flow\n already mints. Pure-bearer clients (Claude\n Desktop with bearer in config) work without it.\n --admin-token <t> Pin the admin bearer that gates the console\n /keys plane (list / mint / revoke pairings +\n /admin-login route). Without this, the server\n mints a fresh ggui_admin_* token per boot and\n prints it on the banner. Pin a stable value\n when you want the bearer to survive restarts\n (e.g. paste once into a password manager).\n --ephemeral Disable cross-restart persistence. Without\n this flag, the HMAC secrets that sign MCP\n Apps bootstrap tokens and signed render URLs\n are read or minted under .ggui/persistent/\n (project-local if a ggui.json was resolved,\n else ~/.ggui/persistent/) so cached tokens\n survive a server restart. Pass this flag to\n keep restarts ephemeral (tests / CI / nuclear-\n revoke). Override the dir via GGUI_PERSISTENT_DIR.\n --seed-pool <dir> Load a read-only shared blueprint pool from a\n directory artifact (repeatable). Blueprints in it\n are reused by exact contract match, after your own.\n --help, -h Show this help.\n\nAgent runtime:\n The default boots your agent alongside MCP. The agent's entry file\n comes from ggui.json:\n\n { \"agent\": { \"entry\": \"./agent.ts\" } }\n\n Supported extensions: .js / .mjs / .cjs (node), .ts / .tsx / .mts\n (node --import=tsx). No ggui.json, or no agent.entry, falls back\n to MCP-only with a warning. Malformed ggui.json or a bad entry\n is a hard error.\n\n Agent crashes after startup are logged; the MCP server keeps\n running. No auto-restart \u2014 compose that with your own supervisor.\n\nPersistent storage (default-on; opt-out via --ephemeral):\n By default `ggui serve` writes a small bundle under .ggui/persistent/\n (project-local if a ggui.json was resolved, else ~/.ggui/persistent/)\n so cached MCP Apps tokens, shortCodes, renders,\n vectors, and paired bearers survive a restart \u2014 claude.ai chat-history\n revisits keep working.\n\n Bundle layout:\n\n .ggui/persistent/\n \u251C\u2500\u2500 ws-token-secret.hex (HMAC, 0600)\n \u251C\u2500\u2500 short-codes.sqlite (signed render-URL resolution)\n \u251C\u2500\u2500 sessions.sqlite (GguiSessionStore \u2014 renders + event history)\n \u251C\u2500\u2500 vectors.sqlite (RAG corpus)\n \u2514\u2500\u2500 keys.json (paired bearers)\n\n Override the dir with `GGUI_PERSISTENT_DIR`. Pass `--ephemeral` to\n skip the bundle entirely (tests, CI loops, nuclear-revoke).\n\n Explicit ggui.json#storage declarations always win \u2014 declare\n `{ \"renders\": { \"driver\": \"memory\" } }` to opt renders back to\n in-memory while keeping other surfaces persistent. Custom sqlite\n paths via ggui.json#storage are honored verbatim:\n\n {\n \"storage\": {\n \"renders\": { \"driver\": \"sqlite\", \"path\": \"./ggui-sessions.sqlite\" },\n \"vectors\": { \"driver\": \"sqlite\", \"path\": \"./ggui-vectors.sqlite\" },\n \"threads\": { \"driver\": \"sqlite\", \"path\": \"./ggui-threads.sqlite\" }\n }\n }\n\n Threads: the persistent-chat route family (/threads) is opt-in.\n Absent `storage.threads` leaves the routes unmounted \u2014 pairs\n with Portal's self-hosted origins only when declared. Driver\n semantics:\n - `driver: \"sqlite\"` \u2014 durable. Threads survive restart.\n /ggui/health reports `threads.durability: \"durable\"`;\n Portal hides its non-durable caveat.\n - `driver: \"memory\"` \u2014 routes mount but data is lost on\n restart. /ggui/health reports `threads.durability:\n \"ephemeral\"`; Portal shows its caveat.\n\n Paths resolve relative to the ggui.json directory. The sqlite\n driver needs `better-sqlite3` installed in your project.\n\nRecommended setups:\n\n Local-only smoke (no tunnel, no remote MCP host):\n ggui serve\n\n claude.ai custom connector (over a public tunnel):\n 1. Tunnel: cloudflared tunnel --url http://localhost:6781\n 2. ggui serve --oauth \\\n --public-base-url https://<tunnel>.trycloudflare.com\n 3. Note PAIR_CODE from boot. Visit the landing page (the public URL\n above), complete pairing, save the bearer.\n 4. claude.ai \u2192 Settings \u2192 Connectors \u2192 Add custom connector.\n URL: https://<tunnel>.trycloudflare.com/mcp\n Client ID / Secret: leave empty (server uses Dynamic Client\n Registration; no pre-shared secret needed).\n 5. Click Connect \u2192 claude.ai redirects you to a paste-key page.\n Paste the bearer from step 3. Done.\n\n Quick local-dev WITHOUT auth (NEVER over a public tunnel):\n ggui serve --dev-allow-all\n\nCurrent limits:\n - Strict-auth only: /mcp rejects any bearer that isn't pair-minted.\n The CLI pre-mints one pairing code on boot (printed as\n `PAIR_CODE <code>` and shown in the banner + on the landing\n page). Complete `POST /pair {code, deviceName}` to receive a\n bearer you can use with MCP. Embed @ggui-ai/mcp-server in your\n own entrypoint to inject a different AuthAdapter (OIDC, Cognito,\n etc.) for production.\n - Single-user: every request scopes to one `builder` app id.\n";
557
+ export declare const SERVE_HELP = "ggui serve \u2014 run the open self-hosted personal-mode app\n\nBoots the @ggui-ai/mcp-server binding with persistent defaults AND\n(by default) supervises your agent alongside it. The current MCP\nhandler surface is the blueprint-read family (search / list_featured\n/ render); more tools come online as @ggui-ai/mcp-server-handlers\ngrows.\n\nFirst-run bundle (all on by default):\n - Landing page at / \u2014 operator opens a browser and sees\n server identity + pair-code card.\n - Render viewer at /r/<code> \u2014 same-origin viewer for renders\n minted by ggui_render; same-origin\n HTTP-only cookie authenticates the\n live-channel /ws upgrade.\n - POST /pair + POST /admin/pair/init \u2014 pairing endpoint for MCP\n hosts and third-party clients.\n - Live-channel /ws \u2014 live render plane for MCP Apps\n iframes and the console.\n\nEmbedding hosts that want a different shape (no landing page, no\npairing, programmatic control) should compose createGguiServer()\ndirectly in code rather than invoking `ggui serve`.\n\nUsage:\n ggui serve [options]\n\nOptions:\n --port <n> Bind port (default: 6781, 0 = OS-assigned).\n --host <addr> Bind host (default: 127.0.0.1).\n --mcp-only Run just the MCP server; do not supervise an agent.\n --dev-allow-all Accept any non-empty bearer (incl. no bearer) as\n builder. Local-dev / tunnel smoke ONLY \u2014 never\n expose to the open internet.\n --public-demo Public-facing demo posture: same any-bearer auth\n as --dev-allow-all, plus per-IP rate limit on\n ggui_render (30 generations / 10 min) and a\n \"PUBLIC DEMO \u2014 operator pays\" banner. Mutually\n exclusive with --dev-allow-all.\n --multi-user Multi-user posture: switches the /settings LLM-\n keys gate from admin-token to auth-adapter so\n each authenticated end-user manages their OWN\n key plane (scope = userId/appId). kind:'builder'\n identities are rejected at the gate. Strict-auth\n only; mutually exclusive with --dev-allow-all\n and --public-demo. (Generation-time BYOK still\n resolves at the operator 'global' scope \u2014 a\n follow-up will thread per-user BYOK through\n ggui_render.)\n --local-cli-login Generate with the Claude login already on this\n machine (the one `claude` uses) when no\n ANTHROPIC_API_KEY is set, instead of an API key.\n Anthropic routes only; a real key always wins. Your\n Claude plan's terms and usage limits apply, and\n generation is about 2x slower (2.2x at the median)\n than with an API key. For your own use: refused with\n --multi-user and --public-demo. Needs\n @anthropic-ai/claude-agent-sdk installed beside\n ggui (not installed with it); boot says how.\n --public-base-url <u> Override the public base URL used to compose\n iframe-runtime + shortCode URLs. Set this to a\n tunnel URL (https://<random>.trycloudflare.com)\n when testing against a remote MCP host so URLs\n resolve from the host's perspective. Without\n this, URLs derive from --host:--port and only\n work from the same machine.\n --browser-origin <o> Allow pages at this origin past MCP-wire Origin\n validation (repeatable; env GGUI_BROWSER_ORIGINS,\n comma-separated). Pages on localhost need no\n entry. NOT authentication \u2014 /mcp still requires\n a bearer. Non-browser clients are unaffected.\n --oauth Mount OAuth 2.1 + PKCE + Dynamic Client\n Registration routes (.well-known/oauth-* +\n /oauth/{authorize,token,register}). Required\n for MCP custom-connector hosts (claude.ai,\n ChatGPT) whose Add Connector form has no field\n for a pre-shared bearer; OAuth lets them do\n a discovery \u2192 DCR \u2192 consent dance ending in\n the same paired bearer the strict-auth flow\n already mints. Pure-bearer clients (Claude\n Desktop with bearer in config) work without it.\n --admin-token <t> Pin the admin bearer that gates the console\n /keys plane (list / mint / revoke pairings +\n /admin-login route). Without this, the server\n mints a fresh ggui_admin_* token per boot and\n prints it on the banner. Pin a stable value\n when you want the bearer to survive restarts\n (e.g. paste once into a password manager).\n --ephemeral Disable cross-restart persistence. Without\n this flag, the HMAC secrets that sign MCP\n Apps bootstrap tokens and signed render URLs\n are read or minted under .ggui/persistent/\n (project-local if a ggui.json was resolved,\n else ~/.ggui/persistent/) so cached tokens\n survive a server restart. Pass this flag to\n keep restarts ephemeral (tests / CI / nuclear-\n revoke). Override the dir via GGUI_PERSISTENT_DIR.\n --seed-pool <dir> Load a read-only shared blueprint pool from a\n directory artifact (repeatable). Blueprints in it\n are reused by exact contract match, after your own.\n --help, -h Show this help.\n\nAgent runtime:\n The default boots your agent alongside MCP. The agent's entry file\n comes from ggui.json:\n\n { \"agent\": { \"entry\": \"./agent.ts\" } }\n\n Supported extensions: .js / .mjs / .cjs (node), .ts / .tsx / .mts\n (node --import=tsx). No ggui.json, or no agent.entry, falls back\n to MCP-only with a warning. Malformed ggui.json or a bad entry\n is a hard error.\n\n Agent crashes after startup are logged; the MCP server keeps\n running. No auto-restart \u2014 compose that with your own supervisor.\n\nPersistent storage (default-on; opt-out via --ephemeral):\n By default `ggui serve` writes a small bundle under .ggui/persistent/\n (project-local if a ggui.json was resolved, else ~/.ggui/persistent/)\n so cached MCP Apps tokens, shortCodes, renders,\n vectors, and paired bearers survive a restart \u2014 claude.ai chat-history\n revisits keep working.\n\n Bundle layout:\n\n .ggui/persistent/\n \u251C\u2500\u2500 ws-token-secret.hex (HMAC, 0600)\n \u251C\u2500\u2500 short-codes.sqlite (signed render-URL resolution)\n \u251C\u2500\u2500 sessions.sqlite (GguiSessionStore \u2014 renders + event history)\n \u251C\u2500\u2500 vectors.sqlite (blueprint cache \u2014 vectors + their identity index)\n \u2514\u2500\u2500 keys.json (paired bearers)\n\n Override the dir with `GGUI_PERSISTENT_DIR`. Pass `--ephemeral` to\n skip the bundle entirely (tests, CI loops, nuclear-revoke).\n\n Explicit ggui.json#storage declarations always win \u2014 declare\n `{ \"renders\": { \"driver\": \"memory\" } }` to opt renders back to\n in-memory while keeping other surfaces persistent. Custom sqlite\n paths via ggui.json#storage are honored verbatim:\n\n {\n \"storage\": {\n \"renders\": { \"driver\": \"sqlite\", \"path\": \"./ggui-sessions.sqlite\" },\n \"vectors\": { \"driver\": \"sqlite\", \"path\": \"./ggui-vectors.sqlite\" },\n \"threads\": { \"driver\": \"sqlite\", \"path\": \"./ggui-threads.sqlite\" }\n }\n }\n\n Threads: the persistent-chat route family (/threads) is opt-in.\n Absent `storage.threads` leaves the routes unmounted \u2014 pairs\n with Portal's self-hosted origins only when declared. Driver\n semantics:\n - `driver: \"sqlite\"` \u2014 durable. Threads survive restart.\n /ggui/health reports `threads.durability: \"durable\"`;\n Portal hides its non-durable caveat.\n - `driver: \"memory\"` \u2014 routes mount but data is lost on\n restart. /ggui/health reports `threads.durability:\n \"ephemeral\"`; Portal shows its caveat.\n\n Paths resolve relative to the ggui.json directory. The sqlite\n driver needs `better-sqlite3` installed in your project.\n\nRecommended setups:\n\n Local-only smoke (no tunnel, no remote MCP host):\n ggui serve\n\n claude.ai custom connector (over a public tunnel):\n 1. Tunnel: cloudflared tunnel --url http://localhost:6781\n 2. ggui serve --oauth \\\n --public-base-url https://<tunnel>.trycloudflare.com\n 3. Note PAIR_CODE from boot. Visit the landing page (the public URL\n above), complete pairing, save the bearer.\n 4. claude.ai \u2192 Settings \u2192 Connectors \u2192 Add custom connector.\n URL: https://<tunnel>.trycloudflare.com/mcp\n Client ID / Secret: leave empty (server uses Dynamic Client\n Registration; no pre-shared secret needed).\n 5. Click Connect \u2192 claude.ai redirects you to a paste-key page.\n Paste the bearer from step 3. Done.\n\n Quick local-dev WITHOUT auth (NEVER over a public tunnel):\n ggui serve --dev-allow-all\n\nCurrent limits:\n - Strict-auth only: /mcp rejects any bearer that isn't pair-minted.\n The CLI pre-mints one pairing code on boot (printed as\n `PAIR_CODE <code>` and shown in the banner + on the landing\n page). Complete `POST /pair {code, deviceName}` to receive a\n bearer you can use with MCP. Embed @ggui-ai/mcp-server in your\n own entrypoint to inject a different AuthAdapter (OIDC, Cognito,\n etc.) for production.\n - Single-user: every request scopes to one `builder` app id.\n";
541
558
  //# sourceMappingURL=serve-command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serve-command.d.ts","sourceRoot":"","sources":["../src/serve-command.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serve-command.d.ts","sourceRoot":"","sources":["../src/serve-command.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,7 @@ export function parseServeFlags(args) {
23
23
  host: DEFAULT_SERVE_HOST,
24
24
  mcpOnly: false,
25
25
  devAllowAll: false,
26
+ noConsole: false,
26
27
  withholdResultMeta: false,
27
28
  publicDemo: false,
28
29
  multiUser: false,
@@ -60,6 +61,10 @@ export function parseServeFlags(args) {
60
61
  out.mcpOnly = true;
61
62
  continue;
62
63
  }
64
+ if (arg === '--no-console') {
65
+ out.noConsole = true;
66
+ continue;
67
+ }
63
68
  if (arg === '--withhold-result-meta') {
64
69
  out.withholdResultMeta = true;
65
70
  continue;
@@ -214,7 +219,7 @@ export function describeServeBanner(input) {
214
219
  // signal that closes the first-run story. Landing page lives at `/`
215
220
  // by default when `ggui serve` is the entry point (see
216
221
  // `cli.ts::buildMcpServerBackend`).
217
- ` open → ${httpUrl}/`,
222
+ ...(input.noConsole ? [] : [` open → ${httpUrl}/`]),
218
223
  ` mcp → ${httpUrl}/mcp`,
219
224
  ` health → ${httpUrl}/ggui/health`,
220
225
  ` tools → ${input.toolCount} registered`,
@@ -241,7 +246,12 @@ export function describeServeBanner(input) {
241
246
  // both bearers are obvious in the boot banner.
242
247
  lines.push(` admin token → ${input.adminToken} (console /keys gate — paste at /admin-login)`);
243
248
  }
244
- if (input.noLlmKey) {
249
+ if (input.noLlmKey && input.noConsole) {
250
+ // ggui#1167 — no console, so no /settings page and no admin token:
251
+ // the only way to add a key is the environment.
252
+ lines.push(` ⚠ no LLM key configured. export ANTHROPIC_API_KEY /`, ` OPENAI_API_KEY / GOOGLE_API_KEY / OPENROUTER_API_KEY`, ` before re-running.`);
253
+ }
254
+ else if (input.noLlmKey) {
245
255
  // First-run nudge — without an LLM key, ggui_render falls back to
246
256
  // the Connect-Claude card and the iframe never bootstraps. Surface
247
257
  // the /settings URL alongside admin-token so the operator can paste
@@ -408,6 +418,7 @@ export async function runServe(opts) {
408
418
  : {}),
409
419
  ...(backend.adminToken ? { adminToken: backend.adminToken } : {}),
410
420
  ...(opts.noLlmKey ? { noLlmKey: true } : {}),
421
+ ...(opts.flags.noConsole ? { noConsole: true } : {}),
411
422
  ...(backend.embeddingModel ? { embeddingModel: backend.embeddingModel } : {}),
412
423
  ...(opts.flags.browserOrigins.length > 0
413
424
  ? { browserOrigins: opts.flags.browserOrigins }
@@ -419,12 +430,14 @@ export async function runServe(opts) {
419
430
  // to the admin onboarding flow at `/admin-login?next=/admin/llm-keys`,
420
431
  // so the URL bar lands on the right page without the CLI needing
421
432
  // to know which target to pick. Skipped for: `--no-open`, non-TTY
422
- // (CI / supervised / piped), `--mcp-only` (no UI surface to open).
433
+ // (CI / supervised / piped), `--mcp-only` (no UI surface to open),
434
+ // `--no-console` (no console to open, ggui#1167).
423
435
  // Errors are swallowed — the banner already shows the URL so the
424
436
  // operator can copy/paste if launchBrowser fails (xdg-open missing,
425
437
  // etc.).
426
438
  const shouldAutoOpenServe = !opts.flags.noOpen
427
439
  && !opts.flags.mcpOnly
440
+ && !opts.flags.noConsole
428
441
  && opts.stdout === process.stdout
429
442
  && process.stdout.isTTY === true;
430
443
  if (shouldAutoOpenServe) {
@@ -521,7 +534,9 @@ Options:
521
534
  Claude plan's terms and usage limits apply, and
522
535
  generation is about 2x slower (2.2x at the median)
523
536
  than with an API key. For your own use: refused with
524
- --multi-user and --public-demo.
537
+ --multi-user and --public-demo. Needs
538
+ @anthropic-ai/claude-agent-sdk installed beside
539
+ ggui (not installed with it); boot says how.
525
540
  --public-base-url <u> Override the public base URL used to compose
526
541
  iframe-runtime + shortCode URLs. Set this to a
527
542
  tunnel URL (https://<random>.trycloudflare.com)
@@ -592,7 +607,7 @@ Persistent storage (default-on; opt-out via --ephemeral):
592
607
  ├── ws-token-secret.hex (HMAC, 0600)
593
608
  ├── short-codes.sqlite (signed render-URL resolution)
594
609
  ├── sessions.sqlite (GguiSessionStore — renders + event history)
595
- ├── vectors.sqlite (RAG corpus)
610
+ ├── vectors.sqlite (blueprint cache — vectors + their identity index)
596
611
  └── keys.json (paired bearers)
597
612
 
598
613
  Override the dir with \`GGUI_PERSISTENT_DIR\`. Pass \`--ephemeral\` to
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ggui-ai/cli",
3
- "version": "0.27.0",
4
- "description": "The `ggui` CLI — run the ggui protocol locally: dev server, self-hosted MCP server, and gadget / blueprint authoring. End-to-end OSS and self-hosted workflows; no account required.",
3
+ "version": "0.29.0",
4
+ "description": "The `ggui` CLI — run the GGUI protocol locally: dev server, self-hosted MCP server, and gadget / blueprint authoring. End-to-end OSS and self-hosted workflows; no account required.",
5
5
  "keywords": [
6
6
  "ggui",
7
7
  "cli",
@@ -36,28 +36,28 @@
36
36
  "@huggingface/transformers": "^3.0.0",
37
37
  "@noble/hashes": "^1.6.0",
38
38
  "esbuild": "^0.28.2",
39
- "zod": "^4.3.6",
40
- "@ggui-ai/agent-runtime": "0.27.0",
41
- "@ggui-ai/artifact-manifest": "0.27.0",
42
- "@ggui-ai/dev-stack": "0.27.0",
43
- "@ggui-ai/design": "0.27.0",
44
- "@ggui-ai/gadget-signing": "0.27.0",
45
- "@ggui-ai/mcp-server": "0.27.0",
46
- "@ggui-ai/project-config": "0.27.0",
47
- "@ggui-ai/preview-a2ui": "0.27.0",
48
- "@ggui-ai/protocol": "0.27.0",
49
- "@ggui-ai/registry-core": "0.27.0",
50
- "@ggui-ai/embedding-local": "0.27.0",
51
- "@ggui-ai/mcp-server-handlers": "0.27.0",
52
- "@ggui-ai/iframe-runtime": "0.27.0",
53
- "@ggui-ai/ui-gen": "0.27.0",
54
- "@ggui-ai/ui-registry": "0.27.0",
55
- "@ggui-ai/mcp-server-core": "0.27.0"
39
+ "zod": "^4.6.6",
40
+ "@ggui-ai/dev-stack": "0.29.0",
41
+ "@ggui-ai/gadget-signing": "0.29.0",
42
+ "@ggui-ai/agent-runtime": "0.29.0",
43
+ "@ggui-ai/embedding-local": "0.29.0",
44
+ "@ggui-ai/artifact-manifest": "0.29.0",
45
+ "@ggui-ai/design": "0.29.0",
46
+ "@ggui-ai/mcp-server-handlers": "0.29.0",
47
+ "@ggui-ai/project-config": "0.29.0",
48
+ "@ggui-ai/preview-a2ui": "0.29.0",
49
+ "@ggui-ai/mcp-server-core": "0.29.0",
50
+ "@ggui-ai/protocol": "0.29.0",
51
+ "@ggui-ai/mcp-server": "0.29.0",
52
+ "@ggui-ai/iframe-runtime": "0.29.0",
53
+ "@ggui-ai/registry-core": "0.29.0",
54
+ "@ggui-ai/ui-registry": "0.29.0",
55
+ "@ggui-ai/ui-gen": "0.29.0"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@types/node": "^24.0.0",
59
59
  "typescript": "^5.0.0",
60
- "vitest": "^3.2.6"
60
+ "vitest": "^4.1.11"
61
61
  },
62
62
  "repository": {
63
63
  "type": "git",