gemix 0.0.1
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/auth/oauth.d.ts +10 -0
- package/dist/auth/oauth.d.ts.map +1 -0
- package/dist/auth/oauth.js +186 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/commands/current.d.ts +2 -0
- package/dist/commands/current.d.ts.map +1 -0
- package/dist/commands/current.js +16 -0
- package/dist/commands/current.js.map +1 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +19 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/register.d.ts +2 -0
- package/dist/commands/register.d.ts.map +1 -0
- package/dist/commands/register.js +43 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/commands/remove.d.ts +2 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/remove.js +31 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/use.d.ts +2 -0
- package/dist/commands/use.d.ts.map +1 -0
- package/dist/commands/use.js +23 -0
- package/dist/commands/use.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/storage/accounts.d.ts +46 -0
- package/dist/storage/accounts.d.ts.map +1 -0
- package/dist/storage/accounts.js +210 -0
- package/dist/storage/accounts.js.map +1 -0
- package/dist/storage/paths.d.ts +12 -0
- package/dist/storage/paths.d.ts.map +1 -0
- package/dist/storage/paths.js +45 -0
- package/dist/storage/paths.js.map +1 -0
- package/dist/utils/table.d.ts +8 -0
- package/dist/utils/table.d.ts.map +1 -0
- package/dist/utils/table.js +37 -0
- package/dist/utils/table.js.map +1 -0
- package/dist/utils/validation.d.ts +5 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +22 -0
- package/dist/utils/validation.js.map +1 -0
- package/doc/README.md +90 -0
- package/package.json +30 -0
- package/src/auth/oauth.ts +231 -0
- package/src/commands/current.ts +18 -0
- package/src/commands/list.ts +20 -0
- package/src/commands/register.ts +53 -0
- package/src/commands/remove.ts +37 -0
- package/src/commands/use.ts +25 -0
- package/src/index.ts +59 -0
- package/src/storage/accounts.ts +285 -0
- package/src/storage/paths.ts +56 -0
- package/src/utils/table.ts +58 -0
- package/src/utils/validation.ts +27 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import { getAccountsFilePath, getActiveFilePath, getAccountTokensPath, getAccountDir, ensureGemxDir, ensureAccountDir, getGeminiOAuthCredsPath, getGeminiGoogleAccountsPath, getAntigravityOAuthTokenPath, } from './paths.js';
|
|
4
|
+
import { refreshTokens } from '../auth/oauth.js';
|
|
5
|
+
const DEFAULT_LIMITS = {
|
|
6
|
+
requestsPerDay: 500,
|
|
7
|
+
tokensPerDay: 1000000,
|
|
8
|
+
};
|
|
9
|
+
export async function loadAccounts() {
|
|
10
|
+
await ensureGemxDir();
|
|
11
|
+
const filePath = getAccountsFilePath();
|
|
12
|
+
try {
|
|
13
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
14
|
+
return JSON.parse(content);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return { accounts: {}, active: null };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export async function saveAccounts(data) {
|
|
21
|
+
await ensureGemxDir();
|
|
22
|
+
const filePath = getAccountsFilePath();
|
|
23
|
+
await fs.writeFile(filePath, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
24
|
+
}
|
|
25
|
+
export async function loadTokens(username) {
|
|
26
|
+
const tokensPath = getAccountTokensPath(username);
|
|
27
|
+
try {
|
|
28
|
+
const content = await fs.readFile(tokensPath, 'utf-8');
|
|
29
|
+
return JSON.parse(content);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export async function saveTokens(username, tokens) {
|
|
36
|
+
await ensureAccountDir(username);
|
|
37
|
+
const tokensPath = getAccountTokensPath(username);
|
|
38
|
+
await fs.writeFile(tokensPath, JSON.stringify(tokens, null, 2), { mode: 0o600 });
|
|
39
|
+
}
|
|
40
|
+
export async function loadGoogleAccounts() {
|
|
41
|
+
const filePath = getGeminiGoogleAccountsPath();
|
|
42
|
+
try {
|
|
43
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
44
|
+
return JSON.parse(content);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return { active: '', old: [] };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function saveGoogleAccounts(data) {
|
|
51
|
+
const filePath = getGeminiGoogleAccountsPath();
|
|
52
|
+
const dir = filePath.substring(0, filePath.lastIndexOf('/'));
|
|
53
|
+
await fs.mkdir(dir, { recursive: true });
|
|
54
|
+
await fs.writeFile(filePath, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
55
|
+
}
|
|
56
|
+
export async function updateGoogleAccountsActive(email) {
|
|
57
|
+
const data = await loadGoogleAccounts();
|
|
58
|
+
if (data.active && data.active !== email) {
|
|
59
|
+
data.old = [...(data.old || []), data.active];
|
|
60
|
+
}
|
|
61
|
+
data.active = email;
|
|
62
|
+
await saveGoogleAccounts(data);
|
|
63
|
+
}
|
|
64
|
+
export async function copyTokensToAntigravity(tokens) {
|
|
65
|
+
try {
|
|
66
|
+
const antigravityPath = getAntigravityOAuthTokenPath();
|
|
67
|
+
const dir = antigravityPath.substring(0, antigravityPath.lastIndexOf('/'));
|
|
68
|
+
await fs.mkdir(dir, { recursive: true });
|
|
69
|
+
const antigravityData = {
|
|
70
|
+
token: {
|
|
71
|
+
access_token: tokens.access_token,
|
|
72
|
+
token_type: tokens.token_type,
|
|
73
|
+
refresh_token: tokens.refresh_token,
|
|
74
|
+
expiry: new Date(tokens.expiry_date).toISOString(),
|
|
75
|
+
},
|
|
76
|
+
auth_method: 'consumer',
|
|
77
|
+
};
|
|
78
|
+
await fs.writeFile(antigravityPath, JSON.stringify(antigravityData, null, 2), { mode: 0o600 });
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export async function copyTokensToKeychain(tokens) {
|
|
86
|
+
try {
|
|
87
|
+
const keychainData = {
|
|
88
|
+
token: {
|
|
89
|
+
access_token: tokens.access_token,
|
|
90
|
+
token_type: tokens.token_type,
|
|
91
|
+
refresh_token: tokens.refresh_token,
|
|
92
|
+
expiry: new Date(tokens.expiry_date).toISOString(),
|
|
93
|
+
},
|
|
94
|
+
auth_method: 'consumer',
|
|
95
|
+
};
|
|
96
|
+
const password = `go-keyring-base64:${Buffer.from(JSON.stringify(keychainData)).toString('base64')}`;
|
|
97
|
+
try {
|
|
98
|
+
execSync(`security delete-generic-password -s "gemini" -a "antigravity" 2>/dev/null`, { stdio: 'ignore' });
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Ignore if entry doesn't exist
|
|
102
|
+
}
|
|
103
|
+
execSync(`security add-generic-password -s "gemini" -a "antigravity" -w "${password}" -U`, { stdio: 'ignore' });
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export async function getActiveUsername() {
|
|
111
|
+
const activePath = getActiveFilePath();
|
|
112
|
+
try {
|
|
113
|
+
return await fs.readFile(activePath, 'utf-8');
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export async function setActiveUsername(username) {
|
|
120
|
+
await ensureGemxDir();
|
|
121
|
+
const activePath = getActiveFilePath();
|
|
122
|
+
await fs.writeFile(activePath, username, { mode: 0o600 });
|
|
123
|
+
}
|
|
124
|
+
export async function copyTokensToGemini(username) {
|
|
125
|
+
const tokens = await loadTokens(username);
|
|
126
|
+
if (!tokens)
|
|
127
|
+
return false;
|
|
128
|
+
const geminiPath = getGeminiOAuthCredsPath();
|
|
129
|
+
const geminiDir = geminiPath.substring(0, geminiPath.lastIndexOf('/'));
|
|
130
|
+
await fs.mkdir(geminiDir, { recursive: true });
|
|
131
|
+
await fs.writeFile(geminiPath, JSON.stringify(buildGeminiCredsObject(tokens), null, 2), { mode: 0o600 });
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
export async function addAccount(username, email, name, picture, tokens, limits = DEFAULT_LIMITS) {
|
|
135
|
+
const accounts = await loadAccounts();
|
|
136
|
+
accounts.accounts[username] = {
|
|
137
|
+
email,
|
|
138
|
+
name,
|
|
139
|
+
picture,
|
|
140
|
+
limits,
|
|
141
|
+
created: new Date().toISOString(),
|
|
142
|
+
lastUsed: null,
|
|
143
|
+
};
|
|
144
|
+
await saveTokens(username, tokens);
|
|
145
|
+
await saveAccounts(accounts);
|
|
146
|
+
if (!accounts.active) {
|
|
147
|
+
await setActiveUsername(username);
|
|
148
|
+
accounts.active = username;
|
|
149
|
+
await saveAccounts(accounts);
|
|
150
|
+
await updateGoogleAccountsActive(email);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export async function removeAccount(username) {
|
|
154
|
+
const accounts = await loadAccounts();
|
|
155
|
+
if (!accounts.accounts[username])
|
|
156
|
+
return false;
|
|
157
|
+
delete accounts.accounts[username];
|
|
158
|
+
if (accounts.active === username) {
|
|
159
|
+
const remaining = Object.keys(accounts.accounts);
|
|
160
|
+
accounts.active = remaining.length > 0 ? remaining[0] : null;
|
|
161
|
+
}
|
|
162
|
+
await fs.rm(getAccountDir(username), { recursive: true, force: true });
|
|
163
|
+
await saveAccounts(accounts);
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
export function buildGeminiCredsObject(tokens) {
|
|
167
|
+
const creds = {
|
|
168
|
+
access_token: tokens.access_token,
|
|
169
|
+
refresh_token: tokens.refresh_token,
|
|
170
|
+
scope: tokens.scope,
|
|
171
|
+
token_type: tokens.token_type,
|
|
172
|
+
expiry_date: tokens.expiry_date,
|
|
173
|
+
};
|
|
174
|
+
if (tokens.id_token) {
|
|
175
|
+
creds.id_token = tokens.id_token;
|
|
176
|
+
}
|
|
177
|
+
return creds;
|
|
178
|
+
}
|
|
179
|
+
export async function switchAccount(username) {
|
|
180
|
+
const accounts = await loadAccounts();
|
|
181
|
+
if (!accounts.accounts[username])
|
|
182
|
+
return false;
|
|
183
|
+
let tokens = await loadTokens(username);
|
|
184
|
+
if (!tokens)
|
|
185
|
+
return false;
|
|
186
|
+
// Refresh tokens if expired
|
|
187
|
+
const now = Date.now();
|
|
188
|
+
const fiveMinutes = 5 * 60 * 1000;
|
|
189
|
+
if (tokens.expiry_date && tokens.expiry_date - now < fiveMinutes) {
|
|
190
|
+
const refreshed = await refreshTokens(tokens);
|
|
191
|
+
if (refreshed) {
|
|
192
|
+
tokens = refreshed;
|
|
193
|
+
await saveTokens(username, tokens);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
await fs.mkdir(getGeminiOAuthCredsPath().substring(0, getGeminiOAuthCredsPath().lastIndexOf('/')), { recursive: true });
|
|
197
|
+
await fs.writeFile(getGeminiOAuthCredsPath(), JSON.stringify(buildGeminiCredsObject(tokens), null, 2), { mode: 0o600 });
|
|
198
|
+
await copyTokensToAntigravity(tokens);
|
|
199
|
+
await copyTokensToKeychain(tokens);
|
|
200
|
+
await updateGoogleAccountsActive(accounts.accounts[username].email);
|
|
201
|
+
accounts.active = username;
|
|
202
|
+
accounts.accounts[username].lastUsed = new Date().toISOString();
|
|
203
|
+
await saveAccounts(accounts);
|
|
204
|
+
await setActiveUsername(username);
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
export function validateUsername(username) {
|
|
208
|
+
return /^[a-zA-Z0-9]([a-zA-Z0-9-]{1,28}[a-zA-Z0-9])?$/.test(username);
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=accounts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/storage/accounts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function getGemxDir(): string;
|
|
2
|
+
export declare function getAccountsFilePath(): string;
|
|
3
|
+
export declare function getActiveFilePath(): string;
|
|
4
|
+
export declare function getAccountsDir(): string;
|
|
5
|
+
export declare function getAccountDir(username: string): string;
|
|
6
|
+
export declare function getAccountTokensPath(username: string): string;
|
|
7
|
+
export declare function getGeminiOAuthCredsPath(): string;
|
|
8
|
+
export declare function getGeminiGoogleAccountsPath(): string;
|
|
9
|
+
export declare function getAntigravityOAuthTokenPath(): string;
|
|
10
|
+
export declare function ensureGemxDir(): Promise<void>;
|
|
11
|
+
export declare function ensureAccountDir(username: string): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/storage/paths.ts"],"names":[],"mappings":"AAYA,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAED,wBAAgB,2BAA2B,IAAI,MAAM,CAEpD;AAED,wBAAgB,4BAA4B,IAAI,MAAM,CAErD;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAGnD;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import { promises as fs } from 'node:fs';
|
|
4
|
+
const GEMX_DIR = '.gemx';
|
|
5
|
+
const GEMINI_DIR = '.gemini';
|
|
6
|
+
const ACCOUNTS_FILE = 'accounts.json';
|
|
7
|
+
const ACTIVE_FILE = 'active';
|
|
8
|
+
const ACCOUNTS_DIR = 'accounts';
|
|
9
|
+
const OAUTH_CREDS_FILE = 'oauth_creds.json';
|
|
10
|
+
const GOOGLE_ACCOUNTS_FILE = 'google_accounts.json';
|
|
11
|
+
export function getGemxDir() {
|
|
12
|
+
return path.join(os.homedir(), GEMX_DIR);
|
|
13
|
+
}
|
|
14
|
+
export function getAccountsFilePath() {
|
|
15
|
+
return path.join(getGemxDir(), ACCOUNTS_FILE);
|
|
16
|
+
}
|
|
17
|
+
export function getActiveFilePath() {
|
|
18
|
+
return path.join(getGemxDir(), ACTIVE_FILE);
|
|
19
|
+
}
|
|
20
|
+
export function getAccountsDir() {
|
|
21
|
+
return path.join(getGemxDir(), ACCOUNTS_DIR);
|
|
22
|
+
}
|
|
23
|
+
export function getAccountDir(username) {
|
|
24
|
+
return path.join(getAccountsDir(), username);
|
|
25
|
+
}
|
|
26
|
+
export function getAccountTokensPath(username) {
|
|
27
|
+
return path.join(getAccountDir(username), 'tokens.json');
|
|
28
|
+
}
|
|
29
|
+
export function getGeminiOAuthCredsPath() {
|
|
30
|
+
return path.join(os.homedir(), GEMINI_DIR, OAUTH_CREDS_FILE);
|
|
31
|
+
}
|
|
32
|
+
export function getGeminiGoogleAccountsPath() {
|
|
33
|
+
return path.join(os.homedir(), GEMINI_DIR, GOOGLE_ACCOUNTS_FILE);
|
|
34
|
+
}
|
|
35
|
+
export function getAntigravityOAuthTokenPath() {
|
|
36
|
+
return path.join(os.homedir(), GEMINI_DIR, 'antigravity-cli', 'antigravity-oauth-token');
|
|
37
|
+
}
|
|
38
|
+
export async function ensureGemxDir() {
|
|
39
|
+
await fs.mkdir(getGemxDir(), { recursive: true });
|
|
40
|
+
await fs.mkdir(getAccountsDir(), { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
export async function ensureAccountDir(username) {
|
|
43
|
+
await fs.mkdir(getAccountDir(username), { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/storage/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,QAAQ,GAAG,OAAO,CAAC;AACzB,MAAM,UAAU,GAAG,SAAS,CAAC;AAC7B,MAAM,aAAa,GAAG,eAAe,CAAC;AACtC,MAAM,WAAW,GAAG,QAAQ,CAAC;AAC7B,MAAM,YAAY,GAAG,UAAU,CAAC;AAChC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAC5C,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAEpD,MAAM,UAAU,UAAU;IACxB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,aAAa,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,WAAW,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,YAAY,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,2BAA2B;IACzC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,oBAAoB,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,4BAA4B;IAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,iBAAiB,EAAE,yBAAyB,CAAC,CAAC;AAC3F,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,QAAgB;IACrD,MAAM,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AccountInfo } from '../storage/accounts.js';
|
|
2
|
+
export declare function formatAccountTable(accounts: Record<string, AccountInfo>, activeUsername: string | null): string;
|
|
3
|
+
export declare function formatAccountTableJson(accounts: Record<string, AccountInfo>, activeUsername: string | null): object;
|
|
4
|
+
export declare function formatLimits(limits: {
|
|
5
|
+
requestsPerDay: number;
|
|
6
|
+
tokensPerDay: number;
|
|
7
|
+
}): string;
|
|
8
|
+
//# sourceMappingURL=table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/utils/table.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAS1D,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACrC,cAAc,EAAE,MAAM,GAAG,IAAI,GAC5B,MAAM,CAkBR;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACrC,cAAc,EAAE,MAAM,GAAG,IAAI,GAC5B,MAAM,CAaR;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAM7F"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import Table from 'cli-table3';
|
|
2
|
+
export function formatAccountTable(accounts, activeUsername) {
|
|
3
|
+
const table = new Table({
|
|
4
|
+
head: ['USERNAME', 'EMAIL', 'LIMITS', 'STATUS'],
|
|
5
|
+
style: { head: ['cyan'] },
|
|
6
|
+
});
|
|
7
|
+
const sortedUsernames = Object.keys(accounts).sort();
|
|
8
|
+
for (const username of sortedUsernames) {
|
|
9
|
+
const account = accounts[username];
|
|
10
|
+
const isActive = username === activeUsername;
|
|
11
|
+
const limits = `${account.limits.requestsPerDay} req/day`;
|
|
12
|
+
const status = isActive ? '● active' : '○ inactive';
|
|
13
|
+
table.push([username, account.email, limits, status]);
|
|
14
|
+
}
|
|
15
|
+
return table.toString();
|
|
16
|
+
}
|
|
17
|
+
export function formatAccountTableJson(accounts, activeUsername) {
|
|
18
|
+
const sortedUsernames = Object.keys(accounts).sort();
|
|
19
|
+
const result = sortedUsernames.map((username) => ({
|
|
20
|
+
username,
|
|
21
|
+
email: accounts[username].email,
|
|
22
|
+
name: accounts[username].name,
|
|
23
|
+
limits: accounts[username].limits,
|
|
24
|
+
active: username === activeUsername,
|
|
25
|
+
created: accounts[username].created,
|
|
26
|
+
lastUsed: accounts[username].lastUsed,
|
|
27
|
+
}));
|
|
28
|
+
return { accounts: result };
|
|
29
|
+
}
|
|
30
|
+
export function formatLimits(limits) {
|
|
31
|
+
const reqStr = `${limits.requestsPerDay} requests/day`;
|
|
32
|
+
const tokenStr = limits.tokensPerDay >= 1000000
|
|
33
|
+
? `${(limits.tokensPerDay / 1000000).toFixed(0)}M tokens/day`
|
|
34
|
+
: `${limits.tokensPerDay.toLocaleString()} tokens/day`;
|
|
35
|
+
return `${reqStr}, ${tokenStr}`;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.js","sourceRoot":"","sources":["../../src/utils/table.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAU/B,MAAM,UAAU,kBAAkB,CAChC,QAAqC,EACrC,cAA6B;IAE7B,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;QACtB,IAAI,EAAE,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC;QAC/C,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE;KAC1B,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;IAErD,KAAK,MAAM,QAAQ,IAAI,eAAe,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,QAAQ,KAAK,cAAc,CAAC;QAC7C,MAAM,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,UAAU,CAAC;QAC1D,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC;QAEpD,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,QAAqC,EACrC,cAA6B;IAE7B,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;IACrD,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAChD,QAAQ;QACR,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,KAAK;QAC/B,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI;QAC7B,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,MAAM;QACjC,MAAM,EAAE,QAAQ,KAAK,cAAc;QACnC,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO;QACnC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,QAAQ;KACtC,CAAC,CAAC,CAAC;IAEJ,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAwD;IACnF,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,cAAc,eAAe,CAAC;IACvD,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,IAAI,OAAO;QAC7C,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,GAAG,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc;QAC7D,CAAC,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,cAAc,EAAE,aAAa,CAAC;IACzD,OAAO,GAAG,MAAM,KAAK,QAAQ,EAAE,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CA0BrF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function validateUsername(username) {
|
|
2
|
+
if (!username || username.length < 3) {
|
|
3
|
+
return { valid: false, error: 'Username must be at least 3 characters' };
|
|
4
|
+
}
|
|
5
|
+
if (username.length > 30) {
|
|
6
|
+
return { valid: false, error: 'Username must be at most 30 characters' };
|
|
7
|
+
}
|
|
8
|
+
if (!/^[a-zA-Z0-9]/.test(username)) {
|
|
9
|
+
return { valid: false, error: 'Username must start with a letter or number' };
|
|
10
|
+
}
|
|
11
|
+
if (!/[a-zA-Z0-9]$/.test(username)) {
|
|
12
|
+
return { valid: false, error: 'Username must end with a letter or number' };
|
|
13
|
+
}
|
|
14
|
+
if (!/^[a-zA-Z0-9-]+$/.test(username)) {
|
|
15
|
+
return { valid: false, error: 'Username can only contain letters, numbers, and hyphens' };
|
|
16
|
+
}
|
|
17
|
+
if (username.includes('--')) {
|
|
18
|
+
return { valid: false, error: 'Username cannot contain consecutive hyphens' };
|
|
19
|
+
}
|
|
20
|
+
return { valid: true };
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,wCAAwC,EAAE,CAAC;IAC3E,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,wCAAwC,EAAE,CAAC;IAC3E,CAAC;IAED,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,6CAA6C,EAAE,CAAC;IAChF,CAAC;IAED,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,2CAA2C,EAAE,CAAC;IAC9E,CAAC;IAED,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,yDAAyD,EAAE,CAAC;IAC5F,CAAC;IAED,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,6CAA6C,EAAE,CAAC;IAChF,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC"}
|
package/doc/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# gemx - 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 gemx
|
|
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
|
+
gemx register personal
|
|
27
|
+
|
|
28
|
+
# Register another account
|
|
29
|
+
gemx register work
|
|
30
|
+
|
|
31
|
+
# List all accounts
|
|
32
|
+
gemx list
|
|
33
|
+
|
|
34
|
+
# Switch accounts
|
|
35
|
+
gemx use work
|
|
36
|
+
|
|
37
|
+
# Check active account
|
|
38
|
+
gemx current
|
|
39
|
+
|
|
40
|
+
# Remove an account
|
|
41
|
+
gemx remove personal
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Commands
|
|
45
|
+
|
|
46
|
+
| Command | Description |
|
|
47
|
+
|---------|-------------|
|
|
48
|
+
| `gemx register <username>` | Register a new Google account via OAuth |
|
|
49
|
+
| `gemx list` | List all registered accounts |
|
|
50
|
+
| `gemx list --json` | Output in JSON format |
|
|
51
|
+
| `gemx use <username>` | Switch active account |
|
|
52
|
+
| `gemx current` | Show active account |
|
|
53
|
+
| `gemx remove <username>` | Remove an account |
|
|
54
|
+
|
|
55
|
+
## How It Works
|
|
56
|
+
|
|
57
|
+
### Storage
|
|
58
|
+
|
|
59
|
+
- `~/.gemx/accounts.json` - Master registry of all accounts
|
|
60
|
+
- `~/.gemx/accounts/<username>/tokens.json` - Per-account OAuth tokens
|
|
61
|
+
- `~/.gemini/oauth_creds.json` - Gemini CLI reads from here
|
|
62
|
+
|
|
63
|
+
### Account Switching
|
|
64
|
+
|
|
65
|
+
When you run `gemx use <username>`, it copies that account's tokens to `~/.gemini/oauth_creds.json` where Gemini CLI expects them.
|
|
66
|
+
|
|
67
|
+
### OAuth Flow
|
|
68
|
+
|
|
69
|
+
Uses the same OAuth Client ID as Gemini CLI (public for installed apps). Your credentials never leave your machine.
|
|
70
|
+
|
|
71
|
+
## Username Rules
|
|
72
|
+
|
|
73
|
+
- 3-30 characters long
|
|
74
|
+
- Letters, numbers, and hyphens only
|
|
75
|
+
- Must start and end with letter or number
|
|
76
|
+
- No consecutive hyphens
|
|
77
|
+
|
|
78
|
+
## Troubleshooting
|
|
79
|
+
|
|
80
|
+
| Issue | Solution |
|
|
81
|
+
|-------|----------|
|
|
82
|
+
| "Account already exists" | Run `gemx remove <username>` first |
|
|
83
|
+
| "Account not found" | Run `gemx list` to see available accounts |
|
|
84
|
+
| Browser doesn't open | Visit the URL shown in the terminal manually |
|
|
85
|
+
| Auth timed out | Try again, complete login within 5 minutes |
|
|
86
|
+
| "No active account" | Run `gemx register <username>` to add one |
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gemix",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Gemini multi-account CLI - manage multiple Google accounts for Gemini API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"gemx": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"link": "npm run build && npm link"
|
|
15
|
+
},
|
|
16
|
+
"keywords": ["gemini", "google", "oauth", "multi-account", "cli"],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"commander": "^12.1.0",
|
|
21
|
+
"google-auth-library": "^9.15.1",
|
|
22
|
+
"open": "^10.1.0",
|
|
23
|
+
"cli-table3": "^0.6.5"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.10.0",
|
|
27
|
+
"tsx": "^4.19.0",
|
|
28
|
+
"typescript": "^5.7.0"
|
|
29
|
+
}
|
|
30
|
+
}
|