@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/env.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { genericTokenEndpointRequest, processGenericTokenEndpointResponse, clientCredentialsGrantRequest, processClientCredentialsResponse, customFetch, allowInsecureRequests, discoveryRequest, processDiscoveryResponse, ResponseBodyError, OperationProcessingError } from 'oauth4webapi';
|
|
2
|
+
import 'crypto';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
4
13
|
|
|
5
14
|
// src/encoding.ts
|
|
6
15
|
function encodeClientSecretBasic(clientId, clientSecret) {
|
|
@@ -8,35 +17,69 @@ function encodeClientSecretBasic(clientId, clientSecret) {
|
|
|
8
17
|
const b64 = typeof Buffer !== "undefined" ? Buffer.from(raw, "utf8").toString("base64") : btoa(Array.from(new TextEncoder().encode(raw), (c) => String.fromCharCode(c)).join(""));
|
|
9
18
|
return `Basic ${b64}`;
|
|
10
19
|
}
|
|
20
|
+
var init_encoding = __esm({
|
|
21
|
+
"src/encoding.ts"() {
|
|
22
|
+
}
|
|
23
|
+
});
|
|
11
24
|
|
|
12
25
|
// src/errors.ts
|
|
13
|
-
var PmtHouseError
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
var PmtHouseError;
|
|
27
|
+
var init_errors = __esm({
|
|
28
|
+
"src/errors.ts"() {
|
|
29
|
+
PmtHouseError = class extends Error {
|
|
30
|
+
status;
|
|
31
|
+
code;
|
|
32
|
+
details;
|
|
33
|
+
constructor(message, {
|
|
34
|
+
status = 500,
|
|
35
|
+
code = "pymthouse_error",
|
|
36
|
+
details
|
|
37
|
+
} = {}) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "PmtHouseError";
|
|
40
|
+
this.status = status;
|
|
41
|
+
this.code = code;
|
|
42
|
+
this.details = details;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
27
45
|
}
|
|
28
|
-
};
|
|
46
|
+
});
|
|
29
47
|
|
|
30
48
|
// src/string-utils.ts
|
|
31
49
|
function stripTrailingSlashes(value) {
|
|
32
50
|
let end = value.length;
|
|
33
|
-
while (end > 0 && value.
|
|
51
|
+
while (end > 0 && (value.codePointAt(end - 1) ?? 0) === 47) {
|
|
34
52
|
end--;
|
|
35
53
|
}
|
|
36
54
|
return value.slice(0, end);
|
|
37
55
|
}
|
|
38
|
-
|
|
39
|
-
|
|
56
|
+
function endsWithIgnoreCase(value, suffix) {
|
|
57
|
+
if (suffix.length > value.length) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const start = value.length - suffix.length;
|
|
61
|
+
for (let i = 0; i < suffix.length; i++) {
|
|
62
|
+
const a = value.codePointAt(start + i) ?? 0;
|
|
63
|
+
const b = suffix.codePointAt(i) ?? 0;
|
|
64
|
+
if (a !== b && (a | 32) !== (b | 32)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
function stripSuffixIgnoreCase(value, suffix) {
|
|
71
|
+
return endsWithIgnoreCase(value, suffix) ? value.slice(0, value.length - suffix.length) : value;
|
|
72
|
+
}
|
|
73
|
+
function stripIssuerOriginFromOidcUrl(issuerUrl) {
|
|
74
|
+
let base = stripTrailingSlashes(issuerUrl.trim());
|
|
75
|
+
base = stripSuffixIgnoreCase(base, "/api/v1/oidc");
|
|
76
|
+
base = stripSuffixIgnoreCase(base, "/oidc");
|
|
77
|
+
return stripTrailingSlashes(base);
|
|
78
|
+
}
|
|
79
|
+
var init_string_utils = __esm({
|
|
80
|
+
"src/string-utils.ts"() {
|
|
81
|
+
}
|
|
82
|
+
});
|
|
40
83
|
function authorizationServerToOidcDocument(as) {
|
|
41
84
|
const tokenEndpoint = as.token_endpoint;
|
|
42
85
|
const jwksUri = as.jwks_uri;
|
|
@@ -55,8 +98,6 @@ function authorizationServerToOidcDocument(as) {
|
|
|
55
98
|
device_authorization_endpoint: as.device_authorization_endpoint
|
|
56
99
|
};
|
|
57
100
|
}
|
|
58
|
-
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
59
|
-
var discoveryCache = /* @__PURE__ */ new Map();
|
|
60
101
|
function normalizedIssuerKey(issuerUrl) {
|
|
61
102
|
return stripTrailingSlashes(issuerUrl);
|
|
62
103
|
}
|
|
@@ -121,6 +162,612 @@ function mapDiscoveryNetworkError(error) {
|
|
|
121
162
|
code: "oidc_discovery_failed"
|
|
122
163
|
});
|
|
123
164
|
}
|
|
165
|
+
var CACHE_TTL_MS, discoveryCache;
|
|
166
|
+
var init_discovery = __esm({
|
|
167
|
+
"src/discovery.ts"() {
|
|
168
|
+
init_errors();
|
|
169
|
+
init_string_utils();
|
|
170
|
+
CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
171
|
+
discoveryCache = /* @__PURE__ */ new Map();
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// src/signer/fetch-json.ts
|
|
176
|
+
function oauthFailureDescription(parsed, failureLabel, status) {
|
|
177
|
+
if (typeof parsed.error_description === "string") {
|
|
178
|
+
return parsed.error_description;
|
|
179
|
+
}
|
|
180
|
+
if (typeof parsed.error === "string") {
|
|
181
|
+
return parsed.error;
|
|
182
|
+
}
|
|
183
|
+
return `${failureLabel} (${status})`;
|
|
184
|
+
}
|
|
185
|
+
async function readJsonObjectFromResponse(response, options) {
|
|
186
|
+
const text = await response.text();
|
|
187
|
+
let parsed;
|
|
188
|
+
try {
|
|
189
|
+
parsed = text ? JSON.parse(text) : {};
|
|
190
|
+
} catch {
|
|
191
|
+
throw new PmtHouseError(options.invalidJsonMessage, {
|
|
192
|
+
status: 502,
|
|
193
|
+
code: options.invalidJsonCode,
|
|
194
|
+
details: { status: response.status }
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (!response.ok) {
|
|
198
|
+
const description = oauthFailureDescription(parsed, options.failureLabel, response.status);
|
|
199
|
+
throw new PmtHouseError(description, {
|
|
200
|
+
status: response.status,
|
|
201
|
+
code: typeof parsed.error === "string" ? parsed.error : options.defaultErrorCode,
|
|
202
|
+
details: parsed
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
return parsed;
|
|
206
|
+
}
|
|
207
|
+
var init_fetch_json = __esm({
|
|
208
|
+
"src/signer/fetch-json.ts"() {
|
|
209
|
+
init_errors();
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// src/signer/handler-errors.ts
|
|
214
|
+
function signerHandlerErrorResponse(error) {
|
|
215
|
+
if (error instanceof PmtHouseError) {
|
|
216
|
+
return new Response(
|
|
217
|
+
JSON.stringify({
|
|
218
|
+
error: error.code,
|
|
219
|
+
error_description: error.message,
|
|
220
|
+
details: error.details
|
|
221
|
+
}),
|
|
222
|
+
{
|
|
223
|
+
status: error.status,
|
|
224
|
+
headers: { "Content-Type": "application/json" }
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
const message = error instanceof Error ? error.message : "Internal error";
|
|
229
|
+
return new Response(JSON.stringify({ error: "internal_error", error_description: message }), {
|
|
230
|
+
status: 500,
|
|
231
|
+
headers: { "Content-Type": "application/json" }
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
var init_handler_errors = __esm({
|
|
235
|
+
"src/signer/handler-errors.ts"() {
|
|
236
|
+
init_errors();
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// src/signer/json-fields.ts
|
|
241
|
+
function readStringField(body, key, errorCode, messagePrefix = "Response") {
|
|
242
|
+
const value = body[key];
|
|
243
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
244
|
+
throw new PmtHouseError(`${messagePrefix} missing ${key}`, {
|
|
245
|
+
status: 502,
|
|
246
|
+
code: errorCode
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return value.trim();
|
|
250
|
+
}
|
|
251
|
+
function readExpiresIn(body, errorCode) {
|
|
252
|
+
const expiresIn = body.expires_in;
|
|
253
|
+
if (typeof expiresIn !== "number" || !Number.isFinite(expiresIn) || expiresIn <= 0) {
|
|
254
|
+
throw new PmtHouseError("Response missing expires_in", {
|
|
255
|
+
status: 502,
|
|
256
|
+
code: errorCode
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
return Math.floor(expiresIn);
|
|
260
|
+
}
|
|
261
|
+
var init_json_fields = __esm({
|
|
262
|
+
"src/signer/json-fields.ts"() {
|
|
263
|
+
init_errors();
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// src/signer/mint-token.ts
|
|
268
|
+
function parseMintUserSignerTokenResponse(body, ttlRefreshRatio = DEFAULT_TTL_REFRESH_RATIO) {
|
|
269
|
+
const accessToken = readStringField(body, "access_token", TOKEN_RESPONSE_ERROR, "Token response");
|
|
270
|
+
const expiresIn = readExpiresIn(body, TOKEN_RESPONSE_ERROR);
|
|
271
|
+
const balanceUsdMicros = readStringField(
|
|
272
|
+
body,
|
|
273
|
+
"balanceUsdMicros",
|
|
274
|
+
TOKEN_RESPONSE_ERROR,
|
|
275
|
+
"Token response"
|
|
276
|
+
);
|
|
277
|
+
const lifetimeGrantedUsdMicros = readStringField(
|
|
278
|
+
body,
|
|
279
|
+
"lifetimeGrantedUsdMicros",
|
|
280
|
+
TOKEN_RESPONSE_ERROR,
|
|
281
|
+
"Token response"
|
|
282
|
+
);
|
|
283
|
+
const now = Date.now();
|
|
284
|
+
const expiresAt = now + expiresIn * 1e3;
|
|
285
|
+
const refreshAt = now + Math.floor(expiresIn * 1e3 * ttlRefreshRatio);
|
|
286
|
+
return {
|
|
287
|
+
jwt: accessToken,
|
|
288
|
+
expiresAt,
|
|
289
|
+
refreshAt,
|
|
290
|
+
balanceUsdMicros,
|
|
291
|
+
lifetimeGrantedUsdMicros
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
var LIVEPEER_REMOTE_SIGNER_AUDIENCE, DEFAULT_TTL_REFRESH_RATIO, TOKEN_RESPONSE_ERROR;
|
|
295
|
+
var init_mint_token = __esm({
|
|
296
|
+
"src/signer/mint-token.ts"() {
|
|
297
|
+
init_json_fields();
|
|
298
|
+
LIVEPEER_REMOTE_SIGNER_AUDIENCE = "livepeer-remote-signer";
|
|
299
|
+
DEFAULT_TTL_REFRESH_RATIO = 0.8;
|
|
300
|
+
TOKEN_RESPONSE_ERROR = "invalid_token_response";
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// src/signer/device-exchange.ts
|
|
305
|
+
function extractSignerAccessTokenFromExchangeBody(body) {
|
|
306
|
+
const tokenObj = body.token;
|
|
307
|
+
if (tokenObj !== null && typeof tokenObj === "object" && !Array.isArray(tokenObj)) {
|
|
308
|
+
const nested = tokenObj;
|
|
309
|
+
for (const key of ["accessToken", "access_token"]) {
|
|
310
|
+
const value = nested[key];
|
|
311
|
+
if (typeof value === "string" && value.trim()) {
|
|
312
|
+
return value.trim();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
for (const key of ["accessToken", "access_token"]) {
|
|
317
|
+
const value = body[key];
|
|
318
|
+
if (typeof value === "string" && value.trim()) {
|
|
319
|
+
return value.trim();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
throw new PmtHouseError("Device exchange response missing signer access token", {
|
|
323
|
+
status: 502,
|
|
324
|
+
code: "invalid_exchange_response"
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
function normalizeDeviceExchangeResponse(minted, options) {
|
|
328
|
+
const scope = minted.scope.trim() || "sign:job";
|
|
329
|
+
const body = {
|
|
330
|
+
access_token: minted.access_token,
|
|
331
|
+
token_type: "Bearer",
|
|
332
|
+
expires_in: minted.expires_in,
|
|
333
|
+
scope,
|
|
334
|
+
balanceUsdMicros: minted.balanceUsdMicros,
|
|
335
|
+
lifetimeGrantedUsdMicros: minted.lifetimeGrantedUsdMicros,
|
|
336
|
+
token: {
|
|
337
|
+
accessToken: minted.access_token,
|
|
338
|
+
access_token: minted.access_token,
|
|
339
|
+
expiresIn: minted.expires_in,
|
|
340
|
+
expires_in: minted.expires_in,
|
|
341
|
+
scope,
|
|
342
|
+
balanceUsdMicros: minted.balanceUsdMicros,
|
|
343
|
+
lifetimeGrantedUsdMicros: minted.lifetimeGrantedUsdMicros
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
const signerUrl = options?.signerUrl?.trim();
|
|
347
|
+
if (signerUrl) {
|
|
348
|
+
body.signerUrl = signerUrl;
|
|
349
|
+
}
|
|
350
|
+
return body;
|
|
351
|
+
}
|
|
352
|
+
async function mintSignerTokenFromDeviceToken(options) {
|
|
353
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
354
|
+
const issuerUrl = stripTrailingSlashes(options.issuerUrl);
|
|
355
|
+
const as = await loadAuthorizationServer(issuerUrl, fetchImpl, {
|
|
356
|
+
allowInsecureHttp: options.allowInsecureHttp
|
|
357
|
+
});
|
|
358
|
+
const tokenEndpoint = as.token_endpoint;
|
|
359
|
+
if (!tokenEndpoint) {
|
|
360
|
+
throw new PmtHouseError("OIDC discovery document is missing token_endpoint", {
|
|
361
|
+
status: 500,
|
|
362
|
+
code: "oidc_discovery_invalid"
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
const audience = options.audience?.trim() || LIVEPEER_REMOTE_SIGNER_AUDIENCE;
|
|
366
|
+
const params = new URLSearchParams({
|
|
367
|
+
grant_type: TOKEN_EXCHANGE_GRANT,
|
|
368
|
+
subject_token: options.deviceToken,
|
|
369
|
+
subject_token_type: SUBJECT_ACCESS_TOKEN_TYPE,
|
|
370
|
+
audience,
|
|
371
|
+
resource: audience
|
|
372
|
+
});
|
|
373
|
+
if (options.scope?.trim()) {
|
|
374
|
+
params.set("scope", options.scope.trim());
|
|
375
|
+
}
|
|
376
|
+
const response = await fetchImpl(tokenEndpoint, {
|
|
377
|
+
method: "POST",
|
|
378
|
+
headers: {
|
|
379
|
+
Authorization: encodeClientSecretBasic(options.m2mClientId, options.m2mClientSecret),
|
|
380
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
381
|
+
Accept: "application/json"
|
|
382
|
+
},
|
|
383
|
+
body: params.toString(),
|
|
384
|
+
cache: "no-store"
|
|
385
|
+
});
|
|
386
|
+
const parsed = await readJsonObjectFromResponse(response, {
|
|
387
|
+
invalidJsonMessage: "Token endpoint returned invalid JSON",
|
|
388
|
+
invalidJsonCode: "invalid_token_response",
|
|
389
|
+
failureLabel: "Signer JWT exchange failed",
|
|
390
|
+
defaultErrorCode: "token_exchange_failed"
|
|
391
|
+
});
|
|
392
|
+
const cached = parseMintUserSignerTokenResponse(parsed);
|
|
393
|
+
return {
|
|
394
|
+
access_token: cached.jwt,
|
|
395
|
+
expires_in: readExpiresIn(parsed, EXCHANGE_RESPONSE_ERROR),
|
|
396
|
+
scope: readStringField(parsed, "scope", EXCHANGE_RESPONSE_ERROR),
|
|
397
|
+
balanceUsdMicros: cached.balanceUsdMicros,
|
|
398
|
+
lifetimeGrantedUsdMicros: cached.lifetimeGrantedUsdMicros
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
var TOKEN_EXCHANGE_GRANT, SUBJECT_ACCESS_TOKEN_TYPE, EXCHANGE_RESPONSE_ERROR;
|
|
402
|
+
var init_device_exchange = __esm({
|
|
403
|
+
"src/signer/device-exchange.ts"() {
|
|
404
|
+
init_discovery();
|
|
405
|
+
init_encoding();
|
|
406
|
+
init_errors();
|
|
407
|
+
init_string_utils();
|
|
408
|
+
init_fetch_json();
|
|
409
|
+
init_json_fields();
|
|
410
|
+
init_mint_token();
|
|
411
|
+
TOKEN_EXCHANGE_GRANT = "urn:ietf:params:oauth:grant-type:token-exchange";
|
|
412
|
+
SUBJECT_ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
|
|
413
|
+
EXCHANGE_RESPONSE_ERROR = "invalid_exchange_response";
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// src/signer/api-key-exchange.ts
|
|
418
|
+
var api_key_exchange_exports = {};
|
|
419
|
+
__export(api_key_exchange_exports, {
|
|
420
|
+
createApiKeyExchangeHandler: () => createApiKeyExchangeHandler,
|
|
421
|
+
exchangeApiKeyForSigner: () => exchangeApiKeyForSigner,
|
|
422
|
+
mintSignerSessionFromApiKey: () => mintSignerSessionFromApiKey,
|
|
423
|
+
mintUserAccessTokenFromApiKey: () => mintUserAccessTokenFromApiKey,
|
|
424
|
+
parseApiKeyExchangeRequestBody: () => parseApiKeyExchangeRequestBody
|
|
425
|
+
});
|
|
426
|
+
async function parseApiKeyExchangeRequestBody(request) {
|
|
427
|
+
let body;
|
|
428
|
+
try {
|
|
429
|
+
body = await request.json();
|
|
430
|
+
} catch {
|
|
431
|
+
throw new PmtHouseError("Request body must be JSON", {
|
|
432
|
+
status: 400,
|
|
433
|
+
code: "invalid_request"
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
if (body === null || typeof body !== "object" || Array.isArray(body)) {
|
|
437
|
+
throw new PmtHouseError("Request body must be a JSON object", {
|
|
438
|
+
status: 400,
|
|
439
|
+
code: "invalid_request"
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
const record = body;
|
|
443
|
+
const apiKeyRaw = record.apiKey;
|
|
444
|
+
if (typeof apiKeyRaw !== "string" || !apiKeyRaw.trim()) {
|
|
445
|
+
throw new PmtHouseError("Request body must include apiKey", {
|
|
446
|
+
status: 400,
|
|
447
|
+
code: "invalid_request"
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
const scope = typeof record.scope === "string" && record.scope.trim() ? record.scope.trim() : void 0;
|
|
451
|
+
const clientId = typeof record.clientId === "string" && record.clientId.trim() ? record.clientId.trim() : void 0;
|
|
452
|
+
return { apiKey: apiKeyRaw.trim(), scope, clientId };
|
|
453
|
+
}
|
|
454
|
+
async function mintUserAccessTokenFromApiKey(input) {
|
|
455
|
+
const fetchImpl = input.fetch ?? fetch;
|
|
456
|
+
const issuerOrigin = stripIssuerOriginFromOidcUrl(input.issuerUrl);
|
|
457
|
+
const url = `${issuerOrigin}/api/v1/apps/${encodeURIComponent(input.publicClientId)}/auth/api-key/token`;
|
|
458
|
+
const response = await fetchImpl(url, {
|
|
459
|
+
method: "POST",
|
|
460
|
+
headers: {
|
|
461
|
+
Authorization: `Bearer ${input.apiKey}`,
|
|
462
|
+
"Content-Type": "application/json",
|
|
463
|
+
Accept: "application/json"
|
|
464
|
+
},
|
|
465
|
+
body: JSON.stringify(input.scope ? { scope: input.scope } : {}),
|
|
466
|
+
cache: "no-store"
|
|
467
|
+
});
|
|
468
|
+
const parsed = await readJsonObjectFromResponse(response, {
|
|
469
|
+
invalidJsonMessage: "API key token exchange returned invalid JSON",
|
|
470
|
+
invalidJsonCode: "invalid_token_response",
|
|
471
|
+
failureLabel: "API key token exchange failed",
|
|
472
|
+
defaultErrorCode: "api_key_token_exchange_failed"
|
|
473
|
+
});
|
|
474
|
+
const accessToken = parsed.access_token;
|
|
475
|
+
if (typeof accessToken !== "string" || !accessToken.trim()) {
|
|
476
|
+
throw new PmtHouseError("API key token exchange missing access_token", {
|
|
477
|
+
status: 502,
|
|
478
|
+
code: EXCHANGE_RESPONSE_ERROR2
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
const expiresIn = typeof parsed.expires_in === "number" && Number.isFinite(parsed.expires_in) ? parsed.expires_in : 900;
|
|
482
|
+
const scope = typeof parsed.scope === "string" && parsed.scope.trim() ? parsed.scope.trim() : input.scope?.trim() || "sign:job";
|
|
483
|
+
return {
|
|
484
|
+
access_token: accessToken.trim(),
|
|
485
|
+
expires_in: expiresIn,
|
|
486
|
+
scope
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
async function mintSignerSessionFromApiKey(input) {
|
|
490
|
+
const userToken = await mintUserAccessTokenFromApiKey({
|
|
491
|
+
issuerUrl: input.issuerUrl,
|
|
492
|
+
publicClientId: input.publicClientId,
|
|
493
|
+
apiKey: input.apiKey,
|
|
494
|
+
scope: input.scope,
|
|
495
|
+
fetch: input.fetch
|
|
496
|
+
});
|
|
497
|
+
return mintSignerTokenFromDeviceToken({
|
|
498
|
+
issuerUrl: input.issuerUrl,
|
|
499
|
+
m2mClientId: input.m2mClientId,
|
|
500
|
+
m2mClientSecret: input.m2mClientSecret,
|
|
501
|
+
deviceToken: userToken.access_token,
|
|
502
|
+
scope: userToken.scope,
|
|
503
|
+
audience: input.audience,
|
|
504
|
+
fetch: input.fetch,
|
|
505
|
+
allowInsecureHttp: input.allowInsecureHttp
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
async function exchangeApiKeyForSigner(options) {
|
|
509
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
510
|
+
const url = `${stripTrailingSlashes(options.facadeUrl)}/api/pymthouse/keys/exchange`;
|
|
511
|
+
const body = { apiKey: options.apiKey };
|
|
512
|
+
if (options.scope?.trim()) {
|
|
513
|
+
body.scope = options.scope.trim();
|
|
514
|
+
}
|
|
515
|
+
if (options.clientId?.trim()) {
|
|
516
|
+
body.clientId = options.clientId.trim();
|
|
517
|
+
}
|
|
518
|
+
const response = await fetchImpl(url, {
|
|
519
|
+
method: "POST",
|
|
520
|
+
headers: {
|
|
521
|
+
"Content-Type": "application/json",
|
|
522
|
+
Accept: "application/json"
|
|
523
|
+
},
|
|
524
|
+
body: JSON.stringify(body),
|
|
525
|
+
cache: "no-store"
|
|
526
|
+
});
|
|
527
|
+
const parsed = await readJsonObjectFromResponse(response, {
|
|
528
|
+
invalidJsonMessage: "API key exchange returned invalid JSON",
|
|
529
|
+
invalidJsonCode: EXCHANGE_RESPONSE_ERROR2,
|
|
530
|
+
failureLabel: "API key exchange failed",
|
|
531
|
+
defaultErrorCode: "api_key_exchange_failed"
|
|
532
|
+
});
|
|
533
|
+
const accessToken = extractSignerAccessTokenFromExchangeBody(parsed);
|
|
534
|
+
const signerUrlRaw = parsed.signerUrl ?? parsed.signer_url;
|
|
535
|
+
const signerUrl = typeof signerUrlRaw === "string" && signerUrlRaw.trim() ? signerUrlRaw.trim() : void 0;
|
|
536
|
+
return normalizeDeviceExchangeResponse(
|
|
537
|
+
{
|
|
538
|
+
access_token: accessToken,
|
|
539
|
+
expires_in: typeof parsed.expires_in === "number" && Number.isFinite(parsed.expires_in) ? parsed.expires_in : 3600,
|
|
540
|
+
scope: typeof parsed.scope === "string" && parsed.scope.trim() ? parsed.scope.trim() : "sign:job",
|
|
541
|
+
balanceUsdMicros: typeof parsed.balanceUsdMicros === "string" ? parsed.balanceUsdMicros : "0",
|
|
542
|
+
lifetimeGrantedUsdMicros: typeof parsed.lifetimeGrantedUsdMicros === "string" ? parsed.lifetimeGrantedUsdMicros : "0"
|
|
543
|
+
},
|
|
544
|
+
{ signerUrl }
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
function createApiKeyExchangeHandler(config) {
|
|
548
|
+
const publicClientId = config.publicClientId.trim();
|
|
549
|
+
return async function apiKeyExchangeHandler(request) {
|
|
550
|
+
try {
|
|
551
|
+
if (request.method !== "POST") {
|
|
552
|
+
return new Response(JSON.stringify({ error: "method_not_allowed" }), {
|
|
553
|
+
status: 405,
|
|
554
|
+
headers: { "Content-Type": "application/json" }
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
const parsed = await parseApiKeyExchangeRequestBody(request);
|
|
558
|
+
const effectiveClientId = parsed.clientId?.trim() || publicClientId;
|
|
559
|
+
if (effectiveClientId !== publicClientId) {
|
|
560
|
+
throw new PmtHouseError("clientId does not match configured public client", {
|
|
561
|
+
status: 400,
|
|
562
|
+
code: "invalid_request"
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
const minted = await mintSignerSessionFromApiKey({
|
|
566
|
+
issuerUrl: config.issuerUrl,
|
|
567
|
+
publicClientId,
|
|
568
|
+
m2mClientId: config.m2mClientId,
|
|
569
|
+
m2mClientSecret: config.m2mClientSecret,
|
|
570
|
+
apiKey: parsed.apiKey,
|
|
571
|
+
scope: parsed.scope,
|
|
572
|
+
audience: config.audience,
|
|
573
|
+
fetch: config.fetch,
|
|
574
|
+
allowInsecureHttp: config.allowInsecureHttp
|
|
575
|
+
});
|
|
576
|
+
const signerUrlValue = typeof config.signerUrl === "string" && config.signerUrl.trim() ? config.signerUrl.trim() : void 0;
|
|
577
|
+
const body = normalizeDeviceExchangeResponse(minted, { signerUrl: signerUrlValue });
|
|
578
|
+
return new Response(JSON.stringify(body), {
|
|
579
|
+
status: 200,
|
|
580
|
+
headers: {
|
|
581
|
+
"Content-Type": "application/json",
|
|
582
|
+
"Cache-Control": "no-store"
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
} catch (error) {
|
|
586
|
+
return signerHandlerErrorResponse(error);
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
var EXCHANGE_RESPONSE_ERROR2;
|
|
591
|
+
var init_api_key_exchange = __esm({
|
|
592
|
+
"src/signer/api-key-exchange.ts"() {
|
|
593
|
+
init_string_utils();
|
|
594
|
+
init_errors();
|
|
595
|
+
init_fetch_json();
|
|
596
|
+
init_handler_errors();
|
|
597
|
+
init_device_exchange();
|
|
598
|
+
EXCHANGE_RESPONSE_ERROR2 = "invalid_exchange_response";
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
// src/client.ts
|
|
603
|
+
init_encoding();
|
|
604
|
+
init_discovery();
|
|
605
|
+
init_errors();
|
|
606
|
+
function parseAppManifestResponse(json) {
|
|
607
|
+
if (!json || typeof json !== "object" || Array.isArray(json)) {
|
|
608
|
+
return { capabilities: [], excludedCapabilities: [] };
|
|
609
|
+
}
|
|
610
|
+
const record = json;
|
|
611
|
+
const capabilities = parseCapabilityArray(record.capabilities);
|
|
612
|
+
const excludedCapabilities = parseCapabilityArray(record.excludedCapabilities);
|
|
613
|
+
const manifestVersion = typeof record.manifestVersion === "string" && record.manifestVersion.trim() ? record.manifestVersion.trim() : void 0;
|
|
614
|
+
return { capabilities, excludedCapabilities, manifestVersion };
|
|
615
|
+
}
|
|
616
|
+
function parseCapabilityArray(raw) {
|
|
617
|
+
if (!Array.isArray(raw)) return [];
|
|
618
|
+
return raw.filter(
|
|
619
|
+
(c) => !!c && typeof c === "object" && typeof c.pipeline === "string" && typeof c.modelId === "string"
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/client.ts
|
|
624
|
+
init_string_utils();
|
|
625
|
+
|
|
626
|
+
// src/tokens.ts
|
|
627
|
+
var SIGNER_SESSION_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
628
|
+
var SIGNER_SESSION_EXPIRES_IN_SEC = Math.floor(SIGNER_SESSION_TTL_MS / 1e3);
|
|
629
|
+
var SIGN_JOB_SCOPE = "sign:job";
|
|
630
|
+
function isLikelyOidcJwt(rawToken) {
|
|
631
|
+
const t = rawToken.trim();
|
|
632
|
+
return t.startsWith("eyJ") && t.split(".").length >= 3;
|
|
633
|
+
}
|
|
634
|
+
function parseSignerSessionExchange(res) {
|
|
635
|
+
const accessToken = typeof res.access_token === "string" ? res.access_token.trim() : "";
|
|
636
|
+
if (!accessToken) {
|
|
637
|
+
throw new Error("PymtHouse signer session exchange returned no access_token");
|
|
638
|
+
}
|
|
639
|
+
if (isLikelyOidcJwt(accessToken)) {
|
|
640
|
+
throw new Error(
|
|
641
|
+
"PymtHouse signer session exchange returned a JWT; expected opaque signer session token"
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
const tokenType = typeof res.token_type === "string" && res.token_type.trim() ? res.token_type.trim() : "Bearer";
|
|
645
|
+
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;
|
|
646
|
+
const scope = typeof res.scope === "string" && res.scope.trim() ? res.scope.trim() : SIGN_JOB_SCOPE;
|
|
647
|
+
return {
|
|
648
|
+
accessToken,
|
|
649
|
+
tokenType,
|
|
650
|
+
expiresIn,
|
|
651
|
+
scope
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// src/usage.ts
|
|
656
|
+
function parseSafeBigInt(value, fallback = 0n) {
|
|
657
|
+
try {
|
|
658
|
+
return BigInt(value);
|
|
659
|
+
} catch {
|
|
660
|
+
return fallback;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
function getEndUserIdsForExternalUser(usage, externalUserId) {
|
|
664
|
+
const userIds = /* @__PURE__ */ new Set();
|
|
665
|
+
for (const row of usage.byUser ?? []) {
|
|
666
|
+
if (row.externalUserId === externalUserId && row.endUserId !== "unknown") {
|
|
667
|
+
userIds.add(row.endUserId);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return [...userIds];
|
|
671
|
+
}
|
|
672
|
+
function summarizeUsageFiatForExternalUser(usageByUser, externalUserId) {
|
|
673
|
+
const rows = usageByUser.byUser ?? [];
|
|
674
|
+
let requestCount = 0;
|
|
675
|
+
let networkFeeUsdMicros = 0n;
|
|
676
|
+
let ownerChargeUsdMicros = 0n;
|
|
677
|
+
let endUserBillableUsdMicros = 0n;
|
|
678
|
+
let currency = "USD";
|
|
679
|
+
for (const row of rows) {
|
|
680
|
+
if (row.externalUserId !== externalUserId) continue;
|
|
681
|
+
requestCount += row.requestCount;
|
|
682
|
+
if (row.currency) currency = row.currency;
|
|
683
|
+
if (row.networkFeeUsdMicros) {
|
|
684
|
+
networkFeeUsdMicros += BigInt(row.networkFeeUsdMicros);
|
|
685
|
+
}
|
|
686
|
+
if (row.ownerChargeUsdMicros) {
|
|
687
|
+
ownerChargeUsdMicros += BigInt(row.ownerChargeUsdMicros);
|
|
688
|
+
}
|
|
689
|
+
if (row.endUserBillableUsdMicros) {
|
|
690
|
+
endUserBillableUsdMicros += BigInt(row.endUserBillableUsdMicros);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
return {
|
|
694
|
+
externalUserId,
|
|
695
|
+
requestCount,
|
|
696
|
+
currency,
|
|
697
|
+
networkFeeUsdMicros: networkFeeUsdMicros.toString(),
|
|
698
|
+
ownerChargeUsdMicros: ownerChargeUsdMicros.toString(),
|
|
699
|
+
endUserBillableUsdMicros: endUserBillableUsdMicros.toString()
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
function mergeUsageByPipelineModel(usagePipelineModels) {
|
|
703
|
+
let responses;
|
|
704
|
+
if (Array.isArray(usagePipelineModels)) {
|
|
705
|
+
responses = usagePipelineModels;
|
|
706
|
+
} else if (usagePipelineModels) {
|
|
707
|
+
responses = [usagePipelineModels];
|
|
708
|
+
} else {
|
|
709
|
+
responses = [];
|
|
710
|
+
}
|
|
711
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
712
|
+
for (const response of responses) {
|
|
713
|
+
for (const row of response.byPipelineModel ?? []) {
|
|
714
|
+
const { pipeline, modelId } = row;
|
|
715
|
+
if (!pipeline || !modelId) continue;
|
|
716
|
+
const key = JSON.stringify([pipeline, modelId]);
|
|
717
|
+
const existing = byKey.get(key);
|
|
718
|
+
const rowCurrency = row.currency ?? "USD";
|
|
719
|
+
const networkFee = row.networkFeeUsdMicros ?? "0";
|
|
720
|
+
const ownerCharge = row.ownerChargeUsdMicros ?? "0";
|
|
721
|
+
const endUserBillable = row.endUserBillableUsdMicros ?? "0";
|
|
722
|
+
if (!existing) {
|
|
723
|
+
byKey.set(key, {
|
|
724
|
+
pipeline,
|
|
725
|
+
modelId,
|
|
726
|
+
requestCount: row.requestCount,
|
|
727
|
+
currency: rowCurrency,
|
|
728
|
+
networkFeeUsdMicros: networkFee,
|
|
729
|
+
ownerChargeUsdMicros: ownerCharge,
|
|
730
|
+
endUserBillableUsdMicros: endUserBillable
|
|
731
|
+
});
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
byKey.set(key, {
|
|
735
|
+
...existing,
|
|
736
|
+
requestCount: existing.requestCount + row.requestCount,
|
|
737
|
+
networkFeeUsdMicros: (parseSafeBigInt(existing.networkFeeUsdMicros) + parseSafeBigInt(networkFee)).toString(),
|
|
738
|
+
ownerChargeUsdMicros: (parseSafeBigInt(existing.ownerChargeUsdMicros) + parseSafeBigInt(ownerCharge)).toString(),
|
|
739
|
+
endUserBillableUsdMicros: (parseSafeBigInt(existing.endUserBillableUsdMicros) + parseSafeBigInt(endUserBillable)).toString()
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
return [...byKey.values()].sort((a, b) => {
|
|
744
|
+
if (a.pipeline === b.pipeline) return a.modelId.localeCompare(b.modelId);
|
|
745
|
+
return a.pipeline.localeCompare(b.pipeline);
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
function buildMeScopeUsagePayload(usageByUser, externalUserId, usagePipelineModel, usageDaily) {
|
|
749
|
+
const summary = summarizeUsageFiatForExternalUser(usageByUser, externalUserId);
|
|
750
|
+
const pipelineModels = mergeUsageByPipelineModel(usagePipelineModel);
|
|
751
|
+
const dailyByPipeline = usageDaily?.byDailyPipeline ?? [];
|
|
752
|
+
return {
|
|
753
|
+
clientId: usageByUser.clientId,
|
|
754
|
+
period: usageByUser.period,
|
|
755
|
+
currentUser: {
|
|
756
|
+
externalUserId: summary.externalUserId,
|
|
757
|
+
requestCount: summary.requestCount,
|
|
758
|
+
currency: summary.currency,
|
|
759
|
+
networkFeeUsdMicros: summary.networkFeeUsdMicros,
|
|
760
|
+
ownerChargeUsdMicros: summary.ownerChargeUsdMicros,
|
|
761
|
+
endUserBillableUsdMicros: summary.endUserBillableUsdMicros,
|
|
762
|
+
pipelineModels,
|
|
763
|
+
dailyByPipeline
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
var DEFAULT_MAX_END_USER_IDS = 25;
|
|
768
|
+
|
|
769
|
+
// src/oauth-map.ts
|
|
770
|
+
init_errors();
|
|
124
771
|
var ACCEPTED_ISSUED_TOKEN_TYPES = /* @__PURE__ */ new Set([
|
|
125
772
|
"urn:ietf:params:oauth:token-type:access_token",
|
|
126
773
|
"urn:pmth:token-type:remote-signer-session"
|
|
@@ -213,9 +860,94 @@ function m2mClient(clientId) {
|
|
|
213
860
|
return { client_id: clientId };
|
|
214
861
|
}
|
|
215
862
|
|
|
863
|
+
// src/ingest.ts
|
|
864
|
+
init_encoding();
|
|
865
|
+
init_errors();
|
|
866
|
+
init_string_utils();
|
|
867
|
+
function ingestUrl(issuerUrl, publicClientId) {
|
|
868
|
+
const origin = new URL(stripTrailingSlashes(issuerUrl)).origin;
|
|
869
|
+
return `${origin}/api/v1/apps/${encodeURIComponent(publicClientId)}/usage/signed-tickets`;
|
|
870
|
+
}
|
|
871
|
+
async function readJsonResponse(response) {
|
|
872
|
+
const text = await response.text();
|
|
873
|
+
if (!text.trim()) {
|
|
874
|
+
return {};
|
|
875
|
+
}
|
|
876
|
+
try {
|
|
877
|
+
const parsed = JSON.parse(text);
|
|
878
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
|
|
879
|
+
} catch {
|
|
880
|
+
return {};
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
async function ingestSignedTicket(options) {
|
|
884
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
885
|
+
const url = ingestUrl(options.issuerUrl, options.publicClientId);
|
|
886
|
+
const response = await fetchImpl(url, {
|
|
887
|
+
method: "POST",
|
|
888
|
+
headers: {
|
|
889
|
+
Authorization: encodeClientSecretBasic(options.m2mClientId, options.m2mClientSecret),
|
|
890
|
+
"Content-Type": "application/json",
|
|
891
|
+
Accept: "application/json"
|
|
892
|
+
},
|
|
893
|
+
body: JSON.stringify(options.ticket),
|
|
894
|
+
cache: "no-store"
|
|
895
|
+
});
|
|
896
|
+
const body = await readJsonResponse(response);
|
|
897
|
+
if (!response.ok) {
|
|
898
|
+
const message = typeof body.error === "string" ? body.error : `Signed-ticket ingest failed (${response.status})`;
|
|
899
|
+
throw new PmtHouseError(message, {
|
|
900
|
+
status: response.status,
|
|
901
|
+
code: "ingest_failed",
|
|
902
|
+
details: body
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
return {
|
|
906
|
+
ingested: Boolean(body.ingested),
|
|
907
|
+
duplicate: Boolean(body.duplicate),
|
|
908
|
+
source: body.source === "openmeter" ? "openmeter" : "disabled"
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
async function ingestSignedTicketsBatch(options) {
|
|
912
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
913
|
+
const url = ingestUrl(options.issuerUrl, options.publicClientId);
|
|
914
|
+
const response = await fetchImpl(url, {
|
|
915
|
+
method: "POST",
|
|
916
|
+
headers: {
|
|
917
|
+
Authorization: encodeClientSecretBasic(options.m2mClientId, options.m2mClientSecret),
|
|
918
|
+
"Content-Type": "application/json",
|
|
919
|
+
Accept: "application/json"
|
|
920
|
+
},
|
|
921
|
+
body: JSON.stringify({ tickets: options.tickets }),
|
|
922
|
+
cache: "no-store"
|
|
923
|
+
});
|
|
924
|
+
const body = await readJsonResponse(response);
|
|
925
|
+
if (!response.ok) {
|
|
926
|
+
const message = typeof body.error === "string" ? body.error : `Signed-ticket batch ingest failed (${response.status})`;
|
|
927
|
+
throw new PmtHouseError(message, {
|
|
928
|
+
status: response.status,
|
|
929
|
+
code: "ingest_failed",
|
|
930
|
+
details: body
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
const rawResults = Array.isArray(body.results) ? body.results : [];
|
|
934
|
+
return {
|
|
935
|
+
results: rawResults.map((entry) => {
|
|
936
|
+
const row = entry ?? {};
|
|
937
|
+
return {
|
|
938
|
+
requestId: typeof row.requestId === "string" ? row.requestId : void 0,
|
|
939
|
+
ok: row.ok === true,
|
|
940
|
+
ingested: Boolean(row.ingested),
|
|
941
|
+
duplicate: Boolean(row.duplicate),
|
|
942
|
+
source: row.source === "openmeter" ? "openmeter" : "disabled"
|
|
943
|
+
};
|
|
944
|
+
})
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
|
|
216
948
|
// src/client.ts
|
|
217
|
-
var
|
|
218
|
-
var
|
|
949
|
+
var TOKEN_EXCHANGE_GRANT2 = "urn:ietf:params:oauth:grant-type:token-exchange";
|
|
950
|
+
var SUBJECT_ACCESS_TOKEN_TYPE2 = "urn:ietf:params:oauth:token-type:access_token";
|
|
219
951
|
var REQUESTED_ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
|
|
220
952
|
var DEVICE_RESOURCE_PREFIX = "urn:pmth:device_code:";
|
|
221
953
|
function normalizeUserCode(value) {
|
|
@@ -342,6 +1074,50 @@ var PmtHouseClient = class {
|
|
|
342
1074
|
cache: "no-store"
|
|
343
1075
|
});
|
|
344
1076
|
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Exchange a long-lived dashboard API key (`pmth_*`) for a short-lived user JWT.
|
|
1079
|
+
*/
|
|
1080
|
+
async exchangeApiKeyForUserAccessToken(input) {
|
|
1081
|
+
const url = `${this.getAppsBaseUrl()}/auth/api-key/token`;
|
|
1082
|
+
return this.requestJson(url, {
|
|
1083
|
+
method: "POST",
|
|
1084
|
+
headers: {
|
|
1085
|
+
Authorization: `Bearer ${input.apiKey.trim()}`,
|
|
1086
|
+
"Content-Type": "application/json",
|
|
1087
|
+
Accept: "application/json"
|
|
1088
|
+
},
|
|
1089
|
+
body: JSON.stringify(input.scope ? { scope: input.scope } : {}),
|
|
1090
|
+
cache: "no-store"
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Exchange a dashboard API key for a signer session via a trusted facade (recommended)
|
|
1095
|
+
* or directly when M2M credentials are available on this client.
|
|
1096
|
+
*/
|
|
1097
|
+
async exchangeApiKeyForSignerSession(input) {
|
|
1098
|
+
if (input.facadeUrl?.trim()) {
|
|
1099
|
+
const { exchangeApiKeyForSigner: exchangeApiKeyForSigner2 } = await Promise.resolve().then(() => (init_api_key_exchange(), api_key_exchange_exports));
|
|
1100
|
+
const exchanged = await exchangeApiKeyForSigner2({
|
|
1101
|
+
facadeUrl: input.facadeUrl.trim(),
|
|
1102
|
+
apiKey: input.apiKey,
|
|
1103
|
+
scope: input.scope,
|
|
1104
|
+
clientId: this.publicClientId,
|
|
1105
|
+
fetch: this.fetchImpl
|
|
1106
|
+
});
|
|
1107
|
+
return {
|
|
1108
|
+
access_token: exchanged.access_token,
|
|
1109
|
+
token_type: exchanged.token_type,
|
|
1110
|
+
expires_in: exchanged.expires_in,
|
|
1111
|
+
scope: exchanged.scope,
|
|
1112
|
+
issued_token_type: "urn:ietf:params:oauth:token-type:access_token"
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
const userToken = await this.exchangeApiKeyForUserAccessToken({
|
|
1116
|
+
apiKey: input.apiKey,
|
|
1117
|
+
scope: input.scope
|
|
1118
|
+
});
|
|
1119
|
+
return this.exchangeForSignerSession({ userJwt: userToken.access_token });
|
|
1120
|
+
}
|
|
345
1121
|
async completeDeviceApproval(input) {
|
|
346
1122
|
const as = await loadAuthorizationServer(this.issuerUrl, this.fetchImpl, {
|
|
347
1123
|
allowInsecureHttp: this.allowInsecureHttp
|
|
@@ -350,14 +1126,14 @@ var PmtHouseClient = class {
|
|
|
350
1126
|
const clientAuth = this.m2mClientAuth();
|
|
351
1127
|
const params = new URLSearchParams();
|
|
352
1128
|
params.set("subject_token", input.userJwt);
|
|
353
|
-
params.set("subject_token_type",
|
|
1129
|
+
params.set("subject_token_type", SUBJECT_ACCESS_TOKEN_TYPE2);
|
|
354
1130
|
params.set("resource", buildDeviceCodeResource(input.userCode));
|
|
355
1131
|
try {
|
|
356
1132
|
const response = await genericTokenEndpointRequest(
|
|
357
1133
|
as,
|
|
358
1134
|
client,
|
|
359
1135
|
clientAuth,
|
|
360
|
-
|
|
1136
|
+
TOKEN_EXCHANGE_GRANT2,
|
|
361
1137
|
params,
|
|
362
1138
|
this.tokenEndpointFetchOptions()
|
|
363
1139
|
);
|
|
@@ -405,7 +1181,7 @@ var PmtHouseClient = class {
|
|
|
405
1181
|
const clientAuth = this.m2mClientAuth();
|
|
406
1182
|
const params = new URLSearchParams();
|
|
407
1183
|
params.set("subject_token", input.userJwt);
|
|
408
|
-
params.set("subject_token_type",
|
|
1184
|
+
params.set("subject_token_type", SUBJECT_ACCESS_TOKEN_TYPE2);
|
|
409
1185
|
params.set("requested_token_type", REQUESTED_ACCESS_TOKEN_TYPE);
|
|
410
1186
|
const resourceCandidate = typeof input.resource === "string" && input.resource.trim() !== "" ? input.resource.trim() : this.issuerUrl;
|
|
411
1187
|
params.set("resource", stripTrailingSlashes(resourceCandidate));
|
|
@@ -414,7 +1190,7 @@ var PmtHouseClient = class {
|
|
|
414
1190
|
as,
|
|
415
1191
|
client,
|
|
416
1192
|
clientAuth,
|
|
417
|
-
|
|
1193
|
+
TOKEN_EXCHANGE_GRANT2,
|
|
418
1194
|
params,
|
|
419
1195
|
this.tokenEndpointFetchOptions()
|
|
420
1196
|
);
|
|
@@ -470,12 +1246,267 @@ var PmtHouseClient = class {
|
|
|
470
1246
|
if (input.groupBy) url.searchParams.set("groupBy", input.groupBy);
|
|
471
1247
|
if (input.userId) url.searchParams.set("userId", input.userId);
|
|
472
1248
|
if (input.gatewayRequestId) url.searchParams.set("gatewayRequestId", input.gatewayRequestId);
|
|
1249
|
+
if (input.includeRetail) url.searchParams.set("include", "retail");
|
|
1250
|
+
return this.requestJson(url.toString(), {
|
|
1251
|
+
method: "GET",
|
|
1252
|
+
headers: this.builderHeaders(),
|
|
1253
|
+
cache: "no-store"
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Session-scoped usage for one `externalUserId`: user rollup plus merged pipeline/model breakdown.
|
|
1258
|
+
*/
|
|
1259
|
+
async ingestSignedTicket(ticket) {
|
|
1260
|
+
return ingestSignedTicket({
|
|
1261
|
+
issuerUrl: this.issuerUrl,
|
|
1262
|
+
publicClientId: this.publicClientId,
|
|
1263
|
+
m2mClientId: this.m2mClientId,
|
|
1264
|
+
m2mClientSecret: this.m2mClientSecret,
|
|
1265
|
+
ticket,
|
|
1266
|
+
fetch: this.fetchImpl
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
async ingestSignedTickets(tickets) {
|
|
1270
|
+
return ingestSignedTicketsBatch({
|
|
1271
|
+
issuerUrl: this.issuerUrl,
|
|
1272
|
+
publicClientId: this.publicClientId,
|
|
1273
|
+
m2mClientId: this.m2mClientId,
|
|
1274
|
+
m2mClientSecret: this.m2mClientSecret,
|
|
1275
|
+
tickets,
|
|
1276
|
+
fetch: this.fetchImpl
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
async getSignerRouting() {
|
|
1280
|
+
return this.requestJson(
|
|
1281
|
+
`${this.getAppsBaseUrl()}/signer/routing`,
|
|
1282
|
+
{
|
|
1283
|
+
method: "GET",
|
|
1284
|
+
headers: this.builderHeaders(),
|
|
1285
|
+
cache: "no-store"
|
|
1286
|
+
}
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
async listBillingProducts() {
|
|
1290
|
+
const url = `${this.getAppsBaseUrl()}/plans?apiVersion=2`;
|
|
1291
|
+
const body = await this.requestJson(
|
|
1292
|
+
url,
|
|
1293
|
+
{
|
|
1294
|
+
method: "GET",
|
|
1295
|
+
headers: this.builderHeaders(),
|
|
1296
|
+
cache: "no-store"
|
|
1297
|
+
}
|
|
1298
|
+
);
|
|
1299
|
+
return {
|
|
1300
|
+
apiVersion: body.apiVersion ?? 2,
|
|
1301
|
+
products: body.products ?? body.plans ?? []
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
async syncBillingProduct(planId) {
|
|
1305
|
+
return this.requestJson(
|
|
1306
|
+
`${this.getAppsBaseUrl()}/plans/${encodeURIComponent(planId)}/sync`,
|
|
1307
|
+
{
|
|
1308
|
+
method: "POST",
|
|
1309
|
+
headers: this.builderHeaders(),
|
|
1310
|
+
cache: "no-store"
|
|
1311
|
+
}
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
async getUsageBalance(externalUserId) {
|
|
1315
|
+
const url = new URL(`${this.getAppsBaseUrl()}/usage/balance`);
|
|
1316
|
+
url.searchParams.set("externalUserId", externalUserId);
|
|
473
1317
|
return this.requestJson(url.toString(), {
|
|
474
1318
|
method: "GET",
|
|
475
1319
|
headers: this.builderHeaders(),
|
|
476
1320
|
cache: "no-store"
|
|
477
1321
|
});
|
|
478
1322
|
}
|
|
1323
|
+
async getUserAllowances(externalUserId) {
|
|
1324
|
+
return this.requestJson(
|
|
1325
|
+
`${this.getAppsBaseUrl()}/users/${encodeURIComponent(externalUserId)}/allowances`,
|
|
1326
|
+
{
|
|
1327
|
+
method: "GET",
|
|
1328
|
+
headers: this.builderHeaders(),
|
|
1329
|
+
cache: "no-store"
|
|
1330
|
+
}
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
async grantUserAllowance(externalUserId, input) {
|
|
1334
|
+
return this.requestJson(
|
|
1335
|
+
`${this.getAppsBaseUrl()}/users/${encodeURIComponent(externalUserId)}/allowances`,
|
|
1336
|
+
{
|
|
1337
|
+
method: "POST",
|
|
1338
|
+
headers: this.builderHeaders(),
|
|
1339
|
+
body: JSON.stringify(input),
|
|
1340
|
+
cache: "no-store"
|
|
1341
|
+
}
|
|
1342
|
+
);
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* @deprecated Removed from PymtHouse — use {@link getUsageBalance} or {@link getUserAllowances}.
|
|
1346
|
+
*/
|
|
1347
|
+
async getUserCredits(externalUserId) {
|
|
1348
|
+
return this.getUsageBalance(externalUserId);
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* @deprecated Removed from PymtHouse — use {@link grantUserAllowance} (`POST .../allowances`).
|
|
1352
|
+
*/
|
|
1353
|
+
async grantUserCredits(externalUserId, input) {
|
|
1354
|
+
const result = await this.grantUserAllowance(externalUserId, {
|
|
1355
|
+
amountUsdMicros: input.amountUsdMicros,
|
|
1356
|
+
source: input.source ?? "manual",
|
|
1357
|
+
featureKey: input.featureKey
|
|
1358
|
+
});
|
|
1359
|
+
const flat = result;
|
|
1360
|
+
const nested = result.allowances;
|
|
1361
|
+
return {
|
|
1362
|
+
externalUserId: result.externalUserId,
|
|
1363
|
+
balanceUsdMicros: flat.balanceUsdMicros ?? nested?.balanceUsdMicros ?? "0",
|
|
1364
|
+
consumedUsdMicros: flat.consumedUsdMicros ?? nested?.consumedUsdMicros ?? "0",
|
|
1365
|
+
lifetimeGrantedUsdMicros: flat.lifetimeGrantedUsdMicros ?? nested?.lifetimeGrantedUsdMicros ?? "0",
|
|
1366
|
+
hasAccess: flat.hasAccess ?? nested?.hasAccess ?? false,
|
|
1367
|
+
remainingUsdMicros: flat.balanceUsdMicros ?? nested?.balanceUsdMicros,
|
|
1368
|
+
grantedUsdMicros: flat.grantedUsdMicros,
|
|
1369
|
+
featureKey: flat.featureKey
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
async getUserSubscription(externalUserId) {
|
|
1373
|
+
return this.requestJson(
|
|
1374
|
+
`${this.getAppsBaseUrl()}/users/${encodeURIComponent(externalUserId)}/subscription`,
|
|
1375
|
+
{
|
|
1376
|
+
method: "GET",
|
|
1377
|
+
headers: this.builderHeaders(),
|
|
1378
|
+
cache: "no-store"
|
|
1379
|
+
}
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
async fetchUsageForExternalUser(input) {
|
|
1383
|
+
const usageByUser = await this.getUsage({
|
|
1384
|
+
startDate: input.startDate,
|
|
1385
|
+
endDate: input.endDate,
|
|
1386
|
+
groupBy: "user"
|
|
1387
|
+
});
|
|
1388
|
+
const userIds = getEndUserIdsForExternalUser(usageByUser, input.externalUserId);
|
|
1389
|
+
const cap = input.maxEndUserIds ?? DEFAULT_MAX_END_USER_IDS;
|
|
1390
|
+
const cappedUserIds = userIds.slice(0, cap);
|
|
1391
|
+
const usagePipelineModels = await Promise.all(
|
|
1392
|
+
cappedUserIds.map(
|
|
1393
|
+
(userId) => this.getUsage({
|
|
1394
|
+
startDate: input.startDate,
|
|
1395
|
+
endDate: input.endDate,
|
|
1396
|
+
groupBy: "pipeline_model",
|
|
1397
|
+
userId
|
|
1398
|
+
})
|
|
1399
|
+
)
|
|
1400
|
+
);
|
|
1401
|
+
const usageDaily = await this.getUsage({
|
|
1402
|
+
startDate: input.startDate,
|
|
1403
|
+
endDate: input.endDate,
|
|
1404
|
+
groupBy: "daily_pipeline",
|
|
1405
|
+
userId: input.externalUserId
|
|
1406
|
+
});
|
|
1407
|
+
return buildMeScopeUsagePayload(
|
|
1408
|
+
usageByUser,
|
|
1409
|
+
input.externalUserId,
|
|
1410
|
+
usagePipelineModels,
|
|
1411
|
+
usageDaily
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
async getAppManifest(opts) {
|
|
1415
|
+
const url = `${this.getAppsBaseUrl()}/manifest`;
|
|
1416
|
+
const headers = {
|
|
1417
|
+
...this.builderHeadersRecord()
|
|
1418
|
+
};
|
|
1419
|
+
if (opts?.ifNoneMatch) {
|
|
1420
|
+
headers["If-None-Match"] = opts.ifNoneMatch;
|
|
1421
|
+
}
|
|
1422
|
+
this.logger?.debug?.("PmtHouse request", { method: "GET", url });
|
|
1423
|
+
const response = await this.fetchImpl(url, {
|
|
1424
|
+
method: "GET",
|
|
1425
|
+
headers,
|
|
1426
|
+
signal: opts?.signal,
|
|
1427
|
+
cache: "no-store"
|
|
1428
|
+
});
|
|
1429
|
+
const etag = response.headers.get("etag")?.trim() ?? null;
|
|
1430
|
+
if (response.status === 304) {
|
|
1431
|
+
return {
|
|
1432
|
+
manifest: null,
|
|
1433
|
+
etag: etag ?? opts?.ifNoneMatch ?? null,
|
|
1434
|
+
notModified: true
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
const raw = await response.text();
|
|
1438
|
+
const ct = response.headers.get("content-type") ?? "";
|
|
1439
|
+
const looksJson = ct.includes("application/json") || ct.includes("json");
|
|
1440
|
+
const parsed = raw && looksJson ? this.safeParseJson(raw) : null;
|
|
1441
|
+
if (!response.ok) {
|
|
1442
|
+
const details = parsed ?? {};
|
|
1443
|
+
let description;
|
|
1444
|
+
if (typeof details.error_description === "string") {
|
|
1445
|
+
description = details.error_description;
|
|
1446
|
+
} else if (typeof details.error === "string") {
|
|
1447
|
+
description = details.error;
|
|
1448
|
+
} else {
|
|
1449
|
+
description = `Request failed (${response.status})`;
|
|
1450
|
+
}
|
|
1451
|
+
throw new PmtHouseError(description, {
|
|
1452
|
+
status: response.status,
|
|
1453
|
+
code: typeof details.error === "string" ? details.error : "pymthouse_http_error",
|
|
1454
|
+
details
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
if (!looksJson || parsed === null) {
|
|
1458
|
+
throw new PmtHouseError("Expected JSON response from Builder manifest endpoint", {
|
|
1459
|
+
status: 502,
|
|
1460
|
+
code: "invalid_response",
|
|
1461
|
+
details: { contentType: ct, preview: raw.slice(0, 200) }
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
return {
|
|
1465
|
+
manifest: parseAppManifestResponse(parsed),
|
|
1466
|
+
etag,
|
|
1467
|
+
notModified: false
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Upsert an external user, mint a short-lived JWT, and exchange for an opaque signer session.
|
|
1472
|
+
*/
|
|
1473
|
+
async mintSignerSessionForExternalUser(input) {
|
|
1474
|
+
await this.upsertAppUser({
|
|
1475
|
+
externalUserId: input.externalUserId,
|
|
1476
|
+
email: input.email,
|
|
1477
|
+
status: "active"
|
|
1478
|
+
});
|
|
1479
|
+
const exchange = await this.mintUserSignerSessionToken({
|
|
1480
|
+
externalUserId: input.externalUserId,
|
|
1481
|
+
scope: input.scope ?? SIGN_JOB_SCOPE,
|
|
1482
|
+
resource: this.issuerUrl
|
|
1483
|
+
});
|
|
1484
|
+
return parseSignerSessionExchange(exchange);
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* Approve a pending RFC 8628 device code for an external user (Option B).
|
|
1488
|
+
*/
|
|
1489
|
+
async approveDeviceLogin(input) {
|
|
1490
|
+
if (input.publicClientId && input.publicClientId !== this.publicClientId) {
|
|
1491
|
+
throw new PmtHouseError(
|
|
1492
|
+
"publicClientId does not match configured public client id",
|
|
1493
|
+
{ status: 400, code: "invalid_client" }
|
|
1494
|
+
);
|
|
1495
|
+
}
|
|
1496
|
+
await this.upsertAppUser({
|
|
1497
|
+
externalUserId: input.externalUserId,
|
|
1498
|
+
email: input.email,
|
|
1499
|
+
status: "active"
|
|
1500
|
+
});
|
|
1501
|
+
const userToken = await this.mintUserAccessToken({
|
|
1502
|
+
externalUserId: input.externalUserId,
|
|
1503
|
+
scope: SIGN_JOB_SCOPE
|
|
1504
|
+
});
|
|
1505
|
+
await this.completeDeviceApproval({
|
|
1506
|
+
userJwt: userToken.access_token,
|
|
1507
|
+
userCode: input.userCode
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
479
1510
|
tokenEndpointFetchOptions() {
|
|
480
1511
|
const o = {
|
|
481
1512
|
[customFetch]: this.fetchImpl
|
|
@@ -492,6 +1523,9 @@ var PmtHouseClient = class {
|
|
|
492
1523
|
return new URL(this.issuerUrl).origin;
|
|
493
1524
|
}
|
|
494
1525
|
builderHeaders() {
|
|
1526
|
+
return this.builderHeadersRecord();
|
|
1527
|
+
}
|
|
1528
|
+
builderHeadersRecord() {
|
|
495
1529
|
return {
|
|
496
1530
|
Authorization: encodeClientSecretBasic(this.m2mClientId, this.m2mClientSecret),
|
|
497
1531
|
"Content-Type": "application/json",
|
|
@@ -515,7 +1549,14 @@ var PmtHouseClient = class {
|
|
|
515
1549
|
const parsed = raw && looksJson ? this.safeParseJson(raw) : raw ? null : null;
|
|
516
1550
|
if (!response.ok) {
|
|
517
1551
|
const details = parsed ?? {};
|
|
518
|
-
|
|
1552
|
+
let description;
|
|
1553
|
+
if (typeof details.error_description === "string") {
|
|
1554
|
+
description = details.error_description;
|
|
1555
|
+
} else if (typeof details.error === "string") {
|
|
1556
|
+
description = details.error;
|
|
1557
|
+
} else {
|
|
1558
|
+
description = `Request failed (${response.status})`;
|
|
1559
|
+
}
|
|
519
1560
|
throw new PmtHouseError(description, {
|
|
520
1561
|
status: response.status,
|
|
521
1562
|
code: typeof details.error === "string" ? details.error : "pymthouse_http_error",
|
|
@@ -559,6 +1600,8 @@ var PmtHouseClient = class {
|
|
|
559
1600
|
};
|
|
560
1601
|
|
|
561
1602
|
// src/env.ts
|
|
1603
|
+
init_errors();
|
|
1604
|
+
init_string_utils();
|
|
562
1605
|
function assertEnvModuleServerOnly() {
|
|
563
1606
|
if (typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined") {
|
|
564
1607
|
throw new Error(
|