@huseynsnmz/pi-board 0.14.0 → 0.15.3
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/CHANGELOG.md +30 -0
- package/README.md +97 -88
- package/package.json +14 -9
- package/runner/title-runner.mjs +1 -1
- package/src/commands/agent-board.ts +4 -1
- package/src/commands/bg.ts +28 -5
- package/src/commands/board-model.ts +21 -0
- package/src/core/config.mjs +77 -0
- package/src/core/load-config.mjs +4 -0
- package/src/core/rows.mjs +20 -0
- package/src/index.ts +17 -0
- package/src/runtime/service.mjs +28 -1
- package/src/ui/dashboard.ts +319 -282
- package/src/ui/double-left.ts +12 -18
- package/src/ui/external-editor.ts +25 -0
- package/src/ui/focus.ts +32 -0
- package/src/ui/waiting.ts +30 -0
- package/scripts/patch-vulns.mjs +0 -59
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* launch background workers and the detached runner, and keeps a small footer status with
|
|
6
6
|
* the count of sessions needing attention. See docs/EXPLORATION.md for the design.
|
|
7
7
|
*/
|
|
8
|
+
import "./core/load-config.mjs";
|
|
8
9
|
import { fileURLToPath } from "node:url";
|
|
9
10
|
import { createConnection } from "node:net";
|
|
10
11
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
@@ -15,8 +16,10 @@ import { createService, envInt } from "./runtime/service.mjs";
|
|
|
15
16
|
import { attachWarmHostSweeper } from "./core/warm-host-sweeper.mjs";
|
|
16
17
|
import { openDashboard, registerAgentBoardCommand } from "./commands/agent-board.js";
|
|
17
18
|
import { registerBgCommand } from "./commands/bg.js";
|
|
19
|
+
import { registerBoardModelCommand } from "./commands/board-model.ts";
|
|
18
20
|
import { announceBoardClosedOn } from "./ui/chrome.ts";
|
|
19
21
|
import { watchDoubleLeft } from "./ui/double-left.ts";
|
|
22
|
+
import { watchWaiting } from "./ui/waiting.ts";
|
|
20
23
|
|
|
21
24
|
const RUNNER_SCRIPT = fileURLToPath(new URL("../runner/job-runner.mjs", import.meta.url));
|
|
22
25
|
const PTY_RUNNER_SCRIPT = fileURLToPath(new URL("../runner/pty-runner.mjs", import.meta.url));
|
|
@@ -40,6 +43,7 @@ let hostedSessionEpoch = 0;
|
|
|
40
43
|
export default function piAgentBoard(pi: ExtensionAPI): void {
|
|
41
44
|
announceBoardClosedOn(pi.events);
|
|
42
45
|
let stopDoubleLeft: (() => void) | null = null;
|
|
46
|
+
let stopWaiting: (() => void) | null = null;
|
|
43
47
|
const root = defaultRoot();
|
|
44
48
|
const { piCommand, piArgsPrefix } = resolvePiInvocation();
|
|
45
49
|
|
|
@@ -85,6 +89,7 @@ export default function piAgentBoard(pi: ExtensionAPI): void {
|
|
|
85
89
|
|
|
86
90
|
registerAgentBoardCommand(pi, commandOpts);
|
|
87
91
|
registerBgCommand(pi, commandOpts);
|
|
92
|
+
registerBoardModelCommand(pi);
|
|
88
93
|
pi.registerFlag("agent-board", {
|
|
89
94
|
description: "Open the agent-board dashboard on startup",
|
|
90
95
|
type: "boolean",
|
|
@@ -193,11 +198,23 @@ export default function piAgentBoard(pi: ExtensionAPI): void {
|
|
|
193
198
|
hostedFrameWitness = { stop: () => cleanup("session-shutdown") };
|
|
194
199
|
}
|
|
195
200
|
|
|
201
|
+
// After this the context they hold is stale, and using one takes Pi down.
|
|
202
|
+
pi.on("session_shutdown", () => {
|
|
203
|
+
stopDoubleLeft?.();
|
|
204
|
+
stopDoubleLeft = null;
|
|
205
|
+
stopWaiting?.();
|
|
206
|
+
stopWaiting = null;
|
|
207
|
+
});
|
|
208
|
+
|
|
196
209
|
pi.on("session_start", async (event, ctx) => {
|
|
197
210
|
if (!isHostedChild && ctx.mode === "tui") {
|
|
198
211
|
stopDoubleLeft?.();
|
|
199
212
|
stopDoubleLeft = watchDoubleLeft(ctx);
|
|
200
213
|
}
|
|
214
|
+
if (ctx.mode === "tui") {
|
|
215
|
+
stopWaiting?.();
|
|
216
|
+
stopWaiting = watchWaiting(ctx, (waiting) => syncForeground({ type: "agent_board_waiting", waiting }, ctx));
|
|
217
|
+
}
|
|
201
218
|
if (isHostedChild && !hostedEditorReporter && typeof ctx.ui?.getEditorText === "function" && hostedViewId) {
|
|
202
219
|
const { createEditorStateReporter } = await import("./core/editor-state-reporter.mjs");
|
|
203
220
|
hostedEditorReporter = createEditorStateReporter({
|
package/src/runtime/service.mjs
CHANGED
|
@@ -1377,7 +1377,8 @@ export function createService(opts) {
|
|
|
1377
1377
|
const pty = ptySupport();
|
|
1378
1378
|
if (!pty.ok) return { ok: false, error: "PTY unavailable", fallbackReason: nodePtyFallbackMessage(pty) };
|
|
1379
1379
|
if (isAgentBusy(row)) return { ok: false, error: "A non-live background run is active for this session" };
|
|
1380
|
-
|
|
1380
|
+
// A missing file is a session that was put on the board before its first message. Pi starts
|
|
1381
|
+
// it at that path, as it would have.
|
|
1381
1382
|
|
|
1382
1383
|
const lock = tryAcquireLockImpl(root, viewId, "host-start", { identity: serviceIdentity() });
|
|
1383
1384
|
if (!lock.acquired) {
|
|
@@ -1809,6 +1810,16 @@ export function createService(opts) {
|
|
|
1809
1810
|
appendDiagnostic(root, row.meta.id, { source: "evidence", level: "warn", code: "evidence_reduce_failed", message: "Could not reduce hosted evidence", details: { error: err instanceof Error ? err.message : String(err) } });
|
|
1810
1811
|
}
|
|
1811
1812
|
|
|
1813
|
+
if (event.type === "agent_board_waiting") {
|
|
1814
|
+
status.semanticState = event.waiting ? "needs_input" : "working";
|
|
1815
|
+
status.processState = "alive";
|
|
1816
|
+
status.question = event.waiting ? "Waiting on a prompt. Attach to answer it." : null;
|
|
1817
|
+
status.summary = event.waiting ? "Waiting on a prompt" : "Running…";
|
|
1818
|
+
status.lastActivityAt = now;
|
|
1819
|
+
void writeForegroundState(row, status);
|
|
1820
|
+
return true;
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1812
1823
|
if (event.type === "input" || event.type === "before_agent_start" || event.type === "agent_start") {
|
|
1813
1824
|
status.semanticState = "working";
|
|
1814
1825
|
status.processState = "alive";
|
|
@@ -2293,6 +2304,22 @@ export function createService(opts) {
|
|
|
2293
2304
|
return { ok: true };
|
|
2294
2305
|
},
|
|
2295
2306
|
|
|
2307
|
+
/**
|
|
2308
|
+
* Gives these rows this order, numbered from the lowest they had: rows created in the same
|
|
2309
|
+
* millisecond have no order between them to swap.
|
|
2310
|
+
* @param {string[]} viewIds
|
|
2311
|
+
*/
|
|
2312
|
+
setRowOrder(viewIds) {
|
|
2313
|
+
const metas = viewIds.map((id) => loadRow(root, id)?.meta);
|
|
2314
|
+
if (metas.some((meta) => !meta)) return { ok: false, error: "Unknown session" };
|
|
2315
|
+
const base = Math.min(...metas.map((meta) => meta.order ?? meta.createdAt ?? 0));
|
|
2316
|
+
metas.forEach((meta, index) => {
|
|
2317
|
+
meta.order = base + index;
|
|
2318
|
+
writeMeta(root, meta);
|
|
2319
|
+
});
|
|
2320
|
+
return { ok: true };
|
|
2321
|
+
},
|
|
2322
|
+
|
|
2296
2323
|
/** @param {string} viewId @param {string} name */
|
|
2297
2324
|
rename(viewId, name) {
|
|
2298
2325
|
const clean = String(name || "").trim();
|