@antseed/cli 0.1.167 → 0.1.169
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/dist/buyer-identities/loader.d.ts +28 -0
- package/dist/buyer-identities/loader.d.ts.map +1 -0
- package/dist/buyer-identities/loader.js +58 -0
- package/dist/buyer-identities/loader.js.map +1 -0
- package/dist/buyer-identities/store.d.ts +41 -0
- package/dist/buyer-identities/store.d.ts.map +1 -0
- package/dist/buyer-identities/store.js +171 -0
- package/dist/buyer-identities/store.js.map +1 -0
- package/dist/buyer-identities/store.test.d.ts +2 -0
- package/dist/buyer-identities/store.test.d.ts.map +1 -0
- package/dist/buyer-identities/store.test.js +122 -0
- package/dist/buyer-identities/store.test.js.map +1 -0
- package/dist/cli/commands/buyer/identity.d.ts +3 -0
- package/dist/cli/commands/buyer/identity.d.ts.map +1 -0
- package/dist/cli/commands/buyer/identity.js +115 -0
- package/dist/cli/commands/buyer/identity.js.map +1 -0
- package/dist/cli/commands/buyer/index.d.ts.map +1 -1
- package/dist/cli/commands/buyer/index.js +2 -0
- package/dist/cli/commands/buyer/index.js.map +1 -1
- package/dist/cli/commands/buyer/start.d.ts.map +1 -1
- package/dist/cli/commands/buyer/start.js +30 -2
- package/dist/cli/commands/buyer/start.js.map +1 -1
- package/dist/cli/commands/gateway/index.d.ts +3 -0
- package/dist/cli/commands/gateway/index.d.ts.map +1 -0
- package/dist/cli/commands/gateway/index.js +43 -0
- package/dist/cli/commands/gateway/index.js.map +1 -0
- package/dist/cli/commands/gateway/key.d.ts +3 -0
- package/dist/cli/commands/gateway/key.d.ts.map +1 -0
- package/dist/cli/commands/gateway/key.js +240 -0
- package/dist/cli/commands/gateway/key.js.map +1 -0
- package/dist/cli/commands/gateway/shared.d.ts +29 -0
- package/dist/cli/commands/gateway/shared.d.ts.map +1 -0
- package/dist/cli/commands/gateway/shared.js +90 -0
- package/dist/cli/commands/gateway/shared.js.map +1 -0
- package/dist/cli/commands/tunnel/start.d.ts.map +1 -1
- package/dist/cli/commands/tunnel/start.js +25 -14
- package/dist/cli/commands/tunnel/start.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/gateway/accounting.d.ts +45 -0
- package/dist/gateway/accounting.d.ts.map +1 -0
- package/dist/gateway/accounting.js +94 -0
- package/dist/gateway/accounting.js.map +1 -0
- package/dist/gateway/cdp-auth.d.ts +12 -0
- package/dist/gateway/cdp-auth.d.ts.map +1 -0
- package/dist/gateway/cdp-auth.js +53 -0
- package/dist/gateway/cdp-auth.js.map +1 -0
- package/dist/gateway/cdp-auth.test.d.ts +2 -0
- package/dist/gateway/cdp-auth.test.d.ts.map +1 -0
- package/dist/gateway/cdp-auth.test.js +96 -0
- package/dist/gateway/cdp-auth.test.js.map +1 -0
- package/dist/gateway/gateway.test.d.ts.map +1 -0
- package/dist/gateway/gateway.test.js +709 -0
- package/dist/gateway/gateway.test.js.map +1 -0
- package/dist/gateway/keys.d.ts +13 -0
- package/dist/gateway/keys.d.ts.map +1 -0
- package/dist/gateway/keys.js +24 -0
- package/dist/gateway/keys.js.map +1 -0
- package/dist/gateway/limits.d.ts +25 -0
- package/dist/gateway/limits.d.ts.map +1 -0
- package/dist/gateway/limits.js +45 -0
- package/dist/gateway/limits.js.map +1 -0
- package/dist/gateway/money.d.ts +9 -0
- package/dist/gateway/money.d.ts.map +1 -0
- package/dist/gateway/money.js +37 -0
- package/dist/gateway/money.js.map +1 -0
- package/dist/gateway/runtime.d.ts +38 -0
- package/dist/gateway/runtime.d.ts.map +1 -0
- package/dist/gateway/runtime.js +144 -0
- package/dist/gateway/runtime.js.map +1 -0
- package/dist/gateway/server.d.ts +49 -0
- package/dist/gateway/server.d.ts.map +1 -0
- package/dist/gateway/server.js +460 -0
- package/dist/gateway/server.js.map +1 -0
- package/dist/gateway/spend-feed.d.ts +32 -0
- package/dist/gateway/spend-feed.d.ts.map +1 -0
- package/dist/gateway/spend-feed.js +86 -0
- package/dist/gateway/spend-feed.js.map +1 -0
- package/dist/gateway/store.d.ts +114 -0
- package/dist/gateway/store.d.ts.map +1 -0
- package/dist/gateway/store.js +253 -0
- package/dist/gateway/store.js.map +1 -0
- package/dist/gateway/x402.d.ts +95 -0
- package/dist/gateway/x402.d.ts.map +1 -0
- package/dist/gateway/x402.js +120 -0
- package/dist/gateway/x402.js.map +1 -0
- package/dist/proxy/buyer-proxy.d.ts +13 -0
- package/dist/proxy/buyer-proxy.d.ts.map +1 -1
- package/dist/proxy/buyer-proxy.js +99 -11
- package/dist/proxy/buyer-proxy.js.map +1 -1
- package/dist/proxy/request-utils.d.ts +3 -0
- package/dist/proxy/request-utils.d.ts.map +1 -1
- package/dist/proxy/request-utils.js +3 -0
- package/dist/proxy/request-utils.js.map +1 -1
- package/dist/proxy/spend-attribution.d.ts +63 -0
- package/dist/proxy/spend-attribution.d.ts.map +1 -0
- package/dist/proxy/spend-attribution.js +97 -0
- package/dist/proxy/spend-attribution.js.map +1 -0
- package/package.json +9 -7
- package/dist/tunnel/gateway.d.ts +0 -16
- package/dist/tunnel/gateway.d.ts.map +0 -1
- package/dist/tunnel/gateway.js +0 -146
- package/dist/tunnel/gateway.js.map +0 -1
- package/dist/tunnel/gateway.test.d.ts.map +0 -1
- package/dist/tunnel/gateway.test.js +0 -89
- package/dist/tunnel/gateway.test.js.map +0 -1
- /package/dist/{tunnel → gateway}/gateway.test.d.ts +0 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { type PeriodSpend, type SpendLimits } from './limits.js';
|
|
2
|
+
export type ApiKeySource = 'created' | 'tunnel-env';
|
|
3
|
+
export type ApiKeyStatus = 'active' | 'revoked';
|
|
4
|
+
export interface ApiKeyRecord {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
hint: string;
|
|
8
|
+
/** Buyer identity (wallet) that pays for this key's requests. */
|
|
9
|
+
buyerIdentity: string;
|
|
10
|
+
source: ApiKeySource;
|
|
11
|
+
status: ApiKeyStatus;
|
|
12
|
+
limits: SpendLimits;
|
|
13
|
+
/** Whether the key holder may fund the key's wallet with x402 top-ups. */
|
|
14
|
+
topupEnabled: boolean;
|
|
15
|
+
expiresAt: number | null;
|
|
16
|
+
createdAt: number;
|
|
17
|
+
revokedAt: number | null;
|
|
18
|
+
lastUsedAt: number | null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Balance movements per key, as signed deltas in USDC base units: spend is
|
|
22
|
+
* negative, credit (a top-up or payment received for the key) is positive.
|
|
23
|
+
* `externalRef` makes every write idempotent against its source event.
|
|
24
|
+
*/
|
|
25
|
+
export type LedgerEntryKind = 'spend' | 'credit';
|
|
26
|
+
export interface LedgerEntryInput {
|
|
27
|
+
kind: LedgerEntryKind;
|
|
28
|
+
keyId: string;
|
|
29
|
+
buyerIdentity: string;
|
|
30
|
+
amountUsdc: number;
|
|
31
|
+
externalRef: string;
|
|
32
|
+
requestTag?: string | null;
|
|
33
|
+
sellerPeerId?: string | null;
|
|
34
|
+
inputTokens?: number;
|
|
35
|
+
cachedInputTokens?: number;
|
|
36
|
+
outputTokens?: number;
|
|
37
|
+
note?: string | null;
|
|
38
|
+
createdAt: number;
|
|
39
|
+
}
|
|
40
|
+
export interface GatewayRequestStart {
|
|
41
|
+
tag: string;
|
|
42
|
+
keyId: string;
|
|
43
|
+
buyerIdentity: string;
|
|
44
|
+
method: string;
|
|
45
|
+
path: string;
|
|
46
|
+
model: string | null;
|
|
47
|
+
startedAt: number;
|
|
48
|
+
}
|
|
49
|
+
export interface KeyUsageStats {
|
|
50
|
+
requests: number;
|
|
51
|
+
failedRequests: number;
|
|
52
|
+
spentUsdc: number;
|
|
53
|
+
creditedUsdc: number;
|
|
54
|
+
inputTokens: number;
|
|
55
|
+
cachedInputTokens: number;
|
|
56
|
+
outputTokens: number;
|
|
57
|
+
}
|
|
58
|
+
export declare function gatewayDir(dataDir: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* Durable state for the API-key gateway: keys, request log and the per-key
|
|
61
|
+
* ledger. SQLite in WAL mode so `antseed gateway key …`
|
|
62
|
+
* commands can change keys while a gateway process is serving them.
|
|
63
|
+
*/
|
|
64
|
+
export declare class GatewayStore {
|
|
65
|
+
private readonly _now;
|
|
66
|
+
private readonly _db;
|
|
67
|
+
constructor(dataDir: string, _now?: () => number);
|
|
68
|
+
close(): void;
|
|
69
|
+
private _migrate;
|
|
70
|
+
createKey(input: {
|
|
71
|
+
label: string;
|
|
72
|
+
buyerIdentity: string;
|
|
73
|
+
limits: SpendLimits;
|
|
74
|
+
expiresAt: number | null;
|
|
75
|
+
topupEnabled?: boolean;
|
|
76
|
+
}): {
|
|
77
|
+
key: ApiKeyRecord;
|
|
78
|
+
secret: string;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* The single key `antseed tunnel start` used to take from
|
|
82
|
+
* ANTSEED_TUNNEL_API_KEY becomes an unlimited key on the default identity,
|
|
83
|
+
* so existing tunnel clients keep working. A changed env key replaces it.
|
|
84
|
+
*/
|
|
85
|
+
syncEnvironmentKey(secret: string): ApiKeyRecord;
|
|
86
|
+
/**
|
|
87
|
+
* Revokes the ANTSEED_TUNNEL_API_KEY key once the tunnel starts without the
|
|
88
|
+
* env var, so unsetting it retires the shared unlimited key. Returns the
|
|
89
|
+
* revoked key, or null when there was no active one.
|
|
90
|
+
*/
|
|
91
|
+
retireEnvironmentKey(): ApiKeyRecord | null;
|
|
92
|
+
findKeyBySecret(secret: string): ApiKeyRecord | null;
|
|
93
|
+
getKey(id: string): ApiKeyRecord | null;
|
|
94
|
+
listKeys(): ApiKeyRecord[];
|
|
95
|
+
countActiveKeys(): number;
|
|
96
|
+
setLimits(id: string, limits: Partial<SpendLimits>): ApiKeyRecord;
|
|
97
|
+
setTopupEnabled(id: string, enabled: boolean): ApiKeyRecord;
|
|
98
|
+
revokeKey(id: string): ApiKeyRecord;
|
|
99
|
+
touchKey(id: string): void;
|
|
100
|
+
startRequest(input: GatewayRequestStart): void;
|
|
101
|
+
finishRequest(tag: string, result: {
|
|
102
|
+
status: number;
|
|
103
|
+
buyerRequestId: string | null;
|
|
104
|
+
}): void;
|
|
105
|
+
findRequest(tag: string): {
|
|
106
|
+
keyId: string;
|
|
107
|
+
buyerIdentity: string;
|
|
108
|
+
} | null;
|
|
109
|
+
/** Returns false when the entry was already recorded (same externalRef). */
|
|
110
|
+
recordLedgerEntry(entry: LedgerEntryInput): boolean;
|
|
111
|
+
periodSpend(keyId: string, now?: number): PeriodSpend;
|
|
112
|
+
usageStats(keyId: string, since?: number): KeyUsageStats;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/gateway/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { DEFAULT_BUYER_IDENTITY } from '@antseed/node';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
import { mkdirSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { generateApiKey, hashApiKey, apiKeyHint, newKeyId } from './keys.js';
|
|
6
|
+
import { LIMIT_PERIODS, periodStart } from './limits.js';
|
|
7
|
+
const MIGRATIONS = [
|
|
8
|
+
`
|
|
9
|
+
CREATE TABLE api_keys (
|
|
10
|
+
id TEXT PRIMARY KEY,
|
|
11
|
+
label TEXT NOT NULL,
|
|
12
|
+
key_hash TEXT NOT NULL UNIQUE,
|
|
13
|
+
key_hint TEXT NOT NULL,
|
|
14
|
+
buyer_identity TEXT NOT NULL,
|
|
15
|
+
source TEXT NOT NULL DEFAULT 'created',
|
|
16
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
17
|
+
daily_limit_usdc INTEGER,
|
|
18
|
+
monthly_limit_usdc INTEGER,
|
|
19
|
+
total_limit_usdc INTEGER,
|
|
20
|
+
expires_at INTEGER,
|
|
21
|
+
created_at INTEGER NOT NULL,
|
|
22
|
+
revoked_at INTEGER,
|
|
23
|
+
last_used_at INTEGER
|
|
24
|
+
);
|
|
25
|
+
CREATE TABLE gateway_requests (
|
|
26
|
+
tag TEXT PRIMARY KEY,
|
|
27
|
+
key_id TEXT NOT NULL,
|
|
28
|
+
buyer_identity TEXT NOT NULL,
|
|
29
|
+
method TEXT NOT NULL,
|
|
30
|
+
path TEXT NOT NULL,
|
|
31
|
+
model TEXT,
|
|
32
|
+
status INTEGER,
|
|
33
|
+
buyer_request_id TEXT,
|
|
34
|
+
started_at INTEGER NOT NULL,
|
|
35
|
+
finished_at INTEGER
|
|
36
|
+
);
|
|
37
|
+
CREATE INDEX gateway_requests_key_time ON gateway_requests(key_id, started_at);
|
|
38
|
+
CREATE TABLE ledger_entries (
|
|
39
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
40
|
+
kind TEXT NOT NULL CHECK (kind IN ('spend', 'credit')),
|
|
41
|
+
key_id TEXT NOT NULL,
|
|
42
|
+
buyer_identity TEXT NOT NULL,
|
|
43
|
+
amount_usdc INTEGER NOT NULL,
|
|
44
|
+
external_ref TEXT NOT NULL UNIQUE,
|
|
45
|
+
request_tag TEXT,
|
|
46
|
+
seller_peer_id TEXT,
|
|
47
|
+
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
48
|
+
cached_input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
49
|
+
output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
50
|
+
note TEXT,
|
|
51
|
+
created_at INTEGER NOT NULL
|
|
52
|
+
);
|
|
53
|
+
CREATE INDEX ledger_entries_key_time ON ledger_entries(key_id, kind, created_at);
|
|
54
|
+
`,
|
|
55
|
+
`
|
|
56
|
+
ALTER TABLE api_keys ADD COLUMN topup_enabled INTEGER NOT NULL DEFAULT 0;
|
|
57
|
+
`,
|
|
58
|
+
];
|
|
59
|
+
export function gatewayDir(dataDir) {
|
|
60
|
+
return join(dataDir, 'gateway');
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Durable state for the API-key gateway: keys, request log and the per-key
|
|
64
|
+
* ledger. SQLite in WAL mode so `antseed gateway key …`
|
|
65
|
+
* commands can change keys while a gateway process is serving them.
|
|
66
|
+
*/
|
|
67
|
+
export class GatewayStore {
|
|
68
|
+
_now;
|
|
69
|
+
_db;
|
|
70
|
+
constructor(dataDir, _now = () => Date.now()) {
|
|
71
|
+
this._now = _now;
|
|
72
|
+
mkdirSync(gatewayDir(dataDir), { recursive: true });
|
|
73
|
+
this._db = new Database(join(gatewayDir(dataDir), 'gateway.db'));
|
|
74
|
+
this._db.pragma('journal_mode = WAL');
|
|
75
|
+
this._db.pragma('busy_timeout = 5000');
|
|
76
|
+
this._db.pragma('foreign_keys = ON');
|
|
77
|
+
this._migrate();
|
|
78
|
+
}
|
|
79
|
+
close() {
|
|
80
|
+
this._db.close();
|
|
81
|
+
}
|
|
82
|
+
_migrate() {
|
|
83
|
+
const version = this._db.pragma('user_version', { simple: true });
|
|
84
|
+
for (let index = version; index < MIGRATIONS.length; index += 1) {
|
|
85
|
+
this._db.transaction(() => {
|
|
86
|
+
this._db.exec(MIGRATIONS[index]);
|
|
87
|
+
this._db.pragma(`user_version = ${index + 1}`);
|
|
88
|
+
})();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// ── Keys ────────────────────────────────────────────────────────────────
|
|
92
|
+
createKey(input) {
|
|
93
|
+
const generated = generateApiKey();
|
|
94
|
+
const id = newKeyId();
|
|
95
|
+
this._db.prepare(`
|
|
96
|
+
INSERT INTO api_keys (id, label, key_hash, key_hint, buyer_identity, source, daily_limit_usdc, monthly_limit_usdc, total_limit_usdc, topup_enabled, expires_at, created_at)
|
|
97
|
+
VALUES (?, ?, ?, ?, ?, 'created', ?, ?, ?, ?, ?, ?)
|
|
98
|
+
`).run(id, input.label, generated.hash, generated.hint, input.buyerIdentity, input.limits.daily, input.limits.monthly, input.limits.total, input.topupEnabled ? 1 : 0, input.expiresAt, this._now());
|
|
99
|
+
return { key: this.getKey(id), secret: generated.secret };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The single key `antseed tunnel start` used to take from
|
|
103
|
+
* ANTSEED_TUNNEL_API_KEY becomes an unlimited key on the default identity,
|
|
104
|
+
* so existing tunnel clients keep working. A changed env key replaces it.
|
|
105
|
+
*/
|
|
106
|
+
syncEnvironmentKey(secret) {
|
|
107
|
+
const hash = hashApiKey(secret);
|
|
108
|
+
const existing = this._db.prepare("SELECT id FROM api_keys WHERE source = 'tunnel-env'").get();
|
|
109
|
+
if (existing) {
|
|
110
|
+
this._db.prepare("UPDATE api_keys SET key_hash = ?, key_hint = ?, status = 'active', revoked_at = NULL WHERE id = ?")
|
|
111
|
+
.run(hash, apiKeyHint(secret), existing.id);
|
|
112
|
+
return this.getKey(existing.id);
|
|
113
|
+
}
|
|
114
|
+
const id = newKeyId();
|
|
115
|
+
this._db.prepare(`
|
|
116
|
+
INSERT INTO api_keys (id, label, key_hash, key_hint, buyer_identity, source, created_at)
|
|
117
|
+
VALUES (?, 'Tunnel key', ?, ?, ?, 'tunnel-env', ?)
|
|
118
|
+
`).run(id, hash, apiKeyHint(secret), DEFAULT_BUYER_IDENTITY, this._now());
|
|
119
|
+
return this.getKey(id);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Revokes the ANTSEED_TUNNEL_API_KEY key once the tunnel starts without the
|
|
123
|
+
* env var, so unsetting it retires the shared unlimited key. Returns the
|
|
124
|
+
* revoked key, or null when there was no active one.
|
|
125
|
+
*/
|
|
126
|
+
retireEnvironmentKey() {
|
|
127
|
+
const existing = this._db.prepare("SELECT id FROM api_keys WHERE source = 'tunnel-env' AND status = 'active'").get();
|
|
128
|
+
return existing ? this.revokeKey(existing.id) : null;
|
|
129
|
+
}
|
|
130
|
+
findKeyBySecret(secret) {
|
|
131
|
+
const row = this._db.prepare('SELECT * FROM api_keys WHERE key_hash = ?').get(hashApiKey(secret));
|
|
132
|
+
return row ? toKey(row) : null;
|
|
133
|
+
}
|
|
134
|
+
getKey(id) {
|
|
135
|
+
const row = this._db.prepare('SELECT * FROM api_keys WHERE id = ?').get(id);
|
|
136
|
+
return row ? toKey(row) : null;
|
|
137
|
+
}
|
|
138
|
+
listKeys() {
|
|
139
|
+
const rows = this._db.prepare('SELECT * FROM api_keys ORDER BY created_at, id').all();
|
|
140
|
+
return rows.map(toKey);
|
|
141
|
+
}
|
|
142
|
+
countActiveKeys() {
|
|
143
|
+
return this._db.prepare("SELECT COUNT(*) AS count FROM api_keys WHERE status = 'active'").get().count;
|
|
144
|
+
}
|
|
145
|
+
setLimits(id, limits) {
|
|
146
|
+
const key = this.getKey(id);
|
|
147
|
+
if (!key)
|
|
148
|
+
throw new Error(`Unknown key "${id}".`);
|
|
149
|
+
const next = { ...key.limits, ...limits };
|
|
150
|
+
this._db.prepare('UPDATE api_keys SET daily_limit_usdc = ?, monthly_limit_usdc = ?, total_limit_usdc = ? WHERE id = ?')
|
|
151
|
+
.run(next.daily, next.monthly, next.total, id);
|
|
152
|
+
return this.getKey(id);
|
|
153
|
+
}
|
|
154
|
+
setTopupEnabled(id, enabled) {
|
|
155
|
+
const result = this._db.prepare('UPDATE api_keys SET topup_enabled = ? WHERE id = ?').run(enabled ? 1 : 0, id);
|
|
156
|
+
if (result.changes === 0)
|
|
157
|
+
throw new Error(`Unknown key "${id}".`);
|
|
158
|
+
return this.getKey(id);
|
|
159
|
+
}
|
|
160
|
+
revokeKey(id) {
|
|
161
|
+
const result = this._db.prepare("UPDATE api_keys SET status = 'revoked', revoked_at = ? WHERE id = ? AND status = 'active'")
|
|
162
|
+
.run(this._now(), id);
|
|
163
|
+
if (result.changes === 0 && !this.getKey(id))
|
|
164
|
+
throw new Error(`Unknown key "${id}".`);
|
|
165
|
+
return this.getKey(id);
|
|
166
|
+
}
|
|
167
|
+
touchKey(id) {
|
|
168
|
+
this._db.prepare('UPDATE api_keys SET last_used_at = ? WHERE id = ?').run(this._now(), id);
|
|
169
|
+
}
|
|
170
|
+
// ── Requests ────────────────────────────────────────────────────────────
|
|
171
|
+
startRequest(input) {
|
|
172
|
+
this._db.prepare(`
|
|
173
|
+
INSERT INTO gateway_requests (tag, key_id, buyer_identity, method, path, model, started_at)
|
|
174
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
175
|
+
`).run(input.tag, input.keyId, input.buyerIdentity, input.method, input.path, input.model, input.startedAt);
|
|
176
|
+
}
|
|
177
|
+
finishRequest(tag, result) {
|
|
178
|
+
this._db.prepare('UPDATE gateway_requests SET status = ?, buyer_request_id = ?, finished_at = ? WHERE tag = ?')
|
|
179
|
+
.run(result.status, result.buyerRequestId, this._now(), tag);
|
|
180
|
+
}
|
|
181
|
+
findRequest(tag) {
|
|
182
|
+
const row = this._db.prepare('SELECT key_id, buyer_identity FROM gateway_requests WHERE tag = ?').get(tag);
|
|
183
|
+
return row ? { keyId: row.key_id, buyerIdentity: row.buyer_identity } : null;
|
|
184
|
+
}
|
|
185
|
+
// ── Ledger ──────────────────────────────────────────────────────────────
|
|
186
|
+
/** Returns false when the entry was already recorded (same externalRef). */
|
|
187
|
+
recordLedgerEntry(entry) {
|
|
188
|
+
if (!Number.isSafeInteger(entry.amountUsdc) || entry.amountUsdc < 0) {
|
|
189
|
+
throw new Error('Ledger amounts are non-negative integer USDC base units; the kind sets the sign.');
|
|
190
|
+
}
|
|
191
|
+
const signed = entry.kind === 'spend' ? -entry.amountUsdc : entry.amountUsdc;
|
|
192
|
+
const result = this._db.prepare(`
|
|
193
|
+
INSERT OR IGNORE INTO ledger_entries
|
|
194
|
+
(kind, key_id, buyer_identity, amount_usdc, external_ref, request_tag, seller_peer_id,
|
|
195
|
+
input_tokens, cached_input_tokens, output_tokens, note, created_at)
|
|
196
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
197
|
+
`).run(entry.kind, entry.keyId, entry.buyerIdentity, signed, entry.externalRef, entry.requestTag ?? null, entry.sellerPeerId ?? null, entry.inputTokens ?? 0, entry.cachedInputTokens ?? 0, entry.outputTokens ?? 0, entry.note ?? null, entry.createdAt);
|
|
198
|
+
return result.changes > 0;
|
|
199
|
+
}
|
|
200
|
+
periodSpend(keyId, now = this._now()) {
|
|
201
|
+
const statement = this._db.prepare("SELECT COALESCE(-SUM(amount_usdc), 0) AS spent FROM ledger_entries WHERE key_id = ? AND kind = 'spend' AND created_at >= ?");
|
|
202
|
+
const spend = {};
|
|
203
|
+
for (const period of LIMIT_PERIODS) {
|
|
204
|
+
spend[period] = statement.get(keyId, periodStart(period, now)).spent;
|
|
205
|
+
}
|
|
206
|
+
return spend;
|
|
207
|
+
}
|
|
208
|
+
usageStats(keyId, since = 0) {
|
|
209
|
+
const requests = this._db.prepare(`
|
|
210
|
+
SELECT COUNT(*) AS requests,
|
|
211
|
+
COALESCE(SUM(CASE WHEN status IS NULL OR status >= 400 THEN 1 ELSE 0 END), 0) AS failed
|
|
212
|
+
FROM gateway_requests WHERE key_id = ? AND started_at >= ?
|
|
213
|
+
`).get(keyId, since);
|
|
214
|
+
const ledger = this._db.prepare(`
|
|
215
|
+
SELECT COALESCE(-SUM(CASE WHEN kind = 'spend' THEN amount_usdc ELSE 0 END), 0) AS spent,
|
|
216
|
+
COALESCE(SUM(CASE WHEN kind = 'credit' THEN amount_usdc ELSE 0 END), 0) AS credited,
|
|
217
|
+
COALESCE(SUM(input_tokens), 0) AS input_tokens,
|
|
218
|
+
COALESCE(SUM(cached_input_tokens), 0) AS cached_input_tokens,
|
|
219
|
+
COALESCE(SUM(output_tokens), 0) AS output_tokens
|
|
220
|
+
FROM ledger_entries WHERE key_id = ? AND created_at >= ?
|
|
221
|
+
`).get(keyId, since);
|
|
222
|
+
return {
|
|
223
|
+
requests: requests.requests,
|
|
224
|
+
failedRequests: requests.failed,
|
|
225
|
+
spentUsdc: ledger.spent,
|
|
226
|
+
creditedUsdc: ledger.credited,
|
|
227
|
+
inputTokens: ledger.input_tokens,
|
|
228
|
+
cachedInputTokens: ledger.cached_input_tokens,
|
|
229
|
+
outputTokens: ledger.output_tokens,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function toKey(row) {
|
|
234
|
+
return {
|
|
235
|
+
id: row.id,
|
|
236
|
+
label: row.label,
|
|
237
|
+
hint: row.key_hint,
|
|
238
|
+
buyerIdentity: row.buyer_identity,
|
|
239
|
+
source: row.source,
|
|
240
|
+
status: row.status,
|
|
241
|
+
limits: {
|
|
242
|
+
daily: row.daily_limit_usdc,
|
|
243
|
+
monthly: row.monthly_limit_usdc,
|
|
244
|
+
total: row.total_limit_usdc,
|
|
245
|
+
},
|
|
246
|
+
topupEnabled: row.topup_enabled === 1,
|
|
247
|
+
expiresAt: row.expires_at,
|
|
248
|
+
createdAt: row.created_at,
|
|
249
|
+
revokedAt: row.revoked_at,
|
|
250
|
+
lastUsedAt: row.last_used_at,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/gateway/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* x402 v2 over HTTP, `exact` scheme with EIP-3009 USDC transfers: the
|
|
3
|
+
* gateway names a payee and amount in `PAYMENT-REQUIRED`, the client signs a
|
|
4
|
+
* `transferWithAuthorization` and sends it as `PAYMENT-SIGNATURE`, and a
|
|
5
|
+
* facilitator settles it on-chain. See https://github.com/coinbase/x402.
|
|
6
|
+
*/
|
|
7
|
+
export declare const X402_VERSION = 2;
|
|
8
|
+
export declare const PAYMENT_REQUIRED_HEADER = "payment-required";
|
|
9
|
+
export declare const PAYMENT_SIGNATURE_HEADER = "payment-signature";
|
|
10
|
+
export declare const PAYMENT_RESPONSE_HEADER = "payment-response";
|
|
11
|
+
export interface X402Asset {
|
|
12
|
+
/** CAIP-2 network id, e.g. `eip155:8453` for Base. */
|
|
13
|
+
network: string;
|
|
14
|
+
chainId: number;
|
|
15
|
+
/** USDC contract address. */
|
|
16
|
+
address: string;
|
|
17
|
+
/** EIP-712 domain of the token, as `transferWithAuthorization` checks it. */
|
|
18
|
+
name: string;
|
|
19
|
+
version: string;
|
|
20
|
+
}
|
|
21
|
+
export interface PaymentRequirements {
|
|
22
|
+
scheme: 'exact';
|
|
23
|
+
network: string;
|
|
24
|
+
amount: string;
|
|
25
|
+
asset: string;
|
|
26
|
+
payTo: string;
|
|
27
|
+
maxTimeoutSeconds: number;
|
|
28
|
+
extra: {
|
|
29
|
+
assetTransferMethod: 'eip3009';
|
|
30
|
+
name: string;
|
|
31
|
+
version: string;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export interface PaymentRequired {
|
|
35
|
+
x402Version: number;
|
|
36
|
+
error?: string;
|
|
37
|
+
resource: {
|
|
38
|
+
url: string;
|
|
39
|
+
description: string;
|
|
40
|
+
mimeType: string;
|
|
41
|
+
};
|
|
42
|
+
accepts: PaymentRequirements[];
|
|
43
|
+
}
|
|
44
|
+
export interface Eip3009Authorization {
|
|
45
|
+
from: string;
|
|
46
|
+
to: string;
|
|
47
|
+
value: string;
|
|
48
|
+
validAfter: string;
|
|
49
|
+
validBefore: string;
|
|
50
|
+
nonce: string;
|
|
51
|
+
}
|
|
52
|
+
export interface PaymentPayload {
|
|
53
|
+
x402Version: number;
|
|
54
|
+
resource?: PaymentRequired['resource'];
|
|
55
|
+
accepted: PaymentRequirements;
|
|
56
|
+
payload: {
|
|
57
|
+
signature: string;
|
|
58
|
+
authorization: Eip3009Authorization;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export interface SettlementResponse {
|
|
62
|
+
success: boolean;
|
|
63
|
+
transaction: string;
|
|
64
|
+
network: string;
|
|
65
|
+
payer: string;
|
|
66
|
+
errorReason?: string;
|
|
67
|
+
}
|
|
68
|
+
export declare function encodeHeaderJson(value: unknown): string;
|
|
69
|
+
export declare function decodeHeaderJson<T>(value: string): T | null;
|
|
70
|
+
export declare function buildPaymentRequirements(asset: X402Asset, payTo: string, amountUsdc: number): PaymentRequirements;
|
|
71
|
+
/**
|
|
72
|
+
* Checks the gateway can make before asking a facilitator: the payment pays
|
|
73
|
+
* exactly what was asked, to the key's wallet, inside its validity window,
|
|
74
|
+
* and is signed by the payer it names. Balance and nonce use are left to the
|
|
75
|
+
* facilitator's on-chain simulation. Returns an x402 error reason, or null.
|
|
76
|
+
*/
|
|
77
|
+
export declare function checkPaymentPayload(payment: PaymentPayload, required: PaymentRequirements, asset: X402Asset, nowSeconds: number): string | null;
|
|
78
|
+
export interface FacilitatorOptions {
|
|
79
|
+
url: string;
|
|
80
|
+
/** Authorization header for a call to `endpointUrl`, for facilitators that need one. */
|
|
81
|
+
authorize?: (endpointUrl: string) => string;
|
|
82
|
+
fetchImpl?: typeof fetch;
|
|
83
|
+
}
|
|
84
|
+
/** Client for the standard facilitator `/verify` and `/settle` endpoints. */
|
|
85
|
+
export declare class X402Facilitator {
|
|
86
|
+
private readonly _options;
|
|
87
|
+
constructor(_options: FacilitatorOptions);
|
|
88
|
+
verify(payment: PaymentPayload, requirements: PaymentRequirements): Promise<{
|
|
89
|
+
isValid: boolean;
|
|
90
|
+
invalidReason?: string;
|
|
91
|
+
}>;
|
|
92
|
+
settle(payment: PaymentPayload, requirements: PaymentRequirements): Promise<SettlementResponse>;
|
|
93
|
+
private _post;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=x402.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x402.d.ts","sourceRoot":"","sources":["../../src/gateway/x402.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { getAddress, verifyTypedData } from 'ethers';
|
|
2
|
+
/**
|
|
3
|
+
* x402 v2 over HTTP, `exact` scheme with EIP-3009 USDC transfers: the
|
|
4
|
+
* gateway names a payee and amount in `PAYMENT-REQUIRED`, the client signs a
|
|
5
|
+
* `transferWithAuthorization` and sends it as `PAYMENT-SIGNATURE`, and a
|
|
6
|
+
* facilitator settles it on-chain. See https://github.com/coinbase/x402.
|
|
7
|
+
*/
|
|
8
|
+
export const X402_VERSION = 2;
|
|
9
|
+
export const PAYMENT_REQUIRED_HEADER = 'payment-required';
|
|
10
|
+
export const PAYMENT_SIGNATURE_HEADER = 'payment-signature';
|
|
11
|
+
export const PAYMENT_RESPONSE_HEADER = 'payment-response';
|
|
12
|
+
const DEFAULT_MAX_TIMEOUT_SECONDS = 300;
|
|
13
|
+
export function encodeHeaderJson(value) {
|
|
14
|
+
return Buffer.from(JSON.stringify(value), 'utf8').toString('base64');
|
|
15
|
+
}
|
|
16
|
+
export function decodeHeaderJson(value) {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(Buffer.from(value, 'base64').toString('utf8'));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function buildPaymentRequirements(asset, payTo, amountUsdc) {
|
|
25
|
+
return {
|
|
26
|
+
scheme: 'exact',
|
|
27
|
+
network: asset.network,
|
|
28
|
+
amount: String(amountUsdc),
|
|
29
|
+
asset: asset.address,
|
|
30
|
+
payTo: getAddress(payTo),
|
|
31
|
+
maxTimeoutSeconds: DEFAULT_MAX_TIMEOUT_SECONDS,
|
|
32
|
+
extra: { assetTransferMethod: 'eip3009', name: asset.name, version: asset.version },
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const TRANSFER_WITH_AUTHORIZATION_TYPES = {
|
|
36
|
+
TransferWithAuthorization: [
|
|
37
|
+
{ name: 'from', type: 'address' },
|
|
38
|
+
{ name: 'to', type: 'address' },
|
|
39
|
+
{ name: 'value', type: 'uint256' },
|
|
40
|
+
{ name: 'validAfter', type: 'uint256' },
|
|
41
|
+
{ name: 'validBefore', type: 'uint256' },
|
|
42
|
+
{ name: 'nonce', type: 'bytes32' },
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
function sameAddress(a, b) {
|
|
46
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Checks the gateway can make before asking a facilitator: the payment pays
|
|
50
|
+
* exactly what was asked, to the key's wallet, inside its validity window,
|
|
51
|
+
* and is signed by the payer it names. Balance and nonce use are left to the
|
|
52
|
+
* facilitator's on-chain simulation. Returns an x402 error reason, or null.
|
|
53
|
+
*/
|
|
54
|
+
export function checkPaymentPayload(payment, required, asset, nowSeconds) {
|
|
55
|
+
if (payment.x402Version !== X402_VERSION)
|
|
56
|
+
return 'invalid_x402_version';
|
|
57
|
+
const accepted = payment.accepted;
|
|
58
|
+
if (!accepted || accepted.scheme !== 'exact')
|
|
59
|
+
return 'unsupported_scheme';
|
|
60
|
+
if (accepted.network !== required.network)
|
|
61
|
+
return 'invalid_network';
|
|
62
|
+
if (!sameAddress(accepted.asset, required.asset) || !sameAddress(accepted.payTo, required.payTo)) {
|
|
63
|
+
return 'invalid_payment_requirements';
|
|
64
|
+
}
|
|
65
|
+
const authorization = payment.payload?.authorization;
|
|
66
|
+
const signature = payment.payload?.signature;
|
|
67
|
+
if (!authorization || typeof signature !== 'string')
|
|
68
|
+
return 'invalid_payload';
|
|
69
|
+
if (!sameAddress(authorization.to, required.payTo))
|
|
70
|
+
return 'invalid_exact_evm_payload_recipient_mismatch';
|
|
71
|
+
if (authorization.value !== required.amount)
|
|
72
|
+
return 'invalid_exact_evm_payload_authorization_value_mismatch';
|
|
73
|
+
if (!/^\d+$/.test(String(authorization.validAfter)) || !/^\d+$/.test(String(authorization.validBefore))) {
|
|
74
|
+
return 'invalid_payload';
|
|
75
|
+
}
|
|
76
|
+
if (Number(authorization.validAfter) > nowSeconds)
|
|
77
|
+
return 'invalid_exact_evm_payload_authorization_valid_after';
|
|
78
|
+
// Leave the facilitator enough time to land the transaction.
|
|
79
|
+
if (Number(authorization.validBefore) < nowSeconds + 6)
|
|
80
|
+
return 'invalid_exact_evm_payload_authorization_valid_before';
|
|
81
|
+
let signer;
|
|
82
|
+
try {
|
|
83
|
+
signer = verifyTypedData({ name: asset.name, version: asset.version, chainId: asset.chainId, verifyingContract: asset.address }, TRANSFER_WITH_AUTHORIZATION_TYPES, authorization, signature);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return 'invalid_exact_evm_payload_signature';
|
|
87
|
+
}
|
|
88
|
+
return sameAddress(signer, authorization.from) ? null : 'invalid_exact_evm_payload_signature';
|
|
89
|
+
}
|
|
90
|
+
/** Client for the standard facilitator `/verify` and `/settle` endpoints. */
|
|
91
|
+
export class X402Facilitator {
|
|
92
|
+
_options;
|
|
93
|
+
constructor(_options) {
|
|
94
|
+
this._options = _options;
|
|
95
|
+
}
|
|
96
|
+
async verify(payment, requirements) {
|
|
97
|
+
return this._post('verify', payment, requirements);
|
|
98
|
+
}
|
|
99
|
+
async settle(payment, requirements) {
|
|
100
|
+
return this._post('settle', payment, requirements);
|
|
101
|
+
}
|
|
102
|
+
async _post(path, payment, requirements) {
|
|
103
|
+
const fetchImpl = this._options.fetchImpl ?? fetch;
|
|
104
|
+
const endpointUrl = `${this._options.url.replace(/\/+$/, '')}/${path}`;
|
|
105
|
+
const response = await fetchImpl(endpointUrl, {
|
|
106
|
+
method: 'POST',
|
|
107
|
+
headers: {
|
|
108
|
+
'content-type': 'application/json',
|
|
109
|
+
...(this._options.authorize ? { authorization: this._options.authorize(endpointUrl) } : {}),
|
|
110
|
+
},
|
|
111
|
+
body: JSON.stringify({ x402Version: X402_VERSION, paymentPayload: payment, paymentRequirements: requirements }),
|
|
112
|
+
signal: AbortSignal.timeout(path === 'settle' ? 90_000 : 15_000),
|
|
113
|
+
});
|
|
114
|
+
const body = await response.json().catch(() => null);
|
|
115
|
+
if (!body || typeof body !== 'object')
|
|
116
|
+
throw new Error(`Facilitator /${path} answered ${response.status} without a JSON body`);
|
|
117
|
+
return body;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=x402.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x402.js","sourceRoot":"","sources":["../../src/gateway/x402.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AntseedNode, type PeerInfo, type ModelRoutingPreferences, type SerializedHttpResponse, type SweepReceiptPayload } from '@antseed/node';
|
|
2
|
+
import { BuyerIdentityLoader } from '../buyer-identities/loader.js';
|
|
2
3
|
import type { DepositWatcher } from './deposit-watcher.js';
|
|
3
4
|
import { type VerifierPolicy, type SellerReach } from '../plugins/verifier.js';
|
|
4
5
|
export { selectCandidatePeersForRouting, type CandidatePeerRouteSelection } from './routing.js';
|
|
@@ -41,6 +42,8 @@ export interface BuyerProxyConfig {
|
|
|
41
42
|
now?: () => number;
|
|
42
43
|
/** Verifier-SDK policy: which verifier the buyer commits to + whether it is required. */
|
|
43
44
|
verifier?: VerifierPolicy;
|
|
45
|
+
/** Loads stored buyer identities on demand; defaults to one reading `dataDir`. */
|
|
46
|
+
buyerIdentities?: BuyerIdentityLoader;
|
|
44
47
|
}
|
|
45
48
|
/**
|
|
46
49
|
* Detect a "model not served" rejection: the seller's own pre-payment 400
|
|
@@ -111,6 +114,7 @@ export declare class BuyerProxy {
|
|
|
111
114
|
*/
|
|
112
115
|
private _lastModelActivityAt;
|
|
113
116
|
private readonly _verifier?;
|
|
117
|
+
private readonly _buyerIdentities;
|
|
114
118
|
private readonly _teeVerification;
|
|
115
119
|
private readonly _teeControl;
|
|
116
120
|
private _stateWatchDebounce;
|
|
@@ -157,9 +161,18 @@ export declare class BuyerProxy {
|
|
|
157
161
|
* deltas (buyer- and seller-initiated auth both advance the cumulative).
|
|
158
162
|
*/
|
|
159
163
|
private readonly _requestConversations;
|
|
164
|
+
/** Signed spend for requests a local gateway tagged, polled over the control plane. */
|
|
165
|
+
private readonly _spendAttribution;
|
|
160
166
|
constructor(config: BuyerProxyConfig);
|
|
161
167
|
/** Roll a signed spend delta into the conversation that triggered it. */
|
|
162
168
|
private _attributeSpend;
|
|
169
|
+
/**
|
|
170
|
+
* Identity named by a request: null for the default, the name when it is
|
|
171
|
+
* (or can be) loaded, undefined when no stored identity has that name.
|
|
172
|
+
*/
|
|
173
|
+
private _resolveBuyerIdentity;
|
|
174
|
+
/** `?identity=` on control-plane reads; undefined when it names no stored identity. */
|
|
175
|
+
private _controlPlaneIdentity;
|
|
163
176
|
/**
|
|
164
177
|
* Remember which chat a request belongs to for the life of the request plus
|
|
165
178
|
* a grace window — the seller-initiated auth path can land just after the
|