pi-banana 2.1.4 → 2.3.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.
Files changed (2) hide show
  1. package/extensions/index.ts +75 -18
  2. package/package.json +1 -1
@@ -9,9 +9,13 @@
9
9
  * pi-coding-agent renders them inline (Kitty / iTerm2). Also writes the file
10
10
  * to disk so the agent and the user always have a path to refer back to.
11
11
  *
12
- * Auth: GOOGLE_API_KEY in the environment.
13
- * - AI Studio keys (AIza...) hit the Gemini API.
14
- * - Vertex AI Express keys (AQ....) automatically switch to Vertex.
12
+ * Auth (checked in this order — first match wins):
13
+ * 1. pi modelRegistry auth.json / env vars / models.json
14
+ * (set via /login Google, or GEMINI_API_KEY env var)
15
+ * 2. Vertex AI ADC — gcloud auth application-default login,
16
+ * GOOGLE_APPLICATION_CREDENTIALS, or GOOGLE_CLOUD_PROJECT
17
+ * 3. GOOGLE_API_KEY env var (AIza… or AQ.…) — pi-banana backward compat
18
+ * Vertex AI Express keys (AQ.…) automatically switch to Vertex.
15
19
  */
16
20
 
17
21
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
@@ -25,6 +29,7 @@ import {
25
29
  } from "@earendil-works/pi-tui";
26
30
  import { GoogleGenAI } from "@google/genai";
27
31
  import { existsSync } from "node:fs";
32
+ import { homedir } from "node:os";
28
33
  import { mkdir, readFile, writeFile } from "node:fs/promises";
29
34
  import { extname, isAbsolute, resolve } from "node:path";
30
35
  import { Type } from "typebox";
