@almyty/runner 1.2.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 (59) hide show
  1. package/README.md +72 -0
  2. package/dist/binaries.d.ts +30 -0
  3. package/dist/binaries.js +99 -0
  4. package/dist/binaries.js.map +1 -0
  5. package/dist/cli.d.ts +12 -0
  6. package/dist/cli.js +128 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/coding-agents/detect.d.ts +12 -0
  9. package/dist/coding-agents/detect.js +35 -0
  10. package/dist/coding-agents/detect.js.map +1 -0
  11. package/dist/coding-agents/index.d.ts +15 -0
  12. package/dist/coding-agents/index.js +15 -0
  13. package/dist/coding-agents/index.js.map +1 -0
  14. package/dist/coding-agents/registry.d.ts +9 -0
  15. package/dist/coding-agents/registry.js +260 -0
  16. package/dist/coding-agents/registry.js.map +1 -0
  17. package/dist/coding-agents/spawn-spec.d.ts +47 -0
  18. package/dist/coding-agents/spawn-spec.js +58 -0
  19. package/dist/coding-agents/spawn-spec.js.map +1 -0
  20. package/dist/coding-agents/status.d.ts +38 -0
  21. package/dist/coding-agents/status.js +111 -0
  22. package/dist/coding-agents/status.js.map +1 -0
  23. package/dist/coding-agents/types.d.ts +104 -0
  24. package/dist/coding-agents/types.js +18 -0
  25. package/dist/coding-agents/types.js.map +1 -0
  26. package/dist/coding-sessions.d.ts +80 -0
  27. package/dist/coding-sessions.js +121 -0
  28. package/dist/coding-sessions.js.map +1 -0
  29. package/dist/config.d.ts +49 -0
  30. package/dist/config.js +180 -0
  31. package/dist/config.js.map +1 -0
  32. package/dist/daemon.d.ts +71 -0
  33. package/dist/daemon.js +353 -0
  34. package/dist/daemon.js.map +1 -0
  35. package/dist/handlers.d.ts +39 -0
  36. package/dist/handlers.js +299 -0
  37. package/dist/handlers.js.map +1 -0
  38. package/dist/index.d.ts +17 -0
  39. package/dist/index.js +18 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/policy.d.ts +16 -0
  42. package/dist/policy.js +134 -0
  43. package/dist/policy.js.map +1 -0
  44. package/dist/process-manager.d.ts +107 -0
  45. package/dist/process-manager.js +334 -0
  46. package/dist/process-manager.js.map +1 -0
  47. package/dist/protocol.d.ts +53 -0
  48. package/dist/protocol.js +39 -0
  49. package/dist/protocol.js.map +1 -0
  50. package/dist/runtime-info.d.ts +23 -0
  51. package/dist/runtime-info.js +35 -0
  52. package/dist/runtime-info.js.map +1 -0
  53. package/dist/streamable-client.d.ts +69 -0
  54. package/dist/streamable-client.js +204 -0
  55. package/dist/streamable-client.js.map +1 -0
  56. package/dist/types.d.ts +112 -0
  57. package/dist/types.js +21 -0
  58. package/dist/types.js.map +1 -0
  59. package/package.json +54 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/coding-agents/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
