@centerforagenticai/pi-multi-account 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +29 -0
  3. package/README.md +999 -0
  4. package/config/models/pi-multi-account.v1.json +32 -0
  5. package/config/subscription-plans.v1.json +122 -0
  6. package/package.json +76 -0
  7. package/packages/pi-anthropic-oauth/LICENSE +21 -0
  8. package/packages/pi-anthropic-oauth/package.json +54 -0
  9. package/packages/pi-anthropic-oauth/src/auth.ts +396 -0
  10. package/packages/pi-anthropic-oauth/src/context.ts +116 -0
  11. package/packages/pi-anthropic-oauth/src/convert.ts +303 -0
  12. package/packages/pi-anthropic-oauth/src/index.ts +37 -0
  13. package/packages/pi-anthropic-oauth/src/prompt.ts +137 -0
  14. package/packages/pi-anthropic-oauth/src/stream.ts +476 -0
  15. package/packages/pi-antigravity/LICENSE +21 -0
  16. package/packages/pi-antigravity/package.json +77 -0
  17. package/packages/pi-antigravity/src/auth/index.ts +14 -0
  18. package/packages/pi-antigravity/src/auth/oauth.ts +442 -0
  19. package/packages/pi-antigravity/src/client/client.ts +561 -0
  20. package/packages/pi-antigravity/src/client/index.ts +1 -0
  21. package/packages/pi-antigravity/src/context.ts +110 -0
  22. package/packages/pi-antigravity/src/diagnostics/diagnostics.ts +96 -0
  23. package/packages/pi-antigravity/src/diagnostics/index.ts +1 -0
  24. package/packages/pi-antigravity/src/image/image.ts +336 -0
  25. package/packages/pi-antigravity/src/image/index.ts +1 -0
  26. package/packages/pi-antigravity/src/index.ts +280 -0
  27. package/packages/pi-antigravity/src/models/discovery.ts +154 -0
  28. package/packages/pi-antigravity/src/models/grouping.ts +424 -0
  29. package/packages/pi-antigravity/src/models/index.ts +3 -0
  30. package/packages/pi-antigravity/src/models/models.ts +500 -0
  31. package/packages/pi-antigravity/src/stream/index.ts +1 -0
  32. package/packages/pi-antigravity/src/stream/stream.ts +1478 -0
  33. package/packages/pi-antigravity/src/types/enums.ts +42 -0
  34. package/packages/pi-antigravity/src/types/index.ts +2 -0
  35. package/packages/pi-antigravity/src/types/types.ts +292 -0
  36. package/packages/pi-antigravity/src/usage/index.ts +1 -0
  37. package/packages/pi-antigravity/src/usage/usage.ts +416 -0
  38. package/packages/pi-antigravity/src/utils/http.ts +91 -0
  39. package/packages/pi-antigravity/src/utils/index.ts +3 -0
  40. package/packages/pi-antigravity/src/utils/security.ts +73 -0
  41. package/packages/pi-antigravity/src/utils/util.ts +132 -0
  42. package/scripts/multi-account.mjs +44 -0
  43. package/src/account-labels.ts +223 -0
  44. package/src/account-plan-assignment.ts +340 -0
  45. package/src/account-rate-history.ts +372 -0
  46. package/src/anthropic-adaptive-stream.ts +531 -0
  47. package/src/anthropic-alias-stream.ts +140 -0
  48. package/src/anthropic-context-compat.ts +80 -0
  49. package/src/api-pricing.ts +579 -0
  50. package/src/bounded-file-lines.ts +97 -0
  51. package/src/catalog-rebinding.ts +177 -0
  52. package/src/catalog-registration-probe.ts +111 -0
  53. package/src/codex-adapter.ts +345 -0
  54. package/src/codex-model-defaults.ts +785 -0
  55. package/src/command-completions.ts +404 -0
  56. package/src/commands.ts +2000 -0
  57. package/src/compaction.ts +14 -0
  58. package/src/config.ts +1317 -0
  59. package/src/continuation.ts +569 -0
  60. package/src/cooldowns.ts +110 -0
  61. package/src/cost-digest-store.ts +332 -0
  62. package/src/cost-digest.ts +1044 -0
  63. package/src/cost-history.ts +251 -0
  64. package/src/cost-period-closer.ts +160 -0
  65. package/src/cost-report-json.ts +318 -0
  66. package/src/cost-report-reader.ts +368 -0
  67. package/src/cost-report-render.ts +207 -0
  68. package/src/cost-report.ts +1104 -0
  69. package/src/coverage-attestation.ts +397 -0
  70. package/src/credential-lifecycle.ts +169 -0
  71. package/src/credential-refresh.ts +248 -0
  72. package/src/declaration-notice-marker.ts +238 -0
  73. package/src/diagnostic-store.ts +276 -0
  74. package/src/diagnostics.ts +309 -0
  75. package/src/discovery.ts +471 -0
  76. package/src/duration.ts +13 -0
  77. package/src/error-classification.ts +256 -0
  78. package/src/fuzzy.ts +15 -0
  79. package/src/group-policy.ts +81 -0
  80. package/src/history-store.ts +897 -0
  81. package/src/index.ts +5572 -0
  82. package/src/lifecycle.ts +378 -0
  83. package/src/logical-dispatch.ts +279 -0
  84. package/src/logical-model-selector.ts +254 -0
  85. package/src/logical-model-switcher.ts +430 -0
  86. package/src/logical-provider-attribution.ts +544 -0
  87. package/src/logical-provider.ts +1237 -0
  88. package/src/logical-route-indicator.ts +215 -0
  89. package/src/machine-lease.ts +445 -0
  90. package/src/model-support.ts +66 -0
  91. package/src/models-declaration.ts +1091 -0
  92. package/src/openai-adapter.ts +117 -0
  93. package/src/openrouter-budget.ts +304 -0
  94. package/src/openrouter-fallback.ts +146 -0
  95. package/src/period-boundaries.ts +376 -0
  96. package/src/pi-anthropic-oauth.d.ts +6 -0
  97. package/src/preflight.ts +253 -0
  98. package/src/pricing-cache.ts +235 -0
  99. package/src/project-identity.ts +100 -0
  100. package/src/provider-registration.ts +942 -0
  101. package/src/rate-formula.ts +163 -0
  102. package/src/recovery-engine.ts +853 -0
  103. package/src/recovery-output.ts +837 -0
  104. package/src/recovery-plan.ts +239 -0
  105. package/src/report-range.ts +203 -0
  106. package/src/route-resolver.ts +789 -0
  107. package/src/routing-config-transaction.ts +232 -0
  108. package/src/routing.ts +1163 -0
  109. package/src/runtime-state.ts +630 -0
  110. package/src/session-account-groups.ts +284 -0
  111. package/src/session-restore.ts +287 -0
  112. package/src/shared-usage.ts +1392 -0
  113. package/src/standalone-cli.ts +720 -0
  114. package/src/status-view.ts +578 -0
  115. package/src/subscription-plan-catalog.ts +346 -0
  116. package/src/tier-model-resolver.ts +46 -0
  117. package/src/upstream-anthropic.ts +315 -0
  118. package/src/upstream-antigravity.ts +327 -0
  119. package/src/usage-fetch.ts +1634 -0
  120. package/src/usage.ts +1026 -0
  121. package/src/vendor.ts +87 -0
  122. package/src/warmer.ts +231 -0
  123. package/src/watchdog.ts +219 -0
  124. package/src/window-history.ts +270 -0
