firebase-tools 14.15.1 → 14.15.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.
@@ -8,37 +8,41 @@ const projectUtils_1 = require("../projectUtils");
8
8
  const load_1 = require("../dataconnect/load");
9
9
  const logger_1 = require("../logger");
10
10
  const auth_1 = require("../auth");
11
+ const utils_1 = require("../utils");
11
12
  exports.command = new command_1.Command("dataconnect:sdk:generate")
12
13
  .description("generate typed SDKs for your Data Connect connectors")
13
14
  .option("--watch", "watch for changes to your connector GQL files and regenerate your SDKs when updates occur")
14
15
  .action(async (options) => {
15
16
  const projectId = (0, projectUtils_1.needProjectId)(options);
16
17
  const serviceInfos = await (0, load_1.loadAll)(projectId, options.config);
17
- for (const serviceInfo of serviceInfos) {
18
- const configDir = serviceInfo.sourceDirectory;
19
- const hasGeneratables = serviceInfo.connectorInfo.some((c) => {
20
- var _a, _b, _c, _d;
21
- return (((_a = c.connectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk) ||
22
- ((_b = c.connectorYaml.generate) === null || _b === void 0 ? void 0 : _b.kotlinSdk) ||
23
- ((_c = c.connectorYaml.generate) === null || _c === void 0 ? void 0 : _c.swiftSdk) ||
24
- ((_d = c.connectorYaml.generate) === null || _d === void 0 ? void 0 : _d.dartSdk));
18
+ const serviceInfosWithSDKs = serviceInfos.filter((serviceInfo) => serviceInfo.connectorInfo.some((c) => {
19
+ var _a, _b, _c, _d;
20
+ return (((_a = c.connectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk) ||
21
+ ((_b = c.connectorYaml.generate) === null || _b === void 0 ? void 0 : _b.kotlinSdk) ||
22
+ ((_c = c.connectorYaml.generate) === null || _c === void 0 ? void 0 : _c.swiftSdk) ||
23
+ ((_d = c.connectorYaml.generate) === null || _d === void 0 ? void 0 : _d.dartSdk));
24
+ }));
25
+ if (!serviceInfosWithSDKs.length) {
26
+ logger_1.logger.warn("No generated SDKs have been declared in connector.yaml files.");
27
+ logger_1.logger.warn(`Run ${clc.bold("firebase init dataconnect:sdk")} to configure a generated SDK.`);
28
+ logger_1.logger.warn(`See https://firebase.google.com/docs/data-connect/web-sdk for more details of how to configure generated SDKs.`);
29
+ return;
30
+ }
31
+ async function generateSDK(serviceInfo) {
32
+ return dataconnectEmulator_1.DataConnectEmulator.generate({
33
+ configDir: serviceInfo.sourceDirectory,
34
+ watch: options.watch,
35
+ account: (0, auth_1.getProjectDefaultAccount)(options.projectRoot),
25
36
  });
26
- if (!hasGeneratables) {
27
- logger_1.logger.warn("No generated SDKs have been declared in connector.yaml files.");
28
- logger_1.logger.warn(`Run ${clc.bold("firebase init dataconnect:sdk")} to configure a generated SDK.`);
29
- logger_1.logger.warn(`See https://firebase.google.com/docs/data-connect/web-sdk for more details of how to configure generated SDKs.`);
30
- return;
31
- }
32
- for (const conn of serviceInfo.connectorInfo) {
33
- const account = (0, auth_1.getProjectDefaultAccount)(options.projectRoot);
34
- const output = await dataconnectEmulator_1.DataConnectEmulator.generate({
35
- configDir,
36
- connectorId: conn.connectorYaml.connectorId,
37
- watch: options.watch,
38
- account,
39
- });
40
- logger_1.logger.info(output);
41
- logger_1.logger.info(`Generated SDKs for ${conn.connectorYaml.connectorId}`);
37
+ }
38
+ if (options.watch) {
39
+ await Promise.race(serviceInfosWithSDKs.map(generateSDK));
40
+ }
41
+ else {
42
+ for (const s of serviceInfosWithSDKs) {
43
+ await generateSDK(s);
42
44
  }
45
+ const services = serviceInfosWithSDKs.map((s) => s.dataConnectYaml.serviceId).join(", ");
46
+ (0, utils_1.logLabeledSuccess)("dataconnect", `Successfully Generated SDKs for services: ${clc.bold(services)}`);
43
47
  }
44
48
  });
@@ -3,11 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.applyPrefix = exports.toBackend = exports.envWithTypes = exports.resolveBackend = exports.AllIngressSettings = exports.AllVpcEgressSettings = exports.AllFunctionsPlatforms = exports.isBlockingTriggered = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isCallableTriggered = exports.isHttpsTriggered = exports.of = exports.empty = void 0;
4
4
  const backend = require("./backend");
5
5
  const proto = require("../../gcp/proto");
6
- const api = require("../../.../../api");
6
+ const api = require("../../api");
7
7
  const params = require("./params");
8
8
  const error_1 = require("../../error");
9
9
  const functional_1 = require("../../functional");
10
- const env_1 = require("../../functions/env");
11
10
  const cel_1 = require("./cel");
12
11
  function empty() {
13
12
  return {
@@ -56,17 +55,7 @@ exports.AllIngressSettings = [
56
55
  "ALLOW_INTERNAL_AND_GCLB",
57
56
  ];
58
57
  async function resolveBackend(opts) {
59
- let paramValues = {};
60
- paramValues = await params.resolveParams(opts.build.params, opts.firebaseConfig, envWithTypes(opts.build.params, opts.userEnvs), opts.nonInteractive, opts.isEmulator);
61
- const toWrite = {};
62
- for (const paramName of Object.keys(paramValues)) {
63
- const paramValue = paramValues[paramName];
64
- if (Object.prototype.hasOwnProperty.call(opts.userEnvs, paramName) || paramValue.internal) {
65
- continue;
66
- }
67
- toWrite[paramName] = paramValue.toString();
68
- }
69
- (0, env_1.writeUserEnvs)(toWrite, opts.userEnvOpt);
58
+ const paramValues = await params.resolveParams(opts.build.params, opts.firebaseConfig, envWithTypes(opts.build.params, opts.userEnvs), opts.nonInteractive, opts.isEmulator);
70
59
  return { backend: toBackend(opts.build, paramValues), envs: paramValues };
71
60
  }
72
61
  exports.resolveBackend = resolveBackend;
@@ -59,6 +59,7 @@ async function prepare(context, options, payload) {
59
59
  if (allowFunctionsConfig && checkAPIsEnabled[1]) {
60
60
  runtimeConfig = Object.assign(Object.assign({}, runtimeConfig), (await (0, prepareFunctionsUpload_1.getFunctionsConfig)(projectId)));
61
61
  }
62
+ context.hasRuntimeConfig = Object.keys(runtimeConfig).some((k) => k !== "firebase");
62
63
  const wantBuilds = await loadCodebases(context.config, options, firebaseConfig, runtimeConfig, context.filters);
63
64
  if (Object.values(wantBuilds).some((b) => b.extensions)) {
64
65
  const extContext = {};
@@ -83,11 +84,11 @@ async function prepare(context, options, payload) {
83
84
  const { backend: wantBackend, envs: resolvedEnvs } = await build.resolveBackend({
84
85
  build: wantBuild,
85
86
  firebaseConfig,
86
- userEnvOpt,
87
87
  userEnvs,
88
88
  nonInteractive: options.nonInteractive,
89
89
  isEmulator: false,
90
90
  });
91
+ functionsEnv.writeResolvedParams(resolvedEnvs, userEnvs, userEnvOpt);
91
92
  let hasEnvsFromParams = false;
92
93
  wantBackend.environmentVariables = envs;
93
94
  for (const envName of Object.keys(resolvedEnvs)) {
@@ -6,7 +6,7 @@ const fs = require("fs");
6
6
  const path = require("path");
7
7
  const yaml = require("yaml");
8
8
  const logger_1 = require("../../../../logger");
9
- const api = require("../../.../../../../api");
9
+ const api = require("../../../../api");
10
10
  const v1alpha1 = require("./v1alpha1");
11
11
  const error_1 = require("../../../../error");
12
12
  const TIMEOUT_OVERRIDE_ENV_VAR = "FUNCTIONS_DISCOVERY_TIMEOUT";
@@ -1512,7 +1512,16 @@ async function mfaSignInFinalize(state, reqBody) {
1512
1512
  (0, errors_1.assert)(sessionInfo, "MISSING_SESSION_INFO");
1513
1513
  const phoneNumber = verifyPhoneNumber(state, sessionInfo, code);
1514
1514
  let { user, signInProvider } = parsePendingCredential(state, reqBody.mfaPendingCredential);
1515
- const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((enrollment) => enrollment.unobfuscatedPhoneInfo === phoneNumber);
1515
+ const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((enrollment) => {
1516
+ if (enrollment.unobfuscatedPhoneInfo === phoneNumber) {
1517
+ return true;
1518
+ }
1519
+ if (!!enrollment.unobfuscatedPhoneInfo &&
1520
+ obfuscatePhoneNumber(enrollment.unobfuscatedPhoneInfo) === phoneNumber) {
1521
+ return true;
1522
+ }
1523
+ return false;
1524
+ });
1516
1525
  const { updates, extraClaims } = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, { signInMethod: signInProvider, signInSecondFactor: "phone" });
1517
1526
  user = state.updateUserByLocalId(user.localId, Object.assign(Object.assign({}, updates), { lastLoginAt: Date.now().toString() }));
1518
1527
  (0, errors_1.assert)(enrollment && enrollment.mfaEnrollmentId, "MFA_ENROLLMENT_NOT_FOUND");
@@ -15,7 +15,6 @@ const emulatorLogger_1 = require("./emulatorLogger");
15
15
  const types_2 = require("../dataconnect/types");
16
16
  const portUtils_1 = require("./portUtils");
17
17
  const registry_1 = require("./registry");
18
- const logger_1 = require("../logger");
19
18
  const load_1 = require("../dataconnect/load");
20
19
  const pgliteServer_1 = require("./dataconnect/pgliteServer");
21
20
  const controller_1 = require("./controller");
@@ -156,33 +155,37 @@ class DataConnectEmulator {
156
155
  }
157
156
  static async generate(args) {
158
157
  const commandInfo = await (0, downloadableEmulators_1.downloadIfNecessary)(types_1.Emulators.DATACONNECT);
159
- const cmd = [
160
- "--logtostderr",
161
- "-v=2",
162
- "generate",
163
- `--config_dir=${args.configDir}`,
164
- `--connector_id=${args.connectorId}`,
165
- ];
158
+ const cmd = ["--logtostderr", "-v=2", "sdk", "generate", `--config_dir=${args.configDir}`];
166
159
  if (args.watch) {
167
160
  cmd.push("--watch");
168
161
  }
169
162
  const env = await DataConnectEmulator.getEnv(args.account);
170
- const res = childProcess.spawnSync(commandInfo.binary, cmd, { encoding: "utf-8", env });
171
- if ((0, downloadableEmulators_1.isIncomaptibleArchError)(res.error)) {
172
- throw new error_1.FirebaseError(`Unknown system error when running the Data Connect toolkit. ` +
173
- `You may be able to fix this by installing Rosetta: ` +
174
- `softwareupdate --install-rosetta`);
175
- }
176
- logger_1.logger.info(res.stderr);
177
- if (res.error) {
178
- throw new error_1.FirebaseError(`Error starting up Data Connect generate: ${res.error.message}`, {
179
- original: res.error,
180
- });
181
- }
182
- if (res.status !== 0) {
183
- throw new error_1.FirebaseError(`Unable to generate your Data Connect SDKs (exit code ${res.status}): ${res.stderr}`);
184
- }
185
- return res.stdout;
163
+ return new Promise((resolve, reject) => {
164
+ try {
165
+ const proc = childProcess.spawn(commandInfo.binary, cmd, { stdio: "inherit", env });
166
+ proc.on("close", (code) => {
167
+ if (code === 0) {
168
+ resolve();
169
+ }
170
+ else {
171
+ reject(new Error(`Command failed with exit code ${code}`));
172
+ }
173
+ });
174
+ proc.on("error", (err) => {
175
+ reject(err);
176
+ });
177
+ }
178
+ catch (e) {
179
+ if ((0, downloadableEmulators_1.isIncomaptibleArchError)(e)) {
180
+ reject(new error_1.FirebaseError(`Unknown system error when running the Data Connect toolkit. ` +
181
+ `You may be able to fix this by installing Rosetta: ` +
182
+ `softwareupdate --install-rosetta`));
183
+ }
184
+ else {
185
+ reject(e);
186
+ }
187
+ }
188
+ });
186
189
  }
187
190
  static async build(args) {
188
191
  var _a;
@@ -341,11 +341,11 @@ class FunctionsEmulator {
341
341
  const resolution = await (0, build_1.resolveBackend)({
342
342
  build: discoveredBuild,
343
343
  firebaseConfig: JSON.parse(firebaseConfig),
344
- userEnvOpt,
345
344
  userEnvs,
346
345
  nonInteractive: false,
347
346
  isEmulator: true,
348
347
  });
348
+ functionsEnv.writeResolvedParams(resolution.envs, userEnvs, userEnvOpt);
349
349
  const discoveredBackend = resolution.backend;
350
350
  const endpoints = backend.allEndpoints(discoveredBackend);
351
351
  (0, functionsEmulatorShared_1.prepareEndpoints)(endpoints);
@@ -10,7 +10,6 @@ const types_1 = require("./types");
10
10
  const hubExport_1 = require("./hubExport");
11
11
  const registry_1 = require("./registry");
12
12
  const ExpressBasedEmulator_1 = require("./ExpressBasedEmulator");
13
- const vsCodeUtils_1 = require("../vsCodeUtils");
14
13
  const pkg = require("../../package.json");
15
14
  class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
16
15
  static readLocatorFile(projectId) {
@@ -20,9 +19,8 @@ class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
20
19
  }
21
20
  const data = fs.readFileSync(locatorPath, "utf8").toString();
22
21
  const locator = JSON.parse(data);
23
- if (!(0, vsCodeUtils_1.isVSCodeExtension)() && locator.version !== this.CLI_VERSION) {
24
- logger_1.logger.debug(`Found locator with mismatched version, ignoring: ${JSON.stringify(locator)}`);
25
- return undefined;
22
+ if (locator.version !== this.CLI_VERSION) {
23
+ logger_1.logger.debug(`Found emulator locator with different version: ${JSON.stringify(locator)}, CLI_VERSION: ${this.CLI_VERSION}`);
26
24
  }
27
25
  return locator;
28
26
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadFirebaseEnvs = exports.loadUserEnvs = exports.checkForDuplicateKeys = exports.writeUserEnvs = exports.hasUserEnvs = exports.parseStrict = exports.validateKey = exports.KeyValidationError = exports.parse = void 0;
3
+ exports.writeResolvedParams = exports.loadFirebaseEnvs = exports.loadUserEnvs = exports.checkForDuplicateKeys = exports.writeUserEnvs = exports.hasUserEnvs = exports.parseStrict = exports.validateKey = exports.KeyValidationError = exports.parse = void 0;
4
4
  const clc = require("colorette");
5
5
  const fs = require("fs");
6
6
  const path = require("path");
@@ -250,3 +250,14 @@ function loadFirebaseEnvs(firebaseConfig, projectId) {
250
250
  };
251
251
  }
252
252
  exports.loadFirebaseEnvs = loadFirebaseEnvs;
253
+ function writeResolvedParams(resolvedEnvs, userEnvs, userEnvOpt) {
254
+ const toWrite = {};
255
+ for (const paramName of Object.keys(resolvedEnvs)) {
256
+ const paramValue = resolvedEnvs[paramName];
257
+ if (!paramValue.internal && !Object.prototype.hasOwnProperty.call(userEnvs, paramName)) {
258
+ toWrite[paramName] = paramValue.toString();
259
+ }
260
+ }
261
+ writeUserEnvs(toWrite, userEnvOpt);
262
+ }
263
+ exports.writeResolvedParams = writeResolvedParams;
@@ -153,7 +153,6 @@ async function actuateWithInfo(setup, config, info) {
153
153
  const account = (0, auth_1.getGlobalDefaultAccount)();
154
154
  await dataconnectEmulator_1.DataConnectEmulator.generate({
155
155
  configDir: connectorInfo.directory,
156
- connectorId: connectorInfo.connectorYaml.connectorId,
157
156
  account,
158
157
  });
159
158
  (0, utils_1.logLabeledSuccess)("dataconnect", `Installed generated SDKs for ${clc.bold(apps.map((a) => (0, appFinder_1.appDescription)(a)).join(", "))}`);
package/lib/mcp/index.js CHANGED
@@ -154,7 +154,7 @@ class FirebaseMcpServer {
154
154
  const emulators = await hubClient.getEmulators();
155
155
  const emulatorInfo = emulators[emulatorType];
156
156
  if (!emulatorInfo) {
157
- throw Error("No Firestore Emulator found running. Make sure your project firebase.json file includes firestore and then rerun emulator using `firebase emulators:start` from your project directory.");
157
+ throw Error(`No ${emulatorType} Emulator found running. Make sure your project firebase.json file includes ${emulatorType} and then rerun emulator using \`firebase emulators:start\` from your project directory.`);
158
158
  }
159
159
  const host = emulatorInfo.host.includes(":") ? `[${emulatorInfo.host}]` : emulatorInfo.host;
160
160
  return `http://${host}:${emulatorInfo.port}`;
@@ -83,7 +83,7 @@ Follow the steps below taking note of any user instructions provided above.
83
83
  Create \`firebase.json\ with an "apphosting" configuration, setting backendId to the app's name in package.json: \`{"apphosting": {"backendId": "<backendId>"}}\
84
84
  4b. If the app does NOT require SSR, configure Firebase Hosting:
85
85
  Create \`firebase.json\ with a "hosting" configuration. Add a \`{"hosting": {"predeploy": "<build_script>"}}\` config to build before deploying.
86
- 5. Check if there is an active Firebase project for this environment (the \`firebase_get_environment\` tool may be helpful). If there is, proceed using that project. If there is not an active project, give the user two options: Use an existing Firebase project or Create a new one. Wait for their response before proceeding.
86
+ 5. Check if there is an active Firebase project for this environment (the \`firebase_get_environment\` tool may be helpful). If there is, provide the active project ID to the user and ask them if they want to proceed using that project. If there is not an active project, give the user two options: Provide an existing project ID or create a new project. Only use the list_projects tool on user request. Wait for their response before proceeding.
87
87
  5a. If the user chooses to use an existing Firebase project, the \`firebase_list_projects\` tool may be helpful. Set the selected project as the active project (the \`firebase_update_environment\` tool may be helpful).
88
88
  5b. If the user chooses to create a new project, use the \`firebase_create_project \` tool. Then set the new project as the active project (the \`firebase_update_environment\` tool may be helpful).
89
89
  6. If firebase.json contains an "apphosting" configuration, check if a backend exists matching the provided backendId (the \`apphosting_list_backends\` tool may be helpful).
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.execute = void 0;
4
+ const zod_1 = require("zod");
5
+ const tool_1 = require("../../tool");
6
+ const dataplane = require("../../../dataconnect/dataplaneClient");
7
+ const load_1 = require("../../../dataconnect/load");
8
+ const converter_1 = require("../../util/dataconnect/converter");
9
+ const emulator_1 = require("../../util/dataconnect/emulator");
10
+ exports.execute = (0, tool_1.tool)({
11
+ name: "execute",
12
+ description: "Executes a GraphQL operation against a Data Connect service or its emulator.",
13
+ inputSchema: zod_1.z.object({
14
+ query: zod_1.z.string().describe(`A Firebase Data Connect GraphQL query or mutation to execute.
15
+ You can use the \`dataconnect_generate_operation\` tool to generate a query.
16
+ Example Data Connect schema and example queries can be found in files ending in \`.graphql\` or \`.gql\`.
17
+ `),
18
+ service_id: zod_1.z.string().optional()
19
+ .describe(`Data Connect Service ID to dis-ambulate if there are multiple.
20
+ It's only necessary if there are multiple dataconnect sources in \`firebase.json\`.
21
+ You can find candidate service_id in \`dataconnect.yaml\`
22
+ `),
23
+ variables_json: zod_1.z
24
+ .string()
25
+ .optional()
26
+ .describe("GraphQL variables to pass into the query. MUST be a valid stringified JSON object."),
27
+ auth_token_json: zod_1.z
28
+ .string()
29
+ .optional()
30
+ .describe("Firebase Auth Token JWT to use in this query. MUST be a valid stringified JSON object." +
31
+ 'Importantly, when executing queries with `@auth(level: USER)` or `auth.uid`, a valid Firebase Auth Token JWT with "sub" field is required. ' +
32
+ '"auth.uid" expression in the query evaluates to the value of "sub" field in Firebase Auth token.'),
33
+ use_emulator: zod_1.z
34
+ .boolean()
35
+ .default(false)
36
+ .describe("If true, target the DataConnect emulator. Run `firebase emulators:start` to start it"),
37
+ }),
38
+ annotations: {
39
+ title: "Execute Firebase Data Connect Query",
40
+ },
41
+ _meta: {
42
+ requiresProject: true,
43
+ requiresAuth: true,
44
+ },
45
+ }, async ({ query, service_id, variables_json: unparsedVariables, use_emulator, auth_token_json: unparsedAuthToken, }, { projectId, config, host }) => {
46
+ const serviceInfo = await (0, load_1.pickService)(projectId, config, service_id || undefined);
47
+ let apiClient;
48
+ if (use_emulator) {
49
+ apiClient = await (0, emulator_1.getDataConnectEmulatorClient)(host);
50
+ }
51
+ else {
52
+ apiClient = dataplane.dataconnectDataplaneClient();
53
+ }
54
+ let executeGraphQL = dataplane.executeGraphQL;
55
+ if (query.startsWith("query")) {
56
+ executeGraphQL = dataplane.executeGraphQLRead;
57
+ }
58
+ const response = await executeGraphQL(apiClient, serviceInfo.serviceName, {
59
+ name: "",
60
+ query,
61
+ variables: (0, converter_1.parseVariables)(unparsedVariables),
62
+ extensions: {
63
+ impersonate: unparsedAuthToken
64
+ ? {
65
+ authClaims: (0, converter_1.parseVariables)(unparsedAuthToken),
66
+ }
67
+ : undefined,
68
+ },
69
+ });
70
+ return (0, converter_1.graphqlResponseToToolResponse)(response.body);
71
+ });
@@ -3,23 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.dataconnectTools = void 0;
4
4
  const generate_operation_1 = require("./generate_operation");
5
5
  const generate_schema_1 = require("./generate_schema");
6
- const list_services_1 = require("./list_services");
7
- const get_schema_1 = require("./get_schema");
8
- const get_connector_1 = require("./get_connector");
9
- const execute_graphql_1 = require("./execute_graphql");
10
- const execute_graphql_read_1 = require("./execute_graphql_read");
11
- const execute_query_1 = require("./execute_query");
12
- const execute_mutation_1 = require("./execute_mutation");
6
+ const info_1 = require("./info");
13
7
  const compile_1 = require("./compile");
8
+ const execute_1 = require("./execute");
14
9
  exports.dataconnectTools = [
15
10
  compile_1.compile,
16
- list_services_1.list_services,
17
11
  generate_schema_1.generate_schema,
18
12
  generate_operation_1.generate_operation,
19
- get_schema_1.get_schema,
20
- get_connector_1.get_connectors,
21
- execute_graphql_1.execute_graphql,
22
- execute_graphql_read_1.execute_graphql_read,
23
- execute_mutation_1.execute_mutation,
24
- execute_query_1.execute_query,
13
+ info_1.info,
14
+ execute_1.execute,
25
15
  ];
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.info = void 0;
4
+ const zod_1 = require("zod");
5
+ const path = require("path");
6
+ const tool_1 = require("../../tool");
7
+ const util_1 = require("../../util");
8
+ const client = require("../../../dataconnect/client");
9
+ const load_1 = require("../../../dataconnect/load");
10
+ const js_yaml_1 = require("js-yaml");
11
+ const logger_1 = require("../../../logger");
12
+ exports.info = (0, tool_1.tool)({
13
+ name: "info",
14
+ description: "Get information about the Firebase Data Connect local and deployed resources.",
15
+ inputSchema: zod_1.z.object({}),
16
+ annotations: {
17
+ title: "Get information about Firebase Data Connect",
18
+ readOnlyHint: true,
19
+ },
20
+ _meta: {
21
+ requiresProject: false,
22
+ requiresAuth: false,
23
+ },
24
+ }, async (_, { projectId, config }) => {
25
+ const localServiceInfos = await (0, load_1.loadAll)(projectId, config);
26
+ const serviceInfos = new Map();
27
+ for (const l of localServiceInfos) {
28
+ serviceInfos.set(`locations/${l.dataConnectYaml.location}/services/${l.dataConnectYaml.serviceId}`, { local: l });
29
+ }
30
+ if (projectId) {
31
+ try {
32
+ const [services, schemas, connectors] = await Promise.all([
33
+ client.listAllServices(projectId),
34
+ client.listSchemas(`projects/${projectId}/locations/-/services/-`),
35
+ client.listConnectors(`projects/${projectId}/locations/-/services/-`),
36
+ ]);
37
+ console.log(services, schemas, connectors);
38
+ for (const s of services) {
39
+ const k = s.name.split("/").slice(2, 6).join("/");
40
+ const st = serviceInfos.get(k) || {};
41
+ st.deployed = st.deployed || {};
42
+ st.deployed.service = s;
43
+ serviceInfos.set(k, st);
44
+ }
45
+ for (const s of schemas) {
46
+ const k = s.name.split("/").slice(2, 6).join("/");
47
+ const st = serviceInfos.get(k) || {};
48
+ st.deployed = st.deployed || {};
49
+ st.deployed.schemas = st.deployed.schemas || [];
50
+ st.deployed.schemas.push(s);
51
+ serviceInfos.set(k, st);
52
+ }
53
+ for (const s of connectors) {
54
+ const k = s.name.split("/").slice(2, 6).join("/");
55
+ const st = serviceInfos.get(k) || {};
56
+ st.deployed = st.deployed || {};
57
+ st.deployed.connectors = st.deployed.connectors || [];
58
+ st.deployed.connectors.push(s);
59
+ serviceInfos.set(k, st);
60
+ }
61
+ }
62
+ catch (e) {
63
+ logger_1.logger.debug("cannot fetch dataconnect resources in the backend", e);
64
+ }
65
+ }
66
+ const localServices = Array.from(serviceInfos.values()).filter((s) => s.local);
67
+ const remoteOnlyServices = Array.from(serviceInfos.values()).filter((s) => !s.local);
68
+ const output = [];
69
+ function includeDeployedServiceInfo(deployed) {
70
+ var _a, _b;
71
+ if ((_a = deployed.schemas) === null || _a === void 0 ? void 0 : _a.length) {
72
+ output.push(`### Schemas`);
73
+ for (const s of deployed.schemas) {
74
+ clearCCFEFields(s);
75
+ output.push((0, js_yaml_1.dump)(s));
76
+ }
77
+ }
78
+ if ((_b = deployed.connectors) === null || _b === void 0 ? void 0 : _b.length) {
79
+ output.push(`### Connectors`);
80
+ for (const c of deployed.connectors) {
81
+ clearCCFEFields(c);
82
+ output.push((0, js_yaml_1.dump)(c));
83
+ }
84
+ }
85
+ }
86
+ if (localServices.length) {
87
+ output.push(`# Local Data Connect Sources`);
88
+ for (const s of localServices) {
89
+ const local = s.local;
90
+ output.push((0, js_yaml_1.dump)(local.dataConnectYaml));
91
+ const schemaDir = path.join(local.sourceDirectory, local.dataConnectYaml.schema.source);
92
+ output.push(`You can find all of schema sources under ${schemaDir}/`);
93
+ if (s.deployed) {
94
+ output.push(`It's already deployed in the backend:\n`);
95
+ includeDeployedServiceInfo(s.deployed);
96
+ }
97
+ }
98
+ }
99
+ if (remoteOnlyServices.length) {
100
+ output.push(`# Data Connect Services in project ${projectId}`);
101
+ for (const s of remoteOnlyServices) {
102
+ if (s.deployed) {
103
+ includeDeployedServiceInfo(s.deployed);
104
+ }
105
+ }
106
+ }
107
+ output.push(`\n# What's next?`);
108
+ if (!localServices.length) {
109
+ output.push(`- There is no local Data Connect service in the local workspace. Consider use the \`firebase_init\` MCP tool to setup one.`);
110
+ }
111
+ output.push(`- You can use the \`dataconnect_compile\` tool to compile all local Data Connect schemas and query sources.`);
112
+ output.push(`- You run \`firebase deploy\` in command line to deploy the Data Connect schemas, connector and perform SQL migrations.`);
113
+ return (0, util_1.toContent)(output.join("\n"));
114
+ });
115
+ function clearCCFEFields(r) {
116
+ const fieldsToClear = ["updateTime", "uid", "etag"];
117
+ for (const k of fieldsToClear) {
118
+ delete r[k];
119
+ }
120
+ }
package/lib/track.js CHANGED
@@ -8,6 +8,19 @@ const configstore_1 = require("./configstore");
8
8
  const logger_1 = require("./logger");
9
9
  const env_1 = require("./env");
10
10
  const pkg = require("../package.json");
11
+ function detectAIAgent() {
12
+ if (process.env.CODEX_SANDBOX)
13
+ return "codex_cli";
14
+ if (process.env.CLAUDECODE)
15
+ return "claude_code";
16
+ if (process.env.GEMINI_CLI)
17
+ return "gemini_cli";
18
+ if (process.env.CURSOR_AGENT)
19
+ return "cursor";
20
+ if (process.env.OPENCODE)
21
+ return "open_code";
22
+ return "unknown";
23
+ }
11
24
  exports.GA4_PROPERTIES = {
12
25
  cli: {
13
26
  measurementId: process.env.FIREBASE_CLI_GA4_MEASUREMENT_ID || "G-PDN0QWHQJR",
@@ -45,6 +58,9 @@ const GA4_USER_PROPS = {
45
58
  is_firebase_studio: {
46
59
  value: (0, env_1.isFirebaseStudio)().toString(),
47
60
  },
61
+ ai_agent: {
62
+ value: detectAIAgent(),
63
+ },
48
64
  };
49
65
  async function trackGA4(eventName, params, duration = 1) {
50
66
  const session = cliSession();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "14.15.1",
3
+ "version": "14.15.2",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -1,48 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.execute_graphql = void 0;
4
- const zod_1 = require("zod");
5
- const tool_1 = require("../../tool");
6
- const dataplane = require("../../../dataconnect/dataplaneClient");
7
- const load_1 = require("../../../dataconnect/load");
8
- const converter_1 = require("../../util/dataconnect/converter");
9
- const emulator_1 = require("../../util/dataconnect/emulator");
10
- exports.execute_graphql = (0, tool_1.tool)({
11
- name: "execute_graphql",
12
- description: "Executes an arbitrary GraphQL against a Data Connect service or its emulator.",
13
- inputSchema: zod_1.z.object({
14
- query: zod_1.z.string().describe("A GraphQL query or mutation to execute against the service"),
15
- service_id: zod_1.z
16
- .string()
17
- .nullable()
18
- .describe("The Firebase Data Connect service ID to look for. If there is only one service defined in firebase.json, this can be omitted and that will be used."),
19
- variables: zod_1.z
20
- .string()
21
- .optional()
22
- .describe("A stringified JSON object containing variables for the operation. MUST be valid JSON."),
23
- use_emulator: zod_1.z.boolean().default(false).describe("Target the DataConnect emulator if true."),
24
- }),
25
- annotations: {
26
- title: "Execute GraphQL Operation",
27
- readOnlyHint: false,
28
- },
29
- _meta: {
30
- requiresProject: true,
31
- requiresAuth: true,
32
- },
33
- }, async ({ query, service_id, variables: unparsedVariables, use_emulator }, { projectId, config, host }) => {
34
- const serviceInfo = await (0, load_1.pickService)(projectId, config, service_id || undefined);
35
- let apiClient;
36
- if (use_emulator) {
37
- apiClient = await (0, emulator_1.getDataConnectEmulatorClient)(host);
38
- }
39
- else {
40
- apiClient = dataplane.dataconnectDataplaneClient();
41
- }
42
- const response = await dataplane.executeGraphQL(apiClient, serviceInfo.serviceName, {
43
- name: "",
44
- query,
45
- variables: (0, converter_1.parseVariables)(unparsedVariables),
46
- });
47
- return (0, converter_1.graphqlResponseToToolResponse)(response.body);
48
- });
@@ -1,48 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.execute_graphql_read = void 0;
4
- const zod_1 = require("zod");
5
- const tool_1 = require("../../tool");
6
- const dataplane = require("../../../dataconnect/dataplaneClient");
7
- const load_1 = require("../../../dataconnect/load");
8
- const converter_1 = require("../../util/dataconnect/converter");
9
- const emulator_1 = require("../../util/dataconnect/emulator");
10
- exports.execute_graphql_read = (0, tool_1.tool)({
11
- name: "execute_graphql_read",
12
- description: "Executes an arbitrary GraphQL query against a Data Connect service or its emulator. Cannot write data.",
13
- inputSchema: zod_1.z.object({
14
- query: zod_1.z.string().describe("A GraphQL query to execute against the service"),
15
- service_id: zod_1.z
16
- .string()
17
- .nullable()
18
- .describe("The Firebase Data Connect service ID to look for. If there is only one service defined in firebase.json, this can be omitted and that will be used."),
19
- variables: zod_1.z
20
- .string()
21
- .optional()
22
- .describe("A stringified JSON object containing variables for the operation. MUST be valid JSON."),
23
- use_emulator: zod_1.z.boolean().default(false).describe("Target the DataConnect emulator if true."),
24
- }),
25
- annotations: {
26
- title: "Execute Data Connect GraphQL Query",
27
- readOnlyHint: true,
28
- },
29
- _meta: {
30
- requiresProject: true,
31
- requiresAuth: true,
32
- },
33
- }, async ({ query, service_id, variables: unparsedVariables, use_emulator }, { projectId, config, host }) => {
34
- const serviceInfo = await (0, load_1.pickService)(projectId, config, service_id || undefined);
35
- let apiClient;
36
- if (use_emulator) {
37
- apiClient = await (0, emulator_1.getDataConnectEmulatorClient)(host);
38
- }
39
- else {
40
- apiClient = dataplane.dataconnectDataplaneClient();
41
- }
42
- const response = await dataplane.executeGraphQLRead(apiClient, serviceInfo.serviceName, {
43
- name: "",
44
- query,
45
- variables: (0, converter_1.parseVariables)(unparsedVariables),
46
- });
47
- return (0, converter_1.graphqlResponseToToolResponse)(response.body);
48
- });
@@ -1,62 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.execute_mutation = void 0;
4
- const zod_1 = require("zod");
5
- const tool_1 = require("../../tool");
6
- const util_1 = require("../../util");
7
- const dataplane = require("../../../dataconnect/dataplaneClient");
8
- const load_1 = require("../../../dataconnect/load");
9
- const converter_1 = require("../../util/dataconnect/converter");
10
- const emulator_1 = require("../../util/dataconnect/emulator");
11
- exports.execute_mutation = (0, tool_1.tool)({
12
- name: "execute_mutation",
13
- description: "Executes a deployed Data Connect mutation against a service or its emulator. Can read and write data.",
14
- inputSchema: zod_1.z.object({
15
- operationName: zod_1.z.string().describe("The name of the deployed operation you want to execute"),
16
- service_id: zod_1.z
17
- .string()
18
- .nullable()
19
- .describe("The Firebase Data Connect service ID to look for. If there is only one service defined in firebase.json, this can be omitted and that will be used."),
20
- connector_id: zod_1.z
21
- .string()
22
- .nullable()
23
- .describe("The Firebase Data Connect connector ID to look for. If there is only one connector defined in dataconnect.yaml, this can be omitted and that will be used."),
24
- variables: zod_1.z
25
- .string()
26
- .optional()
27
- .describe("A stringified JSON object containing the variables needed to execute the operation. The value MUST be able to be parsed as a JSON object."),
28
- use_emulator: zod_1.z.boolean().default(false).describe("Target the DataConnect emulator if true."),
29
- }),
30
- annotations: {
31
- title: "Execute Data Connect Connector Mutation",
32
- readOnlyHint: false,
33
- },
34
- _meta: {
35
- requiresProject: true,
36
- requiresAuth: true,
37
- },
38
- }, async ({ operationName, service_id, connector_id, variables: unparsedVariables, use_emulator }, { projectId, config, host }) => {
39
- const serviceInfo = await (0, load_1.pickService)(projectId, config, service_id || undefined);
40
- let apiClient;
41
- if (!connector_id) {
42
- if (serviceInfo.connectorInfo.length === 0) {
43
- return (0, util_1.mcpError)(`Service ${serviceInfo.serviceName} has no connectors`, "NO_CONNECTORS_FOUND");
44
- }
45
- if (serviceInfo.connectorInfo.length > 1) {
46
- return (0, util_1.mcpError)(`Service ${serviceInfo.serviceName} has more than one connector. Please use the connector_id argument to specify which connector this operation is part of.`, "MULTIPLE_CONNECTORS_FOUND");
47
- }
48
- connector_id = serviceInfo.connectorInfo[0].connectorYaml.connectorId;
49
- }
50
- const connectorPath = `${serviceInfo.serviceName}/connectors/${connector_id}`;
51
- if (use_emulator) {
52
- apiClient = await (0, emulator_1.getDataConnectEmulatorClient)(host);
53
- }
54
- else {
55
- apiClient = dataplane.dataconnectDataplaneClient();
56
- }
57
- const response = await dataplane.executeGraphQLMutation(apiClient, connectorPath, {
58
- operationName,
59
- variables: (0, converter_1.parseVariables)(unparsedVariables),
60
- });
61
- return (0, converter_1.graphqlResponseToToolResponse)(response.body);
62
- });
@@ -1,62 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.execute_query = void 0;
4
- const zod_1 = require("zod");
5
- const tool_1 = require("../../tool");
6
- const util_1 = require("../../util");
7
- const dataplane = require("../../../dataconnect/dataplaneClient");
8
- const load_1 = require("../../../dataconnect/load");
9
- const converter_1 = require("../../util/dataconnect/converter");
10
- const emulator_1 = require("../../util/dataconnect/emulator");
11
- exports.execute_query = (0, tool_1.tool)({
12
- name: "execute_query",
13
- description: "Executes a deployed Data Connect query against a service or its emulator. Cannot write any data.",
14
- inputSchema: zod_1.z.object({
15
- operationName: zod_1.z.string().describe("The name of the deployed operation you want to execute"),
16
- service_id: zod_1.z
17
- .string()
18
- .nullable()
19
- .describe("The Firebase Data Connect service ID to look for. If there is only one service defined in firebase.json, this can be omitted and that will be used."),
20
- connector_id: zod_1.z
21
- .string()
22
- .nullable()
23
- .describe("The Firebase Data Connect connector ID to look for. If there is only one connector defined in dataconnect.yaml, this can be omitted and that will be used."),
24
- variables: zod_1.z
25
- .string()
26
- .optional()
27
- .describe("A stringified JSON object containing the variables needed to execute the operation. The value MUST be able to be parsed as a JSON object."),
28
- use_emulator: zod_1.z.boolean().default(false).describe("Target the DataConnect emulator if true."),
29
- }),
30
- annotations: {
31
- title: "Executes a deployed Data Connect query.",
32
- readOnlyHint: true,
33
- },
34
- _meta: {
35
- requiresProject: true,
36
- requiresAuth: true,
37
- },
38
- }, async ({ operationName, service_id, connector_id, variables: unparsedVariables, use_emulator }, { projectId, config, host }) => {
39
- const serviceInfo = await (0, load_1.pickService)(projectId, config, service_id || undefined);
40
- let apiClient;
41
- if (!connector_id) {
42
- if (serviceInfo.connectorInfo.length === 0) {
43
- return (0, util_1.mcpError)(`Service ${serviceInfo.serviceName} has no connectors`, "NO_CONNECTORS_FOUND");
44
- }
45
- if (serviceInfo.connectorInfo.length > 1) {
46
- return (0, util_1.mcpError)(`Service ${serviceInfo.serviceName} has more than one connector. Please use the connector_id argument to specify which connector this operation is part of.`, "MULTIPLE_CONNECTORS_FOUND");
47
- }
48
- connector_id = serviceInfo.connectorInfo[0].connectorYaml.connectorId;
49
- }
50
- const connectorPath = `${serviceInfo.serviceName}/connectors/${connector_id}`;
51
- if (use_emulator) {
52
- apiClient = await (0, emulator_1.getDataConnectEmulatorClient)(host);
53
- }
54
- else {
55
- apiClient = dataplane.dataconnectDataplaneClient();
56
- }
57
- const response = await dataplane.executeGraphQLQuery(apiClient, connectorPath, {
58
- operationName,
59
- variables: (0, converter_1.parseVariables)(unparsedVariables),
60
- });
61
- return (0, converter_1.graphqlResponseToToolResponse)(response.body);
62
- });
@@ -1,31 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.get_connectors = void 0;
4
- const zod_1 = require("zod");
5
- const tool_1 = require("../../tool");
6
- const util_1 = require("../../util");
7
- const client = require("../../../dataconnect/client");
8
- const load_1 = require("../../../dataconnect/load");
9
- const converter_1 = require("../../util/dataconnect/converter");
10
- exports.get_connectors = (0, tool_1.tool)({
11
- name: "get_connectors",
12
- description: "Get the Firebase Data Connect Connectors in the project, which includes the pre-defined GraphQL queries accessible to client SDKs.",
13
- inputSchema: zod_1.z.object({
14
- service_id: zod_1.z
15
- .string()
16
- .nullable()
17
- .describe("The Firebase Data Connect service ID to look for. If there is only one service defined in firebase.json, this can be omitted and that will be used."),
18
- }),
19
- annotations: {
20
- title: "Get Data Connect Connectors",
21
- readOnlyHint: true,
22
- },
23
- _meta: {
24
- requiresProject: true,
25
- requiresAuth: true,
26
- },
27
- }, async ({ service_id }, { projectId, config }) => {
28
- const serviceInfo = await (0, load_1.pickService)(projectId, config, service_id || undefined);
29
- const connectors = await client.listConnectors(serviceInfo.serviceName, ["*"]);
30
- return (0, util_1.toContent)(connectors.map(converter_1.connectorToText).join("\n\n"));
31
- });
@@ -1,31 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.get_schema = void 0;
4
- const zod_1 = require("zod");
5
- const tool_1 = require("../../tool");
6
- const util_1 = require("../../util");
7
- const client = require("../../../dataconnect/client");
8
- const load_1 = require("../../../dataconnect/load");
9
- const converter_1 = require("../../util/dataconnect/converter");
10
- exports.get_schema = (0, tool_1.tool)({
11
- name: "get_schema",
12
- description: "Retrieve information about the Firebase Data Connect Schema in the project, including Cloud SQL data sources and the GraphQL Schema describing the data model.",
13
- inputSchema: zod_1.z.object({
14
- service_id: zod_1.z
15
- .string()
16
- .nullable()
17
- .describe("The Firebase Data Connect service ID to look for. If there is only one service defined in firebase.json, this can be omitted and that will be used."),
18
- }),
19
- annotations: {
20
- title: "Get Data Connect Schemas",
21
- readOnlyHint: true,
22
- },
23
- _meta: {
24
- requiresProject: true,
25
- requiresAuth: true,
26
- },
27
- }, async ({ service_id }, { projectId, config }) => {
28
- const serviceInfo = await (0, load_1.pickService)(projectId, config, service_id || undefined);
29
- const schemas = await client.listSchemas(serviceInfo.serviceName, ["*"]);
30
- return (0, util_1.toContent)(schemas === null || schemas === void 0 ? void 0 : schemas.map(converter_1.schemaToText).join("\n\n"));
31
- });
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.list_services = void 0;
4
- const zod_1 = require("zod");
5
- const tool_1 = require("../../tool");
6
- const util_1 = require("../../util");
7
- const client = require("../../../dataconnect/client");
8
- exports.list_services = (0, tool_1.tool)({
9
- name: "list_services",
10
- description: "List the Firebase Data Connect services available in the current project.",
11
- inputSchema: zod_1.z.object({}),
12
- annotations: {
13
- title: "List Data Connect Services",
14
- readOnlyHint: true,
15
- },
16
- _meta: {
17
- requiresProject: true,
18
- requiresAuth: true,
19
- },
20
- }, async (_, { projectId }) => {
21
- const services = await client.listAllServices(projectId);
22
- return (0, util_1.toContent)(services, { format: "yaml" });
23
- });