@globio/cli 0.1.3 → 0.1.5
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 +437 -223
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/auth/login.ts +62 -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/profiles.ts +26 -0
- package/src/commands/projects.ts +41 -14
- package/src/commands/services.ts +4 -1
- package/src/index.ts +55 -13
- package/src/lib/config.ts +119 -71
- package/src/lib/manage.ts +4 -3
- package/src/lib/sdk.ts +6 -2
package/src/index.ts
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { login } from './auth/login.js';
|
|
4
4
|
import { logout } from './auth/logout.js';
|
|
5
|
+
import { useProfile } from './auth/useProfile.js';
|
|
5
6
|
import { whoami } from './auth/whoami.js';
|
|
6
7
|
import { init } from './commands/init.js';
|
|
7
8
|
import { projectsCreate, projectsList, projectsUse } from './commands/projects.js';
|
|
9
|
+
import { profilesList } from './commands/profiles.js';
|
|
8
10
|
import { servicesList } from './commands/services.js';
|
|
9
11
|
import {
|
|
10
12
|
functionsList,
|
|
@@ -32,51 +34,89 @@ program
|
|
|
32
34
|
.addHelpText('beforeAll', () => {
|
|
33
35
|
printBanner(version);
|
|
34
36
|
return '';
|
|
35
|
-
})
|
|
37
|
+
})
|
|
38
|
+
.addHelpText(
|
|
39
|
+
'after',
|
|
40
|
+
`
|
|
41
|
+
Examples:
|
|
42
|
+
$ globio login
|
|
43
|
+
$ globio login --profile work
|
|
44
|
+
$ globio use work
|
|
45
|
+
$ globio projects list
|
|
46
|
+
$ globio projects use proj_abc123
|
|
47
|
+
$ globio functions deploy my-function
|
|
48
|
+
$ globio migrate firestore --from ./key.json --all
|
|
49
|
+
|
|
50
|
+
Credentials are stored in ~/.globio/profiles/
|
|
51
|
+
`
|
|
52
|
+
);
|
|
36
53
|
|
|
37
54
|
program
|
|
38
55
|
.command('login')
|
|
39
56
|
.description('Log in to your Globio account')
|
|
57
|
+
.option('-p, --profile <name>', 'Profile name', 'default')
|
|
40
58
|
.option('--token', 'Use a personal access token')
|
|
41
59
|
.action(login);
|
|
42
|
-
program.command('logout').description('Log out').action(logout);
|
|
43
|
-
program.command('whoami').description('Show current account and project').action(whoami);
|
|
60
|
+
program.command('logout').description('Log out').option('--profile <name>', 'Use a specific profile').action(logout);
|
|
61
|
+
program.command('whoami').description('Show current account and project').option('--profile <name>', 'Use a specific profile').action(whoami);
|
|
62
|
+
program.command('use <profile>').description('Switch active profile').action(useProfile);
|
|
63
|
+
|
|
64
|
+
program.command('init').description('Initialize a Globio project').option('--profile <name>', 'Use a specific profile').action(init);
|
|
65
|
+
|
|
66
|
+
const profiles = program
|
|
67
|
+
.command('profiles')
|
|
68
|
+
.description('Manage login profiles')
|
|
69
|
+
.action(profilesList);
|
|
44
70
|
|
|
45
|
-
|
|
71
|
+
profiles
|
|
72
|
+
.command('list')
|
|
73
|
+
.description('List all profiles')
|
|
74
|
+
.action(profilesList);
|
|
46
75
|
|
|
47
76
|
const projects = program.command('projects').description('Manage projects');
|
|
48
|
-
projects.command('list').description('List projects').action(projectsList);
|
|
49
|
-
projects.command('create').description('Create a project').action(projectsCreate);
|
|
50
|
-
projects.command('use <projectId>').description('Set active project').action(projectsUse);
|
|
77
|
+
projects.command('list').description('List projects').option('--profile <name>', 'Use a specific profile').action(projectsList);
|
|
78
|
+
projects.command('create').description('Create a project').option('--profile <name>', 'Use a specific profile').action(projectsCreate);
|
|
79
|
+
projects.command('use <projectId>').description('Set active project').option('--profile <name>', 'Use a specific profile').action(projectsUse);
|
|
51
80
|
|
|
52
|
-
program.command('services').description('List available Globio services').action(servicesList);
|
|
81
|
+
program.command('services').description('List available Globio services').option('--profile <name>', 'Use a specific profile').action(servicesList);
|
|
53
82
|
|
|
54
83
|
const functions = program
|
|
55
84
|
.command('functions')
|
|
56
85
|
.alias('fn')
|
|
57
86
|
.description('Manage GlobalCode edge functions');
|
|
58
87
|
|
|
59
|
-
functions.command('list').description('List all functions').action(functionsList);
|
|
60
|
-
functions.command('create <slug>').description('Scaffold a new function file locally').action(functionsCreate);
|
|
88
|
+
functions.command('list').description('List all functions').option('--profile <name>', 'Use a specific profile').action(functionsList);
|
|
89
|
+
functions.command('create <slug>').description('Scaffold a new function file locally').option('--profile <name>', 'Use a specific profile').action(functionsCreate);
|
|
61
90
|
functions
|
|
62
91
|
.command('deploy <slug>')
|
|
63
92
|
.description('Deploy a function to GlobalCode')
|
|
64
93
|
.option('-f, --file <path>', 'Path to function file')
|
|
65
94
|
.option('-n, --name <name>', 'Display name')
|
|
95
|
+
.option('--profile <name>', 'Use a specific profile')
|
|
66
96
|
.action(functionsDeploy);
|
|
67
97
|
functions
|
|
68
98
|
.command('invoke <slug>')
|
|
69
99
|
.description('Invoke a function')
|
|
70
100
|
.option('-i, --input <json>', 'JSON input payload')
|
|
101
|
+
.option('--profile <name>', 'Use a specific profile')
|
|
71
102
|
.action(functionsInvoke);
|
|
72
103
|
functions
|
|
73
104
|
.command('logs <slug>')
|
|
74
105
|
.description('Show invocation history')
|
|
75
106
|
.option('-l, --limit <n>', 'Number of entries', '20')
|
|
107
|
+
.option('--profile <name>', 'Use a specific profile')
|
|
76
108
|
.action(functionsLogs);
|
|
77
|
-
functions.command('delete <slug>').description('Delete a function').action(functionsDelete);
|
|
78
|
-
functions
|
|
79
|
-
|
|
109
|
+
functions.command('delete <slug>').description('Delete a function').option('--profile <name>', 'Use a specific profile').action(functionsDelete);
|
|
110
|
+
functions
|
|
111
|
+
.command('enable <slug>')
|
|
112
|
+
.description('Enable a function')
|
|
113
|
+
.option('--profile <name>', 'Use a specific profile')
|
|
114
|
+
.action((slug, options) => functionsToggle(slug, true, options));
|
|
115
|
+
functions
|
|
116
|
+
.command('disable <slug>')
|
|
117
|
+
.description('Disable a function')
|
|
118
|
+
.option('--profile <name>', 'Use a specific profile')
|
|
119
|
+
.action((slug, options) => functionsToggle(slug, false, options));
|
|
80
120
|
|
|
81
121
|
const migrate = program
|
|
82
122
|
.command('migrate')
|
|
@@ -88,6 +128,7 @@ migrate
|
|
|
88
128
|
.requiredOption('--from <path>', 'Path to Firebase service account JSON')
|
|
89
129
|
.option('--collection <name>', 'Migrate a specific collection')
|
|
90
130
|
.option('--all', 'Migrate all collections')
|
|
131
|
+
.option('--profile <name>', 'Use a specific profile')
|
|
91
132
|
.action(migrateFirestore);
|
|
92
133
|
|
|
93
134
|
migrate
|
|
@@ -97,6 +138,7 @@ migrate
|
|
|
97
138
|
.requiredOption('--bucket <name>', 'Firebase Storage bucket')
|
|
98
139
|
.option('--folder <path>', 'Migrate a specific folder')
|
|
99
140
|
.option('--all', 'Migrate all files')
|
|
141
|
+
.option('--profile <name>', 'Use a specific profile')
|
|
100
142
|
.action(migrateFirebaseStorage);
|
|
101
143
|
|
|
102
144
|
async function main() {
|
package/src/lib/config.ts
CHANGED
|
@@ -1,91 +1,139 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'fs';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import path from 'path';
|
|
3
5
|
|
|
4
|
-
interface
|
|
5
|
-
|
|
6
|
-
accountEmail?: string;
|
|
7
|
-
accountName?: string;
|
|
8
|
-
projectId?: string;
|
|
9
|
-
projectName?: string;
|
|
10
|
-
projectApiKeys?: Record<string, string>;
|
|
11
|
-
projectNames?: Record<string, string>;
|
|
6
|
+
interface GlobalConfig {
|
|
7
|
+
active_profile: string;
|
|
12
8
|
}
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
export interface ProfileData {
|
|
11
|
+
pat: string;
|
|
12
|
+
account_email: string;
|
|
13
|
+
account_name: string;
|
|
14
|
+
active_project_id?: string;
|
|
15
|
+
active_project_name?: string;
|
|
16
|
+
project_api_key?: string;
|
|
17
|
+
created_at: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const baseDir = path.join(os.homedir(), '.globio');
|
|
21
|
+
const profilesDir = path.join(baseDir, 'profiles');
|
|
22
|
+
const configPath = path.join(baseDir, 'config.json');
|
|
23
|
+
|
|
24
|
+
function ensureBaseDir() {
|
|
25
|
+
mkdirSync(baseDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function ensureProfilesDir() {
|
|
29
|
+
ensureBaseDir();
|
|
30
|
+
mkdirSync(profilesDir, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readGlobalConfig(): GlobalConfig {
|
|
34
|
+
if (!existsSync(configPath)) {
|
|
35
|
+
return { active_profile: 'default' };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const raw = JSON.parse(readFileSync(configPath, 'utf-8')) as Partial<GlobalConfig>;
|
|
40
|
+
return {
|
|
41
|
+
active_profile: raw.active_profile ?? 'default',
|
|
42
|
+
};
|
|
43
|
+
} catch {
|
|
44
|
+
return { active_profile: 'default' };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function writeGlobalConfig(data: GlobalConfig) {
|
|
49
|
+
ensureBaseDir();
|
|
50
|
+
writeFileSync(configPath, JSON.stringify(data, null, 2) + '\n');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function profilePath(name: string) {
|
|
54
|
+
return path.join(profilesDir, `${name}.json`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readProfile(name: string): ProfileData | null {
|
|
58
|
+
const file = profilePath(name);
|
|
59
|
+
if (!existsSync(file)) return null;
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(readFileSync(file, 'utf-8')) as ProfileData;
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function writeProfile(name: string, data: ProfileData) {
|
|
69
|
+
ensureProfilesDir();
|
|
70
|
+
writeFileSync(profilePath(name), JSON.stringify(data, null, 2) + '\n');
|
|
71
|
+
}
|
|
18
72
|
|
|
19
73
|
export const config = {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
});
|
|
74
|
+
getBaseDir: () => baseDir,
|
|
75
|
+
getProfilesDir: () => profilesDir,
|
|
76
|
+
getActiveProfile: (): string => readGlobalConfig().active_profile,
|
|
77
|
+
setActiveProfile: (name: string): void => {
|
|
78
|
+
writeGlobalConfig({ active_profile: name });
|
|
27
79
|
},
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (!pat) {
|
|
33
|
-
console.error(chalk.red('Not logged in. Run: npx @globio/cli login'));
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
return pat;
|
|
80
|
+
getProfile: (name?: string): ProfileData | null => {
|
|
81
|
+
const profileName = name ?? config.getActiveProfile();
|
|
82
|
+
if (!profileName) return null;
|
|
83
|
+
return readProfile(profileName);
|
|
37
84
|
},
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
85
|
+
setProfile: (name: string, data: Partial<ProfileData>): void => {
|
|
86
|
+
const existing = readProfile(name);
|
|
87
|
+
const next: ProfileData = {
|
|
88
|
+
pat: data.pat ?? existing?.pat ?? '',
|
|
89
|
+
account_email: data.account_email ?? existing?.account_email ?? '',
|
|
90
|
+
account_name: data.account_name ?? existing?.account_name ?? '',
|
|
91
|
+
active_project_id: data.active_project_id ?? existing?.active_project_id,
|
|
92
|
+
active_project_name: data.active_project_name ?? existing?.active_project_name,
|
|
93
|
+
project_api_key: data.project_api_key ?? existing?.project_api_key,
|
|
94
|
+
created_at: data.created_at ?? existing?.created_at ?? Date.now(),
|
|
95
|
+
};
|
|
96
|
+
writeProfile(name, next);
|
|
47
97
|
},
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (projectName) {
|
|
53
|
-
projectNames[projectId] = projectName;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
store.set('projectApiKeys', projectApiKeys);
|
|
57
|
-
store.set('projectNames', projectNames);
|
|
58
|
-
store.set('projectId', projectId);
|
|
59
|
-
if (projectName) {
|
|
60
|
-
store.set('projectName', projectName);
|
|
98
|
+
deleteProfile: (name: string): void => {
|
|
99
|
+
const file = profilePath(name);
|
|
100
|
+
if (existsSync(file)) {
|
|
101
|
+
rmSync(file);
|
|
61
102
|
}
|
|
62
103
|
},
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return
|
|
104
|
+
listProfiles: (): string[] => {
|
|
105
|
+
if (!existsSync(profilesDir)) return [];
|
|
106
|
+
return readdirSync(profilesDir)
|
|
107
|
+
.filter((file) => file.endsWith('.json'))
|
|
108
|
+
.map((file) => file.replace(/\.json$/, ''))
|
|
109
|
+
.sort();
|
|
66
110
|
},
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
if (!
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
111
|
+
getActiveProfileData: (): ProfileData | null => {
|
|
112
|
+
const active = config.getActiveProfile();
|
|
113
|
+
if (!active) return null;
|
|
114
|
+
return config.getProfile(active);
|
|
115
|
+
},
|
|
116
|
+
requireAuth: (profileName?: string): { pat: string; profileName: string } => {
|
|
117
|
+
const resolvedProfile = profileName ?? config.getActiveProfile() ?? 'default';
|
|
118
|
+
const profile = config.getProfile(resolvedProfile);
|
|
119
|
+
if (!profile?.pat) {
|
|
120
|
+
console.error(chalk.red('Not logged in. Run: npx @globio/cli login'));
|
|
73
121
|
process.exit(1);
|
|
74
122
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
123
|
+
return { pat: profile.pat, profileName: resolvedProfile };
|
|
124
|
+
},
|
|
125
|
+
requireProject: (profileName?: string): { projectId: string; projectName: string } => {
|
|
126
|
+
const resolvedProfile = profileName ?? config.getActiveProfile() ?? 'default';
|
|
127
|
+
const profile = config.getProfile(resolvedProfile);
|
|
128
|
+
if (!profile?.active_project_id) {
|
|
79
129
|
console.error(
|
|
80
|
-
chalk.red(
|
|
81
|
-
'No project API key stored for the active project. Run: npx @globio/cli projects use <projectId>'
|
|
82
|
-
)
|
|
130
|
+
chalk.red('No active project. Run: npx @globio/cli projects use <projectId>')
|
|
83
131
|
);
|
|
84
132
|
process.exit(1);
|
|
85
133
|
}
|
|
86
|
-
|
|
87
|
-
|
|
134
|
+
return {
|
|
135
|
+
projectId: profile.active_project_id,
|
|
136
|
+
projectName: profile.active_project_name ?? 'unnamed',
|
|
137
|
+
};
|
|
88
138
|
},
|
|
89
139
|
};
|
|
90
|
-
|
|
91
|
-
export type { GlobioConfig };
|
package/src/lib/manage.ts
CHANGED
|
@@ -7,6 +7,7 @@ interface ManageRequestOptions {
|
|
|
7
7
|
method?: string;
|
|
8
8
|
body?: unknown;
|
|
9
9
|
token?: string;
|
|
10
|
+
profileName?: string;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export interface ManageAccount {
|
|
@@ -45,9 +46,9 @@ export interface ManageProjectKey {
|
|
|
45
46
|
token?: string;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
function getAuthToken(explicitToken?: string): string | undefined {
|
|
49
|
+
function getAuthToken(explicitToken?: string, profileName?: string): string | undefined {
|
|
49
50
|
if (explicitToken) return explicitToken;
|
|
50
|
-
return config.
|
|
51
|
+
return config.getProfile(profileName)?.pat;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
export async function manageRequest<T>(
|
|
@@ -59,7 +60,7 @@ export async function manageRequest<T>(
|
|
|
59
60
|
headers.set('Content-Type', 'application/json');
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
const token = getAuthToken(options.token);
|
|
63
|
+
const token = getAuthToken(options.token, options.profileName);
|
|
63
64
|
if (token) {
|
|
64
65
|
headers.set('Authorization', `Bearer ${token}`);
|
|
65
66
|
}
|
package/src/lib/sdk.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { Globio } from '@globio/sdk';
|
|
2
2
|
import { config } from './config.js';
|
|
3
3
|
|
|
4
|
-
export function getClient(): Globio {
|
|
5
|
-
const
|
|
4
|
+
export function getClient(profileName?: string): Globio {
|
|
5
|
+
const { pat } = config.requireAuth(profileName);
|
|
6
|
+
const { projectId } = config.requireProject(profileName);
|
|
7
|
+
const profile = config.getProfile(profileName);
|
|
8
|
+
const apiKey = profile?.project_api_key ?? pat;
|
|
9
|
+
void projectId;
|
|
6
10
|
return new Globio({ apiKey });
|
|
7
11
|
}
|
|
8
12
|
|