firebase-tools 15.11.0 → 15.12.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 (36) hide show
  1. package/lib/agentSkills.js +70 -0
  2. package/lib/api.js +3 -1
  3. package/lib/apphosting/backend.js +22 -3
  4. package/lib/bin/mcp.js +5 -1
  5. package/lib/commands/apphosting-backends-create.js +19 -2
  6. package/lib/commands/apphosting-backends-list.js +21 -5
  7. package/lib/commands/functions-delete.js +1 -0
  8. package/lib/commands/functions-export.js +40 -0
  9. package/lib/commands/index.js +3 -0
  10. package/lib/commands/init.js +1 -0
  11. package/lib/deploy/apphosting/deploy.js +11 -6
  12. package/lib/deploy/apphosting/prepare.js +21 -1
  13. package/lib/deploy/apphosting/release.js +2 -5
  14. package/lib/deploy/apphosting/util.js +45 -2
  15. package/lib/deploy/functions/prepare.js +4 -1
  16. package/lib/deploy/functions/release/fabricator.js +4 -3
  17. package/lib/deploy/functions/release/index.js +5 -0
  18. package/lib/deploy/functions/services/ailogic.js +68 -0
  19. package/lib/deploy/functions/services/index.js +4 -0
  20. package/lib/emulator/downloadableEmulatorInfo.json +24 -24
  21. package/lib/emulator/storage/rules/manager.js +10 -3
  22. package/lib/emulator/storage/rules/runtime.js +9 -7
  23. package/lib/experiments.js +22 -0
  24. package/lib/firebase_studio/migrate.js +30 -61
  25. package/lib/functions/iac/export.js +36 -0
  26. package/lib/functions/iac/terraform.js +146 -0
  27. package/lib/gcp/ailogic.js +108 -0
  28. package/lib/gcp/cloudfunctionsv2.js +24 -0
  29. package/lib/init/features/agentSkills.js +26 -0
  30. package/lib/init/features/dataconnect/sdk.js +26 -12
  31. package/lib/init/features/index.js +4 -1
  32. package/lib/init/index.js +6 -0
  33. package/lib/tsconfig.publish.tsbuildinfo +1 -1
  34. package/lib/utils.js +8 -0
  35. package/package.json +5 -3
  36. package/standalone/package.json +1 -1
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AILogicService = exports.AI_LOGIC_EVENTS = exports.AI_LOGIC_AFTER_GENERATE_CONTENT = exports.AI_LOGIC_BEFORE_GENERATE_CONTENT = void 0;
4
+ exports.isAILogicEvent = isAILogicEvent;
5
+ const backend = require("../backend");
6
+ const error_1 = require("../../../error");
7
+ const ailogicApi = require("../../../gcp/ailogic");
8
+ exports.AI_LOGIC_BEFORE_GENERATE_CONTENT = "firebase.vertexai.v1beta.beforeGenerateContent";
9
+ exports.AI_LOGIC_AFTER_GENERATE_CONTENT = "firebase.vertexai.v1beta.afterGenerateContent";
10
+ exports.AI_LOGIC_EVENTS = [
11
+ exports.AI_LOGIC_BEFORE_GENERATE_CONTENT,
12
+ exports.AI_LOGIC_AFTER_GENERATE_CONTENT,
13
+ ];
14
+ function isAILogicEvent(endpoint) {
15
+ if (!backend.isBlockingTriggered(endpoint)) {
16
+ return false;
17
+ }
18
+ return exports.AI_LOGIC_EVENTS.includes(endpoint.blockingTrigger.eventType);
19
+ }
20
+ class AILogicService {
21
+ constructor() {
22
+ this.ensureTriggerRegion = () => Promise.resolve();
23
+ this.name = "ailogic";
24
+ this.api = "firebasevertexai.googleapis.com";
25
+ }
26
+ validateTrigger(endpoint, wantBackend) {
27
+ if (!isAILogicEvent(endpoint)) {
28
+ return;
29
+ }
30
+ const eventType = endpoint.blockingTrigger.eventType;
31
+ const regionalWebhook = !!endpoint.blockingTrigger.options?.regionalWebhook;
32
+ const conflict = backend.allEndpoints(wantBackend).some((ep) => {
33
+ if (!isAILogicEvent(ep)) {
34
+ return false;
35
+ }
36
+ if (ep.blockingTrigger.eventType !== eventType || ep.id === endpoint.id) {
37
+ return false;
38
+ }
39
+ if (regionalWebhook) {
40
+ return ep.blockingTrigger.options?.regionalWebhook && ep.region === endpoint.region;
41
+ }
42
+ else {
43
+ return !ep.blockingTrigger.options?.regionalWebhook;
44
+ }
45
+ });
46
+ if (conflict) {
47
+ if (regionalWebhook) {
48
+ throw new error_1.FirebaseError(`Can only create at most one regional AI Logic Trigger for ${eventType} in region ${endpoint.region}`);
49
+ }
50
+ else {
51
+ throw new error_1.FirebaseError(`Can only create at most one global AI Logic Trigger for ${eventType}`);
52
+ }
53
+ }
54
+ }
55
+ async registerTrigger(ep) {
56
+ if (!isAILogicEvent(ep)) {
57
+ return;
58
+ }
59
+ await ailogicApi.upsertBlockingFunction(ep);
60
+ }
61
+ async unregisterTrigger(ep) {
62
+ if (!isAILogicEvent(ep)) {
63
+ return;
64
+ }
65
+ await ailogicApi.deleteBlockingFunction(ep);
66
+ }
67
+ }
68
+ exports.AILogicService = AILogicService;
@@ -11,6 +11,7 @@ const remoteConfig_1 = require("./remoteConfig");
11
11
  const testLab_1 = require("./testLab");
