@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
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { listRecipeIds, resolveRecipeMethod, resolveSourceSpec } from './chunk-NEGXQWE5.mjs';
|
|
2
|
+
import { httpTarget, provisionSealed, resolveCredential } from './chunk-NLZ5HXGO.mjs';
|
|
3
|
+
import { unseal, seal, generateSealKeyPair, sealKeyId } from './chunk-MVHOJPML.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @agentproto/secrets v0.1.0-alpha
|
|
7
|
+
* AIP-19 SECRETS.md `defineSecrets` reference implementation.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// src/cli.ts
|
|
11
|
+
function parse(argv) {
|
|
12
|
+
const positionals = [];
|
|
13
|
+
const flags = {};
|
|
14
|
+
const headers = [];
|
|
15
|
+
for (let i = 0; i < argv.length; i++) {
|
|
16
|
+
const a = argv[i];
|
|
17
|
+
if (a.startsWith("--")) {
|
|
18
|
+
const eq = a.indexOf("=");
|
|
19
|
+
let key;
|
|
20
|
+
let val;
|
|
21
|
+
if (eq !== -1) {
|
|
22
|
+
key = a.slice(2, eq);
|
|
23
|
+
val = a.slice(eq + 1);
|
|
24
|
+
} else {
|
|
25
|
+
key = a.slice(2);
|
|
26
|
+
const next = argv[i + 1];
|
|
27
|
+
if (next !== void 0 && !next.startsWith("--")) {
|
|
28
|
+
val = next;
|
|
29
|
+
i++;
|
|
30
|
+
} else {
|
|
31
|
+
val = "true";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (key === "header") headers.push(val);
|
|
35
|
+
else flags[key] = val;
|
|
36
|
+
} else {
|
|
37
|
+
positionals.push(a);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { positionals, flags, headers };
|
|
41
|
+
}
|
|
42
|
+
function credentialSource(args) {
|
|
43
|
+
return {
|
|
44
|
+
...args.flags["from-env"] ? { fromEnv: args.flags["from-env"] } : {},
|
|
45
|
+
...args.flags["from-file"] ? { fromFile: args.flags["from-file"] } : {},
|
|
46
|
+
...args.flags["json-path"] ? { jsonPath: args.flags["json-path"] } : {}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async function readValue(args) {
|
|
50
|
+
if (args.positionals[0]) return args.positionals[0];
|
|
51
|
+
return resolveCredential(credentialSource(args));
|
|
52
|
+
}
|
|
53
|
+
async function promptStdin(prompt) {
|
|
54
|
+
const { createInterface } = await import('readline/promises');
|
|
55
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
56
|
+
try {
|
|
57
|
+
return (await rl.question(`${prompt}: `)).trim();
|
|
58
|
+
} finally {
|
|
59
|
+
rl.close();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function resolveProvision(args) {
|
|
63
|
+
const provider = args.flags.provider;
|
|
64
|
+
const explicit = args.flags["from-file"] || args.flags["from-env"];
|
|
65
|
+
if (explicit) {
|
|
66
|
+
const methodId = args.flags.method;
|
|
67
|
+
if (!methodId)
|
|
68
|
+
throw new Error(
|
|
69
|
+
"--method is required when passing an explicit --from-file/--from-env source"
|
|
70
|
+
);
|
|
71
|
+
return {
|
|
72
|
+
methodId,
|
|
73
|
+
credential: await resolveCredential(credentialSource(args)),
|
|
74
|
+
...args.flags.label ? { label: args.flags.label } : {}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const { recipe, method } = resolveRecipeMethod(provider, args.flags.method);
|
|
78
|
+
const credential = await resolveSourceSpec(method.source, {
|
|
79
|
+
promptImpl: promptStdin
|
|
80
|
+
});
|
|
81
|
+
const label = args.flags.label ?? method.label ?? recipe.label;
|
|
82
|
+
return { methodId: method.id, credential, ...label ? { label } : {} };
|
|
83
|
+
}
|
|
84
|
+
function parseHeaders(raw) {
|
|
85
|
+
const out = {};
|
|
86
|
+
for (const h of raw) {
|
|
87
|
+
const idx = h.indexOf(":");
|
|
88
|
+
if (idx === -1) continue;
|
|
89
|
+
out[h.slice(0, idx).trim()] = h.slice(idx + 1).trim();
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
var USAGE = `agentproto-secrets \u2014 seal secrets and install them into a vault
|
|
94
|
+
|
|
95
|
+
keygen mint a sealing keypair (JSON to stdout)
|
|
96
|
+
seal --pubkey <b64> [value|src] seal a value -> sealed blob (stdout)
|
|
97
|
+
unseal --privkey <b64> [blob|src] open a sealed blob -> plaintext (stdout)
|
|
98
|
+
provision --provider P --seal-key-url URL --install-url URL \\
|
|
99
|
+
[--method M] [--header 'K: V']... [src]
|
|
100
|
+
seal a local credential and install it,
|
|
101
|
+
carrying only ciphertext. A known
|
|
102
|
+
--provider resolves its method + source
|
|
103
|
+
from a builtin recipe; --method picks a
|
|
104
|
+
flavor; an explicit src overrides.
|
|
105
|
+
|
|
106
|
+
src: --from-file <path> [--json-path a.b.c] | --from-env <VAR>
|
|
107
|
+
builtin providers: ${listRecipeIds().join(", ")}`;
|
|
108
|
+
async function main() {
|
|
109
|
+
const [cmd, ...rest] = process.argv.slice(2);
|
|
110
|
+
const args = parse(rest);
|
|
111
|
+
switch (cmd) {
|
|
112
|
+
case "keygen": {
|
|
113
|
+
const kp = generateSealKeyPair();
|
|
114
|
+
process.stdout.write(
|
|
115
|
+
JSON.stringify(
|
|
116
|
+
{ keyId: sealKeyId(kp.publicKey), publicKey: kp.publicKey, privateKey: kp.privateKey },
|
|
117
|
+
null,
|
|
118
|
+
2
|
|
119
|
+
) + "\n"
|
|
120
|
+
);
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
case "seal": {
|
|
124
|
+
const pubkey = args.flags.pubkey;
|
|
125
|
+
if (!pubkey) return fail("seal: --pubkey <b64> required");
|
|
126
|
+
const value = await readValue(args);
|
|
127
|
+
process.stdout.write(seal(value, pubkey));
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
case "unseal": {
|
|
131
|
+
const privkey = args.flags.privkey;
|
|
132
|
+
if (!privkey) return fail("unseal: --privkey <b64> required");
|
|
133
|
+
const blob = args.positionals[0] ?? await resolveCredential(credentialSource(args));
|
|
134
|
+
process.stdout.write(unseal(blob, privkey));
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
case "provision": {
|
|
138
|
+
const provider = args.flags.provider;
|
|
139
|
+
const sealKeyUrl = args.flags["seal-key-url"];
|
|
140
|
+
const installUrl = args.flags["install-url"];
|
|
141
|
+
if (!provider) return fail("provision: --provider required");
|
|
142
|
+
if (!sealKeyUrl || !installUrl)
|
|
143
|
+
return fail("provision: --seal-key-url and --install-url required");
|
|
144
|
+
let methodId;
|
|
145
|
+
let credential;
|
|
146
|
+
let label;
|
|
147
|
+
try {
|
|
148
|
+
;
|
|
149
|
+
({ methodId, credential, label } = await resolveProvision(args));
|
|
150
|
+
} catch (err) {
|
|
151
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
152
|
+
}
|
|
153
|
+
const target = httpTarget({
|
|
154
|
+
sealKeyUrl,
|
|
155
|
+
installUrl,
|
|
156
|
+
headers: parseHeaders(args.headers)
|
|
157
|
+
});
|
|
158
|
+
try {
|
|
159
|
+
const out = await provisionSealed({
|
|
160
|
+
target,
|
|
161
|
+
provider,
|
|
162
|
+
methodId,
|
|
163
|
+
credential,
|
|
164
|
+
...label ? { label } : {}
|
|
165
|
+
});
|
|
166
|
+
process.stdout.write(
|
|
167
|
+
`sealed + installed ${provider} (${methodId})` + (out.secretId ? ` \u2014 secret ${out.secretId.slice(0, 8)}` : "") + ` [key ${out.keyId.slice(0, 8)}]
|
|
168
|
+
the plaintext never left this machine; the server unsealed it.
|
|
169
|
+
`
|
|
170
|
+
);
|
|
171
|
+
return 0;
|
|
172
|
+
} catch (err) {
|
|
173
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
case "-h":
|
|
177
|
+
case "--help":
|
|
178
|
+
case "help":
|
|
179
|
+
case void 0:
|
|
180
|
+
process.stdout.write(USAGE + "\n");
|
|
181
|
+
return cmd ? 0 : 1;
|
|
182
|
+
default:
|
|
183
|
+
return fail(`unknown command '${cmd}'
|
|
184
|
+
|
|
185
|
+
${USAGE}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function fail(message) {
|
|
189
|
+
process.stderr.write(`agentproto-secrets: ${message}
|
|
190
|
+
`);
|
|
191
|
+
return 1;
|
|
192
|
+
}
|
|
193
|
+
main().then((code) => {
|
|
194
|
+
process.exitCode = code;
|
|
195
|
+
}).catch((err) => {
|
|
196
|
+
process.stderr.write(
|
|
197
|
+
`agentproto-secrets: ${err instanceof Error ? err.message : String(err)}
|
|
198
|
+
`
|
|
199
|
+
);
|
|
200
|
+
process.exitCode = 1;
|
|
201
|
+
});
|
|
202
|
+
//# sourceMappingURL=cli.mjs.map
|
|
203
|
+
//# sourceMappingURL=cli.mjs.map
|
package/dist/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"names":[],"mappings":"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","file":"cli.mjs","sourcesContent":["/**\n * agentproto-secrets — a small CLI over the seal primitive + provision flow.\n *\n * agentproto-secrets keygen mint a sealing keypair (JSON)\n * agentproto-secrets seal --pubkey <b64> [src] seal a value -> sealed blob\n * agentproto-secrets unseal --privkey <b64> [blob] open a sealed blob -> plaintext\n * agentproto-secrets provision --provider P --method M \\\n * --seal-key-url URL --install-url URL [--header 'K: V']... [src]\n * seal a local credential and\n * install it into a vault,\n * carrying only ciphertext\n *\n * credential source [src]: --from-file <path> [--json-path a.b.c] | --from-env <VAR>\n *\n * Vendor-neutral: `provision` takes the server's URLs + auth headers as flags.\n * The plaintext credential is read, sealed, and sent — it is never printed.\n */\n\nimport {\n generateSealKeyPair,\n sealKeyId,\n seal,\n unseal,\n} from \"./seal/index.js\"\nimport {\n provisionSealed,\n httpTarget,\n resolveCredential,\n type CredentialSource,\n} from \"./provision/index.js\"\nimport {\n resolveRecipeMethod,\n resolveSourceSpec,\n listRecipeIds,\n} from \"./provision/recipe/index.js\"\n\ninterface Args {\n positionals: string[]\n flags: Record<string, string>\n headers: string[]\n}\n\nfunction parse(argv: string[]): Args {\n const positionals: string[] = []\n const flags: Record<string, string> = {}\n const headers: string[] = []\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]!\n if (a.startsWith(\"--\")) {\n const eq = a.indexOf(\"=\")\n let key: string\n let val: string\n if (eq !== -1) {\n key = a.slice(2, eq)\n val = a.slice(eq + 1)\n } else {\n key = a.slice(2)\n const next = argv[i + 1]\n if (next !== undefined && !next.startsWith(\"--\")) {\n val = next\n i++\n } else {\n val = \"true\"\n }\n }\n if (key === \"header\") headers.push(val)\n else flags[key] = val\n } else {\n positionals.push(a)\n }\n }\n return { positionals, flags, headers }\n}\n\nfunction credentialSource(args: Args): CredentialSource {\n return {\n ...(args.flags[\"from-env\"] ? { fromEnv: args.flags[\"from-env\"] } : {}),\n ...(args.flags[\"from-file\"] ? { fromFile: args.flags[\"from-file\"] } : {}),\n ...(args.flags[\"json-path\"] ? { jsonPath: args.flags[\"json-path\"] } : {}),\n }\n}\n\n/** Read a value for seal: a positional, or a credential source. */\nasync function readValue(args: Args): Promise<string> {\n if (args.positionals[0]) return args.positionals[0]\n return resolveCredential(credentialSource(args))\n}\n\n/** Read one line from stdin (for a recipe's `prompt` credential source). */\nasync function promptStdin(prompt: string): Promise<string> {\n const { createInterface } = await import(\"node:readline/promises\")\n const rl = createInterface({ input: process.stdin, output: process.stderr })\n try {\n return (await rl.question(`${prompt}: `)).trim()\n } finally {\n rl.close()\n }\n}\n\n/**\n * Resolve the {methodId, credential, label} a provision installs. A recipe\n * (`--provider <id>`) supplies the auth-method id + where the credential lives,\n * so no source flags are needed; explicit `--from-file/--from-env` override the\n * recipe for ad-hoc / unknown providers (then `--method` is required).\n */\nasync function resolveProvision(\n args: Args,\n): Promise<{ methodId: string; credential: string; label?: string }> {\n const provider = args.flags.provider!\n const explicit = args.flags[\"from-file\"] || args.flags[\"from-env\"]\n if (explicit) {\n const methodId = args.flags.method\n if (!methodId)\n throw new Error(\n \"--method is required when passing an explicit --from-file/--from-env source\",\n )\n return {\n methodId,\n credential: await resolveCredential(credentialSource(args)),\n ...(args.flags.label ? { label: args.flags.label } : {}),\n }\n }\n const { recipe, method } = resolveRecipeMethod(provider, args.flags.method)\n const credential = await resolveSourceSpec(method.source, {\n promptImpl: promptStdin,\n })\n const label = args.flags.label ?? method.label ?? recipe.label\n return { methodId: method.id, credential, ...(label ? { label } : {}) }\n}\n\nfunction parseHeaders(raw: string[]): Record<string, string> {\n const out: Record<string, string> = {}\n for (const h of raw) {\n const idx = h.indexOf(\":\")\n if (idx === -1) continue\n out[h.slice(0, idx).trim()] = h.slice(idx + 1).trim()\n }\n return out\n}\n\nconst USAGE = `agentproto-secrets — seal secrets and install them into a vault\n\n keygen mint a sealing keypair (JSON to stdout)\n seal --pubkey <b64> [value|src] seal a value -> sealed blob (stdout)\n unseal --privkey <b64> [blob|src] open a sealed blob -> plaintext (stdout)\n provision --provider P --seal-key-url URL --install-url URL \\\\\n [--method M] [--header 'K: V']... [src]\n seal a local credential and install it,\n carrying only ciphertext. A known\n --provider resolves its method + source\n from a builtin recipe; --method picks a\n flavor; an explicit src overrides.\n\n src: --from-file <path> [--json-path a.b.c] | --from-env <VAR>\n builtin providers: ${listRecipeIds().join(\", \")}`\n\nasync function main(): Promise<number> {\n const [cmd, ...rest] = process.argv.slice(2)\n const args = parse(rest)\n\n switch (cmd) {\n case \"keygen\": {\n const kp = generateSealKeyPair()\n process.stdout.write(\n JSON.stringify(\n { keyId: sealKeyId(kp.publicKey), publicKey: kp.publicKey, privateKey: kp.privateKey },\n null,\n 2\n ) + \"\\n\"\n )\n return 0\n }\n\n case \"seal\": {\n const pubkey = args.flags.pubkey\n if (!pubkey) return fail(\"seal: --pubkey <b64> required\")\n const value = await readValue(args)\n process.stdout.write(seal(value, pubkey))\n return 0\n }\n\n case \"unseal\": {\n const privkey = args.flags.privkey\n if (!privkey) return fail(\"unseal: --privkey <b64> required\")\n const blob =\n args.positionals[0] ?? (await resolveCredential(credentialSource(args)))\n process.stdout.write(unseal(blob, privkey))\n return 0\n }\n\n case \"provision\": {\n const provider = args.flags.provider\n const sealKeyUrl = args.flags[\"seal-key-url\"]\n const installUrl = args.flags[\"install-url\"]\n if (!provider) return fail(\"provision: --provider required\")\n if (!sealKeyUrl || !installUrl)\n return fail(\"provision: --seal-key-url and --install-url required\")\n\n let methodId: string\n let credential: string\n let label: string | undefined\n try {\n ;({ methodId, credential, label } = await resolveProvision(args))\n } catch (err) {\n return fail(err instanceof Error ? err.message : String(err))\n }\n\n const target = httpTarget({\n sealKeyUrl,\n installUrl,\n headers: parseHeaders(args.headers),\n })\n try {\n const out = await provisionSealed({\n target,\n provider,\n methodId,\n credential,\n ...(label ? { label } : {}),\n })\n process.stdout.write(\n `sealed + installed ${provider} (${methodId})` +\n (out.secretId ? ` — secret ${out.secretId.slice(0, 8)}` : \"\") +\n ` [key ${out.keyId.slice(0, 8)}]\\n` +\n `the plaintext never left this machine; the server unsealed it.\\n`\n )\n return 0\n } catch (err) {\n return fail(err instanceof Error ? err.message : String(err))\n }\n }\n\n case \"-h\":\n case \"--help\":\n case \"help\":\n case undefined:\n process.stdout.write(USAGE + \"\\n\")\n return cmd ? 0 : 1\n\n default:\n return fail(`unknown command '${cmd}'\\n\\n${USAGE}`)\n }\n}\n\nfunction fail(message: string): number {\n process.stderr.write(`agentproto-secrets: ${message}\\n`)\n return 1\n}\n\nmain()\n .then(code => {\n process.exitCode = code\n })\n .catch(err => {\n process.stderr.write(\n `agentproto-secrets: ${err instanceof Error ? err.message : String(err)}\\n`\n )\n process.exitCode = 1\n })\n"]}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export { E as EgressSubstituteExposure, a as EnvExposure, F as FileExposure, S as SecretExposure, b as SecretExposureWrap, c as SecretExposureWrapContext, i as isExposureKind } from '../types-C2dZDHn7.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sigil + substitution engine for `$$SECRET[NAME]$$` placeholders.
|
|
5
|
+
*
|
|
6
|
+
* Used by hosts that implement egress-time secret substitution (the
|
|
7
|
+
* narrow case in `@agentproto/egress`'s cooperative mode), and any
|
|
8
|
+
* other consumer that wants to materialize secrets only at the
|
|
9
|
+
* boundary instead of in agent-process env.
|
|
10
|
+
*
|
|
11
|
+
* Sigil shape: `$$SECRET[<NAME>]$$` where `<NAME>` is `[A-Z_][A-Z0-9_]*`
|
|
12
|
+
* (matches typical env-var naming). The double-`$$` boundary on both
|
|
13
|
+
* sides plus the bracketed name means a single regex pass can extract
|
|
14
|
+
* names without false positives on strings that happen to contain `$$`
|
|
15
|
+
* elsewhere.
|
|
16
|
+
*
|
|
17
|
+
* # Security
|
|
18
|
+
* Resolved values get a sanity check before substitution:
|
|
19
|
+
* - Reject CR (`\r`), LF (`\n`), NUL (`\0`) — header injection
|
|
20
|
+
* - Reject empty strings — degenerate, almost always a bug
|
|
21
|
+
*
|
|
22
|
+
* Hosts that need stricter validation (length caps, charset narrowing)
|
|
23
|
+
* wrap the resolver passed in; this layer only enforces the universal
|
|
24
|
+
* "won't break the wire" guarantees.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Match a single `$$SECRET[NAME]$$` placeholder. Capture group 1 is
|
|
28
|
+
* the name. Use the global flag (`g`) when scanning a longer string;
|
|
29
|
+
* the constant here is non-global so callers can `.exec()` reliably.
|
|
30
|
+
*
|
|
31
|
+
* Exported because some consumers (e.g. the host-side install form)
|
|
32
|
+
* want to validate that a string contains no placeholder before
|
|
33
|
+
* accepting user input — they can re-derive a global variant cheaply.
|
|
34
|
+
*/
|
|
35
|
+
declare const SECRET_PLACEHOLDER_PATTERN: RegExp;
|
|
36
|
+
/** Resolver function the substitution engine calls per-name. Returns
|
|
37
|
+
* the secret value or `null` when the name doesn't resolve. The
|
|
38
|
+
* engine treats null as "no substitution" (placeholder remains in
|
|
39
|
+
* the output). Hosts that prefer hard-fail-on-miss should throw from
|
|
40
|
+
* the resolver; the engine propagates. */
|
|
41
|
+
type SecretResolver = (name: string) => Promise<string | null> | string | null;
|
|
42
|
+
/** Outcome of a single substitution call. `replacements` includes one
|
|
43
|
+
* entry per matched placeholder, in the order they appeared. Useful
|
|
44
|
+
* for audit logs ("this call substituted OPENAI_API_KEY"). */
|
|
45
|
+
interface SubstituteResult {
|
|
46
|
+
output: string;
|
|
47
|
+
replacements: SubstitutionRecord[];
|
|
48
|
+
}
|
|
49
|
+
interface SubstitutionRecord {
|
|
50
|
+
name: string;
|
|
51
|
+
/** True when the resolver returned a non-null value. False when the
|
|
52
|
+
* resolver returned null (placeholder kept verbatim). */
|
|
53
|
+
resolved: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Validate a resolved secret value before it's substituted. Throws
|
|
57
|
+
* with a stable error code so hosts can map the failure to a 4xx
|
|
58
|
+
* cleanly. Exported so resolvers that pre-fetch can run the same
|
|
59
|
+
* check before handing values back.
|
|
60
|
+
*/
|
|
61
|
+
declare function assertSafeSecretValue(name: string, value: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* Substitute every `$$SECRET[NAME]$$` placeholder in `input` using
|
|
64
|
+
* `resolver`. Returns the rewritten string + the list of names that
|
|
65
|
+
* were touched (for audit).
|
|
66
|
+
*
|
|
67
|
+
* Async because resolvers usually decrypt or hit a vault. Even when
|
|
68
|
+
* the resolver is synchronous, the result is a Promise — callers that
|
|
69
|
+
* want the sync path can `await` it cheaply since the underlying work
|
|
70
|
+
* is microtask-scheduled.
|
|
71
|
+
*
|
|
72
|
+
* Whole-string matches are NOT enforced — the placeholder may appear
|
|
73
|
+
* anywhere inside the input (`Bearer $$SECRET[X]$$` is the common
|
|
74
|
+
* case for HTTP Authorization headers). Hosts that want stricter
|
|
75
|
+
* placement (e.g. body-substitution gated to specific JSON paths)
|
|
76
|
+
* should pre-narrow the input before calling here.
|
|
77
|
+
*/
|
|
78
|
+
declare function substituteSecrets(input: string, resolver: SecretResolver): Promise<SubstituteResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Convenience: format a placeholder string for a given name.
|
|
81
|
+
* `formatPlaceholder("OPENAI_API_KEY") === "$$SECRET[OPENAI_API_KEY]$$"`
|
|
82
|
+
*
|
|
83
|
+
* Throws on invalid names so a bug in the host can't accidentally
|
|
84
|
+
* inject a placeholder that won't round-trip through the regex.
|
|
85
|
+
*/
|
|
86
|
+
declare function formatPlaceholder(name: string): string;
|
|
87
|
+
/** Stable error class — hosts catch by `.code` to map to user-facing
|
|
88
|
+
* responses (typically 400 / 403 / 500). */
|
|
89
|
+
declare class SecretSubstitutionError extends Error {
|
|
90
|
+
readonly code: string;
|
|
91
|
+
constructor(code: string, message: string);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { SECRET_PLACEHOLDER_PATTERN, type SecretResolver, SecretSubstitutionError, type SubstituteResult, type SubstitutionRecord, assertSafeSecretValue, formatPlaceholder, substituteSecrets };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @agentproto/secrets v0.1.0-alpha
|
|
3
|
+
* AIP-19 SECRETS.md `defineSecrets` reference implementation.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// src/exposure/types.ts
|
|
7
|
+
function isExposureKind(exposure, kind) {
|
|
8
|
+
return exposure.kind === kind;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/exposure/substitute.ts
|
|
12
|
+
var SECRET_PLACEHOLDER_PATTERN = /\$\$SECRET\[([A-Z_][A-Z0-9_]*)\]\$\$/;
|
|
13
|
+
var SECRET_PLACEHOLDER_PATTERN_G = new RegExp(
|
|
14
|
+
SECRET_PLACEHOLDER_PATTERN.source,
|
|
15
|
+
"g"
|
|
16
|
+
);
|
|
17
|
+
function assertSafeSecretValue(name, value) {
|
|
18
|
+
if (value.length === 0) {
|
|
19
|
+
throw new SecretSubstitutionError(
|
|
20
|
+
"secret_value_empty",
|
|
21
|
+
`Resolved value for '${name}' is empty.`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
if (/[\r\n\0]/.test(value)) {
|
|
25
|
+
throw new SecretSubstitutionError(
|
|
26
|
+
"secret_value_unsafe",
|
|
27
|
+
`Resolved value for '${name}' contains forbidden control characters (CR/LF/NUL).`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function substituteSecrets(input, resolver) {
|
|
32
|
+
const replacements = [];
|
|
33
|
+
const matches = [];
|
|
34
|
+
let m;
|
|
35
|
+
const scan = new RegExp(
|
|
36
|
+
SECRET_PLACEHOLDER_PATTERN_G.source,
|
|
37
|
+
SECRET_PLACEHOLDER_PATTERN_G.flags
|
|
38
|
+
);
|
|
39
|
+
while ((m = scan.exec(input)) !== null) {
|
|
40
|
+
matches.push({
|
|
41
|
+
name: m[1],
|
|
42
|
+
start: m.index,
|
|
43
|
+
end: m.index + m[0].length
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (matches.length === 0) {
|
|
47
|
+
return { output: input, replacements: [] };
|
|
48
|
+
}
|
|
49
|
+
const uniqueNames = Array.from(new Set(matches.map((x) => x.name)));
|
|
50
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
51
|
+
await Promise.all(
|
|
52
|
+
uniqueNames.map(async (name) => {
|
|
53
|
+
const value = await resolver(name);
|
|
54
|
+
if (value !== null) assertSafeSecretValue(name, value);
|
|
55
|
+
resolved.set(name, value);
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
let output = input;
|
|
59
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
60
|
+
const { name, start, end } = matches[i];
|
|
61
|
+
const value = resolved.get(name) ?? null;
|
|
62
|
+
if (value !== null) {
|
|
63
|
+
output = output.slice(0, start) + value + output.slice(end);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const { name } of matches) {
|
|
67
|
+
replacements.push({ name, resolved: resolved.get(name) !== null });
|
|
68
|
+
}
|
|
69
|
+
return { output, replacements };
|
|
70
|
+
}
|
|
71
|
+
function formatPlaceholder(name) {
|
|
72
|
+
if (!/^[A-Z_][A-Z0-9_]*$/.test(name)) {
|
|
73
|
+
throw new SecretSubstitutionError(
|
|
74
|
+
"invalid_placeholder_name",
|
|
75
|
+
`Placeholder name '${name}' must match [A-Z_][A-Z0-9_]*`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return `$$SECRET[${name}]$$`;
|
|
79
|
+
}
|
|
80
|
+
var SecretSubstitutionError = class extends Error {
|
|
81
|
+
code;
|
|
82
|
+
constructor(code, message) {
|
|
83
|
+
super(message);
|
|
84
|
+
this.code = code;
|
|
85
|
+
this.name = "SecretSubstitutionError";
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export { SECRET_PLACEHOLDER_PATTERN, SecretSubstitutionError, assertSafeSecretValue, formatPlaceholder, isExposureKind, substituteSecrets };
|
|
90
|
+
//# sourceMappingURL=index.mjs.map
|
|
91
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/exposure/types.ts","../../src/exposure/substitute.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * SecretExposure — describes HOW a secret value reaches the runtime\n * that needs it (env var, config file, egress placeholder, future\n * MCP-header injection, future HTTP-bearer routing, …).\n *\n * Distinct from `SecretEntry` (AIP-19), which describes the secret's\n * *declaration* (slug, kind, access policy). Exposure is a *runtime*\n * concern — same secret might be exposed multiple ways across hosts.\n *\n * Discriminated union by `kind` so each new mechanism slots in as one\n * variant rather than another top-level field on whatever catalog the\n * host uses to map secrets onto agent runtimes.\n *\n * # Why this lives here (not in @agentproto/egress)\n * The substitution sigil + the \"what surfaces does this secret have\"\n * vocabulary are properties of the secret itself — they describe its\n * exposure surface independent of WHO consumes it. Egress is one\n * consumer; future ones (MCP servers, HTTP relays) are others.\n *\n * The egress *gating* (mode registry, proxy core) lives in\n * @agentproto/egress because that's a different concern (network-\n * boundary control). Egress imports from here.\n */\n\n/** Function applied to the seed value before write. Receives the raw\n * secret value (or empty string when `field` is omitted) plus an\n * optional context bag the host populates. Returns the file body. */\nexport type SecretExposureWrap = (\n value: string,\n ctx?: SecretExposureWrapContext\n) => string\n\n/** Generic context passed to `wrap` callbacks. Hosts populate fields\n * they have; wraps that don't care ignore the second arg entirely.\n * Kept open-ended (string-keyed) so hosts can attach app-specific\n * data without forcing a schema change here. */\nexport interface SecretExposureWrapContext {\n [key: string]: unknown\n}\n\n/** Inject the secret value as a process env var inside the runtime. */\nexport interface EnvExposure {\n kind: \"env\"\n /** Env var name as the runtime sees it. */\n name: string\n /** Field on the secret/connector settings whose value seeds the env.\n * Required for env exposures — env vars don't have a `wrap` path. */\n field: string\n}\n\n/** Drop a file inside the runtime's filesystem before any process\n * starts. Path uses `~` expansion against the runtime's exec user\n * home (host-resolved). */\nexport interface FileExposure {\n kind: \"file\"\n /** Absolute path or `~`-relative path inside the runtime sandbox. */\n path: string\n /** Field on the secret/connector settings whose value seeds the file\n * body. Omit when `wrap` produces synthetic content (e.g. claude's\n * `.claude.json` migration flags don't depend on a token). */\n field?: string\n /** POSIX file mode. Default `0o600`. */\n mode?: number\n /** Transform applied before write — see SecretExposureWrap. */\n wrap?: SecretExposureWrap\n}\n\n/**\n * Mark this secret as eligible for substitution at the egress proxy.\n * Agents see only `$$SECRET[<placeholderName>]$$` placeholders; the\n * proxy substitutes the real value at the network boundary.\n *\n * The actual proxy + sigil regex + substitution engine live in\n * `@agentproto/egress` — this exposure variant just declares the\n * intent + the per-secret defaults the host install handler stamps.\n */\nexport interface EgressSubstituteExposure {\n kind: \"egress-substitute\"\n /** The token agents reference in `$$SECRET[NAME]$$`. By convention\n * this matches the env-exposure name on the same secret, but it\n * doesn't have to — placeholders and env vars are independent. */\n placeholderName: string\n /** Default value for the per-secret allowlist flag stamped at install\n * time. UI may offer a per-install override. When false, the secret\n * is declared egress-aware but rejected by the resolver until an\n * admin opts in. */\n allowedByDefault: boolean\n /** Audit / UX hint — the providers this secret is *meant* for. NOT\n * enforced at egress (proxy substitutes any matching placeholder\n * regardless of upstream); the field exists so UIs can surface\n * \"this secret is for OpenAI\" without parsing the slug. */\n intendedProviders?: string[]\n}\n\n/**\n * Discriminated union of all exposure mechanisms. Add new variants here\n * as new exposure surfaces are added (MCP-header, HTTP-bearer, etc.) —\n * each adds one variant, one switch case in consumers.\n */\nexport type SecretExposure =\n | EnvExposure\n | FileExposure\n | EgressSubstituteExposure\n\n/** Type guard — convenient for filtering an exposures array by kind. */\nexport function isExposureKind<K extends SecretExposure[\"kind\"]>(\n exposure: SecretExposure,\n kind: K\n): exposure is Extract<SecretExposure, { kind: K }> {\n return exposure.kind === kind\n}\n","/**\n * Sigil + substitution engine for `$$SECRET[NAME]$$` placeholders.\n *\n * Used by hosts that implement egress-time secret substitution (the\n * narrow case in `@agentproto/egress`'s cooperative mode), and any\n * other consumer that wants to materialize secrets only at the\n * boundary instead of in agent-process env.\n *\n * Sigil shape: `$$SECRET[<NAME>]$$` where `<NAME>` is `[A-Z_][A-Z0-9_]*`\n * (matches typical env-var naming). The double-`$$` boundary on both\n * sides plus the bracketed name means a single regex pass can extract\n * names without false positives on strings that happen to contain `$$`\n * elsewhere.\n *\n * # Security\n * Resolved values get a sanity check before substitution:\n * - Reject CR (`\\r`), LF (`\\n`), NUL (`\\0`) — header injection\n * - Reject empty strings — degenerate, almost always a bug\n *\n * Hosts that need stricter validation (length caps, charset narrowing)\n * wrap the resolver passed in; this layer only enforces the universal\n * \"won't break the wire\" guarantees.\n */\n\n/**\n * Match a single `$$SECRET[NAME]$$` placeholder. Capture group 1 is\n * the name. Use the global flag (`g`) when scanning a longer string;\n * the constant here is non-global so callers can `.exec()` reliably.\n *\n * Exported because some consumers (e.g. the host-side install form)\n * want to validate that a string contains no placeholder before\n * accepting user input — they can re-derive a global variant cheaply.\n */\nexport const SECRET_PLACEHOLDER_PATTERN = /\\$\\$SECRET\\[([A-Z_][A-Z0-9_]*)\\]\\$\\$/\n\n/** Global variant — used internally for scanning + substitution. */\nconst SECRET_PLACEHOLDER_PATTERN_G = new RegExp(\n SECRET_PLACEHOLDER_PATTERN.source,\n \"g\"\n)\n\n/** Resolver function the substitution engine calls per-name. Returns\n * the secret value or `null` when the name doesn't resolve. The\n * engine treats null as \"no substitution\" (placeholder remains in\n * the output). Hosts that prefer hard-fail-on-miss should throw from\n * the resolver; the engine propagates. */\nexport type SecretResolver = (name: string) => Promise<string | null> | string | null\n\n/** Outcome of a single substitution call. `replacements` includes one\n * entry per matched placeholder, in the order they appeared. Useful\n * for audit logs (\"this call substituted OPENAI_API_KEY\"). */\nexport interface SubstituteResult {\n output: string\n replacements: SubstitutionRecord[]\n}\n\nexport interface SubstitutionRecord {\n name: string\n /** True when the resolver returned a non-null value. False when the\n * resolver returned null (placeholder kept verbatim). */\n resolved: boolean\n}\n\n/**\n * Validate a resolved secret value before it's substituted. Throws\n * with a stable error code so hosts can map the failure to a 4xx\n * cleanly. Exported so resolvers that pre-fetch can run the same\n * check before handing values back.\n */\nexport function assertSafeSecretValue(name: string, value: string): void {\n if (value.length === 0) {\n throw new SecretSubstitutionError(\n \"secret_value_empty\",\n `Resolved value for '${name}' is empty.`\n )\n }\n // CR / LF / NUL break header framing and stdio respectively. Other\n // control chars are allowed — some legitimate tokens contain them\n // (rare, but a base64 with `\\t` would be wrongly rejected by a\n // broader filter).\n if (/[\\r\\n\\0]/.test(value)) {\n throw new SecretSubstitutionError(\n \"secret_value_unsafe\",\n `Resolved value for '${name}' contains forbidden control characters (CR/LF/NUL).`\n )\n }\n}\n\n/**\n * Substitute every `$$SECRET[NAME]$$` placeholder in `input` using\n * `resolver`. Returns the rewritten string + the list of names that\n * were touched (for audit).\n *\n * Async because resolvers usually decrypt or hit a vault. Even when\n * the resolver is synchronous, the result is a Promise — callers that\n * want the sync path can `await` it cheaply since the underlying work\n * is microtask-scheduled.\n *\n * Whole-string matches are NOT enforced — the placeholder may appear\n * anywhere inside the input (`Bearer $$SECRET[X]$$` is the common\n * case for HTTP Authorization headers). Hosts that want stricter\n * placement (e.g. body-substitution gated to specific JSON paths)\n * should pre-narrow the input before calling here.\n */\nexport async function substituteSecrets(\n input: string,\n resolver: SecretResolver\n): Promise<SubstituteResult> {\n const replacements: SubstitutionRecord[] = []\n // Two-pass: first find all matches + resolve concurrently, then\n // rewrite. Lets parallel resolvers (multiple secrets) overlap their\n // decrypt cost.\n const matches: Array<{ name: string; start: number; end: number }> = []\n let m: RegExpExecArray | null\n const scan = new RegExp(\n SECRET_PLACEHOLDER_PATTERN_G.source,\n SECRET_PLACEHOLDER_PATTERN_G.flags\n )\n while ((m = scan.exec(input)) !== null) {\n matches.push({\n name: m[1]!,\n start: m.index,\n end: m.index + m[0].length,\n })\n }\n if (matches.length === 0) {\n return { output: input, replacements: [] }\n }\n\n // Dedupe so multiple placeholders for the same name only resolve once.\n const uniqueNames = Array.from(new Set(matches.map((x) => x.name)))\n const resolved = new Map<string, string | null>()\n await Promise.all(\n uniqueNames.map(async (name) => {\n const value = await resolver(name)\n if (value !== null) assertSafeSecretValue(name, value)\n resolved.set(name, value)\n })\n )\n\n // Rewrite right-to-left so earlier substitutions don't shift\n // subsequent indices.\n let output = input\n for (let i = matches.length - 1; i >= 0; i--) {\n const { name, start, end } = matches[i]!\n const value = resolved.get(name) ?? null\n if (value !== null) {\n output = output.slice(0, start) + value + output.slice(end)\n }\n // Order-preserving record for audit (forward order).\n }\n for (const { name } of matches) {\n replacements.push({ name, resolved: resolved.get(name) !== null })\n }\n\n return { output, replacements }\n}\n\n/**\n * Convenience: format a placeholder string for a given name.\n * `formatPlaceholder(\"OPENAI_API_KEY\") === \"$$SECRET[OPENAI_API_KEY]$$\"`\n *\n * Throws on invalid names so a bug in the host can't accidentally\n * inject a placeholder that won't round-trip through the regex.\n */\nexport function formatPlaceholder(name: string): string {\n if (!/^[A-Z_][A-Z0-9_]*$/.test(name)) {\n throw new SecretSubstitutionError(\n \"invalid_placeholder_name\",\n `Placeholder name '${name}' must match [A-Z_][A-Z0-9_]*`\n )\n }\n return `$$SECRET[${name}]$$`\n}\n\n/** Stable error class — hosts catch by `.code` to map to user-facing\n * responses (typically 400 / 403 / 500). */\nexport class SecretSubstitutionError extends Error {\n readonly code: string\n constructor(code: string, message: string) {\n super(message)\n this.code = code\n this.name = \"SecretSubstitutionError\"\n }\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { S as SecretsDefinition } from './types-Clav8IDA.js';
|
|
2
|
+
export { a as SecretsHandle } from './types-Clav8IDA.js';
|
|
3
|
+
import './types-C2dZDHn7.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* AIP-19 reference implementation of `defineSecrets`.
|
|
7
|
+
*
|
|
8
|
+
* Built on `createDoctype` so the cross-AIP invariants (id pattern,
|
|
9
|
+
* description length, top-level freeze, "defineSecrets (AIP-19): …"
|
|
10
|
+
* error prefix) run uniformly with every other AIP defineX.
|
|
11
|
+
*
|
|
12
|
+
* Field-level validation runs the schema-derived zod from
|
|
13
|
+
* `./schema.ts` against the input. Same source of truth as the .md
|
|
14
|
+
* path uses (`parseSecretsManifest`), so a malformed TS-authored
|
|
15
|
+
* definition fails with the same diagnostic as a malformed manifest.
|
|
16
|
+
* Cross-field rules go in `validate(def)` after the zod check.
|
|
17
|
+
*
|
|
18
|
+
* Identity / description extractors detected from the JSON Schema:
|
|
19
|
+
* readIdentity: def.id
|
|
20
|
+
* readDescription: skipped (no string-y required field detected).
|
|
21
|
+
*/
|
|
22
|
+
declare const defineSecrets: (def: SecretsDefinition) => Readonly<SecretsDefinition>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @agentproto/secrets — AIP-19 SECRETS.md `defineSecrets` reference impl.
|
|
26
|
+
*
|
|
27
|
+
* A workspace-level manifest format for declaring secret slugs, their purpose, access grants, and audit metadata — without ever storing the values themselves. Hosts resolve slugs against a real vault at reveal time.
|
|
28
|
+
*
|
|
29
|
+
* Spec: https://agentproto.sh/docs/aip-19
|
|
30
|
+
*
|
|
31
|
+
* Authoring paths:
|
|
32
|
+
* - TS: `defineSecrets({...})` → `SecretsHandle`
|
|
33
|
+
* - MD: `parseSecretsManifest(src) → secretsFromManifest({...})` → `SecretsHandle`
|
|
34
|
+
*/
|
|
35
|
+
declare const SPEC_NAME: "agentsecrets/v1";
|
|
36
|
+
declare const SPEC_VERSION: "1.0.0-alpha";
|
|
37
|
+
|
|
38
|
+
export { SPEC_NAME, SPEC_VERSION, SecretsDefinition, defineSecrets };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { defineSecrets } from './chunk-SFLTQZQB.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @agentproto/secrets v0.1.0-alpha
|
|
5
|
+
* AIP-19 SECRETS.md `defineSecrets` reference implementation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// src/index.ts
|
|
9
|
+
var SPEC_NAME = "agentsecrets/v1";
|
|
10
|
+
var SPEC_VERSION = "1.0.0-alpha";
|
|
11
|
+
|
|
12
|
+
export { SPEC_NAME, SPEC_VERSION };
|
|
13
|
+
//# sourceMappingURL=index.mjs.map
|
|
14
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;AAYO,IAAM,SAAA,GAAY;AAClB,IAAM,YAAA,GAAe","file":"index.mjs","sourcesContent":["/**\n * @agentproto/secrets — AIP-19 SECRETS.md `defineSecrets` reference impl.\n *\n * A workspace-level manifest format for declaring secret slugs, their purpose, access grants, and audit metadata — without ever storing the values themselves. Hosts resolve slugs against a real vault at reveal time.\n *\n * Spec: https://agentproto.sh/docs/aip-19\n *\n * Authoring paths:\n * - TS: `defineSecrets({...})` → `SecretsHandle`\n * - MD: `parseSecretsManifest(src) → secretsFromManifest({...})` → `SecretsHandle`\n */\n\nexport const SPEC_NAME = \"agentsecrets/v1\" as const\nexport const SPEC_VERSION = \"1.0.0-alpha\" as const\n\nexport { defineSecrets } from \"./define-secrets.js\"\nexport type { SecretsDefinition, SecretsHandle } from \"./types.js\"\n"]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { a as SecretsHandle } from '../types-Clav8IDA.js';
|
|
3
|
+
import '../types-C2dZDHn7.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* AIP-19 SECRETS.md frontmatter zod schema.
|
|
7
|
+
*
|
|
8
|
+
* Generated from `resources/aip-19/draft/SECRETS.schema.json` via
|
|
9
|
+
* json-schema-to-zod. Imported by both `define-secrets.ts` (TS path
|
|
10
|
+
* validation) and `manifest/index.ts` (.md path validation) so every
|
|
11
|
+
* field-level constraint runs in both authoring paths from a single
|
|
12
|
+
* source of truth — re-run scaffold-aip to refresh after spec changes.
|
|
13
|
+
*
|
|
14
|
+
* Cross-field rules (if/then/allOf in JSON Schema) don't translate
|
|
15
|
+
* cleanly and live in `define-secrets.ts`'s `validate(def)` instead.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
declare const secretsFrontmatterSchema: z.ZodObject<{
|
|
19
|
+
secrets: z.ZodArray<z.ZodAny>;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
type SecretsFrontmatter = z.infer<typeof secretsFrontmatterSchema>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* AIP-19 SECRETS.md sidecar parser + manifest-to-handle constructor.
|
|
25
|
+
*
|
|
26
|
+
* Mirror of `@agentproto/tool/manifest` and `@agentproto/driver/manifest`:
|
|
27
|
+
* the .md provides metadata; the TS module supplies any spec-specific
|
|
28
|
+
* runtime bits (schemas, execute bodies, …) that can't live in
|
|
29
|
+
* frontmatter. Both inputs end up in `defineSecrets` so the cross-AIP
|
|
30
|
+
* invariants run uniformly.
|
|
31
|
+
*
|
|
32
|
+
*
|
|
33
|
+
* The frontmatter zod schema below was generated from
|
|
34
|
+
* `resources/aip-19/draft/SECRETS.schema.json` via json-schema-to-zod.
|
|
35
|
+
* Re-run scaffold-aip to refresh after spec changes (or hand-tune
|
|
36
|
+
* any constraint the converter doesn't capture cleanly).
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
interface SecretsManifest {
|
|
40
|
+
frontmatter: SecretsFrontmatter;
|
|
41
|
+
body: string;
|
|
42
|
+
}
|
|
43
|
+
declare function parseSecretsManifest(source: string): SecretsManifest;
|
|
44
|
+
declare function secretsFromManifest(manifest: SecretsManifest): SecretsHandle;
|
|
45
|
+
|
|
46
|
+
export { type SecretsFrontmatter, type SecretsManifest, parseSecretsManifest, secretsFromManifest, secretsFrontmatterSchema };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { secretsFrontmatterSchema, defineSecrets } from '../chunk-SFLTQZQB.mjs';
|
|
2
|
+
export { secretsFrontmatterSchema } from '../chunk-SFLTQZQB.mjs';
|
|
3
|
+
import matter from 'gray-matter';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @agentproto/secrets v0.1.0-alpha
|
|
7
|
+
* AIP-19 SECRETS.md `defineSecrets` reference implementation.
|
|
8
|
+
*/
|
|
9
|
+
function parseSecretsManifest(source) {
|
|
10
|
+
const parsed = matter(source);
|
|
11
|
+
if (Object.keys(parsed.data).length === 0) {
|
|
12
|
+
throw new Error("parseSecretsManifest: missing or empty frontmatter");
|
|
13
|
+
}
|
|
14
|
+
const result = secretsFrontmatterSchema.safeParse(parsed.data);
|
|
15
|
+
if (!result.success) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`parseSecretsManifest: invalid frontmatter \u2014 ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return { frontmatter: result.data, body: parsed.content };
|
|
21
|
+
}
|
|
22
|
+
function secretsFromManifest(manifest) {
|
|
23
|
+
return defineSecrets(manifest.frontmatter);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { parseSecretsManifest, secretsFromManifest };
|
|
27
|
+
//# sourceMappingURL=index.mjs.map
|
|
28
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/manifest/index.ts"],"names":[],"mappings":";;;;;;;;AA8BO,SAAS,qBAAqB,MAAA,EAAiC;AACpE,EAAA,MAAM,MAAA,GAAS,OAAO,MAAM,CAAA;AAC5B,EAAA,IAAI,OAAO,IAAA,CAAK,MAAA,CAAO,IAAI,CAAA,CAAE,WAAW,CAAA,EAAG;AACzC,IAAA,MAAM,IAAI,MAAM,oDAAoD,CAAA;AAAA,EACtE;AACA,EAAA,MAAM,MAAA,GAAS,wBAAA,CAAyB,SAAA,CAAU,MAAA,CAAO,IAAI,CAAA;AAC7D,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,iDAAA,EAA+C,OAAO,KAAA,CAAM,MAAA,CACzD,IAAI,CAAC,CAAA,KAAM,GAAG,CAAA,CAAE,IAAA,CAAK,KAAK,GAAG,CAAC,KAAK,CAAA,CAAE,OAAO,EAAE,CAAA,CAC9C,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,KACf;AAAA,EACF;AACA,EAAA,OAAO,EAAE,WAAA,EAAa,MAAA,CAAO,IAAA,EAAM,IAAA,EAAM,OAAO,OAAA,EAAQ;AAC1D;AAEO,SAAS,oBAAoB,QAAA,EAA0C;AAK5E,EAAA,OAAO,aAAA,CAAc,SAAS,WAA2C,CAAA;AAC3E","file":"index.mjs","sourcesContent":["/**\n * AIP-19 SECRETS.md sidecar parser + manifest-to-handle constructor.\n *\n * Mirror of `@agentproto/tool/manifest` and `@agentproto/driver/manifest`:\n * the .md provides metadata; the TS module supplies any spec-specific\n * runtime bits (schemas, execute bodies, …) that can't live in\n * frontmatter. Both inputs end up in `defineSecrets` so the cross-AIP\n * invariants run uniformly.\n *\n *\n * The frontmatter zod schema below was generated from\n * `resources/aip-19/draft/SECRETS.schema.json` via json-schema-to-zod.\n * Re-run scaffold-aip to refresh after spec changes (or hand-tune\n * any constraint the converter doesn't capture cleanly).\n */\n\nimport matter from \"gray-matter\"\nimport { secretsFrontmatterSchema, type SecretsFrontmatter } from \"../schema.js\"\nimport { defineSecrets } from \"../define-secrets.js\"\nimport type { SecretsDefinition, SecretsHandle } from \"../types.js\"\n\n// Re-export so consumers can import the schema + inferred type either\n// from \"@@agentproto/secrets/manifest\" or directly from \"@@agentproto/secrets/schema\".\nexport { secretsFrontmatterSchema, type SecretsFrontmatter }\n\nexport interface SecretsManifest {\n frontmatter: SecretsFrontmatter\n body: string\n}\n\nexport function parseSecretsManifest(source: string): SecretsManifest {\n const parsed = matter(source)\n if (Object.keys(parsed.data).length === 0) {\n throw new Error(\"parseSecretsManifest: missing or empty frontmatter\")\n }\n const result = secretsFrontmatterSchema.safeParse(parsed.data)\n if (!result.success) {\n throw new Error(\n `parseSecretsManifest: invalid frontmatter — ${result.error.issues\n .map((i) => `${i.path.join(\".\")}: ${i.message}`)\n .join(\"; \")}`,\n )\n }\n return { frontmatter: result.data, body: parsed.content }\n}\n\nexport function secretsFromManifest(manifest: SecretsManifest): SecretsHandle {\n // The zod-validated frontmatter is structurally compatible with\n // SecretsDefinition; the cast pins the typing once the manifest\n // schema and the TS interface diverge (e.g. handle has frozen fields\n // a literal config doesn't carry yet).\n return defineSecrets(manifest.frontmatter as unknown as SecretsDefinition)\n}\n"]}
|