@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.
Files changed (85) hide show
  1. package/README.md +106 -2
  2. package/dist/client-BHfjDvIe.d.ts +129 -0
  3. package/dist/client-CvhJEhjV.d.cts +129 -0
  4. package/dist/config.cjs +122 -0
  5. package/dist/config.cjs.map +1 -0
  6. package/dist/config.d.cts +29 -0
  7. package/dist/config.d.ts +29 -0
  8. package/dist/config.js +111 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/device-initiate.cjs +88 -0
  11. package/dist/device-initiate.cjs.map +1 -0
  12. package/dist/device-initiate.d.cts +32 -0
  13. package/dist/device-initiate.d.ts +32 -0
  14. package/dist/device-initiate.js +84 -0
  15. package/dist/device-initiate.js.map +1 -0
  16. package/dist/device.cjs +1 -1
  17. package/dist/device.cjs.map +1 -1
  18. package/dist/device.d.cts +1 -1
  19. package/dist/device.d.ts +1 -1
  20. package/dist/device.js +1 -1
  21. package/dist/device.js.map +1 -1
  22. package/dist/env.cjs +1071 -28
  23. package/dist/env.cjs.map +1 -1
  24. package/dist/env.d.cts +15 -2
  25. package/dist/env.d.ts +15 -2
  26. package/dist/env.js +1071 -28
  27. package/dist/env.js.map +1 -1
  28. package/dist/gateway/client/index.cjs +492 -0
  29. package/dist/gateway/client/index.cjs.map +1 -0
  30. package/dist/gateway/client/index.d.cts +63 -0
  31. package/dist/gateway/client/index.d.ts +63 -0
  32. package/dist/gateway/client/index.js +489 -0
  33. package/dist/gateway/client/index.js.map +1 -0
  34. package/dist/gateway/index.cjs +16 -0
  35. package/dist/gateway/index.cjs.map +1 -0
  36. package/dist/gateway/index.d.cts +52 -0
  37. package/dist/gateway/index.d.ts +52 -0
  38. package/dist/gateway/index.js +10 -0
  39. package/dist/gateway/index.js.map +1 -0
  40. package/dist/gateway/server/index.cjs +1248 -0
  41. package/dist/gateway/server/index.cjs.map +1 -0
  42. package/dist/gateway/server/index.d.cts +31 -0
  43. package/dist/gateway/server/index.d.ts +31 -0
  44. package/dist/gateway/server/index.js +1233 -0
  45. package/dist/gateway/server/index.js.map +1 -0
  46. package/dist/index.cjs +1401 -137
  47. package/dist/index.cjs.map +1 -1
  48. package/dist/index.d.cts +41 -5
  49. package/dist/index.d.ts +41 -5
  50. package/dist/index.js +1334 -105
  51. package/dist/index.js.map +1 -1
  52. package/dist/ingest-B3Yi8Tb1.d.cts +271 -0
  53. package/dist/ingest-DoKJTWU9.d.ts +271 -0
  54. package/dist/plan-pricing.cjs +108 -0
  55. package/dist/plan-pricing.cjs.map +1 -0
  56. package/dist/plan-pricing.d.cts +15 -0
  57. package/dist/plan-pricing.d.ts +15 -0
  58. package/dist/plan-pricing.js +98 -0
  59. package/dist/plan-pricing.js.map +1 -0
  60. package/dist/signer/server.cjs +1366 -0
  61. package/dist/signer/server.cjs.map +1 -0
  62. package/dist/signer/server.d.cts +73 -0
  63. package/dist/signer/server.d.ts +73 -0
  64. package/dist/signer/server.js +1331 -0
  65. package/dist/signer/server.js.map +1 -0
  66. package/dist/tokens.cjs +75 -0
  67. package/dist/tokens.cjs.map +1 -0
  68. package/dist/tokens.d.cts +48 -0
  69. package/dist/tokens.d.ts +48 -0
  70. package/dist/tokens.js +64 -0
  71. package/dist/tokens.js.map +1 -0
  72. package/dist/types-_R1AwEZp.d.cts +343 -0
  73. package/dist/types-_R1AwEZp.d.ts +343 -0
  74. package/dist/verify.cjs +1 -1
  75. package/dist/verify.cjs.map +1 -1
  76. package/dist/verify.d.cts +1 -1
  77. package/dist/verify.d.ts +1 -1
  78. package/dist/verify.js +1 -1
  79. package/dist/verify.js.map +1 -1
  80. package/gateway/proto/lp_rpc.proto +542 -0
  81. package/package.json +57 -1
  82. package/dist/env-4YmzarGJ.d.ts +0 -68
  83. package/dist/env-CZczUMzR.d.cts +0 -68
  84. package/dist/types-W9PJAspR.d.cts +0 -136
  85. package/dist/types-W9PJAspR.d.ts +0 -136
