@pymthouse/builder-sdk 0.1.0 → 0.3.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.
Files changed (77) hide show
  1. package/README.md +66 -0
  2. package/dist/{client-BroVFyIy.d.ts → client-BHfjDvIe.d.ts} +49 -1
  3. package/dist/{client-BhC1YhB1.d.cts → client-CvhJEhjV.d.cts} +49 -1
  4. package/dist/config.cjs +59 -3
  5. package/dist/config.cjs.map +1 -1
  6. package/dist/config.d.cts +8 -1
  7. package/dist/config.d.ts +8 -1
  8. package/dist/config.js +57 -4
  9. package/dist/config.js.map +1 -1
  10. package/dist/device-initiate.cjs +1 -1
  11. package/dist/device-initiate.cjs.map +1 -1
  12. package/dist/device-initiate.js +1 -1
  13. package/dist/device-initiate.js.map +1 -1
  14. package/dist/device.cjs +1 -1
  15. package/dist/device.cjs.map +1 -1
  16. package/dist/device.d.cts +1 -1
  17. package/dist/device.d.ts +1 -1
  18. package/dist/device.js +1 -1
  19. package/dist/device.js.map +1 -1
  20. package/dist/env.cjs +794 -36
  21. package/dist/env.cjs.map +1 -1
  22. package/dist/env.d.cts +2 -2
  23. package/dist/env.d.ts +2 -2
  24. package/dist/env.js +794 -36
  25. package/dist/env.js.map +1 -1
  26. package/dist/gateway/client/index.cjs +492 -0
  27. package/dist/gateway/client/index.cjs.map +1 -0
  28. package/dist/gateway/client/index.d.cts +63 -0
  29. package/dist/gateway/client/index.d.ts +63 -0
  30. package/dist/gateway/client/index.js +489 -0
  31. package/dist/gateway/client/index.js.map +1 -0
  32. package/dist/gateway/index.cjs +16 -0
  33. package/dist/gateway/index.cjs.map +1 -0
  34. package/dist/gateway/index.d.cts +52 -0
  35. package/dist/gateway/index.d.ts +52 -0
  36. package/dist/gateway/index.js +10 -0
  37. package/dist/gateway/index.js.map +1 -0
  38. package/dist/gateway/server/index.cjs +1248 -0
  39. package/dist/gateway/server/index.cjs.map +1 -0
  40. package/dist/gateway/server/index.d.cts +31 -0
  41. package/dist/gateway/server/index.d.ts +31 -0
  42. package/dist/gateway/server/index.js +1233 -0
  43. package/dist/gateway/server/index.js.map +1 -0
  44. package/dist/index.cjs +1075 -186
  45. package/dist/index.cjs.map +1 -1
  46. package/dist/index.d.cts +6 -4
  47. package/dist/index.d.ts +6 -4
  48. package/dist/index.js +1042 -163
  49. package/dist/index.js.map +1 -1
  50. package/dist/ingest-B3Yi8Tb1.d.cts +271 -0
  51. package/dist/ingest-DoKJTWU9.d.ts +271 -0
  52. package/dist/plan-pricing.cjs +108 -0
  53. package/dist/plan-pricing.cjs.map +1 -0
  54. package/dist/plan-pricing.d.cts +15 -0
  55. package/dist/plan-pricing.d.ts +15 -0
  56. package/dist/plan-pricing.js +98 -0
  57. package/dist/plan-pricing.js.map +1 -0
  58. package/dist/signer/server.cjs +1366 -0
  59. package/dist/signer/server.cjs.map +1 -0
  60. package/dist/signer/server.d.cts +73 -0
  61. package/dist/signer/server.d.ts +73 -0
  62. package/dist/signer/server.js +1331 -0
  63. package/dist/signer/server.js.map +1 -0
  64. package/dist/tokens.d.cts +1 -1
  65. package/dist/tokens.d.ts +1 -1
  66. package/dist/types-_R1AwEZp.d.cts +343 -0
  67. package/dist/types-_R1AwEZp.d.ts +343 -0
  68. package/dist/verify.cjs +1 -1
  69. package/dist/verify.cjs.map +1 -1
  70. package/dist/verify.d.cts +1 -1
  71. package/dist/verify.d.ts +1 -1
  72. package/dist/verify.js +1 -1
  73. package/dist/verify.js.map +1 -1
  74. package/gateway/proto/lp_rpc.proto +542 -0
  75. package/package.json +42 -1
  76. package/dist/types-rKzVXvMu.d.cts +0 -196
  77. package/dist/types-rKzVXvMu.d.ts +0 -196
package/dist/index.js CHANGED
@@ -1,6 +1,830 @@
1
1
  import { discoveryRequest, processDiscoveryResponse, genericTokenEndpointRequest, processGenericTokenEndpointResponse, clientCredentialsGrantRequest, processClientCredentialsResponse, customFetch, allowInsecureRequests, ResponseBodyError, OperationProcessingError } from 'oauth4webapi';
2
2
  import { createHash } from 'crypto';
