firebase-tools 14.8.0 → 14.10.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 +2 -4
- package/lib/apphosting/backend.js +18 -18
- package/lib/commands/login.js +2 -1
- package/lib/dataconnect/ensureApis.js +6 -3
- package/lib/deploy/apphosting/prepare.js +1 -1
- package/lib/deploy/functions/runtimes/discovery/index.js +3 -2
- package/lib/deploy/functions/runtimes/supported/types.js +1 -1
- package/lib/emulator/controller.js +2 -1
- package/lib/emulator/dataconnect/pgliteServer.js +66 -4
- package/lib/emulator/dataconnectEmulator.js +0 -1
- package/lib/emulator/downloadableEmulatorInfo.json +18 -18
- package/lib/extensions/paramHelper.js +2 -1
- package/lib/gcp/cloudfunctions.js +15 -1
- package/lib/gcp/cloudfunctionsv2.js +10 -5
- package/lib/gemini/fdcExperience.js +12 -2
- package/lib/init/features/apphosting.js +1 -11
- package/lib/init/features/dataconnect/index.js +23 -2
- package/lib/management/projects.js +38 -4
- package/lib/mcp/errors.js +6 -1
- package/lib/mcp/index.js +17 -5
- package/lib/mcp/tools/core/consult_assistant.js +1 -0
- package/lib/mcp/tools/core/index.js +2 -0
- package/lib/mcp/tools/core/list_apps.js +2 -2
- package/lib/mcp/tools/crashlytics/list_top_issues.js +1 -1
- package/lib/mcp/tools/dataconnect/generate_operation.js +1 -0
- package/lib/mcp/tools/dataconnect/generate_schema.js +1 -0
- package/lib/mcp/tools/dataconnect/index.js +4 -0
- package/package.json +1 -1
- package/schema/firebase-config.json +1 -0
package/lib/api.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.runtimeconfigOrigin = exports.rulesOrigin = exports.resourceManagerOrigin = exports.crashlyticsApiOrigin = exports.messagingApiOrigin = exports.remoteConfigApiOrigin = exports.rtdbMetadataOrigin = exports.rtdbManagementOrigin = exports.realtimeOrigin = exports.extensionsTOSOrigin = exports.extensionsPublisherOrigin = exports.extensionsOrigin = exports.iamOrigin = exports.identityOrigin = exports.hostingOrigin = exports.googleOrigin = exports.pubsubOrigin = exports.cloudTasksOrigin = exports.cloudschedulerOrigin = exports.
|
|
4
|
-
exports.setScopes = exports.getScopes = exports.cloudAiCompanionOrigin = exports.vertexAIOrigin = exports.cloudSQLAdminOrigin = exports.dataConnectLocalConnString = exports.dataconnectP4SADomain = exports.dataconnectOrigin = exports.githubClientSecret = exports.githubClientId = exports.computeOrigin = exports.secretManagerOrigin = exports.githubApiOrigin = exports.githubOrigin = exports.serviceUsageOrigin = exports.cloudRunApiOrigin = exports.hostingApiOrigin = exports.firebaseStorageOrigin =
|
|
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.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;
|
|
5
5
|
const constants_1 = require("./emulator/constants");
|
|
6
6
|
const logger_1 = require("./logger");
|
|
7
7
|
const scopes = require("./scopes");
|
|
@@ -72,8 +72,6 @@ const functionsDefaultRegion = () => utils.envOverride("FIREBASE_FUNCTIONS_DEFAU
|
|
|
72
72
|
exports.functionsDefaultRegion = functionsDefaultRegion;
|
|
73
73
|
const cloudbuildOrigin = () => utils.envOverride("FIREBASE_CLOUDBUILD_URL", "https://cloudbuild.googleapis.com");
|
|
74
74
|
exports.cloudbuildOrigin = cloudbuildOrigin;
|
|
75
|
-
const cloudCompanionOrigin = () => utils.envOverride("CLOUD_COMPANION_URL", "https://cloudaicompanion.googleapis.com");
|
|
76
|
-
exports.cloudCompanionOrigin = cloudCompanionOrigin;
|
|
77
75
|
const cloudschedulerOrigin = () => utils.envOverride("FIREBASE_CLOUDSCHEDULER_URL", "https://cloudscheduler.googleapis.com");
|
|
78
76
|
exports.cloudschedulerOrigin = cloudschedulerOrigin;
|
|
79
77
|
const cloudTasksOrigin = () => utils.envOverride("FIREBASE_CLOUD_TAKS_URL", "https://cloudtasks.googleapis.com");
|
|
@@ -152,7 +152,7 @@ async function createGitRepoLink(projectId, location, connectionId) {
|
|
|
152
152
|
await githubConnections.linkGitHubRepository(projectId, location, connectionId);
|
|
153
153
|
}
|
|
154
154
|
exports.createGitRepoLink = createGitRepoLink;
|
|
155
|
-
async function ensureAppHostingComputeServiceAccount(projectId, serviceAccount
|
|
155
|
+
async function ensureAppHostingComputeServiceAccount(projectId, serviceAccount) {
|
|
156
156
|
const sa = serviceAccount || defaultComputeServiceAccountEmail(projectId);
|
|
157
157
|
const name = `projects/${projectId}/serviceAccounts/${sa}`;
|
|
158
158
|
try {
|
|
@@ -162,21 +162,11 @@ async function ensureAppHostingComputeServiceAccount(projectId, serviceAccount,
|
|
|
162
162
|
if (!(err instanceof error_1.FirebaseError)) {
|
|
163
163
|
throw err;
|
|
164
164
|
}
|
|
165
|
-
if (err.status === 404) {
|
|
166
|
-
await provisionDefaultComputeServiceAccount(projectId);
|
|
167
|
-
}
|
|
168
165
|
else if (err.status === 403) {
|
|
169
166
|
throw new error_1.FirebaseError(`Failed to create backend due to missing delegation permissions for ${sa}. Make sure you have the iam.serviceAccounts.actAs permission.`, { original: err });
|
|
170
167
|
}
|
|
171
168
|
}
|
|
172
|
-
|
|
173
|
-
const policy = await (0, resourceManager_1.getIamPolicy)(projectId);
|
|
174
|
-
const objectViewerBinding = policy.bindings.find((binding) => binding.role === "roles/storage.objectViewer");
|
|
175
|
-
if (!objectViewerBinding ||
|
|
176
|
-
!objectViewerBinding.members.includes(`serviceAccount:${defaultComputeServiceAccountEmail(projectId)}`)) {
|
|
177
|
-
await (0, resourceManager_1.addServiceAccountToRoles)(projectId, defaultComputeServiceAccountEmail(projectId), ["roles/storage.objectViewer"], true);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
169
|
+
await provisionDefaultComputeServiceAccount(projectId);
|
|
180
170
|
}
|
|
181
171
|
exports.ensureAppHostingComputeServiceAccount = ensureAppHostingComputeServiceAccount;
|
|
182
172
|
async function promptNewBackendId(projectId, location) {
|
|
@@ -232,12 +222,22 @@ async function provisionDefaultComputeServiceAccount(projectId) {
|
|
|
232
222
|
throw err;
|
|
233
223
|
}
|
|
234
224
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
225
|
+
try {
|
|
226
|
+
await (0, resourceManager_1.addServiceAccountToRoles)(projectId, defaultComputeServiceAccountEmail(projectId), [
|
|
227
|
+
"roles/firebaseapphosting.computeRunner",
|
|
228
|
+
"roles/firebase.sdkAdminServiceAgent",
|
|
229
|
+
"roles/developerconnect.readTokenAccessor",
|
|
230
|
+
"roles/storage.objectViewer",
|
|
231
|
+
], true);
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
if ((0, error_1.getErrStatus)(err) === 400) {
|
|
235
|
+
(0, utils_1.logWarning)("Your App Hosting compute service account is still being provisioned in the background; you may continue with the init flow.");
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
throw err;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
241
|
}
|
|
242
242
|
async function setDefaultTrafficPolicy(projectId, location, backendId, codebaseBranch) {
|
|
243
243
|
const traffic = {
|
package/lib/commands/login.js
CHANGED
|
@@ -27,7 +27,8 @@ exports.command = new command_1.Command("login")
|
|
|
27
27
|
return user;
|
|
28
28
|
}
|
|
29
29
|
if (!options.reauth) {
|
|
30
|
-
utils.logBullet("Firebase CLI
|
|
30
|
+
utils.logBullet("The Firebase CLI’s MCP server feature can optionally make use of Gemini in Firebase. " +
|
|
31
|
+
"Learn more about Gemini in Firebase and how it uses your data: https://firebase.google.com/docs/gemini-in-firebase#how-gemini-in-firebase-uses-your-data");
|
|
31
32
|
const geminiUsage = await (0, prompt_1.confirm)("Enable Gemini in Firebase features?");
|
|
32
33
|
configstore_1.configstore.set("gemini", geminiUsage);
|
|
33
34
|
logger_1.logger.info();
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ensureSparkApis = exports.ensureApis = void 0;
|
|
3
|
+
exports.ensureGIFApis = exports.ensureSparkApis = exports.ensureApis = void 0;
|
|
4
4
|
const api = require("../api");
|
|
5
5
|
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
6
|
+
const prefix = "dataconnect";
|
|
6
7
|
async function ensureApis(projectId) {
|
|
7
|
-
const prefix = "dataconnect";
|
|
8
8
|
await (0, ensureApiEnabled_1.ensure)(projectId, api.dataconnectOrigin(), prefix);
|
|
9
9
|
await (0, ensureApiEnabled_1.ensure)(projectId, api.cloudSQLAdminOrigin(), prefix);
|
|
10
10
|
await (0, ensureApiEnabled_1.ensure)(projectId, api.computeOrigin(), prefix);
|
|
11
11
|
}
|
|
12
12
|
exports.ensureApis = ensureApis;
|
|
13
13
|
async function ensureSparkApis(projectId) {
|
|
14
|
-
const prefix = "dataconnect";
|
|
15
14
|
await (0, ensureApiEnabled_1.ensure)(projectId, api.cloudSQLAdminOrigin(), prefix);
|
|
16
15
|
}
|
|
17
16
|
exports.ensureSparkApis = ensureSparkApis;
|
|
17
|
+
async function ensureGIFApis(projectId) {
|
|
18
|
+
await (0, ensureApiEnabled_1.ensure)(projectId, api.cloudAiCompanionOrigin(), prefix);
|
|
19
|
+
}
|
|
20
|
+
exports.ensureGIFApis = ensureGIFApis;
|
|
@@ -14,7 +14,7 @@ async function default_1(context, options) {
|
|
|
14
14
|
await (0, apphosting_1.ensureApiEnabled)(options);
|
|
15
15
|
await (0, backend_1.ensureRequiredApisEnabled)(projectId);
|
|
16
16
|
try {
|
|
17
|
-
await (0, backend_1.ensureAppHostingComputeServiceAccount)(projectId, ""
|
|
17
|
+
await (0, backend_1.ensureAppHostingComputeServiceAccount)(projectId, "");
|
|
18
18
|
}
|
|
19
19
|
catch (err) {
|
|
20
20
|
if (err.status === 400) {
|
|
@@ -52,12 +52,13 @@ async function detectFromYaml(directory, project, runtime) {
|
|
|
52
52
|
exports.detectFromYaml = detectFromYaml;
|
|
53
53
|
async function detectFromPort(port, project, runtime, initialDelay = 0, timeout = 10000) {
|
|
54
54
|
let res;
|
|
55
|
+
const discoveryTimeout = getFunctionDiscoveryTimeout() || timeout;
|
|
55
56
|
const timedOut = new Promise((resolve, reject) => {
|
|
56
57
|
setTimeout(() => {
|
|
57
58
|
const originalError = "User code failed to load. Cannot determine backend specification.";
|
|
58
|
-
const error = `${originalError} Timeout after ${
|
|
59
|
+
const error = `${originalError} Timeout after ${discoveryTimeout}. See https://firebase.google.com/docs/functions/tips#avoid_deployment_timeouts_during_initialization'`;
|
|
59
60
|
reject(new error_1.FirebaseError(error));
|
|
60
|
-
},
|
|
61
|
+
}, discoveryTimeout);
|
|
61
62
|
});
|
|
62
63
|
if (initialDelay > 0) {
|
|
63
64
|
await new Promise((resolve) => setTimeout(resolve, initialDelay));
|
|
@@ -358,6 +358,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
358
358
|
if (extensionEmulator) {
|
|
359
359
|
await startEmulator(extensionEmulator);
|
|
360
360
|
}
|
|
361
|
+
const account = (0, auth_1.getProjectDefaultAccount)(options.projectRoot);
|
|
361
362
|
if (emulatableBackends.length) {
|
|
362
363
|
if (!listenForEmulator.functions || !listenForEmulator.eventarc || !listenForEmulator.tasks) {
|
|
363
364
|
listenForEmulator = await (0, portUtils_1.resolveHostAndAssignPorts)(Object.assign(Object.assign({}, listenForEmulator), { functions: (_h = listenForEmulator.functions) !== null && _h !== void 0 ? _h : getListenConfig(options, types_1.Emulators.FUNCTIONS), eventarc: (_j = listenForEmulator.eventarc) !== null && _j !== void 0 ? _j : getListenConfig(options, types_1.Emulators.EVENTARC), tasks: (_k = listenForEmulator.eventarc) !== null && _k !== void 0 ? _k : getListenConfig(options, types_1.Emulators.TASKS) }));
|
|
@@ -378,7 +379,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
378
379
|
if (emulatorsNotRunning.length > 0 && !constants_1.Constants.isDemoProject(projectId)) {
|
|
379
380
|
functionsLogger.logLabeled("WARN", "functions", `The following emulators are not running, calls to these services from the Functions emulator will affect production: ${clc.bold(emulatorsNotRunning.join(", "))}`);
|
|
380
381
|
}
|
|
381
|
-
const account = (0, auth_1.getProjectDefaultAccount)(options.projectRoot);
|
|
382
382
|
const functionsEmulator = new functionsEmulator_1.FunctionsEmulator({
|
|
383
383
|
projectId,
|
|
384
384
|
projectDir,
|
|
@@ -574,6 +574,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
574
574
|
enable_output_generated_sdk: true,
|
|
575
575
|
enable_output_schema_extensions: true,
|
|
576
576
|
debug: options.debug,
|
|
577
|
+
account,
|
|
577
578
|
};
|
|
578
579
|
if (exportMetadata.dataconnect) {
|
|
579
580
|
utils.assertIsString(options.import);
|
|
@@ -19,7 +19,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
|
|
|
19
19
|
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
|
20
20
|
};
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.fromNodeSocket = exports.PostgresServer = exports.TRUNCATE_TABLES_SQL = void 0;
|
|
22
|
+
exports.PGliteExtendedQueryPatch = exports.fromNodeSocket = exports.PostgresServer = exports.TRUNCATE_TABLES_SQL = void 0;
|
|
23
23
|
const pglite_1 = require("@electric-sql/pglite");
|
|
24
24
|
const { dynamicImport } = require(true && "../../dynamicImport");
|
|
25
25
|
const net = require("node:net");
|
|
@@ -30,6 +30,7 @@ const pg_gateway_1 = require("pg-gateway");
|
|
|
30
30
|
const logger_1 = require("../../logger");
|
|
31
31
|
const error_1 = require("../../error");
|
|
32
32
|
const fsutils_1 = require("../../fsutils");
|
|
33
|
+
const node_string_decoder_1 = require("node:string_decoder");
|
|
33
34
|
exports.TRUNCATE_TABLES_SQL = `
|
|
34
35
|
DO $do$
|
|
35
36
|
DECLARE _clear text;
|
|
@@ -42,11 +43,13 @@ BEGIN
|
|
|
42
43
|
EXECUTE COALESCE(_clear, 'select now()');
|
|
43
44
|
END
|
|
44
45
|
$do$;`;
|
|
46
|
+
const decoder = new node_string_decoder_1.StringDecoder();
|
|
47
|
+
const pgliteDebugLog = fs.createWriteStream("pglite-debug.log");
|
|
45
48
|
class PostgresServer {
|
|
46
49
|
async createPGServer(host = "127.0.0.1", port) {
|
|
47
50
|
const getDb = this.getDb.bind(this);
|
|
48
51
|
const server = net.createServer(async (socket) => {
|
|
49
|
-
await fromNodeSocket(socket, {
|
|
52
|
+
const connection = await fromNodeSocket(socket, {
|
|
50
53
|
serverVersion: "17.4 (PGlite 0.3.3)",
|
|
51
54
|
auth: { method: "trust" },
|
|
52
55
|
onMessage(data, { isAuthenticated }) {
|
|
@@ -61,7 +64,7 @@ class PostgresServer {
|
|
|
61
64
|
}
|
|
62
65
|
const response = yield __await(db.execProtocolRaw(data));
|
|
63
66
|
try {
|
|
64
|
-
for (var _d = true, _e = __asyncValues((
|
|
67
|
+
for (var _d = true, _e = __asyncValues(extendedQueryPatch.filterResponse(data, response)), _f; _f = yield __await(_e.next()), _a = _f.done, !_a;) {
|
|
65
68
|
_c = _f.value;
|
|
66
69
|
_d = false;
|
|
67
70
|
try {
|
|
@@ -83,6 +86,7 @@ class PostgresServer {
|
|
|
83
86
|
});
|
|
84
87
|
},
|
|
85
88
|
});
|
|
89
|
+
const extendedQueryPatch = new PGliteExtendedQueryPatch(connection);
|
|
86
90
|
socket.on("end", () => {
|
|
87
91
|
logger_1.logger.debug("Postgres client disconnected");
|
|
88
92
|
});
|
|
@@ -223,7 +227,7 @@ class PostgresServer {
|
|
|
223
227
|
this.server = undefined;
|
|
224
228
|
this.baseDataDirectory = args.dataDirectory;
|
|
225
229
|
this.importPath = args.importPath;
|
|
226
|
-
this.debug = args.debug ?
|
|
230
|
+
this.debug = args.debug ? 1 : 0;
|
|
227
231
|
}
|
|
228
232
|
}
|
|
229
233
|
exports.PostgresServer = PostgresServer;
|
|
@@ -235,3 +239,61 @@ async function fromNodeSocket(socket, options) {
|
|
|
235
239
|
return new pg_gateway_1.PostgresConnection({ readable: rs, writable: ws }, opts);
|
|
236
240
|
}
|
|
237
241
|
exports.fromNodeSocket = fromNodeSocket;
|
|
242
|
+
class PGliteExtendedQueryPatch {
|
|
243
|
+
constructor(connection) {
|
|
244
|
+
this.connection = connection;
|
|
245
|
+
this.isExtendedQuery = false;
|
|
246
|
+
this.eqpErrored = false;
|
|
247
|
+
}
|
|
248
|
+
filterResponse(message, response) {
|
|
249
|
+
return __asyncGenerator(this, arguments, function* filterResponse_1() {
|
|
250
|
+
var _a, e_2, _b, _c;
|
|
251
|
+
const pipelineStartMessages = [
|
|
252
|
+
pg_gateway_1.FrontendMessageCode.Parse,
|
|
253
|
+
pg_gateway_1.FrontendMessageCode.Bind,
|
|
254
|
+
pg_gateway_1.FrontendMessageCode.Close,
|
|
255
|
+
];
|
|
256
|
+
const decoded = decoder.write(message);
|
|
257
|
+
pgliteDebugLog.write("Front: " + decoded);
|
|
258
|
+
if (pipelineStartMessages.includes(message[0])) {
|
|
259
|
+
this.isExtendedQuery = true;
|
|
260
|
+
}
|
|
261
|
+
if (message[0] === pg_gateway_1.FrontendMessageCode.Sync) {
|
|
262
|
+
this.isExtendedQuery = false;
|
|
263
|
+
this.eqpErrored = false;
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
for (var _d = true, _e = __asyncValues((0, pg_gateway_1.getMessages)(response)), _f; _f = yield __await(_e.next()), _a = _f.done, !_a;) {
|
|
267
|
+
_c = _f.value;
|
|
268
|
+
_d = false;
|
|
269
|
+
try {
|
|
270
|
+
const bm = _c;
|
|
271
|
+
if (this.eqpErrored) {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (this.isExtendedQuery && bm[0] === pg_gateway_1.BackendMessageCode.ErrorMessage) {
|
|
275
|
+
this.eqpErrored = true;
|
|
276
|
+
}
|
|
277
|
+
if (this.isExtendedQuery && bm[0] === pg_gateway_1.BackendMessageCode.ReadyForQuery) {
|
|
278
|
+
pgliteDebugLog.write("Filtered: " + decoder.write(bm));
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
pgliteDebugLog.write("Sent: " + decoder.write(bm));
|
|
282
|
+
yield yield __await(bm);
|
|
283
|
+
}
|
|
284
|
+
finally {
|
|
285
|
+
_d = true;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
290
|
+
finally {
|
|
291
|
+
try {
|
|
292
|
+
if (!_d && !_a && (_b = _e.return)) yield __await(_b.call(_e));
|
|
293
|
+
}
|
|
294
|
+
finally { if (e_2) throw e_2.error; }
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
exports.PGliteExtendedQueryPatch = PGliteExtendedQueryPatch;
|
|
@@ -250,7 +250,6 @@ class DataConnectEmulator {
|
|
|
250
250
|
if (account) {
|
|
251
251
|
const defaultCredPath = await (0, defaultCredentials_1.getCredentialPathAsync)(account);
|
|
252
252
|
if (defaultCredPath) {
|
|
253
|
-
logger_1.logger.log("DEBUG", `Setting GAC to ${defaultCredPath}`);
|
|
254
253
|
credsEnv.GOOGLE_APPLICATION_CREDENTIALS = defaultCredPath;
|
|
255
254
|
}
|
|
256
255
|
}
|
|
@@ -54,28 +54,28 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dataconnect": {
|
|
56
56
|
"darwin": {
|
|
57
|
-
"version": "2.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.
|
|
57
|
+
"version": "2.9.0",
|
|
58
|
+
"expectedSize": 27415296,
|
|
59
|
+
"expectedChecksum": "5814d22b06b1321409f40693dfe47288",
|
|
60
|
+
"expectedChecksumSHA256": "8df1cfac6ef747909b3c18b57ba52b70a11d53b9d0a22b197011d6550ceca133",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.9.0",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.9.0"
|
|
63
63
|
},
|
|
64
64
|
"win32": {
|
|
65
|
-
"version": "2.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.
|
|
65
|
+
"version": "2.9.0",
|
|
66
|
+
"expectedSize": 27875328,
|
|
67
|
+
"expectedChecksum": "21381052c2bf767ee84fb85975cfd4d5",
|
|
68
|
+
"expectedChecksumSHA256": "6bebaa9575b460c4fa170b191d94722923a3c869bc1eca24c7e444428e6b7a70",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.9.0",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.9.0.exe"
|
|
71
71
|
},
|
|
72
72
|
"linux": {
|
|
73
|
-
"version": "2.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.
|
|
73
|
+
"version": "2.9.0",
|
|
74
|
+
"expectedSize": 27332760,
|
|
75
|
+
"expectedChecksum": "0480a92f5af8e8441e680dc4e7995263",
|
|
76
|
+
"expectedChecksumSHA256": "4f60baf4649c670227314302056f50b1c40ccc8d673b98d16935977508342a67",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.9.0",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.9.0"
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -33,7 +33,8 @@ function setNewDefaults(params, newDefaults) {
|
|
|
33
33
|
if (newDefaults[param.param]) {
|
|
34
34
|
param.default = newDefaults[param.param];
|
|
35
35
|
}
|
|
36
|
-
else if (
|
|
36
|
+
else if (param.param === `firebaseextensions.v1beta.function/location` &&
|
|
37
|
+
newDefaults["LOCATION"]) {
|
|
37
38
|
param.default = newDefaults["LOCATION"];
|
|
38
39
|
}
|
|
39
40
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFunctions = exports.listFunctions = exports.deleteFunction = exports.updateFunction = exports.setInvokerUpdate = exports.setInvokerCreate = exports.getIamPolicy = exports.setIamPolicy = exports.createFunction = exports.generateUploadUrl = exports.API_VERSION = void 0;
|
|
3
|
+
exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFunctions = exports.listFunctions = exports.deleteFunction = exports.updateFunction = exports.setInvokerUpdate = exports.setInvokerCreate = exports.getIamPolicy = exports.setIamPolicy = exports.createFunction = exports.generateUploadUrl = exports.captureRuntimeValidationError = exports.API_VERSION = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const error_1 = require("../error");
|
|
6
6
|
const logger_1 = require("../logger");
|
|
@@ -14,8 +14,22 @@ const api_1 = require("../api");
|
|
|
14
14
|
const constants_1 = require("../functions/constants");
|
|
15
15
|
exports.API_VERSION = "v1";
|
|
16
16
|
const client = new apiv2_1.Client({ urlPrefix: (0, api_1.functionsOrigin)(), apiVersion: exports.API_VERSION });
|
|
17
|
+
function captureRuntimeValidationError(errMessage) {
|
|
18
|
+
const regex = /message: "((?:\\.|[^"\\])*)"/;
|
|
19
|
+
const match = errMessage.match(regex);
|
|
20
|
+
if (match && match[1]) {
|
|
21
|
+
const capturedMessage = match[1].replace(/\\"/g, '"');
|
|
22
|
+
return capturedMessage;
|
|
23
|
+
}
|
|
24
|
+
return "invalid runtime detected, please see https://cloud.google.com/functions/docs/runtime-support for the latest supported runtimes";
|
|
25
|
+
}
|
|
26
|
+
exports.captureRuntimeValidationError = captureRuntimeValidationError;
|
|
17
27
|
function functionsOpLogReject(funcName, type, err) {
|
|
18
28
|
var _a, _b, _c, _d;
|
|
29
|
+
if ((err === null || err === void 0 ? void 0 : err.message).includes("Runtime validation errors")) {
|
|
30
|
+
const capturedMessage = captureRuntimeValidationError(err.message);
|
|
31
|
+
utils.logWarning(clc.bold(clc.yellow("functions:")) + " " + capturedMessage + " for function " + funcName);
|
|
32
|
+
}
|
|
19
33
|
if (((_b = (_a = err === null || err === void 0 ? void 0 : err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode) === 429) {
|
|
20
34
|
utils.logWarning(`${clc.bold(clc.yellow("functions:"))} got "Quota Exceeded" error while trying to ${type} ${funcName}. Waiting to retry...`);
|
|
21
35
|
}
|
|
@@ -12,6 +12,7 @@ const proto = require("./proto");
|
|
|
12
12
|
const utils = require("../utils");
|
|
13
13
|
const projectConfig = require("../functions/projectConfig");
|
|
14
14
|
const constants_1 = require("../functions/constants");
|
|
15
|
+
const cloudfunctions_1 = require("./cloudfunctions");
|
|
15
16
|
exports.API_VERSION = "v2";
|
|
16
17
|
const DEFAULT_MAX_INSTANCE_COUNT = 100;
|
|
17
18
|
const client = new apiv2_1.Client({
|
|
@@ -49,8 +50,12 @@ function mebibytes(memory) {
|
|
|
49
50
|
}
|
|
50
51
|
exports.mebibytes = mebibytes;
|
|
51
52
|
function functionsOpLogReject(func, type, err) {
|
|
52
|
-
var _a, _b, _c, _d, _e, _f;
|
|
53
|
-
if ((_a = err === null || err === void 0 ? void 0 : err.message) === null || _a === void 0 ? void 0 : _a.includes("
|
|
53
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
54
|
+
if ((_a = err === null || err === void 0 ? void 0 : err.message) === null || _a === void 0 ? void 0 : _a.includes("Runtime validation errors")) {
|
|
55
|
+
const capturedMessage = (0, cloudfunctions_1.captureRuntimeValidationError)(err.message);
|
|
56
|
+
utils.logLabeledWarning("functions", capturedMessage + " for function " + func.name);
|
|
57
|
+
}
|
|
58
|
+
if ((_b = err === null || err === void 0 ? void 0 : err.message) === null || _b === void 0 ? void 0 : _b.includes("maxScale may not exceed")) {
|
|
54
59
|
const maxInstances = func.serviceConfig.maxInstanceCount || DEFAULT_MAX_INSTANCE_COUNT;
|
|
55
60
|
utils.logLabeledWarning("functions", `Your current project quotas don't allow for the current max instances setting of ${maxInstances}. ` +
|
|
56
61
|
"Either reduce this function's maximum instances, or request a quota increase on the underlying Cloud Run service " +
|
|
@@ -62,17 +67,17 @@ function functionsOpLogReject(func, type, err) {
|
|
|
62
67
|
}
|
|
63
68
|
else {
|
|
64
69
|
utils.logLabeledWarning("functions", `${err === null || err === void 0 ? void 0 : err.message}`);
|
|
65
|
-
if (((
|
|
70
|
+
if (((_d = (_c = err === null || err === void 0 ? void 0 : err.context) === null || _c === void 0 ? void 0 : _c.response) === null || _d === void 0 ? void 0 : _d.statusCode) === 429) {
|
|
66
71
|
utils.logLabeledWarning("functions", `Got "Quota Exceeded" error while trying to ${type} ${func.name}. Waiting to retry...`);
|
|
67
72
|
}
|
|
68
|
-
else if ((
|
|
73
|
+
else if ((_e = err === null || err === void 0 ? void 0 : err.message) === null || _e === void 0 ? void 0 : _e.includes("If you recently started to use Eventarc, it may take a few minutes before all necessary permissions are propagated to the Service Agent")) {
|
|
69
74
|
utils.logLabeledWarning("functions", `Since this is your first time using 2nd gen functions, we need a little bit longer to finish setting everything up. Retry the deployment in a few minutes.`);
|
|
70
75
|
}
|
|
71
76
|
utils.logLabeledWarning("functions", ` failed to ${type} function ${func.name}`);
|
|
72
77
|
}
|
|
73
78
|
throw new error_1.FirebaseError(`Failed to ${type} function ${func.name}`, {
|
|
74
79
|
original: err,
|
|
75
|
-
status: (
|
|
80
|
+
status: (_g = (_f = err === null || err === void 0 ? void 0 : err.context) === null || _f === void 0 ? void 0 : _f.response) === null || _g === void 0 ? void 0 : _g.statusCode,
|
|
76
81
|
context: { function: func.name },
|
|
77
82
|
});
|
|
78
83
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateOperation = exports.chatWithFirebase = exports.generateSchema = void 0;
|
|
3
|
+
exports.extractCodeBlock = exports.generateOperation = exports.chatWithFirebase = exports.generateSchema = void 0;
|
|
4
4
|
const apiv2_1 = require("../apiv2");
|
|
5
5
|
const api_1 = require("../api");
|
|
6
|
-
const
|
|
6
|
+
const error_1 = require("../error");
|
|
7
|
+
const apiClient = new apiv2_1.Client({ urlPrefix: (0, api_1.cloudAiCompanionOrigin)(), auth: true });
|
|
7
8
|
const SCHEMA_GENERATOR_EXPERIENCE = "/appeco/firebase/fdc-schema-generator";
|
|
8
9
|
const GEMINI_IN_FIREBASE_EXPERIENCE = "/appeco/firebase/firebase-chat/free";
|
|
9
10
|
const OPERATION_GENERATION_EXPERIENCE = "/appeco/firebase/fdc-query-generator";
|
|
@@ -44,3 +45,12 @@ async function generateOperation(prompt, service, project, chatHistory = []) {
|
|
|
44
45
|
return res.body.output.messages[0].content;
|
|
45
46
|
}
|
|
46
47
|
exports.generateOperation = generateOperation;
|
|
48
|
+
function extractCodeBlock(text) {
|
|
49
|
+
const regex = /```(?:[a-z]+\n)?([\s\S]*?)```/m;
|
|
50
|
+
const match = text.match(regex);
|
|
51
|
+
if (match && match[1]) {
|
|
52
|
+
return match[1].trim();
|
|
53
|
+
}
|
|
54
|
+
throw new error_1.FirebaseError(`No code block found in the generated response: ${text}`);
|
|
55
|
+
}
|
|
56
|
+
exports.extractCodeBlock = extractCodeBlock;
|
|
@@ -22,17 +22,7 @@ async function doSetup(setup, config) {
|
|
|
22
22
|
}
|
|
23
23
|
await (0, apphosting_1.ensureApiEnabled)({ projectId });
|
|
24
24
|
await (0, backend_1.ensureRequiredApisEnabled)(projectId);
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
await (0, backend_1.ensureAppHostingComputeServiceAccount)(projectId, "", true);
|
|
28
|
-
spinner.succeed("App Hosting compute Service account is ready");
|
|
29
|
-
}
|
|
30
|
-
catch (err) {
|
|
31
|
-
if (err.status === 400) {
|
|
32
|
-
spinner.warn("Your App Hosting compute service account is still being provisioned. Please try again in a few moments.");
|
|
33
|
-
}
|
|
34
|
-
throw err;
|
|
35
|
-
}
|
|
25
|
+
await (0, backend_1.ensureAppHostingComputeServiceAccount)(projectId, "");
|
|
36
26
|
utils.logBullet("This command links your local project to Firebase App Hosting. You will be able to deploy your web app with `firebase deploy` after setup.");
|
|
37
27
|
const backendConfig = {
|
|
38
28
|
backendId: "",
|
|
@@ -18,6 +18,8 @@ const utils_1 = require("../../../utils");
|
|
|
18
18
|
const cloudbilling_1 = require("../../../gcp/cloudbilling");
|
|
19
19
|
const sdk = require("./sdk");
|
|
20
20
|
const fileUtils_1 = require("../../../dataconnect/fileUtils");
|
|
21
|
+
const fdcExperience_1 = require("../../../gemini/fdcExperience");
|
|
22
|
+
const configstore_1 = require("../../../configstore");
|
|
21
23
|
const DATACONNECT_YAML_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/dataconnect.yaml");
|
|
22
24
|
const CONNECTOR_YAML_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/connector.yaml");
|
|
23
25
|
const SCHEMA_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/schema.gql");
|
|
@@ -71,7 +73,7 @@ async function askQuestions(setup) {
|
|
|
71
73
|
default: true,
|
|
72
74
|
}));
|
|
73
75
|
if (shouldConfigureBackend) {
|
|
74
|
-
info = await
|
|
76
|
+
info = await promptForSchema(setup, info);
|
|
75
77
|
info = await promptForCloudSQL(setup, info);
|
|
76
78
|
info.shouldProvisionCSQL = !!(setup.projectId &&
|
|
77
79
|
(info.isNewInstance || info.isNewDatabase) &&
|
|
@@ -322,12 +324,31 @@ async function promptForCloudSQL(setup, info) {
|
|
|
322
324
|
}
|
|
323
325
|
return info;
|
|
324
326
|
}
|
|
325
|
-
async function
|
|
327
|
+
async function promptForSchema(setup, info) {
|
|
326
328
|
if (info.serviceId === "") {
|
|
327
329
|
info.serviceId = await (0, prompt_1.input)({
|
|
328
330
|
message: "What ID would you like to use for this service?",
|
|
329
331
|
default: (0, path_1.basename)(process.cwd()),
|
|
330
332
|
});
|
|
333
|
+
if (setup.projectId) {
|
|
334
|
+
if (!configstore_1.configstore.get("gemini")) {
|
|
335
|
+
(0, utils_1.logBullet)("Learn more about Gemini in Firebase and how it uses your data: https://firebase.google.com/docs/gemini-in-firebase#how-gemini-in-firebase-uses-your-data");
|
|
336
|
+
}
|
|
337
|
+
if (await (0, prompt_1.confirm)({
|
|
338
|
+
message: `Do you want Gemini in Firebase to help generate a schema for your service?`,
|
|
339
|
+
default: false,
|
|
340
|
+
})) {
|
|
341
|
+
configstore_1.configstore.set("gemini", true);
|
|
342
|
+
await (0, ensureApis_1.ensureGIFApis)(setup.projectId);
|
|
343
|
+
const prompt = await (0, prompt_1.input)({
|
|
344
|
+
message: "Describe the app you are building:",
|
|
345
|
+
default: "movie rating app",
|
|
346
|
+
});
|
|
347
|
+
const schema = await (0, utils_1.promiseWithSpinner)(() => (0, fdcExperience_1.generateSchema)(prompt, setup.projectId), "Generating the Data Connect Schema...");
|
|
348
|
+
info.schemaGql = [{ path: "schema.gql", content: (0, fdcExperience_1.extractCodeBlock)(schema) }];
|
|
349
|
+
info.connectors = [emptyConnector];
|
|
350
|
+
}
|
|
351
|
+
}
|
|
331
352
|
}
|
|
332
353
|
return info;
|
|
333
354
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.checkFirebaseEnabledForCloudProject = exports.getProject = exports.getFirebaseProject = exports.listFirebaseProjects = exports.getAvailableCloudProjectPage = exports.getFirebaseProjectPage = exports.addFirebaseToCloudProject = exports.createCloudProject = exports.promptAvailableProjectId = exports.getOrPromptProject = exports.addFirebaseToCloudProjectAndLog = exports.createFirebaseProjectAndLog = exports.promptProjectCreation = exports.ProjectParentResourceType = void 0;
|
|
3
|
+
exports.checkFirebaseEnabledForCloudProject = exports.getProject = exports.getFirebaseProject = exports.checkAndRecommendProjectId = exports.listFirebaseProjects = exports.getAvailableCloudProjectPage = exports.getFirebaseProjectPage = exports.addFirebaseToCloudProject = exports.createCloudProject = exports.promptAvailableProjectId = exports.getOrPromptProject = exports.addFirebaseToCloudProjectAndLog = exports.createFirebaseProjectAndLog = exports.promptProjectCreation = exports.ProjectParentResourceType = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const ora = require("ora");
|
|
6
6
|
const apiv2_1 = require("../apiv2");
|
|
@@ -15,6 +15,7 @@ const TIMEOUT_MILLIS = 30000;
|
|
|
15
15
|
const MAXIMUM_PROMPT_LIST = 100;
|
|
16
16
|
const PROJECT_LIST_PAGE_SIZE = 1000;
|
|
17
17
|
const CREATE_PROJECT_API_REQUEST_TIMEOUT_MILLIS = 15000;
|
|
18
|
+
const CHECK_PROJECT_ID_API_REQUEST_TIMEOUT_MILLIS = 15000;
|
|
18
19
|
var ProjectParentResourceType;
|
|
19
20
|
(function (ProjectParentResourceType) {
|
|
20
21
|
ProjectParentResourceType["ORGANIZATION"] = "organization";
|
|
@@ -25,16 +26,23 @@ async function promptProjectCreation(options) {
|
|
|
25
26
|
const projectId = (_a = options.projectId) !== null && _a !== void 0 ? _a : (await prompt.input({
|
|
26
27
|
message: "Please specify a unique project id " +
|
|
27
28
|
`(${clc.yellow("warning")}: cannot be modified afterward) [6-30 characters]:\n`,
|
|
28
|
-
validate: (projectId) => {
|
|
29
|
+
validate: async (projectId) => {
|
|
29
30
|
if (projectId.length < 6) {
|
|
30
31
|
return "Project ID must be at least 6 characters long";
|
|
31
32
|
}
|
|
32
33
|
else if (projectId.length > 30) {
|
|
33
34
|
return "Project ID cannot be longer than 30 characters";
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
try {
|
|
37
|
+
const { isAvailable, suggestedProjectId } = await checkAndRecommendProjectId(projectId);
|
|
38
|
+
if (!isAvailable && suggestedProjectId) {
|
|
39
|
+
return `Project ID is taken or unavailable. Try ${clc.bold(suggestedProjectId)}.`;
|
|
40
|
+
}
|
|
37
41
|
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
logger_1.logger.debug(`Couldn't check if project ID ${projectId} is available. Original error: ${error}`);
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
38
46
|
},
|
|
39
47
|
}));
|
|
40
48
|
const displayName = (_b = options.displayName) !== null && _b !== void 0 ? _b : (await prompt.input({
|
|
@@ -60,6 +68,11 @@ const firebaseAPIClient = new apiv2_1.Client({
|
|
|
60
68
|
auth: true,
|
|
61
69
|
apiVersion: "v1beta1",
|
|
62
70
|
});
|
|
71
|
+
const firebaseV1APIClient = new apiv2_1.Client({
|
|
72
|
+
urlPrefix: api.firebaseApiOrigin(),
|
|
73
|
+
auth: true,
|
|
74
|
+
apiVersion: "v1",
|
|
75
|
+
});
|
|
63
76
|
const resourceManagerClient = new apiv2_1.Client({
|
|
64
77
|
urlPrefix: api.resourceManagerOrigin(),
|
|
65
78
|
apiVersion: "v1",
|
|
@@ -306,6 +319,27 @@ async function listFirebaseProjects(pageSize) {
|
|
|
306
319
|
return projects;
|
|
307
320
|
}
|
|
308
321
|
exports.listFirebaseProjects = listFirebaseProjects;
|
|
322
|
+
async function checkAndRecommendProjectId(projectId) {
|
|
323
|
+
try {
|
|
324
|
+
const res = await firebaseV1APIClient.request({
|
|
325
|
+
method: "POST",
|
|
326
|
+
path: "/projects:checkProjectId",
|
|
327
|
+
body: {
|
|
328
|
+
proposedId: projectId,
|
|
329
|
+
},
|
|
330
|
+
timeout: CHECK_PROJECT_ID_API_REQUEST_TIMEOUT_MILLIS,
|
|
331
|
+
});
|
|
332
|
+
const { projectIdStatus, suggestedProjectId } = res.body;
|
|
333
|
+
return {
|
|
334
|
+
isAvailable: projectIdStatus === "PROJECT_ID_AVAILABLE",
|
|
335
|
+
suggestedProjectId,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
catch (err) {
|
|
339
|
+
throw new error_1.FirebaseError("Failed to check if project ID is available. See firebase-debug.log for more info.", { exit: 2, original: err });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
exports.checkAndRecommendProjectId = checkAndRecommendProjectId;
|
|
309
343
|
async function getFirebaseProject(projectId) {
|
|
310
344
|
try {
|
|
311
345
|
const res = await firebaseAPIClient.request({
|
package/lib/mcp/errors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.mcpAuthError = exports.NO_PROJECT_ERROR = void 0;
|
|
3
|
+
exports.mcpGeminiError = exports.mcpAuthError = exports.NO_PROJECT_ERROR = void 0;
|
|
4
4
|
const util_1 = require("./util");
|
|
5
5
|
exports.NO_PROJECT_ERROR = (0, util_1.mcpError)('No active project was found. Use the `firebase_update_environment` tool to set the project directory to an absolute folder location containing a firebase.json config file. Alternatively, change the MCP server config to add [...,"--dir","/absolute/path/to/project/directory"] in its command-line arguments.', "PRECONDITION_FAILED");
|
|
6
6
|
function mcpAuthError() {
|
|
@@ -13,3 +13,8 @@ ${cmd} login
|
|
|
13
13
|
[ADC]: https://cloud.google.com/docs/authentication/application-default-credentials`);
|
|
14
14
|
}
|
|
15
15
|
exports.mcpAuthError = mcpAuthError;
|
|
16
|
+
function mcpGeminiError(projectId) {
|
|
17
|
+
const consoleUrl = `https://firebase.corp.google.com/project/${projectId}/overview`;
|
|
18
|
+
return (0, util_1.mcpError)(`This tool uses the Gemini in Firebase API. Visit Firebase Console to enable the Gemini in Firebase API ${consoleUrl} and try again.`, "PRECONDITION_FAILED");
|
|
19
|
+
}
|
|
20
|
+
exports.mcpGeminiError = mcpGeminiError;
|
package/lib/mcp/index.js
CHANGED
|
@@ -17,6 +17,8 @@ const config_js_1 = require("../config.js");
|
|
|
17
17
|
const rc_js_1 = require("../rc.js");
|
|
18
18
|
const hubClient_js_1 = require("../emulator/hubClient.js");
|
|
19
19
|
const node_fs_1 = require("node:fs");
|
|
20
|
+
const ensureApiEnabled_js_1 = require("../ensureApiEnabled.js");
|
|
21
|
+
const api = require("../api.js");
|
|
20
22
|
const SERVER_VERSION = "0.1.0";
|
|
21
23
|
const cmd = new command_js_1.Command("experimental:mcp").before(requireAuth_js_1.requireAuth);
|
|
22
24
|
class FirebaseMcpServer {
|
|
@@ -165,7 +167,7 @@ class FirebaseMcpServer {
|
|
|
165
167
|
};
|
|
166
168
|
}
|
|
167
169
|
async mcpCallTool(request) {
|
|
168
|
-
var _a, _b, _c, _d, _e, _f;
|
|
170
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
169
171
|
await this.detectProjectRoot();
|
|
170
172
|
const toolName = request.params.name;
|
|
171
173
|
const toolArgs = request.params.arguments;
|
|
@@ -185,6 +187,16 @@ class FirebaseMcpServer {
|
|
|
185
187
|
if (((_b = tool.mcp._meta) === null || _b === void 0 ? void 0 : _b.requiresAuth) && !accountEmail) {
|
|
186
188
|
return (0, errors_js_1.mcpAuthError)();
|
|
187
189
|
}
|
|
190
|
+
if ((_c = tool.mcp._meta) === null || _c === void 0 ? void 0 : _c.requiresGemini) {
|
|
191
|
+
if (configstore_js_1.configstore.get("gemini")) {
|
|
192
|
+
await (0, ensureApiEnabled_js_1.ensure)(projectId, api.cloudAiCompanionOrigin(), "");
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
if (!(await (0, ensureApiEnabled_js_1.check)(projectId, api.cloudAiCompanionOrigin(), ""))) {
|
|
196
|
+
return (0, errors_js_1.mcpGeminiError)(projectId);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
188
200
|
const options = { projectDir: this.cachedProjectRoot, cwd: this.cachedProjectRoot };
|
|
189
201
|
const toolsCtx = {
|
|
190
202
|
projectId: projectId,
|
|
@@ -198,8 +210,8 @@ class FirebaseMcpServer {
|
|
|
198
210
|
await (0, track_js_1.trackGA4)("mcp_tool_call", {
|
|
199
211
|
tool_name: toolName,
|
|
200
212
|
error: res.isError ? 1 : 0,
|
|
201
|
-
mcp_client_name: (
|
|
202
|
-
mcp_client_version: (
|
|
213
|
+
mcp_client_name: (_d = this.clientInfo) === null || _d === void 0 ? void 0 : _d.name,
|
|
214
|
+
mcp_client_version: (_e = this.clientInfo) === null || _e === void 0 ? void 0 : _e.version,
|
|
203
215
|
});
|
|
204
216
|
return res;
|
|
205
217
|
}
|
|
@@ -207,8 +219,8 @@ class FirebaseMcpServer {
|
|
|
207
219
|
await (0, track_js_1.trackGA4)("mcp_tool_call", {
|
|
208
220
|
tool_name: toolName,
|
|
209
221
|
error: 1,
|
|
210
|
-
mcp_client_name: (
|
|
211
|
-
mcp_client_version: (
|
|
222
|
+
mcp_client_name: (_f = this.clientInfo) === null || _f === void 0 ? void 0 : _f.name,
|
|
223
|
+
mcp_client_version: (_g = this.clientInfo) === null || _g === void 0 ? void 0 : _g.version,
|
|
212
224
|
});
|
|
213
225
|
return (0, util_js_1.mcpError)(err);
|
|
214
226
|
}
|
|
@@ -20,6 +20,7 @@ exports.consult_assistant = (0, tool_js_1.tool)({
|
|
|
20
20
|
_meta: {
|
|
21
21
|
requiresProject: true,
|
|
22
22
|
requiresAuth: true,
|
|
23
|
+
requiresGemini: true,
|
|
23
24
|
},
|
|
24
25
|
}, async ({ prompt }, { projectId }) => {
|
|
25
26
|
const schema = await (0, fdcExperience_js_1.chatWithFirebase)(prompt, projectId);
|
|
@@ -12,6 +12,7 @@ const init_js_1 = require("./init.js");
|
|
|
12
12
|
const get_environment_js_1 = require("./get_environment.js");
|
|
13
13
|
const update_environment_js_1 = require("./update_environment.js");
|
|
14
14
|
const list_projects_js_1 = require("./list_projects.js");
|
|
15
|
+
const consult_assistant_js_1 = require("./consult_assistant.js");
|
|
15
16
|
exports.coreTools = [
|
|
16
17
|
get_project_js_1.get_project,
|
|
17
18
|
list_apps_js_1.list_apps,
|
|
@@ -21,6 +22,7 @@ exports.coreTools = [
|
|
|
21
22
|
create_project_js_1.create_project,
|
|
22
23
|
create_app_js_1.create_app,
|
|
23
24
|
create_android_sha_js_1.create_android_sha,
|
|
25
|
+
consult_assistant_js_1.consult_assistant,
|
|
24
26
|
get_environment_js_1.get_environment,
|
|
25
27
|
update_environment_js_1.update_environment,
|
|
26
28
|
init_js_1.init,
|
|
@@ -11,7 +11,7 @@ exports.list_apps = (0, tool_js_1.tool)({
|
|
|
11
11
|
inputSchema: zod_1.z.object({
|
|
12
12
|
platform: zod_1.z
|
|
13
13
|
.enum(["ios", "android", "web", "all"])
|
|
14
|
-
.
|
|
14
|
+
.optional()
|
|
15
15
|
.describe("the specific platform to list (omit to list all platforms)"),
|
|
16
16
|
}),
|
|
17
17
|
annotations: {
|
|
@@ -24,7 +24,7 @@ exports.list_apps = (0, tool_js_1.tool)({
|
|
|
24
24
|
},
|
|
25
25
|
}, async ({ platform }, { projectId }) => {
|
|
26
26
|
try {
|
|
27
|
-
const apps = await (0, apps_js_1.listFirebaseApps)(projectId, platform === "all" ? apps_js_1.AppPlatform.ANY : platform.toUpperCase());
|
|
27
|
+
const apps = await (0, apps_js_1.listFirebaseApps)(projectId, !platform || platform === "all" ? apps_js_1.AppPlatform.ANY : platform.toUpperCase());
|
|
28
28
|
return (0, util_js_1.toContent)(apps);
|
|
29
29
|
}
|
|
30
30
|
catch (err) {
|
|
@@ -12,7 +12,7 @@ exports.list_top_issues = (0, tool_js_1.tool)({
|
|
|
12
12
|
app_id: zod_1.z
|
|
13
13
|
.string()
|
|
14
14
|
.optional()
|
|
15
|
-
.describe("AppId for which the issues list
|
|
15
|
+
.describe("AppId for which the issues list should be fetched. For an Android application, read the mobilesdk_app_id value specified in the google-services.json file for the current package name. For an iOS Application, read the GOOGLE_APP_ID from GoogleService-Info.plist. If neither is available, use the `firebase_list_apps` tool to find an app_id to pass to this tool."),
|
|
16
16
|
issue_count: zod_1.z
|
|
17
17
|
.number()
|
|
18
18
|
.optional()
|
|
@@ -25,6 +25,7 @@ exports.generate_operation = (0, tool_js_1.tool)({
|
|
|
25
25
|
_meta: {
|
|
26
26
|
requiresProject: true,
|
|
27
27
|
requiresAuth: true,
|
|
28
|
+
requiresGemini: true,
|
|
28
29
|
},
|
|
29
30
|
}, async ({ prompt, service_id }, { projectId, config }) => {
|
|
30
31
|
const serviceInfo = await (0, fileUtils_js_1.pickService)(projectId, config, service_id || undefined);
|
|
@@ -18,6 +18,7 @@ exports.generate_schema = (0, tool_js_1.tool)({
|
|
|
18
18
|
_meta: {
|
|
19
19
|
requiresProject: true,
|
|
20
20
|
requiresAuth: true,
|
|
21
|
+
requiresGemini: true,
|
|
21
22
|
},
|
|
22
23
|
}, async ({ prompt }, { projectId }) => {
|
|
23
24
|
const schema = await (0, fdcExperience_js_1.generateSchema)(prompt, projectId);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.dataconnectTools = void 0;
|
|
4
|
+
const generate_operation_js_1 = require("./generate_operation.js");
|
|
5
|
+
const generate_schema_js_1 = require("./generate_schema.js");
|
|
4
6
|
const list_services_js_1 = require("./list_services.js");
|
|
5
7
|
const get_schema_js_1 = require("./get_schema.js");
|
|
6
8
|
const get_connector_js_1 = require("./get_connector.js");
|
|
@@ -10,6 +12,8 @@ const execute_query_js_1 = require("./execute_query.js");
|
|
|
10
12
|
const execute_mutation_js_1 = require("./execute_mutation.js");
|
|
11
13
|
exports.dataconnectTools = [
|
|
12
14
|
list_services_js_1.list_services,
|
|
15
|
+
generate_schema_js_1.generate_schema,
|
|
16
|
+
generate_operation_js_1.generate_operation,
|
|
13
17
|
get_schema_js_1.get_schema,
|
|
14
18
|
get_connector_js_1.get_connectors,
|
|
15
19
|
execute_graphql_js_1.execute_graphql,
|
package/package.json
CHANGED