@zgeoff/atc 3.3.1 → 3.4.1
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 +5 -3
- package/src/agents/agent-adapter.ts +10 -0
- package/src/agents/build-agent-adapters.ts +21 -3
- package/src/agents/build-cli-argv.ts +2 -4
- package/src/agents/claude-adapter.ts +13 -3
- package/src/agents/codex-adapter.ts +31 -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/render-sdk-message.ts +57 -0
- package/src/agents/resolve-agent-home.ts +7 -3
- package/src/agents/resolve-claude-global-config-path.ts +4 -4
- package/src/agents/start-claude-headless-run.ts +2 -57
- package/src/agents/update-claude-project-trust.ts +41 -30
- package/src/agents/with-claude-config-lock.ts +37 -12
- package/src/agents/write-atc-bridge.ts +10 -7
- package/src/agents/write-hook-settings.ts +13 -7
- package/src/client/boot-daemon.ts +122 -39
- 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/resolve-workspace-root.ts +10 -5
- 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/session-runtime.ts +0 -4
- 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/gateway.ts +2 -179
- 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 +88 -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-cli.ts +327 -0
- package/src/run-gateway.ts +51 -18
- 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 +24 -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-execution-target.ts +25 -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-imp-session-request.ts +53 -0
- package/src/test-utils/build-mock-key-binding-claim.ts +34 -0
- package/src/test-utils/build-mock-message-record.ts +26 -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-mock-stored-event.ts +20 -0
- package/src/test-utils/build-mock-stored-row.ts +23 -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-brokered-gateway-adapter.ts +50 -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/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-mcp-stdio-server.ts +20 -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-recording-filter.ts +9 -0
- package/src/test-utils/build-stub-recording-git.ts +8 -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 +117 -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-echo-claude.ts +21 -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/{fixture-imp-port.ts → create-stub-imp-port.ts} +144 -63
- package/src/test-utils/create-stub-recording-claude.ts +20 -0
- package/src/test-utils/create-stub-recording-codex.ts +20 -0
- package/src/test-utils/create-stub-shared-server-codex.ts +56 -0
- package/src/test-utils/{setup-fake-systemd.ts → create-stub-systemd.ts} +15 -9
- 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/register-test-cleanup.ts +63 -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-mcp-authorization.ts +7 -4
- 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 +110 -0
- package/src/test-utils/setup-mcp-http.ts +26 -30
- package/src/test-utils/setup-temp-dir.ts +7 -6
- 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 +29 -8
- package/src/test-utils/start-daemon-process.ts +193 -0
- package/src/test-utils/start-git-http-server.ts +28 -4
- package/src/test-utils/start-mcp-stdio.ts +190 -0
- package/src/test-utils/start-output-capture.ts +20 -0
- package/src/test-utils/start-stub-answering-listener.ts +65 -0
- package/src/test-utils/start-stub-closing-listener.ts +31 -0
- package/src/test-utils/start-stub-dropping-daemon.ts +105 -0
- package/src/test-utils/start-stub-impd-info.ts +38 -0
- package/src/test-utils/start-stub-impd.ts +143 -0
- package/src/test-utils/start-stub-legacy-daemon.ts +171 -0
- package/src/test-utils/start-stub-mcp-server.ts +55 -0
- package/src/test-utils/start-stub-recording-listener.ts +47 -0
- package/src/test-utils/start-stub-refusing-listener.ts +43 -0
- package/src/test-utils/start-stub-reporter-socket.ts +79 -0
- package/src/test-utils/start-stub-restore-daemon.ts +59 -0
- package/src/test-utils/start-stub-session-bridge.ts +100 -0
- package/src/test-utils/start-stub-silent-server.ts +38 -0
- package/src/test-utils/start-stub-source-daemon.ts +41 -0
- package/src/test-utils/start-stub-stalled-client.ts +61 -0
- package/src/test-utils/start-stub-stalled-listener.ts +44 -0
- package/src/test-utils/start-stub-tap.ts +38 -0
- package/src/test-utils/start-stub-tcp-daemon.ts +75 -0
- package/src/test-utils/start-stub-telemetry-collector.ts +32 -0
- package/src/test-utils/start-stub-unanswering-listener.ts +41 -0
- package/src/test-utils/start-stub-unanswering-unix-listener.ts +38 -0
- package/src/test-utils/start-test-daemon.ts +229 -0
- package/src/test-utils/start-tui-harness.ts +291 -0
- package/src/test-utils/subscribe-to-socket-lines.ts +81 -14
- 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
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import type { AuthProfile } from '../shared/collect-auth-profiles';
|
|
3
|
+
import { mergeDeep } from './merge-deep';
|
|
4
|
+
import type { MockOverrides } from './mock-overrides';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The auth profile kinds that inject a header for a broker host.
|
|
8
|
+
*/
|
|
9
|
+
type HeaderAuthProfile = Extract<AuthProfile, { readonly host: string }>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The auth profile kind that signs in to GitHub.
|
|
13
|
+
*/
|
|
14
|
+
type GitHubAuthProfile = Extract<AuthProfile, { readonly kind: 'github' }>;
|
|
15
|
+
|
|
16
|
+
type HeaderOverrides = MockOverrides<HeaderAuthProfile, keyof HeaderAuthProfile>;
|
|
17
|
+
|
|
18
|
+
type GitHubOverrides = MockOverrides<GitHubAuthProfile, keyof GitHubAuthProfile> & {
|
|
19
|
+
readonly kind: 'github';
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* An auth profile of kind `custom` that sends its secret as a bearer token
|
|
24
|
+
* in a header, or, for an override of kind `github`, a GitHub profile.
|
|
25
|
+
* Either has no environment and no dependencies, and an arbitrary name and
|
|
26
|
+
* secret; a custom profile's host and header name are arbitrary too.
|
|
27
|
+
* Overrides merge into fresh defaults at every depth.
|
|
28
|
+
*/
|
|
29
|
+
export function buildMockAuthProfile(overrides?: HeaderOverrides): HeaderAuthProfile;
|
|
30
|
+
export function buildMockAuthProfile(overrides: GitHubOverrides): GitHubAuthProfile;
|
|
31
|
+
|
|
32
|
+
export function buildMockAuthProfile(
|
|
33
|
+
overrides: HeaderOverrides | GitHubOverrides = {},
|
|
34
|
+
): AuthProfile {
|
|
35
|
+
const name = faker.string.alpha({ length: 8, casing: 'lower' });
|
|
36
|
+
const secret = faker.string.alpha({ length: 8, casing: 'lower' });
|
|
37
|
+
|
|
38
|
+
return isGitHubOverrides(overrides)
|
|
39
|
+
? mergeDeep<GitHubAuthProfile>(
|
|
40
|
+
{ name, secret, kind: 'github', env: {}, dependencies: [] },
|
|
41
|
+
overrides,
|
|
42
|
+
)
|
|
43
|
+
: mergeDeep<HeaderAuthProfile>(
|
|
44
|
+
{
|
|
45
|
+
name,
|
|
46
|
+
secret,
|
|
47
|
+
kind: 'custom',
|
|
48
|
+
host: faker.internet.domainName(),
|
|
49
|
+
header: `x-${faker.string.alpha({ length: 6, casing: 'lower' })}-key`,
|
|
50
|
+
scheme: 'bearer',
|
|
51
|
+
env: {},
|
|
52
|
+
dependencies: [],
|
|
53
|
+
},
|
|
54
|
+
overrides,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isGitHubOverrides(
|
|
59
|
+
overrides: HeaderOverrides | GitHubOverrides,
|
|
60
|
+
): overrides is GitHubOverrides {
|
|
61
|
+
return overrides.kind === 'github';
|
|
62
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import type { DaemonRecord } from '../shared/find-daemon-record';
|
|
3
|
+
import { mergeDeep } from './merge-deep';
|
|
4
|
+
import type { MockOverrides } from './mock-overrides';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The record a daemon writes beside its state directory, for a daemon with
|
|
8
|
+
* no events socket and no TCP listener. The pid and the socket paths are
|
|
9
|
+
* arbitrary. Overrides replace the defaults field by field.
|
|
10
|
+
*/
|
|
11
|
+
export function buildMockDaemonRecord(
|
|
12
|
+
overrides: MockOverrides<DaemonRecord, keyof DaemonRecord> = {},
|
|
13
|
+
): DaemonRecord {
|
|
14
|
+
const dir = faker.system.directoryPath();
|
|
15
|
+
|
|
16
|
+
return mergeDeep<DaemonRecord>(
|
|
17
|
+
{
|
|
18
|
+
pid: faker.number.int({ min: 2, max: 4_194_304 }),
|
|
19
|
+
socketPath: `${dir}/atc-daemon.sock`,
|
|
20
|
+
reporterSocketPath: `${dir}/atc.sock`,
|
|
21
|
+
eventsSocketPath: null,
|
|
22
|
+
listenPort: null,
|
|
23
|
+
},
|
|
24
|
+
overrides,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import type { ExecutionTarget } from '../daemon/build-execution-targets';
|
|
3
|
+
import { mergeDeep } from './merge-deep';
|
|
4
|
+
import type { MockOverrides } from './mock-overrides';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A local-pty execution target with an arbitrary id and identity, no
|
|
8
|
+
* options, and no provider, as a target of a kind this daemon lacks has.
|
|
9
|
+
* Overrides merge into fresh defaults at every depth; a provider override
|
|
10
|
+
* replaces the default whole.
|
|
11
|
+
*/
|
|
12
|
+
export function buildMockExecutionTarget(
|
|
13
|
+
overrides: MockOverrides<ExecutionTarget, keyof ExecutionTarget> = {},
|
|
14
|
+
): ExecutionTarget {
|
|
15
|
+
return mergeDeep<ExecutionTarget>(
|
|
16
|
+
{
|
|
17
|
+
id: faker.word.noun(),
|
|
18
|
+
kind: 'local-pty',
|
|
19
|
+
options: {},
|
|
20
|
+
identity: `local-pty:${faker.string.uuid()}`,
|
|
21
|
+
provider: null,
|
|
22
|
+
},
|
|
23
|
+
overrides,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import { toAgentSessionID } from '../shared/to-agent-session-id';
|
|
3
|
+
import { toSessionID } from '../shared/to-session-id';
|
|
4
|
+
import type { FleetEntry } from '../store/fleet-entry';
|
|
5
|
+
import { mergeDeep } from './merge-deep';
|
|
6
|
+
import type { MockOverrides } from './mock-overrides';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The fields the defaults set; every other field is absent until an
|
|
10
|
+
* override gives it whole.
|
|
11
|
+
*/
|
|
12
|
+
type DefaultedKey = 'sessionID' | 'name' | 'cwd' | 'agentSessionID' | 'agent';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A fleet row for a live Claude session on the local target: a fresh
|
|
16
|
+
* session id and a fresh agent session id, so it restores as resumable,
|
|
17
|
+
* with an arbitrary name and directory and every optional field absent.
|
|
18
|
+
* Overrides merge into fresh defaults at every depth.
|
|
19
|
+
*/
|
|
20
|
+
export function buildMockFleetEntry(
|
|
21
|
+
overrides: MockOverrides<FleetEntry, DefaultedKey> = {},
|
|
22
|
+
): FleetEntry {
|
|
23
|
+
return mergeDeep<FleetEntry, DefaultedKey>(
|
|
24
|
+
{
|
|
25
|
+
sessionID: toSessionID(faker.string.uuid()),
|
|
26
|
+
name: faker.word.noun(),
|
|
27
|
+
cwd: faker.system.directoryPath(),
|
|
28
|
+
agentSessionID: toAgentSessionID(faker.string.uuid()),
|
|
29
|
+
agent: 'claude',
|
|
30
|
+
},
|
|
31
|
+
overrides,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import type { GatewayConfig } from '../shared/collect-gateways';
|
|
3
|
+
import { mergeDeep } from './merge-deep';
|
|
4
|
+
import type { MockOverrides } from './mock-overrides';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The fields the defaults set; every other field is absent until an
|
|
8
|
+
* override gives it whole.
|
|
9
|
+
*/
|
|
10
|
+
type DefaultedKey = 'id' | 'label' | 'mark' | 'bin' | 'args' | 'baseURL' | 'env';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A Claude-compatible backend with no arguments, no environment, no helper
|
|
14
|
+
* command, no settings, and no auth. The id, label, mark, binary, and base
|
|
15
|
+
* URL are arbitrary. Overrides merge into fresh defaults at every depth.
|
|
16
|
+
*/
|
|
17
|
+
export function buildMockGatewayConfig(
|
|
18
|
+
overrides: MockOverrides<GatewayConfig, DefaultedKey> = {},
|
|
19
|
+
): GatewayConfig {
|
|
20
|
+
return mergeDeep<GatewayConfig, DefaultedKey>(
|
|
21
|
+
{
|
|
22
|
+
id: faker.string.alpha({ length: 8, casing: 'lower' }),
|
|
23
|
+
label: faker.company.name(),
|
|
24
|
+
mark: faker.string.alpha({ length: 1, casing: 'lower' }),
|
|
25
|
+
bin: faker.system.filePath(),
|
|
26
|
+
args: [],
|
|
27
|
+
baseURL: faker.internet.url(),
|
|
28
|
+
env: {},
|
|
29
|
+
},
|
|
30
|
+
overrides,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import type { ImpIdentity } from '../daemon/imp-port';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* An impd token identity with an arbitrary name that manages the imps
|
|
6
|
+
* matching `atc-*` and may grant no secret. An override replaces the field
|
|
7
|
+
* it names.
|
|
8
|
+
*/
|
|
9
|
+
export function buildMockImpIdentity(overrides: Partial<ImpIdentity> = {}): ImpIdentity {
|
|
10
|
+
return {
|
|
11
|
+
kind: 'token',
|
|
12
|
+
name: faker.word.noun(),
|
|
13
|
+
scope: 'manage',
|
|
14
|
+
imps: ['atc-*'],
|
|
15
|
+
grantable: [],
|
|
16
|
+
...overrides,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import type { ImpSessionRequest } from '../daemon/imp-port';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The request that starts a process on an imp.
|
|
6
|
+
*/
|
|
7
|
+
type StartRequest = Extract<ImpSessionRequest, { readonly kind: 'start' }>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The request that attaches to a process already running on an imp.
|
|
11
|
+
*/
|
|
12
|
+
type AttachRequest = Extract<ImpSessionRequest, { readonly kind: 'attach' }>;
|
|
13
|
+
|
|
14
|
+
type AttachOverrides = Partial<AttachRequest> & { readonly kind: 'attach' };
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A request that starts `true` on an imp with an empty environment, or, for
|
|
18
|
+
* an override of kind `attach`, one that attaches without waking the imp.
|
|
19
|
+
* Either carries no resume point and no requirements, and an arbitrary imp
|
|
20
|
+
* name, session name, and terminal size; a start's directory is arbitrary
|
|
21
|
+
* too. Each override replaces the default of its field.
|
|
22
|
+
*/
|
|
23
|
+
export function buildMockImpSessionRequest(overrides?: Partial<StartRequest>): StartRequest;
|
|
24
|
+
export function buildMockImpSessionRequest(overrides: AttachOverrides): AttachRequest;
|
|
25
|
+
|
|
26
|
+
export function buildMockImpSessionRequest(
|
|
27
|
+
overrides: Partial<StartRequest> | AttachOverrides = {},
|
|
28
|
+
): ImpSessionRequest {
|
|
29
|
+
const name = `imp-${faker.string.alpha({ length: 6, casing: 'lower' })}`;
|
|
30
|
+
const session = faker.string.uuid();
|
|
31
|
+
const cols = faker.number.int({ min: 20, max: 300 });
|
|
32
|
+
const rows = faker.number.int({ min: 5, max: 100 });
|
|
33
|
+
|
|
34
|
+
return isAttachOverrides(overrides)
|
|
35
|
+
? { name, session, cols, rows, wake: false, ...overrides }
|
|
36
|
+
: {
|
|
37
|
+
kind: 'start',
|
|
38
|
+
name,
|
|
39
|
+
session,
|
|
40
|
+
argv: ['true'],
|
|
41
|
+
env: {},
|
|
42
|
+
cwd: faker.system.directoryPath(),
|
|
43
|
+
cols,
|
|
44
|
+
rows,
|
|
45
|
+
...overrides,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isAttachOverrides(
|
|
50
|
+
overrides: Partial<StartRequest> | AttachOverrides,
|
|
51
|
+
): overrides is AttachOverrides {
|
|
52
|
+
return overrides.kind === 'attach';
|
|
53
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import type { GatewayStore } from '../federation/gateway-store';
|
|
3
|
+
import { mergeDeep } from './merge-deep';
|
|
4
|
+
import type { MockOverrides } from './mock-overrides';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The binding a keyed request claims in the gateway store, before any
|
|
8
|
+
* outcome or send is recorded for it.
|
|
9
|
+
*/
|
|
10
|
+
type KeyBindingClaim = Parameters<GatewayStore['claimBinding']>[0];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A claim of a keyed spawn by an arbitrary principal under an arbitrary
|
|
14
|
+
* key, bound to an arbitrary daemon that announced an arbitrary retention,
|
|
15
|
+
* with an arbitrary payload hash and claim id. Overrides replace the
|
|
16
|
+
* defaults field by field.
|
|
17
|
+
*/
|
|
18
|
+
export function buildMockKeyBindingClaim(
|
|
19
|
+
overrides: MockOverrides<KeyBindingClaim, keyof KeyBindingClaim> = {},
|
|
20
|
+
): KeyBindingClaim {
|
|
21
|
+
return mergeDeep<KeyBindingClaim>(
|
|
22
|
+
{
|
|
23
|
+
principal: faker.string.alphanumeric(12),
|
|
24
|
+
operation: 'session.spawn',
|
|
25
|
+
key: faker.string.alphanumeric(16),
|
|
26
|
+
daemon: faker.string.alpha({ length: 8, casing: 'lower' }),
|
|
27
|
+
daemonID: faker.string.uuid(),
|
|
28
|
+
retentionMs: faker.number.int({ min: 1000, max: 86_400_000 }),
|
|
29
|
+
payloadHash: faker.string.hexadecimal({ length: 64, casing: 'lower', prefix: '' }),
|
|
30
|
+
claimID: faker.string.uuid(),
|
|
31
|
+
},
|
|
32
|
+
overrides,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import { toMessageID } from '../shared/to-message-id';
|
|
3
|
+
import { toSessionID } from '../shared/to-session-id';
|
|
4
|
+
import type { MessageRecord } from '../store/message-record';
|
|
5
|
+
|
|
6
|
+
type MessageRecordOverrides = {
|
|
7
|
+
readonly [K in keyof MessageRecord]?: MessageRecord[K] | undefined;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A message accepted for a session and not yet delivered: a fresh message
|
|
12
|
+
* id and a fresh atc id, an arbitrary sender, text, and send time, and no
|
|
13
|
+
* agent session id, delivery, answer, or turn. An override replaces the
|
|
14
|
+
* field it names, and an override of undefined leaves that field out.
|
|
15
|
+
*/
|
|
16
|
+
export function buildMockMessageRecord(overrides: MessageRecordOverrides = {}): MessageRecord {
|
|
17
|
+
return {
|
|
18
|
+
id: toMessageID(faker.string.uuid()),
|
|
19
|
+
atcID: toSessionID(faker.string.uuid()),
|
|
20
|
+
from: faker.person.firstName(),
|
|
21
|
+
text: faker.lorem.sentence(),
|
|
22
|
+
status: 'accepted',
|
|
23
|
+
sentAt: faker.date.past().getTime(),
|
|
24
|
+
...Object.fromEntries(Object.entries(overrides).filter(([, value]) => value !== undefined)),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import type { MirrorSession } from '../client/to-mirror-session';
|
|
3
|
+
import { mergeDeep } from './merge-deep';
|
|
4
|
+
import type { MockOverrides } from './mock-overrides';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A client's mirror of a live, running Claude session in a local terminal
|
|
8
|
+
* on the `local` target: read, unpinned, not resumable, unable to eject,
|
|
9
|
+
* with no parent and the agent's default model. The id, name, directory,
|
|
10
|
+
* last message, and times are arbitrary. Overrides replace the defaults
|
|
11
|
+
* field by field.
|
|
12
|
+
*/
|
|
13
|
+
export function buildMockMirrorSession(
|
|
14
|
+
overrides: MockOverrides<MirrorSession, keyof MirrorSession> = {},
|
|
15
|
+
): MirrorSession {
|
|
16
|
+
const cwd = faker.system.directoryPath();
|
|
17
|
+
|
|
18
|
+
return mergeDeep<MirrorSession>(
|
|
19
|
+
{
|
|
20
|
+
id: faker.string.uuid(),
|
|
21
|
+
name: faker.word.noun(),
|
|
22
|
+
cwd,
|
|
23
|
+
pinned: false,
|
|
24
|
+
lastAttachedAt: faker.date.recent().getTime(),
|
|
25
|
+
repoRoot: cwd,
|
|
26
|
+
state: 'running',
|
|
27
|
+
unread: false,
|
|
28
|
+
lastMsg: faker.lorem.sentence(),
|
|
29
|
+
createdAt: faker.date.past().getTime(),
|
|
30
|
+
kind: 'pty',
|
|
31
|
+
alive: true,
|
|
32
|
+
resumable: false,
|
|
33
|
+
canEject: false,
|
|
34
|
+
agent: 'claude',
|
|
35
|
+
parent: null,
|
|
36
|
+
target: 'local',
|
|
37
|
+
model: null,
|
|
38
|
+
harness: 'running',
|
|
39
|
+
},
|
|
40
|
+
overrides,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import type { RegistryDaemon } from '../federation/types';
|
|
3
|
+
import { mergeDeep } from './merge-deep';
|
|
4
|
+
import type { MockOverrides } from './mock-overrides';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A registry daemon with an arbitrary name, address, pinned daemon id,
|
|
8
|
+
* and token. Its incarnation is the first 8 characters of the
|
|
9
|
+
* daemon id in play, the override's when one is given, so a gateway id
|
|
10
|
+
* built from it routes back unless an override sets the incarnation too.
|
|
11
|
+
* Overrides merge into fresh defaults at every depth.
|
|
12
|
+
*/
|
|
13
|
+
export function buildMockRegistryDaemon(
|
|
14
|
+
overrides: MockOverrides<RegistryDaemon, keyof RegistryDaemon> = {},
|
|
15
|
+
): RegistryDaemon {
|
|
16
|
+
const daemonID = overrides.daemonID ?? faker.string.uuid();
|
|
17
|
+
|
|
18
|
+
return mergeDeep<RegistryDaemon>(
|
|
19
|
+
{
|
|
20
|
+
name: faker.string.alpha({ length: 8, casing: 'lower' }),
|
|
21
|
+
address: { host: faker.internet.ipv4(), port: faker.internet.port() },
|
|
22
|
+
daemonID,
|
|
23
|
+
incarnation: daemonID.slice(0, 8),
|
|
24
|
+
token: faker.string.hexadecimal({ length: 32, casing: 'lower', prefix: '' }),
|
|
25
|
+
},
|
|
26
|
+
overrides,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import type { SessionDescriptor } from '../daemon/sessions';
|
|
3
|
+
import { toDaemonID } from '../shared/to-daemon-id';
|
|
4
|
+
import { toSessionID } from '../shared/to-session-id';
|
|
5
|
+
import { mergeDeep } from './merge-deep';
|
|
6
|
+
import type { MockOverrides } from './mock-overrides';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The fields the defaults set; every other field is absent until an
|
|
10
|
+
* override gives it whole.
|
|
11
|
+
*/
|
|
12
|
+
type DefaultedKey =
|
|
13
|
+
| 'id'
|
|
14
|
+
| 'name'
|
|
15
|
+
| 'cwd'
|
|
16
|
+
| 'state'
|
|
17
|
+
| 'unread'
|
|
18
|
+
| 'lastMsg'
|
|
19
|
+
| 'agent'
|
|
20
|
+
| 'pinned'
|
|
21
|
+
| 'lastAttachedAt'
|
|
22
|
+
| 'repoRoot'
|
|
23
|
+
| 'namedBy'
|
|
24
|
+
| 'createdAt'
|
|
25
|
+
| 'kind'
|
|
26
|
+
| 'alive'
|
|
27
|
+
| 'canEject'
|
|
28
|
+
| 'locator'
|
|
29
|
+
| 'lifecycle';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The wire view of a live, running Claude session in a local terminal on
|
|
33
|
+
* the `local` target, as `session.list` and the session events carry it:
|
|
34
|
+
* read, unpinned, named by the user, with no parent, workspace, model, or
|
|
35
|
+
* agent session id. The id, name, directories, last message, times, and
|
|
36
|
+
* daemon id are arbitrary. Overrides merge into fresh defaults at every
|
|
37
|
+
* depth.
|
|
38
|
+
*/
|
|
39
|
+
export function buildMockSessionDescriptor(
|
|
40
|
+
overrides: MockOverrides<SessionDescriptor, DefaultedKey> = {},
|
|
41
|
+
): SessionDescriptor {
|
|
42
|
+
const cwd = faker.system.directoryPath();
|
|
43
|
+
|
|
44
|
+
return mergeDeep<SessionDescriptor, DefaultedKey>(
|
|
45
|
+
{
|
|
46
|
+
id: toSessionID(faker.string.uuid()),
|
|
47
|
+
name: faker.word.noun(),
|
|
48
|
+
cwd,
|
|
49
|
+
state: 'running',
|
|
50
|
+
unread: false,
|
|
51
|
+
lastMsg: faker.lorem.sentence(),
|
|
52
|
+
agent: 'claude',
|
|
53
|
+
pinned: false,
|
|
54
|
+
lastAttachedAt: faker.date.recent().getTime(),
|
|
55
|
+
repoRoot: cwd,
|
|
56
|
+
namedBy: 'user',
|
|
57
|
+
createdAt: faker.date.past().getTime(),
|
|
58
|
+
kind: 'pty',
|
|
59
|
+
alive: true,
|
|
60
|
+
canEject: false,
|
|
61
|
+
locator: { daemonID: toDaemonID(faker.string.uuid()), targetID: 'local' },
|
|
62
|
+
lifecycle: { desired: 'run', vm: 'none', harness: 'running', attachment: 'local' },
|
|
63
|
+
},
|
|
64
|
+
overrides,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import type { Session } from '../daemon/sessions';
|
|
3
|
+
import { toSessionID } from '../shared/to-session-id';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A session manager's record of an exited Claude session on the `local`
|
|
7
|
+
* target with no terminal: read, unpinned, auto-named, with no parent,
|
|
8
|
+
* withheld env, workspace, model, or agent session id, and its own host.
|
|
9
|
+
* The id, name, directories, and times are arbitrary. An override replaces
|
|
10
|
+
* the field it names.
|
|
11
|
+
*/
|
|
12
|
+
export function buildMockSession(overrides: Partial<Session> = {}): Session {
|
|
13
|
+
const id = overrides.id ?? toSessionID(faker.string.uuid());
|
|
14
|
+
const cwd = faker.system.directoryPath();
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
id,
|
|
18
|
+
name: faker.word.noun(),
|
|
19
|
+
cwd,
|
|
20
|
+
kind: 'pty',
|
|
21
|
+
pty: null,
|
|
22
|
+
state: 'exited',
|
|
23
|
+
unread: false,
|
|
24
|
+
lastMsg: '',
|
|
25
|
+
agent: 'claude',
|
|
26
|
+
pinned: false,
|
|
27
|
+
lastAttachedAt: faker.date.recent().getTime(),
|
|
28
|
+
repoRoot: cwd,
|
|
29
|
+
namedBy: 'auto',
|
|
30
|
+
createdAt: faker.date.past().getTime(),
|
|
31
|
+
parent: null,
|
|
32
|
+
target: 'local',
|
|
33
|
+
targetIdentity: 'local-pty:test',
|
|
34
|
+
withheldEnv: [],
|
|
35
|
+
desired: 'run',
|
|
36
|
+
vm: 'none',
|
|
37
|
+
attachment: 'local',
|
|
38
|
+
suspended: false,
|
|
39
|
+
hostKey: id,
|
|
40
|
+
bridgeEpoch: 0,
|
|
41
|
+
...overrides,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import type { SortableSessionView } from '../protocol/sortable-session-view';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The ordering fields of a running, unpinned, top-level session, as the
|
|
6
|
+
* session list sorts it. The id and both times are arbitrary.
|
|
7
|
+
*/
|
|
8
|
+
export function buildMockSortableSessionView(
|
|
9
|
+
overrides: Partial<SortableSessionView> = {},
|
|
10
|
+
): SortableSessionView {
|
|
11
|
+
return {
|
|
12
|
+
id: faker.string.uuid(),
|
|
13
|
+
parent: null,
|
|
14
|
+
state: 'running',
|
|
15
|
+
pinned: false,
|
|
16
|
+
lastAttachedAt: faker.date.recent().getTime(),
|
|
17
|
+
createdAt: faker.date.past().getTime(),
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import { toSessionID } from '../shared/to-session-id';
|
|
3
|
+
import type { StoredEvent } from '../store/state-store';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A row of the event trail as the store reads it back: a `turn-done` event
|
|
7
|
+
* with an arbitrary row id, time, and fresh atc id, and no agent session
|
|
8
|
+
* id, detail, message, or label. Each override replaces the default of its field.
|
|
9
|
+
*/
|
|
10
|
+
export function buildMockStoredEvent(overrides: Partial<StoredEvent> = {}): StoredEvent {
|
|
11
|
+
return {
|
|
12
|
+
id: faker.number.int({ min: 1, max: 1_000_000 }),
|
|
13
|
+
at: faker.date.past().getTime(),
|
|
14
|
+
atcID: toSessionID(faker.string.uuid()),
|
|
15
|
+
agentSessionID: null,
|
|
16
|
+
kind: 'turn-done',
|
|
17
|
+
detail: null,
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import type { StoredRow } from '../read-stored-rows';
|
|
3
|
+
import { mergeDeep } from './merge-deep';
|
|
4
|
+
import type { MockOverrides } from './mock-overrides';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A fleet row a daemon has stored, for a live session with a fresh id, an
|
|
8
|
+
* arbitrary name, and no known agent session id. Overrides replace the
|
|
9
|
+
* defaults field by field.
|
|
10
|
+
*/
|
|
11
|
+
export function buildMockStoredRow(
|
|
12
|
+
overrides: MockOverrides<StoredRow, keyof StoredRow> = {},
|
|
13
|
+
): StoredRow {
|
|
14
|
+
return mergeDeep<StoredRow>(
|
|
15
|
+
{
|
|
16
|
+
id: faker.string.uuid(),
|
|
17
|
+
name: faker.word.noun(),
|
|
18
|
+
exited: false,
|
|
19
|
+
agentSessionID: null,
|
|
20
|
+
},
|
|
21
|
+
overrides,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AgentAdapter } from '../agents/agent-adapter';
|
|
2
|
+
import { buildMockAgentAdapter } from './build-mock-agent-adapter';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* An agent adapter for daemon tests whose hooks report attention: id
|
|
6
|
+
* `claude`, a spawn that runs `sleep 30`, sessions that take inbox
|
|
7
|
+
* messages, a `Notification` hook read as needing input and every other
|
|
8
|
+
* hook as a submitted prompt, and `claude --resume` as the resume command.
|
|
9
|
+
* It reads nothing from a hook's payload, so a payload's session id binds
|
|
10
|
+
* nothing. Every other member is the mock adapter's. An override replaces
|
|
11
|
+
* the member it names.
|
|
12
|
+
*/
|
|
13
|
+
export function buildStubAttentionAdapter(overrides: Partial<AgentAdapter> = {}): AgentAdapter {
|
|
14
|
+
return buildMockAgentAdapter({
|
|
15
|
+
takesMessages: true,
|
|
16
|
+
normalizeHook: (hook) => ({
|
|
17
|
+
kind: hook.event === 'Notification' ? 'needs-input' : 'prompt-submitted',
|
|
18
|
+
}),
|
|
19
|
+
buildResumeCommand: () => 'claude --resume',
|
|
20
|
+
...overrides,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BridgeContext } from '../daemon/start-session-bridge';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The daemon's side of a session bridge, for tests that drive one bridge
|
|
5
|
+
* without a daemon behind it: no live session, hook events that go nowhere,
|
|
6
|
+
* every report recorded, every tap attached, every ack for an unknown
|
|
7
|
+
* message, and detaches that do nothing. An override replaces the member it
|
|
8
|
+
* names.
|
|
9
|
+
*/
|
|
10
|
+
export function buildStubBridgeContext(overrides: Partial<BridgeContext> = {}): BridgeContext {
|
|
11
|
+
return {
|
|
12
|
+
findSession: () => {},
|
|
13
|
+
applyHookEvent: () => {},
|
|
14
|
+
applyReport: () => Promise.resolve(true),
|
|
15
|
+
attachTap: () => 'ok',
|
|
16
|
+
ackMessage: () => Promise.resolve('unknown'),
|
|
17
|
+
detachTap: () => {},
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|