neuvikon 0.2.19 → 0.2.21
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/README.md +3 -1
- package/dist/index.mjs +61 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,7 +57,9 @@ npx neuvikon start --tickets EWWQ-1,EWWQ-2,EWWQ-3 --spawn
|
|
|
57
57
|
|
|
58
58
|
If [Herdr](https://herdr.dev) is installed, every ticket in the batch lands as
|
|
59
59
|
its own tab in one Herdr workspace instead of separate OS windows — Herdr's
|
|
60
|
-
own UI then shows each agent's live working/blocked/done state.
|
|
60
|
+
own UI then shows each agent's live working/blocked/done state. Run it from
|
|
61
|
+
inside a Herdr-managed pane and the batch adds its tabs to that same
|
|
62
|
+
workspace instead of opening a new one. Otherwise it
|
|
61
63
|
opens one terminal per ticket the usual way (a tmux window if you're already
|
|
62
64
|
in tmux, an iTerm2 or Terminal.app tab on macOS, a Windows Terminal tab on
|
|
63
65
|
Windows), each `cd`'d into this directory. Either way, if a coding agent CLI
|
package/dist/index.mjs
CHANGED
|
@@ -560,16 +560,26 @@ async function postUsage(credentials, session, delta) {
|
|
|
560
560
|
* Switching tickets used to stop the outgoing session bare, which dropped every
|
|
561
561
|
* token it had earned since its last turn ended — and a ticket that was started
|
|
562
562
|
* and left inside a single turn recorded exactly nothing.
|
|
563
|
+
*
|
|
564
|
+
* Returns whether the server actually confirmed the stop, so a caller can tell
|
|
565
|
+
* the difference between "closed" and "we gave up trying to tell the server" —
|
|
566
|
+
* a repeat `stop` for an already-stopped session is a safe no-op on the server
|
|
567
|
+
* side, so it's fine for a caller to retry this on failure.
|
|
563
568
|
*/
|
|
564
569
|
async function stopSession(credentials, session, transcript) {
|
|
565
570
|
const usage = pendingUsage(session, transcript);
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
571
|
+
try {
|
|
572
|
+
await post(credentials.url, "/api/agent/events", {
|
|
573
|
+
token: credentials.token,
|
|
574
|
+
event: "stop",
|
|
575
|
+
sessionKey: session.sessionKey,
|
|
576
|
+
tokensIn: usage.tokensIn,
|
|
577
|
+
tokensOut: usage.tokensOut
|
|
578
|
+
});
|
|
579
|
+
return true;
|
|
580
|
+
} catch {
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
573
583
|
}
|
|
574
584
|
/**
|
|
575
585
|
* Called from an agent hook on every turn, so it is deliberately silent and
|
|
@@ -1303,9 +1313,14 @@ function agentName(ref) {
|
|
|
1303
1313
|
* working/blocked/done state per agent — instead of scattered across OS
|
|
1304
1314
|
* windows. The first successful ref seeds the workspace; every later ref in
|
|
1305
1315
|
* the same batch adds a tab to it.
|
|
1316
|
+
*
|
|
1317
|
+
* Except when `--spawn` is itself run from inside a Herdr-managed pane
|
|
1318
|
+
* (`HERDR_WORKSPACE_ID` set, per Herdr's own caller-context injection): then
|
|
1319
|
+
* the batch reuses that workspace instead of creating a new one that yanks
|
|
1320
|
+
* focus away from wherever the user already was.
|
|
1306
1321
|
*/
|
|
1307
1322
|
var HerdrBatch = class {
|
|
1308
|
-
workspaceId = null;
|
|
1323
|
+
workspaceId = process.env.HERDR_WORKSPACE_ID || null;
|
|
1309
1324
|
openPane(ref, cwd) {
|
|
1310
1325
|
if (!this.workspaceId) {
|
|
1311
1326
|
const created = runHerdr([
|
|
@@ -1334,15 +1349,16 @@ var HerdrBatch = class {
|
|
|
1334
1349
|
])?.result.root_pane.pane_id ?? null;
|
|
1335
1350
|
}
|
|
1336
1351
|
/**
|
|
1337
|
-
*
|
|
1338
|
-
*
|
|
1339
|
-
*
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
1352
|
+
* "agent" — a coding agent is live in its own Herdr pane with the ticket
|
|
1353
|
+
* text confirmed delivered as its first prompt. "manual" — the pane exists
|
|
1354
|
+
* (either as a plain shell with the ticket echoed, or a started agent whose
|
|
1355
|
+
* prompt delivery couldn't be confirmed) but the user has to paste or type
|
|
1356
|
+
* into it themselves. `null` only means Herdr itself couldn't be reached,
|
|
1357
|
+
* and the caller should fall back to opening a native OS terminal instead.
|
|
1342
1358
|
*/
|
|
1343
1359
|
spawn(ref, cwd, agent, ticketText) {
|
|
1344
1360
|
const paneId = this.openPane(ref, cwd);
|
|
1345
|
-
if (!paneId) return
|
|
1361
|
+
if (!paneId) return null;
|
|
1346
1362
|
if (agent && ticketText) {
|
|
1347
1363
|
if (runHerdr([
|
|
1348
1364
|
"agent",
|
|
@@ -1352,15 +1368,17 @@ var HerdrBatch = class {
|
|
|
1352
1368
|
HERDR_KIND[agent.id],
|
|
1353
1369
|
"--pane",
|
|
1354
1370
|
paneId
|
|
1355
|
-
]))
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1371
|
+
])) return runHerdr([
|
|
1372
|
+
"agent",
|
|
1373
|
+
"prompt",
|
|
1374
|
+
agentName(ref),
|
|
1375
|
+
ticketText,
|
|
1376
|
+
"--wait",
|
|
1377
|
+
"--until",
|
|
1378
|
+
"working",
|
|
1379
|
+
"--timeout",
|
|
1380
|
+
"8000"
|
|
1381
|
+
]) ? "agent" : "manual";
|
|
1364
1382
|
}
|
|
1365
1383
|
runHerdr([
|
|
1366
1384
|
"pane",
|
|
@@ -1368,7 +1386,7 @@ var HerdrBatch = class {
|
|
|
1368
1386
|
paneId,
|
|
1369
1387
|
`echo 'Ticket: ${ref}' && echo 'Paste it into your agent, or run: npx neuvikon start --ticket ${ref}'`
|
|
1370
1388
|
]);
|
|
1371
|
-
return
|
|
1389
|
+
return "manual";
|
|
1372
1390
|
}
|
|
1373
1391
|
};
|
|
1374
1392
|
//#endregion
|
|
@@ -1549,8 +1567,13 @@ async function spawnMode(flags, credentials, refs) {
|
|
|
1549
1567
|
info(`Opening ${refs.length} terminal${refs.length === 1 ? "" : "s"}${herdr ? " in Herdr" : ` in ${cwd}`}...`);
|
|
1550
1568
|
for (const ref of refs) {
|
|
1551
1569
|
const ticketText = agent ? await fetchTicketText(credentials, ref) : null;
|
|
1552
|
-
|
|
1553
|
-
|
|
1570
|
+
const herdrOutcome = herdr?.spawn(ref, cwd, agent, ticketText);
|
|
1571
|
+
if (herdrOutcome === "agent") {
|
|
1572
|
+
success(`${bold(ref)} — ${agent.label} starting in Herdr`);
|
|
1573
|
+
continue;
|
|
1574
|
+
}
|
|
1575
|
+
if (herdrOutcome === "manual") {
|
|
1576
|
+
info(`${bold(ref)} — opened in Herdr, paste the ticket manually: ${dim(`npx neuvikon start --ticket ${ref}`)}`);
|
|
1554
1577
|
continue;
|
|
1555
1578
|
}
|
|
1556
1579
|
const launch = agent && ticketText ? {
|
|
@@ -1614,7 +1637,9 @@ async function start(flags, positional) {
|
|
|
1614
1637
|
}
|
|
1615
1638
|
throw error;
|
|
1616
1639
|
}
|
|
1617
|
-
if (existing)
|
|
1640
|
+
if (existing) {
|
|
1641
|
+
if (!await stopSession(credentials, existing, transcript)) warn(`Switched off ${existing.ticket} locally, but Neuvikon didn't confirm the stop — it may still show in progress.`);
|
|
1642
|
+
}
|
|
1618
1643
|
writeSession({
|
|
1619
1644
|
sessionKey: result.sessionKey,
|
|
1620
1645
|
ticket: ref,
|
|
@@ -1650,7 +1675,15 @@ async function stop(flags) {
|
|
|
1650
1675
|
return;
|
|
1651
1676
|
}
|
|
1652
1677
|
const credentials = readCredentials();
|
|
1653
|
-
if (credentials)
|
|
1678
|
+
if (!credentials) {
|
|
1679
|
+
clearSession();
|
|
1680
|
+
success(`Stopped tracking ${session.ticket}`);
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
if (!await stopSession(credentials, session, flags.get("transcript") || claudeTranscriptPath())) {
|
|
1684
|
+
warn(`Stopped tracking ${session.ticket} locally, but Neuvikon didn't confirm it — the ticket may still show in progress.`);
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1654
1687
|
clearSession();
|
|
1655
1688
|
success(`Stopped tracking ${session.ticket}`);
|
|
1656
1689
|
}
|