junecoder 1.0.5 → 1.0.6

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.
Files changed (3) hide show
  1. package/cli.js +7 -11
  2. package/package.json +8 -2
  3. package/tui.mjs +41 -1
package/cli.js CHANGED
@@ -9,10 +9,11 @@
9
9
  * junecoder "write a hello world" # Single-shot (non-TUI)
10
10
  *
11
11
  * API keys can be provided via:
12
- * 1. .env file in project directory (DEEPSEEK_API_KEY=sk-...)
13
- * 2. ~/.junecoder/.env
14
- * 3. DEEPSEEK_API_KEY environment variable
15
- * 4. ~/.junecoder/config.json
12
+ * 1. Interactive setup on first run (TUI)
13
+ * 2. .env file in project directory (DEEPSEEK_API_KEY=sk-...)
14
+ * 3. ~/.junecoder/.env
15
+ * 4. DEEPSEEK_API_KEY environment variable
16
+ * 5. ~/.junecoder/config.json
16
17
  */
17
18
  import { existsSync, statSync, readFileSync } from 'node:fs';
18
19
  import { resolve, join } from 'node:path';
@@ -153,12 +154,7 @@ if (existsSync(targetDir) && statSync(targetDir).isDirectory()) {
153
154
  const config = loadConfig();
154
155
  const apiKey = config.provider.apiKey || process.env.DEEPSEEK_API_KEY;
155
156
 
156
- if (!apiKey) {
157
- console.error('Error: No API key found. Set DEEPSEEK_API_KEY in .env or in ~/.junecoder/config.json.');
158
- process.exit(1);
159
- }
160
-
161
- config.provider.apiKey = apiKey;
157
+ config.provider.apiKey = apiKey || '';
162
158
 
163
159
  // Create Agent instance with target cwd
164
160
  const agent = createAgent({
@@ -196,4 +192,4 @@ if (!process.stdin.isTTY || !process.stdout.isTTY) {
196
192
  }
197
193
 
198
194
  const restored = loadSession(agent.cwd);
199
- startTUI(agent, { projectDir: agent.cwd, restored });
195
+ startTUI(agent, { projectDir: agent.cwd, restored, needsSetup: !apiKey });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "junecoder",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Zero Npm Dependencies Agent Framework",
5
5
  "main": "agent.mjs",
6
6
  "type": "module",
@@ -20,7 +20,13 @@
20
20
  "engines": {
21
21
  "node": ">=21.7"
22
22
  },
23
- "keywords": ["agent", "react", "llm", "coding", "ai"],
23
+ "keywords": [
24
+ "agent",
25
+ "react",
26
+ "llm",
27
+ "coding",
28
+ "ai"
29
+ ],
24
30
  "author": "",
25
31
  "license": "ISC"
26
32
  }
package/tui.mjs CHANGED
@@ -8,7 +8,9 @@
8
8
  */
9
9
  import { emitKeypressEvents } from "node:readline";
10
10
  import { PassThrough } from "node:stream";
11
- import { basename } from "node:path";
11
+ import { basename, join } from "node:path";
12
+ import { homedir } from "node:os";
13
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
12
14
  import { runAgent, ContinueError } from "./agent.mjs";
13
15
  import { estimateTokens } from "./context.mjs";
14
16
  import {
@@ -326,9 +328,47 @@ export async function startTUI(agent, opts = {}) {
326
328
  if (str && str.length > 0 && !key.ctrl && !key.meta && str !== "\r" && str !== "\n") { for (const ch of str) { state.input.splice(state.cursor, 0, ch); state.cursor++; } }
327
329
  }
328
330
 
331
+ // ─── First-run setup: capture API key from input box ──────────────────────────
332
+ let setupMode = opts.needsSetup;
333
+ if (setupMode) {
334
+ state.status = "Paste your DeepSeek API key and press Enter (https://platform.deepseek.com/api_keys)";
335
+ }
336
+
329
337
  async function submit() {
330
338
  const text = state.input.join("").trim();
331
339
  if (!text || state.processing) return;
340
+
341
+ // ─── Setup mode: first input is the API key ──────────────────────────────
342
+ if (setupMode) {
343
+ setupMode = false;
344
+ state.input = []; state.cursor = 0;
345
+ const trimmed = text.trim();
346
+
347
+ if (!trimmed) {
348
+ pushLine("No API key provided. Exiting.", C.error);
349
+ render();
350
+ await new Promise(r => setTimeout(r, 1500));
351
+ cleanup();
352
+ process.exit(1);
353
+ }
354
+
355
+ if (!trimmed.startsWith("sk-")) {
356
+ pushLine("Warning: API key doesn't start with 'sk-'. It may not work.", C.warn);
357
+ }
358
+
359
+ const configDir = join(homedir(), '.junecoder');
360
+ if (!existsSync(configDir)) mkdirSync(configDir, { recursive: true });
361
+ writeFileSync(join(configDir, '.env'), `DEEPSEEK_API_KEY=${trimmed}\n`, 'utf-8');
362
+ process.env.DEEPSEEK_API_KEY = trimmed;
363
+ agent.provider.apiKey = trimmed;
364
+
365
+ pushLine("API key saved to ~/.junecoder/.env", C.tool);
366
+ pushLine("", C.dim);
367
+ state.status = "Ready";
368
+ render();
369
+ return;
370
+ }
371
+
332
372
  state.input = []; state.cursor = 0; state.history.push(text); state.historyIndex = -1; state.scroll = 0;
333
373
  if (text.startsWith("/")) { await handleSlash(text); return; }
334
374
  pushLabel("\u276f You:", ansi.bold + C.user); pushLine(text, C.text);