@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/src/index.ts CHANGED
@@ -2,6 +2,7 @@
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';
@@ -32,51 +33,79 @@ program
32
33
  .addHelpText('beforeAll', () => {
33
34
  printBanner(version);
34
35
  return '';
35
- });
36
+ })
37
+ .addHelpText(
38
+ 'after',
39
+ `
40
+ Examples:
41
+ $ globio login
42
+ $ globio login --profile work
43
+ $ globio use work
44
+ $ globio projects list
45
+ $ globio projects use proj_abc123
46
+ $ globio functions deploy my-function
47
+ $ globio migrate firestore --from ./key.json --all
48
+
49
+ Credentials are stored in ~/.globio/profiles/
50
+ `
51
+ );
36
52
 
37
53
  program
38
54
  .command('login')
39
55
  .description('Log in to your Globio account')
56
+ .option('-p, --profile <name>', 'Profile name', 'default')
40
57
  .option('--token', 'Use a personal access token')
41
58
  .action(login);
42
- program.command('logout').description('Log out').action(logout);
43
- program.command('whoami').description('Show current account and project').action(whoami);
59
+ program.command('logout').description('Log out').option('--profile <name>', 'Use a specific profile').action(logout);
60
+ program.command('whoami').description('Show current account and project').option('--profile <name>', 'Use a specific profile').action(whoami);
61
+ program.command('use <profile>').description('Switch active profile').action(useProfile);
44
62
 
45
- program.command('init').description('Initialize a Globio project').action(init);
63
+ program.command('init').description('Initialize a Globio project').option('--profile <name>', 'Use a specific profile').action(init);
46
64
 
47
65
  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);
66
+ projects.command('list').description('List projects').option('--profile <name>', 'Use a specific profile').action(projectsList);
67
+ projects.command('create').description('Create a project').option('--profile <name>', 'Use a specific profile').action(projectsCreate);
68
+ projects.command('use <projectId>').description('Set active project').option('--profile <name>', 'Use a specific profile').action(projectsUse);
51
69
 
52
- program.command('services').description('List available Globio services').action(servicesList);
70
+ program.command('services').description('List available Globio services').option('--profile <name>', 'Use a specific profile').action(servicesList);
53
71
 
54
72
  const functions = program
55
73
  .command('functions')
56
74
  .alias('fn')
57
75
  .description('Manage GlobalCode edge functions');
58
76
 
59
- functions.command('list').description('List all functions').action(functionsList);
60
- functions.command('create <slug>').description('Scaffold a new function file locally').action(functionsCreate);
77
+ functions.command('list').description('List all functions').option('--profile <name>', 'Use a specific profile').action(functionsList);
78
+ functions.command('create <slug>').description('Scaffold a new function file locally').option('--profile <name>', 'Use a specific profile').action(functionsCreate);
61
79
  functions
62
80
  .command('deploy <slug>')
63
81
  .description('Deploy a function to GlobalCode')
64
82
  .option('-f, --file <path>', 'Path to function file')
65
83
  .option('-n, --name <name>', 'Display name')
84
+ .option('--profile <name>', 'Use a specific profile')
66
85
  .action(functionsDeploy);
67
86
  functions
68
87
  .command('invoke <slug>')
69
88
  .description('Invoke a function')
70
89
  .option('-i, --input <json>', 'JSON input payload')
90
+ .option('--profile <name>', 'Use a specific profile')
71
91
  .action(functionsInvoke);
72
92
  functions
73
93
  .command('logs <slug>')
74
94
  .description('Show invocation history')
75
95
  .option('-l, --limit <n>', 'Number of entries', '20')
96
+ .option('--profile <name>', 'Use a specific profile')
76
97
  .action(functionsLogs);
77
- functions.command('delete <slug>').description('Delete a function').action(functionsDelete);
78
- functions.command('enable <slug>').description('Enable a function').action((slug) => functionsToggle(slug, true));
79
- functions.command('disable <slug>').description('Disable a function').action((slug) => functionsToggle(slug, false));
98
+ functions.command('delete <slug>').description('Delete a function').option('--profile <name>', 'Use a specific profile').action(functionsDelete);
99
+ functions
100
+ .command('enable <slug>')
101
+ .description('Enable a function')
102
+ .option('--profile <name>', 'Use a specific profile')
103
+ .action((slug, options) => functionsToggle(slug, true, options));
104
+ functions
105
+ .command('disable <slug>')
106
+ .description('Disable a function')
107
+ .option('--profile <name>', 'Use a specific profile')
108
+ .action((slug, options) => functionsToggle(slug, false, options));
80
109
 
81
110
  const migrate = program
82
111
  .command('migrate')
@@ -88,6 +117,7 @@ migrate
88
117
  .requiredOption('--from <path>', 'Path to Firebase service account JSON')
89
118
  .option('--collection <name>', 'Migrate a specific collection')
90
119
  .option('--all', 'Migrate all collections')
120
+ .option('--profile <name>', 'Use a specific profile')
91
121
  .action(migrateFirestore);
92
122
 
93
123
  migrate
@@ -97,6 +127,7 @@ migrate
97
127
  .requiredOption('--bucket <name>', 'Firebase Storage bucket')
98
128
  .option('--folder <path>', 'Migrate a specific folder')
99
129
  .option('--all', 'Migrate all files')
130
+ .option('--profile <name>', 'Use a specific profile')
100
131
  .action(migrateFirebaseStorage);
101
132
 
102
133
  async function main() {
package/src/lib/config.ts CHANGED
@@ -1,91 +1,139 @@
1
1
  import chalk from 'chalk';
2
- import Conf from 'conf';
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'fs';
3
+ import os from 'os';
4
+ import path from 'path';
3
5
 
4
- interface GlobioConfig {
5
- pat?: string;
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
- const store = new Conf<GlobioConfig>({
15
- projectName: 'globio',
16
- defaults: {},
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
- get: (): GlobioConfig => store.store,
21
- set: (values: Partial<GlobioConfig>) => {
22
- Object.entries(values).forEach(([key, value]) => {
23
- if (value !== undefined) {
24
- store.set(key as keyof GlobioConfig, value);
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
- clear: () => store.clear(),
29
- getPat: () => store.get('pat'),
30
- requirePat: () => {
31
- const pat = store.get('pat');
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
- requireProject: () => {
39
- const projectId = store.get('projectId');
40
- if (!projectId) {
41
- console.error(
42
- chalk.red('No active project. Run: npx @globio/cli projects use <projectId>')
43
- );
44
- process.exit(1);
45
- }
46
- return projectId;
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
- setProjectAuth: (projectId: string, apiKey: string, projectName?: string) => {
49
- const projectApiKeys = store.get('projectApiKeys') ?? {};
50
- const projectNames = store.get('projectNames') ?? {};
51
- projectApiKeys[projectId] = apiKey;
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
- getProjectApiKey: (projectId: string) => {
64
- const projectApiKeys = store.get('projectApiKeys') ?? {};
65
- return projectApiKeys[projectId];
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
- requireProjectApiKey: () => {
68
- const projectId = store.get('projectId');
69
- if (!projectId) {
70
- console.error(
71
- chalk.red('No active project. Run: npx @globio/cli projects use <projectId>')
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
- const projectApiKeys = store.get('projectApiKeys') ?? {};
77
- const apiKey = projectApiKeys[projectId];
78
- if (!apiKey) {
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
- return apiKey;
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.getPat();
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 apiKey = config.requireProjectApiKey();
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