agentfootprint 9.7.0 → 9.9.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/dist/adapters/identity/vault.js +336 -0
- package/dist/adapters/identity/vault.js.map +1 -0
- package/dist/adapters/observability/audit.js +2 -28
- package/dist/adapters/observability/audit.js.map +1 -1
- package/dist/adapters/observability/file.js +307 -0
- package/dist/adapters/observability/file.js.map +1 -0
- package/dist/adapters/observability/githubBugReporter.js +461 -0
- package/dist/adapters/observability/githubBugReporter.js.map +1 -0
- package/dist/adapters/observability/githubDeviceSignIn.js +263 -0
- package/dist/adapters/observability/githubDeviceSignIn.js.map +1 -0
- package/dist/esm/adapters/identity/vault.d.ts +146 -0
- package/dist/esm/adapters/identity/vault.js +332 -0
- package/dist/esm/adapters/identity/vault.js.map +1 -0
- package/dist/esm/adapters/observability/audit.js +1 -27
- package/dist/esm/adapters/observability/audit.js.map +1 -1
- package/dist/esm/adapters/observability/file.d.ts +145 -0
- package/dist/esm/adapters/observability/file.js +303 -0
- package/dist/esm/adapters/observability/file.js.map +1 -0
- package/dist/esm/adapters/observability/githubBugReporter.d.ts +154 -0
- package/dist/esm/adapters/observability/githubBugReporter.js +457 -0
- package/dist/esm/adapters/observability/githubBugReporter.js.map +1 -0
- package/dist/esm/adapters/observability/githubDeviceSignIn.d.ts +131 -0
- package/dist/esm/adapters/observability/githubDeviceSignIn.js +259 -0
- package/dist/esm/adapters/observability/githubDeviceSignIn.js.map +1 -0
- package/dist/esm/identity.d.ts +1 -0
- package/dist/esm/identity.js +4 -0
- package/dist/esm/identity.js.map +1 -1
- package/dist/esm/lib/bug-report/build.d.ts +103 -0
- package/dist/esm/lib/bug-report/build.js +648 -0
- package/dist/esm/lib/bug-report/build.js.map +1 -0
- package/dist/esm/lib/bug-report/index.d.ts +14 -0
- package/dist/esm/lib/bug-report/index.js +13 -0
- package/dist/esm/lib/bug-report/index.js.map +1 -0
- package/dist/esm/lib/bug-report/transcript.d.ts +61 -0
- package/dist/esm/lib/bug-report/transcript.js +124 -0
- package/dist/esm/lib/bug-report/transcript.js.map +1 -0
- package/dist/esm/lib/bug-report/types.d.ts +206 -0
- package/dist/esm/lib/bug-report/types.js +12 -0
- package/dist/esm/lib/bug-report/types.js.map +1 -0
- package/dist/esm/lib/bug-report/zip.d.ts +71 -0
- package/dist/esm/lib/bug-report/zip.js +202 -0
- package/dist/esm/lib/bug-report/zip.js.map +1 -0
- package/dist/esm/lib/libraryVersion.d.ts +23 -0
- package/dist/esm/lib/libraryVersion.js +46 -0
- package/dist/esm/lib/libraryVersion.js.map +1 -0
- package/dist/esm/lib/trace-toolpack/openRecording.d.ts +17 -0
- package/dist/esm/lib/trace-toolpack/openRecording.js +8 -2
- package/dist/esm/lib/trace-toolpack/openRecording.js.map +1 -1
- package/dist/esm/observability-providers.d.ts +6 -1
- package/dist/esm/observability-providers.js +16 -1
- package/dist/esm/observability-providers.js.map +1 -1
- package/dist/esm/observe.d.ts +1 -0
- package/dist/esm/observe.js +6 -0
- package/dist/esm/observe.js.map +1 -1
- package/dist/identity.js +6 -1
- package/dist/identity.js.map +1 -1
- package/dist/lib/bug-report/build.js +656 -0
- package/dist/lib/bug-report/build.js.map +1 -0
- package/dist/lib/bug-report/index.js +18 -0
- package/dist/lib/bug-report/index.js.map +1 -0
- package/dist/lib/bug-report/transcript.js +128 -0
- package/dist/lib/bug-report/transcript.js.map +1 -0
- package/dist/lib/bug-report/types.js +13 -0
- package/dist/lib/bug-report/types.js.map +1 -0
- package/dist/lib/bug-report/zip.js +207 -0
- package/dist/lib/bug-report/zip.js.map +1 -0
- package/dist/lib/libraryVersion.js +51 -0
- package/dist/lib/libraryVersion.js.map +1 -0
- package/dist/lib/trace-toolpack/openRecording.js +9 -2
- package/dist/lib/trace-toolpack/openRecording.js.map +1 -1
- package/dist/observability-providers.js +20 -2
- package/dist/observability-providers.js.map +1 -1
- package/dist/observe.js +14 -6
- package/dist/observe.js.map +1 -1
- package/dist/types/adapters/identity/vault.d.ts +147 -0
- package/dist/types/adapters/identity/vault.d.ts.map +1 -0
- package/dist/types/adapters/observability/audit.d.ts.map +1 -1
- package/dist/types/adapters/observability/file.d.ts +146 -0
- package/dist/types/adapters/observability/file.d.ts.map +1 -0
- package/dist/types/adapters/observability/githubBugReporter.d.ts +155 -0
- package/dist/types/adapters/observability/githubBugReporter.d.ts.map +1 -0
- package/dist/types/adapters/observability/githubDeviceSignIn.d.ts +132 -0
- package/dist/types/adapters/observability/githubDeviceSignIn.d.ts.map +1 -0
- package/dist/types/identity.d.ts +1 -0
- package/dist/types/identity.d.ts.map +1 -1
- package/dist/types/lib/bug-report/build.d.ts +104 -0
- package/dist/types/lib/bug-report/build.d.ts.map +1 -0
- package/dist/types/lib/bug-report/index.d.ts +15 -0
- package/dist/types/lib/bug-report/index.d.ts.map +1 -0
- package/dist/types/lib/bug-report/transcript.d.ts +62 -0
- package/dist/types/lib/bug-report/transcript.d.ts.map +1 -0
- package/dist/types/lib/bug-report/types.d.ts +207 -0
- package/dist/types/lib/bug-report/types.d.ts.map +1 -0
- package/dist/types/lib/bug-report/zip.d.ts +72 -0
- package/dist/types/lib/bug-report/zip.d.ts.map +1 -0
- package/dist/types/lib/libraryVersion.d.ts +24 -0
- package/dist/types/lib/libraryVersion.d.ts.map +1 -0
- package/dist/types/lib/trace-toolpack/openRecording.d.ts +17 -0
- package/dist/types/lib/trace-toolpack/openRecording.d.ts.map +1 -1
- package/dist/types/observability-providers.d.ts +6 -1
- package/dist/types/observability-providers.d.ts.map +1 -1
- package/dist/types/observe.d.ts +1 -0
- package/dist/types/observe.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* githubDeviceSignIn — let the REPORTER sign in, so the issue is filed as them.
|
|
4
|
+
*
|
|
5
|
+
* import { githubDeviceSignIn } from 'agentfootprint/observe';
|
|
6
|
+
*
|
|
7
|
+
* const signIn = await githubDeviceSignIn({ clientId: 'Iv1.0123456789abcdef' });
|
|
8
|
+
* show(`Go to ${signIn.verificationUri} and enter ${signIn.userCode}`);
|
|
9
|
+
* const { token, login } = await signIn.completed; // resolves on authorize
|
|
10
|
+
*
|
|
11
|
+
* GitHub's OAuth **device flow**, spoken as three plain `fetch` calls and
|
|
12
|
+
* nothing else: request a code, show it to the human, poll until they approve.
|
|
13
|
+
* Zero dependencies, and it runs in a browser as well as on a server — there is
|
|
14
|
+
* no client secret in this flow, which is exactly why it is the one that works
|
|
15
|
+
* from a page.
|
|
16
|
+
*
|
|
17
|
+
* ## Why this exists beside a server token
|
|
18
|
+
*
|
|
19
|
+
* A server-side PAT files every report as the application. That is right for an
|
|
20
|
+
* automated "Report a problem" button: the app owns the repo, the app owns the
|
|
21
|
+
* token. It is wrong when the value is ATTRIBUTION — a field tester filing
|
|
22
|
+
* upstream should appear as themselves, so a maintainer can ask them a
|
|
23
|
+
* follow-up question and so their report counts as theirs.
|
|
24
|
+
*
|
|
25
|
+
* | | server PAT | device sign-in |
|
|
26
|
+
* |---|---|---|
|
|
27
|
+
* | Who the issue is from | the application | the reporter |
|
|
28
|
+
* | Where the token lives | server environment | the reporter's session, in memory |
|
|
29
|
+
* | Human steps | none | one: enter a code, approve |
|
|
30
|
+
* | Right for | in-app "report a problem" | field testers filing upstream |
|
|
31
|
+
*
|
|
32
|
+
* The token this returns is handed to {@link githubBugReporter} as `token`,
|
|
33
|
+
* with no special-casing anywhere: a token is a token.
|
|
34
|
+
*
|
|
35
|
+
* ## Keep it in memory, for the session only
|
|
36
|
+
*
|
|
37
|
+
* **Never `localStorage`, never a cookie, never a log line.** A device-flow
|
|
38
|
+
* token is a live credential for the account that approved it; persisting it in
|
|
39
|
+
* a browser turns one XSS into a lasting account compromise. Hold it in a
|
|
40
|
+
* variable, use it, drop it when the tab closes.
|
|
41
|
+
*
|
|
42
|
+
* ## Scopes are coarse here, and that is GitHub's design
|
|
43
|
+
*
|
|
44
|
+
* The device flow issues a CLASSIC OAuth token, and classic scopes are coarse:
|
|
45
|
+
* `public_repo` (the default here) grants write across every public repository
|
|
46
|
+
* the account can reach, and `repo` grants it across private ones too. There is
|
|
47
|
+
* no fine-grained equivalent in this flow. That trade buys attribution — the
|
|
48
|
+
* issue is really from that person — and it is the reason the default stops at
|
|
49
|
+
* `public_repo`: filing an issue needs no more, and the token disappears with
|
|
50
|
+
* the session. Where least privilege matters more than attribution, use a
|
|
51
|
+
* fine-grained PAT on a server instead (see {@link githubBugReporter}).
|
|
52
|
+
*
|
|
53
|
+
* ## The collaborator caveat, stated rather than discovered
|
|
54
|
+
*
|
|
55
|
+
* A reporter who signs in as themselves can file an issue on a public repo, and
|
|
56
|
+
* can commit evidence ONLY to a repository they can write to. Pointing
|
|
57
|
+
* `evidenceRepo` at a private repo the reporter is not a collaborator on will
|
|
58
|
+
* fail with a 404 (GitHub hides private repos from tokens that cannot see
|
|
59
|
+
* them). Either add the reporter as a collaborator, or let them attach the zip
|
|
60
|
+
* to the issue by hand — `exportBugReport` gives them the file either way.
|
|
61
|
+
*
|
|
62
|
+
* ## Secrecy
|
|
63
|
+
*
|
|
64
|
+
* The token appears in no message this module can produce. The three failure
|
|
65
|
+
* shapes of the flow — the human denied it, the code expired, the poll was
|
|
66
|
+
* aborted — are named plainly, with GitHub's `error_description` only, never a
|
|
67
|
+
* response body or a request.
|
|
68
|
+
*/
|
|
69
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
70
|
+
exports.githubDeviceSignIn = void 0;
|
|
71
|
+
/** The endpoints, so a GitHub Enterprise Server deployment can move them. */
|
|
72
|
+
const DEFAULT_AUTH_BASE = 'https://github.com';
|
|
73
|
+
const DEFAULT_API_BASE = 'https://api.github.com';
|
|
74
|
+
const defaultSleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
75
|
+
if (signal?.aborted) {
|
|
76
|
+
reject(abortedError());
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const timer = setTimeout(() => {
|
|
80
|
+
signal?.removeEventListener('abort', onAbort);
|
|
81
|
+
resolve();
|
|
82
|
+
}, ms);
|
|
83
|
+
const onAbort = () => {
|
|
84
|
+
clearTimeout(timer);
|
|
85
|
+
reject(abortedError());
|
|
86
|
+
};
|
|
87
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
88
|
+
});
|
|
89
|
+
const abortedError = () => new Error('githubDeviceSignIn: sign-in was cancelled before the code was approved.');
|
|
90
|
+
/**
|
|
91
|
+
* Start a device-flow sign-in.
|
|
92
|
+
*
|
|
93
|
+
* Resolves as soon as GitHub hands back a code — that is the point, because
|
|
94
|
+
* the human cannot approve a code they have not been shown. The waiting happens
|
|
95
|
+
* on the returned `completed` promise.
|
|
96
|
+
*
|
|
97
|
+
* @throws TypeError when `clientId` is missing (naming where it comes from).
|
|
98
|
+
* @throws Error naming the status when GitHub refuses to issue a code.
|
|
99
|
+
*/
|
|
100
|
+
async function githubDeviceSignIn(options) {
|
|
101
|
+
if (typeof options?.clientId !== 'string' || options.clientId.trim() === '') {
|
|
102
|
+
throw new TypeError('githubDeviceSignIn: `clientId` is required. Create an OAuth App once under your ' +
|
|
103
|
+
'organisation (Settings → Developer settings → OAuth Apps), tick "Enable Device ' +
|
|
104
|
+
'Flow", and pass its Client ID here. It is public by design — the device flow has no ' +
|
|
105
|
+
'client secret, which is why it can run in a browser.');
|
|
106
|
+
}
|
|
107
|
+
const authBase = (options.authBase ?? DEFAULT_AUTH_BASE).replace(/\/+$/, '');
|
|
108
|
+
const apiBase = (options.apiBase ?? DEFAULT_API_BASE).replace(/\/+$/, '');
|
|
109
|
+
const doFetch = options._fetch ?? ((...args) => fetch(...args));
|
|
110
|
+
const sleep = options._sleep ?? defaultSleep;
|
|
111
|
+
const scopes = options.scopes ?? ['public_repo'];
|
|
112
|
+
const start = await postForm({
|
|
113
|
+
doFetch,
|
|
114
|
+
url: `${authBase}/login/device/code`,
|
|
115
|
+
what: 'request a device code',
|
|
116
|
+
form: { client_id: options.clientId, scope: scopes.join(' ') },
|
|
117
|
+
});
|
|
118
|
+
const deviceCode = stringField(start, 'device_code');
|
|
119
|
+
const userCode = stringField(start, 'user_code');
|
|
120
|
+
const verificationUri = stringField(start, 'verification_uri');
|
|
121
|
+
if (!deviceCode || !userCode || !verificationUri) {
|
|
122
|
+
throw new Error(`githubDeviceSignIn: GitHub did not return a device code${errorSuffix(start)}. Check that ` +
|
|
123
|
+
'the OAuth App exists and has Device Flow enabled.');
|
|
124
|
+
}
|
|
125
|
+
const expiresIn = numberField(start, 'expires_in') ?? 900;
|
|
126
|
+
const interval = numberField(start, 'interval') ?? 5;
|
|
127
|
+
const completed = pollForToken({
|
|
128
|
+
doFetch,
|
|
129
|
+
sleep,
|
|
130
|
+
authBase,
|
|
131
|
+
apiBase,
|
|
132
|
+
clientId: options.clientId,
|
|
133
|
+
deviceCode,
|
|
134
|
+
interval,
|
|
135
|
+
expiresIn,
|
|
136
|
+
...(options.signal !== undefined && { signal: options.signal }),
|
|
137
|
+
});
|
|
138
|
+
// Nobody has to await `completed` for polling to run; make sure an unawaited
|
|
139
|
+
// rejection is not an unhandled one in the meantime.
|
|
140
|
+
completed.catch(() => undefined);
|
|
141
|
+
return { userCode, verificationUri, expiresIn, interval, completed };
|
|
142
|
+
}
|
|
143
|
+
exports.githubDeviceSignIn = githubDeviceSignIn;
|
|
144
|
+
async function pollForToken(args) {
|
|
145
|
+
let waitSeconds = args.interval;
|
|
146
|
+
const deadline = Date.now() + args.expiresIn * 1000;
|
|
147
|
+
for (;;) {
|
|
148
|
+
if (args.signal?.aborted)
|
|
149
|
+
throw abortedError();
|
|
150
|
+
await args.sleep(waitSeconds * 1000, args.signal);
|
|
151
|
+
if (Date.now() > deadline) {
|
|
152
|
+
throw new Error(`githubDeviceSignIn: the code expired after ${args.expiresIn}s without being ` +
|
|
153
|
+
'approved. Start the sign-in again to get a fresh one.');
|
|
154
|
+
}
|
|
155
|
+
const body = await postForm({
|
|
156
|
+
doFetch: args.doFetch,
|
|
157
|
+
url: `${args.authBase}/login/oauth/access_token`,
|
|
158
|
+
what: 'exchange the device code',
|
|
159
|
+
form: {
|
|
160
|
+
client_id: args.clientId,
|
|
161
|
+
device_code: args.deviceCode,
|
|
162
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
const token = stringField(body, 'access_token');
|
|
166
|
+
if (token) {
|
|
167
|
+
const scope = stringField(body, 'scope') ?? '';
|
|
168
|
+
const login = await readLogin(args.doFetch, args.apiBase, token);
|
|
169
|
+
return {
|
|
170
|
+
token,
|
|
171
|
+
tokenType: stringField(body, 'token_type') ?? 'bearer',
|
|
172
|
+
scopes: scope ? scope.split(/[\s,]+/).filter(Boolean) : [],
|
|
173
|
+
...(login !== undefined && { login }),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const error = stringField(body, 'error');
|
|
177
|
+
switch (error) {
|
|
178
|
+
case 'authorization_pending':
|
|
179
|
+
// The human has not finished yet. This is the normal answer.
|
|
180
|
+
break;
|
|
181
|
+
case 'slow_down':
|
|
182
|
+
// GitHub's own back-pressure: it tells us the new interval, and polling
|
|
183
|
+
// faster than it asked gets the flow rate-limited out entirely.
|
|
184
|
+
waitSeconds = numberField(body, 'interval') ?? waitSeconds + 5;
|
|
185
|
+
break;
|
|
186
|
+
case 'expired_token':
|
|
187
|
+
throw new Error('githubDeviceSignIn: the code expired before it was approved. Start the sign-in ' +
|
|
188
|
+
'again to get a fresh one.');
|
|
189
|
+
case 'access_denied':
|
|
190
|
+
throw new Error('githubDeviceSignIn: the sign-in was denied on GitHub, so no token was issued. ' +
|
|
191
|
+
'Nothing has been filed.');
|
|
192
|
+
default:
|
|
193
|
+
throw new Error(`githubDeviceSignIn: GitHub refused the device code exchange${errorSuffix(body)}.`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/** Attribution: which login will the issue read as? Never fatal — a token that
|
|
198
|
+
* cannot read `/user` can still file an issue. */
|
|
199
|
+
async function readLogin(doFetch, apiBase, token) {
|
|
200
|
+
try {
|
|
201
|
+
const res = await doFetch(`${apiBase}/user`, {
|
|
202
|
+
headers: {
|
|
203
|
+
authorization: `Bearer ${token}`,
|
|
204
|
+
accept: 'application/vnd.github+json',
|
|
205
|
+
'x-github-api-version': '2022-11-28',
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
if (!res.ok)
|
|
209
|
+
return undefined;
|
|
210
|
+
const body = (await res.json());
|
|
211
|
+
return typeof body?.login === 'string' ? body.login : undefined;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* One `application/x-www-form-urlencoded` POST that asks for JSON back.
|
|
219
|
+
*
|
|
220
|
+
* The device flow's endpoints answer form-encoded by default; `accept: json` is
|
|
221
|
+
* what makes them speak JSON. A thrown fetch is re-wrapped so no implementation
|
|
222
|
+
* can put the request — which carries the device code, and later the token — in
|
|
223
|
+
* the message.
|
|
224
|
+
*/
|
|
225
|
+
async function postForm(args) {
|
|
226
|
+
let res;
|
|
227
|
+
try {
|
|
228
|
+
res = await args.doFetch(args.url, {
|
|
229
|
+
method: 'POST',
|
|
230
|
+
headers: { accept: 'application/json', 'content-type': 'application/x-www-form-urlencoded' },
|
|
231
|
+
body: new URLSearchParams(args.form).toString(),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
throw new Error(`githubDeviceSignIn: could not reach GitHub to ${args.what} ` +
|
|
236
|
+
`(${err instanceof Error ? err.name || 'network error' : 'network error'}).`);
|
|
237
|
+
}
|
|
238
|
+
let body;
|
|
239
|
+
try {
|
|
240
|
+
body = await res.json();
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
body = undefined;
|
|
244
|
+
}
|
|
245
|
+
const record = typeof body === 'object' && body !== null ? body : {};
|
|
246
|
+
// A 4xx/5xx with no parsed error field is all we may say: the response body
|
|
247
|
+
// of this endpoint can echo the request.
|
|
248
|
+
if (!res.ok && typeof record.error !== 'string') {
|
|
249
|
+
throw new Error(`githubDeviceSignIn: GitHub answered ${res.status} when asked to ${args.what}.`);
|
|
250
|
+
}
|
|
251
|
+
return record;
|
|
252
|
+
}
|
|
253
|
+
const stringField = (body, key) => typeof body[key] === 'string' && body[key] !== '' ? body[key] : undefined;
|
|
254
|
+
const numberField = (body, key) => typeof body[key] === 'number' ? body[key] : undefined;
|
|
255
|
+
/** GitHub's `error` / `error_description` — the only response text we echo. */
|
|
256
|
+
function errorSuffix(body) {
|
|
257
|
+
const code = stringField(body, 'error');
|
|
258
|
+
const description = stringField(body, 'error_description');
|
|
259
|
+
if (!code && !description)
|
|
260
|
+
return '';
|
|
261
|
+
return ` (${[code, description].filter(Boolean).join(': ').slice(0, 200)})`;
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=githubDeviceSignIn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"githubDeviceSignIn.js","sourceRoot":"","sources":["../../../src/adapters/observability/githubDeviceSignIn.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;;;AAEH,6EAA6E;AAC7E,MAAM,iBAAiB,GAAG,oBAAoB,CAAC;AAC/C,MAAM,gBAAgB,GAAG,wBAAwB,CAAC;AA0DlD,MAAM,YAAY,GAAG,CAAC,EAAU,EAAE,MAAoB,EAAiB,EAAE,CACvE,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAC9B,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACpB,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;QACvB,OAAO;IACT,CAAC;IACD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;QAC5B,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9C,OAAO,EAAE,CAAC;IACZ,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,MAAM,OAAO,GAAG,GAAS,EAAE;QACzB,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;IACzB,CAAC,CAAC;IACF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7D,CAAC,CAAC,CAAC;AAEL,MAAM,YAAY,GAAG,GAAU,EAAE,CAC/B,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;AAEvF;;;;;;;;;GASG;AACI,KAAK,UAAU,kBAAkB,CACtC,OAAkC;IAElC,IAAI,OAAO,OAAO,EAAE,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC5E,MAAM,IAAI,SAAS,CACjB,kFAAkF;YAChF,iFAAiF;YACjF,sFAAsF;YACtF,sDAAsD,CACzD,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC7E,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC1E,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,IAA8B,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1F,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,IAAI,YAAY,CAAC;IAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,CAAC;IAEjD,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC;QAC3B,OAAO;QACP,GAAG,EAAE,GAAG,QAAQ,oBAAoB;QACpC,IAAI,EAAE,uBAAuB;QAC7B,IAAI,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;KAC/D,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACjD,MAAM,eAAe,GAAG,WAAW,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC;IAC/D,IAAI,CAAC,UAAU,IAAI,CAAC,QAAQ,IAAI,CAAC,eAAe,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CACb,0DAA0D,WAAW,CAAC,KAAK,CAAC,eAAe;YACzF,mDAAmD,CACtD,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,GAAG,CAAC;IAC1D,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC;IAErD,MAAM,SAAS,GAAG,YAAY,CAAC;QAC7B,OAAO;QACP,KAAK;QACL,QAAQ;QACR,OAAO;QACP,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,UAAU;QACV,QAAQ;QACR,SAAS;QACT,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;KAChE,CAAC,CAAC;IACH,6EAA6E;IAC7E,qDAAqD;IACrD,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAEjC,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACvE,CAAC;AArDD,gDAqDC;AAED,KAAK,UAAU,YAAY,CAAC,IAU3B;IACC,IAAI,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC;IAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IAEpD,SAAS,CAAC;QACR,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO;YAAE,MAAM,YAAY,EAAE,CAAC;QAC/C,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAClD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,8CAA8C,IAAI,CAAC,SAAS,kBAAkB;gBAC5E,uDAAuD,CAC1D,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC;YAC1B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,2BAA2B;YAChD,IAAI,EAAE,0BAA0B;YAChC,IAAI,EAAE;gBACJ,SAAS,EAAE,IAAI,CAAC,QAAQ;gBACxB,WAAW,EAAE,IAAI,CAAC,UAAU;gBAC5B,UAAU,EAAE,8CAA8C;aAC3D;SACF,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAChD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;YAC/C,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACjE,OAAO;gBACL,KAAK;gBACL,SAAS,EAAE,WAAW,CAAC,IAAI,EAAE,YAAY,CAAC,IAAI,QAAQ;gBACtD,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;gBAC1D,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,CAAC;aACtC,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACzC,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,uBAAuB;gBAC1B,6DAA6D;gBAC7D,MAAM;YACR,KAAK,WAAW;gBACd,wEAAwE;gBACxE,gEAAgE;gBAChE,WAAW,GAAG,WAAW,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,WAAW,GAAG,CAAC,CAAC;gBAC/D,MAAM;YACR,KAAK,eAAe;gBAClB,MAAM,IAAI,KAAK,CACb,iFAAiF;oBAC/E,2BAA2B,CAC9B,CAAC;YACJ,KAAK,eAAe;gBAClB,MAAM,IAAI,KAAK,CACb,gFAAgF;oBAC9E,yBAAyB,CAC5B,CAAC;YACJ;gBACE,MAAM,IAAI,KAAK,CACb,8DAA8D,WAAW,CAAC,IAAI,CAAC,GAAG,CACnF,CAAC;QACN,CAAC;IACH,CAAC;AACH,CAAC;AAED;mDACmD;AACnD,KAAK,UAAU,SAAS,CACtB,OAAqB,EACrB,OAAe,EACf,KAAa;IAEb,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,OAAO,OAAO,EAAE;YAC3C,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,KAAK,EAAE;gBAChC,MAAM,EAAE,6BAA6B;gBACrC,sBAAsB,EAAE,YAAY;aACrC;SACF,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,SAAS,CAAC;QAC9B,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAwB,CAAC;QACvD,OAAO,OAAO,IAAI,EAAE,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,QAAQ,CAAC,IAKvB;IACC,IAAI,GAAsC,CAAC;IAC3C,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE;YACjC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,cAAc,EAAE,mCAAmC,EAAE;YAC5F,IAAI,EAAE,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;SAChD,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,iDAAiD,IAAI,CAAC,IAAI,GAAG;YAC3D,IAAI,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,eAAe,CAAC,CAAC,CAAC,eAAe,IAAI,CAC/E,CAAC;IACJ,CAAC;IAED,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,GAAG,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAE,IAAgC,CAAC,CAAC,CAAC,EAAE,CAAC;IAElG,4EAA4E;IAC5E,yCAAyC;IACzC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CACb,uCAAuC,GAAG,CAAC,MAAM,kBAAkB,IAAI,CAAC,IAAI,GAAG,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,WAAW,GAAG,CAAC,IAA6B,EAAE,GAAW,EAAsB,EAAE,CACrF,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAE,IAAI,CAAC,GAAG,CAAY,CAAC,CAAC,CAAC,SAAS,CAAC;AAExF,MAAM,WAAW,GAAG,CAAC,IAA6B,EAAE,GAAW,EAAsB,EAAE,CACrF,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAI,CAAC,GAAG,CAAY,CAAC,CAAC,CAAC,SAAS,CAAC;AAEpE,+EAA+E;AAC/E,SAAS,WAAW,CAAC,IAA6B;IAChD,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,MAAM,WAAW,GAAG,WAAW,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;IAC3D,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW;QAAE,OAAO,EAAE,CAAC;IACrC,OAAO,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vaultCredentials — a {@link CredentialProvider} over a HashiCorp-Vault-compatible
|
|
3
|
+
* KV v2 secret store, spoken as plain HTTP.
|
|
4
|
+
*
|
|
5
|
+
* import { vaultCredentials } from 'agentfootprint/security';
|
|
6
|
+
*
|
|
7
|
+
* const credentials = vaultCredentials({
|
|
8
|
+
* address: 'https://vault.internal:8200', // https, or say `allowHttp` out loud
|
|
9
|
+
* mount: 'secret', // KV v2 mount, default 'secret'
|
|
10
|
+
* paths: { github: 'ci/github' }, // service → path INSIDE the mount
|
|
11
|
+
* }); // token: VAULT_TOKEN, or `token`
|
|
12
|
+
*
|
|
13
|
+
* Zero dependencies and no SDK: one `GET` per resolution through the runtime's
|
|
14
|
+
* own `fetch`. Vault's HTTP API is small, stable and the thing every
|
|
15
|
+
* Vault-compatible store (OpenBao, and the Vault-API modes of several managed
|
|
16
|
+
* stores) implements — so the adapter that speaks HTTP works against more
|
|
17
|
+
* backends than the adapter that imports one vendor's client.
|
|
18
|
+
*
|
|
19
|
+
* ## V1 is deliberately one shape, and says so by name
|
|
20
|
+
*
|
|
21
|
+
* | Axis | V1 | Anything else |
|
|
22
|
+
* |---|---|---|
|
|
23
|
+
* | Auth | a **token** (`token` option, else `VAULT_TOKEN`) | AppRole / Kubernetes / JWT / AWS IAM login are **refused by name**, naming the option that would carry them |
|
|
24
|
+
* | Secret engine | **KV v2** (`<mount>/data/<path>`, the `data.data` envelope) | a KV v1 mount is refused by name once the response shape gives it away |
|
|
25
|
+
* | Leases / renewal | **none** — every `getCredential` re-reads the secret | a lease-aware provider is a different object, and the library's model since 9.7.0 is re-resolve-per-call |
|
|
26
|
+
*
|
|
27
|
+
* That is not modesty, it is the honest edge: an auth method the author cannot
|
|
28
|
+
* exercise against a real cluster would be a guess wearing an adapter's clothes.
|
|
29
|
+
* Each refusal names the option it would arrive on, so "tell us your auth shape"
|
|
30
|
+
* is a field report rather than an issue title.
|
|
31
|
+
*
|
|
32
|
+
* ## Field → credential kind
|
|
33
|
+
*
|
|
34
|
+
* A KV v2 read returns `{ data: { data: { …your fields… }, metadata: {…} } }`.
|
|
35
|
+
* The inner object is mapped to a {@link Credential} by the FIRST rule that
|
|
36
|
+
* matches, so a secret written the ordinary way needs no configuration:
|
|
37
|
+
*
|
|
38
|
+
* | Fields present | Becomes | Header it applies |
|
|
39
|
+
* |---|---|---|
|
|
40
|
+
* | `token` | `bearer(token)` | `authorization: Bearer …` |
|
|
41
|
+
* | `api_key` \| `apiKey` \| `key` | `apiKey(value, header ?? 'x-api-key')` | that header |
|
|
42
|
+
* | `username` + `password` | `basic(username, password)` | `authorization: Basic …` |
|
|
43
|
+
* | `headers` (an object of strings) | `headers(map)` | all of them |
|
|
44
|
+
*
|
|
45
|
+
* A secret matching none of them is refused — naming the PATH and the four
|
|
46
|
+
* shapes, never the secret. `toCredential` is the seam for a shop whose fields
|
|
47
|
+
* are named otherwise; it sees the secret and returns a `Credential`, and
|
|
48
|
+
* returning `undefined` falls back to the table above.
|
|
49
|
+
*
|
|
50
|
+
* ## Secrecy (the 8.6.0 two-clause law, applied here)
|
|
51
|
+
*
|
|
52
|
+
* A thrown message reaches the model as a tool result AND rides
|
|
53
|
+
* `agentfootprint.credential.failed`. So every error this adapter raises names
|
|
54
|
+
* **the service, the path and the HTTP status, and nothing from the response
|
|
55
|
+
* body or the token**. Nothing here logs, and no secret value, no `X-Vault-Token`
|
|
56
|
+
* header and no field name from the payload appears in any message it can throw
|
|
57
|
+
* — pinned by a grep-shaped test over every failure path. The credential it
|
|
58
|
+
* returns hides its own secret fields (non-enumerable) and carries `toHeaders`,
|
|
59
|
+
* so `structuredClone` rejects it and it cannot enter tracked scope by accident.
|
|
60
|
+
*
|
|
61
|
+
* @example Dev → prod is the same two lines
|
|
62
|
+
* ```ts
|
|
63
|
+
* // dev
|
|
64
|
+
* const credentials = staticTokens({ github: 'ghp_dev_xxx' });
|
|
65
|
+
* // prod — the tool code does not change
|
|
66
|
+
* const credentials = vaultCredentials({ address: process.env.VAULT_ADDR! });
|
|
67
|
+
* Agent.create({ provider, model, credentials }).build();
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
import type { Credential, CredentialProvider } from '../../identity/types.js';
|
|
71
|
+
/** The base options every form shares. */
|
|
72
|
+
interface VaultCredentialsBase {
|
|
73
|
+
/** Vault's base URL, e.g. `https://vault.internal:8200` — **required**, and
|
|
74
|
+
* **https** unless {@link VaultCredentialsBase.allowHttp} says otherwise. No
|
|
75
|
+
* `VAULT_ADDR` fallback: an agent that silently picks up an address from the
|
|
76
|
+
* environment is an agent that reads a different vault when the environment
|
|
77
|
+
* changes under it. Name it. */
|
|
78
|
+
readonly address: string;
|
|
79
|
+
/** The Vault token. Falls back to `VAULT_TOKEN` (the variable every Vault
|
|
80
|
+
* tool already sets). This is a secret: it is sent as `X-Vault-Token` and
|
|
81
|
+
* appears in no message this adapter can throw. */
|
|
82
|
+
readonly token?: string;
|
|
83
|
+
/** Auth method. **`'token'` is the only one V1 implements.** Anything else is
|
|
84
|
+
* refused at construction, by name, with what it would take — see
|
|
85
|
+
* {@link vaultCredentials}. */
|
|
86
|
+
readonly auth?: 'token';
|
|
87
|
+
/** KV v2 mount point. Default `'secret'` (Vault's own default for the KV v2
|
|
88
|
+
* engine). The read URL is `<address>/v1/<mount>/data/<path>`. */
|
|
89
|
+
readonly mount?: string;
|
|
90
|
+
/** Vault Enterprise / HCP namespace, sent as `X-Vault-Namespace`. Omit for
|
|
91
|
+
* open-source Vault and OpenBao, which have no namespaces. */
|
|
92
|
+
readonly namespace?: string;
|
|
93
|
+
/** Map the secret's fields to a {@link Credential} yourself. Returns
|
|
94
|
+
* `undefined` to fall back to the built-in table (`token` / `api_key` /
|
|
95
|
+
* `username`+`password` / `headers`). The seam for a shop whose field names
|
|
96
|
+
* are its own — and the reason this adapter does not need an option per
|
|
97
|
+
* spelling. **Never log or return the fields from here**; they are the
|
|
98
|
+
* secret. */
|
|
99
|
+
readonly toCredential?: (secret: Readonly<Record<string, unknown>>, service: string) => Credential | undefined;
|
|
100
|
+
/** Header name for the `api_key` shape when the secret does not carry its own
|
|
101
|
+
* `header` field. Default `'x-api-key'`. */
|
|
102
|
+
readonly apiKeyHeader?: string;
|
|
103
|
+
/** Request timeout in ms. Default 5000 — a credential resolution sits in
|
|
104
|
+
* front of a tool call, so a hung vault must fail rather than hang a run. */
|
|
105
|
+
readonly timeoutMs?: number;
|
|
106
|
+
/** Allow a plain-`http://` address. **Refused unless you set this**, because
|
|
107
|
+
* the Vault token travels in a request header: over plaintext HTTP, anyone
|
|
108
|
+
* on the path reads a token that can usually read every secret it can reach.
|
|
109
|
+
* Set it only for a loopback dev server (`http://127.0.0.1:8200`). */
|
|
110
|
+
readonly allowHttp?: boolean;
|
|
111
|
+
/** Stable provider id (default `'vault'`). Shows up in "which provider vended
|
|
112
|
+
* this". */
|
|
113
|
+
readonly id?: string;
|
|
114
|
+
/** Test seam — inject `fetch`. Bypasses the network entirely. */
|
|
115
|
+
readonly _fetch?: typeof fetch;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* How a `service` becomes a path inside the mount. Three arms, and they
|
|
119
|
+
* EXCLUDE each other — two spellings of one rule can disagree, so the type
|
|
120
|
+
* refuses the pair and so does the constructor.
|
|
121
|
+
*/
|
|
122
|
+
type VaultPathMapping = {
|
|
123
|
+
/** `service → path inside the mount`, the {@link staticTokens} shape one
|
|
124
|
+
* level up: the same literal map, holding a path instead of a token. An
|
|
125
|
+
* unknown service is refused by name, listing the known ones. */
|
|
126
|
+
readonly paths: Readonly<Record<string, string>>;
|
|
127
|
+
readonly resolve?: never;
|
|
128
|
+
} | {
|
|
129
|
+
/** `service → path`, computed. Return `undefined` to refuse a service.
|
|
130
|
+
* For the convention-driven shop: ``(s) => `agents/${s}` ``. */
|
|
131
|
+
readonly resolve: (service: string) => string | undefined;
|
|
132
|
+
readonly paths?: never;
|
|
133
|
+
} | {
|
|
134
|
+
/** Neither: the **service id IS the path** under the mount, so
|
|
135
|
+
* `service: 'github'` reads `<mount>/data/github`. */
|
|
136
|
+
readonly paths?: undefined;
|
|
137
|
+
readonly resolve?: undefined;
|
|
138
|
+
};
|
|
139
|
+
export type VaultCredentialsOptions = VaultCredentialsBase & VaultPathMapping;
|
|
140
|
+
/**
|
|
141
|
+
* Build a {@link CredentialProvider} that reads KV v2 secrets from a
|
|
142
|
+
* Vault-compatible store. See {@link VaultCredentialsOptions} for the
|
|
143
|
+
* per-option contract and this module's docstring for the V1 boundary.
|
|
144
|
+
*/
|
|
145
|
+
export declare function vaultCredentials(options: VaultCredentialsOptions): CredentialProvider;
|
|
146
|
+
export {};
|