firebase-tools 14.21.0 → 14.23.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 (40) hide show
  1. package/README.md +13 -5
  2. package/lib/apiv2.js +12 -2
  3. package/lib/appdistribution/client.js +62 -16
  4. package/lib/appdistribution/types.js +1 -12
  5. package/lib/appdistribution/yaml_helper.js +69 -0
  6. package/lib/commands/appdistribution-groups-list.js +3 -5
  7. package/lib/commands/appdistribution-testcases-export.js +32 -0
  8. package/lib/commands/appdistribution-testcases-import.js +34 -0
  9. package/lib/commands/appdistribution-testers-list.js +3 -5
  10. package/lib/commands/deploy.js +6 -4
  11. package/lib/commands/hosting-sites-create.js +4 -3
  12. package/lib/commands/index.js +8 -5
  13. package/lib/commands/init.js +5 -7
  14. package/lib/deploy/functions/params.js +7 -11
  15. package/lib/emulator/auth/operations.js +7 -1
  16. package/lib/emulator/downloadableEmulatorInfo.json +25 -25
  17. package/lib/emulator/functionsEmulatorRuntime.js +8 -0
  18. package/lib/emulator/taskQueue.js +5 -0
  19. package/lib/experiments.js +0 -6
  20. package/lib/firestore/api.js +22 -4
  21. package/lib/frameworks/angular/index.js +1 -1
  22. package/lib/frameworks/flutter/index.js +1 -1
  23. package/lib/frameworks/next/index.js +1 -1
  24. package/lib/frameworks/nuxt/index.js +1 -1
  25. package/lib/frameworks/vite/index.js +5 -2
  26. package/lib/hosting/interactive.js +14 -19
  27. package/lib/init/features/hosting/github.js +6 -6
  28. package/lib/init/features/hosting/index.js +89 -86
  29. package/lib/init/features/index.js +3 -2
  30. package/lib/init/index.js +5 -1
  31. package/lib/management/provisioning/errorHandler.js +54 -0
  32. package/lib/management/provisioning/provision.js +2 -5
  33. package/lib/mcp/resources/guides/init_backend.js +3 -26
  34. package/lib/mcp/resources/guides/init_hosting.js +15 -10
  35. package/lib/mcp/tools/core/init.js +27 -0
  36. package/lib/mcp/tools/functions/index.js +2 -1
  37. package/lib/mcp/tools/functions/list_functions.js +48 -0
  38. package/lib/utils.js +4 -1
  39. package/package.json +2 -2
  40. package/schema/firebase-config.json +0 -1
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enhanceProvisioningError = void 0;
4
+ const error_1 = require("../../error");
5
+ function isErrorInfo(detail) {
6
+ return detail["@type"] === "type.googleapis.com/google.rpc.ErrorInfo";
7
+ }
8
+ function isHelpLinks(detail) {
9
+ return detail["@type"] === "type.googleapis.com/google.rpc.Help";
10
+ }
11
+ function extractErrorDetails(err) {
12
+ var _a;
13
+ if (!(err instanceof Error)) {
14
+ return "";
15
+ }
16
+ if (err instanceof error_1.FirebaseError && err.context) {
17
+ const context = err.context;
18
+ const errorBody = (_a = context.body) === null || _a === void 0 ? void 0 : _a.error;
19
+ if ((errorBody === null || errorBody === void 0 ? void 0 : errorBody.details) && Array.isArray(errorBody.details)) {
20
+ const parts = [];
21
+ for (const detail of errorBody.details) {
22
+ if (isErrorInfo(detail)) {
23
+ parts.push(`Error details:`);
24
+ parts.push(` Reason: ${detail.reason}`);
25
+ parts.push(` Domain: ${detail.domain}`);
26
+ if (detail.metadata) {
27
+ parts.push(` Additional Info: ${JSON.stringify(detail.metadata)}`);
28
+ }
29
+ }
30
+ else if (isHelpLinks(detail)) {
31
+ parts.push(`\nFor help resolving this issue:`);
32
+ for (const link of detail.links) {
33
+ parts.push(` - ${link.description}`);
34
+ parts.push(` ${link.url}`);
35
+ }
36
+ }
37
+ }
38
+ return parts.length > 0 ? `\n\n${parts.join("\n")}` : "";
39
+ }
40
+ }
41
+ return "";
42
+ }
43
+ function enhanceProvisioningError(err, contextMessage) {
44
+ const originalError = (0, error_1.getError)(err);
45
+ const errorDetails = extractErrorDetails(err);
46
+ const fullMessage = errorDetails
47
+ ? `${contextMessage}: ${originalError.message}${errorDetails}`
48
+ : `${contextMessage}: ${originalError.message}`;
49
+ return new error_1.FirebaseError(fullMessage, {
50
+ exit: 2,
51
+ original: originalError,
52
+ });
53
+ }
54
+ exports.enhanceProvisioningError = enhanceProvisioningError;
@@ -7,6 +7,7 @@ const error_1 = require("../../error");
7
7
  const logger_1 = require("../../logger");