12
12
  const firestore_1 = require("./firestore");
13
13
  const dataconnect_1 = require("./dataconnect");
14
+ const ailogic_1 = require("./ailogic");
14
15
  const noop = () => Promise.resolve();
15
16
  exports.noop = noop;
16
17
  const noopProjectBindings = () => Promise.resolve([]);
@@ -51,6 +52,7 @@ const firebaseAlertsService = {
51
52
  unregisterTrigger: exports.noop,
52
53
  };
53
54
  const authBlockingService = new auth_1.AuthBlockingService();
55
+ const aiLogicService = new ailogic_1.AILogicService();
54
56
  const databaseService = {
55
57
  name: "database",
56
58
  api: "firebasedatabase.googleapis.com",
@@ -122,6 +124,8 @@ const EVENT_SERVICE_MAPPING = {
122
124
  "google.cloud.firestore.document.v1.updated.withAuthContext": firestoreService,
123
125
  "google.cloud.firestore.document.v1.deleted.withAuthContext": firestoreService,
124
126
  "google.firebase.dataconnect.connector.v1.mutationExecuted": dataconnectService,
127
+ "firebase.vertexai.v1beta.beforeGenerateContent": aiLogicService,
128
+ "firebase.vertexai.v1beta.afterGenerateContent": aiLogicService,
125
129
  };
126
130
  function serviceForEndpoint(endpoint) {
127
131
  if (backend.isEventTriggered(endpoint)) {
@@ -54,36 +54,36 @@
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "3.3.0",
58
- "expectedSize": 32051312,
59
- "expectedChecksum": "9c9d9fb375826d90f9a57ce7a1770afc",
60
- "expectedChecksumSHA256": "ae2fe4f4b9a79b6bfd52ef227dcdb5e58c5b0e3a662a3874abc07c73d2e8f8e4",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.3.0",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.0"
57
+ "version": "3.3.1",
58
+ "expectedSize": 32105392,
59
+ "expectedChecksum": "dfe6ff725864c37889c238aac24be304",
60
+ "expectedChecksumSHA256": "1ccfebb4c0ff85f0503b907aa589658774bb6d4fac0180c3fa6bdff3a865090d",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.3.1",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.1"
63
63
  },
64
64
  "darwin_arm64": {
65
- "version": "3.3.0",
66
- "expectedSize": 30237490,
67
- "expectedChecksum": "cd6e0b50e7614edd420a44cde1a6de54",
68
- "expectedChecksumSHA256": "1524161c428f6c97ae1e4eb6371028bace2280f1090ebe89e73313afb30b7bdc",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.3.0",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.0"
65
+ "version": "3.3.1",
66
+ "expectedSize": 30287922,
67
+ "expectedChecksum": "59af3cb51c78e8114d634afea811f8cc",
68
+ "expectedChecksumSHA256": "75bde31d15c4cc14de7071ff50cdd0d8fb644491e0c9ee3c1da82fdaf8d976a9",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.3.1",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.1"
71
71
  },
72
72
  "win32": {
73
- "version": "3.3.0",
74
- "expectedSize": 32093696,
75
- "expectedChecksum": "e2b6a90994609e613aae189fb6bb732b",
76
- "expectedChecksumSHA256": "ddf44b362658a43ffb97c23e605e26d257e786adb9a71466ebd756b36c4dd212",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.3.0",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.0.exe"
73
+ "version": "3.3.1",
74
+ "expectedSize": 32148992,
75
+ "expectedChecksum": "3205dc92bbc7edb4c5821641486dc70c",
76
+ "expectedChecksumSHA256": "fa7796077728414b0682f6ccf1df0fc7b2f56bb590f1e62698dbb7c1ddaf0fb0",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.3.1",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.1.exe"
79
79
  },
80
80
  "linux": {
81
- "version": "3.3.0",
82
- "expectedSize": 31211704,
83
- "expectedChecksum": "48b40f31c1897afd39e3b256c356730a",
84
- "expectedChecksumSHA256": "e6774287f3a3ea7ee240f524644e0b0441720dd5d55d6a970a6c4db9923b517f",
85
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.3.0",
86
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.0"
81
+ "version": "3.3.1",
82
+ "expectedSize": 31264952,
83
+ "expectedChecksum": "85ffdef78810e0074ac94c453a200daa",
84
+ "expectedChecksumSHA256": "f232e03165c28f72bc99dfbd2dde1bcaf8298045817b60d72d407da7dae9e9f1",
85
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.3.1",
86
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.1"
87
87
  }
88
88
  }
89
89
  }
