firebase-tools 14.8.0 → 14.9.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/lib/api.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runtimeconfigOrigin = exports.rulesOrigin = exports.resourceManagerOrigin = exports.crashlyticsApiOrigin = exports.messagingApiOrigin = exports.remoteConfigApiOrigin = exports.rtdbMetadataOrigin = exports.rtdbManagementOrigin = exports.realtimeOrigin = exports.extensionsTOSOrigin = exports.extensionsPublisherOrigin = exports.extensionsOrigin = exports.iamOrigin = exports.identityOrigin = exports.hostingOrigin = exports.googleOrigin = exports.pubsubOrigin = exports.cloudTasksOrigin = exports.cloudschedulerOrigin = exports.cloudCompanionOrigin = exports.cloudbuildOrigin = exports.functionsDefaultRegion = exports.runOrigin = exports.functionsV2Origin = exports.functionsOrigin = exports.firestoreOrigin = exports.firestoreOriginOrEmulator = exports.firedataOrigin = exports.firebaseExtensionsRegistryOrigin = exports.firebaseApiOrigin = exports.eventarcOrigin = exports.dynamicLinksKey = exports.dynamicLinksOrigin = exports.consoleOrigin = exports.authManagementOrigin = exports.authOrigin = exports.apphostingGitHubAppInstallationURL = exports.apphostingP4SADomain = exports.apphostingOrigin = exports.appDistributionOrigin = exports.artifactRegistryDomain = exports.developerConnectP4SADomain = exports.developerConnectOrigin = exports.containerRegistryDomain = exports.cloudMonitoringOrigin = exports.cloudloggingOrigin = exports.cloudbillingOrigin = exports.clientSecret = exports.clientId = exports.authProxyOrigin = void 0;
4
- exports.setScopes = exports.getScopes = exports.cloudAiCompanionOrigin = exports.vertexAIOrigin = exports.cloudSQLAdminOrigin = exports.dataConnectLocalConnString = exports.dataconnectP4SADomain = exports.dataconnectOrigin = exports.githubClientSecret = exports.githubClientId = exports.computeOrigin = exports.secretManagerOrigin = exports.githubApiOrigin = exports.githubOrigin = exports.serviceUsageOrigin = exports.cloudRunApiOrigin = exports.hostingApiOrigin = exports.firebaseStorageOrigin = exports.storageOrigin = void 0;
3
+ exports.storageOrigin = exports.runtimeconfigOrigin = exports.rulesOrigin = exports.resourceManagerOrigin = exports.crashlyticsApiOrigin = exports.messagingApiOrigin = exports.remoteConfigApiOrigin = exports.rtdbMetadataOrigin = exports.rtdbManagementOrigin = exports.realtimeOrigin = exports.extensionsTOSOrigin = exports.extensionsPublisherOrigin = exports.extensionsOrigin = exports.iamOrigin = exports.identityOrigin = exports.hostingOrigin = exports.googleOrigin = exports.pubsubOrigin = exports.cloudTasksOrigin = exports.cloudschedulerOrigin = exports.cloudbuildOrigin = exports.functionsDefaultRegion = exports.runOrigin = exports.functionsV2Origin = exports.functionsOrigin = exports.firestoreOrigin = exports.firestoreOriginOrEmulator = exports.firedataOrigin = exports.firebaseExtensionsRegistryOrigin = exports.firebaseApiOrigin = exports.eventarcOrigin = exports.dynamicLinksKey = exports.dynamicLinksOrigin = exports.consoleOrigin = exports.authManagementOrigin = exports.authOrigin = exports.apphostingGitHubAppInstallationURL = exports.apphostingP4SADomain = exports.apphostingOrigin = exports.appDistributionOrigin = exports.artifactRegistryDomain = exports.developerConnectP4SADomain = exports.developerConnectOrigin = exports.containerRegistryDomain = exports.cloudMonitoringOrigin = exports.cloudloggingOrigin = exports.cloudbillingOrigin = exports.clientSecret = exports.clientId = exports.authProxyOrigin = void 0;
4
+ exports.setScopes = exports.getScopes = exports.cloudAiCompanionOrigin = exports.vertexAIOrigin = exports.cloudSQLAdminOrigin = exports.dataConnectLocalConnString = exports.dataconnectP4SADomain = exports.dataconnectOrigin = exports.githubClientSecret = exports.githubClientId = exports.computeOrigin = exports.secretManagerOrigin = exports.githubApiOrigin = exports.githubOrigin = exports.serviceUsageOrigin = exports.cloudRunApiOrigin = exports.hostingApiOrigin = exports.firebaseStorageOrigin = void 0;
5
5
  const constants_1 = require("./emulator/constants");
6
6
  const logger_1 = require("./logger");
7
7
  const scopes = require("./scopes");
@@ -72,8 +72,6 @@ const functionsDefaultRegion = () => utils.envOverride("FIREBASE_FUNCTIONS_DEFAU
72
72
  exports.functionsDefaultRegion = functionsDefaultRegion;
73
73
  const cloudbuildOrigin = () => utils.envOverride("FIREBASE_CLOUDBUILD_URL", "https://cloudbuild.googleapis.com");
74
74
  exports.cloudbuildOrigin = cloudbuildOrigin;
75
- const cloudCompanionOrigin = () => utils.envOverride("CLOUD_COMPANION_URL", "https://cloudaicompanion.googleapis.com");
76
- exports.cloudCompanionOrigin = cloudCompanionOrigin;
77
75
  const cloudschedulerOrigin = () => utils.envOverride("FIREBASE_CLOUDSCHEDULER_URL", "https://cloudscheduler.googleapis.com");
78
76
  exports.cloudschedulerOrigin = cloudschedulerOrigin;
79
77
  const cloudTasksOrigin = () => utils.envOverride("FIREBASE_CLOUD_TAKS_URL", "https://cloudtasks.googleapis.com");
@@ -27,7 +27,8 @@ exports.command = new command_1.Command("login")
27
27
  return user;
28
28
  }