3
3
 
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+
14
+ // src/encoding.ts
15
+ function encodeClientSecretBasic(clientId, clientSecret) {
16
+ const raw = `${clientId}:${clientSecret}`;
17
+ const b64 = typeof Buffer !== "undefined" ? Buffer.from(raw, "utf8").toString("base64") : btoa(Array.from(new TextEncoder().encode(raw), (c) => String.fromCharCode(c)).join(""));
18
+ return `Basic ${b64}`;
19
+ }
20
+ var init_encoding = __esm({
21
+ "src/encoding.ts"() {
22
+ }
23
+ });
24
+
25
+ // src/errors.ts
26
+ function toPmtHouseError(error, fallbackMessage) {
27
+ if (error instanceof PmtHouseError) {
28
+ return error;
29
+ }
30
+ if (error instanceof Error) {
31
+ return new PmtHouseError(error.message || fallbackMessage, {
32
+ code: "unexpected_error",
33
+ status: 500
34
+ });
35
+ }
36
+ return new PmtHouseError(fallbackMessage, {
37
+ code: "unexpected_error",
38
+ status: 500
39
+ });
40
+ }
41
+ var PmtHouseError;
42
+ var init_errors = __esm({
43
+ "src/errors.ts"() {
44
+ PmtHouseError = class extends Error {
45
+ status;
46
+ code;
47
+ details;
48
+ constructor(message, {
49
+ status = 500,
50
+ code = "pymthouse_error",
51
+ details
52
+ } = {}) {
53
+ super(message);
54
+ this.name = "PmtHouseError";
55
+ this.status = status;
56
+ this.code = code;
57
+ this.details = details;
58
+ }
59
+ };
60
+ }
61
+ });
62
+
63
+ // src/string-utils.ts
64
+ function stripTrailingSlashes(value) {
65
+ let end = value.length;
66
+ while (end > 0 && (value.codePointAt(end - 1) ?? 0) === 47) {
67
+ end--;
68
+ }
69
+ return value.slice(0, end);
70
+ }
71
+ function endsWithIgnoreCase(value, suffix) {
72
+ if (suffix.length > value.length) {
73
+ return false;
74
+ }
75
+ const start = value.length - suffix.length;
76
+ for (let i = 0; i < suffix.length; i++) {
77
+ const a = value.codePointAt(start + i) ?? 0;
78
+ const b = suffix.codePointAt(i) ?? 0;
79
+ if (a !== b && (a | 32) !== (b | 32)) {
80
+ return false;
81
+ }
82
+ }
83
+ return true;
84
+ }
85
+ function stripSuffixIgnoreCase(value, suffix) {
86
+ return endsWithIgnoreCase(value, suffix) ? value.slice(0, value.length - suffix.length) : value;
87
+ }
88
+ function stripOidcPathSuffix(issuerUrl) {
89
+ let base = stripTrailingSlashes(issuerUrl.trim());
90
+ base = stripSuffixIgnoreCase(base, "/oidc");
91
+ return stripTrailingSlashes(base);
92
+ }
93
+ function stripIssuerOriginFromOidcUrl(issuerUrl) {
94
+ let base = stripTrailingSlashes(issuerUrl.trim());
95
+ base = stripSuffixIgnoreCase(base, "/api/v1/oidc");
96
+ base = stripSuffixIgnoreCase(base, "/oidc");
97
+ return stripTrailingSlashes(base);
98
+ }
99
+ var init_string_utils = __esm({
100
+ "src/string-utils.ts"() {
101
+ }
102
+ });
103
+ function authorizationServerToOidcDocument(as) {
104
+ const tokenEndpoint = as.token_endpoint;
105
+ const jwksUri = as.jwks_uri;
106
+ if (!tokenEndpoint || !jwksUri) {
107
+ throw new PmtHouseError("OIDC discovery document is missing token_endpoint or jwks_uri", {
108
+ status: 500,
109
+ code: "oidc_discovery_invalid"
110
+ });
111
+ }
112
+ return {
113
+ issuer: as.issuer,
114
+ authorization_endpoint: as.authorization_endpoint ?? "",
115
+ token_endpoint: tokenEndpoint,
116
+ jwks_uri: jwksUri,
117
+ userinfo_endpoint: as.userinfo_endpoint,
118
+ device_authorization_endpoint: as.device_authorization_endpoint
119
+ };
120
+ }
121
+ function normalizedIssuerKey(issuerUrl) {
122
+ return stripTrailingSlashes(issuerUrl);
123
+ }
124
+ async function loadAuthorizationServer(issuerUrl, fetchImpl, options = {}) {
125
+ const key = normalizedIssuerKey(issuerUrl);
126
+ const now = Date.now();
127
+ const cached = discoveryCache.get(key);
128
+ if (!options.force && cached && now - cached.fetchedAt < CACHE_TTL_MS) {
129
+ return cached.as;
130
+ }
131
+ const issuerIdentifier = new URL(key);
132
+ const discoveryOpts = {
133
+ algorithm: "oidc",
134
+ [customFetch]: fetchImpl
135
+ };
136
+ if (options.allowInsecureHttp) {
137
+ discoveryOpts[allowInsecureRequests] = true;
138
+ }
139
+ let response;
140
+ try {
141
+ response = await discoveryRequest(issuerIdentifier, discoveryOpts);
142
+ } catch (e) {
143
+ throw mapDiscoveryNetworkError(e);
144
+ }
145
+ let as;
146
+ try {
147
+ as = await processDiscoveryResponse(issuerIdentifier, response);
148
+ } catch (e) {
149
+ throw mapOAuthDiscoveryError(e);
150
+ }
151
+ discoveryCache.set(key, { as, fetchedAt: now });
152
+ return as;
153
+ }
154
+ async function fetchDiscoveryDocument(issuerUrl, fetchImpl, options = {}) {
155
+ const as = await loadAuthorizationServer(issuerUrl, fetchImpl, options);
156
+ return authorizationServerToOidcDocument(as);
157
+ }
158
+ function clearDiscoveryCache(issuerUrl) {
159
+ if (!issuerUrl) {
160
+ discoveryCache.clear();
161
+ return;
162
+ }
163
+ discoveryCache.delete(normalizedIssuerKey(issuerUrl));
164
+ }
165
+ function mapOAuthDiscoveryError(error) {
166
+ if (error instanceof PmtHouseError) {
167
+ return error;
168
+ }
169
+ if (error instanceof Error) {
170
+ return new PmtHouseError(error.message, {
171
+ status: 500,
172
+ code: "oidc_discovery_invalid",
173
+ details: { cause: error.cause }
174
+ });
175
+ }
176
+ return new PmtHouseError("OIDC discovery failed", {
177
+ status: 500,
178
+ code: "oidc_discovery_invalid"
179
+ });
180
+ }
181
+ function mapDiscoveryNetworkError(error) {
182
+ if (error instanceof PmtHouseError) {
183
+ return error;
184
+ }
185
+ if (error instanceof Error) {
186
+ return new PmtHouseError(`Failed to load OIDC discovery: ${error.message}`, {
187
+ status: 502,
188
+ code: "oidc_discovery_failed"
189
+ });
190
+ }
191
+ return new PmtHouseError("Failed to load OIDC discovery", {
192
+ status: 502,
193
+ code: "oidc_discovery_failed"
194
+ });
195
+ }
196
+ var CACHE_TTL_MS, discoveryCache;
197
+ var init_discovery = __esm({
198
+ "src/discovery.ts"() {
199
+ init_errors();
200
+ init_string_utils();
201
+ CACHE_TTL_MS = 5 * 60 * 1e3;
202
+ discoveryCache = /* @__PURE__ */ new Map();
203
+ }
204
+ });
205
+
206
+ // src/signer/fetch-json.ts
207
+ function oauthFailureDescription(parsed, failureLabel, status) {
208
+ if (typeof parsed.error_description === "string") {
209
+ return parsed.error_description;
210
+ }
211
+ if (typeof parsed.error === "string") {
212
+ return parsed.error;
213
+ }
214
+ return `${failureLabel} (${status})`;
215
+ }
216
+ async function readJsonObjectFromResponse(response, options) {
217
+ const text = await response.text();
218
+ let parsed;
219
+ try {
220
+ parsed = text ? JSON.parse(text) : {};
221
+ } catch {
222
+ throw new PmtHouseError(options.invalidJsonMessage, {
223
+ status: 502,
224
+ code: options.invalidJsonCode,
225
+ details: { status: response.status }
226
+ });
227
+ }
228
+ if (!response.ok) {
229
+ const description = oauthFailureDescription(parsed, options.failureLabel, response.status);
230
+ throw new PmtHouseError(description, {
231
+ status: response.status,
232
+ code: typeof parsed.error === "string" ? parsed.error : options.defaultErrorCode,
233
+ details: parsed
234
+ });
235
+ }
236
+ return parsed;
237
+ }
238
+ var init_fetch_json = __esm({
239
+ "src/signer/fetch-json.ts"() {
240
+ init_errors();
241
+ }
242
+ });
243
+
244
+ // src/signer/handler-errors.ts
245
+ function signerHandlerErrorResponse(error) {
246
+ if (error instanceof PmtHouseError) {
247
+ return new Response(
248
+ JSON.stringify({
249
+ error: error.code,
250
+ error_description: error.message,
251
+ details: error.details
252
+ }),
253
+ {
254
+ status: error.status,
255
+ headers: { "Content-Type": "application/json" }
256
+ }
257
+ );
258
+ }
259
+ const message = error instanceof Error ? error.message : "Internal error";
260
+ return new Response(JSON.stringify({ error: "internal_error", error_description: message }), {
261
+ status: 500,
262
+ headers: { "Content-Type": "application/json" }
263
+ });
264
+ }
265
+ var init_handler_errors = __esm({
266
+ "src/signer/handler-errors.ts"() {
267
+ init_errors();
268
+ }
269
+ });
270
+
271
+ // src/signer/json-fields.ts
272
+ function readStringField(body, key, errorCode, messagePrefix = "Response") {
273
+ const value = body[key];
274
+ if (typeof value !== "string" || !value.trim()) {
275
+ throw new PmtHouseError(`${messagePrefix} missing ${key}`, {
276
+ status: 502,
277
+ code: errorCode
278
+ });
279
+ }
280
+ return value.trim();
281
+ }
282
+ function readExpiresIn(body, errorCode) {
283
+ const expiresIn = body.expires_in;
284
+ if (typeof expiresIn !== "number" || !Number.isFinite(expiresIn) || expiresIn <= 0) {
285
+ throw new PmtHouseError("Response missing expires_in", {
286
+ status: 502,
287
+ code: errorCode
288
+ });
289
+ }
290
+ return Math.floor(expiresIn);
291
+ }
292
+ var init_json_fields = __esm({
293
+ "src/signer/json-fields.ts"() {
294
+ init_errors();
295
+ }
296
+ });
297
+
298
+ // src/signer/mint-token.ts
299
+ function parseMintUserSignerTokenResponse(body, ttlRefreshRatio = DEFAULT_TTL_REFRESH_RATIO) {
300
+ const accessToken = readStringField(body, "access_token", TOKEN_RESPONSE_ERROR, "Token response");
301
+ const expiresIn = readExpiresIn(body, TOKEN_RESPONSE_ERROR);
302
+ const balanceUsdMicros = readStringField(
303
+ body,
304
+ "balanceUsdMicros",
305
+ TOKEN_RESPONSE_ERROR,
306
+ "Token response"
307
+ );
308
+ const lifetimeGrantedUsdMicros = readStringField(
309
+ body,
310
+ "lifetimeGrantedUsdMicros",
311
+ TOKEN_RESPONSE_ERROR,
312
+ "Token response"
313
+ );
314
+ const now = Date.now();
315
+ const expiresAt = now + expiresIn * 1e3;
316
+ const refreshAt = now + Math.floor(expiresIn * 1e3 * ttlRefreshRatio);
317
+ return {
318
+ jwt: accessToken,
319
+ expiresAt,
320
+ refreshAt,
321
+ balanceUsdMicros,
322
+ lifetimeGrantedUsdMicros
323
+ };
324
+ }
325
+ var LIVEPEER_REMOTE_SIGNER_AUDIENCE, DEFAULT_TTL_REFRESH_RATIO, TOKEN_RESPONSE_ERROR;
326
+ var init_mint_token = __esm({
327
+ "src/signer/mint-token.ts"() {
328
+ init_json_fields();
329
+ LIVEPEER_REMOTE_SIGNER_AUDIENCE = "livepeer-remote-signer";
330
+ DEFAULT_TTL_REFRESH_RATIO = 0.8;
331
+ TOKEN_RESPONSE_ERROR = "invalid_token_response";
332
+ }
333
+ });
334
+
335
+ // src/signer/device-exchange.ts
336
+ function extractSignerAccessTokenFromExchangeBody(body) {
337
+ const tokenObj = body.token;
338
+ if (tokenObj !== null && typeof tokenObj === "object" && !Array.isArray(tokenObj)) {
339
+ const nested = tokenObj;
340
+ for (const key of ["accessToken", "access_token"]) {
341
+ const value = nested[key];
342
+ if (typeof value === "string" && value.trim()) {
343
+ return value.trim();
344
+ }
345
+ }
346
+ }
347
+ for (const key of ["accessToken", "access_token"]) {
348
+ const value = body[key];
349
+ if (typeof value === "string" && value.trim()) {
350
+ return value.trim();
351
+ }
352
+ }
353
+ throw new PmtHouseError("Device exchange response missing signer access token", {
354
+ status: 502,
355
+ code: "invalid_exchange_response"
356
+ });
357
+ }
358
+ function normalizeDeviceExchangeResponse(minted, options) {
359
+ const scope = minted.scope.trim() || "sign:job";
360
+ const body = {
361
+ access_token: minted.access_token,
362
+ token_type: "Bearer",
363
+ expires_in: minted.expires_in,
364
+ scope,
365
+ balanceUsdMicros: minted.balanceUsdMicros,
366
+ lifetimeGrantedUsdMicros: minted.lifetimeGrantedUsdMicros,
367
+ token: {
368
+ accessToken: minted.access_token,
369
+ access_token: minted.access_token,
370
+ expiresIn: minted.expires_in,
371
+ expires_in: minted.expires_in,
372
+ scope,
373
+ balanceUsdMicros: minted.balanceUsdMicros,
374
+ lifetimeGrantedUsdMicros: minted.lifetimeGrantedUsdMicros
375
+ }
376
+ };
377
+ const signerUrl = options?.signerUrl?.trim();
378
+ if (signerUrl) {
379
+ body.signerUrl = signerUrl;
380
+ }
381
+ return body;
382
+ }
383
+ async function mintSignerTokenFromDeviceToken(options) {
384
+ const fetchImpl = options.fetch ?? fetch;
385
+ const issuerUrl = stripTrailingSlashes(options.issuerUrl);
386
+ const as = await loadAuthorizationServer(issuerUrl, fetchImpl, {
387
+ allowInsecureHttp: options.allowInsecureHttp
388
+ });
389
+ const tokenEndpoint = as.token_endpoint;
390
+ if (!tokenEndpoint) {
391
+ throw new PmtHouseError("OIDC discovery document is missing token_endpoint", {
392
+ status: 500,
393
+ code: "oidc_discovery_invalid"
394
+ });
395
+ }
396
+ const audience = options.audience?.trim() || LIVEPEER_REMOTE_SIGNER_AUDIENCE;
397
+ const params = new URLSearchParams({
398
+ grant_type: TOKEN_EXCHANGE_GRANT,
399
+ subject_token: options.deviceToken,
400
+ subject_token_type: SUBJECT_ACCESS_TOKEN_TYPE,
401
+ audience,
402
+ resource: audience
403
+ });
404
+ if (options.scope?.trim()) {
405
+ params.set("scope", options.scope.trim());
406
+ }
407
+ const response = await fetchImpl(tokenEndpoint, {
408
+ method: "POST",
409
+ headers: {
410
+ Authorization: encodeClientSecretBasic(options.m2mClientId, options.m2mClientSecret),
411
+ "Content-Type": "application/x-www-form-urlencoded",
412
+ Accept: "application/json"
413
+ },
414
+ body: params.toString(),
415
+ cache: "no-store"
416
+ });
417
+ const parsed = await readJsonObjectFromResponse(response, {
418
+ invalidJsonMessage: "Token endpoint returned invalid JSON",
419
+ invalidJsonCode: "invalid_token_response",
420
+ failureLabel: "Signer JWT exchange failed",
421
+ defaultErrorCode: "token_exchange_failed"
422
+ });
423
+ const cached = parseMintUserSignerTokenResponse(parsed);
424
+ return {
425
+ access_token: cached.jwt,
426
+ expires_in: readExpiresIn(parsed, EXCHANGE_RESPONSE_ERROR),
427
+ scope: readStringField(parsed, "scope", EXCHANGE_RESPONSE_ERROR),
428
+ balanceUsdMicros: cached.balanceUsdMicros,
429
+ lifetimeGrantedUsdMicros: cached.lifetimeGrantedUsdMicros
430
+ };
431
+ }
432
+ var TOKEN_EXCHANGE_GRANT, SUBJECT_ACCESS_TOKEN_TYPE, EXCHANGE_RESPONSE_ERROR;
433
+ var init_device_exchange = __esm({
434
+ "src/signer/device-exchange.ts"() {
435
+ init_discovery();
436
+ init_encoding();
437
+ init_errors();
438
+ init_string_utils();
439
+ init_fetch_json();
440
+ init_json_fields();
441
+ init_mint_token();
442
+ TOKEN_EXCHANGE_GRANT = "urn:ietf:params:oauth:grant-type:token-exchange";
443
+ SUBJECT_ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
444
+ EXCHANGE_RESPONSE_ERROR = "invalid_exchange_response";
445
+ }
446
+ });
447
+
448
+ // src/signer/api-key-exchange.ts
449
+ var api_key_exchange_exports = {};
450
+ __export(api_key_exchange_exports, {
451
+ createApiKeyExchangeHandler: () => createApiKeyExchangeHandler,
452
+ exchangeApiKeyForSigner: () => exchangeApiKeyForSigner,
453
+ mintSignerSessionFromApiKey: () => mintSignerSessionFromApiKey,
454
+ mintUserAccessTokenFromApiKey: () => mintUserAccessTokenFromApiKey,
455
+ parseApiKeyExchangeRequestBody: () => parseApiKeyExchangeRequestBody
456
+ });
457
+ async function parseApiKeyExchangeRequestBody(request) {
458
+ let body;
459
+ try {
460
+ body = await request.json();
461
+ } catch {
462
+ throw new PmtHouseError("Request body must be JSON", {
463
+ status: 400,
464
+ code: "invalid_request"
465
+ });
466
+ }
467
+ if (body === null || typeof body !== "object" || Array.isArray(body)) {
468
+ throw new PmtHouseError("Request body must be a JSON object", {
469
+ status: 400,
470
+ code: "invalid_request"
471
+ });
472
+ }
473
+ const record = body;
474
+ const apiKeyRaw = record.apiKey;
475
+ if (typeof apiKeyRaw !== "string" || !apiKeyRaw.trim()) {
476
+ throw new PmtHouseError("Request body must include apiKey", {
477
+ status: 400,
478
+ code: "invalid_request"
479
+ });
480
+ }
481
+ const scope = typeof record.scope === "string" && record.scope.trim() ? record.scope.trim() : void 0;
482
+ const clientId = typeof record.clientId === "string" && record.clientId.trim() ? record.clientId.trim() : void 0;
483
+ return { apiKey: apiKeyRaw.trim(), scope, clientId };
484
+ }
485
+ async function mintUserAccessTokenFromApiKey(input) {
486
+ const fetchImpl = input.fetch ?? fetch;
487
+ const issuerOrigin = stripIssuerOriginFromOidcUrl(input.issuerUrl);
488
+ const url = `${issuerOrigin}/api/v1/apps/${encodeURIComponent(input.publicClientId)}/auth/api-key/token`;
489
+ const response = await fetchImpl(url, {
490
+ method: "POST",
491
+ headers: {
492
+ Authorization: `Bearer ${input.apiKey}`,
493
+ "Content-Type": "application/json",
494
+ Accept: "application/json"
495
+ },
496
+ body: JSON.stringify(input.scope ? { scope: input.scope } : {}),
497
+ cache: "no-store"
498
+ });
499
+ const parsed = await readJsonObjectFromResponse(response, {
500
+ invalidJsonMessage: "API key token exchange returned invalid JSON",
501
+ invalidJsonCode: "invalid_token_response",
502
+ failureLabel: "API key token exchange failed",
503
+ defaultErrorCode: "api_key_token_exchange_failed"
504
+ });
505
+ const accessToken = parsed.access_token;
506
+ if (typeof accessToken !== "string" || !accessToken.trim()) {
507
+ throw new PmtHouseError("API key token exchange missing access_token", {
508
+ status: 502,
509
+ code: EXCHANGE_RESPONSE_ERROR2
510
+ });
511
+ }
512
+ const expiresIn = typeof parsed.expires_in === "number" && Number.isFinite(parsed.expires_in) ? parsed.expires_in : 900;
513
+ const scope = typeof parsed.scope === "string" && parsed.scope.trim() ? parsed.scope.trim() : input.scope?.trim() || "sign:job";
514
+ return {
515
+ access_token: accessToken.trim(),
516
+ expires_in: expiresIn,
517
+ scope
518
+ };
519
+ }
520
+ async function mintSignerSessionFromApiKey(input) {
521
+ const userToken = await mintUserAccessTokenFromApiKey({
522
+ issuerUrl: input.issuerUrl,
523
+ publicClientId: input.publicClientId,
524
+ apiKey: input.apiKey,
525
+ scope: input.scope,
526
+ fetch: input.fetch
527
+ });
528
+ return mintSignerTokenFromDeviceToken({
529
+ issuerUrl: input.issuerUrl,
530
+ m2mClientId: input.m2mClientId,
531
+ m2mClientSecret: input.m2mClientSecret,
532
+ deviceToken: userToken.access_token,
533
+ scope: userToken.scope,
534
+ audience: input.audience,
535
+ fetch: input.fetch,
536
+ allowInsecureHttp: input.allowInsecureHttp
537
+ });
538
+ }
539
+ async function exchangeApiKeyForSigner(options) {
540
+ const fetchImpl = options.fetch ?? fetch;
541
+ const url = `${stripTrailingSlashes(options.facadeUrl)}/api/pymthouse/keys/exchange`;
542
+ const body = { apiKey: options.apiKey };
543
+ if (options.scope?.trim()) {
544
+ body.scope = options.scope.trim();
545
+ }
546
+ if (options.clientId?.trim()) {
547
+ body.clientId = options.clientId.trim();
548
+ }
549
+ const response = await fetchImpl(url, {
550
+ method: "POST",
551
+ headers: {
552
+ "Content-Type": "application/json",
553
+ Accept: "application/json"
554
+ },
555
+ body: JSON.stringify(body),
556
+ cache: "no-store"
557
+ });
558
+ const parsed = await readJsonObjectFromResponse(response, {
559
+ invalidJsonMessage: "API key exchange returned invalid JSON",
560
+ invalidJsonCode: EXCHANGE_RESPONSE_ERROR2,
561
+ failureLabel: "API key exchange failed",
562
+ defaultErrorCode: "api_key_exchange_failed"
563
+ });
564
+ const accessToken = extractSignerAccessTokenFromExchangeBody(parsed);
565
+ const signerUrlRaw = parsed.signerUrl ?? parsed.signer_url;
566
+ const signerUrl = typeof signerUrlRaw === "string" && signerUrlRaw.trim() ? signerUrlRaw.trim() : void 0;
567
+ return normalizeDeviceExchangeResponse(
568
+ {
569
+ access_token: accessToken,
570
+ expires_in: typeof parsed.expires_in === "number" && Number.isFinite(parsed.expires_in) ? parsed.expires_in : 3600,
571
+ scope: typeof parsed.scope === "string" && parsed.scope.trim() ? parsed.scope.trim() : "sign:job",
572
+ balanceUsdMicros: typeof parsed.balanceUsdMicros === "string" ? parsed.balanceUsdMicros : "0",
573
+ lifetimeGrantedUsdMicros: typeof parsed.lifetimeGrantedUsdMicros === "string" ? parsed.lifetimeGrantedUsdMicros : "0"
574
+ },
575
+ { signerUrl }
576
+ );
577
+ }
578
+ function createApiKeyExchangeHandler(config) {
579
+ const publicClientId = config.publicClientId.trim();
580
+ return async function apiKeyExchangeHandler(request) {
581
+ try {
582
+ if (request.method !== "POST") {
583
+ return new Response(JSON.stringify({ error: "method_not_allowed" }), {
584
+ status: 405,
585
+ headers: { "Content-Type": "application/json" }
586
+ });
587
+ }
588
+ const parsed = await parseApiKeyExchangeRequestBody(request);
589
+ const effectiveClientId = parsed.clientId?.trim() || publicClientId;
590
+ if (effectiveClientId !== publicClientId) {
591
+ throw new PmtHouseError("clientId does not match configured public client", {
592
+ status: 400,
593
+ code: "invalid_request"
594
+ });
595
+ }
596
+ const minted = await mintSignerSessionFromApiKey({
597
+ issuerUrl: config.issuerUrl,
598
+ publicClientId,
599
+ m2mClientId: config.m2mClientId,
600
+ m2mClientSecret: config.m2mClientSecret,
601
+ apiKey: parsed.apiKey,
602
+ scope: parsed.scope,
603
+ audience: config.audience,
604
+ fetch: config.fetch,
605
+ allowInsecureHttp: config.allowInsecureHttp
606
+ });
607
+ const signerUrlValue = typeof config.signerUrl === "string" && config.signerUrl.trim() ? config.signerUrl.trim() : void 0;
608
+ const body = normalizeDeviceExchangeResponse(minted, { signerUrl: signerUrlValue });
609
+ return new Response(JSON.stringify(body), {
610
+ status: 200,
611
+ headers: {
612
+ "Content-Type": "application/json",
613
+ "Cache-Control": "no-store"
614
+ }
615
+ });
616
+ } catch (error) {
617
+ return signerHandlerErrorResponse(error);
618
+ }
619
+ };
620
+ }
621
+ var EXCHANGE_RESPONSE_ERROR2;
622
+ var init_api_key_exchange = __esm({
623
+ "src/signer/api-key-exchange.ts"() {
624
+ init_string_utils();
625
+ init_errors();
626
+ init_fetch_json();
627
+ init_handler_errors();
628
+ init_device_exchange();
629
+ EXCHANGE_RESPONSE_ERROR2 = "invalid_exchange_response";
630
+ }
631
+ });
632
+
633
+ // src/plan-pricing.ts
634
+ var NETWORK_USD_PER_MICRO = 1e-6;
635
+ var RETAIL_RATE_DECIMALS = 9;
636
+ function trimFixedDecimalZeros(fixed) {
637
+ const dotIndex = fixed.indexOf(".");
638
+ if (dotIndex === -1) {
639
+ return fixed;
640
+ }
641
+ let end = fixed.length;
642
+ while (end > dotIndex + 1 && fixed[end - 1] === "0") {
643
+ end -= 1;
644
+ }
645
+ if (end === dotIndex + 1) {
646
+ end = dotIndex;
647
+ }
648
+ const trimmed = fixed.slice(0, end);
649
+ return trimmed.length > 0 ? trimmed : "0";
650
+ }
651
+ function defaultRetailRateUsd() {
652
+ return formatRetailRateUsd(NETWORK_USD_PER_MICRO);
653
+ }
654
+ function formatRetailRateUsd(value) {
655
+ if (!Number.isFinite(value) || value < 0) {
656
+ return defaultRetailRateUsd();
657
+ }
658
+ return trimFixedDecimalZeros(value.toFixed(RETAIL_RATE_DECIMALS));
659
+ }
660
+ function parseRetailRateUsd(raw) {
661
+ if (raw === null || raw === void 0) {
662
+ return null;
663
+ }
664
+ const trimmed = String(raw).trim();
665
+ if (!trimmed) {
666
+ return null;
667
+ }
668
+ const n = Number(trimmed);
669
+ if (!Number.isFinite(n) || n < 0) {
670
+ return null;
671
+ }
672
+ return formatRetailRateUsd(n);
673
+ }
674
+ function markupPercentToRetailRateUsd(markupPercent) {
675
+ const pct = Number.isFinite(markupPercent) ? Math.max(0, markupPercent) : 0;
676
+ return formatRetailRateUsd(NETWORK_USD_PER_MICRO * (1 + pct / 100));
677
+ }
678
+ function retailRateUsdToMarkupPercent(raw) {
679
+ const rate = parseRetailRateUsd(raw);
680
+ if (!rate) {
681
+ return "";
682
+ }
683
+ const n = Number(rate);
684
+ if (!Number.isFinite(n) || n <= NETWORK_USD_PER_MICRO) {
685
+ return n === NETWORK_USD_PER_MICRO ? "0" : "";
686
+ }
687
+ const pct = (n / NETWORK_USD_PER_MICRO - 1) * 100;
688
+ if (!Number.isFinite(pct) || pct <= 0) {
689
+ return "";
690
+ }
691
+ return pct % 1 === 0 ? String(Math.round(pct)) : pct.toFixed(1);
692
+ }
693
+ function retailRateUsdPerMillion(raw) {
694
+ const rate = parseRetailRateUsd(raw);
695
+ if (!rate) {
696
+ return "";
697
+ }
698
+ const perM = Number(rate) * 1e6;
699
+ if (!Number.isFinite(perM)) {
700
+ return "";
701
+ }
702
+ return perM.toFixed(2);
703
+ }
704
+ function parseMarkupPercentInput(raw) {
705
+ const trimmed = raw.trim();
706
+ if (!trimmed) {
707
+ return null;
708
+ }
709
+ const n = Number(trimmed);
710
+ if (!Number.isFinite(n) || n < 0) {
711
+ return null;
712
+ }
713
+ return n;
714
+ }
715
+ function applyRetailRateToNetworkMicros(networkFeeUsdMicros, retailRateUsd) {
716
+ const networkPerMicro = NETWORK_USD_PER_MICRO;
717
+ const retail = Number(retailRateUsd);
718
+ if (!Number.isFinite(retail) || retail <= 0) {
719
+ return networkFeeUsdMicros;
720
+ }
721
+ const ratio = retail / networkPerMicro;
722
+ if (!Number.isFinite(ratio) || ratio <= 0) {
723
+ return networkFeeUsdMicros;
724
+ }
725
+ return networkFeeUsdMicros * BigInt(Math.round(ratio * 1e6)) / 1000000n;
726
+ }
727
+
728
+ // src/ingest.ts
729
+ init_encoding();
730
+ init_errors();
731
+ init_string_utils();
732
+ function signerSnapshotToIngestPayload(input) {
733
+ return {
734
+ requestId: input.snapshot.requestId,
735
+ externalUserId: input.externalUserId,
736
+ networkFeeUsdMicros: input.snapshot.computedFeeUsdMicros.toString(),
737
+ feeWei: input.snapshot.computedFeeWei,
738
+ pixels: input.snapshot.pixels,
739
+ pipeline: input.snapshot.pipeline,
740
+ modelId: input.snapshot.modelId,
741
+ gatewayRequestId: input.gatewayRequestId,
742
+ ethUsdPrice: input.snapshot.ethUsdPrice,
743
+ ethUsdRoundId: input.snapshot.ethUsdRoundId,
744
+ ethUsdObservedAt: input.snapshot.ethUsdObservedAt
745
+ };
746
+ }
747
+ function ingestUrl(issuerUrl, publicClientId) {
748
+ const origin = new URL(stripTrailingSlashes(issuerUrl)).origin;
749
+ return `${origin}/api/v1/apps/${encodeURIComponent(publicClientId)}/usage/signed-tickets`;
750
+ }
751
+ async function readJsonResponse(response) {
752
+ const text = await response.text();
753
+ if (!text.trim()) {
754
+ return {};
755
+ }
756
+ try {
757
+ const parsed = JSON.parse(text);
758
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
759
+ } catch {
760
+ return {};
761
+ }
762
+ }
763
+ async function ingestSignedTicket(options) {
764
+ const fetchImpl = options.fetch ?? fetch;
765
+ const url = ingestUrl(options.issuerUrl, options.publicClientId);
766
+ const response = await fetchImpl(url, {
767
+ method: "POST",
768
+ headers: {
769
+ Authorization: encodeClientSecretBasic(options.m2mClientId, options.m2mClientSecret),
770
+ "Content-Type": "application/json",
771
+ Accept: "application/json"
772
+ },
773
+ body: JSON.stringify(options.ticket),
774
+ cache: "no-store"
775
+ });
776
+ const body = await readJsonResponse(response);
777
+ if (!response.ok) {
778
+ const message = typeof body.error === "string" ? body.error : `Signed-ticket ingest failed (${response.status})`;
779
+ throw new PmtHouseError(message, {
780
+ status: response.status,
781
+ code: "ingest_failed",
782
+ details: body
783
+ });
784
+ }
785
+ return {
786
+ ingested: Boolean(body.ingested),
787
+ duplicate: Boolean(body.duplicate),
788
+ source: body.source === "openmeter" ? "openmeter" : "disabled"
789
+ };
790
+ }
791
+ async function ingestSignedTicketsBatch(options) {
792
+ const fetchImpl = options.fetch ?? fetch;
793
+ const url = ingestUrl(options.issuerUrl, options.publicClientId);
794
+ const response = await fetchImpl(url, {
795
+ method: "POST",
796
+ headers: {
797
+ Authorization: encodeClientSecretBasic(options.m2mClientId, options.m2mClientSecret),
798
+ "Content-Type": "application/json",
799
+ Accept: "application/json"
800
+ },
801
+ body: JSON.stringify({ tickets: options.tickets }),
802
+ cache: "no-store"
803
+ });
804
+ const body = await readJsonResponse(response);
805
+ if (!response.ok) {
806
+ const message = typeof body.error === "string" ? body.error : `Signed-ticket batch ingest failed (${response.status})`;
807
+ throw new PmtHouseError(message, {
808
+ status: response.status,
809
+ code: "ingest_failed",
810
+ details: body
811
+ });
812
+ }
813
+ const rawResults = Array.isArray(body.results) ? body.results : [];
814
+ return {
815
+ results: rawResults.map((entry) => {
816
+ const row = entry ?? {};
817
+ return {
818
+ requestId: typeof row.requestId === "string" ? row.requestId : void 0,
819
+ ok: row.ok === true,
820
+ ingested: Boolean(row.ingested),
821
+ duplicate: Boolean(row.duplicate),
822
+ source: row.source === "openmeter" ? "openmeter" : "disabled"
823
+ };
824
+ })
825
+ };
826
+ }
827
+
4
828
  // src/usage.ts
