firebase-tools 14.3.1 → 14.5.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 (162) hide show
  1. package/lib/api.js +10 -2
  2. package/lib/apphosting/backend.js +74 -18
  3. package/lib/apphosting/rollout.js +2 -2
  4. package/lib/apphosting/secrets/dialogs.js +4 -4
  5. package/lib/apphosting/secrets/index.js +2 -2
  6. package/lib/auth.js +22 -2
  7. package/lib/bin/cli.js +1 -33
  8. package/lib/bin/mcp.js +13 -2
  9. package/lib/checkValidTargetFilters.js +1 -0
  10. package/lib/command.js +3 -2
  11. package/lib/commands/apphosting-secrets-grantaccess.js +1 -1
  12. package/lib/commands/deploy.js +1 -0
  13. package/lib/commands/init.js +1 -4
  14. package/lib/commands/login-use.js +2 -11
  15. package/lib/commands/use.js +9 -8
  16. package/lib/config.js +43 -24
  17. package/lib/crashlytics/listTopIssues.js +44 -0
  18. package/lib/dataconnect/cloudAICompanionClient.js +72 -0
  19. package/lib/dataconnect/cloudAICompanionTypes.js +2 -0
  20. package/lib/dataconnect/fileUtils.js +11 -4
  21. package/lib/dataconnect/schemaMigration.js +6 -7
  22. package/lib/deploy/apphosting/args.js +2 -0
  23. package/lib/deploy/apphosting/deploy.js +77 -0
  24. package/lib/deploy/apphosting/index.js +9 -0
  25. package/lib/deploy/apphosting/prepare.js +147 -0
  26. package/lib/deploy/apphosting/release.js +56 -0
  27. package/lib/deploy/apphosting/util.js +65 -0
  28. package/lib/deploy/extensions/v2FunctionHelper.js +2 -1
  29. package/lib/deploy/firestore/deploy.js +47 -4
  30. package/lib/deploy/functions/checkIam.js +3 -3
  31. package/lib/deploy/functions/ensure.js +2 -1
  32. package/lib/deploy/functions/prepare.js +23 -16
  33. package/lib/deploy/functions/release/fabricator.js +4 -4
  34. package/lib/deploy/functions/release/index.js +1 -1
  35. package/lib/deploy/functions/runtimes/python/index.js +3 -0
  36. package/lib/deploy/functions/runtimes/supported/types.js +17 -11
  37. package/lib/deploy/index.js +2 -0
  38. package/lib/emulator/apphosting/index.js +1 -0
  39. package/lib/emulator/apphosting/serve.js +77 -3
  40. package/lib/emulator/auth/widget_ui.js +2 -1
  41. package/lib/emulator/controller.js +18 -4
  42. package/lib/emulator/dataconnectEmulator.js +9 -2
  43. package/lib/emulator/downloadableEmulatorInfo.json +81 -0
  44. package/lib/emulator/downloadableEmulators.js +28 -108
  45. package/lib/experiments.js +1 -1
  46. package/lib/extensions/manifest.js +2 -5
  47. package/lib/frameworks/angular/index.js +1 -1
  48. package/lib/frameworks/angular/utils.js +17 -6
  49. package/lib/fsAsync.js +9 -2
  50. package/lib/gcp/apphosting.js +13 -1
  51. package/lib/gcp/auth.js +32 -2
  52. package/lib/gcp/cloudbilling.js +12 -1
  53. package/lib/gcp/cloudfunctions.js +1 -2
  54. package/lib/gcp/cloudfunctionsv2.js +1 -2
  55. package/lib/gcp/cloudscheduler.js +2 -2
  56. package/lib/gcp/computeEngine.js +19 -2
  57. package/lib/gcp/devConnect.js +6 -1
  58. package/lib/gcp/firestore.js +24 -1
  59. package/lib/gcp/iam.js +1 -5
  60. package/lib/gcp/run.js +19 -1
  61. package/lib/gcp/storage.js +25 -1
  62. package/lib/index.js +1 -2
  63. package/lib/init/features/apphosting.js +85 -6
  64. package/lib/init/features/database.js +63 -52
  65. package/lib/init/features/dataconnect/index.js +78 -79
  66. package/lib/init/features/dataconnect/sdk.js +19 -6
  67. package/lib/init/features/emulators.js +12 -7
  68. package/lib/init/features/firestore/index.js +80 -39
  69. package/lib/init/features/firestore/indexes.js +29 -31
  70. package/lib/init/features/firestore/rules.js +35 -48
  71. package/lib/init/features/functions/index.js +2 -0
  72. package/lib/init/features/functions/javascript.js +3 -2
  73. package/lib/init/features/functions/typescript.js +3 -2
  74. package/lib/init/features/genkit/index.js +18 -10
  75. package/lib/init/features/hosting/github.js +3 -2
  76. package/lib/init/features/hosting/index.js +9 -8
  77. package/lib/init/features/index.js +10 -5
  78. package/lib/init/features/remoteconfig.js +3 -2
  79. package/lib/init/features/storage.js +31 -8
  80. package/lib/init/index.js +80 -24
  81. package/lib/logger.js +71 -7
  82. package/lib/management/projects.js +24 -1
  83. package/lib/mcp/errors.js +1 -1
  84. package/lib/mcp/index.js +142 -46
  85. package/lib/mcp/tool.js +2 -1
  86. package/lib/mcp/tools/apphosting/fetch_logs.js +69 -0
  87. package/lib/mcp/tools/apphosting/index.js +6 -0
  88. package/lib/mcp/tools/apphosting/list_backends.js +51 -0
  89. package/lib/mcp/tools/auth/get_user.js +16 -7
  90. package/lib/mcp/tools/auth/index.js +8 -1
  91. package/lib/mcp/tools/auth/list_users.js +47 -0
  92. package/lib/mcp/tools/auth/set_claims.js +20 -11
  93. package/lib/mcp/tools/auth/set_sms_region_policy.js +1 -1
  94. package/lib/mcp/tools/core/consult_assistant.js +1 -1
  95. package/lib/mcp/tools/core/create_android_sha.js +40 -0
  96. package/lib/mcp/tools/core/create_app.js +90 -0
  97. package/lib/mcp/tools/core/create_project.js +68 -0
  98. package/lib/mcp/tools/core/get_admin_sdk_config.js +26 -0
  99. package/lib/mcp/tools/core/get_environment.js +51 -0
  100. package/lib/mcp/tools/core/get_sdk_config.js +6 -3
  101. package/lib/mcp/tools/core/index.js +21 -2
  102. package/lib/mcp/tools/core/init.js +153 -0
  103. package/lib/mcp/tools/core/list_apps.js +10 -5
  104. package/lib/mcp/tools/core/list_projects.js +45 -0
  105. package/lib/mcp/tools/core/update_environment.js +55 -0
  106. package/lib/mcp/tools/crashlytics/index.js +5 -0
  107. package/lib/mcp/tools/crashlytics/list_top_issues.js +34 -0
  108. package/lib/mcp/tools/dataconnect/converter.js +13 -1
  109. package/lib/mcp/tools/dataconnect/emulator.js +32 -0
  110. package/lib/mcp/tools/dataconnect/execute_graphql.js +24 -8
  111. package/lib/mcp/tools/dataconnect/execute_graphql_read.js +24 -8
  112. package/lib/mcp/tools/dataconnect/execute_mutation.js +27 -15
  113. package/lib/mcp/tools/dataconnect/execute_query.js +26 -14
  114. package/lib/mcp/tools/dataconnect/generate_operation.js +7 -7
  115. package/lib/mcp/tools/dataconnect/generate_schema.js +1 -1
  116. package/lib/mcp/tools/dataconnect/get_connector.js +7 -7
  117. package/lib/mcp/tools/dataconnect/get_schema.js +5 -5
  118. package/lib/mcp/tools/dataconnect/index.js +1 -5
  119. package/lib/mcp/tools/dataconnect/list_services.js +1 -1
  120. package/lib/mcp/tools/firestore/converter.js +47 -1
  121. package/lib/mcp/tools/firestore/delete_document.js +37 -0
  122. package/lib/mcp/tools/firestore/index.js +12 -2
  123. package/lib/mcp/tools/firestore/list_collections.js +1 -6
  124. package/lib/mcp/tools/firestore/query_collection.js +122 -0
  125. package/lib/mcp/tools/index.js +37 -16
  126. package/lib/mcp/tools/messaging/index.js +5 -0
  127. package/lib/mcp/tools/messaging/send_message.js +42 -0
  128. package/lib/mcp/tools/remoteconfig/get_template.js +27 -0
  129. package/lib/mcp/tools/remoteconfig/index.js +7 -0
  130. package/lib/mcp/tools/remoteconfig/publish_template.js +34 -0
  131. package/lib/mcp/tools/remoteconfig/rollback_template.js +29 -0
  132. package/lib/mcp/tools/rules/get_rules.js +29 -0
  133. package/lib/mcp/tools/rules/validate_rules.js +98 -0
  134. package/lib/mcp/tools/storage/get_download_url.js +8 -5
  135. package/lib/mcp/tools/storage/index.js +7 -2
  136. package/lib/mcp/types.js +10 -1
  137. package/lib/mcp/util.js +165 -2
  138. package/lib/mcp/util.test.js +468 -0
  139. package/lib/messaging/interfaces.js +2 -0
  140. package/lib/messaging/sendMessage.js +48 -0
  141. package/lib/prompt.js +1 -1
  142. package/lib/remoteconfig/publish.js +39 -0
  143. package/lib/requireAuth.js +2 -2
  144. package/lib/track.js +1 -1
  145. package/lib/utils.js +2 -37
  146. package/package.json +2 -1
  147. package/schema/connector-yaml.json +12 -0
  148. package/schema/extension-yaml.json +17 -4
  149. package/schema/firebase-config.json +65 -10
  150. package/standalone/package.json +1 -1
  151. package/templates/dataconnect-prompts/operation-generation-cursor-windsurf-rule.txt +273 -0
  152. package/templates/dataconnect-prompts/schema-generation-cursor-windsurf-rule.txt +653 -0
  153. package/templates/genkit/firebase.1.0.0.template +5 -0
  154. package/templates/init/firestore/firestore.rules +2 -0
  155. package/templates/init/functions/javascript/package.lint.json +1 -1
  156. package/templates/init/functions/javascript/package.nolint.json +1 -1
  157. package/templates/init/functions/typescript/package.lint.json +2 -2
  158. package/templates/init/functions/typescript/package.nolint.json +3 -3
  159. package/lib/mcp/tools/directory/get_project_directory.js +0 -20
  160. package/lib/mcp/tools/directory/index.js +0 -6
  161. package/lib/mcp/tools/directory/set_project_directory.js +0 -33
  162. package/lib/mcp/tools/firestore/get_rules.js +0 -26
