firebase-tools 14.2.2 → 14.3.1
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 +4 -2
- package/lib/apphosting/backend.js +12 -23
- package/lib/apphosting/config.js +9 -20
- package/lib/apphosting/githubConnections.js +65 -99
- package/lib/apphosting/repo.js +22 -36
- package/lib/apphosting/secrets/dialogs.js +2 -3
- package/lib/apphosting/utils.js +1 -3
- package/lib/auth.js +1 -4
- package/lib/commands/apphosting-backends-delete.js +5 -5
- package/lib/commands/apphosting-secrets-set.js +2 -6
- package/lib/commands/apps-create.js +2 -3
- package/lib/commands/apps-sdkconfig.js +2 -7
- package/lib/commands/database-import.js +4 -6
- package/lib/commands/database-remove.js +4 -6
- package/lib/commands/database-set.js +6 -6
- package/lib/commands/database-update.js +5 -5
- package/lib/commands/dataconnect-sql-shell.js +4 -6
- package/lib/commands/ext-dev-deprecate.js +1 -0
- package/lib/commands/ext-dev-init.js +3 -12
- package/lib/commands/ext-dev-register.js +2 -4
- package/lib/commands/ext-dev-undeprecate.js +1 -2
- package/lib/commands/ext-dev-usage.js +1 -3
- package/lib/commands/ext-export.js +1 -2
- package/lib/commands/ext-install.js +1 -0
- package/lib/commands/ext-sdk-install.js +1 -0
- package/lib/commands/ext-update.js +1 -0
- package/lib/commands/firestore-backups-delete.js +1 -2
- package/lib/commands/firestore-backups-schedules-delete.js +1 -2
- package/lib/commands/firestore-databases-delete.js +1 -5
- package/lib/commands/firestore-delete.js +6 -6
- package/lib/commands/functions-artifacts-setpolicy.js +12 -2
- package/lib/commands/functions-config-export.js +5 -9
- package/lib/commands/functions-delete.js +5 -5
- package/lib/commands/functions-deletegcfartifacts.js +4 -4
- package/lib/commands/functions-secrets-destroy.js +8 -10
- package/lib/commands/functions-secrets-prune.js +12 -13
- package/lib/commands/functions-secrets-set.js +11 -15
- package/lib/commands/hosting-channel-create.js +1 -2
- package/lib/commands/hosting-channel-delete.js +4 -4
- package/lib/commands/hosting-channel-open.js +2 -3
- package/lib/commands/hosting-disable.js +5 -5
- package/lib/commands/hosting-sites-delete.js +4 -4
- package/lib/commands/init.js +41 -58
- package/lib/commands/login.js +1 -5
- package/lib/commands/logout.js +2 -3
- package/lib/commands/open.js +1 -2
- package/lib/commands/projects-create.js +1 -2
- package/lib/commands/remoteconfig-rollback.js +5 -5
- package/lib/commands/use.js +110 -110
- package/lib/config.js +1 -2
- package/lib/dataconnect/build.js +2 -4
- package/lib/dataconnect/client.js +20 -1
- package/lib/dataconnect/dataplaneClient.js +16 -1
- package/lib/dataconnect/schemaMigration.js +1 -6
- package/lib/dataconnect/types.js +5 -1
- package/lib/deploy/functions/params.js +10 -17
- package/lib/deploy/functions/prompts.js +6 -28
- package/lib/emulator/commandUtils.js +4 -13
- package/lib/emulator/controller.js +1 -2
- package/lib/emulator/downloadableEmulators.js +12 -12
- package/lib/emulator/initEmulators.js +9 -20
- package/lib/ensureApiEnabled.js +11 -1
- package/lib/extensions/askUserForEventsConfig.js +6 -15
- package/lib/extensions/askUserForParam.js +23 -52
- package/lib/extensions/checkProjectBilling.js +3 -9
- package/lib/extensions/diagnose.js +3 -6
- package/lib/extensions/extensionsHelper.js +17 -27
- package/lib/extensions/manifest.js +1 -2
- package/lib/extensions/tos.js +10 -2
- package/lib/extensions/utils.js +1 -10
- package/lib/frameworks/index.js +1 -2
- package/lib/frameworks/next/index.js +7 -4
- package/lib/frameworks/vite/index.js +1 -2
- package/lib/functions/secrets.js +14 -15
- package/lib/gcp/auth.js +29 -1
- package/lib/gcp/storage.js +19 -1
- package/lib/gif/fdcExperience.js +45 -0
- package/lib/hosting/interactive.js +2 -3
- package/lib/init/features/account.js +2 -4
- package/lib/init/features/database.js +11 -20
- package/lib/init/features/dataconnect/index.js +7 -14
- package/lib/init/features/dataconnect/sdk.js +15 -22
- package/lib/init/features/emulators.js +19 -41
- package/lib/init/features/firestore/index.js +2 -6
- package/lib/init/features/firestore/indexes.js +18 -31
- package/lib/init/features/firestore/rules.js +18 -31
- package/lib/init/features/functions/index.js +9 -16
- package/lib/init/features/functions/javascript.js +16 -30
- package/lib/init/features/functions/npm-dependencies.js +4 -8
- package/lib/init/features/functions/python.js +1 -3
- package/lib/init/features/functions/typescript.js +24 -43
- package/lib/init/features/genkit/index.js +23 -38
- package/lib/init/features/hosting/github.js +20 -51
- package/lib/init/features/hosting/index.js +36 -57
- package/lib/init/features/project.js +6 -16
- package/lib/init/features/remoteconfig.js +2 -8
- package/lib/init/features/storage.js +1 -3
- package/lib/management/apps.js +19 -44
- package/lib/management/projects.js +17 -28
- package/lib/mcp/index.js +13 -11
- package/lib/mcp/tools/auth/{disable_auth_user.js → disable_user.js} +3 -3
- package/lib/mcp/tools/auth/{get_auth_user.js → get_user.js} +4 -4
- package/lib/mcp/tools/auth/index.js +5 -4
- package/lib/mcp/tools/auth/{set_auth_claims.js → set_claims.js} +3 -3
- package/lib/mcp/tools/auth/set_sms_region_policy.js +36 -0
- package/lib/mcp/tools/core/consult_assistant.js +27 -0
- package/lib/mcp/tools/core/index.js +5 -3
- package/lib/mcp/tools/dataconnect/converter.js +50 -0
- package/lib/mcp/tools/dataconnect/execute_graphql.js +32 -0
- package/lib/mcp/tools/dataconnect/execute_graphql_read.js +32 -0
- package/lib/mcp/tools/dataconnect/execute_mutation.js +50 -0
- package/lib/mcp/tools/dataconnect/execute_query.js +50 -0
- package/lib/mcp/tools/dataconnect/generate_operation.js +33 -0
- package/lib/mcp/tools/dataconnect/generate_schema.js +25 -0
- package/lib/mcp/tools/dataconnect/get_connector.js +31 -0
- package/lib/mcp/tools/dataconnect/get_schema.js +31 -0
- package/lib/mcp/tools/dataconnect/index.js +20 -2
- package/lib/mcp/tools/dataconnect/{list_dataconnect_services.js → list_services.js} +4 -4
- package/lib/mcp/tools/{core/get_firebase_directory.js → directory/get_project_directory.js} +5 -5
- package/lib/mcp/tools/directory/index.js +6 -0
- package/lib/mcp/tools/{core/set_firebase_directory.js → directory/set_project_directory.js} +3 -3
- package/lib/mcp/tools/firestore/get_documents.js +2 -2
- package/lib/mcp/tools/firestore/{get_firestore_rules.js → get_rules.js} +6 -6
- package/lib/mcp/tools/firestore/index.js +2 -2
- package/lib/mcp/tools/index.js +26 -8
- package/lib/mcp/tools/storage/get_download_url.js +31 -0
- package/lib/mcp/tools/storage/get_rules.js +26 -0
- package/lib/mcp/tools/storage/index.js +6 -0
- package/lib/mcp/types.js +1 -1
- package/lib/prompt.js +78 -65
- package/lib/requireTosAcceptance.js +4 -0
- package/lib/rulesDeploy.js +10 -15
- package/lib/track.js +1 -34
- package/lib/utils.js +27 -5
- package/package.json +2 -3
- package/lib/mcp/tools/project/index.js +0 -7
- /package/lib/mcp/tools/{project → core}/get_project.js +0 -0
- /package/lib/mcp/tools/{project → core}/get_sdk_config.js +0 -0
- /package/lib/mcp/tools/{project → core}/list_apps.js +0 -0
|
@@ -33,8 +33,7 @@ async function doSetup(setup, config, options) {
|
|
|
33
33
|
hasHostingSite = false;
|
|
34
34
|
}
|
|
35
35
|
if (!hasHostingSite) {
|
|
36
|
-
const confirmCreate = await (0, prompt_1.
|
|
37
|
-
type: "confirm",
|
|
36
|
+
const confirmCreate = await (0, prompt_1.confirm)({
|
|
38
37
|
message: "A Firebase Hosting site is required to deploy. Would you like to create one now?",
|
|
39
38
|
default: true,
|
|
40
39
|
});
|
|
@@ -56,44 +55,33 @@ async function doSetup(setup, config, options) {
|
|
|
56
55
|
if (experiments.isEnabled("webframeworks")) {
|
|
57
56
|
if (discoveredFramework) {
|
|
58
57
|
const name = frameworks_1.WebFrameworks[discoveredFramework.framework].name;
|
|
59
|
-
await (0, prompt_1.
|
|
60
|
-
name: "useDiscoveredFramework",
|
|
61
|
-
type: "confirm",
|
|
62
|
-
default: true,
|
|
58
|
+
setup.hosting.useDiscoveredFramework = await (0, prompt_1.confirm)({
|
|
63
59
|
message: `Detected an existing ${name} codebase in the current directory, should we use this?`,
|
|
64
|
-
|
|
60
|
+
default: true,
|
|
61
|
+
});
|
|
65
62
|
}
|
|
66
63
|
if (setup.hosting.useDiscoveredFramework) {
|
|
67
64
|
setup.hosting.source = ".";
|
|
68
65
|
setup.hosting.useWebFrameworks = true;
|
|
69
66
|
}
|
|
70
67
|
else {
|
|
71
|
-
await (0, prompt_1.
|
|
72
|
-
name: "useWebFrameworks",
|
|
73
|
-
type: "confirm",
|
|
74
|
-
default: false,
|
|
75
|
-
message: `Do you want to use a web framework? (${clc.bold("experimental")})`,
|
|
76
|
-
}, setup.hosting);
|
|
68
|
+
setup.hosting.useWebFrameworks = await (0, prompt_1.confirm)(`Do you want to use a web framework? (${clc.bold("experimental")})`);
|
|
77
69
|
}
|
|
78
70
|
}
|
|
79
71
|
if (setup.hosting.useWebFrameworks) {
|
|
80
|
-
await (0, prompt_1.
|
|
81
|
-
name: "source",
|
|
82
|
-
type: "input",
|
|
83
|
-
default: "hosting",
|
|
72
|
+
setup.hosting.source = await (0, prompt_1.input)({
|
|
84
73
|
message: "What folder would you like to use for your web application's root directory?",
|
|
85
|
-
|
|
74
|
+
default: "hosting",
|
|
75
|
+
});
|
|
86
76
|
if (setup.hosting.source !== ".")
|
|
87
77
|
delete setup.hosting.useDiscoveredFramework;
|
|
88
78
|
discoveredFramework = await (0, frameworks_1.discover)((0, path_1.join)(config.projectDir, setup.hosting.source));
|
|
89
79
|
if (discoveredFramework) {
|
|
90
80
|
const name = frameworks_1.WebFrameworks[discoveredFramework.framework].name;
|
|
91
|
-
await (0, prompt_1.
|
|
92
|
-
name: "useDiscoveredFramework",
|
|
93
|
-
type: "confirm",
|
|
94
|
-
default: true,
|
|
81
|
+
setup.hosting.useDiscoveredFramework = await (0, prompt_1.confirm)({
|
|
95
82
|
message: `Detected an existing ${name} codebase in ${setup.hosting.source}, should we use this?`,
|
|
96
|
-
|
|
83
|
+
default: true,
|
|
84
|
+
});
|
|
97
85
|
}
|
|
98
86
|
if (setup.hosting.useDiscoveredFramework && discoveredFramework) {
|
|
99
87
|
setup.hosting.webFramework = discoveredFramework.framework;
|
|
@@ -108,24 +96,24 @@ async function doSetup(setup, config, options) {
|
|
|
108
96
|
}
|
|
109
97
|
}
|
|
110
98
|
const defaultChoice = (_a = choices.find(({ value }) => value === (discoveredFramework === null || discoveredFramework === void 0 ? void 0 : discoveredFramework.framework))) === null || _a === void 0 ? void 0 : _a.value;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
99
|
+
setup.hosting.whichFramework =
|
|
100
|
+
setup.hosting.whichFramework ||
|
|
101
|
+
(await (0, prompt_1.select)({
|
|
102
|
+
message: "Please choose the framework:",
|
|
103
|
+
default: defaultChoice,
|
|
104
|
+
choices,
|
|
105
|
+
}));
|
|
118
106
|
if (discoveredFramework)
|
|
119
107
|
(0, node_fs_1.rmSync)(setup.hosting.source, { recursive: true });
|
|
120
108
|
await frameworks_1.WebFrameworks[setup.hosting.whichFramework].init(setup, config);
|
|
121
109
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
110
|
+
setup.hosting.region =
|
|
111
|
+
setup.hosting.region ||
|
|
112
|
+
(await (0, prompt_1.select)({
|
|
113
|
+
message: "In which region would you like to host server-side content, if applicable?",
|
|
114
|
+
default: constants_1.DEFAULT_REGION,
|
|
115
|
+
choices: constants_1.ALLOWED_SSR_REGIONS.filter((region) => region.recommended),
|
|
116
|
+
}));
|
|
129
117
|
setup.config.hosting = {
|
|
130
118
|
source: setup.hosting.source,
|
|
131
119
|
ignore: DEFAULT_IGNORES,
|
|
@@ -140,31 +128,22 @@ async function doSetup(setup, config, options) {
|
|
|
140
128
|
logger_1.logger.info(`will contain Hosting assets to be uploaded with ${clc.bold("firebase deploy")}. If you`);
|
|
141
129
|
logger_1.logger.info("have a build process for your assets, use your build's output directory.");
|
|
142
130
|
logger_1.logger.info();
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
type: "confirm",
|
|
153
|
-
default: false,
|
|
154
|
-
message: "Configure as a single-page app (rewrite all urls to /index.html)?",
|
|
155
|
-
},
|
|
156
|
-
]);
|
|
131
|
+
setup.hosting.public =
|
|
132
|
+
setup.hosting.public ||
|
|
133
|
+
(await (0, prompt_1.input)({
|
|
134
|
+
message: "What do you want to use as your public directory?",
|
|
135
|
+
default: "public",
|
|
136
|
+
}));
|
|
137
|
+
setup.hosting.spa =
|
|
138
|
+
setup.hosting.spa ||
|
|
139
|
+
(await (0, prompt_1.confirm)("Configure as a single-page app (rewrite all urls to /index.html)?"));
|
|
157
140
|
setup.config.hosting = {
|
|
158
141
|
public: setup.hosting.public,
|
|
159
142
|
ignore: DEFAULT_IGNORES,
|
|
160
143
|
};
|
|
161
144
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
type: "confirm",
|
|
165
|
-
default: false,
|
|
166
|
-
message: "Set up automatic builds and deploys with GitHub?",
|
|
167
|
-
}, setup.hosting);
|
|
145
|
+
setup.hosting.github =
|
|
146
|
+
setup.hosting.github || (await (0, prompt_1.confirm)("Set up automatic builds and deploys with GitHub?"));
|
|
168
147
|
if (!setup.hosting.useWebFrameworks) {
|
|
169
148
|
if (setup.hosting.spa) {
|
|
170
149
|
setup.config.hosting.rewrites = [{ source: "**", destination: "/index.html" }];
|
|
@@ -6,8 +6,8 @@ const _ = require("lodash");
|
|
|
6
6
|
const error_1 = require("../../error");
|
|
7
7
|
const projects_1 = require("../../management/projects");
|
|
8
8
|
const logger_1 = require("../../logger");
|
|
9
|
-
const prompt_1 = require("../../prompt");
|
|
10
9
|
const utils = require("../../utils");
|
|
10
|
+
const prompt = require("../../prompt");
|
|
11
11
|
const OPTION_NO_PROJECT = "Don't set up a default project";
|
|
12
12
|
const OPTION_USE_PROJECT = "Use an existing project";
|
|
13
13
|
const OPTION_NEW_PROJECT = "Create a new project";
|
|
@@ -24,14 +24,11 @@ function toInitProjectInfo(projectMetaData) {
|
|
|
24
24
|
async function promptAndCreateNewProject() {
|
|
25
25
|
utils.logBullet("If you want to create a project in a Google Cloud organization or folder, please use " +
|
|
26
26
|
`"firebase projects:create" instead, and return to this command when you've created the project.`);
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
if (!promptAnswer.projectId) {
|
|
27
|
+
const { projectId, displayName } = await (0, projects_1.promptProjectCreation)();
|
|
28
|
+
if (!projectId) {
|
|
30
29
|
throw new error_1.FirebaseError("Project ID cannot be empty");
|
|
31
30
|
}
|
|
32
|
-
return await (0, projects_1.createFirebaseProjectAndLog)(
|
|
33
|
-
displayName: promptAnswer.displayName,
|
|
34
|
-
});
|
|
31
|
+
return await (0, projects_1.createFirebaseProjectAndLog)(projectId, { displayName });
|
|
35
32
|
}
|
|
36
33
|
async function promptAndAddFirebaseToCloudProject() {
|
|
37
34
|
const projectId = await (0, projects_1.promptAvailableProjectId)();
|
|
@@ -41,15 +38,8 @@ async function promptAndAddFirebaseToCloudProject() {
|
|
|
41
38
|
return await (0, projects_1.addFirebaseToCloudProjectAndLog)(projectId);
|
|
42
39
|
}
|
|
43
40
|
async function projectChoicePrompt(options) {
|
|
44
|
-
const choices = [
|
|
45
|
-
|
|
46
|
-
{ name: OPTION_NEW_PROJECT, value: OPTION_NEW_PROJECT },
|
|
47
|
-
{ name: OPTION_ADD_FIREBASE, value: OPTION_ADD_FIREBASE },
|
|
48
|
-
{ name: OPTION_NO_PROJECT, value: OPTION_NO_PROJECT },
|
|
49
|
-
];
|
|
50
|
-
const projectSetupOption = await (0, prompt_1.promptOnce)({
|
|
51
|
-
type: "list",
|
|
52
|
-
name: "id",
|
|
41
|
+
const choices = [OPTION_USE_PROJECT, OPTION_NEW_PROJECT, OPTION_ADD_FIREBASE, OPTION_NO_PROJECT];
|
|
42
|
+
const projectSetupOption = await prompt.select({
|
|
53
43
|
message: "Please select an option:",
|
|
54
44
|
choices,
|
|
55
45
|
});
|
|
@@ -6,9 +6,7 @@ const fsutils = require("../../fsutils");
|
|
|
6
6
|
const clc = require("colorette");
|
|
7
7
|
async function doSetup(setup, config) {
|
|
8
8
|
setup.config.remoteconfig = {};
|
|
9
|
-
const jsonFilePath = await (0, prompt_1.
|
|
10
|
-
type: "input",
|
|
11
|
-
name: "template",
|
|
9
|
+
const jsonFilePath = await (0, prompt_1.input)({
|
|
12
10
|
message: "What file should be used for your Remote Config template?",
|
|
13
11
|
default: "remoteconfig.template.json",
|
|
14
12
|
});
|
|
@@ -17,11 +15,7 @@ async function doSetup(setup, config) {
|
|
|
17
15
|
clc.bold(jsonFilePath) +
|
|
18
16
|
" already exists." +
|
|
19
17
|
" Do you want to overwrite the existing Remote Config template?";
|
|
20
|
-
const overwrite = await (0, prompt_1.
|
|
21
|
-
type: "confirm",
|
|
22
|
-
message: msg,
|
|
23
|
-
default: false,
|
|
24
|
-
});
|
|
18
|
+
const overwrite = await (0, prompt_1.confirm)(msg);
|
|
25
19
|
if (!overwrite) {
|
|
26
20
|
return;
|
|
27
21
|
}
|
|
@@ -13,9 +13,7 @@ async function doSetup(setup, config) {
|
|
|
13
13
|
logger_1.logger.info("uploads and downloads. You can keep these rules in your project directory");
|
|
14
14
|
logger_1.logger.info("and publish them with " + clc.bold("firebase deploy") + ".");
|
|
15
15
|
logger_1.logger.info();
|
|
16
|
-
const storageRulesFile = await (0, prompt_1.
|
|
17
|
-
type: "input",
|
|
18
|
-
name: "rules",
|
|
16
|
+
const storageRulesFile = await (0, prompt_1.input)({
|
|
19
17
|
message: "What file should be used for Storage Rules?",
|
|
20
18
|
default: "storage.rules",
|
|
21
19
|
});
|
package/lib/management/apps.js
CHANGED
|
@@ -11,23 +11,20 @@ const logger_1 = require("../logger");
|
|
|
11
11
|
const operation_poller_1 = require("../operation-poller");
|
|
12
12
|
const types_1 = require("../dataconnect/types");
|
|
13
13
|
const projectUtils_1 = require("../projectUtils");
|
|
14
|
-
const
|
|
14
|
+
const prompt = require("../prompt");
|
|
15
15
|
const projects_1 = require("./projects");
|
|
16
16
|
const fileUtils_1 = require("../dataconnect/fileUtils");
|
|
17
17
|
const utils_1 = require("../utils");
|
|
18
18
|
const TIMEOUT_MILLIS = 30000;
|
|
19
19
|
exports.APP_LIST_PAGE_SIZE = 100;
|
|
20
20
|
const CREATE_APP_API_REQUEST_TIMEOUT_MILLIS = 15000;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
default: "",
|
|
25
|
-
message: "What would you like to call your app?",
|
|
26
|
-
};
|
|
21
|
+
async function getDisplayName() {
|
|
22
|
+
return await prompt.input("What would you like to call your app?");
|
|
23
|
+
}
|
|
27
24
|
async function getPlatform(appDir, config) {
|
|
28
25
|
let targetPlatform = await (0, fileUtils_1.getPlatformFromFolder)(appDir);
|
|
29
26
|
if (targetPlatform === types_1.Platform.NONE) {
|
|
30
|
-
appDir = await (0,
|
|
27
|
+
appDir = await (0, utils_1.promptForDirectory)({
|
|
31
28
|
config,
|
|
32
29
|
relativeTo: appDir,
|
|
33
30
|
message: "We couldn't determine what kind of app you're using. Where is your app directory?",
|
|
@@ -46,9 +43,8 @@ async function getPlatform(appDir, config) {
|
|
|
46
43
|
{ name: "Web (JavaScript)", value: types_1.Platform.WEB },
|
|
47
44
|
{ name: "Android (Kotlin)", value: types_1.Platform.ANDROID },
|
|
48
45
|
];
|
|
49
|
-
targetPlatform = await
|
|
46
|
+
targetPlatform = await prompt.select({
|
|
50
47
|
message: "Which platform do you want to set up an SDK for? Note: We currently do not support automatically setting up C++ or Unity projects.",
|
|
51
|
-
type: "list",
|
|
52
48
|
choices: platforms,
|
|
53
49
|
});
|
|
54
50
|
}
|
|
@@ -69,21 +65,11 @@ https://firebase.google.com/docs/flutter/setup
|
|
|
69
65
|
exports.getPlatform = getPlatform;
|
|
70
66
|
async function initiateIosAppCreation(options) {
|
|
71
67
|
if (!options.nonInteractive) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
name: "bundleId",
|
|
78
|
-
message: "Please specify your iOS app bundle ID:",
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
type: "input",
|
|
82
|
-
default: "",
|
|
83
|
-
name: "appStoreId",
|
|
84
|
-
message: "Please specify your iOS app App Store ID:",
|
|
85
|
-
},
|
|
86
|
-
]);
|
|
68
|
+
options.displayName = options.displayName || (await getDisplayName());
|
|
69
|
+
options.bundleId =
|
|
70
|
+
options.bundleId || (await prompt.input("Please specify your iOS app bundle ID:"));
|
|
71
|
+
options.appStoreId =
|
|
72
|
+
options.appStoreId || (await prompt.input("Please specify your iOS app App Store ID:"));
|
|
87
73
|
}
|
|
88
74
|
if (!options.bundleId) {
|
|
89
75
|
throw new error_1.FirebaseError("Bundle ID for iOS app cannot be empty");
|
|
@@ -105,15 +91,9 @@ async function initiateIosAppCreation(options) {
|
|
|
105
91
|
}
|
|
106
92
|
async function initiateAndroidAppCreation(options) {
|
|
107
93
|
if (!options.nonInteractive) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
type: "input",
|
|
112
|
-
default: "",
|
|
113
|
-
name: "packageName",
|
|
114
|
-
message: "Please specify your Android app package name:",
|
|
115
|
-
},
|
|
116
|
-
]);
|
|
94
|
+
options.displayName = options.displayName || (await getDisplayName());
|
|
95
|
+
options.packageName =
|
|
96
|
+
options.packageName || (await prompt.input("Please specify your Android app package name:"));
|
|
117
97
|
}
|
|
118
98
|
if (!options.packageName) {
|
|
119
99
|
throw new error_1.FirebaseError("Package name for Android app cannot be empty");
|
|
@@ -134,7 +114,7 @@ async function initiateAndroidAppCreation(options) {
|
|
|
134
114
|
}
|
|
135
115
|
async function initiateWebAppCreation(options) {
|
|
136
116
|
if (!options.nonInteractive) {
|
|
137
|
-
|
|
117
|
+
options.displayName = options.displayName || (await getDisplayName());
|
|
138
118
|
}
|
|
139
119
|
if (!options.displayName) {
|
|
140
120
|
throw new error_1.FirebaseError("Display name for Web app cannot be empty");
|
|
@@ -198,8 +178,7 @@ async function selectAppInteractively(apps, appPlatform) {
|
|
|
198
178
|
value: app,
|
|
199
179
|
};
|
|
200
180
|
});
|
|
201
|
-
return await
|
|
202
|
-
type: "list",
|
|
181
|
+
return await prompt.select({
|
|
203
182
|
message: `Select the ${appPlatform === AppPlatform.ANY ? "" : appPlatform + " "}` +
|
|
204
183
|
"app to get the configuration data:",
|
|
205
184
|
choices,
|
|
@@ -436,11 +415,7 @@ async function writeConfigToFile(filename, nonInteractive, fileContents) {
|
|
|
436
415
|
if (nonInteractive) {
|
|
437
416
|
throw new error_1.FirebaseError(`${filename} already exists`);
|
|
438
417
|
}
|
|
439
|
-
const overwrite = await (
|
|
440
|
-
type: "confirm",
|
|
441
|
-
default: false,
|
|
442
|
-
message: `${filename} already exists. Do you want to overwrite?`,
|
|
443
|
-
});
|
|
418
|
+
const overwrite = await prompt.confirm(`${filename} already exists. Do you want to overwrite?`);
|
|
444
419
|
if (!overwrite) {
|
|
445
420
|
return false;
|
|
446
421
|
}
|
|
@@ -544,7 +519,7 @@ async function findIntelligentPathForIOS(appDir, options) {
|
|
|
544
519
|
}
|
|
545
520
|
let outputPath = null;
|
|
546
521
|
if (!options.nonInteractive) {
|
|
547
|
-
outputPath = await (0,
|
|
522
|
+
outputPath = await (0, utils_1.promptForDirectory)({
|
|
548
523
|
config: options.config,
|
|
549
524
|
message: `We weren't able to automatically determine the output directory. Where would you like to output your config file?`,
|
|
550
525
|
relativeTo: appDir,
|
|
@@ -579,7 +554,7 @@ async function findIntelligentPathForAndroid(appDir, options) {
|
|
|
579
554
|
return module;
|
|
580
555
|
}
|
|
581
556
|
if (!options.nonInteractive) {
|
|
582
|
-
module = await (0,
|
|
557
|
+
module = await (0, utils_1.promptForDirectory)({
|
|
583
558
|
config: options.config,
|
|
584
559
|
message: `We weren't able to automatically determine the output directory. Where would you like to output your config file?`,
|
|
585
560
|
});
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getProject = exports.getFirebaseProject = exports.listFirebaseProjects = exports.getAvailableCloudProjectPage = exports.getFirebaseProjectPage = exports.addFirebaseToCloudProject = exports.createCloudProject = exports.promptAvailableProjectId = exports.getOrPromptProject = exports.addFirebaseToCloudProjectAndLog = exports.createFirebaseProjectAndLog = exports.
|
|
3
|
+
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;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const ora = require("ora");
|
|
6
6
|
const apiv2_1 = require("../apiv2");
|
|
7
7
|
const error_1 = require("../error");
|
|
8
8
|
const operation_poller_1 = require("../operation-poller");
|
|
9
|
-
const
|
|
9
|
+
const prompt = require("../prompt");
|
|
10
10
|
const api = require("../api");
|
|
11
11
|
const logger_1 = require("../logger");
|
|
12
12
|
const utils = require("../utils");
|
|
13
|
+
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
13
14
|
const TIMEOUT_MILLIS = 30000;
|
|
14
15
|
const MAXIMUM_PROMPT_LIST = 100;
|
|
15
16
|
const PROJECT_LIST_PAGE_SIZE = 1000;
|
|
@@ -19,11 +20,8 @@ var ProjectParentResourceType;
|
|
|
19
20
|
ProjectParentResourceType["ORGANIZATION"] = "organization";
|
|
20
21
|
ProjectParentResourceType["FOLDER"] = "folder";
|
|
21
22
|
})(ProjectParentResourceType = exports.ProjectParentResourceType || (exports.ProjectParentResourceType = {}));
|
|
22
|
-
|
|
23
|
-
{
|
|
24
|
-
type: "input",
|
|
25
|
-
name: "projectId",
|
|
26
|
-
default: "",
|
|
23
|
+
async function promptProjectCreation() {
|
|
24
|
+
const projectId = await prompt.input({
|
|
27
25
|
message: "Please specify a unique project id " +
|
|
28
26
|
`(${clc.yellow("warning")}: cannot be modified afterward) [6-30 characters]:\n`,
|
|
29
27
|
validate: (projectId) => {
|
|
@@ -37,11 +35,9 @@ exports.PROJECTS_CREATE_QUESTIONS = [
|
|
|
37
35
|
return true;
|
|
38
36
|
}
|
|
39
37
|
},
|
|
40
|
-
}
|
|
41
|
-
{
|
|
42
|
-
|
|
43
|
-
name: "displayName",
|
|
44
|
-
default: (answers) => answers.projectId,
|
|
38
|
+
});
|
|
39
|
+
const displayName = await prompt.input({
|
|
40
|
+
default: projectId,
|
|
45
41
|
message: "What would you like to call your project? (defaults to your project ID)",
|
|
46
42
|
validate: (displayName) => {
|
|
47
43
|
if (displayName.length < 4) {
|
|
@@ -54,8 +50,10 @@ exports.PROJECTS_CREATE_QUESTIONS = [
|
|
|
54
50
|
return true;
|
|
55
51
|
}
|
|
56
52
|
},
|
|
57
|
-
}
|
|
58
|
-
|
|
53
|
+
});
|
|
54
|
+
return { projectId, displayName };
|
|
55
|
+
}
|
|
56
|
+
exports.promptProjectCreation = promptProjectCreation;
|
|
59
57
|
const firebaseAPIClient = new apiv2_1.Client({
|
|
60
58
|
urlPrefix: api.firebaseApiOrigin(),
|
|
61
59
|
auth: true,
|
|
@@ -130,10 +128,7 @@ async function selectProjectInteractively(pageSize = MAXIMUM_PROMPT_LIST) {
|
|
|
130
128
|
return selectProjectFromList(projects);
|
|
131
129
|
}
|
|
132
130
|
async function selectProjectByPrompting() {
|
|
133
|
-
const projectId = await (
|
|
134
|
-
type: "input",
|
|
135
|
-
message: "Please input the project ID you would like to use:",
|
|
136
|
-
});
|
|
131
|
+
const projectId = await prompt.input("Please input the project ID you would like to use:");
|
|
137
132
|
return await getFirebaseProject(projectId);
|
|
138
133
|
}
|
|
139
134
|
async function selectProjectFromList(projects = []) {
|
|
@@ -150,9 +145,7 @@ async function selectProjectFromList(projects = []) {
|
|
|
150
145
|
utils.logBullet(`Don't want to scroll through all your projects? If you know your project ID, ` +
|
|
151
146
|
`you can initialize it directly using ${clc.bold("firebase init --project <project_id>")}.\n`);
|
|
152
147
|
}
|
|
153
|
-
const projectId = await
|
|
154
|
-
type: "list",
|
|
155
|
-
name: "id",
|
|
148
|
+
const projectId = await prompt.select({
|
|
156
149
|
message: "Select a default Firebase project for this directory:",
|
|
157
150
|
choices,
|
|
158
151
|
});
|
|
@@ -172,10 +165,7 @@ async function promptAvailableProjectId() {
|
|
|
172
165
|
throw new error_1.FirebaseError("There are no available Google Cloud projects to add Firebase services.");
|
|
173
166
|
}
|
|
174
167
|
if (nextPageToken) {
|
|
175
|
-
return await (
|
|
176
|
-
type: "input",
|
|
177
|
-
message: "Please input the ID of the Google Cloud Project you would like to add Firebase:",
|
|
178
|
-
});
|
|
168
|
+
return await prompt.input("Please input the ID of the Google Cloud Project you would like to add Firebase:");
|
|
179
169
|
}
|
|
180
170
|
else {
|
|
181
171
|
const choices = projects
|
|
@@ -188,9 +178,7 @@ async function promptAvailableProjectId() {
|
|
|
188
178
|
};
|
|
189
179
|
})
|
|
190
180
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
191
|
-
return await
|
|
192
|
-
type: "list",
|
|
193
|
-
name: "id",
|
|
181
|
+
return await prompt.select({
|
|
194
182
|
message: "Select the Google Cloud Platform project you would like to add Firebase:",
|
|
195
183
|
choices,
|
|
196
184
|
});
|
|
@@ -348,6 +336,7 @@ async function getFirebaseProject(projectId) {
|
|
|
348
336
|
}
|
|
349
337
|
exports.getFirebaseProject = getFirebaseProject;
|
|
350
338
|
async function getProject(projectId) {
|
|
339
|
+
await (0, ensureApiEnabled_1.bestEffortEnsure)(projectId, api.resourceManagerOrigin(), "firebase", true);
|
|
351
340
|
const response = await resourceManagerClient.get(`/projects/${projectId}`);
|
|
352
341
|
return response.body;
|
|
353
342
|
}
|
package/lib/mcp/index.js
CHANGED
|
@@ -7,11 +7,12 @@ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
|
7
7
|
const util_js_1 = require("./util.js");
|
|
8
8
|
const index_js_2 = require("./tools/index.js");
|
|
9
9
|
const configstore_js_1 = require("../configstore.js");
|
|
10
|
-
const index_js_3 = require("./tools/core/index.js");
|
|
11
10
|
const command_js_1 = require("../command.js");
|
|
12
11
|
const requireAuth_js_1 = require("../requireAuth.js");
|
|
13
12
|
const projectUtils_js_1 = require("../projectUtils.js");
|
|
14
13
|
const errors_js_1 = require("./errors.js");
|
|
14
|
+
const track_js_1 = require("../track.js");
|
|
15
|
+
const config_js_1 = require("../config.js");
|
|
15
16
|
const SERVER_VERSION = "0.0.1";
|
|
16
17
|
const PROJECT_ROOT_KEY = "mcp.projectRoot";
|
|
17
18
|
const cmd = new command_js_1.Command("experimental:mcp").before(requireAuth_js_1.requireAuth);
|
|
@@ -29,21 +30,14 @@ class FirebaseMcpServer {
|
|
|
29
30
|
this.fixedRoot = true;
|
|
30
31
|
}
|
|
31
32
|
get availableTools() {
|
|
32
|
-
|
|
33
|
-
const toolDefs = this.fixedRoot ? [] : [...index_js_3.coreTools];
|
|
34
|
-
const activeFeatures = ((_a = this.activeFeatures) === null || _a === void 0 ? void 0 : _a.length)
|
|
35
|
-
? this.activeFeatures
|
|
36
|
-
: Object.keys(index_js_2.tools);
|
|
37
|
-
for (const key of activeFeatures || []) {
|
|
38
|
-
toolDefs.push(...index_js_2.tools[key]);
|
|
39
|
-
}
|
|
40
|
-
return toolDefs;
|
|
33
|
+
return (0, index_js_2.availableTools)(!!this.fixedRoot, this.activeFeatures);
|
|
41
34
|
}
|
|
42
35
|
getTool(name) {
|
|
43
36
|
return this.availableTools.find((t) => t.mcp.name === name) || null;
|
|
44
37
|
}
|
|
45
38
|
async mcpListTools() {
|
|
46
39
|
const hasActiveProject = !!(await this.getProjectId());
|
|
40
|
+
await (0, track_js_1.trackGA4)("mcp_list_tools", {});
|
|
47
41
|
return {
|
|
48
42
|
tools: this.availableTools.map((t) => t.mcp),
|
|
49
43
|
_meta: {
|
|
@@ -95,9 +89,17 @@ class FirebaseMcpServer {
|
|
|
95
89
|
if (((_b = tool.mcp._meta) === null || _b === void 0 ? void 0 : _b.requiresProject) && !projectId)
|
|
96
90
|
return errors_js_1.NO_PROJECT_ERROR;
|
|
97
91
|
try {
|
|
98
|
-
|
|
92
|
+
const config = config_js_1.Config.load({ cwd: this.projectRoot });
|
|
93
|
+
const res = await tool.fn(toolArgs, {
|
|
94
|
+
projectId: await this.getProjectId(),
|
|
95
|
+
host: this,
|
|
96
|
+
config,
|
|
97
|
+
});
|
|
98
|
+
await (0, track_js_1.trackGA4)("mcp_tool_call", { tool_name: toolName, error: res.isError ? 1 : 0 });
|
|
99
|
+
return res;
|
|
99
100
|
}
|
|
100
101
|
catch (err) {
|
|
102
|
+
await (0, track_js_1.trackGA4)("mcp_tool_call", { tool_name: toolName, error: 1 });
|
|
101
103
|
return (0, util_js_1.mcpError)(err);
|
|
102
104
|
}
|
|
103
105
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.disable_user = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const tool_js_1 = require("../../tool.js");
|
|
6
6
|
const util_js_1 = require("../../util.js");
|
|
7
7
|
const auth_js_1 = require("../../../gcp/auth.js");
|
|
8
|
-
exports.
|
|
9
|
-
name: "
|
|
8
|
+
exports.disable_user = (0, tool_js_1.tool)({
|
|
9
|
+
name: "disable_user",
|
|
10
10
|
description: "Disables or enables a user based on a UID.",
|
|
11
11
|
inputSchema: zod_1.z.object({
|
|
12
12
|
uid: zod_1.z.string().describe("The localId or UID of the user to disable or enable"),
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.get_user = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const tool_js_1 = require("../../tool.js");
|
|
6
6
|
const util_js_1 = require("../../util.js");
|
|
7
7
|
const auth_js_1 = require("../../../gcp/auth.js");
|
|
8
|
-
exports.
|
|
9
|
-
name: "
|
|
8
|
+
exports.get_user = (0, tool_js_1.tool)({
|
|
9
|
+
name: "get_user",
|
|
10
10
|
description: "Retrieves a user based on an email address, phone number, or UID.",
|
|
11
11
|
inputSchema: zod_1.z.object({
|
|
12
12
|
email: zod_1.z.string().optional(),
|
|
@@ -23,7 +23,7 @@ exports.get_auth_user = (0, tool_js_1.tool)({
|
|
|
23
23
|
},
|
|
24
24
|
}, async ({ email, phoneNumber, uid }, { projectId }) => {
|
|
25
25
|
if (email === undefined && phoneNumber === undefined && uid === undefined) {
|
|
26
|
-
return (0, util_js_1.mcpError)(`No user identifier supplied in
|
|
26
|
+
return (0, util_js_1.mcpError)(`No user identifier supplied in auth_get_user tool`);
|
|
27
27
|
}
|
|
28
28
|
return (0, util_js_1.toContent)(await (0, auth_js_1.findUser)(projectId, email, phoneNumber, uid));
|
|
29
29
|
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.authTools = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
4
|
+
const get_user_js_1 = require("./get_user.js");
|
|
5
|
+
const disable_user_js_1 = require("./disable_user.js");
|
|
6
|
+
const set_claims_js_1 = require("./set_claims.js");
|
|
7
|
+
const set_sms_region_policy_js_1 = require("./set_sms_region_policy.js");
|
|
8
|
+
exports.authTools = [get_user_js_1.get_user, disable_user_js_1.disable_user, set_claims_js_1.set_claim, set_sms_region_policy_js_1.set_sms_region_policy];
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.set_claim = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const tool_js_1 = require("../../tool.js");
|
|
6
6
|
const util_js_1 = require("../../util.js");
|
|
7
7
|
const auth_js_1 = require("../../../gcp/auth.js");
|
|
8
|
-
exports.
|
|
9
|
-
name: "
|
|
8
|
+
exports.set_claim = (0, tool_js_1.tool)({
|
|
9
|
+
name: "set_claims",
|
|
10
10
|
description: "Sets custom claims on a specific user's account. Use to create trusted values associated with a user e.g. marking them as an admin. Claims are limited in size and should be succinct in name and value.",
|
|
11
11
|
inputSchema: zod_1.z.object({
|
|
12
12
|
uid: zod_1.z.string().describe("the UID of the user to update"),
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.set_sms_region_policy = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const tool_js_1 = require("../../tool.js");
|
|
6
|
+
const util_js_1 = require("../../util.js");
|
|
7
|
+
const auth_js_1 = require("../../../gcp/auth.js");
|
|
8
|
+
exports.set_sms_region_policy = (0, tool_js_1.tool)({
|
|
9
|
+
name: "set_sms_region_policy",
|
|
10
|
+
description: "Sets an SMS Region Policy for Firebase Auth to restrict the regions which can receive text messages based on an ALLOW or DENY list of country codes. This policy will override any existing policies when set.",
|
|
11
|
+
inputSchema: zod_1.z.object({
|
|
12
|
+
policy_type: zod_1.z
|
|
13
|
+
.enum(["ALLOW", "DENY"])
|
|
14
|
+
.describe("with an ALLOW policy, only the specified country codes can use SMS auth. with a DENY policy, all countries can use SMS auth except the ones specified"),
|
|
15
|
+
country_codes: zod_1.z
|
|
16
|
+
.array(zod_1.z.string())
|
|
17
|
+
.describe("the country codes to allow or deny based on ISO 3166"),
|
|
18
|
+
}),
|
|
19
|
+
annotations: {
|
|
20
|
+
title: "Set the SMS Region Policy on your Firebase Project",
|
|
21
|
+
idempotentHint: true,
|
|
22
|
+
destructiveHint: true,
|
|
23
|
+
},
|
|
24
|
+
_meta: {
|
|
25
|
+
requiresProject: true,
|
|
26
|
+
requiresAuth: true,
|
|
27
|
+
},
|
|
28
|
+
}, async ({ policy_type, country_codes }, { projectId }) => {
|
|
29
|
+
country_codes = country_codes.map((code) => {
|
|
30
|
+
return code.toUpperCase();
|
|
31
|
+
});
|
|
32
|
+
if (policy_type === "ALLOW") {
|
|
33
|
+
return (0, util_js_1.toContent)(await (0, auth_js_1.setAllowSmsRegionPolicy)(projectId, country_codes));
|
|
34
|
+
}
|
|
35
|
+
return (0, util_js_1.toContent)(await (0, auth_js_1.setDenySmsRegionPolicy)(projectId, country_codes));
|
|
36
|
+
});
|