@geohar/un-bien 0.7.0 → 0.9.0

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.
Files changed (89) hide show
  1. package/README.md +237 -268
  2. package/dist/bin/launcher.js +38 -0
  3. package/dist/bin/launcher.js.map +1 -0
  4. package/dist/config.d.ts +41 -1
  5. package/dist/config.js.map +1 -1
  6. package/dist/daemon/install.d.ts +33 -35
  7. package/dist/daemon/install.js +43 -73
  8. package/dist/daemon/install.js.map +1 -1
  9. package/dist/enrich_tool_args.d.ts +9 -0
  10. package/dist/enrich_tool_args.js +171 -0
  11. package/dist/enrich_tool_args.js.map +1 -0
  12. package/dist/image_codec.d.ts +22 -0
  13. package/dist/image_codec.js +149 -0
  14. package/dist/image_codec.js.map +1 -0
  15. package/dist/index.d.ts +0 -26
  16. package/dist/index.js +235 -1237
  17. package/dist/index.js.map +1 -1
  18. package/dist/launch.d.ts +34 -0
  19. package/dist/launch.js +171 -0
  20. package/dist/launch.js.map +1 -0
  21. package/dist/launcher/launcher.d.ts +6 -0
  22. package/dist/launcher/launcher.js +188 -0
  23. package/dist/launcher/launcher.js.map +1 -0
  24. package/dist/pairing/crypto.d.ts +4 -0
  25. package/dist/pairing/crypto.js +10 -0
  26. package/dist/pairing/crypto.js.map +1 -1
  27. package/dist/pairing/peer_trust.d.ts +20 -0
  28. package/dist/pairing/peer_trust.js +82 -0
  29. package/dist/pairing/peer_trust.js.map +1 -0
  30. package/dist/pairing/storage.d.ts +51 -26
  31. package/dist/pairing/storage.js +346 -136
  32. package/dist/pairing/storage.js.map +1 -1
  33. package/dist/panel_bridge.d.ts +3 -1
  34. package/dist/panel_bridge.js +10 -10
  35. package/dist/panel_bridge.js.map +1 -1
  36. package/dist/rooms.d.ts +16 -0
  37. package/dist/rooms.js +22 -0
  38. package/dist/rooms.js.map +1 -1
  39. package/dist/session/debug_log.js +2 -2
  40. package/dist/session/debug_log.js.map +1 -1
  41. package/dist/session/local_config.js +2 -0
  42. package/dist/session/local_config.js.map +1 -1
  43. package/dist/session/rpc_envelope.d.ts +18 -0
  44. package/dist/session/rpc_envelope.js +1 -1
  45. package/dist/session/rpc_envelope.js.map +1 -1
  46. package/dist/session/rpc_inbound.d.ts +2 -2
  47. package/dist/session/rpc_inbound.js +1 -1
  48. package/dist/session/rpc_inbound.js.map +1 -1
  49. package/dist/subagent_rooms.d.ts +49 -0
  50. package/dist/subagent_rooms.js +787 -0
  51. package/dist/subagent_rooms.js.map +1 -0
  52. package/dist/transport/peer_channel.d.ts +7 -1
  53. package/dist/transport/peer_channel.js +8 -1
  54. package/dist/transport/peer_channel.js.map +1 -1
  55. package/dist/transport/relay_client.d.ts +19 -0
  56. package/dist/transport/relay_client.js.map +1 -1
  57. package/docs/daemon.md +65 -64
  58. package/package.json +13 -11
  59. package/service-templates/launchd.plist.template +5 -5
  60. package/service-templates/systemd.service.template +2 -2
  61. package/service-templates/task-launcher.vbs.template +5 -5
  62. package/service-templates/task-scheduler.xml.template +1 -1
  63. package/dist/bin/supervisord.js +0 -86
  64. package/dist/bin/supervisord.js.map +0 -1
  65. package/dist/daemon/client.d.ts +0 -20
  66. package/dist/daemon/client.js +0 -131
  67. package/dist/daemon/client.js.map +0 -1
  68. package/dist/daemon/control_protocol.d.ts +0 -176
  69. package/dist/daemon/control_protocol.js +0 -63
  70. package/dist/daemon/control_protocol.js.map +0 -1
  71. package/dist/daemon/cron_log.d.ts +0 -45
  72. package/dist/daemon/cron_log.js +0 -71
  73. package/dist/daemon/cron_log.js.map +0 -1
  74. package/dist/daemon/cron_registry.d.ts +0 -80
  75. package/dist/daemon/cron_registry.js +0 -198
  76. package/dist/daemon/cron_registry.js.map +0 -1
  77. package/dist/daemon/id.d.ts +0 -24
  78. package/dist/daemon/id.js +0 -36
  79. package/dist/daemon/id.js.map +0 -1
  80. package/dist/daemon/registry.d.ts +0 -63
  81. package/dist/daemon/registry.js +0 -153
  82. package/dist/daemon/registry.js.map +0 -1
  83. package/dist/daemon/rpc_child.d.ts +0 -194
  84. package/dist/daemon/rpc_child.js +0 -419
  85. package/dist/daemon/rpc_child.js.map +0 -1
  86. package/dist/daemon/supervisor.d.ts +0 -93
  87. package/dist/daemon/supervisor.js +0 -666
  88. package/dist/daemon/supervisor.js.map +0 -1
  89. /package/dist/bin/{supervisord.d.ts → launcher.d.ts} +0 -0