5
829
  function parseSafeBigInt(value, fallback = 0n) {
6
830
  try {
@@ -115,24 +939,27 @@ function mergeUsageByPipelineModel(usagePipelineModels) {
115
939
  const key = JSON.stringify([pipeline, modelId]);
116
940
  const existing = byKey.get(key);
117
941
  const rowCurrency = row.currency ?? "USD";
942
+ const networkFee = row.networkFeeUsdMicros ?? "0";
943
+ const ownerCharge = row.ownerChargeUsdMicros ?? "0";
944
+ const endUserBillable = row.endUserBillableUsdMicros ?? "0";
118
945
  if (!existing) {
119
946
  byKey.set(key, {
120
947
  pipeline,
121
948
  modelId,
122
949
  requestCount: row.requestCount,
123
950
  currency: rowCurrency,
124
- networkFeeUsdMicros: row.networkFeeUsdMicros,
125
- ownerChargeUsdMicros: row.ownerChargeUsdMicros,
126
- endUserBillableUsdMicros: row.endUserBillableUsdMicros
951
+ networkFeeUsdMicros: networkFee,
952
+ ownerChargeUsdMicros: ownerCharge,
953
+ endUserBillableUsdMicros: endUserBillable
127
954
  });
128
955
  continue;
129
956
  }
130
957
  byKey.set(key, {
131
958
  ...existing,
132
959
  requestCount: existing.requestCount + row.requestCount,
133
- networkFeeUsdMicros: (parseSafeBigInt(existing.networkFeeUsdMicros) + parseSafeBigInt(row.networkFeeUsdMicros)).toString(),
134
- ownerChargeUsdMicros: (parseSafeBigInt(existing.ownerChargeUsdMicros) + parseSafeBigInt(row.ownerChargeUsdMicros)).toString(),
135
- endUserBillableUsdMicros: (parseSafeBigInt(existing.endUserBillableUsdMicros) + parseSafeBigInt(row.endUserBillableUsdMicros)).toString()
960
+ networkFeeUsdMicros: (parseSafeBigInt(existing.networkFeeUsdMicros) + parseSafeBigInt(networkFee)).toString(),
961
+ ownerChargeUsdMicros: (parseSafeBigInt(existing.ownerChargeUsdMicros) + parseSafeBigInt(ownerCharge)).toString(),
962
+ endUserBillableUsdMicros: (parseSafeBigInt(existing.endUserBillableUsdMicros) + parseSafeBigInt(endUserBillable)).toString()
136
963
  });
137
964
  }
138
965
  }
@@ -141,9 +968,10 @@ function mergeUsageByPipelineModel(usagePipelineModels) {
141
968
  return a.pipeline.localeCompare(b.pipeline);
142
969
  });