package/README.md CHANGED
@@ -12,6 +12,8 @@ OAuth/OIDC protocol calls use **[oauth4webapi](https://github.com/panva/oauth4we
12
12
  pnpm add @pymthouse/builder-sdk
13
13
  ```
14
14
 
15
+ Maintainers: see [docs/RELEASING.md](docs/RELEASING.md) for trusted publishing and re-running failed releases.
16
+
15
17
  ## Quick start
16
18
 
17
19
  ```ts
@@ -66,14 +68,94 @@ const signerSession = await client.mintUserSignerSessionToken({
66
68
  For advanced flows that already have a user JWT, call
67
69
  `exchangeForSignerSession({ userJwt })` directly.
68
70
 
71
+ ### Dashboard API keys (long-lived `pmth_*`)
72
+
73
+ Create a key in the Dashboard **API keys** page, then exchange it for a signer
74
+ session without repeating device login:
75
+
76
+ ```ts
77
+ const session = await client.exchangeApiKeyForSignerSession({
78
+ apiKey: process.env.PMTH_API_KEY!,
79
+ facadeUrl: process.env.DASHBOARD_ORIGIN!, // e.g. https://dashboard.example.com
80
+ scope: "sign:job",
81
+ });
82
+ // session.access_token — opaque signer bearer for discovery / gateway
83
+ ```
84
+
85
+ See `examples/stream-with-api-key.mjs` for a minimal Node script.
86
+
87
+ ## Browser gateway (optional module)
88
+
89
+ Live Video-to-Video streaming from the browser uses a **same-origin HTTP segment relay**
90
+ implemented in optional subpaths (not exported from the main entry):
91
+
92
+ | Subpath | Use |
93
+ |---------|-----|
94
+ | `@pymthouse/builder-sdk/gateway` | Shared types |
95
+ | `@pymthouse/builder-sdk/gateway/client` | `BrowserGatewayClient` for dashboard / browser apps |
96
+ | `@pymthouse/builder-sdk/gateway/server` | Route Handler factories for Next.js |
97
+
98
+ Install peer dependencies when using the server module:
99
+
100
+ ```bash
101
+ pnpm add @grpc/grpc-js @grpc/proto-loader
102
+ ```
103
+
104
+ Auth flow (same signer bearer as Python `livepeer-python-gateway`):
105
+
106
+ 1. `exchangeApiKeyForSignerSession({ apiKey, facadeUrl: dashboardOrigin })` or `POST /api/pymthouse/keys/exchange`
107
+ 2. `Authorization: Bearer <signer_token>` on `POST /api/gateway/sessions`
108
+
109
+ Enable relay on the dashboard with `GATEWAY_ENABLED=1` and `NEXT_PUBLIC_GATEWAY_ENABLED=1`.
110
+
111
+ See `examples/gateway-session-smoke.mjs` for a headless session start test.
112
+
113
+ Integrators can use the higher-level workflow helpers:
114
+
115
+ ```ts
116
+ const session = await client.mintSignerSessionForExternalUser({
117
+ externalUserId: "naap-user-123",
118
+ email: "user@example.com",
119
+ });
120
+ // session.accessToken is opaque pmth_…
121
+
122
+ await client.approveDeviceLogin({
123
+ externalUserId: "naap-user-123",
124
+ userCode: "ABCD-EFGH",
125
+ publicClientId: process.env.PYMTHOUSE_PUBLIC_CLIENT_ID,
126
+ });
127
+ ```
128
+
129
+ ## Usage API: session-scoped `scope=me` BFF helper
130
+
131
+ ```ts
132
+ const payload = await client.fetchUsageForExternalUser({
133
+ externalUserId: "naap-user-123",
134
+ startDate,
135
+ endDate,
136
+ });
137
+ // payload.currentUser includes fiat totals + merged pipelineModels
138
+ ```
139
+
140
+ ## App manifest
141
+
142
+ ```ts
143
+ const { manifest, etag, notModified } = await client.getAppManifest({
144
+ ifNoneMatch: cachedEtag ?? undefined,
145
+ });
146
+ ```
147
+
69
148
  ## Subpath exports
70
149
 
71
150
  | Import | Purpose |
72
151
  |--------|---------|
73
- | `@pymthouse/builder-sdk` | `PmtHouseClient`, discovery cache, errors, usage aggregation helpers |
152
+ | `@pymthouse/builder-sdk` | `PmtHouseClient`, usage helpers, manifest parsers, token helpers |
153
+ | `@pymthouse/builder-sdk/config` | `isPymthouseConfigured`, `readPymthouseEnv` (Edge/middleware-safe) |
154
+ | `@pymthouse/builder-sdk/tokens` | Signer session TTL, JWT shape helpers, `parseSignerSessionExchange` |
74
155
  | `@pymthouse/builder-sdk/format` | Wei formatting for Usage API |
75
- | `@pymthouse/builder-sdk/env` | `createPmtHouseClientFromEnv`, `getPymthouseBaseUrl` |
156
+ | `@pymthouse/builder-sdk/env` | `createPmtHouseClientFromEnv`, `getPymthouseBaseUrl` (server-only) |
76
157
  | `@pymthouse/builder-sdk/device` | RFC 8628 `pollDeviceToken` |
158
+ | `@pymthouse/builder-sdk/device-initiate` | Option B device login validation (Edge-safe) |
77
159
  | `@pymthouse/builder-sdk/verify` | RFC 9068 `verifyJwt` |
78
160
 
79
161
  ## Usage API: duplicate `byUser` rows
@@ -90,6 +172,28 @@ const summary = summarizeUsageForExternalUser(usage, externalUserId);
90
172
  // summary.requestCount, summary.feeWei (wei string)
91
173
  ```
92
174
 
175
+ ## Billing: plans, retail usage, signed-ticket ingest
176
+
177
+ **Plans (apiVersion=2):** `listBillingProducts({ apiVersion: "2" })` returns `BillingProduct[]` with capability pricing and sync status. `syncBillingProduct(planId)` POSTs to OpenMeter.
178
+
179
+ **Retail estimates:** `getUsage({ includeRetail: true, groupBy: "pipeline_model" })` adds `endUserBillableUsdMicros` / fiat rows when the active plan has retail rates.
180
+
181
+ **Signed-ticket ingest (platform metering):** after a signer proxy response, call `ingestSignedTicket` or use `forwardWithOptionalMetering` with `metering: { mode: "pymthouse_hosted" }` on `createSignerProxyServer` — usage is stripped from the client response and POSTed to `POST /api/v1/apps/{id}/usage/signed-tickets`.
182
+
183
+ **Routing:** `getSignerRouting()` returns `signerApiUrl`, `remoteDmzUrl`, `meteringMode`, and pattern hints for hosted vs platform-ingest vs BYO OpenMeter.
184
+
185
+ **Allowances (OpenMeter):** Trial and manual USD micros allowance use OpenMeter entitlements — not a Postgres wei ledger.
186
+
187
+ | Method | SDK | HTTP |
188
+ |--------|-----|------|
189
+ | Read balance | `getUsageBalance(externalUserId)` | `GET .../usage/balance?externalUserId=` |
190
+ | Read allowance detail | `getUserAllowances(externalUserId)` | `GET .../users/{id}/allowances` |
191
+ | Top-up grant | `grantUserAllowance(externalUserId, { amountUsdMicros, source })` | `POST .../users/{id}/allowances` |
192
+
193
+ `grantUserCredits` / `getUserCredits` remain as **deprecated** aliases that call the allowances / balance endpoints. `POST .../users/{id}/credits` was removed from PymtHouse (the route may still re-export allowances temporarily).
194
+
195
+ **Plan pricing helpers:** `markupPercentToRetailRateUsd`, `applyRetailRateToNetworkMicros` (exported from the main entry).
196
+
93
197
  ## Usage API: pipeline/model grouping
94
198
 
95
199
  When `getUsage({ groupBy: "pipeline_model", startDate, endDate, userId })` returns
@@ -0,0 +1,129 @@
1
+ import { SignerSessionToken } from './tokens.js';
2
+ import { P as PmtHouseClientOptions, G as GetDiscoveryOptions, O as OidcDiscoveryDocument, a as ParsedDeviceApprovalRedirect, A as AppUserRecord, U as UpsertAppUserInput, M as MintUserAccessTokenInput, b as MintUserAccessTokenResponse, T as TokenExchangeResponse, D as DeviceApprovalInput, C as ClientCredentialsTokenResponse, c as MintUserSignerSessionTokenInput, d as UsageQueryInput, e as UsageApiResponse, S as SignedTicketIngestInput, f as SignedTicketIngestResult, g as SignerRoutingResponse, L as ListBillingProductsResult, h as PlanSyncResult, i as UsageBalanceResponse, j as UserAllowancesResponse, k as UserAllowanceGrantInput, l as GrantSource, m as UserSubscriptionResponse, n as MeScopeUsagePayload, o as GetAppManifestResult, p as MintSignerSessionForExternalUserInput, q as ApproveDeviceLoginInput } from './types-_R1AwEZp.js';
3
+
4
+ /**
5
+ * Normalize RFC 8628 user codes for comparison and resource URIs (uppercase, strip separators).
6
+ */
7
+ declare function normalizeUserCode(value: string): string;
8
+ /**
9
+ * RFC 8707 resource indicator for NaaP Option B device approval (`urn:pmth:device_code:<normalized>`).
10
+ */
11
+ declare function buildDeviceCodeResource(userCode: string): string;
12
+ declare class PmtHouseClient {
13
+ private readonly issuerUrl;
14
+ private readonly publicClientId;
15
+ private readonly m2mClientId;
16
+ private readonly m2mClientSecret;
17
+ private readonly fetchImpl;
18
+ private readonly logger?;
19
+ private readonly allowInsecureHttp;
20
+ constructor(options: PmtHouseClientOptions);
21
+ getDiscovery(options?: GetDiscoveryOptions): Promise<OidcDiscoveryDocument>;
22
+ verifyIssuer(iss: string): boolean;
23
+ parseDeviceApprovalRedirect(searchParams: URLSearchParams): ParsedDeviceApprovalRedirect;
24
+ listAppUsers(): Promise<{
25
+ users: AppUserRecord[];
26
+ }>;
27
+ upsertAppUser(input: UpsertAppUserInput): Promise<AppUserRecord>;
28
+ deleteAppUser(params: {
29
+ externalUserId: string;
30
+ }): Promise<{
31
+ success: boolean;
32
+ }>;
33
+ mintUserAccessToken(input: MintUserAccessTokenInput): Promise<MintUserAccessTokenResponse>;
34
+ /**
35
+ * Exchange a long-lived dashboard API key (`pmth_*`) for a short-lived user JWT.
36
+ */
37
+ exchangeApiKeyForUserAccessToken(input: {
38
+ apiKey: string;
39
+ scope?: string;
40
+ }): Promise<MintUserAccessTokenResponse>;
41
+ /**
42
+ * Exchange a dashboard API key for a signer session via a trusted facade (recommended)
43
+ * or directly when M2M credentials are available on this client.
44
+ */
45
+ exchangeApiKeyForSignerSession(input: {
46
+ apiKey: string;
47
+ scope?: string;
48
+ facadeUrl?: string;
49
+ }): Promise<TokenExchangeResponse>;
50
+ completeDeviceApproval(input: DeviceApprovalInput): Promise<TokenExchangeResponse>;
51
+ issueMachineAccessToken(scope?: string): Promise<ClientCredentialsTokenResponse>;
52
+ exchangeForSignerSession(input: {
53
+ userJwt: string;
54
+ resource?: string;
55
+ }): Promise<TokenExchangeResponse>;
56
+ /**
57
+ * Mint a short-lived per-user JWT with the Builder API, then exchange it for
58
+ * a long-lived opaque signer session token at the PymtHouse OIDC token endpoint.
59
+ */
60
+ mintUserSignerSessionToken(input: MintUserSignerSessionTokenInput): Promise<TokenExchangeResponse>;
61
+ createSignerSessionToken(params: {
62
+ userJwt?: string;
63
+ }): Promise<TokenExchangeResponse>;
64
+ getUsage(input?: UsageQueryInput): Promise<UsageApiResponse>;
65
+ /**
66
+ * Session-scoped usage for one `externalUserId`: user rollup plus merged pipeline/model breakdown.
67
+ */
68
+ ingestSignedTicket(ticket: SignedTicketIngestInput): Promise<SignedTicketIngestResult>;
69
+ ingestSignedTickets(tickets: SignedTicketIngestInput[]): Promise<{
70
+ results: Array<SignedTicketIngestResult & {
71
+ requestId?: string;
72
+ ok?: boolean;
73
+ }>;
74
+ }>;
75
+ getSignerRouting(): Promise<SignerRoutingResponse>;
76
+ listBillingProducts(): Promise<ListBillingProductsResult>;
77
+ syncBillingProduct(planId: string): Promise<PlanSyncResult>;
78
+ getUsageBalance(externalUserId: string): Promise<UsageBalanceResponse>;
79
+ getUserAllowances(externalUserId: string): Promise<UserAllowancesResponse>;
80
+ grantUserAllowance(externalUserId: string, input: UserAllowanceGrantInput): Promise<UserAllowancesResponse & {
81
+ grantedUsdMicros?: string;
82
+ featureKey?: string;
83
+ }>;
84
+ /**
85
+ * @deprecated Removed from PymtHouse — use {@link getUsageBalance} or {@link getUserAllowances}.
86
+ */
87
+ getUserCredits(externalUserId: string): Promise<UsageBalanceResponse>;
88
+ /**
89
+ * @deprecated Removed from PymtHouse — use {@link grantUserAllowance} (`POST .../allowances`).
90
+ */
91
+ grantUserCredits(externalUserId: string, input: {
92
+ amountUsdMicros: string;
93
+ source?: GrantSource;
94
+ featureKey?: string;
95
+ }): Promise<UsageBalanceResponse & {
96
+ grantedUsdMicros?: string;
97
+ featureKey?: string;
98
+ }>;
99
+ getUserSubscription(externalUserId: string): Promise<UserSubscriptionResponse>;
100
+ fetchUsageForExternalUser(input: {
101
+ externalUserId: string;
102
+ startDate: string;
103
+ endDate: string;
104
+ maxEndUserIds?: number;
105
+ }): Promise<MeScopeUsagePayload>;
106
+ getAppManifest(opts?: {
107
+ ifNoneMatch?: string;
108
+ signal?: AbortSignal;
109
+ }): Promise<GetAppManifestResult>;
110
+ /**
111
+ * Upsert an external user, mint a short-lived JWT, and exchange for an opaque signer session.
112
+ */
113
+ mintSignerSessionForExternalUser(input: MintSignerSessionForExternalUserInput): Promise<SignerSessionToken>;
114
+ /**
115
+ * Approve a pending RFC 8628 device code for an external user (Option B).
116
+ */
117
+ approveDeviceLogin(input: ApproveDeviceLoginInput): Promise<void>;
118
+ private tokenEndpointFetchOptions;
119
+ private getAppsBaseUrl;
120
+ private getIssuerOrigin;
121
+ private builderHeaders;
122
+ private builderHeadersRecord;
123
+ private m2mClientAuth;
124
+ private requestJson;
125
+ private safeParseJson;
126
+ private asError;
127
+ }
128
+
129
+ export { PmtHouseClient as P, buildDeviceCodeResource as b, normalizeUserCode as n };
@@ -0,0 +1,129 @@
1
+ import { SignerSessionToken } from './tokens.cjs';
2
+ import { P as PmtHouseClientOptions, G as GetDiscoveryOptions, O as OidcDiscoveryDocument, a as ParsedDeviceApprovalRedirect, A as AppUserRecord, U as UpsertAppUserInput, M as MintUserAccessTokenInput, b as MintUserAccessTokenResponse, T as TokenExchangeResponse, D as DeviceApprovalInput, C as ClientCredentialsTokenResponse, c as MintUserSignerSessionTokenInput, d as UsageQueryInput, e as UsageApiResponse, S as SignedTicketIngestInput, f as SignedTicketIngestResult, g as SignerRoutingResponse, L as ListBillingProductsResult, h as PlanSyncResult, i as UsageBalanceResponse, j as UserAllowancesResponse, k as UserAllowanceGrantInput, l as GrantSource, m as UserSubscriptionResponse, n as MeScopeUsagePayload, o as GetAppManifestResult, p as MintSignerSessionForExternalUserInput, q as ApproveDeviceLoginInput } from './types-_R1AwEZp.cjs';
3
+
4
+ /**
5
+ * Normalize RFC 8628 user codes for comparison and resource URIs (uppercase, strip separators).
6
+ */
7
+ declare function normalizeUserCode(value: string): string;
8
+ /**
9
+ * RFC 8707 resource indicator for NaaP Option B device approval (`urn:pmth:device_code:<normalized>`).
10
+ */
11
+ declare function buildDeviceCodeResource(userCode: string): string;
12
+ declare class PmtHouseClient {
13
+ private readonly issuerUrl;
14
+ private readonly publicClientId;
15
+ private readonly m2mClientId;
16
+ private readonly m2mClientSecret;
17
+ private readonly fetchImpl;
18
+ private readonly logger?;
19
+ private readonly allowInsecureHttp;
20
+ constructor(options: PmtHouseClientOptions);
21
+ getDiscovery(options?: GetDiscoveryOptions): Promise<OidcDiscoveryDocument>;
22
+ verifyIssuer(iss: string): boolean;
23
+ parseDeviceApprovalRedirect(searchParams: URLSearchParams): ParsedDeviceApprovalRedirect;
24
+ listAppUsers(): Promise<{
25
+ users: AppUserRecord[];
26
+ }>;
27
+ upsertAppUser(input: UpsertAppUserInput): Promise<AppUserRecord>;
28
+ deleteAppUser(params: {
29
+ externalUserId: string;
30
+ }): Promise<{
31
+ success: boolean;
32
+ }>;
33
+ mintUserAccessToken(input: MintUserAccessTokenInput): Promise<MintUserAccessTokenResponse>;
34
+ /**
35
+ * Exchange a long-lived dashboard API key (`pmth_*`) for a short-lived user JWT.
36
+ */
37
+ exchangeApiKeyForUserAccessToken(input: {
38
+ apiKey: string;
39
+ scope?: string;
40
+ }): Promise<MintUserAccessTokenResponse>;
41
+ /**
42
+ * Exchange a dashboard API key for a signer session via a trusted facade (recommended)
43
+ * or directly when M2M credentials are available on this client.
44
+ */
45
+ exchangeApiKeyForSignerSession(input: {
46
+ apiKey: string;
47
+ scope?: string;
48
+ facadeUrl?: string;
49
+ }): Promise<TokenExchangeResponse>;
50
+ completeDeviceApproval(input: DeviceApprovalInput): Promise<TokenExchangeResponse>;
51
+ issueMachineAccessToken(scope?: string): Promise<ClientCredentialsTokenResponse>;
52
+ exchangeForSignerSession(input: {
53
+ userJwt: string;
54
+ resource?: string;
55
+ }): Promise<TokenExchangeResponse>;
56
+ /**
57
+ * Mint a short-lived per-user JWT with the Builder API, then exchange it for
58
+ * a long-lived opaque signer session token at the PymtHouse OIDC token endpoint.
59
+ */
60
+ mintUserSignerSessionToken(input: MintUserSignerSessionTokenInput): Promise<TokenExchangeResponse>;
61
+ createSignerSessionToken(params: {
62
+ userJwt?: string;
63
+ }): Promise<TokenExchangeResponse>;
64
+ getUsage(input?: UsageQueryInput): Promise<UsageApiResponse>;
65
+ /**
66
+ * Session-scoped usage for one `externalUserId`: user rollup plus merged pipeline/model breakdown.
67
+ */
68
+ ingestSignedTicket(ticket: SignedTicketIngestInput): Promise<SignedTicketIngestResult>;
69
+ ingestSignedTickets(tickets: SignedTicketIngestInput[]): Promise<{
70
+ results: Array<SignedTicketIngestResult & {
71
+ requestId?: string;
72
+ ok?: boolean;
73
+ }>;
74
+ }>;
75
+ getSignerRouting(): Promise<SignerRoutingResponse>;
76
+ listBillingProducts(): Promise<ListBillingProductsResult>;
77
+ syncBillingProduct(planId: string): Promise<PlanSyncResult>;
78
+ getUsageBalance(externalUserId: string): Promise<UsageBalanceResponse>;
79
+ getUserAllowances(externalUserId: string): Promise<UserAllowancesResponse>;
80
+ grantUserAllowance(externalUserId: string, input: UserAllowanceGrantInput): Promise<UserAllowancesResponse & {
81
+ grantedUsdMicros?: string;
82
+ featureKey?: string;
83
+ }>;
84
+ /**
85
+ * @deprecated Removed from PymtHouse — use {@link getUsageBalance} or {@link getUserAllowances}.
86
+ */
87
+ getUserCredits(externalUserId: string): Promise<UsageBalanceResponse>;
88
+ /**
89
+ * @deprecated Removed from PymtHouse — use {@link grantUserAllowance} (`POST .../allowances`).
90
+ */
91
+ grantUserCredits(externalUserId: string, input: {
92
+ amountUsdMicros: string;
93
+ source?: GrantSource;
94
+ featureKey?: string;
95
+ }): Promise<UsageBalanceResponse & {
96
+ grantedUsdMicros?: string;
97
+ featureKey?: string;
98
+ }>;
99
+ getUserSubscription(externalUserId: string): Promise<UserSubscriptionResponse>;
100
+ fetchUsageForExternalUser(input: {
101
+ externalUserId: string;
102
+ startDate: string;
103
+ endDate: string;
104
+ maxEndUserIds?: number;
105
+ }): Promise<MeScopeUsagePayload>;
106
+ getAppManifest(opts?: {
107
+ ifNoneMatch?: string;
108
+ signal?: AbortSignal;
109
+ }): Promise<GetAppManifestResult>;
110
+ /**
111
+ * Upsert an external user, mint a short-lived JWT, and exchange for an opaque signer session.
112
+ */
113
+ mintSignerSessionForExternalUser(input: MintSignerSessionForExternalUserInput): Promise<SignerSessionToken>;
114
+ /**
115
+ * Approve a pending RFC 8628 device code for an external user (Option B).
116
+ */
117
+ approveDeviceLogin(input: ApproveDeviceLoginInput): Promise<void>;
118
+ private tokenEndpointFetchOptions;
119
+ private getAppsBaseUrl;
120
+ private getIssuerOrigin;
121
+ private builderHeaders;
122
+ private builderHeadersRecord;
123
+ private m2mClientAuth;
124
+ private requestJson;
125
+ private safeParseJson;
126
+ private asError;
127
+ }
128
+
129
+ export { PmtHouseClient as P, buildDeviceCodeResource as b, normalizeUserCode as n };
@@ -0,0 +1,122 @@
1
+ 'use strict';
2
+
3
+ // src/string-utils.ts
4
+ function stripTrailingSlashes(value) {
5
+ let end = value.length;
6
+ while (end > 0 && (value.codePointAt(end - 1) ?? 0) === 47) {
7
+ end--;
8
+ }
9
+ return value.slice(0, end);
10
+ }
11
+ function endsWithIgnoreCase(value, suffix) {
12
+ if (suffix.length > value.length) {
13
+ return false;
14
+ }
15
+ const start = value.length - suffix.length;
16
+ for (let i = 0; i < suffix.length; i++) {
17
+ const a = value.codePointAt(start + i) ?? 0;
18
+ const b = suffix.codePointAt(i) ?? 0;
19
+ if (a !== b && (a | 32) !== (b | 32)) {
20
+ return false;
21
+ }
22
+ }
23
+ return true;
24
+ }
25
+ function stripSuffixIgnoreCase(value, suffix) {
26
+ return endsWithIgnoreCase(value, suffix) ? value.slice(0, value.length - suffix.length) : value;
27
+ }
28
+ function stripOidcPathSuffix(issuerUrl) {
29
+ let base = stripTrailingSlashes(issuerUrl.trim());
30
+ base = stripSuffixIgnoreCase(base, "/oidc");
31
+ return stripTrailingSlashes(base);
32
+ }
33
+ function isSafePathSegment(value) {
34
+ if (typeof value !== "string" || value.length === 0 || value.length > 128) {
35
+ return false;
36
+ }
37
+ for (let i = 0; i < value.length; i++) {
38
+ const c = value.codePointAt(i) ?? 0;
39
+ const ok = c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95 || c === 45;
40
+ if (!ok) {
41
+ return false;
42
+ }
43
+ }
44
+ return true;
45
+ }
46
+ function parseHttpOrigin(raw, fallback) {
47
+ const trimmed = (raw ?? fallback).trim();
48
+ let parsed;
49
+ try {
50
+ parsed = new URL(trimmed);
51
+ } catch {
52
+ throw new TypeError("Origin must be a valid http(s) URL");
53
+ }
54
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
55
+ throw new TypeError("Origin must use http or https");
56
+ }
57
+ return parsed.origin;
58
+ }
59
+ function buildGatewaySessionDeleteUrl(origin, sessionId) {
60
+ if (!isSafePathSegment(sessionId)) {
61
+ throw new TypeError("Invalid gateway session id");
62
+ }
63
+ return new URL(`/api/gateway/sessions/${encodeURIComponent(sessionId)}`, origin);
64
+ }
65
+
66
+ // src/config.ts
67
+ 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.";
68
+ function trimEnv(name) {
69
+ const value = process.env[name];
70
+ if (!value) return null;
71
+ const trimmed = value.trim();
72
+ return trimmed || null;
73
+ }
74
+ function readPymthouseEnv() {
75
+ const issuerUrl = trimEnv("PYMTHOUSE_ISSUER_URL");
76
+ const publicClientId = trimEnv("PYMTHOUSE_PUBLIC_CLIENT_ID");
77
+ const m2mClientId = trimEnv("PYMTHOUSE_M2M_CLIENT_ID");
78
+ const m2mClientSecret = trimEnv("PYMTHOUSE_M2M_CLIENT_SECRET");
79
+ if (!issuerUrl || !publicClientId || !m2mClientId || !m2mClientSecret) {
80
+ return null;
81
+ }
82
+ return {
83
+ issuerUrl: stripTrailingSlashes(issuerUrl),
84
+ publicClientId,
85
+ m2mClientId,
86
+ m2mClientSecret
87
+ };
88
+ }
89
+ function getPymthouseIssuerUrlFromEnv() {
90
+ const raw = trimEnv("PYMTHOUSE_ISSUER_URL");
91
+ if (!raw) return null;
92
+ try {
93
+ return stripTrailingSlashes(new URL(raw).href);
94
+ } catch {
95
+ return null;
96
+ }
97
+ }
98
+ function getPymthousePublicClientIdFromEnv() {
99
+ return trimEnv("PYMTHOUSE_PUBLIC_CLIENT_ID");
100
+ }
101
+ function isPymthouseConfigured() {
102
+ return readPymthouseEnv() !== null;
103
+ }
104
+ function getBuilderApiV1BaseFromIssuerUrl(issuerUrl) {
105
+ return stripOidcPathSuffix(issuerUrl);
106
+ }
107
+ function getPymthouseIssuerOrigin(issuerUrl) {
108
+ return new URL(stripTrailingSlashes(issuerUrl.trim())).origin;
109
+ }
110
+
111
+ exports.PYMTHOUSE_NOT_CONFIGURED_MESSAGE = PYMTHOUSE_NOT_CONFIGURED_MESSAGE;
112
+ exports.buildGatewaySessionDeleteUrl = buildGatewaySessionDeleteUrl;
113
+ exports.getBuilderApiV1BaseFromIssuerUrl = getBuilderApiV1BaseFromIssuerUrl;
114
+ exports.getPymthouseIssuerOrigin = getPymthouseIssuerOrigin;
115
+ exports.getPymthouseIssuerUrlFromEnv = getPymthouseIssuerUrlFromEnv;
116
+ exports.getPymthousePublicClientIdFromEnv = getPymthousePublicClientIdFromEnv;
117
+ exports.isPymthouseConfigured = isPymthouseConfigured;
118
+ exports.isSafePathSegment = isSafePathSegment;
119
+ exports.parseHttpOrigin = parseHttpOrigin;
120
+ exports.readPymthouseEnv = readPymthouseEnv;
121
+ //# sourceMappingURL=config.cjs.map
122
+ //# sourceMappingURL=config.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/string-utils.ts","../src/config.ts"],"names":[],"mappings":"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","file":"config.cjs","sourcesContent":["/** Removes trailing `/` without regex (linear time). */\nexport function stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && (value.codePointAt(end - 1) ?? 0) === 47) {\n end--;\n }\n return value.slice(0, end);\n}\n\nfunction endsWithIgnoreCase(value: string, suffix: string): boolean {\n if (suffix.length > value.length) {\n return false;\n }\n const start = value.length - suffix.length;\n for (let i = 0; i < suffix.length; i++) {\n const a = value.codePointAt(start + i) ?? 0;\n const b = suffix.codePointAt(i) ?? 0;\n if (a !== b && (a | 32) !== (b | 32)) {\n return false;\n }\n }\n return true;\n}\n\nfunction stripSuffixIgnoreCase(value: string, suffix: string): string {\n return endsWithIgnoreCase(value, suffix)\n ? value.slice(0, value.length - suffix.length)\n : value;\n}\n\n/** Issuer URL (`…/oidc`) → Builder API base (`…/api/v1`). Linear-time; no regex. */\nexport function stripOidcPathSuffix(issuerUrl: string): string {\n let base = stripTrailingSlashes(issuerUrl.trim());\n base = stripSuffixIgnoreCase(base, \"/oidc\");\n return stripTrailingSlashes(base);\n}\n\n/** Issuer URL (`…/api/v1/oidc`) → host origin for signer/API-key routes. Linear-time; no regex. */\nexport function stripIssuerOriginFromOidcUrl(issuerUrl: string): string {\n let base = stripTrailingSlashes(issuerUrl.trim());\n base = stripSuffixIgnoreCase(base, \"/api/v1/oidc\");\n base = stripSuffixIgnoreCase(base, \"/oidc\");\n return stripTrailingSlashes(base);\n}\n\n/** Validate gateway session ids before embedding in request URLs. */\nexport function isSafePathSegment(value: unknown): value is string {\n if (typeof value !== \"string\" || value.length === 0 || value.length > 128) {\n return false;\n }\n for (let i = 0; i < value.length; i++) {\n const c = value.codePointAt(i) ?? 0;\n const ok =\n (c >= 48 && c <= 57) ||\n (c >= 65 && c <= 90) ||\n (c >= 97 && c <= 122) ||\n c === 95 ||\n c === 45;\n if (!ok) {\n return false;\n }\n }\n return true;\n}\n\n/** Parse and validate an http(s) facade origin (no path). */\nexport function parseHttpOrigin(raw: string | undefined, fallback: string): string {\n const trimmed = (raw ?? fallback).trim();\n let parsed: URL;\n try {\n parsed = new URL(trimmed);\n } catch {\n throw new TypeError(\"Origin must be a valid http(s) URL\");\n }\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw new TypeError(\"Origin must use http or https\");\n }\n return parsed.origin;\n}\n\n/** Build a validated DELETE URL for `/api/gateway/sessions/:id`. */\nexport function buildGatewaySessionDeleteUrl(origin: string, sessionId: string): URL {\n if (!isSafePathSegment(sessionId)) {\n throw new TypeError(\"Invalid gateway session id\");\n }\n return new URL(`/api/gateway/sessions/${encodeURIComponent(sessionId)}`, origin);\n}\n","import {\n buildGatewaySessionDeleteUrl,\n isSafePathSegment,\n parseHttpOrigin,\n stripOidcPathSuffix,\n stripTrailingSlashes,\n} from \"./string-utils.js\";\n\nexport { buildGatewaySessionDeleteUrl, isSafePathSegment, parseHttpOrigin };\n\n/** Operator hint when Builder / Usage cannot run. */\nexport const PYMTHOUSE_NOT_CONFIGURED_MESSAGE =\n \"PymtHouse is not configured. Set PYMTHOUSE_ISSUER_URL, PYMTHOUSE_PUBLIC_CLIENT_ID, PYMTHOUSE_M2M_CLIENT_ID, and PYMTHOUSE_M2M_CLIENT_SECRET, then restart.\";\n\nexport interface PymthouseEnvConfig {\n issuerUrl: string;\n publicClientId: string;\n m2mClientId: string;\n m2mClientSecret: string;\n}\n\nfunction trimEnv(name: string): string | null {\n const value = process.env[name];\n if (!value) return null;\n const trimmed = value.trim();\n return trimmed || null;\n}\n\n/** Read `PYMTHOUSE_*` env vars without throwing. Returns null when incomplete. */\nexport function readPymthouseEnv(): PymthouseEnvConfig | null {\n const issuerUrl = trimEnv(\"PYMTHOUSE_ISSUER_URL\");\n const publicClientId = trimEnv(\"PYMTHOUSE_PUBLIC_CLIENT_ID\");\n const m2mClientId = trimEnv(\"PYMTHOUSE_M2M_CLIENT_ID\");\n const m2mClientSecret = trimEnv(\"PYMTHOUSE_M2M_CLIENT_SECRET\");\n if (!issuerUrl || !publicClientId || !m2mClientId || !m2mClientSecret) {\n return null;\n }\n return {\n issuerUrl: stripTrailingSlashes(issuerUrl),\n publicClientId,\n m2mClientId,\n m2mClientSecret,\n };\n}\n\n/** Read `PYMTHOUSE_ISSUER_URL` without requiring full M2M configuration. */\nexport function getPymthouseIssuerUrlFromEnv(): string | null {\n const raw = trimEnv(\"PYMTHOUSE_ISSUER_URL\");\n if (!raw) return null;\n try {\n return stripTrailingSlashes(new URL(raw).href);\n } catch {\n return null;\n }\n}\n\n/** Read `PYMTHOUSE_PUBLIC_CLIENT_ID` without requiring full M2M configuration. */\nexport function getPymthousePublicClientIdFromEnv(): string | null {\n return trimEnv(\"PYMTHOUSE_PUBLIC_CLIENT_ID\");\n}\n\n/** True when all vars required by `createPmtHouseClientFromEnv` are present. */\nexport function isPymthouseConfigured(): boolean {\n return readPymthouseEnv() !== null;\n}\n\n/** Resolve Builder API base (`…/api/v1`) from issuer URL (`…/api/v1/oidc`). */\nexport function getBuilderApiV1BaseFromIssuerUrl(issuerUrl: string): string {\n return stripOidcPathSuffix(issuerUrl);\n}\n\n/** Origin of the OIDC issuer host (e.g. `https://pymthouse.com`). */\nexport function getPymthouseIssuerOrigin(issuerUrl: string): string {\n return new URL(stripTrailingSlashes(issuerUrl.trim())).origin;\n}\n"]}
@@ -0,0 +1,29 @@
1
+ /** Validate gateway session ids before embedding in request URLs. */
2
+ declare function isSafePathSegment(value: unknown): value is string;
3
+ /** Parse and validate an http(s) facade origin (no path). */
4
+ declare function parseHttpOrigin(raw: string | undefined, fallback: string): string;
5
+ /** Build a validated DELETE URL for `/api/gateway/sessions/:id`. */
6
+ declare function buildGatewaySessionDeleteUrl(origin: string, sessionId: string): URL;
7
+
8
+ /** Operator hint when Builder / Usage cannot run. */
9
+ declare const 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.";
10
+ interface PymthouseEnvConfig {
11
+ issuerUrl: string;
12
+ publicClientId: string;
13
+ m2mClientId: string;
14
+ m2mClientSecret: string;
15
+ }
16
+ /** Read `PYMTHOUSE_*` env vars without throwing. Returns null when incomplete. */
17
+ declare function readPymthouseEnv(): PymthouseEnvConfig | null;
18
+ /** Read `PYMTHOUSE_ISSUER_URL` without requiring full M2M configuration. */
19
+ declare function getPymthouseIssuerUrlFromEnv(): string | null;
20
+ /** Read `PYMTHOUSE_PUBLIC_CLIENT_ID` without requiring full M2M configuration. */
21
+ declare function getPymthousePublicClientIdFromEnv(): string | null;
22
+ /** True when all vars required by `createPmtHouseClientFromEnv` are present. */
23
+ declare function isPymthouseConfigured(): boolean;
24
+ /** Resolve Builder API base (`…/api/v1`) from issuer URL (`…/api/v1/oidc`). */
25
+ declare function getBuilderApiV1BaseFromIssuerUrl(issuerUrl: string): string;
26
+ /** Origin of the OIDC issuer host (e.g. `https://pymthouse.com`). */
27
+ declare function getPymthouseIssuerOrigin(issuerUrl: string): string;
28
+
29
+ export { PYMTHOUSE_NOT_CONFIGURED_MESSAGE, type PymthouseEnvConfig, buildGatewaySessionDeleteUrl, getBuilderApiV1BaseFromIssuerUrl, getPymthouseIssuerOrigin, getPymthouseIssuerUrlFromEnv, getPymthousePublicClientIdFromEnv, isPymthouseConfigured, isSafePathSegment, parseHttpOrigin, readPymthouseEnv };
@@ -0,0 +1,29 @@
1
+ /** Validate gateway session ids before embedding in request URLs. */
2
+ declare function isSafePathSegment(value: unknown): value is string;
3
+ /** Parse and validate an http(s) facade origin (no path). */
4
+ declare function parseHttpOrigin(raw: string | undefined, fallback: string): string;
5
+ /** Build a validated DELETE URL for `/api/gateway/sessions/:id`. */
6
+ declare function buildGatewaySessionDeleteUrl(origin: string, sessionId: string): URL;
7
+
8
+ /** Operator hint when Builder / Usage cannot run. */
9
+ declare const 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.";
10
+ interface PymthouseEnvConfig {
11
+ issuerUrl: string;
12
+ publicClientId: string;
13
+ m2mClientId: string;
14
+ m2mClientSecret: string;
15
+ }
16
+ /** Read `PYMTHOUSE_*` env vars without throwing. Returns null when incomplete. */
17
+ declare function readPymthouseEnv(): PymthouseEnvConfig | null;
18
+ /** Read `PYMTHOUSE_ISSUER_URL` without requiring full M2M configuration. */
19
+ declare function getPymthouseIssuerUrlFromEnv(): string | null;
20
+ /** Read `PYMTHOUSE_PUBLIC_CLIENT_ID` without requiring full M2M configuration. */
21
+ declare function getPymthousePublicClientIdFromEnv(): string | null;
22
+ /** True when all vars required by `createPmtHouseClientFromEnv` are present. */
23
+ declare function isPymthouseConfigured(): boolean;
24
+ /** Resolve Builder API base (`…/api/v1`) from issuer URL (`…/api/v1/oidc`). */
25
+ declare function getBuilderApiV1BaseFromIssuerUrl(issuerUrl: string): string;
26
+ /** Origin of the OIDC issuer host (e.g. `https://pymthouse.com`). */
27
+ declare function getPymthouseIssuerOrigin(issuerUrl: string): string;
28
+
29
+ export { PYMTHOUSE_NOT_CONFIGURED_MESSAGE, type PymthouseEnvConfig, buildGatewaySessionDeleteUrl, getBuilderApiV1BaseFromIssuerUrl, getPymthouseIssuerOrigin, getPymthouseIssuerUrlFromEnv, getPymthousePublicClientIdFromEnv, isPymthouseConfigured, isSafePathSegment, parseHttpOrigin, readPymthouseEnv };