codemaxxing 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/auth-cli.d.ts +16 -0
- package/dist/auth-cli.js +265 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +27 -0
- package/dist/index.js +24 -0
- package/dist/utils/auth.d.ts +59 -0
- package/dist/utils/auth.js +495 -0
- package/package.json +3 -2
- package/src/auth-cli.ts +286 -0
- package/src/config.ts +33 -0
- package/src/index.tsx +24 -0
- package/src/utils/auth.ts +606 -0
package/src/auth-cli.ts
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* codemaxxing auth CLI
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* codemaxxing login — Interactive auth setup
|
|
8
|
+
* codemaxxing auth list — List saved credentials
|
|
9
|
+
* codemaxxing auth remove <name> — Remove a credential
|
|
10
|
+
* codemaxxing auth openrouter — Start OpenRouter OAuth
|
|
11
|
+
* codemaxxing auth anthropic — Get Anthropic via Claude Code
|
|
12
|
+
* codemaxxing auth openai — Import Codex CLI credentials
|
|
13
|
+
* codemaxxing auth qwen — Import Qwen CLI credentials
|
|
14
|
+
* codemaxxing auth copilot — GitHub Copilot device flow
|
|
15
|
+
* codemaxxing auth api-key <name> <key> — Save API key directly
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
PROVIDERS,
|
|
20
|
+
getCredentials,
|
|
21
|
+
removeCredential,
|
|
22
|
+
openRouterOAuth,
|
|
23
|
+
anthropicSetupToken,
|
|
24
|
+
importCodexToken,
|
|
25
|
+
importQwenToken,
|
|
26
|
+
copilotDeviceFlow,
|
|
27
|
+
saveApiKey,
|
|
28
|
+
detectAvailableAuth,
|
|
29
|
+
} from "./utils/auth.js";
|
|
30
|
+
|
|
31
|
+
const command = process.argv[2] ?? "login";
|
|
32
|
+
|
|
33
|
+
async function main() {
|
|
34
|
+
switch (command) {
|
|
35
|
+
case "login": {
|
|
36
|
+
console.log("\n💪 Codemaxxing Authentication\n");
|
|
37
|
+
console.log("Available providers:\n");
|
|
38
|
+
|
|
39
|
+
PROVIDERS.forEach((p, i) => {
|
|
40
|
+
const methods = p.methods.filter((m) => m !== "none").join(", ");
|
|
41
|
+
console.log(` ${i + 1}. ${p.name}`);
|
|
42
|
+
console.log(` ${p.description}`);
|
|
43
|
+
console.log(` Methods: ${methods}\n`);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
console.log("Detected on this machine:");
|
|
47
|
+
const detected = detectAvailableAuth();
|
|
48
|
+
if (detected.length === 0) {
|
|
49
|
+
console.log(" — No existing CLI credentials found\n");
|
|
50
|
+
} else {
|
|
51
|
+
detected.forEach((d) => {
|
|
52
|
+
console.log(` ⚡ ${d.provider} — ${d.description}`);
|
|
53
|
+
});
|
|
54
|
+
console.log("");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const readline = await import("readline");
|
|
58
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
59
|
+
|
|
60
|
+
const ask = (q: string): Promise<string> =>
|
|
61
|
+
new Promise((resolve) => rl.question(q, resolve));
|
|
62
|
+
|
|
63
|
+
const choice = await ask("Select a provider (1-" + PROVIDERS.length + ") or name: ");
|
|
64
|
+
const providerId = PROVIDERS[parseInt(choice) - 1]?.id ?? choice.toLowerCase();
|
|
65
|
+
const provider = PROVIDERS.find((p) => p.id === providerId);
|
|
66
|
+
|
|
67
|
+
if (!provider) {
|
|
68
|
+
console.log(`Unknown provider: ${providerId}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(`\nSetting up ${provider.name}...\n`);
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
if (providerId === "openrouter") {
|
|
76
|
+
const cred = await openRouterOAuth((msg) => console.log(` ${msg}`));
|
|
77
|
+
console.log(`\n✅ OpenRouter authenticated! (${cred.label})`);
|
|
78
|
+
} else if (providerId === "anthropic") {
|
|
79
|
+
if (provider.methods.includes("setup-token")) {
|
|
80
|
+
const cred = await anthropicSetupToken((msg) => console.log(` ${msg}`));
|
|
81
|
+
console.log(`\n✅ Anthropic authenticated! (${cred.label})`);
|
|
82
|
+
} else {
|
|
83
|
+
const apiKey = await ask("Enter your Anthropic API key: ");
|
|
84
|
+
const cred = saveApiKey(providerId, apiKey);
|
|
85
|
+
console.log(`\n✅ Saved API key for ${provider.name}`);
|
|
86
|
+
}
|
|
87
|
+
} else if (providerId === "openai") {
|
|
88
|
+
const imported = importCodexToken((msg) => console.log(` ${msg}`));
|
|
89
|
+
if (imported) {
|
|
90
|
+
console.log(`\n✅ Imported Codex credentials! (${imported.label})`);
|
|
91
|
+
} else {
|
|
92
|
+
const apiKey = await ask("Enter your OpenAI API key: ");
|
|
93
|
+
const cred = saveApiKey(providerId, apiKey);
|
|
94
|
+
console.log(`\n✅ Saved API key for ${provider.name}`);
|
|
95
|
+
}
|
|
96
|
+
} else if (providerId === "qwen") {
|
|
97
|
+
const imported = importQwenToken((msg) => console.log(` ${msg}`));
|
|
98
|
+
if (imported) {
|
|
99
|
+
console.log(`\n✅ Imported Qwen credentials! (${imported.label})`);
|
|
100
|
+
} else {
|
|
101
|
+
const apiKey = await ask("Enter your Qwen/DashScope API key: ");
|
|
102
|
+
const cred = saveApiKey(providerId, apiKey);
|
|
103
|
+
console.log(`\n✅ Saved API key for ${provider.name}`);
|
|
104
|
+
}
|
|
105
|
+
} else if (providerId === "copilot") {
|
|
106
|
+
const cred = await copilotDeviceFlow((msg) => console.log(` ${msg}`));
|
|
107
|
+
console.log(`\n✅ GitHub Copilot authenticated!`);
|
|
108
|
+
} else if (providerId === "custom") {
|
|
109
|
+
const baseUrl = await ask("Enter the base URL: ");
|
|
110
|
+
const apiKey = await ask("Enter your API key: ");
|
|
111
|
+
const cred = saveApiKey(providerId, apiKey, baseUrl, "Custom provider");
|
|
112
|
+
console.log(`\n✅ Saved custom provider`);
|
|
113
|
+
} else {
|
|
114
|
+
const apiKey = await ask(`Enter your ${provider.name} API key (${provider.consoleUrl}): `);
|
|
115
|
+
const cred = saveApiKey(providerId, apiKey);
|
|
116
|
+
console.log(`\n✅ Saved API key for ${provider.name}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.log("\nRun 'codemaxxing' to start coding!");
|
|
120
|
+
} catch (err: any) {
|
|
121
|
+
console.error(`\n❌ Error: ${err.message}`);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
} finally {
|
|
124
|
+
rl.close();
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
case "list":
|
|
130
|
+
case "ls": {
|
|
131
|
+
const creds = getCredentials();
|
|
132
|
+
console.log("\n💪 Saved Credentials\n");
|
|
133
|
+
|
|
134
|
+
if (creds.length === 0) {
|
|
135
|
+
console.log(" No credentials saved.\n");
|
|
136
|
+
console.log(" Run 'codemaxxing login' to set up authentication.\n");
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
creds.forEach((c) => {
|
|
141
|
+
console.log(` ${c.provider}`);
|
|
142
|
+
console.log(` Method: ${c.method}`);
|
|
143
|
+
console.log(` Label: ${c.label ?? "—"}`);
|
|
144
|
+
const maskedKey = c.apiKey.length > 12
|
|
145
|
+
? c.apiKey.slice(0, 4) + "•".repeat(8) + c.apiKey.slice(-4)
|
|
146
|
+
: "••••••••";
|
|
147
|
+
console.log(` Key: ${maskedKey}`);
|
|
148
|
+
console.log(` Base: ${c.baseUrl}`);
|
|
149
|
+
console.log("");
|
|
150
|
+
});
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
case "remove":
|
|
155
|
+
case "rm":
|
|
156
|
+
case "delete": {
|
|
157
|
+
const target = process.argv[3];
|
|
158
|
+
if (!target) {
|
|
159
|
+
console.log("Usage: codemaxxing auth remove <provider-name>");
|
|
160
|
+
console.log("\nSaved providers:");
|
|
161
|
+
getCredentials().forEach((c) => console.log(` ${c.provider}`));
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const removed = removeCredential(target);
|
|
166
|
+
if (removed) {
|
|
167
|
+
console.log(`✅ Removed ${target}`);
|
|
168
|
+
} else {
|
|
169
|
+
console.log(`❌ No credential found for: ${target}`);
|
|
170
|
+
console.log("\nSaved providers:");
|
|
171
|
+
getCredentials().forEach((c) => console.log(` ${c.provider}`));
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
case "openrouter": {
|
|
178
|
+
console.log("Starting OpenRouter OAuth flow...\n");
|
|
179
|
+
try {
|
|
180
|
+
const cred = await openRouterOAuth((msg) => console.log(` ${msg}`));
|
|
181
|
+
console.log(`\n✅ OpenRouter authenticated!`);
|
|
182
|
+
} catch (err: any) {
|
|
183
|
+
console.error(`\n❌ ${err.message}`);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
case "anthropic": {
|
|
190
|
+
console.log("Starting Anthropic setup-token flow...\n");
|
|
191
|
+
try {
|
|
192
|
+
const cred = await anthropicSetupToken((msg) => console.log(` ${msg}`));
|
|
193
|
+
console.log(`\n✅ Anthropic authenticated!`);
|
|
194
|
+
} catch (err: any) {
|
|
195
|
+
console.error(`\n❌ ${err.message}`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
case "openai": {
|
|
202
|
+
console.log("Checking for Codex CLI credentials...\n");
|
|
203
|
+
const imported = importCodexToken((msg) => console.log(` ${msg}`));
|
|
204
|
+
if (imported) {
|
|
205
|
+
console.log(`\n✅ Imported Codex credentials!`);
|
|
206
|
+
} else {
|
|
207
|
+
console.log("\n❌ No Codex CLI credentials found.");
|
|
208
|
+
console.log("Make sure Codex CLI is installed and you've logged in.");
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
case "qwen": {
|
|
214
|
+
console.log("Checking for Qwen CLI credentials...\n");
|
|
215
|
+
const imported = importQwenToken((msg) => console.log(` ${msg}`));
|
|
216
|
+
if (imported) {
|
|
217
|
+
console.log(`\n✅ Imported Qwen credentials!`);
|
|
218
|
+
} else {
|
|
219
|
+
console.log("\n❌ No Qwen CLI credentials found.");
|
|
220
|
+
console.log("Make sure Qwen CLI is installed and you've logged in.");
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
case "copilot": {
|
|
226
|
+
console.log("Starting GitHub Copilot device flow...\n");
|
|
227
|
+
try {
|
|
228
|
+
const cred = await copilotDeviceFlow((msg) => console.log(` ${msg}`));
|
|
229
|
+
console.log(`\n✅ GitHub Copilot authenticated!`);
|
|
230
|
+
} catch (err: any) {
|
|
231
|
+
console.error(`\n❌ ${err.message}`);
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
case "api-key": {
|
|
238
|
+
const providerId = process.argv[3];
|
|
239
|
+
const apiKey = process.argv[4];
|
|
240
|
+
if (!providerId || !apiKey) {
|
|
241
|
+
console.log("Usage: codemaxxing auth api-key <provider-id> <api-key>");
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
const cred = saveApiKey(providerId, apiKey);
|
|
245
|
+
console.log(`\n✅ Saved API key for ${cred.provider}`);
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
case "help":
|
|
250
|
+
case "--help":
|
|
251
|
+
case "-h": {
|
|
252
|
+
console.log(`
|
|
253
|
+
💪 Codemaxxing Auth
|
|
254
|
+
|
|
255
|
+
Commands:
|
|
256
|
+
codemaxxing login Interactive authentication setup
|
|
257
|
+
codemaxxing auth list List saved credentials
|
|
258
|
+
codemaxxing auth remove <name> Remove a credential
|
|
259
|
+
codemaxxing auth openrouter Start OpenRouter OAuth flow
|
|
260
|
+
codemaxxing auth anthropic Get Anthropic via Claude Code CLI
|
|
261
|
+
codemaxxing auth openai Import Codex CLI credentials
|
|
262
|
+
codemaxxing auth qwen Import Qwen CLI credentials
|
|
263
|
+
codemaxxing auth copilot GitHub Copilot device flow
|
|
264
|
+
codemaxxing auth api-key <id> <key> Save API key directly
|
|
265
|
+
codemaxxing auth help Show this help
|
|
266
|
+
|
|
267
|
+
Examples:
|
|
268
|
+
codemaxxing login # Interactive provider picker
|
|
269
|
+
codemaxxing auth openrouter # One browser login, access to 200+ models
|
|
270
|
+
codemaxxing auth anthropic # Use your Claude subscription via Claude Code
|
|
271
|
+
codemaxxing auth list # See what's saved
|
|
272
|
+
`);
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
default:
|
|
277
|
+
console.log(`Unknown command: ${command}`);
|
|
278
|
+
console.log("Run 'codemaxxing auth help' for available commands.");
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
main().catch((err) => {
|
|
284
|
+
console.error(`Error: ${err.message}`);
|
|
285
|
+
process.exit(1);
|
|
286
|
+
});
|
package/src/config.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { join } from "path";
|
|
4
|
+
import { getCredential, type AuthCredential } from "./utils/auth.js";
|
|
4
5
|
|
|
5
6
|
export interface ProviderConfig {
|
|
6
7
|
baseUrl: string;
|
|
@@ -209,3 +210,35 @@ export async function listModels(baseUrl: string, apiKey: string): Promise<strin
|
|
|
209
210
|
} catch { /* ignore */ }
|
|
210
211
|
return [];
|
|
211
212
|
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Resolve provider configuration from auth store or config file
|
|
216
|
+
* Priority: CLI args > auth store > config file > auto-detect
|
|
217
|
+
*/
|
|
218
|
+
export function resolveProvider(
|
|
219
|
+
providerId: string,
|
|
220
|
+
cliArgs: CLIArgs
|
|
221
|
+
): { baseUrl: string; apiKey: string; model: string } | null {
|
|
222
|
+
// Check auth store first
|
|
223
|
+
const authCred = getCredential(providerId);
|
|
224
|
+
if (authCred) {
|
|
225
|
+
return {
|
|
226
|
+
baseUrl: authCred.baseUrl,
|
|
227
|
+
apiKey: authCred.apiKey,
|
|
228
|
+
model: cliArgs.model || "auto",
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Fall back to config file
|
|
233
|
+
const config = loadConfig();
|
|
234
|
+
const provider = config.providers?.[providerId];
|
|
235
|
+
if (provider) {
|
|
236
|
+
return {
|
|
237
|
+
baseUrl: provider.baseUrl,
|
|
238
|
+
apiKey: cliArgs.apiKey || provider.apiKey,
|
|
239
|
+
model: cliArgs.model || provider.model,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return null;
|
|
244
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -28,6 +28,7 @@ function formatTimeAgo(date: Date): string {
|
|
|
28
28
|
// ── Slash Commands ──
|
|
29
29
|
const SLASH_COMMANDS = [
|
|
30
30
|
{ cmd: "/help", desc: "show commands" },
|
|
31
|
+
{ cmd: "/login", desc: "set up authentication" },
|
|
31
32
|
{ cmd: "/map", desc: "show repository map" },
|
|
32
33
|
{ cmd: "/reset", desc: "clear conversation" },
|
|
33
34
|
{ cmd: "/context", desc: "show message count" },
|
|
@@ -323,10 +324,33 @@ function App() {
|
|
|
323
324
|
exit();
|
|
324
325
|
return;
|
|
325
326
|
}
|
|
327
|
+
if (trimmed === "/login" || trimmed === "/auth") {
|
|
328
|
+
addMsg("info", [
|
|
329
|
+
"💪 Authentication Setup",
|
|
330
|
+
"",
|
|
331
|
+
"Run 'codemaxxing login' in a new terminal to set up:",
|
|
332
|
+
" • OpenRouter OAuth (browser login, 200+ models)",
|
|
333
|
+
" • Anthropic via Claude Code (your Pro/Max subscription)",
|
|
334
|
+
" • ChatGPT via Codex CLI (your Plus/Pro subscription)",
|
|
335
|
+
" • Qwen CLI (your Qwen access)",
|
|
336
|
+
" • GitHub Copilot (device flow)",
|
|
337
|
+
" • Manual API keys for any provider",
|
|
338
|
+
"",
|
|
339
|
+
"Commands:",
|
|
340
|
+
" codemaxxing login — interactive setup",
|
|
341
|
+
" codemaxxing auth list — see saved credentials",
|
|
342
|
+
" codemaxxing auth openrouter — OpenRouter OAuth",
|
|
343
|
+
" codemaxxing auth anthropic — Anthropic subscription",
|
|
344
|
+
" codemaxxing auth openai — ChatGPT subscription",
|
|
345
|
+
" codemaxxing auth copilot — GitHub Copilot",
|
|
346
|
+
].join("\n"));
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
326
349
|
if (trimmed === "/help") {
|
|
327
350
|
addMsg("info", [
|
|
328
351
|
"Commands:",
|
|
329
352
|
" /help — show this",
|
|
353
|
+
" /login — authentication setup (run codemaxxing login in terminal)",
|
|
330
354
|
" /model — switch model mid-session",
|
|
331
355
|
" /models — list available models",
|
|
332
356
|
" /map — show repository map",
|