@pymthouse/builder-sdk 0.0.8 → 0.1.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/index.js CHANGED
@@ -1,6 +1,29 @@
1
1
  import { discoveryRequest, processDiscoveryResponse, genericTokenEndpointRequest, processGenericTokenEndpointResponse, clientCredentialsGrantRequest, processClientCredentialsResponse, customFetch, allowInsecureRequests, ResponseBodyError, OperationProcessingError } from 'oauth4webapi';
2
+ import { createHash } from 'crypto';
2
3
 
3
4
  // src/usage.ts
5
+ function parseSafeBigInt(value, fallback = 0n) {
6
+ try {
7
+ return BigInt(value);
8
+ } catch {
9
+ return fallback;
10
+ }
11
+ }
12
+ function getUtcCalendarMonthIsoBounds(now = /* @__PURE__ */ new Date()) {
13
+ const y = now.getUTCFullYear();
14
+ const m = now.getUTCMonth();
15
+ const start = new Date(Date.UTC(y, m, 1, 0, 0, 0, 0));
16
+ const end = new Date(Date.UTC(y, m + 1, 0, 23, 59, 59, 999));
17
+ return { startDate: start.toISOString(), endDate: end.toISOString() };
18
+ }
19
+ function parseUsageDateParam(raw) {
20
+ if (raw == null) return null;
21
+ const trimmed = raw.trim();
22
+ if (!trimmed) return null;
23
+ const t = Date.parse(trimmed);
24
+ if (Number.isNaN(t)) return null;
25
+ return trimmed;
26
+ }
4
27
  function aggregateUsageByExternalUserId(byUser, externalUserId) {
5
28
  const rows = byUser?.filter((row) => row.externalUserId === externalUserId) ?? [];
6
29
  if (rows.length === 0) {
@@ -13,7 +36,9 @@ function aggregateUsageByExternalUserId(byUser, externalUserId) {
13
36
  let feeWei = 0n;
14
37
  let requestCount = 0;
15
38
  for (const row of rows) {
16
- feeWei += BigInt(row.feeWei);
39
+ if (row.feeWei) {
40
+ feeWei += BigInt(row.feeWei);
41
+ }
17
42
  requestCount += row.requestCount;
18
43
  }
19
44
  return {
@@ -33,6 +58,107 @@ function listUsageByPipelineModel(usage) {
33
58
  return a.modelId.localeCompare(b.modelId);
34
59
  });
35
60
  }
61
+ function getEndUserIdsForExternalUser(usage, externalUserId) {
62
+ const userIds = /* @__PURE__ */ new Set();
63
+ for (const row of usage.byUser ?? []) {
64
+ if (row.externalUserId === externalUserId && row.endUserId !== "unknown") {
65
+ userIds.add(row.endUserId);
66
+ }
67
+ }
68
+ return [...userIds];
69
+ }
70
+ var getUsageRecordUserIdsForExternalUser = getEndUserIdsForExternalUser;
71
+ function summarizeUsageFiatForExternalUser(usageByUser, externalUserId) {
72
+ const rows = usageByUser.byUser ?? [];
73
+ let requestCount = 0;
74
+ let networkFeeUsdMicros = 0n;
75
+ let ownerChargeUsdMicros = 0n;
76
+ let endUserBillableUsdMicros = 0n;
77
+ let currency = "USD";
78
+ for (const row of rows) {
79
+ if (row.externalUserId !== externalUserId) continue;
80
+ requestCount += row.requestCount;
81
+ if (row.currency) currency = row.currency;
82
+ if (row.networkFeeUsdMicros) {
83
+ networkFeeUsdMicros += BigInt(row.networkFeeUsdMicros);
84
+ }
85
+ if (row.ownerChargeUsdMicros) {
86
+ ownerChargeUsdMicros += BigInt(row.ownerChargeUsdMicros);
87
+ }
88
+ if (row.endUserBillableUsdMicros) {
89
+ endUserBillableUsdMicros += BigInt(row.endUserBillableUsdMicros);
90
+ }
91
+ }
92
+ return {
93
+ externalUserId,
94
+ requestCount,
95
+ currency,
96
+ networkFeeUsdMicros: networkFeeUsdMicros.toString(),
97
+ ownerChargeUsdMicros: ownerChargeUsdMicros.toString(),
98
+ endUserBillableUsdMicros: endUserBillableUsdMicros.toString()
99
+ };
100
+ }
101
+ function mergeUsageByPipelineModel(usagePipelineModels) {
102
+ let responses;
103
+ if (Array.isArray(usagePipelineModels)) {
104
+ responses = usagePipelineModels;
105
+ } else if (usagePipelineModels) {
106
+ responses = [usagePipelineModels];
107
+ } else {
108
+ responses = [];
109
+ }
110
+ const byKey = /* @__PURE__ */ new Map();
111
+ for (const response of responses) {
112
+ for (const row of response.byPipelineModel ?? []) {
113
+ const { pipeline, modelId } = row;
114
+ if (!pipeline || !modelId) continue;
115
+ const key = JSON.stringify([pipeline, modelId]);
116
+ const existing = byKey.get(key);
117
+ const rowCurrency = row.currency ?? "USD";
118
+ if (!existing) {
119
+ byKey.set(key, {
120
+ pipeline,
121
+ modelId,
122
+ requestCount: row.requestCount,
123
+ currency: rowCurrency,
124
+ networkFeeUsdMicros: row.networkFeeUsdMicros,
125
+ ownerChargeUsdMicros: row.ownerChargeUsdMicros,
126
+ endUserBillableUsdMicros: row.endUserBillableUsdMicros
127
+ });
128
+ continue;
129
+ }
130
+ byKey.set(key, {
131
+ ...existing,
132
+ 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()
136
+ });
137
+ }
138
+ }
139
+ return [...byKey.values()].sort((a, b) => {
140
+ if (a.pipeline === b.pipeline) return a.modelId.localeCompare(b.modelId);
141
+ return a.pipeline.localeCompare(b.pipeline);
142
+ });
143
+ }
144
+ function buildMeScopeUsagePayload(usageByUser, externalUserId, usagePipelineModel) {
145
+ const summary = summarizeUsageFiatForExternalUser(usageByUser, externalUserId);
146
+ const pipelineModels = mergeUsageByPipelineModel(usagePipelineModel);
147
+ return {
148
+ clientId: usageByUser.clientId,
149
+ period: usageByUser.period,
150
+ currentUser: {
151
+ externalUserId: summary.externalUserId,
152
+ requestCount: summary.requestCount,
153
+ currency: summary.currency,
154
+ networkFeeUsdMicros: summary.networkFeeUsdMicros,
155
+ ownerChargeUsdMicros: summary.ownerChargeUsdMicros,
156
+ endUserBillableUsdMicros: summary.endUserBillableUsdMicros,
157
+ pipelineModels
158
+ }
159
+ };
160
+ }
161
+ var DEFAULT_MAX_END_USER_IDS = 25;
36
162
 
37
163
  // src/encoding.ts
38
164
  function encodeClientSecretBasic(clientId, clientSecret) {
@@ -179,6 +305,103 @@ function mapDiscoveryNetworkError(error) {
179
305
  code: "oidc_discovery_failed"
180
306
  });
181
307
  }
308
+ function parseAppManifestResponse(json) {
309
+ if (!json || typeof json !== "object" || Array.isArray(json)) {
310
+ return { capabilities: [], excludedCapabilities: [] };
311
+ }
312
+ const record = json;
313
+ const capabilities = parseCapabilityArray(record.capabilities);
314
+ const excludedCapabilities = parseCapabilityArray(record.excludedCapabilities);
315
+ const manifestVersion = typeof record.manifestVersion === "string" && record.manifestVersion.trim() ? record.manifestVersion.trim() : void 0;
316
+ return { capabilities, excludedCapabilities, manifestVersion };
317
+ }
318
+ function parseCapabilityArray(raw) {
319
+ if (!Array.isArray(raw)) return [];
320
+ return raw.filter(
321
+ (c) => !!c && typeof c === "object" && typeof c.pipeline === "string" && typeof c.modelId === "string"
322
+ );
323
+ }
324
+ function sortedCaps(caps) {
325
+ return [...caps].sort((a, b) => {
326
+ const p = a.pipeline.localeCompare(b.pipeline);
327
+ return p === 0 ? a.modelId.localeCompare(b.modelId) : p;
328
+ });
329
+ }
330
+ function computeManifestRevision(data) {
331
+ if (data == null) {
332
+ return "unavailable";
333
+ }
334
+ if (data.manifestVersion?.trim()) {
335
+ return data.manifestVersion.trim();
336
+ }
337
+ const caps = sortedCaps(data.capabilities ?? []);
338
+ const excl = sortedCaps(data.excludedCapabilities ?? []);
339
+ if (caps.length === 0 && excl.length === 0) {
340
+ return "empty";
341
+ }
342
+ return createHash("sha256").update(JSON.stringify({ capabilities: caps, excludedCapabilities: excl })).digest("hex").slice(0, 24);
343
+ }
344
+
345
+ // src/tokens.ts
346
+ var SIGNER_SESSION_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
347
+ var SIGNER_SESSION_EXPIRES_IN_SEC = Math.floor(SIGNER_SESSION_TTL_MS / 1e3);
348
+ var SIGN_JOB_SCOPE = "sign:job";
349
+ var PYMTHOUSE_SIGNER_SESSION_TTL_MS = SIGNER_SESSION_TTL_MS;
350
+ function computeSignerSessionExpiry(input) {
351
+ const createdAt = input instanceof Date ? input : new Date(input);
352
+ return new Date(createdAt.getTime() + SIGNER_SESSION_TTL_MS);
353
+ }
354
+ var computePymthouseExpiry = computeSignerSessionExpiry;
355
+ function isLikelyOidcJwt(rawToken) {
356
+ const t = rawToken.trim();
357
+ return t.startsWith("eyJ") && t.split(".").length >= 3;
358
+ }
359
+ function isOpaqueSignerSessionToken(rawToken) {
360
+ const t = rawToken.trim();
361
+ return t.length > 0 && !isLikelyOidcJwt(t);
362
+ }
363
+ function base64UrlPayloadToUtf8(payloadB64) {
364
+ const normalized = payloadB64.replaceAll("-", "+").replaceAll("_", "/");
365
+ const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
366
+ if (typeof Buffer !== "undefined") {
367
+ return Buffer.from(padded, "base64").toString("utf8");
368
+ }
369
+ return atob(padded);
370
+ }
371
+ function decodeJwtExp(rawToken) {
372
+ try {
373
+ const parts = rawToken.split(".");
374
+ if (parts.length < 2) return null;
375
+ const payloadJson = base64UrlPayloadToUtf8(parts[1]);
376
+ const payload = JSON.parse(payloadJson);
377
+ if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) return null;
378
+ const expMs = Math.floor(payload.exp * 1e3);
379
+ if (expMs <= 0) return null;
380
+ return new Date(expMs);
381
+ } catch {
382
+ return null;
383
+ }
384
+ }
385
+ function parseSignerSessionExchange(res) {
386
+ const accessToken = typeof res.access_token === "string" ? res.access_token.trim() : "";
387
+ if (!accessToken) {
388
+ throw new Error("PymtHouse signer session exchange returned no access_token");
389
+ }
390
+ if (isLikelyOidcJwt(accessToken)) {
391
+ throw new Error(
392
+ "PymtHouse signer session exchange returned a JWT; expected opaque signer session token"
393
+ );
394
+ }
395
+ const tokenType = typeof res.token_type === "string" && res.token_type.trim() ? res.token_type.trim() : "Bearer";
396
+ const expiresIn = typeof res.expires_in === "number" && Number.isFinite(res.expires_in) && res.expires_in > 0 ? Math.floor(res.expires_in) : SIGNER_SESSION_EXPIRES_IN_SEC;
397
+ const scope = typeof res.scope === "string" && res.scope.trim() ? res.scope.trim() : SIGN_JOB_SCOPE;
398
+ return {
399
+ accessToken,
400
+ tokenType,
401
+ expiresIn,
402
+ scope
403
+ };
404
+ }
182
405
  var ACCEPTED_ISSUED_TOKEN_TYPES = /* @__PURE__ */ new Set([
183
406
  "urn:ietf:params:oauth:token-type:access_token",
184
407
  "urn:pmth:token-type:remote-signer-session"
@@ -534,6 +757,126 @@ var PmtHouseClient = class {
534
757
  cache: "no-store"
535
758
  });
536
759
  }
