/** * Commands run from the browser, in the foreground or left running behind. * * The web had a card for every command the AGENT ran and no way to run one yourself, which makes * the interface a spectator's seat. Half of working in a repository is running things — a test, a * build, a dev server — and being unable to do that without going back to a terminal is what made * the browser a second-class place to work. * * Two kinds, and the distinction is the useful part rather than a setting: * * - Foreground: something that finishes. Its output streams and you watch it. * - Background: something that does not. `npm run dev` never exits, and a runner that waits for * it looks broken. A background job keeps running, keeps collecting output, and is listed with * a way to kill it — which is the thing you actually want from a dev server. * * No new authority. The token already permits /api/send, which runs an agent that executes * commands; a person typing the command themselves is strictly less surprising than a model * choosing it. What this adds is visibility, so it is announced on the launching terminal the same * way a declared provider is. */ import { EventBus } from './events.js'; export type JobState = 'running' | 'exited' | 'killed' | 'failed'; export interface Job { id: string; command: string; cwd: string; background: boolean; /** Who ran it. An agent's command belongs in the same list as yours, not a separate one. */ by: 'you' | 'agent'; /** True when it ran on a real terminal, so its output carries colour and progress. */ pty: boolean; state: JobState; exitCode: number | null; startedAt: number; endedAt: number | null; /** Output lines, newest last, capped. */ lines: string[]; /** True once the cap has dropped anything, so the interface can say so. */ trimmed: boolean; } /** Output kept per job. A dev server left running for a day must not become the heap. */ export declare const MAX_LINES = 2000; /** Jobs remembered. Beyond this the oldest finished ones are forgotten. */ export declare const MAX_JOBS = 40; /** A foreground job that runs longer than this is almost certainly a server. */ export declare const FOREGROUND_LIMIT_MS = 120000; /** * How to allocate a pty on this machine, or nothing. * * util-linux and macOS ship the same program with incompatible flags, and getting them the wrong * way round produces a job that fails instantly with a usage message. util-linux takes the command * after -c and needs a file argument; the BSD one takes the file first and then the command. */ export declare function ptyWrapper(platform?: string, exists?: (p: string) => boolean): { command: string; before: string[]; after: string[]; } | null; export declare class JobRunner { private readonly announce; readonly bus: EventBus; private readonly live; private readonly done; private counter; constructor(announce?: (line: string) => void); list(): Job[]; get(id: string): Job | null; /** * Starts a command on a real terminal. * * Through `script`, which allocates a pty. It is the difference between a terminal and a * transcript of one: a program asked whether its output is a tty answers yes, so `npm test` * prints its colours, `git log` does not silently switch to plain mode, a progress bar redraws * itself, and anything that reads a key can be typed at. Piping stdout instead gives you the * program's opinion of a log file. * * No native dependency to get it. node-pty means a compiler on every machine that installs * KONECK, and `script` ships with util-linux and macOS. Where it is missing — Windows — the * pipes are used and the job says so rather than pretending. * * Detached either way, so it gets its own process group: a dev server starts children of its own * and killing only the shell would leave one holding the port with nothing left to stop it. */ start(command: string, cwd: string, background: boolean, by?: 'you' | 'agent'): Job; /** * Stops a job, and everything it started. * * The group, not the process: a shell that ran `npm run dev` has a node under it holding the * port, and killing the shell alone leaves that behind with nothing left to stop it — which is * how a machine ends up with four dev servers and a port conflict nobody can explain. */ kill(id: string): boolean; /** Kills everything still running. Called when the server shuts down. */ killAll(): void; /** Adds a line, or replaces the last one when a program is redrawing it. */ private pushLine; record(command: string, cwd: string): Job; /** Output from a recorded agent command. */ append(id: string, line: string): void; /** Closes a recorded agent command. */ finish(id: string, ok: boolean): void; private retire; /** A job without its output, for a listing. */ summary(job: Job): Omit & { lines: number; }; } //# sourceMappingURL=jobs.d.ts.map