@pymthouse/builder-sdk 0.0.8 → 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.
- package/README.md +106 -2
- package/dist/client-BHfjDvIe.d.ts +129 -0
- package/dist/client-CvhJEhjV.d.cts +129 -0
- package/dist/config.cjs +122 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +29 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.js +111 -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.cjs +1 -1
- package/dist/device.cjs.map +1 -1
- package/dist/device.d.cts +1 -1
- package/dist/device.d.ts +1 -1
- package/dist/device.js +1 -1
- package/dist/device.js.map +1 -1
- package/dist/env.cjs +1071 -28
- 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 +1071 -28
- package/dist/env.js.map +1 -1
- package/dist/gateway/client/index.cjs +492 -0
- package/dist/gateway/client/index.cjs.map +1 -0
- package/dist/gateway/client/index.d.cts +63 -0
- package/dist/gateway/client/index.d.ts +63 -0
- package/dist/gateway/client/index.js +489 -0
- package/dist/gateway/client/index.js.map +1 -0
- package/dist/gateway/index.cjs +16 -0
- package/dist/gateway/index.cjs.map +1 -0
- package/dist/gateway/index.d.cts +52 -0
- package/dist/gateway/index.d.ts +52 -0
- package/dist/gateway/index.js +10 -0
- package/dist/gateway/index.js.map +1 -0
- package/dist/gateway/server/index.cjs +1248 -0
- package/dist/gateway/server/index.cjs.map +1 -0
- package/dist/gateway/server/index.d.cts +31 -0
- package/dist/gateway/server/index.d.ts +31 -0
- package/dist/gateway/server/index.js +1233 -0
- package/dist/gateway/server/index.js.map +1 -0
- package/dist/index.cjs +1401 -137
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -5
- package/dist/index.d.ts +41 -5
- package/dist/index.js +1334 -105
- package/dist/index.js.map +1 -1
- package/dist/ingest-B3Yi8Tb1.d.cts +271 -0
- package/dist/ingest-DoKJTWU9.d.ts +271 -0
- package/dist/plan-pricing.cjs +108 -0
- package/dist/plan-pricing.cjs.map +1 -0
- package/dist/plan-pricing.d.cts +15 -0
- package/dist/plan-pricing.d.ts +15 -0
- package/dist/plan-pricing.js +98 -0
- package/dist/plan-pricing.js.map +1 -0
- package/dist/signer/server.cjs +1366 -0
- package/dist/signer/server.cjs.map +1 -0
- package/dist/signer/server.d.cts +73 -0
- package/dist/signer/server.d.ts +73 -0
- package/dist/signer/server.js +1331 -0
- package/dist/signer/server.js.map +1 -0
- 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-_R1AwEZp.d.cts +343 -0
- package/dist/types-_R1AwEZp.d.ts +343 -0
- package/dist/verify.cjs +1 -1
- package/dist/verify.cjs.map +1 -1
- package/dist/verify.d.cts +1 -1
- package/dist/verify.d.ts +1 -1
- package/dist/verify.js +1 -1
- package/dist/verify.js.map +1 -1
- package/gateway/proto/lp_rpc.proto +542 -0
- package/package.json +57 -1
- package/dist/env-4YmzarGJ.d.ts +0 -68
- package/dist/env-CZczUMzR.d.cts +0 -68
- package/dist/types-W9PJAspR.d.cts +0 -136
- package/dist/types-W9PJAspR.d.ts +0 -136
package/dist/index.cjs
CHANGED
|
@@ -1,40 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var oauth4webapi = require('oauth4webapi');
|
|
4
|
+
var crypto = require('crypto');
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
let feeWei = 0n;
|
|
16
|
-
let requestCount = 0;
|
|
17
|
-
for (const row of rows) {
|
|
18
|
-
feeWei += BigInt(row.feeWei);
|
|
19
|
-
requestCount += row.requestCount;
|
|
20
|
-
}
|
|
21
|
-
return {
|
|
22
|
-
externalUserId,
|
|
23
|
-
requestCount,
|
|
24
|
-
feeWei: feeWei.toString()
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
function summarizeUsageForExternalUser(usage, externalUserId) {
|
|
28
|
-
return aggregateUsageByExternalUserId(usage.byUser, externalUserId);
|
|
29
|
-
}
|
|
30
|
-
function listUsageByPipelineModel(usage) {
|
|
31
|
-
const rows = usage.byPipelineModel ?? [];
|
|
32
|
-
return [...rows].sort((a, b) => {
|
|
33
|
-
const p = a.pipeline.localeCompare(b.pipeline);
|
|
34
|
-
if (p !== 0) return p;
|
|
35
|
-
return a.modelId.localeCompare(b.modelId);
|
|
36
|
-
});
|
|
37
|
-
}
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
38
15
|
|
|
39
16
|
// src/encoding.ts
|
|
40
17
|
function encodeClientSecretBasic(clientId, clientSecret) {
|
|
@@ -42,55 +19,94 @@ function encodeClientSecretBasic(clientId, clientSecret) {
|
|
|
42
19
|
const b64 = typeof Buffer !== "undefined" ? Buffer.from(raw, "utf8").toString("base64") : btoa(Array.from(new TextEncoder().encode(raw), (c) => String.fromCharCode(c)).join(""));
|
|
43
20
|
return `Basic ${b64}`;
|
|
44
21
|
}
|
|
22
|
+
var init_encoding = __esm({
|
|
23
|
+
"src/encoding.ts"() {
|
|
24
|
+
}
|
|
25
|
+
});
|
|
45
26
|
|
|
46
27
|
// src/errors.ts
|
|
47
|
-
var PmtHouseError = class extends Error {
|
|
48
|
-
status;
|
|
49
|
-
code;
|
|
50
|
-
details;
|
|
51
|
-
constructor(message, {
|
|
52
|
-
status = 500,
|
|
53
|
-
code = "pymthouse_error",
|
|
54
|
-
details
|
|
55
|
-
} = {}) {
|
|
56
|
-
super(message);
|
|
57
|
-
this.name = "PmtHouseError";
|
|
58
|
-
this.status = status;
|
|
59
|
-
this.code = code;
|
|
60
|
-
this.details = details;
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
28
|
function toPmtHouseError(error, fallbackMessage) {
|
|
64
|
-
if (error instanceof PmtHouseError) {
|
|
29
|
+
if (error instanceof exports.PmtHouseError) {
|
|
65
30
|
return error;
|
|
66
31
|
}
|
|
67
32
|
if (error instanceof Error) {
|
|
68
|
-
return new PmtHouseError(error.message || fallbackMessage, {
|
|
33
|
+
return new exports.PmtHouseError(error.message || fallbackMessage, {
|
|
69
34
|
code: "unexpected_error",
|
|
70
35
|
status: 500
|
|
71
36
|
});
|
|
72
37
|
}
|
|
73
|
-
return new PmtHouseError(fallbackMessage, {
|
|
38
|
+
return new exports.PmtHouseError(fallbackMessage, {
|
|
74
39
|
code: "unexpected_error",
|
|
75
40
|
status: 500
|
|
76
41
|
});
|
|
77
42
|
}
|
|
43
|
+
exports.PmtHouseError = void 0;
|
|
44
|
+
var init_errors = __esm({
|
|
45
|
+
"src/errors.ts"() {
|
|
46
|
+
exports.PmtHouseError = class extends Error {
|
|
47
|
+
status;
|
|
48
|
+
code;
|
|
49
|
+
details;
|
|
50
|
+
constructor(message, {
|
|
51
|
+
status = 500,
|
|
52
|
+
code = "pymthouse_error",
|
|
53
|
+
details
|
|
54
|
+
} = {}) {
|
|
55
|
+
super(message);
|
|
56
|
+
this.name = "PmtHouseError";
|
|
57
|
+
this.status = status;
|
|
58
|
+
this.code = code;
|
|
59
|
+
this.details = details;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
});
|
|
78
64
|
|
|
79
65
|
// src/string-utils.ts
|
|
80
66
|
function stripTrailingSlashes(value) {
|
|
81
67
|
let end = value.length;
|
|
82
|
-
while (end > 0 && value.
|
|
68
|
+
while (end > 0 && (value.codePointAt(end - 1) ?? 0) === 47) {
|
|
83
69
|
end--;
|
|
84
70
|
}
|
|
85
71
|
return value.slice(0, end);
|
|
86
72
|
}
|
|
87
|
-
|
|
88
|
-
|
|
73
|
+
function endsWithIgnoreCase(value, suffix) {
|
|
74
|
+
if (suffix.length > value.length) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
const start = value.length - suffix.length;
|
|
78
|
+
for (let i = 0; i < suffix.length; i++) {
|
|
79
|
+
const a = value.codePointAt(start + i) ?? 0;
|
|
80
|
+
const b = suffix.codePointAt(i) ?? 0;
|
|
81
|
+
if (a !== b && (a | 32) !== (b | 32)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
function stripSuffixIgnoreCase(value, suffix) {
|
|
88
|
+
return endsWithIgnoreCase(value, suffix) ? value.slice(0, value.length - suffix.length) : value;
|
|
89
|
+
}
|
|
90
|
+
function stripOidcPathSuffix(issuerUrl) {
|
|
91
|
+
let base = stripTrailingSlashes(issuerUrl.trim());
|
|
92
|
+
base = stripSuffixIgnoreCase(base, "/oidc");
|
|
93
|
+
return stripTrailingSlashes(base);
|
|
94
|
+
}
|
|
95
|
+
function stripIssuerOriginFromOidcUrl(issuerUrl) {
|
|
96
|
+
let base = stripTrailingSlashes(issuerUrl.trim());
|
|
97
|
+
base = stripSuffixIgnoreCase(base, "/api/v1/oidc");
|
|
98
|
+
base = stripSuffixIgnoreCase(base, "/oidc");
|
|
99
|
+
return stripTrailingSlashes(base);
|
|
100
|
+
}
|
|
101
|
+
var init_string_utils = __esm({
|
|
102
|
+
"src/string-utils.ts"() {
|
|
103
|
+
}
|
|
104
|
+
});
|
|
89
105
|
function authorizationServerToOidcDocument(as) {
|
|
90
106
|
const tokenEndpoint = as.token_endpoint;
|
|
91
107
|
const jwksUri = as.jwks_uri;
|
|
92
108
|
if (!tokenEndpoint || !jwksUri) {
|
|
93
|
-
throw new PmtHouseError("OIDC discovery document is missing token_endpoint or jwks_uri", {
|
|
109
|
+
throw new exports.PmtHouseError("OIDC discovery document is missing token_endpoint or jwks_uri", {
|
|
94
110
|
status: 500,
|
|
95
111
|
code: "oidc_discovery_invalid"
|
|
96
112
|
});
|
|
@@ -104,8 +120,6 @@ function authorizationServerToOidcDocument(as) {
|
|
|
104
120
|
device_authorization_endpoint: as.device_authorization_endpoint
|
|
105
121
|
};
|
|
106
122
|
}
|
|
107
|
-
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
108
|
-
var discoveryCache = /* @__PURE__ */ new Map();
|
|
109
123
|
function normalizedIssuerKey(issuerUrl) {
|
|
110
124
|
return stripTrailingSlashes(issuerUrl);
|
|
111
125
|
}
|
|
@@ -151,42 +165,945 @@ function clearDiscoveryCache(issuerUrl) {
|
|
|
151
165
|
discoveryCache.delete(normalizedIssuerKey(issuerUrl));
|
|
152
166
|
}
|
|
153
167
|
function mapOAuthDiscoveryError(error) {
|
|
154
|
-
if (error instanceof PmtHouseError) {
|
|
168
|
+
if (error instanceof exports.PmtHouseError) {
|
|
155
169
|
return error;
|
|
156
170
|
}
|
|
157
171
|
if (error instanceof Error) {
|
|
158
|
-
return new PmtHouseError(error.message, {
|
|
172
|
+
return new exports.PmtHouseError(error.message, {
|
|
159
173
|
status: 500,
|
|
160
174
|
code: "oidc_discovery_invalid",
|
|
161
175
|
details: { cause: error.cause }
|
|
162
176
|
});
|
|
163
177
|
}
|
|
164
|
-
return new PmtHouseError("OIDC discovery failed", {
|
|
178
|
+
return new exports.PmtHouseError("OIDC discovery failed", {
|
|
165
179
|
status: 500,
|
|
166
180
|
code: "oidc_discovery_invalid"
|
|
167
181
|
});
|
|
168
182
|
}
|
|
169
183
|
function mapDiscoveryNetworkError(error) {
|
|
170
|
-
if (error instanceof PmtHouseError) {
|
|
184
|
+
if (error instanceof exports.PmtHouseError) {
|
|
171
185
|
return error;
|
|
172
186
|
}
|
|
173
187
|
if (error instanceof Error) {
|
|
174
|
-
return new PmtHouseError(`Failed to load OIDC discovery: ${error.message}`, {
|
|
188
|
+
return new exports.PmtHouseError(`Failed to load OIDC discovery: ${error.message}`, {
|
|
175
189
|
status: 502,
|
|
176
190
|
code: "oidc_discovery_failed"
|
|
177
191
|
});
|
|
178
192
|
}
|
|
179
|
-
return new PmtHouseError("Failed to load OIDC discovery", {
|
|
193
|
+
return new exports.PmtHouseError("Failed to load OIDC discovery", {
|
|
180
194
|
status: 502,
|
|
181
195
|
code: "oidc_discovery_failed"
|
|
182
196
|
});
|
|
183
197
|
}
|
|
198
|
+
var CACHE_TTL_MS, discoveryCache;
|
|
199
|
+
var init_discovery = __esm({
|
|
200
|
+
"src/discovery.ts"() {
|
|
201
|
+
init_errors();
|
|
202
|
+
init_string_utils();
|
|
203
|
+
CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
204
|
+
discoveryCache = /* @__PURE__ */ new Map();
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// src/signer/fetch-json.ts
|
|
209
|
+
function oauthFailureDescription(parsed, failureLabel, status) {
|
|
210
|
+
if (typeof parsed.error_description === "string") {
|
|
211
|
+
return parsed.error_description;
|
|
212
|
+
}
|
|
213
|
+
if (typeof parsed.error === "string") {
|
|
214
|
+
return parsed.error;
|
|
215
|
+
}
|
|
216
|
+
return `${failureLabel} (${status})`;
|
|
217
|
+
}
|
|
218
|
+
async function readJsonObjectFromResponse(response, options) {
|
|
219
|
+
const text = await response.text();
|
|
220
|
+
let parsed;
|
|
221
|
+
try {
|
|
222
|
+
parsed = text ? JSON.parse(text) : {};
|
|
223
|
+
} catch {
|
|
224
|
+
throw new exports.PmtHouseError(options.invalidJsonMessage, {
|
|
225
|
+
status: 502,
|
|
226
|
+
code: options.invalidJsonCode,
|
|
227
|
+
details: { status: response.status }
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
if (!response.ok) {
|
|
231
|
+
const description = oauthFailureDescription(parsed, options.failureLabel, response.status);
|
|
232
|
+
throw new exports.PmtHouseError(description, {
|
|
233
|
+
status: response.status,
|
|
234
|
+
code: typeof parsed.error === "string" ? parsed.error : options.defaultErrorCode,
|
|
235
|
+
details: parsed
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return parsed;
|
|
239
|
+
}
|
|
240
|
+
var init_fetch_json = __esm({
|
|
241
|
+
"src/signer/fetch-json.ts"() {
|
|
242
|
+
init_errors();
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// src/signer/handler-errors.ts
|
|
247
|
+
function signerHandlerErrorResponse(error) {
|
|
248
|
+
if (error instanceof exports.PmtHouseError) {
|
|
249
|
+
return new Response(
|
|
250
|
+
JSON.stringify({
|
|
251
|
+
error: error.code,
|
|
252
|
+
error_description: error.message,
|
|
253
|
+
details: error.details
|
|
254
|
+
}),
|
|
255
|
+
{
|
|
256
|
+
status: error.status,
|
|
257
|
+
headers: { "Content-Type": "application/json" }
|
|
258
|
+
}
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
const message = error instanceof Error ? error.message : "Internal error";
|
|
262
|
+
return new Response(JSON.stringify({ error: "internal_error", error_description: message }), {
|
|
263
|
+
status: 500,
|
|
264
|
+
headers: { "Content-Type": "application/json" }
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
var init_handler_errors = __esm({
|
|
268
|
+
"src/signer/handler-errors.ts"() {
|
|
269
|
+
init_errors();
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// src/signer/json-fields.ts
|
|
274
|
+
function readStringField(body, key, errorCode, messagePrefix = "Response") {
|
|
275
|
+
const value = body[key];
|
|
276
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
277
|
+
throw new exports.PmtHouseError(`${messagePrefix} missing ${key}`, {
|
|
278
|
+
status: 502,
|
|
279
|
+
code: errorCode
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
return value.trim();
|
|
283
|
+
}
|
|
284
|
+
function readExpiresIn(body, errorCode) {
|
|
285
|
+
const expiresIn = body.expires_in;
|
|
286
|
+
if (typeof expiresIn !== "number" || !Number.isFinite(expiresIn) || expiresIn <= 0) {
|
|
287
|
+
throw new exports.PmtHouseError("Response missing expires_in", {
|
|
288
|
+
status: 502,
|
|
289
|
+
code: errorCode
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
return Math.floor(expiresIn);
|
|
293
|
+
}
|
|
294
|
+
var init_json_fields = __esm({
|
|
295
|
+
"src/signer/json-fields.ts"() {
|
|
296
|
+
init_errors();
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// src/signer/mint-token.ts
|
|
301
|
+
function parseMintUserSignerTokenResponse(body, ttlRefreshRatio = DEFAULT_TTL_REFRESH_RATIO) {
|
|
302
|
+
const accessToken = readStringField(body, "access_token", TOKEN_RESPONSE_ERROR, "Token response");
|
|
303
|
+
const expiresIn = readExpiresIn(body, TOKEN_RESPONSE_ERROR);
|
|
304
|
+
const balanceUsdMicros = readStringField(
|
|
305
|
+
body,
|
|
306
|
+
"balanceUsdMicros",
|
|
307
|
+
TOKEN_RESPONSE_ERROR,
|
|
308
|
+
"Token response"
|
|
309
|
+
);
|
|
310
|
+
const lifetimeGrantedUsdMicros = readStringField(
|
|
311
|
+
body,
|
|
312
|
+
"lifetimeGrantedUsdMicros",
|
|
313
|
+
TOKEN_RESPONSE_ERROR,
|
|
314
|
+
"Token response"
|
|
315
|
+
);
|
|
316
|
+
const now = Date.now();
|
|
317
|
+
const expiresAt = now + expiresIn * 1e3;
|
|
318
|
+
const refreshAt = now + Math.floor(expiresIn * 1e3 * ttlRefreshRatio);
|
|
319
|
+
return {
|
|
320
|
+
jwt: accessToken,
|
|
321
|
+
expiresAt,
|
|
322
|
+
refreshAt,
|
|
323
|
+
balanceUsdMicros,
|
|
324
|
+
lifetimeGrantedUsdMicros
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
var LIVEPEER_REMOTE_SIGNER_AUDIENCE, DEFAULT_TTL_REFRESH_RATIO, TOKEN_RESPONSE_ERROR;
|
|
328
|
+
var init_mint_token = __esm({
|
|
329
|
+
"src/signer/mint-token.ts"() {
|
|
330
|
+
init_json_fields();
|
|
331
|
+
LIVEPEER_REMOTE_SIGNER_AUDIENCE = "livepeer-remote-signer";
|
|
332
|
+
DEFAULT_TTL_REFRESH_RATIO = 0.8;
|
|
333
|
+
TOKEN_RESPONSE_ERROR = "invalid_token_response";
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// src/signer/device-exchange.ts
|
|
338
|
+
function extractSignerAccessTokenFromExchangeBody(body) {
|
|
339
|
+
const tokenObj = body.token;
|
|
340
|
+
if (tokenObj !== null && typeof tokenObj === "object" && !Array.isArray(tokenObj)) {
|
|
341
|
+
const nested = tokenObj;
|
|
342
|
+
for (const key of ["accessToken", "access_token"]) {
|
|
343
|
+
const value = nested[key];
|
|
344
|
+
if (typeof value === "string" && value.trim()) {
|
|
345
|
+
return value.trim();
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
for (const key of ["accessToken", "access_token"]) {
|
|
350
|
+
const value = body[key];
|
|
351
|
+
if (typeof value === "string" && value.trim()) {
|
|
352
|
+
return value.trim();
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
throw new exports.PmtHouseError("Device exchange response missing signer access token", {
|
|
356
|
+
status: 502,
|
|
357
|
+
code: "invalid_exchange_response"
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
function normalizeDeviceExchangeResponse(minted, options) {
|
|
361
|
+
const scope = minted.scope.trim() || "sign:job";
|
|
362
|
+
const body = {
|
|
363
|
+
access_token: minted.access_token,
|
|
364
|
+
token_type: "Bearer",
|
|
365
|
+
expires_in: minted.expires_in,
|
|
366
|
+
scope,
|
|
367
|
+
balanceUsdMicros: minted.balanceUsdMicros,
|
|
368
|
+
lifetimeGrantedUsdMicros: minted.lifetimeGrantedUsdMicros,
|
|
369
|
+
token: {
|
|
370
|
+
accessToken: minted.access_token,
|
|
371
|
+
access_token: minted.access_token,
|
|
372
|
+
expiresIn: minted.expires_in,
|
|
373
|
+
expires_in: minted.expires_in,
|
|
374
|
+
scope,
|
|
375
|
+
balanceUsdMicros: minted.balanceUsdMicros,
|
|
376
|
+
lifetimeGrantedUsdMicros: minted.lifetimeGrantedUsdMicros
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
const signerUrl = options?.signerUrl?.trim();
|
|
380
|
+
if (signerUrl) {
|
|
381
|
+
body.signerUrl = signerUrl;
|
|
382
|
+
}
|
|
383
|
+
return body;
|
|
384
|
+
}
|
|
385
|
+
async function mintSignerTokenFromDeviceToken(options) {
|
|
386
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
387
|
+
const issuerUrl = stripTrailingSlashes(options.issuerUrl);
|
|
388
|
+
const as = await loadAuthorizationServer(issuerUrl, fetchImpl, {
|
|
389
|
+
allowInsecureHttp: options.allowInsecureHttp
|
|
390
|
+
});
|
|
391
|
+
const tokenEndpoint = as.token_endpoint;
|
|
392
|
+
if (!tokenEndpoint) {
|
|
393
|
+
throw new exports.PmtHouseError("OIDC discovery document is missing token_endpoint", {
|
|
394
|
+
status: 500,
|
|
395
|
+
code: "oidc_discovery_invalid"
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
const audience = options.audience?.trim() || LIVEPEER_REMOTE_SIGNER_AUDIENCE;
|
|
399
|
+
const params = new URLSearchParams({
|
|
400
|
+
grant_type: TOKEN_EXCHANGE_GRANT,
|
|
401
|
+
subject_token: options.deviceToken,
|
|
402
|
+
subject_token_type: SUBJECT_ACCESS_TOKEN_TYPE,
|
|
403
|
+
audience,
|
|
404
|
+
resource: audience
|
|
405
|
+
});
|
|
406
|
+
if (options.scope?.trim()) {
|
|
407
|
+
params.set("scope", options.scope.trim());
|
|
408
|
+
}
|
|
409
|
+
const response = await fetchImpl(tokenEndpoint, {
|
|
410
|
+
method: "POST",
|
|
411
|
+
headers: {
|
|
412
|
+
Authorization: encodeClientSecretBasic(options.m2mClientId, options.m2mClientSecret),
|
|
413
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
414
|
+
Accept: "application/json"
|
|
415
|
+
},
|
|
416
|
+
body: params.toString(),
|
|
417
|
+
cache: "no-store"
|
|
418
|
+
});
|
|
419
|
+
const parsed = await readJsonObjectFromResponse(response, {
|
|
420
|
+
invalidJsonMessage: "Token endpoint returned invalid JSON",
|
|
421
|
+
invalidJsonCode: "invalid_token_response",
|
|
422
|
+
failureLabel: "Signer JWT exchange failed",
|
|
423
|
+
defaultErrorCode: "token_exchange_failed"
|
|
424
|
+
});
|
|
425
|
+
const cached = parseMintUserSignerTokenResponse(parsed);
|
|
426
|
+
return {
|
|
427
|
+
access_token: cached.jwt,
|
|
428
|
+
expires_in: readExpiresIn(parsed, EXCHANGE_RESPONSE_ERROR),
|
|
429
|
+
scope: readStringField(parsed, "scope", EXCHANGE_RESPONSE_ERROR),
|
|
430
|
+
balanceUsdMicros: cached.balanceUsdMicros,
|
|
431
|
+
lifetimeGrantedUsdMicros: cached.lifetimeGrantedUsdMicros
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
var TOKEN_EXCHANGE_GRANT, SUBJECT_ACCESS_TOKEN_TYPE, EXCHANGE_RESPONSE_ERROR;
|
|
435
|
+
var init_device_exchange = __esm({
|
|
436
|
+
"src/signer/device-exchange.ts"() {
|
|
437
|
+
init_discovery();
|
|
438
|
+
init_encoding();
|
|
439
|
+
init_errors();
|
|
440
|
+
init_string_utils();
|
|
441
|
+
init_fetch_json();
|
|
442
|
+
init_json_fields();
|
|
443
|
+
init_mint_token();
|
|
444
|
+
TOKEN_EXCHANGE_GRANT = "urn:ietf:params:oauth:grant-type:token-exchange";
|
|
445
|
+
SUBJECT_ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
|
|
446
|
+
EXCHANGE_RESPONSE_ERROR = "invalid_exchange_response";
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// src/signer/api-key-exchange.ts
|
|
451
|
+
var api_key_exchange_exports = {};
|
|
452
|
+
__export(api_key_exchange_exports, {
|
|
453
|
+
createApiKeyExchangeHandler: () => createApiKeyExchangeHandler,
|
|
454
|
+
exchangeApiKeyForSigner: () => exchangeApiKeyForSigner,
|
|
455
|
+
mintSignerSessionFromApiKey: () => mintSignerSessionFromApiKey,
|
|
456
|
+
mintUserAccessTokenFromApiKey: () => mintUserAccessTokenFromApiKey,
|
|
457
|
+
parseApiKeyExchangeRequestBody: () => parseApiKeyExchangeRequestBody
|
|
458
|
+
});
|
|
459
|
+
async function parseApiKeyExchangeRequestBody(request) {
|
|
460
|
+
let body;
|
|
461
|
+
try {
|
|
462
|
+
body = await request.json();
|
|
463
|
+
} catch {
|
|
464
|
+
throw new exports.PmtHouseError("Request body must be JSON", {
|
|
465
|
+
status: 400,
|
|
466
|
+
code: "invalid_request"
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
if (body === null || typeof body !== "object" || Array.isArray(body)) {
|
|
470
|
+
throw new exports.PmtHouseError("Request body must be a JSON object", {
|
|
471
|
+
status: 400,
|
|
472
|
+
code: "invalid_request"
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
const record = body;
|
|
476
|
+
const apiKeyRaw = record.apiKey;
|
|
477
|
+
if (typeof apiKeyRaw !== "string" || !apiKeyRaw.trim()) {
|
|
478
|
+
throw new exports.PmtHouseError("Request body must include apiKey", {
|
|
479
|
+
status: 400,
|
|
480
|
+
code: "invalid_request"
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
const scope = typeof record.scope === "string" && record.scope.trim() ? record.scope.trim() : void 0;
|
|
484
|
+
const clientId = typeof record.clientId === "string" && record.clientId.trim() ? record.clientId.trim() : void 0;
|
|
485
|
+
return { apiKey: apiKeyRaw.trim(), scope, clientId };
|
|
486
|
+
}
|
|
487
|
+
async function mintUserAccessTokenFromApiKey(input) {
|
|
488
|
+
const fetchImpl = input.fetch ?? fetch;
|
|
489
|
+
const issuerOrigin = stripIssuerOriginFromOidcUrl(input.issuerUrl);
|
|
490
|
+
const url = `${issuerOrigin}/api/v1/apps/${encodeURIComponent(input.publicClientId)}/auth/api-key/token`;
|
|
491
|
+
const response = await fetchImpl(url, {
|
|
492
|
+
method: "POST",
|
|
493
|
+
headers: {
|
|
494
|
+
Authorization: `Bearer ${input.apiKey}`,
|
|
495
|
+
"Content-Type": "application/json",
|
|
496
|
+
Accept: "application/json"
|
|
497
|
+
},
|
|
498
|
+
body: JSON.stringify(input.scope ? { scope: input.scope } : {}),
|
|
499
|
+
cache: "no-store"
|
|
500
|
+
});
|
|
501
|
+
const parsed = await readJsonObjectFromResponse(response, {
|
|
502
|
+
invalidJsonMessage: "API key token exchange returned invalid JSON",
|
|
503
|
+
invalidJsonCode: "invalid_token_response",
|
|
504
|
+
failureLabel: "API key token exchange failed",
|
|
505
|
+
defaultErrorCode: "api_key_token_exchange_failed"
|
|
506
|
+
});
|
|
507
|
+
const accessToken = parsed.access_token;
|
|
508
|
+
if (typeof accessToken !== "string" || !accessToken.trim()) {
|
|
509
|
+
throw new exports.PmtHouseError("API key token exchange missing access_token", {
|
|
510
|
+
status: 502,
|
|
511
|
+
code: EXCHANGE_RESPONSE_ERROR2
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
const expiresIn = typeof parsed.expires_in === "number" && Number.isFinite(parsed.expires_in) ? parsed.expires_in : 900;
|
|
515
|
+
const scope = typeof parsed.scope === "string" && parsed.scope.trim() ? parsed.scope.trim() : input.scope?.trim() || "sign:job";
|
|
516
|
+
return {
|
|
517
|
+
access_token: accessToken.trim(),
|
|
518
|
+
expires_in: expiresIn,
|
|
519
|
+
scope
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
async function mintSignerSessionFromApiKey(input) {
|
|
523
|
+
const userToken = await mintUserAccessTokenFromApiKey({
|
|
524
|
+
issuerUrl: input.issuerUrl,
|
|
525
|
+
publicClientId: input.publicClientId,
|
|
526
|
+
apiKey: input.apiKey,
|
|
527
|
+
scope: input.scope,
|
|
528
|
+
fetch: input.fetch
|
|
529
|
+
});
|
|
530
|
+
return mintSignerTokenFromDeviceToken({
|
|
531
|
+
issuerUrl: input.issuerUrl,
|
|
532
|
+
m2mClientId: input.m2mClientId,
|
|
533
|
+
m2mClientSecret: input.m2mClientSecret,
|
|
534
|
+
deviceToken: userToken.access_token,
|
|
535
|
+
scope: userToken.scope,
|
|
536
|
+
audience: input.audience,
|
|
537
|
+
fetch: input.fetch,
|
|
538
|
+
allowInsecureHttp: input.allowInsecureHttp
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
async function exchangeApiKeyForSigner(options) {
|
|
542
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
543
|
+
const url = `${stripTrailingSlashes(options.facadeUrl)}/api/pymthouse/keys/exchange`;
|
|
544
|
+
const body = { apiKey: options.apiKey };
|
|
545
|
+
if (options.scope?.trim()) {
|
|
546
|
+
body.scope = options.scope.trim();
|
|
547
|
+
}
|
|
548
|
+
if (options.clientId?.trim()) {
|
|
549
|
+
body.clientId = options.clientId.trim();
|
|
550
|
+
}
|
|
551
|
+
const response = await fetchImpl(url, {
|
|
552
|
+
method: "POST",
|
|
553
|
+
headers: {
|
|
554
|
+
"Content-Type": "application/json",
|
|
555
|
+
Accept: "application/json"
|
|
556
|
+
},
|
|
557
|
+
body: JSON.stringify(body),
|
|
558
|
+
cache: "no-store"
|
|
559
|
+
});
|
|
560
|
+
const parsed = await readJsonObjectFromResponse(response, {
|
|
561
|
+
invalidJsonMessage: "API key exchange returned invalid JSON",
|
|
562
|
+
invalidJsonCode: EXCHANGE_RESPONSE_ERROR2,
|
|
563
|
+
failureLabel: "API key exchange failed",
|
|
564
|
+
defaultErrorCode: "api_key_exchange_failed"
|
|
565
|
+
});
|
|
566
|
+
const accessToken = extractSignerAccessTokenFromExchangeBody(parsed);
|
|
567
|
+
const signerUrlRaw = parsed.signerUrl ?? parsed.signer_url;
|
|
568
|
+
const signerUrl = typeof signerUrlRaw === "string" && signerUrlRaw.trim() ? signerUrlRaw.trim() : void 0;
|
|
569
|
+
return normalizeDeviceExchangeResponse(
|
|
570
|
+
{
|
|
571
|
+
access_token: accessToken,
|
|
572
|
+
expires_in: typeof parsed.expires_in === "number" && Number.isFinite(parsed.expires_in) ? parsed.expires_in : 3600,
|
|
573
|
+
scope: typeof parsed.scope === "string" && parsed.scope.trim() ? parsed.scope.trim() : "sign:job",
|
|
574
|
+
balanceUsdMicros: typeof parsed.balanceUsdMicros === "string" ? parsed.balanceUsdMicros : "0",
|
|
575
|
+
lifetimeGrantedUsdMicros: typeof parsed.lifetimeGrantedUsdMicros === "string" ? parsed.lifetimeGrantedUsdMicros : "0"
|
|
576
|
+
},
|
|
577
|
+
{ signerUrl }
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
function createApiKeyExchangeHandler(config) {
|
|
581
|
+
const publicClientId = config.publicClientId.trim();
|
|
582
|
+
return async function apiKeyExchangeHandler(request) {
|
|
583
|
+
try {
|
|
584
|
+
if (request.method !== "POST") {
|
|
585
|
+
return new Response(JSON.stringify({ error: "method_not_allowed" }), {
|
|
586
|
+
status: 405,
|
|
587
|
+
headers: { "Content-Type": "application/json" }
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
const parsed = await parseApiKeyExchangeRequestBody(request);
|
|
591
|
+
const effectiveClientId = parsed.clientId?.trim() || publicClientId;
|
|
592
|
+
if (effectiveClientId !== publicClientId) {
|
|
593
|
+
throw new exports.PmtHouseError("clientId does not match configured public client", {
|
|
594
|
+
status: 400,
|
|
595
|
+
code: "invalid_request"
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
const minted = await mintSignerSessionFromApiKey({
|
|
599
|
+
issuerUrl: config.issuerUrl,
|
|
600
|
+
publicClientId,
|
|
601
|
+
m2mClientId: config.m2mClientId,
|
|
602
|
+
m2mClientSecret: config.m2mClientSecret,
|
|
603
|
+
apiKey: parsed.apiKey,
|
|
604
|
+
scope: parsed.scope,
|
|
605
|
+
audience: config.audience,
|
|
606
|
+
fetch: config.fetch,
|
|
607
|
+
allowInsecureHttp: config.allowInsecureHttp
|
|
608
|
+
});
|
|
609
|
+
const signerUrlValue = typeof config.signerUrl === "string" && config.signerUrl.trim() ? config.signerUrl.trim() : void 0;
|
|
610
|
+
const body = normalizeDeviceExchangeResponse(minted, { signerUrl: signerUrlValue });
|
|
611
|
+
return new Response(JSON.stringify(body), {
|
|
612
|
+
status: 200,
|
|
613
|
+
headers: {
|
|
614
|
+
"Content-Type": "application/json",
|
|
615
|
+
"Cache-Control": "no-store"
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
} catch (error) {
|
|
619
|
+
return signerHandlerErrorResponse(error);
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
var EXCHANGE_RESPONSE_ERROR2;
|
|
624
|
+
var init_api_key_exchange = __esm({
|
|
625
|
+
"src/signer/api-key-exchange.ts"() {
|
|
626
|
+
init_string_utils();
|
|
627
|
+
init_errors();
|
|
628
|
+
init_fetch_json();
|
|
629
|
+
init_handler_errors();
|
|
630
|
+
init_device_exchange();
|
|
631
|
+
EXCHANGE_RESPONSE_ERROR2 = "invalid_exchange_response";
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
// src/plan-pricing.ts
|
|
636
|
+
var NETWORK_USD_PER_MICRO = 1e-6;
|
|
637
|
+
var RETAIL_RATE_DECIMALS = 9;
|
|
638
|
+
function trimFixedDecimalZeros(fixed) {
|
|
639
|
+
const dotIndex = fixed.indexOf(".");
|
|
640
|
+
if (dotIndex === -1) {
|
|
641
|
+
return fixed;
|
|
642
|
+
}
|
|
643
|
+
let end = fixed.length;
|
|
644
|
+
while (end > dotIndex + 1 && fixed[end - 1] === "0") {
|
|
645
|
+
end -= 1;
|
|
646
|
+
}
|
|
647
|
+
if (end === dotIndex + 1) {
|
|
648
|
+
end = dotIndex;
|
|
649
|
+
}
|
|
650
|
+
const trimmed = fixed.slice(0, end);
|
|
651
|
+
return trimmed.length > 0 ? trimmed : "0";
|
|
652
|
+
}
|
|
653
|
+
function defaultRetailRateUsd() {
|
|
654
|
+
return formatRetailRateUsd(NETWORK_USD_PER_MICRO);
|
|
655
|
+
}
|
|
656
|
+
function formatRetailRateUsd(value) {
|
|
657
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
658
|
+
return defaultRetailRateUsd();
|
|
659
|
+
}
|
|
660
|
+
return trimFixedDecimalZeros(value.toFixed(RETAIL_RATE_DECIMALS));
|
|
661
|
+
}
|
|
662
|
+
function parseRetailRateUsd(raw) {
|
|
663
|
+
if (raw === null || raw === void 0) {
|
|
664
|
+
return null;
|
|
665
|
+
}
|
|
666
|
+
const trimmed = String(raw).trim();
|
|
667
|
+
if (!trimmed) {
|
|
668
|
+
return null;
|
|
669
|
+
}
|
|
670
|
+
const n = Number(trimmed);
|
|
671
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
return formatRetailRateUsd(n);
|
|
675
|
+
}
|
|
676
|
+
function markupPercentToRetailRateUsd(markupPercent) {
|
|
677
|
+
const pct = Number.isFinite(markupPercent) ? Math.max(0, markupPercent) : 0;
|
|
678
|
+
return formatRetailRateUsd(NETWORK_USD_PER_MICRO * (1 + pct / 100));
|
|
679
|
+
}
|
|
680
|
+
function retailRateUsdToMarkupPercent(raw) {
|
|
681
|
+
const rate = parseRetailRateUsd(raw);
|
|
682
|
+
if (!rate) {
|
|
683
|
+
return "";
|
|
684
|
+
}
|
|
685
|
+
const n = Number(rate);
|
|
686
|
+
if (!Number.isFinite(n) || n <= NETWORK_USD_PER_MICRO) {
|
|
687
|
+
return n === NETWORK_USD_PER_MICRO ? "0" : "";
|
|
688
|
+
}
|
|
689
|
+
const pct = (n / NETWORK_USD_PER_MICRO - 1) * 100;
|
|
690
|
+
if (!Number.isFinite(pct) || pct <= 0) {
|
|
691
|
+
return "";
|
|
692
|
+
}
|
|
693
|
+
return pct % 1 === 0 ? String(Math.round(pct)) : pct.toFixed(1);
|
|
694
|
+
}
|
|
695
|
+
function retailRateUsdPerMillion(raw) {
|
|
696
|
+
const rate = parseRetailRateUsd(raw);
|
|
697
|
+
if (!rate) {
|
|
698
|
+
return "";
|
|
699
|
+
}
|
|
700
|
+
const perM = Number(rate) * 1e6;
|
|
701
|
+
if (!Number.isFinite(perM)) {
|
|
702
|
+
return "";
|
|
703
|
+
}
|
|
704
|
+
return perM.toFixed(2);
|
|
705
|
+
}
|
|
706
|
+
function parseMarkupPercentInput(raw) {
|
|
707
|
+
const trimmed = raw.trim();
|
|
708
|
+
if (!trimmed) {
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
const n = Number(trimmed);
|
|
712
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
return n;
|
|
716
|
+
}
|
|
717
|
+
function applyRetailRateToNetworkMicros(networkFeeUsdMicros, retailRateUsd) {
|
|
718
|
+
const networkPerMicro = NETWORK_USD_PER_MICRO;
|
|
719
|
+
const retail = Number(retailRateUsd);
|
|
720
|
+
if (!Number.isFinite(retail) || retail <= 0) {
|
|
721
|
+
return networkFeeUsdMicros;
|
|
722
|
+
}
|
|
723
|
+
const ratio = retail / networkPerMicro;
|
|
724
|
+
if (!Number.isFinite(ratio) || ratio <= 0) {
|
|
725
|
+
return networkFeeUsdMicros;
|
|
726
|
+
}
|
|
727
|
+
return networkFeeUsdMicros * BigInt(Math.round(ratio * 1e6)) / 1000000n;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// src/ingest.ts
|
|
731
|
+
init_encoding();
|
|
732
|
+
init_errors();
|
|
733
|
+
init_string_utils();
|
|
734
|
+
function signerSnapshotToIngestPayload(input) {
|
|
735
|
+
return {
|
|
736
|
+
requestId: input.snapshot.requestId,
|
|
737
|
+
externalUserId: input.externalUserId,
|
|
738
|
+
networkFeeUsdMicros: input.snapshot.computedFeeUsdMicros.toString(),
|
|
739
|
+
feeWei: input.snapshot.computedFeeWei,
|
|
740
|
+
pixels: input.snapshot.pixels,
|
|
741
|
+
pipeline: input.snapshot.pipeline,
|
|
742
|
+
modelId: input.snapshot.modelId,
|
|
743
|
+
gatewayRequestId: input.gatewayRequestId,
|
|
744
|
+
ethUsdPrice: input.snapshot.ethUsdPrice,
|
|
745
|
+
ethUsdRoundId: input.snapshot.ethUsdRoundId,
|
|
746
|
+
ethUsdObservedAt: input.snapshot.ethUsdObservedAt
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
function ingestUrl(issuerUrl, publicClientId) {
|
|
750
|
+
const origin = new URL(stripTrailingSlashes(issuerUrl)).origin;
|
|
751
|
+
return `${origin}/api/v1/apps/${encodeURIComponent(publicClientId)}/usage/signed-tickets`;
|
|
752
|
+
}
|
|
753
|
+
async function readJsonResponse(response) {
|
|
754
|
+
const text = await response.text();
|
|
755
|
+
if (!text.trim()) {
|
|
756
|
+
return {};
|
|
757
|
+
}
|
|
758
|
+
try {
|
|
759
|
+
const parsed = JSON.parse(text);
|
|
760
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
|
|
761
|
+
} catch {
|
|
762
|
+
return {};
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
async function ingestSignedTicket(options) {
|
|
766
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
767
|
+
const url = ingestUrl(options.issuerUrl, options.publicClientId);
|
|
768
|
+
const response = await fetchImpl(url, {
|
|
769
|
+
method: "POST",
|
|
770
|
+
headers: {
|
|
771
|
+
Authorization: encodeClientSecretBasic(options.m2mClientId, options.m2mClientSecret),
|
|
772
|
+
"Content-Type": "application/json",
|
|
773
|
+
Accept: "application/json"
|
|
774
|
+
},
|
|
775
|
+
body: JSON.stringify(options.ticket),
|
|
776
|
+
cache: "no-store"
|
|
777
|
+
});
|
|
778
|
+
const body = await readJsonResponse(response);
|
|
779
|
+
if (!response.ok) {
|
|
780
|
+
const message = typeof body.error === "string" ? body.error : `Signed-ticket ingest failed (${response.status})`;
|
|
781
|
+
throw new exports.PmtHouseError(message, {
|
|
782
|
+
status: response.status,
|
|
783
|
+
code: "ingest_failed",
|
|
784
|
+
details: body
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
return {
|
|
788
|
+
ingested: Boolean(body.ingested),
|
|
789
|
+
duplicate: Boolean(body.duplicate),
|
|
790
|
+
source: body.source === "openmeter" ? "openmeter" : "disabled"
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
async function ingestSignedTicketsBatch(options) {
|
|
794
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
795
|
+
const url = ingestUrl(options.issuerUrl, options.publicClientId);
|
|
796
|
+
const response = await fetchImpl(url, {
|
|
797
|
+
method: "POST",
|
|
798
|
+
headers: {
|
|
799
|
+
Authorization: encodeClientSecretBasic(options.m2mClientId, options.m2mClientSecret),
|
|
800
|
+
"Content-Type": "application/json",
|
|
801
|
+
Accept: "application/json"
|
|
802
|
+
},
|
|
803
|
+
body: JSON.stringify({ tickets: options.tickets }),
|
|
804
|
+
cache: "no-store"
|
|
805
|
+
});
|
|
806
|
+
const body = await readJsonResponse(response);
|
|
807
|
+
if (!response.ok) {
|
|
808
|
+
const message = typeof body.error === "string" ? body.error : `Signed-ticket batch ingest failed (${response.status})`;
|
|
809
|
+
throw new exports.PmtHouseError(message, {
|
|
810
|
+
status: response.status,
|
|
811
|
+
code: "ingest_failed",
|
|
812
|
+
details: body
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
const rawResults = Array.isArray(body.results) ? body.results : [];
|
|
816
|
+
return {
|
|
817
|
+
results: rawResults.map((entry) => {
|
|
818
|
+
const row = entry ?? {};
|
|
819
|
+
return {
|
|
820
|
+
requestId: typeof row.requestId === "string" ? row.requestId : void 0,
|
|
821
|
+
ok: row.ok === true,
|
|
822
|
+
ingested: Boolean(row.ingested),
|
|
823
|
+
duplicate: Boolean(row.duplicate),
|
|
824
|
+
source: row.source === "openmeter" ? "openmeter" : "disabled"
|
|
825
|
+
};
|
|
826
|
+
})
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// src/usage.ts
|
|
831
|
+
function parseSafeBigInt(value, fallback = 0n) {
|
|
832
|
+
try {
|
|
833
|
+
return BigInt(value);
|
|
834
|
+
} catch {
|
|
835
|
+
return fallback;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
function getUtcCalendarMonthIsoBounds(now = /* @__PURE__ */ new Date()) {
|
|
839
|
+
const y = now.getUTCFullYear();
|
|
840
|
+
const m = now.getUTCMonth();
|
|
841
|
+
const start = new Date(Date.UTC(y, m, 1, 0, 0, 0, 0));
|
|
842
|
+
const end = new Date(Date.UTC(y, m + 1, 0, 23, 59, 59, 999));
|
|
843
|
+
return { startDate: start.toISOString(), endDate: end.toISOString() };
|
|
844
|
+
}
|
|
845
|
+
function parseUsageDateParam(raw) {
|
|
846
|
+
if (raw == null) return null;
|
|
847
|
+
const trimmed = raw.trim();
|
|
848
|
+
if (!trimmed) return null;
|
|
849
|
+
const t = Date.parse(trimmed);
|
|
850
|
+
if (Number.isNaN(t)) return null;
|
|
851
|
+
return trimmed;
|
|
852
|
+
}
|
|
853
|
+
function aggregateUsageByExternalUserId(byUser, externalUserId) {
|
|
854
|
+
const rows = byUser?.filter((row) => row.externalUserId === externalUserId) ?? [];
|
|
855
|
+
if (rows.length === 0) {
|
|
856
|
+
return {
|
|
857
|
+
externalUserId,
|
|
858
|
+
requestCount: 0,
|
|
859
|
+
feeWei: "0"
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
let feeWei = 0n;
|
|
863
|
+
let requestCount = 0;
|
|
864
|
+
for (const row of rows) {
|
|
865
|
+
if (row.feeWei) {
|
|
866
|
+
feeWei += BigInt(row.feeWei);
|
|
867
|
+
}
|
|
868
|
+
requestCount += row.requestCount;
|
|
869
|
+
}
|
|
870
|
+
return {
|
|
871
|
+
externalUserId,
|
|
872
|
+
requestCount,
|
|
873
|
+
feeWei: feeWei.toString()
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function summarizeUsageForExternalUser(usage, externalUserId) {
|
|
877
|
+
return aggregateUsageByExternalUserId(usage.byUser, externalUserId);
|
|
878
|
+
}
|
|
879
|
+
function listUsageByPipelineModel(usage) {
|
|
880
|
+
const rows = usage.byPipelineModel ?? [];
|
|
881
|
+
return [...rows].sort((a, b) => {
|
|
882
|
+
const p = a.pipeline.localeCompare(b.pipeline);
|
|
883
|
+
if (p !== 0) return p;
|
|
884
|
+
return a.modelId.localeCompare(b.modelId);
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
function getEndUserIdsForExternalUser(usage, externalUserId) {
|
|
888
|
+
const userIds = /* @__PURE__ */ new Set();
|
|
889
|
+
for (const row of usage.byUser ?? []) {
|
|
890
|
+
if (row.externalUserId === externalUserId && row.endUserId !== "unknown") {
|
|
891
|
+
userIds.add(row.endUserId);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
return [...userIds];
|
|
895
|
+
}
|
|
896
|
+
var getUsageRecordUserIdsForExternalUser = getEndUserIdsForExternalUser;
|
|
897
|
+
function summarizeUsageFiatForExternalUser(usageByUser, externalUserId) {
|
|
898
|
+
const rows = usageByUser.byUser ?? [];
|
|
899
|
+
let requestCount = 0;
|
|
900
|
+
let networkFeeUsdMicros = 0n;
|
|
901
|
+
let ownerChargeUsdMicros = 0n;
|
|
902
|
+
let endUserBillableUsdMicros = 0n;
|
|
903
|
+
let currency = "USD";
|
|
904
|
+
for (const row of rows) {
|
|
905
|
+
if (row.externalUserId !== externalUserId) continue;
|
|
906
|
+
requestCount += row.requestCount;
|
|
907
|
+
if (row.currency) currency = row.currency;
|
|
908
|
+
if (row.networkFeeUsdMicros) {
|
|
909
|
+
networkFeeUsdMicros += BigInt(row.networkFeeUsdMicros);
|
|
910
|
+
}
|
|
911
|
+
if (row.ownerChargeUsdMicros) {
|
|
912
|
+
ownerChargeUsdMicros += BigInt(row.ownerChargeUsdMicros);
|
|
913
|
+
}
|
|
914
|
+
if (row.endUserBillableUsdMicros) {
|
|
915
|
+
endUserBillableUsdMicros += BigInt(row.endUserBillableUsdMicros);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
return {
|
|
919
|
+
externalUserId,
|
|
920
|
+
requestCount,
|
|
921
|
+
currency,
|
|
922
|
+
networkFeeUsdMicros: networkFeeUsdMicros.toString(),
|
|
923
|
+
ownerChargeUsdMicros: ownerChargeUsdMicros.toString(),
|
|
924
|
+
endUserBillableUsdMicros: endUserBillableUsdMicros.toString()
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
function mergeUsageByPipelineModel(usagePipelineModels) {
|
|
928
|
+
let responses;
|
|
929
|
+
if (Array.isArray(usagePipelineModels)) {
|
|
930
|
+
responses = usagePipelineModels;
|
|
931
|
+
} else if (usagePipelineModels) {
|
|
932
|
+
responses = [usagePipelineModels];
|
|
933
|
+
} else {
|
|
934
|
+
responses = [];
|
|
935
|
+
}
|
|
936
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
937
|
+
for (const response of responses) {
|
|
938
|
+
for (const row of response.byPipelineModel ?? []) {
|
|
939
|
+
const { pipeline, modelId } = row;
|
|
940
|
+
if (!pipeline || !modelId) continue;
|
|
941
|
+
const key = JSON.stringify([pipeline, modelId]);
|
|
942
|
+
const existing = byKey.get(key);
|
|
943
|
+
const rowCurrency = row.currency ?? "USD";
|
|
944
|
+
const networkFee = row.networkFeeUsdMicros ?? "0";
|
|
945
|
+
const ownerCharge = row.ownerChargeUsdMicros ?? "0";
|
|
946
|
+
const endUserBillable = row.endUserBillableUsdMicros ?? "0";
|
|
947
|
+
if (!existing) {
|
|
948
|
+
byKey.set(key, {
|
|
949
|
+
pipeline,
|
|
950
|
+
modelId,
|
|
951
|
+
requestCount: row.requestCount,
|
|
952
|
+
currency: rowCurrency,
|
|
953
|
+
networkFeeUsdMicros: networkFee,
|
|
954
|
+
ownerChargeUsdMicros: ownerCharge,
|
|
955
|
+
endUserBillableUsdMicros: endUserBillable
|
|
956
|
+
});
|
|
957
|
+
continue;
|
|
958
|
+
}
|
|
959
|
+
byKey.set(key, {
|
|
960
|
+
...existing,
|
|
961
|
+
requestCount: existing.requestCount + row.requestCount,
|
|
962
|
+
networkFeeUsdMicros: (parseSafeBigInt(existing.networkFeeUsdMicros) + parseSafeBigInt(networkFee)).toString(),
|
|
963
|
+
ownerChargeUsdMicros: (parseSafeBigInt(existing.ownerChargeUsdMicros) + parseSafeBigInt(ownerCharge)).toString(),
|
|
964
|
+
endUserBillableUsdMicros: (parseSafeBigInt(existing.endUserBillableUsdMicros) + parseSafeBigInt(endUserBillable)).toString()
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return [...byKey.values()].sort((a, b) => {
|
|
969
|
+
if (a.pipeline === b.pipeline) return a.modelId.localeCompare(b.modelId);
|
|
970
|
+
return a.pipeline.localeCompare(b.pipeline);
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
function buildMeScopeUsagePayload(usageByUser, externalUserId, usagePipelineModel, usageDaily) {
|
|
974
|
+
const summary = summarizeUsageFiatForExternalUser(usageByUser, externalUserId);
|
|
975
|
+
const pipelineModels = mergeUsageByPipelineModel(usagePipelineModel);
|
|
976
|
+
const dailyByPipeline = usageDaily?.byDailyPipeline ?? [];
|
|
977
|
+
return {
|
|
978
|
+
clientId: usageByUser.clientId,
|
|
979
|
+
period: usageByUser.period,
|
|
980
|
+
currentUser: {
|
|
981
|
+
externalUserId: summary.externalUserId,
|
|
982
|
+
requestCount: summary.requestCount,
|
|
983
|
+
currency: summary.currency,
|
|
984
|
+
networkFeeUsdMicros: summary.networkFeeUsdMicros,
|
|
985
|
+
ownerChargeUsdMicros: summary.ownerChargeUsdMicros,
|
|
986
|
+
endUserBillableUsdMicros: summary.endUserBillableUsdMicros,
|
|
987
|
+
pipelineModels,
|
|
988
|
+
dailyByPipeline
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
var DEFAULT_MAX_END_USER_IDS = 25;
|
|
993
|
+
|
|
994
|
+
// src/client.ts
|
|
995
|
+
init_encoding();
|
|
996
|
+
init_discovery();
|
|
997
|
+
init_errors();
|
|
998
|
+
function parseAppManifestResponse(json) {
|
|
999
|
+
if (!json || typeof json !== "object" || Array.isArray(json)) {
|
|
1000
|
+
return { capabilities: [], excludedCapabilities: [] };
|
|
1001
|
+
}
|
|
1002
|
+
const record = json;
|
|
1003
|
+
const capabilities = parseCapabilityArray(record.capabilities);
|
|
1004
|
+
const excludedCapabilities = parseCapabilityArray(record.excludedCapabilities);
|
|
1005
|
+
const manifestVersion = typeof record.manifestVersion === "string" && record.manifestVersion.trim() ? record.manifestVersion.trim() : void 0;
|
|
1006
|
+
return { capabilities, excludedCapabilities, manifestVersion };
|
|
1007
|
+
}
|
|
1008
|
+
function parseCapabilityArray(raw) {
|
|
1009
|
+
if (!Array.isArray(raw)) return [];
|
|
1010
|
+
return raw.filter(
|
|
1011
|
+
(c) => !!c && typeof c === "object" && typeof c.pipeline === "string" && typeof c.modelId === "string"
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
function sortedCaps(caps) {
|
|
1015
|
+
return [...caps].sort((a, b) => {
|
|
1016
|
+
const p = a.pipeline.localeCompare(b.pipeline);
|
|
1017
|
+
return p === 0 ? a.modelId.localeCompare(b.modelId) : p;
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
function computeManifestRevision(data) {
|
|
1021
|
+
if (data == null) {
|
|
1022
|
+
return "unavailable";
|
|
1023
|
+
}
|
|
1024
|
+
if (data.manifestVersion?.trim()) {
|
|
1025
|
+
return data.manifestVersion.trim();
|
|
1026
|
+
}
|
|
1027
|
+
const caps = sortedCaps(data.capabilities ?? []);
|
|
1028
|
+
const excl = sortedCaps(data.excludedCapabilities ?? []);
|
|
1029
|
+
if (caps.length === 0 && excl.length === 0) {
|
|
1030
|
+
return "empty";
|
|
1031
|
+
}
|
|
1032
|
+
return crypto.createHash("sha256").update(JSON.stringify({ capabilities: caps, excludedCapabilities: excl })).digest("hex").slice(0, 24);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
// src/client.ts
|
|
1036
|
+
init_string_utils();
|
|
1037
|
+
|
|
1038
|
+
// src/tokens.ts
|
|
1039
|
+
var SIGNER_SESSION_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
1040
|
+
var SIGNER_SESSION_EXPIRES_IN_SEC = Math.floor(SIGNER_SESSION_TTL_MS / 1e3);
|
|
1041
|
+
var SIGN_JOB_SCOPE = "sign:job";
|
|
1042
|
+
var PYMTHOUSE_SIGNER_SESSION_TTL_MS = SIGNER_SESSION_TTL_MS;
|
|
1043
|
+
function computeSignerSessionExpiry(input) {
|
|
1044
|
+
const createdAt = input instanceof Date ? input : new Date(input);
|
|
1045
|
+
return new Date(createdAt.getTime() + SIGNER_SESSION_TTL_MS);
|
|
1046
|
+
}
|
|
1047
|
+
var computePymthouseExpiry = computeSignerSessionExpiry;
|
|
1048
|
+
function isLikelyOidcJwt(rawToken) {
|
|
1049
|
+
const t = rawToken.trim();
|
|
1050
|
+
return t.startsWith("eyJ") && t.split(".").length >= 3;
|
|
1051
|
+
}
|
|
1052
|
+
function isOpaqueSignerSessionToken(rawToken) {
|
|
1053
|
+
const t = rawToken.trim();
|
|
1054
|
+
return t.length > 0 && !isLikelyOidcJwt(t);
|
|
1055
|
+
}
|
|
1056
|
+
function base64UrlPayloadToUtf8(payloadB64) {
|
|
1057
|
+
const normalized = payloadB64.replaceAll("-", "+").replaceAll("_", "/");
|
|
1058
|
+
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
|
|
1059
|
+
if (typeof Buffer !== "undefined") {
|
|
1060
|
+
return Buffer.from(padded, "base64").toString("utf8");
|
|
1061
|
+
}
|
|
1062
|
+
return atob(padded);
|
|
1063
|
+
}
|
|
1064
|
+
function decodeJwtExp(rawToken) {
|
|
1065
|
+
try {
|
|
1066
|
+
const parts = rawToken.split(".");
|
|
1067
|
+
if (parts.length < 2) return null;
|
|
1068
|
+
const payloadJson = base64UrlPayloadToUtf8(parts[1]);
|
|
1069
|
+
const payload = JSON.parse(payloadJson);
|
|
1070
|
+
if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) return null;
|
|
1071
|
+
const expMs = Math.floor(payload.exp * 1e3);
|
|
1072
|
+
if (expMs <= 0) return null;
|
|
1073
|
+
return new Date(expMs);
|
|
1074
|
+
} catch {
|
|
1075
|
+
return null;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
function parseSignerSessionExchange(res) {
|
|
1079
|
+
const accessToken = typeof res.access_token === "string" ? res.access_token.trim() : "";
|
|
1080
|
+
if (!accessToken) {
|
|
1081
|
+
throw new Error("PymtHouse signer session exchange returned no access_token");
|
|
1082
|
+
}
|
|
1083
|
+
if (isLikelyOidcJwt(accessToken)) {
|
|
1084
|
+
throw new Error(
|
|
1085
|
+
"PymtHouse signer session exchange returned a JWT; expected opaque signer session token"
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
const tokenType = typeof res.token_type === "string" && res.token_type.trim() ? res.token_type.trim() : "Bearer";
|
|
1089
|
+
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;
|
|
1090
|
+
const scope = typeof res.scope === "string" && res.scope.trim() ? res.scope.trim() : SIGN_JOB_SCOPE;
|
|
1091
|
+
return {
|
|
1092
|
+
accessToken,
|
|
1093
|
+
tokenType,
|
|
1094
|
+
expiresIn,
|
|
1095
|
+
scope
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// src/oauth-map.ts
|
|
1100
|
+
init_errors();
|
|
184
1101
|
var ACCEPTED_ISSUED_TOKEN_TYPES = /* @__PURE__ */ new Set([
|
|
185
1102
|
"urn:ietf:params:oauth:token-type:access_token",
|
|
186
1103
|
"urn:pmth:token-type:remote-signer-session"
|
|
187
1104
|
]);
|
|
188
1105
|
function mapOAuthError(error) {
|
|
189
|
-
if (error instanceof PmtHouseError) {
|
|
1106
|
+
if (error instanceof exports.PmtHouseError) {
|
|
190
1107
|
return error;
|
|
191
1108
|
}
|
|
192
1109
|
if (error instanceof oauth4webapi.ResponseBodyError) {
|
|
@@ -196,26 +1113,26 @@ function mapOAuthError(error) {
|
|
|
196
1113
|
if (typeof cause.error_uri === "string") {
|
|
197
1114
|
details.error_uri = cause.error_uri;
|
|
198
1115
|
}
|
|
199
|
-
return new PmtHouseError(description, {
|
|
1116
|
+
return new exports.PmtHouseError(description, {
|
|
200
1117
|
status: error.status,
|
|
201
1118
|
code: error.error,
|
|
202
1119
|
details
|
|
203
1120
|
});
|
|
204
1121
|
}
|
|
205
1122
|
if (error instanceof oauth4webapi.OperationProcessingError) {
|
|
206
|
-
return new PmtHouseError(error.message, {
|
|
1123
|
+
return new exports.PmtHouseError(error.message, {
|
|
207
1124
|
status: 502,
|
|
208
1125
|
code: error.code ?? "oauth_processing_error",
|
|
209
1126
|
details: { cause: error.cause }
|
|
210
1127
|
});
|
|
211
1128
|
}
|
|
212
1129
|
if (error instanceof Error) {
|
|
213
|
-
return new PmtHouseError(error.message, {
|
|
1130
|
+
return new exports.PmtHouseError(error.message, {
|
|
214
1131
|
status: 500,
|
|
215
1132
|
code: "unexpected_error"
|
|
216
1133
|
});
|
|
217
1134
|
}
|
|
218
|
-
return new PmtHouseError("Unexpected error", {
|
|
1135
|
+
return new exports.PmtHouseError("Unexpected error", {
|
|
219
1136
|
status: 500,
|
|
220
1137
|
code: "unexpected_error"
|
|
221
1138
|
});
|
|
@@ -223,7 +1140,7 @@ function mapOAuthError(error) {
|
|
|
223
1140
|
function tokenEndpointResponseToExchange(tr) {
|
|
224
1141
|
const issued = tr.issued_token_type;
|
|
225
1142
|
if (typeof issued !== "string" || !ACCEPTED_ISSUED_TOKEN_TYPES.has(issued)) {
|
|
226
|
-
throw new PmtHouseError("Token exchange returned an unexpected issued_token_type", {
|
|
1143
|
+
throw new exports.PmtHouseError("Token exchange returned an unexpected issued_token_type", {
|
|
227
1144
|
status: 502,
|
|
228
1145
|
code: "invalid_token_response",
|
|
229
1146
|
details: { issued_token_type: issued }
|
|
@@ -231,7 +1148,7 @@ function tokenEndpointResponseToExchange(tr) {
|
|
|
231
1148
|
}
|
|
232
1149
|
const tt = tr.token_type;
|
|
233
1150
|
if (typeof tt !== "string" || tt.toLowerCase() !== "bearer") {
|
|
234
|
-
throw new PmtHouseError("Token endpoint returned a non-Bearer token_type", {
|
|
1151
|
+
throw new exports.PmtHouseError("Token endpoint returned a non-Bearer token_type", {
|
|
235
1152
|
status: 502,
|
|
236
1153
|
code: "invalid_token_response",
|
|
237
1154
|
details: { token_type: tt }
|
|
@@ -239,7 +1156,7 @@ function tokenEndpointResponseToExchange(tr) {
|
|
|
239
1156
|
}
|
|
240
1157
|
const expiresIn = tr.expires_in;
|
|
241
1158
|
if (typeof expiresIn !== "number") {
|
|
242
|
-
throw new PmtHouseError("Token response missing expires_in", {
|
|
1159
|
+
throw new exports.PmtHouseError("Token response missing expires_in", {
|
|
243
1160
|
status: 502,
|
|
244
1161
|
code: "invalid_token_response"
|
|
245
1162
|
});
|
|
@@ -256,7 +1173,7 @@ function tokenEndpointResponseToExchange(tr) {
|
|
|
256
1173
|
function tokenEndpointResponseToClientCredentials(tr) {
|
|
257
1174
|
const tt = tr.token_type;
|
|
258
1175
|
if (typeof tt !== "string" || tt.toLowerCase() !== "bearer") {
|
|
259
|
-
throw new PmtHouseError("Token endpoint returned a non-Bearer token_type", {
|
|
1176
|
+
throw new exports.PmtHouseError("Token endpoint returned a non-Bearer token_type", {
|
|
260
1177
|
status: 502,
|
|
261
1178
|
code: "invalid_token_response",
|
|
262
1179
|
details: { token_type: tt }
|
|
@@ -274,8 +1191,8 @@ function m2mClient(clientId) {
|
|
|
274
1191
|
}
|
|
275
1192
|
|
|
276
1193
|
// src/client.ts
|
|
277
|
-
var
|
|
278
|
-
var
|
|
1194
|
+
var TOKEN_EXCHANGE_GRANT2 = "urn:ietf:params:oauth:grant-type:token-exchange";
|
|
1195
|
+
var SUBJECT_ACCESS_TOKEN_TYPE2 = "urn:ietf:params:oauth:token-type:access_token";
|
|
279
1196
|
var REQUESTED_ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
|
|
280
1197
|
var DEVICE_RESOURCE_PREFIX = "urn:pmth:device_code:";
|
|
281
1198
|
function normalizeUserCode(value) {
|
|
@@ -316,13 +1233,13 @@ var PmtHouseClient = class {
|
|
|
316
1233
|
const issuer = searchParams.get("iss")?.trim() ?? "";
|
|
317
1234
|
const targetLinkUri = searchParams.get("target_link_uri")?.trim() ?? "";
|
|
318
1235
|
if (!issuer || !targetLinkUri) {
|
|
319
|
-
throw new PmtHouseError("Missing iss or target_link_uri", {
|
|
1236
|
+
throw new exports.PmtHouseError("Missing iss or target_link_uri", {
|
|
320
1237
|
status: 400,
|
|
321
1238
|
code: "invalid_request"
|
|
322
1239
|
});
|
|
323
1240
|
}
|
|
324
1241
|
if (!this.verifyIssuer(issuer)) {
|
|
325
|
-
throw new PmtHouseError("Issuer mismatch for initiate login", {
|
|
1242
|
+
throw new exports.PmtHouseError("Issuer mismatch for initiate login", {
|
|
326
1243
|
status: 400,
|
|
327
1244
|
code: "invalid_issuer"
|
|
328
1245
|
});
|
|
@@ -331,14 +1248,14 @@ var PmtHouseClient = class {
|
|
|
331
1248
|
try {
|
|
332
1249
|
targetUrl = new URL(targetLinkUri);
|
|
333
1250
|
} catch {
|
|
334
|
-
throw new PmtHouseError("target_link_uri is not a valid URL", {
|
|
1251
|
+
throw new exports.PmtHouseError("target_link_uri is not a valid URL", {
|
|
335
1252
|
status: 400,
|
|
336
1253
|
code: "invalid_target"
|
|
337
1254
|
});
|
|
338
1255
|
}
|
|
339
1256
|
const issuerOrigin = new URL(this.issuerUrl).origin;
|
|
340
1257
|
if (targetUrl.origin !== issuerOrigin || targetUrl.pathname !== "/oidc/device") {
|
|
341
|
-
throw new PmtHouseError(
|
|
1258
|
+
throw new exports.PmtHouseError(
|
|
342
1259
|
"target_link_uri does not point to the issuer device path",
|
|
343
1260
|
{
|
|
344
1261
|
status: 400,
|
|
@@ -349,7 +1266,7 @@ var PmtHouseClient = class {
|
|
|
349
1266
|
const userCode = normalizeUserCode(targetUrl.searchParams.get("user_code") ?? "");
|
|
350
1267
|
const clientId = targetUrl.searchParams.get("client_id")?.trim() ?? "";
|
|
351
1268
|
if (!userCode || !clientId) {
|
|
352
|
-
throw new PmtHouseError("target_link_uri is missing user_code or client_id", {
|
|
1269
|
+
throw new exports.PmtHouseError("target_link_uri is missing user_code or client_id", {
|
|
353
1270
|
status: 400,
|
|
354
1271
|
code: "invalid_target"
|
|
355
1272
|
});
|
|
@@ -402,6 +1319,50 @@ var PmtHouseClient = class {
|
|
|
402
1319
|
cache: "no-store"
|
|
403
1320
|
});
|
|
404
1321
|
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Exchange a long-lived dashboard API key (`pmth_*`) for a short-lived user JWT.
|
|
1324
|
+
*/
|
|
1325
|
+
async exchangeApiKeyForUserAccessToken(input) {
|
|
1326
|
+
const url = `${this.getAppsBaseUrl()}/auth/api-key/token`;
|
|
1327
|
+
return this.requestJson(url, {
|
|
1328
|
+
method: "POST",
|
|
1329
|
+
headers: {
|
|
1330
|
+
Authorization: `Bearer ${input.apiKey.trim()}`,
|
|
1331
|
+
"Content-Type": "application/json",
|
|
1332
|
+
Accept: "application/json"
|
|
1333
|
+
},
|
|
1334
|
+
body: JSON.stringify(input.scope ? { scope: input.scope } : {}),
|
|
1335
|
+
cache: "no-store"
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
/**
|
|
1339
|
+
* Exchange a dashboard API key for a signer session via a trusted facade (recommended)
|
|
1340
|
+
* or directly when M2M credentials are available on this client.
|
|
1341
|
+
*/
|
|
1342
|
+
async exchangeApiKeyForSignerSession(input) {
|
|
1343
|
+
if (input.facadeUrl?.trim()) {
|
|
1344
|
+
const { exchangeApiKeyForSigner: exchangeApiKeyForSigner2 } = await Promise.resolve().then(() => (init_api_key_exchange(), api_key_exchange_exports));
|
|
1345
|
+
const exchanged = await exchangeApiKeyForSigner2({
|
|
1346
|
+
facadeUrl: input.facadeUrl.trim(),
|
|
1347
|
+
apiKey: input.apiKey,
|
|
1348
|
+
scope: input.scope,
|
|
1349
|
+
clientId: this.publicClientId,
|
|
1350
|
+
fetch: this.fetchImpl
|
|
1351
|
+
});
|
|
1352
|
+
return {
|
|
1353
|
+
access_token: exchanged.access_token,
|
|
1354
|
+
token_type: exchanged.token_type,
|
|
1355
|
+
expires_in: exchanged.expires_in,
|
|
1356
|
+
scope: exchanged.scope,
|
|
1357
|
+
issued_token_type: "urn:ietf:params:oauth:token-type:access_token"
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
const userToken = await this.exchangeApiKeyForUserAccessToken({
|
|
1361
|
+
apiKey: input.apiKey,
|
|
1362
|
+
scope: input.scope
|
|
1363
|
+
});
|
|
1364
|
+
return this.exchangeForSignerSession({ userJwt: userToken.access_token });
|
|
1365
|
+
}
|
|
405
1366
|
async completeDeviceApproval(input) {
|
|
406
1367
|
const as = await loadAuthorizationServer(this.issuerUrl, this.fetchImpl, {
|
|
407
1368
|
allowInsecureHttp: this.allowInsecureHttp
|
|
@@ -410,14 +1371,14 @@ var PmtHouseClient = class {
|
|
|
410
1371
|
const clientAuth = this.m2mClientAuth();
|
|
411
1372
|
const params = new URLSearchParams();
|
|
412
1373
|
params.set("subject_token", input.userJwt);
|
|
413
|
-
params.set("subject_token_type",
|
|
1374
|
+
params.set("subject_token_type", SUBJECT_ACCESS_TOKEN_TYPE2);
|
|
414
1375
|
params.set("resource", buildDeviceCodeResource(input.userCode));
|
|
415
1376
|
try {
|
|
416
1377
|
const response = await oauth4webapi.genericTokenEndpointRequest(
|
|
417
1378
|
as,
|
|
418
1379
|
client,
|
|
419
1380
|
clientAuth,
|
|
420
|
-
|
|
1381
|
+
TOKEN_EXCHANGE_GRANT2,
|
|
421
1382
|
params,
|
|
422
1383
|
this.tokenEndpointFetchOptions()
|
|
423
1384
|
);
|
|
@@ -465,7 +1426,7 @@ var PmtHouseClient = class {
|
|
|
465
1426
|
const clientAuth = this.m2mClientAuth();
|
|
466
1427
|
const params = new URLSearchParams();
|
|
467
1428
|
params.set("subject_token", input.userJwt);
|
|
468
|
-
params.set("subject_token_type",
|
|
1429
|
+
params.set("subject_token_type", SUBJECT_ACCESS_TOKEN_TYPE2);
|
|
469
1430
|
params.set("requested_token_type", REQUESTED_ACCESS_TOKEN_TYPE);
|
|
470
1431
|
const resourceCandidate = typeof input.resource === "string" && input.resource.trim() !== "" ? input.resource.trim() : this.issuerUrl;
|
|
471
1432
|
params.set("resource", stripTrailingSlashes(resourceCandidate));
|
|
@@ -474,7 +1435,7 @@ var PmtHouseClient = class {
|
|
|
474
1435
|
as,
|
|
475
1436
|
client,
|
|
476
1437
|
clientAuth,
|
|
477
|
-
|
|
1438
|
+
TOKEN_EXCHANGE_GRANT2,
|
|
478
1439
|
params,
|
|
479
1440
|
this.tokenEndpointFetchOptions()
|
|
480
1441
|
);
|
|
@@ -516,7 +1477,7 @@ var PmtHouseClient = class {
|
|
|
516
1477
|
}
|
|
517
1478
|
const machineToken = await this.issueMachineAccessToken("sign:job");
|
|
518
1479
|
if (!machineToken.access_token) {
|
|
519
|
-
throw new PmtHouseError("Client credentials flow did not return access_token", {
|
|
1480
|
+
throw new exports.PmtHouseError("Client credentials flow did not return access_token", {
|
|
520
1481
|
status: 502,
|
|
521
1482
|
code: "invalid_token_response"
|
|
522
1483
|
});
|
|
@@ -530,12 +1491,267 @@ var PmtHouseClient = class {
|
|
|
530
1491
|
if (input.groupBy) url.searchParams.set("groupBy", input.groupBy);
|
|
531
1492
|
if (input.userId) url.searchParams.set("userId", input.userId);
|
|
532
1493
|
if (input.gatewayRequestId) url.searchParams.set("gatewayRequestId", input.gatewayRequestId);
|
|
1494
|
+
if (input.includeRetail) url.searchParams.set("include", "retail");
|
|
533
1495
|
return this.requestJson(url.toString(), {
|
|
534
1496
|
method: "GET",
|
|
535
1497
|
headers: this.builderHeaders(),
|
|
536
1498
|
cache: "no-store"
|
|
537
1499
|
});
|
|
538
1500
|
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Session-scoped usage for one `externalUserId`: user rollup plus merged pipeline/model breakdown.
|
|
1503
|
+
*/
|
|
1504
|
+
async ingestSignedTicket(ticket) {
|
|
1505
|
+
return ingestSignedTicket({
|
|
1506
|
+
issuerUrl: this.issuerUrl,
|
|
1507
|
+
publicClientId: this.publicClientId,
|
|
1508
|
+
m2mClientId: this.m2mClientId,
|
|
1509
|
+
m2mClientSecret: this.m2mClientSecret,
|
|
1510
|
+
ticket,
|
|
1511
|
+
fetch: this.fetchImpl
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1514
|
+
async ingestSignedTickets(tickets) {
|
|
1515
|
+
return ingestSignedTicketsBatch({
|
|
1516
|
+
issuerUrl: this.issuerUrl,
|
|
1517
|
+
publicClientId: this.publicClientId,
|
|
1518
|
+
m2mClientId: this.m2mClientId,
|
|
1519
|
+
m2mClientSecret: this.m2mClientSecret,
|
|
1520
|
+
tickets,
|
|
1521
|
+
fetch: this.fetchImpl
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
async getSignerRouting() {
|
|
1525
|
+
return this.requestJson(
|
|
1526
|
+
`${this.getAppsBaseUrl()}/signer/routing`,
|
|
1527
|
+
{
|
|
1528
|
+
method: "GET",
|
|
1529
|
+
headers: this.builderHeaders(),
|
|
1530
|
+
cache: "no-store"
|
|
1531
|
+
}
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
async listBillingProducts() {
|
|
1535
|
+
const url = `${this.getAppsBaseUrl()}/plans?apiVersion=2`;
|
|
1536
|
+
const body = await this.requestJson(
|
|
1537
|
+
url,
|
|
1538
|
+
{
|
|
1539
|
+
method: "GET",
|
|
1540
|
+
headers: this.builderHeaders(),
|
|
1541
|
+
cache: "no-store"
|
|
1542
|
+
}
|
|
1543
|
+
);
|
|
1544
|
+
return {
|
|
1545
|
+
apiVersion: body.apiVersion ?? 2,
|
|
1546
|
+
products: body.products ?? body.plans ?? []
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
async syncBillingProduct(planId) {
|
|
1550
|
+
return this.requestJson(
|
|
1551
|
+
`${this.getAppsBaseUrl()}/plans/${encodeURIComponent(planId)}/sync`,
|
|
1552
|
+
{
|
|
1553
|
+
method: "POST",
|
|
1554
|
+
headers: this.builderHeaders(),
|
|
1555
|
+
cache: "no-store"
|
|
1556
|
+
}
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
async getUsageBalance(externalUserId) {
|
|
1560
|
+
const url = new URL(`${this.getAppsBaseUrl()}/usage/balance`);
|
|
1561
|
+
url.searchParams.set("externalUserId", externalUserId);
|
|
1562
|
+
return this.requestJson(url.toString(), {
|
|
1563
|
+
method: "GET",
|
|
1564
|
+
headers: this.builderHeaders(),
|
|
1565
|
+
cache: "no-store"
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
async getUserAllowances(externalUserId) {
|
|
1569
|
+
return this.requestJson(
|
|
1570
|
+
`${this.getAppsBaseUrl()}/users/${encodeURIComponent(externalUserId)}/allowances`,
|
|
1571
|
+
{
|
|
1572
|
+
method: "GET",
|
|
1573
|
+
headers: this.builderHeaders(),
|
|
1574
|
+
cache: "no-store"
|
|
1575
|
+
}
|
|
1576
|
+
);
|
|
1577
|
+
}
|
|
1578
|
+
async grantUserAllowance(externalUserId, input) {
|
|
1579
|
+
return this.requestJson(
|
|
1580
|
+
`${this.getAppsBaseUrl()}/users/${encodeURIComponent(externalUserId)}/allowances`,
|
|
1581
|
+
{
|
|
1582
|
+
method: "POST",
|
|
1583
|
+
headers: this.builderHeaders(),
|
|
1584
|
+
body: JSON.stringify(input),
|
|
1585
|
+
cache: "no-store"
|
|
1586
|
+
}
|
|
1587
|
+
);
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* @deprecated Removed from PymtHouse — use {@link getUsageBalance} or {@link getUserAllowances}.
|
|
1591
|
+
*/
|
|
1592
|
+
async getUserCredits(externalUserId) {
|
|
1593
|
+
return this.getUsageBalance(externalUserId);
|
|
1594
|
+
}
|
|
1595
|
+
/**
|
|
1596
|
+
* @deprecated Removed from PymtHouse — use {@link grantUserAllowance} (`POST .../allowances`).
|
|
1597
|
+
*/
|
|
1598
|
+
async grantUserCredits(externalUserId, input) {
|
|
1599
|
+
const result = await this.grantUserAllowance(externalUserId, {
|
|
1600
|
+
amountUsdMicros: input.amountUsdMicros,
|
|
1601
|
+
source: input.source ?? "manual",
|
|
1602
|
+
featureKey: input.featureKey
|
|
1603
|
+
});
|
|
1604
|
+
const flat = result;
|
|
1605
|
+
const nested = result.allowances;
|
|
1606
|
+
return {
|
|
1607
|
+
externalUserId: result.externalUserId,
|
|
1608
|
+
balanceUsdMicros: flat.balanceUsdMicros ?? nested?.balanceUsdMicros ?? "0",
|
|
1609
|
+
consumedUsdMicros: flat.consumedUsdMicros ?? nested?.consumedUsdMicros ?? "0",
|
|
1610
|
+
lifetimeGrantedUsdMicros: flat.lifetimeGrantedUsdMicros ?? nested?.lifetimeGrantedUsdMicros ?? "0",
|
|
1611
|
+
hasAccess: flat.hasAccess ?? nested?.hasAccess ?? false,
|
|
1612
|
+
remainingUsdMicros: flat.balanceUsdMicros ?? nested?.balanceUsdMicros,
|
|
1613
|
+
grantedUsdMicros: flat.grantedUsdMicros,
|
|
1614
|
+
featureKey: flat.featureKey
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1617
|
+
async getUserSubscription(externalUserId) {
|
|
1618
|
+
return this.requestJson(
|
|
1619
|
+
`${this.getAppsBaseUrl()}/users/${encodeURIComponent(externalUserId)}/subscription`,
|
|
1620
|
+
{
|
|
1621
|
+
method: "GET",
|
|
1622
|
+
headers: this.builderHeaders(),
|
|
1623
|
+
cache: "no-store"
|
|
1624
|
+
}
|
|
1625
|
+
);
|
|
1626
|
+
}
|
|
1627
|
+
async fetchUsageForExternalUser(input) {
|
|
1628
|
+
const usageByUser = await this.getUsage({
|
|
1629
|
+
startDate: input.startDate,
|
|
1630
|
+
endDate: input.endDate,
|
|
1631
|
+
groupBy: "user"
|
|
1632
|
+
});
|
|
1633
|
+
const userIds = getEndUserIdsForExternalUser(usageByUser, input.externalUserId);
|
|
1634
|
+
const cap = input.maxEndUserIds ?? DEFAULT_MAX_END_USER_IDS;
|
|
1635
|
+
const cappedUserIds = userIds.slice(0, cap);
|
|
1636
|
+
const usagePipelineModels = await Promise.all(
|
|
1637
|
+
cappedUserIds.map(
|
|
1638
|
+
(userId) => this.getUsage({
|
|
1639
|
+
startDate: input.startDate,
|
|
1640
|
+
endDate: input.endDate,
|
|
1641
|
+
groupBy: "pipeline_model",
|
|
1642
|
+
userId
|
|
1643
|
+
})
|
|
1644
|
+
)
|
|
1645
|
+
);
|
|
1646
|
+
const usageDaily = await this.getUsage({
|
|
1647
|
+
startDate: input.startDate,
|
|
1648
|
+
endDate: input.endDate,
|
|
1649
|
+
groupBy: "daily_pipeline",
|
|
1650
|
+
userId: input.externalUserId
|
|
1651
|
+
});
|
|
1652
|
+
return buildMeScopeUsagePayload(
|
|
1653
|
+
usageByUser,
|
|
1654
|
+
input.externalUserId,
|
|
1655
|
+
usagePipelineModels,
|
|
1656
|
+
usageDaily
|
|
1657
|
+
);
|
|
1658
|
+
}
|
|
1659
|
+
async getAppManifest(opts) {
|
|
1660
|
+
const url = `${this.getAppsBaseUrl()}/manifest`;
|
|
1661
|
+
const headers = {
|
|
1662
|
+
...this.builderHeadersRecord()
|
|
1663
|
+
};
|
|
1664
|
+
if (opts?.ifNoneMatch) {
|
|
1665
|
+
headers["If-None-Match"] = opts.ifNoneMatch;
|
|
1666
|
+
}
|
|
1667
|
+
this.logger?.debug?.("PmtHouse request", { method: "GET", url });
|
|
1668
|
+
const response = await this.fetchImpl(url, {
|
|
1669
|
+
method: "GET",
|
|
1670
|
+
headers,
|
|
1671
|
+
signal: opts?.signal,
|
|
1672
|
+
cache: "no-store"
|
|
1673
|
+
});
|
|
1674
|
+
const etag = response.headers.get("etag")?.trim() ?? null;
|
|
1675
|
+
if (response.status === 304) {
|
|
1676
|
+
return {
|
|
1677
|
+
manifest: null,
|
|
1678
|
+
etag: etag ?? opts?.ifNoneMatch ?? null,
|
|
1679
|
+
notModified: true
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
const raw = await response.text();
|
|
1683
|
+
const ct = response.headers.get("content-type") ?? "";
|
|
1684
|
+
const looksJson = ct.includes("application/json") || ct.includes("json");
|
|
1685
|
+
const parsed = raw && looksJson ? this.safeParseJson(raw) : null;
|
|
1686
|
+
if (!response.ok) {
|
|
1687
|
+
const details = parsed ?? {};
|
|
1688
|
+
let description;
|
|
1689
|
+
if (typeof details.error_description === "string") {
|
|
1690
|
+
description = details.error_description;
|
|
1691
|
+
} else if (typeof details.error === "string") {
|
|
1692
|
+
description = details.error;
|
|
1693
|
+
} else {
|
|
1694
|
+
description = `Request failed (${response.status})`;
|
|
1695
|
+
}
|
|
1696
|
+
throw new exports.PmtHouseError(description, {
|
|
1697
|
+
status: response.status,
|
|
1698
|
+
code: typeof details.error === "string" ? details.error : "pymthouse_http_error",
|
|
1699
|
+
details
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
if (!looksJson || parsed === null) {
|
|
1703
|
+
throw new exports.PmtHouseError("Expected JSON response from Builder manifest endpoint", {
|
|
1704
|
+
status: 502,
|
|
1705
|
+
code: "invalid_response",
|
|
1706
|
+
details: { contentType: ct, preview: raw.slice(0, 200) }
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
return {
|
|
1710
|
+
manifest: parseAppManifestResponse(parsed),
|
|
1711
|
+
etag,
|
|
1712
|
+
notModified: false
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
/**
|
|
1716
|
+
* Upsert an external user, mint a short-lived JWT, and exchange for an opaque signer session.
|
|
1717
|
+
*/
|
|
1718
|
+
async mintSignerSessionForExternalUser(input) {
|
|
1719
|
+
await this.upsertAppUser({
|
|
1720
|
+
externalUserId: input.externalUserId,
|
|
1721
|
+
email: input.email,
|
|
1722
|
+
status: "active"
|
|
1723
|
+
});
|
|
1724
|
+
const exchange = await this.mintUserSignerSessionToken({
|
|
1725
|
+
externalUserId: input.externalUserId,
|
|
1726
|
+
scope: input.scope ?? SIGN_JOB_SCOPE,
|
|
1727
|
+
resource: this.issuerUrl
|
|
1728
|
+
});
|
|
1729
|
+
return parseSignerSessionExchange(exchange);
|
|
1730
|
+
}
|
|
1731
|
+
/**
|
|
1732
|
+
* Approve a pending RFC 8628 device code for an external user (Option B).
|
|
1733
|
+
*/
|
|
1734
|
+
async approveDeviceLogin(input) {
|
|
1735
|
+
if (input.publicClientId && input.publicClientId !== this.publicClientId) {
|
|
1736
|
+
throw new exports.PmtHouseError(
|
|
1737
|
+
"publicClientId does not match configured public client id",
|
|
1738
|
+
{ status: 400, code: "invalid_client" }
|
|
1739
|
+
);
|
|
1740
|
+
}
|
|
1741
|
+
await this.upsertAppUser({
|
|
1742
|
+
externalUserId: input.externalUserId,
|
|
1743
|
+
email: input.email,
|
|
1744
|
+
status: "active"
|
|
1745
|
+
});
|
|
1746
|
+
const userToken = await this.mintUserAccessToken({
|
|
1747
|
+
externalUserId: input.externalUserId,
|
|
1748
|
+
scope: SIGN_JOB_SCOPE
|
|
1749
|
+
});
|
|
1750
|
+
await this.completeDeviceApproval({
|
|
1751
|
+
userJwt: userToken.access_token,
|
|
1752
|
+
userCode: input.userCode
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
539
1755
|
tokenEndpointFetchOptions() {
|
|
540
1756
|
const o = {
|
|
541
1757
|
[oauth4webapi.customFetch]: this.fetchImpl
|
|
@@ -552,6 +1768,9 @@ var PmtHouseClient = class {
|
|
|
552
1768
|
return new URL(this.issuerUrl).origin;
|
|
553
1769
|
}
|
|
554
1770
|
builderHeaders() {
|
|
1771
|
+
return this.builderHeadersRecord();
|
|
1772
|
+
}
|
|
1773
|
+
builderHeadersRecord() {
|
|
555
1774
|
return {
|
|
556
1775
|
Authorization: encodeClientSecretBasic(this.m2mClientId, this.m2mClientSecret),
|
|
557
1776
|
"Content-Type": "application/json",
|
|
@@ -575,15 +1794,22 @@ var PmtHouseClient = class {
|
|
|
575
1794
|
const parsed = raw && looksJson ? this.safeParseJson(raw) : raw ? null : null;
|
|
576
1795
|
if (!response.ok) {
|
|
577
1796
|
const details = parsed ?? {};
|
|
578
|
-
|
|
579
|
-
|
|
1797
|
+
let description;
|
|
1798
|
+
if (typeof details.error_description === "string") {
|
|
1799
|
+
description = details.error_description;
|
|
1800
|
+
} else if (typeof details.error === "string") {
|
|
1801
|
+
description = details.error;
|
|
1802
|
+
} else {
|
|
1803
|
+
description = `Request failed (${response.status})`;
|
|
1804
|
+
}
|
|
1805
|
+
throw new exports.PmtHouseError(description, {
|
|
580
1806
|
status: response.status,
|
|
581
1807
|
code: typeof details.error === "string" ? details.error : "pymthouse_http_error",
|
|
582
1808
|
details
|
|
583
1809
|
});
|
|
584
1810
|
}
|
|
585
1811
|
if (!looksJson || parsed === null) {
|
|
586
|
-
throw new PmtHouseError("Expected JSON response from Builder or Usage API", {
|
|
1812
|
+
throw new exports.PmtHouseError("Expected JSON response from Builder or Usage API", {
|
|
587
1813
|
status: 502,
|
|
588
1814
|
code: "invalid_response",
|
|
589
1815
|
details: { contentType: ct, preview: raw.slice(0, 200) }
|
|
@@ -602,83 +1828,121 @@ var PmtHouseClient = class {
|
|
|
602
1828
|
}
|
|
603
1829
|
}
|
|
604
1830
|
asError(error) {
|
|
605
|
-
if (error instanceof PmtHouseError) {
|
|
1831
|
+
if (error instanceof exports.PmtHouseError) {
|
|
606
1832
|
return error;
|
|
607
1833
|
}
|
|
608
1834
|
if (error instanceof Error) {
|
|
609
|
-
return new PmtHouseError(error.message, {
|
|
1835
|
+
return new exports.PmtHouseError(error.message, {
|
|
610
1836
|
code: "unexpected_error",
|
|
611
1837
|
status: 500
|
|
612
1838
|
});
|
|
613
1839
|
}
|
|
614
|
-
return new PmtHouseError("Unexpected error", {
|
|
1840
|
+
return new exports.PmtHouseError("Unexpected error", {
|
|
615
1841
|
code: "unexpected_error",
|
|
616
1842
|
status: 500
|
|
617
1843
|
});
|
|
618
1844
|
}
|
|
619
1845
|
};
|
|
620
1846
|
|
|
621
|
-
// src/
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
1847
|
+
// src/index.ts
|
|
1848
|
+
init_errors();
|
|
1849
|
+
|
|
1850
|
+
// src/config.ts
|
|
1851
|
+
init_string_utils();
|
|
1852
|
+
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.";
|
|
1853
|
+
function trimEnv(name) {
|
|
1854
|
+
const value = process.env[name];
|
|
1855
|
+
if (!value) return null;
|
|
1856
|
+
const trimmed = value.trim();
|
|
1857
|
+
return trimmed || null;
|
|
1858
|
+
}
|
|
1859
|
+
function readPymthouseEnv() {
|
|
1860
|
+
const issuerUrl = trimEnv("PYMTHOUSE_ISSUER_URL");
|
|
1861
|
+
const publicClientId = trimEnv("PYMTHOUSE_PUBLIC_CLIENT_ID");
|
|
1862
|
+
const m2mClientId = trimEnv("PYMTHOUSE_M2M_CLIENT_ID");
|
|
1863
|
+
const m2mClientSecret = trimEnv("PYMTHOUSE_M2M_CLIENT_SECRET");
|
|
1864
|
+
if (!issuerUrl || !publicClientId || !m2mClientId || !m2mClientSecret) {
|
|
1865
|
+
return null;
|
|
627
1866
|
}
|
|
1867
|
+
return {
|
|
1868
|
+
issuerUrl: stripTrailingSlashes(issuerUrl),
|
|
1869
|
+
publicClientId,
|
|
1870
|
+
m2mClientId,
|
|
1871
|
+
m2mClientSecret
|
|
1872
|
+
};
|
|
628
1873
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
1874
|
+
function getPymthouseIssuerUrlFromEnv() {
|
|
1875
|
+
const raw = trimEnv("PYMTHOUSE_ISSUER_URL");
|
|
1876
|
+
if (!raw) return null;
|
|
1877
|
+
try {
|
|
1878
|
+
return stripTrailingSlashes(new URL(raw).href);
|
|
1879
|
+
} catch {
|
|
1880
|
+
return null;
|
|
635
1881
|
}
|
|
636
|
-
throw new PmtHouseError(`Missing required environment variable: ${name}`, {
|
|
637
|
-
status: 500,
|
|
638
|
-
code: "missing_env"
|
|
639
|
-
});
|
|
640
1882
|
}
|
|
641
|
-
function
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
if (cachedClient) {
|
|
647
|
-
return cachedClient;
|
|
648
|
-
}
|
|
649
|
-
const issuerUrl = requiredEnv("PYMTHOUSE_ISSUER_URL");
|
|
650
|
-
cachedClient = new PmtHouseClient({
|
|
651
|
-
issuerUrl,
|
|
652
|
-
publicClientId: requiredEnv("PYMTHOUSE_PUBLIC_CLIENT_ID"),
|
|
653
|
-
m2mClientId: requiredEnv("PYMTHOUSE_M2M_CLIENT_ID"),
|
|
654
|
-
m2mClientSecret: requiredEnv("PYMTHOUSE_M2M_CLIENT_SECRET"),
|
|
655
|
-
allowInsecureHttp: issuerUrl.startsWith("http:"),
|
|
656
|
-
logger: {
|
|
657
|
-
debug: (message, details) => {
|
|
658
|
-
if (process.env.NODE_ENV !== "production") {
|
|
659
|
-
console.debug(`[pymthouse] ${message}`, details ?? {});
|
|
660
|
-
}
|
|
661
|
-
},
|
|
662
|
-
warn: (message, details) => {
|
|
663
|
-
console.warn(`[pymthouse] ${message}`, details ?? {});
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
return cachedClient;
|
|
1883
|
+
function getPymthousePublicClientIdFromEnv() {
|
|
1884
|
+
return trimEnv("PYMTHOUSE_PUBLIC_CLIENT_ID");
|
|
1885
|
+
}
|
|
1886
|
+
function isPymthouseConfigured() {
|
|
1887
|
+
return readPymthouseEnv() !== null;
|
|
668
1888
|
}
|
|
1889
|
+
function getBuilderApiV1BaseFromIssuerUrl(issuerUrl) {
|
|
1890
|
+
return stripOidcPathSuffix(issuerUrl);
|
|
1891
|
+
}
|
|
1892
|
+
function getPymthouseIssuerOrigin(issuerUrl) {
|
|
1893
|
+
return new URL(stripTrailingSlashes(issuerUrl.trim())).origin;
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
// src/index.ts
|
|
1897
|
+
init_discovery();
|
|
669
1898
|
|
|
1899
|
+
exports.DEFAULT_MAX_END_USER_IDS = DEFAULT_MAX_END_USER_IDS;
|
|
1900
|
+
exports.NETWORK_USD_PER_MICRO = NETWORK_USD_PER_MICRO;
|
|
1901
|
+
exports.PYMTHOUSE_NOT_CONFIGURED_MESSAGE = PYMTHOUSE_NOT_CONFIGURED_MESSAGE;
|
|
1902
|
+
exports.PYMTHOUSE_SIGNER_SESSION_TTL_MS = PYMTHOUSE_SIGNER_SESSION_TTL_MS;
|
|
670
1903
|
exports.PmtHouseClient = PmtHouseClient;
|
|
671
|
-
exports.
|
|
1904
|
+
exports.SIGNER_SESSION_EXPIRES_IN_SEC = SIGNER_SESSION_EXPIRES_IN_SEC;
|
|
1905
|
+
exports.SIGNER_SESSION_TTL_MS = SIGNER_SESSION_TTL_MS;
|
|
1906
|
+
exports.SIGN_JOB_SCOPE = SIGN_JOB_SCOPE;
|
|
672
1907
|
exports.aggregateUsageByExternalUserId = aggregateUsageByExternalUserId;
|
|
1908
|
+
exports.applyRetailRateToNetworkMicros = applyRetailRateToNetworkMicros;
|
|
673
1909
|
exports.authorizationServerToOidcDocument = authorizationServerToOidcDocument;
|
|
674
1910
|
exports.buildDeviceCodeResource = buildDeviceCodeResource;
|
|
1911
|
+
exports.buildMeScopeUsagePayload = buildMeScopeUsagePayload;
|
|
675
1912
|
exports.clearDiscoveryCache = clearDiscoveryCache;
|
|
676
|
-
exports.
|
|
1913
|
+
exports.computeManifestRevision = computeManifestRevision;
|
|
1914
|
+
exports.computePymthouseExpiry = computePymthouseExpiry;
|
|
1915
|
+
exports.computeSignerSessionExpiry = computeSignerSessionExpiry;
|
|
1916
|
+
exports.decodeJwtExp = decodeJwtExp;
|
|
1917
|
+
exports.defaultRetailRateUsd = defaultRetailRateUsd;
|
|
677
1918
|
exports.fetchDiscoveryDocument = fetchDiscoveryDocument;
|
|
678
|
-
exports.
|
|
1919
|
+
exports.getBuilderApiV1BaseFromIssuerUrl = getBuilderApiV1BaseFromIssuerUrl;
|
|
1920
|
+
exports.getEndUserIdsForExternalUser = getEndUserIdsForExternalUser;
|
|
1921
|
+
exports.getPymthouseIssuerOrigin = getPymthouseIssuerOrigin;
|
|
1922
|
+
exports.getPymthouseIssuerUrlFromEnv = getPymthouseIssuerUrlFromEnv;
|
|
1923
|
+
exports.getPymthousePublicClientIdFromEnv = getPymthousePublicClientIdFromEnv;
|
|
1924
|
+
exports.getUsageRecordUserIdsForExternalUser = getUsageRecordUserIdsForExternalUser;
|
|
1925
|
+
exports.getUtcCalendarMonthIsoBounds = getUtcCalendarMonthIsoBounds;
|
|
1926
|
+
exports.ingestSignedTicket = ingestSignedTicket;
|
|
1927
|
+
exports.ingestSignedTicketsBatch = ingestSignedTicketsBatch;
|
|
1928
|
+
exports.isLikelyOidcJwt = isLikelyOidcJwt;
|
|
1929
|
+
exports.isOpaqueSignerSessionToken = isOpaqueSignerSessionToken;
|
|
1930
|
+
exports.isPymthouseConfigured = isPymthouseConfigured;
|
|
679
1931
|
exports.listUsageByPipelineModel = listUsageByPipelineModel;
|
|
680
1932
|
exports.loadAuthorizationServer = loadAuthorizationServer;
|
|
1933
|
+
exports.markupPercentToRetailRateUsd = markupPercentToRetailRateUsd;
|
|
1934
|
+
exports.mergeUsageByPipelineModel = mergeUsageByPipelineModel;
|
|
681
1935
|
exports.normalizeUserCode = normalizeUserCode;
|
|
1936
|
+
exports.parseAppManifestResponse = parseAppManifestResponse;
|
|
1937
|
+
exports.parseMarkupPercentInput = parseMarkupPercentInput;
|
|
1938
|
+
exports.parseRetailRateUsd = parseRetailRateUsd;
|
|
1939
|
+
exports.parseSignerSessionExchange = parseSignerSessionExchange;
|
|
1940
|
+
exports.parseUsageDateParam = parseUsageDateParam;
|
|
1941
|
+
exports.readPymthouseEnv = readPymthouseEnv;
|
|
1942
|
+
exports.retailRateUsdPerMillion = retailRateUsdPerMillion;
|
|
1943
|
+
exports.retailRateUsdToMarkupPercent = retailRateUsdToMarkupPercent;
|
|
1944
|
+
exports.signerSnapshotToIngestPayload = signerSnapshotToIngestPayload;
|
|
1945
|
+
exports.summarizeUsageFiatForExternalUser = summarizeUsageFiatForExternalUser;
|
|
682
1946
|
exports.summarizeUsageForExternalUser = summarizeUsageForExternalUser;
|
|
683
1947
|
exports.toPmtHouseError = toPmtHouseError;
|
|
684
1948
|
//# sourceMappingURL=index.cjs.map
|