junecoder 1.0.4 → 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/agent.mjs +5 -0
- package/cli.js +7 -11
- package/package.json +8 -2
- package/tui.mjs +43 -3
package/agent.mjs
CHANGED
|
@@ -533,6 +533,11 @@ export async function runAgent(agent, input, callbacks = {}, options = {}) {
|
|
|
533
533
|
|
|
534
534
|
// Report result
|
|
535
535
|
try { cb.onToolResult(r.name, r.output, r.error); } catch { /* ignore */ }
|
|
536
|
+
|
|
537
|
+
// Sync task list to TUI after task tool
|
|
538
|
+
if (r.name === 'task' && !r.error && !r.denied) {
|
|
539
|
+
try { cb.onTaskUpdate(agent.tasks); } catch { /* ignore */ }
|
|
540
|
+
}
|
|
536
541
|
}
|
|
537
542
|
|
|
538
543
|
// Step 8: Inject pending reminders
|
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);
|
|
@@ -359,7 +399,7 @@ export async function startTUI(agent, opts = {}) {
|
|
|
359
399
|
}
|
|
360
400
|
}
|
|
361
401
|
clearInterval(ticker); state.processing = false; state.controller = null; state.status = "Ready";
|
|
362
|
-
if (state.tasks.length > 0 && state.tasks.every(t => t.status === "done")) state.tasks = [];
|
|
402
|
+
if (state.tasks.length > 0 && state.tasks.every(t => t.status === "done")) { state.tasks = []; agent.tasks = []; }
|
|
363
403
|
try { saveSession(agent, state.lines); } catch {} render();
|
|
364
404
|
}
|
|
365
405
|
function flushStream() { if (state.reasoning) { pushLine(state.reasoning, C.reason); state.reasoning = ""; } if (state.streaming) { pushLine(state.streaming, C.text); state.streaming = ""; } }
|
|
@@ -424,7 +464,7 @@ export async function startTUI(agent, opts = {}) {
|
|
|
424
464
|
if (typeof raw === "string") state.lines.push({ text: raw, color: C.text });
|
|
425
465
|
else if (raw && typeof raw.text === "string") state.lines.push(raw);
|
|
426
466
|
}
|
|
427
|
-
if (restored.tasks) state.tasks = restored.tasks;
|
|
467
|
+
if (restored.tasks) { state.tasks = restored.tasks; agent.tasks = restored.tasks; }
|
|
428
468
|
if (restored.planMode !== undefined) agent.planMode = restored.planMode;
|
|
429
469
|
state.status = "Session restored";
|
|
430
470
|
} else {
|