agent-shim 7.13.1 → 7.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/cli.cjs +95 -27
  2. package/dist/index.cjs +70 -7
  3. package/dist/index.mjs +70 -7
  4. package/dist/types/index.d.ts +2789 -40
  5. package/package.json +3 -1
  6. package/dist/types/checkReport.d.ts +0 -154
  7. package/dist/types/claudeShim.d.ts +0 -114
  8. package/dist/types/cli/bool.d.ts +0 -8
  9. package/dist/types/cli/commandDeps.d.ts +0 -39
  10. package/dist/types/cli/envBool.d.ts +0 -11
  11. package/dist/types/cli/pairs.d.ts +0 -19
  12. package/dist/types/cliError.d.ts +0 -27
  13. package/dist/types/codex/http.d.ts +0 -15
  14. package/dist/types/config/classify.d.ts +0 -49
  15. package/dist/types/config/load.d.ts +0 -42
  16. package/dist/types/config/schema.d.ts +0 -746
  17. package/dist/types/config/store.d.ts +0 -46
  18. package/dist/types/configProfilesStore.d.ts +0 -61
  19. package/dist/types/configure.d.ts +0 -170
  20. package/dist/types/credential.d.ts +0 -136
  21. package/dist/types/credentialCache.d.ts +0 -46
  22. package/dist/types/directoryRulesStore.d.ts +0 -43
  23. package/dist/types/doctorReport.d.ts +0 -117
  24. package/dist/types/frontdoor/capability.d.ts +0 -19
  25. package/dist/types/frontdoor/capture.d.ts +0 -58
  26. package/dist/types/frontdoor/connect.d.ts +0 -209
  27. package/dist/types/frontdoor/connectEffects.d.ts +0 -3
  28. package/dist/types/frontdoor/custody.d.ts +0 -32
  29. package/dist/types/frontdoor/ensure.d.ts +0 -36
  30. package/dist/types/frontdoor/headroomHop.d.ts +0 -22
  31. package/dist/types/frontdoor/pipeline.d.ts +0 -109
  32. package/dist/types/frontdoor/probe.d.ts +0 -13
  33. package/dist/types/frontdoor/realFrontDoorPort.d.ts +0 -5
  34. package/dist/types/frontdoor/route.d.ts +0 -112
  35. package/dist/types/frontdoor/server.d.ts +0 -44
  36. package/dist/types/frontdoor/state.d.ts +0 -57
  37. package/dist/types/frontdoor/supervisor.d.ts +0 -48
  38. package/dist/types/frontdoor/tap.d.ts +0 -9
  39. package/dist/types/frontdoor/trust.d.ts +0 -30
  40. package/dist/types/frontdoor/x509.d.ts +0 -11
  41. package/dist/types/headroom/ensure.d.ts +0 -31
  42. package/dist/types/headroom/headers.d.ts +0 -11
  43. package/dist/types/headroom/settings.d.ts +0 -13
  44. package/dist/types/headroom/source.d.ts +0 -8
  45. package/dist/types/headroom/state.d.ts +0 -77
  46. package/dist/types/headroom/supervisor.d.ts +0 -107
  47. package/dist/types/identityStore.d.ts +0 -85
  48. package/dist/types/launchWiring.d.ts +0 -49
  49. package/dist/types/launcher/argv.d.ts +0 -42
  50. package/dist/types/launcher/cascade.d.ts +0 -42
  51. package/dist/types/launcher/cliOverride.d.ts +0 -38
  52. package/dist/types/launcher/extraFlags.d.ts +0 -6
  53. package/dist/types/launcher/farm.d.ts +0 -168
  54. package/dist/types/launcher/flags.d.ts +0 -96
  55. package/dist/types/launcher/guard.d.ts +0 -57
  56. package/dist/types/launcher/identity.d.ts +0 -95
  57. package/dist/types/launcher/lock.d.ts +0 -41
  58. package/dist/types/launcher/pool.d.ts +0 -44
  59. package/dist/types/launcher/ports.d.ts +0 -135
  60. package/dist/types/launcher/spawn.d.ts +0 -20
  61. package/dist/types/launcher.d.ts +0 -98
  62. package/dist/types/legacy.d.ts +0 -29
  63. package/dist/types/pathNorm.d.ts +0 -14
  64. package/dist/types/paths.d.ts +0 -79
  65. package/dist/types/poolPickReport.d.ts +0 -44
  66. package/dist/types/poolStore.d.ts +0 -18
  67. package/dist/types/providersStore.d.ts +0 -137
  68. package/dist/types/realCredentialCache.d.ts +0 -11
  69. package/dist/types/realPorts.d.ts +0 -109
  70. package/dist/types/resolve/conditions.d.ts +0 -37
  71. package/dist/types/resolve/decide.d.ts +0 -45
  72. package/dist/types/resolve/extends.d.ts +0 -41
  73. package/dist/types/resolve/flatten.d.ts +0 -13
  74. package/dist/types/resolve/match.d.ts +0 -58
  75. package/dist/types/resolve/pipeline.d.ts +0 -33
  76. package/dist/types/resolve/plan.d.ts +0 -57
  77. package/dist/types/resolve/projects.d.ts +0 -61
  78. package/dist/types/resolve/reconcile.d.ts +0 -80
  79. package/dist/types/resolve/types.d.ts +0 -159
  80. package/dist/types/resolve/walk.d.ts +0 -85
  81. package/dist/types/resolve/walkPolicy.d.ts +0 -15
  82. package/dist/types/usage/account.d.ts +0 -20
  83. package/dist/types/usage/middleware.d.ts +0 -17
  84. package/dist/types/usage/pick.d.ts +0 -62
  85. package/dist/types/usage/plan.d.ts +0 -18
  86. package/dist/types/usage/poolPick.d.ts +0 -31
  87. package/dist/types/usage/preflight.d.ts +0 -20
  88. package/dist/types/usage/rateLimit.d.ts +0 -25
  89. package/dist/types/usage/read.d.ts +0 -70
  90. package/dist/types/usage/scan.d.ts +0 -24
  91. package/dist/types/usage/schema.d.ts +0 -318
  92. package/dist/types/usage/store.d.ts +0 -43
  93. package/dist/types/versionDiscovery.d.ts +0 -47
