@zgeoff/atc 2.12.0 → 2.14.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.
- package/README.md +12 -11
- package/package.json +2 -1
- package/src/agents/agent-adapter.ts +48 -11
- package/src/agents/build-atc-bridge-files.ts +13 -0
- package/src/agents/build-claude-query-options.ts +72 -0
- package/src/agents/build-cli-command.ts +7 -5
- package/src/{daemon → agents}/build-headless-env.ts +12 -6
- package/src/agents/build-hook-settings.ts +8 -2
- package/src/agents/build-restore-mode-args.ts +23 -0
- package/src/agents/claude-adapter.ts +64 -17
- package/src/agents/codex-adapter.ts +5 -0
- package/src/agents/find-claude-permission-mode.ts +24 -0
- package/src/agents/gateway-adapter.ts +38 -11
- package/src/agents/grok-adapter.ts +5 -0
- package/src/agents/make-claude-headless-runner.ts +46 -0
- package/src/agents/plan-pasted-line-input.ts +22 -0
- package/src/agents/plan-typed-line-input.ts +8 -0
- package/src/agents/resolve-claude-permission-mode.ts +13 -0
- package/src/{daemon/start-headless-run.ts → agents/start-claude-headless-run.ts} +6 -53
- package/src/agents/write-atc-bridge.ts +2 -6
- package/src/cli.ts +8 -12
- package/src/client/boot-daemon.ts +47 -21
- package/src/client/format-protocol-mismatch.ts +37 -0
- package/src/client/index.ts +104 -2
- package/src/client/ui.ts +8 -0
- package/src/daemon/build-execution-targets.ts +49 -10
- package/src/daemon/build-imp-provider.ts +68 -0
- package/src/daemon/build-report-trail-entry.ts +6 -2
- package/src/daemon/build-report-view.ts +41 -0
- package/src/daemon/build-scoped-context.ts +9 -1
- package/src/daemon/build-session-lifecycle.ts +52 -0
- package/src/daemon/build-tar-archive.ts +85 -0
- package/src/daemon/daemon-connection.ts +134 -4
- package/src/daemon/daemon.ts +306 -42
- package/src/daemon/execution-provider.ts +109 -4
- package/src/daemon/hooks.ts +5 -17
- package/src/daemon/imp-client-port.ts +343 -0
- package/src/daemon/imp-harness.ts +618 -0
- package/src/daemon/imp-port-error.ts +18 -0
- package/src/daemon/imp-port.ts +246 -0
- package/src/daemon/imp-provider.ts +601 -0
- package/src/daemon/is-binding-current.ts +43 -0
- package/src/daemon/local-pty-provider.ts +62 -3
- package/src/daemon/materialize-workspace.ts +574 -0
- package/src/daemon/parse-hook-line.ts +31 -0
- package/src/daemon/pick-session-state.ts +15 -0
- package/src/daemon/restore-fleet.ts +41 -23
- package/src/daemon/screen-model.ts +7 -0
- package/src/daemon/sessions.ts +574 -61
- package/src/daemon/start-headless-turn.ts +1 -1
- package/src/daemon/start-session-bridge.ts +294 -0
- package/src/mcp/mcp-tools.ts +41 -3
- package/src/mcp/reconnecting-caller.ts +1 -0
- package/src/mcp/require-daemon-features.ts +4 -0
- package/src/mcp/run-tool.ts +32 -8
- package/src/protocol/daemon-features.ts +14 -0
- package/src/protocol/protocol.ts +23 -0
- package/src/protocol/request-param-schemas.ts +72 -0
- package/src/report.ts +37 -2
- package/src/run-bridge-tap.ts +241 -0
- package/src/shared/collect-clean-env.ts +9 -1
- package/src/shared/open-bridge-socket.ts +86 -0
- package/src/shared/send-bridge-request.ts +47 -0
- package/src/statusline.ts +34 -3
- package/src/store/fleet-entry.ts +19 -0
- package/src/store/run-migrations.ts +85 -0
- package/src/store/state-store.ts +226 -21
- package/src/store/trail-entry.ts +7 -0
- package/src/store/workspace-materialization.ts +70 -0
- package/src/tap.ts +11 -0
- package/src/workspace/check-url-credentials.ts +59 -0
- package/src/workspace/create-workspace-clone.ts +13 -1
- package/src/workspace/repository-env-vars.ts +22 -0
- package/src/workspace/run-git.ts +2 -18
- /package/src/{daemon → agents}/resolve-headless-executable.ts +0 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the imp provider needs from impd, the imp host daemon, in atc's own
|
|
3
|
+
* terms. The real binding wraps `@zgeoff/imp-client`, and the test fixture
|
|
4
|
+
* runs imps as local pseudo-terminals, so nothing outside the imp modules
|
|
5
|
+
* depends on the client library. Every call that impd refuses rejects with
|
|
6
|
+
* an `ImpPortError` carrying impd's code and data.
|
|
7
|
+
*/
|
|
8
|
+
export interface ImpPort {
|
|
9
|
+
// impd's capability flags; an impd without them has neither.
|
|
10
|
+
readonly readFeatures: () => Promise<ImpFeatures>;
|
|
11
|
+
|
|
12
|
+
// The imp under a name, or null when impd holds none.
|
|
13
|
+
readonly readImp: (name: string) => Promise<ImpView | null>;
|
|
14
|
+
readonly createImp: (spec: ImpCreateSpec) => Promise<ImpView>;
|
|
15
|
+
|
|
16
|
+
// Creates or extends the caller's lease under a label, booting or waking
|
|
17
|
+
// the imp first. The label `hold` is refused.
|
|
18
|
+
readonly acquireLease: (name: string, label: string, ttlSeconds: number) => Promise<ImpLease>;
|
|
19
|
+
|
|
20
|
+
// Extends the caller's unexpired lease; `LEASE_NOT_HELD` when it ended.
|
|
21
|
+
readonly renewLease: (name: string, label: string, ttlSeconds: number) => Promise<ImpLease>;
|
|
22
|
+
|
|
23
|
+
// Ends the caller's lease, and reports whether there was one.
|
|
24
|
+
readonly releaseLease: (name: string, label: string) => Promise<boolean>;
|
|
25
|
+
|
|
26
|
+
// Puts the imp to sleep without force: `LEASED` while any lease holds it.
|
|
27
|
+
readonly suspendImp: (name: string) => Promise<void>;
|
|
28
|
+
readonly destroyImp: (name: string) => Promise<void>;
|
|
29
|
+
|
|
30
|
+
// Opens one connection to a session's terminal, as impd's single
|
|
31
|
+
// attacher: a start runs the command under the session name, or attaches
|
|
32
|
+
// when it already runs; an attach only attaches.
|
|
33
|
+
readonly openSession: (
|
|
34
|
+
request: ImpSessionRequest,
|
|
35
|
+
handlers: ImpSessionHandlers,
|
|
36
|
+
) => ImpSessionConnection;
|
|
37
|
+
|
|
38
|
+
// Runs a command in the imp to its exit, without a terminal.
|
|
39
|
+
// oxlint-disable-next-line prefer-readonly-parameter-types -- the command's input bytes have no readonly form
|
|
40
|
+
readonly runCommand: (name: string, command: ImpCommand) => Promise<ImpCommandResult>;
|
|
41
|
+
|
|
42
|
+
// Listens on a unix socket path inside the imp and hands each connection
|
|
43
|
+
// the guest opens there to the daemon.
|
|
44
|
+
readonly openReverseForward: (
|
|
45
|
+
name: string,
|
|
46
|
+
guestPath: string,
|
|
47
|
+
onConnection: (connection: ImpRelayConnection) => void,
|
|
48
|
+
) => ImpReverseForward;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ImpFeatures {
|
|
52
|
+
readonly sessionOffsets: boolean;
|
|
53
|
+
readonly leases: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type ImpState = 'creating' | 'running' | 'sleeping' | 'stopped' | 'error';
|
|
57
|
+
|
|
58
|
+
export interface ImpView {
|
|
59
|
+
readonly name: string;
|
|
60
|
+
readonly state: ImpState;
|
|
61
|
+
|
|
62
|
+
// The caller's own leases in full, and a count of everyone else's.
|
|
63
|
+
readonly leases: readonly ImpLease[];
|
|
64
|
+
readonly otherLeaseCount: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface ImpLease {
|
|
68
|
+
readonly name: string;
|
|
69
|
+
readonly owner: { readonly principal: string; readonly display: string; readonly label: string };
|
|
70
|
+
|
|
71
|
+
// Epoch ms; null holds with no end.
|
|
72
|
+
readonly until: number | null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ImpCreateSpec {
|
|
76
|
+
readonly name: string;
|
|
77
|
+
readonly image?: string;
|
|
78
|
+
readonly memoryMib?: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type ColdBootCause =
|
|
82
|
+
| 'start'
|
|
83
|
+
| 'wake_fallback'
|
|
84
|
+
| 'watchdog'
|
|
85
|
+
| 'restore'
|
|
86
|
+
| 'recovery'
|
|
87
|
+
| 'unknown';
|
|
88
|
+
|
|
89
|
+
export interface ColdBoot {
|
|
90
|
+
readonly bootId: string;
|
|
91
|
+
readonly cause: ColdBootCause;
|
|
92
|
+
readonly at: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// The last generation that ran under a session name in this boot, final
|
|
96
|
+
// once written.
|
|
97
|
+
export interface PreviousGeneration {
|
|
98
|
+
readonly executionGeneration: string;
|
|
99
|
+
readonly end: number;
|
|
100
|
+
readonly exitCode: number | null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface ResumeFrom {
|
|
104
|
+
readonly executionGeneration: string;
|
|
105
|
+
readonly offset: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export type ResumeResult =
|
|
109
|
+
| { readonly kind: 'exact' }
|
|
110
|
+
| { readonly kind: 'gap'; readonly from: number; readonly to: number }
|
|
111
|
+
| {
|
|
112
|
+
readonly kind: 'generation_changed';
|
|
113
|
+
readonly executionGeneration: string;
|
|
114
|
+
readonly firstOffset: number;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Where a connection's data sits in the session's output. `none` is an imp
|
|
119
|
+
* whose agent carries no offsets, which replays recent output on every
|
|
120
|
+
* connection.
|
|
121
|
+
*/
|
|
122
|
+
type SessionOutput =
|
|
123
|
+
| { readonly continuity: 'none' }
|
|
124
|
+
| {
|
|
125
|
+
readonly continuity: 'offsets';
|
|
126
|
+
readonly bootId: string;
|
|
127
|
+
readonly executionGeneration: string;
|
|
128
|
+
readonly bufferStart: number;
|
|
129
|
+
readonly end: number;
|
|
130
|
+
readonly offset: number;
|
|
131
|
+
readonly prelude: number;
|
|
132
|
+
readonly coldBoots: readonly ColdBoot[];
|
|
133
|
+
readonly previous?: PreviousGeneration | undefined;
|
|
134
|
+
readonly resume?: ResumeResult | undefined;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export type ImpSessionRequest =
|
|
138
|
+
| {
|
|
139
|
+
readonly kind: 'start';
|
|
140
|
+
readonly name: string;
|
|
141
|
+
readonly session: string;
|
|
142
|
+
readonly argv: readonly string[];
|
|
143
|
+
readonly env: Readonly<Record<string, string>>;
|
|
144
|
+
readonly cwd: string;
|
|
145
|
+
readonly cols: number;
|
|
146
|
+
readonly rows: number;
|
|
147
|
+
readonly resumeFrom?: ResumeFrom;
|
|
148
|
+
}
|
|
149
|
+
| {
|
|
150
|
+
readonly kind: 'attach';
|
|
151
|
+
readonly name: string;
|
|
152
|
+
readonly session: string;
|
|
153
|
+
readonly cols: number;
|
|
154
|
+
readonly rows: number;
|
|
155
|
+
readonly resumeFrom?: ResumeFrom;
|
|
156
|
+
|
|
157
|
+
// false fails with `INVALID_STATE` instead of booting or waking the imp.
|
|
158
|
+
readonly wake: boolean;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export interface ImpSessionHandlers {
|
|
162
|
+
readonly onStarted: (started: ImpSessionStarted) => void;
|
|
163
|
+
|
|
164
|
+
// oxlint-disable-next-line prefer-readonly-parameter-types -- output bytes have no readonly form
|
|
165
|
+
readonly onOutput: (data: Uint8Array) => void;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface ImpSessionStarted {
|
|
169
|
+
// Whether this connection started the process rather than attaching.
|
|
170
|
+
readonly created: boolean;
|
|
171
|
+
readonly output: SessionOutput;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* How a connection ended: the process exited, impd refused or failed the
|
|
176
|
+
* request, impd detached the connection while the process ran on, the
|
|
177
|
+
* socket closed, with its close code once it had opened, or a handler on
|
|
178
|
+
* the daemon's side threw and the client closed the socket.
|
|
179
|
+
*/
|
|
180
|
+
export type ImpSessionOutcome =
|
|
181
|
+
| {
|
|
182
|
+
readonly kind: 'exit';
|
|
183
|
+
readonly code: number | null;
|
|
184
|
+
readonly signal: string | null;
|
|
185
|
+
readonly offset?: number;
|
|
186
|
+
}
|
|
187
|
+
| {
|
|
188
|
+
readonly kind: 'failed';
|
|
189
|
+
readonly code: string | null;
|
|
190
|
+
readonly message: string;
|
|
191
|
+
readonly data?: unknown;
|
|
192
|
+
}
|
|
193
|
+
| {
|
|
194
|
+
readonly kind: 'detached';
|
|
195
|
+
readonly reason: 'taken_over' | 'slow' | 'lost';
|
|
196
|
+
readonly offset?: number;
|
|
197
|
+
}
|
|
198
|
+
| { readonly kind: 'closed'; readonly reason: string; readonly closeCode?: number }
|
|
199
|
+
| { readonly kind: 'unreachable'; readonly detail: string }
|
|
200
|
+
| { readonly kind: 'unauthorized' }
|
|
201
|
+
| { readonly kind: 'bad_message'; readonly detail: string }
|
|
202
|
+
| { readonly kind: 'local_error'; readonly detail: string };
|
|
203
|
+
|
|
204
|
+
export interface ImpSessionConnection {
|
|
205
|
+
readonly outcome: Promise<ImpSessionOutcome>;
|
|
206
|
+
|
|
207
|
+
// oxlint-disable-next-line prefer-readonly-parameter-types -- input bytes have no readonly form
|
|
208
|
+
readonly write: (data: Uint8Array) => void;
|
|
209
|
+
readonly resize: (cols: number, rows: number) => void;
|
|
210
|
+
|
|
211
|
+
// Sends the process a signal by name, such as `SIGHUP`.
|
|
212
|
+
readonly sendSignal: (signal: string) => void;
|
|
213
|
+
|
|
214
|
+
// Closes the connection; the process runs on under its session name.
|
|
215
|
+
readonly close: () => void;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface ImpCommand {
|
|
219
|
+
readonly argv: readonly string[];
|
|
220
|
+
readonly cwd?: string;
|
|
221
|
+
readonly stdin?: Uint8Array;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface ImpCommandResult {
|
|
225
|
+
readonly code: number | null;
|
|
226
|
+
readonly stdout: Uint8Array;
|
|
227
|
+
readonly stderr: Uint8Array;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface ImpRelayConnection {
|
|
231
|
+
// oxlint-disable-next-line prefer-readonly-parameter-types -- relayed bytes have no readonly form
|
|
232
|
+
readonly onData: (listener: (data: Uint8Array) => void) => void;
|
|
233
|
+
readonly onClose: (listener: () => void) => void;
|
|
234
|
+
|
|
235
|
+
// Sends bytes to the guest's end of the connection, and settles once the
|
|
236
|
+
// relay has room for more.
|
|
237
|
+
// oxlint-disable-next-line prefer-readonly-parameter-types -- relayed bytes have no readonly form
|
|
238
|
+
readonly write: (data: Uint8Array) => Promise<void>;
|
|
239
|
+
readonly close: () => void;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface ImpReverseForward {
|
|
243
|
+
// Settles once impd listens on the guest path.
|
|
244
|
+
readonly listening: Promise<void>;
|
|
245
|
+
readonly stop: () => void;
|
|
246
|
+
}
|