firebase-tools 14.2.1 → 14.2.2

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 (44) hide show
  1. package/lib/bin/cli.js +131 -0
  2. package/lib/bin/firebase.js +7 -123
  3. package/lib/bin/mcp.js +38 -0
  4. package/lib/commands/index.js +3 -0
  5. package/lib/commands/mcp.js +11 -0
  6. package/lib/emulator/commandUtils.js +8 -6
  7. package/lib/emulator/downloadableEmulators.js +9 -9
  8. package/lib/emulator/env.js +19 -20
  9. package/lib/emulator/functionsEmulator.js +0 -1
  10. package/lib/emulator/hub.js +2 -1
  11. package/lib/emulator/ui.js +0 -2
  12. package/lib/experiments.js +5 -0
  13. package/lib/frameworks/index.js +4 -1
  14. package/lib/gcp/auth.js +56 -1
  15. package/lib/gcp/firestore.js +12 -1
  16. package/lib/hosting/api.js +4 -0
  17. package/lib/init/features/dataconnect/sdk.js +9 -7
  18. package/lib/logger.js +11 -2
  19. package/lib/mcp/errors.js +15 -0
  20. package/lib/mcp/index.js +109 -0
  21. package/lib/mcp/tool.js +11 -0
  22. package/lib/mcp/tools/auth/disable_auth_user.js +30 -0
  23. package/lib/mcp/tools/auth/get_auth_user.js +29 -0
  24. package/lib/mcp/tools/auth/index.js +7 -0
  25. package/lib/mcp/tools/auth/set_auth_claims.js +34 -0
  26. package/lib/mcp/tools/core/get_firebase_directory.js +20 -0
  27. package/lib/mcp/tools/core/index.js +6 -0
  28. package/lib/mcp/tools/core/set_firebase_directory.js +33 -0
  29. package/lib/mcp/tools/dataconnect/index.js +5 -0
  30. package/lib/mcp/tools/dataconnect/list_dataconnect_services.js +23 -0
  31. package/lib/mcp/tools/firestore/converter.js +57 -0
  32. package/lib/mcp/tools/firestore/get_documents.js +48 -0
  33. package/lib/mcp/tools/firestore/get_firestore_rules.js +26 -0
  34. package/lib/mcp/tools/firestore/index.js +7 -0
  35. package/lib/mcp/tools/firestore/list_collections.js +30 -0
  36. package/lib/mcp/tools/index.js +14 -0
  37. package/lib/mcp/tools/project/get_project.js +22 -0
  38. package/lib/mcp/tools/project/get_sdk_config.js +38 -0
  39. package/lib/mcp/tools/project/index.js +7 -0
  40. package/lib/mcp/tools/project/list_apps.js +29 -0
  41. package/lib/mcp/types.js +4 -0
  42. package/lib/mcp/util.js +52 -0
  43. package/package.json +5 -2
  44. package/templates/init/dataconnect/connector.yaml +3 -0
