@agentproto/secrets 0.1.0-alpha.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 +23 -0
- package/bin/agentproto-secrets.mjs +4 -0
- package/dist/chunk-MVHOJPML.mjs +132 -0
- package/dist/chunk-MVHOJPML.mjs.map +1 -0
- package/dist/chunk-NEGXQWE5.mjs +226 -0
- package/dist/chunk-NEGXQWE5.mjs.map +1 -0
- package/dist/chunk-NLZ5HXGO.mjs +90 -0
- package/dist/chunk-NLZ5HXGO.mjs.map +1 -0
- package/dist/chunk-SFLTQZQB.mjs +29 -0
- package/dist/chunk-SFLTQZQB.mjs.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.mjs +203 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/exposure/index.d.ts +94 -0
- package/dist/exposure/index.mjs +91 -0
- package/dist/exposure/index.mjs.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.mjs +14 -0
- package/dist/index.mjs.map +1 -0
- package/dist/manifest/index.d.ts +46 -0
- package/dist/manifest/index.mjs +28 -0
- package/dist/manifest/index.mjs.map +1 -0
- package/dist/provision/index.d.ts +90 -0
- package/dist/provision/index.mjs +4 -0
- package/dist/provision/index.mjs.map +1 -0
- package/dist/provision/recipe/index.d.ts +242 -0
- package/dist/provision/recipe/index.mjs +5 -0
- package/dist/provision/recipe/index.mjs.map +1 -0
- package/dist/seal/index.d.ts +77 -0
- package/dist/seal/index.mjs +3 -0
- package/dist/seal/index.mjs.map +1 -0
- package/dist/types-C2dZDHn7.d.ts +97 -0
- package/dist/types-Clav8IDA.d.ts +141 -0
- package/package.json +92 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @agentproto/secrets/provision — seal a secret and install it into a remote
|
|
3
|
+
* vault, carrying only ciphertext.
|
|
4
|
+
*
|
|
5
|
+
* The flow is: resolve a credential → fetch the target's sealing public key →
|
|
6
|
+
* seal to it → hand the sealed blob to the target's install. The plaintext
|
|
7
|
+
* exists only on the origin machine and, transiently, inside the target's
|
|
8
|
+
* unseal boundary — never in whatever drives this (an agent, a relay, a log).
|
|
9
|
+
*
|
|
10
|
+
* The target is an INJECTED PORT: this module knows nothing about any specific
|
|
11
|
+
* server. A caller provides a `SealedInstallTarget` — `httpTarget()` builds a
|
|
12
|
+
* generic one from a seal-key URL + install URL + headers; a host-specific
|
|
13
|
+
* caller (e.g. a product that knows its own server's paths and auth) injects
|
|
14
|
+
* its own. No vendor names leak into this package.
|
|
15
|
+
*/
|
|
16
|
+
/** Public sealing material a target advertises so a sender can seal to it. */
|
|
17
|
+
interface SealKeyInfo {
|
|
18
|
+
keyId: string;
|
|
19
|
+
alg: string;
|
|
20
|
+
publicKey: string;
|
|
21
|
+
}
|
|
22
|
+
/** The sealed payload handed to a target's install. `value` is already a
|
|
23
|
+
* sealed envelope (opaque); `keyId` lets the target reject a value sealed
|
|
24
|
+
* against a rotated key. */
|
|
25
|
+
interface SealedInstallInput {
|
|
26
|
+
provider: string;
|
|
27
|
+
methodId: string;
|
|
28
|
+
value: string;
|
|
29
|
+
keyId: string;
|
|
30
|
+
label?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A server that can receive a sealed credential. The two halves a provision
|
|
34
|
+
* needs: advertise the sealing key, and accept the sealed install. Implement
|
|
35
|
+
* this for any concrete server; the provision flow is agnostic to how.
|
|
36
|
+
*/
|
|
37
|
+
interface SealedInstallTarget {
|
|
38
|
+
fetchSealKey(): Promise<SealKeyInfo>;
|
|
39
|
+
installSealed(input: SealedInstallInput): Promise<{
|
|
40
|
+
secretId?: string;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
interface ProvisionSealedInput {
|
|
44
|
+
target: SealedInstallTarget;
|
|
45
|
+
provider: string;
|
|
46
|
+
methodId: string;
|
|
47
|
+
/** The plaintext credential. Resolve it (e.g. via `resolveCredential`) right
|
|
48
|
+
* before calling so it lives as briefly as possible; it is sealed here and
|
|
49
|
+
* never returned or logged. */
|
|
50
|
+
credential: string;
|
|
51
|
+
label?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Seal `credential` to the target's current key and install it. Returns the
|
|
55
|
+
* target's result plus the keyId used. The plaintext is consumed here and
|
|
56
|
+
* never leaves.
|
|
57
|
+
*/
|
|
58
|
+
declare function provisionSealed(input: ProvisionSealedInput): Promise<{
|
|
59
|
+
secretId?: string;
|
|
60
|
+
keyId: string;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Build a generic HTTP target from a seal-key URL, an install URL, and
|
|
64
|
+
* optional headers (auth). Vendor-neutral — the URLs and headers are the
|
|
65
|
+
* caller's to supply. The install POSTs `{ ...input, sealed: true }` as JSON.
|
|
66
|
+
*/
|
|
67
|
+
declare function httpTarget(config: {
|
|
68
|
+
sealKeyUrl: string;
|
|
69
|
+
installUrl: string;
|
|
70
|
+
headers?: Record<string, string>;
|
|
71
|
+
/** Injected for tests; defaults to global fetch. */
|
|
72
|
+
fetchImpl?: typeof fetch;
|
|
73
|
+
}): SealedInstallTarget;
|
|
74
|
+
interface CredentialSource {
|
|
75
|
+
/** Read from this process env var. */
|
|
76
|
+
fromEnv?: string;
|
|
77
|
+
/** Read from this file path (`~` expands to the home dir). */
|
|
78
|
+
fromFile?: string;
|
|
79
|
+
/** Dot-path to extract a string from the file's JSON (else whole file). */
|
|
80
|
+
jsonPath?: string;
|
|
81
|
+
}
|
|
82
|
+
declare function extractJsonPath(raw: string, path: string): string;
|
|
83
|
+
/**
|
|
84
|
+
* Resolve a plaintext credential from a local source. Sensitive — callers
|
|
85
|
+
* seal the result immediately and never print it. Uses a dynamic `fs` import
|
|
86
|
+
* so the module has no hard Node filesystem dependency at import time.
|
|
87
|
+
*/
|
|
88
|
+
declare function resolveCredential(source: CredentialSource): Promise<string>;
|
|
89
|
+
|
|
90
|
+
export { type CredentialSource, type ProvisionSealedInput, type SealKeyInfo, type SealedInstallInput, type SealedInstallTarget, extractJsonPath, httpTarget, provisionSealed, resolveCredential };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.mjs"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Provision-recipe doctype — types.
|
|
5
|
+
*
|
|
6
|
+
* A recipe declares, for one credential provider (claude-code-oauth, codex,
|
|
7
|
+
* gemini, …), WHERE its credential lives on an origin machine and WHICH auth
|
|
8
|
+
* method id it installs as. It is the manifest the `provision` flow resolves
|
|
9
|
+
* against so a caller writes `--provider codex` instead of hand-passing
|
|
10
|
+
* `--from-file / --json-path / --method`.
|
|
11
|
+
*
|
|
12
|
+
* A recipe holds LOCATIONS, never VALUES — the schema forbids a `value` field
|
|
13
|
+
* so nobody can stuff a live secret into a recipe. The plaintext is read only
|
|
14
|
+
* at provision time, sealed immediately, and never printed.
|
|
15
|
+
*
|
|
16
|
+
* Hand-written (not scaffold-aip-generated): a recipe is AIP-19-adjacent
|
|
17
|
+
* config, not yet a numbered spec artifact. If it earns its own AIP later,
|
|
18
|
+
* promote `schema.ts` to a generated frontmatter schema then.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Where a method's credential is read on the origin machine. Discriminated by
|
|
22
|
+
* the present key; exactly one of file / env / keychain / prompt.
|
|
23
|
+
*
|
|
24
|
+
* - file read a file (`~` expands); optionally extract a JSON string field.
|
|
25
|
+
* - env read a process env var.
|
|
26
|
+
* - keychain read the macOS login Keychain (`security find-generic-password`)
|
|
27
|
+
* by service name; optionally extract a JSON string field. macOS
|
|
28
|
+
* CLIs (e.g. Claude Code) store their OAuth token here, not on disk.
|
|
29
|
+
* - prompt read interactively (e.g. a website password for a remote browser);
|
|
30
|
+
* never on disk, never logged. Needs an injected prompt impl.
|
|
31
|
+
*/
|
|
32
|
+
type CredentialSourceSpec = {
|
|
33
|
+
file: string;
|
|
34
|
+
jsonPath?: string;
|
|
35
|
+
} | {
|
|
36
|
+
env: string;
|
|
37
|
+
} | {
|
|
38
|
+
keychain: string;
|
|
39
|
+
account?: string;
|
|
40
|
+
jsonPath?: string;
|
|
41
|
+
} | {
|
|
42
|
+
prompt: string;
|
|
43
|
+
};
|
|
44
|
+
/** One installable flavor of a provider's credential. The first method in a
|
|
45
|
+
* recipe is the default when `--method` is omitted. */
|
|
46
|
+
interface RecipeMethod {
|
|
47
|
+
/** Auth-method id this flavor installs as (e.g. "subscription-token",
|
|
48
|
+
* "api-key"). Becomes the vault connector's methodId. */
|
|
49
|
+
id: string;
|
|
50
|
+
/** Human label for this flavor; falls back to the recipe label. */
|
|
51
|
+
label?: string;
|
|
52
|
+
/** Where to read the credential for this flavor. An array is an ordered
|
|
53
|
+
* fallback chain — the first source that resolves wins (e.g. macOS Keychain,
|
|
54
|
+
* then a file, so one recipe spans platforms). */
|
|
55
|
+
source: CredentialSourceSpec | CredentialSourceSpec[];
|
|
56
|
+
}
|
|
57
|
+
interface ProvisionRecipeDefinition {
|
|
58
|
+
/** Provider id — kebab/dot, 2–80 chars (the cross-AIP id pattern). */
|
|
59
|
+
id: string;
|
|
60
|
+
/** LLM/human-facing prose; ≤2000 chars. */
|
|
61
|
+
description: string;
|
|
62
|
+
/** Installable flavors; ≥1, first = default. */
|
|
63
|
+
methods: [RecipeMethod, ...RecipeMethod[]];
|
|
64
|
+
/** Default connector label; a method's own label overrides. */
|
|
65
|
+
label?: string;
|
|
66
|
+
}
|
|
67
|
+
type ProvisionRecipe = Readonly<ProvisionRecipeDefinition>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Provision-recipe frontmatter / literal zod schema.
|
|
71
|
+
*
|
|
72
|
+
* Single source of truth for both authoring paths: `defineProvisionRecipe`
|
|
73
|
+
* (TS literal) and `parseRecipeManifest` (.md) run this same schema, so a
|
|
74
|
+
* malformed literal and a malformed manifest fail identically — mirrors how
|
|
75
|
+
* `secretsFrontmatterSchema` backs both `defineSecrets` and
|
|
76
|
+
* `parseSecretsManifest`.
|
|
77
|
+
*
|
|
78
|
+
* Hand-written rather than scaffold-aip-generated: a recipe is not yet a
|
|
79
|
+
* numbered-AIP artifact. Promote to a generated schema if it gets its own spec.
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
/** Exactly one of file / env / keychain / prompt. `value` is explicitly
|
|
83
|
+
* forbidden — a recipe carries locations, never secrets. */
|
|
84
|
+
declare const credentialSourceSpecSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
85
|
+
file: z.ZodString;
|
|
86
|
+
jsonPath: z.ZodOptional<z.ZodString>;
|
|
87
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
88
|
+
env: z.ZodString;
|
|
89
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
90
|
+
keychain: z.ZodString;
|
|
91
|
+
account: z.ZodOptional<z.ZodString>;
|
|
92
|
+
jsonPath: z.ZodOptional<z.ZodString>;
|
|
93
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
94
|
+
prompt: z.ZodString;
|
|
95
|
+
}, z.core.$strict>]>;
|
|
96
|
+
declare const recipeMethodSchema: z.ZodObject<{
|
|
97
|
+
id: z.ZodString;
|
|
98
|
+
label: z.ZodOptional<z.ZodString>;
|
|
99
|
+
source: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodObject<{
|
|
100
|
+
file: z.ZodString;
|
|
101
|
+
jsonPath: z.ZodOptional<z.ZodString>;
|
|
102
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
103
|
+
env: z.ZodString;
|
|
104
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
105
|
+
keychain: z.ZodString;
|
|
106
|
+
account: z.ZodOptional<z.ZodString>;
|
|
107
|
+
jsonPath: z.ZodOptional<z.ZodString>;
|
|
108
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
109
|
+
prompt: z.ZodString;
|
|
110
|
+
}, z.core.$strict>]>, z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
111
|
+
file: z.ZodString;
|
|
112
|
+
jsonPath: z.ZodOptional<z.ZodString>;
|
|
113
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
114
|
+
env: z.ZodString;
|
|
115
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
116
|
+
keychain: z.ZodString;
|
|
117
|
+
account: z.ZodOptional<z.ZodString>;
|
|
118
|
+
jsonPath: z.ZodOptional<z.ZodString>;
|
|
119
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
120
|
+
prompt: z.ZodString;
|
|
121
|
+
}, z.core.$strict>]>>]>;
|
|
122
|
+
}, z.core.$strict>;
|
|
123
|
+
declare const provisionRecipeFrontmatterSchema: z.ZodObject<{
|
|
124
|
+
id: z.ZodString;
|
|
125
|
+
description: z.ZodString;
|
|
126
|
+
methods: z.ZodArray<z.ZodObject<{
|
|
127
|
+
id: z.ZodString;
|
|
128
|
+
label: z.ZodOptional<z.ZodString>;
|
|
129
|
+
source: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodObject<{
|
|
130
|
+
file: z.ZodString;
|
|
131
|
+
jsonPath: z.ZodOptional<z.ZodString>;
|
|
132
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
133
|
+
env: z.ZodString;
|
|
134
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
135
|
+
keychain: z.ZodString;
|
|
136
|
+
account: z.ZodOptional<z.ZodString>;
|
|
137
|
+
jsonPath: z.ZodOptional<z.ZodString>;
|
|
138
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
139
|
+
prompt: z.ZodString;
|
|
140
|
+
}, z.core.$strict>]>, z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
141
|
+
file: z.ZodString;
|
|
142
|
+
jsonPath: z.ZodOptional<z.ZodString>;
|
|
143
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
144
|
+
env: z.ZodString;
|
|
145
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
146
|
+
keychain: z.ZodString;
|
|
147
|
+
account: z.ZodOptional<z.ZodString>;
|
|
148
|
+
jsonPath: z.ZodOptional<z.ZodString>;
|
|
149
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
150
|
+
prompt: z.ZodString;
|
|
151
|
+
}, z.core.$strict>]>>]>;
|
|
152
|
+
}, z.core.$strict>>;
|
|
153
|
+
label: z.ZodOptional<z.ZodString>;
|
|
154
|
+
}, z.core.$strict>;
|
|
155
|
+
type ProvisionRecipeFrontmatter = z.infer<typeof provisionRecipeFrontmatterSchema>;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* `defineProvisionRecipe` — the TS-literal authoring path for a recipe.
|
|
159
|
+
*
|
|
160
|
+
* Built on `createDoctype` so a recipe gets the same cross-AIP invariants as
|
|
161
|
+
* every other doctype: id-pattern check, ≤2000-char description, frozen handle,
|
|
162
|
+
* canonical "defineProvisionRecipe (AIP-19): …" error prefix. Field-level
|
|
163
|
+
* validation runs the shared zod from `./schema.ts`, so a malformed literal
|
|
164
|
+
* fails with the same diagnostic as a malformed `.md`.
|
|
165
|
+
*/
|
|
166
|
+
|
|
167
|
+
declare const defineProvisionRecipe: (def: ProvisionRecipeDefinition) => Readonly<ProvisionRecipeDefinition>;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* `.md` authoring path for a provision recipe — parse a frontmatter manifest
|
|
171
|
+
* into a frozen handle. Mirrors `parseSecretsManifest`: gray-matter splits the
|
|
172
|
+
* frontmatter, the shared zod validates it, `defineProvisionRecipe` builds the
|
|
173
|
+
* handle so the `.md` and TS-literal paths converge on one validated shape.
|
|
174
|
+
*
|
|
175
|
+
* The package ships its builtin recipes as TS literals (they bundle with zero
|
|
176
|
+
* fs); this parser is for a HOST that reads its own external `.md` recipes and
|
|
177
|
+
* registers them — the extension seam.
|
|
178
|
+
*/
|
|
179
|
+
|
|
180
|
+
interface ProvisionRecipeManifest {
|
|
181
|
+
frontmatter: ProvisionRecipeFrontmatter;
|
|
182
|
+
body: string;
|
|
183
|
+
}
|
|
184
|
+
declare function parseRecipeManifestRaw(source: string): ProvisionRecipeManifest;
|
|
185
|
+
/** Parse a recipe `.md` straight to a frozen handle (frontmatter →
|
|
186
|
+
* `defineProvisionRecipe`). */
|
|
187
|
+
declare function parseRecipeManifest(source: string): ProvisionRecipe;
|
|
188
|
+
|
|
189
|
+
/** Claude Code subscription OAuth token. macOS stores it in the login Keychain;
|
|
190
|
+
* Linux writes it to ~/.claude/.credentials.json. Try the Keychain first, fall
|
|
191
|
+
* back to the file, so one recipe spans both. */
|
|
192
|
+
declare const claudeCodeOauthRecipe: Readonly<ProvisionRecipeDefinition>;
|
|
193
|
+
/** Codex CLI — two flavors: the subscription OAuth token, or a raw API key. */
|
|
194
|
+
declare const codexRecipe: Readonly<ProvisionRecipeDefinition>;
|
|
195
|
+
/** Gemini CLI OAuth token written by the local CLI. */
|
|
196
|
+
declare const geminiRecipe: Readonly<ProvisionRecipeDefinition>;
|
|
197
|
+
declare const BUILTIN_RECIPES: readonly ProvisionRecipe[];
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Provision-recipe registry — id → recipe lookup, pre-seeded with the builtins.
|
|
201
|
+
*
|
|
202
|
+
* Mirrors the host-side OAuth-provider registry shape: a module-level Map a
|
|
203
|
+
* caller resolves by id at provision time, plus a `registerRecipe` seam so a
|
|
204
|
+
* host can add recipes parsed from its own `.md` (`parseRecipeManifest`)
|
|
205
|
+
* without editing this package. Re-registering an id overrides it — last write
|
|
206
|
+
* wins, so a host can shadow a builtin.
|
|
207
|
+
*/
|
|
208
|
+
|
|
209
|
+
declare function registerRecipe(recipe: ProvisionRecipe): void;
|
|
210
|
+
declare function getRecipe(id: string): ProvisionRecipe | undefined;
|
|
211
|
+
declare function listRecipes(): ProvisionRecipe[];
|
|
212
|
+
declare function listRecipeIds(): string[];
|
|
213
|
+
/**
|
|
214
|
+
* Resolve a recipe + one of its methods. `methodId` selects among the recipe's
|
|
215
|
+
* flavors; omitted picks the first (default). Throws a caller-friendly error
|
|
216
|
+
* naming the available methods when the id is unknown or ambiguous.
|
|
217
|
+
*/
|
|
218
|
+
declare function resolveRecipeMethod(id: string, methodId?: string): {
|
|
219
|
+
recipe: ProvisionRecipe;
|
|
220
|
+
method: RecipeMethod;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Resolve a recipe method's source to plaintext.
|
|
225
|
+
*
|
|
226
|
+
* A source is a single `CredentialSourceSpec` or an ordered fallback chain
|
|
227
|
+
* (array) — the first that resolves wins, so one recipe spans platforms
|
|
228
|
+
* (macOS Keychain → file). Bridges onto `resolveCredential` (file / env), the
|
|
229
|
+
* macOS `security` Keychain, and an injected prompt impl (interactive, e.g. a
|
|
230
|
+
* website password for a remote browser). The prompt impl is INJECTED — no
|
|
231
|
+
* hard TTY dependency at import, same discipline as `fetchImpl` on
|
|
232
|
+
* `httpTarget`. The result is sensitive: seal it immediately, never print it.
|
|
233
|
+
*/
|
|
234
|
+
|
|
235
|
+
interface ResolveSourceOptions {
|
|
236
|
+
/** Asked for a `{ prompt }` source. Receives the prompt text, returns the
|
|
237
|
+
* entered value. Required only when a method uses a prompt source. */
|
|
238
|
+
promptImpl?: (prompt: string) => Promise<string>;
|
|
239
|
+
}
|
|
240
|
+
declare function resolveSourceSpec(source: CredentialSourceSpec | CredentialSourceSpec[], opts?: ResolveSourceOptions): Promise<string>;
|
|
241
|
+
|
|
242
|
+
export { BUILTIN_RECIPES, type CredentialSourceSpec, type ProvisionRecipe, type ProvisionRecipeDefinition, type ProvisionRecipeFrontmatter, type ProvisionRecipeManifest, type RecipeMethod, type ResolveSourceOptions, claudeCodeOauthRecipe, codexRecipe, credentialSourceSpecSchema, defineProvisionRecipe, geminiRecipe, getRecipe, listRecipeIds, listRecipes, parseRecipeManifest, parseRecipeManifestRaw, provisionRecipeFrontmatterSchema, recipeMethodSchema, registerRecipe, resolveRecipeMethod, resolveSourceSpec };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { BUILTIN_RECIPES, claudeCodeOauthRecipe, codexRecipe, credentialSourceSpecSchema, defineProvisionRecipe, geminiRecipe, getRecipe, listRecipeIds, listRecipes, parseRecipeManifest, parseRecipeManifestRaw, provisionRecipeFrontmatterSchema, recipeMethodSchema, registerRecipe, resolveRecipeMethod, resolveSourceSpec } from '../../chunk-NEGXQWE5.mjs';
|
|
2
|
+
import '../../chunk-NLZ5HXGO.mjs';
|
|
3
|
+
import '../../chunk-MVHOJPML.mjs';
|
|
4
|
+
//# sourceMappingURL=index.mjs.map
|
|
5
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.mjs"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @agentproto/secrets/seal — anonymous public-key sealing for secret values.
|
|
3
|
+
*
|
|
4
|
+
* A "sealed box": encrypt a secret TO a recipient's public key such that only
|
|
5
|
+
* the holder of the matching private key can open it. The sender needs only
|
|
6
|
+
* the public key, and the sealed envelope reveals nothing about the sender.
|
|
7
|
+
*
|
|
8
|
+
* The point is custody. A secret value (a CLI subscription token, an API key)
|
|
9
|
+
* can be sealed on the machine that holds it, then handed to ANY intermediary
|
|
10
|
+
* — a relay, an agent, a log — as opaque ciphertext, because only the server's
|
|
11
|
+
* private key can recover the plaintext. The plaintext never has to exist
|
|
12
|
+
* anywhere but the origin machine and, transiently, inside the recipient's
|
|
13
|
+
* unseal boundary.
|
|
14
|
+
*
|
|
15
|
+
* Construction (ECIES, Node built-ins only — no native dep):
|
|
16
|
+
* - X25519 ephemeral key agreement with the recipient's public key
|
|
17
|
+
* - HKDF-SHA256 over the shared secret, salted by both public keys
|
|
18
|
+
* - AES-256-GCM AEAD for the payload (authenticated; tampering fails closed)
|
|
19
|
+
*
|
|
20
|
+
* This is the same shape as libsodium's `crypto_box_seal`, expressed in terms
|
|
21
|
+
* of Node's `crypto` so the package keeps its zero-native-dependency footprint.
|
|
22
|
+
*
|
|
23
|
+
* Sealing (confidentiality) is NOT signing (authenticity): this hides the
|
|
24
|
+
* value, it does not prove who sent it. Bind the sender separately (e.g. an
|
|
25
|
+
* authenticated transport) when provenance matters.
|
|
26
|
+
*/
|
|
27
|
+
/** Versioned algorithm tag embedded in every envelope so the format can
|
|
28
|
+
* evolve without ambiguity at the unseal site. */
|
|
29
|
+
declare const SEAL_ALG: "x25519-hkdf-sha256-aes256gcm";
|
|
30
|
+
declare const SEAL_VERSION: 1;
|
|
31
|
+
/** Raised for every seal/unseal failure. The message is safe to surface —
|
|
32
|
+
* it never contains key material or plaintext. */
|
|
33
|
+
declare class SealError extends Error {
|
|
34
|
+
constructor(message: string);
|
|
35
|
+
}
|
|
36
|
+
/** An X25519 keypair for sealing. Both halves are base64-encoded DER so they
|
|
37
|
+
* travel as plain strings (env vars, JSON, config). The public half is safe
|
|
38
|
+
* to publish; the private half must stay with the unseal boundary. */
|
|
39
|
+
interface SealKeyPair {
|
|
40
|
+
/** base64 DER (SPKI) X25519 public key — publishable. */
|
|
41
|
+
publicKey: string;
|
|
42
|
+
/** base64 DER (PKCS8) X25519 private key — secret. */
|
|
43
|
+
privateKey: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Mint a fresh sealing keypair. The recipient (e.g. a server) generates this
|
|
47
|
+
* once, stores `privateKey` in its own secret store, and publishes
|
|
48
|
+
* `publicKey` for senders to seal against.
|
|
49
|
+
*/
|
|
50
|
+
declare function generateSealKeyPair(): SealKeyPair;
|
|
51
|
+
/**
|
|
52
|
+
* Derive the publishable public key from a stored private key. Lets a
|
|
53
|
+
* recipient hold only the private half (one secret) and serve the public
|
|
54
|
+
* half on demand — the seal-key a sender needs.
|
|
55
|
+
*/
|
|
56
|
+
declare function sealingPublicKey(privateKey: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* Stable short identifier for a sealing key, derived from the public key.
|
|
59
|
+
* Lets the seal-key endpoint and the sealed envelope name which key was used
|
|
60
|
+
* so rotation is unambiguous. Not a secret.
|
|
61
|
+
*/
|
|
62
|
+
declare function sealKeyId(publicKey: string): string;
|
|
63
|
+
/**
|
|
64
|
+
* Seal a plaintext value to a recipient's public key. Returns a single
|
|
65
|
+
* base64 string (the envelope) that only the matching private key can open.
|
|
66
|
+
* The sender needs nothing but the public key.
|
|
67
|
+
*/
|
|
68
|
+
declare function seal(plaintext: string | Uint8Array, recipientPublicKey: string): string;
|
|
69
|
+
/**
|
|
70
|
+
* Open a sealed envelope with the recipient's private key, returning the
|
|
71
|
+
* original UTF-8 plaintext. Throws `SealError` on a malformed envelope, an
|
|
72
|
+
* unsupported version/alg, the wrong key, or any tampering (the AEAD tag
|
|
73
|
+
* fails closed — a modified ciphertext never decrypts to garbage, it throws).
|
|
74
|
+
*/
|
|
75
|
+
declare function unseal(sealed: string, privateKey: string): string;
|
|
76
|
+
|
|
77
|
+
export { SEAL_ALG, SEAL_VERSION, SealError, type SealKeyPair, generateSealKeyPair, seal, sealKeyId, sealingPublicKey, unseal };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.mjs"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SecretExposure — describes HOW a secret value reaches the runtime
|
|
3
|
+
* that needs it (env var, config file, egress placeholder, future
|
|
4
|
+
* MCP-header injection, future HTTP-bearer routing, …).
|
|
5
|
+
*
|
|
6
|
+
* Distinct from `SecretEntry` (AIP-19), which describes the secret's
|
|
7
|
+
* *declaration* (slug, kind, access policy). Exposure is a *runtime*
|
|
8
|
+
* concern — same secret might be exposed multiple ways across hosts.
|
|
9
|
+
*
|
|
10
|
+
* Discriminated union by `kind` so each new mechanism slots in as one
|
|
11
|
+
* variant rather than another top-level field on whatever catalog the
|
|
12
|
+
* host uses to map secrets onto agent runtimes.
|
|
13
|
+
*
|
|
14
|
+
* # Why this lives here (not in @agentproto/egress)
|
|
15
|
+
* The substitution sigil + the "what surfaces does this secret have"
|
|
16
|
+
* vocabulary are properties of the secret itself — they describe its
|
|
17
|
+
* exposure surface independent of WHO consumes it. Egress is one
|
|
18
|
+
* consumer; future ones (MCP servers, HTTP relays) are others.
|
|
19
|
+
*
|
|
20
|
+
* The egress *gating* (mode registry, proxy core) lives in
|
|
21
|
+
* @agentproto/egress because that's a different concern (network-
|
|
22
|
+
* boundary control). Egress imports from here.
|
|
23
|
+
*/
|
|
24
|
+
/** Function applied to the seed value before write. Receives the raw
|
|
25
|
+
* secret value (or empty string when `field` is omitted) plus an
|
|
26
|
+
* optional context bag the host populates. Returns the file body. */
|
|
27
|
+
type SecretExposureWrap = (value: string, ctx?: SecretExposureWrapContext) => string;
|
|
28
|
+
/** Generic context passed to `wrap` callbacks. Hosts populate fields
|
|
29
|
+
* they have; wraps that don't care ignore the second arg entirely.
|
|
30
|
+
* Kept open-ended (string-keyed) so hosts can attach app-specific
|
|
31
|
+
* data without forcing a schema change here. */
|
|
32
|
+
interface SecretExposureWrapContext {
|
|
33
|
+
[key: string]: unknown;
|
|
34
|
+
}
|
|
35
|
+
/** Inject the secret value as a process env var inside the runtime. */
|
|
36
|
+
interface EnvExposure {
|
|
37
|
+
kind: "env";
|
|
38
|
+
/** Env var name as the runtime sees it. */
|
|
39
|
+
name: string;
|
|
40
|
+
/** Field on the secret/connector settings whose value seeds the env.
|
|
41
|
+
* Required for env exposures — env vars don't have a `wrap` path. */
|
|
42
|
+
field: string;
|
|
43
|
+
}
|
|
44
|
+
/** Drop a file inside the runtime's filesystem before any process
|
|
45
|
+
* starts. Path uses `~` expansion against the runtime's exec user
|
|
46
|
+
* home (host-resolved). */
|
|
47
|
+
interface FileExposure {
|
|
48
|
+
kind: "file";
|
|
49
|
+
/** Absolute path or `~`-relative path inside the runtime sandbox. */
|
|
50
|
+
path: string;
|
|
51
|
+
/** Field on the secret/connector settings whose value seeds the file
|
|
52
|
+
* body. Omit when `wrap` produces synthetic content (e.g. claude's
|
|
53
|
+
* `.claude.json` migration flags don't depend on a token). */
|
|
54
|
+
field?: string;
|
|
55
|
+
/** POSIX file mode. Default `0o600`. */
|
|
56
|
+
mode?: number;
|
|
57
|
+
/** Transform applied before write — see SecretExposureWrap. */
|
|
58
|
+
wrap?: SecretExposureWrap;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Mark this secret as eligible for substitution at the egress proxy.
|
|
62
|
+
* Agents see only `$$SECRET[<placeholderName>]$$` placeholders; the
|
|
63
|
+
* proxy substitutes the real value at the network boundary.
|
|
64
|
+
*
|
|
65
|
+
* The actual proxy + sigil regex + substitution engine live in
|
|
66
|
+
* `@agentproto/egress` — this exposure variant just declares the
|
|
67
|
+
* intent + the per-secret defaults the host install handler stamps.
|
|
68
|
+
*/
|
|
69
|
+
interface EgressSubstituteExposure {
|
|
70
|
+
kind: "egress-substitute";
|
|
71
|
+
/** The token agents reference in `$$SECRET[NAME]$$`. By convention
|
|
72
|
+
* this matches the env-exposure name on the same secret, but it
|
|
73
|
+
* doesn't have to — placeholders and env vars are independent. */
|
|
74
|
+
placeholderName: string;
|
|
75
|
+
/** Default value for the per-secret allowlist flag stamped at install
|
|
76
|
+
* time. UI may offer a per-install override. When false, the secret
|
|
77
|
+
* is declared egress-aware but rejected by the resolver until an
|
|
78
|
+
* admin opts in. */
|
|
79
|
+
allowedByDefault: boolean;
|
|
80
|
+
/** Audit / UX hint — the providers this secret is *meant* for. NOT
|
|
81
|
+
* enforced at egress (proxy substitutes any matching placeholder
|
|
82
|
+
* regardless of upstream); the field exists so UIs can surface
|
|
83
|
+
* "this secret is for OpenAI" without parsing the slug. */
|
|
84
|
+
intendedProviders?: string[];
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Discriminated union of all exposure mechanisms. Add new variants here
|
|
88
|
+
* as new exposure surfaces are added (MCP-header, HTTP-bearer, etc.) —
|
|
89
|
+
* each adds one variant, one switch case in consumers.
|
|
90
|
+
*/
|
|
91
|
+
type SecretExposure = EnvExposure | FileExposure | EgressSubstituteExposure;
|
|
92
|
+
/** Type guard — convenient for filtering an exposures array by kind. */
|
|
93
|
+
declare function isExposureKind<K extends SecretExposure["kind"]>(exposure: SecretExposure, kind: K): exposure is Extract<SecretExposure, {
|
|
94
|
+
kind: K;
|
|
95
|
+
}>;
|
|
96
|
+
|
|
97
|
+
export { type EgressSubstituteExposure as E, type FileExposure as F, type SecretExposure as S, type EnvExposure as a, type SecretExposureWrap as b, type SecretExposureWrapContext as c, isExposureKind as i };
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { S as SecretExposure } from './types-C2dZDHn7.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AIP-19 SecretsDefinition + SecretsHandle.
|
|
5
|
+
*
|
|
6
|
+
* `SecretsDefinition` was generated from
|
|
7
|
+
* `resources/aip-19/draft/SECRETS.schema.json` via json-schema-to-typescript.
|
|
8
|
+
* `SecretsHandle` is the readonly view of the same shape; tighten it
|
|
9
|
+
* by hand for fields that get defaults applied in build().
|
|
10
|
+
*
|
|
11
|
+
* The `exposures` field on each entry was added by hand (not regen'd
|
|
12
|
+
* yet) — runtime exposure descriptors live in `./exposure/types.ts` so
|
|
13
|
+
* they can be consumed independently by hosts that don't need the
|
|
14
|
+
* full SECRETS.md doctype machinery (Guilde's connector registry,
|
|
15
|
+
* @agentproto/egress's proxy core).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
type SecretEntry = {
|
|
19
|
+
[k: string]: unknown;
|
|
20
|
+
} & {
|
|
21
|
+
/**
|
|
22
|
+
* Machine identifier. Lowercase, digits, dashes; optional <namespace>/ prefix. 2–80 chars total. Unique within the workspace inventory.
|
|
23
|
+
*/
|
|
24
|
+
slug: string;
|
|
25
|
+
name: string;
|
|
26
|
+
description: string;
|
|
27
|
+
/**
|
|
28
|
+
* Value shape. 'opaque' = single string. 'oauth' = { accessToken, refreshToken, expiresAt }. 'keypair' = { public, private }. 'json' = arbitrary structured.
|
|
29
|
+
*/
|
|
30
|
+
kind?: "opaque" | "oauth" | "keypair" | "json";
|
|
31
|
+
/**
|
|
32
|
+
* Vault URI (optional). Default: host-resolved by slug. Setting it explicitly exposes infra topology and SHOULD be reviewer-audited.
|
|
33
|
+
*/
|
|
34
|
+
backend?: string;
|
|
35
|
+
access?: AccessGrants;
|
|
36
|
+
audit?: AuditConfig;
|
|
37
|
+
tags?: string[];
|
|
38
|
+
metadata?: {
|
|
39
|
+
[k: string]: unknown;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* How this secret is exposed to the runtime that consumes it (env,
|
|
43
|
+
* file, egress placeholder, future MCP-header / HTTP-bearer / etc.).
|
|
44
|
+
* Optional — hosts MAY honor or remap based on their own catalog.
|
|
45
|
+
* Discriminated by `kind`; consumers ignore unknown kinds.
|
|
46
|
+
*
|
|
47
|
+
* See `@agentproto/secrets/exposure` for the variant types.
|
|
48
|
+
*/
|
|
49
|
+
exposures?: SecretExposure[];
|
|
50
|
+
} & {
|
|
51
|
+
/**
|
|
52
|
+
* Machine identifier. Lowercase, digits, dashes; optional <namespace>/ prefix. 2–80 chars total. Unique within the workspace inventory.
|
|
53
|
+
*/
|
|
54
|
+
slug: string;
|
|
55
|
+
name: string;
|
|
56
|
+
description: string;
|
|
57
|
+
/**
|
|
58
|
+
* Value shape. 'opaque' = single string. 'oauth' = { accessToken, refreshToken, expiresAt }. 'keypair' = { public, private }. 'json' = arbitrary structured.
|
|
59
|
+
*/
|
|
60
|
+
kind?: "opaque" | "oauth" | "keypair" | "json";
|
|
61
|
+
/**
|
|
62
|
+
* Vault URI (optional). Default: host-resolved by slug. Setting it explicitly exposes infra topology and SHOULD be reviewer-audited.
|
|
63
|
+
*/
|
|
64
|
+
backend?: string;
|
|
65
|
+
access?: AccessGrants;
|
|
66
|
+
audit?: AuditConfig;
|
|
67
|
+
tags?: string[];
|
|
68
|
+
metadata?: {
|
|
69
|
+
[k: string]: unknown;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* How this secret is exposed to the runtime that consumes it (env,
|
|
73
|
+
* file, egress placeholder, future MCP-header / HTTP-bearer / etc.).
|
|
74
|
+
* Optional — hosts MAY honor or remap based on their own catalog.
|
|
75
|
+
* Discriminated by `kind`; consumers ignore unknown kinds.
|
|
76
|
+
*
|
|
77
|
+
* See `@agentproto/secrets/exposure` for the variant types.
|
|
78
|
+
*/
|
|
79
|
+
exposures?: SecretExposure[];
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* A single access-grant entry. Exactly ONE of role/userId/cap/tool/workflow.
|
|
83
|
+
*/
|
|
84
|
+
type AccessEntry = {
|
|
85
|
+
role?: string;
|
|
86
|
+
userId?: string;
|
|
87
|
+
cap?: string;
|
|
88
|
+
tool?: string;
|
|
89
|
+
workflow?: string;
|
|
90
|
+
} & AccessEntry1 & {
|
|
91
|
+
role?: string;
|
|
92
|
+
userId?: string;
|
|
93
|
+
cap?: string;
|
|
94
|
+
tool?: string;
|
|
95
|
+
workflow?: string;
|
|
96
|
+
} & AccessEntry1 & {
|
|
97
|
+
role?: string;
|
|
98
|
+
userId?: string;
|
|
99
|
+
cap?: string;
|
|
100
|
+
tool?: string;
|
|
101
|
+
workflow?: string;
|
|
102
|
+
} & AccessEntry1;
|
|
103
|
+
type AccessEntry1 = {
|
|
104
|
+
[k: string]: unknown;
|
|
105
|
+
} | {
|
|
106
|
+
[k: string]: unknown;
|
|
107
|
+
} | {
|
|
108
|
+
[k: string]: unknown;
|
|
109
|
+
} | {
|
|
110
|
+
[k: string]: unknown;
|
|
111
|
+
} | {
|
|
112
|
+
[k: string]: unknown;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Validates the YAML frontmatter portion of an AIP-19 SECRETS.md manifest. The manifest declares secret slugs + access policy + audit metadata. Values are NEVER stored in the manifest.
|
|
116
|
+
*/
|
|
117
|
+
interface SecretsDefinition {
|
|
118
|
+
/**
|
|
119
|
+
* @minItems 1
|
|
120
|
+
*/
|
|
121
|
+
secrets: [SecretEntry, ...SecretEntry[]];
|
|
122
|
+
}
|
|
123
|
+
interface AccessGrants {
|
|
124
|
+
reveal?: AccessEntry[];
|
|
125
|
+
bind?: AccessEntry[];
|
|
126
|
+
/**
|
|
127
|
+
* Reserved for a future rotation AIP. Hosts MAY ignore.
|
|
128
|
+
*/
|
|
129
|
+
rotate?: AccessEntry[];
|
|
130
|
+
}
|
|
131
|
+
interface AuditConfig {
|
|
132
|
+
/**
|
|
133
|
+
* ISO 8601 duration ('P7Y') or shorthand ('7y', '90d').
|
|
134
|
+
*/
|
|
135
|
+
retention?: string;
|
|
136
|
+
pii?: boolean;
|
|
137
|
+
classification?: string[];
|
|
138
|
+
}
|
|
139
|
+
type SecretsHandle = Readonly<SecretsDefinition>;
|
|
140
|
+
|
|
141
|
+
export type { SecretsDefinition as S, SecretsHandle as a };
|