29
29
  if (!options.reauth) {
30
- utils.logBullet("Firebase CLI integrates with Gemini in Firebase API to provide assistant features. Learn more about using Gemini in Firebase and how we train our models: https://firebase.google.com/docs/gemini-in-firebase/set-up-gemini#required-permissions");
30
+ utils.logBullet("The Firebase CLI’s MCP server feature can optionally make use of Gemini in Firebase. " +
31
+ "Learn more about Gemini in Firebase and how it uses your data: https://firebase.google.com/docs/gemini-in-firebase#how-gemini-in-firebase-uses-your-data");
31
32
  const geminiUsage = await (0, prompt_1.confirm)("Enable Gemini in Firebase features?");
32
33
  configstore_1.configstore.set("gemini", geminiUsage);
33
34
  logger_1.logger.info();
@@ -1,17 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ensureSparkApis = exports.ensureApis = void 0;
3
+ exports.ensureGIFApis = exports.ensureSparkApis = exports.ensureApis = void 0;
4
4
  const api = require("../api");
5
5
  const ensureApiEnabled_1 = require("../ensureApiEnabled");
6
+ const prefix = "dataconnect";
6
7
  async function ensureApis(projectId) {
7
- const prefix = "dataconnect";
8
8
  await (0, ensureApiEnabled_1.ensure)(projectId, api.dataconnectOrigin(), prefix);
9
9
  await (0, ensureApiEnabled_1.ensure)(projectId, api.cloudSQLAdminOrigin(), prefix);
10
10
  await (0, ensureApiEnabled_1.ensure)(projectId, api.computeOrigin(), prefix);
11
11
  }
12
12
  exports.ensureApis = ensureApis;
13
13
  async function ensureSparkApis(projectId) {
14
- const prefix = "dataconnect";
15
14
  await (0, ensureApiEnabled_1.ensure)(projectId, api.cloudSQLAdminOrigin(), prefix);
16
15
  }
17
16
  exports.ensureSparkApis = ensureSparkApis;
17
+ async function ensureGIFApis(projectId) {
18
+ await (0, ensureApiEnabled_1.ensure)(projectId, api.cloudAiCompanionOrigin(), prefix);
19
+ }
20
+ exports.ensureGIFApis = ensureGIFApis;
@@ -52,12 +52,13 @@ async function detectFromYaml(directory, project, runtime) {
52
52
  exports.detectFromYaml = detectFromYaml;
53
53
  async function detectFromPort(port, project, runtime, initialDelay = 0, timeout = 10000) {
54
54
  let res;
55
+ const discoveryTimeout = getFunctionDiscoveryTimeout() || timeout;
55
56
  const timedOut = new Promise((resolve, reject) => {
56
57
  setTimeout(() => {
57
58
  const originalError = "User code failed to load. Cannot determine backend specification.";
58
- const error = `${originalError} Timeout after ${timeout}. See https://firebase.google.com/docs/functions/tips#avoid_deployment_timeouts_during_initialization'`;
59
+ const error = `${originalError} Timeout after ${discoveryTimeout}. See https://firebase.google.com/docs/functions/tips#avoid_deployment_timeouts_during_initialization'`;
59
60
  reject(new error_1.FirebaseError(error));
60
- }, getFunctionDiscoveryTimeout() || timeout);
61
+ }, discoveryTimeout);
61
62
  });
62
63
  if (initialDelay > 0) {
63
64
  await new Promise((resolve) => setTimeout(resolve, initialDelay));
@@ -43,7 +43,7 @@ exports.RUNTIMES = runtimes({
43
43
  },
44
44
  nodejs18: {
45
45
  friendly: "Node.js 18",
46
- status: "decommissioned",
46
+ status: "deprecated",
47
47
  deprecationDate: "2025-04-30",
48
48
  decommissionDate: "2025-10-30",
49
49
  },
@@ -19,7 +19,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
19
19
  function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
20
20
  };
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.fromNodeSocket = exports.PostgresServer = exports.TRUNCATE_TABLES_SQL = void 0;
22
+ exports.PGliteExtendedQueryPatch = exports.fromNodeSocket = exports.PostgresServer = exports.TRUNCATE_TABLES_SQL = void 0;
23
23
  const pglite_1 = require("@electric-sql/pglite");
24
24
  const { dynamicImport } = require(true && "../../dynamicImport");
25
25
  const net = require("node:net");
@@ -30,6 +30,7 @@ const pg_gateway_1 = require("pg-gateway");
30
30
  const logger_1 = require("../../logger");
31
31
  const error_1 = require("../../error");
32
32
  const fsutils_1 = require("../../fsutils");
33
+ const node_string_decoder_1 = require("node:string_decoder");
33
34
  exports.TRUNCATE_TABLES_SQL = `
34
35
  DO $do$
35
36
  DECLARE _clear text;
@@ -42,11 +43,13 @@ BEGIN
42
43
  EXECUTE COALESCE(_clear, 'select now()');
43
44
  END
44
45
  $do$;`;
46
+ const decoder = new node_string_decoder_1.StringDecoder();
47
+ const pgliteDebugLog = fs.createWriteStream("pglite-debug.log");
45
48
  class PostgresServer {
46
49
  async createPGServer(host = "127.0.0.1", port) {
47
50
  const getDb = this.getDb.bind(this);
48
51
  const server = net.createServer(async (socket) => {
49
- await fromNodeSocket(socket, {
52
+ const connection = await fromNodeSocket(socket, {
50
53
  serverVersion: "17.4 (PGlite 0.3.3)",
51
54
  auth: { method: "trust" },
52
55
  onMessage(data, { isAuthenticated }) {
@@ -61,7 +64,7 @@ class PostgresServer {
61
64
  }
62
65
  const response = yield __await(db.execProtocolRaw(data));
63
66
  try {
64
- for (var _d = true, _e = __asyncValues((0, pg_gateway_1.getMessages)(response)), _f; _f = yield __await(_e.next()), _a = _f.done, !_a;) {
67
+ for (var _d = true, _e = __asyncValues(extendedQueryPatch.filterResponse(data, response)), _f; _f = yield __await(_e.next()), _a = _f.done, !_a;) {
65
68
  _c = _f.value;
66
69
  _d = false;
67
70
  try {
@@ -83,6 +86,7 @@ class PostgresServer {
83
86
  });
84
87
  },
85
88
  });
89
+ const extendedQueryPatch = new PGliteExtendedQueryPatch(connection);
86
90
  socket.on("end", () => {
87
91
  logger_1.logger.debug("Postgres client disconnected");
88
92
  });
@@ -223,7 +227,7 @@ class PostgresServer {
223
227
  this.server = undefined;
224
228
  this.baseDataDirectory = args.dataDirectory;
225
229
  this.importPath = args.importPath;
226
- this.debug = args.debug ? 5 : 0;
230
+ this.debug = args.debug ? 1 : 0;
227
231
  }
228
232
  }
229
233
  exports.PostgresServer = PostgresServer;
@@ -235,3 +239,61 @@ async function fromNodeSocket(socket, options) {
235
239
  return new pg_gateway_1.PostgresConnection({ readable: rs, writable: ws }, opts);
236
240
  }
237
241
  exports.fromNodeSocket = fromNodeSocket;
242
+ class PGliteExtendedQueryPatch {
243
+ constructor(connection) {
244
+ this.connection = connection;
245
+ this.isExtendedQuery = false;
246
+ this.eqpErrored = false;
247
+ }
248
+ filterResponse(message, response) {
249
+ return __asyncGenerator(this, arguments, function* filterResponse_1() {
250
+ var _a, e_2, _b, _c;
251
+ const pipelineStartMessages = [
252
+ pg_gateway_1.FrontendMessageCode.Parse,
253
+ pg_gateway_1.FrontendMessageCode.Bind,
254
+ pg_gateway_1.FrontendMessageCode.Close,
255
+ ];
256
+ const decoded = decoder.write(message);
257
+ pgliteDebugLog.write("Front: " + decoded);
258
+ if (pipelineStartMessages.includes(message[0])) {
259
+ this.isExtendedQuery = true;
260
+ }
261
+ if (message[0] === pg_gateway_1.FrontendMessageCode.Sync) {
262
+ this.isExtendedQuery = false;
263
+ this.eqpErrored = false;
264
+ }
265
+ try {
266
+ for (var _d = true, _e = __asyncValues((0, pg_gateway_1.getMessages)(response)), _f; _f = yield __await(_e.next()), _a = _f.done, !_a;) {
267
+ _c = _f.value;
268
+ _d = false;
269
+ try {
270
+ const bm = _c;
271
+ if (this.eqpErrored) {
272
+ continue;
273
+ }
274
+ if (this.isExtendedQuery && bm[0] === pg_gateway_1.BackendMessageCode.ErrorMessage) {
275
+ this.eqpErrored = true;
276
+ }
277
+ if (this.isExtendedQuery && bm[0] === pg_gateway_1.BackendMessageCode.ReadyForQuery) {
278
+ pgliteDebugLog.write("Filtered: " + decoder.write(bm));
279
+ continue;
280
+ }
281
+ pgliteDebugLog.write("Sent: " + decoder.write(bm));
282
+ yield yield __await(bm);
283
+ }
284
+ finally {
285
+ _d = true;
286
+ }
287
+ }
288
+ }
289
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
290
+ finally {
291
+ try {
292
+ if (!_d && !_a && (_b = _e.return)) yield __await(_b.call(_e));
293
+ }
294
+ finally { if (e_2) throw e_2.error; }
295
+ }
296
+ });
297
+ }
298
+ }
299
+ exports.PGliteExtendedQueryPatch = PGliteExtendedQueryPatch;
@@ -54,28 +54,28 @@
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "2.7.1",
58
- "expectedSize": 27607808,
59
- "expectedChecksum": "3cf0b4dcb96639f1b7ddf3e2052ea28f",
60
- "expectedChecksumSHA256": "76b6db1f87e14e3c39035301ae199bec0458a2e7534a1db962276a92fa99c215",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.7.1",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.7.1"
57
+ "version": "2.8.0",
58
+ "expectedSize": 29279072,
59
+ "expectedChecksum": "0381ba8dd2eb67629cbbf4a88b276850",
60
+ "expectedChecksumSHA256": "bc88bca96c83df21f7ed4b66c2a518ca48a21ba259fe481c0911fb5010d2fa0f",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.8.0",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.8.0"
63
63
  },
64
64
  "win32": {
65
- "version": "2.7.1",
66
- "expectedSize": 28067328,
67
- "expectedChecksum": "8dacedaecac242199dfcf3f6c023f89a",
68
- "expectedChecksumSHA256": "fc098efa9aed9b1ac7d35b90da47608190f3a5562f37444012785a9793d743a0",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.7.1",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.7.1.exe"
65
+ "version": "2.8.0",
66
+ "expectedSize": 29769216,
67
+ "expectedChecksum": "622e7c7e23b0bd7592ad5a0ca0934987",
68
+ "expectedChecksumSHA256": "c23484eaf8f1ac68653bb29bb307de9d1c47a1fc4005610b094772e7ac368512",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.8.0",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.8.0.exe"
71
71
  },
72
72
  "linux": {
73
- "version": "2.7.1",
74
- "expectedSize": 27521176,
75
- "expectedChecksum": "3c03d85c2d1de80a2f2ba13fe3b5a3ff",
76
- "expectedChecksumSHA256": "94c35ab64bd8d206e7843513848e7375cd8f9b6a06d013b1ec541ac831cda8a8",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.7.1",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.7.1"
73
+ "version": "2.8.0",
74
+ "expectedSize": 29208760,
75
+ "expectedChecksum": "dd9b1b9a55761f4d763a11bb7b84cbff",
76
+ "expectedChecksumSHA256": "7ffe98a28e9c185ddb07cdbf1610dc86747083afca9ac8cb850214ca8f1147c4",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.8.0",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.8.0"
79
79
  }
80
80
  }
81
81
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFunctions = exports.listFunctions = exports.deleteFunction = exports.updateFunction = exports.setInvokerUpdate = exports.setInvokerCreate = exports.getIamPolicy = exports.setIamPolicy = exports.createFunction = exports.generateUploadUrl = exports.API_VERSION = void 0;
3
+ exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFunctions = exports.listFunctions = exports.deleteFunction = exports.updateFunction = exports.setInvokerUpdate = exports.setInvokerCreate = exports.getIamPolicy = exports.setIamPolicy = exports.createFunction = exports.generateUploadUrl = exports.captureRuntimeValidationError = exports.API_VERSION = void 0;
4
4
  const clc = require("colorette");
5
5
  const error_1 = require("../error");
6
6
  const logger_1 = require("../logger");
@@ -14,8 +14,22 @@ const api_1 = require("../api");
14
14
  const constants_1 = require("../functions/constants");
15
15
  exports.API_VERSION = "v1";
16
16
  const client = new apiv2_1.Client({ urlPrefix: (0, api_1.functionsOrigin)(), apiVersion: exports.API_VERSION });
17
+ function captureRuntimeValidationError(errMessage) {
18
+ const regex = /message: "((?:\\.|[^"\\])*)"/;
19
+ const match = errMessage.match(regex);
20
+ if (match && match[1]) {
21
+ const capturedMessage = match[1].replace(/\\"/g, '"');
22
+ return capturedMessage;
23
+ }
24
+ return "invalid runtime detected, please see https://cloud.google.com/functions/docs/runtime-support for the latest supported runtimes";
25
+ }
26
+ exports.captureRuntimeValidationError = captureRuntimeValidationError;
17
27
  function functionsOpLogReject(funcName, type, err) {
18
28
  var _a, _b, _c, _d;
29
+ if ((err === null || err === void 0 ? void 0 : err.message).includes("Runtime validation errors")) {
30
+ const capturedMessage = captureRuntimeValidationError(err.message);
31
+ utils.logWarning(clc.bold(clc.yellow("functions:")) + " " + capturedMessage + " for function " + funcName);
32
+ }
19
33
  if (((_b = (_a = err === null || err === void 0 ? void 0 : err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode) === 429) {
20
34
  utils.logWarning(`${clc.bold(clc.yellow("functions:"))} got "Quota Exceeded" error while trying to ${type} ${funcName}. Waiting to retry...`);
21
35
  }
@@ -12,6 +12,7 @@ const proto = require("./proto");
12
12
  const utils = require("../utils");
13
13
  const projectConfig = require("../functions/projectConfig");
14
14
  const constants_1 = require("../functions/constants");
15
+ const cloudfunctions_1 = require("./cloudfunctions");
15
16
  exports.API_VERSION = "v2";
16
17
  const DEFAULT_MAX_INSTANCE_COUNT = 100;
17
18
  const client = new apiv2_1.Client({
@@ -49,8 +50,12 @@ function mebibytes(memory) {
49
50
  }
50
51
  exports.mebibytes = mebibytes;
51
52
  function functionsOpLogReject(func, type, err) {
52
- var _a, _b, _c, _d, _e, _f;
53
- if ((_a = err === null || err === void 0 ? void 0 : err.message) === null || _a === void 0 ? void 0 : _a.includes("maxScale may not exceed")) {
53
+ var _a, _b, _c, _d, _e, _f, _g;
54
+ if ((_a = err === null || err === void 0 ? void 0 : err.message) === null || _a === void 0 ? void 0 : _a.includes("Runtime validation errors")) {
55
+ const capturedMessage = (0, cloudfunctions_1.captureRuntimeValidationError)(err.message);
56
+ utils.logLabeledWarning("functions", capturedMessage + " for function " + func.name);
57
+ }
58
+ if ((_b = err === null || err === void 0 ? void 0 : err.message) === null || _b === void 0 ? void 0 : _b.includes("maxScale may not exceed")) {
54
59
  const maxInstances = func.serviceConfig.maxInstanceCount || DEFAULT_MAX_INSTANCE_COUNT;
55
60
  utils.logLabeledWarning("functions", `Your current project quotas don't allow for the current max instances setting of ${maxInstances}. ` +
56
61
  "Either reduce this function's maximum instances, or request a quota increase on the underlying Cloud Run service " +
@@ -62,17 +67,17 @@ function functionsOpLogReject(func, type, err) {
62
67
  }
63
68
  else {
64
69
  utils.logLabeledWarning("functions", `${err === null || err === void 0 ? void 0 : err.message}`);
65
- if (((_c = (_b = err === null || err === void 0 ? void 0 : err.context) === null || _b === void 0 ? void 0 : _b.response) === null || _c === void 0 ? void 0 : _c.statusCode) === 429) {
70
+ if (((_d = (_c = err === null || err === void 0 ? void 0 : err.context) === null || _c === void 0 ? void 0 : _c.response) === null || _d === void 0 ? void 0 : _d.statusCode) === 429) {
66
71
  utils.logLabeledWarning("functions", `Got "Quota Exceeded" error while trying to ${type} ${func.name}. Waiting to retry...`);
67
72
  }
68
- else if ((_d = err === null || err === void 0 ? void 0 : err.message) === null || _d === void 0 ? void 0 : _d.includes("If you recently started to use Eventarc, it may take a few minutes before all necessary permissions are propagated to the Service Agent")) {
73
+ else if ((_e = err === null || err === void 0 ? void 0 : err.message) === null || _e === void 0 ? void 0 : _e.includes("If you recently started to use Eventarc, it may take a few minutes before all necessary permissions are propagated to the Service Agent")) {
69
74
  utils.logLabeledWarning("functions", `Since this is your first time using 2nd gen functions, we need a little bit longer to finish setting everything up. Retry the deployment in a few minutes.`);
70
75
  }
71
76
  utils.logLabeledWarning("functions", ` failed to ${type} function ${func.name}`);
72
77
  }
73
78
  throw new error_1.FirebaseError(`Failed to ${type} function ${func.name}`, {
74
79
  original: err,
75
- status: (_f = (_e = err === null || err === void 0 ? void 0 : err.context) === null || _e === void 0 ? void 0 : _e.response) === null || _f === void 0 ? void 0 : _f.statusCode,
80
+ status: (_g = (_f = err === null || err === void 0 ? void 0 : err.context) === null || _f === void 0 ? void 0 : _f.response) === null || _g === void 0 ? void 0 : _g.statusCode,
76
81
  context: { function: func.name },
77
82
  });
78
83
  }
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateOperation = exports.chatWithFirebase = exports.generateSchema = void 0;
3
+ exports.extractCodeBlock = exports.generateOperation = exports.chatWithFirebase = exports.generateSchema = void 0;
4
4
  const apiv2_1 = require("../apiv2");
5
5
  const api_1 = require("../api");
6
- const apiClient = new apiv2_1.Client({ urlPrefix: (0, api_1.cloudCompanionOrigin)(), auth: true });
6
+ const error_1 = require("../error");
7
+ const apiClient = new apiv2_1.Client({ urlPrefix: (0, api_1.cloudAiCompanionOrigin)(), auth: true });
7
8
  const SCHEMA_GENERATOR_EXPERIENCE = "/appeco/firebase/fdc-schema-generator";
8
9
  const GEMINI_IN_FIREBASE_EXPERIENCE = "/appeco/firebase/firebase-chat/free";
9
10
  const OPERATION_GENERATION_EXPERIENCE = "/appeco/firebase/fdc-query-generator";
@@ -44,3 +45,12 @@ async function generateOperation(prompt, service, project, chatHistory = []) {
44
45
  return res.body.output.messages[0].content;
45
46
  }
46
47
  exports.generateOperation = generateOperation;
48
+ function extractCodeBlock(text) {
49
+ const regex = /```(?:[a-z]+\n)?([\s\S]*?)```/m;
50
+ const match = text.match(regex);
51
+ if (match && match[1]) {
52
+ return match[1].trim();
53
+ }
54
+ throw new error_1.FirebaseError(`No code block found in the generated response: ${text}`);
55
+ }
56
+ exports.extractCodeBlock = extractCodeBlock;
@@ -18,6 +18,8 @@ const utils_1 = require("../../../utils");
18
18
  const cloudbilling_1 = require("../../../gcp/cloudbilling");
19
19
  const sdk = require("./sdk");
20
20
  const fileUtils_1 = require("../../../dataconnect/fileUtils");
21
+ const fdcExperience_1 = require("../../../gemini/fdcExperience");
22
+ const configstore_1 = require("../../../configstore");
21
23
  const DATACONNECT_YAML_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/dataconnect.yaml");
22
24
  const CONNECTOR_YAML_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/connector.yaml");
23
25
  const SCHEMA_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/schema.gql");
@@ -71,7 +73,7 @@ async function askQuestions(setup) {
71
73
  default: true,
72
74
  }));
73
75
  if (shouldConfigureBackend) {
74
- info = await promptForService(info);
76
+ info = await promptForSchema(setup, info);
75
77
  info = await promptForCloudSQL(setup, info);
76
78
  info.shouldProvisionCSQL = !!(setup.projectId &&
77
79
  (info.isNewInstance || info.isNewDatabase) &&
@@ -322,12 +324,31 @@ async function promptForCloudSQL(setup, info) {
322
324
  }
323
325
  return info;
324
326
  }
325
- async function promptForService(info) {
327
+ async function promptForSchema(setup, info) {
326
328
  if (info.serviceId === "") {
327
329
  info.serviceId = await (0, prompt_1.input)({
328
330
  message: "What ID would you like to use for this service?",
329
331
  default: (0, path_1.basename)(process.cwd()),
330
332
  });
333
+ if (setup.projectId) {
334
+ if (!configstore_1.configstore.get("gemini")) {
335
+ (0, utils_1.logBullet)("Learn more about Gemini in Firebase and how it uses your data: https://firebase.google.com/docs/gemini-in-firebase#how-gemini-in-firebase-uses-your-data");
336
+ }
337
+ if (await (0, prompt_1.confirm)({
338
+ message: `Do you want Gemini in Firebase to help generate a schema for your service?`,
339
+ default: false,
340
+ })) {
341
+ configstore_1.configstore.set("gemini", true);
342
+ await (0, ensureApis_1.ensureGIFApis)(setup.projectId);
343
+ const prompt = await (0, prompt_1.input)({
344
+ message: "Describe the app you are building:",
345
+ default: "movie rating app",
346
+ });
347
+ const schema = await (0, utils_1.promiseWithSpinner)(() => (0, fdcExperience_1.generateSchema)(prompt, setup.projectId), "Generating the Data Connect Schema...");
348
+ info.schemaGql = [{ path: "schema.gql", content: (0, fdcExperience_1.extractCodeBlock)(schema) }];
349
+ info.connectors = [emptyConnector];
350
+ }
351
+ }
331
352
  }
332
353
  return info;
333
354
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkFirebaseEnabledForCloudProject = exports.getProject = exports.getFirebaseProject = exports.listFirebaseProjects = exports.getAvailableCloudProjectPage = exports.getFirebaseProjectPage = exports.addFirebaseToCloudProject = exports.createCloudProject = exports.promptAvailableProjectId = exports.getOrPromptProject = exports.addFirebaseToCloudProjectAndLog = exports.createFirebaseProjectAndLog = exports.promptProjectCreation = exports.ProjectParentResourceType = void 0;
3
+ exports.checkFirebaseEnabledForCloudProject = exports.getProject = exports.getFirebaseProject = exports.checkAndRecommendProjectId = exports.listFirebaseProjects = exports.getAvailableCloudProjectPage = exports.getFirebaseProjectPage = exports.addFirebaseToCloudProject = exports.createCloudProject = exports.promptAvailableProjectId = exports.getOrPromptProject = exports.addFirebaseToCloudProjectAndLog = exports.createFirebaseProjectAndLog = exports.promptProjectCreation = exports.ProjectParentResourceType = void 0;
4
4
  const clc = require("colorette");
5
5
  const ora = require("ora");
6
6
  const apiv2_1 = require("../apiv2");
@@ -15,6 +15,7 @@ const TIMEOUT_MILLIS = 30000;
15
15
  const MAXIMUM_PROMPT_LIST = 100;
16
16
  const PROJECT_LIST_PAGE_SIZE = 1000;
17
17
  const CREATE_PROJECT_API_REQUEST_TIMEOUT_MILLIS = 15000;
18
+ const CHECK_PROJECT_ID_API_REQUEST_TIMEOUT_MILLIS = 15000;
18
19
  var ProjectParentResourceType;
19
20
  (function (ProjectParentResourceType) {
20
21
  ProjectParentResourceType["ORGANIZATION"] = "organization";
@@ -25,16 +26,23 @@ async function promptProjectCreation(options) {
25
26
  const projectId = (_a = options.projectId) !== null && _a !== void 0 ? _a : (await prompt.input({
26
27
  message: "Please specify a unique project id " +
27
28
  `(${clc.yellow("warning")}: cannot be modified afterward) [6-30 characters]:\n`,
28
- validate: (projectId) => {
29
+ validate: async (projectId) => {
29
30
  if (projectId.length < 6) {
30
31
  return "Project ID must be at least 6 characters long";
31
32
  }
32
33
  else if (projectId.length > 30) {
33
34
  return "Project ID cannot be longer than 30 characters";
34
35
  }
35
- else {
36
- return true;
36
+ try {
37
+ const { isAvailable, suggestedProjectId } = await checkAndRecommendProjectId(projectId);
38
+ if (!isAvailable && suggestedProjectId) {
39
+ return `Project ID is taken or unavailable. Try ${clc.bold(suggestedProjectId)}.`;
40
+ }
37
41
  }
42
+ catch (error) {
43
+ logger_1.logger.debug(`Couldn't check if project ID ${projectId} is available. Original error: ${error}`);
44
+ }
45
+ return true;
38
46
  },
39
47
  }));
40
48
  const displayName = (_b = options.displayName) !== null && _b !== void 0 ? _b : (await prompt.input({
@@ -60,6 +68,11 @@ const firebaseAPIClient = new apiv2_1.Client({
60
68
  auth: true,
61
69
  apiVersion: "v1beta1",
62
70
  });
71
+ const firebaseV1APIClient = new apiv2_1.Client({
72
+ urlPrefix: api.firebaseApiOrigin(),
73
+ auth: true,
74
+ apiVersion: "v1",
75
+ });
63
76
  const resourceManagerClient = new apiv2_1.Client({
64
77
  urlPrefix: api.resourceManagerOrigin(),
65
78
  apiVersion: "v1",
@@ -306,6 +319,27 @@ async function listFirebaseProjects(pageSize) {
306
319
  return projects;
307
320
  }
308
321
  exports.listFirebaseProjects = listFirebaseProjects;
322
+ async function checkAndRecommendProjectId(projectId) {
323
+ try {
324
+ const res = await firebaseV1APIClient.request({
325
+ method: "POST",
326
+ path: "/projects:checkProjectId",
327
+ body: {
328
+ proposedId: projectId,
329
+ },
330
+ timeout: CHECK_PROJECT_ID_API_REQUEST_TIMEOUT_MILLIS,
331
+ });
332
+ const { projectIdStatus, suggestedProjectId } = res.body;
333
+ return {
334
+ isAvailable: projectIdStatus === "PROJECT_ID_AVAILABLE",
335
+ suggestedProjectId,
336
+ };
337
+ }
338
+ catch (err) {
339
+ throw new error_1.FirebaseError("Failed to check if project ID is available. See firebase-debug.log for more info.", { exit: 2, original: err });
340
+ }
341
+ }
342
+ exports.checkAndRecommendProjectId = checkAndRecommendProjectId;
309
343
  async function getFirebaseProject(projectId) {
310
344
  try {
311
345
  const res = await firebaseAPIClient.request({
package/lib/mcp/errors.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.mcpAuthError = exports.NO_PROJECT_ERROR = void 0;
3
+ exports.mcpGeminiError = exports.mcpAuthError = exports.NO_PROJECT_ERROR = void 0;
4
4
  const util_1 = require("./util");
5
5
  exports.NO_PROJECT_ERROR = (0, util_1.mcpError)('No active project was found. Use the `firebase_update_environment` tool to set the project directory to an absolute folder location containing a firebase.json config file. Alternatively, change the MCP server config to add [...,"--dir","/absolute/path/to/project/directory"] in its command-line arguments.', "PRECONDITION_FAILED");
6
6
  function mcpAuthError() {
@@ -13,3 +13,8 @@ ${cmd} login
13
13
  [ADC]: https://cloud.google.com/docs/authentication/application-default-credentials`);
14
14
  }
15
15
  exports.mcpAuthError = mcpAuthError;
16
+ function mcpGeminiError(projectId) {
17
+ const consoleUrl = `https://firebase.corp.google.com/project/${projectId}/overview`;
18
+ return (0, util_1.mcpError)(`This tool uses the Gemini in Firebase API. Visit Firebase Console to enable the Gemini in Firebase API ${consoleUrl} and try again.`, "PRECONDITION_FAILED");
19
+ }
20
+ exports.mcpGeminiError = mcpGeminiError;
package/lib/mcp/index.js CHANGED
@@ -17,6 +17,8 @@ const config_js_1 = require("../config.js");
17
17
  const rc_js_1 = require("../rc.js");
18
18
  const hubClient_js_1 = require("../emulator/hubClient.js");
19
19
  const node_fs_1 = require("node:fs");
20
+ const ensureApiEnabled_js_1 = require("../ensureApiEnabled.js");
21
+ const api = require("../api.js");
20
22
  const SERVER_VERSION = "0.1.0";
21
23
  const cmd = new command_js_1.Command("experimental:mcp").before(requireAuth_js_1.requireAuth);
22
24
  class FirebaseMcpServer {
@@ -165,7 +167,7 @@ class FirebaseMcpServer {
165
167
  };
166
168
  }
167
169
  async mcpCallTool(request) {
168
- var _a, _b, _c, _d, _e, _f;
170
+ var _a, _b, _c, _d, _e, _f, _g;
169
171
  await this.detectProjectRoot();
170
172
  const toolName = request.params.name;
171
173
  const toolArgs = request.params.arguments;
@@ -185,6 +187,16 @@ class FirebaseMcpServer {
185
187
  if (((_b = tool.mcp._meta) === null || _b === void 0 ? void 0 : _b.requiresAuth) && !accountEmail) {
186
188
  return (0, errors_js_1.mcpAuthError)();
187
189
  }
190
+ if ((_c = tool.mcp._meta) === null || _c === void 0 ? void 0 : _c.requiresGemini) {
191
+ if (configstore_js_1.configstore.get("gemini")) {
192
+ await (0, ensureApiEnabled_js_1.ensure)(projectId, api.cloudAiCompanionOrigin(), "");
193
+ }
194
+ else {
195
+ if (!(await (0, ensureApiEnabled_js_1.check)(projectId, api.cloudAiCompanionOrigin(), ""))) {
196
+ return (0, errors_js_1.mcpGeminiError)(projectId);
197
+ }
198
+ }
199
+ }
188
200
  const options = { projectDir: this.cachedProjectRoot, cwd: this.cachedProjectRoot };
189
201
  const toolsCtx = {
190
202
  projectId: projectId,
@@ -198,8 +210,8 @@ class FirebaseMcpServer {
198
210
  await (0, track_js_1.trackGA4)("mcp_tool_call", {
199
211
  tool_name: toolName,
200
212
  error: res.isError ? 1 : 0,
201
- mcp_client_name: (_c = this.clientInfo) === null || _c === void 0 ? void 0 : _c.name,
202
- mcp_client_version: (_d = this.clientInfo) === null || _d === void 0 ? void 0 : _d.version,
213
+ mcp_client_name: (_d = this.clientInfo) === null || _d === void 0 ? void 0 : _d.name,
214
+ mcp_client_version: (_e = this.clientInfo) === null || _e === void 0 ? void 0 : _e.version,
203
215
  });
204
216
  return res;
205
217
  }
@@ -207,8 +219,8 @@ class FirebaseMcpServer {
207
219
  await (0, track_js_1.trackGA4)("mcp_tool_call", {
208
220
  tool_name: toolName,
209
221
  error: 1,
210
- mcp_client_name: (_e = this.clientInfo) === null || _e === void 0 ? void 0 : _e.name,
211
- mcp_client_version: (_f = this.clientInfo) === null || _f === void 0 ? void 0 : _f.version,
222
+ mcp_client_name: (_f = this.clientInfo) === null || _f === void 0 ? void 0 : _f.name,
223
+ mcp_client_version: (_g = this.clientInfo) === null || _g === void 0 ? void 0 : _g.version,
212
224
  });
213
225
  return (0, util_js_1.mcpError)(err);
214
226
  }
@@ -20,6 +20,7 @@ exports.consult_assistant = (0, tool_js_1.tool)({
20
20
  _meta: {
21
21
  requiresProject: true,
22
22
  requiresAuth: true,
23
+ requiresGemini: true,
23
24
  },
24
25
  }, async ({ prompt }, { projectId }) => {
25
26
  const schema = await (0, fdcExperience_js_1.chatWithFirebase)(prompt, projectId);
@@ -12,6 +12,7 @@ const init_js_1 = require("./init.js");
12
12
  const get_environment_js_1 = require("./get_environment.js");
13
13
  const update_environment_js_1 = require("./update_environment.js");
14
14
  const list_projects_js_1 = require("./list_projects.js");
15
+ const consult_assistant_js_1 = require("./consult_assistant.js");
15
16
  exports.coreTools = [
16
17
  get_project_js_1.get_project,
17
18
  list_apps_js_1.list_apps,
@@ -21,6 +22,7 @@ exports.coreTools = [
21
22
  create_project_js_1.create_project,
22
23
  create_app_js_1.create_app,
23
24
  create_android_sha_js_1.create_android_sha,
25
+ consult_assistant_js_1.consult_assistant,
24
26
  get_environment_js_1.get_environment,
25
27
  update_environment_js_1.update_environment,
26
28
  init_js_1.init,
@@ -11,7 +11,7 @@ exports.list_apps = (0, tool_js_1.tool)({
11
11
  inputSchema: zod_1.z.object({
12
12
  platform: zod_1.z
13
13
  .enum(["ios", "android", "web", "all"])
14
- .default("all")
14
+ .optional()
15
15
  .describe("the specific platform to list (omit to list all platforms)"),
16
16
  }),
17
17
  annotations: {
@@ -24,7 +24,7 @@ exports.list_apps = (0, tool_js_1.tool)({
24
24
  },
25
25
  }, async ({ platform }, { projectId }) => {
26
26
  try {
27
- const apps = await (0, apps_js_1.listFirebaseApps)(projectId, platform === "all" ? apps_js_1.AppPlatform.ANY : platform.toUpperCase());
27
+ const apps = await (0, apps_js_1.listFirebaseApps)(projectId, !platform || platform === "all" ? apps_js_1.AppPlatform.ANY : platform.toUpperCase());
28
28
  return (0, util_js_1.toContent)(apps);
29
29
  }
30
30
  catch (err) {
@@ -25,6 +25,7 @@ exports.generate_operation = (0, tool_js_1.tool)({
25
25
  _meta: {
26
26
  requiresProject: true,
27
27
  requiresAuth: true,
28
+ requiresGemini: true,
28
29
  },
29
30
  }, async ({ prompt, service_id }, { projectId, config }) => {
30
31
  const serviceInfo = await (0, fileUtils_js_1.pickService)(projectId, config, service_id || undefined);
@@ -18,6 +18,7 @@ exports.generate_schema = (0, tool_js_1.tool)({
18
18
  _meta: {
19
19
  requiresProject: true,
20
20
  requiresAuth: true,
21
+ requiresGemini: true,
21
22
  },
22
23
  }, async ({ prompt }, { projectId }) => {
23
24
  const schema = await (0, fdcExperience_js_1.generateSchema)(prompt, projectId);
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.dataconnectTools = void 0;
4
+ const generate_operation_js_1 = require("./generate_operation.js");
5
+ const generate_schema_js_1 = require("./generate_schema.js");
4
6
  const list_services_js_1 = require("./list_services.js");
5
7
  const get_schema_js_1 = require("./get_schema.js");
6
8
  const get_connector_js_1 = require("./get_connector.js");
@@ -10,6 +12,8 @@ const execute_query_js_1 = require("./execute_query.js");
10
12
  const execute_mutation_js_1 = require("./execute_mutation.js");
11
13
  exports.dataconnectTools = [
12
14
  list_services_js_1.list_services,
15
+ generate_schema_js_1.generate_schema,
16
+ generate_operation_js_1.generate_operation,
13
17
  get_schema_js_1.get_schema,
14
18
  get_connector_js_1.get_connectors,
15
19
  execute_graphql_js_1.execute_graphql,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "14.8.0",
3
+ "version": "14.9.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -278,6 +278,7 @@
278
278
  },
279
279
  "runtime": {
280
280
  "enum": [
281
+ "nodejs18",
281
282
  "nodejs20",
282
283
  "nodejs22",
283
284
  "python310",