@agents24/client 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -6
- package/compatibility.json +10 -10
- package/dist/adapters/browser.d.ts +83 -1
- package/dist/adapters/browser.js +303 -3
- package/dist/adapters/browser.js.map +1 -1
- package/dist/adapters/cloudflare.js +3 -2
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/node.js +3 -2
- package/dist/adapters/node.js.map +1 -1
- package/dist/adapters/worker.js +3 -2
- package/dist/adapters/worker.js.map +1 -1
- package/dist/bff.d.ts +10 -0
- package/dist/bff.js +282 -0
- package/dist/bff.js.map +1 -0
- package/dist/chunk-FNR3KPB6.js +569 -0
- package/dist/chunk-FNR3KPB6.js.map +1 -0
- package/dist/{chunk-45ITVMX3.js → chunk-GXISGBVW.js} +126 -480
- package/dist/chunk-GXISGBVW.js.map +1 -0
- package/dist/{chunk-VVLMMNXI.js → chunk-IOVCURGW.js} +32 -14
- package/dist/chunk-IOVCURGW.js.map +1 -0
- package/dist/chunk-SPWEZFHZ.js +430 -0
- package/dist/chunk-SPWEZFHZ.js.map +1 -0
- package/dist/context-window.d.ts +16 -1
- package/dist/errors.d.ts +27 -2
- package/dist/generated/protocol.d.ts +50 -9
- package/dist/index.d.ts +4 -3
- package/dist/index.js +4 -555
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +2 -2
- package/dist/protocol.js +31 -1
- package/dist/protocol.js.map +1 -1
- package/dist/runtime-types.d.ts +37 -2
- package/dist/types.d.ts +148 -10
- package/package.json +5 -1
- package/dist/chunk-45ITVMX3.js.map +0 -1
- package/dist/chunk-VVLMMNXI.js.map +0 -1
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
import { defaultClock, defaultIds, resolveEncoder, canonicalHtu, Agents24ClientError, missingCapability, normalizeBaseUrl, resolveFetch, dpopTokenRequest, absoluteUrl, encodePath, responseError, responseJson, AuthenticatedHttp } from './chunk-GXISGBVW.js';
|
|
2
|
+
|
|
3
|
+
// src/base64url.ts
|
|
4
|
+
function base64UrlEncode(bytes) {
|
|
5
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
6
|
+
let output = "";
|
|
7
|
+
for (let index = 0; index < bytes.length; index += 3) {
|
|
8
|
+
const first = bytes[index] ?? 0;
|
|
9
|
+
const second = bytes[index + 1];
|
|
10
|
+
const third = bytes[index + 2];
|
|
11
|
+
const packed = first << 16 | (second ?? 0) << 8 | (third ?? 0);
|
|
12
|
+
output += alphabet[packed >>> 18 & 63];
|
|
13
|
+
output += alphabet[packed >>> 12 & 63];
|
|
14
|
+
output += second === void 0 ? "=" : alphabet[packed >>> 6 & 63];
|
|
15
|
+
output += third === void 0 ? "=" : alphabet[packed & 63];
|
|
16
|
+
}
|
|
17
|
+
return output.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/dpop.ts
|
|
21
|
+
function resolveCrypto(injected) {
|
|
22
|
+
const value = injected ?? globalThis.crypto;
|
|
23
|
+
if (!value?.subtle) throw missingCapability("crypto.subtle");
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
function publicJwkOnly(jwk) {
|
|
27
|
+
if (jwk.kty !== "EC" || jwk.crv !== "P-256" || !jwk.x || !jwk.y) {
|
|
28
|
+
throw new Agents24ClientError("DPoP requires a P-256 public key.", {
|
|
29
|
+
kind: "configuration",
|
|
30
|
+
code: "INVALID_DPOP_KEY"
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return { kty: "EC", crv: "P-256", x: jwk.x, y: jwk.y };
|
|
34
|
+
}
|
|
35
|
+
async function hash(subtle, encoder, input) {
|
|
36
|
+
return base64UrlEncode(new Uint8Array(await subtle.digest("SHA-256", encoder.encode(input))));
|
|
37
|
+
}
|
|
38
|
+
function createWebCryptoDpopKeyProvider(options = {}) {
|
|
39
|
+
const crypto = resolveCrypto(options.crypto);
|
|
40
|
+
const clock = options.clock ?? defaultClock();
|
|
41
|
+
const ids = options.ids ?? defaultIds();
|
|
42
|
+
const encoder = resolveEncoder(options.encoder);
|
|
43
|
+
let handlePromise;
|
|
44
|
+
let jwkPromise;
|
|
45
|
+
const getHandle = () => {
|
|
46
|
+
if (!handlePromise) {
|
|
47
|
+
handlePromise = (async () => {
|
|
48
|
+
const stored = await options.storage?.load();
|
|
49
|
+
if (stored) return stored;
|
|
50
|
+
const generated = await crypto.subtle.generateKey(
|
|
51
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
52
|
+
false,
|
|
53
|
+
["sign", "verify"]
|
|
54
|
+
);
|
|
55
|
+
await options.storage?.save(generated);
|
|
56
|
+
return generated;
|
|
57
|
+
})();
|
|
58
|
+
}
|
|
59
|
+
return handlePromise;
|
|
60
|
+
};
|
|
61
|
+
const getPublicJwk = () => {
|
|
62
|
+
if (!jwkPromise) {
|
|
63
|
+
jwkPromise = getHandle().then(async (handle) => publicJwkOnly(await crypto.subtle.exportKey("jwk", handle.publicKey)));
|
|
64
|
+
}
|
|
65
|
+
return jwkPromise;
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
getPublicJwk,
|
|
69
|
+
async getThumbprint() {
|
|
70
|
+
const jwk = await getPublicJwk();
|
|
71
|
+
return hash(crypto.subtle, encoder, JSON.stringify({ crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y }));
|
|
72
|
+
},
|
|
73
|
+
async signProof(input) {
|
|
74
|
+
const jwk = await getPublicJwk();
|
|
75
|
+
const header = base64UrlEncode(encoder.encode(JSON.stringify({ typ: "dpop+jwt", alg: "ES256", jwk })));
|
|
76
|
+
const claims = {
|
|
77
|
+
jti: ids.createId(),
|
|
78
|
+
htm: input.method.toUpperCase(),
|
|
79
|
+
htu: canonicalHtu(input.url),
|
|
80
|
+
iat: Math.floor(clock.now() / 1e3)
|
|
81
|
+
};
|
|
82
|
+
if (input.accessToken) claims.ath = await hash(crypto.subtle, encoder, input.accessToken);
|
|
83
|
+
if (input.nonce) claims.nonce = input.nonce;
|
|
84
|
+
const payload = base64UrlEncode(encoder.encode(JSON.stringify(claims)));
|
|
85
|
+
const signingInput = `${header}.${payload}`;
|
|
86
|
+
const handle = await getHandle();
|
|
87
|
+
const signature = new Uint8Array(
|
|
88
|
+
await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, handle.privateKey, encoder.encode(signingInput))
|
|
89
|
+
);
|
|
90
|
+
if (signature.byteLength !== 64) {
|
|
91
|
+
throw new Agents24ClientError("The WebCrypto provider returned a non-JOSE ECDSA signature.", {
|
|
92
|
+
kind: "configuration",
|
|
93
|
+
code: "INVALID_DPOP_SIGNATURE"
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return `${signingInput}.${base64UrlEncode(signature)}`;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async function createPkcePair(cryptoInput, encoderInput) {
|
|
101
|
+
const crypto = resolveCrypto(cryptoInput);
|
|
102
|
+
const encoder = resolveEncoder(encoderInput);
|
|
103
|
+
const random = new Uint8Array(32);
|
|
104
|
+
const getRandomValues = crypto.getRandomValues?.bind(crypto);
|
|
105
|
+
if (!getRandomValues) throw missingCapability("crypto.getRandomValues");
|
|
106
|
+
getRandomValues(random);
|
|
107
|
+
const verifier = base64UrlEncode(random);
|
|
108
|
+
return { verifier, challenge: await hash(crypto.subtle, encoder, verifier) };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/sessions.ts
|
|
112
|
+
function parseSessionToken(value, options) {
|
|
113
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
114
|
+
throw new Agents24ClientError("The session endpoint returned an invalid response.", {
|
|
115
|
+
kind: "protocol",
|
|
116
|
+
code: "INVALID_SESSION_RESPONSE"
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const body = value;
|
|
120
|
+
if (typeof body.access_token !== "string" || !body.access_token || /[\r\n\0]/.test(body.access_token)) {
|
|
121
|
+
throw new Agents24ClientError("The session endpoint returned an invalid access token.", {
|
|
122
|
+
kind: "protocol",
|
|
123
|
+
code: "INVALID_SESSION_RESPONSE"
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const tokenType = body.token_type === "DPoP" ? "DPoP" : body.token_type === "Bearer" ? "Bearer" : void 0;
|
|
127
|
+
if (!tokenType || tokenType === "DPoP" && !options.dpopKeyProvider) {
|
|
128
|
+
throw new Agents24ClientError("The session endpoint returned an unsupported token profile.", {
|
|
129
|
+
kind: "protocol",
|
|
130
|
+
code: "INVALID_TOKEN_PROFILE"
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
const parsedExpiresAt = typeof body.expires_at === "string" ? Date.parse(body.expires_at) : Number.NaN;
|
|
134
|
+
const expiresIn = typeof body.expires_in === "number" && Number.isFinite(body.expires_in) ? body.expires_in : 0;
|
|
135
|
+
const expiresAt = Number.isFinite(parsedExpiresAt) ? parsedExpiresAt : options.clock.now() + expiresIn * 1e3;
|
|
136
|
+
if (!Number.isFinite(expiresAt) || expiresAt <= options.clock.now()) {
|
|
137
|
+
throw new Agents24ClientError("The session endpoint returned an expired access token.", {
|
|
138
|
+
kind: "protocol",
|
|
139
|
+
code: "INVALID_SESSION_EXPIRY"
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
const refreshGrant = typeof body.refresh_grant === "string" && body.refresh_grant && !/[\r\n\0]/.test(body.refresh_grant) ? body.refresh_grant : void 0;
|
|
143
|
+
if (body.refresh_grant !== void 0 && !refreshGrant) {
|
|
144
|
+
throw new Agents24ClientError("The session endpoint returned an invalid refresh grant.", {
|
|
145
|
+
kind: "protocol",
|
|
146
|
+
code: "INVALID_SESSION_RESPONSE"
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (refreshGrant && (tokenType !== "DPoP" || !options.dpopKeyProvider)) {
|
|
150
|
+
throw new Agents24ClientError("Persistent sessions require a DPoP-bound token profile.", {
|
|
151
|
+
kind: "protocol",
|
|
152
|
+
code: "INVALID_TOKEN_PROFILE"
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
access: {
|
|
157
|
+
accessToken: body.access_token,
|
|
158
|
+
tokenType,
|
|
159
|
+
expiresAt,
|
|
160
|
+
...typeof body.session_id === "string" ? { sessionId: body.session_id } : {},
|
|
161
|
+
...Array.isArray(body.capabilities) && body.capabilities.every((item) => typeof item === "string") ? { capabilities: body.capabilities } : {},
|
|
162
|
+
...options.dpopKeyProvider ? { dpopKeyProvider: options.dpopKeyProvider } : {},
|
|
163
|
+
...typeof body.dpop_nonce === "string" ? { dpopNonce: body.dpop_nonce } : {}
|
|
164
|
+
},
|
|
165
|
+
...refreshGrant ? { refreshGrant } : {},
|
|
166
|
+
...typeof (body.refresh_expires_at ?? body.refresh_grant_absolute_expires_at) === "string" ? { absoluteExpiresAt: body.refresh_expires_at ?? body.refresh_grant_absolute_expires_at } : {}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function refreshFenceLost() {
|
|
170
|
+
return new Agents24ClientError("Refresh coordination ownership changed before completion.", {
|
|
171
|
+
kind: "conflict",
|
|
172
|
+
code: "REFRESH_FENCE_LOST",
|
|
173
|
+
retryable: true
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function sessionRequestAborted() {
|
|
177
|
+
return new Agents24ClientError("The session request was locally detached.", {
|
|
178
|
+
kind: "aborted",
|
|
179
|
+
code: "ABORTED",
|
|
180
|
+
retryable: false
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function defaultWait(milliseconds) {
|
|
184
|
+
const schedule = globalThis.setTimeout;
|
|
185
|
+
if (!schedule) {
|
|
186
|
+
throw new Agents24ClientError("Cross-runtime refresh coordination requires a wait capability.", {
|
|
187
|
+
kind: "missing_capability",
|
|
188
|
+
code: "MISSING_WAIT_CAPABILITY"
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
return new Promise((resolve) => schedule(resolve, milliseconds));
|
|
192
|
+
}
|
|
193
|
+
function createFencedRefreshCoordinator(options) {
|
|
194
|
+
const clock = options.clock ?? defaultClock();
|
|
195
|
+
const ids = options.ids ?? defaultIds();
|
|
196
|
+
const ownerId = options.ownerId ?? ids.createId();
|
|
197
|
+
const leaseDurationMs = Math.max(1e3, options.leaseDurationMs ?? 6e4);
|
|
198
|
+
const pollIntervalMs = Math.max(10, options.pollIntervalMs ?? 100);
|
|
199
|
+
const wait = options.wait ?? defaultWait;
|
|
200
|
+
let local;
|
|
201
|
+
const assertActive = async (record) => {
|
|
202
|
+
const current = await options.storage.load();
|
|
203
|
+
if (!current?.active || current.fence !== record.fence || current.operationId !== record.operationId || current.ownerId !== ownerId || current.expiresAt <= clock.now()) {
|
|
204
|
+
throw refreshFenceLost();
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const acquire = async () => {
|
|
208
|
+
while (true) {
|
|
209
|
+
const current = await options.storage.load();
|
|
210
|
+
const now = clock.now();
|
|
211
|
+
if (current?.active && current.expiresAt > now) {
|
|
212
|
+
await wait(Math.max(1, Math.min(pollIntervalMs, current.expiresAt - now)));
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const next = {
|
|
216
|
+
active: true,
|
|
217
|
+
expiresAt: now + leaseDurationMs,
|
|
218
|
+
fence: (current?.fence ?? 0) + 1,
|
|
219
|
+
operationId: current?.active ? current.operationId : ids.createId(),
|
|
220
|
+
ownerId
|
|
221
|
+
};
|
|
222
|
+
if (await options.storage.compareAndSwap(current?.fence ?? null, next)) return next;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
return {
|
|
226
|
+
async runExclusive(operation) {
|
|
227
|
+
if (local) return local;
|
|
228
|
+
const pending = (async () => {
|
|
229
|
+
const record = await acquire();
|
|
230
|
+
const lease = {
|
|
231
|
+
fence: record.fence,
|
|
232
|
+
operationId: record.operationId,
|
|
233
|
+
assertActive: () => assertActive(record)
|
|
234
|
+
};
|
|
235
|
+
try {
|
|
236
|
+
return await operation(lease);
|
|
237
|
+
} finally {
|
|
238
|
+
const current = await options.storage.load();
|
|
239
|
+
if (current?.active && current.fence === record.fence && current.operationId === record.operationId && current.ownerId === ownerId) {
|
|
240
|
+
await options.storage.compareAndSwap(current.fence, {
|
|
241
|
+
...current,
|
|
242
|
+
active: false,
|
|
243
|
+
expiresAt: clock.now()
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
})();
|
|
248
|
+
local = pending;
|
|
249
|
+
try {
|
|
250
|
+
return await pending;
|
|
251
|
+
} finally {
|
|
252
|
+
if (local === pending) local = void 0;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function createInMemoryRefreshCoordinator() {
|
|
258
|
+
let active;
|
|
259
|
+
let fence = 0;
|
|
260
|
+
return {
|
|
261
|
+
async runExclusive(operation) {
|
|
262
|
+
if (active) return active;
|
|
263
|
+
fence += 1;
|
|
264
|
+
const ownedFence = fence;
|
|
265
|
+
const current = Promise.resolve().then(() => operation({
|
|
266
|
+
fence: ownedFence,
|
|
267
|
+
operationId: `memory-refresh-${ownedFence}`,
|
|
268
|
+
async assertActive() {
|
|
269
|
+
if (active !== current) throw refreshFenceLost();
|
|
270
|
+
}
|
|
271
|
+
}));
|
|
272
|
+
active = current;
|
|
273
|
+
try {
|
|
274
|
+
return await current;
|
|
275
|
+
} finally {
|
|
276
|
+
if (active === current) active = void 0;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
function createStaticSessionProvider(access) {
|
|
282
|
+
return {
|
|
283
|
+
async getAccess() {
|
|
284
|
+
return access;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function createManagedSessionProviderInternal(options, recoverInvalidRefreshWithMint) {
|
|
289
|
+
if (options.storage && !options.dpopKeyProvider) {
|
|
290
|
+
throw new Agents24ClientError("Persistent session storage requires a DPoP key provider.", {
|
|
291
|
+
kind: "configuration",
|
|
292
|
+
code: "PERSISTENT_SESSION_REQUIRES_DPOP"
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
const baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
296
|
+
const fetch = resolveFetch(options.fetch);
|
|
297
|
+
const clock = options.clock ?? defaultClock();
|
|
298
|
+
const ids = options.ids ?? defaultIds();
|
|
299
|
+
const coordinator = options.coordinator ?? createInMemoryRefreshCoordinator();
|
|
300
|
+
let current;
|
|
301
|
+
let refreshGrant;
|
|
302
|
+
let minting;
|
|
303
|
+
const emit = async (event) => {
|
|
304
|
+
try {
|
|
305
|
+
await options.telemetry?.emit(event);
|
|
306
|
+
} catch {
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
const loadStored = async () => {
|
|
310
|
+
const stored = await options.storage?.load() ?? null;
|
|
311
|
+
if (!stored) return null;
|
|
312
|
+
if (!stored.refreshGrant || /[\r\n\0]/.test(stored.refreshGrant)) {
|
|
313
|
+
throw new Agents24ClientError("Stored session state is invalid.", {
|
|
314
|
+
kind: "authentication",
|
|
315
|
+
code: "INVALID_STORED_SESSION"
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
if (stored.absoluteExpiresAt) {
|
|
319
|
+
const absoluteExpiry = Date.parse(stored.absoluteExpiresAt);
|
|
320
|
+
if (!Number.isFinite(absoluteExpiry) || absoluteExpiry <= clock.now()) {
|
|
321
|
+
await options.storage?.clear();
|
|
322
|
+
throw new Agents24ClientError("The persistent session has expired.", {
|
|
323
|
+
kind: "authentication",
|
|
324
|
+
code: "SESSION_EXPIRED"
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return stored;
|
|
329
|
+
};
|
|
330
|
+
const save = async (parsed) => {
|
|
331
|
+
current = parsed.access;
|
|
332
|
+
if (parsed.refreshGrant) refreshGrant = parsed.refreshGrant;
|
|
333
|
+
if (refreshGrant && options.storage) {
|
|
334
|
+
await options.storage.save({
|
|
335
|
+
refreshGrant,
|
|
336
|
+
...current.sessionId ? { sessionId: current.sessionId } : {},
|
|
337
|
+
...parsed.absoluteExpiresAt ? { absoluteExpiresAt: parsed.absoluteExpiresAt } : {}
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
const mint = async (signal) => {
|
|
342
|
+
if (signal?.aborted) throw sessionRequestAborted();
|
|
343
|
+
if (!minting) {
|
|
344
|
+
const url = absoluteUrl(baseUrl, `/public/client-runtime/deployments/${encodePath(options.deploymentId)}/sessions/anonymous`);
|
|
345
|
+
minting = options.mint({ fetch, url, dpopKeyProvider: options.dpopKeyProvider }).then(async (value) => {
|
|
346
|
+
const parsed = parseSessionToken(value, { clock, dpopKeyProvider: options.dpopKeyProvider });
|
|
347
|
+
await save(parsed);
|
|
348
|
+
return parsed.access;
|
|
349
|
+
}).finally(() => {
|
|
350
|
+
minting = void 0;
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
return minting;
|
|
354
|
+
};
|
|
355
|
+
const refresh = async (signal) => coordinator.runExclusive(async (lease) => {
|
|
356
|
+
if (signal?.aborted) throw sessionRequestAborted();
|
|
357
|
+
if (options.storage) {
|
|
358
|
+
const stored = await loadStored();
|
|
359
|
+
if (stored?.refreshGrant) refreshGrant = stored.refreshGrant;
|
|
360
|
+
}
|
|
361
|
+
if (!refreshGrant) return mint(signal);
|
|
362
|
+
await lease.assertActive();
|
|
363
|
+
const response = await dpopTokenRequest({
|
|
364
|
+
fetch,
|
|
365
|
+
url: absoluteUrl(baseUrl, "/public/client-runtime/sessions/refresh"),
|
|
366
|
+
body: { refresh_grant: refreshGrant },
|
|
367
|
+
dpopKeyProvider: options.dpopKeyProvider,
|
|
368
|
+
// Refresh grants rotate exactly once. Never abandon the credential
|
|
369
|
+
// transaction because a consuming request was locally cancelled.
|
|
370
|
+
idempotencyKey: lease.operationId
|
|
371
|
+
});
|
|
372
|
+
if (!response.ok) {
|
|
373
|
+
if (response.status === 400 || response.status === 401 || response.status === 403) {
|
|
374
|
+
await lease.assertActive();
|
|
375
|
+
await options.storage?.clear();
|
|
376
|
+
refreshGrant = void 0;
|
|
377
|
+
current = void 0;
|
|
378
|
+
if (recoverInvalidRefreshWithMint) return mint(signal);
|
|
379
|
+
}
|
|
380
|
+
throw await responseError(response);
|
|
381
|
+
}
|
|
382
|
+
await lease.assertActive();
|
|
383
|
+
const parsed = parseSessionToken(await responseJson(response), { clock, dpopKeyProvider: options.dpopKeyProvider });
|
|
384
|
+
await save(parsed);
|
|
385
|
+
await emit({ type: "session.refreshed", operation: "session" });
|
|
386
|
+
return parsed.access;
|
|
387
|
+
});
|
|
388
|
+
let provider;
|
|
389
|
+
provider = {
|
|
390
|
+
async getAccess(context) {
|
|
391
|
+
if (current && current.expiresAt > clock.now()) return current;
|
|
392
|
+
if (refreshGrant || (await loadStored())?.refreshGrant) return refresh(context.signal);
|
|
393
|
+
return mint(context.signal);
|
|
394
|
+
},
|
|
395
|
+
async refresh(context) {
|
|
396
|
+
return refresh(context.signal);
|
|
397
|
+
},
|
|
398
|
+
async revoke(input) {
|
|
399
|
+
const authenticated = new AuthenticatedHttp({
|
|
400
|
+
baseUrl,
|
|
401
|
+
sessionProvider: provider,
|
|
402
|
+
fetch,
|
|
403
|
+
clock
|
|
404
|
+
});
|
|
405
|
+
await authenticated.json("POST", "/public/client-runtime/sessions/revoke", {
|
|
406
|
+
operation: "sessions.revoke",
|
|
407
|
+
idempotencyKey: input?.idempotencyKey ?? ids.createId(),
|
|
408
|
+
...input?.signal ? { signal: input.signal } : {}
|
|
409
|
+
});
|
|
410
|
+
current = void 0;
|
|
411
|
+
refreshGrant = void 0;
|
|
412
|
+
await options.storage?.clear();
|
|
413
|
+
await emit({ type: "session.revoked", operation: "session" });
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
return provider;
|
|
417
|
+
}
|
|
418
|
+
function createManagedSessionProvider(options) {
|
|
419
|
+
return createManagedSessionProviderInternal(options, false);
|
|
420
|
+
}
|
|
421
|
+
function createAnonymousSessionProvider(options) {
|
|
422
|
+
if ((options.persistent || options.nativeProfileId) && !options.dpopKeyProvider) {
|
|
423
|
+
throw new Agents24ClientError("Persistent and native anonymous sessions require a DPoP key provider.", {
|
|
424
|
+
kind: "configuration",
|
|
425
|
+
code: "MISSING_DPOP_PROVIDER"
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
return createManagedSessionProviderInternal(
|
|
429
|
+
{
|
|
430
|
+
...options,
|
|
431
|
+
async mint({ fetch, url, dpopKeyProvider, signal }) {
|
|
432
|
+
const response = await dpopTokenRequest({
|
|
433
|
+
fetch,
|
|
434
|
+
url,
|
|
435
|
+
body: {
|
|
436
|
+
persistent: options.persistent ?? false,
|
|
437
|
+
requested_capabilities: [...options.requestedCapabilities ?? []],
|
|
438
|
+
...options.nativeProfileId ? { native_profile_id: options.nativeProfileId } : {},
|
|
439
|
+
...dpopKeyProvider ? { dpop_public_jwk: await dpopKeyProvider.getPublicJwk() } : {},
|
|
440
|
+
...options.attestation ? { attestation: options.attestation } : {}
|
|
441
|
+
},
|
|
442
|
+
dpopKeyProvider,
|
|
443
|
+
prooflessInitialRequest: Boolean(dpopKeyProvider),
|
|
444
|
+
signal
|
|
445
|
+
});
|
|
446
|
+
if (!response.ok) throw await responseError(response);
|
|
447
|
+
return responseJson(response);
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
true
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
function createHostedOidcSessionProvider(options) {
|
|
454
|
+
const baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
455
|
+
const fetch = resolveFetch(options.fetch);
|
|
456
|
+
const clock = options.clock ?? defaultClock();
|
|
457
|
+
let current;
|
|
458
|
+
let refreshGrant;
|
|
459
|
+
let enrollmentNonce;
|
|
460
|
+
const ids = options.ids ?? defaultIds();
|
|
461
|
+
let memoryRecord = null;
|
|
462
|
+
const effectiveStorage = {
|
|
463
|
+
async load() {
|
|
464
|
+
return await options.storage?.load() ?? memoryRecord;
|
|
465
|
+
},
|
|
466
|
+
async save(record) {
|
|
467
|
+
memoryRecord = record;
|
|
468
|
+
await options.storage?.save(record);
|
|
469
|
+
},
|
|
470
|
+
async clear() {
|
|
471
|
+
memoryRecord = null;
|
|
472
|
+
await options.storage?.clear();
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
const baseProvider = createManagedSessionProvider({
|
|
476
|
+
...options,
|
|
477
|
+
storage: effectiveStorage,
|
|
478
|
+
async mint() {
|
|
479
|
+
throw new Agents24ClientError("Complete the hosted OIDC authorization before requesting access.", {
|
|
480
|
+
kind: "authentication",
|
|
481
|
+
code: "OIDC_AUTHORIZATION_REQUIRED"
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
return {
|
|
486
|
+
async beginAuthorization(input) {
|
|
487
|
+
const url = absoluteUrl(
|
|
488
|
+
baseUrl,
|
|
489
|
+
`/public/client-runtime/deployments/${encodePath(options.deploymentId)}/sessions/oidc/authorize`
|
|
490
|
+
);
|
|
491
|
+
const response = await dpopTokenRequest({
|
|
492
|
+
fetch,
|
|
493
|
+
url,
|
|
494
|
+
dpopKeyProvider: options.dpopKeyProvider,
|
|
495
|
+
signal: input.signal,
|
|
496
|
+
body: {
|
|
497
|
+
redirect_uri: input.redirectUri,
|
|
498
|
+
code_challenge: input.codeChallenge,
|
|
499
|
+
code_challenge_method: "S256",
|
|
500
|
+
persistent: true,
|
|
501
|
+
dpop_public_jwk: await options.dpopKeyProvider.getPublicJwk(),
|
|
502
|
+
...input.nativeProfileId ? { native_profile_id: input.nativeProfileId } : {}
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
if (!response.ok) throw await responseError(response);
|
|
506
|
+
const body = await responseJson(response);
|
|
507
|
+
if (typeof body.authorization_url !== "string") {
|
|
508
|
+
throw new Agents24ClientError("The OIDC endpoint returned an invalid authorization URL.", {
|
|
509
|
+
kind: "protocol",
|
|
510
|
+
code: "INVALID_OIDC_RESPONSE"
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
if (typeof body.dpop_nonce !== "string" || !body.dpop_nonce) {
|
|
514
|
+
throw new Agents24ClientError("The OIDC endpoint did not return a DPoP enrollment nonce.", {
|
|
515
|
+
kind: "protocol",
|
|
516
|
+
code: "INVALID_OIDC_RESPONSE"
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
enrollmentNonce = body.dpop_nonce;
|
|
520
|
+
return { authorizationUrl: body.authorization_url };
|
|
521
|
+
},
|
|
522
|
+
async exchange(input) {
|
|
523
|
+
const response = await dpopTokenRequest({
|
|
524
|
+
fetch,
|
|
525
|
+
url: absoluteUrl(
|
|
526
|
+
baseUrl,
|
|
527
|
+
`/public/client-runtime/deployments/${encodePath(options.deploymentId)}/sessions/oidc/exchange`
|
|
528
|
+
),
|
|
529
|
+
dpopKeyProvider: options.dpopKeyProvider,
|
|
530
|
+
signal: input.signal,
|
|
531
|
+
idempotencyKey: ids.createId(),
|
|
532
|
+
nonce: enrollmentNonce,
|
|
533
|
+
body: { code: input.code, code_verifier: input.codeVerifier }
|
|
534
|
+
});
|
|
535
|
+
if (!response.ok) throw await responseError(response);
|
|
536
|
+
const parsed = parseSessionToken(await responseJson(response), {
|
|
537
|
+
clock,
|
|
538
|
+
dpopKeyProvider: options.dpopKeyProvider
|
|
539
|
+
});
|
|
540
|
+
current = parsed.access;
|
|
541
|
+
enrollmentNonce = void 0;
|
|
542
|
+
refreshGrant = parsed.refreshGrant;
|
|
543
|
+
if (refreshGrant) {
|
|
544
|
+
await effectiveStorage.save({
|
|
545
|
+
refreshGrant,
|
|
546
|
+
...current.sessionId ? { sessionId: current.sessionId } : {},
|
|
547
|
+
...parsed.absoluteExpiresAt ? { absoluteExpiresAt: parsed.absoluteExpiresAt } : {}
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
return current;
|
|
551
|
+
},
|
|
552
|
+
async getAccess(context) {
|
|
553
|
+
if (current && current.expiresAt > clock.now()) return current;
|
|
554
|
+
return baseProvider.getAccess(context);
|
|
555
|
+
},
|
|
556
|
+
async refresh(context) {
|
|
557
|
+
return baseProvider.refresh?.(context) ?? Promise.reject(new Error("Refresh unavailable"));
|
|
558
|
+
},
|
|
559
|
+
async revoke(input) {
|
|
560
|
+
await baseProvider.revoke?.(input);
|
|
561
|
+
current = void 0;
|
|
562
|
+
refreshGrant = void 0;
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export { createAnonymousSessionProvider, createFencedRefreshCoordinator, createHostedOidcSessionProvider, createInMemoryRefreshCoordinator, createManagedSessionProvider, createPkcePair, createStaticSessionProvider, createWebCryptoDpopKeyProvider };
|
|
568
|
+
//# sourceMappingURL=chunk-FNR3KPB6.js.map
|
|
569
|
+
//# sourceMappingURL=chunk-FNR3KPB6.js.map
|