package/lib/bin/cli.js ADDED
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cli = void 0;
4
+ const updateNotifierPkg = require("update-notifier-cjs");
5
+ const clc = require("colorette");
6
+ const marked_terminal_1 = require("marked-terminal");
7
+ const marked_1 = require("marked");
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
+ const fs = require("node:fs");
13
+ const configstore_1 = require("../configstore");
14
+ const errorOut_1 = require("../errorOut");
15
+ const handlePreviewToggles_1 = require("../handlePreviewToggles");
16
+ const logger_1 = require("../logger");
17
+ const client = require("..");
18
+ const fsutils = require("../fsutils");
19
+ const utils = require("../utils");
20
+ const winston = require("winston");
21
+ const experiments_1 = require("../experiments");
22
+ const fetchMOTD_1 = require("../fetchMOTD");
23
+ function cli(pkg) {
24
+ const updateNotifier = updateNotifierPkg({ pkg });
25
+ const args = process.argv.slice(2);
26
+ 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
+ if (!process.env.DEBUG && args.includes("--debug")) {
49
+ process.env.DEBUG = "true";
50
+ }
51
+ 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
+ }));
60
+ logger_1.logger.debug("-".repeat(70));
61
+ logger_1.logger.debug("Command: ", process.argv.join(" "));
62
+ logger_1.logger.debug("CLI Version: ", pkg.version);
63
+ logger_1.logger.debug("Platform: ", process.platform);
64
+ logger_1.logger.debug("Node Version: ", process.version);
65
+ logger_1.logger.debug("Time: ", new Date().toString());
66
+ if (utils.envOverrides.length) {
67
+ logger_1.logger.debug("Env Overrides:", utils.envOverrides.join(", "));
68
+ }
69
+ logger_1.logger.debug("-".repeat(70));
70
+ logger_1.logger.debug();
71
+ (0, experiments_1.enableExperimentsFromCliEnvVariable)();
72
+ (0, fetchMOTD_1.fetchMOTD)();
73
+ process.on("exit", (code) => {
74
+ code = process.exitCode || code;
75
+ if (!process.env.DEBUG && code < 2 && fsutils.fileExistsSync(logFilename)) {
76
+ fs.unlinkSync(logFilename);
77
+ }
78
+ if (code > 0 && process.stdout.isTTY) {
79
+ const lastError = configstore_1.configstore.get("lastError") || 0;
80
+ const timestamp = Date.now();
81
+ if (lastError > timestamp - 120000) {
82
+ let help;
83
+ if (code === 1 && cmd) {
84
+ help = "Having trouble? Try " + clc.bold("firebase [command] --help");
85
+ }
86
+ else {
87
+ help = "Having trouble? Try again or contact support with contents of firebase-debug.log";
88
+ }
89
+ if (cmd) {
90
+ console.log();
91
+ console.log(help);
92
+ }
93
+ }
94
+ configstore_1.configstore.set("lastError", timestamp);
95
+ }
96
+ else {
97
+ configstore_1.configstore.delete("lastError");
98
+ }
99
+ try {
100
+ const installMethod = !process.env.FIREPIT_VERSION ? "npm" : "automatic script";
101
+ const updateCommand = !process.env.FIREPIT_VERSION
102
+ ? "npm install -g firebase-tools"
103
+ : "curl -sL https://firebase.tools | upgrade=true bash";
104
+ const updateMessage = `Update available ${clc.gray("{currentVersion}")} → ${clc.green("{latestVersion}")}\n` +
105
+ `To update to the latest version using ${installMethod}, run\n${clc.cyan(updateCommand)}\n` +
106
+ `For other CLI management options, visit the ${(0, marked_1.marked)("[CLI documentation](https://firebase.google.com/docs/cli#update-cli)")}`;
107
+ updateNotifier.notify({ defer: false, isGlobal: true, message: updateMessage });
108
+ }
109
+ catch (err) {
110
+ logger_1.logger.debug("Error when notifying about new CLI updates:");
111
+ if (err instanceof Error) {
112
+ logger_1.logger.debug(err);
113
+ }
114
+ else {
115
+ logger_1.logger.debug(`${err}`);
116
+ }
117
+ }
118
+ });
119
+ process.on("uncaughtException", (err) => {
120
+ (0, errorOut_1.errorOut)(err);
121
+ });
122
+ if (!(0, handlePreviewToggles_1.handlePreviewToggles)(args)) {
123
+ if (!args.length) {
124
+ client.cli.help();
125
+ }
126
+ else {
127
+ cmd = client.cli.parse(process.argv);
128
+ }
129
+ }
130
+ }
131
+ exports.cli = cli;
@@ -2,134 +2,18 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const semver = require("semver");
5
+ const experiments_1 = require("../experiments");
5
6
  const pkg = require("../../package.json");
6
7
  const nodeVersion = process.version;
7
8
  if (!semver.satisfies(nodeVersion, pkg.engines.node)) {
8
9
  console.error(`Firebase CLI v${pkg.version} is incompatible with Node.js ${nodeVersion} Please upgrade Node.js to version ${pkg.engines.node}`);
9
10
  process.exit(1);
10
11
  }
