firebase-tools 14.11.2 → 14.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/api.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.storageOrigin = exports.runtimeconfigOrigin = exports.rulesOrigin = exports.resourceManagerOrigin = exports.crashlyticsApiOrigin = exports.messagingApiOrigin = exports.remoteConfigApiOrigin = exports.rtdbMetadataOrigin = exports.rtdbManagementOrigin = exports.realtimeOrigin = exports.extensionsTOSOrigin = exports.extensionsPublisherOrigin = exports.extensionsOrigin = exports.iamOrigin = exports.identityOrigin = exports.hostingOrigin = exports.googleOrigin = exports.pubsubOrigin = exports.cloudTasksOrigin = exports.cloudschedulerOrigin = exports.cloudbuildOrigin = exports.functionsDefaultRegion = exports.runOrigin = exports.functionsV2Origin = exports.functionsOrigin = exports.firestoreOrigin = exports.firestoreOriginOrEmulator = exports.firedataOrigin = exports.firebaseExtensionsRegistryOrigin = exports.firebaseApiOrigin = exports.eventarcOrigin = exports.dynamicLinksKey = exports.dynamicLinksOrigin = exports.consoleOrigin = exports.authManagementOrigin = exports.authOrigin = exports.apphostingGitHubAppInstallationURL = exports.apphostingP4SADomain = exports.apphostingOrigin = exports.appDistributionOrigin = exports.artifactRegistryDomain = exports.developerConnectP4SADomain = exports.developerConnectOrigin = exports.containerRegistryDomain = exports.cloudMonitoringOrigin = exports.cloudloggingOrigin = exports.cloudbillingOrigin = exports.clientSecret = exports.clientId = exports.authProxyOrigin = void 0;
4
- exports.setScopes = exports.getScopes = exports.appTestingOrigin = exports.cloudAiCompanionOrigin = exports.vertexAIOrigin = exports.cloudSQLAdminOrigin = exports.dataConnectLocalConnString = exports.dataconnectP4SADomain = exports.dataconnectOrigin = exports.githubClientSecret = exports.githubClientId = exports.computeOrigin = exports.secretManagerOrigin = exports.githubApiOrigin = exports.githubOrigin = exports.serviceUsageOrigin = exports.cloudRunApiOrigin = exports.hostingApiOrigin = exports.firebaseStorageOrigin = void 0;
4
+ exports.setScopes = exports.getScopes = exports.appTestingOrigin = 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.studioApiOrigin = exports.serviceUsageOrigin = exports.cloudRunApiOrigin = exports.hostingApiOrigin = exports.firebaseStorageOrigin = void 0;
5
5
  const constants_1 = require("./emulator/constants");
6
6
  const logger_1 = require("./logger");
7
7
  const scopes = require("./scopes");
