firebase-tools 15.10.1 → 15.11.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/appdistribution/client.js +17 -0
- package/lib/apptesting/parseTestFiles.js +11 -8
- package/lib/commands/apptesting.js +16 -7
- package/lib/commands/studio-export.js +2 -2
- package/lib/deploy/firestore/prepare.js +17 -0
- package/lib/deploy/functions/runtimes/dart/index.js +282 -0
- package/lib/deploy/functions/runtimes/index.js +1 -1
- package/lib/deploy/functions/runtimes/supported/index.js +4 -0
- package/lib/emulator/downloadableEmulatorInfo.json +30 -30
- package/lib/emulator/functionsEmulator.js +103 -24
- package/lib/emulator/functionsRuntimeWorker.js +21 -18
- package/lib/firebase_studio/migrate.js +69 -25
- package/lib/init/features/functions/dart.js +31 -0
- package/lib/init/features/functions/index.js +14 -0
- package/lib/tsconfig.publish.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/schema/firebase-config.json +7 -0
- package/templates/firebase-studio-export/readme_template.md +2 -0
- package/templates/init/functions/dart/_gitignore +11 -0
- package/templates/init/functions/dart/pubspec.yaml +14 -0
- package/templates/init/functions/dart/server.dart +15 -0
- package/lib/deploy/functions/runtimes/dart.js +0 -42
|
@@ -35,6 +35,8 @@ const v1_1 = require("../functions/events/v1");
|
|
|
35
35
|
const build_1 = require("../deploy/functions/build");
|
|
36
36
|
const env_1 = require("./env");
|
|
37
37
|
const python_1 = require("../functions/python");
|
|
38
|
+
const supported_1 = require("../deploy/functions/runtimes/supported");
|
|
39
|
+
const dart_1 = require("../deploy/functions/runtimes/dart");
|
|
38
40
|
const EVENT_INVOKE_GA4 = "functions_invoke";
|
|
39
41
|
const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
|
|
40
42
|
class IPCConn {
|
|
@@ -83,6 +85,7 @@ class FunctionsEmulator {
|
|
|
83
85
|
this.staticBackends = [];
|
|
84
86
|
this.dynamicBackends = [];
|
|
85
87
|
this.watchers = [];
|
|
88
|
+
this.watchCleanups = [];
|
|
86
89
|
this.debugMode = false;
|
|
87
90
|
this.staticBackends = args.emulatableBackends;
|
|
88
91
|
emulatorLogger_1.EmulatorLogger.setVerbosity(this.args.verbosity ? emulatorLogger_1.Verbosity[this.args.verbosity] : emulatorLogger_1.Verbosity["DEBUG"]);
|
|
@@ -208,7 +211,7 @@ class FunctionsEmulator {
|
|
|
208
211
|
async sendRequest(trigger, body) {
|
|
209
212
|
const record = this.getTriggerRecordByKey(this.getTriggerKey(trigger));
|
|
210
213
|
const pool = this.workerPools[record.backend.codebase];
|
|
211
|
-
if (!pool.readyForWork(trigger.id)) {
|
|
214
|
+
if (!pool.readyForWork(trigger.id, record.backend.runtime)) {
|
|
212
215
|
try {
|
|
213
216
|
await this.startRuntime(record.backend, trigger);
|
|
214
217
|
}
|
|
@@ -217,7 +220,7 @@ class FunctionsEmulator {
|
|
|
217
220
|
return;
|
|
218
221
|
}
|
|
219
222
|
}
|
|
220
|
-
const worker = pool.getIdleWorker(trigger.id);
|
|
223
|
+
const worker = pool.getIdleWorker(trigger.id, record.backend.runtime);
|
|
221
224
|
if (this.debugMode) {
|
|
222
225
|
await worker.sendDebugMsg({
|
|
223
226
|
functionTarget: trigger.entryPoint,
|
|
@@ -229,10 +232,13 @@ class FunctionsEmulator {
|
|
|
229
232
|
"Content-Type": "application/json",
|
|
230
233
|
"Content-Length": `${reqBody.length}`,
|
|
231
234
|
};
|
|
235
|
+
const isDart = (0, supported_1.isLanguageRuntime)(record.backend.runtime, "dart");
|
|
236
|
+
const path = isDart ? `/${trigger.entryPoint}` : `/`;
|
|
232
237
|
return new Promise((resolve, reject) => {
|
|
233
238
|
const req = http.request({
|
|
234
239
|
...worker.runtime.conn.httpReqOpts(),
|
|
235
|
-
|
|
240
|
+
method: "POST",
|
|
241
|
+
path: path,
|
|
236
242
|
headers: headers,
|
|
237
243
|
}, resolve);
|
|
238
244
|
req.on("error", reject);
|
|
@@ -264,23 +270,43 @@ class FunctionsEmulator {
|
|
|
264
270
|
async connect() {
|
|
265
271
|
for (const backend of this.staticBackends) {
|
|
266
272
|
this.logger.logLabeled("BULLET", "functions", `Watching "${backend.functionsDir}" for Cloud Functions...`);
|
|
267
|
-
const watcher = chokidar.watch(backend.functionsDir, {
|
|
268
|
-
ignored: [
|
|
269
|
-
/.+?[\\\/]node_modules[\\\/].+?/,
|
|
270
|
-
/(^|[\/\\])\../,
|
|
271
|
-
/.+\.log/,
|
|
272
|
-
/.+?[\\\/]venv[\\\/].+?/,
|
|
273
|
-
...(backend.ignore?.map((i) => `**/${i}`) ?? []),
|
|
274
|
-
],
|
|
275
|
-
persistent: true,
|
|
276
|
-
});
|
|
277
|
-
this.watchers.push(watcher);
|
|
278
|
-
const debouncedLoadTriggers = (0, utils_1.debounce)(() => this.loadTriggers(backend), 1000);
|
|
279
|
-
watcher.on("change", (filePath) => {
|
|
280
|
-
this.logger.log("DEBUG", `File ${filePath} changed, reloading triggers`);
|
|
281
|
-
return debouncedLoadTriggers();
|
|
282
|
-
});
|
|
283
273
|
await this.loadTriggers(backend, true);
|
|
274
|
+
const isDart = (0, supported_1.isLanguageRuntime)(backend.runtime, "dart");
|
|
275
|
+
if (isDart) {
|
|
276
|
+
const runtimeDelegateContext = {
|
|
277
|
+
projectId: this.args.projectId,
|
|
278
|
+
projectDir: this.args.projectDir,
|
|
279
|
+
sourceDir: backend.functionsDir,
|
|
280
|
+
runtime: backend.runtime,
|
|
281
|
+
};
|
|
282
|
+
const delegate = await runtimes.getRuntimeDelegate(runtimeDelegateContext);
|
|
283
|
+
this.logger.logLabeled("BULLET", "functions", `Starting build_runner watch for Dart functions...`);
|
|
284
|
+
const debouncedLoadTriggers = (0, utils_1.debounce)(() => this.loadTriggers(backend), 1000);
|
|
285
|
+
const cleanup = await delegate.watch(() => {
|
|
286
|
+
this.logger.log("DEBUG", "build_runner rebuilt, reloading triggers");
|
|
287
|
+
debouncedLoadTriggers();
|
|
288
|
+
});
|
|
289
|
+
this.watchCleanups.push(cleanup);
|
|
290
|
+
this.logger.logLabeled("SUCCESS", "functions", `build_runner initial build completed`);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
const watcher = chokidar.watch(backend.functionsDir, {
|
|
294
|
+
ignored: [
|
|
295
|
+
/(^|[\/\\])\../,
|
|
296
|
+
/.+\.log/,
|
|
297
|
+
/.+?[\\\/]node_modules[\\\/].+?/,
|
|
298
|
+
/.+?[\\\/]venv[\\\/].+?/,
|
|
299
|
+
...(backend.ignore?.map((i) => `**/${i}`) ?? []),
|
|
300
|
+
],
|
|
301
|
+
persistent: true,
|
|
302
|
+
});
|
|
303
|
+
this.watchers.push(watcher);
|
|
304
|
+
const debouncedLoadTriggers = (0, utils_1.debounce)(() => this.loadTriggers(backend), 1000);
|
|
305
|
+
watcher.on("change", (filePath) => {
|
|
306
|
+
this.logger.log("DEBUG", `File ${filePath} changed, reloading triggers`);
|
|
307
|
+
return debouncedLoadTriggers();
|
|
308
|
+
});
|
|
309
|
+
}
|
|
284
310
|
}
|
|
285
311
|
await this.performPostLoadOperations();
|
|
286
312
|
return;
|
|
@@ -300,6 +326,10 @@ class FunctionsEmulator {
|
|
|
300
326
|
await watcher.close();
|
|
301
327
|
}
|
|
302
328
|
this.watchers = [];
|
|
329
|
+
for (const cleanup of this.watchCleanups) {
|
|
330
|
+
await cleanup();
|
|
331
|
+
}
|
|
332
|
+
this.watchCleanups = [];
|
|
303
333
|
if (this.destroyServer) {
|
|
304
334
|
await this.destroyServer();
|
|
305
335
|
}
|
|
@@ -1098,13 +1128,52 @@ class FunctionsEmulator {
|
|
|
1098
1128
|
conn: new TCPConn("127.0.0.1", port),
|
|
1099
1129
|
};
|
|
1100
1130
|
}
|
|
1131
|
+
async startDart(backend, envs) {
|
|
1132
|
+
if (this.debugMode) {
|
|
1133
|
+
this.logger.log("WARN", "--inspect-functions not supported for Dart functions. Ignored.");
|
|
1134
|
+
}
|
|
1135
|
+
const port = await portfinder.getPortPromise({
|
|
1136
|
+
port: 8081 + (0, utils_1.randomInt)(0, 1000),
|
|
1137
|
+
});
|
|
1138
|
+
const args = ["run", "--no-serve-devtools", dart_1.DART_ENTRY_POINT];
|
|
1139
|
+
const dartEnvs = { ...envs };
|
|
1140
|
+
delete dartEnvs.FUNCTION_TARGET;
|
|
1141
|
+
delete dartEnvs.FUNCTION_SIGNATURE_TYPE;
|
|
1142
|
+
const bin = backend.bin || "dart";
|
|
1143
|
+
logger_1.logger.debug(`Starting Dart runtime with args: ${args.join(" ")} on port ${port}`);
|
|
1144
|
+
const childProcess = spawn(bin, args, {
|
|
1145
|
+
cwd: backend.functionsDir,
|
|
1146
|
+
env: {
|
|
1147
|
+
...process.env,
|
|
1148
|
+
...dartEnvs,
|
|
1149
|
+
HOST: "127.0.0.1",
|
|
1150
|
+
PORT: port.toString(),
|
|
1151
|
+
},
|
|
1152
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1153
|
+
});
|
|
1154
|
+
childProcess.stdout?.on("data", (chunk) => {
|
|
1155
|
+
this.logger.log("DEBUG", `[dart] ${chunk.toString("utf8")}`);
|
|
1156
|
+
});
|
|
1157
|
+
childProcess.stderr?.on("data", (chunk) => {
|
|
1158
|
+
this.logger.log("DEBUG", `[dart] ${chunk.toString("utf8")}`);
|
|
1159
|
+
});
|
|
1160
|
+
return {
|
|
1161
|
+
process: childProcess,
|
|
1162
|
+
events: new events_1.EventEmitter(),
|
|
1163
|
+
cwd: backend.functionsDir,
|
|
1164
|
+
conn: new TCPConn("127.0.0.1", port),
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1101
1167
|
async startRuntime(backend, trigger) {
|
|
1102
1168
|
const runtimeEnv = this.getRuntimeEnvs(backend, trigger);
|
|
1103
1169
|
const secretEnvs = await this.resolveSecretEnvs(backend, trigger);
|
|
1104
1170
|
let runtime;
|
|
1105
|
-
if (backend.runtime
|
|
1171
|
+
if ((0, supported_1.isLanguageRuntime)(backend.runtime, "python")) {
|
|
1106
1172
|
runtime = await this.startPython(backend, { ...runtimeEnv, ...secretEnvs });
|
|
1107
1173
|
}
|
|
1174
|
+
else if ((0, supported_1.isLanguageRuntime)(backend.runtime, "dart")) {
|
|
1175
|
+
runtime = await this.startDart(backend, { ...runtimeEnv, ...secretEnvs });
|
|
1176
|
+
}
|
|
1108
1177
|
else {
|
|
1109
1178
|
runtime = await this.startNode(backend, { ...runtimeEnv, ...secretEnvs });
|
|
1110
1179
|
}
|
|
@@ -1113,7 +1182,7 @@ class FunctionsEmulator {
|
|
|
1113
1182
|
ref: backend.extensionVersion?.ref,
|
|
1114
1183
|
};
|
|
1115
1184
|
const pool = this.workerPools[backend.codebase];
|
|
1116
|
-
const worker = pool.addWorker(trigger, runtime, extensionLogInfo);
|
|
1185
|
+
const worker = pool.addWorker(trigger, runtime, extensionLogInfo, backend.runtime);
|
|
1117
1186
|
await worker.waitForSocketReady();
|
|
1118
1187
|
return worker;
|
|
1119
1188
|
}
|
|
@@ -1172,10 +1241,20 @@ class FunctionsEmulator {
|
|
|
1172
1241
|
});
|
|
1173
1242
|
this.logger.log("DEBUG", `[functions] Runtime ready! Sending request!`);
|
|
1174
1243
|
const url = new url_1.URL(`${req.protocol}://${req.hostname}${req.url}`);
|
|
1175
|
-
|
|
1244
|
+
let path = `${url.pathname}${url.search}`.replace(new RegExp(`\/${this.args.projectId}\/[^\/]*\/${req.params.trigger_name}\/?`), "/");
|
|
1245
|
+
const isDart = (0, supported_1.isLanguageRuntime)(record.backend.runtime, "dart");
|
|
1246
|
+
if (isDart) {
|
|
1247
|
+
const isBackgroundRoute = req.url.startsWith("/functions/projects/");
|
|
1248
|
+
if (isBackgroundRoute || path === "/") {
|
|
1249
|
+
path = `/${trigger.entryPoint}`;
|
|
1250
|
+
}
|
|
1251
|
+
else {
|
|
1252
|
+
path = `/${trigger.entryPoint}${path}`;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1176
1255
|
this.logger.log("DEBUG", `[functions] Got req.url=${req.url}, mapping to path=${path}`);
|
|
1177
1256
|
const pool = this.workerPools[record.backend.codebase];
|
|
1178
|
-
if (!pool.readyForWork(trigger.id)) {
|
|
1257
|
+
if (!pool.readyForWork(trigger.id, record.backend.runtime)) {
|
|
1179
1258
|
try {
|
|
1180
1259
|
await this.startRuntime(record.backend, trigger);
|
|
1181
1260
|
}
|
|
@@ -1196,7 +1275,7 @@ class FunctionsEmulator {
|
|
|
1196
1275
|
method,
|
|
1197
1276
|
path,
|
|
1198
1277
|
headers: req.headers,
|
|
1199
|
-
}, res, reqBody, debugBundle);
|
|
1278
|
+
}, res, reqBody, debugBundle, record.backend.runtime);
|
|
1200
1279
|
}
|
|
1201
1280
|
}
|
|
1202
1281
|
exports.FunctionsEmulator = FunctionsEmulator;
|
|
@@ -8,6 +8,7 @@ const events_1 = require("events");
|
|
|
8
8
|
const emulatorLogger_1 = require("./emulatorLogger");
|
|
9
9
|
const error_1 = require("../error");
|
|
10
10
|
const discovery_1 = require("../deploy/functions/runtimes/discovery");
|
|
11
|
+
const supported_1 = require("../deploy/functions/runtimes/supported");
|
|
11
12
|
var RuntimeWorkerState;
|
|
12
13
|
(function (RuntimeWorkerState) {
|
|
13
14
|
RuntimeWorkerState["CREATED"] = "CREATED";
|
|
@@ -226,13 +227,14 @@ class RuntimeWorkerPool {
|
|
|
226
227
|
this.mode = mode;
|
|
227
228
|
this.workers = new Map();
|
|
228
229
|
}
|
|
229
|
-
getKey(triggerId) {
|
|
230
|
+
getKey(triggerId, runtime) {
|
|
230
231
|
if (this.mode === types_1.FunctionsExecutionMode.SEQUENTIAL) {
|
|
231
232
|
return "~shared~";
|
|
232
233
|
}
|
|
233
|
-
|
|
234
|
-
return
|
|
234
|
+
if ((0, supported_1.isLanguageRuntime)(runtime, "dart")) {
|
|
235
|
+
return "~dart-shared~";
|
|
235
236
|
}
|
|
237
|
+
return triggerId || "~diagnostic~";
|
|
236
238
|
}
|
|
237
239
|
refresh() {
|
|
238
240
|
for (const arr of this.workers.values()) {
|
|
@@ -261,13 +263,13 @@ class RuntimeWorkerPool {
|
|
|
261
263
|
});
|
|
262
264
|
}
|
|
263
265
|
}
|
|
264
|
-
readyForWork(triggerId) {
|
|
265
|
-
const idleWorker = this.getIdleWorker(triggerId);
|
|
266
|
+
readyForWork(triggerId, runtime) {
|
|
267
|
+
const idleWorker = this.getIdleWorker(triggerId, runtime);
|
|
266
268
|
return !!idleWorker;
|
|
267
269
|
}
|
|
268
|
-
async submitRequest(triggerId, req, resp, body, debug) {
|
|
270
|
+
async submitRequest(triggerId, req, resp, body, debug, runtime) {
|
|
269
271
|
this.log(`submitRequest(triggerId=${triggerId})`);
|
|
270
|
-
const worker = this.getIdleWorker(triggerId);
|
|
272
|
+
const worker = this.getIdleWorker(triggerId, runtime);
|
|
271
273
|
if (!worker) {
|
|
272
274
|
throw new error_1.FirebaseError("Internal Error: can't call submitRequest without checking for idle workers");
|
|
273
275
|
}
|
|
@@ -276,11 +278,11 @@ class RuntimeWorkerPool {
|
|
|
276
278
|
}
|
|
277
279
|
return worker.request(req, resp, body, !!debug);
|
|
278
280
|
}
|
|
279
|
-
getIdleWorker(triggerId) {
|
|
281
|
+
getIdleWorker(triggerId, runtime) {
|
|
280
282
|
this.cleanUpWorkers();
|
|
281
|
-
const triggerWorkers = this.getTriggerWorkers(triggerId);
|
|
283
|
+
const triggerWorkers = this.getTriggerWorkers(triggerId, runtime);
|
|
282
284
|
if (!triggerWorkers.length) {
|
|
283
|
-
this.setTriggerWorkers(triggerId, []);
|
|
285
|
+
this.setTriggerWorkers(triggerId, [], runtime);
|
|
284
286
|
return;
|
|
285
287
|
}
|
|
286
288
|
for (const worker of triggerWorkers) {
|
|
@@ -290,21 +292,22 @@ class RuntimeWorkerPool {
|
|
|
290
292
|
}
|
|
291
293
|
return;
|
|
292
294
|
}
|
|
293
|
-
addWorker(trigger, runtime, extensionLogInfo) {
|
|
294
|
-
this.
|
|
295
|
+
addWorker(trigger, runtime, extensionLogInfo, runtimeType) {
|
|
296
|
+
const key = this.getKey(trigger?.id, runtimeType);
|
|
297
|
+
this.log(`addWorker(${key})`);
|
|
295
298
|
const disableTimeout = !trigger?.id || this.mode === types_1.FunctionsExecutionMode.SEQUENTIAL;
|
|
296
299
|
const worker = new RuntimeWorker(trigger?.id, runtime, extensionLogInfo, disableTimeout ? undefined : trigger?.timeoutSeconds);
|
|
297
|
-
const keyWorkers = this.getTriggerWorkers(trigger?.id);
|
|
300
|
+
const keyWorkers = this.getTriggerWorkers(trigger?.id, runtimeType);
|
|
298
301
|
keyWorkers.push(worker);
|
|
299
|
-
this.setTriggerWorkers(trigger?.id, keyWorkers);
|
|
302
|
+
this.setTriggerWorkers(trigger?.id, keyWorkers, runtimeType);
|
|
300
303
|
this.log(`Adding worker with key ${worker.triggerKey}, total=${keyWorkers.length}`);
|
|
301
304
|
return worker;
|
|
302
305
|
}
|
|
303
|
-
getTriggerWorkers(triggerId) {
|
|
304
|
-
return this.workers.get(this.getKey(triggerId)) || [];
|
|
306
|
+
getTriggerWorkers(triggerId, runtime) {
|
|
307
|
+
return this.workers.get(this.getKey(triggerId, runtime)) || [];
|
|
305
308
|
}
|
|
306
|
-
setTriggerWorkers(triggerId, workers) {
|
|
307
|
-
this.workers.set(this.getKey(triggerId), workers);
|
|
309
|
+
setTriggerWorkers(triggerId, workers, runtime) {
|
|
310
|
+
this.workers.set(this.getKey(triggerId, runtime), workers);
|
|
308
311
|
}
|
|
309
312
|
cleanUpWorkers() {
|
|
310
313
|
for (const [key, keyWorkers] of this.workers.entries()) {
|
|
@@ -6,6 +6,7 @@ exports.migrate = migrate;
|
|
|
6
6
|
const fs = require("fs/promises");
|
|
7
7
|
const path = require("path");
|
|
8
8
|
const child_process_1 = require("child_process");
|
|
9
|
+
const semver = require("semver");
|
|
9
10
|
const logger_1 = require("../logger");
|
|
10
11
|
const prompt = require("../prompt");
|
|
11
12
|
const apphosting = require("../gcp/apphosting");
|
|
@@ -38,12 +39,23 @@ async function setupAntigravityMcpServer(rootPath, appType) {
|
|
|
38
39
|
}
|
|
39
40
|
let updated = false;
|
|
40
41
|
if (!mcpConfig.mcpServers["firebase"]) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
if (utils.commandExistsSync("npx")) {
|
|
43
|
+
const confirmFirebase = await prompt.confirm({
|
|
44
|
+
message: "Would you like to enable the Firebase MCP server for Antigravity?",
|
|
45
|
+
default: true,
|
|
46
|
+
});
|
|
47
|
+
if (confirmFirebase) {
|
|
48
|
+
mcpConfig.mcpServers["firebase"] = {
|
|
49
|
+
command: "npx",
|
|
50
|
+
args: ["-y", "firebase-tools@latest", "mcp", "--dir", path.resolve(rootPath)],
|
|
51
|
+
};
|
|
52
|
+
updated = true;
|
|
53
|
+
logger_1.logger.info(`✅ Configured Firebase MCP server in ${mcpConfigPath}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
logger_1.logger.info("ℹ️ npx not found on PATH, skipping Firebase MCP server configuration.");
|
|
58
|
+
}
|
|
47
59
|
}
|
|
48
60
|
else {
|
|
49
61
|
logger_1.logger.info("ℹ️ Firebase MCP server already configured in Antigravity, skipping.");
|
|
@@ -51,12 +63,18 @@ async function setupAntigravityMcpServer(rootPath, appType) {
|
|
|
51
63
|
if (appType === "FLUTTER") {
|
|
52
64
|
if (utils.commandExistsSync("dart")) {
|
|
53
65
|
if (!mcpConfig.mcpServers["dart"]) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
const confirmDart = await prompt.confirm({
|
|
67
|
+
message: "Would you like to enable the Dart MCP server for Antigravity?",
|
|
68
|
+
default: true,
|
|
69
|
+
});
|
|
70
|
+
if (confirmDart) {
|
|
71
|
+
mcpConfig.mcpServers["dart"] = {
|
|
72
|
+
command: "dart",
|
|
73
|
+
args: ["mcp-server"],
|
|
74
|
+
};
|
|
75
|
+
updated = true;
|
|
76
|
+
logger_1.logger.info(`✅ Configured Dart MCP server in ${mcpConfigPath}`);
|
|
77
|
+
}
|
|
60
78
|
}
|
|
61
79
|
else {
|
|
62
80
|
logger_1.logger.info("ℹ️ Dart MCP server already configured in Antigravity, skipping.");
|
|
@@ -208,9 +226,32 @@ async function injectAntigravityContext(rootPath, projectId, appName) {
|
|
|
208
226
|
await fs.mkdir(rulesDir, { recursive: true });
|
|
209
227
|
await fs.mkdir(workflowsDir, { recursive: true });
|
|
210
228
|
await fs.mkdir(skillsDir, { recursive: true });
|
|
229
|
+
const installLocation = await prompt.select({
|
|
230
|
+
message: "Where would you like to install Firebase project skills?",
|
|
231
|
+
choices: [
|
|
232
|
+
{ name: "Locally in the project", value: "local" },
|
|
233
|
+
{ name: "Globally for all projects", value: "global" },
|
|
234
|
+
],
|
|
235
|
+
default: "local",
|
|
236
|
+
nonInteractive: process.env.NODE_ENV === "test",
|
|
237
|
+
});
|
|
211
238
|
logger_1.logger.info("⏳ Adding Antigravity skills...");
|
|
212
239
|
try {
|
|
213
|
-
const
|
|
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, {
|
|
214
255
|
cwd: rootPath,
|
|
215
256
|
stdio: "ignore",
|
|
216
257
|
shell: process.platform === "win32",
|
|
@@ -484,24 +525,26 @@ async function upgradeGenkitVersion(rootPath) {
|
|
|
484
525
|
const packageJsonContent = await fs.readFile(packageJsonPath, "utf8");
|
|
485
526
|
const packageJson = JSON.parse(packageJsonContent);
|
|
486
527
|
let modified = false;
|
|
487
|
-
const
|
|
488
|
-
|
|
528
|
+
const targetVersion = "1.29.0";
|
|
529
|
+
const checkAndUpgrade = (deps) => {
|
|
530
|
+
if (!deps || !deps["genkit-cli"]) {
|
|
489
531
|
return;
|
|
490
532
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
533
|
+
const currentVersion = deps["genkit-cli"];
|
|
534
|
+
if (currentVersion.startsWith("^")) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
const coerced = semver.coerce(currentVersion);
|
|
538
|
+
if (coerced && semver.lt(coerced, targetVersion)) {
|
|
539
|
+
deps["genkit-cli"] = "^1.29";
|
|
540
|
+
modified = true;
|
|
498
541
|
}
|
|
499
542
|
};
|
|
500
|
-
|
|
501
|
-
|
|
543
|
+
checkAndUpgrade(packageJson.dependencies);
|
|
544
|
+
checkAndUpgrade(packageJson.devDependencies);
|
|
502
545
|
if (modified) {
|
|
503
546
|
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n");
|
|
504
|
-
logger_1.logger.info("✅ Upgraded
|
|
547
|
+
logger_1.logger.info("✅ Upgraded genkit-cli version to 1.29 in package.json");
|
|
505
548
|
}
|
|
506
549
|
}
|
|
507
550
|
catch (err) {
|
|
@@ -588,6 +631,7 @@ async function migrate(rootPath, options = { startAntigravity: true }) {
|
|
|
588
631
|
const appType = await detectAppType(rootPath);
|
|
589
632
|
void track.trackGA4("firebase_studio_migrate", { app_type: appType, result: "started" });
|
|
590
633
|
logger_1.logger.info("🚀 Starting Firebase Studio to Antigravity migration...");
|
|
634
|
+
logger_1.logger.info("\nFile any bugs at https://github.com/firebase/firebase-tools/issues");
|
|
591
635
|
const { projectId, appName } = await extractMetadata(rootPath, options.project);
|
|
592
636
|
if (appType) {
|
|
593
637
|
logger_1.logger.info(`✅ Detected framework: ${appType}`);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setup = setup;
|
|
4
|
+
const spawn = require("cross-spawn");
|
|
5
|
+
const prompt_1 = require("../../../prompt");
|
|
6
|
+
const supported_1 = require("../../../deploy/functions/runtimes/supported");
|
|
7
|
+
const templates_1 = require("../../../templates");
|
|
8
|
+
const PUBSPEC_TEMPLATE = (0, templates_1.readTemplateSync)("init/functions/dart/pubspec.yaml");
|
|
9
|
+
const MAIN_TEMPLATE = (0, templates_1.readTemplateSync)("init/functions/dart/server.dart");
|
|
10
|
+
const GITIGNORE_TEMPLATE = (0, templates_1.readTemplateSync)("init/functions/dart/_gitignore");
|
|
11
|
+
async function setup(setup, config) {
|
|
12
|
+
await config.askWriteProjectFile(`${setup.functions.source}/pubspec.yaml`, PUBSPEC_TEMPLATE);
|
|
13
|
+
await config.askWriteProjectFile(`${setup.functions.source}/.gitignore`, GITIGNORE_TEMPLATE);
|
|
14
|
+
await config.askWriteProjectFile(`${setup.functions.source}/bin/server.dart`, MAIN_TEMPLATE);
|
|
15
|
+
config.set("functions.runtime", (0, supported_1.latest)("dart"));
|
|
16
|
+
config.set("functions.ignore", [".dart_tool", "build"]);
|
|
17
|
+
const install = await (0, prompt_1.confirm)({
|
|
18
|
+
message: "Do you want to install dependencies now?",
|
|
19
|
+
default: true,
|
|
20
|
+
});
|
|
21
|
+
if (install) {
|
|
22
|
+
const installProcess = spawn("dart", ["pub", "get"], {
|
|
23
|
+
cwd: config.path(setup.functions.source),
|
|
24
|
+
stdio: ["inherit", "inherit", "inherit"],
|
|
25
|
+
});
|
|
26
|
+
await new Promise((resolve, reject) => {
|
|
27
|
+
installProcess.on("exit", resolve);
|
|
28
|
+
installProcess.on("error", reject);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -144,6 +144,10 @@ async function languageSetup(setup) {
|
|
|
144
144
|
value: "python",
|
|
145
145
|
});
|
|
146
146
|
}
|
|
147
|
+
choices.push({
|
|
148
|
+
name: "Dart",
|
|
149
|
+
value: "dart",
|
|
150
|
+
});
|
|
147
151
|
const language = await (0, prompt_1.select)({
|
|
148
152
|
message: "What language would you like to use to write Cloud Functions?",
|
|
149
153
|
default: "javascript",
|
|
@@ -173,6 +177,16 @@ async function languageSetup(setup) {
|
|
|
173
177
|
cbconfig.ignore = ["venv", ".git", "firebase-debug.log", "firebase-debug.*.log", "*.local"];
|
|
174
178
|
cbconfig.runtime = supported.latest("python");
|
|
175
179
|
break;
|
|
180
|
+
case "dart":
|
|
181
|
+
cbconfig.ignore = [
|
|
182
|
+
".dart_tool",
|
|
183
|
+
".git",
|
|
184
|
+
"firebase-debug.log",
|
|
185
|
+
"firebase-debug.*.log",
|
|
186
|
+
"*.local",
|
|
187
|
+
];
|
|
188
|
+
cbconfig.runtime = supported.latest("dart");
|
|
189
|
+
break;
|
|
176
190
|
}
|
|
177
191
|
setup.functions.languageChoice = language;
|
|
178
192
|
}
|