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.
- package/cli.js +7 -11
- package/package.json +8 -2
- 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.
|
|
13
|
-
* 2.
|
|
14
|
-
* 3.
|
|
15
|
-
* 4.
|
|
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
|
-
|
|
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.
|
|
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": [
|
|
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);
|