@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/README.md +40 -2
- package/dist/{env-CZczUMzR.d.cts → client-BhC1YhB1.d.cts} +25 -12
- package/dist/{env-4YmzarGJ.d.ts → client-BroVFyIy.d.ts} +25 -12
- package/dist/config.cjs +66 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +22 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.js +58 -0
- package/dist/config.js.map +1 -0
- package/dist/device-initiate.cjs +88 -0
- package/dist/device-initiate.cjs.map +1 -0
- package/dist/device-initiate.d.cts +32 -0
- package/dist/device-initiate.d.ts +32 -0
- package/dist/device-initiate.js +84 -0
- package/dist/device-initiate.js.map +1 -0
- package/dist/device.d.cts +1 -1
- package/dist/device.d.ts +1 -1
- package/dist/env.cjs +286 -1
- package/dist/env.cjs.map +1 -1
- package/dist/env.d.cts +15 -2
- package/dist/env.d.ts +15 -2
- package/dist/env.js +286 -1
- package/dist/env.js.map +1 -1
- package/dist/index.cjs +422 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -5
- package/dist/index.d.ts +39 -5
- package/dist/index.js +396 -46
- package/dist/index.js.map +1 -1
- package/dist/tokens.cjs +75 -0
- package/dist/tokens.cjs.map +1 -0
- package/dist/tokens.d.cts +48 -0
- package/dist/tokens.d.ts +48 -0
- package/dist/tokens.js +64 -0
- package/dist/tokens.js.map +1 -0
- package/dist/{types-W9PJAspR.d.cts → types-rKzVXvMu.d.cts} +64 -4
- package/dist/{types-W9PJAspR.d.ts → types-rKzVXvMu.d.ts} +64 -4
- package/dist/verify.d.cts +1 -1
- package/dist/verify.d.ts +1 -1
- package/package.json +16 -1
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
|
-
|
|
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
|
-
|
|
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/
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
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
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
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,
|
|
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
|