@globio/cli 0.1.3 → 0.1.4
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 +398 -223
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/auth/login.ts +40 -16
- package/src/auth/logout.ts +26 -4
- package/src/auth/useProfile.ts +20 -0
- package/src/auth/whoami.ts +26 -8
- package/src/commands/functions.ts +28 -13
- package/src/commands/init.ts +21 -7
- package/src/commands/migrate.ts +9 -2
- package/src/commands/projects.ts +41 -14
- package/src/commands/services.ts +4 -1
- package/src/index.ts +44 -13
- package/src/lib/config.ts +119 -71
- package/src/lib/manage.ts +4 -3
- package/src/lib/sdk.ts +6 -2
package/dist/index.js
CHANGED
|
@@ -10,97 +10,134 @@ import chalk2 from "chalk";
|
|
|
10
10
|
|
|
11
11
|
// src/lib/config.ts
|
|
12
12
|
import chalk from "chalk";
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "fs";
|
|
14
|
+
import os from "os";
|
|
15
|
+
import path from "path";
|
|
16
|
+
var baseDir = path.join(os.homedir(), ".globio");
|
|
17
|
+
var profilesDir = path.join(baseDir, "profiles");
|
|
18
|
+
var configPath = path.join(baseDir, "config.json");
|
|
19
|
+
function ensureBaseDir() {
|
|
20
|
+
mkdirSync(baseDir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
function ensureProfilesDir() {
|
|
23
|
+
ensureBaseDir();
|
|
24
|
+
mkdirSync(profilesDir, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
function readGlobalConfig() {
|
|
27
|
+
if (!existsSync(configPath)) {
|
|
28
|
+
return { active_profile: "default" };
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
32
|
+
return {
|
|
33
|
+
active_profile: raw.active_profile ?? "default"
|
|
34
|
+
};
|
|
35
|
+
} catch {
|
|
36
|
+
return { active_profile: "default" };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function writeGlobalConfig(data) {
|
|
40
|
+
ensureBaseDir();
|
|
41
|
+
writeFileSync(configPath, JSON.stringify(data, null, 2) + "\n");
|
|
42
|
+
}
|
|
43
|
+
function profilePath(name) {
|
|
44
|
+
return path.join(profilesDir, `${name}.json`);
|
|
45
|
+
}
|
|
46
|
+
function readProfile(name) {
|
|
47
|
+
const file = profilePath(name);
|
|
48
|
+
if (!existsSync(file)) return null;
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(readFileSync(file, "utf-8"));
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function writeProfile(name, data) {
|
|
56
|
+
ensureProfilesDir();
|
|
57
|
+
writeFileSync(profilePath(name), JSON.stringify(data, null, 2) + "\n");
|
|
58
|
+
}
|
|
18
59
|
var config = {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
});
|
|
60
|
+
getBaseDir: () => baseDir,
|
|
61
|
+
getProfilesDir: () => profilesDir,
|
|
62
|
+
getActiveProfile: () => readGlobalConfig().active_profile,
|
|
63
|
+
setActiveProfile: (name) => {
|
|
64
|
+
writeGlobalConfig({ active_profile: name });
|
|
26
65
|
},
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (!pat) {
|
|
32
|
-
console.error(chalk.red("Not logged in. Run: npx @globio/cli login"));
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
return pat;
|
|
66
|
+
getProfile: (name) => {
|
|
67
|
+
const profileName = name ?? config.getActiveProfile();
|
|
68
|
+
if (!profileName) return null;
|
|
69
|
+
return readProfile(profileName);
|
|
36
70
|
},
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
71
|
+
setProfile: (name, data) => {
|
|
72
|
+
const existing = readProfile(name);
|
|
73
|
+
const next = {
|
|
74
|
+
pat: data.pat ?? existing?.pat ?? "",
|
|
75
|
+
account_email: data.account_email ?? existing?.account_email ?? "",
|
|
76
|
+
account_name: data.account_name ?? existing?.account_name ?? "",
|
|
77
|
+
active_project_id: data.active_project_id ?? existing?.active_project_id,
|
|
78
|
+
active_project_name: data.active_project_name ?? existing?.active_project_name,
|
|
79
|
+
project_api_key: data.project_api_key ?? existing?.project_api_key,
|
|
80
|
+
created_at: data.created_at ?? existing?.created_at ?? Date.now()
|
|
81
|
+
};
|
|
82
|
+
writeProfile(name, next);
|
|
46
83
|
},
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (projectName) {
|
|
52
|
-
projectNames[projectId] = projectName;
|
|
53
|
-
}
|
|
54
|
-
store.set("projectApiKeys", projectApiKeys);
|
|
55
|
-
store.set("projectNames", projectNames);
|
|
56
|
-
store.set("projectId", projectId);
|
|
57
|
-
if (projectName) {
|
|
58
|
-
store.set("projectName", projectName);
|
|
84
|
+
deleteProfile: (name) => {
|
|
85
|
+
const file = profilePath(name);
|
|
86
|
+
if (existsSync(file)) {
|
|
87
|
+
rmSync(file);
|
|
59
88
|
}
|
|
60
89
|
},
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return
|
|
90
|
+
listProfiles: () => {
|
|
91
|
+
if (!existsSync(profilesDir)) return [];
|
|
92
|
+
return readdirSync(profilesDir).filter((file) => file.endsWith(".json")).map((file) => file.replace(/\.json$/, "")).sort();
|
|
64
93
|
},
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
if (!
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
94
|
+
getActiveProfileData: () => {
|
|
95
|
+
const active = config.getActiveProfile();
|
|
96
|
+
if (!active) return null;
|
|
97
|
+
return config.getProfile(active);
|
|
98
|
+
},
|
|
99
|
+
requireAuth: (profileName) => {
|
|
100
|
+
const resolvedProfile = profileName ?? config.getActiveProfile() ?? "default";
|
|
101
|
+
const profile = config.getProfile(resolvedProfile);
|
|
102
|
+
if (!profile?.pat) {
|
|
103
|
+
console.error(chalk.red("Not logged in. Run: npx @globio/cli login"));
|
|
71
104
|
process.exit(1);
|
|
72
105
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
106
|
+
return { pat: profile.pat, profileName: resolvedProfile };
|
|
107
|
+
},
|
|
108
|
+
requireProject: (profileName) => {
|
|
109
|
+
const resolvedProfile = profileName ?? config.getActiveProfile() ?? "default";
|
|
110
|
+
const profile = config.getProfile(resolvedProfile);
|
|
111
|
+
if (!profile?.active_project_id) {
|
|
76
112
|
console.error(
|
|
77
|
-
chalk.red(
|
|
78
|
-
"No project API key stored for the active project. Run: npx @globio/cli projects use <projectId>"
|
|
79
|
-
)
|
|
113
|
+
chalk.red("No active project. Run: npx @globio/cli projects use <projectId>")
|
|
80
114
|
);
|
|
81
115
|
process.exit(1);
|
|
82
116
|
}
|
|
83
|
-
return
|
|
117
|
+
return {
|
|
118
|
+
projectId: profile.active_project_id,
|
|
119
|
+
projectName: profile.active_project_name ?? "unnamed"
|
|
120
|
+
};
|
|
84
121
|
}
|
|
85
122
|
};
|
|
86
123
|
|
|
87
124
|
// src/lib/manage.ts
|
|
88
125
|
var API_BASE_URL = "https://api.globio.stanlink.online";
|
|
89
126
|
var CONSOLE_BASE_URL = "https://console.globio.stanlink.online";
|
|
90
|
-
function getAuthToken(explicitToken) {
|
|
127
|
+
function getAuthToken(explicitToken, profileName) {
|
|
91
128
|
if (explicitToken) return explicitToken;
|
|
92
|
-
return config.
|
|
129
|
+
return config.getProfile(profileName)?.pat;
|
|
93
130
|
}
|
|
94
|
-
async function manageRequest(
|
|
131
|
+
async function manageRequest(path2, options = {}) {
|
|
95
132
|
const headers = new Headers();
|
|
96
133
|
if (!(options.body instanceof FormData)) {
|
|
97
134
|
headers.set("Content-Type", "application/json");
|
|
98
135
|
}
|
|
99
|
-
const token = getAuthToken(options.token);
|
|
136
|
+
const token = getAuthToken(options.token, options.profileName);
|
|
100
137
|
if (token) {
|
|
101
138
|
headers.set("Authorization", `Bearer ${token}`);
|
|
102
139
|
}
|
|
103
|
-
const response = await fetch(`${API_BASE_URL}/manage${
|
|
140
|
+
const response = await fetch(`${API_BASE_URL}/manage${path2}`, {
|
|
104
141
|
method: options.method ?? "GET",
|
|
105
142
|
headers,
|
|
106
143
|
body: options.body ? JSON.stringify(options.body) : void 0
|
|
@@ -116,7 +153,7 @@ function getConsoleCliAuthUrl(state) {
|
|
|
116
153
|
}
|
|
117
154
|
|
|
118
155
|
// src/lib/banner.ts
|
|
119
|
-
import { readFileSync } from "fs";
|
|
156
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
120
157
|
import figlet from "figlet";
|
|
121
158
|
import gradientString from "gradient-string";
|
|
122
159
|
var globioGradient = gradientString(
|
|
@@ -144,7 +181,7 @@ var orange = (s) => "\x1B[38;2;244;140;6m" + s + "\x1B[0m";
|
|
|
144
181
|
var gold = (s) => "\x1B[38;2;255;208;0m" + s + "\x1B[0m";
|
|
145
182
|
var muted = (s) => "\x1B[2m" + s + "\x1B[0m";
|
|
146
183
|
function getCliVersion() {
|
|
147
|
-
const file =
|
|
184
|
+
const file = readFileSync2(new URL("../package.json", import.meta.url), "utf8");
|
|
148
185
|
return JSON.parse(file).version;
|
|
149
186
|
}
|
|
150
187
|
|
|
@@ -159,14 +196,10 @@ function sleep(ms) {
|
|
|
159
196
|
}
|
|
160
197
|
async function savePat(token) {
|
|
161
198
|
const account = await manageRequest("/account", { token });
|
|
162
|
-
config.set({
|
|
163
|
-
pat: token,
|
|
164
|
-
accountEmail: account.email,
|
|
165
|
-
accountName: account.display_name ?? account.email
|
|
166
|
-
});
|
|
167
199
|
return account;
|
|
168
200
|
}
|
|
169
|
-
async function runTokenLogin() {
|
|
201
|
+
async function runTokenLogin(profileName) {
|
|
202
|
+
const hadProfiles = config.listProfiles().length > 0;
|
|
170
203
|
const token = await p.text({
|
|
171
204
|
message: "Paste your personal access token",
|
|
172
205
|
placeholder: "glo_pat_...",
|
|
@@ -184,17 +217,28 @@ async function runTokenLogin() {
|
|
|
184
217
|
spinner2.start("Validating personal access token...");
|
|
185
218
|
try {
|
|
186
219
|
const account = await savePat(token);
|
|
220
|
+
config.setProfile(profileName, {
|
|
221
|
+
pat: token,
|
|
222
|
+
account_email: account.email,
|
|
223
|
+
account_name: account.display_name ?? account.email,
|
|
224
|
+
created_at: Date.now()
|
|
225
|
+
});
|
|
226
|
+
if (profileName === "default" || !hadProfiles) {
|
|
227
|
+
config.setActiveProfile(profileName);
|
|
228
|
+
}
|
|
187
229
|
spinner2.stop("Token validated.");
|
|
188
|
-
p.outro(`Logged in as ${account.email}
|
|
230
|
+
p.outro(`Logged in as ${account.email}
|
|
231
|
+
Profile: ${profileName}`);
|
|
189
232
|
} catch (error) {
|
|
190
233
|
spinner2.stop("Validation failed.");
|
|
191
234
|
p.outro(chalk2.red(error instanceof Error ? error.message : "Could not validate token"));
|
|
192
235
|
process.exit(1);
|
|
193
236
|
}
|
|
194
237
|
}
|
|
195
|
-
async function runBrowserLogin() {
|
|
238
|
+
async function runBrowserLogin(profileName) {
|
|
196
239
|
const state = crypto.randomUUID();
|
|
197
240
|
const spinner2 = p.spinner();
|
|
241
|
+
const hadProfiles = config.listProfiles().length > 0;
|
|
198
242
|
await manageRequest("/cli-auth/request", {
|
|
199
243
|
method: "POST",
|
|
200
244
|
body: { state }
|
|
@@ -220,13 +264,18 @@ async function runBrowserLogin() {
|
|
|
220
264
|
method: "POST",
|
|
221
265
|
body: { code: status.code }
|
|
222
266
|
});
|
|
223
|
-
config.
|
|
267
|
+
config.setProfile(profileName, {
|
|
224
268
|
pat: exchange.token,
|
|
225
|
-
|
|
226
|
-
|
|
269
|
+
account_email: exchange.account.email,
|
|
270
|
+
account_name: exchange.account.display_name ?? exchange.account.email,
|
|
271
|
+
created_at: Date.now()
|
|
227
272
|
});
|
|
273
|
+
if (profileName === "default" || !hadProfiles) {
|
|
274
|
+
config.setActiveProfile(profileName);
|
|
275
|
+
}
|
|
228
276
|
spinner2.stop("Browser approval received.");
|
|
229
|
-
p.outro(`Logged in as ${exchange.account.email}
|
|
277
|
+
p.outro(`Logged in as ${exchange.account.email}
|
|
278
|
+
Profile: ${profileName}`);
|
|
230
279
|
return;
|
|
231
280
|
}
|
|
232
281
|
} catch {
|
|
@@ -239,8 +288,20 @@ async function runBrowserLogin() {
|
|
|
239
288
|
}
|
|
240
289
|
async function login(options = {}) {
|
|
241
290
|
printBanner(version);
|
|
291
|
+
const profileName = options.profile ?? "default";
|
|
292
|
+
const existing = config.getProfile(profileName);
|
|
293
|
+
if (existing) {
|
|
294
|
+
const proceed = await p.confirm({
|
|
295
|
+
message: `Already logged in as ${existing.account_email} on profile "${profileName}". Replace?`,
|
|
296
|
+
initialValue: false
|
|
297
|
+
});
|
|
298
|
+
if (p.isCancel(proceed) || !proceed) {
|
|
299
|
+
p.outro("Login cancelled.");
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
242
303
|
if (options.token) {
|
|
243
|
-
await runTokenLogin();
|
|
304
|
+
await runTokenLogin(profileName);
|
|
244
305
|
return;
|
|
245
306
|
}
|
|
246
307
|
const choice = await p.select({
|
|
@@ -255,11 +316,11 @@ async function login(options = {}) {
|
|
|
255
316
|
process.exit(0);
|
|
256
317
|
}
|
|
257
318
|
if (choice === "token") {
|
|
258
|
-
await runTokenLogin();
|
|
319
|
+
await runTokenLogin(profileName);
|
|
259
320
|
return;
|
|
260
321
|
}
|
|
261
322
|
try {
|
|
262
|
-
await runBrowserLogin();
|
|
323
|
+
await runBrowserLogin(profileName);
|
|
263
324
|
} catch (error) {
|
|
264
325
|
p.outro(chalk2.red(error instanceof Error ? error.message : "Could not connect to Globio."));
|
|
265
326
|
process.exit(1);
|
|
@@ -267,51 +328,98 @@ async function login(options = {}) {
|
|
|
267
328
|
}
|
|
268
329
|
|
|
269
330
|
// src/auth/logout.ts
|
|
270
|
-
import * as p2 from "@clack/prompts";
|
|
271
331
|
import chalk3 from "chalk";
|
|
272
|
-
async function logout() {
|
|
273
|
-
config.
|
|
274
|
-
|
|
332
|
+
async function logout(options = {}) {
|
|
333
|
+
const activeProfile = config.getActiveProfile();
|
|
334
|
+
const profileName = options.profile ?? activeProfile;
|
|
335
|
+
const profile = profileName ? config.getProfile(profileName) : null;
|
|
336
|
+
if (!profileName || !profile) {
|
|
337
|
+
console.log(chalk3.yellow(`No active session on profile "${profileName || "default"}".`));
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
config.deleteProfile(profileName);
|
|
341
|
+
if (profileName === activeProfile) {
|
|
342
|
+
const remaining = config.listProfiles();
|
|
343
|
+
if (remaining.length > 0) {
|
|
344
|
+
config.setActiveProfile(remaining[0]);
|
|
345
|
+
console.log(chalk3.green(`Logged out. Switched to profile: ${remaining[0]}`));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
config.setActiveProfile("");
|
|
349
|
+
console.log(chalk3.green("Logged out."));
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
console.log(chalk3.green(`Logged out profile: ${profileName}`));
|
|
275
353
|
}
|
|
276
354
|
|
|
277
|
-
// src/auth/
|
|
355
|
+
// src/auth/useProfile.ts
|
|
278
356
|
import chalk4 from "chalk";
|
|
279
|
-
async function
|
|
280
|
-
const
|
|
281
|
-
if (!
|
|
282
|
-
console.log(
|
|
357
|
+
async function useProfile(profileName) {
|
|
358
|
+
const profile = config.getProfile(profileName);
|
|
359
|
+
if (!profile) {
|
|
360
|
+
console.log(
|
|
361
|
+
chalk4.red(
|
|
362
|
+
`Profile "${profileName}" not found. Run: globio login --profile ${profileName}`
|
|
363
|
+
)
|
|
364
|
+
);
|
|
365
|
+
process.exit(1);
|
|
366
|
+
}
|
|
367
|
+
config.setActiveProfile(profileName);
|
|
368
|
+
console.log(
|
|
369
|
+
chalk4.green("Switched to profile: ") + orange(profileName) + ` (${profile.account_email})`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// src/auth/whoami.ts
|
|
374
|
+
import chalk5 from "chalk";
|
|
375
|
+
async function whoami(options = {}) {
|
|
376
|
+
const profileName = options.profile ?? config.getActiveProfile() ?? "default";
|
|
377
|
+
const profile = config.getProfile(profileName);
|
|
378
|
+
if (!profile) {
|
|
379
|
+
console.log(chalk5.red("Not logged in. Run: globio login"));
|
|
283
380
|
return;
|
|
284
381
|
}
|
|
382
|
+
const allProfiles = config.listProfiles();
|
|
383
|
+
const activeProfile = config.getActiveProfile();
|
|
285
384
|
console.log("");
|
|
286
|
-
console.log(chalk4.cyan("Account: ") + (cfg.accountEmail ?? "unknown"));
|
|
287
|
-
console.log(chalk4.cyan("Name: ") + (cfg.accountName ?? "unknown"));
|
|
288
385
|
console.log(
|
|
289
|
-
|
|
386
|
+
muted("Profile: ") + orange(profileName) + (profileName === activeProfile ? muted(" (active)") : "")
|
|
387
|
+
);
|
|
388
|
+
console.log(muted("Account: ") + profile.account_email);
|
|
389
|
+
console.log(muted("Name: ") + (profile.account_name || "\u2014"));
|
|
390
|
+
console.log(
|
|
391
|
+
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>"))
|
|
290
392
|
);
|
|
393
|
+
if (allProfiles.length > 1) {
|
|
394
|
+
console.log("");
|
|
395
|
+
console.log(
|
|
396
|
+
muted("Other profiles: ") + allProfiles.filter((name) => name !== profileName).join(", ")
|
|
397
|
+
);
|
|
398
|
+
}
|
|
291
399
|
console.log("");
|
|
292
400
|
}
|
|
293
401
|
|
|
294
402
|
// src/commands/init.ts
|
|
295
|
-
import * as
|
|
296
|
-
import { existsSync, readFileSync as
|
|
403
|
+
import * as p5 from "@clack/prompts";
|
|
404
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
297
405
|
|
|
298
406
|
// src/prompts/init.ts
|
|
299
|
-
import * as
|
|
407
|
+
import * as p2 from "@clack/prompts";
|
|
300
408
|
async function promptInit() {
|
|
301
|
-
return
|
|
409
|
+
return p2.group(
|
|
302
410
|
{
|
|
303
|
-
migrateFromFirebase: () =>
|
|
411
|
+
migrateFromFirebase: () => p2.confirm({
|
|
304
412
|
message: "Migrating from Firebase?",
|
|
305
413
|
initialValue: false
|
|
306
414
|
}),
|
|
307
|
-
serviceAccountPath: ({ results }) => results.migrateFromFirebase ?
|
|
415
|
+
serviceAccountPath: ({ results }) => results.migrateFromFirebase ? p2.text({
|
|
308
416
|
message: "Path to Firebase service account JSON",
|
|
309
417
|
placeholder: "./serviceAccountKey.json"
|
|
310
418
|
}) : Promise.resolve(void 0)
|
|
311
419
|
},
|
|
312
420
|
{
|
|
313
421
|
onCancel: () => {
|
|
314
|
-
|
|
422
|
+
p2.cancel("Cancelled.");
|
|
315
423
|
process.exit(0);
|
|
316
424
|
}
|
|
317
425
|
}
|
|
@@ -319,15 +427,15 @@ async function promptInit() {
|
|
|
319
427
|
}
|
|
320
428
|
|
|
321
429
|
// src/commands/migrate.ts
|
|
322
|
-
import * as
|
|
323
|
-
import
|
|
430
|
+
import * as p3 from "@clack/prompts";
|
|
431
|
+
import chalk7 from "chalk";
|
|
324
432
|
import { basename } from "path";
|
|
325
433
|
|
|
326
434
|
// src/lib/firebase.ts
|
|
327
435
|
async function initFirebase(serviceAccountPath) {
|
|
328
436
|
const admin = await import("firebase-admin");
|
|
329
|
-
const { readFileSync:
|
|
330
|
-
const serviceAccount = JSON.parse(
|
|
437
|
+
const { readFileSync: readFileSync5 } = await import("fs");
|
|
438
|
+
const serviceAccount = JSON.parse(readFileSync5(serviceAccountPath, "utf-8"));
|
|
331
439
|
if (!admin.default.apps.length) {
|
|
332
440
|
admin.default.initializeApp({
|
|
333
441
|
credential: admin.default.credential.cert(serviceAccount),
|
|
@@ -342,12 +450,12 @@ async function initFirebase(serviceAccountPath) {
|
|
|
342
450
|
}
|
|
343
451
|
|
|
344
452
|
// src/lib/progress.ts
|
|
345
|
-
import
|
|
453
|
+
import chalk6 from "chalk";
|
|
346
454
|
import cliProgress from "cli-progress";
|
|
347
455
|
function createProgressBar(label) {
|
|
348
456
|
const bar = new cliProgress.SingleBar(
|
|
349
457
|
{
|
|
350
|
-
format:
|
|
458
|
+
format: chalk6.cyan(label) + " [{bar}] {percentage}% | {value}/{total}",
|
|
351
459
|
barCompleteChar: "\u2588",
|
|
352
460
|
barIncompleteChar: "\u2591",
|
|
353
461
|
hideCursor: true
|
|
@@ -359,31 +467,38 @@ function createProgressBar(label) {
|
|
|
359
467
|
|
|
360
468
|
// src/lib/sdk.ts
|
|
361
469
|
import { Globio } from "@globio/sdk";
|
|
362
|
-
function getClient() {
|
|
363
|
-
const
|
|
470
|
+
function getClient(profileName) {
|
|
471
|
+
const { pat } = config.requireAuth(profileName);
|
|
472
|
+
const { projectId } = config.requireProject(profileName);
|
|
473
|
+
const profile = config.getProfile(profileName);
|
|
474
|
+
const apiKey = profile?.project_api_key ?? pat;
|
|
475
|
+
void projectId;
|
|
364
476
|
return new Globio({ apiKey });
|
|
365
477
|
}
|
|
366
478
|
|
|
367
479
|
// src/commands/migrate.ts
|
|
368
480
|
var version2 = getCliVersion();
|
|
481
|
+
function resolveProfileName(profile) {
|
|
482
|
+
return profile ?? config.getActiveProfile() ?? "default";
|
|
483
|
+
}
|
|
369
484
|
async function migrateFirestore(options) {
|
|
370
485
|
printBanner(version2);
|
|
371
|
-
|
|
486
|
+
p3.intro(gold("\u21D2\u21D2") + " Firebase \u2192 Globio Migration");
|
|
372
487
|
const { firestore } = await initFirebase(options.from);
|
|
373
|
-
const client = getClient();
|
|
488
|
+
const client = getClient(resolveProfileName(options.profile));
|
|
374
489
|
let collections = [];
|
|
375
490
|
if (options.all) {
|
|
376
491
|
const snapshot = await firestore.listCollections();
|
|
377
492
|
collections = snapshot.map((collection) => collection.id);
|
|
378
493
|
console.log(
|
|
379
|
-
|
|
494
|
+
chalk7.cyan(
|
|
380
495
|
`Found ${collections.length} collections: ${collections.join(", ")}`
|
|
381
496
|
)
|
|
382
497
|
);
|
|
383
498
|
} else if (options.collection) {
|
|
384
499
|
collections = [options.collection];
|
|
385
500
|
} else {
|
|
386
|
-
console.log(
|
|
501
|
+
console.log(chalk7.red("Specify --collection <name> or --all"));
|
|
387
502
|
process.exit(1);
|
|
388
503
|
}
|
|
389
504
|
const results = {};
|
|
@@ -428,32 +543,32 @@ async function migrateFirestore(options) {
|
|
|
428
543
|
}
|
|
429
544
|
bar.stop();
|
|
430
545
|
console.log(
|
|
431
|
-
|
|
546
|
+
chalk7.green(` \u2713 ${results[collectionId].success} documents migrated`)
|
|
432
547
|
);
|
|
433
548
|
if (results[collectionId].failed > 0) {
|
|
434
|
-
console.log(
|
|
549
|
+
console.log(chalk7.red(` \u2717 ${results[collectionId].failed} failed`));
|
|
435
550
|
console.log(
|
|
436
|
-
|
|
551
|
+
chalk7.gray(
|
|
437
552
|
" Failed IDs: " + results[collectionId].failedIds.slice(0, 10).join(", ") + (results[collectionId].failedIds.length > 10 ? "..." : "")
|
|
438
553
|
)
|
|
439
554
|
);
|
|
440
555
|
}
|
|
441
556
|
}
|
|
442
557
|
console.log("");
|
|
443
|
-
|
|
558
|
+
p3.outro(
|
|
444
559
|
orange("\u2713") + " Migration complete.\n\n " + muted("Your Firebase data is intact.") + "\n " + muted("Delete it manually when ready.")
|
|
445
560
|
);
|
|
446
561
|
}
|
|
447
562
|
async function migrateFirebaseStorage(options) {
|
|
448
563
|
printBanner(version2);
|
|
449
|
-
|
|
564
|
+
p3.intro(gold("\u21D2\u21D2") + " Firebase \u2192 Globio Migration");
|
|
450
565
|
const { storage } = await initFirebase(options.from);
|
|
451
|
-
const client = getClient();
|
|
566
|
+
const client = getClient(resolveProfileName(options.profile));
|
|
452
567
|
const bucketName = options.bucket.replace(/^gs:\/\//, "");
|
|
453
568
|
const bucket = storage.bucket(bucketName);
|
|
454
569
|
const prefix = options.folder ? options.folder.replace(/^\//, "") : "";
|
|
455
570
|
const [files] = await bucket.getFiles(prefix ? { prefix } : {});
|
|
456
|
-
console.log(
|
|
571
|
+
console.log(chalk7.cyan(`Found ${files.length} files to migrate`));
|
|
457
572
|
const bar = createProgressBar("Storage");
|
|
458
573
|
bar.start(files.length, 0);
|
|
459
574
|
let success = 0;
|
|
@@ -481,39 +596,43 @@ async function migrateFirebaseStorage(options) {
|
|
|
481
596
|
}
|
|
482
597
|
bar.stop();
|
|
483
598
|
console.log("");
|
|
484
|
-
console.log(
|
|
599
|
+
console.log(chalk7.green(` \u2713 ${success} files migrated`));
|
|
485
600
|
if (failed > 0) {
|
|
486
|
-
console.log(
|
|
601
|
+
console.log(chalk7.red(` \u2717 ${failed} failed`));
|
|
487
602
|
}
|
|
488
|
-
|
|
603
|
+
p3.outro(
|
|
489
604
|
orange("\u2713") + " Migration complete.\n\n " + muted("Your Firebase data is intact.") + "\n " + muted("Delete it manually when ready.")
|
|
490
605
|
);
|
|
491
606
|
}
|
|
492
607
|
|
|
493
608
|
// src/commands/projects.ts
|
|
494
|
-
import * as
|
|
495
|
-
import
|
|
609
|
+
import * as p4 from "@clack/prompts";
|
|
610
|
+
import chalk8 from "chalk";
|
|
496
611
|
function slugify(value) {
|
|
497
612
|
return value.toLowerCase().trim().replace(/[^a-z0-9\\s-]/g, "").replace(/\\s+/g, "-").replace(/-+/g, "-");
|
|
498
613
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
614
|
+
function resolveProfileName2(profileName) {
|
|
615
|
+
return profileName ?? config.getActiveProfile() ?? "default";
|
|
616
|
+
}
|
|
617
|
+
async function createServerKey(projectId, profileName) {
|
|
502
618
|
const created = await manageRequest(`/projects/${projectId}/keys`, {
|
|
503
619
|
method: "POST",
|
|
504
620
|
body: {
|
|
505
621
|
name: "CLI server key",
|
|
506
622
|
scope: "server"
|
|
507
|
-
}
|
|
623
|
+
},
|
|
624
|
+
profileName
|
|
508
625
|
});
|
|
509
626
|
if (!created.token) {
|
|
510
627
|
throw new Error("Management API did not return a project API key");
|
|
511
628
|
}
|
|
512
629
|
return created.token;
|
|
513
630
|
}
|
|
514
|
-
async function projectsList() {
|
|
515
|
-
const
|
|
516
|
-
|
|
631
|
+
async function projectsList(options = {}) {
|
|
632
|
+
const profileName = resolveProfileName2(options.profile);
|
|
633
|
+
config.requireAuth(profileName);
|
|
634
|
+
const projects2 = await manageRequest("/projects", { profileName });
|
|
635
|
+
const activeProjectId = config.getProfile(profileName)?.active_project_id;
|
|
517
636
|
const grouped = /* @__PURE__ */ new Map();
|
|
518
637
|
for (const project of projects2) {
|
|
519
638
|
const list = grouped.get(project.org_name) ?? [];
|
|
@@ -522,38 +641,50 @@ async function projectsList() {
|
|
|
522
641
|
}
|
|
523
642
|
console.log("");
|
|
524
643
|
if (!projects2.length) {
|
|
525
|
-
console.log(
|
|
644
|
+
console.log(chalk8.gray("No projects found."));
|
|
526
645
|
console.log("");
|
|
527
646
|
return;
|
|
528
647
|
}
|
|
529
648
|
for (const [orgName, orgProjects] of grouped.entries()) {
|
|
530
|
-
console.log(
|
|
649
|
+
console.log(chalk8.cyan(`org: ${orgName}`));
|
|
531
650
|
for (const project of orgProjects) {
|
|
532
|
-
const marker = project.id === activeProjectId ?
|
|
533
|
-
const active = project.id === activeProjectId ?
|
|
534
|
-
console.log(` ${marker} ${project.slug.padEnd(22)} ${
|
|
651
|
+
const marker = project.id === activeProjectId ? chalk8.green("\u25CF") : chalk8.gray("\u25CB");
|
|
652
|
+
const active = project.id === activeProjectId ? chalk8.green(" (active)") : "";
|
|
653
|
+
console.log(` ${marker} ${project.slug.padEnd(22)} ${chalk8.gray(project.id)}${active}`);
|
|
535
654
|
}
|
|
536
655
|
console.log("");
|
|
537
656
|
}
|
|
538
657
|
}
|
|
539
|
-
async function projectsUse(projectId) {
|
|
540
|
-
const
|
|
658
|
+
async function projectsUse(projectId, options = {}) {
|
|
659
|
+
const profileName = resolveProfileName2(options.profile);
|
|
660
|
+
config.requireAuth(profileName);
|
|
661
|
+
const projects2 = await manageRequest("/projects", { profileName });
|
|
541
662
|
const project = projects2.find((item) => item.id === projectId);
|
|
542
663
|
if (!project) {
|
|
543
|
-
console.log(
|
|
664
|
+
console.log(chalk8.red(`Project not found: ${projectId}`));
|
|
544
665
|
process.exit(1);
|
|
545
666
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
667
|
+
await manageRequest(`/projects/${projectId}/keys`, { profileName });
|
|
668
|
+
const apiKey = await createServerKey(projectId, profileName);
|
|
669
|
+
config.setProfile(profileName, {
|
|
670
|
+
active_project_id: project.id,
|
|
671
|
+
active_project_name: project.name,
|
|
672
|
+
project_api_key: apiKey
|
|
673
|
+
});
|
|
674
|
+
config.setActiveProfile(profileName);
|
|
675
|
+
console.log(
|
|
676
|
+
chalk8.green("Active project set to: ") + chalk8.cyan(`${project.name} (${project.id})`)
|
|
677
|
+
);
|
|
549
678
|
}
|
|
550
|
-
async function projectsCreate() {
|
|
551
|
-
const
|
|
679
|
+
async function projectsCreate(options = {}) {
|
|
680
|
+
const profileName = resolveProfileName2(options.profile);
|
|
681
|
+
config.requireAuth(profileName);
|
|
682
|
+
const orgs = await manageRequest("/orgs", { profileName });
|
|
552
683
|
if (!orgs.length) {
|
|
553
|
-
console.log(
|
|
684
|
+
console.log(chalk8.red("No organizations found. Create one in the console first."));
|
|
554
685
|
process.exit(1);
|
|
555
686
|
}
|
|
556
|
-
const orgId = await
|
|
687
|
+
const orgId = await p4.select({
|
|
557
688
|
message: "Select an organization",
|
|
558
689
|
options: orgs.map((org) => ({
|
|
559
690
|
value: org.id,
|
|
@@ -561,22 +692,22 @@ async function projectsCreate() {
|
|
|
561
692
|
hint: org.role
|
|
562
693
|
}))
|
|
563
694
|
});
|
|
564
|
-
if (
|
|
565
|
-
|
|
695
|
+
if (p4.isCancel(orgId)) {
|
|
696
|
+
p4.cancel("Project creation cancelled.");
|
|
566
697
|
process.exit(0);
|
|
567
698
|
}
|
|
568
|
-
const values = await
|
|
699
|
+
const values = await p4.group(
|
|
569
700
|
{
|
|
570
|
-
name: () =>
|
|
701
|
+
name: () => p4.text({
|
|
571
702
|
message: "Project name",
|
|
572
703
|
validate: (value) => !value ? "Project name is required" : void 0
|
|
573
704
|
}),
|
|
574
|
-
slug: ({ results }) =>
|
|
705
|
+
slug: ({ results }) => p4.text({
|
|
575
706
|
message: "Project slug",
|
|
576
707
|
initialValue: slugify(String(results.name ?? "")),
|
|
577
708
|
validate: (value) => !value ? "Project slug is required" : void 0
|
|
578
709
|
}),
|
|
579
|
-
environment: () =>
|
|
710
|
+
environment: () => p4.select({
|
|
580
711
|
message: "Environment",
|
|
581
712
|
options: [
|
|
582
713
|
{ value: "development", label: "development" },
|
|
@@ -587,7 +718,7 @@ async function projectsCreate() {
|
|
|
587
718
|
},
|
|
588
719
|
{
|
|
589
720
|
onCancel: () => {
|
|
590
|
-
|
|
721
|
+
p4.cancel("Project creation cancelled.");
|
|
591
722
|
process.exit(0);
|
|
592
723
|
}
|
|
593
724
|
}
|
|
@@ -599,33 +730,49 @@ async function projectsCreate() {
|
|
|
599
730
|
name: values.name,
|
|
600
731
|
slug: values.slug,
|
|
601
732
|
environment: values.environment
|
|
602
|
-
}
|
|
733
|
+
},
|
|
734
|
+
profileName
|
|
735
|
+
});
|
|
736
|
+
config.setProfile(profileName, {
|
|
737
|
+
active_project_id: result.project.id,
|
|
738
|
+
active_project_name: result.project.name,
|
|
739
|
+
project_api_key: result.keys.server
|
|
603
740
|
});
|
|
604
|
-
config.
|
|
741
|
+
config.setActiveProfile(profileName);
|
|
605
742
|
console.log("");
|
|
606
|
-
console.log(
|
|
607
|
-
console.log(
|
|
608
|
-
console.log(
|
|
609
|
-
console.log(
|
|
743
|
+
console.log(chalk8.green("Project created successfully."));
|
|
744
|
+
console.log(chalk8.cyan("Project: ") + `${result.project.name} (${result.project.id})`);
|
|
745
|
+
console.log(chalk8.cyan("Client key: ") + result.keys.client);
|
|
746
|
+
console.log(chalk8.cyan("Server key: ") + result.keys.server);
|
|
610
747
|
console.log("");
|
|
611
748
|
}
|
|
612
749
|
|
|
613
750
|
// src/commands/init.ts
|
|
614
751
|
var version3 = getCliVersion();
|
|
615
|
-
async function init() {
|
|
752
|
+
async function init(options = {}) {
|
|
616
753
|
printBanner(version3);
|
|
617
|
-
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
|
|
754
|
+
p5.intro(orange("\u21D2\u21D2") + " Initialize your Globio project");
|
|
755
|
+
const profileName = options.profile ?? config.getActiveProfile() ?? "default";
|
|
756
|
+
const profile = config.getProfile(profileName);
|
|
757
|
+
if (!profile) {
|
|
758
|
+
console.log("Run: npx @globio/cli login --profile " + profileName);
|
|
759
|
+
process.exit(1);
|
|
760
|
+
}
|
|
761
|
+
if (!profile.active_project_id) {
|
|
762
|
+
await projectsCreate({ profile: profileName });
|
|
621
763
|
} else {
|
|
622
|
-
await projectsUse(
|
|
764
|
+
await projectsUse(profile.active_project_id, { profile: profileName });
|
|
623
765
|
}
|
|
624
766
|
const values = await promptInit();
|
|
625
|
-
const
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
767
|
+
const activeProfile = config.getProfile(profileName);
|
|
768
|
+
const activeProjectKey = activeProfile?.project_api_key;
|
|
769
|
+
const { projectId: activeProjectId } = config.requireProject(profileName);
|
|
770
|
+
if (!activeProjectKey) {
|
|
771
|
+
console.log("No project API key cached. Run: npx @globio/cli projects use " + activeProjectId);
|
|
772
|
+
process.exit(1);
|
|
773
|
+
}
|
|
774
|
+
if (!existsSync2("globio.config.ts")) {
|
|
775
|
+
writeFileSync2(
|
|
629
776
|
"globio.config.ts",
|
|
630
777
|
`import { Globio } from '@globio/sdk';
|
|
631
778
|
|
|
@@ -636,8 +783,8 @@ export const globio = new Globio({
|
|
|
636
783
|
);
|
|
637
784
|
printSuccess("Created globio.config.ts");
|
|
638
785
|
}
|
|
639
|
-
if (!
|
|
640
|
-
|
|
786
|
+
if (!existsSync2(".env")) {
|
|
787
|
+
writeFileSync2(".env", `GLOBIO_API_KEY=${activeProjectKey}
|
|
641
788
|
`);
|
|
642
789
|
printSuccess("Created .env");
|
|
643
790
|
}
|
|
@@ -646,19 +793,21 @@ export const globio = new Globio({
|
|
|
646
793
|
printSuccess("Starting Firebase migration...");
|
|
647
794
|
await migrateFirestore({
|
|
648
795
|
from: values.serviceAccountPath,
|
|
649
|
-
all: true
|
|
796
|
+
all: true,
|
|
797
|
+
profile: profileName
|
|
650
798
|
});
|
|
651
799
|
const serviceAccount = JSON.parse(
|
|
652
|
-
|
|
800
|
+
readFileSync3(values.serviceAccountPath, "utf-8")
|
|
653
801
|
);
|
|
654
802
|
await migrateFirebaseStorage({
|
|
655
803
|
from: values.serviceAccountPath,
|
|
656
804
|
bucket: `${serviceAccount.project_id}.appspot.com`,
|
|
657
|
-
all: true
|
|
805
|
+
all: true,
|
|
806
|
+
profile: profileName
|
|
658
807
|
});
|
|
659
808
|
}
|
|
660
809
|
console.log("");
|
|
661
|
-
|
|
810
|
+
p5.outro(
|
|
662
811
|
orange("\u21D2\u21D2") + " Your project is ready.\n\n " + muted("Next steps:") + `
|
|
663
812
|
|
|
664
813
|
npm install @globio/sdk
|
|
@@ -668,7 +817,7 @@ export const globio = new Globio({
|
|
|
668
817
|
}
|
|
669
818
|
|
|
670
819
|
// src/commands/services.ts
|
|
671
|
-
import
|
|
820
|
+
import chalk9 from "chalk";
|
|
672
821
|
var ALL_SERVICES = [
|
|
673
822
|
"id",
|
|
674
823
|
"doc",
|
|
@@ -681,30 +830,36 @@ var ALL_SERVICES = [
|
|
|
681
830
|
"brain",
|
|
682
831
|
"code"
|
|
683
832
|
];
|
|
684
|
-
async function servicesList() {
|
|
833
|
+
async function servicesList(options = {}) {
|
|
834
|
+
void options.profile;
|
|
835
|
+
void config;
|
|
685
836
|
console.log("");
|
|
686
|
-
console.log(
|
|
837
|
+
console.log(chalk9.cyan("Available Globio services:"));
|
|
687
838
|
ALL_SERVICES.forEach((service) => {
|
|
688
|
-
console.log(" " +
|
|
839
|
+
console.log(" " + chalk9.white(service));
|
|
689
840
|
});
|
|
690
841
|
console.log("");
|
|
691
842
|
console.log(
|
|
692
|
-
|
|
843
|
+
chalk9.gray("Manage service access via console.globio.stanlink.online")
|
|
693
844
|
);
|
|
694
845
|
console.log("");
|
|
695
846
|
}
|
|
696
847
|
|
|
697
848
|
// src/commands/functions.ts
|
|
698
|
-
import
|
|
849
|
+
import chalk10 from "chalk";
|
|
699
850
|
import ora from "ora";
|
|
700
|
-
import { existsSync as
|
|
701
|
-
|
|
702
|
-
|
|
851
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
852
|
+
function resolveProfileName3(profile) {
|
|
853
|
+
return profile ?? config.getActiveProfile() ?? "default";
|
|
854
|
+
}
|
|
855
|
+
async function functionsList(options = {}) {
|
|
856
|
+
const profileName = resolveProfileName3(options.profile);
|
|
857
|
+
const client = getClient(profileName);
|
|
703
858
|
const spinner2 = ora("Fetching functions...").start();
|
|
704
859
|
const result = await client.code.listFunctions();
|
|
705
860
|
spinner2.stop();
|
|
706
861
|
if (!result.success || !result.data.length) {
|
|
707
|
-
console.log(
|
|
862
|
+
console.log(chalk10.gray("No functions found."));
|
|
708
863
|
return;
|
|
709
864
|
}
|
|
710
865
|
console.log("");
|
|
@@ -718,10 +873,10 @@ async function functionsList() {
|
|
|
718
873
|
});
|
|
719
874
|
console.log("");
|
|
720
875
|
}
|
|
721
|
-
async function functionsCreate(slug) {
|
|
876
|
+
async function functionsCreate(slug, _options = {}) {
|
|
722
877
|
const filename = `${slug}.js`;
|
|
723
|
-
if (
|
|
724
|
-
console.log(
|
|
878
|
+
if (existsSync3(filename)) {
|
|
879
|
+
console.log(chalk10.yellow(`${filename} already exists.`));
|
|
725
880
|
return;
|
|
726
881
|
}
|
|
727
882
|
const template = `/**
|
|
@@ -739,24 +894,25 @@ async function handler(input, globio) {
|
|
|
739
894
|
};
|
|
740
895
|
}
|
|
741
896
|
`;
|
|
742
|
-
|
|
743
|
-
console.log(
|
|
897
|
+
writeFileSync3(filename, template);
|
|
898
|
+
console.log(chalk10.green(`Created ${filename}`));
|
|
744
899
|
console.log(
|
|
745
|
-
|
|
900
|
+
chalk10.gray(`Deploy with: npx @globio/cli functions deploy ${slug}`)
|
|
746
901
|
);
|
|
747
902
|
}
|
|
748
903
|
async function functionsDeploy(slug, options) {
|
|
749
904
|
const filename = options.file ?? `${slug}.js`;
|
|
750
|
-
if (!
|
|
905
|
+
if (!existsSync3(filename)) {
|
|
751
906
|
console.log(
|
|
752
|
-
|
|
907
|
+
chalk10.red(
|
|
753
908
|
`File not found: ${filename}. Create it with: npx @globio/cli functions create ${slug}`
|
|
754
909
|
)
|
|
755
910
|
);
|
|
756
911
|
process.exit(1);
|
|
757
912
|
}
|
|
758
|
-
const code =
|
|
759
|
-
const
|
|
913
|
+
const code = readFileSync4(filename, "utf-8");
|
|
914
|
+
const profileName = resolveProfileName3(options.profile);
|
|
915
|
+
const client = getClient(profileName);
|
|
760
916
|
const spinner2 = ora(`Deploying ${slug}...`).start();
|
|
761
917
|
const existing = await client.code.getFunction(slug);
|
|
762
918
|
let result;
|
|
@@ -786,16 +942,17 @@ async function functionsInvoke(slug, options) {
|
|
|
786
942
|
try {
|
|
787
943
|
input = JSON.parse(options.input);
|
|
788
944
|
} catch {
|
|
789
|
-
console.error(
|
|
945
|
+
console.error(chalk10.red("--input must be valid JSON"));
|
|
790
946
|
process.exit(1);
|
|
791
947
|
}
|
|
792
948
|
}
|
|
793
|
-
const
|
|
949
|
+
const profileName = resolveProfileName3(options.profile);
|
|
950
|
+
const client = getClient(profileName);
|
|
794
951
|
const spinner2 = ora(`Invoking ${slug}...`).start();
|
|
795
952
|
const result = await client.code.invoke(slug, input);
|
|
796
953
|
spinner2.stop();
|
|
797
954
|
if (!result.success) {
|
|
798
|
-
console.log(
|
|
955
|
+
console.log(chalk10.red("Invocation failed"));
|
|
799
956
|
console.error(result.error.message);
|
|
800
957
|
return;
|
|
801
958
|
}
|
|
@@ -807,12 +964,13 @@ Duration: ${result.data.duration_ms}ms`));
|
|
|
807
964
|
}
|
|
808
965
|
async function functionsLogs(slug, options) {
|
|
809
966
|
const limit = options.limit ? parseInt(options.limit, 10) : 20;
|
|
810
|
-
const
|
|
967
|
+
const profileName = resolveProfileName3(options.profile);
|
|
968
|
+
const client = getClient(profileName);
|
|
811
969
|
const spinner2 = ora("Fetching invocations...").start();
|
|
812
970
|
const result = await client.code.getInvocations(slug, limit);
|
|
813
971
|
spinner2.stop();
|
|
814
972
|
if (!result.success || !result.data.length) {
|
|
815
|
-
console.log(
|
|
973
|
+
console.log(chalk10.gray("No invocations yet."));
|
|
816
974
|
return;
|
|
817
975
|
}
|
|
818
976
|
console.log("");
|
|
@@ -820,13 +978,14 @@ async function functionsLogs(slug, options) {
|
|
|
820
978
|
const status = inv.success ? "\x1B[38;2;244;140;6m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
|
|
821
979
|
const date = new Date(inv.invoked_at * 1e3).toISOString().replace("T", " ").slice(0, 19);
|
|
822
980
|
console.log(
|
|
823
|
-
` ${status} ${
|
|
981
|
+
` ${status} ${chalk10.gray(date)} ${inv.duration_ms}ms ${chalk10.gray(`[${inv.trigger_type}]`)}`
|
|
824
982
|
);
|
|
825
983
|
});
|
|
826
984
|
console.log("");
|
|
827
985
|
}
|
|
828
|
-
async function functionsDelete(slug) {
|
|
829
|
-
const
|
|
986
|
+
async function functionsDelete(slug, options = {}) {
|
|
987
|
+
const profileName = resolveProfileName3(options.profile);
|
|
988
|
+
const client = getClient(profileName);
|
|
830
989
|
const spinner2 = ora(`Deleting ${slug}...`).start();
|
|
831
990
|
const result = await client.code.deleteFunction(slug);
|
|
832
991
|
if (!result.success) {
|
|
@@ -836,8 +995,9 @@ async function functionsDelete(slug) {
|
|
|
836
995
|
}
|
|
837
996
|
spinner2.succeed(`Deleted ${slug}`);
|
|
838
997
|
}
|
|
839
|
-
async function functionsToggle(slug, active) {
|
|
840
|
-
const
|
|
998
|
+
async function functionsToggle(slug, active, options = {}) {
|
|
999
|
+
const profileName = resolveProfileName3(options.profile);
|
|
1000
|
+
const client = getClient(profileName);
|
|
841
1001
|
const spinner2 = ora(
|
|
842
1002
|
`${active ? "Enabling" : "Disabling"} ${slug}...`
|
|
843
1003
|
).start();
|
|
@@ -856,28 +1016,43 @@ var program = new Command();
|
|
|
856
1016
|
program.name("globio").description("The official Globio CLI").version(version4).addHelpText("beforeAll", () => {
|
|
857
1017
|
printBanner(version4);
|
|
858
1018
|
return "";
|
|
859
|
-
})
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1019
|
+
}).addHelpText(
|
|
1020
|
+
"after",
|
|
1021
|
+
`
|
|
1022
|
+
Examples:
|
|
1023
|
+
$ globio login
|
|
1024
|
+
$ globio login --profile work
|
|
1025
|
+
$ globio use work
|
|
1026
|
+
$ globio projects list
|
|
1027
|
+
$ globio projects use proj_abc123
|
|
1028
|
+
$ globio functions deploy my-function
|
|
1029
|
+
$ globio migrate firestore --from ./key.json --all
|
|
1030
|
+
|
|
1031
|
+
Credentials are stored in ~/.globio/profiles/
|
|
1032
|
+
`
|
|
1033
|
+
);
|
|
1034
|
+
program.command("login").description("Log in to your Globio account").option("-p, --profile <name>", "Profile name", "default").option("--token", "Use a personal access token").action(login);
|
|
1035
|
+
program.command("logout").description("Log out").option("--profile <name>", "Use a specific profile").action(logout);
|
|
1036
|
+
program.command("whoami").description("Show current account and project").option("--profile <name>", "Use a specific profile").action(whoami);
|
|
1037
|
+
program.command("use <profile>").description("Switch active profile").action(useProfile);
|
|
1038
|
+
program.command("init").description("Initialize a Globio project").option("--profile <name>", "Use a specific profile").action(init);
|
|
864
1039
|
var projects = program.command("projects").description("Manage projects");
|
|
865
|
-
projects.command("list").description("List projects").action(projectsList);
|
|
866
|
-
projects.command("create").description("Create a project").action(projectsCreate);
|
|
867
|
-
projects.command("use <projectId>").description("Set active project").action(projectsUse);
|
|
868
|
-
program.command("services").description("List available Globio services").action(servicesList);
|
|
1040
|
+
projects.command("list").description("List projects").option("--profile <name>", "Use a specific profile").action(projectsList);
|
|
1041
|
+
projects.command("create").description("Create a project").option("--profile <name>", "Use a specific profile").action(projectsCreate);
|
|
1042
|
+
projects.command("use <projectId>").description("Set active project").option("--profile <name>", "Use a specific profile").action(projectsUse);
|
|
1043
|
+
program.command("services").description("List available Globio services").option("--profile <name>", "Use a specific profile").action(servicesList);
|
|
869
1044
|
var functions = program.command("functions").alias("fn").description("Manage GlobalCode edge functions");
|
|
870
|
-
functions.command("list").description("List all functions").action(functionsList);
|
|
871
|
-
functions.command("create <slug>").description("Scaffold a new function file locally").action(functionsCreate);
|
|
872
|
-
functions.command("deploy <slug>").description("Deploy a function to GlobalCode").option("-f, --file <path>", "Path to function file").option("-n, --name <name>", "Display name").action(functionsDeploy);
|
|
873
|
-
functions.command("invoke <slug>").description("Invoke a function").option("-i, --input <json>", "JSON input payload").action(functionsInvoke);
|
|
874
|
-
functions.command("logs <slug>").description("Show invocation history").option("-l, --limit <n>", "Number of entries", "20").action(functionsLogs);
|
|
875
|
-
functions.command("delete <slug>").description("Delete a function").action(functionsDelete);
|
|
876
|
-
functions.command("enable <slug>").description("Enable a function").action((slug) => functionsToggle(slug, true));
|
|
877
|
-
functions.command("disable <slug>").description("Disable a function").action((slug) => functionsToggle(slug, false));
|
|
1045
|
+
functions.command("list").description("List all functions").option("--profile <name>", "Use a specific profile").action(functionsList);
|
|
1046
|
+
functions.command("create <slug>").description("Scaffold a new function file locally").option("--profile <name>", "Use a specific profile").action(functionsCreate);
|
|
1047
|
+
functions.command("deploy <slug>").description("Deploy a function to GlobalCode").option("-f, --file <path>", "Path to function file").option("-n, --name <name>", "Display name").option("--profile <name>", "Use a specific profile").action(functionsDeploy);
|
|
1048
|
+
functions.command("invoke <slug>").description("Invoke a function").option("-i, --input <json>", "JSON input payload").option("--profile <name>", "Use a specific profile").action(functionsInvoke);
|
|
1049
|
+
functions.command("logs <slug>").description("Show invocation history").option("-l, --limit <n>", "Number of entries", "20").option("--profile <name>", "Use a specific profile").action(functionsLogs);
|
|
1050
|
+
functions.command("delete <slug>").description("Delete a function").option("--profile <name>", "Use a specific profile").action(functionsDelete);
|
|
1051
|
+
functions.command("enable <slug>").description("Enable a function").option("--profile <name>", "Use a specific profile").action((slug, options) => functionsToggle(slug, true, options));
|
|
1052
|
+
functions.command("disable <slug>").description("Disable a function").option("--profile <name>", "Use a specific profile").action((slug, options) => functionsToggle(slug, false, options));
|
|
878
1053
|
var migrate = program.command("migrate").description("Migrate from Firebase to Globio");
|
|
879
|
-
migrate.command("firestore").description("Migrate Firestore collections to GlobalDoc").requiredOption("--from <path>", "Path to Firebase service account JSON").option("--collection <name>", "Migrate a specific collection").option("--all", "Migrate all collections").action(migrateFirestore);
|
|
880
|
-
migrate.command("firebase-storage").description("Migrate Firebase Storage to GlobalVault").requiredOption("--from <path>", "Path to Firebase service account JSON").requiredOption("--bucket <name>", "Firebase Storage bucket").option("--folder <path>", "Migrate a specific folder").option("--all", "Migrate all files").action(migrateFirebaseStorage);
|
|
1054
|
+
migrate.command("firestore").description("Migrate Firestore collections to GlobalDoc").requiredOption("--from <path>", "Path to Firebase service account JSON").option("--collection <name>", "Migrate a specific collection").option("--all", "Migrate all collections").option("--profile <name>", "Use a specific profile").action(migrateFirestore);
|
|
1055
|
+
migrate.command("firebase-storage").description("Migrate Firebase Storage to GlobalVault").requiredOption("--from <path>", "Path to Firebase service account JSON").requiredOption("--bucket <name>", "Firebase Storage bucket").option("--folder <path>", "Migrate a specific folder").option("--all", "Migrate all files").option("--profile <name>", "Use a specific profile").action(migrateFirebaseStorage);
|
|
881
1056
|
async function main() {
|
|
882
1057
|
if (process.argv.length <= 2) {
|
|
883
1058
|
program.help();
|