8
8
  const operation_poller_1 = require("../../operation-poller");
9
9
  const apps_1 = require("../apps");
10
+ const errorHandler_1 = require("./errorHandler");
10
11
  const apiClient = new apiv2_1.Client({
11
12
  urlPrefix: (0, api_1.firebaseApiOrigin)(),
12
13
  apiVersion: "v1alpha",
@@ -100,11 +101,7 @@ async function provisionFirebaseApp(options) {
100
101
  return result;
101
102
  }
102
103
  catch (err) {
103
- const errorMessage = err instanceof Error ? err.message : String(err);
104
- throw new error_1.FirebaseError(`Failed to provision Firebase app: ${errorMessage}`, {
105
- exit: 2,
106
- original: err instanceof Error ? err : new Error(String(err)),
107
- });
104
+ throw (0, errorHandler_1.enhanceProvisioningError)(err, "Failed to provision Firebase app");
108
105
  }
109
106
  }
110
107
  exports.provisionFirebaseApp = provisionFirebaseApp;
@@ -7,7 +7,7 @@ exports.init_backend = (0, resource_1.resource)({
7
7
  name: "backend_init_guide",
8
8
  title: "Firebase Backend Init Guide",
9
9
  description: "guides the coding agent through configuring Firebase backend services in the current project",
10
- }, async (uri) => {
10
+ }, async (uri, ctx) => {
11
11
  return {
12
12
  contents: [
13
13
  {
@@ -24,31 +24,8 @@ The user will likely need to setup Firestore, Authentication, and Hosting. Read
24
24
  3. [Firestore Rules](firebase://guides/init/firestore_rules): read this to setup the \`firestore.rules\` file for securing your database
25
25
  4. [Hosting](firebase://guides/init/hosting): read this if the user would like to deploy to Firebase Hosting
26
26
 
27
- **firebase.json**
28
- The firebase.json file is used to deploy Firebase products with the firebase deploy command.
29
-
30
- Here is an example firebase.json file with Firebase Hosting, Firestore, and Cloud Functions. Note that you do not need entries for services that the user isn't using. Do not remove sections from the user's firebase.json unless the user gives explicit permission. For more information, refer to [firebase.json file documentation](https://firebase.google.com/docs/cli/#the_firebasejson_file)
31
- \`\`\`json
32
- {
33
- "hosting": {
34
- "public": "public",
35
- "ignore": [
36
- "firebase.json",
37
- "**/.*",
38
- "**/node_modules/**"
39
- ]
40
- },
41
- "firestore": {
42
- "rules": "firestore.rules",
43
- "indexes": "firestore.indexes.json"
44
- },
45
- "functions": {
46
- "predeploy": [
47
- "npm --prefix "$RESOURCE_DIR" run lint",
48
- "npm --prefix "$RESOURCE_DIR" run build"
49
- ]
50
- }
51
- }
27
+ Once you are done setting up, ask the user if they would like to deploy.
28
+ If they say yes, run the command '${ctx.firebaseCliCommand} deploy --non-interactive' to do so.
52
29
  \`\`\`
53
30
  `.trim(),
54
31
  },
@@ -1,13 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.init_hosting = void 0;
4
+ const getDefaultHostingSite_1 = require("../../../getDefaultHostingSite");
4
5
  const resource_1 = require("../../resource");
5
6
  exports.init_hosting = (0, resource_1.resource)({
6
7
  uri: "firebase://guides/init/hosting",
7
8
  name: "hosting_init_guide",
8
9
  title: "Firebase Hosting Deployment Guide",
9
10
  description: "guides the coding agent through deploying to Firebase Hosting in the current project",
10
- }, async (uri) => {
11
+ }, async (uri, ctx) => {
12
+ const defaultHostingSite = await (0, getDefaultHostingSite_1.getDefaultHostingSite)(ctx);
11
13
  return {
12
14
  contents: [
13
15
  {
@@ -15,18 +17,21 @@ exports.init_hosting = (0, resource_1.resource)({
15
17
  type: "text",
16
18
  text: `
17
19
  ### Configure Firebase Hosting
20
+ Default hosting site for ${ctx.projectId}: ${defaultHostingSite || "Does not exist"}
21
+ If there is not a default hosting site configured, ask the user what the site ID should be, and suggest ${ctx.projectId} as a good choice.
22
+ Next, use the 'firebase_init' tool to set up hosting. Below is an example of what the arguments to do so look like;
23
+ however, you should change the values to match the user's choices and project structure:
24
+ {
25
+ features: {
26
+ hosting: {
27
+ site_id: ${ctx.projectId},
28
+ public_directory: public,
29
+ }
30
+ }
31
+ }
18
32
 
19
33
  **Security Warning:**
20
34
  - Files included in the public folder of a hosting site are publicly accessible. Do not include sensitive API keys for services other than Firebase in these files.
21
-
22
- **When to Deploy:**
23
- - Introduce Firebase Hosting when developers are ready to deploy their application to production.
24
- - Alternative: Developers can deploy later using the \`/firebase:deploy\` command.
25
-
26
- **Deployment Process:**
27
- - Request developer's permission before implementing Firebase Hosting
28
- - Request developer's permission before deploying Firebase Hosting app to production.
29
- - Configure Firebase Hosting and deploy the application to production
30
35
  `.trim(),
31
36
  },
32
37
  ],
@@ -115,6 +115,25 @@ exports.init = (0, tool_1.tool)("core", {
115
115
  })
116
116
  .optional()
117
117
  .describe("Enable Firebase AI Logic feature for existing app"),
118
+ hosting: zod_1.z
119
+ .object({
120
+ site_id: zod_1.z
121
+ .string()
122
+ .optional()
123
+ .describe("The ID of the hosting site to configure. If omitted and there is a default hosting site, that will be used."),
124
+ public_directory: zod_1.z
125
+ .string()
126
+ .optional()
127
+ .default("public")
128
+ .describe("The directory containing public files that will be served. If using a build tool, this likely should be the output directory of that tool."),
129
+ single_page_app: zod_1.z
130
+ .boolean()
131
+ .optional()
132
+ .default(false)
133
+ .describe("Configure as a single-page app."),
134
+ })
135
+ .optional()
136
+ .describe("Provide this object to initialize Firebase Hosting in this project directory."),
118
137
  }),
119
138
  }),
120
139
  annotations: {
@@ -185,6 +204,14 @@ exports.init = (0, tool_1.tool)("core", {
185
204
  displayName: appData.displayName,
186
205
  };
187
206
  }
207
+ if (features.hosting) {
208
+ featuresList.push("hosting");
209
+ featureInfo.hosting = {
210
+ newSiteId: features.hosting.site_id,
211
+ public: features.hosting.public_directory,
212
+ spa: features.hosting.single_page_app,
213
+ };
214
+ }
188
215
  const setup = {
189
216
  config: config === null || config === void 0 ? void 0 : config.src,
190
217
  rcfile: rc === null || rc === void 0 ? void 0 : rc.data,
@@ -2,4 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.functionsTools = void 0;
4
4
  const get_logs_1 = require("./get_logs");
5
- exports.functionsTools = [get_logs_1.get_logs];
5
+ const list_functions_1 = require("./list_functions");
6
+ exports.functionsTools = [get_logs_1.get_logs, list_functions_1.list_functions];
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.list_functions = void 0;
4
+ const zod_1 = require("zod");
5
+ const tool_1 = require("../../tool");
6
+ const util_1 = require("../../util");
7
+ const backend = require("../../../deploy/functions/backend");
8
+ const error_1 = require("../../../error");
9
+ exports.list_functions = (0, tool_1.tool)("functions", {
10
+ name: "list_functions",
11
+ description: "List all deployed functions in your Firebase project.",
12
+ inputSchema: zod_1.z.object({}),
13
+ annotations: {
14
+ title: "List Deployed Functions",
15
+ readOnlyHint: true,
16
+ openWorldHint: true,
17
+ },
18
+ _meta: {
19
+ requiresAuth: true,
20
+ requiresProject: true,
21
+ },
22
+ }, async (_, { projectId }) => {
23
+ const context = {
24
+ projectId,
25
+ };
26
+ try {
27
+ const existing = await backend.existingBackend(context);
28
+ const endpointsList = backend.allEndpoints(existing).sort(backend.compareFunctions);
29
+ const formattedList = endpointsList.map((endpoint) => ({
30
+ function: endpoint.id,
31
+ version: endpoint.platform === "gcfv2" ? "v2" : "v1",
32
+ trigger: backend.endpointTriggerType(endpoint),
33
+ location: endpoint.region,
34
+ memory: endpoint.availableMemoryMb || "---",
35
+ runtime: endpoint.runtime,
36
+ }));
37
+ if (!formattedList.length) {
38
+ return (0, util_1.toContent)([], {
39
+ contentPrefix: "No functions found in this project.\n\n",
40
+ });
41
+ }
42
+ return (0, util_1.toContent)(formattedList);
43
+ }
44
+ catch (err) {
45
+ const errMsg = (0, error_1.getErrMsg)((err === null || err === void 0 ? void 0 : err.original) || err, "Failed to list functions.");
46
+ return (0, util_1.mcpError)(errMsg);
47
+ }
48
+ });
package/lib/utils.js CHANGED
@@ -357,7 +357,10 @@ function datetimeString(d) {
357
357
  }
358
358
  exports.datetimeString = datetimeString;
359
359
  function isCloudEnvironment() {
360
- return !!process.env.CODESPACES || !!process.env.GOOGLE_CLOUD_WORKSTATIONS;
360
+ return (!!process.env.CODESPACES ||
361
+ !!process.env.GOOGLE_CLOUD_WORKSTATIONS ||
362
+ !!process.env.CLOUD_SHELL ||
363
+ !!process.env.GOOGLE_CLOUD_SHELL);
361
364
  }
362
365
  exports.isCloudEnvironment = isCloudEnvironment;
363
366
  function isRunningInWSL() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "14.21.0",
3
+ "version": "14.23.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -66,7 +66,7 @@
66
66
  "@electric-sql/pglite": "^0.3.3",
67
67
  "@electric-sql/pglite-tools": "^0.2.8",
68
68
  "@google-cloud/cloud-sql-connector": "^1.3.3",
69
- "@google-cloud/pubsub": "^4.5.0",
69
+ "@google-cloud/pubsub": "^4.11.0",
70
70
  "@inquirer/prompts": "^7.4.0",
71
71
  "@modelcontextprotocol/sdk": "^1.10.2",
72
72
  "abort-controller": "^3.0.0",
@@ -1090,7 +1090,6 @@
1090
1090
  },
1091
1091
  "properties": {
1092
1092
  "$schema": {
1093
- "format": "uri",
1094
1093
  "type": "string"
1095
1094
  },
1096
1095
  "apphosting": {