@@ -120,6 +120,8 @@ const cloudRunApiOrigin = () => utils.envOverride("CLOUD_RUN_API_URL", "https://
120
120
  exports.cloudRunApiOrigin = cloudRunApiOrigin;
121
121
  const serviceUsageOrigin = () => utils.envOverride("FIREBASE_SERVICE_USAGE_URL", "https://serviceusage.googleapis.com");
122
122
  exports.serviceUsageOrigin = serviceUsageOrigin;
123
+ const studioApiOrigin = () => utils.envOverride("FIREBASE_STUDIO_URL", "https://monospace-pa.googleapis.com");
124
+ exports.studioApiOrigin = studioApiOrigin;
123
125
  const githubOrigin = () => utils.envOverride("GITHUB_URL", "https://github.com");
124
126
  exports.githubOrigin = githubOrigin;
125
127
  const githubApiOrigin = () => utils.envOverride("GITHUB_API_URL", "https://api.github.com");
package/lib/command.js CHANGED
@@ -12,8 +12,10 @@ const detectProjectRoot_1 = require("./detectProjectRoot");
12
12
  const track_1 = require("./track");
13
13
  const auth_1 = require("./auth");
14
14
  const projects_1 = require("./management/projects");
15
+ const studio_1 = require("./management/studio");
15
16
  const requireAuth_1 = require("./requireAuth");
16
17
  const logger_1 = require("./logger");
18
+ const env_1 = require("./env");
17
19
  class Command {
18
20
  constructor(cmd) {
19
21
  this.cmd = cmd;
@@ -184,19 +186,23 @@ class Command {
184
186
  if (activeAccount) {
185
187
  (0, auth_1.setActiveAccount)(options, activeAccount);
186
188
  }
187
- this.applyRC(options);
189
+ await this.applyRC(options);
188
190
  if (options.project) {
189
191
  await this.resolveProjectIdentifiers(options);
190
192
  validateProjectId(options.projectId);
191
193
  }
192
194
  }
193
- applyRC(options) {
195
+ async applyRC(options) {
194
196
  var _a, _b;
195
197
  const rc = (0, rc_1.loadRC)(options);
196
198
  options.rc = rc;
197
- const activeProject = options.projectRoot
199
+ let activeProject = options.projectRoot
198
200
  ? ((_a = configstore_1.configstore.get("activeProjects")) !== null && _a !== void 0 ? _a : {})[options.projectRoot]
199
201
  : undefined;
202
+ const isUseCommand = process.argv.includes("use");
203
+ if ((0, env_1.isFirebaseStudio)() && !options.project && !isUseCommand) {
204
+ activeProject = await (0, studio_1.reconcileStudioFirebaseProject)(options, activeProject);
205
+ }
200
206
  options.project = (_b = options.project) !== null && _b !== void 0 ? _b : activeProject;
201
207
  if (options.config && !options.project) {
202
208
  options.project = options.config.defaults.project;
@@ -16,7 +16,6 @@ const utils = require("../utils");
16
16
  const experiments_1 = require("../experiments");
17
17
  const templates_1 = require("../templates");
18
18
  const error_1 = require("../error");
19
- const track_1 = require("../track");
20
19
  const homeDir = os.homedir();
21
20
  const BANNER_TEXT = (0, templates_1.readTemplateSync)("banner.txt");
22
21
  const GITIGNORE_TEMPLATE = (0, templates_1.readTemplateSync)("_gitignore");
@@ -106,6 +105,12 @@ if ((0, experiments_1.isEnabled)("apptesting")) {
106
105
  checked: false,
107
106
  });
108
107
  }
108
+ choices.push({
109
+ value: "aitools",
110
+ name: "AI Tools: Configure AI coding assistants to work with your Firebase project",
111
+ checked: false,
112
+ hidden: true,
113
+ });
109
114
  const featureNames = choices.map((choice) => choice.value);
110
115
  const HELP = `Interactively configure the current directory as a Firebase project or initialize new features in an already configured Firebase project directory.
111
116
 
@@ -122,14 +127,13 @@ exports.command = new command_1.Command("init [feature]")
122
127
  .before(requireAuth_1.requireAuth)
123
128
  .action(initAction);
124
129
  async function initAction(feature, options) {
125
- var _a, _b;
130
+ var _a;
126
131
  if (feature && !featureNames.includes(feature)) {
127
132
  return utils.reject(clc.bold(feature) +
128
133
  " is not a supported feature; must be one of " +
129
134
  featureNames.join(", ") +
130
135
  ".");
131
136
  }
132
- const start = process.uptime();
133
137
  const cwd = options.cwd || process.cwd();
134
138
  const warnings = [];
135
139
  let warningText = "";
@@ -209,8 +213,6 @@ async function initAction(feature, options) {
209
213
  if (!fsutils.fileExistsSync(config.path(".gitignore"))) {
210
214
  config.writeProjectFile(".gitignore", GITIGNORE_TEMPLATE);
211
215
  }
212
- const duration = Math.floor((process.uptime() - start) * 1000);
213
- await (0, track_1.trackGA4)("product_init", { products_initialized: (_b = setup.features) === null || _b === void 0 ? void 0 : _b.join(",") }, duration);
214
216
  logger_1.logger.info();
215
217
  utils.logSuccess("Firebase initialization complete!");
216
218
  }
@@ -8,6 +8,10 @@ const projectConfig_1 = require("../functions/projectConfig");
8
8
  const adminSdkConfig_1 = require("../emulator/adminSdkConfig");
9
9
  const projectUtils_1 = require("../projectUtils");
10
10
  const error_1 = require("../error");
11
+ const ensureApiEnabled = require("../ensureApiEnabled");
12
+ const api_1 = require("../api");
13
+ const experiments = require("../experiments");
14
+ const prepareFunctionsUpload_1 = require("../deploy/functions/prepareFunctionsUpload");
11
15
  exports.command = new command_1.Command("internaltesting:functions:discover")
12
16
  .description("discover function triggers defined in the current project directory")
13
17
  .action(async (options) => {
@@ -17,9 +21,20 @@ exports.command = new command_1.Command("internaltesting:functions:discover")
17
21
  if (!firebaseConfig) {
18
22
  throw new error_1.FirebaseError("Admin SDK config unexpectedly undefined - have you run firebase init?");
19
23
  }
20
- const builds = await (0, prepare_1.loadCodebases)(fnConfig, options, firebaseConfig, {
21
- firebase: firebaseConfig,
22
- });
23
- logger_1.logger.info(JSON.stringify(builds, null, 2));
24
- return builds;
24
+ let runtimeConfig = { firebase: firebaseConfig };
25
+ const allowFunctionsConfig = experiments.isEnabled("dangerouslyAllowFunctionsConfig");
26
+ if (allowFunctionsConfig) {
27
+ try {
28
+ const runtimeConfigApiEnabled = await ensureApiEnabled.check(projectId, (0, api_1.runtimeconfigOrigin)(), "runtimeconfig", true);
29
+ if (runtimeConfigApiEnabled) {
30
+ runtimeConfig = Object.assign(Object.assign({}, runtimeConfig), (await (0, prepareFunctionsUpload_1.getFunctionsConfig)(projectId)));
31
+ }
32
+ }
33
+ catch (err) {
34
+ logger_1.logger.debug("Could not check Runtime Config API status, assuming disabled:", err);
35
+ }
36
+ }
37
+ const wantBuilds = await (0, prepare_1.loadCodebases)(fnConfig, options, firebaseConfig, runtimeConfig, undefined);
38
+ logger_1.logger.info(JSON.stringify(wantBuilds, null, 2));
39
+ return wantBuilds;
25
40
  });
@@ -4,12 +4,14 @@ 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");
7
+ const studio_1 = require("../management/studio");
7
8
  const logger_1 = require("../logger");
8
9
  const prompt_1 = require("../prompt");
9
10
  const requireAuth_1 = require("../requireAuth");
10
11
  const command_2 = require("../command");
11
12
  const utils = require("../utils");
12
13
  const error_1 = require("../error");
14
+ const env_1 = require("../env");
13
15
  function listAliases(options) {
14
16
  if (options.rc.hasProjects) {
15
17
  logger_1.logger.info("Project aliases for", clc.bold(options.projectRoot || "") + ":");
@@ -41,6 +43,9 @@ async function setNewActive(projectOrAlias, aliasOpt, rc, projectRoot) {
41
43
  catch (_a) {
42
44
  throw new error_1.FirebaseError("Invalid project selection, " + verifyMessage(projectOrAlias));
43
45
  }
46
+ if ((0, env_1.isFirebaseStudio)()) {
47
+ await (0, studio_1.updateStudioFirebaseProject)(resolvedProject);
48
+ }
44
49
  if (aliasOpt) {
45
50
  if (!project) {
46
51
  throw new error_1.FirebaseError(`Cannot create alias ${clc.bold(aliasOpt)}, ${verifyMessage(projectOrAlias)}`);
@@ -12,6 +12,7 @@ async function ensureApis(projectId) {
12
12
  exports.ensureApis = ensureApis;
13
13
  async function ensureSparkApis(projectId) {
14
14
  await (0, ensureApiEnabled_1.ensure)(projectId, api.cloudSQLAdminOrigin(), prefix);
15
+ await (0, ensureApiEnabled_1.ensure)(projectId, api.dataconnectOrigin(), prefix);
15
16
  }
16
17
  exports.ensureSparkApis = ensureSparkApis;
17
18
  async function ensureGIFApis(projectId) {
@@ -11,6 +11,7 @@ const runtimes = require("./runtimes");
11
11
  const supported = require("./runtimes/supported");
12
12
  const validate = require("./validate");
13
13
  const ensure = require("./ensure");
14
+ const experiments = require("../../experiments");
14
15
  const api_1 = require("../../api");
15
16
  const functionsDeployHelper_1 = require("./functionsDeployHelper");
16
17
  const utils_1 = require("../../utils");
@@ -51,13 +52,12 @@ async function prepare(context, options, payload) {
51
52
  ]);
52
53
  const firebaseConfig = await functionsConfig.getFirebaseConfig(options);
53
54
  context.firebaseConfig = firebaseConfig;
55
+ context.codebaseDeployEvents = {};
54
56
  let runtimeConfig = { firebase: firebaseConfig };
55
- if (checkAPIsEnabled[1]) {
56
- const config = await (0, prepareFunctionsUpload_1.getFunctionsConfig)(projectId);
57
- runtimeConfig = Object.assign(Object.assign({}, runtimeConfig), config);
58
- context.hasRuntimeConfig = Object.keys(config).length > 0;
57
+ const allowFunctionsConfig = experiments.isEnabled("dangerouslyAllowFunctionsConfig");
58
+ if (allowFunctionsConfig && checkAPIsEnabled[1]) {
59
+ runtimeConfig = Object.assign(Object.assign({}, runtimeConfig), (await (0, prepareFunctionsUpload_1.getFunctionsConfig)(projectId)));
59
60
  }
60
- context.codebaseDeployEvents = {};
61
61
  const wantBuilds = await loadCodebases(context.config, options, firebaseConfig, runtimeConfig, context.filters);
62
62
  if (Object.values(wantBuilds).some((b) => b.extensions)) {
63
63
  const extContext = {};
@@ -54,28 +54,28 @@
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "2.10.1",
58
- "expectedSize": 29336416,
59
- "expectedChecksum": "328ae0a354505430f1c162edf37da776",
60
- "expectedChecksumSHA256": "ede5dc299045151f228b2adc346f47ad5d3ee10c3c9d7528df5fc0f0c6d70808",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.10.1",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.10.1"
57
+ "version": "2.11.0",
58
+ "expectedSize": 29234016,
59
+ "expectedChecksum": "96da48708b8210f0d3d97099d777b322",
60
+ "expectedChecksumSHA256": "10fe334f2c4145e4c9d27bc442bdc92b1252164d3daac47bb7555da42b6d7050",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.11.0",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.0"
63
63
  },
64
64
  "win32": {
65
- "version": "2.10.1",
66
- "expectedSize": 29829632,
67
- "expectedChecksum": "82e82e35c1728a214db37967dba751e7",
68
- "expectedChecksumSHA256": "ba509e921c692ac60c1138b5b9c83a23b4e11feb339f08df5f1cfced44cffc91",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.10.1",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.10.1.exe"
65
+ "version": "2.11.0",
66
+ "expectedSize": 29719040,
67
+ "expectedChecksum": "df6b221af204a4a21163bcc73367f99e",
68
+ "expectedChecksumSHA256": "af17c0d873b2b8f1919652c1ad9fd4aed417cd82bc35bbc5cc4a302fc90f9a03",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.11.0",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.0.exe"
71
71
  },
72
72
  "linux": {
73
- "version": "2.10.1",
74
- "expectedSize": 29266104,
75
- "expectedChecksum": "d183c2335e16f8c568bdc9e782dcd240",
76
- "expectedChecksumSHA256": "338e0cce561d29993737b91abfe7fc12c7d82d014ef114348ccdafeb3b7be3ae",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.10.1",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.10.1"
73
+ "version": "2.11.0",
74
+ "expectedSize": 29159608,
75
+ "expectedChecksum": "38d6161f7e8f06ee89e5dc4094f1a9b6",
76
+ "expectedChecksumSHA256": "3391efac3570164141b5d7f3e3d48b6426ec9c8cf9eafeabee3fa31678120218",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.11.0",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.0"
79
79
  }
80
80
  }
81
81
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RecurrenceType = exports.PointInTimeRecoveryEnablement = exports.PointInTimeRecoveryEnablementOption = exports.DatabaseDeleteProtectionState = exports.DatabaseDeleteProtectionStateOption = exports.DatabaseType = exports.StateTtl = exports.State = exports.ArrayConfig = exports.Order = exports.QueryScope = exports.Mode = void 0;
3
+ exports.RecurrenceType = exports.DatabaseEdition = exports.PointInTimeRecoveryEnablement = exports.PointInTimeRecoveryEnablementOption = exports.DatabaseDeleteProtectionState = exports.DatabaseDeleteProtectionStateOption = exports.DatabaseType = exports.StateTtl = exports.State = exports.ArrayConfig = exports.Order = exports.QueryScope = exports.Mode = void 0;
4
4
  var Mode;
5
5
  (function (Mode) {
6
6
  Mode["ASCENDING"] = "ASCENDING";
@@ -58,6 +58,12 @@ var PointInTimeRecoveryEnablement;
58
58
  PointInTimeRecoveryEnablement["ENABLED"] = "POINT_IN_TIME_RECOVERY_ENABLED";
59
59
  PointInTimeRecoveryEnablement["DISABLED"] = "POINT_IN_TIME_RECOVERY_DISABLED";
60
60
  })(PointInTimeRecoveryEnablement = exports.PointInTimeRecoveryEnablement || (exports.PointInTimeRecoveryEnablement = {}));
61
+ var DatabaseEdition;
62
+ (function (DatabaseEdition) {
63
+ DatabaseEdition["DATABASE_EDITION_UNSPECIFIED"] = "DATABASE_EDITION_UNSPECIFIED";
64
+ DatabaseEdition["STANDARD"] = "STANDARD";
65
+ DatabaseEdition["ENTERPRISE"] = "ENTERPRISE";
66
+ })(DatabaseEdition = exports.DatabaseEdition || (exports.DatabaseEdition = {}));
61
67
  var RecurrenceType;
62
68
  (function (RecurrenceType) {
63
69
  RecurrenceType["DAILY"] = "DAILY";
@@ -41,7 +41,11 @@ class PrettyPrint {
41
41
  head: ["Field", "Value"],
42
42
  colWidths: [30, colValueWidth],
43
43
  });
44
- table.push(["Name", clc.yellow(database.name)], ["Create Time", clc.yellow(database.createTime)], ["Last Update Time", clc.yellow(database.updateTime)], ["Type", clc.yellow(database.type)], ["Location", clc.yellow(database.locationId)], ["Delete Protection State", clc.yellow(database.deleteProtectionState)], ["Point In Time Recovery", clc.yellow(database.pointInTimeRecoveryEnablement)], ["Earliest Version Time", clc.yellow(database.earliestVersionTime)], ["Version Retention Period", clc.yellow(database.versionRetentionPeriod)]);
44
+ const edition = !database.databaseEdition ||
45
+ database.databaseEdition === types.DatabaseEdition.DATABASE_EDITION_UNSPECIFIED
46
+ ? types.DatabaseEdition.STANDARD
47
+ : database.databaseEdition;
48
+ table.push(["Name", clc.yellow(database.name)], ["Create Time", clc.yellow(database.createTime)], ["Last Update Time", clc.yellow(database.updateTime)], ["Type", clc.yellow(database.type)], ["Edition", clc.yellow(edition)], ["Location", clc.yellow(database.locationId)], ["Delete Protection State", clc.yellow(database.deleteProtectionState)], ["Point In Time Recovery", clc.yellow(database.pointInTimeRecoveryEnablement)], ["Earliest Version Time", clc.yellow(database.earliestVersionTime)], ["Version Retention Period", clc.yellow(database.versionRetentionPeriod)]);
45
49
  if (database.cmekConfig) {
46
50
  table.push(["KMS Key Name", clc.yellow(database.cmekConfig.kmsKeyName)]);
47
51
  if (database.cmekConfig.activeKeyVersion) {
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.claude = void 0;
4
4
  const promptUpdater_1 = require("./promptUpdater");
5
- const CLAUDE_SETTINGS_PATH = ".claude/settings.local.json";
6
- const CLAUDE_PROMPT_PATH = "CLAUDE.local.md";
5
+ const MCP_CONFIG_PATH = ".mcp.json";
6
+ const CLAUDE_PROMPT_PATH = "CLAUDE.md";
7
7
  exports.claude = {
8
8
  name: "claude",
9
9
  displayName: "Claude Code",
@@ -11,9 +11,9 @@ exports.claude = {
11
11
  var _a;
12
12
  const files = [];
13
13
  let existingConfig = {};
14
- let settingsUpdated = false;
14
+ let mcpUpdated = false;
15
15
  try {
16
- const existingContent = config.readProjectFile(CLAUDE_SETTINGS_PATH);
16
+ const existingContent = config.readProjectFile(MCP_CONFIG_PATH);
17
17
  if (existingContent) {
18
18
  existingConfig = JSON.parse(existingContent);
19
19
  }
@@ -28,10 +28,10 @@ exports.claude = {
28
28
  command: "npx",
29
29
  args: ["-y", "firebase-tools", "experimental:mcp", "--dir", projectPath],
30
30
  };
31
- config.writeProjectFile(CLAUDE_SETTINGS_PATH, JSON.stringify(existingConfig, null, 2));
32
- settingsUpdated = true;
31
+ config.writeProjectFile(MCP_CONFIG_PATH, JSON.stringify(existingConfig, null, 2));
32
+ mcpUpdated = true;
33
33
  }
34
- files.push({ path: CLAUDE_SETTINGS_PATH, updated: settingsUpdated });
34
+ files.push({ path: MCP_CONFIG_PATH, updated: mcpUpdated });
35
35
  const { updated } = await (0, promptUpdater_1.updateFirebaseSection)(config, CLAUDE_PROMPT_PATH, enabledFeatures, {
36
36
  interactive: true,
37
37
  });
@@ -107,7 +107,7 @@ async function actuate(setup, config, options) {
107
107
  info.connectors = [defaultConnector];
108
108
  }
109
109
  await writeFiles(config, info, options);
110
- if (setup.projectId && info.shouldProvisionCSQL) {
110
+ if (setup.projectId && info.shouldProvisionCSQL && (await (0, cloudbilling_1.isBillingEnabled)(setup))) {
111
111
  await (0, provisionCloudSql_1.provisionCloudSql)({
112
112
  projectId: setup.projectId,
113
113
  location: info.locationId,
@@ -170,7 +170,7 @@ async function actuate(sdkInfo, config) {
170
170
  var _a, _b;
171
171
  const connectorYamlPath = `${sdkInfo.connectorInfo.directory}/connector.yaml`;
172
172
  (0, utils_1.logBullet)(`Writing your new SDK configuration to ${connectorYamlPath}`);
173
- await config.askWriteProjectFile(path.relative(config.projectDir, connectorYamlPath), sdkInfo.connectorYamlContents, false, true);
173
+ config.writeProjectFile(path.relative(config.projectDir, connectorYamlPath), sdkInfo.connectorYamlContents);
174
174
  const account = (0, auth_1.getGlobalDefaultAccount)();
175
175
  await dataconnectEmulator_1.DataConnectEmulator.generate({
176
176
  configDir: sdkInfo.connectorInfo.directory,
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.apptestingAcutate = exports.apptestingAskQuestions = exports.genkit = exports.apphosting = exports.dataconnectSdk = exports.dataconnectPostSetup = exports.dataconnectActuate = exports.dataconnectAskQuestions = exports.hostingGithub = exports.remoteconfig = exports.project = exports.extensions = exports.emulators = exports.storageActuate = exports.storageAskQuestions = exports.hosting = exports.functions = exports.firestoreActuate = exports.firestoreAskQuestions = exports.databaseActuate = exports.databaseAskQuestions = exports.account = void 0;
3
+ exports.aitools = exports.apptestingAcutate = exports.apptestingAskQuestions = exports.genkit = exports.apphosting = exports.dataconnectSdk = exports.dataconnectPostSetup = exports.dataconnectActuate = exports.dataconnectAskQuestions = exports.hostingGithub = exports.remoteconfig = exports.project = exports.extensions = exports.emulators = exports.storageActuate = exports.storageAskQuestions = exports.hosting = exports.functions = exports.firestoreActuate = exports.firestoreAskQuestions = exports.databaseActuate = exports.databaseAskQuestions = exports.account = void 0;
4
4
  var account_1 = require("./account");
5
5
  Object.defineProperty(exports, "account", { enumerable: true, get: function () { return account_1.doSetup; } });
6
6
  var database_1 = require("./database");
@@ -39,3 +39,5 @@ Object.defineProperty(exports, "genkit", { enumerable: true, get: function () {
39
39
  var apptesting_1 = require("./apptesting");
40
40
  Object.defineProperty(exports, "apptestingAskQuestions", { enumerable: true, get: function () { return apptesting_1.askQuestions; } });
41
41
  Object.defineProperty(exports, "apptestingAcutate", { enumerable: true, get: function () { return apptesting_1.actuate; } });
42
+ var aitools_1 = require("./aitools");
43
+ Object.defineProperty(exports, "aitools", { enumerable: true, get: function () { return aitools_1.doSetup; } });
package/lib/init/index.js CHANGED
@@ -6,6 +6,7 @@ const clc = require("colorette");
6
6
  const error_1 = require("../error");
7
7
  const logger_1 = require("../logger");
8
8
  const features = require("./features");
9
+ const track_1 = require("../track");
9
10
  const featuresList = [
10
11
  { name: "account", doSetup: features.account },
11
12
  {
@@ -44,12 +45,14 @@ const featuresList = [
44
45
  askQuestions: features.apptestingAskQuestions,
45
46
  actuate: features.apptestingAcutate,
46
47
  },
48
+ { name: "aitools", displayName: "AI Tools", doSetup: features.aitools },
47
49
  ];
48
50
  const featureMap = new Map(featuresList.map((feature) => [feature.name, feature]));
49
51
  async function init(setup, config, options) {
50
52
  var _a;
51
53
  const nextFeature = (_a = setup.features) === null || _a === void 0 ? void 0 : _a.shift();
52
54
  if (nextFeature) {
55
+ const start = process.uptime();
53
56
  const f = featureMap.get(nextFeature);
54
57
  if (!f) {
55
58
  const availableFeatures = Object.keys(features)
@@ -72,6 +75,8 @@ async function init(setup, config, options) {
72
75
  if (f.postSetup) {
73
76
  await f.postSetup(setup, config, options);
74
77
  }
78
+ const duration = Math.floor((process.uptime() - start) * 1000);
79
+ await (0, track_1.trackGA4)("product_init", { feature: nextFeature }, duration);
75
80
  return init(setup, config, options);
76
81
  }
77
82
  }
@@ -80,6 +85,7 @@ async function actuate(setup, config, options) {
80
85
  var _a;
81
86
  const nextFeature = (_a = setup.features) === null || _a === void 0 ? void 0 : _a.shift();
82
87
  if (nextFeature) {
88
+ const start = process.uptime();
83
89
  const f = lookupFeature(nextFeature);
84
90
  logger_1.logger.info(clc.bold(`\n${clc.white("===")} ${(0, lodash_1.capitalize)(nextFeature)} Setup Actuation`));
85
91
  if (f.doSetup) {
@@ -90,6 +96,8 @@ async function actuate(setup, config, options) {
90
96
  await f.actuate(setup, config, options);
91
97
  }
92
98
  }
99
+ const duration = Math.floor((process.uptime() - start) * 1000);
100
+ await (0, track_1.trackGA4)("product_init_mcp", { feature: nextFeature }, duration);
93
101
  return actuate(setup, config, options);
94
102
  }
95
103
  }
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.updateStudioFirebaseProject = exports.reconcileStudioFirebaseProject = void 0;
4
+ const apiv2_1 = require("../apiv2");
5
+ const prompt = require("../prompt");
6
+ const api = require("../api");
7
+ const logger_1 = require("../logger");
8
+ const utils = require("../utils");
9
+ const configstore_1 = require("../configstore");
10
+ const TIMEOUT_MILLIS = 30000;
11
+ const studioClient = new apiv2_1.Client({
12
+ urlPrefix: api.studioApiOrigin(),
13
+ apiVersion: "v1",
14
+ });
15
+ async function reconcileStudioFirebaseProject(options, activeProjectFromConfig) {
16
+ const studioWorkspace = await getStudioWorkspace();
17
+ if (!studioWorkspace) {
18
+ return activeProjectFromConfig;
19
+ }
20
+ if (!studioWorkspace.firebaseProjectId) {
21
+ if (activeProjectFromConfig) {
22
+ await updateStudioFirebaseProject(activeProjectFromConfig);
23
+ }
24
+ return activeProjectFromConfig;
25
+ }
26
+ if (!activeProjectFromConfig) {
27
+ await writeStudioProjectToConfigStore(options, studioWorkspace.firebaseProjectId);
28
+ return studioWorkspace.firebaseProjectId;
29
+ }
30
+ if (studioWorkspace.firebaseProjectId !== activeProjectFromConfig && !options.nonInteractive) {
31
+ const choices = [
32
+ {
33
+ name: `Set ${studioWorkspace.firebaseProjectId} from Firebase Studio as my active project in both places`,
34
+ value: false,
35
+ },
36
+ {
37
+ name: `Set ${activeProjectFromConfig} from Firebase CLI as my active project in both places`,
38
+ value: true,
39
+ },
40
+ ];
41
+ const useCliProject = await prompt.select({
42
+ message: "Found different active Firebase Projects in the Firebase CLI and your Firebase Studio Workspace. Which project would you like to set as your active project?",
43
+ choices,
44
+ });
45
+ if (useCliProject) {
46
+ await updateStudioFirebaseProject(activeProjectFromConfig);
47
+ return activeProjectFromConfig;
48
+ }
49
+ else {
50
+ await writeStudioProjectToConfigStore(options, studioWorkspace.firebaseProjectId);
51
+ return studioWorkspace.firebaseProjectId;
52
+ }
53
+ }
54
+ return studioWorkspace.firebaseProjectId;
55
+ }
56
+ exports.reconcileStudioFirebaseProject = reconcileStudioFirebaseProject;
57
+ async function getStudioWorkspace() {
58
+ const workspaceId = process.env.WORKSPACE_SLUG;
59
+ if (!workspaceId) {
60
+ logger_1.logger.error(`Failed to fetch Firebase Project from Studio Workspace because WORKSPACE_SLUG environment variable is empty`);
61
+ return undefined;
62
+ }
63
+ try {
64
+ const res = await studioClient.request({
65
+ method: "GET",
66
+ path: `/workspaces/${workspaceId}`,
67
+ timeout: TIMEOUT_MILLIS,
68
+ });
69
+ return res.body;
70
+ }
71
+ catch (err) {
72
+ let message = err.message;
73
+ if (err.original) {
74
+ message += ` (original: ${err.original.message})`;
75
+ }
76
+ logger_1.logger.error(`Failed to fetch Firebase Project from current Studio Workspace: ${message}`);
77
+ return undefined;
78
+ }
79
+ }
80
+ async function writeStudioProjectToConfigStore(options, studioProjectId) {
81
+ if (options.projectRoot) {
82
+ logger_1.logger.info(`Updating Firebase CLI active project to match Studio Workspace '${studioProjectId}'`);
83
+ utils.makeActiveProject(options.projectRoot, studioProjectId);
84
+ recordStudioProjectSyncTime();
85
+ }
86
+ }
87
+ async function updateStudioFirebaseProject(projectId) {
88
+ logger_1.logger.info(`Updating Studio Workspace active project to match Firebase CLI '${projectId}'`);
89
+ const workspaceId = process.env.WORKSPACE_SLUG;
90
+ if (!workspaceId) {
91
+ logger_1.logger.error(`Failed to update Firebase Project for Studio Workspace because WORKSPACE_SLUG environment variable is empty`);
92
+ return;
93
+ }
94
+ try {
95
+ await studioClient.request({
96
+ method: "PATCH",
97
+ path: `/workspaces/${workspaceId}`,
98
+ responseType: "json",
99
+ body: {
100
+ firebaseProjectId: projectId,
101
+ },
102
+ queryParams: {
103
+ updateMask: "workspace.firebaseProjectId",
104
+ },
105
+ timeout: TIMEOUT_MILLIS,
106
+ });
107
+ }
108
+ catch (err) {
109
+ let message = err.message;
110
+ if (err.original) {
111
+ message += ` (original: ${err.original.message})`;
112
+ }
113
+ logger_1.logger.warn(`Failed to update active Firebase Project for current Studio Workspace: ${message}`);
114
+ }
115
+ recordStudioProjectSyncTime();
116
+ }
117
+ exports.updateStudioFirebaseProject = updateStudioFirebaseProject;
118
+ function recordStudioProjectSyncTime() {
119
+ configstore_1.configstore.set("firebaseStudioProjectLastSynced", Date.now());
120
+ }
@@ -7,11 +7,16 @@ const util_1 = require("../../util");
7
7
  const fdcExperience_1 = require("../../../gemini/fdcExperience");
8
8
  exports.consult_assistant = (0, tool_1.tool)({
9
9
  name: "consult_assistant",
10
- description: "Send a question to an AI assistant specifically enhanced to answer Firebase questions.",
10
+ description: "Access an AI assistant specialized in all aspects of **Firebase**. " +
11
+ "Use this tool to get **detailed information**, **best practices**, **troubleshooting steps**, **code examples**, and **contextual help** regarding Firebase services, features, and project configuration. " +
12
+ "This includes questions about Firestore, Authentication, Cloud Functions, Hosting, Storage, Analytics, and more. " +
13
+ "It can also provide insights based on the **current Firebase project context**.",
11
14
  inputSchema: zod_1.z.object({
12
15
  prompt: zod_1.z
13
16
  .string()
14
- .describe("A description of what the user is trying to do or learn with Firebase."),
17
+ .describe("The specific question or task related to Firebase. " +
18
+ "Be precise and include relevant details, such as the Firebase service in question, the desired outcome, or any error messages encountered. " +
19
+ "Examples: 'What's the best way to deploy a React app to Firebase Hosting?', 'Explain Firebase Authentication with Google Sign-In.' , 'What are the current project settings for 'projectId'? "),
15
20
  }),
16
21
  annotations: {
17
22
  title: "Consult Firebase Assistant",
@@ -135,7 +135,7 @@ exports.init = (0, tool_1.tool)({
135
135
  isNewInstance: false,
136
136
  isNewDatabase: false,
137
137
  schemaGql: [],
138
- shouldProvisionCSQL: false,
138
+ shouldProvisionCSQL: true,
139
139
  };
140
140
  }
141
141
  const setup = {
@@ -61,7 +61,11 @@ async function refreshAuth() {
61
61
  exports.refreshAuth = refreshAuth;
62
62
  async function requireAuth(options, skipAutoAuth = false) {
63
63
  lastOptions = options;
64
- api.setScopes([scopes.CLOUD_PLATFORM, scopes.FIREBASE_PLATFORM]);
64
+ const requiredScopes = [scopes.CLOUD_PLATFORM];
65
+ if ((0, env_1.isFirebaseStudio)()) {
66
+ requiredScopes.push(scopes.USERINFO_EMAIL);
67
+ }
68
+ api.setScopes(requiredScopes);
65
69
  options.authScopes = api.getScopes();
66
70
  const tokens = options.tokens;
67
71
  const user = options.user;
package/lib/scopes.js CHANGED
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CLOUD_PUBSUB = exports.CLOUD_STORAGE = exports.CLOUD_PLATFORM = exports.FIREBASE_PLATFORM = exports.CLOUD_PROJECTS_READONLY = exports.EMAIL = exports.OPENID = void 0;
3
+ exports.CLOUD_PUBSUB = exports.CLOUD_STORAGE = exports.CLOUD_PLATFORM = exports.FIREBASE_PLATFORM = exports.CLOUD_PROJECTS_READONLY = exports.USERINFO_EMAIL = exports.EMAIL = exports.OPENID = void 0;
4
4
  exports.OPENID = "openid";
5
5
  exports.EMAIL = "email";
6
+ exports.USERINFO_EMAIL = "https://www.googleapis.com/auth/userinfo.email";
6
7
  exports.CLOUD_PROJECTS_READONLY = "https://www.googleapis.com/auth/cloudplatformprojects.readonly";
7
8
  exports.FIREBASE_PLATFORM = "https://www.googleapis.com/auth/firebase";
8
9
  exports.CLOUD_PLATFORM = "https://www.googleapis.com/auth/cloud-platform";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "14.11.2",
3
+ "version": "14.12.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -32,8 +32,7 @@ firebase deploy --except functions
32
32
 
33
33
  firebase use staging
34
34
  firebase use production
35
-
36
- ````
35
+ ```
37
36
  </example>
38
37
 
39
38
  ## Local Development