package/lib/api.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hostingApiOrigin = exports.firebaseStorageOrigin = exports.storageOrigin = exports.runtimeconfigOrigin = exports.rulesOrigin = exports.resourceManagerOrigin = 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.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.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 = void 0;
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;
5
5
  const constants_1 = require("./emulator/constants");
6
6
  const logger_1 = require("./logger");
7
7
  const scopes = require("./scopes");
@@ -37,6 +37,8 @@ const apphostingGitHubAppInstallationURL = () => utils.envOverride("FIREBASE_APP
37
37
  exports.apphostingGitHubAppInstallationURL = apphostingGitHubAppInstallationURL;
38
38
  const authOrigin = () => utils.envOverride("FIREBASE_AUTH_URL", "https://accounts.google.com");
39
39
  exports.authOrigin = authOrigin;
40
+ const authManagementOrigin = () => utils.envOverride("FIREBASE_AUTH_MANAGEMENT_URL", "https://identitytoolkit.googleapis.com");
41
+ exports.authManagementOrigin = authManagementOrigin;
40
42
  const consoleOrigin = () => utils.envOverride("FIREBASE_CONSOLE_URL", "https://console.firebase.google.com");
41
43
  exports.consoleOrigin = consoleOrigin;
42
44
  const dynamicLinksOrigin = () => utils.envOverride("FIREBASE_DYNAMIC_LINKS_URL", "https://firebasedynamiclinks.googleapis.com");
@@ -100,6 +102,10 @@ const rtdbMetadataOrigin = () => utils.envOverride("FIREBASE_RTDB_METADATA_URL",
100
102
  exports.rtdbMetadataOrigin = rtdbMetadataOrigin;
101
103
  const remoteConfigApiOrigin = () => utils.envOverride("FIREBASE_REMOTE_CONFIG_URL", "https://firebaseremoteconfig.googleapis.com");
102
104
  exports.remoteConfigApiOrigin = remoteConfigApiOrigin;
105
+ const messagingApiOrigin = () => utils.envOverride("FIREBASE_MESSAGING_CONFIG_URL", "https://fcm.googleapis.com");
106
+ exports.messagingApiOrigin = messagingApiOrigin;
107
+ const crashlyticsApiOrigin = () => utils.envOverride("FIREBASE_CRASHLYTICS_URL", "https://firebasecrashlytics.googleapis.com");
108
+ exports.crashlyticsApiOrigin = crashlyticsApiOrigin;
103
109
  const resourceManagerOrigin = () => utils.envOverride("FIREBASE_RESOURCEMANAGER_URL", "https://cloudresourcemanager.googleapis.com");
104
110
  exports.resourceManagerOrigin = resourceManagerOrigin;
105
111
  const rulesOrigin = () => utils.envOverride("FIREBASE_RULES_URL", "https://firebaserules.googleapis.com");
@@ -138,6 +144,8 @@ const cloudSQLAdminOrigin = () => utils.envOverride("CLOUD_SQL_URL", "https://sq
138
144
  exports.cloudSQLAdminOrigin = cloudSQLAdminOrigin;
139
145
  const vertexAIOrigin = () => utils.envOverride("VERTEX_AI_URL", "https://aiplatform.googleapis.com");
140
146
  exports.vertexAIOrigin = vertexAIOrigin;
147
+ const cloudAiCompanionOrigin = () => utils.envOverride("CLOUD_AI_COMPANION_URL", "https://cloudaicompanion.googleapis.com");
148
+ exports.cloudAiCompanionOrigin = cloudAiCompanionOrigin;
141
149
  function getScopes() {
142
150
  return Array.from(commandScopes);
143
151
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getBackend = exports.getBackendForAmbiguousLocation = exports.chooseBackends = exports.getBackendForLocation = exports.promptLocation = exports.deleteBackendAndPoll = exports.setDefaultTrafficPolicy = exports.createBackend = exports.ensureAppHostingComputeServiceAccount = exports.createGitRepoLink = exports.doSetup = void 0;
3
+ exports.getBackend = exports.getBackendForAmbiguousLocation = exports.chooseBackends = exports.promptExistingBackend = exports.getBackendForLocation = exports.promptLocation = exports.deleteBackendAndPoll = exports.setDefaultTrafficPolicy = exports.createBackend = exports.promptNewBackendId = exports.ensureAppHostingComputeServiceAccount = exports.createGitRepoLink = exports.ensureRequiredApisEnabled = exports.doSetupSourceDeploy = exports.doSetup = void 0;
4
4
  const clc = require("colorette");
5
5
  const poller = require("../operation-poller");
6
6
  const apphosting = require("../gcp/apphosting");
@@ -19,6 +19,7 @@ const app_1 = require("./app");
19
19
  const ora = require("ora");
20
20
  const node_fetch_1 = require("node-fetch");
21
21
  const rollout_1 = require("./rollout");
22
+ const fuzzy = require("fuzzy");
22
23
  const DEFAULT_COMPUTE_SERVICE_ACCOUNT_NAME = "firebase-app-hosting-compute";
23
24
  const apphostingPollerOptions = {
24
25
  apiOrigin: (0, api_1.apphostingOrigin)(),
@@ -51,14 +52,7 @@ async function awaitTlsReady(url) {
51
52
  } while (!ready);
52
53
  }
53
54
  async function doSetup(projectId, webAppName, serviceAccount) {
54
- await Promise.all([
55
- (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.developerConnectOrigin)(), "apphosting", true),
56
- (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.cloudbuildOrigin)(), "apphosting", true),
57
- (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.secretManagerOrigin)(), "apphosting", true),
58
- (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.cloudRunApiOrigin)(), "apphosting", true),
59
- (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.artifactRegistryDomain)(), "apphosting", true),
60
- (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.iamOrigin)(), "apphosting", true),
61
- ]);
55
+ await ensureRequiredApisEnabled(projectId);
62
56
  await ensureAppHostingComputeServiceAccount(projectId, serviceAccount);