@@ -34,9 +34,16 @@ class DefaultStorageRulesManager {
34
34
  .watch(rulesFile, { persistent: true, ignoreInitial: true })
35
35
  .on("change", async () => {
36
36
  await new Promise((res) => setTimeout(res, 5));
37
- this._logger.logLabeled("BULLET", "storage", "Change detected, updating rules for Cloud Storage...");
38
- this._rules.content = (0, fsutils_1.readFile)(rulesFile);
39
- await this.loadRuleset();
37
+ try {
38
+ const content = (0, fsutils_1.readFile)(rulesFile);
39
+ this._rules.content = content;
40
+ this._logger.logLabeled("BULLET", "storage", "Change detected, updating rules for Cloud Storage...");
41
+ await this.loadRuleset();
42
+ }
43
+ catch (e) {
44
+ const message = e instanceof Error ? e.message : String(e);
45
+ this._logger.logLabeled("DEBUG", "storage", `A rule file change was detected, but there was an error reading it: ${message}`);
46
+ }
40
47
  });
41
48
  }
42
49
  async loadRuleset() {
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.StorageRulesRuntime = exports.StorageRulesIssues = exports.StorageRulesetInstance = void 0;
4
+ exports.createAuthExpressionValue = createAuthExpressionValue;
4
5
  const cross_spawn_1 = require("cross-spawn");
5
6
  const error_1 = require("../../../error");
6
7
  const AsyncLock = require("async-lock");
@@ -332,14 +333,15 @@ function createAuthExpressionValue(opts) {
332
333
  if (!opts.token) {
333
334
  return toExpressionValue(null);
334
335
  }
335
- else {
336
- const tokenPayload = jwt.decode(opts.token, { json: true });
337
- const jsonValue = {
338
- uid: tokenPayload.user_id,
339
- token: tokenPayload,
340
- };
341
- return toExpressionValue(jsonValue);
336
+ const tokenPayload = jwt.decode(opts.token, { json: true });
337
+ if (typeof tokenPayload !== "object" || !tokenPayload) {
338
+ return toExpressionValue(null);
342
339
  }
340
+ const jsonValue = {
341
+ uid: "user_id" in tokenPayload ? tokenPayload.user_id : undefined,
342
+ token: tokenPayload,
343
+ };
344
+ return toExpressionValue(jsonValue);
343
345
  }
344
346
  function createRequestExpressionValue(opts) {
345
347
  const fields = {
@@ -58,10 +58,22 @@ exports.ALL_EXPERIMENTS = experiments({
58
58
  shortDescription: "Functions created using the V2 API target Cloud Run Functions (not production ready)",
59
59
  public: false,
60
60
  },
61
+ functionsiac: {
62
+ shortDescription: "Exports functions IaC code",
63
+ public: false,
64
+ },
61
65
  functionsrunapionly: {
62
66
  shortDescription: "Use Cloud Run API to list v2 functions",
63
67
  public: false,
64
68
  },
69
+ bypassfunctionsdeprecationcheck: {
70
+ shortDescription: "Bypass Functions check for old runtimes",
71
+ fullDescription: "Bypasses the local check for whether a functions runtime is " +
72
+ "decommissioned. This does not, by itself, allow you to deploy a function with a " +
73
+ "decommissioned runtime, as there are server-side checks as well.",
74
+ public: false,
75
+ default: false,
76
+ },
65
77
  emulatoruisnapshot: {
66
78
  shortDescription: "Load pre-release versions of the emulator UI",
67
79
  },
@@ -109,6 +121,11 @@ exports.ALL_EXPERIMENTS = experiments({
109
121
  default: false,
110
122
  public: false,
111
123
  },
124
+ abiu: {
125
+ shortDescription: "Enable App Hosting ABIU and runtime selection",
126
+ default: false,
127
+ public: false,
128
+ },
112
129
  dataconnect: {
113
130
  shortDescription: "Deprecated. Previosuly, enabled Data Connect related features.",
114
131
  fullDescription: "Deprecated. Previously, enabled Data Connect related features.",
@@ -150,6 +167,11 @@ exports.ALL_EXPERIMENTS = experiments({
150
167
  default: true,
151
168
  public: false,
152
169
  },
170
+ fdcrealtime: {
171
+ shortDescription: "Enable Firebase Data Connect realtime feature.",
172
+ default: false,
173
+ public: false,
174
+ },
153
175
  });
154
176
  function isValidExperiment(name) {
155
177
  return Object.keys(exports.ALL_EXPERIMENTS).includes(name);
@@ -17,7 +17,8 @@ const secrets_1 = require("../apphosting/secrets");
17
17
  const env = require("../functions/env");
18
18
  const error_1 = require("../error");
19
19
  const os = require("os");
20
- async function setupAntigravityMcpServer(rootPath, appType) {
20
+ const agentSkills_1 = require("../agentSkills");
21
+ async function setupAntigravityMcpServer(rootPath, appType, nonInteractive) {
21
22
  const mcpConfigDir = path.join(os.homedir(), ".gemini", "antigravity");
22
23
  const mcpConfigPath = path.join(mcpConfigDir, "mcp_config.json");
23
24
  let mcpConfig = { mcpServers: {} };
@@ -43,6 +44,7 @@ async function setupAntigravityMcpServer(rootPath, appType) {
43
44
  const confirmFirebase = await prompt.confirm({
44
45
  message: "Would you like to enable the Firebase MCP server for Antigravity?",
45
46
  default: true,
47
+ nonInteractive,
46
48
  });
47
49
  if (confirmFirebase) {
48
50
  mcpConfig.mcpServers["firebase"] = {
@@ -66,6 +68,7 @@ async function setupAntigravityMcpServer(rootPath, appType) {
66
68
  const confirmDart = await prompt.confirm({
67
69
  message: "Would you like to enable the Dart MCP server for Antigravity?",
68
70
  default: true,
71
+ nonInteractive,
69
72
  });
70
73
  if (confirmDart) {
71
74
  mcpConfig.mcpServers["dart"] = {
@@ -89,8 +92,7 @@ async function setupAntigravityMcpServer(rootPath, appType) {
89
92
  }
90
93
  }
91
94
  catch (err) {
92
- const message = err instanceof Error ? err.message : String(err);
93
- utils.logWarning(`Could not configure Antigravity MCP server: ${message}`);
95
+ utils.logWarning(`Could not configure Antigravity MCP server: ${(0, error_1.getErrMsg)(err)}`);
94
96
  }
95
97
  }
96
98
  async function detectAppType(rootPath) {
@@ -218,7 +220,7 @@ async function updateReadme(rootPath, framework) {
218
220
  await fs.writeFile(readmePath, newReadme);
219
221
  logger_1.logger.info("✅ Updated README.md with project details and origin info");
220
222
  }
221
- async function injectAntigravityContext(rootPath, projectId, appName) {
223
+ async function injectAntigravityContext(rootPath, projectId, appName, nonInteractive) {
222
224
  const agentDir = path.join(rootPath, ".agents");
223
225
  const rulesDir = path.join(agentDir, "rules");
224
226
  const workflowsDir = path.join(agentDir, "workflows");
@@ -233,40 +235,14 @@ async function injectAntigravityContext(rootPath, projectId, appName) {
233
235
  { name: "Globally for all projects", value: "global" },
234
236
  ],
235
237
  default: "local",
236
- nonInteractive: process.env.NODE_ENV === "test",
238
+ nonInteractive: nonInteractive || process.env.NODE_ENV === "test",
239
+ });
240
+ await (0, agentSkills_1.installAgentSkills)({
241
+ cwd: rootPath,
242
+ global: installLocation === "global",
243
+ background: false,
244
+ agentName: "gemini-cli",
237
245
  });
238
- logger_1.logger.info("⏳ Adding Antigravity skills...");
239
- try {
240
- const args = [
241
- "-y",
242
- "skills",
243
- "add",
244
- "firebase/agent-skills",
245
- "-a",
246
- "gemini-cli",
247
- "--skill",
248
- "*",
249
- "-y",
250
- ];
251
- if (installLocation === "global") {
252
- args.push("-g");
253
- }
254
- const result = (0, child_process_1.spawnSync)("npx", args, {
255
- cwd: rootPath,
256
- stdio: "ignore",
257
- shell: process.platform === "win32",
258
- });
259
- if (result.error) {
260
- throw result.error;
261
- }
262
- if (result.status !== 0) {
263
- throw new Error(`npx skills add exited with code ${result.status}`);
264
- }
265
- logger_1.logger.info(`✅ Added Antigravity skills`);
266
- }
267
- catch (err) {
268
- utils.logWarning(`Could not add Antigravity skills, skipping. ${err}`);
269
- }
270
246
  const systemInstructionsTemplate = await (0, templates_1.readTemplate)("firebase-studio-export/system_instructions_template.md");
271
247
  const systemInstructions = systemInstructionsTemplate.replace("${appName}", appName);
272
248
  await fs.writeFile(path.join(rulesDir, "migration-context.md"), systemInstructions);
@@ -277,8 +253,7 @@ async function injectAntigravityContext(rootPath, projectId, appName) {
277
253
  logger_1.logger.info("✅ Created Antigravity startup workflow");
278
254
  }
279
255
  catch (err) {
280
- const message = err instanceof Error ? err.message : String(err);
281
- logger_1.logger.debug(`Could not read or write startup workflow: ${message}`);
256
+ logger_1.logger.debug(`Could not read or write startup workflow: ${(0, error_1.getErrMsg)(err)}`);
282
257
  }
283
258
  }
284
259
  async function getAgyCommand(startAgy) {
@@ -316,7 +291,7 @@ async function getAgyCommand(startAgy) {
316
291
  logger_1.logger.info(`⚠️ Antigravity IDE not found in your PATH. To ensure a seamless migration, please download and install Antigravity: ${downloadLink}`);
317
292
  return undefined;
318
293
  }
319
- async function createFirebaseConfigs(rootPath, projectId) {
294
+ async function createFirebaseConfigs(rootPath, projectId, nonInteractive) {
320
295
  if (!projectId) {
321
296
  return;
322
297
  }
@@ -351,7 +326,7 @@ async function createFirebaseConfigs(rootPath, projectId) {
351
326
  const confirmBackend = await prompt.confirm({
352
327
  message: `Would you like to use the App Hosting backend "${selectedBackendId}"?`,
353
328
  default: true,
354
- nonInteractive: process.env.NODE_ENV === "test",
329
+ nonInteractive: nonInteractive || process.env.NODE_ENV === "test",
355
330
  });
356
331
  if (confirmBackend) {
357
332
  backendId = selectedBackendId;
@@ -376,8 +351,7 @@ async function createFirebaseConfigs(rootPath, projectId) {
376
351
  }
377
352
  }
378
353
  catch (err) {
379
- const message = err instanceof Error ? err.message : String(err);
380
- utils.logWarning(`Could not fetch backends from Firebase CLI, using default "studio". ${message}`);
354
+ utils.logWarning(`Could not fetch backends from Firebase CLI, using default "studio". ${(0, error_1.getErrMsg)(err)}`);
381
355
  }
382
356
  const firebaseJson = {
383
357
  apphosting: {
@@ -433,8 +407,7 @@ async function writeAntigravityConfigs(rootPath, framework) {
433
407
  settings = JSON.parse(settingsContent);
434
408
  }
435
409
  catch (err) {
436
- const message = err instanceof Error ? err.message : String(err);
437
- logger_1.logger.debug(`Could not read ${settingsPath}: ${message}`);
410
+ logger_1.logger.debug(`Could not read ${settingsPath}: ${(0, error_1.getErrMsg)(err)}`);
438
411
  }
439
412
  const cleanSettings = {};
440
413
  for (const [key, value] of Object.entries(settings)) {
@@ -497,8 +470,7 @@ async function cleanupUnusedFiles(rootPath) {
497
470
  }
498
471
  }
499
472
  catch (err) {
500
- const message = err instanceof Error ? err.message : String(err);
501
- logger_1.logger.debug(`Could not remove ${docsDir}: ${message}`);
473
+ logger_1.logger.debug(`Could not remove ${docsDir}: ${(0, error_1.getErrMsg)(err)}`);
502
474
  }
503
475
  const modifiedPath = path.join(rootPath, ".modified");
504
476
  try {
@@ -506,8 +478,7 @@ async function cleanupUnusedFiles(rootPath) {
506
478
  logger_1.logger.info("✅ Cleaned up .modified");
507
479
  }
508
480
  catch (err) {
509
- const message = err instanceof Error ? err.message : String(err);
510
- logger_1.logger.debug(`Could not delete ${modifiedPath}: ${message}`);
481
+ logger_1.logger.debug(`Could not delete ${modifiedPath}: ${(0, error_1.getErrMsg)(err)}`);
511
482
  }
512
483
  const mcpJsonPath = path.join(rootPath, ".idx", "mcp.json");
513
484
  try {
@@ -515,8 +486,7 @@ async function cleanupUnusedFiles(rootPath) {
515
486
  logger_1.logger.info("✅ Cleaned up .idx/mcp.json");
516
487
  }
517
488
  catch (err) {
518
- const message = err instanceof Error ? err.message : String(err);
519
- logger_1.logger.debug(`Could not delete ${mcpJsonPath}: ${message}`);
489
+ logger_1.logger.debug(`Could not delete ${mcpJsonPath}: ${(0, error_1.getErrMsg)(err)}`);
520
490
  }
521
491
  }
522
492
  async function upgradeGenkitVersion(rootPath) {
@@ -548,8 +518,7 @@ async function upgradeGenkitVersion(rootPath) {
548
518
  }
549
519
  }
550
520
  catch (err) {
551
- const message = err instanceof Error ? err.message : String(err);
552
- logger_1.logger.debug(`Could not upgrade Genkit version: ${message}`);
521
+ logger_1.logger.debug(`Could not upgrade Genkit version: ${(0, error_1.getErrMsg)(err)}`);
553
522
  }
554
523
  }
555
524
  async function uploadSecrets(rootPath, projectId) {
@@ -577,11 +546,10 @@ async function uploadSecrets(rootPath, projectId) {
577
546
  }
578
547
  }
579
548
  catch (err) {
580
- const message = err instanceof Error ? err.message : String(err);
581
- utils.logWarning(`Failed to upload GEMINI_API_KEY secret: ${message}`);
549
+ utils.logWarning(`Failed to upload GEMINI_API_KEY secret: ${(0, error_1.getErrMsg)(err)}`);
582
550
  }
583
551
  }
584
- async function askToOpenAntigravity(rootPath, appName, startAntigravity) {
552
+ async function askToOpenAntigravity(rootPath, appName, startAntigravity, nonInteractive) {
585
553
  const agyCommand = await getAgyCommand(startAntigravity);
586
554
  logger_1.logger.info(`\n🎉 Your Firebase Studio project "${appName}" is now ready for Antigravity!`);
587
555
  logger_1.logger.info("Antigravity is Google's agentic IDE, where you can collaborate with AI agents to build, test, and deploy your application.");
@@ -595,6 +563,7 @@ async function askToOpenAntigravity(rootPath, appName, startAntigravity) {
595
563
  const answer = await prompt.confirm({
596
564
  message: "Would you like to open it in Antigravity now?",
597
565
  default: true,
566
+ nonInteractive,
598
567
  });
599
568
  if (answer) {
600
569
  logger_1.logger.info(`⏳ Opening ${appName} in Antigravity...`);
@@ -629,7 +598,7 @@ async function checkDirectoryExists(dir) {
629
598
  async function migrate(rootPath, options = { startAntigravity: true }) {
630
599
  await checkDirectoryExists(rootPath);
631
600
  const appType = await detectAppType(rootPath);
632
- void track.trackGA4("firebase_studio_migrate", { app_type: appType, result: "started" });
601
+ await track.trackGA4("firebase_studio_migrate", { app_type: appType, result: "started" });
633
602
  logger_1.logger.info("🚀 Starting Firebase Studio to Antigravity migration...");
634
603
  logger_1.logger.info("\nFile any bugs at https://github.com/firebase/firebase-tools/issues");
635
604
  const { projectId, appName } = await extractMetadata(rootPath, options.project);
@@ -637,17 +606,17 @@ async function migrate(rootPath, options = { startAntigravity: true }) {
637
606
  logger_1.logger.info(`✅ Detected framework: ${appType}`);
638
607
  }
639
608
  await updateReadme(rootPath, appType);
640
- await createFirebaseConfigs(rootPath, projectId);
609
+ await createFirebaseConfigs(rootPath, projectId, options.nonInteractive);
641
610
  await uploadSecrets(rootPath, projectId);
642
611
  await upgradeGenkitVersion(rootPath);
643
- await injectAntigravityContext(rootPath, projectId, appName);
612
+ await injectAntigravityContext(rootPath, projectId, appName, options.nonInteractive);
644
613
  await writeAntigravityConfigs(rootPath, appType);
645
- await setupAntigravityMcpServer(rootPath, appType);
614
+ await setupAntigravityMcpServer(rootPath, appType, options.nonInteractive);
646
615
  await cleanupUnusedFiles(rootPath);
647
616
  const currentFolderName = path.basename(rootPath);
648
617
  if (currentFolderName === "download") {
649
618
  logger_1.logger.info(`\n💡 Tip: You may want to rename this folder to "${appName.toLowerCase().replace(/\s+/g, "-")}"`);
650
619
  }
651
620
  await track.trackGA4("firebase_studio_migrate", { app_type: appType, result: "success" });
652
- await askToOpenAntigravity(rootPath, appName, options.startAntigravity);
621
+ await askToOpenAntigravity(rootPath, appName, options.startAntigravity, options.nonInteractive);
653
622
  }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getInternalIac = getInternalIac;
4
+ const runtimes = require("../../deploy/functions/runtimes");
5
+ const supported = require("../../deploy/functions/runtimes/supported");
6
+ const functionsConfig = require("../../functionsConfig");
7
+ const functionsEnv = require("../../functions/env");
8
+ const logger_1 = require("../../logger");
9
+ const yaml = require("js-yaml");
10
+ const projectUtils_1 = require("../../projectUtils");
11
+ const error_1 = require("../../error");
12
+ async function getInternalIac(options, codebase) {
13
+ if (!codebase.source) {
14
+ throw new error_1.FirebaseError("Cannot export a codebase with no source");
15
+ }
16
+ const projectId = (0, projectUtils_1.needProjectId)(options);
17
+ const firebaseConfig = await functionsConfig.getFirebaseConfig(options);
18
+ const firebaseEnvs = functionsEnv.loadFirebaseEnvs(firebaseConfig, projectId);
19
+ const delegateContext = {
20
+ projectId,
21
+ sourceDir: options.config.path(codebase.source),
22
+ projectDir: options.config.projectDir,
23
+ runtime: codebase.runtime,
24
+ };
25
+ const runtimeDelegate = await runtimes.getRuntimeDelegate(delegateContext);
26
+ logger_1.logger.debug(`Validating ${runtimeDelegate.language} source`);
27
+ supported.guardVersionSupport(runtimeDelegate.runtime);
28
+ await runtimeDelegate.validate();
29
+ logger_1.logger.debug(`Building ${runtimeDelegate.language} source`);
30
+ await runtimeDelegate.build();
31
+ logger_1.logger.debug(`Discovering ${runtimeDelegate.language} source`);
32
+ const build = await runtimeDelegate.discoverBuild({}, firebaseEnvs);
33
+ return {
34
+ "functions.yaml": yaml.dump(build),
35
+ };
36
+ }