@globio/cli 0.1.7 → 0.1.9
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/README.md +6 -0
- package/dist/index.js +304 -145
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/auth/login.ts +9 -10
- package/src/auth/logout.ts +5 -5
- package/src/auth/whoami.ts +58 -19
- package/src/commands/functions.ts +60 -34
- package/src/commands/init.ts +3 -2
- package/src/commands/migrate.ts +32 -10
- package/src/commands/profiles.ts +39 -12
- package/src/commands/projects.ts +51 -29
- package/src/commands/services.ts +62 -22
- package/src/lib/api.ts +21 -0
- package/src/lib/banner.ts +1 -6
- package/src/lib/config.ts +2 -0
- package/src/lib/manage.ts +4 -0
- package/src/lib/table.ts +97 -0
package/dist/index.js
CHANGED
|
@@ -6,7 +6,6 @@ import { Command } from "commander";
|
|
|
6
6
|
// src/auth/login.ts
|
|
7
7
|
import * as p from "@clack/prompts";
|
|
8
8
|
import { exec } from "child_process";
|
|
9
|
-
import chalk2 from "chalk";
|
|
10
9
|
|
|
11
10
|
// src/lib/config.ts
|
|
12
11
|
import chalk from "chalk";
|
|
@@ -74,6 +73,7 @@ var config = {
|
|
|
74
73
|
pat: data.pat ?? existing?.pat ?? "",
|
|
75
74
|
account_email: data.account_email ?? existing?.account_email ?? "",
|
|
76
75
|
account_name: data.account_name ?? existing?.account_name ?? "",
|
|
76
|
+
org_name: data.org_name ?? existing?.org_name,
|
|
77
77
|
active_project_id: data.active_project_id ?? existing?.active_project_id,
|
|
78
78
|
active_project_name: data.active_project_name ?? existing?.active_project_name,
|
|
79
79
|
project_api_key: data.project_api_key ?? existing?.project_api_key,
|
|
@@ -156,6 +156,73 @@ function getConsoleCliAuthUrl(state) {
|
|
|
156
156
|
import { readFileSync as readFileSync2 } from "fs";
|
|
157
157
|
import figlet from "figlet";
|
|
158
158
|
import gradientString from "gradient-string";
|
|
159
|
+
|
|
160
|
+
// src/lib/table.ts
|
|
161
|
+
var ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
|
|
162
|
+
var orange = (s) => "\x1B[38;2;244;140;6m" + s;
|
|
163
|
+
var gold = (s) => "\x1B[38;2;255;208;0m" + s;
|
|
164
|
+
var dim = (s) => "\x1B[2m" + s + "\x1B[0m";
|
|
165
|
+
var white = (s) => "\x1B[97m" + s;
|
|
166
|
+
var green = (s) => "\x1B[38;2;34;197;94m" + s;
|
|
167
|
+
var muted = (s) => "\x1B[38;2;85;85;85m" + s;
|
|
168
|
+
var inactive = (s) => "\x1B[38;2;68;68;68m" + s;
|
|
169
|
+
var failure = (s) => "\x1B[38;2;232;93;4m" + s;
|
|
170
|
+
var reset = "\x1B[0m";
|
|
171
|
+
function stripAnsi(value) {
|
|
172
|
+
return value.replace(ANSI_PATTERN, "");
|
|
173
|
+
}
|
|
174
|
+
function fitCell(value, width) {
|
|
175
|
+
const plain = stripAnsi(value);
|
|
176
|
+
if (plain.length <= width) {
|
|
177
|
+
return value + " ".repeat(width - plain.length);
|
|
178
|
+
}
|
|
179
|
+
const truncated = plain.slice(0, width);
|
|
180
|
+
return truncated;
|
|
181
|
+
}
|
|
182
|
+
function renderTable(options) {
|
|
183
|
+
const { columns, rows } = options;
|
|
184
|
+
const lines = [];
|
|
185
|
+
lines.push(
|
|
186
|
+
" \u250C" + columns.map((c) => "\u2500".repeat(c.width + 2)).join("\u252C") + "\u2510"
|
|
187
|
+
);
|
|
188
|
+
lines.push(
|
|
189
|
+
" \u2502" + columns.map((c) => " " + dim(c.header.padEnd(c.width)) + " \u2502").join("")
|
|
190
|
+
);
|
|
191
|
+
lines.push(
|
|
192
|
+
" \u251C" + columns.map((c) => "\u2500".repeat(c.width + 2)).join("\u253C") + "\u2524"
|
|
193
|
+
);
|
|
194
|
+
for (const row of rows) {
|
|
195
|
+
lines.push(
|
|
196
|
+
" \u2502" + columns.map((c, i) => {
|
|
197
|
+
const raw = row[i] ?? "";
|
|
198
|
+
const fitted = fitCell(raw, c.width);
|
|
199
|
+
const colored = c.color ? fitCell(c.color(stripAnsi(raw)), c.width) : fitted;
|
|
200
|
+
return " " + colored + " " + reset + "\u2502";
|
|
201
|
+
}).join("")
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
lines.push(
|
|
205
|
+
" \u2514" + columns.map((c) => "\u2500".repeat(c.width + 2)).join("\u2534") + "\u2518"
|
|
206
|
+
);
|
|
207
|
+
return lines.join("\n");
|
|
208
|
+
}
|
|
209
|
+
function header(version10, subtitle) {
|
|
210
|
+
const lines = [
|
|
211
|
+
"",
|
|
212
|
+
orange(" \u21D2\u21D2") + reset + " globio " + dim(version10),
|
|
213
|
+
dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")
|
|
214
|
+
];
|
|
215
|
+
if (subtitle) {
|
|
216
|
+
lines.push(" " + subtitle);
|
|
217
|
+
}
|
|
218
|
+
lines.push("");
|
|
219
|
+
return lines.join("\n");
|
|
220
|
+
}
|
|
221
|
+
function footer(text4) {
|
|
222
|
+
return "\n" + dim(" " + text4) + "\n";
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/lib/banner.ts
|
|
159
226
|
var globioGradient = gradientString(
|
|
160
227
|
"#e85d04",
|
|
161
228
|
"#f48c06",
|
|
@@ -163,23 +230,20 @@ var globioGradient = gradientString(
|
|
|
163
230
|
"#ffba08",
|
|
164
231
|
"#ffd000"
|
|
165
232
|
);
|
|
166
|
-
function printBanner(
|
|
233
|
+
function printBanner(version10) {
|
|
167
234
|
const art = figlet.textSync("Globio", {
|
|
168
235
|
font: "ANSI Shadow",
|
|
169
236
|
horizontalLayout: "default"
|
|
170
237
|
});
|
|
171
238
|
console.log(globioGradient.multiline(art));
|
|
172
239
|
console.log(
|
|
173
|
-
globioGradient(" \u21D2\u21D2") + " Game Backend as a Service \x1B[2mv" +
|
|
240
|
+
globioGradient(" \u21D2\u21D2") + " Game Backend as a Service \x1B[2mv" + version10 + "\x1B[0m"
|
|
174
241
|
);
|
|
175
242
|
console.log("");
|
|
176
243
|
}
|
|
177
244
|
function printSuccess(message) {
|
|
178
245
|
console.log("\x1B[38;2;250;163;7m\u2713\x1B[0m " + message);
|
|
179
246
|
}
|
|
180
|
-
var orange = (s) => "\x1B[38;2;244;140;6m" + s + "\x1B[0m";
|
|
181
|
-
var gold = (s) => "\x1B[38;2;255;208;0m" + s + "\x1B[0m";
|
|
182
|
-
var muted = (s) => "\x1B[2m" + s + "\x1B[0m";
|
|
183
247
|
function getCliVersion() {
|
|
184
248
|
const file = readFileSync2(new URL("../package.json", import.meta.url), "utf8");
|
|
185
249
|
return JSON.parse(file).version;
|
|
@@ -207,7 +271,7 @@ function warnOnDuplicateAccount(accountEmail, targetProfileName) {
|
|
|
207
271
|
if (!duplicate) return;
|
|
208
272
|
console.log("");
|
|
209
273
|
console.log(
|
|
210
|
-
|
|
274
|
+
failure(" \u26A0 ") + white(accountEmail) + "\x1B[2m is already logged in under profile \x1B[0m" + orange(`"${duplicate}"`) + "\x1B[2m.\x1B[0m"
|
|
211
275
|
);
|
|
212
276
|
console.log("");
|
|
213
277
|
}
|
|
@@ -245,7 +309,7 @@ async function runTokenLogin(profileName) {
|
|
|
245
309
|
Profile: ${profileName}`);
|
|
246
310
|
} catch (error) {
|
|
247
311
|
spinner2.stop("Validation failed.");
|
|
248
|
-
p.outro(
|
|
312
|
+
p.outro(failure(error instanceof Error ? error.message : "Could not validate token") + "\x1B[0m");
|
|
249
313
|
process.exit(1);
|
|
250
314
|
}
|
|
251
315
|
}
|
|
@@ -270,7 +334,7 @@ async function runBrowserLogin(profileName) {
|
|
|
270
334
|
);
|
|
271
335
|
if (status.status === "expired") {
|
|
272
336
|
spinner2.stop("Approval window expired.");
|
|
273
|
-
p.outro(
|
|
337
|
+
p.outro(failure("CLI auth request expired. Try again or use globio login --token.") + "\x1B[0m");
|
|
274
338
|
process.exit(1);
|
|
275
339
|
}
|
|
276
340
|
if (status.status === "approved" && status.code) {
|
|
@@ -298,7 +362,7 @@ Profile: ${profileName}`);
|
|
|
298
362
|
await sleep(2e3);
|
|
299
363
|
}
|
|
300
364
|
spinner2.stop("Approval timed out.");
|
|
301
|
-
p.outro(
|
|
365
|
+
p.outro(failure("Timed out waiting for browser approval. Try again or use globio login --token.") + "\x1B[0m");
|
|
302
366
|
process.exit(1);
|
|
303
367
|
}
|
|
304
368
|
async function login(options = {}) {
|
|
@@ -337,19 +401,18 @@ async function login(options = {}) {
|
|
|
337
401
|
try {
|
|
338
402
|
await runBrowserLogin(profileName);
|
|
339
403
|
} catch (error) {
|
|
340
|
-
p.outro(
|
|
404
|
+
p.outro(failure(error instanceof Error ? error.message : "Could not connect to Globio.") + "\x1B[0m");
|
|
341
405
|
process.exit(1);
|
|
342
406
|
}
|
|
343
407
|
}
|
|
344
408
|
|
|
345
409
|
// src/auth/logout.ts
|
|
346
|
-
import chalk3 from "chalk";
|
|
347
410
|
async function logout(options = {}) {
|
|
348
411
|
const activeProfile = config.getActiveProfile();
|
|
349
412
|
const profileName = options.profile ?? activeProfile;
|
|
350
413
|
const profile = profileName ? config.getProfile(profileName) : null;
|
|
351
414
|
if (!profileName || !profile) {
|
|
352
|
-
console.log(
|
|
415
|
+
console.log(inactive(`No active session on profile "${profileName || "default"}".`));
|
|
353
416
|
return;
|
|
354
417
|
}
|
|
355
418
|
config.deleteProfile(profileName);
|
|
@@ -357,23 +420,23 @@ async function logout(options = {}) {
|
|
|
357
420
|
const remaining = config.listProfiles();
|
|
358
421
|
if (remaining.length > 0) {
|
|
359
422
|
config.setActiveProfile(remaining[0]);
|
|
360
|
-
console.log(
|
|
423
|
+
console.log(green(`Logged out. Switched to profile: ${remaining[0]}`));
|
|
361
424
|
return;
|
|
362
425
|
}
|
|
363
426
|
config.setActiveProfile("");
|
|
364
|
-
console.log(
|
|
427
|
+
console.log(green("Logged out."));
|
|
365
428
|
return;
|
|
366
429
|
}
|
|
367
|
-
console.log(
|
|
430
|
+
console.log(green(`Logged out profile: ${profileName}`));
|
|
368
431
|
}
|
|
369
432
|
|
|
370
433
|
// src/auth/useProfile.ts
|
|
371
|
-
import
|
|
434
|
+
import chalk2 from "chalk";
|
|
372
435
|
async function useProfile(profileName) {
|
|
373
436
|
const profile = config.getProfile(profileName);
|
|
374
437
|
if (!profile) {
|
|
375
438
|
console.log(
|
|
376
|
-
|
|
439
|
+
chalk2.red(
|
|
377
440
|
`Profile "${profileName}" not found. Run: globio login --profile ${profileName}`
|
|
378
441
|
)
|
|
379
442
|
);
|
|
@@ -381,36 +444,51 @@ async function useProfile(profileName) {
|
|
|
381
444
|
}
|
|
382
445
|
config.setActiveProfile(profileName);
|
|
383
446
|
console.log(
|
|
384
|
-
|
|
447
|
+
chalk2.green("Switched to profile: ") + orange(profileName) + ` (${profile.account_email})`
|
|
385
448
|
);
|
|
386
449
|
}
|
|
387
450
|
|
|
388
451
|
// src/auth/whoami.ts
|
|
389
|
-
|
|
452
|
+
var version2 = getCliVersion();
|
|
390
453
|
async function whoami(options = {}) {
|
|
391
454
|
const profileName = options.profile ?? config.getActiveProfile() ?? "default";
|
|
392
455
|
const profile = config.getProfile(profileName);
|
|
393
456
|
if (!profile) {
|
|
394
|
-
console.log(
|
|
457
|
+
console.log(
|
|
458
|
+
header(version2) + " " + failure("Not logged in.") + reset + " Run: globio login\n"
|
|
459
|
+
);
|
|
395
460
|
return;
|
|
396
461
|
}
|
|
397
462
|
const allProfiles = config.listProfiles();
|
|
398
|
-
const
|
|
399
|
-
|
|
463
|
+
const active = config.getActiveProfile();
|
|
464
|
+
const otherProfiles = allProfiles.filter((p6) => p6 !== profileName).join(", ") || "\u2014";
|
|
400
465
|
console.log(
|
|
401
|
-
|
|
466
|
+
header(
|
|
467
|
+
version2,
|
|
468
|
+
"Logged in as " + orange(profile.account_name || profile.account_email) + reset + " \xB7 " + muted(profile.account_email)
|
|
469
|
+
)
|
|
402
470
|
);
|
|
403
|
-
console.log(muted("Account: ") + profile.account_email);
|
|
404
|
-
console.log(muted("Name: ") + (profile.account_name || "\u2014"));
|
|
405
471
|
console.log(
|
|
406
|
-
|
|
472
|
+
renderTable({
|
|
473
|
+
columns: [
|
|
474
|
+
{ header: "Profile", width: 16 },
|
|
475
|
+
{ header: "Value", width: 44 }
|
|
476
|
+
],
|
|
477
|
+
rows: [
|
|
478
|
+
[
|
|
479
|
+
"Profile",
|
|
480
|
+
profileName === active ? orange(profileName) + reset + " " + inactive("(active)") : inactive(profileName)
|
|
481
|
+
],
|
|
482
|
+
["Other profiles", inactive(otherProfiles)],
|
|
483
|
+
["Account", white(profile.account_name || "\u2014")],
|
|
484
|
+
["Organization", white(profile.org_name || "\u2014")],
|
|
485
|
+
[
|
|
486
|
+
"Active project",
|
|
487
|
+
profile.active_project_id ? gold(profile.active_project_name || "unnamed") + reset + " " + inactive(profile.active_project_id) : inactive("none \u2014 run: globio projects use <id>")
|
|
488
|
+
]
|
|
489
|
+
]
|
|
490
|
+
})
|
|
407
491
|
);
|
|
408
|
-
if (allProfiles.length > 1) {
|
|
409
|
-
console.log("");
|
|
410
|
-
console.log(
|
|
411
|
-
muted("Other profiles: ") + allProfiles.filter((name) => name !== profileName).join(", ")
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
492
|
console.log("");
|
|
415
493
|
}
|
|
416
494
|
|
|
@@ -443,7 +521,6 @@ async function promptInit() {
|
|
|
443
521
|
|
|
444
522
|
// src/commands/migrate.ts
|
|
445
523
|
import * as p3 from "@clack/prompts";
|
|
446
|
-
import chalk7 from "chalk";
|
|
447
524
|
import { basename } from "path";
|
|
448
525
|
|
|
449
526
|
// src/lib/api.ts
|
|
@@ -475,6 +552,20 @@ async function docSet(collection, docId, data, profile) {
|
|
|
475
552
|
profile
|
|
476
553
|
});
|
|
477
554
|
}
|
|
555
|
+
async function createIndex(collection, field, fieldType = "string", profile) {
|
|
556
|
+
void fieldType;
|
|
557
|
+
try {
|
|
558
|
+
await apiCall(`/doc/${collection}/indexes`, {
|
|
559
|
+
method: "POST",
|
|
560
|
+
body: {
|
|
561
|
+
field_path: field,
|
|
562
|
+
index_type: "asc"
|
|
563
|
+
},
|
|
564
|
+
profile
|
|
565
|
+
});
|
|
566
|
+
} catch {
|
|
567
|
+
}
|
|
568
|
+
}
|
|
478
569
|
|
|
479
570
|
// src/lib/firebase.ts
|
|
480
571
|
async function initFirebase(serviceAccountPath) {
|
|
@@ -495,12 +586,12 @@ async function initFirebase(serviceAccountPath) {
|
|
|
495
586
|
}
|
|
496
587
|
|
|
497
588
|
// src/lib/progress.ts
|
|
498
|
-
import
|
|
589
|
+
import chalk3 from "chalk";
|
|
499
590
|
import cliProgress from "cli-progress";
|
|
500
591
|
function createProgressBar(label) {
|
|
501
592
|
const bar = new cliProgress.SingleBar(
|
|
502
593
|
{
|
|
503
|
-
format:
|
|
594
|
+
format: chalk3.cyan(label) + " [{bar}] {percentage}% | {value}/{total}",
|
|
504
595
|
barCompleteChar: "\u2588",
|
|
505
596
|
barIncompleteChar: "\u2591",
|
|
506
597
|
hideCursor: true
|
|
@@ -511,12 +602,12 @@ function createProgressBar(label) {
|
|
|
511
602
|
}
|
|
512
603
|
|
|
513
604
|
// src/commands/migrate.ts
|
|
514
|
-
var
|
|
605
|
+
var version3 = getCliVersion();
|
|
515
606
|
function resolveProfileName(profile) {
|
|
516
607
|
return profile ?? config.getActiveProfile() ?? "default";
|
|
517
608
|
}
|
|
518
609
|
async function migrateFirestore(options) {
|
|
519
|
-
printBanner(
|
|
610
|
+
printBanner(version3);
|
|
520
611
|
p3.intro(gold("\u21D2\u21D2") + " Firebase \u2192 Globio Migration");
|
|
521
612
|
const { firestore } = await initFirebase(options.from);
|
|
522
613
|
const profileName = resolveProfileName(options.profile);
|
|
@@ -525,14 +616,14 @@ async function migrateFirestore(options) {
|
|
|
525
616
|
const snapshot = await firestore.listCollections();
|
|
526
617
|
collections = snapshot.map((collection) => collection.id);
|
|
527
618
|
console.log(
|
|
528
|
-
|
|
619
|
+
green(
|
|
529
620
|
`Found ${collections.length} collections: ${collections.join(", ")}`
|
|
530
621
|
)
|
|
531
622
|
);
|
|
532
623
|
} else if (options.collection) {
|
|
533
624
|
collections = [options.collection];
|
|
534
625
|
} else {
|
|
535
|
-
console.log(
|
|
626
|
+
console.log(failure("Specify --collection <name> or --all"));
|
|
536
627
|
process.exit(1);
|
|
537
628
|
}
|
|
538
629
|
const results = {};
|
|
@@ -550,6 +641,8 @@ async function migrateFirestore(options) {
|
|
|
550
641
|
};
|
|
551
642
|
let lastDoc = null;
|
|
552
643
|
let processed = 0;
|
|
644
|
+
let firstDocData = null;
|
|
645
|
+
let indexFieldCount = 0;
|
|
553
646
|
while (processed < total) {
|
|
554
647
|
let query = firestore.collection(collectionId).limit(100);
|
|
555
648
|
if (lastDoc) {
|
|
@@ -561,6 +654,14 @@ async function migrateFirestore(options) {
|
|
|
561
654
|
}
|
|
562
655
|
for (const doc of snapshot.docs) {
|
|
563
656
|
try {
|
|
657
|
+
if (!firstDocData) {
|
|
658
|
+
firstDocData = doc.data();
|
|
659
|
+
for (const [field, value] of Object.entries(firstDocData)) {
|
|
660
|
+
const fieldType = typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : "string";
|
|
661
|
+
await createIndex(collectionId, field, fieldType, profileName);
|
|
662
|
+
}
|
|
663
|
+
indexFieldCount = Object.keys(firstDocData).length;
|
|
664
|
+
}
|
|
564
665
|
await docSet(collectionId, doc.id, doc.data(), profileName);
|
|
565
666
|
results[collectionId].success++;
|
|
566
667
|
} catch {
|
|
@@ -574,12 +675,17 @@ async function migrateFirestore(options) {
|
|
|
574
675
|
}
|
|
575
676
|
bar.stop();
|
|
576
677
|
console.log(
|
|
577
|
-
|
|
678
|
+
green(` \u2713 ${results[collectionId].success} documents migrated`)
|
|
578
679
|
);
|
|
680
|
+
if (indexFieldCount > 0) {
|
|
681
|
+
console.log(
|
|
682
|
+
muted(` Indexes created for ${indexFieldCount} fields`)
|
|
683
|
+
);
|
|
684
|
+
}
|
|
579
685
|
if (results[collectionId].failed > 0) {
|
|
580
|
-
console.log(
|
|
686
|
+
console.log(failure(` \u2717 ${results[collectionId].failed} failed`) + "\x1B[0m");
|
|
581
687
|
console.log(
|
|
582
|
-
|
|
688
|
+
muted(
|
|
583
689
|
" Failed IDs: " + results[collectionId].failedIds.slice(0, 10).join(", ") + (results[collectionId].failedIds.length > 10 ? "..." : "")
|
|
584
690
|
)
|
|
585
691
|
);
|
|
@@ -591,7 +697,7 @@ async function migrateFirestore(options) {
|
|
|
591
697
|
);
|
|
592
698
|
}
|
|
593
699
|
async function migrateFirebaseStorage(options) {
|
|
594
|
-
printBanner(
|
|
700
|
+
printBanner(version3);
|
|
595
701
|
p3.intro(gold("\u21D2\u21D2") + " Firebase \u2192 Globio Migration");
|
|
596
702
|
const { storage } = await initFirebase(options.from);
|
|
597
703
|
const profileName = resolveProfileName(options.profile);
|
|
@@ -603,7 +709,7 @@ async function migrateFirebaseStorage(options) {
|
|
|
603
709
|
const bucket = storage.bucket(bucketName);
|
|
604
710
|
const prefix = options.folder ? options.folder.replace(/^\//, "") : "";
|
|
605
711
|
const [files] = await bucket.getFiles(prefix ? { prefix } : {});
|
|
606
|
-
console.log(
|
|
712
|
+
console.log(green(`Found ${files.length} files to migrate`));
|
|
607
713
|
const bar = createProgressBar("Storage");
|
|
608
714
|
bar.start(files.length, 0);
|
|
609
715
|
let success = 0;
|
|
@@ -640,9 +746,9 @@ async function migrateFirebaseStorage(options) {
|
|
|
640
746
|
}
|
|
641
747
|
bar.stop();
|
|
642
748
|
console.log("");
|
|
643
|
-
console.log(
|
|
749
|
+
console.log(green(` \u2713 ${success} files migrated`));
|
|
644
750
|
if (failed > 0) {
|
|
645
|
-
console.log(
|
|
751
|
+
console.log(failure(` \u2717 ${failed} failed`) + "\x1B[0m");
|
|
646
752
|
}
|
|
647
753
|
p3.outro(
|
|
648
754
|
orange("\u2713") + " Migration complete.\n\n " + muted("Your Firebase data is intact.") + "\n " + muted("Delete it manually when ready.")
|
|
@@ -651,7 +757,7 @@ async function migrateFirebaseStorage(options) {
|
|
|
651
757
|
|
|
652
758
|
// src/commands/projects.ts
|
|
653
759
|
import * as p4 from "@clack/prompts";
|
|
654
|
-
|
|
760
|
+
var version4 = getCliVersion();
|
|
655
761
|
function slugify(value) {
|
|
656
762
|
return value.toLowerCase().trim().replace(/[^a-z0-9\\s-]/g, "").replace(/\\s+/g, "-").replace(/-+/g, "-");
|
|
657
763
|
}
|
|
@@ -677,27 +783,31 @@ async function projectsList(options = {}) {
|
|
|
677
783
|
config.requireAuth(profileName);
|
|
678
784
|
const projects2 = await manageRequest("/projects", { profileName });
|
|
679
785
|
const activeProjectId = config.getProfile(profileName)?.active_project_id;
|
|
680
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
681
|
-
for (const project of projects2) {
|
|
682
|
-
const list = grouped.get(project.org_name) ?? [];
|
|
683
|
-
list.push(project);
|
|
684
|
-
grouped.set(project.org_name, list);
|
|
685
|
-
}
|
|
686
|
-
console.log("");
|
|
687
786
|
if (!projects2.length) {
|
|
688
|
-
console.log(
|
|
689
|
-
console.log("");
|
|
787
|
+
console.log(header(version4) + " " + muted("No projects found.") + "\n");
|
|
690
788
|
return;
|
|
691
789
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
790
|
+
const rows = projects2.map((project) => [
|
|
791
|
+
activeProjectId === project.id ? gold(project.name) + reset + " " + orange("\u25CF") + reset : white(project.name),
|
|
792
|
+
muted(project.id),
|
|
793
|
+
muted(project.org_name || project.org_id),
|
|
794
|
+
inactive(project.environment?.slice(0, 4) ?? "dev")
|
|
795
|
+
]);
|
|
796
|
+
console.log(header(version4));
|
|
797
|
+
console.log(
|
|
798
|
+
renderTable({
|
|
799
|
+
columns: [
|
|
800
|
+
{ header: "Project", width: 24 },
|
|
801
|
+
{ header: "ID", width: 26 },
|
|
802
|
+
{ header: "Org", width: 16 },
|
|
803
|
+
{ header: "Env", width: 6 }
|
|
804
|
+
],
|
|
805
|
+
rows
|
|
806
|
+
})
|
|
807
|
+
);
|
|
808
|
+
console.log(
|
|
809
|
+
footer("\u25CF active project \xB7 run globio projects use <id> to switch")
|
|
810
|
+
);
|
|
701
811
|
}
|
|
702
812
|
async function projectsUse(projectId, options = {}) {
|
|
703
813
|
const profileName = resolveProfileName2(options.profile);
|
|
@@ -705,7 +815,7 @@ async function projectsUse(projectId, options = {}) {
|
|
|
705
815
|
const projects2 = await manageRequest("/projects", { profileName });
|
|
706
816
|
const project = projects2.find((item) => item.id === projectId);
|
|
707
817
|
if (!project) {
|
|
708
|
-
console.log(
|
|
818
|
+
console.log(failure(`Project not found: ${projectId}`));
|
|
709
819
|
process.exit(1);
|
|
710
820
|
}
|
|
711
821
|
await manageRequest(`/projects/${projectId}/keys`, { profileName });
|
|
@@ -713,19 +823,18 @@ async function projectsUse(projectId, options = {}) {
|
|
|
713
823
|
config.setProfile(profileName, {
|
|
714
824
|
active_project_id: project.id,
|
|
715
825
|
active_project_name: project.name,
|
|
826
|
+
org_name: project.org_name,
|
|
716
827
|
project_api_key: apiKey
|
|
717
828
|
});
|
|
718
829
|
config.setActiveProfile(profileName);
|
|
719
|
-
console.log(
|
|
720
|
-
chalk8.green("Active project set to: ") + chalk8.cyan(`${project.name} (${project.id})`)
|
|
721
|
-
);
|
|
830
|
+
console.log(green("Active project: ") + `${project.name} (${project.id})`);
|
|
722
831
|
}
|
|
723
832
|
async function projectsCreate(options = {}) {
|
|
724
833
|
const profileName = resolveProfileName2(options.profile);
|
|
725
834
|
config.requireAuth(profileName);
|
|
726
835
|
const orgs = await manageRequest("/orgs", { profileName });
|
|
727
836
|
if (!orgs.length) {
|
|
728
|
-
console.log(
|
|
837
|
+
console.log(failure("No organizations found. Create one in the console first."));
|
|
729
838
|
process.exit(1);
|
|
730
839
|
}
|
|
731
840
|
const orgId = await p4.select({
|
|
@@ -780,26 +889,27 @@ async function projectsCreate(options = {}) {
|
|
|
780
889
|
config.setProfile(profileName, {
|
|
781
890
|
active_project_id: result.project.id,
|
|
782
891
|
active_project_name: result.project.name,
|
|
892
|
+
org_name: orgs.find((org) => org.id === orgId)?.name,
|
|
783
893
|
project_api_key: result.keys.server
|
|
784
894
|
});
|
|
785
895
|
config.setActiveProfile(profileName);
|
|
786
896
|
console.log("");
|
|
787
|
-
console.log(
|
|
788
|
-
console.log(
|
|
789
|
-
console.log(
|
|
790
|
-
console.log(
|
|
897
|
+
console.log(green("Project created successfully."));
|
|
898
|
+
console.log(orange("Project: ") + reset + `${result.project.name} (${result.project.id})`);
|
|
899
|
+
console.log(orange("Client key: ") + reset + result.keys.client);
|
|
900
|
+
console.log(orange("Server key: ") + reset + result.keys.server);
|
|
791
901
|
console.log("");
|
|
792
902
|
}
|
|
793
903
|
|
|
794
904
|
// src/commands/init.ts
|
|
795
|
-
var
|
|
905
|
+
var version5 = getCliVersion();
|
|
796
906
|
async function init(options = {}) {
|
|
797
|
-
printBanner(
|
|
907
|
+
printBanner(version5);
|
|
798
908
|
p5.intro(orange("\u21D2\u21D2") + " Initialize your Globio project");
|
|
799
909
|
const profileName = options.profile ?? config.getActiveProfile() ?? "default";
|
|
800
910
|
const profile = config.getProfile(profileName);
|
|
801
911
|
if (!profile) {
|
|
802
|
-
console.log("Run: npx @globio/cli login --profile " + profileName);
|
|
912
|
+
console.log(failure("Run: npx @globio/cli login --profile " + profileName));
|
|
803
913
|
process.exit(1);
|
|
804
914
|
}
|
|
805
915
|
if (!profile.active_project_id) {
|
|
@@ -812,7 +922,7 @@ async function init(options = {}) {
|
|
|
812
922
|
const activeProjectKey = activeProfile?.project_api_key;
|
|
813
923
|
const { projectId: activeProjectId } = config.requireProject(profileName);
|
|
814
924
|
if (!activeProjectKey) {
|
|
815
|
-
console.log("No project API key cached. Run: npx @globio/cli projects use " + activeProjectId);
|
|
925
|
+
console.log(failure("No project API key cached. Run: npx @globio/cli projects use " + activeProjectId));
|
|
816
926
|
process.exit(1);
|
|
817
927
|
}
|
|
818
928
|
if (!existsSync2("globio.config.ts")) {
|
|
@@ -862,58 +972,90 @@ export const globio = new Globio({
|
|
|
862
972
|
}
|
|
863
973
|
|
|
864
974
|
// src/commands/profiles.ts
|
|
865
|
-
|
|
975
|
+
var version6 = getCliVersion();
|
|
866
976
|
async function profilesList() {
|
|
867
977
|
const profiles2 = config.listProfiles();
|
|
868
978
|
const active = config.getActiveProfile();
|
|
869
979
|
if (!profiles2.length) {
|
|
870
|
-
console.log(
|
|
980
|
+
console.log(
|
|
981
|
+
header(version6) + " " + muted("No profiles. Run: globio login") + "\n"
|
|
982
|
+
);
|
|
871
983
|
return;
|
|
872
984
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
const data = config.getProfile(name);
|
|
985
|
+
const rows = profiles2.map((name) => {
|
|
986
|
+
const p6 = config.getProfile(name);
|
|
876
987
|
const isActive = name === active;
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
}
|
|
988
|
+
return [
|
|
989
|
+
isActive ? orange(name) : inactive(name),
|
|
990
|
+
p6?.account_email ? isActive ? white(p6.account_email) : muted(p6.account_email) : inactive("\u2014"),
|
|
991
|
+
isActive ? green("active") : inactive("\u2014")
|
|
992
|
+
];
|
|
993
|
+
});
|
|
994
|
+
console.log(header(version6));
|
|
995
|
+
console.log(
|
|
996
|
+
renderTable({
|
|
997
|
+
columns: [
|
|
998
|
+
{ header: "Profile", width: 16 },
|
|
999
|
+
{ header: "Account", width: 36 },
|
|
1000
|
+
{ header: "Status", width: 10 }
|
|
1001
|
+
],
|
|
1002
|
+
rows
|
|
1003
|
+
})
|
|
1004
|
+
);
|
|
883
1005
|
console.log("");
|
|
884
1006
|
}
|
|
885
1007
|
|
|
886
1008
|
// src/commands/services.ts
|
|
887
|
-
|
|
888
|
-
var
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1009
|
+
var version7 = getCliVersion();
|
|
1010
|
+
var SERVICE_DESCRIPTIONS = {
|
|
1011
|
+
id: "Authentication and user management",
|
|
1012
|
+
doc: "Document database",
|
|
1013
|
+
vault: "File storage",
|
|
1014
|
+
pulse: "Feature flags and remote config",
|
|
1015
|
+
scope: "Analytics and event tracking",
|
|
1016
|
+
sync: "Real-time multiplayer rooms",
|
|
1017
|
+
signal: "Push notifications",
|
|
1018
|
+
mart: "Game economy and payments",
|
|
1019
|
+
brain: "AI agents and LLM routing",
|
|
1020
|
+
code: "Edge functions and GC Hooks"
|
|
1021
|
+
};
|
|
900
1022
|
async function servicesList(options = {}) {
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
1023
|
+
const profileName = options.profile ?? config.getActiveProfile() ?? "default";
|
|
1024
|
+
const profile = config.getProfile(profileName);
|
|
1025
|
+
let serviceStatuses = {};
|
|
1026
|
+
if (profile?.active_project_id) {
|
|
1027
|
+
try {
|
|
1028
|
+
serviceStatuses = await manageRequest(
|
|
1029
|
+
`/projects/${profile.active_project_id}/services`,
|
|
1030
|
+
{ profileName }
|
|
1031
|
+
);
|
|
1032
|
+
} catch {
|
|
1033
|
+
serviceStatuses = {};
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
const rows = Object.entries(SERVICE_DESCRIPTIONS).map(([slug, desc]) => {
|
|
1037
|
+
const enabled = serviceStatuses[slug] ?? null;
|
|
1038
|
+
return [
|
|
1039
|
+
orange(slug),
|
|
1040
|
+
muted(desc),
|
|
1041
|
+
enabled === true ? green("enabled") : enabled === false ? inactive("disabled") : inactive("\u2014")
|
|
1042
|
+
];
|
|
907
1043
|
});
|
|
908
|
-
console.log(
|
|
1044
|
+
console.log(header(version7));
|
|
909
1045
|
console.log(
|
|
910
|
-
|
|
1046
|
+
renderTable({
|
|
1047
|
+
columns: [
|
|
1048
|
+
{ header: "Service", width: 10 },
|
|
1049
|
+
{ header: "Description", width: 42 },
|
|
1050
|
+
{ header: "Status", width: 10 }
|
|
1051
|
+
],
|
|
1052
|
+
rows
|
|
1053
|
+
})
|
|
911
1054
|
);
|
|
912
|
-
console.log("");
|
|
1055
|
+
console.log(footer("Manage services at console.globio.stanlink.online"));
|
|
913
1056
|
}
|
|
914
1057
|
|
|
915
1058
|
// src/commands/functions.ts
|
|
916
|
-
import chalk11 from "chalk";
|
|
917
1059
|
import ora from "ora";
|
|
918
1060
|
import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
919
1061
|
|
|
@@ -929,34 +1071,42 @@ function getClient(profileName) {
|
|
|
929
1071
|
}
|
|
930
1072
|
|
|
931
1073
|
// src/commands/functions.ts
|
|
1074
|
+
var version8 = getCliVersion();
|
|
932
1075
|
function resolveProfileName3(profile) {
|
|
933
1076
|
return profile ?? config.getActiveProfile() ?? "default";
|
|
934
1077
|
}
|
|
935
1078
|
async function functionsList(options = {}) {
|
|
936
1079
|
const profileName = resolveProfileName3(options.profile);
|
|
937
1080
|
const client = getClient(profileName);
|
|
938
|
-
const spinner2 = ora("Fetching functions...").start();
|
|
939
1081
|
const result = await client.code.listFunctions();
|
|
940
|
-
spinner2.stop();
|
|
941
1082
|
if (!result.success || !result.data.length) {
|
|
942
|
-
console.log(
|
|
1083
|
+
console.log(header(version8) + " " + muted("No functions found.") + "\n");
|
|
943
1084
|
return;
|
|
944
1085
|
}
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
1086
|
+
const rows = result.data.map((fn) => [
|
|
1087
|
+
fn.type === "hook" ? gold(fn.slug) : orange(fn.slug),
|
|
1088
|
+
fn.type === "hook" ? gold("hook") : orange("function"),
|
|
1089
|
+
fn.type === "hook" && fn.trigger_event ? gold(fn.trigger_event) : muted("http"),
|
|
1090
|
+
fn.active ? green("active") : inactive("inactive")
|
|
1091
|
+
]);
|
|
1092
|
+
console.log(header(version8));
|
|
1093
|
+
console.log(
|
|
1094
|
+
renderTable({
|
|
1095
|
+
columns: [
|
|
1096
|
+
{ header: "Function", width: 24 },
|
|
1097
|
+
{ header: "Type", width: 10 },
|
|
1098
|
+
{ header: "Trigger", width: 20 },
|
|
1099
|
+
{ header: "Status", width: 10 }
|
|
1100
|
+
],
|
|
1101
|
+
rows
|
|
1102
|
+
})
|
|
1103
|
+
);
|
|
954
1104
|
console.log("");
|
|
955
1105
|
}
|
|
956
1106
|
async function functionsCreate(slug, _options = {}) {
|
|
957
1107
|
const filename = `${slug}.js`;
|
|
958
1108
|
if (existsSync3(filename)) {
|
|
959
|
-
console.log(
|
|
1109
|
+
console.log(inactive(`${filename} already exists.`));
|
|
960
1110
|
return;
|
|
961
1111
|
}
|
|
962
1112
|
const template = `/**
|
|
@@ -975,16 +1125,14 @@ async function handler(input, globio) {
|
|
|
975
1125
|
}
|
|
976
1126
|
`;
|
|
977
1127
|
writeFileSync3(filename, template);
|
|
978
|
-
console.log(
|
|
979
|
-
console.log(
|
|
980
|
-
chalk11.gray(`Deploy with: npx @globio/cli functions deploy ${slug}`)
|
|
981
|
-
);
|
|
1128
|
+
console.log(green(`Created ${filename}`));
|
|
1129
|
+
console.log(muted(`Deploy with: npx @globio/cli functions deploy ${slug}`));
|
|
982
1130
|
}
|
|
983
1131
|
async function functionsDeploy(slug, options) {
|
|
984
1132
|
const filename = options.file ?? `${slug}.js`;
|
|
985
1133
|
if (!existsSync3(filename)) {
|
|
986
1134
|
console.log(
|
|
987
|
-
|
|
1135
|
+
failure(
|
|
988
1136
|
`File not found: ${filename}. Create it with: npx @globio/cli functions create ${slug}`
|
|
989
1137
|
)
|
|
990
1138
|
);
|
|
@@ -1022,7 +1170,7 @@ async function functionsInvoke(slug, options) {
|
|
|
1022
1170
|
try {
|
|
1023
1171
|
input = JSON.parse(options.input);
|
|
1024
1172
|
} catch {
|
|
1025
|
-
console.error(
|
|
1173
|
+
console.error(failure("--input must be valid JSON"));
|
|
1026
1174
|
process.exit(1);
|
|
1027
1175
|
}
|
|
1028
1176
|
}
|
|
@@ -1032,7 +1180,7 @@ async function functionsInvoke(slug, options) {
|
|
|
1032
1180
|
const result = await client.code.invoke(slug, input);
|
|
1033
1181
|
spinner2.stop();
|
|
1034
1182
|
if (!result.success) {
|
|
1035
|
-
console.log(
|
|
1183
|
+
console.log(failure("Invocation failed"));
|
|
1036
1184
|
console.error(result.error.message);
|
|
1037
1185
|
return;
|
|
1038
1186
|
}
|
|
@@ -1046,21 +1194,32 @@ async function functionsLogs(slug, options) {
|
|
|
1046
1194
|
const limit = options.limit ? parseInt(options.limit, 10) : 20;
|
|
1047
1195
|
const profileName = resolveProfileName3(options.profile);
|
|
1048
1196
|
const client = getClient(profileName);
|
|
1049
|
-
const spinner2 = ora("Fetching invocations...").start();
|
|
1050
1197
|
const result = await client.code.getInvocations(slug, limit);
|
|
1051
|
-
spinner2.stop();
|
|
1052
1198
|
if (!result.success || !result.data.length) {
|
|
1053
|
-
console.log(
|
|
1199
|
+
console.log(header(version8) + " " + muted("No invocations yet.") + "\n");
|
|
1054
1200
|
return;
|
|
1055
1201
|
}
|
|
1056
|
-
|
|
1057
|
-
result.data.forEach((inv) => {
|
|
1058
|
-
const status = inv.success ? "\x1B[38;2;244;140;6m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
|
|
1202
|
+
const rows = result.data.map((inv) => {
|
|
1059
1203
|
const date = new Date(inv.invoked_at * 1e3).toISOString().replace("T", " ").slice(0, 19);
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1204
|
+
return [
|
|
1205
|
+
muted(date),
|
|
1206
|
+
muted(inv.trigger_type),
|
|
1207
|
+
muted(inv.duration_ms + "ms"),
|
|
1208
|
+
inv.success ? green("success") : failure("failed")
|
|
1209
|
+
];
|
|
1063
1210
|
});
|
|
1211
|
+
console.log(header(version8));
|
|
1212
|
+
console.log(
|
|
1213
|
+
renderTable({
|
|
1214
|
+
columns: [
|
|
1215
|
+
{ header: "Time", width: 21 },
|
|
1216
|
+
{ header: "Trigger", width: 9 },
|
|
1217
|
+
{ header: "Duration", width: 10 },
|
|
1218
|
+
{ header: "Status", width: 10 }
|
|
1219
|
+
],
|
|
1220
|
+
rows
|
|
1221
|
+
})
|
|
1222
|
+
);
|
|
1064
1223
|
console.log("");
|
|
1065
1224
|
}
|
|
1066
1225
|
async function functionsDelete(slug, options = {}) {
|
|
@@ -1091,10 +1250,10 @@ async function functionsToggle(slug, active, options = {}) {
|
|
|
1091
1250
|
}
|
|
1092
1251
|
|
|
1093
1252
|
// src/index.ts
|
|
1094
|
-
var
|
|
1253
|
+
var version9 = getCliVersion();
|
|
1095
1254
|
var program = new Command();
|
|
1096
|
-
program.name("globio").description("The official Globio CLI").version(
|
|
1097
|
-
printBanner(
|
|
1255
|
+
program.name("globio").description("The official Globio CLI").version(version9).addHelpText("beforeAll", () => {
|
|
1256
|
+
printBanner(version9);
|
|
1098
1257
|
return "";
|
|
1099
1258
|
}).addHelpText(
|
|
1100
1259
|
"after",
|