63
57
  const location = await promptLocation(projectId, "Select a primary region to host your backend:\n");
64
58
  const gitRepositoryLink = await githubConnections.linkGitHubRepository(projectId, location);
@@ -76,7 +70,7 @@ async function doSetup(projectId, webAppName, serviceAccount) {
76
70
  (0, utils_1.logWarning)(`Firebase web app not set`);
77
71
  }
78
72
  const createBackendSpinner = ora("Creating your new backend...").start();
79
- const backend = await createBackend(projectId, location, backendId, gitRepositoryLink, serviceAccount, webApp === null || webApp === void 0 ? void 0 : webApp.id, rootDir);
73
+ const backend = await createBackend(projectId, location, backendId, serviceAccount, gitRepositoryLink, webApp === null || webApp === void 0 ? void 0 : webApp.id, rootDir);
80
74
  createBackendSpinner.succeed(`Successfully created backend!\n\t${backend.name}\n`);
81
75
  await setDefaultTrafficPolicy(projectId, location, backendId, branch);
82
76
  const confirmRollout = await (0, prompt_1.confirm)({
@@ -112,6 +106,34 @@ async function doSetup(projectId, webAppName, serviceAccount) {
112
106
  (0, utils_1.logSuccess)(`Your backend is now deployed at:\n\thttps://${backend.uri}`);
113
107
  }
114
108
  exports.doSetup = doSetup;
109
+ async function doSetupSourceDeploy(projectId, backendId) {
110
+ const location = await promptLocation(projectId, "Select a primary region to host your backend:\n");
111
+ const webAppSpinner = ora("Creating a new web app...\n").start();
112
+ const webApp = await app_1.webApps.getOrCreateWebApp(projectId, null, backendId);
113
+ if (!webApp) {
114
+ (0, utils_1.logWarning)(`Firebase web app not set`);
115
+ }
116
+ webAppSpinner.stop();
117
+ const createBackendSpinner = ora("Creating your new backend...").start();
118
+ const backend = await createBackend(projectId, location, backendId, null, undefined, webApp === null || webApp === void 0 ? void 0 : webApp.id);
119
+ createBackendSpinner.succeed(`Successfully created backend!\n\t${backend.name}\n`);
120
+ return {
121
+ backend,
122
+ location,
123
+ };
124
+ }
125
+ exports.doSetupSourceDeploy = doSetupSourceDeploy;
126
+ async function ensureRequiredApisEnabled(projectId) {
127
+ await Promise.all([
128
+ (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.developerConnectOrigin)(), "apphosting", true),
129
+ (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.cloudbuildOrigin)(), "apphosting", true),
130
+ (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.secretManagerOrigin)(), "apphosting", true),
131
+ (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.cloudRunApiOrigin)(), "apphosting", true),
132
+ (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.artifactRegistryDomain)(), "apphosting", true),
133
+ (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.iamOrigin)(), "apphosting", true),
134
+ ]);
135
+ }
136
+ exports.ensureRequiredApisEnabled = ensureRequiredApisEnabled;
115
137
  async function createGitRepoLink(projectId, location, connectionId) {
116
138
  await Promise.all([
117
139
  (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.developerConnectOrigin)(), "apphosting", true),
@@ -130,7 +152,7 @@ async function createGitRepoLink(projectId, location, connectionId) {
130
152
  await githubConnections.linkGitHubRepository(projectId, location, connectionId);
131
153
  }
132
154
  exports.createGitRepoLink = createGitRepoLink;
133
- async function ensureAppHostingComputeServiceAccount(projectId, serviceAccount) {
155
+ async function ensureAppHostingComputeServiceAccount(projectId, serviceAccount, deployFromSource = false) {
134
156
  const sa = serviceAccount || defaultComputeServiceAccountEmail(projectId);
135
157
  const name = `projects/${projectId}/serviceAccounts/${sa}`;
136
158
  try {
@@ -147,6 +169,14 @@ async function ensureAppHostingComputeServiceAccount(projectId, serviceAccount)
147
169
  throw new error_1.FirebaseError(`Failed to create backend due to missing delegation permissions for ${sa}. Make sure you have the iam.serviceAccounts.actAs permission.`, { original: err });
148
170
  }
149
171
  }
172
+ if (deployFromSource) {
173
+ const policy = await (0, resourceManager_1.getIamPolicy)(projectId);
174
+ const objectViewerBinding = policy.bindings.find((binding) => binding.role === "roles/storage.objectViewer");
175
+ if (!objectViewerBinding ||
176
+ !objectViewerBinding.members.includes(`serviceAccount:${defaultComputeServiceAccountEmail(projectId)}`)) {
177
+ await (0, resourceManager_1.addServiceAccountToRoles)(projectId, defaultComputeServiceAccountEmail(projectId), ["roles/storage.objectViewer"], true);
178
+ }
179
+ }
150
180
  }
151
181
  exports.ensureAppHostingComputeServiceAccount = ensureAppHostingComputeServiceAccount;
152
182
  async function promptNewBackendId(projectId, location) {
@@ -168,17 +198,20 @@ async function promptNewBackendId(projectId, location) {
168
198
  (0, utils_1.logWarning)(`Backend with id ${backendId} already exists in ${location}`);
169
199
  }
170
200
  }
201
+ exports.promptNewBackendId = promptNewBackendId;
171
202
  function defaultComputeServiceAccountEmail(projectId) {
172
203
  return `${DEFAULT_COMPUTE_SERVICE_ACCOUNT_NAME}@${projectId}.iam.gserviceaccount.com`;
173
204
  }
174
- async function createBackend(projectId, location, backendId, repository, serviceAccount, webAppId, rootDir = "/") {
205
+ async function createBackend(projectId, location, backendId, serviceAccount, repository, webAppId, rootDir = "/") {
175
206
  const defaultServiceAccount = defaultComputeServiceAccountEmail(projectId);
176
207
  const backendReqBody = {
177
208
  servingLocality: "GLOBAL_ACCESS",
178
- codebase: {
179
- repository: `${repository.name}`,
180
- rootDirectory: rootDir,
181
- },
209
+ codebase: repository
210
+ ? {
211
+ repository: `${repository.name}`,
212
+ rootDirectory: rootDir,
213
+ }
214
+ : undefined,
182
215
  labels: deploymentTool.labels(),
183
216
  serviceAccount: serviceAccount || defaultServiceAccount,
184
217
  appId: webAppId,
@@ -203,6 +236,7 @@ async function provisionDefaultComputeServiceAccount(projectId) {
203
236
  "roles/firebaseapphosting.computeRunner",
204
237
  "roles/firebase.sdkAdminServiceAgent",
205
238
  "roles/developerconnect.readTokenAccessor",
239
+ "roles/storage.objectViewer",
206
240
  ], true);
207
241
  }
208
242
  async function setDefaultTrafficPolicy(projectId, location, backendId, codebaseBranch) {
@@ -225,11 +259,11 @@ async function promptLocation(projectId, prompt = "Please select a location:") {
225
259
  if (allowedLocations.length === 1) {
226
260
  return allowedLocations[0];
227
261
  }
228
- const location = (await (0, prompt_1.select)({
262
+ const location = await (0, prompt_1.select)({
229
263
  default: constants_1.DEFAULT_LOCATION,
230
264
  message: prompt,
231
265
  choices: allowedLocations,
232
- }));
266
+ });
233
267
  (0, utils_1.logSuccess)(`Location set to ${location}.\n`);
234
268
  return location;
235
269
  }
@@ -245,6 +279,28 @@ async function getBackendForLocation(projectId, location, backendId) {
245
279
  }
246
280
  }
247
281
  exports.getBackendForLocation = getBackendForLocation;
282
+ async function promptExistingBackend(projectId, promptMessage) {
283
+ const { backends } = await apphosting.listBackends(projectId, "-");
284
+ const backendId = await (0, prompt_1.search)({
285
+ message: promptMessage,
286
+ source: (input = "") => {
287
+ return new Promise((resolve) => resolve([
288
+ ...fuzzy
289
+ .filter(input, backends, {
290
+ extract: (backend) => apphosting.parseBackendName(backend.name).id,
291
+ })
292
+ .map((result) => {
293
+ return {
294
+ name: apphosting.parseBackendName(result.original.name).id,
295
+ value: apphosting.parseBackendName(result.original.name).id,
296
+ };
297
+ }),
298
+ ]));
299
+ },
300
+ });
301
+ return backendId;
302
+ }
303
+ exports.promptExistingBackend = promptExistingBackend;
248
304
  async function chooseBackends(projectId, backendId, chooseBackendPrompt, force) {
249
305
  let { unreachable, backends } = await apphosting.listBackends(projectId, "-");
250
306
  if (unreachable && unreachable.length !== 0) {
@@ -19,8 +19,8 @@ const apphostingPollerOptions = {
19
19
  const GIT_COMMIT_SHA_REGEX = /^(?:[0-9a-f]{40}|[0-9a-f]{7})$/;
20
20
  async function createRollout(backendId, projectId, branch, commit, force) {
21
21
  const backend = await (0, backend_1.getBackend)(projectId, backendId);
22
- if (!backend.codebase.repository) {
23
- throw new error_1.FirebaseError(`Backend ${backendId} is misconfigured due to missing a connected repository. You can delete and recreate your backend using 'firebase apphosting:backends:delete' and 'firebase apphosting:backends:create'.`);
22
+ if (!backend.codebase || !backend.codebase.repository) {
23
+ throw new error_1.FirebaseError(`Backend ${backendId} is missing a connected repository. If you would like to deploy from a branch or commit of a GitHub repository, you can connect one through the Firebase Console. If you would like to deploy from local source, run 'firebase deploy'.`);
24
24
  }
25
25
  const { location } = apphosting.parseBackendName(backend.name);
26
26
  const { repoLink, owner, repo, readToken } = await (0, devConnect_1.getRepoDetailsFromBackend)(projectId, location, backend.codebase.repository);
@@ -9,11 +9,11 @@ const prompt = require("../../prompt");
9
9
  const utils = require("../../utils");
10
10
  const logger_1 = require("../../logger");
11
11
  const env = require("../../functions/env");
12
- function toMetadata(projectNumber, backends) {
12
+ async function toMetadata(projectNumber, backends) {
13
13
  const metadata = [];
14
14
  for (const backend of backends) {
15
15
  const [, , , location, , id] = backend.name.split("/");
16
- metadata.push(Object.assign({ location, id }, (0, _1.serviceAccountsForBackend)(projectNumber, backend)));
16
+ metadata.push(Object.assign({ location, id }, (await (0, _1.serviceAccountsForBackend)(projectNumber, backend))));
17
17
  }
18
18
  return metadata.sort((left, right) => {
19
19
  const cmplocation = left.location.localeCompare(right.location);
@@ -86,12 +86,12 @@ async function selectBackendServiceAccounts(projectNumber, projectId, options) {
86
86
  message: "To use this secret, your backend's service account must be granted access. Would you like to grant access now?",
87
87
  });
88
88
  if (grant) {
89
- return (0, _1.toMulti)((0, _1.serviceAccountsForBackend)(projectNumber, listBackends.backends[0]));
89
+ return (0, _1.toMulti)(await (0, _1.serviceAccountsForBackend)(projectNumber, listBackends.backends[0]));
90
90
  }
91
91
  utils.logBullet(exports.GRANT_ACCESS_IN_FUTURE);
92
92
  return { buildServiceAccounts: [], runServiceAccounts: [] };
93
93
  }
94
- const metadata = toMetadata(projectNumber, listBackends.backends);
94
+ const metadata = await toMetadata(projectNumber, listBackends.backends);
95
95
  if (metadata.every(matchesServiceAccounts(metadata[0]))) {
96
96
  utils.logBullet("To use this secret, your backend's service account must be granted access.");
97
97
  utils.logBullet("All of your backends share the following " +
@@ -21,7 +21,7 @@ function toMulti(accounts) {
21
21
  return m;
22
22
  }
23
23
  exports.toMulti = toMulti;
24
- function serviceAccountsForBackend(projectNumber, backend) {
24
+ async function serviceAccountsForBackend(projectNumber, backend) {
25
25
  if (backend.serviceAccount) {
26
26
  return {
27
27
  buildServiceAccount: backend.serviceAccount,
@@ -30,7 +30,7 @@ function serviceAccountsForBackend(projectNumber, backend) {
30
30
  }
31
31
  return {
32
32
  buildServiceAccount: gcb.getDefaultServiceAccount(projectNumber),
33
- runServiceAccount: gce.getDefaultServiceAccount(projectNumber),
33
+ runServiceAccount: await gce.getDefaultServiceAccount(projectNumber),
34
34
  };
35
35
  }
36
36
  exports.serviceAccountsForBackend = serviceAccountsForBackend;
package/lib/auth.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.addAdditionalAccount = exports.logout = exports.getAccessToken = exports.haveValidTokens = exports.isExpired = exports.loggedIn = exports.findAccountByEmail = exports.loginGithub = exports.loginGoogle = exports.setGlobalDefaultAccount = exports.setProjectAccount = exports.loginAdditionalAccount = exports.selectAccount = exports.setRefreshToken = exports.setActiveAccount = exports.getAllAccounts = exports.getAdditionalAccounts = exports.getProjectDefaultAccount = exports.getGlobalDefaultAccount = void 0;
3
+ exports.addAdditionalAccount = exports.logout = exports.getAccessToken = exports.haveValidTokens = exports.isExpired = exports.loggedIn = exports.findAccountByEmail = exports.loginGithub = exports.loginGoogle = exports.setGlobalDefaultAccount = exports.setProjectAccount = exports.loginAdditionalAccount = exports.selectAccount = exports.setRefreshToken = exports.setActiveAccount = exports.assertAccount = exports.getAllAccounts = exports.getAdditionalAccounts = exports.getProjectDefaultAccount = exports.getGlobalDefaultAccount = void 0;
4
4
  const clc = require("colorette");
5
5
  const FormData = require("form-data");
6
6
  const http = require("http");
@@ -62,6 +62,16 @@ function getAllAccounts() {
62
62
  return res;
63
63
  }
64
64
  exports.getAllAccounts = getAllAccounts;
65
+ function assertAccount(email, options) {
66
+ const allAccounts = getAllAccounts();
67
+ const accountExists = allAccounts.some((a) => a.user.email === email);
68
+ if (!accountExists) {
69
+ throw new error_1.FirebaseError(`Account ${email} does not exist, ${(options === null || options === void 0 ? void 0 : options.mcp)
70
+ ? `use the 'firebase_get_environment' tool to see available accounts or instruct the user to use the 'firebase login:add' terminal command to add a new account.`
71
+ : `run "${clc.bold("firebase login:list")} to see valid accounts`}`);
72
+ }
73
+ }
74
+ exports.assertAccount = assertAccount;
65
75
  function setActiveAccount(options, account) {
66
76
  if (account.tokens.refresh_token) {
67
77
  setRefreshToken(account.tokens.refresh_token);
@@ -121,7 +131,17 @@ function setProjectAccount(projectDir, email) {
121
131
  configstore_1.configstore.set("activeAccounts", activeAccounts);
122
132
  }
123
133
  exports.setProjectAccount = setProjectAccount;
124
- function setGlobalDefaultAccount(account) {
134
+ function setGlobalDefaultAccount(accountOrEmail) {
135
+ let account;
136
+ if (typeof accountOrEmail === "string") {
137
+ const accountFromEmail = getAllAccounts().find((acc) => acc.user.email === accountOrEmail);
138
+ if (!accountFromEmail)
139
+ throw new error_1.FirebaseError(`Account '${accountOrEmail}' is not a signed-in user on this device.`);
140
+ account = accountFromEmail;
141
+ }
142
+ else {
143
+ account = accountOrEmail;
144
+ }
125
145
  configstore_1.configstore.set("user", account.user);
126
146
  configstore_1.configstore.set("tokens", account.tokens);
127
147
  const additionalAccounts = getAdditionalAccounts();
package/lib/bin/cli.js CHANGED
@@ -6,9 +6,6 @@ const clc = require("colorette");
6
6
  const marked_terminal_1 = require("marked-terminal");
7
7
  const marked_1 = require("marked");
8
8
  marked_1.marked.use((0, marked_terminal_1.markedTerminal)());
9
- const node_path_1 = require("node:path");
10
- const triple_beam_1 = require("triple-beam");
11
- const node_util_1 = require("node:util");
12
9
  const fs = require("node:fs");
13
10
  const configstore_1 = require("../configstore");
14
11
  const errorOut_1 = require("../errorOut");
@@ -17,46 +14,17 @@ const logger_1 = require("../logger");
17
14
  const client = require("..");
18
15
  const fsutils = require("../fsutils");
19
16
  const utils = require("../utils");
20
- const winston = require("winston");
21
17
  const experiments_1 = require("../experiments");
22
18
  const fetchMOTD_1 = require("../fetchMOTD");
23
19
  function cli(pkg) {
24
20
  const updateNotifier = updateNotifierPkg({ pkg });
25
21
  const args = process.argv.slice(2);
26
22
  let cmd;
27
- function findAvailableLogFile() {
28
- const candidates = ["firebase-debug.log"];
29
- for (let i = 1; i < 10; i++) {
30
- candidates.push(`firebase-debug.${i}.log`);
31
- }
32
- for (const c of candidates) {
33
- const logFilename = (0, node_path_1.join)(process.cwd(), c);
34
- try {
35
- const fd = fs.openSync(logFilename, "r+");
36
- fs.closeSync(fd);
37
- return logFilename;
38
- }
39
- catch (e) {
40
- if (e.code === "ENOENT") {
41
- return logFilename;
42
- }
43
- }
44
- }
45
- throw new Error("Unable to obtain permissions for firebase-debug.log");
46
- }
47
- const logFilename = findAvailableLogFile();
48
23
  if (!process.env.DEBUG && args.includes("--debug")) {
49
24
  process.env.DEBUG = "true";
50
25
  }
51
26
  process.env.IS_FIREBASE_CLI = "true";
52
- logger_1.logger.add(new winston.transports.File({
53
- level: "debug",
54
- filename: logFilename,
55
- format: winston.format.printf((info) => {
56
- const segments = [info.message, ...(info[triple_beam_1.SPLAT] || [])].map(utils.tryStringify);
57
- return `[${info.level}] ${(0, node_util_1.stripVTControlCharacters)(segments.join(" "))}`;
58
- }),
59
- }));
27
+ const logFilename = (0, logger_1.useFileLogger)();
60
28
  logger_1.logger.debug("-".repeat(70));
61
29
  logger_1.logger.debug("Command: ", process.argv.join(" "));
62
30
  logger_1.logger.debug("CLI Version: ", pkg.version);
package/lib/bin/mcp.js CHANGED
@@ -3,10 +3,11 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.mcp = void 0;
5
5
  const logger_1 = require("../logger");
6
- (0, logger_1.silenceStdout)();
7
6
  const index_1 = require("../mcp/index");
8
7
  const util_1 = require("util");
9
8
  const types_1 = require("../mcp/types");
9
+ const index_js_1 = require("../mcp/tools/index.js");
10
+ const path_1 = require("path");
10
11
  const STARTUP_MESSAGE = `
11
12
  This is a running process of the Firebase MCP server. This command should only be executed by an MCP client. An example MCP client configuration might be:
12
13
 
@@ -24,13 +25,23 @@ async function mcp() {
24
25
  options: {
25
26
  only: { type: "string", default: "" },
26
27
  dir: { type: "string" },
28
+ "generate-tool-list": { type: "boolean", default: false },
27
29
  },
28
30
  allowPositionals: true,
29
31
  });
32
+ if (values["generate-tool-list"]) {
33
+ console.log((0, index_js_1.markdownDocsOfTools)());
34
+ return;
35
+ }
36
+ process.env.IS_FIREBASE_MCP = "true";
37
+ (0, logger_1.useFileLogger)();
30
38
  const activeFeatures = (values.only || "")
31
39
  .split(",")
32
40
  .filter((f) => types_1.SERVER_FEATURES.includes(f));
33
- const server = new index_1.FirebaseMcpServer({ activeFeatures, projectRoot: values.dir });
41
+ const server = new index_1.FirebaseMcpServer({
42
+ activeFeatures,
43
+ projectRoot: values.dir ? (0, path_1.resolve)(values.dir) : undefined,
44
+ });
34
45
  await server.start();
35
46
  if (process.stdin.isTTY)
36
47
  process.stderr.write(STARTUP_MESSAGE);
@@ -26,6 +26,7 @@ const FILTERABLE_TARGETS = new Set([
26
26
  "storage",
27
27
  "database",
28
28
  "dataconnect",
29
+ "apphosting",
29
30
  ]);
30
31
  async function checkValidTargetFilters(options) {
31
32
  const only = !options.only ? [] : options.only.split(",");
package/lib/command.js CHANGED
@@ -13,6 +13,7 @@ const track_1 = require("./track");
13
13
  const auth_1 = require("./auth");
14
14
  const projects_1 = require("./management/projects");
15
15
  const requireAuth_1 = require("./requireAuth");
16
+ const logger_1 = require("./logger");
16
17
  class Command {
17
18
  constructor(cmd) {
18
19
  this.cmd = cmd;
@@ -163,8 +164,8 @@ class Command {
163
164
  if ((0, utils_1.getInheritedOption)(options, "json")) {
164
165
  options.nonInteractive = true;
165
166
  }
166
- else {
167
- (0, utils_1.setupLoggers)();
167
+ else if (!options.isMCP) {
168
+ (0, logger_1.useConsoleLoggers)();
168
169
  }
169
170
  if ((0, utils_1.getInheritedOption)(options, "config")) {
170
171
  options.configPath = (0, utils_1.getInheritedOption)(options, "config");
@@ -54,6 +54,6 @@ exports.command = new command_1.Command("apphosting:secrets:grantaccess <secretN
54
54
  else {
55
55
  backend = await apphosting.getBackend(projectId, location, backendId);
56
56
  }
57
- const accounts = secrets.toMulti(secrets.serviceAccountsForBackend(projectNumber, backend));
57
+ const accounts = secrets.toMulti(await secrets.serviceAccountsForBackend(projectNumber, backend));
58
58
  await Promise.allSettled(secretList.map((secretName) => secrets.grantSecretAccess(projectId, projectNumber, secretName, accounts)));
59
59
  });
@@ -24,6 +24,7 @@ exports.VALID_DEPLOY_TARGETS = [
24
24
  "remoteconfig",
25
25
  "extensions",
26
26
  "dataconnect",
27
+ "apphosting",
27
28
  ];
28
29
  exports.TARGET_PERMISSIONS = {
29
30
  database: ["firebasedatabase.instances.update"],
@@ -40,7 +40,7 @@ let choices = [
40
40
  },
41
41
  {
42
42
  value: "apphosting",
43
- name: "App Hosting: Configure an apphosting.yaml file for App Hosting",
43
+ name: "App Hosting: Enable web app deployments with App Hosting",
44
44
  checked: false,
45
45
  hidden: false,
46
46
  },
@@ -186,12 +186,9 @@ async function initAction(feature, options) {
186
186
  }
187
187
  await (0, init_1.init)(setup, config, options);
188
188
  logger_1.logger.info();
189
- utils.logBullet("Writing configuration info to " + clc.bold("firebase.json") + "...");
190
189
  config.writeProjectFile("firebase.json", setup.config);
191
- utils.logBullet("Writing project information to " + clc.bold(".firebaserc") + "...");
192
190
  config.writeProjectFile(".firebaserc", setup.rcfile);
193
191
  if (!fsutils.fileExistsSync(config.path(".gitignore"))) {
194
- utils.logBullet("Writing gitignore file to " + clc.bold(".gitignore") + "...");
195
192
  config.writeProjectFile(".gitignore", GITIGNORE_TEMPLATE);
196
193
  }
197
194
  logger_1.logger.info();
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.command = void 0;
4
- const clc = require("colorette");
5
4
  const command_1 = require("../command");
6
5
  const utils = require("../utils");
7
6
  const auth = require("../auth");
@@ -9,11 +8,7 @@ const error_1 = require("../error");
9
8
  exports.command = new command_1.Command("login:use <email>")
10
9
  .description("set the default account to use for this project directory or the global default account if not in a Firebase project directory")
11
10
  .action((email, options) => {
12
- const allAccounts = auth.getAllAccounts();
13
- const accountExists = allAccounts.some((a) => a.user.email === email);
14
- if (!accountExists) {
15
- throw new error_1.FirebaseError(`Account ${email} does not exist, run "${clc.bold("firebase login:list")}" to see valid accounts`);
16
- }
11
+ auth.assertAccount(email);
17
12
  const projectDir = options.projectRoot;
18
13
  if (projectDir) {
19
14
  if (options.user.email === email) {
@@ -27,15 +22,11 @@ exports.command = new command_1.Command("login:use <email>")
27
22
  if (options.user.email === email) {
28
23
  throw new error_1.FirebaseError(`Already using account ${email} for the global default account.`);
29
24
  }
30
- const newDefaultAccount = allAccounts.find((a) => a.user.email === email);
31
- if (!newDefaultAccount) {
32
- throw new error_1.FirebaseError(`Account ${email} does not exist, run "${clc.bold("firebase login:list")}" to see valid accounts`);
33
- }
34
25
  const oldDefaultAccount = auth.getGlobalDefaultAccount();
35
26
  if (!oldDefaultAccount) {
36
27
  throw new error_1.FirebaseError("Could not determine global default account");
37
28
  }
38
- auth.setGlobalDefaultAccount(newDefaultAccount);
29
+ auth.setGlobalDefaultAccount(email);
39
30
  auth.addAdditionalAccount(oldDefaultAccount);
40
31
  utils.logSuccess(`Set global default account to ${email}.`);
41
32
  return email;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.command = void 0;
3
+ exports.command = exports.setNewActive = void 0;
4
4
  const clc = require("colorette");
5
5
  const command_1 = require("../command");
6
6
  const projects_1 = require("../management/projects");
@@ -30,10 +30,10 @@ function listAliases(options) {
30
30
  function verifyMessage(name) {
31
31
  return "please verify project " + clc.bold(name) + " exists and you have access.";
32
32
  }
33
- async function setNewActive(projectOrAlias, aliasOpt, options) {
33
+ async function setNewActive(projectOrAlias, aliasOpt, rc, projectRoot) {
34
34
  let project;
35
- const hasAlias = options.rc.hasProjectAlias(projectOrAlias);
36
- const resolvedProject = options.rc.resolveAlias(projectOrAlias);
35
+ const hasAlias = rc.hasProjectAlias(projectOrAlias);
36
+ const resolvedProject = rc.resolveAlias(projectOrAlias);
37
37
  (0, command_2.validateProjectId)(resolvedProject);
38
38
  try {
39
39
  project = await (0, projects_1.getProject)(resolvedProject);
@@ -45,24 +45,25 @@ async function setNewActive(projectOrAlias, aliasOpt, options) {
45
45
  if (!project) {
46
46
  throw new error_1.FirebaseError(`Cannot create alias ${clc.bold(aliasOpt)}, ${verifyMessage(projectOrAlias)}`);
47
47
  }
48
- options.rc.addProjectAlias(aliasOpt, projectOrAlias);
48
+ rc.addProjectAlias(aliasOpt, projectOrAlias);
49
49
  logger_1.logger.info("Created alias", clc.bold(aliasOpt), "for", resolvedProject + ".");
50
50
  }
51
51
  if (hasAlias) {
52
52
  if (!project) {
53
53
  throw new error_1.FirebaseError(`Unable to use alias ${clc.bold(projectOrAlias)}, ${verifyMessage(resolvedProject)}`);
54
54
  }
55
- utils.makeActiveProject(options.projectRoot, projectOrAlias);
55
+ utils.makeActiveProject(projectRoot, projectOrAlias);
56
56
  logger_1.logger.info("Now using alias", clc.bold(projectOrAlias), "(" + resolvedProject + ")");
57
57
  }
58
58
  else if (project) {
59
- utils.makeActiveProject(options.projectRoot, projectOrAlias);
59
+ utils.makeActiveProject(projectRoot, projectOrAlias);
60
60
  logger_1.logger.info("Now using project", clc.bold(projectOrAlias));
61
61
  }
62
62
  else {
63
63
  throw new error_1.FirebaseError(`Invalid project selection, ${verifyMessage(projectOrAlias)}`);
64
64
  }
65
65
  }
66
+ exports.setNewActive = setNewActive;
66
67
  function unalias(alias, options) {
67
68
  if (options.rc.hasProjectAlias(alias)) {
68
69
  options.rc.removeProjectAlias(alias);
@@ -150,7 +151,7 @@ exports.command = new command_1.Command("use [alias_or_project_id]")
150
151
  " to start a project directory in the current folder.");
151
152
  }
152
153
  if (newActive) {
153
- return setNewActive(newActive, aliasOpt, options);
154
+ return setNewActive(newActive, aliasOpt, options.rc, options.projectRoot);
154
155
  }
155
156
  if (options.unalias) {
156
157
  return unalias(options.unalias, options);