@polderlabs/bizar 5.5.0 → 5.5.2
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/bizar-dash/dist/assets/{EnvVarsSection-L413CQEI.js → EnvVarsSection-DoIzqrlb.js} +3 -3
- package/bizar-dash/dist/assets/{EnvVarsSection-L413CQEI.js.map → EnvVarsSection-DoIzqrlb.js.map} +1 -1
- package/bizar-dash/dist/assets/{MobileChat-0kGQb1S6.js → MobileChat-CoUCUsbr.js} +1 -1
- package/bizar-dash/dist/assets/{MobileChat-0kGQb1S6.js.map → MobileChat-CoUCUsbr.js.map} +1 -1
- package/bizar-dash/dist/assets/MobileSettings-BeO04g52.js +1 -0
- package/bizar-dash/dist/assets/MobileSettings-BeO04g52.js.map +1 -0
- package/bizar-dash/dist/assets/{Toast-C6f1zFJ2.js → Toast-D9nv5N6m.js} +1 -1
- package/bizar-dash/dist/assets/{Toast-C6f1zFJ2.js.map → Toast-D9nv5N6m.js.map} +1 -1
- package/bizar-dash/dist/assets/{icons-BsWXWkEF.js → icons-Btipv2pp.js} +100 -90
- package/bizar-dash/dist/assets/icons-Btipv2pp.js.map +1 -0
- package/bizar-dash/dist/assets/main-DHXPuddY.js +18 -0
- package/bizar-dash/dist/assets/main-DHXPuddY.js.map +1 -0
- package/bizar-dash/dist/assets/{main-BklYy02e.css → main-UG4jKK-F.css} +1 -1
- package/bizar-dash/dist/assets/mobile-H5KH9cTL.js +1 -0
- package/bizar-dash/dist/assets/{mobile-CsaN4CxH.js.map → mobile-H5KH9cTL.js.map} +1 -1
- package/bizar-dash/dist/assets/{mobile-layout-iFBdjEE4.js → mobile-layout-BqHxVb6_.js} +2 -2
- package/bizar-dash/dist/assets/{mobile-layout-iFBdjEE4.js.map → mobile-layout-BqHxVb6_.js.map} +1 -1
- package/bizar-dash/dist/assets/{useSlashCommands-DGJcC9iQ.js → useSlashCommands-C24-O1e7.js} +2 -2
- package/bizar-dash/dist/assets/{useSlashCommands-DGJcC9iQ.js.map → useSlashCommands-C24-O1e7.js.map} +1 -1
- package/bizar-dash/dist/index.html +7 -7
- package/bizar-dash/dist/mobile.html +3 -3
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/server/bg-spawner.mjs +522 -305
- package/bizar-dash/src/server/headroom.mjs +3 -1
- package/bizar-dash/src/server/memory-lightrag.mjs +26 -2
- package/bizar-dash/src/server/memory-store.mjs +74 -28
- package/bizar-dash/src/server/opencode-sdk.mjs +63 -3
- package/bizar-dash/src/server/plugins/registry.mjs +1 -1
- package/bizar-dash/src/server/routes/activity.mjs +85 -0
- package/bizar-dash/src/server/routes/background.mjs +31 -13
- package/bizar-dash/src/server/routes/memory.mjs +91 -38
- package/bizar-dash/src/server/routes/opencode-session-detail.mjs +1 -1
- package/bizar-dash/src/server/server.mjs +4 -1
- package/bizar-dash/src/web/components/Sidebar.tsx +0 -1
- package/bizar-dash/src/web/styles/main.css +52 -13
- package/bizar-dash/src/web/styles/memory.css +17 -0
- package/bizar-dash/src/web/styles/settings.css +6 -56
- package/bizar-dash/src/web/views/Marketplace.tsx +192 -31
- package/bizar-dash/src/web/views/memory/ConfigPanel.tsx +157 -7
- package/bizar-dash/src/web/views/memory/MemoryOverview.tsx +11 -0
- package/bizar-dash/src/web/views/settings/MemorySection.tsx +65 -11
- package/bizar-dash/tests/background-sdk-session.test.mjs +94 -0
- package/bizar-dash/tests/background-session-events.test.mjs +74 -0
- package/bizar-dash/tests/background-steer-sdk.test.mjs +71 -0
- package/bizar-dash/tests/background-steer.test.mjs +25 -7
- package/bizar-dash/tests/memory-namespace.test.mjs +2 -2
- package/bizar-dash/tests/memory-status-root.test.mjs +144 -0
- package/bizar-dash/tests/memory-store.test.mjs +5 -4
- package/bizar-dash/tests/memory-vault-config-endpoint.test.mjs +173 -0
- package/bizar-dash/tests/settings-mode-wiring.test.tsx +1 -1
- package/bizar-dash/tests/settings-nav.test.tsx +0 -1
- package/bizar-dash/tests/views/Marketplace.test.tsx +200 -0
- package/bizar-dash/tests/views/Memory.test.tsx +274 -0
- package/bizar-dash/tests/views/sidebar.test.tsx +116 -0
- package/bizar-dash/tests/views/topbar.test.tsx +87 -0
- package/cli/commands/marketplace.mjs +1 -1
- package/cli/memory.mjs +1 -1
- package/package.json +1 -1
- package/plugins/bizar/src/background-state.ts +11 -0
- package/plugins/bizar/src/background.ts +212 -34
- package/plugins/bizar/src/opencode-runner.ts +80 -353
- package/plugins/bizar/src/tools/bg-send-message.ts +160 -61
- package/plugins/bizar/src/tools/bg-spawn.ts +235 -156
- package/plugins/bizar/tests/tools/bg-send-message.test.ts +84 -11
- package/plugins/bizar/tests/tools/bg-spawn-http.test.ts +223 -0
- package/bizar-dash/dist/assets/MobileSettings-Cq1oFIdO.js +0 -1
- package/bizar-dash/dist/assets/MobileSettings-Cq1oFIdO.js.map +0 -1
- package/bizar-dash/dist/assets/icons-BsWXWkEF.js.map +0 -1
- package/bizar-dash/dist/assets/main-CBTgYSeG.js +0 -18
- package/bizar-dash/dist/assets/main-CBTgYSeG.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-CsaN4CxH.js +0 -1
package/cli/memory.mjs
CHANGED
|
@@ -698,7 +698,7 @@ function addRemoteToVault(projectRoot, remote) {
|
|
|
698
698
|
return;
|
|
699
699
|
}
|
|
700
700
|
|
|
701
|
-
const addResult = memoryGit.addRemote(vaultRoot, 'origin', remote);
|
|
701
|
+
const addResult = memoryGit.addRemote(vaultRoot, 'origin', remote, { overwrite: true });
|
|
702
702
|
if (!addResult || addResult.ok === false) {
|
|
703
703
|
warn(`failed to register remote: ${addResult?.error || 'unknown error'}`);
|
|
704
704
|
info('the remote URL is still saved in .bizar/memory.json');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "5.5.
|
|
3
|
+
"version": "5.5.2",
|
|
4
4
|
"description": "Norse-pantheon multi-agent system for opencode — 13 agents across 4 cost tiers with cost-aware routing, plans, and a configurable agent harness. v4 ships as a single npm package bundling the dashboard server, opencode plugin, and typed SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -246,6 +246,17 @@ export interface BackgroundState {
|
|
|
246
246
|
// v0.8.0 — process tracking (see opencode-runner.ts).
|
|
247
247
|
processId?: number;
|
|
248
248
|
exitCode?: number;
|
|
249
|
+
// v5.5.1 — `liveSession: true` means this instance is backed by an
|
|
250
|
+
// opencode serve SDK session managed by the dashboard (not by an OS
|
|
251
|
+
// subprocess). The plugin's bg-kill/bg-pause/bg-resume tools check
|
|
252
|
+
// this flag and delegate to the dashboard HTTP API instead of
|
|
253
|
+
// touching the (now-stubbed) opencode-runner. Optional for backward
|
|
254
|
+
// compat — pre-v5.5.1 state files leave it unset / false.
|
|
255
|
+
liveSession?: boolean;
|
|
256
|
+
// v5.5.1 — instanceId assigned by the dashboard (may differ from the
|
|
257
|
+
// plugin's locally-allocated `instanceId` when the dashboard mints its
|
|
258
|
+
// own). Cross-reference for cross-system debugging.
|
|
259
|
+
dashboardInstanceId?: string;
|
|
249
260
|
runnerState?: string;
|
|
250
261
|
runnerError?: string;
|
|
251
262
|
spawnMessage?: string;
|
|
@@ -79,6 +79,100 @@ import { researchInterventionPrompt } from "./research-prompt.js";
|
|
|
79
79
|
|
|
80
80
|
import * as opencodeRunner from "./opencode-runner.js";
|
|
81
81
|
|
|
82
|
+
// --- v5.5.1 — dashboard HTTP helpers -------------------------------------
|
|
83
|
+
//
|
|
84
|
+
// Background agents run as opencode serve SDK sessions managed by the
|
|
85
|
+
// dashboard. The plugin's `pause`/`resume` (and the tool layer's
|
|
86
|
+
// `kill`/`send-message`) therefore talk to the dashboard over HTTP
|
|
87
|
+
// instead of touching the OS or the opencode SDK directly.
|
|
88
|
+
//
|
|
89
|
+
// These helpers are intentionally tiny — they only need to do POSTs
|
|
90
|
+
// for `pause`/`resume`. The token + URL resolution matches what
|
|
91
|
+
// `bg-spawn.ts` and `bg-send-message.ts` do.
|
|
92
|
+
|
|
93
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
94
|
+
import { join } from "node:path";
|
|
95
|
+
import { homedir } from "node:os";
|
|
96
|
+
|
|
97
|
+
function resolveDashboardUrl(): string {
|
|
98
|
+
const fromEnv = process.env.BIZAR_DASHBOARD_URL;
|
|
99
|
+
if (fromEnv && fromEnv.trim()) return fromEnv.trim().replace(/\/+$/, "");
|
|
100
|
+
const port = process.env.BIZAR_DASHBOARD_PORT;
|
|
101
|
+
if (port && /^\d+$/.test(port)) return `http://127.0.0.1:${port}`;
|
|
102
|
+
return "http://127.0.0.1:4098";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const DEFAULT_DASHBOARD_AUTH_PATHS = [
|
|
106
|
+
join(homedir(), ".config", "bizar", "dashboard-secret"),
|
|
107
|
+
join(homedir(), ".cache", "bizarharness", "dash-auth.json"),
|
|
108
|
+
join(homedir(), ".cache", "bizar", "dash-auth.json"),
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
function readDashboardToken(): string {
|
|
112
|
+
for (const candidate of DEFAULT_DASHBOARD_AUTH_PATHS) {
|
|
113
|
+
try {
|
|
114
|
+
if (!existsSync(candidate)) continue;
|
|
115
|
+
const text = readFileSync(candidate, "utf-8").trim();
|
|
116
|
+
if (text && text.length >= 16) return text;
|
|
117
|
+
} catch {
|
|
118
|
+
/* ignore */
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
for (const candidate of DEFAULT_DASHBOARD_AUTH_PATHS) {
|
|
122
|
+
try {
|
|
123
|
+
if (!existsSync(candidate)) continue;
|
|
124
|
+
const parsed = JSON.parse(readFileSync(candidate, "utf-8")) as { password?: unknown };
|
|
125
|
+
if (typeof parsed.password === "string" && parsed.password.length >= 16) {
|
|
126
|
+
return parsed.password;
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
/* ignore */
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return "";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* POST to the dashboard with a JSON `{}` body. Used for pause/resume —
|
|
137
|
+
* no payload, just the URL. Returns `{ ok: true }` on 2xx, `{ ok: false,
|
|
138
|
+
* error }` on transport failure or non-2xx (so the caller can surface
|
|
139
|
+
* a structured error without catching exceptions).
|
|
140
|
+
*/
|
|
141
|
+
async function postDashboardControl(
|
|
142
|
+
path: string,
|
|
143
|
+
logger: Logger,
|
|
144
|
+
): Promise<{ ok: boolean; error?: string }> {
|
|
145
|
+
const url = `${resolveDashboardUrl()}${path}`;
|
|
146
|
+
const token = readDashboardToken();
|
|
147
|
+
const headers: Record<string, string> = {
|
|
148
|
+
"content-type": "application/json",
|
|
149
|
+
accept: "application/json",
|
|
150
|
+
};
|
|
151
|
+
if (token) headers.authorization = `Bearer ${token}`;
|
|
152
|
+
const ac = new AbortController();
|
|
153
|
+
const timer = setTimeout(() => ac.abort(), 5_000);
|
|
154
|
+
try {
|
|
155
|
+
const res = await fetch(url, {
|
|
156
|
+
method: "POST",
|
|
157
|
+
headers,
|
|
158
|
+
body: JSON.stringify({}),
|
|
159
|
+
signal: ac.signal,
|
|
160
|
+
});
|
|
161
|
+
if (!res.ok) {
|
|
162
|
+
const text = await res.text().catch(() => "");
|
|
163
|
+
logger.warn(`bizar: dashboard ${path} returned ${res.status}: ${text.slice(0, 200)}`);
|
|
164
|
+
return { ok: false, error: `dashboard_http_${res.status}` };
|
|
165
|
+
}
|
|
166
|
+
return { ok: true };
|
|
167
|
+
} catch (err) {
|
|
168
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
169
|
+
logger.warn(`bizar: dashboard ${path} failed: ${msg}`);
|
|
170
|
+
return { ok: false, error: msg };
|
|
171
|
+
} finally {
|
|
172
|
+
clearTimeout(timer);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
82
176
|
// --- Public surface -------------------------------------------------------
|
|
83
177
|
|
|
84
178
|
/** A snapshot of an instance for the `bizar_status` tool. */
|
|
@@ -466,6 +560,13 @@ export class InstanceManager {
|
|
|
466
560
|
* Abort the opencode session and mark the instance `killed`. If the
|
|
467
561
|
* instance is already in a terminal state, this is a no-op (spec §1.5,
|
|
468
562
|
* MEDIUM-40).
|
|
563
|
+
*
|
|
564
|
+
* v5.5.1 — SDK-backed instances are killed via `DELETE /api/background/:id`
|
|
565
|
+
* on the dashboard (which calls `sdk.sessions.abort`). Pre-v5.5.1
|
|
566
|
+
* fallback: bg-only mode marks the instance killed in-memory and lets
|
|
567
|
+
* the runner notice; full mode (with HTTP client) calls
|
|
568
|
+
* `http.abortSession` directly. The two paths are now functionally
|
|
569
|
+
* equivalent — the dashboard IS the SDK client in v5.5.1.
|
|
469
570
|
*/
|
|
470
571
|
async kill(instanceId: string): Promise<void> {
|
|
471
572
|
const inst = this.instances.get(instanceId);
|
|
@@ -476,6 +577,34 @@ export class InstanceManager {
|
|
|
476
577
|
);
|
|
477
578
|
return;
|
|
478
579
|
}
|
|
580
|
+
// v5.5.1 — SDK-backed: ask the dashboard to abort.
|
|
581
|
+
if (inst.liveSession) {
|
|
582
|
+
const url = `${resolveDashboardUrl()}/api/background/${encodeURIComponent(instanceId)}`;
|
|
583
|
+
const token = readDashboardToken();
|
|
584
|
+
const headers: Record<string, string> = { accept: "application/json" };
|
|
585
|
+
if (token) headers.authorization = `Bearer ${token}`;
|
|
586
|
+
const ac = new AbortController();
|
|
587
|
+
const timer = setTimeout(() => ac.abort(), 5_000);
|
|
588
|
+
try {
|
|
589
|
+
await fetch(url, { method: "DELETE", headers, signal: ac.signal });
|
|
590
|
+
} catch (err) {
|
|
591
|
+
this.logger.warn(
|
|
592
|
+
`bizar: kill(${instanceId}) dashboard DELETE failed: ${
|
|
593
|
+
err instanceof Error ? err.message : String(err)
|
|
594
|
+
}`,
|
|
595
|
+
);
|
|
596
|
+
} finally {
|
|
597
|
+
clearTimeout(timer);
|
|
598
|
+
}
|
|
599
|
+
await this.update(instanceId, {
|
|
600
|
+
status: "killed",
|
|
601
|
+
completedAt: Date.now(),
|
|
602
|
+
});
|
|
603
|
+
this.logger.info(
|
|
604
|
+
`bizar: killed background instance ${instanceId} (sdk-backed, dashboard DELETE)`,
|
|
605
|
+
);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
479
608
|
// v0.8.0 — bg-only mode has no HTTP client. The subprocess is
|
|
480
609
|
// already owned by opencode-runner.ts (see src/tools/bg-spawn.ts);
|
|
481
610
|
// mark the instance killed in-memory and let the runner notice the
|
|
@@ -493,16 +622,12 @@ export class InstanceManager {
|
|
|
493
622
|
});
|
|
494
623
|
return;
|
|
495
624
|
}
|
|
496
|
-
//
|
|
497
|
-
// (EventSessionIdle or EventSessionError) will finalize the status.
|
|
625
|
+
// Pre-v5.5.1 fallback — direct abort on the legacy serve child.
|
|
498
626
|
const abort = await this.http.abortSession(inst.sessionId, this.worktree);
|
|
499
627
|
if (!abort.ok) {
|
|
500
628
|
this.logger.warn(
|
|
501
629
|
`bizar: kill(${instanceId}): abort failed: ${abort.error}`,
|
|
502
630
|
);
|
|
503
|
-
// Even if the abort call failed, we still want the in-memory state
|
|
504
|
-
// to reflect a deliberate kill so the user sees it. The next SSE
|
|
505
|
-
// event will overwrite if it disagrees.
|
|
506
631
|
}
|
|
507
632
|
await this.update(instanceId, {
|
|
508
633
|
status: "killed",
|
|
@@ -734,20 +859,18 @@ export class InstanceManager {
|
|
|
734
859
|
* Scan the bg directory, load every instance, and rebuild the in-memory
|
|
735
860
|
* map. Any `running` or `pending` instance is checked for liveness:
|
|
736
861
|
*
|
|
737
|
-
* -
|
|
738
|
-
*
|
|
739
|
-
*
|
|
740
|
-
*
|
|
741
|
-
*
|
|
742
|
-
*
|
|
743
|
-
* the
|
|
862
|
+
* - v5.5.1: bg instances no longer have OS subprocesses — they live
|
|
863
|
+
* as opencode serve SDK sessions on the dashboard. `opencodeRunner.
|
|
864
|
+
* isAlive(pid)` always returns `false`, so the adoption branch is
|
|
865
|
+
* effectively dead code; we keep it for the rare case a state file
|
|
866
|
+
* from v5.5.0 (with a real pid) survives an upgrade. We mark the
|
|
867
|
+
* instance `failed` with reason `"v5.5.1 session reconciliation
|
|
868
|
+
* pending"` so the operator knows to look at the dashboard; the
|
|
869
|
+
* dashboard's own SSE bridge will reconcile.
|
|
870
|
+
* - v5.5.0 and earlier: a still-alive PID re-adopts as before.
|
|
744
871
|
*
|
|
745
872
|
* Historical terminal records (done, failed, killed, timed_out, steered)
|
|
746
873
|
* are preserved as-is.
|
|
747
|
-
*
|
|
748
|
-
* Best-effort: liveness check uses `process.kill(pid, 0)` which is fast
|
|
749
|
-
* (no signal is sent) and safe (returns false on EPERM / ESRCH without
|
|
750
|
-
* raising to us).
|
|
751
874
|
*/
|
|
752
875
|
async rebuildInMemoryMap(): Promise<void> {
|
|
753
876
|
let all: BackgroundState[];
|
|
@@ -768,7 +891,26 @@ export class InstanceManager {
|
|
|
768
891
|
this.instances.set(inst.instanceId, inst);
|
|
769
892
|
rebuilt += 1;
|
|
770
893
|
if (inst.status === "running" || inst.status === "pending") {
|
|
771
|
-
//
|
|
894
|
+
// v5.5.1 — prefer the liveSession flag (the SDK-session marker)
|
|
895
|
+
// when present. If the dashboard still tracks the session, we
|
|
896
|
+
// re-adopt; otherwise we mark failed with a reconcilable message.
|
|
897
|
+
if (inst.liveSession) {
|
|
898
|
+
// SDK-backed: we trust the state file. The dashboard's SSE
|
|
899
|
+
// bridge will deliver the next event. Mark `running` and
|
|
900
|
+
// `runnerState: "adopted"` so the operator sees the plugin
|
|
901
|
+
// has caught up.
|
|
902
|
+
await this.update(inst.instanceId, {
|
|
903
|
+
status: "running",
|
|
904
|
+
completedAt: undefined,
|
|
905
|
+
runnerState: "adopted",
|
|
906
|
+
});
|
|
907
|
+
adopted += 1;
|
|
908
|
+
this.logger.info(
|
|
909
|
+
`bizar: re-adopted SDK-backed bg instance ${inst.instanceId}`,
|
|
910
|
+
);
|
|
911
|
+
continue;
|
|
912
|
+
}
|
|
913
|
+
// Pre-v5.5.1 subprocess fallback (rare; the runner is stubbed).
|
|
772
914
|
const pid = inst.processId;
|
|
773
915
|
const alive = typeof pid === "number" && opencodeRunner.isAlive(pid);
|
|
774
916
|
if (alive) {
|
|
@@ -1046,10 +1188,16 @@ export class InstanceManager {
|
|
|
1046
1188
|
// --- v5.x — pause / resume / progress / tool-call instrumentation -------
|
|
1047
1189
|
|
|
1048
1190
|
/**
|
|
1049
|
-
* Pause a running instance.
|
|
1050
|
-
*
|
|
1051
|
-
*
|
|
1052
|
-
*
|
|
1191
|
+
* Pause a running instance. In v5.5.1 (SDK-backed) this delegates to
|
|
1192
|
+
* the dashboard's `POST /api/background/:id/pause`, which performs an
|
|
1193
|
+
* OUTPUT pause (stops forwarding events to the WS bus; the underlying
|
|
1194
|
+
* opencode serve session keeps running). For pre-v5.5.1 instances
|
|
1195
|
+
* that still carry a `processId`, fall back to the old SIGSTOP path
|
|
1196
|
+
* via the (now stubbed) runner — best-effort, returns ok:false if the
|
|
1197
|
+
* runner reports the OS no longer supports it.
|
|
1198
|
+
*
|
|
1199
|
+
* On success, the in-memory status flips to `"paused"` and `pausedAt`
|
|
1200
|
+
* is stamped.
|
|
1053
1201
|
*
|
|
1054
1202
|
* Already-terminal instances return `{ ok: false, error: "already_terminal" }`.
|
|
1055
1203
|
* Already-paused instances return `{ ok: true }` (no-op).
|
|
@@ -1061,18 +1209,28 @@ export class InstanceManager {
|
|
|
1061
1209
|
return { ok: false, error: "already_terminal" };
|
|
1062
1210
|
}
|
|
1063
1211
|
if (inst.status === "paused") return { ok: true };
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
const
|
|
1068
|
-
if (
|
|
1069
|
-
return { ok: false, error: "
|
|
1212
|
+
|
|
1213
|
+
// v5.5.1 — SDK-backed: delegate to the dashboard.
|
|
1214
|
+
if (inst.liveSession) {
|
|
1215
|
+
const res = await postDashboardControl(`/api/background/${encodeURIComponent(instanceId)}/pause`, this.logger);
|
|
1216
|
+
if (!res.ok) {
|
|
1217
|
+
return { ok: false, error: res.error || "pause_failed" };
|
|
1070
1218
|
}
|
|
1219
|
+
await this.update(instanceId, {
|
|
1220
|
+
status: "paused",
|
|
1221
|
+
pausedAt: Date.now(),
|
|
1222
|
+
});
|
|
1223
|
+
this.logger.info(`bizar: paused background instance ${instanceId} (sdk-backed)`);
|
|
1224
|
+
return { ok: true };
|
|
1071
1225
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1226
|
+
|
|
1227
|
+
// Pre-v5.5.1 fallback: subprocess SIGSTOP. The runner is a stub now,
|
|
1228
|
+
// but if a state file from v5.5.0 is still around we try the old
|
|
1229
|
+
// path for compatibility.
|
|
1230
|
+
if (inst.processId === undefined) {
|
|
1074
1231
|
return { ok: false, error: "no_subprocess" };
|
|
1075
1232
|
}
|
|
1233
|
+
const pid = inst.processId;
|
|
1076
1234
|
const res = opencodeRunner.pauseAgent(pid);
|
|
1077
1235
|
if (!res.ok) return { ok: false, error: res.error || "pause_failed" };
|
|
1078
1236
|
await this.update(instanceId, {
|
|
@@ -1080,20 +1238,40 @@ export class InstanceManager {
|
|
|
1080
1238
|
pausedAt: Date.now(),
|
|
1081
1239
|
});
|
|
1082
1240
|
this.logger.info(
|
|
1083
|
-
`bizar: paused background instance ${instanceId} (pid=${pid})`,
|
|
1241
|
+
`bizar: paused background instance ${instanceId} (pid=${pid}, legacy)`,
|
|
1084
1242
|
);
|
|
1085
1243
|
return { ok: true };
|
|
1086
1244
|
}
|
|
1087
1245
|
|
|
1088
1246
|
/**
|
|
1089
|
-
* Resume a paused instance.
|
|
1090
|
-
*
|
|
1091
|
-
*
|
|
1247
|
+
* Resume a paused instance. In v5.5.1 (SDK-backed) delegates to the
|
|
1248
|
+
* dashboard's `POST /api/background/:id/resume`, which drains the
|
|
1249
|
+
* buffered events back to the WS bus. For pre-v5.5.1 instances with
|
|
1250
|
+
* a `processId`, falls back to the old SIGCONT path via the runner.
|
|
1251
|
+
*
|
|
1252
|
+
* Idempotent: resuming a non-paused instance is a no-op that returns
|
|
1253
|
+
* `{ ok: true }`.
|
|
1092
1254
|
*/
|
|
1093
1255
|
async resume(instanceId: string): Promise<{ ok: boolean; error?: string }> {
|
|
1094
1256
|
const inst = this.instances.get(instanceId);
|
|
1095
1257
|
if (!inst) return { ok: false, error: "instance_not_found" };
|
|
1096
1258
|
if (inst.status !== "paused") return { ok: true };
|
|
1259
|
+
|
|
1260
|
+
// v5.5.1 — SDK-backed: delegate to the dashboard.
|
|
1261
|
+
if (inst.liveSession) {
|
|
1262
|
+
const res = await postDashboardControl(`/api/background/${encodeURIComponent(instanceId)}/resume`, this.logger);
|
|
1263
|
+
if (!res.ok) {
|
|
1264
|
+
return { ok: false, error: res.error || "resume_failed" };
|
|
1265
|
+
}
|
|
1266
|
+
await this.update(instanceId, {
|
|
1267
|
+
status: "running",
|
|
1268
|
+
pausedAt: undefined,
|
|
1269
|
+
});
|
|
1270
|
+
this.logger.info(`bizar: resumed background instance ${instanceId} (sdk-backed)`);
|
|
1271
|
+
return { ok: true };
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// Pre-v5.5.1 fallback: subprocess SIGCONT.
|
|
1097
1275
|
if (inst.processId === undefined) {
|
|
1098
1276
|
return { ok: false, error: "no_subprocess" };
|
|
1099
1277
|
}
|
|
@@ -1104,7 +1282,7 @@ export class InstanceManager {
|
|
|
1104
1282
|
pausedAt: undefined,
|
|
1105
1283
|
});
|
|
1106
1284
|
this.logger.info(
|
|
1107
|
-
`bizar: resumed background instance ${instanceId} (pid=${inst.processId})`,
|
|
1285
|
+
`bizar: resumed background instance ${instanceId} (pid=${inst.processId}, legacy)`,
|
|
1108
1286
|
);
|
|
1109
1287
|
return { ok: true };
|
|
1110
1288
|
}
|