@byok-sdk/client 0.1.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/LICENSE +21 -0
- package/README.md +11 -0
- package/dist/adapters/claude/claude-adapter.d.ts +155 -0
- package/dist/adapters/claude/events.d.ts +91 -0
- package/dist/adapters/claude/permission-mapping.d.ts +142 -0
- package/dist/adapters/claude/process-client.d.ts +103 -0
- package/dist/adapters/claude/resolve-approval-mcp-bin.d.ts +45 -0
- package/dist/adapters/claude/resolve-bin.d.ts +27 -0
- package/dist/adapters/codex/codex-adapter.d.ts +97 -0
- package/dist/adapters/codex/events.d.ts +41 -0
- package/dist/adapters/codex/permission-mapping.d.ts +107 -0
- package/dist/adapters/codex/process-runner.d.ts +86 -0
- package/dist/adapters/codex/resolve-bin.d.ts +25 -0
- package/dist/adapters/pi/events.d.ts +58 -0
- package/dist/adapters/pi/permission-mapping.d.ts +46 -0
- package/dist/adapters/pi/pi-adapter.d.ts +29 -0
- package/dist/adapters/pi/resolve-bin.d.ts +44 -0
- package/dist/adapters/pi/rpc-client.d.ts +101 -0
- package/dist/bin/approval-mcp-server.d.ts +69 -0
- package/dist/bin/audit-log.d.ts +212 -0
- package/dist/bin/byok-agent.d.ts +2 -0
- package/dist/bin/byok-agent.js +12347 -0
- package/dist/bin/byok-agent.js.map +1 -0
- package/dist/bin/byok-approval-mcp.d.ts +2 -0
- package/dist/bin/byok-approval-mcp.js +498 -0
- package/dist/bin/byok-approval-mcp.js.map +1 -0
- package/dist/bin/commands/approvals.d.ts +31 -0
- package/dist/bin/commands/approve-reject.d.ts +29 -0
- package/dist/bin/commands/doctor.d.ts +15 -0
- package/dist/bin/commands/pair.d.ts +7 -0
- package/dist/bin/commands/runtimes.d.ts +7 -0
- package/dist/bin/commands/service.d.ts +54 -0
- package/dist/bin/commands/start.d.ts +21 -0
- package/dist/bin/commands/status.d.ts +25 -0
- package/dist/bin/commands/support-bundle.d.ts +7 -0
- package/dist/bin/commands/tasks.d.ts +27 -0
- package/dist/bin/commands/unpair.d.ts +124 -0
- package/dist/bin/commands/workspaces.d.ts +16 -0
- package/dist/bin/config.d.ts +41 -0
- package/dist/bin/control-client.d.ts +50 -0
- package/dist/bin/format.d.ts +67 -0
- package/dist/bin/runtime-probe.d.ts +34 -0
- package/dist/bin/tasks-view.d.ts +49 -0
- package/dist/daemon/approvals.d.ts +87 -0
- package/dist/daemon/auth-manager.d.ts +52 -0
- package/dist/daemon/blob-client.d.ts +22 -0
- package/dist/daemon/connection-manager.d.ts +502 -0
- package/dist/daemon/control-protocol.d.ts +299 -0
- package/dist/daemon/control-server.d.ts +61 -0
- package/dist/daemon/create-daemon.d.ts +364 -0
- package/dist/daemon/cursor-store.d.ts +30 -0
- package/dist/daemon/daemon-owner.d.ts +24 -0
- package/dist/daemon/deterministic-jitter.d.ts +20 -0
- package/dist/daemon/device-keys.d.ts +45 -0
- package/dist/daemon/device-proof-signer.d.ts +41 -0
- package/dist/daemon/environment.d.ts +96 -0
- package/dist/daemon/git-workspace-store.d.ts +49 -0
- package/dist/daemon/git-workspace.d.ts +80 -0
- package/dist/daemon/http-client.d.ts +8 -0
- package/dist/daemon/journal/journal.d.ts +342 -0
- package/dist/daemon/journal/sqlite-journal.d.ts +173 -0
- package/dist/daemon/journal/sqlite-support.d.ts +96 -0
- package/dist/daemon/journal/storage-policy.d.ts +368 -0
- package/dist/daemon/long-poll-transport.d.ts +142 -0
- package/dist/daemon/observer.d.ts +345 -0
- package/dist/daemon/operational-health.d.ts +87 -0
- package/dist/daemon/policy.d.ts +16 -0
- package/dist/daemon/progress-batcher.d.ts +31 -0
- package/dist/daemon/runtime-capabilities.d.ts +36 -0
- package/dist/daemon/session-workspace-store.d.ts +75 -0
- package/dist/daemon/store.d.ts +54 -0
- package/dist/daemon/task-runner.d.ts +862 -0
- package/dist/daemon/truth-memory-client.d.ts +94 -0
- package/dist/daemon/url.d.ts +60 -0
- package/dist/daemon/ws-transport.d.ts +110 -0
- package/dist/diagnostics/diagnostics.d.ts +114 -0
- package/dist/diagnostics/support-bundle.d.ts +81 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +10056 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle/create-service-lifecycle.d.ts +59 -0
- package/dist/lifecycle/exec-runner.d.ts +141 -0
- package/dist/lifecycle/launchd.d.ts +55 -0
- package/dist/lifecycle/service-types.d.ts +177 -0
- package/dist/lifecycle/systemd.d.ts +46 -0
- package/dist/lifecycle/winsw.d.ts +64 -0
- package/dist/types.d.ts +178 -0
- package/dist/util/async-queue.d.ts +45 -0
- package/dist/util/atomic-write.d.ts +86 -0
- package/dist/util/secure-dir.d.ts +124 -0
- package/package.json +66 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { connectControlClient } from '../control-client';
|
|
2
|
+
export interface ApprovalsListDeps {
|
|
3
|
+
log?: (line: string) => void;
|
|
4
|
+
error?: (line: string) => void;
|
|
5
|
+
/** DI for tests: substitute the real control-socket connection attempt. */
|
|
6
|
+
connectControl?: typeof connectControlClient;
|
|
7
|
+
/** DI for tests: pin "now" for deterministic age rendering — see `formatApprovalsListLines`. */
|
|
8
|
+
now?: () => number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Finding F4 (cross-model adversarial review): `byok-agent approvals` —
|
|
12
|
+
* before this command existed, an operator had no way to ever learn a
|
|
13
|
+
* pending approval's `approvalId` short of reading raw `audit.jsonl`
|
|
14
|
+
* entries by hand (and even that only worked if the daemon happened to
|
|
15
|
+
* surface one there at all): `approve`/`reject` both *require* an
|
|
16
|
+
* `approvalId`, but nothing in this CLI ever printed one.
|
|
17
|
+
*
|
|
18
|
+
* Lists the control socket's `approvals.list` result — the exact same
|
|
19
|
+
* `ApprovalRegistry` entries `approve`/`reject` resolve against
|
|
20
|
+
* (`create-daemon.ts`'s method registry, backed by `daemon/approvals.ts`) —
|
|
21
|
+
* as one line per pending approval: `approvalId`, `taskId`, `age`, and a
|
|
22
|
+
* (possibly truncated — see `formatApprovalsListLines`) summary excerpt.
|
|
23
|
+
*
|
|
24
|
+
* No persisted-state fallback, mirroring `approve`/`reject`
|
|
25
|
+
* (`commands/approve-reject.ts`): a pending approval only ever means
|
|
26
|
+
* anything against a LIVE daemon, so daemon-unreachable is reported as a
|
|
27
|
+
* clear, specific error (and this command exits non-zero) rather than a
|
|
28
|
+
* silent "no approvals" — those are different outcomes and a script
|
|
29
|
+
* parsing this output needs to be able to tell them apart.
|
|
30
|
+
*/
|
|
31
|
+
export declare function runApprovalsCommand(storeDir: string, productId: string, deps?: ApprovalsListDeps): Promise<void>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { connectControlClient } from '../control-client';
|
|
2
|
+
export interface ApproveRejectDeps {
|
|
3
|
+
log?: (line: string) => void;
|
|
4
|
+
error?: (line: string) => void;
|
|
5
|
+
/** DI for tests: substitute the real control-socket connection attempt. */
|
|
6
|
+
connectControl?: typeof connectControlClient;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* M4 Phase 2: `approve`/`reject` now genuinely work from a separate,
|
|
10
|
+
* short-lived CLI invocation — they call the control socket's
|
|
11
|
+
* `approvals.resolve` method (`create-daemon.ts`'s method registry, backed
|
|
12
|
+
* by `daemon/approvals.ts`'s `ApprovalRegistry`) rather than requiring a
|
|
13
|
+
* `Daemon` this same process just started (which never worked at all: see
|
|
14
|
+
* git history for the pre-M4 version of this module, which called
|
|
15
|
+
* `daemon.approve`/`daemon.reject` directly and always failed with
|
|
16
|
+
* "daemon is not started" for exactly that reason — `byok-agent.ts` used to
|
|
17
|
+
* not even dispatch to these commands as a result).
|
|
18
|
+
*
|
|
19
|
+
* Still honest about what these can resolve TODAY: no bundled runtime
|
|
20
|
+
* adapter (pi/claude/codex) raises an approval yet (see `create-daemon.ts`'s
|
|
21
|
+
* `toRuntimeInfoCapabilities` doc comment), so nothing ever calls
|
|
22
|
+
* `ApprovalRegistry.register()` in this SDK version — every `approve`/
|
|
23
|
+
* `reject` against a real daemon today resolves to the registry's own
|
|
24
|
+
* `not_found` error, UNLESS the daemon isn't reachable at all, in which case
|
|
25
|
+
* that's reported first. Both failure modes are reported with a clear,
|
|
26
|
+
* specific message rather than a generic "not started" one.
|
|
27
|
+
*/
|
|
28
|
+
export declare function runApproveCommand(storeDir: string, productId: string, approvalId: string, deps?: ApproveRejectDeps): Promise<void>;
|
|
29
|
+
export declare function runRejectCommand(storeDir: string, productId: string, approvalId: string, reason: string | undefined, deps?: ApproveRejectDeps): Promise<void>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DaemonConfig } from '../../index';
|
|
2
|
+
import { type CollectDiagnosticsOptions } from '../../diagnostics/diagnostics';
|
|
3
|
+
export interface DoctorOptions extends CollectDiagnosticsOptions {
|
|
4
|
+
json?: boolean;
|
|
5
|
+
fix?: boolean;
|
|
6
|
+
confirmed?: boolean;
|
|
7
|
+
log?: (line: string) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare class DoctorConfirmationRequiredError extends Error {
|
|
10
|
+
constructor();
|
|
11
|
+
}
|
|
12
|
+
export declare class DoctorDaemonRunningError extends Error {
|
|
13
|
+
constructor();
|
|
14
|
+
}
|
|
15
|
+
export declare function runDoctorCommand(config: DaemonConfig, options?: DoctorOptions): Promise<void>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Daemon, type DaemonConfig } from '../../index';
|
|
2
|
+
export interface PairDeps {
|
|
3
|
+
log?: (line: string) => void;
|
|
4
|
+
/** DI for tests: skip constructing a real `createDaemon(config)` and drive a stub/pre-built instance instead. */
|
|
5
|
+
daemon?: Pick<Daemon, 'pair'>;
|
|
6
|
+
}
|
|
7
|
+
export declare function runPairCommand(config: DaemonConfig, code: string, deps?: PairDeps): Promise<void>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type DaemonConfig, type RuntimeAdapter } from '../../index';
|
|
2
|
+
export interface RuntimesDeps {
|
|
3
|
+
log?: (line: string) => void;
|
|
4
|
+
/** DI for tests: probe these adapters instead of constructing the real bundled pi/claude/codex set. */
|
|
5
|
+
adapters?: RuntimeAdapter[];
|
|
6
|
+
}
|
|
7
|
+
export declare function runRuntimesCommand(config: DaemonConfig, deps?: RuntimesDeps): Promise<void>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type DaemonConfig, type ServiceDefinition, type ServiceLifecycle } from '../../index';
|
|
2
|
+
export interface ServiceCommandDeps {
|
|
3
|
+
log?: (line: string) => void;
|
|
4
|
+
/** DI for tests: drive a pre-built lifecycle instead of the real platform-dispatched `createServiceLifecycle`. */
|
|
5
|
+
lifecycle?: ServiceLifecycle;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Builds the SAME `ServiceDefinition` every one of `install`/`uninstall`/
|
|
9
|
+
* `service-start`/`service-stop`/`service-status` derives — critical so
|
|
10
|
+
* `uninstall`/`service-stop`/`service-status` compute the exact same
|
|
11
|
+
* service name (and, on Windows, install directory) `install` used,
|
|
12
|
+
* without requiring the operator to re-supply identical ad hoc flags to
|
|
13
|
+
* five separate invocations. `--name`/`--agent-bin`/`--node-bin`/
|
|
14
|
+
* `--winsw-bin`/`--winsw-install-dir` are override escape hatches; every
|
|
15
|
+
* default is derived deterministically from `config` alone (mirrors
|
|
16
|
+
* `bin/config.ts`'s own doc comment: every subcommand loads config the same
|
|
17
|
+
* way).
|
|
18
|
+
*
|
|
19
|
+
* `agentBin` defaults to `process.argv[1]` — the actual script path Node is
|
|
20
|
+
* currently running, always correct regardless of how this CLI was
|
|
21
|
+
* invoked (global install, `npx`, a product's own `node_modules/.bin`
|
|
22
|
+
* symlink) — NOT an `import.meta`-based guess; see
|
|
23
|
+
* `lifecycle/create-service-lifecycle.ts`'s doc comment for why this
|
|
24
|
+
* module family never tries to resolve that path itself. A product
|
|
25
|
+
* shipping a single compiled binary (`templates/packaging/`) instead of
|
|
26
|
+
* `node + byok-agent.js` should pass `--agent-bin`/`--node-bin` explicitly,
|
|
27
|
+
* or construct a `ServiceDefinition` directly via `createServiceLifecycle`
|
|
28
|
+
* rather than this convenience CLI subcommand.
|
|
29
|
+
*
|
|
30
|
+
* `configPath` is resolved to an ABSOLUTE path before being baked into the
|
|
31
|
+
* generated service's `--config <path>` argument: a service launched by
|
|
32
|
+
* launchd/systemd/WinSW starts with the OS service manager's OWN minimal
|
|
33
|
+
* environment and working directory, not the operator's interactive shell
|
|
34
|
+
* — a relative path here would resolve against whatever cwd the service
|
|
35
|
+
* manager happens to choose, not where the operator ran `install` from.
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildServiceDefinition(config: DaemonConfig, configPath: string, rest: string[]): ServiceDefinition;
|
|
38
|
+
/**
|
|
39
|
+
* Generates the platform service definition (plist/unit/WinSW xml),
|
|
40
|
+
* registers it with the OS service manager, and starts it — see
|
|
41
|
+
* `lifecycle/create-service-lifecycle.ts` for the per-platform mechanics.
|
|
42
|
+
* On Windows, `--winsw-bin <path>` is required (the product-bundled WinSW
|
|
43
|
+
* executable — Decision-6: this SDK never bundles/downloads it itself; see
|
|
44
|
+
* `templates/service/winsw/README.md`).
|
|
45
|
+
*/
|
|
46
|
+
export declare function runInstallCommand(config: DaemonConfig, configPath: string, rest: string[], deps?: ServiceCommandDeps): Promise<void>;
|
|
47
|
+
/** Stops (if running) and fully removes the service registration + generated definition file. Safe to call when not installed. */
|
|
48
|
+
export declare function runUninstallCommand(config: DaemonConfig, configPath: string, rest: string[], deps?: ServiceCommandDeps): Promise<void>;
|
|
49
|
+
/** Starts an already-installed service (named so it never collides with the existing `start`, which runs the daemon in the foreground — see `byok-agent.ts`'s header comment). */
|
|
50
|
+
export declare function runServiceStartCommand(config: DaemonConfig, configPath: string, rest: string[], deps?: ServiceCommandDeps): Promise<void>;
|
|
51
|
+
/** Stops a running service without uninstalling it. */
|
|
52
|
+
export declare function runServiceStopCommand(config: DaemonConfig, configPath: string, rest: string[], deps?: ServiceCommandDeps): Promise<void>;
|
|
53
|
+
/** Reports installed/running state straight from the platform's own service manager. */
|
|
54
|
+
export declare function runServiceStatusCommand(config: DaemonConfig, configPath: string, rest: string[], deps?: ServiceCommandDeps): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Daemon, type DaemonConfig } from '../../index';
|
|
2
|
+
export interface StartDeps {
|
|
3
|
+
log?: (line: string) => void;
|
|
4
|
+
error?: (line: string) => void;
|
|
5
|
+
/** DI for tests: drive a pre-built (e.g. stub-adapter-backed) `Daemon` instead of the real `createDaemon(config)`. */
|
|
6
|
+
daemon?: Daemon;
|
|
7
|
+
/** Test hook: fires once `daemon.start()` has resolved and the banner line has been logged — lets a test abort deterministically instead of racing real timing. Unused by the real CLI entry. */
|
|
8
|
+
onReady?: () => void;
|
|
9
|
+
/** Required: there is no sane default "never stop" signal. The real CLI entry (`byok-agent.ts`) wires this to SIGINT/SIGTERM; tests wire their own `AbortController`. */
|
|
10
|
+
signal: AbortSignal;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Runs the daemon in the foreground until `deps.signal` aborts. Every
|
|
14
|
+
* `DaemonEvent` it observes is (a) appended to `<storeDir>/audit.jsonl` —
|
|
15
|
+
* the only channel a separate `status`/`tasks`/`runtimes` invocation has
|
|
16
|
+
* into this process' state, see `byok-agent.ts`'s header comment — and (b)
|
|
17
|
+
* printed as one line to stdout via the SAME formatter `tasks --follow`
|
|
18
|
+
* uses when tailing that log, so watching `start`'s own stdout and tailing
|
|
19
|
+
* the audit log show identical text.
|
|
20
|
+
*/
|
|
21
|
+
export declare function runStartCommand(config: DaemonConfig, deps: StartDeps): Promise<void>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type DaemonConfig, type RuntimeAdapter } from '../../index';
|
|
2
|
+
import { connectControlClient } from '../control-client';
|
|
3
|
+
export interface StatusDeps {
|
|
4
|
+
log?: (line: string) => void;
|
|
5
|
+
/** DI for tests: probe these adapters instead of constructing the real bundled pi/claude/codex set. */
|
|
6
|
+
adapters?: RuntimeAdapter[];
|
|
7
|
+
/** DI for tests: substitute the real control-socket connection attempt. */
|
|
8
|
+
connectControl?: typeof connectControlClient;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* `paired`/`deviceId` are read straight off `DeviceStore` on disk (NOT via
|
|
12
|
+
* a freshly-constructed `Daemon.status()`) — a `Daemon` this process just
|
|
13
|
+
* constructed has never called `start()`/`pair()`, so its in-memory
|
|
14
|
+
* `AuthManager` never loaded the on-disk record and would always report
|
|
15
|
+
* `paired: false` even for a device that's genuinely paired. See
|
|
16
|
+
* `byok-agent.ts`'s header comment for the full read-model rationale.
|
|
17
|
+
*
|
|
18
|
+
* M4 Phase 2: after the persisted-state view above, this ALSO tries the
|
|
19
|
+
* control socket (see `control-client.ts`) — if a `byok-agent start` (or
|
|
20
|
+
* the installed service) is actually running right now, its live pid/
|
|
21
|
+
* uptime/transport/active-tasks are appended, clearly marked `live-`; if
|
|
22
|
+
* not reachable, one line says so and the persisted view above stands on
|
|
23
|
+
* its own, exactly as before this feature existed.
|
|
24
|
+
*/
|
|
25
|
+
export declare function runStatusCommand(config: DaemonConfig, deps?: StatusDeps): Promise<void>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DaemonConfig } from '../../index';
|
|
2
|
+
import type { CollectDiagnosticsOptions } from '../../diagnostics/diagnostics';
|
|
3
|
+
export interface SupportBundleOptions extends CollectDiagnosticsOptions {
|
|
4
|
+
outputPath: string;
|
|
5
|
+
log?: (line: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function runSupportBundleCommand(config: DaemonConfig, options: SupportBundleOptions): Promise<void>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type DaemonConfig } from '../../index';
|
|
2
|
+
import { connectControlClient } from '../control-client';
|
|
3
|
+
export interface TasksListDeps {
|
|
4
|
+
log?: (line: string) => void;
|
|
5
|
+
}
|
|
6
|
+
/** `byok-agent tasks` (no `--follow`): current known tasks, reconstructed from the audit log — see `byok-agent.ts`'s header comment. */
|
|
7
|
+
export declare function runTasksListCommand(config: DaemonConfig, deps?: TasksListDeps): Promise<void>;
|
|
8
|
+
export interface TasksFollowDeps {
|
|
9
|
+
log?: (line: string) => void;
|
|
10
|
+
/** Required — see `commands/start.ts`'s identical `StartDeps.signal` doc comment for why there's no sane default. */
|
|
11
|
+
signal: AbortSignal;
|
|
12
|
+
pollIntervalMs?: number;
|
|
13
|
+
/** DI for tests: substitute the real control-socket connection attempt. */
|
|
14
|
+
connectControl?: typeof connectControlClient;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* `byok-agent tasks --follow`: M4 Phase 2 — prefers a LIVE stream over the
|
|
18
|
+
* control socket's `tasks.subscribe` method (see `control-client.ts`),
|
|
19
|
+
* which taps the exact same `DaemonObserver` events `start`'s own stdout
|
|
20
|
+
* already shows, full fidelity, as they happen. Falls back to tailing the
|
|
21
|
+
* SAME `audit.jsonl` a running `start` appends to (like `tail -f`, not a
|
|
22
|
+
* full replay — run plain `byok-agent tasks` first for history) whenever
|
|
23
|
+
* the control socket isn't reachable (daemon not running, or an older
|
|
24
|
+
* daemon build with no control socket at all) — see `byok-agent.ts`'s
|
|
25
|
+
* header comment for the historical rationale this fallback preserves.
|
|
26
|
+
*/
|
|
27
|
+
export declare function runTasksFollowCommand(config: DaemonConfig, deps: TasksFollowDeps): Promise<void>;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { Daemon, ServiceLifecycle } from '../../index';
|
|
2
|
+
import { connectControlClient, isControlDaemonGone } from '../control-client';
|
|
3
|
+
/** Thrown when `unpair` didn't get a `--yes`/confirmed and either declined interactively or has no TTY to prompt on. Caught by `byok-agent.ts`'s top-level handler like any other error (clean message, exit 1) — never left to hang. */
|
|
4
|
+
export declare class UnpairNotConfirmedError extends Error {
|
|
5
|
+
constructor();
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Finding P1 #2: thrown instead of clearing the store when the OS
|
|
9
|
+
* background service (`byok-agent install`) is currently installed AND
|
|
10
|
+
* running — see this file's own module doc comment for the full "why".
|
|
11
|
+
* Unconditional: unlike {@link UnpairUnknownDaemonStateError}, `--force`
|
|
12
|
+
* never bypasses this one — it exists for a KNOWN-unsafe state, not an
|
|
13
|
+
* uncertain one.
|
|
14
|
+
*/
|
|
15
|
+
export declare class UnpairBlockedByRunningServiceError extends Error {
|
|
16
|
+
constructor(detail: string);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Finding P1 #2 (residual, now fixed across two rounds): thrown instead of
|
|
20
|
+
* clearing the store when unpair could NOT positively confirm that no
|
|
21
|
+
* background service/daemon is running. Three shapes all collapse to
|
|
22
|
+
* "unknown", unsafe BY DEFAULT — exactly like a confirmed-running service —
|
|
23
|
+
* unless the caller opts in via `--force`:
|
|
24
|
+
*
|
|
25
|
+
* - Round 1: a `deps.lifecycle` that could not even be constructed
|
|
26
|
+
* (unsupported platform, win32 without `--winsw-bin`, or no config to
|
|
27
|
+
* build one from at all).
|
|
28
|
+
* - Round 1: a `status()` call that itself rejected (the query to the
|
|
29
|
+
* platform's service manager failed outright).
|
|
30
|
+
* - Round 3: a `status()` call that RESOLVED but reported
|
|
31
|
+
* `determinate: false` — the manager query itself could not be answered
|
|
32
|
+
* (no reachable systemd `--user` D-Bus session, no launchd GUI domain for
|
|
33
|
+
* this uid, permission denied), so `running: false` in that response is a
|
|
34
|
+
* fallback, not a confirmed fact; see `lifecycle/service-types.ts`'s
|
|
35
|
+
* `ServiceStatusResult.determinate`. Every one of `systemd.ts`/
|
|
36
|
+
* `launchd.ts`/`winsw.ts` used to collapse exactly this case into a plain
|
|
37
|
+
* `running: false`, which this file's `checkServiceState` then trusted at
|
|
38
|
+
* face value — reopening the identical fail-open gap round 1 fixed for
|
|
39
|
+
* the other two shapes.
|
|
40
|
+
*
|
|
41
|
+
* Bypassable by `--force`, deliberately unlike
|
|
42
|
+
* {@link UnpairBlockedByRunningServiceError}: this error means "can't
|
|
43
|
+
* tell", not "confirmed unsafe".
|
|
44
|
+
*/
|
|
45
|
+
export declare class UnpairUnknownDaemonStateError extends Error {
|
|
46
|
+
constructor(reason: string);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Finding F6 (cross-model adversarial review): thrown instead of clearing
|
|
50
|
+
* the store on the LIVE (control-socket) unpair path when the daemon's
|
|
51
|
+
* actual exit could NOT be confirmed — the shutdown RPC request itself
|
|
52
|
+
* failed/never got through, teardown hung past `waitForControlExit`'s
|
|
53
|
+
* deadline, or the exit-poll otherwise never observed `isControlDaemonGone`
|
|
54
|
+
* before timing out. All three collapse to the same observable signal
|
|
55
|
+
* (`exited === false`): a daemon that never actually received/finished the
|
|
56
|
+
* shutdown is presumably STILL RUNNING, with its `AuthManager`'s proactive
|
|
57
|
+
* renewal timer ready to write a fresh `device.json` right back over
|
|
58
|
+
* whatever `unpair()` is about to clear — clearing anyway (the pre-fix
|
|
59
|
+
* behavior) meant the log line had to openly admit "did not confirm exit...
|
|
60
|
+
* local device identity has still been cleared" as if that were routine,
|
|
61
|
+
* when it is exactly the silently-self-reverting hazard finding P1 #2
|
|
62
|
+
* originally set out to close. Bypassable via `--force`, same shape as
|
|
63
|
+
* {@link UnpairUnknownDaemonStateError} — this means "can't confirm it
|
|
64
|
+
* actually stopped", an explicit, logged-as-unsafe override, never a
|
|
65
|
+
* silent default.
|
|
66
|
+
*/
|
|
67
|
+
export declare class UnpairExitUnconfirmedError extends Error {
|
|
68
|
+
constructor();
|
|
69
|
+
}
|
|
70
|
+
export interface UnpairDeps {
|
|
71
|
+
log?: (line: string) => void;
|
|
72
|
+
/** `--yes`: skip the confirmation prompt entirely. */
|
|
73
|
+
confirmed?: boolean;
|
|
74
|
+
/** Defaults to `process.stdin.isTTY` — overridable so a test never depends on the real test-runner's stdin. */
|
|
75
|
+
isTTY?: boolean;
|
|
76
|
+
input?: NodeJS.ReadableStream;
|
|
77
|
+
output?: NodeJS.WritableStream;
|
|
78
|
+
/**
|
|
79
|
+
* Finding P1 #2: an already-constructed lifecycle for the OS background
|
|
80
|
+
* service this product MIGHT be installed as — `byok-agent.ts`'s `unpair`
|
|
81
|
+
* dispatch builds this the same way `bin/commands/service.ts` does
|
|
82
|
+
* (`buildServiceDefinition` + `createServiceLifecycle`) and passes it
|
|
83
|
+
* through. `undefined` means "could not even check" (unsupported
|
|
84
|
+
* platform, or Windows without `--winsw-bin`) — this is NOT the same as
|
|
85
|
+
* "confirmed not running": see {@link UnpairUnknownDaemonStateError} and
|
|
86
|
+
* the module doc comment for what that residual gap means and why it's
|
|
87
|
+
* refused by default (bypassable only via `--force`).
|
|
88
|
+
*/
|
|
89
|
+
lifecycle?: Pick<ServiceLifecycle, 'status'>;
|
|
90
|
+
/**
|
|
91
|
+
* Finding P1 #2 (residual): explicit opt-in to proceed when the service
|
|
92
|
+
* check could NOT positively confirm "not running" (lifecycle
|
|
93
|
+
* unavailable, or its `status()` call itself failed) — see
|
|
94
|
+
* {@link UnpairUnknownDaemonStateError}. Does NOT bypass a lifecycle that
|
|
95
|
+
* actively confirmed the service IS running
|
|
96
|
+
* ({@link UnpairBlockedByRunningServiceError} is unconditional either
|
|
97
|
+
* way) — `--force` overrides uncertainty, never a known-unsafe result.
|
|
98
|
+
*
|
|
99
|
+
* Finding F6: this SAME flag also overrides the LIVE (control-socket)
|
|
100
|
+
* path's own exit-confirmation gate — see
|
|
101
|
+
* {@link UnpairExitUnconfirmedError}. One flag, two independent unsafe
|
|
102
|
+
* overrides (whichever path this invocation actually took), each logged
|
|
103
|
+
* as an explicit WARNING rather than silently accepted.
|
|
104
|
+
*/
|
|
105
|
+
force?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* M4 Phase 2: `storeDir`/`productId` to attempt a LIVE control-socket
|
|
108
|
+
* unpair before ever falling back to the service-state-based flow below
|
|
109
|
+
* — see this file's own module doc comment. Omitting either (e.g. an
|
|
110
|
+
* older test exercising only the fallback path) skips straight to that
|
|
111
|
+
* flow, exactly as before this feature existed.
|
|
112
|
+
*/
|
|
113
|
+
storeDir?: string;
|
|
114
|
+
productId?: string;
|
|
115
|
+
/** DI for tests: substitute the real control-socket connection attempt. */
|
|
116
|
+
connectControl?: typeof connectControlClient;
|
|
117
|
+
/** DI for tests: substitute the real "has the daemon actually exited" probe. */
|
|
118
|
+
isControlDaemonGone?: typeof isControlDaemonGone;
|
|
119
|
+
/** How long to wait for the daemon to actually exit after `shutdown` is sent, before giving up (the store is still cleared either way — see the live-path log message). Default 15000ms. */
|
|
120
|
+
controlExitTimeoutMs?: number;
|
|
121
|
+
/** Poll interval while waiting for exit. Default 300ms. */
|
|
122
|
+
controlExitPollIntervalMs?: number;
|
|
123
|
+
}
|
|
124
|
+
export declare function runUnpairCommand(daemon: Pick<Daemon, 'unpair'>, deps?: UnpairDeps): Promise<void>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DaemonConfig } from '../../index';
|
|
2
|
+
import { GitWorkspaceStore, type GitWorkspaceLedgerRecord } from '../../daemon/git-workspace-store';
|
|
3
|
+
export interface WorkspacesCommandDeps {
|
|
4
|
+
log?: (line: string) => void;
|
|
5
|
+
store?: Pick<GitWorkspaceStore, 'list'>;
|
|
6
|
+
showPaths?: boolean;
|
|
7
|
+
}
|
|
8
|
+
declare function abbreviateCommit(value: string | undefined): string;
|
|
9
|
+
declare function formatWorkspaceLine(record: GitWorkspaceLedgerRecord, showPaths: boolean): string;
|
|
10
|
+
/**
|
|
11
|
+
* Lists the private Git workspace ledger without probing or changing any
|
|
12
|
+
* workspace. The ledger store already defines missing, corrupt, and future
|
|
13
|
+
* version behavior; this command deliberately preserves those outcomes.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runWorkspacesCommand(config: DaemonConfig, deps?: WorkspacesCommandDeps): Promise<void>;
|
|
16
|
+
export { abbreviateCommit, formatWorkspaceLine };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { DaemonConfig } from '../daemon/create-daemon';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown by {@link loadConfig} on a missing/unreadable/invalid config.
|
|
4
|
+
* Never calls `process.exit` itself (unlike the pre-M3-2b bin, which did) —
|
|
5
|
+
* every caller, including tests, controls its own exit behavior; only
|
|
6
|
+
* `byok-agent.ts`'s top-level `main().catch(...)` turns this into a clean
|
|
7
|
+
* stderr message + `exit(1)`.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ConfigError extends Error {
|
|
10
|
+
constructor(message: string);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Loads a `DaemonConfig` from a JSON file, if given, merged with explicit
|
|
14
|
+
* overrides (e.g. `pair`'s `--server`). Every subcommand in this bin —
|
|
15
|
+
* including the read-only ones (`status`/`runtimes`/`tasks`) — loads
|
|
16
|
+
* config the same way: they all need `productId`/`storeDir`/`branding`/
|
|
17
|
+
* `runtimeAllowlist` from the same one product install, so there is only
|
|
18
|
+
* ever one config shape to reason about.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadConfig(configPath: string | undefined, overrides?: Partial<DaemonConfig>): DaemonConfig;
|
|
21
|
+
/**
|
|
22
|
+
* Same `storeDir` resolution `create-daemon.ts` uses internally
|
|
23
|
+
* (`config.storeDir ?? DeviceStore.defaultDir(config.productId)`) — reused
|
|
24
|
+
* here via `DeviceStore`'s own static helper (not reimplemented) so a query
|
|
25
|
+
* subcommand always looks in the exact same place `createDaemon`/
|
|
26
|
+
* `createDaemonWithAdapters` would persist to.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveStoreDir(config: Pick<DaemonConfig, 'storeDir' | 'productId'>): string;
|
|
29
|
+
/** Value following `flag` in `args` (e.g. `--config <path>`), or `undefined`. */
|
|
30
|
+
export declare function argValue(args: string[], flag: string): string | undefined;
|
|
31
|
+
/** Whether a bare boolean flag (e.g. `--follow`, `--yes`) is present anywhere in `args`. */
|
|
32
|
+
export declare function hasFlag(args: string[], flag: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Positional (non-flag) arguments, in order, with a fixed set of known
|
|
35
|
+
* `--flag <value>` pairs removed (flag AND its value). Bare boolean flags
|
|
36
|
+
* (e.g. `--follow`/`--yes`) are handled separately via {@link hasFlag} and
|
|
37
|
+
* don't need to be named here unless they'd otherwise be mistaken for a
|
|
38
|
+
* positional. Order-independent: `pair CODE --server URL` and
|
|
39
|
+
* `pair --server URL CODE` both yield `["CODE"]`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function positionalArgs(args: string[], valueFlags?: string[]): string[];
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M4 Phase 2: the CLI-side half of the control socket — connects, performs
|
|
3
|
+
* the mutual HMAC handshake (`../daemon/control-protocol.ts`), and exposes a
|
|
4
|
+
* small `request()`/`subscribe()` surface every rewired command
|
|
5
|
+
* (`status`/`tasks --follow`/`unpair`/`approve`/`reject`) builds on.
|
|
6
|
+
*
|
|
7
|
+
* `connectControlClient` never throws for the ordinary "daemon isn't
|
|
8
|
+
* running" case — it returns a typed `{ok:false, reason}` result instead
|
|
9
|
+
* (see {@link ConnectControlResult}), so a caller can render a clean
|
|
10
|
+
* fallback message rather than catching an exception. Missing
|
|
11
|
+
* `control.token` (the daemon was never started, or was stopped) is by far
|
|
12
|
+
* the most common reason; any other connect/handshake failure collapses
|
|
13
|
+
* into the same shape — a CLI user doesn't need to know WHY the control
|
|
14
|
+
* socket isn't reachable, only that it isn't and a fallback is being used.
|
|
15
|
+
*/
|
|
16
|
+
export interface ControlClientOptions {
|
|
17
|
+
storeDir: string;
|
|
18
|
+
productId: string;
|
|
19
|
+
/** Default: `HANDSHAKE_TIMEOUT_MS` (3000ms) — matches the server's own handshake timeout. */
|
|
20
|
+
handshakeTimeoutMs?: number;
|
|
21
|
+
/** Default 10000ms — applied per `request()` call; never applied to `subscribe()`, which is expected to stay open indefinitely. */
|
|
22
|
+
requestTimeoutMs?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface ControlClient {
|
|
25
|
+
/** Sends `{method, params}`, resolves with the server's `result`, or rejects with a {@link ControlError} (or a plain `Error` for a connection-level failure/timeout). */
|
|
26
|
+
request<T = unknown>(method: string, params?: unknown): Promise<T>;
|
|
27
|
+
/** Sends a streaming request; `onEvent` fires for each `event` frame. Returns a handle whose `close()` ends the WHOLE connection (per the protocol: "client may just close the connection to unsubscribe") — don't share a client between a `subscribe()` and other concurrent `request()` calls if you need them to outlive each other. */
|
|
28
|
+
subscribe(method: string, params: unknown, onEvent: (event: unknown) => void): {
|
|
29
|
+
close: () => void;
|
|
30
|
+
};
|
|
31
|
+
/** Closes the underlying connection. Safe to call more than once. */
|
|
32
|
+
close(): void;
|
|
33
|
+
}
|
|
34
|
+
export type ConnectControlResult = {
|
|
35
|
+
ok: true;
|
|
36
|
+
client: ControlClient;
|
|
37
|
+
} | {
|
|
38
|
+
ok: false;
|
|
39
|
+
reason: string;
|
|
40
|
+
};
|
|
41
|
+
/** Reads the control token, connects, and performs the handshake — see the module doc comment for why failures here collapse into `{ok:false, reason}` rather than throwing. */
|
|
42
|
+
export declare function connectControlClient(opts: ControlClientOptions): Promise<ConnectControlResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Unpair's own poll for "has the daemon actually exited yet" (see
|
|
45
|
+
* `bin/commands/unpair.ts`): both the control token file being gone AND a
|
|
46
|
+
* fresh connect attempt being refused, checked directly rather than through
|
|
47
|
+
* a full `connectControlClient` handshake — cheaper per poll, and matches
|
|
48
|
+
* the exact two-condition check the M4 design calls for.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isControlDaemonGone(storeDir: string, productId: string): Promise<boolean>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { AgentEvent } from '@byok-sdk/protocol';
|
|
2
|
+
import type { ConnectionState, DaemonBranding, DaemonEvent, DaemonTaskInfo } from '../index';
|
|
3
|
+
import type { ControlStatusResult, PendingApproval } from '../daemon/control-protocol';
|
|
4
|
+
import type { ProbedRuntime } from './runtime-probe';
|
|
5
|
+
import type { TaskCounts, DerivedTaskInfo } from './tasks-view';
|
|
6
|
+
/** Renders one `task.progress`-derived `AgentEvent` compactly — the inner payload of a `progress`-kind `DaemonEvent`. */
|
|
7
|
+
export declare function formatAgentEvent(event: AgentEvent): string;
|
|
8
|
+
export interface FormatDaemonEventLineOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Finding F8 (cross-model adversarial review): redact an
|
|
11
|
+
* `awaiting-approval` event's `summary` to a `[redacted: N bytes]`
|
|
12
|
+
* placeholder instead of the raw text. `summary` can carry the exact
|
|
13
|
+
* tool-call text (a shell command, a file's contents) a `confirm`-mode
|
|
14
|
+
* policy is gating — `start.ts`'s stdout is captured verbatim by
|
|
15
|
+
* launchd/systemd/WinSW service logs, which have no business holding
|
|
16
|
+
* that. Default `false` (full fidelity) — every OTHER caller
|
|
17
|
+
* (`tasks --follow`'s control-socket path, which is authenticated;
|
|
18
|
+
* and its audit-log-tailing fallback, whose events are ALREADY redacted
|
|
19
|
+
* at the source by `audit-log.ts` regardless of this flag) keeps showing
|
|
20
|
+
* whatever it already legitimately can. Only `bin/commands/start.ts`
|
|
21
|
+
* sets this to `true`.
|
|
22
|
+
*/
|
|
23
|
+
redactApprovalSummary?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* One line per `DaemonEvent`. Shared by `start` (printed to stdout live
|
|
27
|
+
* while it runs) and `tasks --follow` (tailing the identical shape back out
|
|
28
|
+
* of the audit log) so the two ways of watching the same feed read
|
|
29
|
+
* identically.
|
|
30
|
+
*/
|
|
31
|
+
export declare function formatDaemonEventLine(event: DaemonEvent, options?: FormatDaemonEventLineOptions): string;
|
|
32
|
+
export declare function formatTaskLine(task: DaemonTaskInfo | DerivedTaskInfo): string;
|
|
33
|
+
export declare function formatTaskListLines(tasks: readonly (DaemonTaskInfo | DerivedTaskInfo)[]): string[];
|
|
34
|
+
export declare function formatRuntimeLines(runtimes: readonly ProbedRuntime[]): string[];
|
|
35
|
+
export interface StatusView {
|
|
36
|
+
productName: string;
|
|
37
|
+
productId: string;
|
|
38
|
+
branding?: DaemonBranding;
|
|
39
|
+
paired: boolean;
|
|
40
|
+
deviceId?: string;
|
|
41
|
+
connection?: {
|
|
42
|
+
state: ConnectionState;
|
|
43
|
+
ts: string;
|
|
44
|
+
};
|
|
45
|
+
runtimes: readonly ProbedRuntime[];
|
|
46
|
+
taskCounts: TaskCounts;
|
|
47
|
+
auditLogPath: string;
|
|
48
|
+
auditLogLineCount: number;
|
|
49
|
+
}
|
|
50
|
+
export declare function formatStatusLines(view: StatusView): string[];
|
|
51
|
+
/**
|
|
52
|
+
* M4 Phase 2: renders the control socket's live `status` result — appended
|
|
53
|
+
* after {@link formatStatusLines}' persisted-state view when a running
|
|
54
|
+
* daemon is actually reachable (see `bin/commands/status.ts`). Every line
|
|
55
|
+
* is prefixed `live-` (or is the single `live:` summary line) so it's
|
|
56
|
+
* unambiguous which lines are a live snapshot of a running process versus
|
|
57
|
+
* the historical/persisted view above them.
|
|
58
|
+
*/
|
|
59
|
+
export declare function formatLiveStatusLines(live: ControlStatusResult): string[];
|
|
60
|
+
/**
|
|
61
|
+
* Finding F4: renders `approvals.list`'s registry entries for the new
|
|
62
|
+
* `byok-agent approvals` command (`bin/commands/approvals.ts`) — columns
|
|
63
|
+
* approvalId, taskId, age, summary excerpt, per that finding's own spec.
|
|
64
|
+
* `nowMs` is injected (rather than read via `Date.now()` here) so tests get
|
|
65
|
+
* deterministic age rendering; the real caller passes `Date.now()`.
|
|
66
|
+
*/
|
|
67
|
+
export declare function formatApprovalsListLines(approvals: readonly PendingApproval[], nowMs: number): string[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type RuntimeAdapter } from '../index';
|
|
2
|
+
export declare const RUNTIME_PROBE_TIMEOUT_MS = 5000;
|
|
3
|
+
/** The bundled adapter set `byok-agent status`/`byok-agent runtimes` probe by default — same unset-vs-set allowlist contract as `createDaemon` itself. */
|
|
4
|
+
export declare function defaultRuntimeAdapters(runtimeAllowlist: string[] | undefined): RuntimeAdapter[];
|
|
5
|
+
/**
|
|
6
|
+
* What `byok-agent status`/`byok-agent runtimes` show per runtime — a
|
|
7
|
+
* flattened, display-ready merge of `RuntimeDetectResult` and
|
|
8
|
+
* `RuntimeCapabilities` (see `../types.ts`). Always probed fresh (this
|
|
9
|
+
* module never reads from the daemon or the audit log) — see
|
|
10
|
+
* `byok-agent.ts`'s header comment for why a live, standalone probe is the
|
|
11
|
+
* honest choice for "what's on this machine right now" instead of a
|
|
12
|
+
* historical snapshot.
|
|
13
|
+
*/
|
|
14
|
+
export interface ProbedRuntime {
|
|
15
|
+
id: string;
|
|
16
|
+
present: boolean;
|
|
17
|
+
version?: string;
|
|
18
|
+
authPresent?: boolean;
|
|
19
|
+
steer: boolean;
|
|
20
|
+
resume: boolean;
|
|
21
|
+
permissionModes: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Runs `detect()`/`capabilities()` on each adapter, in parallel. Every
|
|
25
|
+
* bundled adapter's own `detect()` already catches its own failures (e.g.
|
|
26
|
+
* `pi-adapter.ts`'s `detect()` wraps its version probe in try/catch and
|
|
27
|
+
* resolves `{present: false}` rather than rejecting) — the catch here is a
|
|
28
|
+
* defensive backstop for a `RuntimeAdapter` that doesn't hold that
|
|
29
|
+
* convention, not a workaround for an observed failure in the bundled
|
|
30
|
+
* three.
|
|
31
|
+
*/
|
|
32
|
+
export declare function probeRuntimes(adapters: readonly RuntimeAdapter[], options?: {
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
}): Promise<ProbedRuntime[]>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type TaskState } from '@byok-sdk/protocol';
|
|
2
|
+
import { type ConnectionState, type DaemonEvent, type DaemonTaskInfo } from '../index';
|
|
3
|
+
export interface TaskGitStatus {
|
|
4
|
+
/** Opaque local correlation ID; never a path or commit ID. */
|
|
5
|
+
workspaceId: string;
|
|
6
|
+
phase: string;
|
|
7
|
+
headChanged?: boolean;
|
|
8
|
+
commitsSinceBaseline?: number;
|
|
9
|
+
dirty?: {
|
|
10
|
+
staged: number;
|
|
11
|
+
unstaged: number;
|
|
12
|
+
untracked: number;
|
|
13
|
+
conflicted: number;
|
|
14
|
+
};
|
|
15
|
+
errorCategory?: string;
|
|
16
|
+
}
|
|
17
|
+
export type DerivedTaskInfo = DaemonTaskInfo & {
|
|
18
|
+
git?: TaskGitStatus;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Reconstructs the same shape `DaemonObserver.tasks()` exposes to a LIVE
|
|
22
|
+
* in-process daemon, but from a REPLAYED sequence of already-logged
|
|
23
|
+
* `DaemonEvent`s instead — this is what lets a separate, short-lived
|
|
24
|
+
* `byok-agent tasks`/`status` invocation show task state without attaching
|
|
25
|
+
* to the `start` process that actually observed it (see `byok-agent.ts`'s
|
|
26
|
+
* header comment). Deliberately a standalone reducer, not a call into
|
|
27
|
+
* `DaemonObserver` itself: `DaemonObserver`'s own public methods
|
|
28
|
+
* (`handleInboundEnvelope`/`handleOutboundEnvelope`) take a raw protocol
|
|
29
|
+
* `Envelope`, not the already-normalized `DaemonEvent` this module receives
|
|
30
|
+
* from the audit log — the two shapes don't line up, so this mirrors that
|
|
31
|
+
* reducer's logic operating on `DaemonEvent.kind` directly instead.
|
|
32
|
+
*/
|
|
33
|
+
export declare function deriveTasksFromEvents(events: readonly DaemonEvent[]): DerivedTaskInfo[];
|
|
34
|
+
/**
|
|
35
|
+
* Most recent `connection` DaemonEvent, if the log has any — used by
|
|
36
|
+
* `status` to show a best-effort "last known" transport state. Never a
|
|
37
|
+
* live/current guarantee: if `start` isn't running right now, this is
|
|
38
|
+
* simply whatever it last reported before exiting (or nothing, if it never
|
|
39
|
+
* ran at all) — see `byok-agent.ts`'s header comment.
|
|
40
|
+
*/
|
|
41
|
+
export declare function lastConnectionState(events: readonly DaemonEvent[]): {
|
|
42
|
+
state: ConnectionState;
|
|
43
|
+
ts: string;
|
|
44
|
+
} | undefined;
|
|
45
|
+
export type TaskCounts = Record<TaskState, number> & {
|
|
46
|
+
total: number;
|
|
47
|
+
};
|
|
48
|
+
/** Zero-filled over every `TaskState` (via `TASK_STATES`, the single authority — not a hand-maintained list) so a state with zero current tasks still renders instead of being silently absent. */
|
|
49
|
+
export declare function tallyTaskStates(tasks: readonly DaemonTaskInfo[]): TaskCounts;
|