agent-shim 7.7.0 → 7.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/cli.cjs +3682 -3677
- package/dist/index.cjs +1052 -173
- package/dist/index.mjs +982 -158
- package/dist/types/cli/bool.d.ts +8 -0
- package/dist/types/cli/parsers.d.ts +40 -0
- package/dist/types/configProfilesStore.d.ts +61 -0
- package/dist/types/credential.d.ts +136 -0
- package/dist/types/credentialCache.d.ts +46 -0
- package/dist/types/directoryRulesStore.d.ts +43 -0
- package/dist/types/headroom/headers.d.ts +11 -0
- package/dist/types/identityStore.d.ts +85 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/launcher/flags.d.ts +96 -0
- package/dist/types/launcher/identity.d.ts +95 -0
- package/dist/types/poolStore.d.ts +18 -0
- package/dist/types/providersStore.d.ts +137 -0
- package/package.json +1 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { type LoadedFile } from "../config/load";
|
|
2
|
+
import { type Identity } from "../config/schema";
|
|
3
|
+
import type { FsPort } from "./ports";
|
|
4
|
+
/** Which precedence rule produced an identity decision. */
|
|
5
|
+
export type IdentityDecisionSource =
|
|
6
|
+
/** `CLAUDE_CONFIG_DIR` was already set: identity resolution is skipped entirely and the real binary uses whatever it already points to. */
|
|
7
|
+
"config-dir-escape-hatch"
|
|
8
|
+
/** A leading `@name` argv[0] positional, or its explicit form `--identity <name>`. */
|
|
9
|
+
| "argv"
|
|
10
|
+
/** The `AGENT_SHIM_IDENTITY` environment variable. */
|
|
11
|
+
| "env"
|
|
12
|
+
/** A directory rule pinning an identity to the current path. */
|
|
13
|
+
| "directory-pin"
|
|
14
|
+
/** The persisted `~/.agent-shim/active-identity` file. */
|
|
15
|
+
| "active-identity-file"
|
|
16
|
+
/** Nothing resolved an identity at all — a bare launch with no active identity. */
|
|
17
|
+
| "none";
|
|
18
|
+
/** The pool a `pool:<name>` selector names, or undefined when `selector` names an identity directly. */
|
|
19
|
+
export declare function poolNameOf(selector: string): string | undefined;
|
|
20
|
+
/** The result of deciding which identity applies to this launch. */
|
|
21
|
+
export interface IdentityDecision {
|
|
22
|
+
/** The resolved identity name. Absent when the escape hatch applied, when nothing resolved one, or when a pool was selected (the launcher picks a member from `pool`). */
|
|
23
|
+
readonly name?: string;
|
|
24
|
+
/** The pool selected by a `pool:<name>` selector, when the launch named a pool instead of an identity. */
|
|
25
|
+
readonly pool?: string;
|
|
26
|
+
readonly source: IdentityDecisionSource;
|
|
27
|
+
/** True when `CLAUDE_CONFIG_DIR` was already set and every step below was skipped as a result. */
|
|
28
|
+
readonly configDirEscapeHatch: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** Inputs to `decideIdentity`, in the exact precedence order the README's CLI reference table documents. */
|
|
31
|
+
export interface DecideIdentityParams {
|
|
32
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
33
|
+
/** The identity `parseLauncherArgv` found in a leading `@name` or an `--identity <name>` flag, if either was given. */
|
|
34
|
+
readonly argv0Identity?: string;
|
|
35
|
+
/** An identity pinned to `$PWD` by a directory rule. Accepted as an already-resolved value here — the rules-loading code that produces it lands in Phase 4/5. */
|
|
36
|
+
readonly directoryPinnedIdentity?: string;
|
|
37
|
+
/** Reads the persisted active-identity file, returning undefined when it does not exist or is empty. Injected so this stays pure. */
|
|
38
|
+
readonly readActiveIdentityFile: () => string | undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Decides which identity applies to this launch, in precedence order:
|
|
42
|
+
*
|
|
43
|
+
* 1. If `CLAUDE_CONFIG_DIR` is already set, skip identity/cascade resolution entirely: a deliberate carry-forward of the legacy script's own "don't override an explicit launcher" escape hatch.
|
|
44
|
+
* 2. A leading `@name` argv[0] positional, or `--identity <name>`.
|
|
45
|
+
* 3. The `AGENT_SHIM_IDENTITY` environment variable.
|
|
46
|
+
* 4. A directory-pinned identity from a directory rule.
|
|
47
|
+
* 5. The persisted `~/.agent-shim/active-identity` file.
|
|
48
|
+
*
|
|
49
|
+
* Each of steps 2 to 5 may name `pool:<name>` instead of an identity; the decision then carries `pool` and no `name`, and the launcher picks the member.
|
|
50
|
+
*
|
|
51
|
+
* An empty string counts as unset for `CLAUDE_CONFIG_DIR` and `AGENT_SHIM_IDENTITY`, consistent with how this project treats empty-string environment variables everywhere else (see `src/paths.ts` and the ambient-credential guard).
|
|
52
|
+
*/
|
|
53
|
+
export declare function decideIdentity(params: DecideIdentityParams): IdentityDecision;
|
|
54
|
+
/** Which precedence rule produced a configuration-profile decision. */
|
|
55
|
+
export type ConfigProfileDecisionSource =
|
|
56
|
+
/** An explicit `--config-profile` CLI flag. */
|
|
57
|
+
"cli-flag"
|
|
58
|
+
/** The `AGENT_SHIM_CONFIG_PROFILE` environment variable. */
|
|
59
|
+
| "env"
|
|
60
|
+
/** A directory rule's `configProfile` selection for `$PWD`. */
|
|
61
|
+
| "directory-rule"
|
|
62
|
+
/** The active identity's own declared `defaultConfigProfile`. */
|
|
63
|
+
| "identity-default"
|
|
64
|
+
/** The user-global `~/.agent-shim/config.json` default. */
|
|
65
|
+
| "global-default"
|
|
66
|
+
/** Nothing resolved a configuration profile at all. */
|
|
67
|
+
| "none";
|
|
68
|
+
/** The result of deciding which configuration profile applies to this launch. */
|
|
69
|
+
export interface ConfigProfileDecision {
|
|
70
|
+
readonly name?: string;
|
|
71
|
+
readonly source: ConfigProfileDecisionSource;
|
|
72
|
+
}
|
|
73
|
+
/** Inputs to `decideConfigProfile`, in the exact precedence order the README's "Configuration profiles" section documents. */
|
|
74
|
+
export interface DecideConfigProfileParams {
|
|
75
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
76
|
+
readonly cliFlagConfigProfile?: string;
|
|
77
|
+
readonly directoryRuleConfigProfile?: string;
|
|
78
|
+
readonly identityDefaultConfigProfile?: string;
|
|
79
|
+
readonly globalDefaultConfigProfile?: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Decides which configuration profile applies to this launch, in precedence order:
|
|
83
|
+
*
|
|
84
|
+
* 1. An explicit `--config-profile` flag or `AGENT_SHIM_CONFIG_PROFILE` environment variable (this run only).
|
|
85
|
+
* 2. A directory rule's `configProfile` selection for `$PWD`.
|
|
86
|
+
* 3. The active identity's own declared default (`defaultConfigProfile` in its `identity.json`).
|
|
87
|
+
* 4. A global default (`~/.agent-shim/config.json`).
|
|
88
|
+
*/
|
|
89
|
+
export declare function decideConfigProfile(params: DecideConfigProfileParams): ConfigProfileDecision;
|
|
90
|
+
/**
|
|
91
|
+
* Reads and validates one identity's `identity.json` from `<identitiesDir>/<name>/identity.json`, via `IdentitySchema`.
|
|
92
|
+
*
|
|
93
|
+
* Returns undefined when the file does not exist (e.g. the identity was resolved by name but never actually created — `agent-shim identity add` is Phase 4's job, not this one's). Throws `ConfigValidationError` when the file exists but fails validation, the same as any other config file in this project.
|
|
94
|
+
*/
|
|
95
|
+
export declare function loadIdentity(identitiesDir: string, name: string, fs: FsPort): LoadedFile<Identity> | undefined;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CliError } from "./cliError";
|
|
2
|
+
import { type Pool } from "./config/schema";
|
|
3
|
+
import type { LayoutPaths } from "./paths";
|
|
4
|
+
/** Raised when a command names a pool that is not defined in the global config. */
|
|
5
|
+
export declare class PoolNotFoundError extends CliError {
|
|
6
|
+
readonly poolName: string;
|
|
7
|
+
constructor(poolName: string);
|
|
8
|
+
}
|
|
9
|
+
/** The pools defined in `~/.agent-shim/config.json`, by name. */
|
|
10
|
+
export declare function readPools(paths: LayoutPaths): Readonly<Record<string, Pool>>;
|
|
11
|
+
/** The pool named `name`; throws `PoolNotFoundError` when none is defined. */
|
|
12
|
+
export declare function requirePool(paths: LayoutPaths, name: string): Pool;
|
|
13
|
+
/** Defines a new pool. Throws `InvalidPoolNameError` for a name `PoolNameSchema` rejects and `PoolAlreadyExistsError` for one already defined. */
|
|
14
|
+
export declare function addPool(paths: LayoutPaths, name: string, identities: readonly string[]): Pool;
|
|
15
|
+
/** Replaces an existing pool's members. Throws `PoolNotFoundError` when it is not defined. */
|
|
16
|
+
export declare function setPool(paths: LayoutPaths, name: string, identities: readonly string[]): Pool;
|
|
17
|
+
/** Removes a pool. Throws `PoolNotFoundError` when it is not defined. Anything that selected it (an active-identity file, a directory rule) is left for `doctor` to report, as with a removed identity. */
|
|
18
|
+
export declare function removePool(paths: LayoutPaths, name: string): void;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { type PROVIDER_CREDENTIAL_TARGETS, type PROVIDER_KINDS, type CodexProviderConfig, type CredentialCache, type CredentialSource, type Provider } from "./config/schema";
|
|
2
|
+
import { CliError, UsageError } from "./cliError";
|
|
3
|
+
import { type CredentialPort } from "./credential";
|
|
4
|
+
import type { ResolvedProvider } from "./launcher/flags";
|
|
5
|
+
import type { FsPort } from "./launcher/ports";
|
|
6
|
+
import { type CascadeInput } from "./resolve/walk";
|
|
7
|
+
import type { LayoutPaths } from "./paths";
|
|
8
|
+
/** Raised by any operation that requires a provider to already exist, when it does not. */
|
|
9
|
+
export declare class ProviderNotFoundError extends CliError {
|
|
10
|
+
readonly name: string;
|
|
11
|
+
constructor(name: string);
|
|
12
|
+
}
|
|
13
|
+
/** Raised by `addProvider` when a provider with the given name already has a file. */
|
|
14
|
+
export declare class ProviderAlreadyExistsError extends CliError {
|
|
15
|
+
readonly providerName: string;
|
|
16
|
+
constructor(providerName: string);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Raised by `addProvider` when `name` fails the provider naming rule: the same rule `IdentitySchema` applies to identity names (start with a letter or number, then letters, numbers, dots, hyphens, underscores, and at signs). Keeping the two rules identical means a name that works for one works for the other, and neither vocabulary can ever produce a path segment that escapes `providers/` or `identities/`.
|
|
20
|
+
*/
|
|
21
|
+
export declare class InvalidProviderNameError extends CliError {
|
|
22
|
+
readonly attemptedName: string;
|
|
23
|
+
constructor(attemptedName: string);
|
|
24
|
+
}
|
|
25
|
+
/** True when a provider file exists for `name`, regardless of whether it validates. */
|
|
26
|
+
export declare function providerExists(paths: LayoutPaths, name: string): boolean;
|
|
27
|
+
/** Written in a replacement in place of an old fixed `env.ANTHROPIC_AUTH_TOKEN` value, which is never printed: the person converting the file copies the value across themselves. */
|
|
28
|
+
export declare const LEGACY_LITERAL_PLACEHOLDER = "<the value of env.ANTHROPIC_AUTH_TOKEN>";
|
|
29
|
+
/** What converting one old-format provider file involves: the old fields it uses, and the whole file rewritten in the current format. */
|
|
30
|
+
export interface LegacyProviderConversion {
|
|
31
|
+
readonly fields: readonly string[];
|
|
32
|
+
readonly replacement: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Recognises a provider file written in the format before the credential block (a `tokenEnv`, `tokenCommand` or `authScheme` field, or a credential variable in `env`, and no `credential`) and builds its exact replacement: `tokenEnv` becomes an `env` source, `tokenCommand` a `command` source, a non-empty fixed `env.ANTHROPIC_AUTH_TOKEN` a `literal` source (its value replaced by `LEGACY_LITERAL_PLACEHOLDER`, never printed), `authScheme` the `target`, and the credential variables leave `env`. Undefined for anything else, including a file that is merely invalid.
|
|
36
|
+
*/
|
|
37
|
+
export declare function legacyProviderConversion(raw: unknown): LegacyProviderConversion | undefined;
|
|
38
|
+
/** Raised when a provider file is still in the format before the credential block, naming the old fields and giving the exact replacement. */
|
|
39
|
+
export declare class LegacyProviderFileError extends CliError {
|
|
40
|
+
readonly filePath: string;
|
|
41
|
+
readonly conversion: LegacyProviderConversion;
|
|
42
|
+
constructor(filePath: string, conversion: LegacyProviderConversion);
|
|
43
|
+
}
|
|
44
|
+
/** Reads and validates one provider definition, or undefined when it does not exist. */
|
|
45
|
+
export declare function readProvider(paths: LayoutPaths, name: string): Provider | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Reads and validates one provider definition through an injected `FsPort`, the same way `loadIdentity` reads an `identity.json`: the launcher never touches the real filesystem directly, so provider loading must flow through the port it is already handed.
|
|
48
|
+
*/
|
|
49
|
+
export declare function loadProvider(providersDir: string, name: string, port: FsPort): Provider | undefined;
|
|
50
|
+
/** One provider definition as reported by `listProviders`. */
|
|
51
|
+
export interface ProviderListEntry {
|
|
52
|
+
readonly name: string;
|
|
53
|
+
readonly provider: Provider;
|
|
54
|
+
}
|
|
55
|
+
/** Lists every provider under `providersDir` that has a valid `<name>.json`. A file that fails validation is skipped here; `provider list` is the place a broken definition gets surfaced, not a launch. */
|
|
56
|
+
export declare function listProviders(paths: LayoutPaths): readonly ProviderListEntry[];
|
|
57
|
+
/** The credential targets a provider may use. */
|
|
58
|
+
export type ProviderCredentialTarget = (typeof PROVIDER_CREDENTIAL_TARGETS)[number];
|
|
59
|
+
/** The kinds of provider. */
|
|
60
|
+
export type ProviderKind = (typeof PROVIDER_KINDS)[number];
|
|
61
|
+
/** Where a provider sends its sessions, for display: an `http` provider's base URL, or the front door for a codex provider (whose address exists only at launch). */
|
|
62
|
+
export declare function describeProviderEndpoint(provider: Provider): string;
|
|
63
|
+
/** Everything `addProvider` writes into a fresh provider file. `baseUrl` is required for an `http` provider and refused for a codex one; `codex` applies only to a codex provider. */
|
|
64
|
+
export interface AddProviderInput {
|
|
65
|
+
readonly kind?: ProviderKind;
|
|
66
|
+
readonly displayName: string;
|
|
67
|
+
readonly baseUrl?: string;
|
|
68
|
+
readonly sources: readonly CredentialSource[];
|
|
69
|
+
readonly target?: ProviderCredentialTarget;
|
|
70
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
71
|
+
readonly codex?: CodexProviderConfig;
|
|
72
|
+
}
|
|
73
|
+
/** Raised when a provider option does not fit the provider's kind: a base URL for a codex provider, a missing one for an http provider, or codex settings for an http provider. */
|
|
74
|
+
export declare class ProviderKindMismatchError extends UsageError {
|
|
75
|
+
constructor(message: string);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Creates a new provider definition. Throws `ProviderAlreadyExistsError` when a provider with this name already has a file, `InvalidProviderNameError` when `name` fails the naming rule, and `ConfigValidationError` when the assembled definition fails `ProviderSchema` (e.g. a `baseUrl` that is not a URL) rather than letting the underlying `ZodError` escape as an unhandled crash.
|
|
79
|
+
*/
|
|
80
|
+
export declare function addProvider(paths: LayoutPaths, name: string, input: AddProviderInput): Provider;
|
|
81
|
+
/**
|
|
82
|
+
* The fields `updateProvider` changes. `sources` replaces the whole ordered source list (a list is set, not patched, since its order is its meaning); `target` changes where the token goes and keeps the sources. `env` entries merge over the existing ones and `unsetEnv` keys are then deleted.
|
|
83
|
+
*/
|
|
84
|
+
export interface UpdateProviderInput {
|
|
85
|
+
readonly displayName?: string;
|
|
86
|
+
readonly baseUrl?: string;
|
|
87
|
+
readonly sources?: readonly CredentialSource[];
|
|
88
|
+
readonly target?: ProviderCredentialTarget;
|
|
89
|
+
/** A new cache block, or false to remove it; undefined leaves the existing one. */
|
|
90
|
+
readonly cache?: CredentialCache | false;
|
|
91
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
92
|
+
readonly unsetEnv?: readonly string[];
|
|
93
|
+
/** Merged over a codex provider's existing settings: a field given here replaces that field, and `models` entries merge per tier. */
|
|
94
|
+
readonly codex?: CodexProviderConfig;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Updates an existing provider definition in place. Throws `ProviderNotFoundError` when it does not exist, and `ConfigValidationError` when the updated definition fails `ProviderSchema`, leaving the file untouched.
|
|
98
|
+
*/
|
|
99
|
+
export declare function updateProvider(paths: LayoutPaths, name: string, input: UpdateProviderInput): Provider;
|
|
100
|
+
/** Deletes a provider definition. Throws `ProviderNotFoundError` when it does not exist. */
|
|
101
|
+
export declare function removeProvider(paths: LayoutPaths, name: string): void;
|
|
102
|
+
/**
|
|
103
|
+
* The provider name the cascade selects through `launch.provider`, if any layer sets one. Last layer wins, the same rule `flattenLayers` applies to every other launch flag.
|
|
104
|
+
*/
|
|
105
|
+
export declare function cascadeProviderName(cascade: CascadeInput): string | undefined;
|
|
106
|
+
/** Inputs to `resolveProvider`. */
|
|
107
|
+
export interface ResolveProviderParams {
|
|
108
|
+
readonly paths: LayoutPaths;
|
|
109
|
+
readonly port: FsPort;
|
|
110
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
111
|
+
/** Resolves the provider's credential sources. Omitted by a caller that cannot read secret files or run commands; a selected provider is then refused rather than launched without its credential. */
|
|
112
|
+
readonly credentials?: CredentialPort;
|
|
113
|
+
/** The `--provider <name>` flag's value, when one was given. It outranks any cascade layer, matching how the cliOverride layer composes last everywhere else. */
|
|
114
|
+
readonly cliProvider?: string;
|
|
115
|
+
/** The assembled-but-unflattened cascade for this launch, when one was loaded. Its layers are scanned for a `launch.provider` selection so a profile, directory rule, portable file, or the global config can pin a provider exactly the way they pin the other launch flags. */
|
|
116
|
+
readonly cascade?: CascadeInput;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The outcome of resolving this launch's provider: either a fully-resolved provider (definition plus its resolved credential), or a refusal the launcher turns into a stderr line and a non-zero exit.
|
|
120
|
+
*
|
|
121
|
+
* A provider whose credential block yields no token exits `CREDENTIAL_UNAVAILABLE_EXIT` (64), because the invocation cannot authenticate. An unknown provider name exits 1, matching every other "named thing not found" refusal in the launcher.
|
|
122
|
+
*/
|
|
123
|
+
export type ProviderResolution = {
|
|
124
|
+
readonly ok: true;
|
|
125
|
+
readonly provider: ResolvedProvider;
|
|
126
|
+
readonly warnings: readonly string[];
|
|
127
|
+
} | {
|
|
128
|
+
readonly ok: false;
|
|
129
|
+
readonly status: number;
|
|
130
|
+
readonly message: string;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Resolves which API provider this launch routes through, if any: the `--provider` flag first, then the cascade's `launch.provider` selection. Returns undefined when nothing selected a provider at all.
|
|
134
|
+
*
|
|
135
|
+
* Pure over its injected ports, so the launcher's tests exercise refusals without touching a real providers directory, secret store or terminal: this function decides, and the caller owns the log/exit side effects.
|
|
136
|
+
*/
|
|
137
|
+
export declare function resolveProvider(params: ResolveProviderParams): ProviderResolution | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-shim",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.8.0",
|
|
4
4
|
"description": "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Joseph Mearman <joseph@mearman.co.uk>",
|