@ggui-ai/cli 0.20.0 → 0.21.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.
@@ -78,6 +78,14 @@ export interface ByokKeyResolution {
78
78
  * that's what fired, instead of a generic "google env".
79
79
  */
80
80
  readonly envName?: string;
81
+ /**
82
+ * ggui#1132 — env names in this provider's alias list that were ALSO set,
83
+ * to a DIFFERENT value, and lost to `envName` under the locked precedence.
84
+ * Names only, never values. A stale key in a shadowed alias is exactly
85
+ * the silent failure this exists to make loud: a rotated `GEMINI_API_KEY`
86
+ * that a stale `GOOGLE_API_KEY` beats. Absent ⇒ no conflict.
87
+ */
88
+ readonly shadowedEnvNames?: readonly string[];
81
89
  }
82
90
  export interface ByokResolverOptions {
83
91
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"byok-resolver.d.ts","sourceRoot":"","sources":["../src/byok-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,OAAO,KAAK,EACV,WAAW,EAEX,gBAAgB,EACjB,MAAM,0BAA0B,CAAC;AAIlC;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAEnE,uEAAuE;AACvE,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAE9C;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CACvC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC,CAM3D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,kBAAkB,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;;;OASG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC9C;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,CACL,QAAQ,EAAE,WAAW,EACrB,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;CACtC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,GAAE,mBAAwB,GAC7B,YAAY,CA4Ed"}
1
+ {"version":3,"file":"byok-resolver.d.ts","sourceRoot":"","sources":["../src/byok-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,OAAO,KAAK,EACV,WAAW,EAEX,gBAAgB,EACjB,MAAM,0BAA0B,CAAC;AAIlC;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAEnE,uEAAuE;AACvE,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAE9C;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CACvC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC,CAM3D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,kBAAkB,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/C;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;;;OASG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC9C;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,CACL,QAAQ,EAAE,WAAW,EACrB,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;CACtC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,GAAE,mBAAwB,GAC7B,YAAY,CAoFd"}
@@ -59,11 +59,20 @@ export function createByokResolver(opts = {}) {
59
59
  for (const name of envNames) {
60
60
  const value = env[name];
61
61
  if (value !== undefined && value.length > 0) {
62
+ // ggui#1132: name every OTHER alias that is set to a different
63
+ // value — it lost silently under the locked precedence.
64
+ const shadowed = envNames.filter((other) => {
65
+ if (other === name)
66
+ return false;
67
+ const v = env[other];
68
+ return v !== undefined && v.length > 0 && v !== value;
69
+ });
62
70
  return {
63
71
  key: value,
64
72
  source: 'env',
65
73
  provider,
66
74
  envName: name,
75
+ ...(shadowed.length > 0 ? { shadowedEnvNames: shadowed } : {}),
67
76
  };
68
77
  }
69
78
  }
@@ -174,6 +174,8 @@ export interface GenerationBinding {
174
174
  readonly keySource?: ByokKeyResolution['source'];
175
175
  /** Env-var name that produced the boot-scan hit, when applicable. */
176
176
  readonly keyEnvName?: string;
177
+ /** ggui#1132 — aliases set to a different value that lost to `keyEnvName`; names only. */
178
+ readonly keyShadowedEnvNames?: readonly string[];
177
179
  }
178
180
  /**
179
181
  * Build a `GenerationBinding` whose `resolveLlm` does live BYOK
@@ -1 +1 @@
1
- {"version":3,"file":"generation-probe.d.ts","sourceRoot":"","sources":["../src/generation-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;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;;;;;;;;;;;;;GAaG;AACH;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAC9C,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,QAAQ,CAAC,CAMlD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,EAC1B,CAAC;AAElD;;;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;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,iBAAiB,CAAC,CAwF5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,iBAAiB,GACzB,MAAM,CAWR"}
1
+ {"version":3,"file":"generation-probe.d.ts","sourceRoot":"","sources":["../src/generation-probe.ts"],"names":[],"mappings":"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"}
@@ -150,6 +150,9 @@ export async function probeGenerationBinding(opts) {
150
150
  ...(bootHit.envName !== undefined
151
151
  ? { keyEnvName: bootHit.envName }
152
152
  : {}),
153
+ ...(bootHit.shadowedEnvNames !== undefined
154
+ ? { keyShadowedEnvNames: bootHit.shadowedEnvNames }
155
+ : {}),
153
156
  }
154
157
  : {
155
158
  generation,
@@ -181,5 +184,8 @@ export function describeGenerationBinding(binding) {
181
184
  ? `env: ${binding.keyEnvName}`
182
185
  : 'env'
183
186
  : 'credentials-file';
184
- return `generation: ${binding.provider} / ${binding.model} (${src})`;
187
+ const conflict = binding.keyShadowedEnvNames !== undefined && binding.keyEnvName !== undefined
188
+ ? ` — ${binding.keyEnvName} won; ${binding.keyShadowedEnvNames.join(', ')} also set and differs (rotate both or unset one, ggui#1132)`
189
+ : '';
190
+ return `generation: ${binding.provider} / ${binding.model} (${src})${conflict}`;
185
191
  }
@@ -30,7 +30,13 @@ export declare function envVarForProvider(provider: ByokProvider): string;
30
30
  *
31
31
  * - `anthropic` → `ANTHROPIC_API_KEY`
32
32
  * - `openai` → `OPENAI_API_KEY`
33
- * - `google` → `GOOGLE_API_KEY` (primary), then `GEMINI_API_KEY` (fallback)
33
+ * - `google` → `GOOGLE_API_KEY` (primary), then `GEMINI_API_KEY` (fallback).
34
+ * The precedence is locked (Google's own SDKs prefer
35
+ * `GOOGLE_API_KEY` when both are set, and the hosted key
36
+ * routes mirror this order). When both are set to DIFFERENT
37
+ * values the loser is named — see {@link describeAliasConflict}
38
+ * — because a stale key in the shadowed alias is the failure
39
+ * that otherwise happens silently (ggui#1132).
34
40
  * - `openrouter` → `OPENROUTER_API_KEY`
35
41
  *
36
42
  * Order matches `byok-resolver.ts` so that the key the cloud receives is
@@ -39,7 +45,13 @@ export declare function envVarForProvider(provider: ByokProvider): string;
39
45
  * Returns `undefined` when no non-empty value is found.
40
46
  */
41
47
  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";
48
+ /**
49
+ * ggui#1132 — one line naming an alias conflict, or `undefined` when there is
50
+ * none. Only `google` has aliases. Names only, never values: the line is
51
+ * printed to stderr by `ggui provider-key set` and by the `ggui serve` banner.
52
+ */
53
+ export declare function describeAliasConflict(provider: ByokProvider, env?: Readonly<Record<string, string | undefined>>): string | undefined;
54
+ 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 \u2014 if both are set and differ, the shadowed one is named\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
55
  /**
44
56
  * Entry point for `ggui provider-key <subcommand> [options]`.
45
57
  *
@@ -1 +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"}
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;;;;;;;;;;;;;;;;;;GAkBG;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;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,YAAY,EACtB,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,MAAM,GAAG,SAAS,CAUpB;AAMD,eAAO,MAAM,iBAAiB,+9BAwB7B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,QAAQ,GAAE,MAAsB,GAC/B,OAAO,CAAC,MAAM,CAAC,CAiFjB"}
@@ -103,7 +103,13 @@ export function envVarForProvider(provider) {
103
103
  *
104
104
  * - `anthropic` → `ANTHROPIC_API_KEY`
105
105
  * - `openai` → `OPENAI_API_KEY`
106
- * - `google` → `GOOGLE_API_KEY` (primary), then `GEMINI_API_KEY` (fallback)
106
+ * - `google` → `GOOGLE_API_KEY` (primary), then `GEMINI_API_KEY` (fallback).
107
+ * The precedence is locked (Google's own SDKs prefer
108
+ * `GOOGLE_API_KEY` when both are set, and the hosted key
109
+ * routes mirror this order). When both are set to DIFFERENT
110
+ * values the loser is named — see {@link describeAliasConflict}
111
+ * — because a stale key in the shadowed alias is the failure
112
+ * that otherwise happens silently (ggui#1132).
107
113
  * - `openrouter` → `OPENROUTER_API_KEY`
108
114
  *
109
115
  * Order matches `byok-resolver.ts` so that the key the cloud receives is
@@ -122,6 +128,22 @@ export function readKeyFromEnv(provider, env = process.env) {
122
128
  }
123
129
  return undefined;
124
130
  }
131
+ /**
132
+ * ggui#1132 — one line naming an alias conflict, or `undefined` when there is
133
+ * none. Only `google` has aliases. Names only, never values: the line is
134
+ * printed to stderr by `ggui provider-key set` and by the `ggui serve` banner.
135
+ */
136
+ export function describeAliasConflict(provider, env = process.env) {
137
+ if (provider !== 'google')
138
+ return undefined;
139
+ const [primary, fallback] = ['GOOGLE_API_KEY', 'GEMINI_API_KEY'];
140
+ const a = env[primary];
141
+ const b = env[fallback];
142
+ if (!a || !b || a === b)
143
+ return undefined;
144
+ return (`${primary} won; ${fallback} is also set and differs — ` +
145
+ `a stale key in the shadowed alias wins silently, so rotate both or unset one (ggui#1132).`);
146
+ }
125
147
  // ─────────────────────────────────────────────────────────────────────────────
126
148
  // Command runner
127
149
  // ─────────────────────────────────────────────────────────────────────────────
@@ -142,6 +164,7 @@ Subcommands:
142
164
  ANTHROPIC_API_KEY (anthropic)
143
165
  OPENAI_API_KEY (openai)
144
166
  GOOGLE_API_KEY (google, primary); GEMINI_API_KEY (fallback)
167
+ — if both are set and differ, the shadowed one is named
145
168
  OPENROUTER_API_KEY (openrouter)
146
169
 
147
170
  Options:
@@ -201,6 +224,9 @@ export async function runProviderKeyCommand(args, startDir = process.cwd()) {
201
224
  const provider = providerResult;
202
225
  // ── Read key from env ────────────────────────────────────────────────────
203
226
  const key = readKeyFromEnv(provider);
227
+ const conflict = describeAliasConflict(provider);
228
+ if (conflict !== undefined)
229
+ process.stderr.write(`ggui provider-key set: ${conflict}\n`);
204
230
  if (!key) {
205
231
  const primaryVar = envVarForProvider(provider);
206
232
  const hint = provider === 'google'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/cli",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
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",
@@ -37,22 +37,22 @@
37
37
  "@noble/hashes": "^1.6.0",
38
38
  "esbuild": "^0.28.2",
39
39
  "zod": "^4.3.6",
40
- "@ggui-ai/agent-runtime": "0.20.0",
41
- "@ggui-ai/artifact-manifest": "0.20.0",
42
- "@ggui-ai/dev-stack": "0.20.0",
43
- "@ggui-ai/design": "0.20.0",
44
- "@ggui-ai/gadget-signing": "0.20.0",
45
- "@ggui-ai/mcp-server": "0.20.0",
46
- "@ggui-ai/mcp-server-core": "0.20.0",
47
- "@ggui-ai/mcp-server-handlers": "0.20.0",
48
- "@ggui-ai/preview-a2ui": "0.20.0",
49
- "@ggui-ai/embedding-local": "0.20.0",
50
- "@ggui-ai/registry-core": "0.20.0",
51
- "@ggui-ai/iframe-runtime": "0.20.0",
52
- "@ggui-ai/ui-gen": "0.20.0",
53
- "@ggui-ai/ui-registry": "0.20.0",
54
- "@ggui-ai/protocol": "0.20.0",
55
- "@ggui-ai/project-config": "0.20.0"
40
+ "@ggui-ai/agent-runtime": "0.21.0",
41
+ "@ggui-ai/artifact-manifest": "0.21.0",
42
+ "@ggui-ai/design": "0.21.0",
43
+ "@ggui-ai/dev-stack": "0.21.0",
44
+ "@ggui-ai/gadget-signing": "0.21.0",
45
+ "@ggui-ai/embedding-local": "0.21.0",
46
+ "@ggui-ai/mcp-server-core": "0.21.0",
47
+ "@ggui-ai/mcp-server": "0.21.0",
48
+ "@ggui-ai/mcp-server-handlers": "0.21.0",
49
+ "@ggui-ai/preview-a2ui": "0.21.0",
50
+ "@ggui-ai/project-config": "0.21.0",
51
+ "@ggui-ai/protocol": "0.21.0",
52
+ "@ggui-ai/iframe-runtime": "0.21.0",
53
+ "@ggui-ai/registry-core": "0.21.0",
54
+ "@ggui-ai/ui-gen": "0.21.0",
55
+ "@ggui-ai/ui-registry": "0.21.0"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@types/node": "^24.0.0",