@@ -0,0 +1,80 @@
1
+ import { ProcessManager } from './process-manager.js';
2
+ import { type CodingAgentSpec } from './coding-agents/index.js';
3
+ import { type SpawnOptions } from './types.js';
4
+ /**
5
+ * Daemon-side registry of interactive coding sessions — the runner half of
6
+ * the chat-to-runner coding bridge.
7
+ *
8
+ * A coding session is one spawned coding CLI (claude, codex, gemini, ...)
9
+ * driven line-based over stdio (pipe mode, NOT a PTY — full terminal
10
+ * emulation is deliberately out of scope for v1). Output chunks are
11
+ * ANSI-stripped and pushed upstream as `coding.output` event envelopes via
12
+ * the emitter the daemon wires in (the same channel heartbeats ride); exit
13
+ * is pushed as `coding.exit`.
14
+ *
15
+ * Sessions are namespaced under a synthetic workspace (`coding:<sessionId>`)
16
+ * so they reuse the ProcessManager's bookkeeping, capacity accounting, and
17
+ * cross-workspace isolation without inventing a parallel process table.
18
+ */
19
+ export interface CodingStartInput {
20
+ /** The task prompt handed to the CLI as its final positional argument. */
21
+ task: string;
22
+ /** Working directory; defaults to the daemon user's home. */
23
+ cwd?: string;
24
+ /** Model pin, where the CLI supports a plain --model flag. */
25
+ model?: string;
26
+ /** Extra argv appended before the task. */
27
+ extraArgs?: string[];
28
+ }
29
+ export interface CodingSessionRecord {
30
+ sessionId: string;
31
+ /** Coding-agent platform id (claude, codex, ...). */
32
+ agent: string;
33
+ binary: string;
34
+ processId: string;
35
+ workspaceId: string;
36
+ cwd: string;
37
+ task: string;
38
+ startedAt: string;
39
+ status: 'running' | 'exited' | 'killed';
40
+ exitCode: number | null;
41
+ }
42
+ export type CodingEventPayload = {
43
+ kind: 'coding.output';
44
+ sessionId: string;
45
+ agent: string;
46
+ data: string;
47
+ seq: number;
48
+ } | {
49
+ kind: 'coding.exit';
50
+ sessionId: string;
51
+ agent: string;
52
+ exitCode: number | null;
53
+ signal: string | null;
54
+ };
55
+ export type CodingEmitter = (payload: CodingEventPayload) => void;
56
+ export declare class CodingSessionManager {
57
+ private readonly processes;
58
+ private readonly emit;
59
+ private readonly sessions;
60
+ constructor(processes: ProcessManager, emit: CodingEmitter);
61
+ /**
62
+ * Build the SpawnOptions for a session WITHOUT starting it, so the caller
63
+ * (handlers.ts) can run the result through the execution policy first —
64
+ * the same gate process.spawn and agent.spawn go through.
65
+ */
66
+ buildStartOptions(spec: CodingAgentSpec, input: CodingStartInput): SpawnOptions;
67
+ /** Spawn the CLI and start streaming its output as coding.* events. */
68
+ start(spec: CodingAgentSpec, input: CodingStartInput, opts: SpawnOptions): Promise<CodingSessionRecord>;
69
+ /** Write a line of input to the session's stdin. */
70
+ input(sessionId: string, data: string): void;
71
+ /** Snapshot one session (throws if unknown). */
72
+ status(sessionId: string): CodingSessionRecord & {
73
+ idleMs: number | null;
74
+ };
75
+ /** All sessions, newest last. */
76
+ list(): CodingSessionRecord[];
77
+ /** Stop a session (TERM by default, KILL when force). Idempotent. */
78
+ stop(sessionId: string, force?: boolean): CodingSessionRecord;
79
+ private getOrThrow;
80
+ }
@@ -0,0 +1,121 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { homedir } from 'os';
3
+ import { buildAgentSpawn, stripVtEscapes, } from './coding-agents/index.js';
4
+ import { RunnerError, RUNNER_ERROR_CODES } from './types.js';
5
+ export class CodingSessionManager {
6
+ processes;
7
+ emit;
8
+ sessions = new Map();
9
+ constructor(processes, emit) {
10
+ this.processes = processes;
11
+ this.emit = emit;
12
+ }
13
+ /**
14
+ * Build the SpawnOptions for a session WITHOUT starting it, so the caller
15
+ * (handlers.ts) can run the result through the execution policy first —
16
+ * the same gate process.spawn and agent.spawn go through.
17
+ */
18
+ buildStartOptions(spec, input) {
19
+ const opts = buildAgentSpawn(spec, {
20
+ autoApprove: true,
21
+ model: input.model,
22
+ cwd: input.cwd ?? homedir(),
23
+ extraArgs: [...(input.extraArgs ?? []), input.task],
24
+ });
25
+ // Line-based stdio for v1: no PTY, stdout/stderr piped and merged.
26
+ opts.pty = false;
27
+ return opts;
28
+ }
29
+ /** Spawn the CLI and start streaming its output as coding.* events. */
30
+ async start(spec, input, opts) {
31
+ const sessionId = `cs_${randomUUID()}`;
32
+ const workspaceId = `coding:${sessionId}`;
33
+ const handle = await this.processes.spawn(workspaceId, opts);
34
+ const record = {
35
+ sessionId,
36
+ agent: spec.id,
37
+ binary: opts.binary,
38
+ processId: handle.processId,
39
+ workspaceId,
40
+ cwd: opts.cwd ?? homedir(),
41
+ task: input.task,
42
+ startedAt: new Date().toISOString(),
43
+ status: 'running',
44
+ exitCode: null,
45
+ };
46
+ const session = { record, seq: 0, unsubscribe: () => { } };
47
+ session.unsubscribe = this.processes.observe(workspaceId, handle.processId, {
48
+ onData: (chunk) => {
49
+ // Strip ANSI/VT sequences so the chat transcript gets plain text.
50
+ // Escape sequences split across chunk boundaries can leak fragments;
51
+ // acceptable for v1 line-based output.
52
+ const clean = stripVtEscapes(chunk);
53
+ if (!clean)
54
+ return;
55
+ session.seq += 1;
56
+ this.emit({
57
+ kind: 'coding.output',
58
+ sessionId,
59
+ agent: spec.id,
60
+ data: clean,
61
+ seq: session.seq,
62
+ });
63
+ },
64
+ onExit: (info) => {
65
+ record.status = info.signal ? 'killed' : 'exited';
66
+ record.exitCode = info.exitCode;
67
+ session.unsubscribe();
68
+ this.emit({
69
+ kind: 'coding.exit',
70
+ sessionId,
71
+ agent: spec.id,
72
+ exitCode: info.exitCode,
73
+ signal: info.signal,
74
+ });
75
+ },
76
+ });
77
+ this.sessions.set(sessionId, session);
78
+ return record;
79
+ }
80
+ /** Write a line of input to the session's stdin. */
81
+ input(sessionId, data) {
82
+ const session = this.getOrThrow(sessionId);
83
+ if (session.record.status !== 'running') {
84
+ throw new RunnerError(`coding session ${sessionId} has already exited`, RUNNER_ERROR_CODES.PROCESS_ALREADY_EXITED);
85
+ }
86
+ const line = data.endsWith('\n') ? data : `${data}\n`;
87
+ this.processes.write(session.record.workspaceId, session.record.processId, line);
88
+ }
89
+ /** Snapshot one session (throws if unknown). */
90
+ status(sessionId) {
91
+ const session = this.getOrThrow(sessionId);
92
+ let idleMs = null;
93
+ try {
94
+ idleMs = this.processes.snapshot(session.record.workspaceId, session.record.processId).idleMs;
95
+ }
96
+ catch {
97
+ // Process already forgotten; the record still answers.
98
+ }
99
+ return { ...session.record, idleMs };
100
+ }
101
+ /** All sessions, newest last. */
102
+ list() {
103
+ return [...this.sessions.values()].map((s) => ({ ...s.record }));
104
+ }
105
+ /** Stop a session (TERM by default, KILL when force). Idempotent. */
106
+ stop(sessionId, force = false) {
107
+ const session = this.getOrThrow(sessionId);
108
+ if (session.record.status === 'running') {
109
+ this.processes.signal(session.record.workspaceId, session.record.processId, force ? 'KILL' : 'TERM');
110
+ }
111
+ return { ...session.record };
112
+ }
113
+ getOrThrow(sessionId) {
114
+ const session = this.sessions.get(sessionId);
115
+ if (!session) {
116
+ throw new RunnerError(`unknown coding session ${sessionId}`, RUNNER_ERROR_CODES.PROCESS_NOT_FOUND);
117
+ }
118
+ return session;
119
+ }
120
+ }
121
+ //# sourceMappingURL=coding-sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coding-sessions.js","sourceRoot":"","sources":["../src/coding-sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import { ResolvedConfig } from './types.js';
2
+ /**
3
+ * Config layering, lowest precedence first:
4
+ *
5
+ * 1. Built-in defaults (most restrictive: container isolation,
6
+ * no network, no installs)
7
+ * 2. ~/.almyty/config.json (global user)
8
+ * 3. ./.almyty/config.json (project-local)
9
+ * 4. Environment variables (ALMYTY_*)
10
+ * 5. CLI flags
11
+ *
12
+ * Backend overrides apply at registration time and only constrain
13
+ * (lower limits, smaller path allowlists, more deny patterns); they
14
+ * never escalate. Backend overrides aren't applied here — they merge
15
+ * in after the runner registers and the backend returns its
16
+ * effective config.
17
+ *
18
+ * The format mirrors the convention `@almyty/auth` already established:
19
+ * plain JSON, owner-readable, in `~/.almyty/`. No TOML, no YAML, no
20
+ * cosmiconfig.
21
+ */
22
+ export declare const DEFAULT_BINARY_PROBE_LIST: string[];
23
+ export declare const DEFAULT_BACKEND_URL = "https://api.almyty.com";
24
+ export declare const DEFAULTS: ResolvedConfig;
25
+ export declare const GLOBAL_CONFIG_PATH: string;
26
+ export declare const PROJECT_CONFIG_PATH: string;
27
+ /**
28
+ * Inputs to the loader, after argv has been parsed but before any
29
+ * file IO. The loader stays pure-ish: it takes filesystem readers and
30
+ * env as parameters so tests can substitute them without monkey-
31
+ * patching `fs` or `process.env`.
32
+ */
33
+ export interface LoadConfigInputs {
34
+ /** CLI flag overrides. Highest precedence. */
35
+ flags?: Partial<ResolvedConfig> & {
36
+ configPath?: string;
37
+ };
38
+ /** Explicit env map. Defaults to process.env at call site. */
39
+ env?: Record<string, string | undefined>;
40
+ /** Filesystem reader. Defaults to fs.readFileSync. */
41
+ readFile?: (path: string) => string | null;
42
+ /** File existence check. */
43
+ exists?: (path: string) => boolean;
44
+ /** Override global config path (testing). */
45
+ globalPath?: string;
46
+ /** Override project config path (testing). */
47
+ projectPath?: string;
48
+ }
49
+ export declare function loadConfig(inputs?: LoadConfigInputs): ResolvedConfig;
package/dist/config.js ADDED
@@ -0,0 +1,180 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { join, isAbsolute, resolve as pathResolve } from 'path';
4
+ import { allProbeBinaries } from './coding-agents/index.js';
5
+ /**
6
+ * Config layering, lowest precedence first:
7
+ *
8
+ * 1. Built-in defaults (most restrictive: container isolation,
9
+ * no network, no installs)
10
+ * 2. ~/.almyty/config.json (global user)
11
+ * 3. ./.almyty/config.json (project-local)
12
+ * 4. Environment variables (ALMYTY_*)
13
+ * 5. CLI flags
14
+ *
15
+ * Backend overrides apply at registration time and only constrain
16
+ * (lower limits, smaller path allowlists, more deny patterns); they
17
+ * never escalate. Backend overrides aren't applied here — they merge
18
+ * in after the runner registers and the backend returns its
19
+ * effective config.
20
+ *
21
+ * The format mirrors the convention `@almyty/auth` already established:
22
+ * plain JSON, owner-readable, in `~/.almyty/`. No TOML, no YAML, no
23
+ * cosmiconfig.
24
+ */
25
+ export const DEFAULT_BINARY_PROBE_LIST = [
26
+ // language runtimes, VCS, package managers, container runtimes
27
+ 'node', 'python', 'python3', 'git',
28
+ 'npm', 'pip', 'cargo', 'go', 'rustc',
29
+ 'docker', 'podman',
30
+ // every coding-agent CLI we support (claude, codex, gemini, cursor-agent,
31
+ // opencode, crush, copilot, grok, hermes, vibe, openclaw, aider, + aliases)
32
+ ...allProbeBinaries(),
33
+ ];
34
+ export const DEFAULT_BACKEND_URL = 'https://api.almyty.com';
35
+ export const DEFAULTS = {
36
+ name: '',
37
+ labels: {},
38
+ config: {
39
+ // Most-restrictive defaults: container isolation, no installs,
40
+ // no network. Users opt out of any of these explicitly in config
41
+ // or via flags; the design point is that an unconfigured runner
42
+ // is the safest one.
43
+ defaultIsolation: 'container',
44
+ maxConcurrent: 4,
45
+ allowedCwdRoots: [],
46
+ denyPatterns: [],
47
+ networkBlocked: true,
48
+ installBlocked: true,
49
+ },
50
+ binaryProbeList: DEFAULT_BINARY_PROBE_LIST,
51
+ backendUrl: DEFAULT_BACKEND_URL,
52
+ };
53
+ export const GLOBAL_CONFIG_PATH = join(homedir(), '.almyty', 'config.json');
54
+ export const PROJECT_CONFIG_PATH = join(process.cwd(), '.almyty', 'config.json');
55
+ export function loadConfig(inputs = {}) {
56
+ const env = inputs.env ?? process.env;
57
+ const readFile = inputs.readFile ?? safeRead;
58
+ const exists = inputs.exists ?? existsSync;
59
+ const globalPath = inputs.globalPath ?? GLOBAL_CONFIG_PATH;
60
+ const projectPath = inputs.projectPath ?? PROJECT_CONFIG_PATH;
61
+ let resolved = clone(DEFAULTS);
62
+ // Layer 2: global config file.
63
+ if (exists(globalPath)) {
64
+ resolved = mergeIn(resolved, parseConfigFile(readFile(globalPath), globalPath));
65
+ }
66
+ // Layer 3: project-local config file.
67
+ if (exists(projectPath)) {
68
+ resolved = mergeIn(resolved, parseConfigFile(readFile(projectPath), projectPath));
69
+ }
70
+ // Layer 3.5: explicit --config <path> takes precedence over both
71
+ // global and project, but the spec orders it as a flag (layer 4).
72
+ // Treat it as a flag-driven file load: same precedence as flags.
73
+ if (inputs.flags?.configPath) {
74
+ const explicit = inputs.flags.configPath;
75
+ const abs = isAbsolute(explicit) ? explicit : pathResolve(process.cwd(), explicit);
76
+ if (!exists(abs)) {
77
+ throw new Error(`config file not found: ${abs}`);
78
+ }
79
+ resolved = mergeIn(resolved, parseConfigFile(readFile(abs), abs));
80
+ }
81
+ // Layer 4: env overrides. Limited to the few fields it makes sense
82
+ // to set without a config file: backend URL, runner name, isolation.
83
+ if (env.ALMYTY_URL)
84
+ resolved.backendUrl = env.ALMYTY_URL;
85
+ if (env.ALMYTY_RUNNER_NAME)
86
+ resolved.name = env.ALMYTY_RUNNER_NAME;
87
+ if (env.ALMYTY_RUNNER_ISOLATION) {
88
+ if (!isIsolation(env.ALMYTY_RUNNER_ISOLATION)) {
89
+ throw new Error(`ALMYTY_RUNNER_ISOLATION must be one of: container, host`);
90
+ }
91
+ resolved.config.defaultIsolation = env.ALMYTY_RUNNER_ISOLATION;
92
+ }
93
+ // Layer 5: CLI flags. Most have already been validated by the
94
+ // parser; we just merge.
95
+ const f = inputs.flags ?? {};
96
+ if (f.name)
97
+ resolved.name = f.name;
98
+ if (f.labels)
99
+ resolved.labels = { ...resolved.labels, ...f.labels };
100
+ if (f.backendUrl)
101
+ resolved.backendUrl = f.backendUrl;
102
+ if (f.config)
103
+ resolved.config = mergeRunnerConfig(resolved.config, f.config);
104
+ if (f.binaryProbeList && f.binaryProbeList.length > 0) {
105
+ resolved.binaryProbeList = f.binaryProbeList;
106
+ }
107
+ if (!resolved.name) {
108
+ // A runner without a name is meaningless: the backend keys
109
+ // single-runner enforcement on (user, org, name). Surface a
110
+ // clear error early rather than letting the registration call
111
+ // fail with a less-helpful message.
112
+ throw new Error('runner name is required (set via --name, ALMYTY_RUNNER_NAME, or config.name)');
113
+ }
114
+ // The runner's command channel (RCE-capable) and its bearer token ride
115
+ // this URL — refuse plaintext http:// to a remote host. http is allowed
116
+ // only for loopback (local dev).
117
+ try {
118
+ const u = new URL(resolved.backendUrl);
119
+ const host = u.hostname;
120
+ const loopback = host === 'localhost' || host === '127.0.0.1' || host === '::1' || host.endsWith('.localhost');
121
+ if (u.protocol !== 'https:' && !loopback) {
122
+ throw new Error(`Refusing an insecure ${u.protocol}// backend URL for a remote host: ${resolved.backendUrl}. ` +
123
+ 'Use https:// (http is only allowed for localhost).');
124
+ }
125
+ }
126
+ catch (e) {
127
+ if (e instanceof Error && e.message.startsWith('Refusing'))
128
+ throw e;
129
+ throw new Error(`Invalid backend URL: ${resolved.backendUrl}`);
130
+ }
131
+ return resolved;
132
+ }
133
+ // ── helpers ─────────────────────────────────────────────────────────
134
+ function safeRead(path) {
135
+ try {
136
+ return readFileSync(path, 'utf-8');
137
+ }
138
+ catch {
139
+ return null;
140
+ }
141
+ }
142
+ function parseConfigFile(text, path) {
143
+ if (!text)
144
+ return {};
145
+ try {
146
+ const parsed = JSON.parse(text);
147
+ return parsed && typeof parsed === 'object' ? parsed : {};
148
+ }
149
+ catch (err) {
150
+ throw new Error(`failed to parse ${path}: ${err.message}`);
151
+ }
152
+ }
153
+ function mergeIn(into, layer) {
154
+ return {
155
+ name: layer.name ?? into.name,
156
+ labels: { ...into.labels, ...(layer.labels ?? {}) },
157
+ backendUrl: layer.backendUrl ?? into.backendUrl,
158
+ binaryProbeList: layer.binaryProbeList && layer.binaryProbeList.length > 0
159
+ ? layer.binaryProbeList
160
+ : into.binaryProbeList,
161
+ config: layer.config ? mergeRunnerConfig(into.config, layer.config) : into.config,
162
+ };
163
+ }
164
+ function mergeRunnerConfig(base, layer) {
165
+ return {
166
+ defaultIsolation: layer.defaultIsolation ?? base.defaultIsolation,
167
+ maxConcurrent: layer.maxConcurrent ?? base.maxConcurrent,
168
+ allowedCwdRoots: layer.allowedCwdRoots ?? base.allowedCwdRoots,
169
+ denyPatterns: layer.denyPatterns ?? base.denyPatterns,
170
+ networkBlocked: layer.networkBlocked ?? base.networkBlocked,
171
+ installBlocked: layer.installBlocked ?? base.installBlocked,
172
+ };
173
+ }
174
+ function isIsolation(value) {
175
+ return value === 'container' || value === 'host';
176
+ }
177
+ function clone(value) {
178
+ return JSON.parse(JSON.stringify(value));
179
+ }
180
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC;AAEhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAG5D;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,+DAA+D;IAC/D,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK;IAClC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO;IACpC,QAAQ,EAAE,QAAQ;IAClB,0EAA0E;IAC1E,4EAA4E;IAC5E,GAAG,gBAAgB,EAAE;CACtB,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,wBAAwB,CAAC;AAE5D,MAAM,CAAC,MAAM,QAAQ,GAAmB;IACtC,IAAI,EAAE,EAAE;IACR,MAAM,EAAE,EAAE;IACV,MAAM,EAAE;QACN,+DAA+D;QAC/D,iEAAiE;QACjE,gEAAgE;QAChE,qBAAqB;QACrB,gBAAgB,EAAE,WAAW;QAC7B,aAAa,EAAE,CAAC;QAChB,eAAe,EAAE,EAAE;QACnB,YAAY,EAAE,EAAE;QAChB,cAAc,EAAE,IAAI;QACpB,cAAc,EAAE,IAAI;KACrB;IACD,eAAe,EAAE,yBAAyB;IAC1C,UAAU,EAAE,mBAAmB;CAChC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AAC5E,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AAuBjF,MAAM,UAAU,UAAU,CAAC,SAA2B,EAAE;IACtD,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACtC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,QAAQ,CAAC;IAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,UAAU,CAAC;IAC3C,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,kBAAkB,CAAC;IAC3D,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,mBAAmB,CAAC;IAE9D,IAAI,QAAQ,GAAmB,KAAK,CAAC,QAAQ,CAAC,CAAC;IAE/C,+BAA+B;IAC/B,IAAI,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QACvB,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;IAClF,CAAC;IACD,sCAAsC;IACtC,IAAI,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;QACxB,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC;IACpF,CAAC;IACD,iEAAiE;IACjE,kEAAkE;IAClE,iEAAiE;IACjE,IAAI,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;QACzC,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;QACnF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,EAAE,CAAC,CAAC;QACnD,CAAC;QACD,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,mEAAmE;IACnE,qEAAqE;IACrE,IAAI,GAAG,CAAC,UAAU;QAAE,QAAQ,CAAC,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC;IACzD,IAAI,GAAG,CAAC,kBAAkB;QAAE,QAAQ,CAAC,IAAI,GAAG,GAAG,CAAC,kBAAkB,CAAC;IACnE,IAAI,GAAG,CAAC,uBAAuB,EAAE,CAAC;QAChC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,uBAAuB,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QAC7E,CAAC;QACD,QAAQ,CAAC,MAAM,CAAC,gBAAgB,GAAG,GAAG,CAAC,uBAAuB,CAAC;IACjE,CAAC;IAED,8DAA8D;IAC9D,yBAAyB;IACzB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,CAAC,IAAI;QAAE,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;IACnC,IAAI,CAAC,CAAC,MAAM;QAAE,QAAQ,CAAC,MAAM,GAAG,EAAE,GAAG,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;IACpE,IAAI,CAAC,CAAC,UAAU;QAAE,QAAQ,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;IACrD,IAAI,CAAC,CAAC,MAAM;QAAE,QAAQ,CAAC,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IAC7E,IAAI,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtD,QAAQ,CAAC,eAAe,GAAG,CAAC,CAAC,eAAe,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnB,2DAA2D;QAC3D,4DAA4D;QAC5D,8DAA8D;QAC9D,oCAAoC;QACpC,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAC;IAClG,CAAC;IAED,uEAAuE;IACvE,wEAAwE;IACxE,iCAAiC;IACjC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC;QACxB,MAAM,QAAQ,GACZ,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QAChG,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CACb,wBAAwB,CAAC,CAAC,QAAQ,qCAAqC,QAAQ,CAAC,UAAU,IAAI;gBAC5F,oDAAoD,CACvD,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,MAAM,CAAC,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,wBAAwB,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,uEAAuE;AAEvE,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,IAAmB,EAAE,IAAY;IACxD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAE,MAAkC,CAAC,CAAC,CAAC,EAAE,CAAC;IACzF,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,IAAoB,EAAE,KAA8B;IACnE,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI;QAC7B,MAAM,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE;QACnD,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU;QAC/C,eAAe,EAAE,KAAK,CAAC,eAAe,IAAI,KAAK,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC;YACxE,CAAC,CAAC,KAAK,CAAC,eAAe;YACvB,CAAC,CAAC,IAAI,CAAC,eAAe;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM;KAClF,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAkB,EAAE,KAA4B;IACzE,OAAO;QACL,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,IAAI,IAAI,CAAC,gBAAgB;QACjE,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,IAAI,CAAC,aAAa;QACxD,eAAe,EAAE,KAAK,CAAC,eAAe,IAAI,IAAI,CAAC,eAAe;QAC9D,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI,CAAC,YAAY;QACrD,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,IAAI,CAAC,cAAc;QAC3D,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,IAAI,CAAC,cAAc;KAC5D,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,MAAM,CAAC;AACnD,CAAC;AAED,SAAS,KAAK,CAAI,KAAQ;IACxB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,71 @@
1
+ import { ResolvedConfig } from './types.js';
2
+ export interface DaemonStatus {
3
+ pid: number;
4
+ startedAt: string;
5
+ runnerName: string;
6
+ backendUrl: string;
7
+ runnerId: string | null;
8
+ sessionId: string | null;
9
+ inUseProcesses: number;
10
+ connectionState: 'connecting' | 'online' | 'reconnecting' | 'fatal';
11
+ }
12
+ /**
13
+ * Runner daemon. Responsibilities, in order:
14
+ *
15
+ * 1. Resolve config (defaults <- files <- env <- flags).
16
+ * 2. Resolve credentials (env <- ~/.almyty/credentials.json).
17
+ * 3. Detect runtime info + probe binaries.
18
+ * 4. POST /runners/register with the snapshot.
19
+ * 5. Open the Streamable HTTP stream and start dispatching incoming
20
+ * envelopes to handlers.
21
+ * 6. Heartbeat every 30s.
22
+ * 7. On SIGTERM / SIGINT: send a final shutdown envelope, wait
23
+ * briefly for in-flight responses, then exit.
24
+ *
25
+ * The daemon writes its PID and a status snapshot to ~/.almyty/runner/
26
+ * so `almyty runner status` and `almyty runner stop` can find it.
27
+ */
28
+ export declare class RunnerDaemon {
29
+ private resolved;
30
+ private client;
31
+ private processes;
32
+ private coding;
33
+ private runnerId;
34
+ private heartbeatTimer;
35
+ private status;
36
+ /** Guards against overlapping re-establish attempts. */
37
+ private reestablishing;
38
+ /** Consecutive failed re-establish attempts; bounds the retry loop. */
39
+ private sessionLostStreak;
40
+ /**
41
+ * Bootstraps the daemon. Returns when the runner has registered and
42
+ * the stream is open. Errors here exit the process; caller is the
43
+ * CLI's `start` command, which doesn't try to recover from a failed
44
+ * register/connect — just prints and dies.
45
+ */
46
+ start(flags: Partial<ResolvedConfig> & {
47
+ configPath?: string;
48
+ }): Promise<void>;
49
+ /**
50
+ * Exit signal flow. On the first SIGINT/SIGTERM, transition to
51
+ * draining and inform the backend; on the second, hard exit.
52
+ */
53
+ private installSignalHandlers;
54
+ private gracefulShutdown;
55
+ private wireClient;
56
+ private handleRequest;
57
+ private heartbeat;
58
+ /**
59
+ * Re-establish the command stream after a session-lost (404). Re-mints a
60
+ * session (hello envelope) and reopens the GET stream, with backoff. Only
61
+ * gives up — exiting fatal — after MAX_SESSION_LOST_STREAK consecutive
62
+ * failures, so a transient wrong-replica hit recovers transparently while a
63
+ * genuinely-down backend still terminates rather than spinning forever.
64
+ */
65
+ private reestablishSession;
66
+ private writeState;
67
+ private updateState;
68
+ private deleteState;
69
+ }
70
+ export declare function readStatus(): DaemonStatus | null;
71
+ export declare function stopDaemon(): boolean;