760
+ /**
761
+ * Session-scoped usage for one `externalUserId`: user rollup plus merged pipeline/model breakdown.
762
+ */
763
+ async fetchUsageForExternalUser(input) {
764
+ const usageByUser = await this.getUsage({
765
+ startDate: input.startDate,
766
+ endDate: input.endDate,
767
+ groupBy: "user"
768
+ });
769
+ const userIds = getEndUserIdsForExternalUser(usageByUser, input.externalUserId);
770
+ const cap = input.maxEndUserIds ?? DEFAULT_MAX_END_USER_IDS;
771
+ const cappedUserIds = userIds.slice(0, cap);
772
+ const usagePipelineModels = await Promise.all(
773
+ cappedUserIds.map(
774
+ (userId) => this.getUsage({
775
+ startDate: input.startDate,
776
+ endDate: input.endDate,
777
+ groupBy: "pipeline_model",
778
+ userId
779
+ })
780
+ )
781
+ );
782
+ return buildMeScopeUsagePayload(usageByUser, input.externalUserId, usagePipelineModels);
783
+ }
784
+ async getAppManifest(opts) {
785
+ const url = `${this.getAppsBaseUrl()}/manifest`;
786
+ const headers = {
787
+ ...this.builderHeadersRecord()
788
+ };
789
+ if (opts?.ifNoneMatch) {
790
+ headers["If-None-Match"] = opts.ifNoneMatch;
791
+ }
792
+ this.logger?.debug?.("PmtHouse request", { method: "GET", url });
793
+ const response = await this.fetchImpl(url, {
794
+ method: "GET",
795
+ headers,
796
+ signal: opts?.signal,
797
+ cache: "no-store"
798
+ });
799
+ const etag = response.headers.get("etag")?.trim() ?? null;
800
+ if (response.status === 304) {
801
+ return {
802
+ manifest: null,
803
+ etag: etag ?? opts?.ifNoneMatch ?? null,
804
+ notModified: true
805
+ };
806
+ }
807
+ const raw = await response.text();
808
+ const ct = response.headers.get("content-type") ?? "";
809
+ const looksJson = ct.includes("application/json") || ct.includes("json");
810
+ const parsed = raw && looksJson ? this.safeParseJson(raw) : null;
811
+ if (!response.ok) {
812
+ const details = parsed ?? {};
813
+ let description;
814
+ if (typeof details.error_description === "string") {
815
+ description = details.error_description;
816
+ } else if (typeof details.error === "string") {
817
+ description = details.error;
818
+ } else {
819
+ description = `Request failed (${response.status})`;
820
+ }
821
+ throw new PmtHouseError(description, {
822
+ status: response.status,
823
+ code: typeof details.error === "string" ? details.error : "pymthouse_http_error",
824
+ details
825
+ });
826
+ }
827
+ if (!looksJson || parsed === null) {
828
+ throw new PmtHouseError("Expected JSON response from Builder manifest endpoint", {
829
+ status: 502,
830
+ code: "invalid_response",
831
+ details: { contentType: ct, preview: raw.slice(0, 200) }
832
+ });
833
+ }
834
+ return {
835
+ manifest: parseAppManifestResponse(parsed),
836
+ etag,
837
+ notModified: false
838
+ };
839
+ }
840
+ /**
841
+ * Upsert an external user, mint a short-lived JWT, and exchange for an opaque signer session.
842
+ */
843
+ async mintSignerSessionForExternalUser(input) {
844
+ await this.upsertAppUser({
845
+ externalUserId: input.externalUserId,
846
+ email: input.email,
847
+ status: "active"
848
+ });
849
+ const exchange = await this.mintUserSignerSessionToken({
850
+ externalUserId: input.externalUserId,
851
+ scope: input.scope ?? SIGN_JOB_SCOPE,
852
+ resource: this.issuerUrl
853
+ });
854
+ return parseSignerSessionExchange(exchange);
855
+ }
856
+ /**
857
+ * Approve a pending RFC 8628 device code for an external user (Option B).
858
+ */
859
+ async approveDeviceLogin(input) {
860
+ if (input.publicClientId && input.publicClientId !== this.publicClientId) {
861
+ throw new PmtHouseError(
862
+ "publicClientId does not match configured public client id",
863
+ { status: 400, code: "invalid_client" }
864
+ );
865
+ }
866
+ await this.upsertAppUser({
867
+ externalUserId: input.externalUserId,
868
+ email: input.email,
869
+ status: "active"
870
+ });
871
+ const userToken = await this.mintUserAccessToken({
872
+ externalUserId: input.externalUserId,
873
+ scope: SIGN_JOB_SCOPE
874
+ });
875
+ await this.completeDeviceApproval({
876
+ userJwt: userToken.access_token,
877
+ userCode: input.userCode
878
+ });
879
+ }
537
880
  tokenEndpointFetchOptions() {
538
881
  const o = {
539
882
  [customFetch]: this.fetchImpl
@@ -550,6 +893,9 @@ var PmtHouseClient = class {
550
893
  return new URL(this.issuerUrl).origin;
551
894
  }
552
895
  builderHeaders() {
896
+ return this.builderHeadersRecord();
897
+ }
898
+ builderHeadersRecord() {
553
899
  return {
554
900
  Authorization: encodeClientSecretBasic(this.m2mClientId, this.m2mClientSecret),
555
901
  "Content-Type": "application/json",
@@ -573,7 +919,14 @@ var PmtHouseClient = class {
573
919
  const parsed = raw && looksJson ? this.safeParseJson(raw) : raw ? null : null;
574
920
  if (!response.ok) {
575
921
  const details = parsed ?? {};
576
- const description = typeof details.error_description === "string" ? details.error_description : typeof details.error === "string" ? details.error : `Request failed (${response.status})`;
922
+ let description;
923
+ if (typeof details.error_description === "string") {
924
+ description = details.error_description;
925
+ } else if (typeof details.error === "string") {
926
+ description = details.error;
927
+ } else {
928
+ description = `Request failed (${response.status})`;
929
+ }
577
930
  throw new PmtHouseError(description, {
578
931
  status: response.status,
579
932
  code: typeof details.error === "string" ? details.error : "pymthouse_http_error",
@@ -616,55 +969,52 @@ var PmtHouseClient = class {
616
969
  }
617
970
  };
618
971
 
619
- // src/env.ts
620
- function assertEnvModuleServerOnly() {
621
- if (typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined") {
622
- throw new Error(
623
- "@pymthouse/builder-sdk/env is server-only: do not import createPmtHouseClientFromEnv or getPymthouseBaseUrl in client-side code. Use a Route Handler, Server Action, or other server/runtime; keep M2M credentials out of the browser bundle."
624
- );
972
+ // src/config.ts
973
+ 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
+ function trimEnv(name) {
975
+ const value = process.env[name];
976
+ if (!value) return null;
977
+ const trimmed = value.trim();
978
+ return trimmed || null;
979
+ }
980
+ function readPymthouseEnv() {
981
+ const issuerUrl = trimEnv("PYMTHOUSE_ISSUER_URL");
982
+ const publicClientId = trimEnv("PYMTHOUSE_PUBLIC_CLIENT_ID");
983
+ const m2mClientId = trimEnv("PYMTHOUSE_M2M_CLIENT_ID");
984
+ const m2mClientSecret = trimEnv("PYMTHOUSE_M2M_CLIENT_SECRET");
985
+ if (!issuerUrl || !publicClientId || !m2mClientId || !m2mClientSecret) {
986
+ return null;
625
987
  }
988
+ return {
989
+ issuerUrl: stripTrailingSlashes(issuerUrl),
990
+ publicClientId,
991
+ m2mClientId,
992
+ m2mClientSecret
993
+ };
626
994
  }
627
- assertEnvModuleServerOnly();
628
- var cachedClient = null;
629
- function requiredEnv(name) {
630
- const value = process.env[name];
631
- if (value && value.trim()) {
632
- return value.trim();
995
+ function getPymthouseIssuerUrlFromEnv() {
996
+ const raw = trimEnv("PYMTHOUSE_ISSUER_URL");
997
+ if (!raw) return null;
998
+ try {
999
+ return stripTrailingSlashes(new URL(raw).href);
1000
+ } catch {
1001
+ return null;
633
1002
  }
634
- throw new PmtHouseError(`Missing required environment variable: ${name}`, {
635
- status: 500,
636
- code: "missing_env"
637
- });
638
1003
  }
639
- function getPymthouseBaseUrl() {
640
- const issuerUrl = requiredEnv("PYMTHOUSE_ISSUER_URL");
641
- return new URL(stripTrailingSlashes(issuerUrl)).origin;
642
- }
643
- function createPmtHouseClientFromEnv() {
644
- if (cachedClient) {
645
- return cachedClient;
646
- }
647
- const issuerUrl = requiredEnv("PYMTHOUSE_ISSUER_URL");
648
- cachedClient = new PmtHouseClient({
649
- issuerUrl,
650
- publicClientId: requiredEnv("PYMTHOUSE_PUBLIC_CLIENT_ID"),
651
- m2mClientId: requiredEnv("PYMTHOUSE_M2M_CLIENT_ID"),
652
- m2mClientSecret: requiredEnv("PYMTHOUSE_M2M_CLIENT_SECRET"),
653
- allowInsecureHttp: issuerUrl.startsWith("http:"),
654
- logger: {
655
- debug: (message, details) => {
656
- if (process.env.NODE_ENV !== "production") {
657
- console.debug(`[pymthouse] ${message}`, details ?? {});
658
- }
659
- },
660
- warn: (message, details) => {
661
- console.warn(`[pymthouse] ${message}`, details ?? {});
662
- }
663
- }
664
- });
665
- return cachedClient;
1004
+ function getPymthousePublicClientIdFromEnv() {
1005
+ return trimEnv("PYMTHOUSE_PUBLIC_CLIENT_ID");
1006
+ }
1007
+ function isPymthouseConfigured() {
1008
+ return readPymthouseEnv() !== null;
1009
+ }
1010
+ function getBuilderApiV1BaseFromIssuerUrl(issuerUrl) {
1011
+ const noTrail = stripTrailingSlashes(issuerUrl.trim());
1012
+ return noTrail.replace(/\/oidc\/?$/i, "");
1013
+ }
1014
+ function getPymthouseIssuerOrigin(issuerUrl) {
1015
+ return new URL(stripTrailingSlashes(issuerUrl.trim())).origin;
666
1016
  }
667
1017
 
668
- export { PmtHouseClient, PmtHouseError, aggregateUsageByExternalUserId, authorizationServerToOidcDocument, buildDeviceCodeResource, clearDiscoveryCache, createPmtHouseClientFromEnv, fetchDiscoveryDocument, getPymthouseBaseUrl, listUsageByPipelineModel, loadAuthorizationServer, normalizeUserCode, summarizeUsageForExternalUser, toPmtHouseError };
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 };
669
1019
  //# sourceMappingURL=index.js.map
670
1020
  //# sourceMappingURL=index.js.map