firebase-tools 14.24.2 → 14.25.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Firebase CLI and MCP Server [![Actions Status][gh-actions-badge]][gh-actions] [![Node Version][node-badge]][npm] [![NPM version][npm-badge]][npm] [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=firebase&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImZpcmViYXNlLXRvb2xzIiwiZXhwZXBpbWVudGFsOm1jcCIsIi0tZGlyIiwiLiJdfQ==)
1
+ # Firebase CLI and MCP Server [![Actions Status][gh-actions-badge]][gh-actions] [![Node Version][node-badge]][npm] [![NPM version][npm-badge]][npm] [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=firebase&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImZpcmViYXNlLXRvb2xzIiwibWNwIiwiLS1kaXIiLCIuIl19)
2
2
 
3
3
  The Firebase Command Line Interface (CLI) Tools can be used to test, manage, and deploy your Firebase project from the command line. This repository is also the home of the official Firebase MCP Server. For more information, please see the [Firebase MCP Server documentation](./src/mcp).
4
4
 
@@ -146,7 +146,10 @@ function shouldUploadBeSkipped(context, wantBackend, haveBackend) {
146
146
  if (!haveEndpoint) {
147
147
  return false;
148
148
  }
149
- return haveEndpoint.hash && wantEndpoint.hash && haveEndpoint.hash === wantEndpoint.hash;
149
+ return (haveEndpoint.hash &&
150
+ wantEndpoint.hash &&
151
+ haveEndpoint.hash === wantEndpoint.hash &&
152
+ haveEndpoint.state === "ACTIVE");
150
153
  });
151
154
  }
152
155
  exports.shouldUploadBeSkipped = shouldUploadBeSkipped;
@@ -589,7 +589,7 @@ class Fabricator {
589
589
  .catch(rethrowAs(endpoint, "unregister blocking trigger"));
590
590
  }
591
591
  logOpStart(op, endpoint) {
592
- const runtime = supported_1.RUNTIMES[endpoint.runtime].friendly;
592
+ const runtime = endpoint.runtime ? supported_1.RUNTIMES[endpoint.runtime].friendly : "unknown";
593
593
  const platform = (0, functionsDeployHelper_1.getHumanFriendlyPlatformName)(endpoint.platform);
594
594
  const label = helper.getFunctionLabel(endpoint);
595
595
  utils.logLabeledBullet("functions", `${op} ${runtime} (${platform}) function ${clc.bold(label)}...`);
@@ -54,36 +54,36 @@
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "2.17.0",
58
- "expectedSize": 29983584,
59
- "expectedChecksum": "d9a3a5bd575dc24185ad473a440c4738",
60
- "expectedChecksumSHA256": "da5485e68c7adbf86e3fb2f9ca550e4619f55ae75845009837780fcf16dd05cc",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v2.17.0",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.0"
57
+ "version": "2.17.1",
58
+ "expectedSize": 30004064,
59
+ "expectedChecksum": "89314d496595b250e149d8705ccd2ddc",
60
+ "expectedChecksumSHA256": "1e95733bf75fd433047345cb4c069ff4ddd2a2e296c53da3787dd708a26a8642",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v2.17.1",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.1"
63
63
  },
64
64
  "darwin_arm64": {
65
- "version": "2.17.0",
66
- "expectedSize": 29459746,
67
- "expectedChecksum": "8362a56419a66507b1aead4630b9033c",
68
- "expectedChecksumSHA256": "cbb8a3030f69c5aba81d1ef2d64d249f18100915f9738f59b815004b27983dab",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v2.17.0",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.0"
65
+ "version": "2.17.1",
66
+ "expectedSize": 29476450,
67
+ "expectedChecksum": "038125be57400e11f5013001cf68ce9e",
68
+ "expectedChecksumSHA256": "a11f6ea1f8b8d80f502df6e9b34865fb12186b4157f2c282a0e6f99c53077ca2",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v2.17.1",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.1"
71
71
  },
72
72
  "win32": {
73
- "version": "2.17.0",
74
- "expectedSize": 30477824,
75
- "expectedChecksum": "7d8434eee4f3d33cc8ec6c99c6056a77",
76
- "expectedChecksumSHA256": "051c60be0651be4971409da7ab3a15cdfb693400e9293c89010edcb28016d061",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v2.17.0",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.0.exe"
73
+ "version": "2.17.1",
74
+ "expectedSize": 30496768,
75
+ "expectedChecksum": "939a77bb9a549bc9460f888ac3442397",
76
+ "expectedChecksumSHA256": "04d6859668c4afc1fa301f5cc892623669bcbf26ce5195bc80dc208783c33853",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v2.17.1",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.1.exe"
79
79
  },
