gemix 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -0
- package/dist/auth/oauth.d.ts +13 -0
- package/dist/auth/oauth.d.ts.map +1 -1
- package/dist/auth/oauth.js +165 -140
- package/dist/auth/oauth.js.map +1 -1
- package/dist/commands/current.js +2 -2
- package/dist/commands/current.js.map +1 -1
- package/dist/commands/dash.d.ts +2 -0
- package/dist/commands/dash.d.ts.map +1 -0
- package/dist/commands/dash.js +125 -0
- package/dist/commands/dash.js.map +1 -0
- package/dist/commands/list.js +1 -1
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/register.js +1 -1
- package/dist/commands/register.js.map +1 -1
- package/dist/commands/remove.js +1 -1
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/use.js +1 -1
- package/dist/commands/use.js.map +1 -1
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/server/public/app.js +181 -0
- package/dist/server/public/index.html +77 -0
- package/dist/server/public/style.css +132 -0
- package/dist/server/quota.d.ts +22 -0
- package/dist/server/quota.d.ts.map +1 -0
- package/dist/server/quota.js +69 -0
- package/dist/server/quota.js.map +1 -0
- package/dist/server/server.d.ts +11 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +170 -0
- package/dist/server/server.js.map +1 -0
- package/dist/storage/paths.js +1 -1
- package/dist/storage/paths.js.map +1 -1
- package/package.json +20 -9
- package/doc/README.md +0 -90
- package/src/auth/oauth.ts +0 -231
- package/src/commands/current.ts +0 -18
- package/src/commands/list.ts +0 -20
- package/src/commands/register.ts +0 -53
- package/src/commands/remove.ts +0 -37
- package/src/commands/use.ts +0 -25
- package/src/index.ts +0 -59
- package/src/storage/accounts.ts +0 -285
- package/src/storage/paths.ts +0 -56
- package/src/utils/table.ts +0 -58
- package/src/utils/validation.ts +0 -27
- package/tsconfig.json +0 -20
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# gemix - Gemini Multi-Account CLI
|
|
2
|
+
|
|
3
|
+
Manage multiple Google accounts for Gemini API access from a single CLI tool.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### From Source
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone <repo-url>
|
|
11
|
+
cd gemix
|
|
12
|
+
npm install
|
|
13
|
+
npm run build
|
|
14
|
+
npm link
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Requirements
|
|
18
|
+
|
|
19
|
+
- Node.js >= 18
|
|
20
|
+
- npm
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Register your first account
|
|
26
|
+
gemix register personal
|
|
27
|
+
|
|
28
|
+
# Register another account
|
|
29
|
+
gemix register work
|
|
30
|
+
|
|
31
|
+
# List all accounts
|
|
32
|
+
gemix list
|
|
33
|
+
|
|
34
|
+
# Switch accounts
|
|
35
|
+
gemix use work
|
|
36
|
+
|
|
37
|
+
# Check active account
|
|
38
|
+
gemix current
|
|
39
|
+
|
|
40
|
+
# Remove an account
|
|
41
|
+
gemix remove personal
|
|
42
|
+
|
|
43
|
+
# Open local web dashboard
|
|
44
|
+
gemix dash
|
|
45
|
+
|
|
46
|
+
# Check dashboard status or stop it
|
|
47
|
+
gemix dash status
|
|
48
|
+
gemix dash stop
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Commands
|
|
52
|
+
|
|
53
|
+
| Command | Description |
|
|
54
|
+
|---------|-------------|
|
|
55
|
+
| `gemix register <username>` | Register a new Google account via OAuth |
|
|
56
|
+
| `gemix list` | List all registered accounts |
|
|
57
|
+
| `gemix list --json` | Output in JSON format |
|
|
58
|
+
| `gemix use <username>` | Switch active account |
|
|
59
|
+
| `gemix current` | Show active account |
|
|
60
|
+
| `gemix remove <username>` | Remove an account |
|
|
61
|
+
| `gemix dash` | Launch local web dashboard at `http://127.0.0.1:4577` |
|
|
62
|
+
| `gemix dash stop` | Stop running dashboard server |
|
|
63
|
+
| `gemix dash status` | Check dashboard status and PID |
|
|
64
|
+
| `gemix dash -p <port>` | Launch dashboard on a custom port |
|
|
65
|
+
|
|
66
|
+
## How It Works
|
|
67
|
+
|
|
68
|
+
### Storage
|
|
69
|
+
|
|
70
|
+
- `~/.gemix/accounts.json` - Master registry of all accounts
|
|
71
|
+
- `~/.gemix/accounts/<username>/tokens.json` - Per-account OAuth tokens
|
|
72
|
+
- `~/.gemini/oauth_creds.json` - Gemini CLI reads from here
|
|
73
|
+
|
|
74
|
+
### Account Switching
|
|
75
|
+
|
|
76
|
+
When you run `gemix use <username>`, it copies that account's tokens to `~/.gemini/oauth_creds.json` where Gemini CLI expects them.
|
|
77
|
+
|
|
78
|
+
### OAuth Flow
|
|
79
|
+
|
|
80
|
+
Uses the same OAuth Client ID as Gemini CLI (public for installed apps). Your credentials never leave your machine.
|
|
81
|
+
|
|
82
|
+
## Username Rules
|
|
83
|
+
|
|
84
|
+
- 3-30 characters long
|
|
85
|
+
- Letters, numbers, and hyphens only
|
|
86
|
+
- Must start and end with letter or number
|
|
87
|
+
- No consecutive hyphens
|
|
88
|
+
|
|
89
|
+
## Troubleshooting
|
|
90
|
+
|
|
91
|
+
| Issue | Solution |
|
|
92
|
+
|-------|----------|
|
|
93
|
+
| "Account already exists" | Run `gemix remove <username>` first |
|
|
94
|
+
| "Account not found" | Run `gemix list` to see available accounts |
|
|
95
|
+
| Browser doesn't open | Visit the URL shown in the terminal manually |
|
|
96
|
+
| Auth timed out | Try again, complete login within 5 minutes |
|
|
97
|
+
| "No active account" | Run `gemix register <username>` to add one |
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT
|
package/dist/auth/oauth.d.ts
CHANGED
|
@@ -5,6 +5,19 @@ export interface OAuthResult {
|
|
|
5
5
|
name: string;
|
|
6
6
|
picture?: string;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Build a Google OAuth URL for the web/dashboard flow.
|
|
10
|
+
* Returns the URL and the codeVerifier needed to exchange the code later.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildAuthUrl(): {
|
|
13
|
+
url: string;
|
|
14
|
+
codeVerifier: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Exchange an authorization code for tokens + user info.
|
|
18
|
+
* Used by the dashboard web flow after the user pastes the code.
|
|
19
|
+
*/
|
|
20
|
+
export declare function exchangeCode(code: string, codeVerifier: string): Promise<OAuthResult>;
|
|
8
21
|
export declare function authenticateWithGoogle(): Promise<OAuthResult>;
|
|
9
22
|
export declare function refreshTokens(tokens: TokenData): Promise<TokenData | null>;
|
|
10
23
|
//# sourceMappingURL=oauth.d.ts.map
|
package/dist/auth/oauth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AA0BxD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAepE;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,WAAW,CAAC,CAgDtB;AAED,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,WAAW,CAAC,CAuFnE;AAoCD,wBAAsB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAkChF"}
|
package/dist/auth/oauth.js
CHANGED
|
@@ -1,182 +1,207 @@
|
|
|
1
|
-
import { OAuth2Client } from 'google-auth-library';
|
|
2
|
-
import * as http from 'node:http';
|
|
3
|
-
import * as url from 'node:url';
|
|
4
|
-
import * as net from 'node:net';
|
|
5
1
|
import * as crypto from 'node:crypto';
|
|
2
|
+
import * as readline from 'node:readline';
|
|
6
3
|
import open from 'open';
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const OAUTH_CLIENT_SECRET = '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';
|
|
4
|
+
const OAUTH_CLIENT_ID = '1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com';
|
|
5
|
+
const OAUTH_CLIENT_SECRET = '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';
|
|
10
6
|
const OAUTH_SCOPE = [
|
|
11
7
|
'openid',
|
|
12
8
|
'https://www.googleapis.com/auth/cloud-platform',
|
|
13
9
|
'https://www.googleapis.com/auth/userinfo.email',
|
|
14
10
|
'https://www.googleapis.com/auth/userinfo.profile',
|
|
11
|
+
'https://www.googleapis.com/auth/cclog',
|
|
12
|
+
'https://www.googleapis.com/auth/experimentsandconfigs',
|
|
15
13
|
];
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
14
|
+
const REDIRECT_URI = 'https://antigravity.google/oauth-callback';
|
|
15
|
+
function generateCodeVerifier() {
|
|
16
|
+
return crypto.randomBytes(32).toString('base64url');
|
|
17
|
+
}
|
|
18
|
+
function generateCodeChallenge(verifier) {
|
|
19
|
+
return crypto.createHash('sha256').update(verifier).digest('base64url');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Build a Google OAuth URL for the web/dashboard flow.
|
|
23
|
+
* Returns the URL and the codeVerifier needed to exchange the code later.
|
|
24
|
+
*/
|
|
25
|
+
export function buildAuthUrl() {
|
|
26
|
+
const state = crypto.randomBytes(32).toString('hex');
|
|
27
|
+
const codeVerifier = generateCodeVerifier();
|
|
28
|
+
const params = new URLSearchParams({
|
|
29
|
+
client_id: OAUTH_CLIENT_ID,
|
|
30
|
+
response_type: 'code',
|
|
31
|
+
redirect_uri: REDIRECT_URI,
|
|
32
|
+
scope: OAUTH_SCOPE.join(' '),
|
|
33
|
+
access_type: 'offline',
|
|
34
|
+
prompt: 'consent',
|
|
35
|
+
state,
|
|
36
|
+
code_challenge: generateCodeChallenge(codeVerifier),
|
|
37
|
+
code_challenge_method: 'S256',
|
|
28
38
|
});
|
|
39
|
+
return { url: `https://accounts.google.com/o/oauth2/auth?${params}`, codeVerifier };
|
|
29
40
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Exchange an authorization code for tokens + user info.
|
|
43
|
+
* Used by the dashboard web flow after the user pastes the code.
|
|
44
|
+
*/
|
|
45
|
+
export async function exchangeCode(code, codeVerifier) {
|
|
46
|
+
const startTime = Date.now();
|
|
47
|
+
const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
|
50
|
+
body: new URLSearchParams({
|
|
51
|
+
client_id: OAUTH_CLIENT_ID,
|
|
52
|
+
client_secret: OAUTH_CLIENT_SECRET,
|
|
53
|
+
code,
|
|
54
|
+
grant_type: 'authorization_code',
|
|
55
|
+
redirect_uri: REDIRECT_URI,
|
|
56
|
+
code_verifier: codeVerifier,
|
|
57
|
+
}),
|
|
34
58
|
});
|
|
35
|
-
|
|
36
|
-
|
|
59
|
+
if (!tokenResponse.ok) {
|
|
60
|
+
throw new Error(`Token exchange failed: ${tokenResponse.status} ${await tokenResponse.text()}`);
|
|
61
|
+
}
|
|
62
|
+
const t = (await tokenResponse.json());
|
|
63
|
+
const userInfoResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
|
|
64
|
+
headers: { Authorization: `Bearer ${t.access_token}` },
|
|
65
|
+
});
|
|
66
|
+
if (!userInfoResponse.ok) {
|
|
67
|
+
throw new Error(`Failed to fetch user info: ${userInfoResponse.status}`);
|
|
68
|
+
}
|
|
69
|
+
const u = (await userInfoResponse.json());
|
|
70
|
+
return {
|
|
71
|
+
tokens: {
|
|
72
|
+
access_token: t.access_token,
|
|
73
|
+
refresh_token: t.refresh_token,
|
|
74
|
+
id_token: '',
|
|
75
|
+
scope: t.scope || OAUTH_SCOPE.join(' '),
|
|
76
|
+
token_type: t.token_type || 'Bearer',
|
|
77
|
+
expiry_date: startTime + t.expires_in * 1000,
|
|
78
|
+
},
|
|
79
|
+
email: u.email,
|
|
80
|
+
name: u.name,
|
|
81
|
+
picture: u.picture,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export async function authenticateWithGoogle() {
|
|
37
85
|
const state = crypto.randomBytes(32).toString('hex');
|
|
38
|
-
const
|
|
39
|
-
|
|
86
|
+
const codeVerifier = generateCodeVerifier();
|
|
87
|
+
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
88
|
+
const authParams = new URLSearchParams({
|
|
89
|
+
client_id: OAUTH_CLIENT_ID,
|
|
90
|
+
response_type: 'code',
|
|
91
|
+
redirect_uri: REDIRECT_URI,
|
|
92
|
+
scope: OAUTH_SCOPE.join(' '),
|
|
40
93
|
access_type: 'offline',
|
|
41
|
-
scope: OAUTH_SCOPE,
|
|
42
94
|
prompt: 'consent',
|
|
43
95
|
state,
|
|
96
|
+
code_challenge: codeChallenge,
|
|
97
|
+
code_challenge_method: 'S256',
|
|
44
98
|
});
|
|
45
|
-
const
|
|
46
|
-
const server = http.createServer(async (req, res) => {
|
|
47
|
-
try {
|
|
48
|
-
if (!req.url || req.url.indexOf('/oauth2callback') === -1) {
|
|
49
|
-
res.writeHead(HTTP_REDIRECT, { Location: SIGN_IN_FAILURE_URL });
|
|
50
|
-
res.end();
|
|
51
|
-
reject(new Error('Unexpected request: ' + req.url));
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const qs = new url.URL(req.url, `http://127.0.0.1:${port}`).searchParams;
|
|
55
|
-
if (qs.get('error')) {
|
|
56
|
-
res.writeHead(HTTP_REDIRECT, { Location: SIGN_IN_FAILURE_URL });
|
|
57
|
-
res.end();
|
|
58
|
-
reject(new Error(`Google OAuth error: ${qs.get('error')}. ${qs.get('error_description') || ''}`));
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
if (qs.get('state') !== state) {
|
|
62
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
63
|
-
res.end('<h1>State mismatch. Possible CSRF attack.</h1>');
|
|
64
|
-
reject(new Error('State mismatch. Possible CSRF attack.'));
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
if (qs.get('code')) {
|
|
68
|
-
res.writeHead(HTTP_REDIRECT, { Location: SIGN_IN_SUCCESS_URL });
|
|
69
|
-
res.end();
|
|
70
|
-
resolve({ code: qs.get('code') });
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
res.writeHead(HTTP_REDIRECT, { Location: SIGN_IN_FAILURE_URL });
|
|
74
|
-
res.end();
|
|
75
|
-
reject(new Error('No authorization code received.'));
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
catch (e) {
|
|
79
|
-
res.writeHead(HTTP_REDIRECT, { Location: SIGN_IN_FAILURE_URL });
|
|
80
|
-
res.end();
|
|
81
|
-
reject(e instanceof Error ? e : new Error(String(e)));
|
|
82
|
-
}
|
|
83
|
-
finally {
|
|
84
|
-
server.close();
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
server.listen(port, '127.0.0.1', () => { });
|
|
88
|
-
server.on('error', reject);
|
|
89
|
-
});
|
|
99
|
+
const authUrl = `https://accounts.google.com/o/oauth2/auth?${authParams.toString()}`;
|
|
90
100
|
console.log('\nOpening browser for Google authentication...\n');
|
|
101
|
+
console.log('After signing in, copy the authorization code from the redirect page.\n');
|
|
91
102
|
console.log(`If the browser doesn't open, visit:\n${authUrl}\n`);
|
|
92
103
|
const childProcess = await open(authUrl);
|
|
93
104
|
childProcess.on('error', (error) => {
|
|
94
105
|
console.error(`Failed to open browser: ${error.message}`);
|
|
95
106
|
console.log(`Please visit:\n${authUrl}\n`);
|
|
96
107
|
});
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
try {
|
|
110
|
-
const { code } = await Promise.race([
|
|
111
|
-
loginCompletePromise,
|
|
112
|
-
timeoutPromise,
|
|
113
|
-
cancellationPromise,
|
|
114
|
-
]);
|
|
115
|
-
if (timeoutId)
|
|
116
|
-
clearTimeout(timeoutId);
|
|
117
|
-
const { tokens } = await client.getToken({
|
|
108
|
+
const code = await promptForAuthCode();
|
|
109
|
+
const startTime = Date.now();
|
|
110
|
+
const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
headers: {
|
|
113
|
+
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
|
114
|
+
},
|
|
115
|
+
body: new URLSearchParams({
|
|
116
|
+
client_id: OAUTH_CLIENT_ID,
|
|
117
|
+
client_secret: OAUTH_CLIENT_SECRET,
|
|
118
118
|
code,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
id_token: tokens.id_token || '',
|
|
128
|
-
scope: tokens.scope || OAUTH_SCOPE.join(' '),
|
|
129
|
-
token_type: tokens.token_type || 'Bearer',
|
|
130
|
-
expiry_date: tokens.expiry_date || Date.now(),
|
|
131
|
-
},
|
|
132
|
-
email: userInfo.email,
|
|
133
|
-
name: userInfo.name,
|
|
134
|
-
picture: userInfo.picture,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
finally {
|
|
138
|
-
if (timeoutId)
|
|
139
|
-
clearTimeout(timeoutId);
|
|
119
|
+
grant_type: 'authorization_code',
|
|
120
|
+
redirect_uri: REDIRECT_URI,
|
|
121
|
+
code_verifier: codeVerifier,
|
|
122
|
+
}),
|
|
123
|
+
});
|
|
124
|
+
if (!tokenResponse.ok) {
|
|
125
|
+
const errorText = await tokenResponse.text();
|
|
126
|
+
throw new Error(`Token exchange failed: ${tokenResponse.status} ${errorText}`);
|
|
140
127
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (!token)
|
|
145
|
-
throw new Error('No access token available');
|
|
146
|
-
const response = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
|
|
147
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
128
|
+
const tokenPayload = (await tokenResponse.json());
|
|
129
|
+
const userInfoResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
|
|
130
|
+
headers: { Authorization: `Bearer ${tokenPayload.access_token}` },
|
|
148
131
|
});
|
|
149
|
-
if (!
|
|
150
|
-
throw new Error(`Failed to fetch user info: ${
|
|
132
|
+
if (!userInfoResponse.ok) {
|
|
133
|
+
throw new Error(`Failed to fetch user info: ${userInfoResponse.status} ${userInfoResponse.statusText}`);
|
|
151
134
|
}
|
|
152
|
-
const userInfo = (await
|
|
135
|
+
const userInfo = (await userInfoResponse.json());
|
|
153
136
|
return {
|
|
137
|
+
tokens: {
|
|
138
|
+
access_token: tokenPayload.access_token,
|
|
139
|
+
refresh_token: tokenPayload.refresh_token,
|
|
140
|
+
id_token: '',
|
|
141
|
+
scope: tokenPayload.scope || OAUTH_SCOPE.join(' '),
|
|
142
|
+
token_type: tokenPayload.token_type || 'Bearer',
|
|
143
|
+
expiry_date: startTime + tokenPayload.expires_in * 1000,
|
|
144
|
+
},
|
|
154
145
|
email: userInfo.email,
|
|
155
146
|
name: userInfo.name,
|
|
156
147
|
picture: userInfo.picture,
|
|
157
148
|
};
|
|
158
149
|
}
|
|
150
|
+
function promptForAuthCode() {
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
const rl = readline.createInterface({
|
|
153
|
+
input: process.stdin,
|
|
154
|
+
output: process.stdout,
|
|
155
|
+
});
|
|
156
|
+
const authTimeout = 5 * 60 * 1000;
|
|
157
|
+
const timeoutId = setTimeout(() => {
|
|
158
|
+
rl.close();
|
|
159
|
+
reject(new Error('Authentication timed out after 5 minutes.'));
|
|
160
|
+
}, authTimeout);
|
|
161
|
+
const onSigint = () => {
|
|
162
|
+
clearTimeout(timeoutId);
|
|
163
|
+
rl.close();
|
|
164
|
+
reject(new Error('Authentication cancelled by user.'));
|
|
165
|
+
};
|
|
166
|
+
process.on('SIGINT', onSigint);
|
|
167
|
+
rl.question('Paste the authorization code: ', (answer) => {
|
|
168
|
+
clearTimeout(timeoutId);
|
|
169
|
+
process.removeListener('SIGINT', onSigint);
|
|
170
|
+
rl.close();
|
|
171
|
+
const code = answer.trim();
|
|
172
|
+
if (!code) {
|
|
173
|
+
reject(new Error('No authorization code provided.'));
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
resolve(code);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
159
181
|
export async function refreshTokens(tokens) {
|
|
160
182
|
try {
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
183
|
+
const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {
|
|
184
|
+
method: 'POST',
|
|
185
|
+
headers: {
|
|
186
|
+
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
|
187
|
+
},
|
|
188
|
+
body: new URLSearchParams({
|
|
189
|
+
client_id: OAUTH_CLIENT_ID,
|
|
190
|
+
client_secret: OAUTH_CLIENT_SECRET,
|
|
191
|
+
refresh_token: tokens.refresh_token,
|
|
192
|
+
grant_type: 'refresh_token',
|
|
193
|
+
}),
|
|
168
194
|
});
|
|
169
|
-
|
|
170
|
-
if (!newAccessToken)
|
|
195
|
+
if (!tokenResponse.ok)
|
|
171
196
|
return null;
|
|
172
|
-
const
|
|
197
|
+
const tokenPayload = (await tokenResponse.json());
|
|
173
198
|
return {
|
|
174
|
-
access_token:
|
|
175
|
-
refresh_token:
|
|
176
|
-
id_token:
|
|
199
|
+
access_token: tokenPayload.access_token,
|
|
200
|
+
refresh_token: tokens.refresh_token,
|
|
201
|
+
id_token: tokens.id_token || '',
|
|
177
202
|
scope: tokens.scope,
|
|
178
|
-
token_type:
|
|
179
|
-
expiry_date:
|
|
203
|
+
token_type: tokenPayload.token_type || 'Bearer',
|
|
204
|
+
expiry_date: Date.now() + tokenPayload.expires_in * 1000,
|
|
180
205
|
};
|
|
181
206
|
}
|
|
182
207
|
catch {
|
package/dist/auth/oauth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/current.js
CHANGED
|
@@ -5,8 +5,8 @@ export async function currentCommand() {
|
|
|
5
5
|
const accounts = await loadAccounts();
|
|
6
6
|
if (!activeUsername || !accounts.accounts[activeUsername]) {
|
|
7
7
|
console.log('No active account.');
|
|
8
|
-
console.log('Use "
|
|
9
|
-
console.log('Use "
|
|
8
|
+
console.log('Use "gemix list" to see available accounts.');
|
|
9
|
+
console.log('Use "gemix use <username>" to switch accounts.');
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
const account = accounts.accounts[activeUsername];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"current.js","sourceRoot":"","sources":["../../src/commands/current.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,cAAc,GAAG,MAAM,iBAAiB,EAAE,CAAC;IACjD,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IAEtC,IAAI,CAAC,cAAc,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"current.js","sourceRoot":"","sources":["../../src/commands/current.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,cAAc,GAAG,MAAM,iBAAiB,EAAE,CAAC;IACjD,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IAEtC,IAAI,CAAC,cAAc,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;QAC3D,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;QAC9D,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,mBAAmB,cAAc,KAAK,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC;IACpE,OAAO,CAAC,GAAG,CAAC,WAAW,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dash.d.ts","sourceRoot":"","sources":["../../src/commands/dash.ts"],"names":[],"mappings":"AA8DA,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CA0Ef"}
|