clawfire 0.6.4 → 0.6.6
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/dist/cli.js +1 -1
- package/dist/{dev-server-7JCKX7OD.js → dev-server-PQP33VSE.js} +178 -49
- package/dist/dev.cjs +177 -48
- package/dist/dev.cjs.map +1 -1
- package/dist/dev.js +178 -49
- package/dist/dev.js.map +1 -1
- package/package.json +1 -1
package/dist/dev.js
CHANGED
|
@@ -2230,8 +2230,9 @@ function generateDashboardHtml(options) {
|
|
|
2230
2230
|
}
|
|
2231
2231
|
|
|
2232
2232
|
btn.disabled = true;
|
|
2233
|
-
btn.textContent = 'Setting...';
|
|
2234
|
-
status.
|
|
2233
|
+
btn.textContent = 'Setting up...';
|
|
2234
|
+
status.textContent = 'Selecting project, detecting web app, auto-filling config...';
|
|
2235
|
+
status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a0a1c;border:1px solid #3b82f6;color:#93c5fd;';
|
|
2235
2236
|
|
|
2236
2237
|
fetch(API + '/__dev/setup/select-project', {
|
|
2237
2238
|
method: 'POST',
|
|
@@ -2241,8 +2242,11 @@ function generateDashboardHtml(options) {
|
|
|
2241
2242
|
.then(function(r) { return r.json(); })
|
|
2242
2243
|
.then(function(data) {
|
|
2243
2244
|
if (data.success) {
|
|
2244
|
-
|
|
2245
|
-
status.
|
|
2245
|
+
var msg = data.steps ? data.steps.join('\\n') : data.message;
|
|
2246
|
+
status.textContent = msg;
|
|
2247
|
+
status.style.whiteSpace = 'pre-line';
|
|
2248
|
+
status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;white-space:pre-line;';
|
|
2249
|
+
btn.textContent = 'Done';
|
|
2246
2250
|
setTimeout(refreshSetupStatus, 1000);
|
|
2247
2251
|
} else {
|
|
2248
2252
|
status.textContent = data.message;
|
|
@@ -2953,7 +2957,7 @@ function generateDashboardHtml(options) {
|
|
|
2953
2957
|
import { execFile as execFile2, spawn } from "child_process";
|
|
2954
2958
|
import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
2955
2959
|
import { resolve as resolve5, join as join4 } from "path";
|
|
2956
|
-
import { tmpdir, platform } from "os";
|
|
2960
|
+
import { tmpdir, platform, homedir } from "os";
|
|
2957
2961
|
var FirebaseSetup = class {
|
|
2958
2962
|
projectDir;
|
|
2959
2963
|
stateFilePath;
|
|
@@ -3378,8 +3382,57 @@ var FirebaseSetup = class {
|
|
|
3378
3382
|
}
|
|
3379
3383
|
// ─── Firestore Database Automation ──────────────────────────────────
|
|
3380
3384
|
/**
|
|
3381
|
-
*
|
|
3382
|
-
*
|
|
3385
|
+
* Read Firebase CLI's stored OAuth token and exchange for access token.
|
|
3386
|
+
* No gcloud CLI needed — uses the token from `firebase login`.
|
|
3387
|
+
*/
|
|
3388
|
+
async getFirebaseAccessToken() {
|
|
3389
|
+
const home = homedir();
|
|
3390
|
+
const configPath = join4(home, ".config", "configstore", "firebase-tools.json");
|
|
3391
|
+
if (!existsSync6(configPath)) return null;
|
|
3392
|
+
try {
|
|
3393
|
+
const config = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
3394
|
+
let refreshToken = null;
|
|
3395
|
+
if (config?.activeAccounts) {
|
|
3396
|
+
const activeEmail = config?.user?.email;
|
|
3397
|
+
if (activeEmail && config.activeAccounts[activeEmail]?.tokens?.refresh_token) {
|
|
3398
|
+
refreshToken = config.activeAccounts[activeEmail].tokens.refresh_token;
|
|
3399
|
+
}
|
|
3400
|
+
if (!refreshToken) {
|
|
3401
|
+
for (const account of Object.values(config.activeAccounts)) {
|
|
3402
|
+
if (account?.tokens?.refresh_token) {
|
|
3403
|
+
refreshToken = account.tokens.refresh_token;
|
|
3404
|
+
break;
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
if (!refreshToken && config?.tokens?.refresh_token) {
|
|
3410
|
+
refreshToken = config.tokens.refresh_token;
|
|
3411
|
+
}
|
|
3412
|
+
if (!refreshToken) return null;
|
|
3413
|
+
const body = new URLSearchParams({
|
|
3414
|
+
grant_type: "refresh_token",
|
|
3415
|
+
refresh_token: refreshToken,
|
|
3416
|
+
client_id: "563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com",
|
|
3417
|
+
client_secret: "j9iVZfS8kkCEFUPaAeJV0sAi"
|
|
3418
|
+
});
|
|
3419
|
+
const res = await fetch("https://oauth2.googleapis.com/token", {
|
|
3420
|
+
method: "POST",
|
|
3421
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
3422
|
+
body: body.toString()
|
|
3423
|
+
});
|
|
3424
|
+
if (res.ok) {
|
|
3425
|
+
const data = await res.json();
|
|
3426
|
+
return data.access_token || null;
|
|
3427
|
+
}
|
|
3428
|
+
} catch {
|
|
3429
|
+
}
|
|
3430
|
+
return null;
|
|
3431
|
+
}
|
|
3432
|
+
/**
|
|
3433
|
+
* Enable Firestore API using Google Service Usage REST API.
|
|
3434
|
+
* Uses Firebase CLI's stored auth token — no gcloud CLI needed.
|
|
3435
|
+
* Falls back to gcloud CLI, then manual URL.
|
|
3383
3436
|
*/
|
|
3384
3437
|
async enableFirestoreApi() {
|
|
3385
3438
|
const state = this.loadState();
|
|
@@ -3397,24 +3450,46 @@ var FirebaseSetup = class {
|
|
|
3397
3450
|
if (!projectId) {
|
|
3398
3451
|
return { success: false, message: "No project ID found. Select a project first." };
|
|
3399
3452
|
}
|
|
3453
|
+
try {
|
|
3454
|
+
const accessToken = await this.getFirebaseAccessToken();
|
|
3455
|
+
if (accessToken) {
|
|
3456
|
+
const res = await fetch(
|
|
3457
|
+
`https://serviceusage.googleapis.com/v1/projects/${projectId}/services/firestore.googleapis.com:enable`,
|
|
3458
|
+
{
|
|
3459
|
+
method: "POST",
|
|
3460
|
+
headers: {
|
|
3461
|
+
Authorization: `Bearer ${accessToken}`,
|
|
3462
|
+
"Content-Type": "application/json"
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
);
|
|
3466
|
+
if (res.ok) {
|
|
3467
|
+
return { success: true, message: "Firestore API enabled via REST API." };
|
|
3468
|
+
}
|
|
3469
|
+
if (res.status === 409) {
|
|
3470
|
+
return { success: true, message: "Firestore API already enabled." };
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
} catch {
|
|
3474
|
+
}
|
|
3400
3475
|
try {
|
|
3401
3476
|
await this.execTimeout(
|
|
3402
3477
|
"gcloud",
|
|
3403
3478
|
["services", "enable", "firestore.googleapis.com", "--project", projectId],
|
|
3404
3479
|
6e4
|
|
3405
3480
|
);
|
|
3406
|
-
return { success: true, message: "Firestore API enabled." };
|
|
3481
|
+
return { success: true, message: "Firestore API enabled via gcloud." };
|
|
3407
3482
|
} catch (err) {
|
|
3408
3483
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
3409
|
-
if (msg.includes("ENOENT") || msg.includes("not found") || msg.includes("command not found")) {
|
|
3410
|
-
return {
|
|
3411
|
-
success: false,
|
|
3412
|
-
message: `gcloud CLI not found. Please enable the Firestore API manually:
|
|
3413
|
-
https://console.developers.google.com/apis/api/firestore.googleapis.com/overview?project=${projectId}`
|
|
3414
|
-
};
|
|
3484
|
+
if (!(msg.includes("ENOENT") || msg.includes("not found") || msg.includes("command not found"))) {
|
|
3485
|
+
return { success: false, message: `Failed to enable Firestore API: ${msg}` };
|
|
3415
3486
|
}
|
|
3416
|
-
return { success: false, message: `Failed to enable Firestore API: ${msg}` };
|
|
3417
3487
|
}
|
|
3488
|
+
return {
|
|
3489
|
+
success: false,
|
|
3490
|
+
message: `Failed to enable Firestore API automatically. Please enable it manually:
|
|
3491
|
+
https://console.developers.google.com/apis/api/firestore.googleapis.com/overview?project=${projectId}`
|
|
3492
|
+
};
|
|
3418
3493
|
}
|
|
3419
3494
|
/**
|
|
3420
3495
|
* Create Firestore database via CLI.
|
|
@@ -3422,41 +3497,48 @@ https://console.developers.google.com/apis/api/firestore.googleapis.com/overview
|
|
|
3422
3497
|
* Handles "ALREADY_EXISTS" gracefully — returns success.
|
|
3423
3498
|
*/
|
|
3424
3499
|
async createFirestoreDatabase(location = "nam5") {
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
)
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
if (msg.includes("ALREADY_EXISTS") || msg.includes("already exists")) {
|
|
3435
|
-
return { success: true, message: "Firestore database already exists." };
|
|
3436
|
-
}
|
|
3437
|
-
if (msg.includes("403") || msg.includes("has not been used") || msg.includes("is disabled")) {
|
|
3438
|
-
const enableResult = await this.enableFirestoreApi();
|
|
3439
|
-
if (!enableResult.success) {
|
|
3440
|
-
return enableResult;
|
|
3500
|
+
const dbArgs = ["firestore:databases:create", "(default)", "--location", location, "--json"];
|
|
3501
|
+
const tryCreate = async () => {
|
|
3502
|
+
try {
|
|
3503
|
+
await this.execTimeout("firebase", dbArgs, 6e4);
|
|
3504
|
+
return { ok: true, alreadyExists: false, needsApi: false, msg: "" };
|
|
3505
|
+
} catch (err) {
|
|
3506
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
3507
|
+
if (msg.includes("ALREADY_EXISTS") || msg.includes("already exists")) {
|
|
3508
|
+
return { ok: true, alreadyExists: true, needsApi: false, msg };
|
|
3441
3509
|
}
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
await this.execTimeout(
|
|
3445
|
-
"firebase",
|
|
3446
|
-
["firestore:databases:create", "(default)", "--location", location, "--json"],
|
|
3447
|
-
6e4
|
|
3448
|
-
);
|
|
3449
|
-
return { success: true, message: `Firestore API enabled and database created (location: ${location}).` };
|
|
3450
|
-
} catch (retryErr) {
|
|
3451
|
-
const retryMsg = retryErr instanceof Error ? retryErr.message : "Unknown error";
|
|
3452
|
-
if (retryMsg.includes("ALREADY_EXISTS") || retryMsg.includes("already exists")) {
|
|
3453
|
-
return { success: true, message: "Firestore database already exists." };
|
|
3454
|
-
}
|
|
3455
|
-
return { success: false, message: `Failed to create Firestore database after enabling API: ${retryMsg}` };
|
|
3510
|
+
if (msg.includes("403") || msg.includes("has not been used") || msg.includes("is disabled")) {
|
|
3511
|
+
return { ok: false, alreadyExists: false, needsApi: true, msg };
|
|
3456
3512
|
}
|
|
3513
|
+
return { ok: false, alreadyExists: false, needsApi: false, msg };
|
|
3514
|
+
}
|
|
3515
|
+
};
|
|
3516
|
+
const first = await tryCreate();
|
|
3517
|
+
if (first.ok) {
|
|
3518
|
+
return { success: true, message: first.alreadyExists ? "Firestore database already exists." : `Firestore database created (location: ${location}).` };
|
|
3519
|
+
}
|
|
3520
|
+
if (!first.needsApi) {
|
|
3521
|
+
return { success: false, message: `Failed to create Firestore database: ${first.msg}` };
|
|
3522
|
+
}
|
|
3523
|
+
const enableResult = await this.enableFirestoreApi();
|
|
3524
|
+
if (!enableResult.success) {
|
|
3525
|
+
return enableResult;
|
|
3526
|
+
}
|
|
3527
|
+
const waits = [5e3, 5e3, 1e4, 1e4, 15e3];
|
|
3528
|
+
for (let i = 0; i < waits.length; i++) {
|
|
3529
|
+
await new Promise((r) => setTimeout(r, waits[i]));
|
|
3530
|
+
const retry = await tryCreate();
|
|
3531
|
+
if (retry.ok) {
|
|
3532
|
+
return { success: true, message: `Firestore API enabled and database created (location: ${location}).` };
|
|
3533
|
+
}
|
|
3534
|
+
if (!retry.needsApi) {
|
|
3535
|
+
return { success: false, message: `Failed to create Firestore database: ${retry.msg}` };
|
|
3457
3536
|
}
|
|
3458
|
-
return { success: false, message: `Failed to create Firestore database: ${msg}` };
|
|
3459
3537
|
}
|
|
3538
|
+
return {
|
|
3539
|
+
success: false,
|
|
3540
|
+
message: "Firestore API was enabled but database creation timed out waiting for propagation. Please wait a minute and try again."
|
|
3541
|
+
};
|
|
3460
3542
|
}
|
|
3461
3543
|
/**
|
|
3462
3544
|
* Deploy open Firestore security rules for dev testing.
|
|
@@ -4476,10 +4558,57 @@ ${liveReloadScript}
|
|
|
4476
4558
|
sendJson({ success: false, message: "projectId is required" }, 400);
|
|
4477
4559
|
return;
|
|
4478
4560
|
}
|
|
4479
|
-
|
|
4561
|
+
(async () => {
|
|
4562
|
+
const selectResult = await this.firebaseSetup.selectProject(data.projectId);
|
|
4563
|
+
if (!selectResult.success) {
|
|
4564
|
+
sendJson(selectResult);
|
|
4565
|
+
return;
|
|
4566
|
+
}
|
|
4567
|
+
const steps = [selectResult.message];
|
|
4568
|
+
try {
|
|
4569
|
+
const { apps } = await this.firebaseSetup.listWebApps();
|
|
4570
|
+
let webAppId = "";
|
|
4571
|
+
if (apps.length > 0) {
|
|
4572
|
+
webAppId = apps[0].appId;
|
|
4573
|
+
this.firebaseSetup.selectWebApp(apps[0].appId, apps[0].displayName);
|
|
4574
|
+
steps.push(`Web app "${apps[0].displayName}" selected.`);
|
|
4575
|
+
} else {
|
|
4576
|
+
const createResult = await this.firebaseSetup.createWebApp(data.projectId);
|
|
4577
|
+
if (createResult.success && createResult.appId) {
|
|
4578
|
+
webAppId = createResult.appId;
|
|
4579
|
+
steps.push(`Web app "${data.projectId}" created.`);
|
|
4580
|
+
} else {
|
|
4581
|
+
steps.push(`Web app creation skipped: ${createResult.message}`);
|
|
4582
|
+
}
|
|
4583
|
+
}
|
|
4584
|
+
if (webAppId) {
|
|
4585
|
+
try {
|
|
4586
|
+
const sdkConfig = await fetchFirebaseSdkConfig(this.options.projectDir, webAppId);
|
|
4587
|
+
const fields = {};
|
|
4588
|
+
for (const [key, value] of Object.entries(sdkConfig)) {
|
|
4589
|
+
if (value && typeof value === "string") {
|
|
4590
|
+
fields[key] = value;
|
|
4591
|
+
}
|
|
4592
|
+
}
|
|
4593
|
+
if (Object.keys(fields).length > 0) {
|
|
4594
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
4595
|
+
try {
|
|
4596
|
+
this.updateProjectConfig(key, value);
|
|
4597
|
+
} catch {
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
steps.push("Config auto-filled in clawfire.config.ts.");
|
|
4601
|
+
}
|
|
4602
|
+
} catch (configErr) {
|
|
4603
|
+
steps.push(`Config auto-fill skipped: ${configErr instanceof Error ? configErr.message : "unknown error"}`);
|
|
4604
|
+
}
|
|
4605
|
+
}
|
|
4606
|
+
} catch (autoFillErr) {
|
|
4607
|
+
steps.push(`Auto-setup partial: ${autoFillErr instanceof Error ? autoFillErr.message : "unknown error"}`);
|
|
4608
|
+
}
|
|
4480
4609
|
clearFirebaseStatusCache();
|
|
4481
|
-
sendJson(
|
|
4482
|
-
}).catch((err) => sendJson({ success: false, message: err instanceof Error ? err.message : "Failed" }, 500));
|
|
4610
|
+
sendJson({ success: true, message: steps.join(" "), steps });
|
|
4611
|
+
})().catch((err) => sendJson({ success: false, message: err instanceof Error ? err.message : "Failed" }, 500));
|
|
4483
4612
|
} catch {
|
|
4484
4613
|
sendJson({ success: false, message: "Invalid JSON body" }, 400);
|
|
4485
4614
|
}
|