@antseed/cli 0.1.177 → 0.1.179
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 +3 -0
- package/dist/cli/commands/apps/index.d.ts +42 -0
- package/dist/cli/commands/apps/index.d.ts.map +1 -0
- package/dist/cli/commands/apps/index.js +224 -0
- package/dist/cli/commands/apps/index.js.map +1 -0
- package/dist/cli/commands/apps/index.test.d.ts +2 -0
- package/dist/cli/commands/apps/index.test.d.ts.map +1 -0
- package/dist/cli/commands/apps/index.test.js +206 -0
- package/dist/cli/commands/apps/index.test.js.map +1 -0
- package/dist/cli/commands/buyer/deposit.d.ts.map +1 -1
- package/dist/cli/commands/buyer/deposit.js +9 -1
- package/dist/cli/commands/buyer/deposit.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/gateway/gateway.test.js +31 -0
- package/dist/gateway/gateway.test.js.map +1 -1
- package/dist/gateway/server.d.ts.map +1 -1
- package/dist/gateway/server.js +4 -1
- package/dist/gateway/server.js.map +1 -1
- package/dist/proxy/buyer-proxy.d.ts.map +1 -1
- package/dist/proxy/buyer-proxy.js +34 -14
- package/dist/proxy/buyer-proxy.js.map +1 -1
- package/dist/proxy/buyer-proxy.test.js +96 -0
- package/dist/proxy/buyer-proxy.test.js.map +1 -1
- package/dist/proxy/credential-headers.d.ts +17 -0
- package/dist/proxy/credential-headers.d.ts.map +1 -0
- package/dist/proxy/credential-headers.js +29 -0
- package/dist/proxy/credential-headers.js.map +1 -0
- package/dist/proxy/deposit-watcher.d.ts +19 -0
- package/dist/proxy/deposit-watcher.d.ts.map +1 -1
- package/dist/proxy/deposit-watcher.js +44 -8
- package/dist/proxy/deposit-watcher.js.map +1 -1
- package/dist/proxy/deposit-watcher.test.js +27 -4
- package/dist/proxy/deposit-watcher.test.js.map +1 -1
- package/dist/system-proxy/proxy-server.js +2 -2
- package/dist/system-proxy/proxy-server.js.map +1 -1
- package/dist/system-proxy/proxy-server.test.js +40 -0
- package/dist/system-proxy/proxy-server.test.js.map +1 -1
- package/package.json +7 -6
|
@@ -309,6 +309,25 @@ test('BuyerProxy accepts a custom background refresh interval', () => {
|
|
|
309
309
|
});
|
|
310
310
|
assert.equal(proxy._bgRefreshIntervalMs, 15_000);
|
|
311
311
|
});
|
|
312
|
+
test('BuyerProxy rewrites upstream 413 responses into a clear request size error', () => {
|
|
313
|
+
const proxy = new BuyerProxy({
|
|
314
|
+
port: 0,
|
|
315
|
+
dataDir: '/tmp/antseed-test',
|
|
316
|
+
node: { router: null },
|
|
317
|
+
});
|
|
318
|
+
const upstream = {
|
|
319
|
+
requestId: 'req-413',
|
|
320
|
+
statusCode: 413,
|
|
321
|
+
headers: { 'content-type': 'text/html' },
|
|
322
|
+
body: Buffer.from('<html><title>413 Request Entity Too Large</title></html>'),
|
|
323
|
+
};
|
|
324
|
+
const rewritten = proxy._withFriendlyUploadLimitError(upstream, 37 * 1024 * 1024, 'opus-5.5');
|
|
325
|
+
const { error } = JSON.parse(Buffer.from(rewritten.body).toString('utf-8'));
|
|
326
|
+
assert.equal(rewritten.headers['content-type'], 'application/json');
|
|
327
|
+
assert.equal(error.code, 'request_too_large');
|
|
328
|
+
assert.match(error.message, /Request body for opus-5\.5 is 37 MiB/);
|
|
329
|
+
assert.match(error.message, /Images and large attachments/);
|
|
330
|
+
});
|
|
312
331
|
test('BuyerProxy reloads model routing preferences from config', async (t) => {
|
|
313
332
|
const dataDir = await mkdtemp(join(tmpdir(), 'antseed-routing-config-'));
|
|
314
333
|
const configPath = join(dataDir, 'config.json');
|
|
@@ -784,6 +803,29 @@ test('conversation routing keeps the actual peer as a soft preference and fails
|
|
|
784
803
|
assert.equal(route.statusCode, 200);
|
|
785
804
|
assert.equal(JSON.parse(route.body).conversation.lastModel, `${rankedFirst.peerId}@Kimi K3`);
|
|
786
805
|
});
|
|
806
|
+
test('BuyerProxy does not forward the Expect header to the seller', async () => {
|
|
807
|
+
const peer = makePeer('c', ['openai']);
|
|
808
|
+
peer.providerServiceApiProtocols = {
|
|
809
|
+
openai: { services: { 'kimi-k3': ['openai-chat-completions'] } },
|
|
810
|
+
};
|
|
811
|
+
const proxy = makeBuyerProxyWithPeers([peer], [peer], permissiveRouter());
|
|
812
|
+
let forwardedHeaders;
|
|
813
|
+
proxy._node.sendRequest = async (_peer, request) => {
|
|
814
|
+
forwardedHeaders = request.headers;
|
|
815
|
+
return {
|
|
816
|
+
requestId: request.requestId,
|
|
817
|
+
statusCode: 200,
|
|
818
|
+
headers: { 'content-type': 'application/json' },
|
|
819
|
+
body: Buffer.from('{}'),
|
|
820
|
+
};
|
|
821
|
+
};
|
|
822
|
+
await invokeProxy(proxy, makeProxyRequest({
|
|
823
|
+
headers: { expect: '100-continue' },
|
|
824
|
+
body: { model: 'kimi-k3', messages: [{ role: 'user', content: 'hello' }] },
|
|
825
|
+
}));
|
|
826
|
+
assert.ok(forwardedHeaders);
|
|
827
|
+
assert.equal(forwardedHeaders['expect'], undefined);
|
|
828
|
+
});
|
|
787
829
|
test('model-only request applies a cached-input pricing reputation penalty', async () => {
|
|
788
830
|
const priced = makePeer('a', ['openai']);
|
|
789
831
|
priced.reputationScore = 75;
|
|
@@ -3384,6 +3426,60 @@ test('getSweepReceipt returns cached relayer receipts case-insensitively', () =>
|
|
|
3384
3426
|
assert.equal(proxy.getSweepReceipt(nonce.toLowerCase()), receipt);
|
|
3385
3427
|
assert.equal(proxy.getSweepReceipt('0x' + '00'.repeat(32)), null);
|
|
3386
3428
|
});
|
|
3429
|
+
// Issue #1104: a client's own provider credentials must never reach a seller.
|
|
3430
|
+
// Sellers authenticate their upstream with their own keys; the buyer pays
|
|
3431
|
+
// on-chain. A malicious seller would otherwise read the raw key off the wire.
|
|
3432
|
+
const CLIENT_CREDENTIAL_HEADERS = {
|
|
3433
|
+
authorization: 'Bearer sk-real',
|
|
3434
|
+
'proxy-authorization': 'Basic cHJveHk6c2VjcmV0',
|
|
3435
|
+
'x-api-key': 'sk-ant-real',
|
|
3436
|
+
'x-goog-api-key': 'AIza-real',
|
|
3437
|
+
'api-key': 'azure-real',
|
|
3438
|
+
cookie: 'session=real',
|
|
3439
|
+
};
|
|
3440
|
+
test('buyer proxy does not forward client credential headers to the seller (buffered)', async () => {
|
|
3441
|
+
const peer = makePeer('e', ['openai']);
|
|
3442
|
+
const proxy = makeBuyerProxyWithPeers([peer], [peer], permissiveRouter());
|
|
3443
|
+
let seen = null;
|
|
3444
|
+
proxy._node.sendRequest = async (_peer, request) => {
|
|
3445
|
+
seen = request.headers;
|
|
3446
|
+
return { requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' }, body: Buffer.from('{}') };
|
|
3447
|
+
};
|
|
3448
|
+
const res = await invokeProxy(proxy, makeProxyRequest({
|
|
3449
|
+
headers: {
|
|
3450
|
+
...CLIENT_CREDENTIAL_HEADERS,
|
|
3451
|
+
'x-antseed-pin-peer': peer.peerId,
|
|
3452
|
+
'anthropic-version': '2023-06-01',
|
|
3453
|
+
'x-antseed-spending-auth': 'signed-auth-payload',
|
|
3454
|
+
},
|
|
3455
|
+
}));
|
|
3456
|
+
assert.equal(res.statusCode, 200);
|
|
3457
|
+
assert.ok(seen, 'seller received a request');
|
|
3458
|
+
const leaked = Object.keys(CLIENT_CREDENTIAL_HEADERS).filter((name) => seen[name] !== undefined);
|
|
3459
|
+
assert.deepEqual(leaked, [], `credential headers reached the seller: ${leaked.join(', ')}`);
|
|
3460
|
+
// Non-credential client headers still pass through.
|
|
3461
|
+
assert.equal(seen['anthropic-version'], '2023-06-01');
|
|
3462
|
+
assert.equal(seen['content-type'], 'application/json');
|
|
3463
|
+
// AntSeed's own payment-auth header is not a client credential.
|
|
3464
|
+
assert.equal(seen['x-antseed-spending-auth'], 'signed-auth-payload');
|
|
3465
|
+
});
|
|
3466
|
+
test('buyer proxy does not forward client credential headers to the seller (streaming)', async () => {
|
|
3467
|
+
const peer = makePeer('f', ['openai']);
|
|
3468
|
+
const proxy = makeBuyerProxyWithPeers([peer], [peer], permissiveRouter());
|
|
3469
|
+
let seen = null;
|
|
3470
|
+
proxy._node.sendRequestStream = async (_peer, request) => {
|
|
3471
|
+
seen = request.headers;
|
|
3472
|
+
return { requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' }, body: Buffer.from('{}') };
|
|
3473
|
+
};
|
|
3474
|
+
proxy._node.sendRequest = proxy._node.sendRequestStream;
|
|
3475
|
+
await invokeProxy(proxy, makeProxyRequest({
|
|
3476
|
+
headers: { ...CLIENT_CREDENTIAL_HEADERS, accept: 'text/event-stream', 'x-antseed-pin-peer': peer.peerId },
|
|
3477
|
+
body: { model: 'gpt-4o', stream: true, messages: [] },
|
|
3478
|
+
}));
|
|
3479
|
+
assert.ok(seen, 'seller received a request');
|
|
3480
|
+
const leaked = Object.keys(CLIENT_CREDENTIAL_HEADERS).filter((name) => seen[name] !== undefined);
|
|
3481
|
+
assert.deepEqual(leaked, [], `credential headers reached the seller: ${leaked.join(', ')}`);
|
|
3482
|
+
});
|
|
3387
3483
|
test('switching a chat route while a request is in flight is not reverted when that request completes', async () => {
|
|
3388
3484
|
const dir = await mkdtemp(join(tmpdir(), 'antseed-buyer-route-race-'));
|
|
3389
3485
|
try {
|