@zgeoff/atc 3.3.1 → 3.4.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/package.json +4 -2
- package/src/agents/agent-adapter.ts +10 -0
- package/src/agents/build-cli-argv.ts +2 -4
- package/src/agents/claude-adapter.ts +13 -3
- package/src/agents/codex-adapter.ts +11 -2
- package/src/agents/gateway-adapter.ts +20 -8
- package/src/agents/grok-adapter.ts +16 -13
- package/src/agents/load-claude-config-bundle.ts +6 -6
- package/src/agents/print-codex-hook-file.ts +7 -3
- package/src/agents/print-grok-hook-file.ts +8 -3
- package/src/agents/resolve-agent-home.ts +7 -3
- package/src/agents/resolve-claude-global-config-path.ts +4 -4
- package/src/agents/update-claude-project-trust.ts +41 -30
- package/src/agents/with-claude-config-lock.ts +13 -1
- package/src/agents/write-atc-bridge.ts +10 -7
- package/src/agents/write-hook-settings.ts +13 -7
- package/src/client/boot-daemon.ts +48 -15
- package/src/client/dirs.ts +4 -2
- package/src/client/index.ts +17 -1
- package/src/client/log-client-event.ts +20 -0
- package/src/client/spawn-picker.ts +200 -121
- package/src/client/ui.ts +7 -7
- package/src/clients.ts +41 -11
- package/src/daemon/daemon.ts +143 -33
- package/src/daemon/event-signal.ts +19 -3
- package/src/daemon/make-hook-runner.ts +82 -22
- package/src/daemon/permission-registry.ts +11 -6
- package/src/daemon/restore-fleet.ts +62 -17
- package/src/daemon/run-eject-handoff.ts +20 -2
- package/src/daemon/sessions.ts +6 -0
- package/src/daemon/start-tcp-listener.ts +12 -10
- package/src/federation/daemon-caller.ts +25 -4
- package/src/federation/daemon-pool.ts +3 -1
- package/src/federation/open-gateway-caller.ts +6 -1
- package/src/grants.ts +36 -11
- package/src/hook-report.ts +17 -3
- package/src/mcp/read-report-texts.ts +11 -7
- package/src/mcp/start-mcp-http-server.ts +5 -1
- package/src/mcp-http-server.ts +85 -32
- package/src/report.ts +21 -3
- package/src/run-bridge-tap.ts +36 -7
- package/src/run-config-migrate.ts +35 -11
- package/src/run-gateway.ts +4 -2
- package/src/shared/build-atc-command.ts +7 -3
- package/src/shared/bun-sqlite-driver.ts +10 -0
- package/src/shared/claim-daemon-lock.ts +10 -4
- package/src/shared/collect-dir-roots.ts +6 -6
- package/src/shared/collect-hooks.ts +7 -6
- package/src/shared/config.ts +19 -9
- package/src/shared/system-clock.ts +24 -0
- package/src/sources/github/collect-github-repos.ts +1 -1
- package/src/sources/github/read-git-protocol.ts +1 -1
- package/src/sources/github/run-gh.ts +15 -9
- package/src/store/state-store.ts +57 -7
- package/src/tap.ts +44 -13
- package/src/test-utils/build-mock-agent-adapter.ts +25 -0
- package/src/test-utils/build-mock-agent-entry.ts +32 -0
- package/src/test-utils/build-mock-auth-binding.ts +28 -0
- package/src/test-utils/build-mock-auth-profile.ts +62 -0
- package/src/test-utils/build-mock-daemon-record.ts +26 -0
- package/src/test-utils/build-mock-fleet-entry.ts +33 -0
- package/src/test-utils/build-mock-gateway-config.ts +32 -0
- package/src/test-utils/build-mock-imp-identity.ts +18 -0
- package/src/test-utils/build-mock-key-binding-claim.ts +34 -0
- package/src/test-utils/build-mock-message-record.ts +22 -0
- package/src/test-utils/build-mock-mirror-session.ts +42 -0
- package/src/test-utils/build-mock-registry-daemon.ts +28 -0
- package/src/test-utils/build-mock-session-descriptor.ts +66 -0
- package/src/test-utils/build-mock-session.ts +43 -0
- package/src/test-utils/build-mock-sortable-session-view.ts +20 -0
- package/src/test-utils/build-stub-attention-adapter.ts +22 -0
- package/src/test-utils/build-stub-bridge-context.ts +20 -0
- package/src/test-utils/build-stub-brokered-agent-adapter.ts +96 -0
- package/src/test-utils/build-stub-channel-opener.ts +45 -0
- package/src/test-utils/build-stub-claude-headless-run.ts +13 -0
- package/src/test-utils/build-stub-client-screens.ts +33 -0
- package/src/test-utils/build-stub-clock.ts +67 -0
- package/src/test-utils/build-stub-daemon-context.ts +81 -0
- package/src/test-utils/build-stub-daemon-requests.ts +103 -0
- package/src/test-utils/{fixture-dir-provider.ts → build-stub-dir-provider.ts} +20 -12
- package/src/test-utils/build-stub-execution-provider.ts +112 -0
- package/src/test-utils/build-stub-fleet-caller.ts +60 -0
- package/src/test-utils/build-stub-forking-gh.ts +10 -0
- package/src/test-utils/build-stub-gh.ts +50 -0
- package/src/test-utils/build-stub-handoff-daemon.ts +11 -0
- package/src/test-utils/build-stub-harness-relay.ts +50 -0
- package/src/test-utils/build-stub-headless-runner.ts +60 -0
- package/src/test-utils/build-stub-held-gh.ts +19 -0
- package/src/test-utils/build-stub-held-inbox-source.ts +36 -0
- package/src/test-utils/build-stub-held-provider.ts +58 -0
- package/src/test-utils/build-stub-held-zoxide.ts +13 -0
- package/src/test-utils/build-stub-host-hold.ts +31 -0
- package/src/test-utils/build-stub-host-provider.ts +26 -0
- package/src/test-utils/{fixture-imp-port.ts → build-stub-imp-port.ts} +119 -63
- package/src/test-utils/build-stub-log.ts +15 -0
- package/src/test-utils/build-stub-mcp-claude.ts +26 -0
- package/src/test-utils/build-stub-mcp-grok.ts +19 -0
- package/src/test-utils/build-stub-peer-socket.ts +81 -0
- package/src/test-utils/build-stub-picker-host.ts +36 -0
- package/src/test-utils/build-stub-proxied-agent-adapter.ts +29 -0
- package/src/test-utils/build-stub-pty-provider.ts +53 -0
- package/src/test-utils/build-stub-recording-atc.ts +10 -0
- package/src/test-utils/build-stub-signed-in-gh.ts +18 -0
- package/src/test-utils/build-stub-signed-out-gh.ts +8 -0
- package/src/test-utils/build-stub-soft-kill-provider.ts +29 -0
- package/src/test-utils/build-stub-targets.ts +58 -0
- package/src/test-utils/build-stub-terminal.ts +19 -0
- package/src/test-utils/build-stub-timeout-scheduler.ts +48 -0
- package/src/test-utils/build-stub-tui-claude.ts +47 -0
- package/src/test-utils/build-stub-tui-grok.ts +53 -0
- package/src/test-utils/build-stub-wait-clock.ts +23 -0
- package/src/test-utils/build-target-options-from-config.ts +37 -0
- package/src/test-utils/collect-process-tree.ts +75 -0
- package/src/test-utils/create-git-fixture.ts +120 -0
- package/src/test-utils/create-migrated-state-db.ts +12 -10
- package/src/test-utils/create-stub-bin.ts +19 -0
- package/src/test-utils/create-stub-claude.ts +75 -0
- package/src/test-utils/create-stub-codex.ts +31 -0
- package/src/test-utils/create-stub-composer.ts +65 -0
- package/src/test-utils/create-stub-failing-agent-adapter.ts +111 -0
- package/src/test-utils/create-stub-grok.ts +44 -0
- package/src/test-utils/create-stub-guest-clis.ts +55 -0
- package/src/test-utils/create-stub-harness-guest.ts +35 -0
- package/src/test-utils/create-stub-recording-claude.ts +20 -0
- package/src/test-utils/{setup-fake-systemd.ts → create-stub-systemd.ts} +5 -5
- package/src/test-utils/get-gateway-config.ts +3 -3
- package/src/test-utils/get-only-effect-ref.ts +21 -0
- package/src/test-utils/get-only-imp-name.ts +19 -0
- package/src/test-utils/get-records.ts +20 -0
- package/src/test-utils/get-string.ts +14 -0
- package/src/test-utils/keys.ts +26 -0
- package/src/test-utils/merge-deep.ts +36 -0
- package/src/test-utils/mock-overrides.ts +13 -0
- package/src/test-utils/open-repo-step.ts +24 -0
- package/src/test-utils/read-query-plan.ts +22 -0
- package/src/test-utils/resolve-atc-command.ts +13 -0
- package/src/test-utils/resolve-gateway-command.ts +13 -0
- package/src/test-utils/run-atc.ts +56 -0
- package/src/test-utils/run-legacy-daemon.ts +27 -5
- package/src/test-utils/run-listener-daemon.ts +5 -10
- package/src/test-utils/run-source-daemon.ts +2 -2
- package/src/test-utils/seed-faker.ts +7 -0
- package/src/test-utils/send-lines-before-handshake.ts +35 -0
- package/src/test-utils/send-mcp-request.ts +25 -0
- package/src/test-utils/setup-mcp-home.ts +99 -0
- package/src/test-utils/setup-mcp-http.ts +19 -31
- package/src/test-utils/spawn-claude-session.ts +34 -0
- package/src/test-utils/spawn-grok-session.ts +39 -0
- package/src/test-utils/start-cut-proxy.ts +25 -8
- package/src/test-utils/start-daemon-process.ts +192 -0
- package/src/test-utils/start-git-http-server.ts +21 -3
- package/src/test-utils/start-mcp-stdio.ts +195 -0
- package/src/test-utils/start-output-capture.ts +20 -0
- package/src/test-utils/start-stub-dropping-daemon.ts +103 -0
- package/src/test-utils/start-stub-impd-info.ts +32 -0
- package/src/test-utils/start-stub-impd.ts +138 -0
- package/src/test-utils/start-stub-legacy-daemon.ts +166 -0
- package/src/test-utils/start-stub-mcp-server.ts +50 -0
- package/src/test-utils/start-stub-refusing-listener.ts +38 -0
- package/src/test-utils/start-stub-reporter-socket.ts +77 -0
- package/src/test-utils/start-stub-restore-daemon.ts +58 -0
- package/src/test-utils/start-stub-session-bridge.ts +98 -0
- package/src/test-utils/start-stub-silent-server.ts +33 -0
- package/src/test-utils/start-stub-source-daemon.ts +39 -0
- package/src/test-utils/start-stub-stalled-listener.ts +40 -0
- package/src/test-utils/start-stub-tap.ts +38 -0
- package/src/test-utils/start-stub-tcp-daemon.ts +70 -0
- package/src/test-utils/start-stub-telemetry-collector.ts +26 -0
- package/src/test-utils/start-test-daemon.ts +215 -0
- package/src/test-utils/start-tui-harness.ts +284 -0
- package/src/test-utils/subscribe-to-socket-lines.ts +59 -12
- package/src/test-utils/try-send-request.ts +29 -0
- package/src/test-utils/wait-for-event.ts +41 -0
- package/src/test-utils/wait-for.ts +15 -3
- package/src/verify-restored-fleet.ts +30 -17
- package/src/workspace/check-repository-access.ts +4 -0
- package/src/workspace/collect-remote-refs.ts +4 -2
- package/src/workspace/run-git.ts +6 -0
- package/src/test-utils/start-legacy-daemon.ts +0 -111
|
@@ -11,6 +11,8 @@ import { getBuild } from '../shared/get-build';
|
|
|
11
11
|
import { isProcessAlive } from '../shared/is-process-alive';
|
|
12
12
|
import { makeSingleFlight } from '../shared/make-single-flight';
|
|
13
13
|
import { spawnATCDetached } from '../shared/spawn-atc-detached';
|
|
14
|
+
import { systemClock } from '../shared/system-clock';
|
|
15
|
+
import type { Clock } from '../shared/system-clock';
|
|
14
16
|
import { toAgentID } from '../shared/to-agent-id';
|
|
15
17
|
import { DaemonClient } from './daemon-client';
|
|
16
18
|
import { formatProtocolMismatch } from './format-protocol-mismatch';
|
|
@@ -42,6 +44,15 @@ export interface DaemonBootOptions {
|
|
|
42
44
|
// manager starts beside a managed daemon sets it, so the two never race
|
|
43
45
|
// for the state directory.
|
|
44
46
|
readonly waitForDaemonMs?: number;
|
|
47
|
+
|
|
48
|
+
// Called once per boot, when a waiting boot first finds no daemon and
|
|
49
|
+
// starts to wait instead of starting one; a retry after a protocol
|
|
50
|
+
// mismatch that waits again does not call it a second time.
|
|
51
|
+
readonly onWaitForDaemon?: () => void;
|
|
52
|
+
|
|
53
|
+
// The time and the timers a waiting boot reads for its deadline and its
|
|
54
|
+
// polls; the wall clock when absent.
|
|
55
|
+
readonly clock?: Clock;
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
/**
|
|
@@ -59,10 +70,23 @@ export interface DaemonBootOptions {
|
|
|
59
70
|
* its own boot would otherwise flag daemons that are already current.
|
|
60
71
|
*/
|
|
61
72
|
export async function bootDaemonClient(options: DaemonBootOptions = {}): Promise<DaemonBoot> {
|
|
73
|
+
let waited = false;
|
|
74
|
+
const clock = options.clock ?? systemClock;
|
|
75
|
+
|
|
62
76
|
const wait =
|
|
63
77
|
options.waitForDaemonMs === undefined
|
|
64
78
|
? null
|
|
65
|
-
: {
|
|
79
|
+
: {
|
|
80
|
+
deadline: clock.now() + options.waitForDaemonMs,
|
|
81
|
+
timeoutMs: options.waitForDaemonMs,
|
|
82
|
+
clock,
|
|
83
|
+
onWait: () => {
|
|
84
|
+
if (!waited) {
|
|
85
|
+
waited = true;
|
|
86
|
+
options.onWaitForDaemon?.();
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
};
|
|
66
90
|
|
|
67
91
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
68
92
|
const build = getBuild();
|
|
@@ -142,10 +166,15 @@ async function openOrBootDaemon(): Promise<OpenedDaemon> {
|
|
|
142
166
|
interface DaemonWait {
|
|
143
167
|
readonly deadline: number;
|
|
144
168
|
readonly timeoutMs: number;
|
|
169
|
+
readonly clock: Clock;
|
|
170
|
+
|
|
171
|
+
// Called on every miss; the boot reports only the first to its caller.
|
|
172
|
+
readonly onWait: () => void;
|
|
145
173
|
}
|
|
146
174
|
|
|
147
175
|
/**
|
|
148
176
|
* Polls the known sockets until a daemon answers, and never starts one.
|
|
177
|
+
* Each miss reports that the wait goes on.
|
|
149
178
|
*/
|
|
150
179
|
async function waitForDaemon(wait: DaemonWait): Promise<OpenedDaemon> {
|
|
151
180
|
for (;;) {
|
|
@@ -155,11 +184,17 @@ async function waitForDaemon(wait: DaemonWait): Promise<OpenedDaemon> {
|
|
|
155
184
|
return opened;
|
|
156
185
|
}
|
|
157
186
|
|
|
158
|
-
|
|
187
|
+
wait.onWait();
|
|
188
|
+
|
|
189
|
+
if (wait.clock.now() >= wait.deadline) {
|
|
159
190
|
throw new Error(formatWaitFailure(wait.timeoutMs));
|
|
160
191
|
}
|
|
161
192
|
|
|
162
|
-
|
|
193
|
+
const polled = Promise.withResolvers<void>();
|
|
194
|
+
|
|
195
|
+
wait.clock.schedule(polled.resolve, 100);
|
|
196
|
+
|
|
197
|
+
await polled.promise;
|
|
163
198
|
}
|
|
164
199
|
}
|
|
165
200
|
|
|
@@ -172,21 +207,19 @@ async function waitForHello(
|
|
|
172
207
|
sendHello: () => Promise<Readonly<Record<string, unknown>>>,
|
|
173
208
|
wait: DaemonWait,
|
|
174
209
|
): Promise<Readonly<Record<string, unknown>>> {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
()
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
);
|
|
184
|
-
});
|
|
210
|
+
const expired = Promise.withResolvers<never>();
|
|
211
|
+
|
|
212
|
+
const cancel = wait.clock.schedule(
|
|
213
|
+
() => {
|
|
214
|
+
expired.reject(new Error(formatWaitFailure(wait.timeoutMs)));
|
|
215
|
+
},
|
|
216
|
+
Math.max(0, wait.deadline - wait.clock.now()),
|
|
217
|
+
);
|
|
185
218
|
|
|
186
219
|
try {
|
|
187
|
-
return await Promise.race([sendHello(), expired]);
|
|
220
|
+
return await Promise.race([sendHello(), expired.promise]);
|
|
188
221
|
} finally {
|
|
189
|
-
|
|
222
|
+
cancel();
|
|
190
223
|
}
|
|
191
224
|
}
|
|
192
225
|
|
package/src/client/dirs.ts
CHANGED
|
@@ -8,13 +8,15 @@ import { collectRootDirs } from '../shared/collect-root-dirs';
|
|
|
8
8
|
* to find a directory: where the client runs, then the daemon's spawn
|
|
9
9
|
* history (most recent first), then the configured roots, then zoxide's
|
|
10
10
|
* frecency list. A path that no longer exists is dropped, a duplicate keeps
|
|
11
|
-
* its first position, and an empty merge falls back to the home directory
|
|
11
|
+
* its first position, and an empty merge falls back to the home directory:
|
|
12
|
+
* `home` when given, otherwise the account's.
|
|
12
13
|
*/
|
|
13
14
|
export interface DirSources {
|
|
14
15
|
readonly cwd: string;
|
|
15
16
|
readonly recent: readonly string[];
|
|
16
17
|
readonly roots: readonly string[];
|
|
17
18
|
readonly zoxide: readonly string[];
|
|
19
|
+
readonly home?: string;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export function collectDirs(sources: DirSources): string[] {
|
|
@@ -35,7 +37,7 @@ export function collectDirs(sources: DirSources): string[] {
|
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
if (found.length === 0) {
|
|
38
|
-
found.push(homedir());
|
|
40
|
+
found.push(sources.home ?? homedir());
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
return found;
|
package/src/client/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { EventMsg } from '../protocol/protocol';
|
|
|
7
7
|
import { sortGroupedSessionViews } from '../protocol/sort-grouped-session-views';
|
|
8
8
|
import { sortSessionViews } from '../protocol/sort-session-views';
|
|
9
9
|
import type { AgentID } from '../shared/agent-id';
|
|
10
|
-
import { loadConfig } from '../shared/config';
|
|
10
|
+
import { configFile, loadConfig } from '../shared/config';
|
|
11
11
|
import { makeSingleFlight } from '../shared/make-single-flight';
|
|
12
12
|
import { bootDaemonClient } from './boot-daemon';
|
|
13
13
|
import { buildClientMachine } from './build-client-machine';
|
|
@@ -16,6 +16,7 @@ import { collectTargetPicks } from './collect-target-picks';
|
|
|
16
16
|
import { findFuzzyScore, formatDir } from './dirs';
|
|
17
17
|
import type { ProtocolMismatch } from './format-protocol-mismatch';
|
|
18
18
|
import { KEY, isDown, isUp, planTextEdit } from './keys';
|
|
19
|
+
import { logClientEvent } from './log-client-event';
|
|
19
20
|
import { parseDaemonEvent } from './parse-daemon-event';
|
|
20
21
|
import { pickTabTarget } from './pick-tab-target';
|
|
21
22
|
import { resolveAgentMetadata } from './resolve-agent-metadata';
|
|
@@ -341,6 +342,15 @@ const picker = new SpawnPicker<MirrorSession>({
|
|
|
341
342
|
toMirrorSession,
|
|
342
343
|
upsertMirror,
|
|
343
344
|
hasDaemonFeature,
|
|
345
|
+
write: (chunk) => {
|
|
346
|
+
process.stdout.write(chunk);
|
|
347
|
+
},
|
|
348
|
+
cwd: process.cwd(),
|
|
349
|
+
configPath: configFile,
|
|
350
|
+
onDropAnswer: (kind) => {
|
|
351
|
+
logClientEvent(`dropped ${kind} answer`);
|
|
352
|
+
},
|
|
353
|
+
log: logClientEvent,
|
|
344
354
|
});
|
|
345
355
|
|
|
346
356
|
function openPicker(resume: boolean) {
|
|
@@ -721,6 +731,12 @@ function applyOverlayKey(buf: Buffer) {
|
|
|
721
731
|
return;
|
|
722
732
|
}
|
|
723
733
|
|
|
734
|
+
if (ch === 'H') {
|
|
735
|
+
logClientEvent('ignored H on a session that cannot eject');
|
|
736
|
+
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
|
|
724
740
|
if (ch === 'P' && sel !== undefined && sel.resumable && (sel.kind === 'headless' || !sel.alive)) {
|
|
725
741
|
void adoptSession(sel.id);
|
|
726
742
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { appendFileSync } from 'node:fs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Appends one line, a decision the client took without drawing anything, to
|
|
5
|
+
* the file `ATC_CLIENT_LOG` holds, and does nothing when the variable is
|
|
6
|
+
* unset or empty. A write that fails is dropped, so the log never stops the
|
|
7
|
+
* client. It writes off the screen because the client's stdout and stderr
|
|
8
|
+
* are the terminal it draws on.
|
|
9
|
+
*/
|
|
10
|
+
export function logClientEvent(line: string): void {
|
|
11
|
+
const path = process.env['ATC_CLIENT_LOG'];
|
|
12
|
+
|
|
13
|
+
if (path === undefined || path === '') {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
appendFileSync(path, `${line}\n`);
|
|
19
|
+
} catch {}
|
|
20
|
+
}
|