@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/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(version5) {
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" + version5 + "\x1B[0m"
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
- chalk2.yellow(" \u26A0 ") + chalk2.white(accountEmail) + chalk2.gray(" is already logged in under profile ") + orange(`"${duplicate}"`) + chalk2.gray(".")
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(chalk2.red(error instanceof Error ? error.message : "Could not validate token"));
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(chalk2.red("CLI auth request expired. Try again or use globio login --token."));
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(chalk2.red("Timed out waiting for browser approval. Try again or use globio login --token."));
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(chalk2.red(error instanceof Error ? error.message : "Could not connect to Globio."));
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(chalk3.yellow(`No active session on profile "${profileName || "default"}".`));
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(chalk3.green(`Logged out. Switched to profile: ${remaining[0]}`));
423
+ console.log(green(`Logged out. Switched to profile: ${remaining[0]}`));
361
424
  return;
362
425
  }
363
426
  config.setActiveProfile("");
364
- console.log(chalk3.green("Logged out."));
427
+ console.log(green("Logged out."));
365
428
  return;
366
429
  }
367
- console.log(chalk3.green(`Logged out profile: ${profileName}`));
430
+ console.log(green(`Logged out profile: ${profileName}`));
368
431
  }
369
432
 
370
433
  // src/auth/useProfile.ts
371
- import chalk4 from "chalk";
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
- chalk4.red(
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
- chalk4.green("Switched to profile: ") + orange(profileName) + ` (${profile.account_email})`
447
+ chalk2.green("Switched to profile: ") + orange(profileName) + ` (${profile.account_email})`
385
448
  );
386
449
  }
387
450
 
388
451
  // src/auth/whoami.ts
389
- import chalk5 from "chalk";
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(chalk5.red("Not logged in. Run: globio login"));
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 activeProfile = config.getActiveProfile();
399
- console.log("");
463
+ const active = config.getActiveProfile();
464
+ const otherProfiles = allProfiles.filter((p6) => p6 !== profileName).join(", ") || "\u2014";
400
465
  console.log(
401
- muted("Profile: ") + orange(profileName) + (profileName === activeProfile ? muted(" (active)") : "")
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
- muted("Project: ") + (profile.active_project_id ? orange(profile.active_project_name || "unnamed") + muted(` (${profile.active_project_id})`) : chalk5.gray("none \u2014 run: globio projects use <id>"))
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 chalk6 from "chalk";
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: chalk6.cyan(label) + " [{bar}] {percentage}% | {value}/{total}",
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 version2 = getCliVersion();
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(version2);
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
- chalk7.cyan(
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(chalk7.red("Specify --collection <name> or --all"));
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
- chalk7.green(` \u2713 ${results[collectionId].success} documents migrated`)
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(chalk7.red(` \u2717 ${results[collectionId].failed} failed`));
686
+ console.log(failure(` \u2717 ${results[collectionId].failed} failed`) + "\x1B[0m");
581
687
  console.log(
582
- chalk7.gray(
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(version2);
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(chalk7.cyan(`Found ${files.length} files to migrate`));
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(chalk7.green(` \u2713 ${success} files migrated`));
749
+ console.log(green(` \u2713 ${success} files migrated`));
644
750
  if (failed > 0) {
645
- console.log(chalk7.red(` \u2717 ${failed} failed`));
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
- import chalk8 from "chalk";
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(chalk8.gray("No projects found."));
689
- console.log("");
787
+ console.log(header(version4) + " " + muted("No projects found.") + "\n");
690
788
  return;
691
789
  }
692
- for (const [orgName, orgProjects] of grouped.entries()) {
693
- console.log(chalk8.cyan(`org: ${orgName}`));
694
- for (const project of orgProjects) {
695
- const marker = project.id === activeProjectId ? chalk8.green("\u25CF") : chalk8.gray("\u25CB");
696
- const active = project.id === activeProjectId ? chalk8.green(" (active)") : "";
697
- console.log(` ${marker} ${project.slug.padEnd(22)} ${chalk8.gray(project.id)}${active}`);
698
- }
699
- console.log("");
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(chalk8.red(`Project not found: ${projectId}`));
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(chalk8.red("No organizations found. Create one in the console first."));
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(chalk8.green("Project created successfully."));
788
- console.log(chalk8.cyan("Project: ") + `${result.project.name} (${result.project.id})`);
789
- console.log(chalk8.cyan("Client key: ") + result.keys.client);
790
- console.log(chalk8.cyan("Server key: ") + result.keys.server);
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 version3 = getCliVersion();
905
+ var version5 = getCliVersion();
796
906
  async function init(options = {}) {
797
- printBanner(version3);
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
- import chalk9 from "chalk";
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(chalk9.gray("No profiles found. Run: globio login"));
980
+ console.log(
981
+ header(version6) + " " + muted("No profiles. Run: globio login") + "\n"
982
+ );
871
983
  return;
872
984
  }
873
- console.log("");
874
- for (const name of profiles2) {
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
- const bullet = isActive ? orange("\u25CF") : chalk9.gray("\u25CB");
878
- const label = isActive ? orange(name) : chalk9.white(name);
879
- const email = data?.account_email ? muted(data.account_email) : chalk9.gray("unknown");
880
- const tag = isActive ? muted(" (active)") : "";
881
- console.log(` ${bullet} ${label} ${email}${tag}`);
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
- import chalk10 from "chalk";
888
- var ALL_SERVICES = [
889
- "id",
890
- "doc",
891
- "vault",
892
- "pulse",
893
- "scope",
894
- "sync",
895
- "signal",
896
- "mart",
897
- "brain",
898
- "code"
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
- void options.profile;
902
- void config;
903
- console.log("");
904
- console.log(chalk10.cyan("Available Globio services:"));
905
- ALL_SERVICES.forEach((service) => {
906
- console.log(" " + chalk10.white(service));
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
- chalk10.gray("Manage service access via console.globio.stanlink.online")
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(chalk11.gray("No functions found."));
1083
+ console.log(header(version8) + " " + muted("No functions found.") + "\n");
943
1084
  return;
944
1085
  }
945
- console.log("");
946
- result.data.forEach((fn) => {
947
- const status = fn.active ? "\x1B[32m\u25CF\x1B[0m" : "\x1B[2m\u25CB\x1B[0m";
948
- const type = fn.type === "hook" ? gold("[hook]") : orange("[function]");
949
- console.log(" " + status + " " + type + " " + fn.slug);
950
- if (fn.type === "hook" && fn.trigger_event) {
951
- console.log(muted(" trigger: " + fn.trigger_event));
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(chalk11.yellow(`${filename} already exists.`));
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(chalk11.green(`Created ${filename}`));
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
- chalk11.red(
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(chalk11.red("--input must be valid JSON"));
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(chalk11.red("Invocation failed"));
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(chalk11.gray("No invocations yet."));
1199
+ console.log(header(version8) + " " + muted("No invocations yet.") + "\n");
1054
1200
  return;
1055
1201
  }
1056
- console.log("");
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
- console.log(
1061
- ` ${status} ${chalk11.gray(date)} ${inv.duration_ms}ms ${chalk11.gray(`[${inv.trigger_type}]`)}`
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 version4 = getCliVersion();
1253
+ var version9 = getCliVersion();
1095
1254
  var program = new Command();
1096
- program.name("globio").description("The official Globio CLI").version(version4).addHelpText("beforeAll", () => {
1097
- printBanner(version4);
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",