@apicity/cost 0.2.0-alpha.1 → 0.2.1

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 CHANGED
@@ -149,18 +149,22 @@ The cost package maintains a small, explicit **paid-endpoint registry**.
149
149
  Endpoints that are **not** in the registry are assumed free and require no
150
150
  caller changes.
151
151
 
152
- Paid endpoints require a **human-minted, single-request OTP** (one-time
153
- password). Autonomous callers cannot self-approve paid requests by passing
154
- a numeric `maxSpend` they must present an OTP that was signed by an
155
- operator-held Ed25519 key.
152
+ Paid endpoints require a **single-use OTP** (one-time password) minted from a
153
+ shared **HMAC secret**. The gate is fail-closed and does **no** cost
154
+ estimation it is pure authorization: a paid call cannot fire unless the
155
+ provider was constructed with the secret **and** the caller presents a valid,
156
+ request-bound OTP. The autonomous caller never holds the secret, so it cannot
157
+ self-approve; only the human or the code client that holds the secret can mint.
158
+
159
+ There are **no environment variables and no key files** — the secret is passed
160
+ in via factory options (or the MCP server's `--paygate-secret-file`).
156
161
 
157
162
  ### Registry model
158
163
 
159
164
  - `PAID_ENDPOINTS` is the canonical list. Every entry is an exact triple of
160
165
  `(provider, method, dotPath)` — there is no regex, prefix, wildcard, or
161
166
  inferred matching.
162
- - Unlisted endpoints are free. Free endpoints pass through without OTP or
163
- pay gate configuration.
167
+ - Unlisted endpoints are free and pass through without OTP or configuration.
164
168
  - Listed endpoints block unless a valid OTP is supplied.
165
169
 
166
170
  ### Token format
@@ -168,69 +172,80 @@ operator-held Ed25519 key.
168
172
  OTP tokens are a dependency-free compact envelope:
169
173
 
170
174
  ```
171
- <base64url(payloadJson)>.<base64url(signature)>
175
+ <base64url(payloadJson)>.<base64url(HMAC-SHA256(payloadSegment, secret))>
172
176
  ```
173
177
 
174
- The payload JSON is canonicalized with sorted object keys before signing.
175
- Signature is Ed25519 over the exact base64url payload segment bytes.
176
-
177
- Payload schema:
178
+ The signature is HMAC-SHA256 over the exact base64url payload segment bytes,
179
+ verified in constant time. Payload schema:
178
180
 
179
181
  ```ts
180
182
  interface PayGateOtpPayload {
181
- v: 1; // version
182
- jti: string; // random 128-bit hex (unique token id)
183
- provider: string; // e.g. "kie"
184
- method: string; // e.g. "POST"
185
- dotPath: string; // e.g. "api.v1.jobs.createTask"
183
+ v: 1; // version
184
+ jti: string; // random 128-bit hex (unique token id)
185
+ provider: string; // e.g. "kie"
186
+ method: string; // e.g. "POST"
187
+ dotPath: string; // e.g. "api.v1.jobs.createTask"
186
188
  requestHash: `sha256:${string}`; // sha256 of canonical request JSON
187
- maxSpendUsd: number; // maximum allowed spend in USD
188
- iat: number; // issued-at unix seconds
189
- exp: number; // expiration unix seconds
189
+ iat: number; // issued-at unix seconds
190
+ exp: number; // expiration unix seconds
190
191
  }
191
192
  ```
192
193
 
193
- Request hash is `sha256:` + hex SHA-256 of the canonical JSON for the request
194
- payload. Object keys are sorted recursively; array order is preserved.
195
- Non-JSON payload values must fail closed.
194
+ ### Configuration
196
195
 
197
- ### Key configuration
196
+ The code client supplies a `PayGateConfig` via factory options:
198
197
 
199
- **Runtime (verification):**
200
-
201
- | Variable | Required | Description |
202
- |----------|----------|-------------|
203
- | `APICITY_PAYGATE_PUBLIC_KEY_PATH` | Yes | Path to an Ed25519 public key PEM file. Without this, the pay gate is disabled and all paid endpoints throw `PayGateError`. |
198
+ ```ts
199
+ interface PayGateConfig {
200
+ secret: string; // shared HMAC secret (the code client holds it)
201
+ replayStore?: ReplayStore; // defaults to an in-process Set, per provider instance
202
+ now?: () => number; // clock injection for tests; defaults to Date.now
203
+ }
204
204
 
205
- **CLI (minting):**
205
+ interface ReplayStore {
206
+ has(jti: string): boolean;
207
+ add(jti: string): void;
208
+ }
209
+ ```
206
210
 
207
- | Variable | Required | Description |
208
- |----------|----------|-------------|
209
- | `APICITY_PAYGATE_PRIVATE_KEY_PATH` | Yes | Path to an Ed25519 private key PEM file. Used by the `apicity-paygate` CLI to sign OTPs. |
211
+ ```ts
212
+ import { createKie } from "@apicity/kie";
210
213
 
211
- ### Request canonicalization
214
+ const provider = createKie({
215
+ apiKey: process.env.KIE_API_KEY!,
216
+ paygate: { secret: loadSecret() }, // from your secret manager / config
217
+ });
218
+ ```
212
219
 
213
- Before hashing, the request payload is canonicalized:
220
+ ### Minting OTPs
214
221
 
215
- 1. Serialize to JSON with **sorted object keys** (recursive).
216
- 2. Preserve array order exactly.
217
- 3. Reject non-JSON values (functions, undefined, circular references, etc.).
222
+ `mintOtp` is pure and env-free the secret is passed explicitly and the OTP
223
+ binds to the exact request via its canonical hash:
218
224
 
219
- The canonical JSON string is then SHA-256 hashed, and the hash is prefixed
220
- with `sha256:` in the OTP payload.
225
+ ```ts
226
+ import { mintOtp } from "@apicity/cost";
221
227
 
222
- ### Replay ledger
228
+ const otp = mintOtp(secret, {
229
+ dotPath: "api.v1.jobs.createTask", // provider/method resolved from the registry
230
+ request: payload, // bound by canonical hash
231
+ ttl: "10m", // seconds or "10m" / "1h" / "1d"; defaults to 10m
232
+ });
233
+ ```
223
234
 
224
- Consumed OTPs are recorded immediately before dispatch to prevent replay
225
- attacks. The default ledger path:
235
+ ### Request canonicalization
226
236
 
227
- - `$XDG_STATE_HOME/apicity/paygate-used.jsonl`
228
- - Fallback: `~/.local/state/apicity/paygate-used.jsonl`
237
+ Before hashing, the request payload is canonicalized: serialized to JSON with
238
+ **sorted object keys** (recursive), preserving array order, rejecting non-JSON
239
+ values (functions, undefined, circular references). The canonical string is
240
+ SHA-256 hashed and prefixed with `sha256:`. Change any byte of the request and
241
+ verification fails.
229
242
 
230
- Each line is a JSON object: `{ "jti": "...", "consumedAt": 1234567890 }`.
243
+ ### Replay protection
231
244
 
232
- If dispatch later fails (network error, upstream 500, etc.), the OTP remains
233
- consumed. The operator must mint a new OTP for retry.
245
+ Each OTP `jti` is single-use. The default `ReplayStore` is an in-process Set
246
+ scoped to one provider instance (no files, no `XDG_STATE_HOME`). Pass a custom
247
+ `replayStore` for cross-process or persistent protection. The `jti` is consumed
248
+ **before** dispatch — see [Retry semantics](#retry-semantics).
234
249
 
235
250
  ### Public interface
236
251
 
@@ -245,153 +260,135 @@ async function dispatchWithPaidGate<T>(
245
260
  dotPath: string,
246
261
  payload: Record<string, unknown>,
247
262
  approval: PayGateApproval | undefined,
248
- dispatch: () => Promise<T>
263
+ dispatch: () => Promise<T>,
264
+ config?: PayGateConfig
249
265
  ): Promise<T>;
250
266
  ```
251
267
 
252
- Paid endpoint APIs pass the approval as an options object:
268
+ Paid endpoint APIs accept the approval as a second options object:
253
269
 
254
270
  ```ts
255
- import { kie } from "@apicity/kie";
256
-
257
- const provider = kie({ apiKey: process.env.KIE_API_KEY! });
258
-
259
271
  const task = await provider.post.api.v1.jobs.createTask(
260
- {
261
- model: "kling-3.0/video",
262
- input: { prompt: "...", duration: "5", aspect_ratio: "16:9" },
263
- },
264
- { otp: "eyJ2IjoxLCJqdGkiOi4uLn0.uK2J9..." }
272
+ { model: "kling-3.0/video", input: { prompt: "...", duration: "5" } },
273
+ { otp }
265
274
  );
266
275
  ```
267
276
 
268
277
  ### Guard behavior
269
278
 
270
- 1. **Preflight** — before any network dispatch, `dispatchWithPaidGate` checks
271
- whether the endpoint is paid. If the runtime lacks
272
- `APICITY_PAYGATE_PUBLIC_KEY_PATH`, the call throws `PayGateError`.
273
- 2. **OTP presence** — paid endpoints require `approval.otp`. If omitted, the
274
- call throws `PayGateError`.
275
- 3. **Signature verification** — the OTP payload segment is verified against
276
- the Ed25519 public key. If the signature is invalid or forged, the call
277
- throws `PayGateError`.
278
- 4. **Expiration** — if `exp` is in the past, the call throws `PayGateError`.
279
- 5. **Request binding** — the OTP `provider`, `method`, `dotPath`, and
280
- `requestHash` must match the actual call. Any mismatch throws
281
- `PayGateError`.
282
- 6. **Replay check** — if the OTP `jti` already exists in the ledger, the call
283
- throws `PayGateError`.
284
- 7. **Estimate** — the package computes a local cost estimate from the payload.
285
- 8. **Bound check** — if the estimate exceeds `maxSpendUsd`, or if the cost
286
- cannot be estimated (unknown model, missing fields), the call throws
287
- `SpendBoundError`.
288
- 9. **Consume** — the `jti` is appended to the replay ledger.
289
- 10. **Dispatch** — only when all checks pass does the actual HTTP request fire.
279
+ 1. **Preflight** — if the endpoint is not in `PAID_ENDPOINTS`, dispatch runs
280
+ immediately.
281
+ 2. **Configuration** a paid endpoint with no `paygate.secret` throws
282
+ `PayGateError` (`paygate-not-configured`).
283
+ 3. **OTP presence** — paid endpoints require `approval.otp`; if omitted the call
284
+ throws `PayGateError` (`otp-missing`).
285
+ 4. **Signature** the payload segment's HMAC is verified (constant-time)
286
+ against the secret; mismatch throws `PayGateError` (`otp-invalid-signature`).
287
+ 5. **Expiration** — `exp` in the past throws `PayGateError` (`otp-expired`).
288
+ 6. **Request binding** — `provider`, `method`, `dotPath`, and `requestHash` must
289
+ match the actual call, else `PayGateError` (`otp-mismatched-request`).
290
+ 7. **Replay check** — a `jti` already in the store throws `PayGateError`
291
+ (`otp-replayed`).
292
+ 8. **Consume + dispatch** — the `jti` is recorded, then the HTTP request fires.
290
293
 
291
294
  ```ts
292
- import { PayGateError, SpendBoundError } from "@apicity/cost";
295
+ import { PayGateError } from "@apicity/cost";
293
296
 
294
297
  try {
295
- await provider.post.api.v1.jobs.createTask({ ... }, { otp: "..." });
298
+ await provider.post.api.v1.jobs.createTask({ ... }, { otp });
296
299
  } catch (e) {
297
300
  if (e instanceof PayGateError) {
298
- // OTP missing, invalid, expired, replayed, or mismatched request
299
- }
300
- if (e instanceof SpendBoundError) {
301
- // estimated cost > maxSpendUsd, or cost could not be computed
302
- }
301
+ // e.code: paygate-not-configured | otp-missing | otp-malformed
302
+ // | otp-invalid-signature | otp-expired
303
+ // | otp-mismatched-request | otp-replayed
304
+ } else throw e;
303
305
  }
304
306
  ```
305
307
 
306
308
  ### Failure modes
307
309
 
308
- | Condition | Error | Notes |
309
- |-----------|-------|-------|
310
- | No `APICITY_PAYGATE_PUBLIC_KEY_PATH` | `PayGateError` | Pay gate is not configured |
311
- | Paid endpoint without OTP | `PayGateError` | Caller must pass `approval.otp` |
312
- | Invalid signature | `PayGateError` | OTP was not signed by the correct key |
313
- | Expired OTP (`exp` < now) | `PayGateError` | Operator must mint a fresh OTP |
314
- | Replayed OTP (`jti` in ledger) | `PayGateError` | Each OTP is single-use |
315
- | Mismatched provider/method/dotPath | `PayGateError` | OTP is bound to a specific call |
316
- | Mismatched request hash | `PayGateError` | Payload must match exactly |
317
- | Estimate > `maxSpendUsd` | `SpendBoundError` | Request is too expensive |
318
- | Unestimable cost | `SpendBoundError` | Unknown model or missing fields |
310
+ | Condition | `PayGateError.code` |
311
+ | ---------------------------------- | ------------------------ |
312
+ | Provider built without a secret | `paygate-not-configured` |
313
+ | Paid endpoint without OTP | `otp-missing` |
314
+ | Malformed envelope | `otp-malformed` |
315
+ | Invalid HMAC signature | `otp-invalid-signature` |
316
+ | Expired OTP (`exp` < now) | `otp-expired` |
317
+ | Mismatched provider/method/dotPath | `otp-mismatched-request` |
318
+ | Mismatched request hash | `otp-mismatched-request` |
319
+ | Replayed OTP (`jti` seen) | `otp-replayed` |
319
320
 
320
321
  ### CLI: minting OTPs
321
322
 
322
- The `apicity-paygate` binary from `@apicity/cost` mints operator-signed OTPs:
323
+ The `apicity-paygate` binary mints OTPs. The secret is read from a **file**
324
+ (never an env var); only the OTP is printed to stdout:
323
325
 
324
326
  ```bash
325
- # Mint an OTP for a specific request
326
327
  apicity-paygate otp mint \
327
- --provider kie \
328
- --method POST \
328
+ --secret-file ./paygate.secret \
329
329
  --dot-path api.v1.jobs.createTask \
330
330
  --payload-file request.json \
331
- --max-spend 5 \
332
331
  --ttl 10m
333
332
  ```
334
333
 
335
- The CLI requires `APICITY_PAYGATE_PRIVATE_KEY_PATH` and prints only the OTP
336
- to stdout on success.
334
+ ### Wiring the gate into a provider
337
335
 
338
- ### Migration from `maxSpend`
336
+ Providers apply the gate once at the bottom of their factory using
337
+ `withPaidGate`. The walker descends the HTTP-method roots (`post`, `get`,
338
+ `delete`, `patch`, `put`) and routes every leaf whose `(provider, method,
339
+ dotPath)` is in `PAID_ENDPOINTS` through `dispatchWithPaidGate`. Free leaves
340
+ pass through unchanged; sub-providers, schema records, and other non-route
341
+ properties are returned by reference. The config is passed once and shared
342
+ (one replay store per provider instance):
339
343
 
340
- The previous numeric `maxSpend` parameter is **deprecated**. It allowed
341
- autonomous callers to self-approve by passing any positive number, which is
342
- not a true authority boundary.
344
+ ```ts
345
+ import { withPaidGate } from "@apicity/cost";
346
+
347
+ export function createKie(opts: KieOptions): KieProvider {
348
+ // ...build endpoint functions...
349
+ return withPaidGate(
350
+ "kie",
351
+ {
352
+ veo: createVeoProvider(...), // sub-provider, untouched
353
+ modelInputSchemas, // data, untouched
354
+ post: { api: { v1: { jobs: { createTask: Object.assign(createTask, { schema }) } } } },
355
+ get: { api: { v1: { jobs: { recordInfo } } } },
356
+ },
357
+ { config: opts.paygate }
358
+ );
359
+ }
360
+ ```
343
361
 
344
- Migration path:
362
+ The gate is generic — `xai` and other providers opt in simply by adding a
363
+ `PAID_ENDPOINTS` entry and threading `{ config: opts.paygate }` through their
364
+ factory.
345
365
 
346
- 1. **Operator** generates an Ed25519 key pair:
347
- ```bash
348
- openssl genpkey -algorithm Ed25519 -out paygate-private.pem
349
- openssl pkey -in paygate-private.pem -pubout -out paygate-public.pem
350
- ```
351
- 2. **Runtime** sets `APICITY_PAYGATE_PUBLIC_KEY_PATH` to the public key.
352
- 3. **Operator** mints OTPs before each paid call using the CLI or a custom
353
- tool that signs the same payload format.
354
- 4. **Caller** passes `{ otp }` instead of a numeric `maxSpend`:
355
- ```ts
356
- // Before (deprecated)
357
- await provider.post.api.v1.jobs.createTask({ ... }, 5);
366
+ ### Retry semantics
358
367
 
359
- // After
360
- await provider.post.api.v1.jobs.createTask({ ... }, { otp: "..." });
361
- ```
368
+ The OTP `jti` is consumed **before** `dispatch()` runs. If dispatch later fails
369
+ (network error, upstream 5xx, abort), the `jti` stays consumed and the caller
370
+ must mint a fresh OTP to retry. This is intentional — without it, a hostile
371
+ caller could replay a single OTP on every transient failure. Treat each OTP as
372
+ single-use authority for one network attempt.
362
373
 
363
- The old `maxSpendPreflight`, `MaxSpendError`, and `dispatchWithPaidGuard`
364
- interfaces are retained during the transition but will be removed in a
365
- future major version.
374
+ ### MCP server
375
+
376
+ The `@apicity/mcp-server` is the code client: start it with
377
+ `--paygate-secret-file <path>` and it holds the secret to **verify** OTPs (it
378
+ never mints). A human mints an OTP out-of-band (same secret) and the caller
379
+ passes it as the paid tool's `otp` argument — so an AI driving the tool cannot
380
+ self-approve.
366
381
 
367
382
  ### Minimal operator workflow
368
383
 
369
- 1. **Set up keys** (one-time):
370
- ```bash
371
- export APICITY_PAYGATE_PRIVATE_KEY_PATH=./paygate-private.pem
372
- export APICITY_PAYGATE_PUBLIC_KEY_PATH=./paygate-public.pem
373
- ```
374
- 2. **Prepare a request**:
375
- ```bash
376
- cat > request.json << 'EOF'
377
- {
378
- "model": "kling-3.0/video",
379
- "input": {
380
- "prompt": "A cat playing piano",
381
- "duration": "5",
382
- "aspect_ratio": "16:9"
383
- }
384
- }
385
- EOF
386
- ```
384
+ 1. **Generate a secret** (one-time) and store it (secret manager / file).
385
+ 2. **Prepare a request** JSON file.
387
386
  3. **Mint an OTP**:
388
387
  ```bash
389
388
  apicity-paygate otp mint \
390
- --provider kie \
391
- --method POST \
389
+ --secret-file ./paygate.secret \
392
390
  --dot-path api.v1.jobs.createTask \
393
391
  --payload-file request.json \
394
- --max-spend 5 \
395
392
  --ttl 10m
396
393
  ```
397
394
  4. **Pass the OTP to the caller** (copy-paste, secrets manager, etc.).
@@ -1,3 +1,3 @@
1
1
  import type { CostProvider } from "./types";
2
- export declare function cost(): CostProvider;
2
+ export declare function createCost(): CostProvider;
3
3
  //# sourceMappingURL=cost.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cost.d.ts","sourceRoot":"","sources":["../../src/cost.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,SAAS,CAAC;AAE7D,wBAAgB,IAAI,IAAI,YAAY,CAInC"}
1
+ {"version":3,"file":"cost.d.ts","sourceRoot":"","sources":["../../src/cost.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,SAAS,CAAC;AAE7D,wBAAgB,UAAU,IAAI,YAAY,CAIzC"}
package/dist/src/cost.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { computeEstimate } from "./compute.js";
2
- export function cost() {
2
+ export function createCost() {
3
3
  return {
4
4
  estimate: (req) => computeEstimate(req),
5
5
  };
@@ -1 +1 @@
1
- {"version":3,"file":"cost.js","sourceRoot":"","sources":["../../src/cost.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAG5C,MAAM,UAAU,IAAI;IAClB,OAAO;QACL,QAAQ,EAAE,CAAC,GAAoB,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC;KACzD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"cost.js","sourceRoot":"","sources":["../../src/cost.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAG5C,MAAM,UAAU,UAAU;IACxB,OAAO;QACL,QAAQ,EAAE,CAAC,GAAoB,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC;KACzD,CAAC;AACJ,CAAC"}
@@ -1,4 +1,4 @@
1
- export { cost } from "./cost";
1
+ export { createCost } from "./cost";
2
2
  export { computeEstimate } from "./compute";
3
3
  export { PRICING, PRICING_AS_OF } from "./pricing/index";
4
4
  export { MODEL_SLUGS, MODEL_DISPLAY, modelSlug, modelDisplay } from "./slugs";
@@ -6,8 +6,10 @@ export type { SlugProviderId, SlugModelId } from "./slugs";
6
6
  export type { CostUnit, ModelPricing, PerUnitPricing, PricedProviderId, RateSource, TokenPricing, } from "./pricing/index";
7
7
  export type { CostBreakdown, CostEstimate, CostProvider, CostSource, EstimateRequest, } from "./types";
8
8
  export type { ExtractResult, TextExtract } from "./extract/types";
9
- export { PAID_ENDPOINTS, lookupPaidEndpoint, isPaidEndpoint, maxSpendPreflight, MaxSpendError, SpendBoundError, spendBoundCheck, dispatchWithPaidGuard, } from "./paid-endpoints";
9
+ export { PAID_ENDPOINTS, lookupPaidEndpoint, isPaidEndpoint, } from "./paid-endpoints";
10
10
  export type { PaidEndpointKey, PaidEndpointInfo, PaidEndpointEntry, } from "./paid-endpoints";
11
- export { PayGateError, PayGateApproval, dispatchWithPaidGate, canonicalizeJson, canonicalHash, parseOtp, verifyOtpSignature, isJtiConsumed, consumeJti, } from "./paygate";
12
- export type { PayGateOtpPayload } from "./paygate";
11
+ export { PayGateError, dispatchWithPaidGate, mintOtp, createReplayStore, canonicalizeJson, canonicalHash, parseOtp, parseTtl, verifyOtp, } from "./paygate";
12
+ export type { PayGateApproval, PayGateConfig, ReplayStore, OtpCall, PayGateOtpPayload, VerifyFailureCode, VerifyOtpInput, VerifyResult, } from "./paygate";
13
+ export { withPaidGate } from "./with-paid-gate";
14
+ export type { WithPaidGateOptions } from "./with-paid-gate";
13
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC9E,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3D,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,YAAY,GACb,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,eAAe,GAChB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAElE,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,eAAe,EACf,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,YAAY,EACZ,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,UAAU,GACX,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC9E,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3D,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,YAAY,GACb,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,eAAe,GAChB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAElE,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,OAAO,EACP,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,SAAS,GACV,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,eAAe,EACf,aAAa,EACb,WAAW,EACX,OAAO,EACP,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,YAAY,GACb,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/src/index.js CHANGED
@@ -1,7 +1,8 @@
1
- export { cost } from "./cost.js";
1
+ export { createCost } from "./cost.js";
2
2
  export { computeEstimate } from "./compute.js";
3
3
  export { PRICING, PRICING_AS_OF } from "./pricing/index.js";
4
4
  export { MODEL_SLUGS, MODEL_DISPLAY, modelSlug, modelDisplay } from "./slugs.js";
5
- export { PAID_ENDPOINTS, lookupPaidEndpoint, isPaidEndpoint, maxSpendPreflight, MaxSpendError, SpendBoundError, spendBoundCheck, dispatchWithPaidGuard, } from "./paid-endpoints.js";
6
- export { PayGateError, dispatchWithPaidGate, canonicalizeJson, canonicalHash, parseOtp, verifyOtpSignature, isJtiConsumed, consumeJti, } from "./paygate.js";
5
+ export { PAID_ENDPOINTS, lookupPaidEndpoint, isPaidEndpoint, } from "./paid-endpoints.js";
6
+ export { PayGateError, dispatchWithPaidGate, mintOtp, createReplayStore, canonicalizeJson, canonicalHash, parseOtp, parseTtl, verifyOtp, } from "./paygate.js";
7
+ export { withPaidGate } from "./with-paid-gate.js";
7
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAsB9E,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,eAAe,EACf,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAO1B,OAAO,EACL,YAAY,EAEZ,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,UAAU,GACX,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAsB9E,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAO1B,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,OAAO,EACP,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,SAAS,GACV,MAAM,WAAW,CAAC;AAYnB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC"}
@@ -43,68 +43,4 @@ export declare function lookupPaidEndpoint(provider: string, method: string, dot
43
43
  * Unlisted endpoints return `false` (assumed free).
44
44
  */
45
45
  export declare function isPaidEndpoint(provider: string, method: string, dotPath: string): boolean;
46
- /**
47
- * Error thrown when a paid endpoint is called without an explicit maxSpend.
48
- */
49
- export declare class MaxSpendError extends Error {
50
- readonly provider: string;
51
- readonly method: string;
52
- readonly dotPath: string;
53
- readonly maxSpend: number;
54
- constructor(provider: string, method: string, dotPath: string, maxSpend: number);
55
- }
56
- /**
57
- * Error thrown when the estimated cost of a paid endpoint exceeds the
58
- * caller's maxSpend or when the cost cannot be safely estimated.
59
- */
60
- export declare class SpendBoundError extends Error {
61
- readonly provider: string;
62
- readonly method: string;
63
- readonly dotPath: string;
64
- readonly maxSpend: number;
65
- readonly estimatedUsd: number;
66
- constructor(provider: string, method: string, dotPath: string, maxSpend: number, estimatedUsd: number, message?: string);
67
- }
68
- /**
69
- * Preflight check for paid endpoints.
70
- *
71
- * For paid endpoints, maxSpend defaults to 0 when omitted. maxSpend=0 blocks
72
- * before the network request with a MaxSpendError. maxSpend>0 authorizes the
73
- * call to proceed.
74
- *
75
- * Free/unlisted endpoints are always allowed regardless of maxSpend.
76
- */
77
- export declare function maxSpendPreflight(provider: string, method: string, dotPath: string, maxSpend?: number): void;
78
- /**
79
- * Verify that a cost estimate is within the caller's maxSpend.
80
- *
81
- * If the estimate has warnings (could not be computed safely), the spend
82
- * cannot be bounded and the call is blocked. If the estimated USD exceeds
83
- * maxSpend, the call is blocked. Otherwise the call proceeds.
84
- *
85
- * This must run AFTER maxSpendPreflight, so maxSpend is known to be > 0.
86
- */
87
- export declare function spendBoundCheck(provider: string, method: string, dotPath: string, maxSpend: number | undefined, estimate: {
88
- usd: number;
89
- warnings: string[];
90
- }): void;
91
- /**
92
- * Wrap a provider network dispatch with the paid-endpoint guard.
93
- *
94
- * This is the canonical boundary enforcement: it runs the preflight check
95
- * and spend-bound check before the actual HTTP request, and only calls the
96
- * supplied `dispatch` function when the checks pass.
97
- *
98
- * Free/unlisted endpoints return `dispatch()` immediately without guard
99
- * overhead, so callers that omit `maxSpend` on free endpoints see no change.
100
- *
101
- * @param provider - Provider identifier (e.g. "kie", "openai")
102
- * @param method - HTTP method (e.g. "POST", "GET")
103
- * @param dotPath - Exact endpoint dot-path (e.g. "api.v1.jobs.createTask")
104
- * @param payload - Request payload for cost estimation
105
- * @param maxSpend - Maximum spend authorization in USD (undefined for free endpoints)
106
- * @param dispatch - The actual network dispatch to wrap
107
- * @returns The result of `dispatch()`
108
- */
109
- export declare function dispatchWithPaidGuard<T>(provider: string, method: string, dotPath: string, payload: Record<string, unknown>, maxSpend: number | undefined, dispatch: () => Promise<T>): Promise<T>;
110
46
  //# sourceMappingURL=paid-endpoints.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"paid-endpoints.d.ts","sourceRoot":"","sources":["../../src/paid-endpoints.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,eAAe,CAAC;IACrB,IAAI,EAAE,gBAAgB,CAAC;CACxB;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,iBAAiB,EAetD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,gBAAgB,GAAG,SAAS,CAW9B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,OAAO,CAET;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAExB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM;CAanB;AACD;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;gBAE5B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM;CAcnB;AACD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAU,GACnB,IAAI,CAON;AACD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAC5C,IAAI,CA0BN;AACD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAC3C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC,CAcZ"}
1
+ {"version":3,"file":"paid-endpoints.d.ts","sourceRoot":"","sources":["../../src/paid-endpoints.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,eAAe,CAAC;IACrB,IAAI,EAAE,gBAAgB,CAAC;CACxB;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,iBAAiB,EAsEtD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,gBAAgB,GAAG,SAAS,CAW9B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,OAAO,CAET"}