11
- const updateNotifierPkg = require("update-notifier-cjs");
12
- const clc = require("colorette");
13
- const marked_terminal_1 = require("marked-terminal");
14
- const updateNotifier = updateNotifierPkg({ pkg });
15
- const marked_1 = require("marked");
16
- marked_1.marked.use((0, marked_terminal_1.markedTerminal)());
17
- const node_path_1 = require("node:path");
18
- const triple_beam_1 = require("triple-beam");
19
- const node_util_1 = require("node:util");
20
- const fs = require("node:fs");
21
- const configstore_1 = require("../configstore");
22
- const errorOut_1 = require("../errorOut");
23
- const handlePreviewToggles_1 = require("../handlePreviewToggles");
24
- const logger_1 = require("../logger");
25
- const client = require("..");
26
- const fsutils = require("../fsutils");
27
- const utils = require("../utils");
28
- const winston = require("winston");
29
- const args = process.argv.slice(2);
30
- let cmd;
31
- function findAvailableLogFile() {
32
- const candidates = ["firebase-debug.log"];
33
- for (let i = 1; i < 10; i++) {
34
- candidates.push(`firebase-debug.${i}.log`);
35
- }
36
- for (const c of candidates) {
37
- const logFilename = (0, node_path_1.join)(process.cwd(), c);
38
- try {
39
- const fd = fs.openSync(logFilename, "r+");
40
- fs.closeSync(fd);
41
- return logFilename;
42
- }
43
- catch (e) {
44
- if (e.code === "ENOENT") {
45
- return logFilename;
46
- }
47
- }
48
- }
49
- throw new Error("Unable to obtain permissions for firebase-debug.log");
50
- }
51
- const logFilename = findAvailableLogFile();
52
- if (!process.env.DEBUG && args.includes("--debug")) {
53
- process.env.DEBUG = "true";
12
+ if ((0, experiments_1.isEnabled)("mcp") && process.argv[2] === "experimental:mcp") {
13
+ const { mcp } = require("./mcp");
14
+ mcp();
54
15
  }
55
- process.env.IS_FIREBASE_CLI = "true";
56
- logger_1.logger.add(new winston.transports.File({
57
- level: "debug",
58
- filename: logFilename,
59
- format: winston.format.printf((info) => {
60
- const segments = [info.message, ...(info[triple_beam_1.SPLAT] || [])].map(utils.tryStringify);
61
- return `[${info.level}] ${(0, node_util_1.stripVTControlCharacters)(segments.join(" "))}`;
62
- }),
63
- }));
64
- logger_1.logger.debug("-".repeat(70));
65
- logger_1.logger.debug("Command: ", process.argv.join(" "));
66
- logger_1.logger.debug("CLI Version: ", pkg.version);
67
- logger_1.logger.debug("Platform: ", process.platform);
68
- logger_1.logger.debug("Node Version: ", process.version);
69
- logger_1.logger.debug("Time: ", new Date().toString());
70
- if (utils.envOverrides.length) {
71
- logger_1.logger.debug("Env Overrides:", utils.envOverrides.join(", "));
72
- }
73
- logger_1.logger.debug("-".repeat(70));
74
- logger_1.logger.debug();
75
- const experiments_1 = require("../experiments");
76
- const fetchMOTD_1 = require("../fetchMOTD");
77
- (0, experiments_1.enableExperimentsFromCliEnvVariable)();
78
- (0, fetchMOTD_1.fetchMOTD)();
79
- process.on("exit", (code) => {
80
- code = process.exitCode || code;
81
- if (!process.env.DEBUG && code < 2 && fsutils.fileExistsSync(logFilename)) {
82
- fs.unlinkSync(logFilename);
83
- }
84
- if (code > 0 && process.stdout.isTTY) {
85
- const lastError = configstore_1.configstore.get("lastError") || 0;
86
- const timestamp = Date.now();
87
- if (lastError > timestamp - 120000) {
88
- let help;
89
- if (code === 1 && cmd) {
90
- help = "Having trouble? Try " + clc.bold("firebase [command] --help");
91
- }
92
- else {
93
- help = "Having trouble? Try again or contact support with contents of firebase-debug.log";
94
- }
95
- if (cmd) {
96
- console.log();
97
- console.log(help);
98
- }
99
- }
100
- configstore_1.configstore.set("lastError", timestamp);
101
- }
102
- else {
103
- configstore_1.configstore.delete("lastError");
104
- }
105
- try {
106
- const installMethod = !process.env.FIREPIT_VERSION ? "npm" : "automatic script";
107
- const updateCommand = !process.env.FIREPIT_VERSION
108
- ? "npm install -g firebase-tools"
109
- : "curl -sL https://firebase.tools | upgrade=true bash";
110
- const updateMessage = `Update available ${clc.gray("{currentVersion}")} → ${clc.green("{latestVersion}")}\n` +
111
- `To update to the latest version using ${installMethod}, run\n${clc.cyan(updateCommand)}\n` +
112
- `For other CLI management options, visit the ${(0, marked_1.marked)("[CLI documentation](https://firebase.google.com/docs/cli#update-cli)")}`;
113
- updateNotifier.notify({ defer: false, isGlobal: true, message: updateMessage });
114
- }
115
- catch (err) {
116
- logger_1.logger.debug("Error when notifying about new CLI updates:");
117
- if (err instanceof Error) {
118
- logger_1.logger.debug(err);
119
- }
120
- else {
121
- logger_1.logger.debug(`${err}`);
122
- }
123
- }
124
- });
125
- process.on("uncaughtException", (err) => {
126
- (0, errorOut_1.errorOut)(err);
127
- });
128
- if (!(0, handlePreviewToggles_1.handlePreviewToggles)(args)) {
129
- if (!args.length) {
130
- client.cli.help();
131
- }
132
- else {
133
- cmd = client.cli.parse(process.argv);
134
- }
16
+ else {
17
+ const { cli } = require("./cli");
18
+ cli(pkg);
135
19
  }
package/lib/bin/mcp.js ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.mcp = void 0;
5
+ const logger_1 = require("../logger");
6
+ (0, logger_1.silenceStdout)();
7
+ const index_1 = require("../mcp/index");
8
+ const util_1 = require("util");
9
+ const types_1 = require("../mcp/types");
10
+ const STARTUP_MESSAGE = `
11
+ 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
+ {
14
+ "mcpServers": {
15
+ "firebase": {
16
+ "command": "firebase",
17
+ "args": ["experimental:mcp", "--dir", "/path/to/firebase/project"]
18
+ }
19
+ }
20
+ }
21
+ `;
22
+ async function mcp() {
23
+ const { values } = (0, util_1.parseArgs)({
24
+ options: {
25
+ only: { type: "string", default: "" },
26
+ dir: { type: "string" },
27
+ },
28
+ allowPositionals: true,
29
+ });
30
+ const activeFeatures = (values.only || "")
31
+ .split(",")
32
+ .filter((f) => types_1.SERVER_FEATURES.includes(f));
33
+ const server = new index_1.FirebaseMcpServer({ activeFeatures, projectRoot: values.dir });
34
+ await server.start();
35
+ if (process.stdin.isTTY)
36
+ process.stderr.write(STARTUP_MESSAGE);
37
+ }
38
+ exports.mcp = mcp;
@@ -190,6 +190,9 @@ function load(client) {
190
190
  client.login.list = loadCommand("login-list");
191
191
  client.login.use = loadCommand("login-use");
192
192
  client.logout = loadCommand("logout");
193
+ if (experiments.isEnabled("mcp")) {
194
+ client.mcp = loadCommand("mcp");
195
+ }
193
196
  client.open = loadCommand("open");
194
197
  client.projects = {};
195
198
  client.projects.addfirebase = loadCommand("projects-addfirebase");
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const requireAuth_1 = require("../requireAuth");
6
+ exports.command = new command_1.Command("experimental:mcp")
7
+ .description("Start an MCP server with access to the current working directory's project and resources.")
8
+ .before(requireAuth_1.requireAuth)
9
+ .action(() => {
10
+ throw new Error("MCP logic is implemented elsewhere, this should never be reached.");
11
+ });
@@ -101,12 +101,14 @@ async function beforeEmulatorCommand(options) {
101
101
  const canStartWithoutConfig = options.only &&
102
102
  !controller.shouldStart(optionsWithConfig, types_1.Emulators.FUNCTIONS) &&
103
103
  !controller.shouldStart(optionsWithConfig, types_1.Emulators.HOSTING);
104
- try {
105
- await (0, requireAuth_1.requireAuth)(options);
106
- }
107
- catch (e) {
108
- logger_1.logger.debug(e);
109
- utils.logLabeledWarning("emulators", `You are not currently authenticated so some features may not work correctly. Please run ${clc.bold("firebase login")} to authenticate the CLI.`);
104
+ if (!constants_1.Constants.isDemoProject(options.project)) {
105
+ try {
106
+ await (0, requireAuth_1.requireAuth)(options);
107
+ }
108
+ catch (e) {
109
+ logger_1.logger.debug(e);
110
+ utils.logLabeledWarning("emulators", `You are not currently authenticated so some features may not work correctly. Please run ${clc.bold("firebase login")} to authenticate the CLI.`);
111
+ }
110
112
  }
111
113
  if (canStartWithoutConfig && !options.config) {
112
114
  utils.logWarning("Could not find config (firebase.json) so using defaults.");
@@ -48,20 +48,20 @@ const EMULATOR_UPDATE_DETAILS = {
48
48
  },
49
49
  dataconnect: process.platform === "darwin"
50
50
  ? {
51
- version: "2.3.1",
52
- expectedSize: 27271936,
53
- expectedChecksum: "5ffcfe584c5af818477a00eb6450dd66",
51
+ version: "2.4.0",
52
+ expectedSize: 27316992,
53
+ expectedChecksum: "615fb819b38dc61a36f18f0f8017379d",
54
54
  }
55
55
  : process.platform === "win32"
56
56
  ? {
57
- version: "2.3.1",
58
- expectedSize: 27729408,
59
- expectedChecksum: "b71f1686724e8e051810b3a2e14c6d08",
57
+ version: "2.4.0",
58
+ expectedSize: 27774464,
59
+ expectedChecksum: "1968300587f73fff246aa5c2cf9008fe",
60
60
  }
61
61
  : {
62
- version: "2.3.1",
63
- expectedSize: 27185304,
64
- expectedChecksum: "54c40731f4563073404591ef1ffcdc20",
62
+ version: "2.4.0",
63
+ expectedSize: 27230360,
64
+ expectedChecksum: "88407f13d5647aab496c3810666867a4",
65
65
  },
66
66
  };
67
67
  exports.DownloadDetails = {
@@ -6,7 +6,6 @@ const types_1 = require("./types");
6
6
  const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
7
7
  const defaultCredentials_1 = require("../defaultCredentials");
8
8
  function setEnvVarsForEmulators(env, emulators) {
9
- maybeUsePortForwarding(emulators);
10
9
  for (const emu of emulators) {
11
10
  const host = (0, functionsEmulatorShared_1.formatHost)(emu);
12
11
  switch (emu.name) {
@@ -60,29 +59,29 @@ async function getCredentialsEnvironment(account, logger, logLabel, silent = fal
60
59
  return credentialEnv;
61
60
  }
62
61
  exports.getCredentialsEnvironment = getCredentialsEnvironment;
63
- function maybeUsePortForwarding(emulatorInfos) {
62
+ function maybeUsePortForwarding(i) {
64
63
  var _a;
65
64
  const portForwardingHost = process.env.WEB_HOST;
66
65
  if (portForwardingHost) {
67
- for (const info of emulatorInfos) {
68
- if (info.host.includes(portForwardingHost)) {
69
- continue;
70
- }
71
- const url = `${info.port}-${portForwardingHost}`;
72
- info.host = url;
73
- info.listen = (_a = info.listen) === null || _a === void 0 ? void 0 : _a.map((l) => {
74
- l.address = url;
75
- l.port = 443;
76
- return l;
77
- });
78
- info.port = 443;
79
- const fsInfo = info;
80
- if (fsInfo.webSocketPort) {
81
- fsInfo.webSocketHost = `${fsInfo.webSocketPort}-${portForwardingHost}`;
82
- fsInfo.webSocketPort = 443;
83
- }
66
+ const info = Object.assign({}, i);
67
+ if (info.host.includes(portForwardingHost)) {
68
+ return info;
84
69
  }
70
+ const url = `${info.port}-${portForwardingHost}`;
71
+ info.host = url;
72
+ info.listen = (_a = info.listen) === null || _a === void 0 ? void 0 : _a.map((l) => {
73
+ l.address = url;
74
+ l.port = 443;
75
+ return l;
76
+ });
77
+ info.port = 443;
78
+ const fsInfo = info;
79
+ if (fsInfo.webSocketPort) {
80
+ fsInfo.webSocketHost = `${fsInfo.webSocketPort}-${portForwardingHost}`;
81
+ fsInfo.webSocketPort = 443;
82
+ }
83
+ return info;
85
84
  }
86
- return emulatorInfos;
85
+ return i;
87
86
  }
88
87
  exports.maybeUsePortForwarding = maybeUsePortForwarding;
@@ -912,7 +912,6 @@ class FunctionsEmulator {
912
912
  if (this.args.remoteEmulators) {
913
913
  emulatorInfos = emulatorInfos.concat(Object.values(this.args.remoteEmulators));
914
914
  }
915
- (0, env_1.maybeUsePortForwarding)(emulatorInfos);
916
915
  (0, env_1.setEnvVarsForEmulators)(envs, emulatorInfos);
917
916
  if (this.debugMode) {
918
917
  envs["FUNCTION_DEBUG_MODE"] = "true";
@@ -11,6 +11,7 @@ const hubExport_1 = require("./hubExport");
11
11
  const registry_1 = require("./registry");
12
12
  const ExpressBasedEmulator_1 = require("./ExpressBasedEmulator");
13
13
  const vsCodeUtils_1 = require("../vsCodeUtils");
14
+ const env_1 = require("./env");
14
15
  const pkg = require("../../package.json");
15
16
  class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
16
17
  static readLocatorFile(projectId) {
@@ -44,7 +45,7 @@ class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
44
45
  getRunningEmulatorsMapping() {
45
46
  const emulators = {};
46
47
  for (const info of registry_1.EmulatorRegistry.listRunningWithInfo()) {
47
- emulators[info.name] = Object.assign({ listen: this.args.listenForEmulator[info.name] }, info);
48
+ emulators[info.name] = (0, env_1.maybeUsePortForwarding)(Object.assign({ listen: this.args.listenForEmulator[info.name] }, info));
48
49
  }
49
50
  return emulators;
50
51
  }
@@ -12,7 +12,6 @@ const constants_1 = require("./constants");
12
12
  const track_1 = require("../track");
13
13
  const ExpressBasedEmulator_1 = require("./ExpressBasedEmulator");
14
14
  const experiments_1 = require("../experiments");
15
- const env_1 = require("./env");
16
15
  class EmulatorUI extends ExpressBasedEmulator_1.ExpressBasedEmulator {
17
16
  constructor(args) {
18
17
  super({
@@ -37,7 +36,6 @@ class EmulatorUI extends ExpressBasedEmulator_1.ExpressBasedEmulator {
37
36
  const webDir = path.join(downloadDetails.unzipDir, "client");
38
37
  app.get("/api/config", this.jsonHandler(() => {
39
38
  const emulatorInfos = hub.getRunningEmulatorsMapping();
40
- (0, env_1.maybeUsePortForwarding)(Object.values(emulatorInfos));
41
39
  const json = Object.assign({ projectId, experiments: enabledExperiments !== null && enabledExperiments !== void 0 ? enabledExperiments : [], analytics: emulatorGaSession }, emulatorInfos);
42
40
  return Promise.resolve(json);
43
41
  }));
@@ -100,6 +100,11 @@ exports.ALL_EXPERIMENTS = experiments({
100
100
  default: false,
101
101
  public: true,
102
102
  },
103
+ mcp: {
104
+ shortDescription: "Adds experimental `firebase mcp` command for running a Firebase MCP server.",
105
+ default: false,
106
+ public: false,
107
+ },
103
108
  });
104
109
  function isValidExperiment(name) {
105
110
  return Object.keys(exports.ALL_EXPERIMENTS).includes(name);
@@ -82,8 +82,12 @@ async function prepareFrameworks(purpose, targetNames, context, options, emulato
82
82
  var _a, _b, _c, _d, _e, _f;
83
83
  var _g, _h, _j, _k, _l;
84
84
  const project = (0, projectUtils_1.needProjectId)(context || options);
85
+ const isDemoProject = constants_1.Constants.isDemoProject(project);
85
86
  const projectRoot = (0, projectPath_1.resolveProjectPath)(options, ".");
86
87
  const account = (0, auth_1.getProjectDefaultAccount)(projectRoot);
88
+ if (isDemoProject) {
89
+ options.site = project;
90
+ }
87
91
  if (!options.site) {
88
92
  try {
89
93
  await (0, requireHostingSite_1.requireHostingSite)(options);
@@ -150,7 +154,6 @@ async function prepareFrameworks(purpose, targetNames, context, options, emulato
150
154
  });
151
155
  let firebaseConfig = null;
152
156
  if (usesFirebaseJsSdk) {
153
- const isDemoProject = constants_1.Constants.isDemoProject(project);
154
157
  const sites = isDemoProject ? (0, api_1.listDemoSites)(project) : await (0, api_1.listSites)(project);
155
158
  const selectedSite = sites.find((it) => it.name && it.name.split("/").pop() === site);
156
159
  if (selectedSite) {
package/lib/gcp/auth.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.updateAuthDomains = exports.getAuthDomains = void 0;
3
+ exports.setCustomClaim = exports.disableUser = exports.findUser = exports.updateAuthDomains = exports.getAuthDomains = void 0;
4
4
  const apiv2_1 = require("../apiv2");
5
5
  const api_1 = require("../api");
6
6
  const apiClient = new apiv2_1.Client({ urlPrefix: (0, api_1.identityOrigin)(), auth: true });
@@ -17,3 +17,58 @@ async function updateAuthDomains(project, authDomains) {
17
17
  return res.body.authorizedDomains;
18
18
  }
19
19
  exports.updateAuthDomains = updateAuthDomains;
20
+ async function findUser(project, email, phone, uid) {
21
+ const expression = {
22
+ email,
23
+ phoneNumber: phone,
24
+ userId: uid,
25
+ };
26
+ const res = await apiClient.post(`/v1/projects/${project}/accounts:query`, {
27
+ expression: [expression],
28
+ limit: "1",
29
+ });
30
+ if (res.body.userInfo.length === 0) {
31
+ throw new Error("No users found");
32
+ }
33
+ const modifiedUserInfo = res.body.userInfo.map((ui) => {
34
+ ui.uid = ui.localId;
35
+ delete ui.localId;
36
+ return ui;
37
+ });
38
+ return modifiedUserInfo[0];
39
+ }
40
+ exports.findUser = findUser;
41
+ async function disableUser(project, uid, disabled) {
42
+ const res = await apiClient.post("/v1/accounts:update", {
43
+ disableUser: disabled,
44
+ targetProjectId: project,
45
+ localId: uid,
46
+ });
47
+ return res.status === 200;
48
+ }
49
+ exports.disableUser = disableUser;
50
+ async function setCustomClaim(project, uid, claim, options) {
51
+ let user = await findUser(project, undefined, undefined, uid);
52
+ if (user.uid !== uid) {
53
+ throw new Error(`Could not find ${uid} in the auth db, please check the uid again.`);
54
+ }
55
+ let reqClaim = JSON.stringify(claim);
56
+ if (options === null || options === void 0 ? void 0 : options.merge) {
57
+ let attributeJson = new Map();
58
+ if (user.customAttributes !== undefined && user.customAttributes !== "") {
59
+ attributeJson = JSON.parse(user.customAttributes);
60
+ }
61
+ reqClaim = JSON.stringify(Object.assign(Object.assign({}, attributeJson), claim));
62
+ }
63
+ const res = await apiClient.post("/v1/accounts:update", {
64
+ customAttributes: reqClaim,
65
+ targetProjectId: project,
66
+ localId: uid,
67
+ });
68
+ if (res.status !== 200) {
69
+ throw new Error("something went wrong in the request");
70
+ }
71
+ user = await findUser(project, undefined, undefined, uid);
72
+ return user;
73
+ }
74
+ exports.setCustomClaim = setCustomClaim;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getBackupSchedule = exports.listBackupSchedules = exports.getBackup = exports.listBackups = exports.deleteBackupSchedule = exports.deleteBackup = exports.updateBackupSchedule = exports.createBackupSchedule = exports.deleteDocuments = exports.deleteDocument = exports.listCollectionIds = exports.getDatabase = exports.DayOfWeek = void 0;
3
+ exports.getBackupSchedule = exports.listBackupSchedules = exports.getBackup = exports.listBackups = exports.deleteBackupSchedule = exports.deleteBackup = exports.updateBackupSchedule = exports.createBackupSchedule = exports.deleteDocuments = exports.deleteDocument = exports.getDocuments = exports.listCollectionIds = exports.getDatabase = exports.DayOfWeek = void 0;
4
4
  const api_1 = require("../api");
5
5
  const apiv2_1 = require("../apiv2");
6
6
  const logger_1 = require("../logger");
@@ -50,6 +50,17 @@ function listCollectionIds(project, allowEmulator = false) {
50
50
  });
51
51
  }
52
52
  exports.listCollectionIds = listCollectionIds;
53
+ async function getDocuments(project, paths, allowEmulator) {
54
+ const apiClient = allowEmulator ? emuOrProdClient : prodOnlyClient;
55
+ const basePath = `projects/${project}/databases/(default)/documents`;
56
+ const url = `${basePath}:batchGet`;
57
+ const fullPaths = paths.map((p) => `${basePath}/${p}`);
58
+ const res = await apiClient.post(url, { documents: fullPaths });
59
+ const out = { documents: [], missing: [] };
60
+ res.body.map((r) => (r.missing ? out.missing.push(r.missing) : out.documents.push(r.found)));
61
+ return out;
62
+ }
63
+ exports.getDocuments = getDocuments;
53
64
  async function deleteDocument(doc, allowEmulator = false) {
54
65
  const apiClient = allowEmulator ? emuOrProdClient : prodOnlyClient;
55
66
  return apiClient.delete(doc.name);
@@ -9,6 +9,7 @@ const expireUtils_1 = require("../hosting/expireUtils");
9
9
  const auth_1 = require("../gcp/auth");
10
10
  const proto = require("../gcp/proto");
11
11
  const utils_1 = require("../utils");
12
+ const constants_1 = require("../emulator/constants");
12
13
  const ONE_WEEK_MS = 604800000;
13
14
  var ReleaseType;
14
15
  (function (ReleaseType) {
@@ -298,6 +299,9 @@ async function getAllSiteDomains(projectId, siteId) {
298
299
  }
299
300
  exports.getAllSiteDomains = getAllSiteDomains;
300
301
  async function getDeploymentDomain(projectId, siteId, hostingChannel) {
302
+ if (constants_1.Constants.isDemoProject(projectId)) {
303
+ return null;
304
+ }
301
305
  if (hostingChannel) {
302
306
  const channel = await getChannel(projectId, siteId, hostingChannel);
303
307
  return channel && (0, utils_1.getHostnameFromUrl)(channel === null || channel === void 0 ? void 0 : channel.url);