143
970
  }
144
- function buildMeScopeUsagePayload(usageByUser, externalUserId, usagePipelineModel) {
971
+ function buildMeScopeUsagePayload(usageByUser, externalUserId, usagePipelineModel, usageDaily) {
145
972
  const summary = summarizeUsageFiatForExternalUser(usageByUser, externalUserId);
146
973
  const pipelineModels = mergeUsageByPipelineModel(usagePipelineModel);
974
+ const dailyByPipeline = usageDaily?.byDailyPipeline ?? [];
147
975
  return {
148
976
  clientId: usageByUser.clientId,
149
977
  period: usageByUser.period,
@@ -154,157 +982,17 @@ function buildMeScopeUsagePayload(usageByUser, externalUserId, usagePipelineMode
154
982
  networkFeeUsdMicros: summary.networkFeeUsdMicros,
155
983
  ownerChargeUsdMicros: summary.ownerChargeUsdMicros,
156
984
  endUserBillableUsdMicros: summary.endUserBillableUsdMicros,
157
- pipelineModels
985
+ pipelineModels,
986
+ dailyByPipeline
158
987
  }
159
988
  };
160
989
  }
161
990
  var DEFAULT_MAX_END_USER_IDS = 25;
162
991
 
163
- // src/encoding.ts
164
- function encodeClientSecretBasic(clientId, clientSecret) {
165
- const raw = `${clientId}:${clientSecret}`;
166
- const b64 = typeof Buffer !== "undefined" ? Buffer.from(raw, "utf8").toString("base64") : btoa(Array.from(new TextEncoder().encode(raw), (c) => String.fromCharCode(c)).join(""));
167
- return `Basic ${b64}`;
168
- }
169
-
170
- // src/errors.ts
171
- var PmtHouseError = class extends Error {
172
- status;
173
- code;
174
- details;
175
- constructor(message, {
176
- status = 500,
177
- code = "pymthouse_error",
178
- details
179
- } = {}) {
180
- super(message);
181
- this.name = "PmtHouseError";
182
- this.status = status;
183
- this.code = code;
184
- this.details = details;
185
- }
186
- };
187
- function toPmtHouseError(error, fallbackMessage) {
188
- if (error instanceof PmtHouseError) {
189
- return error;
190
- }
191
- if (error instanceof Error) {
192
- return new PmtHouseError(error.message || fallbackMessage, {
193
- code: "unexpected_error",
194
- status: 500
195
- });
196
- }
197
- return new PmtHouseError(fallbackMessage, {
198
- code: "unexpected_error",
199
- status: 500
200
- });
201
- }
202
-
203
- // src/string-utils.ts
204
- function stripTrailingSlashes(value) {
205
- let end = value.length;
206
- while (end > 0 && value.charCodeAt(end - 1) === 47) {
207
- end--;
208
- }
209
- return value.slice(0, end);
210
- }
211
-
212
- // src/discovery.ts
213
- function authorizationServerToOidcDocument(as) {
214
- const tokenEndpoint = as.token_endpoint;
215
- const jwksUri = as.jwks_uri;
216
- if (!tokenEndpoint || !jwksUri) {
217
- throw new PmtHouseError("OIDC discovery document is missing token_endpoint or jwks_uri", {
218
- status: 500,
219
- code: "oidc_discovery_invalid"
220
- });
221
- }
222
- return {
223
- issuer: as.issuer,
224
- authorization_endpoint: as.authorization_endpoint ?? "",
225
- token_endpoint: tokenEndpoint,
226
- jwks_uri: jwksUri,
227
- userinfo_endpoint: as.userinfo_endpoint,
228
- device_authorization_endpoint: as.device_authorization_endpoint
229
- };
230
- }
231
- var CACHE_TTL_MS = 5 * 60 * 1e3;
232
- var discoveryCache = /* @__PURE__ */ new Map();
233
- function normalizedIssuerKey(issuerUrl) {
234
- return stripTrailingSlashes(issuerUrl);
235
- }
236
- async function loadAuthorizationServer(issuerUrl, fetchImpl, options = {}) {
237
- const key = normalizedIssuerKey(issuerUrl);
238
- const now = Date.now();
239
- const cached = discoveryCache.get(key);
240
- if (!options.force && cached && now - cached.fetchedAt < CACHE_TTL_MS) {
241
- return cached.as;
242
- }
243
- const issuerIdentifier = new URL(key);
244
- const discoveryOpts = {
245
- algorithm: "oidc",
246
- [customFetch]: fetchImpl
247
- };
248
- if (options.allowInsecureHttp) {
249
- discoveryOpts[allowInsecureRequests] = true;
250
- }
251
- let response;
252
- try {
253
- response = await discoveryRequest(issuerIdentifier, discoveryOpts);
254
- } catch (e) {
255
- throw mapDiscoveryNetworkError(e);
256
- }
257
- let as;
258
- try {
259
- as = await processDiscoveryResponse(issuerIdentifier, response);
260
- } catch (e) {
261
- throw mapOAuthDiscoveryError(e);
262
- }
263
- discoveryCache.set(key, { as, fetchedAt: now });
264
- return as;
265
- }
266
- async function fetchDiscoveryDocument(issuerUrl, fetchImpl, options = {}) {
267
- const as = await loadAuthorizationServer(issuerUrl, fetchImpl, options);
268
- return authorizationServerToOidcDocument(as);
269
- }
270
- function clearDiscoveryCache(issuerUrl) {
271
- if (!issuerUrl) {
272
- discoveryCache.clear();
273
- return;
274
- }
275
- discoveryCache.delete(normalizedIssuerKey(issuerUrl));
276
- }
277
- function mapOAuthDiscoveryError(error) {
278
- if (error instanceof PmtHouseError) {
279
- return error;
280
- }
281
- if (error instanceof Error) {
282
- return new PmtHouseError(error.message, {
283
- status: 500,
284
- code: "oidc_discovery_invalid",
285
- details: { cause: error.cause }
286
- });
287
- }
288
- return new PmtHouseError("OIDC discovery failed", {
289
- status: 500,
290
- code: "oidc_discovery_invalid"
291
- });
292
- }
293
- function mapDiscoveryNetworkError(error) {
294
- if (error instanceof PmtHouseError) {
295
- return error;
296
- }
297
- if (error instanceof Error) {
298
- return new PmtHouseError(`Failed to load OIDC discovery: ${error.message}`, {
299
- status: 502,
300
- code: "oidc_discovery_failed"
301
- });
302
- }
303
- return new PmtHouseError("Failed to load OIDC discovery", {
304
- status: 502,
305
- code: "oidc_discovery_failed"
306
- });
307
- }
992
+ // src/client.ts
993
+ init_encoding();
994
+ init_discovery();
995
+ init_errors();
308
996
  function parseAppManifestResponse(json) {
309
997
  if (!json || typeof json !== "object" || Array.isArray(json)) {
310
998
  return { capabilities: [], excludedCapabilities: [] };
@@ -342,6 +1030,9 @@ function computeManifestRevision(data) {
342
1030
  return createHash("sha256").update(JSON.stringify({ capabilities: caps, excludedCapabilities: excl })).digest("hex").slice(0, 24);
343
1031
  }
344
1032
 
1033
+ // src/client.ts
1034
+ init_string_utils();
1035
+
345
1036
  // src/tokens.ts
346
1037
  var SIGNER_SESSION_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
347
1038
  var SIGNER_SESSION_EXPIRES_IN_SEC = Math.floor(SIGNER_SESSION_TTL_MS / 1e3);
@@ -402,6 +1093,9 @@ function parseSignerSessionExchange(res) {
402
1093
  scope
403
1094
  };
404
1095
  }
1096
+
1097
+ // src/oauth-map.ts
1098
+ init_errors();
405
1099
  var ACCEPTED_ISSUED_TOKEN_TYPES = /* @__PURE__ */ new Set([
406
1100
  "urn:ietf:params:oauth:token-type:access_token",
407
1101
  "urn:pmth:token-type:remote-signer-session"
@@ -495,8 +1189,8 @@ function m2mClient(clientId) {
495
1189
  }
496
1190
 
497
1191
  // src/client.ts
498
- var TOKEN_EXCHANGE_GRANT = "urn:ietf:params:oauth:grant-type:token-exchange";
499
- var SUBJECT_ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
1192
+ var TOKEN_EXCHANGE_GRANT2 = "urn:ietf:params:oauth:grant-type:token-exchange";
1193
+ var SUBJECT_ACCESS_TOKEN_TYPE2 = "urn:ietf:params:oauth:token-type:access_token";
500
1194
  var REQUESTED_ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
501
1195
  var DEVICE_RESOURCE_PREFIX = "urn:pmth:device_code:";
502
1196
  function normalizeUserCode(value) {
@@ -623,6 +1317,50 @@ var PmtHouseClient = class {
623
1317
  cache: "no-store"
624
1318
  });
625
1319
  }
1320
+ /**
1321
+ * Exchange a long-lived dashboard API key (`pmth_*`) for a short-lived user JWT.
1322
+ */
1323
+ async exchangeApiKeyForUserAccessToken(input) {
1324
+ const url = `${this.getAppsBaseUrl()}/auth/api-key/token`;
1325
+ return this.requestJson(url, {
1326
+ method: "POST",
1327
+ headers: {
1328
+ Authorization: `Bearer ${input.apiKey.trim()}`,
1329
+ "Content-Type": "application/json",
1330
+ Accept: "application/json"
1331
+ },
1332
+ body: JSON.stringify(input.scope ? { scope: input.scope } : {}),
1333
+ cache: "no-store"
1334
+ });
1335
+ }
1336
+ /**
1337
+ * Exchange a dashboard API key for a signer session via a trusted facade (recommended)
1338
+ * or directly when M2M credentials are available on this client.
1339
+ */
1340
+ async exchangeApiKeyForSignerSession(input) {
1341
+ if (input.facadeUrl?.trim()) {
1342
+ const { exchangeApiKeyForSigner: exchangeApiKeyForSigner2 } = await Promise.resolve().then(() => (init_api_key_exchange(), api_key_exchange_exports));
1343
+ const exchanged = await exchangeApiKeyForSigner2({
1344
+ facadeUrl: input.facadeUrl.trim(),
1345
+ apiKey: input.apiKey,
1346
+ scope: input.scope,
1347
+ clientId: this.publicClientId,
1348
+ fetch: this.fetchImpl
1349
+ });
1350
+ return {
1351
+ access_token: exchanged.access_token,
1352
+ token_type: exchanged.token_type,
1353
+ expires_in: exchanged.expires_in,
1354
+ scope: exchanged.scope,
1355
+ issued_token_type: "urn:ietf:params:oauth:token-type:access_token"
1356
+ };
1357
+ }
1358
+ const userToken = await this.exchangeApiKeyForUserAccessToken({
1359
+ apiKey: input.apiKey,
1360
+ scope: input.scope
1361
+ });
1362
+ return this.exchangeForSignerSession({ userJwt: userToken.access_token });
1363
+ }
626
1364
  async completeDeviceApproval(input) {
627
1365
  const as = await loadAuthorizationServer(this.issuerUrl, this.fetchImpl, {
628
1366
  allowInsecureHttp: this.allowInsecureHttp
@@ -631,14 +1369,14 @@ var PmtHouseClient = class {
631
1369
  const clientAuth = this.m2mClientAuth();
632
1370
  const params = new URLSearchParams();
633
1371
  params.set("subject_token", input.userJwt);
634
- params.set("subject_token_type", SUBJECT_ACCESS_TOKEN_TYPE);
1372
+ params.set("subject_token_type", SUBJECT_ACCESS_TOKEN_TYPE2);
635
1373
  params.set("resource", buildDeviceCodeResource(input.userCode));
636
1374
  try {
637
1375
  const response = await genericTokenEndpointRequest(
638
1376
  as,
639
1377
  client,
640
1378
  clientAuth,
641
- TOKEN_EXCHANGE_GRANT,
1379
+ TOKEN_EXCHANGE_GRANT2,
642
1380
  params,
643
1381
  this.tokenEndpointFetchOptions()
644
1382
  );
@@ -686,7 +1424,7 @@ var PmtHouseClient = class {
686
1424
  const clientAuth = this.m2mClientAuth();
687
1425
  const params = new URLSearchParams();
688
1426
  params.set("subject_token", input.userJwt);
689
- params.set("subject_token_type", SUBJECT_ACCESS_TOKEN_TYPE);
1427
+ params.set("subject_token_type", SUBJECT_ACCESS_TOKEN_TYPE2);
690
1428
  params.set("requested_token_type", REQUESTED_ACCESS_TOKEN_TYPE);
691
1429
  const resourceCandidate = typeof input.resource === "string" && input.resource.trim() !== "" ? input.resource.trim() : this.issuerUrl;
692
1430
  params.set("resource", stripTrailingSlashes(resourceCandidate));
@@ -695,7 +1433,7 @@ var PmtHouseClient = class {
695
1433
  as,
696
1434
  client,
697
1435
  clientAuth,
698
- TOKEN_EXCHANGE_GRANT,
1436
+ TOKEN_EXCHANGE_GRANT2,
699
1437
  params,
700
1438
  this.tokenEndpointFetchOptions()
701
1439
  );
@@ -751,6 +1489,7 @@ var PmtHouseClient = class {
751
1489
  if (input.groupBy) url.searchParams.set("groupBy", input.groupBy);
752
1490
  if (input.userId) url.searchParams.set("userId", input.userId);
753
1491
  if (input.gatewayRequestId) url.searchParams.set("gatewayRequestId", input.gatewayRequestId);
1492
+ if (input.includeRetail) url.searchParams.set("include", "retail");
754
1493
  return this.requestJson(url.toString(), {
755
1494
  method: "GET",
756
1495
  headers: this.builderHeaders(),
@@ -760,6 +1499,129 @@ var PmtHouseClient = class {
760
1499
  /**
761
1500
  * Session-scoped usage for one `externalUserId`: user rollup plus merged pipeline/model breakdown.
762
1501
  */
1502
+ async ingestSignedTicket(ticket) {
1503
+ return ingestSignedTicket({
1504
+ issuerUrl: this.issuerUrl,
1505
+ publicClientId: this.publicClientId,
1506
+ m2mClientId: this.m2mClientId,
1507
+ m2mClientSecret: this.m2mClientSecret,
1508
+ ticket,
1509
+ fetch: this.fetchImpl
1510
+ });
1511
+ }
1512
+ async ingestSignedTickets(tickets) {
1513
+ return ingestSignedTicketsBatch({
1514
+ issuerUrl: this.issuerUrl,
1515
+ publicClientId: this.publicClientId,
1516
+ m2mClientId: this.m2mClientId,
1517
+ m2mClientSecret: this.m2mClientSecret,
1518
+ tickets,
1519
+ fetch: this.fetchImpl
1520
+ });
1521
+ }
1522
+ async getSignerRouting() {
1523
+ return this.requestJson(
1524
+ `${this.getAppsBaseUrl()}/signer/routing`,
1525
+ {
1526
+ method: "GET",
1527
+ headers: this.builderHeaders(),
1528
+ cache: "no-store"
1529
+ }
1530
+ );
1531
+ }
1532
+ async listBillingProducts() {
1533
+ const url = `${this.getAppsBaseUrl()}/plans?apiVersion=2`;
1534
+ const body = await this.requestJson(
1535
+ url,
1536
+ {
1537
+ method: "GET",
1538
+ headers: this.builderHeaders(),
1539
+ cache: "no-store"
1540
+ }
1541
+ );
1542
+ return {
1543
+ apiVersion: body.apiVersion ?? 2,
1544
+ products: body.products ?? body.plans ?? []
1545
+ };
1546
+ }
1547
+ async syncBillingProduct(planId) {
1548
+ return this.requestJson(
1549
+ `${this.getAppsBaseUrl()}/plans/${encodeURIComponent(planId)}/sync`,
1550
+ {
1551
+ method: "POST",
1552
+ headers: this.builderHeaders(),
1553
+ cache: "no-store"
1554
+ }
1555
+ );
1556
+ }
1557
+ async getUsageBalance(externalUserId) {
1558
+ const url = new URL(`${this.getAppsBaseUrl()}/usage/balance`);
1559
+ url.searchParams.set("externalUserId", externalUserId);
1560
+ return this.requestJson(url.toString(), {
1561
+ method: "GET",
1562
+ headers: this.builderHeaders(),
1563
+ cache: "no-store"
1564
+ });
1565
+ }
1566
+ async getUserAllowances(externalUserId) {
1567
+ return this.requestJson(
1568
+ `${this.getAppsBaseUrl()}/users/${encodeURIComponent(externalUserId)}/allowances`,
1569
+ {
1570
+ method: "GET",
1571
+ headers: this.builderHeaders(),
1572
+ cache: "no-store"
1573
+ }
1574
+ );
1575
+ }
1576
+ async grantUserAllowance(externalUserId, input) {
1577
+ return this.requestJson(
1578
+ `${this.getAppsBaseUrl()}/users/${encodeURIComponent(externalUserId)}/allowances`,
1579
+ {
1580
+ method: "POST",
1581
+ headers: this.builderHeaders(),
1582
+ body: JSON.stringify(input),
1583
+ cache: "no-store"
1584
+ }
1585
+ );
1586
+ }
1587
+ /**
1588
+ * @deprecated Removed from PymtHouse — use {@link getUsageBalance} or {@link getUserAllowances}.
1589
+ */
1590
+ async getUserCredits(externalUserId) {
1591
+ return this.getUsageBalance(externalUserId);
1592
+ }
1593
+ /**
1594
+ * @deprecated Removed from PymtHouse — use {@link grantUserAllowance} (`POST .../allowances`).
1595
+ */
1596
+ async grantUserCredits(externalUserId, input) {
1597
+ const result = await this.grantUserAllowance(externalUserId, {
1598
+ amountUsdMicros: input.amountUsdMicros,
1599
+ source: input.source ?? "manual",
1600
+ featureKey: input.featureKey
1601
+ });
1602
+ const flat = result;
1603
+ const nested = result.allowances;
1604
+ return {
1605
+ externalUserId: result.externalUserId,
1606
+ balanceUsdMicros: flat.balanceUsdMicros ?? nested?.balanceUsdMicros ?? "0",
1607
+ consumedUsdMicros: flat.consumedUsdMicros ?? nested?.consumedUsdMicros ?? "0",
1608
+ lifetimeGrantedUsdMicros: flat.lifetimeGrantedUsdMicros ?? nested?.lifetimeGrantedUsdMicros ?? "0",
1609
+ hasAccess: flat.hasAccess ?? nested?.hasAccess ?? false,
1610
+ remainingUsdMicros: flat.balanceUsdMicros ?? nested?.balanceUsdMicros,
1611
+ grantedUsdMicros: flat.grantedUsdMicros,
1612
+ featureKey: flat.featureKey
1613
+ };
1614
+ }
1615
+ async getUserSubscription(externalUserId) {
1616
+ return this.requestJson(
1617
+ `${this.getAppsBaseUrl()}/users/${encodeURIComponent(externalUserId)}/subscription`,
1618
+ {
1619
+ method: "GET",
1620
+ headers: this.builderHeaders(),
1621
+ cache: "no-store"
1622
+ }
1623
+ );
1624
+ }
763
1625
  async fetchUsageForExternalUser(input) {
764
1626
  const usageByUser = await this.getUsage({
765
1627
  startDate: input.startDate,
@@ -779,7 +1641,18 @@ var PmtHouseClient = class {
779
1641
  })
780
1642
  )
781
1643
  );
782
- return buildMeScopeUsagePayload(usageByUser, input.externalUserId, usagePipelineModels);
1644
+ const usageDaily = await this.getUsage({
1645
+ startDate: input.startDate,
1646
+ endDate: input.endDate,
1647
+ groupBy: "daily_pipeline",
1648
+ userId: input.externalUserId
1649
+ });
1650
+ return buildMeScopeUsagePayload(
1651
+ usageByUser,
1652
+ input.externalUserId,
1653
+ usagePipelineModels,
1654
+ usageDaily
1655
+ );
783
1656
  }
784
1657
  async getAppManifest(opts) {
785
1658
  const url = `${this.getAppsBaseUrl()}/manifest`;
@@ -969,7 +1842,11 @@ var PmtHouseClient = class {
969
1842
  }
970
1843
  };
971
1844
 
1845
+ // src/index.ts
1846
+ init_errors();
1847
+
972
1848
  // src/config.ts
1849
+ init_string_utils();
973
1850
  var PYMTHOUSE_NOT_CONFIGURED_MESSAGE = "PymtHouse is not configured. Set PYMTHOUSE_ISSUER_URL, PYMTHOUSE_PUBLIC_CLIENT_ID, PYMTHOUSE_M2M_CLIENT_ID, and PYMTHOUSE_M2M_CLIENT_SECRET, then restart.";
974
1851
  function trimEnv(name) {
975
1852
  const value = process.env[name];
@@ -1008,13 +1885,15 @@ function isPymthouseConfigured() {
1008
1885
  return readPymthouseEnv() !== null;
1009
1886
  }
1010
1887
  function getBuilderApiV1BaseFromIssuerUrl(issuerUrl) {
1011
- const noTrail = stripTrailingSlashes(issuerUrl.trim());
1012
- return noTrail.replace(/\/oidc\/?$/i, "");
1888
+ return stripOidcPathSuffix(issuerUrl);
1013
1889
  }
1014
1890
  function getPymthouseIssuerOrigin(issuerUrl) {
1015
1891
  return new URL(stripTrailingSlashes(issuerUrl.trim())).origin;
1016
1892
  }
1017
1893
 
1018
- export { DEFAULT_MAX_END_USER_IDS, PYMTHOUSE_NOT_CONFIGURED_MESSAGE, PYMTHOUSE_SIGNER_SESSION_TTL_MS, PmtHouseClient, PmtHouseError, SIGNER_SESSION_EXPIRES_IN_SEC, SIGNER_SESSION_TTL_MS, SIGN_JOB_SCOPE, aggregateUsageByExternalUserId, authorizationServerToOidcDocument, buildDeviceCodeResource, buildMeScopeUsagePayload, clearDiscoveryCache, computeManifestRevision, computePymthouseExpiry, computeSignerSessionExpiry, decodeJwtExp, fetchDiscoveryDocument, getBuilderApiV1BaseFromIssuerUrl, getEndUserIdsForExternalUser, getPymthouseIssuerOrigin, getPymthouseIssuerUrlFromEnv, getPymthousePublicClientIdFromEnv, getUsageRecordUserIdsForExternalUser, getUtcCalendarMonthIsoBounds, isLikelyOidcJwt, isOpaqueSignerSessionToken, isPymthouseConfigured, listUsageByPipelineModel, loadAuthorizationServer, mergeUsageByPipelineModel, normalizeUserCode, parseAppManifestResponse, parseSignerSessionExchange, parseUsageDateParam, readPymthouseEnv, summarizeUsageFiatForExternalUser, summarizeUsageForExternalUser, toPmtHouseError };
1894
+ // src/index.ts
1895
+ init_discovery();
1896
+
1897
+ export { DEFAULT_MAX_END_USER_IDS, NETWORK_USD_PER_MICRO, PYMTHOUSE_NOT_CONFIGURED_MESSAGE, PYMTHOUSE_SIGNER_SESSION_TTL_MS, PmtHouseClient, PmtHouseError, SIGNER_SESSION_EXPIRES_IN_SEC, SIGNER_SESSION_TTL_MS, SIGN_JOB_SCOPE, aggregateUsageByExternalUserId, applyRetailRateToNetworkMicros, authorizationServerToOidcDocument, buildDeviceCodeResource, buildMeScopeUsagePayload, clearDiscoveryCache, computeManifestRevision, computePymthouseExpiry, computeSignerSessionExpiry, decodeJwtExp, defaultRetailRateUsd, fetchDiscoveryDocument, getBuilderApiV1BaseFromIssuerUrl, getEndUserIdsForExternalUser, getPymthouseIssuerOrigin, getPymthouseIssuerUrlFromEnv, getPymthousePublicClientIdFromEnv, getUsageRecordUserIdsForExternalUser, getUtcCalendarMonthIsoBounds, ingestSignedTicket, ingestSignedTicketsBatch, isLikelyOidcJwt, isOpaqueSignerSessionToken, isPymthouseConfigured, listUsageByPipelineModel, loadAuthorizationServer, markupPercentToRetailRateUsd, mergeUsageByPipelineModel, normalizeUserCode, parseAppManifestResponse, parseMarkupPercentInput, parseRetailRateUsd, parseSignerSessionExchange, parseUsageDateParam, readPymthouseEnv, retailRateUsdPerMillion, retailRateUsdToMarkupPercent, signerSnapshotToIngestPayload, summarizeUsageFiatForExternalUser, summarizeUsageForExternalUser, toPmtHouseError };
1019
1898
  //# sourceMappingURL=index.js.map
1020
1899
  //# sourceMappingURL=index.js.map