@@ -1,194 +0,0 @@
1
- import { EventEmitter } from "node:events";
2
- import type { DaemonState } from "./control_protocol.js";
3
- import { type LocalConfig } from "../session/local_config.js";
4
- /**
5
- * Wrapper around a `pi --mode rpc -e <extension>` child process for the
6
- * supervisor.
7
- *
8
- * Lifecycle:
9
- * - `spawn()` boots the child with the daemon's cwd + `UNBIEN_DAEMON=1`
10
- * env so the extension knows to skip the interactive wizard.
11
- * - `sendPrompt(text)` writes a Pi RPC `prompt` command to stdin.
12
- * - The child's stdout (Pi RPC events) is currently consumed line-by-line
13
- * and ignored — wave 2 only needs fire-and-forget. Later waves can
14
- * surface the events back through the supervisor's status op.
15
- * - Exit/crash fires `exit` event with `{code, signal, isCrash}` so the
16
- * supervisor can decide whether to auto-restart.
17
- *
18
- * Each `RpcChild` instance maps 1:1 to a registry entry (single cwd).
19
- * The supervisor owns the map of these and addresses them by `id`.
20
- */
21
- export interface RpcChildOptions {
22
- /** Path to the `pi` binary. Defaults to "pi" (must be on PATH). */
23
- piBin?: string;
24
- /** Absolute path to the un-bien `dist/index.js` to load as -e. */
25
- extensionPath: string;
26
- /** Working directory for the spawned process. Determines which local
27
- * config the extension reads. */
28
- cwd: string;
29
- /** Additional env vars merged on top of `process.env` + the mandatory
30
- * `UNBIEN_DAEMON=1`. */
31
- env?: NodeJS.ProcessEnv;
32
- /**
33
- * Daemon config injected into the child via `UNBIEN_DIRECT_CONFIG`
34
- * (JSON inline) instead of a per-cwd `.pi/un-bien/config.json` file. The
35
- * supervisor builds this from the registry. When set, the child reads it
36
- * env-first (see `loadLocalConfig`) and no config file is needed. Also the
37
- * source of the `--name` for the session. Falls back to the on-disk config
38
- * when omitted.
39
- */
40
- config?: LocalConfig;
41
- }
42
- export interface RpcChildExitEvent {
43
- code: number | null;
44
- signal: NodeJS.Signals | null;
45
- /** True when exit was not clean (non-zero or signal). */
46
- isCrash: boolean;
47
- }
48
- export declare const EXIT_DAEMON_FRESH_SESSION = 42;
49
- /**
50
- * Resolve the `pi` executable for `spawn` (plan/40, decision C). On Windows a
51
- * bare `pi` is actually `pi.cmd`/`pi.ps1`, and `spawn` won't find it without an
52
- * extension → resolve the real path via `where` (rather than `shell:true`,
53
- * which risks shell injection). An explicit path or an already-suffixed name is
54
- * used as-is. POSIX returns the name unchanged. Best-effort: if `where` fails,
55
- * fall back to the bare name (spawn will surface ENOENT honestly).
56
- *
57
- * `where` lists every match in PATH order, which on pi-node installs puts the
58
- * extensionless `pi` (a shell script) BEFORE `pi.cmd`. Naively taking the first
59
- * line spawns the unrunnable script → ENOENT → "crashed". So we prefer a result
60
- * with a real Windows executable extension and only fall back to the first line.
61
- */
62
- export declare function resolvePiBin(piBin: string, plat?: NodeJS.Platform): string;
63
- /** What to pass to `spawn`: the executable and any args to prepend before the
64
- * caller's args. On POSIX (and for `.exe`) `prefixArgs` is empty. */
65
- export interface PiSpawnTarget {
66
- command: string;
67
- prefixArgs: string[];
68
- }
69
- /**
70
- * Resolve `pi` to a directly-spawnable target (plan/40). On Windows `pi` is an
71
- * npm `.cmd` shim, which modern Node refuses to `spawn` without `shell:true`
72
- * (EINVAL, post CVE-2024-27980). Wrapping it in `cmd.exe` would (a) add a layer
73
- * that orphans the real `node` on kill and (b) complicate stdio — both fatal for
74
- * a long-lived RPC daemon the supervisor must stop/restart cleanly. Instead we
75
- * parse the shim to recover the underlying `node <cli.js>` and spawn that
76
- * directly: one process, clean signals, clean stdio.
77
- *
78
- * Falls back to the bare resolved path when not a parseable shim (e.g. a real
79
- * `pi.exe`, or POSIX) so `spawn` surfaces any real error honestly.
80
- */
81
- export declare function resolvePiSpawn(piBin: string, plat?: NodeJS.Platform, nodeExe?: string): PiSpawnTarget;
82
- /**
83
- * Extract the JS entry an npm `.cmd` shim launches. npm shims invoke
84
- * `"%_prog%" "%dp0%\<relative>\entry.js" %*`; we recover `<dir>\<relative>\entry.js`
85
- * (dir = the shim's own folder, i.e. `%dp0%`). Returns null when the shim
86
- * doesn't match the expected shape or the target is missing.
87
- */
88
- export declare function _npmShimTarget(cmdPath: string): string | null;
89
- /**
90
- * Maps an RPC stdout line to a busy-state transition: `message_start` → true
91
- * (a message is streaming), `message_end` → false. Other lines → null (no
92
- * change). Pure + exported for tests.
93
- *
94
- * NOTE (plan/39 detail 1): the Pi RPC stream has NO turn-level event — only
95
- * per-message start/end (and `response{command:"prompt"}` is emitted at
96
- * PREFLIGHT, i.e. turn START, not end). So this passive flag reflects
97
- * "a message is being produced right now"; the authoritative turn-busy signal
98
- * is `get_state.isStreaming` (see `RpcChild.refreshBusy`).
99
- */
100
- export declare function busyTransition(line: string): boolean | null;
101
- /**
102
- * CLI args for the daemon's `pi --mode rpc` child.
103
- *
104
- * `--continue` is the key bit: it resumes the **most recent** session for the
105
- * cwd (`SessionManager.continueRecent`, non-interactive — unlike `--resume`
106
- * which opens a picker). Without it every supervisor restart spun up a brand
107
- * new session file, piling up thousands of JSONLs per folder. With it, a
108
- * restart REUSES the one session; the app's `/new` (session_new) still rolls
109
- * it over to a fresh one, which the next restart then continues. First boot
110
- * (no prior session) just creates the first one.
111
- *
112
- * `--name <sessionName>`, when given, pins the session's display name to the
113
- * daemon's identity (its `agent_name`) so every restart shows up under the
114
- * same stable name in the picker/app instead of an auto-generated one. The
115
- * daemon's name is set at registration (`unbien create <cwd> --name "…"`).
116
- * Omitted when no name resolves, so the arg list stays minimal.
117
- *
118
- * `--approve` is mandatory for a daemon (pi ≥0.79 project trust): RPC mode is
119
- * non-interactive, so without an override Pi resolves an untrusted project
120
- * folder (any folder with `.pi/` or CLAUDE.md/AGENTS.md) to NOT trusted and
121
- * silently skips its `.pi/settings.json` (model/provider/keys), instructions,
122
- * resources and project extensions — the daemon then comes up with no model
123
- * and fails on the first turn. The operator already authorized this folder by
124
- * registering/launching a daemon in it, so `--approve` (trust-for-this-run) is
125
- * the correct non-interactive stance. (Does NOT affect the separate "extension
126
- * loaded twice" conflict, which comes from the extension being BOTH installed
127
- * in ~/.pi/agent/extensions or cwd/.pi/extensions AND passed via `-e`.)
128
- */
129
- export declare function rpcSpawnArgs(extensionPath: string, sessionName?: string, useContinue?: boolean): string[];
130
- export declare class RpcChild extends EventEmitter {
131
- private readonly opts;
132
- private child;
133
- private _state;
134
- private _startedAt;
135
- private _restartCount;
136
- /** True while a deliberate `stop()` is in flight. The process dies by
137
- * signal (SIGTERM/SIGKILL), which would otherwise look like a crash and
138
- * trip the supervisor's auto-restart — re-spawning a daemon the operator
139
- * just stopped/removed. Gating `isCrash` on this makes a deliberate stop a
140
- * clean exit (no restart). Reset on every `spawn()`. */
141
- private _stopping;
142
- /** Next spawn should create a fresh session instead of --continue. */
143
- private forceFreshSessionOnNextSpawn;
144
- /** Accumulates partial stdout lines while waiting for `\n`. */
145
- private stdoutBuf;
146
- /** Passive busy flag derived from the RPC stream (message_start/end). Hint
147
- * only; `refreshBusy` syncs it authoritatively via get_state.isStreaming. */
148
- private _busy;
149
- /** In-flight `get_state` requests, keyed by request id. */
150
- private readonly _statePending;
151
- constructor(opts: RpcChildOptions);
152
- get state(): DaemonState;
153
- /** Passive busy hint from the stream. Prefer `refreshBusy()` for an
154
- * authoritative check before acting on it (cron skip_if_busy). */
155
- get isBusy(): boolean;
156
- get pid(): number | undefined;
157
- get restartCount(): number;
158
- get uptimeMs(): number | undefined;
159
- /**
160
- * Spawn the child process. Idempotent for the same instance: a second
161
- * call while already running is a no-op.
162
- */
163
- spawn(): void;
164
- /**
165
- * Sends a Pi RPC `prompt` command to the child's stdin. Fire-and-forget
166
- * — we don't wait for the response (response would be the success ack;
167
- * the actual agent output streams via the relay/UDS, not back through
168
- * stdout).
169
- *
170
- * Returns false if the child isn't running (caller decides how to report).
171
- */
172
- sendPrompt(text: string, requestId?: string): boolean;
173
- /**
174
- * Asks the child to exit gracefully. Sends SIGTERM; if the child doesn't
175
- * exit within `timeoutMs`, escalates to SIGKILL. Resolves when the
176
- * `exit` event fires.
177
- */
178
- stop(timeoutMs?: number): Promise<void>;
179
- private _onStdout;
180
- private _handleStdoutLine;
181
- /**
182
- * Authoritative busy check: queries the child's `get_state` and syncs
183
- * `_busy` to `isStreaming`. Resolves the passive flag on timeout (no
184
- * response) or when the child isn't running. Used by the cron `skip_if_busy`
185
- * gate, where a false "not busy" would pile a prompt onto a live turn.
186
- */
187
- refreshBusy(timeoutMs?: number): Promise<boolean>;
188
- /** Test-only: feed a raw stdout line through the same handler. */
189
- _ingestStdoutForTest(line: string): void;
190
- private _onExit;
191
- /** Bumps the restart counter — called by the supervisor when it
192
- * decides to re-spawn after a crash. Exposed so tests can drive it. */
193
- noteRestart(): void;
194
- }
@@ -1,419 +0,0 @@
1
- import { execFileSync, spawn } from "node:child_process";
2
- import { EventEmitter } from "node:events";
3
- import { existsSync, readFileSync } from "node:fs";
4
- import { dirname, join } from "node:path";
5
- import { defaultAgentName, loadLocalConfig, } from "../session/local_config.js";
6
- export const EXIT_DAEMON_FRESH_SESSION = 42;
7
- /** Windows extensions `spawn` (without `shell`) can actually launch, in
8
- * preference order: native `.exe` first, then the `.cmd`/`.bat` shims (run via
9
- * cmd.exe). An extensionless `pi` is a Unix shell script Node CANNOT spawn on
10
- * Windows — picking it yields `spawn … ENOENT` and the daemon shows "crashed". */
11
- const WIN_EXECUTABLE_EXTS = [".exe", ".cmd", ".bat", ".com"];
12
- /**
13
- * Resolve the `pi` executable for `spawn` (plan/40, decision C). On Windows a
14
- * bare `pi` is actually `pi.cmd`/`pi.ps1`, and `spawn` won't find it without an
15
- * extension → resolve the real path via `where` (rather than `shell:true`,
16
- * which risks shell injection). An explicit path or an already-suffixed name is
17
- * used as-is. POSIX returns the name unchanged. Best-effort: if `where` fails,
18
- * fall back to the bare name (spawn will surface ENOENT honestly).
19
- *
20
- * `where` lists every match in PATH order, which on pi-node installs puts the
21
- * extensionless `pi` (a shell script) BEFORE `pi.cmd`. Naively taking the first
22
- * line spawns the unrunnable script → ENOENT → "crashed". So we prefer a result
23
- * with a real Windows executable extension and only fall back to the first line.
24
- */
25
- export function resolvePiBin(piBin, plat = process.platform) {
26
- if (plat !== "win32")
27
- return piBin;
28
- if (piBin.includes("\\") ||
29
- piBin.includes("/") ||
30
- /\.[a-z0-9]+$/i.test(piBin))
31
- return piBin;
32
- try {
33
- const out = execFileSync("where", [piBin], { encoding: "utf8" });
34
- const lines = out
35
- .split(/\r?\n/)
36
- .map((l) => l.trim())
37
- .filter(Boolean);
38
- const runnable = lines.find((l) => WIN_EXECUTABLE_EXTS.some((ext) => l.toLowerCase().endsWith(ext)));
39
- const chosen = runnable ?? lines[0];
40
- if (chosen)
41
- return chosen;
42
- }
43
- catch {
44
- /* `where` unavailable or `pi` not found — fall back to the bare name */
45
- }
46
- return piBin;
47
- }
48
- /**
49
- * Resolve `pi` to a directly-spawnable target (plan/40). On Windows `pi` is an
50
- * npm `.cmd` shim, which modern Node refuses to `spawn` without `shell:true`
51
- * (EINVAL, post CVE-2024-27980). Wrapping it in `cmd.exe` would (a) add a layer
52
- * that orphans the real `node` on kill and (b) complicate stdio — both fatal for
53
- * a long-lived RPC daemon the supervisor must stop/restart cleanly. Instead we
54
- * parse the shim to recover the underlying `node <cli.js>` and spawn that
55
- * directly: one process, clean signals, clean stdio.
56
- *
57
- * Falls back to the bare resolved path when not a parseable shim (e.g. a real
58
- * `pi.exe`, or POSIX) so `spawn` surfaces any real error honestly.
59
- */
60
- export function resolvePiSpawn(piBin, plat = process.platform, nodeExe = process.execPath) {
61
- const resolved = resolvePiBin(piBin, plat);
62
- if (plat !== "win32")
63
- return { command: resolved, prefixArgs: [] };
64
- if (/\.(cmd|bat)$/i.test(resolved)) {
65
- const target = _npmShimTarget(resolved);
66
- if (target)
67
- return { command: nodeExe, prefixArgs: [target] };
68
- }
69
- return { command: resolved, prefixArgs: [] };
70
- }
71
- /**
72
- * Extract the JS entry an npm `.cmd` shim launches. npm shims invoke
73
- * `"%_prog%" "%dp0%\<relative>\entry.js" %*`; we recover `<dir>\<relative>\entry.js`
74
- * (dir = the shim's own folder, i.e. `%dp0%`). Returns null when the shim
75
- * doesn't match the expected shape or the target is missing.
76
- */
77
- export function _npmShimTarget(cmdPath) {
78
- let content;
79
- try {
80
- content = readFileSync(cmdPath, "utf8");
81
- }
82
- catch {
83
- return null;
84
- }
85
- const m = content.match(/"%dp0%\\([^"]+\.[cm]?js)"/i);
86
- if (!m)
87
- return null;
88
- const target = join(dirname(cmdPath), m[1]);
89
- return existsSync(target) ? target : null;
90
- }
91
- /**
92
- * Maps an RPC stdout line to a busy-state transition: `message_start` → true
93
- * (a message is streaming), `message_end` → false. Other lines → null (no
94
- * change). Pure + exported for tests.
95
- *
96
- * NOTE (plan/39 detail 1): the Pi RPC stream has NO turn-level event — only
97
- * per-message start/end (and `response{command:"prompt"}` is emitted at
98
- * PREFLIGHT, i.e. turn START, not end). So this passive flag reflects
99
- * "a message is being produced right now"; the authoritative turn-busy signal
100
- * is `get_state.isStreaming` (see `RpcChild.refreshBusy`).
101
- */
102
- export function busyTransition(line) {
103
- let obj;
104
- try {
105
- obj = JSON.parse(line);
106
- }
107
- catch {
108
- return null;
109
- }
110
- const t = obj?.type;
111
- if (t === "message_start")
112
- return true;
113
- if (t === "message_end")
114
- return false;
115
- return null;
116
- }
117
- /** Parses a `get_state` RPC response line, returning its id + isStreaming. */
118
- function parseGetStateResponse(line) {
119
- let obj;
120
- try {
121
- obj = JSON.parse(line);
122
- }
123
- catch {
124
- return null;
125
- }
126
- const o = obj;
127
- if (o.type !== "response" || o.command !== "get_state")
128
- return null;
129
- const data = o.data;
130
- return {
131
- id: typeof o.id === "string" ? o.id : undefined,
132
- isStreaming: typeof data?.isStreaming === "boolean" ? data.isStreaming : undefined,
133
- };
134
- }
135
- /**
136
- * CLI args for the daemon's `pi --mode rpc` child.
137
- *
138
- * `--continue` is the key bit: it resumes the **most recent** session for the
139
- * cwd (`SessionManager.continueRecent`, non-interactive — unlike `--resume`
140
- * which opens a picker). Without it every supervisor restart spun up a brand
141
- * new session file, piling up thousands of JSONLs per folder. With it, a
142
- * restart REUSES the one session; the app's `/new` (session_new) still rolls
143
- * it over to a fresh one, which the next restart then continues. First boot
144
- * (no prior session) just creates the first one.
145
- *
146
- * `--name <sessionName>`, when given, pins the session's display name to the
147
- * daemon's identity (its `agent_name`) so every restart shows up under the
148
- * same stable name in the picker/app instead of an auto-generated one. The
149
- * daemon's name is set at registration (`unbien create <cwd> --name "…"`).
150
- * Omitted when no name resolves, so the arg list stays minimal.
151
- *
152
- * `--approve` is mandatory for a daemon (pi ≥0.79 project trust): RPC mode is
153
- * non-interactive, so without an override Pi resolves an untrusted project
154
- * folder (any folder with `.pi/` or CLAUDE.md/AGENTS.md) to NOT trusted and
155
- * silently skips its `.pi/settings.json` (model/provider/keys), instructions,
156
- * resources and project extensions — the daemon then comes up with no model
157
- * and fails on the first turn. The operator already authorized this folder by
158
- * registering/launching a daemon in it, so `--approve` (trust-for-this-run) is
159
- * the correct non-interactive stance. (Does NOT affect the separate "extension
160
- * loaded twice" conflict, which comes from the extension being BOTH installed
161
- * in ~/.pi/agent/extensions or cwd/.pi/extensions AND passed via `-e`.)
162
- */
163
- export function rpcSpawnArgs(extensionPath, sessionName, useContinue = true) {
164
- return [
165
- "--mode",
166
- "rpc",
167
- "--approve",
168
- ...(useContinue ? ["--continue"] : []),
169
- ...(sessionName ? ["--name", sessionName] : []),
170
- "-e",
171
- extensionPath,
172
- ];
173
- }
174
- export class RpcChild extends EventEmitter {
175
- opts;
176
- child = null;
177
- _state = "stopped";
178
- _startedAt = null;
179
- _restartCount = 0;
180
- /** True while a deliberate `stop()` is in flight. The process dies by
181
- * signal (SIGTERM/SIGKILL), which would otherwise look like a crash and
182
- * trip the supervisor's auto-restart — re-spawning a daemon the operator
183
- * just stopped/removed. Gating `isCrash` on this makes a deliberate stop a
184
- * clean exit (no restart). Reset on every `spawn()`. */
185
- _stopping = false;
186
- /** Next spawn should create a fresh session instead of --continue. */
187
- forceFreshSessionOnNextSpawn = false;
188
- /** Accumulates partial stdout lines while waiting for `\n`. */
189
- stdoutBuf = "";
190
- /** Passive busy flag derived from the RPC stream (message_start/end). Hint
191
- * only; `refreshBusy` syncs it authoritatively via get_state.isStreaming. */
192
- _busy = false;
193
- /** In-flight `get_state` requests, keyed by request id. */
194
- _statePending = new Map();
195
- constructor(opts) {
196
- super();
197
- this.opts = opts;
198
- }
199
- get state() {
200
- return this._state;
201
- }
202
- /** Passive busy hint from the stream. Prefer `refreshBusy()` for an
203
- * authoritative check before acting on it (cron skip_if_busy). */
204
- get isBusy() {
205
- return this._busy;
206
- }
207
- get pid() {
208
- return this.child?.pid;
209
- }
210
- get restartCount() {
211
- return this._restartCount;
212
- }
213
- get uptimeMs() {
214
- return this._startedAt === null ? undefined : Date.now() - this._startedAt;
215
- }
216
- /**
217
- * Spawn the child process. Idempotent for the same instance: a second
218
- * call while already running is a no-op.
219
- */
220
- spawn() {
221
- if (this.child)
222
- return;
223
- this._stopping = false; // fresh start — a later signal IS a real crash
224
- this._busy = false;
225
- this._state = "starting";
226
- const piTarget = resolvePiSpawn(this.opts.piBin ?? "pi");
227
- // Name the (single) daemon session after the daemon's configured identity,
228
- // so it shows up stably instead of an auto-generated name on each restart.
229
- // Prefer the supervisor-injected config; fall back to the on-disk file.
230
- const cfg = this.opts.config ?? loadLocalConfig(this.opts.cwd);
231
- const sessionName = cfg.agent_name ?? defaultAgentName(this.opts.cwd);
232
- const useContinue = !this.forceFreshSessionOnNextSpawn;
233
- this.forceFreshSessionOnNextSpawn = false;
234
- // On Windows `prefixArgs` carries pi's cli.js (we spawn node directly); on
235
- // POSIX it's empty and `command` is `pi` itself.
236
- const args = [
237
- ...piTarget.prefixArgs,
238
- ...rpcSpawnArgs(this.opts.extensionPath, sessionName, useContinue),
239
- ];
240
- const env = {
241
- ...process.env,
242
- ...this.opts.env,
243
- // Mandatory daemon marker — `_cmdRoot` in index.ts can use this to
244
- // bail early if local config is missing (no wizard in RPC mode).
245
- UNBIEN_DAEMON: "1",
246
- // Inject the daemon config inline so the child needs no config file.
247
- ...(this.opts.config
248
- ? { UNBIEN_DIRECT_CONFIG: JSON.stringify(this.opts.config) }
249
- : {}),
250
- };
251
- const child = spawn(piTarget.command, args, {
252
- cwd: this.opts.cwd,
253
- env,
254
- stdio: ["pipe", "pipe", "pipe"],
255
- // Windows: suppress any console window the child might allocate. With the
256
- // node+cli.js path (resolvePiSpawn) there's no cmd.exe layer, but keep this
257
- // as belt-and-suspenders alongside the supervisor's hidden wscript launcher
258
- // (service-templates/task-launcher.vbs.template). No-op on POSIX.
259
- windowsHide: process.platform === "win32",
260
- });
261
- this.child = child;
262
- this._startedAt = Date.now();
263
- this._state = "running";
264
- child.stdout?.on("data", (chunk) => this._onStdout(chunk));
265
- child.stderr?.on("data", (chunk) => {
266
- // Forward stderr to our own stderr so `journalctl --user -u unbien-supervisord`
267
- // sees daemon logs (with cwd prefix for disambiguation).
268
- process.stderr.write(`[${this.opts.cwd}] ${chunk.toString()}`);
269
- });
270
- child.on("exit", (code, signal) => this._onExit(code, signal));
271
- child.on("error", (err) => {
272
- // spawn() itself failed (e.g. `pi` binary not found).
273
- this._state = "crashed";
274
- process.stderr.write(`[unbien-supervisord] spawn failed for ${this.opts.cwd}: ${String(err)}\n`);
275
- this.emit("exit", { code: null, signal: null, isCrash: true });
276
- });
277
- this.emit("spawn", { pid: child.pid });
278
- }
279
- /**
280
- * Sends a Pi RPC `prompt` command to the child's stdin. Fire-and-forget
281
- * — we don't wait for the response (response would be the success ack;
282
- * the actual agent output streams via the relay/UDS, not back through
283
- * stdout).
284
- *
285
- * Returns false if the child isn't running (caller decides how to report).
286
- */
287
- sendPrompt(text, requestId) {
288
- if (!this.child || !this.child.stdin || this._state !== "running")
289
- return false;
290
- const cmd = {
291
- id: requestId ?? `sv-${Date.now()}`,
292
- type: "prompt",
293
- message: text,
294
- };
295
- try {
296
- this.child.stdin.write(JSON.stringify(cmd) + "\n");
297
- return true;
298
- }
299
- catch {
300
- return false;
301
- }
302
- }
303
- /**
304
- * Asks the child to exit gracefully. Sends SIGTERM; if the child doesn't
305
- * exit within `timeoutMs`, escalates to SIGKILL. Resolves when the
306
- * `exit` event fires.
307
- */
308
- async stop(timeoutMs = 5000) {
309
- if (!this.child)
310
- return;
311
- this._stopping = true; // deliberate — the upcoming signal-exit is NOT a crash
312
- const child = this.child;
313
- return new Promise((resolve) => {
314
- const onExit = () => {
315
- resolve();
316
- };
317
- this.once("exit", onExit);
318
- try {
319
- child.kill("SIGTERM");
320
- }
321
- catch {
322
- /* already dead */
323
- }
324
- const t = setTimeout(() => {
325
- try {
326
- child.kill("SIGKILL");
327
- }
328
- catch {
329
- /* race — already dead */
330
- }
331
- }, timeoutMs);
332
- this.once("exit", () => clearTimeout(t));
333
- });
334
- }
335
- _onStdout(chunk) {
336
- this.stdoutBuf += chunk.toString();
337
- let nl;
338
- while ((nl = this.stdoutBuf.indexOf("\n")) >= 0) {
339
- const line = this.stdoutBuf.slice(0, nl);
340
- this.stdoutBuf = this.stdoutBuf.slice(nl + 1);
341
- if (!line.trim())
342
- continue;
343
- this._handleStdoutLine(line);
344
- }
345
- }
346
- _handleStdoutLine(line) {
347
- const t = busyTransition(line);
348
- if (t !== null)
349
- this._busy = t;
350
- const gs = parseGetStateResponse(line);
351
- if (gs && gs.id) {
352
- const pending = this._statePending.get(gs.id);
353
- if (pending) {
354
- clearTimeout(pending.timer);
355
- this._statePending.delete(gs.id);
356
- if (typeof gs.isStreaming === "boolean")
357
- this._busy = gs.isStreaming;
358
- pending.resolve(this._busy);
359
- }
360
- }
361
- this.emit("stdout", line);
362
- }
363
- /**
364
- * Authoritative busy check: queries the child's `get_state` and syncs
365
- * `_busy` to `isStreaming`. Resolves the passive flag on timeout (no
366
- * response) or when the child isn't running. Used by the cron `skip_if_busy`
367
- * gate, where a false "not busy" would pile a prompt onto a live turn.
368
- */
369
- async refreshBusy(timeoutMs = 1500) {
370
- if (this._state !== "running" || !this.child?.stdin)
371
- return this._busy;
372
- const id = `gs-${Date.now()}-${Math.round(Math.random() * 1e6)}`;
373
- return new Promise((resolve) => {
374
- const timer = setTimeout(() => {
375
- this._statePending.delete(id);
376
- resolve(this._busy);
377
- }, timeoutMs);
378
- this._statePending.set(id, { resolve, timer });
379
- try {
380
- this.child.stdin.write(JSON.stringify({ id, type: "get_state" }) + "\n");
381
- }
382
- catch {
383
- clearTimeout(timer);
384
- this._statePending.delete(id);
385
- resolve(this._busy);
386
- }
387
- });
388
- }
389
- /** Test-only: feed a raw stdout line through the same handler. */
390
- _ingestStdoutForTest(line) {
391
- this._handleStdoutLine(line);
392
- }
393
- _onExit(code, signal) {
394
- // Daemon app-action `/new`: child exits with a private code, supervisor
395
- // restarts it, and the next spawn omits --continue once to create a fresh
396
- // session. Later restarts go back to --continue.
397
- if (code === EXIT_DAEMON_FRESH_SESSION)
398
- this.forceFreshSessionOnNextSpawn = true;
399
- // A deliberate stop() kills by signal — not a crash, so the supervisor
400
- // must NOT auto-restart it. Only an UNexpected exit counts as a crash.
401
- const isCrash = !this._stopping && (code !== 0 || signal !== null);
402
- this._state = isCrash ? "crashed" : "stopped";
403
- this.child = null;
404
- this._startedAt = null;
405
- this._busy = false;
406
- for (const p of this._statePending.values()) {
407
- clearTimeout(p.timer);
408
- p.resolve(false);
409
- }
410
- this._statePending.clear();
411
- this.emit("exit", { code, signal, isCrash });
412
- }
413
- /** Bumps the restart counter — called by the supervisor when it
414
- * decides to re-spawn after a crash. Exposed so tests can drive it. */
415
- noteRestart() {
416
- this._restartCount += 1;
417
- }
418
- }
419
- //# sourceMappingURL=rpc_child.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rpc_child.js","sourceRoot":"","sources":["../../src/daemon/rpc_child.ts"],"names":[],"mappings":"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"}