@apicity/cost 0.2.0-alpha.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -147
- package/dist/src/cost.d.ts +1 -1
- package/dist/src/cost.d.ts.map +1 -1
- package/dist/src/cost.js +1 -1
- package/dist/src/cost.js.map +1 -1
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/paid-endpoints.d.ts +0 -64
- package/dist/src/paid-endpoints.d.ts.map +1 -1
- package/dist/src/paid-endpoints.js +9 -113
- package/dist/src/paid-endpoints.js.map +1 -1
- package/dist/src/paygate-cli.d.ts +1 -17
- package/dist/src/paygate-cli.d.ts.map +1 -1
- package/dist/src/paygate-cli.js +23 -103
- package/dist/src/paygate-cli.js.map +1 -1
- package/dist/src/paygate.d.ts +99 -19
- package/dist/src/paygate.d.ts.map +1 -1
- package/dist/src/paygate.js +180 -122
- package/dist/src/paygate.js.map +1 -1
- package/dist/src/with-paid-gate.d.ts +27 -0
- package/dist/src/with-paid-gate.d.ts.map +1 -0
- package/dist/src/with-paid-gate.js +91 -0
- package/dist/src/with-paid-gate.js.map +1 -0
- package/package.json +1 -1
package/dist/src/paygate.js
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
import { createHash,
|
|
2
|
-
import {
|
|
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";
|
|
1
|
+
import { createHash, createHmac, randomBytes, timingSafeEqual, } from "node:crypto";
|
|
2
|
+
import { isPaidEndpoint, PAID_ENDPOINTS } from "./paid-endpoints.js";
|
|
8
3
|
/**
|
|
9
|
-
* Error thrown when the pay gate blocks a request
|
|
10
|
-
* other than spend bounds (which use SpendBoundError).
|
|
4
|
+
* Error thrown when the pay gate blocks a request.
|
|
11
5
|
*/
|
|
12
6
|
export class PayGateError extends Error {
|
|
13
7
|
provider;
|
|
@@ -69,16 +63,48 @@ export function canonicalHash(value) {
|
|
|
69
63
|
const hash = createHash("sha256").update(canonical, "utf8").digest("hex");
|
|
70
64
|
return `sha256:${hash}`;
|
|
71
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Encode a buffer to unpadded base64url.
|
|
68
|
+
*/
|
|
69
|
+
function base64urlEncode(data) {
|
|
70
|
+
return data
|
|
71
|
+
.toString("base64")
|
|
72
|
+
.replace(/\+/g, "-")
|
|
73
|
+
.replace(/\//g, "_")
|
|
74
|
+
.replace(/=+$/g, "");
|
|
75
|
+
}
|
|
72
76
|
/**
|
|
73
77
|
* Decode base64url (no padding required).
|
|
74
78
|
*/
|
|
75
79
|
function base64urlDecode(str) {
|
|
76
|
-
// Replace base64url chars with base64 chars and add padding
|
|
77
80
|
const base64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
78
81
|
const padLen = (4 - (base64.length % 4)) % 4;
|
|
79
82
|
const padded = base64 + "=".repeat(padLen);
|
|
80
83
|
return Buffer.from(padded, "base64");
|
|
81
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Parse a TTL string like "10m", "1h", "30s", "1d" into seconds.
|
|
87
|
+
*/
|
|
88
|
+
export function parseTtl(ttl) {
|
|
89
|
+
const match = ttl.match(/^(\d+)([smhd])$/i);
|
|
90
|
+
if (!match) {
|
|
91
|
+
throw new Error(`Invalid TTL format: ${ttl}. Expected format like 10m, 1h, 30s.`);
|
|
92
|
+
}
|
|
93
|
+
const value = parseInt(match[1], 10);
|
|
94
|
+
const unit = match[2].toLowerCase();
|
|
95
|
+
switch (unit) {
|
|
96
|
+
case "s":
|
|
97
|
+
return value;
|
|
98
|
+
case "m":
|
|
99
|
+
return value * 60;
|
|
100
|
+
case "h":
|
|
101
|
+
return value * 60 * 60;
|
|
102
|
+
case "d":
|
|
103
|
+
return value * 60 * 60 * 24;
|
|
104
|
+
default:
|
|
105
|
+
throw new Error(`Unknown TTL unit: ${unit}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
82
108
|
/**
|
|
83
109
|
* Parse an OTP envelope: `<base64url(payloadJson)>.<base64url(signature)>`.
|
|
84
110
|
* Returns the payload object and raw signature bytes.
|
|
@@ -99,23 +125,13 @@ export function parseOtp(otp) {
|
|
|
99
125
|
}
|
|
100
126
|
if (typeof payload !== "object" ||
|
|
101
127
|
payload === null ||
|
|
102
|
-
!("v" in payload) ||
|
|
103
128
|
payload.v !== 1 ||
|
|
104
|
-
!("jti" in payload) ||
|
|
105
129
|
typeof payload.jti !== "string" ||
|
|
106
|
-
!("provider" in payload) ||
|
|
107
130
|
typeof payload.provider !== "string" ||
|
|
108
|
-
!("method" in payload) ||
|
|
109
131
|
typeof payload.method !== "string" ||
|
|
110
|
-
!("dotPath" in payload) ||
|
|
111
132
|
typeof payload.dotPath !== "string" ||
|
|
112
|
-
!("requestHash" in payload) ||
|
|
113
133
|
typeof payload.requestHash !== "string" ||
|
|
114
|
-
!("maxSpendUsd" in payload) ||
|
|
115
|
-
typeof payload.maxSpendUsd !== "number" ||
|
|
116
|
-
!("iat" in payload) ||
|
|
117
134
|
typeof payload.iat !== "number" ||
|
|
118
|
-
!("exp" in payload) ||
|
|
119
135
|
typeof payload.exp !== "number") {
|
|
120
136
|
throw new Error("OTP payload missing required fields");
|
|
121
137
|
}
|
|
@@ -125,154 +141,196 @@ export function parseOtp(otp) {
|
|
|
125
141
|
};
|
|
126
142
|
}
|
|
127
143
|
/**
|
|
128
|
-
*
|
|
129
|
-
* `publicKey` is the PEM string read from the public key file.
|
|
144
|
+
* Compute the HMAC-SHA256 of an OTP payload segment with the shared secret.
|
|
130
145
|
*/
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const data = Buffer.from(payloadSegmentBase64url, "utf8");
|
|
134
|
-
return verify(null, data, key, signature);
|
|
146
|
+
function signPayloadSegment(payloadSegment, secret) {
|
|
147
|
+
return createHmac("sha256", secret).update(payloadSegment, "utf8").digest();
|
|
135
148
|
}
|
|
136
149
|
/**
|
|
137
|
-
*
|
|
150
|
+
* Constant-time verification of an OTP payload segment's HMAC signature.
|
|
138
151
|
*/
|
|
139
|
-
function
|
|
140
|
-
const
|
|
141
|
-
if (
|
|
142
|
-
return
|
|
152
|
+
function verifyPayloadSignature(payloadSegment, signature, secret) {
|
|
153
|
+
const expected = signPayloadSegment(payloadSegment, secret);
|
|
154
|
+
if (expected.length !== signature.length) {
|
|
155
|
+
return false;
|
|
143
156
|
}
|
|
144
|
-
return
|
|
157
|
+
return timingSafeEqual(expected, signature);
|
|
145
158
|
}
|
|
146
159
|
/**
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
* @param jti - The OTP jti to check
|
|
150
|
-
* @param ledgerPath - Optional override path; defaults to XDG_STATE_HOME or ~/.local/state
|
|
160
|
+
* Create an in-process, single-use replay store backed by a `Set`.
|
|
161
|
+
* Scoped to whatever holds the reference (typically one provider instance).
|
|
151
162
|
*/
|
|
152
|
-
export function
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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;
|
|
163
|
+
export function createReplayStore() {
|
|
164
|
+
const seen = new Set();
|
|
165
|
+
return {
|
|
166
|
+
has: (jti) => seen.has(jti),
|
|
167
|
+
add: (jti) => {
|
|
168
|
+
seen.add(jti);
|
|
169
|
+
},
|
|
170
|
+
};
|
|
172
171
|
}
|
|
172
|
+
const DEFAULT_TTL_SECONDS = 600;
|
|
173
173
|
/**
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* @param ledgerPath - Optional override path; defaults to XDG_STATE_HOME or ~/.local/state
|
|
174
|
+
* Resolve `(provider, method, dotPath)` for a mint call. When the caller omits
|
|
175
|
+
* provider/method, the dotPath must match exactly one entry in
|
|
176
|
+
* `PAID_ENDPOINTS`.
|
|
178
177
|
*/
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
178
|
+
function resolveCallKey(call) {
|
|
179
|
+
if (call.provider && call.method) {
|
|
180
|
+
return {
|
|
181
|
+
provider: call.provider,
|
|
182
|
+
method: call.method,
|
|
183
|
+
dotPath: call.dotPath,
|
|
184
|
+
};
|
|
183
185
|
}
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
const matches = PAID_ENDPOINTS.filter((e) => e.key.dotPath === call.dotPath &&
|
|
187
|
+
(call.method === undefined || e.key.method === call.method) &&
|
|
188
|
+
(call.provider === undefined || e.key.provider === call.provider));
|
|
189
|
+
if (matches.length === 1) {
|
|
190
|
+
const key = matches[0].key;
|
|
191
|
+
return {
|
|
192
|
+
provider: call.provider ?? key.provider,
|
|
193
|
+
method: call.method ?? key.method,
|
|
194
|
+
dotPath: call.dotPath,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
throw new Error(`Cannot resolve provider/method for dotPath "${call.dotPath}". ` +
|
|
198
|
+
`Pass { provider, method } explicitly.`);
|
|
189
199
|
}
|
|
190
200
|
/**
|
|
191
|
-
*
|
|
201
|
+
* Mint an OTP for a specific request, signed with the shared HMAC secret.
|
|
202
|
+
*
|
|
203
|
+
* Pure and env-free: the secret is passed explicitly. The OTP binds to the
|
|
204
|
+
* exact request via its canonical hash, so changing any byte of the request
|
|
205
|
+
* invalidates the token.
|
|
192
206
|
*/
|
|
193
|
-
function
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return undefined;
|
|
207
|
+
export function mintOtp(secret, call) {
|
|
208
|
+
if (!secret) {
|
|
209
|
+
throw new Error("mintOtp requires a non-empty secret");
|
|
197
210
|
}
|
|
198
|
-
|
|
211
|
+
const key = resolveCallKey(call);
|
|
212
|
+
const ttlSeconds = call.ttl === undefined
|
|
213
|
+
? DEFAULT_TTL_SECONDS
|
|
214
|
+
: typeof call.ttl === "number"
|
|
215
|
+
? call.ttl
|
|
216
|
+
: parseTtl(call.ttl);
|
|
217
|
+
const iat = Math.floor(Date.now() / 1000);
|
|
218
|
+
const payload = {
|
|
219
|
+
v: 1,
|
|
220
|
+
jti: randomBytes(16).toString("hex"),
|
|
221
|
+
provider: key.provider,
|
|
222
|
+
method: key.method,
|
|
223
|
+
dotPath: key.dotPath,
|
|
224
|
+
requestHash: canonicalHash(call.request),
|
|
225
|
+
iat,
|
|
226
|
+
exp: iat + ttlSeconds,
|
|
227
|
+
};
|
|
228
|
+
const payloadSegment = base64urlEncode(Buffer.from(JSON.stringify(payload), "utf8"));
|
|
229
|
+
const signatureSegment = base64urlEncode(signPayloadSegment(payloadSegment, secret));
|
|
230
|
+
return `${payloadSegment}.${signatureSegment}`;
|
|
199
231
|
}
|
|
200
232
|
/**
|
|
201
|
-
*
|
|
202
|
-
*
|
|
233
|
+
* Pure verification of an OTP against expected request context.
|
|
234
|
+
*
|
|
235
|
+
* Returns a tagged-union `VerifyResult` — never throws. The caller is
|
|
236
|
+
* responsible for converting `{ ok: false }` into a `PayGateError` at the
|
|
237
|
+
* boundary.
|
|
203
238
|
*/
|
|
204
|
-
function verifyOtp(
|
|
239
|
+
export function verifyOtp(input) {
|
|
205
240
|
let parsed;
|
|
206
241
|
try {
|
|
207
|
-
parsed = parseOtp(otp);
|
|
242
|
+
parsed = parseOtp(input.otp);
|
|
208
243
|
}
|
|
209
244
|
catch (e) {
|
|
210
|
-
|
|
245
|
+
return {
|
|
246
|
+
ok: false,
|
|
247
|
+
code: "otp-malformed",
|
|
248
|
+
message: e instanceof Error ? e.message : "OTP is malformed",
|
|
249
|
+
};
|
|
211
250
|
}
|
|
212
|
-
const { payload
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
251
|
+
const { payload, signature } = parsed;
|
|
252
|
+
const payloadSegment = input.otp.split(".")[0];
|
|
253
|
+
if (!verifyPayloadSignature(payloadSegment, signature, input.secret)) {
|
|
254
|
+
return {
|
|
255
|
+
ok: false,
|
|
256
|
+
code: "otp-invalid-signature",
|
|
257
|
+
message: "OTP signature is invalid",
|
|
258
|
+
};
|
|
218
259
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
260
|
+
if (payload.exp < input.nowSeconds) {
|
|
261
|
+
return {
|
|
262
|
+
ok: false,
|
|
263
|
+
code: "otp-expired",
|
|
264
|
+
message: `OTP expired at ${payload.exp} (now is ${input.nowSeconds})`,
|
|
265
|
+
};
|
|
222
266
|
}
|
|
223
|
-
if (
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
267
|
+
if (payload.provider !== input.expected.provider ||
|
|
268
|
+
payload.method !== input.expected.method ||
|
|
269
|
+
payload.dotPath !== input.expected.dotPath) {
|
|
270
|
+
return {
|
|
271
|
+
ok: false,
|
|
272
|
+
code: "otp-mismatched-request",
|
|
273
|
+
message: `OTP bound to ${payload.provider} ${payload.method} ${payload.dotPath}, ` +
|
|
274
|
+
`but call is ${input.expected.provider} ${input.expected.method} ${input.expected.dotPath}`,
|
|
275
|
+
};
|
|
228
276
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
277
|
+
if (payload.requestHash !== input.payloadHash) {
|
|
278
|
+
return {
|
|
279
|
+
ok: false,
|
|
280
|
+
code: "otp-mismatched-request",
|
|
281
|
+
message: `OTP request hash mismatch: expected ${input.payloadHash}, got ${payload.requestHash}`,
|
|
282
|
+
};
|
|
232
283
|
}
|
|
233
|
-
if (isJtiConsumed(
|
|
234
|
-
|
|
284
|
+
if (input.isJtiConsumed(payload.jti)) {
|
|
285
|
+
return {
|
|
286
|
+
ok: false,
|
|
287
|
+
code: "otp-replayed",
|
|
288
|
+
message: `OTP jti ${payload.jti} has already been consumed`,
|
|
289
|
+
};
|
|
235
290
|
}
|
|
236
|
-
return
|
|
291
|
+
return { ok: true, jti: payload.jti };
|
|
237
292
|
}
|
|
238
293
|
/**
|
|
239
294
|
* Wrap a provider network dispatch with the OTP-based paid-endpoint gate.
|
|
240
295
|
*
|
|
241
296
|
* Free/unlisted endpoints return `dispatch()` immediately without OTP or
|
|
242
|
-
* pay
|
|
297
|
+
* pay-gate configuration.
|
|
243
298
|
*
|
|
244
299
|
* Paid endpoints fail closed: if the pay gate is not configured, or the OTP
|
|
245
|
-
* is missing, invalid, expired, replayed, mismatched,
|
|
246
|
-
*
|
|
300
|
+
* is missing, invalid, expired, replayed, or mismatched, the call throws
|
|
301
|
+
* before dispatch runs. This is the "no bypass" guarantee — a paid call cannot
|
|
302
|
+
* fire without a configured secret and a valid, human/code-client-minted OTP.
|
|
303
|
+
*
|
|
304
|
+
* The OTP jti is consumed BEFORE dispatch. If dispatch later fails for any
|
|
305
|
+
* reason, the jti remains consumed and the caller must mint a fresh OTP to
|
|
306
|
+
* retry. This is intentional — without it, a hostile caller could replay an
|
|
307
|
+
* OTP on every transient failure.
|
|
247
308
|
*/
|
|
248
|
-
export async function dispatchWithPaidGate(provider, method, dotPath, payload, approval, dispatch) {
|
|
309
|
+
export async function dispatchWithPaidGate(provider, method, dotPath, payload, approval, dispatch, config) {
|
|
249
310
|
if (!isPaidEndpoint(provider, method, dotPath)) {
|
|
250
311
|
return dispatch();
|
|
251
312
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
313
|
+
if (!config || !config.secret) {
|
|
314
|
+
throw new PayGateError(provider, method, dotPath, "paygate-not-configured", `Paid endpoint ${provider} ${method} ${dotPath} requires a pay gate. ` +
|
|
315
|
+
`Construct the provider with { paygate: { secret } }.`);
|
|
255
316
|
}
|
|
256
317
|
if (!approval || !approval.otp) {
|
|
257
318
|
throw new PayGateError(provider, method, dotPath, "otp-missing", "Paid endpoint requires an OTP approval. Pass { otp: '...' }.");
|
|
258
319
|
}
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
320
|
+
const store = config.replayStore ?? createReplayStore();
|
|
321
|
+
const now = config.now ?? (() => Date.now());
|
|
322
|
+
const result = verifyOtp({
|
|
323
|
+
nowSeconds: Math.floor(now() / 1000),
|
|
324
|
+
secret: config.secret,
|
|
325
|
+
expected: { provider, method, dotPath },
|
|
326
|
+
payloadHash: canonicalHash(payload),
|
|
327
|
+
otp: approval.otp,
|
|
328
|
+
isJtiConsumed: (jti) => store.has(jti),
|
|
263
329
|
});
|
|
264
|
-
if (
|
|
265
|
-
throw new
|
|
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).`);
|
|
330
|
+
if (!result.ok) {
|
|
331
|
+
throw new PayGateError(provider, method, dotPath, result.code, result.message);
|
|
273
332
|
}
|
|
274
|
-
|
|
275
|
-
consumeJti(otpPayload.jti);
|
|
333
|
+
store.add(result.jti);
|
|
276
334
|
return dispatch();
|
|
277
335
|
}
|
|
278
336
|
//# sourceMappingURL=paygate.js.map
|
package/dist/src/paygate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paygate.js","sourceRoot":"","sources":["../../src/paygate.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"paygate.js","sourceRoot":"","sources":["../../src/paygate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type PayGateConfig } from "./paygate";
|
|
2
|
+
export interface WithPaidGateOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Top-level keys to recurse into. Default: HTTP-method buckets.
|
|
5
|
+
*/
|
|
6
|
+
roots?: readonly string[];
|
|
7
|
+
/**
|
|
8
|
+
* Pay-gate configuration (shared HMAC secret + optional replay store/clock)
|
|
9
|
+
* forwarded to every gated dispatch. When omitted, paid endpoints fail closed
|
|
10
|
+
* with `paygate-not-configured`.
|
|
11
|
+
*/
|
|
12
|
+
config?: PayGateConfig;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Walk a provider tree once and route every paid-endpoint leaf through
|
|
16
|
+
* `dispatchWithPaidGate`. Free leaves are returned untouched. Callable
|
|
17
|
+
* namespaces (functions with child properties — e.g. `xai.v1.models` is a
|
|
18
|
+
* function with `.languageModels` children) preserve all children and their
|
|
19
|
+
* `.schema` attachments.
|
|
20
|
+
*
|
|
21
|
+
* @param providerName Provider identifier matching `PAID_ENDPOINTS`.
|
|
22
|
+
* @param tree The provider object returned by the factory.
|
|
23
|
+
* @param opts Optional roots allowlist and IO injection.
|
|
24
|
+
* @returns A new tree of the same shape; paid leaves accept `(req, approval?)`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function withPaidGate<T extends object>(providerName: string, tree: T, opts?: WithPaidGateOptions): T;
|
|
27
|
+
//# sourceMappingURL=with-paid-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-paid-gate.d.ts","sourceRoot":"","sources":["../../src/with-paid-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,WAAW,CAAC;AAWnB,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,CAAC,EACP,IAAI,CAAC,EAAE,mBAAmB,GACzB,CAAC,CAoBH"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { dispatchWithPaidGate, createReplayStore, } from "./paygate.js";
|
|
2
|
+
import { isPaidEndpoint } from "./paid-endpoints.js";
|
|
3
|
+
/**
|
|
4
|
+
* HTTP-method roots that the walker will descend into. Properties outside
|
|
5
|
+
* this allowlist (sub-providers like `kie.veo`, schema collections like
|
|
6
|
+
* `kie.modelInputSchemas`, attached helpers like `kie.examples`) are
|
|
7
|
+
* returned by reference and never wrapped.
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_ROOTS = ["post", "get", "delete", "patch", "put"];
|
|
10
|
+
/**
|
|
11
|
+
* Walk a provider tree once and route every paid-endpoint leaf through
|
|
12
|
+
* `dispatchWithPaidGate`. Free leaves are returned untouched. Callable
|
|
13
|
+
* namespaces (functions with child properties — e.g. `xai.v1.models` is a
|
|
14
|
+
* function with `.languageModels` children) preserve all children and their
|
|
15
|
+
* `.schema` attachments.
|
|
16
|
+
*
|
|
17
|
+
* @param providerName Provider identifier matching `PAID_ENDPOINTS`.
|
|
18
|
+
* @param tree The provider object returned by the factory.
|
|
19
|
+
* @param opts Optional roots allowlist and IO injection.
|
|
20
|
+
* @returns A new tree of the same shape; paid leaves accept `(req, approval?)`.
|
|
21
|
+
*/
|
|
22
|
+
export function withPaidGate(providerName, tree, opts) {
|
|
23
|
+
const roots = opts?.roots ?? DEFAULT_ROOTS;
|
|
24
|
+
// Normalize the config once so every gated dispatch on this provider instance
|
|
25
|
+
// shares a single replay store (single-use OTPs must be remembered across
|
|
26
|
+
// calls). A custom store passed by the code client is preserved.
|
|
27
|
+
const config = opts?.config
|
|
28
|
+
? {
|
|
29
|
+
...opts.config,
|
|
30
|
+
replayStore: opts.config.replayStore ?? createReplayStore(),
|
|
31
|
+
}
|
|
32
|
+
: undefined;
|
|
33
|
+
const out = {};
|
|
34
|
+
for (const [key, value] of Object.entries(tree)) {
|
|
35
|
+
if (roots.includes(key) && value && typeof value === "object") {
|
|
36
|
+
out[key] = walk(providerName, [key], value, config);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
out[key] = value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
function walk(providerName, path, node, config) {
|
|
45
|
+
if (typeof node === "function") {
|
|
46
|
+
return wrapCallable(providerName, path, node, config);
|
|
47
|
+
}
|
|
48
|
+
if (node && typeof node === "object" && !Array.isArray(node)) {
|
|
49
|
+
const out = {};
|
|
50
|
+
for (const [k, v] of Object.entries(node)) {
|
|
51
|
+
out[k] = walk(providerName, [...path, k], v, config);
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
return node;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Wrap a callable leaf. If the path resolves to a paid endpoint, the returned
|
|
59
|
+
* function routes through `dispatchWithPaidGate`. Otherwise the original
|
|
60
|
+
* function is returned. Either way, any properties hanging off the function
|
|
61
|
+
* (`.schema`, callable-namespace children) are preserved and recursively
|
|
62
|
+
* walked so nested paid leaves are also gated.
|
|
63
|
+
*/
|
|
64
|
+
function wrapCallable(providerName, path, fn, config) {
|
|
65
|
+
const method = path[0].toUpperCase();
|
|
66
|
+
const dotPath = path.slice(1).join(".");
|
|
67
|
+
const paid = dotPath.length > 0 && isPaidEndpoint(providerName, method, dotPath);
|
|
68
|
+
const base = paid
|
|
69
|
+
? (...args) => {
|
|
70
|
+
const [req, approval] = args;
|
|
71
|
+
return dispatchWithPaidGate(providerName, method, dotPath, req, approval, () => fn(req), config);
|
|
72
|
+
}
|
|
73
|
+
: fn;
|
|
74
|
+
// For callable namespaces (functions with own properties), only recurse into
|
|
75
|
+
// function-typed children — those are sub-endpoints that may themselves be
|
|
76
|
+
// paid. Non-function props (`.schema`, other metadata) are preserved by
|
|
77
|
+
// reference so test assertions and runtime callers can compare by identity.
|
|
78
|
+
const children = {};
|
|
79
|
+
for (const [k, v] of Object.entries(fn)) {
|
|
80
|
+
if (typeof v === "function") {
|
|
81
|
+
children[k] = walk(providerName, [...path, k], v, config);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
children[k] = v;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// If `base === fn` we still merge children, but they're already attached;
|
|
88
|
+
// the assignment is a no-op for own enumerable keys.
|
|
89
|
+
return Object.assign(base, children);
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=with-paid-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-paid-gate.js","sourceRoot":"","sources":["../../src/with-paid-gate.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED