@apicity/cost 0.2.0-alpha.0 → 0.2.0-alpha.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 +258 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/paid-endpoints.d.ts +110 -0
- package/dist/src/paid-endpoints.d.ts.map +1 -0
- package/dist/src/paid-endpoints.js +157 -0
- package/dist/src/paid-endpoints.js.map +1 -0
- package/dist/src/paygate-cli.d.ts +19 -0
- package/dist/src/paygate-cli.d.ts.map +1 -0
- package/dist/src/paygate-cli.js +177 -0
- package/dist/src/paygate-cli.js.map +1 -0
- package/dist/src/paygate.d.ts +80 -0
- package/dist/src/paygate.d.ts.map +1 -0
- package/dist/src/paygate.js +278 -0
- package/dist/src/paygate.js.map +1 -0
- package/dist/src/pricing/kie.d.ts.map +1 -1
- package/dist/src/pricing/kie.js +22 -8
- package/dist/src/pricing/kie.js.map +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -142,6 +142,264 @@ Maintenance is manual: re-fetch each upstream's pricing page, edit `pricing.ts`,
|
|
|
142
142
|
| `kie` | `per-unit-table` | priced per second of video / per image |
|
|
143
143
|
| `free` | `free` | always $0 |
|
|
144
144
|
|
|
145
|
+
## Paid endpoint guard (OTP pay gate)
|
|
146
|
+
|
|
147
|
+
Some endpoints have a direct marginal compute cost (e.g. video generation).
|
|
148
|
+
The cost package maintains a small, explicit **paid-endpoint registry**.
|
|
149
|
+
Endpoints that are **not** in the registry are assumed free and require no
|
|
150
|
+
caller changes.
|
|
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.
|
|
156
|
+
|
|
157
|
+
### Registry model
|
|
158
|
+
|
|
159
|
+
- `PAID_ENDPOINTS` is the canonical list. Every entry is an exact triple of
|
|
160
|
+
`(provider, method, dotPath)` — there is no regex, prefix, wildcard, or
|
|
161
|
+
inferred matching.
|
|
162
|
+
- Unlisted endpoints are free. Free endpoints pass through without OTP or
|
|
163
|
+
pay gate configuration.
|
|
164
|
+
- Listed endpoints block unless a valid OTP is supplied.
|
|
165
|
+
|
|
166
|
+
### Token format
|
|
167
|
+
|
|
168
|
+
OTP tokens are a dependency-free compact envelope:
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
<base64url(payloadJson)>.<base64url(signature)>
|
|
172
|
+
```
|
|
173
|
+
|
|
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
|
+
|
|
179
|
+
```ts
|
|
180
|
+
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"
|
|
186
|
+
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
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
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.
|
|
196
|
+
|
|
197
|
+
### Key configuration
|
|
198
|
+
|
|
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`. |
|
|
204
|
+
|
|
205
|
+
**CLI (minting):**
|
|
206
|
+
|
|
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. |
|
|
210
|
+
|
|
211
|
+
### Request canonicalization
|
|
212
|
+
|
|
213
|
+
Before hashing, the request payload is canonicalized:
|
|
214
|
+
|
|
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.).
|
|
218
|
+
|
|
219
|
+
The canonical JSON string is then SHA-256 hashed, and the hash is prefixed
|
|
220
|
+
with `sha256:` in the OTP payload.
|
|
221
|
+
|
|
222
|
+
### Replay ledger
|
|
223
|
+
|
|
224
|
+
Consumed OTPs are recorded immediately before dispatch to prevent replay
|
|
225
|
+
attacks. The default ledger path:
|
|
226
|
+
|
|
227
|
+
- `$XDG_STATE_HOME/apicity/paygate-used.jsonl`
|
|
228
|
+
- Fallback: `~/.local/state/apicity/paygate-used.jsonl`
|
|
229
|
+
|
|
230
|
+
Each line is a JSON object: `{ "jti": "...", "consumedAt": 1234567890 }`.
|
|
231
|
+
|
|
232
|
+
If dispatch later fails (network error, upstream 500, etc.), the OTP remains
|
|
233
|
+
consumed. The operator must mint a new OTP for retry.
|
|
234
|
+
|
|
235
|
+
### Public interface
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
interface PayGateApproval {
|
|
239
|
+
otp: string;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async function dispatchWithPaidGate<T>(
|
|
243
|
+
provider: string,
|
|
244
|
+
method: string,
|
|
245
|
+
dotPath: string,
|
|
246
|
+
payload: Record<string, unknown>,
|
|
247
|
+
approval: PayGateApproval | undefined,
|
|
248
|
+
dispatch: () => Promise<T>
|
|
249
|
+
): Promise<T>;
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Paid endpoint APIs pass the approval as an options object:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
import { kie } from "@apicity/kie";
|
|
256
|
+
|
|
257
|
+
const provider = kie({ apiKey: process.env.KIE_API_KEY! });
|
|
258
|
+
|
|
259
|
+
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..." }
|
|
265
|
+
);
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Guard behavior
|
|
269
|
+
|
|
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.
|
|
290
|
+
|
|
291
|
+
```ts
|
|
292
|
+
import { PayGateError, SpendBoundError } from "@apicity/cost";
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
await provider.post.api.v1.jobs.createTask({ ... }, { otp: "..." });
|
|
296
|
+
} catch (e) {
|
|
297
|
+
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
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Failure modes
|
|
307
|
+
|
|
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 |
|
|
319
|
+
|
|
320
|
+
### CLI: minting OTPs
|
|
321
|
+
|
|
322
|
+
The `apicity-paygate` binary from `@apicity/cost` mints operator-signed OTPs:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
# Mint an OTP for a specific request
|
|
326
|
+
apicity-paygate otp mint \
|
|
327
|
+
--provider kie \
|
|
328
|
+
--method POST \
|
|
329
|
+
--dot-path api.v1.jobs.createTask \
|
|
330
|
+
--payload-file request.json \
|
|
331
|
+
--max-spend 5 \
|
|
332
|
+
--ttl 10m
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
The CLI requires `APICITY_PAYGATE_PRIVATE_KEY_PATH` and prints only the OTP
|
|
336
|
+
to stdout on success.
|
|
337
|
+
|
|
338
|
+
### Migration from `maxSpend`
|
|
339
|
+
|
|
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.
|
|
343
|
+
|
|
344
|
+
Migration path:
|
|
345
|
+
|
|
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);
|
|
358
|
+
|
|
359
|
+
// After
|
|
360
|
+
await provider.post.api.v1.jobs.createTask({ ... }, { otp: "..." });
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
The old `maxSpendPreflight`, `MaxSpendError`, and `dispatchWithPaidGuard`
|
|
364
|
+
interfaces are retained during the transition but will be removed in a
|
|
365
|
+
future major version.
|
|
366
|
+
|
|
367
|
+
### Minimal operator workflow
|
|
368
|
+
|
|
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
|
+
```
|
|
387
|
+
3. **Mint an OTP**:
|
|
388
|
+
```bash
|
|
389
|
+
apicity-paygate otp mint \
|
|
390
|
+
--provider kie \
|
|
391
|
+
--method POST \
|
|
392
|
+
--dot-path api.v1.jobs.createTask \
|
|
393
|
+
--payload-file request.json \
|
|
394
|
+
--max-spend 5 \
|
|
395
|
+
--ttl 10m
|
|
396
|
+
```
|
|
397
|
+
4. **Pass the OTP to the caller** (copy-paste, secrets manager, etc.).
|
|
398
|
+
5. **Caller uses the OTP**:
|
|
399
|
+
```ts
|
|
400
|
+
await provider.post.api.v1.jobs.createTask({ ... }, { otp: "<paste>" });
|
|
401
|
+
```
|
|
402
|
+
|
|
145
403
|
## Out of scope
|
|
146
404
|
|
|
147
405
|
- Anthropic prompt-cache pricing (rates are in the table but `estimate()` ignores them — assumes no caching)
|
package/dist/src/index.d.ts
CHANGED
|
@@ -6,4 +6,8 @@ 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";
|
|
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";
|
|
9
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -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"}
|
|
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"}
|
package/dist/src/index.js
CHANGED
|
@@ -2,4 +2,6 @@ export { cost } 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
7
|
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
CHANGED
|
@@ -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"}
|
|
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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact paid-endpoint registry.
|
|
3
|
+
*
|
|
4
|
+
* Only endpoints listed here are considered paid. All unlisted endpoints
|
|
5
|
+
* are assumed free and must preserve current behavior with no caller changes.
|
|
6
|
+
*
|
|
7
|
+
* Matching is exact: provider + method + dotPath. No regex, prefix,
|
|
8
|
+
* wildcard, path-family, generated broad match, or fallback-by-method logic.
|
|
9
|
+
*/
|
|
10
|
+
export interface PaidEndpointKey {
|
|
11
|
+
provider: string;
|
|
12
|
+
method: string;
|
|
13
|
+
dotPath: string;
|
|
14
|
+
}
|
|
15
|
+
export interface PaidEndpointInfo {
|
|
16
|
+
/** Human-readable reason why this endpoint is paid. */
|
|
17
|
+
reason: string;
|
|
18
|
+
/** Optional estimator identifier for cost computation. */
|
|
19
|
+
estimatorId?: string;
|
|
20
|
+
/** Optional known cost notes (e.g. per-unit billing details). */
|
|
21
|
+
costNotes?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface PaidEndpointEntry {
|
|
24
|
+
key: PaidEndpointKey;
|
|
25
|
+
info: PaidEndpointInfo;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The canonical list of paid endpoints. Add new entries here only after
|
|
29
|
+
* review. Keep the list small and explicit.
|
|
30
|
+
*/
|
|
31
|
+
export declare const PAID_ENDPOINTS: readonly PaidEndpointEntry[];
|
|
32
|
+
/**
|
|
33
|
+
* Look up a paid endpoint by exact key match.
|
|
34
|
+
*
|
|
35
|
+
* Returns `PaidEndpointInfo` only when provider, method, and dotPath all match
|
|
36
|
+
* an entry in `PAID_ENDPOINTS` exactly. Returns `undefined` for every
|
|
37
|
+
* unlisted endpoint, which callers must treat as free.
|
|
38
|
+
*/
|
|
39
|
+
export declare function lookupPaidEndpoint(provider: string, method: string, dotPath: string): PaidEndpointInfo | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Predicate: is this exact endpoint paid?
|
|
42
|
+
*
|
|
43
|
+
* Unlisted endpoints return `false` (assumed free).
|
|
44
|
+
*/
|
|
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
|
+
//# sourceMappingURL=paid-endpoints.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { computeEstimate } from "./compute.js";
|
|
2
|
+
/**
|
|
3
|
+
* The canonical list of paid endpoints. Add new entries here only after
|
|
4
|
+
* review. Keep the list small and explicit.
|
|
5
|
+
*/
|
|
6
|
+
export const PAID_ENDPOINTS = [
|
|
7
|
+
{
|
|
8
|
+
key: {
|
|
9
|
+
provider: "kie",
|
|
10
|
+
method: "POST",
|
|
11
|
+
dotPath: "api.v1.jobs.createTask",
|
|
12
|
+
},
|
|
13
|
+
info: {
|
|
14
|
+
reason: "Media generation task that incurs direct marginal compute cost per job",
|
|
15
|
+
estimatorId: "kie-per-unit",
|
|
16
|
+
costNotes: "Billed per unit (seconds/images/songs) based on model and resolution",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Look up a paid endpoint by exact key match.
|
|
22
|
+
*
|
|
23
|
+
* Returns `PaidEndpointInfo` only when provider, method, and dotPath all match
|
|
24
|
+
* an entry in `PAID_ENDPOINTS` exactly. Returns `undefined` for every
|
|
25
|
+
* unlisted endpoint, which callers must treat as free.
|
|
26
|
+
*/
|
|
27
|
+
export function lookupPaidEndpoint(provider, method, dotPath) {
|
|
28
|
+
for (const entry of PAID_ENDPOINTS) {
|
|
29
|
+
if (entry.key.provider === provider &&
|
|
30
|
+
entry.key.method === method &&
|
|
31
|
+
entry.key.dotPath === dotPath) {
|
|
32
|
+
return entry.info;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Predicate: is this exact endpoint paid?
|
|
39
|
+
*
|
|
40
|
+
* Unlisted endpoints return `false` (assumed free).
|
|
41
|
+
*/
|
|
42
|
+
export function isPaidEndpoint(provider, method, dotPath) {
|
|
43
|
+
return lookupPaidEndpoint(provider, method, dotPath) !== undefined;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Error thrown when a paid endpoint is called without an explicit maxSpend.
|
|
47
|
+
*/
|
|
48
|
+
export class MaxSpendError extends Error {
|
|
49
|
+
provider;
|
|
50
|
+
method;
|
|
51
|
+
dotPath;
|
|
52
|
+
maxSpend;
|
|
53
|
+
constructor(provider, method, dotPath, maxSpend) {
|
|
54
|
+
super(`Endpoint ${provider} ${method} ${dotPath} may spend money. ` +
|
|
55
|
+
`maxSpend is ${maxSpend} USD. ` +
|
|
56
|
+
`Pass an explicit maxSpend to proceed.`);
|
|
57
|
+
this.name = "MaxSpendError";
|
|
58
|
+
this.provider = provider;
|
|
59
|
+
this.method = method;
|
|
60
|
+
this.dotPath = dotPath;
|
|
61
|
+
this.maxSpend = maxSpend;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Error thrown when the estimated cost of a paid endpoint exceeds the
|
|
66
|
+
* caller's maxSpend or when the cost cannot be safely estimated.
|
|
67
|
+
*/
|
|
68
|
+
export class SpendBoundError extends Error {
|
|
69
|
+
provider;
|
|
70
|
+
method;
|
|
71
|
+
dotPath;
|
|
72
|
+
maxSpend;
|
|
73
|
+
estimatedUsd;
|
|
74
|
+
constructor(provider, method, dotPath, maxSpend, estimatedUsd, message) {
|
|
75
|
+
super(message ??
|
|
76
|
+
`Endpoint ${provider} ${method} ${dotPath} estimated cost ` +
|
|
77
|
+
`(${estimatedUsd} USD) exceeds maxSpend (${maxSpend} USD).`);
|
|
78
|
+
this.name = "SpendBoundError";
|
|
79
|
+
this.provider = provider;
|
|
80
|
+
this.method = method;
|
|
81
|
+
this.dotPath = dotPath;
|
|
82
|
+
this.maxSpend = maxSpend;
|
|
83
|
+
this.estimatedUsd = estimatedUsd;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Preflight check for paid endpoints.
|
|
88
|
+
*
|
|
89
|
+
* For paid endpoints, maxSpend defaults to 0 when omitted. maxSpend=0 blocks
|
|
90
|
+
* before the network request with a MaxSpendError. maxSpend>0 authorizes the
|
|
91
|
+
* call to proceed.
|
|
92
|
+
*
|
|
93
|
+
* Free/unlisted endpoints are always allowed regardless of maxSpend.
|
|
94
|
+
*/
|
|
95
|
+
export function maxSpendPreflight(provider, method, dotPath, maxSpend = 0) {
|
|
96
|
+
if (!isPaidEndpoint(provider, method, dotPath)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (maxSpend <= 0) {
|
|
100
|
+
throw new MaxSpendError(provider, method, dotPath, maxSpend);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Verify that a cost estimate is within the caller's maxSpend.
|
|
105
|
+
*
|
|
106
|
+
* If the estimate has warnings (could not be computed safely), the spend
|
|
107
|
+
* cannot be bounded and the call is blocked. If the estimated USD exceeds
|
|
108
|
+
* maxSpend, the call is blocked. Otherwise the call proceeds.
|
|
109
|
+
*
|
|
110
|
+
* This must run AFTER maxSpendPreflight, so maxSpend is known to be > 0.
|
|
111
|
+
*/
|
|
112
|
+
export function spendBoundCheck(provider, method, dotPath, maxSpend, estimate) {
|
|
113
|
+
if (maxSpend === undefined || maxSpend <= 0) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (estimate.warnings.length > 0) {
|
|
117
|
+
throw new SpendBoundError(provider, method, dotPath, maxSpend, estimate.usd, `Endpoint ${provider} ${method} ${dotPath} spend cannot be bounded ` +
|
|
118
|
+
`from the payload: ${estimate.warnings.join("; ")}. ` +
|
|
119
|
+
`Pass an explicit maxSpend that covers the worst-case cost, ` +
|
|
120
|
+
`or adjust the payload so the cost can be estimated.`);
|
|
121
|
+
}
|
|
122
|
+
if (estimate.usd > maxSpend) {
|
|
123
|
+
throw new SpendBoundError(provider, method, dotPath, maxSpend, estimate.usd);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Wrap a provider network dispatch with the paid-endpoint guard.
|
|
128
|
+
*
|
|
129
|
+
* This is the canonical boundary enforcement: it runs the preflight check
|
|
130
|
+
* and spend-bound check before the actual HTTP request, and only calls the
|
|
131
|
+
* supplied `dispatch` function when the checks pass.
|
|
132
|
+
*
|
|
133
|
+
* Free/unlisted endpoints return `dispatch()` immediately without guard
|
|
134
|
+
* overhead, so callers that omit `maxSpend` on free endpoints see no change.
|
|
135
|
+
*
|
|
136
|
+
* @param provider - Provider identifier (e.g. "kie", "openai")
|
|
137
|
+
* @param method - HTTP method (e.g. "POST", "GET")
|
|
138
|
+
* @param dotPath - Exact endpoint dot-path (e.g. "api.v1.jobs.createTask")
|
|
139
|
+
* @param payload - Request payload for cost estimation
|
|
140
|
+
* @param maxSpend - Maximum spend authorization in USD (undefined for free endpoints)
|
|
141
|
+
* @param dispatch - The actual network dispatch to wrap
|
|
142
|
+
* @returns The result of `dispatch()`
|
|
143
|
+
*/
|
|
144
|
+
export async function dispatchWithPaidGuard(provider, method, dotPath, payload, maxSpend, dispatch) {
|
|
145
|
+
maxSpendPreflight(provider, method, dotPath, maxSpend);
|
|
146
|
+
if (isPaidEndpoint(provider, method, dotPath) &&
|
|
147
|
+
maxSpend !== undefined &&
|
|
148
|
+
maxSpend > 0) {
|
|
149
|
+
const estimate = computeEstimate({
|
|
150
|
+
provider: provider,
|
|
151
|
+
payload,
|
|
152
|
+
});
|
|
153
|
+
spendBoundCheck(provider, method, dotPath, maxSpend, estimate);
|
|
154
|
+
}
|
|
155
|
+
return dispatch();
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=paid-endpoints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paid-endpoints.js","sourceRoot":"","sources":["../../src/paid-endpoints.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Parse a TTL string like "10m", "1h", "30s" into seconds.
|
|
4
|
+
*/
|
|
5
|
+
export declare function parseTtl(ttl: string): number;
|
|
6
|
+
/**
|
|
7
|
+
* Mint an OTP for a specific request.
|
|
8
|
+
*
|
|
9
|
+
* Requires `APICITY_PAYGATE_PRIVATE_KEY_PATH` to point to an Ed25519 private key PEM.
|
|
10
|
+
*/
|
|
11
|
+
export declare function mintOtp(provider: string, method: string, dotPath: string, payload: Record<string, unknown>, maxSpendUsd: number, ttlSeconds: number): string;
|
|
12
|
+
/**
|
|
13
|
+
* Generate a fresh Ed25519 key pair for the pay gate.
|
|
14
|
+
*/
|
|
15
|
+
export declare function generateKeyPair(): {
|
|
16
|
+
publicKeyPem: string;
|
|
17
|
+
privateKeyPem: string;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=paygate-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paygate-cli.d.ts","sourceRoot":"","sources":["../../src/paygate-cli.ts"],"names":[],"mappings":";AAkBA;;GAEG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAqB5C;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,MAAM,CAuCR;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB,CAMA"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { sign, generateKeyPairSync, randomBytes } from "node:crypto";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { canonicalHash } from "./paygate.js";
|
|
6
|
+
/**
|
|
7
|
+
* Encode a buffer to unpadded base64url.
|
|
8
|
+
*/
|
|
9
|
+
function base64urlEncode(data) {
|
|
10
|
+
return data
|
|
11
|
+
.toString("base64")
|
|
12
|
+
.replace(/\+/g, "-")
|
|
13
|
+
.replace(/\//g, "_")
|
|
14
|
+
.replace(/=+$/g, "");
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Parse a TTL string like "10m", "1h", "30s" into seconds.
|
|
18
|
+
*/
|
|
19
|
+
export function parseTtl(ttl) {
|
|
20
|
+
const match = ttl.match(/^(\d+)([smhd])$/i);
|
|
21
|
+
if (!match) {
|
|
22
|
+
throw new Error(`Invalid TTL format: ${ttl}. Expected format like 10m, 1h, 30s.`);
|
|
23
|
+
}
|
|
24
|
+
const value = parseInt(match[1], 10);
|
|
25
|
+
const unit = match[2].toLowerCase();
|
|
26
|
+
switch (unit) {
|
|
27
|
+
case "s":
|
|
28
|
+
return value;
|
|
29
|
+
case "m":
|
|
30
|
+
return value * 60;
|
|
31
|
+
case "h":
|
|
32
|
+
return value * 60 * 60;
|
|
33
|
+
case "d":
|
|
34
|
+
return value * 60 * 60 * 24;
|
|
35
|
+
default:
|
|
36
|
+
throw new Error(`Unknown TTL unit: ${unit}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Mint an OTP for a specific request.
|
|
41
|
+
*
|
|
42
|
+
* Requires `APICITY_PAYGATE_PRIVATE_KEY_PATH` to point to an Ed25519 private key PEM.
|
|
43
|
+
*/
|
|
44
|
+
export function mintOtp(provider, method, dotPath, payload, maxSpendUsd, ttlSeconds) {
|
|
45
|
+
const privateKeyPath = process.env.APICITY_PAYGATE_PRIVATE_KEY_PATH;
|
|
46
|
+
if (!privateKeyPath) {
|
|
47
|
+
throw new Error("APICITY_PAYGATE_PRIVATE_KEY_PATH is not set. " +
|
|
48
|
+
"Export the path to your Ed25519 private key PEM.");
|
|
49
|
+
}
|
|
50
|
+
const privateKeyPem = readFileSync(privateKeyPath, "utf8");
|
|
51
|
+
const jti = randomBytes(16).toString("hex");
|
|
52
|
+
const iat = Math.floor(Date.now() / 1000);
|
|
53
|
+
const exp = iat + ttlSeconds;
|
|
54
|
+
const payloadObj = {
|
|
55
|
+
v: 1,
|
|
56
|
+
jti,
|
|
57
|
+
provider,
|
|
58
|
+
method,
|
|
59
|
+
dotPath,
|
|
60
|
+
requestHash: canonicalHash(payload),
|
|
61
|
+
maxSpendUsd,
|
|
62
|
+
iat,
|
|
63
|
+
exp,
|
|
64
|
+
};
|
|
65
|
+
const payloadJson = JSON.stringify(payloadObj);
|
|
66
|
+
const payloadSegment = base64urlEncode(Buffer.from(payloadJson, "utf8"));
|
|
67
|
+
const signature = sign(null, Buffer.from(payloadSegment, "utf8"), privateKeyPem);
|
|
68
|
+
const signatureSegment = base64urlEncode(signature);
|
|
69
|
+
return `${payloadSegment}.${signatureSegment}`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Generate a fresh Ed25519 key pair for the pay gate.
|
|
73
|
+
*/
|
|
74
|
+
export function generateKeyPair() {
|
|
75
|
+
const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
|
|
76
|
+
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
77
|
+
privateKeyEncoding: { type: "pkcs8", format: "pem" },
|
|
78
|
+
});
|
|
79
|
+
return { publicKeyPem: publicKey, privateKeyPem: privateKey };
|
|
80
|
+
}
|
|
81
|
+
function parseMintArgs(argv) {
|
|
82
|
+
const out = {};
|
|
83
|
+
for (let i = 0; i < argv.length; i++) {
|
|
84
|
+
const a = argv[i];
|
|
85
|
+
if (a === "--provider")
|
|
86
|
+
out.provider = argv[++i];
|
|
87
|
+
else if (a.startsWith("--provider="))
|
|
88
|
+
out.provider = a.slice(11);
|
|
89
|
+
else if (a === "--method")
|
|
90
|
+
out.method = argv[++i];
|
|
91
|
+
else if (a.startsWith("--method="))
|
|
92
|
+
out.method = a.slice(9);
|
|
93
|
+
else if (a === "--dot-path")
|
|
94
|
+
out.dotPath = argv[++i];
|
|
95
|
+
else if (a.startsWith("--dot-path="))
|
|
96
|
+
out.dotPath = a.slice(11);
|
|
97
|
+
else if (a === "--payload-file")
|
|
98
|
+
out.payloadFile = argv[++i];
|
|
99
|
+
else if (a.startsWith("--payload-file="))
|
|
100
|
+
out.payloadFile = a.slice(15);
|
|
101
|
+
else if (a === "--max-spend")
|
|
102
|
+
out.maxSpend = parseFloat(argv[++i]);
|
|
103
|
+
else if (a.startsWith("--max-spend="))
|
|
104
|
+
out.maxSpend = parseFloat(a.slice(12));
|
|
105
|
+
else if (a === "--ttl")
|
|
106
|
+
out.ttl = argv[++i];
|
|
107
|
+
else if (a.startsWith("--ttl="))
|
|
108
|
+
out.ttl = a.slice(6);
|
|
109
|
+
else {
|
|
110
|
+
console.error(`[apicity-paygate] unknown arg: ${a}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const required = [
|
|
114
|
+
"provider",
|
|
115
|
+
"method",
|
|
116
|
+
"dotPath",
|
|
117
|
+
"payloadFile",
|
|
118
|
+
"maxSpend",
|
|
119
|
+
"ttl",
|
|
120
|
+
];
|
|
121
|
+
for (const key of required) {
|
|
122
|
+
if (out[key] === undefined || out[key] === null) {
|
|
123
|
+
throw new Error(`Missing required argument: --${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
function printHelp() {
|
|
129
|
+
console.error([
|
|
130
|
+
"apicity-paygate — Mint OTPs for paid @apicity endpoints.",
|
|
131
|
+
"",
|
|
132
|
+
"Usage:",
|
|
133
|
+
" apicity-paygate otp mint \\",
|
|
134
|
+
" --provider <provider> \\",
|
|
135
|
+
" --method <HTTP method> \\",
|
|
136
|
+
" --dot-path <api.path> \\",
|
|
137
|
+
" --payload-file <path> \\",
|
|
138
|
+
" --max-spend <usd> \\",
|
|
139
|
+
" --ttl <duration>",
|
|
140
|
+
"",
|
|
141
|
+
"Options:",
|
|
142
|
+
" --provider Provider name (e.g. kie, openai, xai)",
|
|
143
|
+
" --method HTTP method (e.g. POST, GET)",
|
|
144
|
+
" --dot-path API dot-path (e.g. api.v1.jobs.createTask)",
|
|
145
|
+
" --payload-file Path to JSON request payload file",
|
|
146
|
+
" --max-spend Maximum spend in USD",
|
|
147
|
+
" --ttl Time-to-live: 10m, 1h, 30s, 1d",
|
|
148
|
+
"",
|
|
149
|
+
"Environment:",
|
|
150
|
+
" APICITY_PAYGATE_PRIVATE_KEY_PATH Path to Ed25519 private key PEM",
|
|
151
|
+
].join("\n"));
|
|
152
|
+
}
|
|
153
|
+
async function main() {
|
|
154
|
+
const argv = process.argv.slice(2);
|
|
155
|
+
if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
|
|
156
|
+
printHelp();
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
if (argv[0] !== "otp" || argv[1] !== "mint") {
|
|
160
|
+
console.error("[apicity-paygate] only 'otp mint' is supported.");
|
|
161
|
+
printHelp();
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
const args = parseMintArgs(argv.slice(2));
|
|
165
|
+
const payload = JSON.parse(readFileSync(args.payloadFile, "utf8"));
|
|
166
|
+
const ttlSeconds = parseTtl(args.ttl);
|
|
167
|
+
const otp = mintOtp(args.provider, args.method, args.dotPath, payload, args.maxSpend, ttlSeconds);
|
|
168
|
+
console.log(otp);
|
|
169
|
+
}
|
|
170
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
171
|
+
if (process.argv[1] === __filename) {
|
|
172
|
+
main().catch((err) => {
|
|
173
|
+
console.error("[apicity-paygate] fatal:", err instanceof Error ? err.message : err);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=paygate-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paygate-cli.js","sourceRoot":"","sources":["../../src/paygate-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTP payload schema.
|
|
3
|
+
*/
|
|
4
|
+
export interface PayGateOtpPayload {
|
|
5
|
+
v: 1;
|
|
6
|
+
jti: string;
|
|
7
|
+
provider: string;
|
|
8
|
+
method: string;
|
|
9
|
+
dotPath: string;
|
|
10
|
+
requestHash: `sha256:${string}`;
|
|
11
|
+
maxSpendUsd: number;
|
|
12
|
+
iat: number;
|
|
13
|
+
exp: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Caller-supplied approval object.
|
|
17
|
+
*/
|
|
18
|
+
export interface PayGateApproval {
|
|
19
|
+
otp: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Error thrown when the pay gate blocks a request for any reason
|
|
23
|
+
* other than spend bounds (which use SpendBoundError).
|
|
24
|
+
*/
|
|
25
|
+
export declare class PayGateError extends Error {
|
|
26
|
+
readonly provider: string;
|
|
27
|
+
readonly method: string;
|
|
28
|
+
readonly dotPath: string;
|
|
29
|
+
readonly code: "paygate-not-configured" | "otp-missing" | "otp-malformed" | "otp-invalid-signature" | "otp-expired" | "otp-mismatched-request" | "otp-replayed";
|
|
30
|
+
constructor(provider: string, method: string, dotPath: string, code: PayGateError["code"], message: string);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Canonicalize a JSON value by sorting object keys recursively.
|
|
34
|
+
* Arrays preserve order. Non-JSON values (undefined, functions, symbols,
|
|
35
|
+
* circular references) cause a TypeError so the caller can fail closed.
|
|
36
|
+
*/
|
|
37
|
+
export declare function canonicalizeJson(value: unknown): string;
|
|
38
|
+
/**
|
|
39
|
+
* Compute SHA-256 of canonical JSON, prefixed with `sha256:`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function canonicalHash(value: unknown): `sha256:${string}`;
|
|
42
|
+
/**
|
|
43
|
+
* Parse an OTP envelope: `<base64url(payloadJson)>.<base64url(signature)>`.
|
|
44
|
+
* Returns the payload object and raw signature bytes.
|
|
45
|
+
*/
|
|
46
|
+
export declare function parseOtp(otp: string): {
|
|
47
|
+
payload: PayGateOtpPayload;
|
|
48
|
+
signature: Buffer;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Verify the Ed25519 signature of an OTP payload segment.
|
|
52
|
+
* `publicKey` is the PEM string read from the public key file.
|
|
53
|
+
*/
|
|
54
|
+
export declare function verifyOtpSignature(payloadSegmentBase64url: string, signature: Buffer, publicKeyPem: string): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Check whether a jti has already been consumed.
|
|
57
|
+
*
|
|
58
|
+
* @param jti - The OTP jti to check
|
|
59
|
+
* @param ledgerPath - Optional override path; defaults to XDG_STATE_HOME or ~/.local/state
|
|
60
|
+
*/
|
|
61
|
+
export declare function isJtiConsumed(jti: string, ledgerPath?: string): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Append a jti to the replay ledger.
|
|
64
|
+
*
|
|
65
|
+
* @param jti - The OTP jti to consume
|
|
66
|
+
* @param ledgerPath - Optional override path; defaults to XDG_STATE_HOME or ~/.local/state
|
|
67
|
+
*/
|
|
68
|
+
export declare function consumeJti(jti: string, ledgerPath?: string): void;
|
|
69
|
+
/**
|
|
70
|
+
* Wrap a provider network dispatch with the OTP-based paid-endpoint gate.
|
|
71
|
+
*
|
|
72
|
+
* Free/unlisted endpoints return `dispatch()` immediately without OTP or
|
|
73
|
+
* pay gate configuration.
|
|
74
|
+
*
|
|
75
|
+
* Paid endpoints fail closed: if the pay gate is not configured, or the OTP
|
|
76
|
+
* is missing, invalid, expired, replayed, mismatched, or the cost exceeds the
|
|
77
|
+
* OTP's maxSpendUsd, the call throws before dispatch runs.
|
|
78
|
+
*/
|
|
79
|
+
export declare function dispatchWithPaidGate<T>(provider: string, method: string, dotPath: string, payload: Record<string, unknown>, approval: PayGateApproval | undefined, dispatch: () => Promise<T>): Promise<T>;
|
|
80
|
+
//# sourceMappingURL=paygate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paygate.d.ts","sourceRoot":"","sources":["../../src/paygate.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,CAAC,EAAE,CAAC,CAAC;IACL,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,UAAU,MAAM,EAAE,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EACT,wBAAwB,GACxB,aAAa,GACb,eAAe,GACf,uBAAuB,GACvB,aAAa,GACb,wBAAwB,GACxB,cAAc,CAAC;gBAGjB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,EAC1B,OAAO,EAAE,MAAM;CASlB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAsCvD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,MAAM,EAAE,CAIhE;AAaD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG;IACrC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB,CAyCA;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,uBAAuB,EAAE,MAAM,EAC/B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAIT;AAaD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAA4B,GACvC,OAAO,CAkBT;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAA4B,GACvC,IAAI,CAUN;AAuGD;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAC1C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,QAAQ,EAAE,eAAe,GAAG,SAAS,EACrC,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC,CAsEZ"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { createHash, createPublicKey, verify } from "node:crypto";
|
|
2
|
+
import { readFileSync, appendFileSync, existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { computeEstimate } from "./compute.js";
|
|
6
|
+
import { isPaidEndpoint } from "./paid-endpoints.js";
|
|
7
|
+
import { SpendBoundError } from "./paid-endpoints.js";
|
|
8
|
+
/**
|
|
9
|
+
* Error thrown when the pay gate blocks a request for any reason
|
|
10
|
+
* other than spend bounds (which use SpendBoundError).
|
|
11
|
+
*/
|
|
12
|
+
export class PayGateError extends Error {
|
|
13
|
+
provider;
|
|
14
|
+
method;
|
|
15
|
+
dotPath;
|
|
16
|
+
code;
|
|
17
|
+
constructor(provider, method, dotPath, code, message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "PayGateError";
|
|
20
|
+
this.provider = provider;
|
|
21
|
+
this.method = method;
|
|
22
|
+
this.dotPath = dotPath;
|
|
23
|
+
this.code = code;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Canonicalize a JSON value by sorting object keys recursively.
|
|
28
|
+
* Arrays preserve order. Non-JSON values (undefined, functions, symbols,
|
|
29
|
+
* circular references) cause a TypeError so the caller can fail closed.
|
|
30
|
+
*/
|
|
31
|
+
export function canonicalizeJson(value) {
|
|
32
|
+
const seen = new WeakSet();
|
|
33
|
+
function walk(v) {
|
|
34
|
+
if (v === null ||
|
|
35
|
+
typeof v === "boolean" ||
|
|
36
|
+
typeof v === "number" ||
|
|
37
|
+
typeof v === "string") {
|
|
38
|
+
return v;
|
|
39
|
+
}
|
|
40
|
+
if (typeof v === "undefined" ||
|
|
41
|
+
typeof v === "function" ||
|
|
42
|
+
typeof v === "symbol") {
|
|
43
|
+
throw new TypeError("Cannot canonicalize non-JSON value: " + typeof v);
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(v)) {
|
|
46
|
+
return v.map(walk);
|
|
47
|
+
}
|
|
48
|
+
if (typeof v === "object") {
|
|
49
|
+
if (seen.has(v)) {
|
|
50
|
+
throw new TypeError("Cannot canonicalize circular reference");
|
|
51
|
+
}
|
|
52
|
+
seen.add(v);
|
|
53
|
+
const sortedKeys = Object.keys(v).sort();
|
|
54
|
+
const out = {};
|
|
55
|
+
for (const k of sortedKeys) {
|
|
56
|
+
out[k] = walk(v[k]);
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
throw new TypeError("Cannot canonicalize unexpected type: " + typeof v);
|
|
61
|
+
}
|
|
62
|
+
return JSON.stringify(walk(value));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Compute SHA-256 of canonical JSON, prefixed with `sha256:`.
|
|
66
|
+
*/
|
|
67
|
+
export function canonicalHash(value) {
|
|
68
|
+
const canonical = canonicalizeJson(value);
|
|
69
|
+
const hash = createHash("sha256").update(canonical, "utf8").digest("hex");
|
|
70
|
+
return `sha256:${hash}`;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Decode base64url (no padding required).
|
|
74
|
+
*/
|
|
75
|
+
function base64urlDecode(str) {
|
|
76
|
+
// Replace base64url chars with base64 chars and add padding
|
|
77
|
+
const base64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
78
|
+
const padLen = (4 - (base64.length % 4)) % 4;
|
|
79
|
+
const padded = base64 + "=".repeat(padLen);
|
|
80
|
+
return Buffer.from(padded, "base64");
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Parse an OTP envelope: `<base64url(payloadJson)>.<base64url(signature)>`.
|
|
84
|
+
* Returns the payload object and raw signature bytes.
|
|
85
|
+
*/
|
|
86
|
+
export function parseOtp(otp) {
|
|
87
|
+
const parts = otp.split(".");
|
|
88
|
+
if (parts.length !== 2) {
|
|
89
|
+
throw new Error("OTP must contain exactly one '.' separator");
|
|
90
|
+
}
|
|
91
|
+
const payloadJson = base64urlDecode(parts[0]).toString("utf8");
|
|
92
|
+
const signature = base64urlDecode(parts[1]);
|
|
93
|
+
let payload;
|
|
94
|
+
try {
|
|
95
|
+
payload = JSON.parse(payloadJson);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
throw new Error("OTP payload is not valid JSON");
|
|
99
|
+
}
|
|
100
|
+
if (typeof payload !== "object" ||
|
|
101
|
+
payload === null ||
|
|
102
|
+
!("v" in payload) ||
|
|
103
|
+
payload.v !== 1 ||
|
|
104
|
+
!("jti" in payload) ||
|
|
105
|
+
typeof payload.jti !== "string" ||
|
|
106
|
+
!("provider" in payload) ||
|
|
107
|
+
typeof payload.provider !== "string" ||
|
|
108
|
+
!("method" in payload) ||
|
|
109
|
+
typeof payload.method !== "string" ||
|
|
110
|
+
!("dotPath" in payload) ||
|
|
111
|
+
typeof payload.dotPath !== "string" ||
|
|
112
|
+
!("requestHash" in payload) ||
|
|
113
|
+
typeof payload.requestHash !== "string" ||
|
|
114
|
+
!("maxSpendUsd" in payload) ||
|
|
115
|
+
typeof payload.maxSpendUsd !== "number" ||
|
|
116
|
+
!("iat" in payload) ||
|
|
117
|
+
typeof payload.iat !== "number" ||
|
|
118
|
+
!("exp" in payload) ||
|
|
119
|
+
typeof payload.exp !== "number") {
|
|
120
|
+
throw new Error("OTP payload missing required fields");
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
payload: payload,
|
|
124
|
+
signature,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Verify the Ed25519 signature of an OTP payload segment.
|
|
129
|
+
* `publicKey` is the PEM string read from the public key file.
|
|
130
|
+
*/
|
|
131
|
+
export function verifyOtpSignature(payloadSegmentBase64url, signature, publicKeyPem) {
|
|
132
|
+
const key = createPublicKey(publicKeyPem);
|
|
133
|
+
const data = Buffer.from(payloadSegmentBase64url, "utf8");
|
|
134
|
+
return verify(null, data, key, signature);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Resolve the default replay ledger path.
|
|
138
|
+
*/
|
|
139
|
+
function defaultLedgerPath() {
|
|
140
|
+
const xdg = process.env.XDG_STATE_HOME;
|
|
141
|
+
if (xdg) {
|
|
142
|
+
return join(xdg, "apicity", "paygate-used.jsonl");
|
|
143
|
+
}
|
|
144
|
+
return join(homedir(), ".local", "state", "apicity", "paygate-used.jsonl");
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Check whether a jti has already been consumed.
|
|
148
|
+
*
|
|
149
|
+
* @param jti - The OTP jti to check
|
|
150
|
+
* @param ledgerPath - Optional override path; defaults to XDG_STATE_HOME or ~/.local/state
|
|
151
|
+
*/
|
|
152
|
+
export function isJtiConsumed(jti, ledgerPath = defaultLedgerPath()) {
|
|
153
|
+
if (!existsSync(ledgerPath)) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
const content = readFileSync(ledgerPath, "utf8");
|
|
157
|
+
for (const line of content.split("\n")) {
|
|
158
|
+
const trimmed = line.trim();
|
|
159
|
+
if (!trimmed)
|
|
160
|
+
continue;
|
|
161
|
+
try {
|
|
162
|
+
const entry = JSON.parse(trimmed);
|
|
163
|
+
if (entry.jti === jti) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// Skip malformed lines
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Append a jti to the replay ledger.
|
|
175
|
+
*
|
|
176
|
+
* @param jti - The OTP jti to consume
|
|
177
|
+
* @param ledgerPath - Optional override path; defaults to XDG_STATE_HOME or ~/.local/state
|
|
178
|
+
*/
|
|
179
|
+
export function consumeJti(jti, ledgerPath = defaultLedgerPath()) {
|
|
180
|
+
const dir = join(ledgerPath, "..");
|
|
181
|
+
if (!existsSync(dir)) {
|
|
182
|
+
mkdirSync(dir, { recursive: true });
|
|
183
|
+
}
|
|
184
|
+
const entry = JSON.stringify({
|
|
185
|
+
jti,
|
|
186
|
+
consumedAt: Math.floor(Date.now() / 1000),
|
|
187
|
+
});
|
|
188
|
+
appendFileSync(ledgerPath, entry + "\n", "utf8");
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Load the Ed25519 public key PEM from the configured path.
|
|
192
|
+
*/
|
|
193
|
+
function loadPublicKey() {
|
|
194
|
+
const path = process.env.APICITY_PAYGATE_PUBLIC_KEY_PATH;
|
|
195
|
+
if (!path) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
return readFileSync(path, "utf8");
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Verify an OTP against the public key, checking signature, expiry,
|
|
202
|
+
* request binding, and replay.
|
|
203
|
+
*/
|
|
204
|
+
function verifyOtp(provider, method, dotPath, payload, otp, publicKeyPem) {
|
|
205
|
+
let parsed;
|
|
206
|
+
try {
|
|
207
|
+
parsed = parseOtp(otp);
|
|
208
|
+
}
|
|
209
|
+
catch (e) {
|
|
210
|
+
throw new PayGateError(provider, method, dotPath, "otp-malformed", e instanceof Error ? e.message : "OTP is malformed");
|
|
211
|
+
}
|
|
212
|
+
const { payload: otpPayload, signature } = parsed;
|
|
213
|
+
const parts = otp.split(".");
|
|
214
|
+
const payloadSegment = parts[0];
|
|
215
|
+
const sigOk = verifyOtpSignature(payloadSegment, signature, publicKeyPem);
|
|
216
|
+
if (!sigOk) {
|
|
217
|
+
throw new PayGateError(provider, method, dotPath, "otp-invalid-signature", "OTP signature is invalid");
|
|
218
|
+
}
|
|
219
|
+
const now = Math.floor(Date.now() / 1000);
|
|
220
|
+
if (otpPayload.exp < now) {
|
|
221
|
+
throw new PayGateError(provider, method, dotPath, "otp-expired", `OTP expired at ${otpPayload.exp} (now is ${now})`);
|
|
222
|
+
}
|
|
223
|
+
if (otpPayload.provider !== provider ||
|
|
224
|
+
otpPayload.method !== method ||
|
|
225
|
+
otpPayload.dotPath !== dotPath) {
|
|
226
|
+
throw new PayGateError(provider, method, dotPath, "otp-mismatched-request", `OTP bound to ${otpPayload.provider} ${otpPayload.method} ${otpPayload.dotPath}, ` +
|
|
227
|
+
`but call is ${provider} ${method} ${dotPath}`);
|
|
228
|
+
}
|
|
229
|
+
const expectedHash = canonicalHash(payload);
|
|
230
|
+
if (otpPayload.requestHash !== expectedHash) {
|
|
231
|
+
throw new PayGateError(provider, method, dotPath, "otp-mismatched-request", `OTP request hash mismatch: expected ${expectedHash}, got ${otpPayload.requestHash}`);
|
|
232
|
+
}
|
|
233
|
+
if (isJtiConsumed(otpPayload.jti)) {
|
|
234
|
+
throw new PayGateError(provider, method, dotPath, "otp-replayed", `OTP jti ${otpPayload.jti} has already been consumed`);
|
|
235
|
+
}
|
|
236
|
+
return otpPayload;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Wrap a provider network dispatch with the OTP-based paid-endpoint gate.
|
|
240
|
+
*
|
|
241
|
+
* Free/unlisted endpoints return `dispatch()` immediately without OTP or
|
|
242
|
+
* pay gate configuration.
|
|
243
|
+
*
|
|
244
|
+
* Paid endpoints fail closed: if the pay gate is not configured, or the OTP
|
|
245
|
+
* is missing, invalid, expired, replayed, mismatched, or the cost exceeds the
|
|
246
|
+
* OTP's maxSpendUsd, the call throws before dispatch runs.
|
|
247
|
+
*/
|
|
248
|
+
export async function dispatchWithPaidGate(provider, method, dotPath, payload, approval, dispatch) {
|
|
249
|
+
if (!isPaidEndpoint(provider, method, dotPath)) {
|
|
250
|
+
return dispatch();
|
|
251
|
+
}
|
|
252
|
+
const publicKeyPem = loadPublicKey();
|
|
253
|
+
if (!publicKeyPem) {
|
|
254
|
+
throw new PayGateError(provider, method, dotPath, "paygate-not-configured", "Pay gate is not configured: APICITY_PAYGATE_PUBLIC_KEY_PATH is not set");
|
|
255
|
+
}
|
|
256
|
+
if (!approval || !approval.otp) {
|
|
257
|
+
throw new PayGateError(provider, method, dotPath, "otp-missing", "Paid endpoint requires an OTP approval. Pass { otp: '...' }.");
|
|
258
|
+
}
|
|
259
|
+
const otpPayload = verifyOtp(provider, method, dotPath, payload, approval.otp, publicKeyPem);
|
|
260
|
+
const estimate = computeEstimate({
|
|
261
|
+
provider: provider,
|
|
262
|
+
payload,
|
|
263
|
+
});
|
|
264
|
+
if (estimate.warnings.length > 0) {
|
|
265
|
+
throw new SpendBoundError(provider, method, dotPath, otpPayload.maxSpendUsd, estimate.usd, `Endpoint ${provider} ${method} ${dotPath} spend cannot be bounded ` +
|
|
266
|
+
`from the payload: ${estimate.warnings.join("; ")}. ` +
|
|
267
|
+
`Pass an OTP with a higher maxSpendUsd, ` +
|
|
268
|
+
`or adjust the payload so the cost can be estimated.`);
|
|
269
|
+
}
|
|
270
|
+
if (estimate.usd > otpPayload.maxSpendUsd) {
|
|
271
|
+
throw new SpendBoundError(provider, method, dotPath, otpPayload.maxSpendUsd, estimate.usd, `Endpoint ${provider} ${method} ${dotPath} estimated cost ` +
|
|
272
|
+
`(${estimate.usd} USD) exceeds OTP maxSpendUsd (${otpPayload.maxSpendUsd} USD).`);
|
|
273
|
+
}
|
|
274
|
+
// Consume the OTP immediately before dispatch.
|
|
275
|
+
consumeJti(otpPayload.jti);
|
|
276
|
+
return dispatch();
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=paygate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paygate.js","sourceRoot":"","sources":["../../src/paygate.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kie.d.ts","sourceRoot":"","sources":["../../../src/pricing/kie.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"kie.d.ts","sourceRoot":"","sources":["../../../src/pricing/kie.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAiG5C,eAAO,MAAM,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAqQ5C,CAAC"}
|
package/dist/src/pricing/kie.js
CHANGED
|
@@ -40,20 +40,34 @@ const flatGen = (perUnit, slug) => ({
|
|
|
40
40
|
source: src(slug),
|
|
41
41
|
});
|
|
42
42
|
// Image entry tiered by input.resolution (e.g. "1K"|"2K"|"4K").
|
|
43
|
-
|
|
43
|
+
// Optional `defaultResolution` is applied when the payload omits
|
|
44
|
+
// input.resolution (matches the upstream schema default).
|
|
45
|
+
const tieredImage = (rates, slug, defaultResolution) => ({
|
|
44
46
|
kind: "perUnit",
|
|
45
47
|
unit: "images",
|
|
46
48
|
units: imageCount,
|
|
47
|
-
select: [
|
|
49
|
+
select: [
|
|
50
|
+
{
|
|
51
|
+
name: "resolution",
|
|
52
|
+
pick: (p) => asString(asObject(p.input)?.resolution) ?? defaultResolution,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
48
55
|
rates,
|
|
49
56
|
source: src(slug),
|
|
50
57
|
});
|
|
51
58
|
// Video entry tiered by input.resolution (grok-imagine, happyhorse).
|
|
52
|
-
|
|
59
|
+
// Optional `defaultResolution` is applied when the payload omits
|
|
60
|
+
// input.resolution (matches the upstream schema default).
|
|
61
|
+
const tieredResolutionVideo = (rates, slug, defaultResolution) => ({
|
|
53
62
|
kind: "perUnit",
|
|
54
63
|
unit: "seconds",
|
|
55
64
|
units: seconds,
|
|
56
|
-
select: [
|
|
65
|
+
select: [
|
|
66
|
+
{
|
|
67
|
+
name: "resolution",
|
|
68
|
+
pick: (p) => asString(asObject(p.input)?.resolution) ?? defaultResolution,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
57
71
|
rates,
|
|
58
72
|
source: src(slug),
|
|
59
73
|
});
|
|
@@ -221,12 +235,12 @@ export const kie = {
|
|
|
221
235
|
// (nano-banana-2, gpt-image-2) require input.resolution; flat-rate
|
|
222
236
|
// families (qwen2, seedream/5-lite) only need the model string.
|
|
223
237
|
// wan/2-7-image accepts an `n` field for batch generation.
|
|
224
|
-
"nano-banana-2": tieredImage({ "1K": 0.04, "2K": 0.06, "4K": 0.09 }, "google/nano-banana-2"),
|
|
238
|
+
"nano-banana-2": tieredImage({ "1K": 0.04, "2K": 0.06, "4K": 0.09 }, "google/nano-banana-2", "2K"),
|
|
225
239
|
// nano-banana-pro: 1K and 2K share the $0.09 rate per the marketplace
|
|
226
240
|
// ("1/2K"), 4K is $0.12.
|
|
227
|
-
"nano-banana-pro": tieredImage({ "1K": 0.09, "2K": 0.09, "4K": 0.12 }, "google/nano-banana-pro"),
|
|
228
|
-
"gpt-image-2-text-to-image": tieredImage({ "1K": 0.03, "2K": 0.05, "4K": 0.08 }, "openai/gpt-image-2"),
|
|
229
|
-
"gpt-image-2-image-to-image": tieredImage({ "1K": 0.03, "2K": 0.05, "4K": 0.08 }, "openai/gpt-image-2"),
|
|
241
|
+
"nano-banana-pro": tieredImage({ "1K": 0.09, "2K": 0.09, "4K": 0.12 }, "google/nano-banana-pro", "2K"),
|
|
242
|
+
"gpt-image-2-text-to-image": tieredImage({ "1K": 0.03, "2K": 0.05, "4K": 0.08 }, "openai/gpt-image-2", "2K"),
|
|
243
|
+
"gpt-image-2-image-to-image": tieredImage({ "1K": 0.03, "2K": 0.05, "4K": 0.08 }, "openai/gpt-image-2", "2K"),
|
|
230
244
|
"wan/2-7-image": flatImage(0.024, "alibaba/wan-2.7"),
|
|
231
245
|
"wan/2-7-image-pro": flatImage(0.06, "alibaba/wan-2.7"),
|
|
232
246
|
"qwen2/text-to-image": flatImage(0.028, "alibaba/qwen-image-2"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kie.js","sourceRoot":"","sources":["../../../src/pricing/kie.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAExE,uEAAuE;AACvE,6EAA6E;AAC7E,2EAA2E;AAC3E,oEAAoE;AACpE,wEAAwE;AAExE,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,yBAAyB,IAAI,EAAE,EAAE,CAAC,CAAC;AAEzE,6EAA6E;AAC7E,0EAA0E;AAC1E,MAAM,OAAO,GAAG,CAAC,CAA0B,EAAsB,EAAE,CACjE,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC;AAE3D,MAAM,eAAe,GAAG,CAAC,CAA0B,EAAsB,EAAE,CACzE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC;AAE1C,MAAM,SAAS,GAAG,CAAC,CAA0B,EAAsB,EAAE,CACnE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;AAEpC,mEAAmE;AACnE,4DAA4D;AAC5D,MAAM,UAAU,GAAG,CAAC,CAA0B,EAAU,EAAE,CACxD,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;AAEtC,MAAM,SAAS,GAAG,CAAC,OAAe,EAAE,IAAY,EAAgB,EAAE,CAAC,CAAC;IAClE,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,EAAE;IACV,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;IACtB,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,CAAC,OAAe,EAAE,IAAY,EAAgB,EAAE,CAAC,CAAC;IAClE,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,QAAQ;IACd,KAAK,EAAE,UAAU;IACjB,MAAM,EAAE,EAAE;IACV,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;IACtB,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC;CAClB,CAAC,CAAC;AAEH,yEAAyE;AACzE,iEAAiE;AACjE,MAAM,OAAO,GAAG,CAAC,OAAe,EAAE,IAAY,EAAgB,EAAE,CAAC,CAAC;IAChE,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IACd,MAAM,EAAE,EAAE;IACV,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;IACtB,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC;CAClB,CAAC,CAAC;AAEH,gEAAgE;AAChE,MAAM,WAAW,GAAG,CAClB,KAA6B,EAC7B,IAAY,
|
|
1
|
+
{"version":3,"file":"kie.js","sourceRoot":"","sources":["../../../src/pricing/kie.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apicity/cost",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.1",
|
|
4
4
|
"description": "Pure-table cost & token estimation across @apicity providers — zero network calls, all rates bundled.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
"types": "./dist/src/index.d.ts"
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
|
+
"bin": {
|
|
24
|
+
"apicity-paygate": "./dist/src/paygate-cli.js"
|
|
25
|
+
},
|
|
23
26
|
"files": [
|
|
24
27
|
"dist",
|
|
25
28
|
"README.md",
|