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.
@@ -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",