@@ -76,19 +81,71 @@ const SUPPORTED_INPUT_MIME = new Set([
76
81
 
77
82
  // ─── Helpers ───────────────────────────────────────────────────────────────
78
83
 
79
- function buildClient(): GoogleGenAI {
80
- const apiKey = process.env.GOOGLE_API_KEY;
81
- if (!apiKey) {
82
- throw new Error(
83
- "GOOGLE_API_KEY not set. Get one at https://aistudio.google.com/apikey (AIza…) " +
84
- "or use a Vertex AI Express key (AQ.…) — both work.",
85
- );
84
+ // Well-known path for gcloud Application Default Credentials.
85
+ const ADC_CREDENTIALS_PATH = resolve(
86
+ homedir(),
87
+ ".config/gcloud/application_default_credentials.json",
88
+ );
89
+
90
+ /**
91
+ * Check whether Vertex AI Application Default Credentials are available.
92
+ * Returns true when any of these are configured:
93
+ * - GOOGLE_APPLICATION_CREDENTIALS points to an existing file
94
+ * - gcloud ADC file exists (~/.config/gcloud/application_default_credentials.json)
95
+ * - GOOGLE_CLOUD_PROJECT is set (implies GKE / Cloud Run / Compute Engine default SA)
96
+ */
97
+ function hasVertexADC(): boolean {
98
+ const gac = process.env.GOOGLE_APPLICATION_CREDENTIALS;
99
+ if (gac && existsSync(gac)) return true;
100
+ if (existsSync(ADC_CREDENTIALS_PATH)) return true;
101
+ if (process.env.GOOGLE_CLOUD_PROJECT) return true;
102
+ return false;
103
+ }
104
+
105
+ async function buildClient(
106
+ modelRegistry: ExtensionAPI["modelRegistry"] | undefined,
107
+ ): Promise<GoogleGenAI> {
108
+ // 1. Pi's model registry — the pi-native auth chain
109
+ // (auth.json → env vars → models.json fallback)
110
+ if (modelRegistry) {
111
+ try {
112
+ const apiKey = await modelRegistry.getApiKeyForProvider("google");
113
+ if (apiKey) {
114
+ return new GoogleGenAI({
115
+ apiKey,
116
+ ...(apiKey.startsWith("AQ.") ? { vertexai: true } : {}),
117
+ });
118
+ }
119
+ } catch {
120
+ // ignore — fall through
121
+ }
86
122
  }
87
- // AQ.* express keys authenticate against Vertex; AIza* against Gemini API.
88
- return new GoogleGenAI({
89
- apiKey,
90
- ...(apiKey.startsWith("AQ.") ? { vertexai: true } : {}),
91
- });
123
+
124
+ // 2. Vertex AI ADC (no API key needed — SDK picks up ADC automatically)
125
+ if (hasVertexADC()) {
126
+ return new GoogleGenAI({ vertexai: true });
127
+ }
128
+
129
+ // 3. GOOGLE_API_KEY env var — backward compat for pi-banana users
130
+ // (pi's standard GEMINI_API_KEY is already covered by modelRegistry above)
131
+ if (process.env.GOOGLE_API_KEY) {
132
+ const key = process.env.GOOGLE_API_KEY;
133
+ return new GoogleGenAI({
134
+ apiKey: key,
135
+ ...(key.startsWith("AQ.") ? { vertexai: true } : {}),
136
+ });
137
+ }
138
+
139
+ throw new Error(
140
+ "No Google credentials found. pi-banana looked in this order:\n" +
141
+ " 1. pi model registry (auth.json → env vars → models.json)\n" +
142
+ " Run /login → Google, or set GEMINI_API_KEY, or configure in models.json\n" +
143
+ " 2. Vertex AI ADC (gcloud auth application-default login,\n" +
144
+ " GOOGLE_APPLICATION_CREDENTIALS, or GOOGLE_CLOUD_PROJECT)\n" +
145
+ " 3. GOOGLE_API_KEY env var (AIza… or AQ.…)\n\n" +
146
+ "For Gemini API: get a key at https://aistudio.google.com/apikey\n" +
147
+ "For Vertex AI: gcloud auth application-default login",
148
+ );
92
149
  }
93
150
 
94
151
  function slugify(text: string): string {
@@ -193,7 +250,7 @@ export default function (pi: ExtensionAPI) {
193
250
  "inline in the terminal AND saved to disk so it can be re-used. " +
194
251
  "Pass `referenceImages` (array of paths) to iterate on existing pictures.",
195
252
  promptSnippet:
196
- "Generate or edit images with Google Nano Banana via the GOOGLE_API_KEY env var.",
253
+ "Generate or edit images with Google Nano Banana. Requires Google credentials (via /login, GOOGLE_API_KEY / GEMINI_API_KEY, or Vertex AI ADC).",
197
254
  promptGuidelines: [
198
255
  "Call banana_image when the user asks to create, draw, illustrate, or edit a picture.",
199
256
  "For tweaks like 'make the sky purple', pass the previous file path (or paths) as referenceImages to banana_image.",
@@ -269,7 +326,7 @@ export default function (pi: ExtensionAPI) {
269
326
  );
270
327
  }
271
328
 
272
- const client = buildClient();
329
+ const client = await buildClient(ctx.modelRegistry);
273
330
 
274
331
  // Build content parts — text + (optional) reference image.
275
332
  const parts: Array<{
@@ -528,7 +585,7 @@ export default function (pi: ExtensionAPI) {
528
585
  return { content: [{ type: "text", text: "Cancelled." }], details: {} };
529
586
  }
530
587
 
531
- const client = buildClient();
588
+ const client = await buildClient(ctx.modelRegistry);
532
589
  const parts: Array<{
533
590
  text?: string;
534
591
  inlineData?: { mimeType: string; data: string };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-banana",
3
- "version": "2.1.4",
3
+ "version": "2.3.0",
4
4
  "description": "Generate, edit, and analyze images in pi using Google Nano Banana (image gen) and Gemini Vision (analysis). Inline terminal preview, reference-image editing, auto-save.",
5
5
  "author": "Francesco Frapporti <effedue@gmail.com>",
6
6
  "license": "MIT",