80
80
  "linux": {
81
- "version": "2.17.0",
82
- "expectedSize": 29905080,
83
- "expectedChecksum": "ca7003aaee41e3c1261f9655c5f9dd8a",
84
- "expectedChecksumSHA256": "79efd09f1bd685cbfa0157b2d08e0f6eb085ed82d83b48fabb7d102db6636c6c",
85
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v2.17.0",
86
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.0"
81
+ "version": "2.17.1",
82
+ "expectedSize": 29925560,
83
+ "expectedChecksum": "239d20f4aedf0bf6f79caca7453bf5cf",
84
+ "expectedChecksumSHA256": "756fa6343a659a8a2606ee0df2a321ffaf47645fff8f4b209286681ee36820e3",
85
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v2.17.1",
86
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.1"
87
87
  }
88
88
  }
89
89
  }
@@ -296,7 +296,7 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
296
296
  if (endpoint.platform !== "gcfv1") {
297
297
  throw new error_1.FirebaseError("Trying to create a v1 CloudFunction with v2 API. This should never happen");
298
298
  }
299
- if (!supported.isRuntime(endpoint.runtime)) {
299
+ if (!endpoint.runtime || !supported.isRuntime(endpoint.runtime)) {
300
300
  throw new error_1.FirebaseError("Failed internal assertion. Trying to deploy a new function with a deprecated runtime." +
301
301
  " This should never happen", { exit: 1 });
302
302
  }
@@ -22,7 +22,7 @@ const client = new apiv2_1.Client({
22
22
  apiVersion: exports.API_VERSION,
23
23
  });
24
24
  function functionsOpLogReject(func, type, err) {
25
- var _a, _b, _c, _d, _e, _f, _g;
25
+ var _a, _b, _c, _d, _e, _f, _g, _h;
26
26
  if ((_a = err === null || err === void 0 ? void 0 : err.message) === null || _a === void 0 ? void 0 : _a.includes("Runtime validation errors")) {
27
27
  const capturedMessage = (0, cloudfunctions_1.captureRuntimeValidationError)(err.message);
28
28
  utils.logLabeledWarning("functions", capturedMessage + " for function " + func.name);
@@ -32,24 +32,24 @@ function functionsOpLogReject(func, type, err) {
32
32
  utils.logLabeledWarning("functions", `Your current project quotas don't allow for the current max instances setting of ${maxInstances}. ` +
33
33
  "Either reduce this function's maximum instances, or request a quota increase on the underlying Cloud Run service " +
34
34
  "at https://cloud.google.com/run/quotas.");
35
- const suggestedFix = func.buildConfig.runtime.startsWith("python")
35
+ const suggestedFix = ((_c = func.buildConfig.runtime) === null || _c === void 0 ? void 0 : _c.startsWith("python"))
36
36
  ? "firebase_functions.options.set_global_options(max_instances=10)"
37
37
  : "setGlobalOptions({maxInstances: 10})";
38
38
  utils.logLabeledWarning("functions", `You can adjust the max instances value in your function's runtime options:\n\t${suggestedFix}`);
39
39
  }
40
40
  else {
41
41
  utils.logLabeledWarning("functions", `${err === null || err === void 0 ? void 0 : err.message}`);
42
- 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) {
42
+ if (((_e = (_d = err === null || err === void 0 ? void 0 : err.context) === null || _d === void 0 ? void 0 : _d.response) === null || _e === void 0 ? void 0 : _e.statusCode) === 429) {
43
43
  utils.logLabeledWarning("functions", `Got "Quota Exceeded" error while trying to ${type} ${func.name}. Waiting to retry...`);
44
44
  }
45
- 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")) {
45
+ else if ((_f = err === null || err === void 0 ? void 0 : err.message) === null || _f === void 0 ? void 0 : _f.includes("If you recently started to use Eventarc, it may take a few minutes before all necessary permissions are propagated to the Service Agent")) {
46
46
  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.`);
47
47
  }
48
48
  utils.logLabeledWarning("functions", ` failed to ${type} function ${func.name}`);
49
49
  }
50
50
  throw new error_1.FirebaseError(`Failed to ${type} function ${func.name}`, {
51
51
  original: err,
52
- 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,
52
+ status: (_h = (_g = err === null || err === void 0 ? void 0 : err.context) === null || _g === void 0 ? void 0 : _g.response) === null || _h === void 0 ? void 0 : _h.statusCode,
53
53
  context: { function: func.name },
54
54
  });
55
55
  }
@@ -143,14 +143,14 @@ function functionFromEndpoint(endpoint) {
143
143
  if (endpoint.platform !== "gcfv2") {
144
144
  throw new error_1.FirebaseError("Trying to create a v2 CloudFunction with v1 API. This should never happen");
145
145
  }
146
- if (!supported.isRuntime(endpoint.runtime)) {
146
+ if (endpoint.runtime && !supported.isRuntime(endpoint.runtime)) {
147
147
  throw new error_1.FirebaseError("Failed internal assertion. Trying to deploy a new function with a deprecated runtime." +
148
148
  " This should never happen");
149
149
  }
150
150
  const gcfFunction = {
151
151
  name: backend.functionName(endpoint),
152
152
  buildConfig: {
153
- runtime: endpoint.runtime,
153
+ runtime: endpoint.runtime || undefined,
154
154
  entryPoint: endpoint.entryPoint,
155
155
  source: {
156
156
  storageSource: (_a = endpoint.source) === null || _a === void 0 ? void 0 : _a.storageSource,
@@ -250,7 +250,7 @@ function functionFromEndpoint(endpoint) {
250
250
  }
251
251
  exports.functionFromEndpoint = functionFromEndpoint;
252
252
  function endpointFromFunction(gcfFunction) {
253
- var _a, _b, _c, _d, _e, _f;
253
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
254
254
  const [, project, , region, , id] = gcfFunction.name.split("/");
255
255
  let trigger;
256
256
  if (((_a = gcfFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) === "true") {
@@ -310,12 +310,12 @@ function endpointFromFunction(gcfFunction) {
310
310
  else {
311
311
  trigger = { httpsTrigger: {} };
312
312
  }
313
- if (!supported.isRuntime(gcfFunction.buildConfig.runtime)) {
313
+ if (((_e = gcfFunction.buildConfig) === null || _e === void 0 ? void 0 : _e.runtime) && !supported.isRuntime(gcfFunction.buildConfig.runtime)) {
314
314
  logger_1.logger.debug("GCFv2 function has a deprecated runtime:", JSON.stringify(gcfFunction, null, 2));
315
315
  }
316
316
  const endpoint = Object.assign(Object.assign({ platform: "gcfv2", id,
317
317
  project,
318
- region }, trigger), { entryPoint: gcfFunction.buildConfig.entryPoint, runtime: gcfFunction.buildConfig.runtime, source: gcfFunction.buildConfig.source });
318
+ region }, trigger), { entryPoint: ((_f = gcfFunction.buildConfig) === null || _f === void 0 ? void 0 : _f.entryPoint) || "", runtime: ((_g = gcfFunction.buildConfig) === null || _g === void 0 ? void 0 : _g.runtime) || undefined, source: (_h = gcfFunction.buildConfig) === null || _h === void 0 ? void 0 : _h.source });
319
319
  if (gcfFunction.serviceConfig) {
320
320
  proto.copyIfPresent(endpoint, gcfFunction.serviceConfig, "ingressSettings", "environmentVariables", "secretEnvironmentVariables", "timeoutSeconds", "uri");
321
321
  proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "serviceAccount", "serviceAccountEmail");
@@ -355,8 +355,8 @@ function endpointFromFunction(gcfFunction) {
355
355
  }
356
356
  }
357
357
  proto.renameIfPresent(endpoint, gcfFunction, "uri", "url");
358
- endpoint.codebase = ((_e = gcfFunction.labels) === null || _e === void 0 ? void 0 : _e[constants_1.CODEBASE_LABEL]) || projectConfig.DEFAULT_CODEBASE;
359
- if ((_f = gcfFunction.labels) === null || _f === void 0 ? void 0 : _f[constants_1.HASH_LABEL]) {
358
+ endpoint.codebase = ((_j = gcfFunction.labels) === null || _j === void 0 ? void 0 : _j[constants_1.CODEBASE_LABEL]) || projectConfig.DEFAULT_CODEBASE;
359
+ if ((_k = gcfFunction.labels) === null || _k === void 0 ? void 0 : _k[constants_1.HASH_LABEL]) {
360
360
  endpoint.hash = gcfFunction.labels[constants_1.HASH_LABEL];
361
361
  }
362
362
  proto.copyIfPresent(endpoint, gcfFunction, "state");
package/lib/gcp/runv2.js CHANGED
@@ -110,7 +110,7 @@ function endpointFromService(service) {
110
110
  }
111
111
  exports.endpointFromService = endpointFromService;
112
112
  function serviceFromEndpoint(endpoint, image) {
113
- const labels = Object.assign(Object.assign({}, endpoint.labels), { [exports.RUNTIME_LABEL]: endpoint.runtime, [exports.CLIENT_NAME_LABEL]: "firebase-functions" });
113
+ const labels = Object.assign(Object.assign(Object.assign({}, endpoint.labels), (endpoint.runtime ? { [exports.RUNTIME_LABEL]: endpoint.runtime } : {})), { [exports.CLIENT_NAME_LABEL]: "firebase-functions" });
114
114
  delete labels["deployment-tool"];
115
115
  if (endpoint.codebase) {
116
116
  labels[constants_1.CODEBASE_LABEL] = endpoint.codebase;
@@ -14,7 +14,8 @@ const logger_1 = require("../../../logger");
14
14
  const error_1 = require("../../../error");
15
15
  const utils_1 = require("../../../utils");
16
16
  const UNKNOWN_VERSION_TOO_HIGH = "2.0.0";
17
- const MIN_VERSION = "0.6.0";
17
+ const MIN_VERSION = "1.0.0-rc.1";
18
+ const UNIFIED_PLUGIN_VERSION = "1.18.0";
18
19
  const LATEST_TEMPLATE = "1.0.0";
19
20
  async function getPackageVersion(packageName, envVariable) {
20
21
  const envVal = process.env[envVariable];
@@ -49,6 +50,7 @@ async function getGenkitInfo() {
49
50
  let stopInstall = false;
50
51
  const genkitVersion = await getPackageVersion("genkit", "GENKIT_DEV_VERSION");
51
52
  const cliVersion = await getPackageVersion("genkit-cli", "GENKIT_CLI_DEV_VERSION");
53
+ const genaiVersion = await getPackageVersion("@genkit-ai/google-genai", "GENKIT_GENAI_VERSION");
52
54
  const vertexVersion = await getPackageVersion("@genkit-ai/vertexai", "GENKIT_VERTEX_VERSION");
53
55
  const googleAiVersion = await getPackageVersion("@genkit-ai/googleai", "GENKIT_GOOGLEAI_VERSION");
54
56
  if (semver.gte(genkitVersion, UNKNOWN_VERSION_TOO_HIGH)) {
@@ -63,11 +65,12 @@ async function getGenkitInfo() {
63
65
  stopInstall = true;
64
66
  }
65
67
  }
66
- else if (semver.gte(genkitVersion, "1.0.0-rc.1")) {
67
- templateVersion = "1.0.0";
68
+ else if (semver.gte(genkitVersion, UNIFIED_PLUGIN_VERSION) &&
69
+ semver.gte(genaiVersion, "0.0.2-rc.1")) {
70
+ templateVersion = UNIFIED_PLUGIN_VERSION;
68
71
  }
69
72
  else if (semver.gte(genkitVersion, MIN_VERSION)) {
70
- templateVersion = "0.9.0";
73
+ templateVersion = "1.0.0";
71
74
  }
72
75
  else {
73
76
  throw new error_1.FirebaseError(`The requested version of Genkit (${genkitVersion}) is no ` +
@@ -78,6 +81,7 @@ async function getGenkitInfo() {
78
81
  cliVersion,
79
82
  vertexVersion,
80
83
  googleAiVersion,
84
+ genaiVersion,
81
85
  templateVersion,
82
86
  stopInstall,
83
87
  };
@@ -152,30 +156,78 @@ async function ensureVertexApiEnabled(options) {
152
156
  }
153
157
  exports.ensureVertexApiEnabled = ensureVertexApiEnabled;
154
158
  function getModelOptions(genkitInfo) {
155
- const modelOptions = {
156
- vertexai: {
157
- label: "Google Cloud Vertex AI",
158
- plugin: "@genkit-ai/vertexai",
159
- package: `@genkit-ai/vertexai@${genkitInfo.vertexVersion}`,
160
- },
161
- googleai: {
162
- label: "Google AI",
163
- plugin: "@genkit-ai/googleai",
164
- package: `@genkit-ai/googleai@${genkitInfo.googleAiVersion}`,
165
- },
166
- none: { label: "None", plugin: undefined, package: undefined },
167
- };
159
+ let modelOptions;
160
+ if (semver.gte(genkitInfo.templateVersion, UNIFIED_PLUGIN_VERSION)) {
161
+ modelOptions = {
162
+ vertexai: {
163
+ label: "Google Cloud Vertex AI",
164
+ provider: "vertexai",
165
+ plugin: "@genkit-ai/google-genai",
166
+ package: `@genkit-ai/google-genai@${genkitInfo.genaiVersion}`,
167
+ },
168
+ googleai: {
169
+ label: "Google AI",
170
+ provider: "googleai",
171
+ plugin: "@genkit-ai/google-genai",
172
+ package: `@genkit-ai/google-genai@${genkitInfo.genaiVersion}`,
173
+ },
174
+ none: { label: "None" },
175
+ };
176
+ }
177
+ else {
178
+ modelOptions = {
179
+ vertexai: {
180
+ label: "Google Cloud Vertex AI",
181
+ plugin: "@genkit-ai/vertexai",
182
+ package: `@genkit-ai/vertexai@${genkitInfo.vertexVersion}`,
183
+ },
184
+ googleai: {
185
+ label: "Google AI",
186
+ plugin: "@genkit-ai/googleai",
187
+ package: `@genkit-ai/googleai@${genkitInfo.googleAiVersion}`,
188
+ },
189
+ none: { label: "None" },
190
+ };
191
+ }
168
192
  return modelOptions;
169
193
  }
170
194
  const pluginToInfo = {
171
195
  "@genkit-ai/firebase": {
196
+ plugin: "@genkit-ai/firebase",
172
197
  imports: "firebase",
173
198
  init: `
174
199
  // Load the Firebase plugin, which provides integrations with several
175
200
  // Firebase services.
176
201
  firebase()`.trimStart(),
177
202
  },
203
+ "@genkit-ai/google-genai(vertexai)": {
204
+ plugin: "@genkit-ai/google-genai",
205
+ imports: "vertexAI",
206
+ modelImportComment: `
207
+ // Import vertexAI provider from the unified plugin. The Vertex AI API provides
208
+ // access to many models.`,
209
+ init: ` // Load the VertexAI provider. You can optionally specify your location
210
+ // and projectID by passing in a config object; if you don't, the provider
211
+ // uses the value from environment variables like GCLOUD_PROJECT and GCLOUD_LOCATION.
212
+ // If you want to use Vertex Express Mode, you can specify apiKey instead.
213
+ vertexAI({location: "global"})`,
214
+ model: 'vertexAI.model("gemini-2.5-flash")',
215
+ },
216
+ "@genkit-ai/google-genai(googleai)": {
217
+ plugin: "@genkit-ai/google-genai",
218
+ imports: "googleAI",
219
+ modelImportComment: `
220
+ // Import googleAI provider from the unified plugin. The Gemini Developer API
221
+ // provides access to several generative models.`,
222
+ init: ` // Load the GoogleAI provider. You can optionally specify your API key by
223
+ // passing in a config object; if you don't, the provider uses the value
224
+ // from the GOOGLE_GENAI_API_KEY environment variable, which is the
225
+ // recommended practice.
226
+ googleAI()`,
227
+ model: 'googleAI.model("gemini-2.5-flash")',
228
+ },
178
229
  "@genkit-ai/vertexai": {
230
+ plugin: "@genkit-ai/vertexai",
179
231
  imports: "vertexAI",
180
232
  modelImportComment: `
181
233
  // Import models from the Vertex AI plugin. The Vertex AI API provides access to
@@ -185,9 +237,10 @@ const pluginToInfo = {
185
237
  // by passing in a config object; if you don't, the Vertex AI plugin uses
186
238
  // the value from the GCLOUD_PROJECT environment variable.
187
239
  vertexAI({location: "us-central1"})`.trimStart(),
188
- model: "gemini20Flash",
240
+ model: 'vertexAI.model("gemini-2.5-flash")',
189
241
  },
190
242
  "@genkit-ai/googleai": {
243
+ plugin: "@genkit-ai/googleai",
191
244
  imports: "googleAI",
192
245
  modelImportComment: `
193
246
  // Import models from the Google AI plugin. The Google AI API provides access to
@@ -198,16 +251,29 @@ const pluginToInfo = {
198
251
  // the value from the GOOGLE_GENAI_API_KEY environment variable, which is
199
252
  // the recommended practice.
200
253
  googleAI()`.trimStart(),
201
- model: "gemini20Flash",
254
+ model: 'googleAI.model("gemini-2.5-flash")',
202
255
  },
203
256
  };
257
+ function getPluginInfo(option) {
258
+ if ((option === null || option === void 0 ? void 0 : option.provider) && option.plugin) {
259
+ return pluginToInfo[`${option.plugin}(${option.provider})`];
260
+ }
261
+ if (option === null || option === void 0 ? void 0 : option.plugin) {
262
+ return pluginToInfo[option.plugin];
263
+ }
264
+ return {
265
+ plugin: "",
266
+ imports: "",
267
+ init: "",
268
+ };
269
+ }
204
270
  function getBasePackages(genkitVersion) {
205
271
  const basePackages = ["express", `genkit@${genkitVersion}`];
206
272
  return basePackages;
207
273
  }
208
274
  const externalDevPackages = ["typescript", "tsx"];
209
275
  async function genkitSetup(options, genkitInfo, projectDir) {
210
- var _a, _b;
276
+ var _a;
211
277
  const modelOptions = getModelOptions(genkitInfo);
212
278
  const supportedModels = Object.keys(modelOptions);
213
279
  const model = await (0, prompt_1.select)({
@@ -220,13 +286,9 @@ async function genkitSetup(options, genkitInfo, projectDir) {
220
286
  if (model === "vertexai") {
221
287
  await ensureVertexApiEnabled(options);
222
288
  }
223
- const plugins = [];
224
289
  const pluginPackages = [];
225
290
  pluginPackages.push(`@genkit-ai/firebase@${genkitInfo.genkitVersion}`);
226
- if ((_a = modelOptions[model]) === null || _a === void 0 ? void 0 : _a.plugin) {
227
- plugins.push(modelOptions[model].plugin || "");
228
- }
229
- if ((_b = modelOptions[model]) === null || _b === void 0 ? void 0 : _b.package) {
291
+ if ((_a = modelOptions[model]) === null || _a === void 0 ? void 0 : _a.package) {
230
292
  pluginPackages.push(modelOptions[model].package || "");
231
293
  }
232
294
  const packages = [...getBasePackages(genkitInfo.genkitVersion)];
@@ -248,7 +310,7 @@ async function genkitSetup(options, genkitInfo, projectDir) {
248
310
  message: "Would like you to enable telemetry collection?",
249
311
  default: true,
250
312
  }));
251
- generateSampleFile(modelOptions[model].plugin, plugins, projectDir, genkitInfo.templateVersion, enableTelemetry);
313
+ generateSampleFile(modelOptions[model], projectDir, genkitInfo.templateVersion, enableTelemetry);
252
314
  }
253
315
  }
254
316
  exports.genkitSetup = genkitSetup;
@@ -325,25 +387,23 @@ async function installNpmPackages(projectDir, packages, devPackages) {
325
387
  process.exit(1);
326
388
  }
327
389
  }
328
- function generateSampleFile(modelPlugin, configPlugins, projectDir, templateVersion, enableTelemetry) {
390
+ function generateSampleFile(modelOption, projectDir, templateVersion, enableTelemetry) {
391
+ var _a;
329
392
  let modelImport = "";
330
- if (modelPlugin && pluginToInfo[modelPlugin].model) {
331
- const modelInfo = pluginToInfo[modelPlugin].model || "";
332
- modelImport = "\n" + generateImportStatement(modelInfo, modelPlugin) + "\n";
393
+ const pluginInfo = getPluginInfo(modelOption);
394
+ if (pluginInfo.imports) {
395
+ modelImport = "\n" + generateImportStatement(pluginInfo) + "\n";
333
396
  }
334
397
  let modelImportComment = "";
335
- if (modelPlugin && pluginToInfo[modelPlugin].modelImportComment) {
336
- const comment = pluginToInfo[modelPlugin].modelImportComment || "";
337
- modelImportComment = `\n${comment}`;
398
+ if (pluginInfo.modelImportComment) {
399
+ modelImportComment = `\n${pluginInfo.modelImportComment}`;
338
400
  }
339
401
  const commentedModelImport = `${modelImportComment}${modelImport}`;
340
402
  const templatePath = path.join(__dirname, `../../../../templates/genkit/firebase.${templateVersion}.template`);
341
403
  const template = fs.readFileSync(templatePath, "utf8");
342
- const sample = renderConfig(configPlugins, template
404
+ const sample = renderConfig(pluginInfo, template
343
405
  .replace("$GENKIT_MODEL_IMPORT\n", commentedModelImport)
344
- .replace("$GENKIT_MODEL", modelPlugin
345
- ? pluginToInfo[modelPlugin].model || pluginToInfo[modelPlugin].modelStr || ""
346
- : "'' /* TODO: Set a model. */"), enableTelemetry);
406
+ .replace("$GENKIT_MODEL", (_a = pluginInfo.model) !== null && _a !== void 0 ? _a : "'' /* TODO: Set a model. */"), enableTelemetry);
347
407
  (0, utils_1.logLabeledBullet)("genkit", "Generating sample file");
348
408
  try {
349
409
  const samplePath = "src/genkit-sample.ts";
@@ -402,19 +462,18 @@ async function updatePackageJson(nonInteractive, projectDir) {
402
462
  process.exit(1);
403
463
  }
404
464
  }
405
- function renderConfig(pluginNames, template, enableTelemetry) {
406
- const imports = pluginNames
407
- .map((pluginName) => generateImportStatement(pluginToInfo[pluginName].imports, pluginName))
408
- .join("\n");
409
- const plugins = pluginNames.map((pluginName) => ` ${pluginToInfo[pluginName].init},`).join("\n") ||
410
- " /* Add your plugins here. */";
465
+ function renderConfig(pluginInfo, template, enableTelemetry) {
466
+ const plugins = pluginInfo.init || " /* Add your plugins here. */";
411
467
  return template
412
- .replace("$GENKIT_CONFIG_IMPORTS", imports)
468
+ .replace("$GENKIT_CONFIG_IMPORTS", generateImportStatement(pluginInfo))
413
469
  .replace("$GENKIT_CONFIG_PLUGINS", plugins)
414
470
  .replaceAll("$TELEMETRY_COMMENT", enableTelemetry ? "" : "// ");
415
471
  }
416
- function generateImportStatement(imports, name) {
417
- return `import {${imports}} from "${name}";`;
472
+ function generateImportStatement(pluginInfo) {
473
+ if (pluginInfo.imports && pluginInfo.plugin) {
474
+ return `import {${pluginInfo.imports}} from "${pluginInfo.plugin}";`;
475
+ }
476
+ return "";
418
477
  }
419
478
  async function promptWriteMode(message, defaultOption = "merge") {
420
479
  return (0, prompt_1.select)({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "14.24.2",
3
+ "version": "14.25.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -30,7 +30,7 @@
30
30
  ],
31
31
  "preferGlobal": true,
32
32
  "engines": {
33
- "node": ">=20.0.0 || >=22.0.0"
33
+ "node": ">=20.0.0 || >=22.0.0 || >=24.0.0"
34
34
  },
35
35
  "author": "Firebase (https://firebase.google.com/)",
36
36
  "license": "MIT",
@@ -123,7 +123,7 @@
123
123
  "sql-formatter": "^15.3.0",
124
124
  "stream-chain": "^2.2.4",
125
125
  "stream-json": "^1.7.3",
126
- "superstatic": "^9.2.0",
126
+ "superstatic": "^10.0.0",
127
127
  "tar": "^6.1.11",
128
128
  "tcp-port-used": "^1.0.2",
129
129
  "tmp": "^0.2.3",
@@ -0,0 +1,72 @@
1
+ import {genkit, z} from "genkit";
2
+ $GENKIT_CONFIG_IMPORTS
3
+
4
+ // Cloud Functions for Firebase supports Genkit natively. The onCallGenkit function creates a callable
5
+ // function from a Genkit action. It automatically implements streaming if your flow does.
6
+ // The https library also has other utility methods such as hasClaim, which verifies that
7
+ // a caller's token has a specific claim (optionally matching a specific value)
8
+ import { onCallGenkit, hasClaim } from "firebase-functions/https";
9
+
10
+ // Gemini Developer API models and Vertex Express Mode models depend on an API key.
11
+ // API keys should be stored in Cloud Secret Manager so that access to these
12
+ // sensitive values can be controlled. defineSecret does this for you automatically.
13
+ // If you are using Google Developer API (googleAI) you can get an API key at https://aistudio.google.com/app/apikey
14
+ // If you are using Vertex Express Mode (vertexAI with apiKey) you can get an API key
15
+ // from the Vertex AI Studio Express Mode setup.
16
+ import { defineSecret } from "firebase-functions/params";
17
+ const apiKey = defineSecret("GOOGLE_GENAI_API_KEY");
18
+
19
+ // The Firebase telemetry plugin exports a combination of metrics, traces, and logs to Google Cloud
20
+ // Observability. See https://firebase.google.com/docs/genkit/observability/telemetry-collection.
21
+ $TELEMETRY_COMMENTimport {enableFirebaseTelemetry} from "@genkit-ai/firebase";
22
+ $TELEMETRY_COMMENTenableFirebaseTelemetry();
23
+
24
+ const ai = genkit({
25
+ plugins: [
26
+ $GENKIT_CONFIG_PLUGINS
27
+ ],
28
+ });
29
+
30
+ // Define a simple flow that prompts an LLM to generate menu suggestions.
31
+ const menuSuggestionFlow = ai.defineFlow({
32
+ name: "menuSuggestionFlow",
33
+ inputSchema: z.string().describe("A restaurant theme").default("seafood"),
34
+ outputSchema: z.string(),
35
+ streamSchema: z.string(),
36
+ }, async (subject, { sendChunk }) => {
37
+ // Construct a request and send it to the model API.
38
+ const prompt =
39
+ `Suggest an item for the menu of a ${subject} themed restaurant`;
40
+ const { response, stream } = ai.generateStream({
41
+ model: $GENKIT_MODEL,
42
+ prompt: prompt,
43
+ config: {
44
+ temperature: 1,
45
+ },
46
+ });
47
+
48
+ for await (const chunk of stream) {
49
+ sendChunk(chunk.text);
50
+ }
51
+
52
+ // Handle the response from the model API. In this sample, we just
53
+ // convert it to a string, but more complicated flows might coerce the
54
+ // response into structured output or chain the response into another
55
+ // LLM call, etc.
56
+ return (await response).text;
57
+ }
58
+ );
59
+
60
+ export const menuSuggestion = onCallGenkit({
61
+ // Uncomment to enable AppCheck. This can reduce costs by ensuring only your Verified
62
+ // app users can use your API. Read more at https://firebase.google.com/docs/app-check/cloud-functions
63
+ // enforceAppCheck: true,
64
+
65
+ // authPolicy can be any callback that accepts an AuthData (a uid and tokens dictionary) and the
66
+ // request data. The isSignedIn() and hasClaim() helpers can be used to simplify. The following
67
+ // will require the user to have the email_verified claim, for example.
68
+ // authPolicy: hasClaim("email_verified"),
69
+
70
+ // Grant access to the API key to this function:
71
+ secrets: [apiKey],
72
+ }, menuSuggestionFlow);
@@ -1,57 +0,0 @@
1
- // Import the Genkit core libraries and plugins.
2
- import {genkit, z} from "genkit";
3
- $GENKIT_CONFIG_IMPORTS
4
- $GENKIT_MODEL_IMPORT
5
-
6
- // From the Firebase plugin, import the functions needed to deploy flows using
7
- // Cloud Functions.
8
- import {firebaseAuth} from "@genkit-ai/firebase/auth";
9
- import {onFlow} from "@genkit-ai/firebase/functions";
10
-
11
- const ai = genkit({
12
- plugins: [
13
- $GENKIT_CONFIG_PLUGINS
14
- ],
15
- });
16
-
17
- // Define a simple flow that prompts an LLM to generate menu suggestions.
18
- export const menuSuggestionFlow = onFlow(
19
- ai,
20
- {
21
- name: "menuSuggestionFlow",
22
- inputSchema: z.string().describe("A restaurant theme").default("seafood"),
23
- outputSchema: z.string(),
24
- authPolicy: firebaseAuth((user) => {
25
- // By default, the firebaseAuth policy requires that all requests have an
26
- // `Authorization: Bearer` header containing the user's Firebase
27
- // Authentication ID token. All other requests are rejected with error
28
- // 403. If your app client uses the Cloud Functions for Firebase callable
29
- // functions feature, the library automatically attaches this header to
30
- // requests.
31
-
32
- // You should also set additional policy requirements as appropriate for
33
- // your app. For example:
34
- // if (!user.email_verified) {
35
- // throw new Error("Verified email required to run flow");
36
- // }
37
- }),
38
- },
39
- async (subject) => {
40
- // Construct a request and send it to the model API.
41
- const prompt =
42
- `Suggest an item for the menu of a ${subject} themed restaurant`;
43
- const llmResponse = await ai.generate({
44
- model: $GENKIT_MODEL,
45
- prompt: prompt,
46
- config: {
47
- temperature: 1,
48
- },
49
- });
50
-
51
- // Handle the response from the model API. In this sample, we just
52
- // convert it to a string, but more complicated flows might coerce the
53
- // response into structured output or chain the response into another
54
- // LLM call, etc.
55
- return llmResponse.text;
56
- }
57
- );