@@ -1,46 +0,0 @@
1
- import type { z } from "zod";
2
- /**
3
- * Filesystem primitives `store.ts` needs, injected so tests can simulate a crash between the temp-file write and the rename without touching real process semantics (e.g. actually killing the process mid-write). The default implementation (`nodeStoreFs`) is real `node:fs`, used by every CLI adapter in normal operation and by this module's own tests when exercising the real atomic-rename behaviour end to end.
4
- */
5
- export interface StoreFs {
6
- /** Reads a file's contents as UTF-8 text, or returns undefined when it does not exist. */
7
- readonly readFileUtf8: (filePath: string) => string | undefined;
8
- /** Writes a file's full contents as UTF-8 text, creating or truncating it. */
9
- readonly writeFileUtf8: (filePath: string, contents: string) => void;
10
- /** Atomically renames `from` to `to` (same filesystem — both are always in the same directory in this module's own usage). */
11
- readonly renameSync: (from: string, to: string) => void;
12
- /** Creates a directory (and any missing parents), succeeding silently if it already exists. */
13
- readonly mkdirSync: (dirPath: string) => void;
14
- /** Removes a file, succeeding silently if it does not exist — used to clean up a stray temp file after a failed rename. */
15
- readonly unlinkSync: (filePath: string) => void;
16
- }
17
- /** The real `node:fs`-backed implementation of `StoreFs`, used by every CLI adapter in normal operation. */
18
- export declare const nodeStoreFs: StoreFs;
19
- /**
20
- * Reads and validates one JSON config file against `schema`.
21
- *
22
- * Returns undefined when the file does not exist. Throws `ConfigValidationError` (the same error type `src/config/load.ts` throws for every other config file in this project) when it exists but fails validation.
23
- */
24
- export declare function readJson<S extends z.ZodType>(filePath: string, schema: S, storeFs?: StoreFs): z.infer<S> | undefined;
25
- /**
26
- * Writes `contents` as UTF-8 text to `filePath` atomically: writes to a temp file in the *same directory* first, then renames it into place. A crash (or thrown error) between the write and the rename leaves the original file at `filePath` completely untouched — the failure mode this exists to prevent is a config file left half-written after an interrupted process.
27
- *
28
- * The temp file's name includes the destination's own basename, the process id, and a random UUID, so two concurrent writers (even to the same file, even from two processes) never collide on the same temp path.
29
- */
30
- export declare function writeTextAtomic(filePath: string, contents: string, storeFs?: StoreFs): void;
31
- /** Writes `data` as pretty-printed JSON to `filePath`, via `writeTextAtomic`. */
32
- export declare function writeJsonAtomic(filePath: string, data: unknown, storeFs?: StoreFs): void;
33
- /** Inputs to `applyPatch`. */
34
- export interface ApplyPatchOptions<T> {
35
- /** Used as the base to merge `patch` into when `filePath` does not exist yet. Required for a file that may not exist yet (e.g. a config profile's own first `set` before it was ever written by `create`); omit only when the file is guaranteed to already exist. */
36
- readonly defaults?: T;
37
- readonly storeFs?: StoreFs;
38
- }
39
- /**
40
- * Reads `filePath` (or falls back to `options.defaults` when it does not exist), shallow-merges `patch` over the top-level keys of the result, re-validates the merged object against `schema`, and writes it back atomically.
41
- *
42
- * The merge is deliberately shallow — only top-level keys of the parsed config are replaced wholesale by the corresponding key in `patch`. A caller that wants to add one key to a nested object (e.g. one more category inside `categories`) must read the existing nested object itself and pass the already-merged nested object as part of `patch`; `applyPatch` does not recurse into nested objects on its own.
43
- *
44
- * Throws when the file does not exist and no `defaults` were given, and re-throws `ConfigValidationError` (via the underlying `schema.parse`) when the merged result does not validate.
45
- */
46
- export declare function applyPatch<S extends z.ZodType>(filePath: string, schema: S, patch: Readonly<Partial<z.infer<S>>>, options?: ApplyPatchOptions<z.infer<S>>): z.infer<S>;
@@ -1,61 +0,0 @@
1
- import { type ConfigProfile, type GlobalConfig, type LaunchFlags } from "./config/schema";
2
- import { CliError } from "./cliError";
3
- import type { LayoutPaths } from "./paths";
4
- /** Raised by any operation that requires a configuration profile to already exist, when it does not. */
5
- export declare class ProfileNotFoundError extends CliError {
6
- readonly profileName: string;
7
- constructor(profileName: string);
8
- }
9
- /** Raised by `createProfile` (`profile add`) when a profile with the given name already has a file. */
10
- export declare class ProfileAlreadyExistsError extends CliError {
11
- readonly profileName: string;
12
- constructor(profileName: string);
13
- }
14
- /** Raised when a `--category` patch names something other than one of the four overridable categories (e.g. `secret`, or a typo). */
15
- export declare class InvalidCategoryNameError extends CliError {
16
- readonly categoryName: string;
17
- constructor(categoryName: string);
18
- }
19
- /** True when a profile file exists for `name`, regardless of whether it validates. */
20
- export declare function profileExists(paths: LayoutPaths, name: string): boolean;
21
- export declare function requireProfileExists(paths: LayoutPaths, name: string): void;
22
- /** Reads and validates one configuration profile, or undefined when it does not exist. */
23
- export declare function readProfile(paths: LayoutPaths, name: string): ConfigProfile | undefined;
24
- /**
25
- * Creates a new configuration profile, empty apart from the optional `extends` list and description. Throws `ProfileAlreadyExistsError` if a profile with this name already has a file, and `ConfigValidationError` when the result fails `ConfigProfileSchema` (e.g. an empty `--extends ""` name), rather than letting the underlying `ZodError` escape as an unhandled crash.
26
- */
27
- export declare function createProfile(paths: LayoutPaths, name: string, extendsList?: readonly string[], description?: string): ConfigProfile;
28
- /** One profile as reported by `listProfiles`. */
29
- export interface ProfileListEntry {
30
- readonly name: string;
31
- readonly profile: ConfigProfile;
32
- }
33
- /** Lists every configuration profile under `configProfilesDir` that has a valid `<name>.json`. */
34
- export declare function listProfiles(paths: LayoutPaths): readonly ProfileListEntry[];
35
- /** Reads the user-global `~/.agent-shim/config.json`, or undefined when it does not exist. */
36
- export declare function readGlobalConfig(paths: LayoutPaths): GlobalConfig | undefined;
37
- /** Sets the user-global default configuration profile in `~/.agent-shim/config.json`, creating the file if it doesn't exist yet. Whether the profile exists is the caller's concern: `profile use` checks it first. */
38
- export declare function setGlobalDefaultProfile(paths: LayoutPaths, name: string): GlobalConfig;
39
- /**
40
- * Merges `patch` (from one or more `--category cat=bool` flags) into `profile`'s own `categories` object and writes it back.
41
- *
42
- * Throws `InvalidCategoryNameError` for any key that isn't one of the four overridable categories — `secret` can never be toggled by any configuration layer, this included.
43
- */
44
- export declare function setProfileCategories(paths: LayoutPaths, name: string, patch: Readonly<Record<string, boolean>>): ConfigProfile;
45
- /**
46
- * Merges `patch` (from one or more `--entry "path"=bool` flags) into `profile`'s own `entries` object and writes it back.
47
- *
48
- * Key validity (the `<category>/<real-relative-path>` prefix requirement) is enforced by `ConfigProfileSchema`'s own `EntriesSchema` at write time — an invalid key surfaces as the usual `ConfigValidationError`.
49
- */
50
- export declare function setProfileEntries(paths: LayoutPaths, name: string, patch: Readonly<Record<string, boolean>>): ConfigProfile;
51
- /**
52
- * Merges `patch` into `profile`'s own `launch` object and writes it back. A key present in `patch` with the value `undefined` removes that setting outright (a `--no-launch-provider`, say) rather than overwriting it, and a `launch` object left with no settings is removed too.
53
- */
54
- export declare function setProfileLaunchFlags(paths: LayoutPaths, name: string, patch: Readonly<LaunchFlags>): ConfigProfile;
55
- /** Replaces `profile`'s own `extends` list and/or description; an empty list or an undefined description removes the field. */
56
- export declare function setProfileMetadata(paths: LayoutPaths, name: string, patch: Readonly<{
57
- extends?: readonly string[];
58
- description?: string | false;
59
- }>): ConfigProfile;
60
- /** Deletes a configuration profile's file. Throws `ProfileNotFoundError` when it does not exist. Nothing that references it by name is rewritten; `agent-shim doctor` reports any identity default, directory rule or `extends` left pointing at it. */
61
- export declare function removeProfile(paths: LayoutPaths, name: string): void;
@@ -1,170 +0,0 @@
1
- import type { Command } from "commander";
2
- import { type CommandDeps } from "./cli/commandDeps";
3
- import { CliError, UsageError } from "./cliError";
4
- import type { LogPort } from "./launcher/ports";
5
- import { type LayoutPaths } from "./paths";
6
- /** Raised by `configure` when no configuration profile resolves for the identity at the working directory, so there is no tier-three file for a toggle to land in. */
7
- export declare class NoConfigProfileResolvedError extends CliError {
8
- readonly identityName: string;
9
- readonly cwd: string;
10
- constructor(identityName: string, cwd: string);
11
- }
12
- /** Raised by `configure` when standard input is not a terminal: every step of it is a prompt, so there is no non-interactive form to fall back to. */
13
- export declare class ConfigureNeedsTerminalError extends UsageError {
14
- constructor();
15
- }
16
- /** Raised by `configure` when no `--identity` was given and nothing else (the environment, a directory pin, the active identity) resolves one. */
17
- export declare class NoIdentityResolvedError extends UsageError {
18
- readonly cwd: string;
19
- constructor(cwd: string);
20
- }
21
- /** One selectable option, shared by `select` and `multiselect`. */
22
- interface PromptOption<Value extends string> {
23
- readonly value: Value;
24
- readonly label: string;
25
- readonly hint?: string;
26
- /** Visible but not selectable — used for the `secret` category, which no configuration layer may ever toggle. */
27
- readonly disabled?: boolean;
28
- }
29
- /** Parameters shared by a single-select prompt. */
30
- export interface SelectParams<Value extends string> {
31
- readonly message: string;
32
- readonly options: readonly PromptOption<Value>[];
33
- /** Which option the cursor starts on. Omit to let the prompt library fall back to its own default (the first option) — pass this explicitly whenever the safest or most common answer isn't the first one listed, so the on-screen reading order and the default-on-Enter answer can differ deliberately rather than being forced to match. */
34
- readonly initialValue?: Value;
35
- }
36
- /** Parameters for a multi-select prompt. */
37
- export interface MultiselectParams<Value extends string> {
38
- readonly message: string;
39
- readonly options: readonly PromptOption<Value>[];
40
- readonly initialValues?: readonly Value[];
41
- }
42
- /** Parameters for a free-text prompt. */
43
- export interface TextParams {
44
- readonly message: string;
45
- readonly placeholder?: string;
46
- /** Returns an error string to reject the value and re-prompt, or `undefined` to accept it. */
47
- readonly validate?: (value: string) => string | undefined;
48
- }
49
- /**
50
- * The interactive-prompt surface `runConfigure` needs, abstracted so it can be driven by a scripted sequence of pre-programmed answers in tests instead of a real TTY — the same injected-ports pattern `src/launcher/ports.ts` already uses for the filesystem, spawn, and clock.
51
- *
52
- * `select`/`multiselect`/`text` resolve to a `symbol` when the user cancels (Ctrl-C), mirroring `@clack/prompts`' own cancellation contract exactly, so `isCancel` is the one place that distinguishes a real answer from a cancellation.
53
- */
54
- export interface PromptsPort {
55
- readonly select: <Value extends string>(params: SelectParams<Value>) => Promise<Value | symbol>;
56
- readonly multiselect: <Value extends string>(params: MultiselectParams<Value>) => Promise<readonly Value[] | symbol>;
57
- readonly text: (params: TextParams) => Promise<string | symbol>;
58
- /** A type guard, mirroring `@clack/prompts`' own `isCancel` exactly, so a passing check narrows `Value | symbol` down to `Value` at every call site without a further cast. */
59
- readonly isCancel: (value: unknown) => value is symbol;
60
- readonly cancel: (message?: string) => void;
61
- readonly intro: (message?: string) => void;
62
- readonly outro: (message?: string) => void;
63
- }
64
- export declare const realPromptsPort: PromptsPort;
65
- /** Where one toggle should be written, per the README's "agent-shim configure: which file it writes to" precedence. */
66
- export type WriteTarget = {
67
- readonly tier: "portable-local";
68
- readonly localConfigPath: string;
69
- } | {
70
- readonly tier: "directory-rule";
71
- readonly rulePath: string;
72
- } | {
73
- readonly tier: "config-profile";
74
- readonly profileName: string;
75
- };
76
- /** One directory level's portable-config presence, shallowest-first — as produced by `walkDirectoryAncestors` plus a filesystem existence check at each level. */
77
- export interface DirectoryLevelPresence {
78
- readonly dir: string;
79
- /** Whether a committed `.agent-shim.json` already exists at this level. */
80
- readonly hasPortable: boolean;
81
- /** Whether a gitignored `.agent-shim.local.json` already exists at this level, independent of whether a committed sibling does — this is what makes tier one apply even when only a personal local override was ever created, with no committed file alongside it. */
82
- readonly hasPortableLocal: boolean;
83
- /** The personal local file a toggle at this level is written into: the former `.claude-use.local.json` when that is the only one that exists, so its settings are not shadowed by a new file, otherwise `.agent-shim.local.json`. */
84
- readonly portableLocalFilename: string;
85
- }
86
- /** Inputs to `chooseWriteTarget`. */
87
- export interface ChooseWriteTargetParams {
88
- readonly cwd: string;
89
- readonly home: string;
90
- /** `cwd`'s ancestor levels, shallowest-first, each flagged with whether a portable/portable-local file already exists there. */
91
- readonly levels: readonly DirectoryLevelPresence[];
92
- /** Every directory rule's own `path` field, exactly as written in `~/.agent-shim/directory-rules.json` (not yet normalised). */
93
- readonly directoryRulePaths: readonly string[];
94
- /** The identity's resolved active configuration profile — the tier-three fallback. */
95
- readonly activeConfigProfile: string;
96
- }
97
- /**
98
- * Decides which file a `configure` toggle should be written into, in the exact three-tier precedence the README documents:
99
- *
100
- * 1. If `$PWD` is inside a directory covered by a committed `.agent-shim.json`, or a `.agent-shim.local.json` already exists there (even without a committed sibling), the toggle goes into `.agent-shim.local.json` in that same directory — the deepest such directory, when more than one ancestor qualifies.
101
- * 2. Otherwise, if an existing directory rule in the user's own `directory-rules.json` already applies to `$PWD` (its `path`, once resolved, is an ancestor of or equal to `$PWD`), the toggle is written into that rule — the most specific (longest-resolved-path) matching rule, when more than one applies.
102
- * 3. Otherwise, the toggle is written into the identity's own active configuration profile.
103
- *
104
- * Never touches a filesystem or a config file itself — every input is already gathered by the caller, which is what makes this function directly unit-testable with synthetic levels and rule paths.
105
- */
106
- export declare function chooseWriteTarget(params: ChooseWriteTargetParams): WriteTarget;
107
- /** Renders a `WriteTarget` as a short, human-readable description for `configure`'s own confirmation output. */
108
- export declare function describeWriteTarget(target: WriteTarget): string;
109
- /** Everything `runConfigure` needs beyond the identity/path the user typed. */
110
- export interface RunConfigureDeps {
111
- readonly paths: LayoutPaths;
112
- readonly prompts: PromptsPort;
113
- readonly log: Pick<LogPort, "info">;
114
- }
115
- /** Inputs to `runConfigure` beyond its injected dependencies. */
116
- export interface RunConfigureParams {
117
- readonly identityName: string;
118
- /** A real `~/.claude`-relative path (never category-prefixed — that prefix is an entries-*key* convention, not a filesystem path). Given: entries mode. Omitted: categories mode. */
119
- readonly path?: string;
120
- readonly cwd: string;
121
- readonly home: string;
122
- readonly claudeHome: string;
123
- }
124
- /** Validates a candidate profile name for the wizard's text prompt: non-empty, matches the allowed shape, and (only when `existingNames` is given) not already taken. */
125
- export declare function validateProfileName(value: string, existingNames?: readonly string[]): string | undefined;
126
- /**
127
- * Inputs to `runProfileWizard` beyond the injected prompt/log ports. Split out so the launch path (which only has a `LayoutPaths`, not a full `ConfigureContext`) can call the wizard too.
128
- *
129
- * A discriminated union over three modes:
130
- * - `{ existingName }`: edit an existing profile's categories.
131
- * - `{ createName }`: create a new profile with this exact name (no name prompt), then choose categories. Used by the launch path, where the name is already decided.
132
- * - `{ defaultNewName? }`: create a new profile, prompting for a name first (with an optional pre-filled default). Used by the manual commands.
133
- */
134
- export type RunProfileWizardParams = {
135
- readonly paths: LayoutPaths;
136
- readonly existingName: string;
137
- } | {
138
- readonly paths: LayoutPaths;
139
- readonly createName: string;
140
- } | {
141
- readonly paths: LayoutPaths;
142
- readonly defaultNewName?: string;
143
- };
144
- /** What `runProfileWizard` produced. */
145
- export interface ProfileWizardResult {
146
- readonly name: string;
147
- readonly created: boolean;
148
- }
149
- /**
150
- * The unified create-or-edit flow for a configuration profile: one guided prompt sequence covering both cases that would otherwise split across a bare `profile add` (empty, no categories) and `configure`'s "edit a profile directly" branch (categories only, profile must already exist).
151
- *
152
- * Given an `existingName`: edits that profile's categories in place via a multiselect, seeded from its current values. Given a `createName`: creates the empty file with that exact name, no name prompt, then runs the category multiselect. Given neither: prompts for a name first (validated against the allowed shape and against every existing profile name), creates the empty file, then runs the same multiselect. Either way, a cancel at any prompt writes nothing and returns `undefined` — the caller decides what to do (e.g. the launch path proceeds without the profile, the manual command just stops).
153
- *
154
- * Driven entirely by the injected `PromptsPort` so the whole flow is unit-testable with a scripted sequence of answers, the same way `runConfigure` already is.
155
- */
156
- export declare function runProfileWizard(prompts: PromptsPort, params: RunProfileWizardParams): Promise<ProfileWizardResult | undefined>;
157
- /**
158
- * Runs `agent-shim configure [path] --identity <name>`'s interactive flow.
159
- *
160
- * Two modes, exactly per the README's "agent-shim configure: which file it writes to" section:
161
- *
162
- * - No `path`: shows the identity's resolved top-level categories (the only mode that ever touches categories), plus an option to edit a named configuration profile's own stored values directly instead.
163
- * - Given a `path` (a real `~/.claude`-relative path, never category-prefixed): shows that path's direct children with their resolved sharing state, for fine-grained entries overrides. Never shows or edits categories.
164
- *
165
- * Every actual toggle (in either mode, other than the "edit a profile directly" branch, which is an explicit target the user chose) is written via `chooseWriteTarget`'s three-tier precedence.
166
- */
167
- export declare function runConfigure(deps: RunConfigureDeps, params: RunConfigureParams): Promise<void>;
168
- /** Registers `agent-shim configure [path] [--identity <name>]` onto `program`. */
169
- export declare function registerConfigureCommand(program: Command, deps: CommandDeps): void;
170
- export {};
@@ -1,136 +0,0 @@
1
- import type { Credential, CredentialSource, CredentialTarget, CredentialTargetVar } from "./config/schema";
2
- import { type CredentialCachePort } from "./credentialCache";
3
- /** The exit status for a launch whose selected provider or identity has a credential block none of whose sources yields a token: 64, the conventional `EX_USAGE`, because the invocation asked for a credential the environment cannot supply. */
4
- export declare const CREDENTIAL_UNAVAILABLE_EXIT = 64;
5
- /** How long a non-interactive credential command may run. Secret-store CLIs answer from a local cache or agent well within a second, so this leaves a wide margin while still failing a wedged command quickly instead of stalling the launch. */
6
- export declare const CREDENTIAL_COMMAND_TIMEOUT_MS = 10000;
7
- /** How long an interactive credential command may run: long enough for a person to notice and approve a biometric or password prompt, short enough that a prompt nobody is watching fails the launch rather than hanging it. */
8
- export declare const CREDENTIAL_INTERACTIVE_TIMEOUT_MS = 120000;
9
- /** What reading one `file` source found: nothing at that path, or its contents and permission bits. `mode` is undefined on a platform with no POSIX permission bits (Windows), where the looseness check cannot apply. */
10
- type SecretFileRead = {
11
- readonly found: false;
12
- } | {
13
- readonly found: true;
14
- readonly content: string;
15
- readonly mode: number | undefined;
16
- };
17
- /** The outcome of one credential command. `stderr` is empty for an interactive run, whose stderr goes to the terminal so a person sees any prompt. */
18
- export interface CredentialCommandResult {
19
- readonly status: number | null;
20
- readonly stdout: string;
21
- readonly stderr: string;
22
- readonly timedOut: boolean;
23
- }
24
- /**
25
- * Everything resolving a credential needs from the outside world, injected so the resolver (and every launcher test) runs without `op`, the Keychain, real secret files or a terminal. `src/realPorts.ts` wires the real one; tests wire a fake.
26
- */
27
- export interface CredentialPort {
28
- /** Reads a `file` source's path (absolute or `~`-rooted). */
29
- readonly readSecretFile: (filePath: string) => SecretFileRead;
30
- /** Runs a command source's argv directly (no shell) under a timeout, capturing stdout. An interactive run inherits the terminal's stdin and stderr so a person can answer a prompt. */
31
- readonly runCommand: (argv: readonly [string, ...string[]], options: {
32
- readonly timeoutMs: number;
33
- readonly interactive: boolean;
34
- }) => CredentialCommandResult;
35
- /** Whether a person can answer a prompt now: standard input is a terminal, or there is a desktop session to show a dialog in. */
36
- readonly personPresent: () => boolean;
37
- /** Where cached credentials live, used only for a credential block that asks for caching. Omit to resolve every launch from the sources. */
38
- readonly cache?: CredentialCacheEnv;
39
- }
40
- /** The sources that run a command: `command` itself and the two presets that compile to one. */
41
- type CommandBackedSource = Extract<CredentialSource, {
42
- readonly command: unknown;
43
- } | {
44
- readonly op: unknown;
45
- } | {
46
- readonly keychain: unknown;
47
- }>;
48
- /**
49
- * The argv a command-backed source runs, with the `op` and `keychain` presets compiled to theirs: `op read <ref>` and `security find-generic-password -s <service> [-a <account>] -w`.
50
- */
51
- export declare function commandArgv(source: CommandBackedSource): readonly [string, ...string[]];
52
- /**
53
- * Whether a source needs a person present. An explicit `interactive` always wins. Otherwise only `op` defaults to interactive, because the desktop app's integration asks for approval, unless `OP_SERVICE_ACCOUNT_TOKEN` is set, in which case `op` authenticates as the service account with nobody asked. A plain `command` and a `keychain` read default to non-interactive: a Keychain item whose access list already trusts `security` reads silently, and one that does not fails at once with "User interaction is not allowed" rather than hanging.
54
- */
55
- export declare function isInteractiveSource(source: CredentialSource, env: Readonly<Record<string, string | undefined>>): boolean;
56
- /** One source as `check`, `doctor` and every `--json` output report it: its kind plus the same non-secret identifying detail `describeSource` prints. */
57
- type CredentialSourceSummary = {
58
- readonly kind: "env";
59
- readonly variable: string;
60
- } | {
61
- readonly kind: "file";
62
- readonly path: string;
63
- } | {
64
- readonly kind: "command";
65
- readonly program: string;
66
- } | {
67
- readonly kind: "op";
68
- readonly reference: string;
69
- } | {
70
- readonly kind: "keychain";
71
- readonly service: string;
72
- readonly account?: string;
73
- } | {
74
- readonly kind: "literal";
75
- };
76
- /** A credential block as it is reported: its effective target and each source's summary, in order. */
77
- export interface CredentialSummary {
78
- readonly target: CredentialTarget;
79
- readonly sources: readonly CredentialSourceSummary[];
80
- /** The block's cache setting when it asks for caching: how long a token is kept (absent for no expiry) and where. */
81
- readonly cache?: {
82
- readonly ttl?: string;
83
- readonly store?: string;
84
- };
85
- }
86
- /** Summarises a credential block for reporting, with `target` resolved to its default. */
87
- export declare function summariseCredential(credential: Credential): CredentialSummary;
88
- /** Renders a source for a message, as `formatSourceSummary` does its summary. */
89
- export declare function describeSource(source: CredentialSource): string;
90
- /** Renders a credential summary on one line for human-readable output: `bearer from env Z_API_TOKEN, then op op://...`. */
91
- export declare function formatCredentialSummary(summary: CredentialSummary): string;
92
- /** Renders a credential block on one line, as `formatCredentialSummary` does its summary. */
93
- export declare function describeCredential(credential: Credential): string;
94
- /** Inputs to `resolveCredential`. */
95
- export interface ResolveCredentialParams {
96
- readonly credential: Credential;
97
- /** The launching environment: where `env` sources are read, and what decides whether `op` is interactive. */
98
- readonly env: Readonly<Record<string, string | undefined>>;
99
- readonly port: CredentialPort;
100
- /** What the credential belongs to, for messages and as its cache key: `provider z` or `identity work`. */
101
- readonly subject: string;
102
- /** True to skip a cached token and fetch from the sources, replacing the cached copy: what `credential warm` does. */
103
- readonly refresh?: boolean;
104
- }
105
- /** What caching a credential needs beyond the block's own `cache` setting: the port holding entries, the platform that picks the default store, and the clock the TTL is measured on. */
106
- export interface CredentialCacheEnv {
107
- readonly port: CredentialCachePort;
108
- readonly platform: NodeJS.Platform;
109
- readonly now: () => number;
110
- }
111
- /** A resolved credential: the token, where it goes, and which source produced it. `warnings` carries any source refused on the way for a reason worth reporting (a secret file with loose permissions). */
112
- export interface ResolvedCredential {
113
- readonly target: CredentialTarget;
114
- readonly token: string;
115
- readonly source: CredentialSource;
116
- readonly warnings: readonly string[];
117
- /** When the token was fetched, if it came from the cache rather than a source; undefined for a live fetch. */
118
- readonly cachedAt?: number;
119
- }
120
- /** The outcome of `resolveCredential`: a resolved credential, or a message naming every source and why none yielded a token. */
121
- export type CredentialResolution = {
122
- readonly ok: true;
123
- readonly credential: ResolvedCredential;
124
- } | {
125
- readonly ok: false;
126
- readonly message: string;
127
- };
128
- /**
129
- * Resolves a credential block: a fresh cached token if the block caches one, else tries each source in order and returns the first non-empty token, with the block's target, storing it when the block caches. When none yields one, the message names every source and why, and never a value; for a caching block it also names the command that fills the cache from a terminal. Pure over its injected ports.
130
- */
131
- export declare function resolveCredential(params: ResolveCredentialParams): CredentialResolution;
132
- /**
133
- * The credential variables a child environment gets for a resolved token: the target's variable set to the token, and the other two set to undefined so that merging this over an environment removes them, and an ambient one inherited from the parent can neither outrank nor sit alongside the credential agent-shim chose. Removing a variable (rather than setting it to the empty string) leaves nothing for Claude Code to interpret.
134
- */
135
- export declare function credentialVariables(target: CredentialTarget, token: string): Record<CredentialTargetVar, string | undefined>;
136
- export {};
@@ -1,46 +0,0 @@
1
- import { z } from "zod";
2
- import { type CredentialCache, type CredentialCacheStore } from "./config/schema";
3
- /** What a cache stores for one credential: the token, when it was fetched, and the source that produced it (a non-secret descriptor, kept so `check` and the launch log can still say where the token came from). */
4
- export declare const CachedCredentialSchema: z.ZodObject<{
5
- token: z.ZodString;
6
- fetchedAt: z.ZodNumber;
7
- source: z.ZodUnion<readonly [z.ZodObject<{
8
- env: z.ZodString;
9
- }, z.core.$strict>, z.ZodObject<{
10
- file: z.ZodString;
11
- }, z.core.$strict>, z.ZodObject<{
12
- interactive: z.ZodOptional<z.ZodBoolean>;
13
- timeoutMs: z.ZodOptional<z.ZodInt>;
14
- command: z.ZodTuple<[z.ZodString], z.ZodString>;
15
- }, z.core.$strict>, z.ZodObject<{
16
- interactive: z.ZodOptional<z.ZodBoolean>;
17
- timeoutMs: z.ZodOptional<z.ZodInt>;
18
- op: z.ZodString;
19
- }, z.core.$strict>, z.ZodObject<{
20
- interactive: z.ZodOptional<z.ZodBoolean>;
21
- timeoutMs: z.ZodOptional<z.ZodInt>;
22
- keychain: z.ZodObject<{
23
- service: z.ZodString;
24
- account: z.ZodOptional<z.ZodString>;
25
- }, z.core.$strict>;
26
- }, z.core.$strict>, z.ZodObject<{
27
- literal: z.ZodString;
28
- }, z.core.$strict>]>;
29
- }, z.core.$strict>;
30
- export type CachedCredential = z.infer<typeof CachedCredentialSchema>;
31
- /**
32
- * Where cached credentials live, injected so resolution and its tests need neither the Keychain nor a real directory. `owner` names what the credential belongs to (`identity work`, `provider z`) and is the entry's key; `store` picks the backing store. `read` returns undefined for a missing or unreadable entry, since an entry that cannot be used is the same as no entry.
33
- */
34
- export interface CredentialCachePort {
35
- readonly read: (store: CredentialCacheStore, owner: string) => CachedCredential | undefined;
36
- readonly write: (store: CredentialCacheStore, owner: string, entry: CachedCredential) => void;
37
- readonly remove: (store: CredentialCacheStore, owner: string) => void;
38
- }
39
- /** The store a block uses: its own choice, else the Keychain on macOS and a file elsewhere. */
40
- export declare function effectiveStore(cache: Readonly<CredentialCache>, platform: NodeJS.Platform): CredentialCacheStore;
41
- /** A `ttl` such as `12h` in milliseconds, or undefined for no expiry. */
42
- export declare function ttlMs(cache: Readonly<CredentialCache>): number | undefined;
43
- /** Whether a cached entry is still within its TTL at `now`. A cache with no TTL never expires; an entry stamped in the future (a clock that moved back) is treated as expired. */
44
- export declare function isFresh(entry: CachedCredential, cache: Readonly<CredentialCache>, now: number): boolean;
45
- /** How old a cached entry is, for `check` and `doctor` to report without the value. */
46
- export declare function cacheAgeMs(entry: CachedCredential, now: number): number;
@@ -1,43 +0,0 @@
1
- import { type DirectoryRule, type DirectoryRules } from "./config/schema";
2
- import { CliError } from "./cliError";
3
- import type { LayoutPaths } from "./paths";
4
- /** Raised by `removeDirectoryRule`, `updateDirectoryRule` and `rule show` when no rule matches the given path exactly. */
5
- export declare class DirectoryRuleNotFoundError extends CliError {
6
- readonly rulePath: string;
7
- constructor(rulePath: string);
8
- }
9
- /** Raised by `addDirectoryRule` when neither `--config-profile` nor `--identity` is given, and by `updateDirectoryRule` when an update would leave a rule that sets nothing at all: a rule that pins and overrides nothing would do nothing. */
10
- export declare class DirectoryRuleMissingTargetError extends CliError {
11
- constructor();
12
- }
13
- /** Raised by `addDirectoryRule` when a rule for the exact path already exists: `rule add` creates, `rule set` updates. */
14
- export declare class DirectoryRuleAlreadyExistsError extends CliError {
15
- readonly rulePath: string;
16
- constructor(rulePath: string);
17
- }
18
- /** Reads `~/.agent-shim/directory-rules.json`, or an empty rule set when the file does not exist yet. */
19
- export declare function readDirectoryRules(paths: LayoutPaths): DirectoryRules;
20
- /** Validates and writes the whole `~/.agent-shim/directory-rules.json` file. Exported so `src/configure.ts` can update a single rule's `categories`/`entries` in place without duplicating this validate-then-write step. Throws `ConfigValidationError` when `rules` fails `DirectoryRulesSchema`, rather than letting the underlying `ZodError` escape as an unhandled crash. */
21
- export declare function writeDirectoryRules(paths: LayoutPaths, rules: DirectoryRules): void;
22
- /** Lists every directory rule, in file order. */
23
- export declare function listDirectoryRules(paths: LayoutPaths): readonly DirectoryRule[];
24
- /** Inputs to `addDirectoryRule` beyond the path itself. */
25
- export interface AddDirectoryRuleOptions {
26
- readonly configProfile?: string;
27
- readonly identity?: string;
28
- }
29
- /**
30
- * Adds a new directory rule for `rulePath`. Throws `DirectoryRuleAlreadyExistsError` when a rule for that exact path already exists (`updateDirectoryRule` changes one), and `DirectoryRuleMissingTargetError` when neither `configProfile` nor `identity` is given, since a rule that pins neither would do nothing.
31
- */
32
- export declare function addDirectoryRule(paths: LayoutPaths, rulePath: string, options: AddDirectoryRuleOptions): DirectoryRule;
33
- /** The fields `updateDirectoryRule` changes. A value of `false` removes that field from the rule. */
34
- export interface UpdateDirectoryRuleOptions {
35
- readonly configProfile?: string | false;
36
- readonly identity?: string | false;
37
- }
38
- /**
39
- * Updates the existing directory rule for `rulePath` in place, keeping its position in the file and every field not named. Throws `DirectoryRuleNotFoundError` when no rule matches that exact path, and `DirectoryRuleMissingTargetError` when the update would leave a rule that does nothing (remove it with `removeDirectoryRule` instead).
40
- */
41
- export declare function updateDirectoryRule(paths: LayoutPaths, rulePath: string, options: UpdateDirectoryRuleOptions): DirectoryRule;
42
- /** Removes the directory rule for `rulePath`. Throws `DirectoryRuleNotFoundError` when no rule matches that exact path. */
43
- export declare function removeDirectoryRule(paths: LayoutPaths, rulePath: string): void;
@@ -1,117 +0,0 @@
1
- import { type ClaudeShimState, type PathShadowStatus } from "./claudeShim";
2
- import type { LayoutPaths } from "./paths";
3
- import type { RunPort } from "./launcher/ports";
4
- import type { DiscoveredClaudeBinary } from "./versionDiscovery";
5
- type DoctorSeverity = "pass" | "warn" | "fail";
6
- type DoctorSection = "ambient-credential" | "binary-discovery" | "claude-shim" | "path-resolution" | "legacy-name" | "config-profile" | "identity" | "pool" | "provider" | "keychain" | "directory-rules" | "global-config" | "categories-local" | "active-identity" | "headroom";
7
- /** One line of `agent-shim doctor`'s report. `subject` names the identity/profile/rule the finding is about, when the section has more than one of those. */
8
- interface DoctorFinding {
9
- readonly section: DoctorSection;
10
- readonly subject?: string;
11
- readonly severity: DoctorSeverity;
12
- readonly message: string;
13
- }
14
- /** The full result of `runDoctor`. `ok` is false iff any finding is a `fail` — a `warn` never fails the report on its own. */
15
- export interface DoctorReport {
16
- readonly findings: readonly DoctorFinding[];
17
- readonly ok: boolean;
18
- }
19
- /** One identity's raw `identity.json`, unparsed — `runDoctor` does its own JSON.parse/schema validation so one malformed file never aborts the rest of the report. */
20
- export interface DoctorIdentityInput {
21
- readonly name: string;
22
- readonly path: string;
23
- readonly raw: string | undefined;
24
- /** The identity's own farm root (`identitiesDir/<name>`) — the account name the Keychain lookup uses. */
25
- readonly farmRoot: string;
26
- }
27
- /** One configuration profile's raw `<name>.json`, unparsed. */
28
- export interface DoctorConfigProfileInput {
29
- readonly name: string;
30
- readonly path: string;
31
- readonly raw: string | undefined;
32
- }
33
- /** One provider's raw `<name>.json`, unparsed. */
34
- export interface DoctorProviderInput {
35
- readonly name: string;
36
- readonly path: string;
37
- readonly raw: string | undefined;
38
- }
39
- /** A single optional top-level file `runDoctor` validates against a schema when present — absent is a legitimate, unconfigured state, not a failure. */
40
- interface DoctorFileInput {
41
- readonly path: string;
42
- readonly raw: string | undefined;
43
- }
44
- /** The outcome of resolving the real Claude Code binary, pre-resolved by the wiring layer since `discoverClaudeBinary` is not itself a parse-shaped pure operation and already has its own dedicated test coverage. */
45
- type DoctorBinaryDiscovery = {
46
- readonly ok: true;
47
- readonly binary: DiscoveredClaudeBinary;
48
- } | {
49
- readonly ok: false;
50
- readonly message: string;
51
- };
52
- /**
53
- * Where a bare command name resolves for the two names this tool owns.
54
- *
55
- * `ownExecutablePath` is this process's own PATH-visible location (`realOwnExecutablePath()`); `agentShim` is `findPathShadow`'s verdict for a bare `agent-shim` against the directory that executable lives in. `claude` is only populated when a shim is actually enabled — without one, a `claude` on PATH is Claude Code's own binary, which is not a shadow of anything.
56
- */
57
- interface DoctorPathResolution {
58
- readonly ownExecutablePath: string;
59
- readonly agentShim: PathShadowStatus;
60
- readonly claude?: PathShadowStatus;
61
- }
62
- /**
63
- * Collapses a `shadowed` verdict back to `ok` when the shadowing entry and this executable are literally the same file reached by two names — `findPathShadow` compares *directories*, so a symlink on PATH pointing at the running executable's own real location otherwise reads as a shadow of itself.
64
- *
65
- * `realpath` must resolve symlinks, and must return its argument unchanged rather than throwing when the path cannot be resolved (a broken symlink, a race with an uninstall), so an unresolvable path simply stays unequal and the shadow verdict stands.
66
- */
67
- export declare function refinePathShadow(status: PathShadowStatus, ownExecutablePath: string, realpath: (target: string) => string): PathShadowStatus;
68
- /** Everything `runDoctor` needs, all of it already loaded/injected — nothing in `runDoctor` itself reads a file, shells out, or touches the farm. */
69
- export interface RunDoctorParams {
70
- readonly env: Readonly<Record<string, string | undefined>>;
71
- readonly identities: readonly DoctorIdentityInput[];
72
- readonly configProfiles: readonly DoctorConfigProfileInput[];
73
- readonly providers: readonly DoctorProviderInput[];
74
- readonly directoryRules: DoctorFileInput;
75
- readonly globalConfig: DoctorFileInput;
76
- readonly categoriesLocal: DoctorFileInput;
77
- readonly activeIdentity: DoctorFileInput;
78
- readonly binaryDiscovery: DoctorBinaryDiscovery;
79
- /** Whether `agent-shim shim enable` has been run, and whether its recorded target still exists on disk — pre-resolved by the wiring layer, since checking a file's existence is real I/O, not a parse-shaped pure operation. */
80
- readonly claudeShim: {
81
- readonly state: ClaudeShimState | undefined;
82
- readonly targetExists: boolean;
83
- };
84
- /** Which executables a bare `agent-shim` (and, when the shim is enabled, a bare `claude`) would actually run — pre-resolved by the wiring layer, since scanning PATH is real I/O. */
85
- readonly pathResolution: DoctorPathResolution;
86
- /** The resolved state root (`paths.root`), whose directory name says whether this installation still lives under the former `.claude-use` name. */
87
- readonly rootPath: string;
88
- /** Runs `security find-generic-password` for the per-identity Keychain check. Omit to skip that check entirely (e.g. off macOS). */
89
- readonly run?: RunPort;
90
- /** `process.platform` in real use; the Keychain check only ever runs when this is `"darwin"`. */
91
- readonly platform: string;
92
- /** The headroom daemon's state.json plus a zombie-aware liveness predicate for the pids it names (a defunct daemon holds no port but still answers signal 0). Omit the raw text when the daemon has never run; that is a pass, not a failure. */
93
- readonly headroom: {
94
- readonly state: DoctorFileInput;
95
- readonly isRunning: (pid: number) => boolean;
96
- };
97
- }
98
- /**
99
- * Audits the whole `~/.agent-shim` config graph for internal consistency: every identity, every configuration profile's own `extends` chain, every provider, `directory-rules.json`, `config.json`, `categories.local.json`, `active-identity`, plus real Claude Code binary discoverability and ambient-credential exposure.
100
- *
101
- * Deliberately identity/directory-agnostic, unlike `runCheck` — there is no single cascade to resolve `doctor` against, so it never touches settings-exposure (which only means anything relative to one resolved cascade).
102
- *
103
- * Every check aggregates rather than throws: a malformed file becomes one `fail` finding for that file, not an aborted report. This is the one place in the codebase that deliberately breaks the "throw a validation error and let it propagate" convention every other command relies on — `doctor`'s whole purpose is to survive a broken file and keep auditing everything else.
104
- */
105
- export declare function runDoctor(params: RunDoctorParams): DoctorReport;
106
- /** Renders a full `DoctorReport` as plain text lines, one section header at a time, in the order `agent-shim doctor` prints them. */
107
- export declare function formatDoctorReport(report: DoctorReport): string[];
108
- /** What `collectDoctorReport` needs from its caller. */
109
- export interface CollectDoctorReportParams {
110
- readonly paths: LayoutPaths;
111
- readonly env: NodeJS.ProcessEnv;
112
- }
113
- /**
114
- * Audits the whole state root against this machine: the edge adapter that enumerates every identity, configuration profile and provider on disk, reads every top-level config file as raw text (never pre-parsing, so one malformed file cannot abort the report), discovers the real Claude Code binary and resolves where a bare command name runs from, and hands the facts to the pure `runDoctor`.
115
- */
116
- export declare function collectDoctorReport(params: CollectDoctorReportParams): DoctorReport;
117
- export {};