@@ -0,0 +1,96 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { redactSecrets } from "../utils/security.js";
3
+
4
+ export type DiagnosticsSnapshot = {
5
+ status?: number;
6
+ endpoint?: string;
7
+ error?: string;
8
+ projectId?: string;
9
+ resolvedRuntimeModel?: string;
10
+ availableModels?: string;
11
+ matchedModelDebug?: string;
12
+ latencyMs?: number;
13
+ maskedEmail?: string;
14
+ tokenExpiry?: string;
15
+ toolSchemaWarnings?: string;
16
+ };
17
+
18
+ const storage = new AsyncLocalStorage<DiagnosticsSnapshot>();
19
+
20
+ /** Last completed request snapshot for `/antigravity.doctor`. */
21
+ let lastSnapshot: DiagnosticsSnapshot = {};
22
+
23
+ function currentBag(): DiagnosticsSnapshot {
24
+ return storage.getStore() ?? lastSnapshot;
25
+ }
26
+
27
+ /** Run work with an isolated diagnostics bag; commits it to `lastSnapshot` when done. */
28
+ export async function runWithDiagnostics<T>(fn: () => Promise<T>): Promise<T> {
29
+ const bag: DiagnosticsSnapshot = {};
30
+ return storage.run(bag, async () => {
31
+ try {
32
+ return await fn();
33
+ } finally {
34
+ lastSnapshot = { ...bag };
35
+ }
36
+ });
37
+ }
38
+
39
+ export function getLastDiagnostics(): Readonly<DiagnosticsSnapshot> {
40
+ return lastSnapshot;
41
+ }
42
+
43
+ /** Read endpoint from the active request bag (or last snapshot outside a request). */
44
+ export function getCurrentEndpoint(): string | undefined {
45
+ return currentBag().endpoint;
46
+ }
47
+
48
+ export function getCurrentMatchedModelDebug(): string | undefined {
49
+ return currentBag().matchedModelDebug;
50
+ }
51
+
52
+ export function getCurrentAvailableModels(): string | undefined {
53
+ return currentBag().availableModels;
54
+ }
55
+
56
+ export function setLastStatus(status: number | undefined): void {
57
+ currentBag().status = status;
58
+ }
59
+ export function setLastEndpoint(endpoint: string | undefined): void {
60
+ currentBag().endpoint = endpoint;
61
+ }
62
+ export function setLastError(error: string | undefined): void {
63
+ currentBag().error = error === undefined ? undefined : redactSecrets(error).slice(0, 800);
64
+ }
65
+ export function setLastProjectId(projectId: string | undefined): void {
66
+ currentBag().projectId = projectId;
67
+ }
68
+ export function setLastResolvedRuntimeModel(model: string | undefined): void {
69
+ currentBag().resolvedRuntimeModel = model;
70
+ }
71
+ export function setLastAvailableModels(models: string | undefined): void {
72
+ currentBag().availableModels = models;
73
+ }
74
+ export function setLastMatchedModelDebug(debug: string | undefined): void {
75
+ currentBag().matchedModelDebug =
76
+ debug === undefined ? undefined : redactSecrets(debug).slice(0, 1200);
77
+ }
78
+ export function setLastLatencyMs(ms: number | undefined): void {
79
+ currentBag().latencyMs = ms;
80
+ }
81
+ export function setLastMaskedEmail(email: string | undefined): void {
82
+ currentBag().maskedEmail = email;
83
+ }
84
+ export function setLastTokenExpiry(expiry: string | undefined): void {
85
+ currentBag().tokenExpiry = expiry;
86
+ }
87
+ /** Store sanitized tool-schema omissions for the next doctor report. */
88
+ export function setLastToolSchemaWarnings(warnings: string[] | undefined): void {
89
+ currentBag().toolSchemaWarnings =
90
+ warnings === undefined ? undefined : redactSecrets(warnings.join(" | ")).slice(0, 1200);
91
+ }
92
+
93
+ /** Test helper: reset last snapshot between cases. */
94
+ export function resetDiagnosticsForTests(): void {
95
+ lastSnapshot = {};
96
+ }
@@ -0,0 +1 @@
1
+ export * from "./diagnostics.js";
@@ -0,0 +1,336 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
3
+ import {
4
+ antigravityHeaders,
5
+ endpointCandidates,
6
+ jsonOrTextError,
7
+ parseApiKey,
8
+ } from "../client/client.js";
9
+ import { AntigravityRequestType, AntigravityUserAgent, GeminiRole } from "../types/enums.js";
10
+ import { antigravityFetch } from "../utils/http.js";
11
+ import { safeError } from "../utils/security.js";
12
+ import { antigravityRequestEnvelope, sanitizeText } from "../utils/util.js";
13
+
14
+ export const DEFAULT_IMAGE_MODEL = "gemini-3-pro-image";
15
+ export const IMAGE_ASPECT_RATIOS = [
16
+ "1:1",
17
+ "2:3",
18
+ "3:2",
19
+ "3:4",
20
+ "4:3",
21
+ "4:5",
22
+ "5:4",
23
+ "9:16",
24
+ "16:9",
25
+ "21:9",
26
+ ] as const;
27
+ export type ImageAspectRatio = (typeof IMAGE_ASPECT_RATIOS)[number];
28
+
29
+ const IMAGE_MODEL_FALLBACKS = [
30
+ DEFAULT_IMAGE_MODEL,
31
+ "gemini-3.1-flash-image",
32
+ "gemini-3-pro-image-preview",
33
+ ];
34
+ const IMAGE_SYSTEM_INSTRUCTION =
35
+ "You are an AI image generator. Generate images based on user descriptions. Focus on creating high-quality, visually appealing images that match the user's request.";
36
+ const DEFAULT_IMAGE_DIR = join(".pi", "generated-images");
37
+ const MAX_PROMPT_CHARS = 8000;
38
+
39
+ export type GeneratedImage = { data: string; mimeType: string };
40
+
41
+ export type ImageGenerateRequest = {
42
+ project: string;
43
+ model: string;
44
+ request: {
45
+ contents: Array<{ role: GeminiRole.User; parts: Array<{ text: string }> }>;
46
+ systemInstruction: { role: GeminiRole.User; parts: Array<{ text: string }> };
47
+ generationConfig: {
48
+ imageConfig: { aspectRatio: string };
49
+ candidateCount: number;
50
+ };
51
+ };
52
+ requestType: AntigravityRequestType.Agent;
53
+ userAgent: AntigravityUserAgent.Antigravity;
54
+ requestId: string;
55
+ };
56
+
57
+ export type ImageCommandArgs = {
58
+ prompt: string;
59
+ aspectRatio?: string;
60
+ model?: string;
61
+ path?: string;
62
+ };
63
+
64
+ export type GenerateImageOptions = ImageCommandArgs & {
65
+ apiKey: string;
66
+ cwd: string;
67
+ signal?: AbortSignal;
68
+ };
69
+
70
+ export type GenerateImageResult = {
71
+ images: GeneratedImage[];
72
+ savedPaths: string[];
73
+ text: string[];
74
+ model: string;
75
+ };
76
+
77
+ type ImageStreamChunk = {
78
+ error?: { message?: string };
79
+ response?: {
80
+ candidates?: Array<{
81
+ content?: {
82
+ parts?: Array<{ text?: string; inlineData?: { mimeType?: string; data?: string } }>;
83
+ };
84
+ }>;
85
+ };
86
+ candidates?: Array<{
87
+ content?: {
88
+ parts?: Array<{ text?: string; inlineData?: { mimeType?: string; data?: string } }>;
89
+ };
90
+ }>;
91
+ };
92
+
93
+ function imageExtension(mimeType: string): string {
94
+ const lower = mimeType.toLowerCase();
95
+ if (lower.includes("jpeg") || lower.includes("jpg")) return "jpg";
96
+ if (lower.includes("webp")) return "webp";
97
+ if (lower.includes("gif")) return "gif";
98
+ return "png";
99
+ }
100
+
101
+ export function assertSafeImageModel(modelId: string): string {
102
+ const id = modelId.trim();
103
+ if (id.length === 0 || id.length > 80) {
104
+ throw new Error("Unsupported image model id.");
105
+ }
106
+ if (!/^(gemini-[a-z0-9.+-]*image[a-z0-9.+-]*|imagen-[a-z0-9.+-]+)$/i.test(id)) {
107
+ throw new Error(`Unsupported image model: ${id}`);
108
+ }
109
+ return id;
110
+ }
111
+
112
+ export function assertSafeAspectRatio(ratio: string): ImageAspectRatio {
113
+ const value = ratio.trim();
114
+ for (const allowed of IMAGE_ASPECT_RATIOS) {
115
+ if (allowed === value) return allowed;
116
+ }
117
+ throw new Error(
118
+ `Unsupported aspect ratio: ${value}. Use one of ${IMAGE_ASPECT_RATIOS.join(", ")}.`,
119
+ );
120
+ }
121
+
122
+ export function parseImageCommandArgs(args: string): ImageCommandArgs {
123
+ const tokens = args.trim().split(/\s+/).filter(Boolean);
124
+ const out: ImageCommandArgs = { prompt: "" };
125
+ const rest: string[] = [];
126
+ for (let i = 0; i < tokens.length; i++) {
127
+ const token = tokens[i];
128
+ if (token === undefined) continue;
129
+ const next = tokens[i + 1];
130
+ if ((token === "--ratio" || token === "--aspect-ratio") && next) {
131
+ out.aspectRatio = next;
132
+ i += 1;
133
+ continue;
134
+ }
135
+ if (token === "--model" && next) {
136
+ out.model = next;
137
+ i += 1;
138
+ continue;
139
+ }
140
+ if (token === "--path" && next) {
141
+ out.path = next;
142
+ i += 1;
143
+ continue;
144
+ }
145
+ rest.push(token);
146
+ }
147
+ out.prompt = rest.join(" ");
148
+ return out;
149
+ }
150
+
151
+ export function resolveImageSavePath(
152
+ cwd: string,
153
+ requested?: string,
154
+ mimeType = "image/png",
155
+ index?: number,
156
+ ): string {
157
+ const ext = imageExtension(mimeType);
158
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
159
+ const suffix = index === undefined ? "" : `-${index + 1}`;
160
+ const defaultName = `image-${stamp}${suffix}.${ext}`;
161
+ const root = resolve(cwd);
162
+ const target = requested?.trim()
163
+ ? resolve(root, requested.trim())
164
+ : resolve(root, DEFAULT_IMAGE_DIR, defaultName);
165
+ const rel = relative(root, target);
166
+ if (!rel || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
167
+ throw new Error("Image save path must be inside the working directory.");
168
+ }
169
+ if (!extname(target)) return join(target, defaultName);
170
+ if (index === undefined) return target;
171
+ const currentExt = extname(target);
172
+ return `${target.slice(0, -currentExt.length)}${suffix}${currentExt}`;
173
+ }
174
+
175
+ export function buildImageGenerateRequest(
176
+ prompt: string,
177
+ model: string,
178
+ projectId: string,
179
+ aspectRatio: string,
180
+ ): ImageGenerateRequest {
181
+ const envelope = antigravityRequestEnvelope(model, false);
182
+ return {
183
+ project: projectId,
184
+ model,
185
+ request: {
186
+ contents: [{ role: GeminiRole.User, parts: [{ text: sanitizeText(prompt) }] }],
187
+ systemInstruction: {
188
+ role: GeminiRole.User,
189
+ parts: [{ text: IMAGE_SYSTEM_INSTRUCTION }],
190
+ },
191
+ generationConfig: {
192
+ imageConfig: { aspectRatio },
193
+ candidateCount: 1,
194
+ },
195
+ },
196
+ requestType: AntigravityRequestType.Agent,
197
+ userAgent: AntigravityUserAgent.Antigravity,
198
+ requestId: envelope.requestId,
199
+ };
200
+ }
201
+
202
+ function collectImagesFromParts(
203
+ parts: Array<{ text?: string; inlineData?: { mimeType?: string; data?: string } }> | undefined,
204
+ images: GeneratedImage[],
205
+ text: string[],
206
+ ): void {
207
+ for (const part of parts || []) {
208
+ if (part.text) text.push(part.text);
209
+ if (part.inlineData?.data) {
210
+ images.push({
211
+ data: part.inlineData.data,
212
+ mimeType: part.inlineData.mimeType || "image/png",
213
+ });
214
+ }
215
+ }
216
+ }
217
+
218
+ export async function collectImagesFromSse(
219
+ response: Response,
220
+ signal?: AbortSignal,
221
+ ): Promise<{ images: GeneratedImage[]; text: string[] }> {
222
+ if (!response.body) throw new Error("No response body");
223
+ const reader = response.body.getReader();
224
+ const decoder = new TextDecoder();
225
+ let buffer = "";
226
+ const images: GeneratedImage[] = [];
227
+ const text: string[] = [];
228
+ try {
229
+ while (true) {
230
+ if (signal?.aborted) throw new Error("Request was aborted");
231
+ const result = await reader.read();
232
+ if (result.done) break;
233
+ if (!(result.value instanceof Uint8Array)) continue;
234
+ buffer += decoder.decode(result.value, { stream: true });
235
+ const lines = buffer.split("\n");
236
+ buffer = lines.pop() || "";
237
+ for (const line of lines) {
238
+ if (!line.startsWith("data:")) continue;
239
+ const json = line.slice(5).trim();
240
+ if (!json || json === "[DONE]") continue;
241
+ let chunk: ImageStreamChunk;
242
+ try {
243
+ chunk = JSON.parse(json) as ImageStreamChunk;
244
+ } catch {
245
+ continue;
246
+ }
247
+ if (chunk.error?.message) throw new Error(chunk.error.message);
248
+ const responseData = chunk.response || chunk;
249
+ for (const candidate of responseData.candidates || []) {
250
+ collectImagesFromParts(candidate.content?.parts, images, text);
251
+ }
252
+ }
253
+ }
254
+ } finally {
255
+ reader.releaseLock();
256
+ }
257
+ return { images, text };
258
+ }
259
+
260
+ async function writeImage(filePath: string, image: GeneratedImage): Promise<string> {
261
+ await mkdir(dirname(filePath), { recursive: true });
262
+ await writeFile(filePath, Buffer.from(image.data, "base64"));
263
+ return filePath;
264
+ }
265
+
266
+ export async function generateAntigravityImage(
267
+ options: GenerateImageOptions,
268
+ ): Promise<GenerateImageResult> {
269
+ const prompt = options.prompt.trim();
270
+ if (!prompt) throw new Error("Image prompt is required.");
271
+ if (prompt.length > MAX_PROMPT_CHARS) {
272
+ throw new Error(`Image prompt is too long (max ${MAX_PROMPT_CHARS} characters).`);
273
+ }
274
+ const aspectRatio = assertSafeAspectRatio(options.aspectRatio || "1:1");
275
+ const preferred = assertSafeImageModel(options.model || DEFAULT_IMAGE_MODEL);
276
+ const models = [preferred, ...IMAGE_MODEL_FALLBACKS.filter((id) => id !== preferred)];
277
+ const creds = parseApiKey(options.apiKey);
278
+ const headers = antigravityHeaders(creds.token);
279
+
280
+ let lastError = "no endpoint available";
281
+ for (const model of models) {
282
+ const body = JSON.stringify(
283
+ buildImageGenerateRequest(prompt, model, creds.projectId, aspectRatio),
284
+ );
285
+ for (const endpoint of endpointCandidates()) {
286
+ if (options.signal?.aborted) throw new Error("Request was aborted");
287
+ try {
288
+ const response = await antigravityFetch(
289
+ `${endpoint}/v1internal:streamGenerateContent?alt=sse`,
290
+ {
291
+ method: "POST",
292
+ headers,
293
+ body,
294
+ signal: options.signal,
295
+ },
296
+ );
297
+ if (!response.ok) {
298
+ lastError = jsonOrTextError(await response.text()).slice(0, 400);
299
+ if (response.status === 404 || [403, 429, 500, 502, 503, 504].includes(response.status)) {
300
+ continue;
301
+ }
302
+ throw new Error(
303
+ `Antigravity image request failed (${response.status}): ${safeError(lastError)}`,
304
+ );
305
+ }
306
+ const parsed = await collectImagesFromSse(response, options.signal);
307
+ if (!parsed.images.length) {
308
+ lastError = parsed.text.join(" ").trim() || "No image data returned.";
309
+ continue;
310
+ }
311
+ const savedPaths: string[] = [];
312
+ const many = parsed.images.length > 1;
313
+ for (const [index, image] of parsed.images.entries()) {
314
+ savedPaths.push(
315
+ await writeImage(
316
+ resolveImageSavePath(
317
+ options.cwd,
318
+ options.path,
319
+ image.mimeType,
320
+ many ? index : undefined,
321
+ ),
322
+ image,
323
+ ),
324
+ );
325
+ }
326
+ return { images: parsed.images, savedPaths, text: parsed.text, model };
327
+ } catch (error) {
328
+ lastError = safeError(error);
329
+ if (options.signal?.aborted) {
330
+ throw new Error("Request was aborted", { cause: error });
331
+ }
332
+ }
333
+ }
334
+ }
335
+ throw new Error(`Antigravity image generation failed: ${safeError(lastError)}`);
336
+ }
@@ -0,0 +1 @@
1
+ export * from "./image.js";