firebase-tools 15.11.0 → 15.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/agentSkills.js +70 -0
- package/lib/api.js +3 -1
- package/lib/apphosting/backend.js +22 -3
- package/lib/bin/mcp.js +5 -1
- package/lib/commands/apphosting-backends-create.js +19 -2
- package/lib/commands/apphosting-backends-list.js +21 -5
- package/lib/commands/functions-delete.js +1 -0
- package/lib/commands/functions-export.js +40 -0
- package/lib/commands/index.js +3 -0
- package/lib/commands/init.js +1 -0
- package/lib/deploy/apphosting/deploy.js +11 -6
- package/lib/deploy/apphosting/prepare.js +21 -1
- package/lib/deploy/apphosting/release.js +2 -5
- package/lib/deploy/apphosting/util.js +45 -2
- package/lib/deploy/functions/prepare.js +4 -1
- package/lib/deploy/functions/release/fabricator.js +4 -3
- package/lib/deploy/functions/release/index.js +5 -0
- package/lib/deploy/functions/services/ailogic.js +68 -0
- package/lib/deploy/functions/services/index.js +4 -0
- package/lib/emulator/downloadableEmulatorInfo.json +24 -24
- package/lib/emulator/storage/rules/manager.js +10 -3
- package/lib/emulator/storage/rules/runtime.js +9 -7
- package/lib/experiments.js +22 -0
- package/lib/firebase_studio/migrate.js +30 -61
- package/lib/functions/iac/export.js +36 -0
- package/lib/functions/iac/terraform.js +146 -0
- package/lib/gcp/ailogic.js +108 -0
- package/lib/gcp/cloudfunctionsv2.js +24 -0
- package/lib/init/features/agentSkills.js +26 -0
- package/lib/init/features/dataconnect/sdk.js +26 -12
- package/lib/init/features/index.js +4 -1
- package/lib/init/index.js +6 -0
- package/lib/tsconfig.publish.tsbuildinfo +1 -1
- package/lib/utils.js +8 -0
- package/package.json +5 -3
- package/standalone/package.json +1 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AILogicService = exports.AI_LOGIC_EVENTS = exports.AI_LOGIC_AFTER_GENERATE_CONTENT = exports.AI_LOGIC_BEFORE_GENERATE_CONTENT = void 0;
|
|
4
|
+
exports.isAILogicEvent = isAILogicEvent;
|
|
5
|
+
const backend = require("../backend");
|
|
6
|
+
const error_1 = require("../../../error");
|
|
7
|
+
const ailogicApi = require("../../../gcp/ailogic");
|
|
8
|
+
exports.AI_LOGIC_BEFORE_GENERATE_CONTENT = "firebase.vertexai.v1beta.beforeGenerateContent";
|
|
9
|
+
exports.AI_LOGIC_AFTER_GENERATE_CONTENT = "firebase.vertexai.v1beta.afterGenerateContent";
|
|
10
|
+
exports.AI_LOGIC_EVENTS = [
|
|
11
|
+
exports.AI_LOGIC_BEFORE_GENERATE_CONTENT,
|
|
12
|
+
exports.AI_LOGIC_AFTER_GENERATE_CONTENT,
|
|
13
|
+
];
|
|
14
|
+
function isAILogicEvent(endpoint) {
|
|
15
|
+
if (!backend.isBlockingTriggered(endpoint)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return exports.AI_LOGIC_EVENTS.includes(endpoint.blockingTrigger.eventType);
|
|
19
|
+
}
|
|
20
|
+
class AILogicService {
|
|
21
|
+
constructor() {
|
|
22
|
+
this.ensureTriggerRegion = () => Promise.resolve();
|
|
23
|
+
this.name = "ailogic";
|
|
24
|
+
this.api = "firebasevertexai.googleapis.com";
|
|
25
|
+
}
|
|
26
|
+
validateTrigger(endpoint, wantBackend) {
|
|
27
|
+
if (!isAILogicEvent(endpoint)) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const eventType = endpoint.blockingTrigger.eventType;
|
|
31
|
+
const regionalWebhook = !!endpoint.blockingTrigger.options?.regionalWebhook;
|
|
32
|
+
const conflict = backend.allEndpoints(wantBackend).some((ep) => {
|
|
33
|
+
if (!isAILogicEvent(ep)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (ep.blockingTrigger.eventType !== eventType || ep.id === endpoint.id) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
if (regionalWebhook) {
|
|
40
|
+
return ep.blockingTrigger.options?.regionalWebhook && ep.region === endpoint.region;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return !ep.blockingTrigger.options?.regionalWebhook;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
if (conflict) {
|
|
47
|
+
if (regionalWebhook) {
|
|
48
|
+
throw new error_1.FirebaseError(`Can only create at most one regional AI Logic Trigger for ${eventType} in region ${endpoint.region}`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
throw new error_1.FirebaseError(`Can only create at most one global AI Logic Trigger for ${eventType}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async registerTrigger(ep) {
|
|
56
|
+
if (!isAILogicEvent(ep)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
await ailogicApi.upsertBlockingFunction(ep);
|
|
60
|
+
}
|
|
61
|
+
async unregisterTrigger(ep) {
|
|
62
|
+
if (!isAILogicEvent(ep)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
await ailogicApi.deleteBlockingFunction(ep);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.AILogicService = AILogicService;
|
|
@@ -11,6 +11,7 @@ const remoteConfig_1 = require("./remoteConfig");
|
|
|
11
11
|
const testLab_1 = require("./testLab");
|
|
12
12
|
const firestore_1 = require("./firestore");
|
|
13
13
|
const dataconnect_1 = require("./dataconnect");
|
|
14
|
+
const ailogic_1 = require("./ailogic");
|
|
14
15
|
const noop = () => Promise.resolve();
|
|
15
16
|
exports.noop = noop;
|
|
16
17
|
const noopProjectBindings = () => Promise.resolve([]);
|
|
@@ -51,6 +52,7 @@ const firebaseAlertsService = {
|
|
|
51
52
|
unregisterTrigger: exports.noop,
|
|
52
53
|
};
|
|
53
54
|
const authBlockingService = new auth_1.AuthBlockingService();
|
|
55
|
+
const aiLogicService = new ailogic_1.AILogicService();
|
|
54
56
|
const databaseService = {
|
|
55
57
|
name: "database",
|
|
56
58
|
api: "firebasedatabase.googleapis.com",
|
|
@@ -122,6 +124,8 @@ const EVENT_SERVICE_MAPPING = {
|
|
|
122
124
|
"google.cloud.firestore.document.v1.updated.withAuthContext": firestoreService,
|
|
123
125
|
"google.cloud.firestore.document.v1.deleted.withAuthContext": firestoreService,
|
|
124
126
|
"google.firebase.dataconnect.connector.v1.mutationExecuted": dataconnectService,
|
|
127
|
+
"firebase.vertexai.v1beta.beforeGenerateContent": aiLogicService,
|
|
128
|
+
"firebase.vertexai.v1beta.afterGenerateContent": aiLogicService,
|
|
125
129
|
};
|
|
126
130
|
function serviceForEndpoint(endpoint) {
|
|
127
131
|
if (backend.isEventTriggered(endpoint)) {
|
|
@@ -54,36 +54,36 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dataconnect": {
|
|
56
56
|
"darwin": {
|
|
57
|
-
"version": "3.3.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.3.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.
|
|
57
|
+
"version": "3.3.1",
|
|
58
|
+
"expectedSize": 32105392,
|
|
59
|
+
"expectedChecksum": "dfe6ff725864c37889c238aac24be304",
|
|
60
|
+
"expectedChecksumSHA256": "1ccfebb4c0ff85f0503b907aa589658774bb6d4fac0180c3fa6bdff3a865090d",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.3.1",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.1"
|
|
63
63
|
},
|
|
64
64
|
"darwin_arm64": {
|
|
65
|
-
"version": "3.3.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.3.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.
|
|
65
|
+
"version": "3.3.1",
|
|
66
|
+
"expectedSize": 30287922,
|
|
67
|
+
"expectedChecksum": "59af3cb51c78e8114d634afea811f8cc",
|
|
68
|
+
"expectedChecksumSHA256": "75bde31d15c4cc14de7071ff50cdd0d8fb644491e0c9ee3c1da82fdaf8d976a9",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.3.1",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.1"
|
|
71
71
|
},
|
|
72
72
|
"win32": {
|
|
73
|
-
"version": "3.3.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.3.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.
|
|
73
|
+
"version": "3.3.1",
|
|
74
|
+
"expectedSize": 32148992,
|
|
75
|
+
"expectedChecksum": "3205dc92bbc7edb4c5821641486dc70c",
|
|
76
|
+
"expectedChecksumSHA256": "fa7796077728414b0682f6ccf1df0fc7b2f56bb590f1e62698dbb7c1ddaf0fb0",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.3.1",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.1.exe"
|
|
79
79
|
},
|
|
80
80
|
"linux": {
|
|
81
|
-
"version": "3.3.
|
|
82
|
-
"expectedSize":
|
|
83
|
-
"expectedChecksum": "
|
|
84
|
-
"expectedChecksumSHA256": "
|
|
85
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.3.
|
|
86
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.
|
|
81
|
+
"version": "3.3.1",
|
|
82
|
+
"expectedSize": 31264952,
|
|
83
|
+
"expectedChecksum": "85ffdef78810e0074ac94c453a200daa",
|
|
84
|
+
"expectedChecksumSHA256": "f232e03165c28f72bc99dfbd2dde1bcaf8298045817b60d72d407da7dae9e9f1",
|
|
85
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.3.1",
|
|
86
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.1"
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -34,9 +34,16 @@ class DefaultStorageRulesManager {
|
|
|
34
34
|
.watch(rulesFile, { persistent: true, ignoreInitial: true })
|
|
35
35
|
.on("change", async () => {
|
|
36
36
|
await new Promise((res) => setTimeout(res, 5));
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
try {
|
|
38
|
+
const content = (0, fsutils_1.readFile)(rulesFile);
|
|
39
|
+
this._rules.content = content;
|
|
40
|
+
this._logger.logLabeled("BULLET", "storage", "Change detected, updating rules for Cloud Storage...");
|
|
41
|
+
await this.loadRuleset();
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
45
|
+
this._logger.logLabeled("DEBUG", "storage", `A rule file change was detected, but there was an error reading it: ${message}`);
|
|
46
|
+
}
|
|
40
47
|
});
|
|
41
48
|
}
|
|
42
49
|
async loadRuleset() {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.StorageRulesRuntime = exports.StorageRulesIssues = exports.StorageRulesetInstance = void 0;
|
|
4
|
+
exports.createAuthExpressionValue = createAuthExpressionValue;
|
|
4
5
|
const cross_spawn_1 = require("cross-spawn");
|
|
5
6
|
const error_1 = require("../../../error");
|
|
6
7
|
const AsyncLock = require("async-lock");
|
|
@@ -332,14 +333,15 @@ function createAuthExpressionValue(opts) {
|
|
|
332
333
|
if (!opts.token) {
|
|
333
334
|
return toExpressionValue(null);
|
|
334
335
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
uid: tokenPayload.user_id,
|
|
339
|
-
token: tokenPayload,
|
|
340
|
-
};
|
|
341
|
-
return toExpressionValue(jsonValue);
|
|
336
|
+
const tokenPayload = jwt.decode(opts.token, { json: true });
|
|
337
|
+
if (typeof tokenPayload !== "object" || !tokenPayload) {
|
|
338
|
+
return toExpressionValue(null);
|
|
342
339
|
}
|
|
340
|
+
const jsonValue = {
|
|
341
|
+
uid: "user_id" in tokenPayload ? tokenPayload.user_id : undefined,
|
|
342
|
+
token: tokenPayload,
|
|
343
|
+
};
|
|
344
|
+
return toExpressionValue(jsonValue);
|
|
343
345
|
}
|
|
344
346
|
function createRequestExpressionValue(opts) {
|
|
345
347
|
const fields = {
|
package/lib/experiments.js
CHANGED
|
@@ -58,10 +58,22 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
58
58
|
shortDescription: "Functions created using the V2 API target Cloud Run Functions (not production ready)",
|
|
59
59
|
public: false,
|
|
60
60
|
},
|
|
61
|
+
functionsiac: {
|
|
62
|
+
shortDescription: "Exports functions IaC code",
|
|
63
|
+
public: false,
|
|
64
|
+
},
|
|
61
65
|
functionsrunapionly: {
|
|
62
66
|
shortDescription: "Use Cloud Run API to list v2 functions",
|
|
63
67
|
public: false,
|
|
64
68
|
},
|
|
69
|
+
bypassfunctionsdeprecationcheck: {
|
|
70
|
+
shortDescription: "Bypass Functions check for old runtimes",
|
|
71
|
+
fullDescription: "Bypasses the local check for whether a functions runtime is " +
|
|
72
|
+
"decommissioned. This does not, by itself, allow you to deploy a function with a " +
|
|
73
|
+
"decommissioned runtime, as there are server-side checks as well.",
|
|
74
|
+
public: false,
|
|
75
|
+
default: false,
|
|
76
|
+
},
|
|
65
77
|
emulatoruisnapshot: {
|
|
66
78
|
shortDescription: "Load pre-release versions of the emulator UI",
|
|
67
79
|
},
|
|
@@ -109,6 +121,11 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
109
121
|
default: false,
|
|
110
122
|
public: false,
|
|
111
123
|
},
|
|
124
|
+
abiu: {
|
|
125
|
+
shortDescription: "Enable App Hosting ABIU and runtime selection",
|
|
126
|
+
default: false,
|
|
127
|
+
public: false,
|
|
128
|
+
},
|
|
112
129
|
dataconnect: {
|
|
113
130
|
shortDescription: "Deprecated. Previosuly, enabled Data Connect related features.",
|
|
114
131
|
fullDescription: "Deprecated. Previously, enabled Data Connect related features.",
|
|
@@ -150,6 +167,11 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
150
167
|
default: true,
|
|
151
168
|
public: false,
|
|
152
169
|
},
|
|
170
|
+
fdcrealtime: {
|
|
171
|
+
shortDescription: "Enable Firebase Data Connect realtime feature.",
|
|
172
|
+
default: false,
|
|
173
|
+
public: false,
|
|
174
|
+
},
|
|
153
175
|
});
|
|
154
176
|
function isValidExperiment(name) {
|
|
155
177
|
return Object.keys(exports.ALL_EXPERIMENTS).includes(name);
|
|
@@ -17,7 +17,8 @@ const secrets_1 = require("../apphosting/secrets");
|
|
|
17
17
|
const env = require("../functions/env");
|
|
18
18
|
const error_1 = require("../error");
|
|
19
19
|
const os = require("os");
|
|
20
|
-
|
|
20
|
+
const agentSkills_1 = require("../agentSkills");
|
|
21
|
+
async function setupAntigravityMcpServer(rootPath, appType, nonInteractive) {
|
|
21
22
|
const mcpConfigDir = path.join(os.homedir(), ".gemini", "antigravity");
|
|
22
23
|
const mcpConfigPath = path.join(mcpConfigDir, "mcp_config.json");
|
|
23
24
|
let mcpConfig = { mcpServers: {} };
|
|
@@ -43,6 +44,7 @@ async function setupAntigravityMcpServer(rootPath, appType) {
|
|
|
43
44
|
const confirmFirebase = await prompt.confirm({
|
|
44
45
|
message: "Would you like to enable the Firebase MCP server for Antigravity?",
|
|
45
46
|
default: true,
|
|
47
|
+
nonInteractive,
|
|
46
48
|
});
|
|
47
49
|
if (confirmFirebase) {
|
|
48
50
|
mcpConfig.mcpServers["firebase"] = {
|
|
@@ -66,6 +68,7 @@ async function setupAntigravityMcpServer(rootPath, appType) {
|
|
|
66
68
|
const confirmDart = await prompt.confirm({
|
|
67
69
|
message: "Would you like to enable the Dart MCP server for Antigravity?",
|
|
68
70
|
default: true,
|
|
71
|
+
nonInteractive,
|
|
69
72
|
});
|
|
70
73
|
if (confirmDart) {
|
|
71
74
|
mcpConfig.mcpServers["dart"] = {
|
|
@@ -89,8 +92,7 @@ async function setupAntigravityMcpServer(rootPath, appType) {
|
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
catch (err) {
|
|
92
|
-
|
|
93
|
-
utils.logWarning(`Could not configure Antigravity MCP server: ${message}`);
|
|
95
|
+
utils.logWarning(`Could not configure Antigravity MCP server: ${(0, error_1.getErrMsg)(err)}`);
|
|
94
96
|
}
|
|
95
97
|
}
|
|
96
98
|
async function detectAppType(rootPath) {
|
|
@@ -218,7 +220,7 @@ async function updateReadme(rootPath, framework) {
|
|
|
218
220
|
await fs.writeFile(readmePath, newReadme);
|
|
219
221
|
logger_1.logger.info("✅ Updated README.md with project details and origin info");
|
|
220
222
|
}
|
|
221
|
-
async function injectAntigravityContext(rootPath, projectId, appName) {
|
|
223
|
+
async function injectAntigravityContext(rootPath, projectId, appName, nonInteractive) {
|
|
222
224
|
const agentDir = path.join(rootPath, ".agents");
|
|
223
225
|
const rulesDir = path.join(agentDir, "rules");
|
|
224
226
|
const workflowsDir = path.join(agentDir, "workflows");
|
|
@@ -233,40 +235,14 @@ async function injectAntigravityContext(rootPath, projectId, appName) {
|
|
|
233
235
|
{ name: "Globally for all projects", value: "global" },
|
|
234
236
|
],
|
|
235
237
|
default: "local",
|
|
236
|
-
nonInteractive: process.env.NODE_ENV === "test",
|
|
238
|
+
nonInteractive: nonInteractive || process.env.NODE_ENV === "test",
|
|
239
|
+
});
|
|
240
|
+
await (0, agentSkills_1.installAgentSkills)({
|
|
241
|
+
cwd: rootPath,
|
|
242
|
+
global: installLocation === "global",
|
|
243
|
+
background: false,
|
|
244
|
+
agentName: "gemini-cli",
|
|
237
245
|
});
|
|
238
|
-
logger_1.logger.info("⏳ Adding Antigravity skills...");
|
|
239
|
-
try {
|
|
240
|
-
const args = [
|
|
241
|
-
"-y",
|
|
242
|
-
"skills",
|
|
243
|
-
"add",
|
|
244
|
-
"firebase/agent-skills",
|
|
245
|
-
"-a",
|
|
246
|
-
"gemini-cli",
|
|
247
|
-
"--skill",
|
|
248
|
-
"*",
|
|
249
|
-
"-y",
|
|
250
|
-
];
|
|
251
|
-
if (installLocation === "global") {
|
|
252
|
-
args.push("-g");
|
|
253
|
-
}
|
|
254
|
-
const result = (0, child_process_1.spawnSync)("npx", args, {
|
|
255
|
-
cwd: rootPath,
|
|
256
|
-
stdio: "ignore",
|
|
257
|
-
shell: process.platform === "win32",
|
|
258
|
-
});
|
|
259
|
-
if (result.error) {
|
|
260
|
-
throw result.error;
|
|
261
|
-
}
|
|
262
|
-
if (result.status !== 0) {
|
|
263
|
-
throw new Error(`npx skills add exited with code ${result.status}`);
|
|
264
|
-
}
|
|
265
|
-
logger_1.logger.info(`✅ Added Antigravity skills`);
|
|
266
|
-
}
|
|
267
|
-
catch (err) {
|
|
268
|
-
utils.logWarning(`Could not add Antigravity skills, skipping. ${err}`);
|
|
269
|
-
}
|
|
270
246
|
const systemInstructionsTemplate = await (0, templates_1.readTemplate)("firebase-studio-export/system_instructions_template.md");
|
|
271
247
|
const systemInstructions = systemInstructionsTemplate.replace("${appName}", appName);
|
|
272
248
|
await fs.writeFile(path.join(rulesDir, "migration-context.md"), systemInstructions);
|
|
@@ -277,8 +253,7 @@ async function injectAntigravityContext(rootPath, projectId, appName) {
|
|
|
277
253
|
logger_1.logger.info("✅ Created Antigravity startup workflow");
|
|
278
254
|
}
|
|
279
255
|
catch (err) {
|
|
280
|
-
|
|
281
|
-
logger_1.logger.debug(`Could not read or write startup workflow: ${message}`);
|
|
256
|
+
logger_1.logger.debug(`Could not read or write startup workflow: ${(0, error_1.getErrMsg)(err)}`);
|
|
282
257
|
}
|
|
283
258
|
}
|
|
284
259
|
async function getAgyCommand(startAgy) {
|
|
@@ -316,7 +291,7 @@ async function getAgyCommand(startAgy) {
|
|
|
316
291
|
logger_1.logger.info(`⚠️ Antigravity IDE not found in your PATH. To ensure a seamless migration, please download and install Antigravity: ${downloadLink}`);
|
|
317
292
|
return undefined;
|
|
318
293
|
}
|
|
319
|
-
async function createFirebaseConfigs(rootPath, projectId) {
|
|
294
|
+
async function createFirebaseConfigs(rootPath, projectId, nonInteractive) {
|
|
320
295
|
if (!projectId) {
|
|
321
296
|
return;
|
|
322
297
|
}
|
|
@@ -351,7 +326,7 @@ async function createFirebaseConfigs(rootPath, projectId) {
|
|
|
351
326
|
const confirmBackend = await prompt.confirm({
|
|
352
327
|
message: `Would you like to use the App Hosting backend "${selectedBackendId}"?`,
|
|
353
328
|
default: true,
|
|
354
|
-
nonInteractive: process.env.NODE_ENV === "test",
|
|
329
|
+
nonInteractive: nonInteractive || process.env.NODE_ENV === "test",
|
|
355
330
|
});
|
|
356
331
|
if (confirmBackend) {
|
|
357
332
|
backendId = selectedBackendId;
|
|
@@ -376,8 +351,7 @@ async function createFirebaseConfigs(rootPath, projectId) {
|
|
|
376
351
|
}
|
|
377
352
|
}
|
|
378
353
|
catch (err) {
|
|
379
|
-
|
|
380
|
-
utils.logWarning(`Could not fetch backends from Firebase CLI, using default "studio". ${message}`);
|
|
354
|
+
utils.logWarning(`Could not fetch backends from Firebase CLI, using default "studio". ${(0, error_1.getErrMsg)(err)}`);
|
|
381
355
|
}
|
|
382
356
|
const firebaseJson = {
|
|
383
357
|
apphosting: {
|
|
@@ -433,8 +407,7 @@ async function writeAntigravityConfigs(rootPath, framework) {
|
|
|
433
407
|
settings = JSON.parse(settingsContent);
|
|
434
408
|
}
|
|
435
409
|
catch (err) {
|
|
436
|
-
|
|
437
|
-
logger_1.logger.debug(`Could not read ${settingsPath}: ${message}`);
|
|
410
|
+
logger_1.logger.debug(`Could not read ${settingsPath}: ${(0, error_1.getErrMsg)(err)}`);
|
|
438
411
|
}
|
|
439
412
|
const cleanSettings = {};
|
|
440
413
|
for (const [key, value] of Object.entries(settings)) {
|
|
@@ -497,8 +470,7 @@ async function cleanupUnusedFiles(rootPath) {
|
|
|
497
470
|
}
|
|
498
471
|
}
|
|
499
472
|
catch (err) {
|
|
500
|
-
|
|
501
|
-
logger_1.logger.debug(`Could not remove ${docsDir}: ${message}`);
|
|
473
|
+
logger_1.logger.debug(`Could not remove ${docsDir}: ${(0, error_1.getErrMsg)(err)}`);
|
|
502
474
|
}
|
|
503
475
|
const modifiedPath = path.join(rootPath, ".modified");
|
|
504
476
|
try {
|
|
@@ -506,8 +478,7 @@ async function cleanupUnusedFiles(rootPath) {
|
|
|
506
478
|
logger_1.logger.info("✅ Cleaned up .modified");
|
|
507
479
|
}
|
|
508
480
|
catch (err) {
|
|
509
|
-
|
|
510
|
-
logger_1.logger.debug(`Could not delete ${modifiedPath}: ${message}`);
|
|
481
|
+
logger_1.logger.debug(`Could not delete ${modifiedPath}: ${(0, error_1.getErrMsg)(err)}`);
|
|
511
482
|
}
|
|
512
483
|
const mcpJsonPath = path.join(rootPath, ".idx", "mcp.json");
|
|
513
484
|
try {
|
|
@@ -515,8 +486,7 @@ async function cleanupUnusedFiles(rootPath) {
|
|
|
515
486
|
logger_1.logger.info("✅ Cleaned up .idx/mcp.json");
|
|
516
487
|
}
|
|
517
488
|
catch (err) {
|
|
518
|
-
|
|
519
|
-
logger_1.logger.debug(`Could not delete ${mcpJsonPath}: ${message}`);
|
|
489
|
+
logger_1.logger.debug(`Could not delete ${mcpJsonPath}: ${(0, error_1.getErrMsg)(err)}`);
|
|
520
490
|
}
|
|
521
491
|
}
|
|
522
492
|
async function upgradeGenkitVersion(rootPath) {
|
|
@@ -548,8 +518,7 @@ async function upgradeGenkitVersion(rootPath) {
|
|
|
548
518
|
}
|
|
549
519
|
}
|
|
550
520
|
catch (err) {
|
|
551
|
-
|
|
552
|
-
logger_1.logger.debug(`Could not upgrade Genkit version: ${message}`);
|
|
521
|
+
logger_1.logger.debug(`Could not upgrade Genkit version: ${(0, error_1.getErrMsg)(err)}`);
|
|
553
522
|
}
|
|
554
523
|
}
|
|
555
524
|
async function uploadSecrets(rootPath, projectId) {
|
|
@@ -577,11 +546,10 @@ async function uploadSecrets(rootPath, projectId) {
|
|
|
577
546
|
}
|
|
578
547
|
}
|
|
579
548
|
catch (err) {
|
|
580
|
-
|
|
581
|
-
utils.logWarning(`Failed to upload GEMINI_API_KEY secret: ${message}`);
|
|
549
|
+
utils.logWarning(`Failed to upload GEMINI_API_KEY secret: ${(0, error_1.getErrMsg)(err)}`);
|
|
582
550
|
}
|
|
583
551
|
}
|
|
584
|
-
async function askToOpenAntigravity(rootPath, appName, startAntigravity) {
|
|
552
|
+
async function askToOpenAntigravity(rootPath, appName, startAntigravity, nonInteractive) {
|
|
585
553
|
const agyCommand = await getAgyCommand(startAntigravity);
|
|
586
554
|
logger_1.logger.info(`\n🎉 Your Firebase Studio project "${appName}" is now ready for Antigravity!`);
|
|
587
555
|
logger_1.logger.info("Antigravity is Google's agentic IDE, where you can collaborate with AI agents to build, test, and deploy your application.");
|
|
@@ -595,6 +563,7 @@ async function askToOpenAntigravity(rootPath, appName, startAntigravity) {
|
|
|
595
563
|
const answer = await prompt.confirm({
|
|
596
564
|
message: "Would you like to open it in Antigravity now?",
|
|
597
565
|
default: true,
|
|
566
|
+
nonInteractive,
|
|
598
567
|
});
|
|
599
568
|
if (answer) {
|
|
600
569
|
logger_1.logger.info(`⏳ Opening ${appName} in Antigravity...`);
|
|
@@ -629,7 +598,7 @@ async function checkDirectoryExists(dir) {
|
|
|
629
598
|
async function migrate(rootPath, options = { startAntigravity: true }) {
|
|
630
599
|
await checkDirectoryExists(rootPath);
|
|
631
600
|
const appType = await detectAppType(rootPath);
|
|
632
|
-
|
|
601
|
+
await track.trackGA4("firebase_studio_migrate", { app_type: appType, result: "started" });
|
|
633
602
|
logger_1.logger.info("🚀 Starting Firebase Studio to Antigravity migration...");
|
|
634
603
|
logger_1.logger.info("\nFile any bugs at https://github.com/firebase/firebase-tools/issues");
|
|
635
604
|
const { projectId, appName } = await extractMetadata(rootPath, options.project);
|
|
@@ -637,17 +606,17 @@ async function migrate(rootPath, options = { startAntigravity: true }) {
|
|
|
637
606
|
logger_1.logger.info(`✅ Detected framework: ${appType}`);
|
|
638
607
|
}
|
|
639
608
|
await updateReadme(rootPath, appType);
|
|
640
|
-
await createFirebaseConfigs(rootPath, projectId);
|
|
609
|
+
await createFirebaseConfigs(rootPath, projectId, options.nonInteractive);
|
|
641
610
|
await uploadSecrets(rootPath, projectId);
|
|
642
611
|
await upgradeGenkitVersion(rootPath);
|
|
643
|
-
await injectAntigravityContext(rootPath, projectId, appName);
|
|
612
|
+
await injectAntigravityContext(rootPath, projectId, appName, options.nonInteractive);
|
|
644
613
|
await writeAntigravityConfigs(rootPath, appType);
|
|
645
|
-
await setupAntigravityMcpServer(rootPath, appType);
|
|
614
|
+
await setupAntigravityMcpServer(rootPath, appType, options.nonInteractive);
|
|
646
615
|
await cleanupUnusedFiles(rootPath);
|
|
647
616
|
const currentFolderName = path.basename(rootPath);
|
|
648
617
|
if (currentFolderName === "download") {
|
|
649
618
|
logger_1.logger.info(`\n💡 Tip: You may want to rename this folder to "${appName.toLowerCase().replace(/\s+/g, "-")}"`);
|
|
650
619
|
}
|
|
651
620
|
await track.trackGA4("firebase_studio_migrate", { app_type: appType, result: "success" });
|
|
652
|
-
await askToOpenAntigravity(rootPath, appName, options.startAntigravity);
|
|
621
|
+
await askToOpenAntigravity(rootPath, appName, options.startAntigravity, options.nonInteractive);
|
|
653
622
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getInternalIac = getInternalIac;
|
|
4
|
+
const runtimes = require("../../deploy/functions/runtimes");
|
|
5
|
+
const supported = require("../../deploy/functions/runtimes/supported");
|
|
6
|
+
const functionsConfig = require("../../functionsConfig");
|
|
7
|
+
const functionsEnv = require("../../functions/env");
|
|
8
|
+
const logger_1 = require("../../logger");
|
|
9
|
+
const yaml = require("js-yaml");
|
|
10
|
+
const projectUtils_1 = require("../../projectUtils");
|
|
11
|
+
const error_1 = require("../../error");
|
|
12
|
+
async function getInternalIac(options, codebase) {
|
|
13
|
+
if (!codebase.source) {
|
|
14
|
+
throw new error_1.FirebaseError("Cannot export a codebase with no source");
|
|
15
|
+
}
|
|
16
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
17
|
+
const firebaseConfig = await functionsConfig.getFirebaseConfig(options);
|
|
18
|
+
const firebaseEnvs = functionsEnv.loadFirebaseEnvs(firebaseConfig, projectId);
|
|
19
|
+
const delegateContext = {
|
|
20
|
+
projectId,
|
|
21
|
+
sourceDir: options.config.path(codebase.source),
|
|
22
|
+
projectDir: options.config.projectDir,
|
|
23
|
+
runtime: codebase.runtime,
|
|
24
|
+
};
|
|
25
|
+
const runtimeDelegate = await runtimes.getRuntimeDelegate(delegateContext);
|
|
26
|
+
logger_1.logger.debug(`Validating ${runtimeDelegate.language} source`);
|
|
27
|
+
supported.guardVersionSupport(runtimeDelegate.runtime);
|
|
28
|
+
await runtimeDelegate.validate();
|
|
29
|
+
logger_1.logger.debug(`Building ${runtimeDelegate.language} source`);
|
|
30
|
+
await runtimeDelegate.build();
|
|
31
|
+
logger_1.logger.debug(`Discovering ${runtimeDelegate.language} source`);
|
|
32
|
+
const build = await runtimeDelegate.discoverBuild({}, firebaseEnvs);
|
|
33
|
+
return {
|
|
34
|
+
"functions.yaml": yaml.dump(build),
|
|
35
|
+
};
|
|
36
|
+
}
|