@antseed/cli 0.1.149 → 0.1.151
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 +38 -19
- package/dist/cli/commands/buyer/activity.d.ts +3 -0
- package/dist/cli/commands/buyer/activity.d.ts.map +1 -0
- package/dist/cli/commands/buyer/activity.js +555 -0
- package/dist/cli/commands/buyer/activity.js.map +1 -0
- package/dist/cli/commands/buyer/daemon.d.ts +13 -0
- package/dist/cli/commands/buyer/daemon.d.ts.map +1 -0
- package/dist/cli/commands/buyer/daemon.js +39 -0
- package/dist/cli/commands/buyer/daemon.js.map +1 -0
- package/dist/cli/commands/buyer/deposit.d.ts +8 -0
- package/dist/cli/commands/buyer/deposit.d.ts.map +1 -1
- package/dist/cli/commands/buyer/deposit.js +338 -25
- package/dist/cli/commands/buyer/deposit.js.map +1 -1
- 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 +44 -2
- package/dist/cli/commands/buyer/start.js.map +1 -1
- package/dist/cli/commands/buyer/sweep.d.ts.map +1 -1
- package/dist/cli/commands/buyer/sweep.js +17 -17
- package/dist/cli/commands/buyer/sweep.js.map +1 -1
- package/dist/cli/commands/payments.d.ts.map +1 -1
- package/dist/cli/commands/payments.js +29 -34
- package/dist/cli/commands/payments.js.map +1 -1
- package/dist/cli/commands/seller/start.d.ts.map +1 -1
- package/dist/cli/commands/seller/start.js +4 -1
- package/dist/cli/commands/seller/start.js.map +1 -1
- package/dist/cli/commands/seller/start.test.js +15 -0
- package/dist/cli/commands/seller/start.test.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +1 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +2 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/loader.test.js +18 -0
- package/dist/config/loader.test.js.map +1 -1
- package/dist/config/types.d.ts +7 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/generated/baked-defaults.d.ts +15 -0
- package/dist/generated/baked-defaults.d.ts.map +1 -0
- package/dist/generated/baked-defaults.js +15 -0
- package/dist/generated/baked-defaults.js.map +1 -0
- package/dist/plugins/manager.d.ts +1 -0
- package/dist/plugins/manager.d.ts.map +1 -1
- package/dist/plugins/manager.js +15 -4
- package/dist/plugins/manager.js.map +1 -1
- package/dist/plugins/manager.test.d.ts +2 -0
- package/dist/plugins/manager.test.d.ts.map +1 -0
- package/dist/plugins/manager.test.js +15 -0
- package/dist/plugins/manager.test.js.map +1 -0
- package/dist/proxy/buyer-proxy.d.ts +12 -1
- package/dist/proxy/buyer-proxy.d.ts.map +1 -1
- package/dist/proxy/buyer-proxy.js +75 -2
- package/dist/proxy/buyer-proxy.js.map +1 -1
- package/dist/proxy/buyer-proxy.test.js +96 -7
- package/dist/proxy/buyer-proxy.test.js.map +1 -1
- package/dist/proxy/deposit-watcher.d.ts +125 -0
- package/dist/proxy/deposit-watcher.d.ts.map +1 -0
- package/dist/proxy/deposit-watcher.js +306 -0
- package/dist/proxy/deposit-watcher.js.map +1 -0
- package/dist/proxy/deposit-watcher.test.d.ts +2 -0
- package/dist/proxy/deposit-watcher.test.d.ts.map +1 -0
- package/dist/proxy/deposit-watcher.test.js +173 -0
- package/dist/proxy/deposit-watcher.test.js.map +1 -0
- package/dist/proxy/request-utils.js +4 -3
- package/dist/proxy/request-utils.js.map +1 -1
- package/package.json +6 -5
|
@@ -8,7 +8,7 @@ import test from 'node:test';
|
|
|
8
8
|
import { ANTSEED_BUYER_FAULT_ERROR_CODE, ANTSEED_FAULT_ATTRIBUTION_HEADER, CONNECTION_CAPABILITY_COOPERATIVE_CLOSE_V1, CONNECTION_CAPABILITY_RELAYS_SWEEPS_V1, buyerFault, computeOnChainReputationScore, } from '@antseed/node';
|
|
9
9
|
import { DEFAULT_BUYER_PEER_REFRESH_INTERVAL_MS } from '../config/defaults.js';
|
|
10
10
|
import { BuyerProxy, isModelNotFoundResponse, makeVerifierReach, mergeJsonStateFile, parsePeerPinnedService, parsePersistedPeers, rewritePeerPinnedServiceInBody, selectCandidatePeersForRouting, substituteRoutedModelAlias, sweepStaleStateTmpFiles, } from './buyer-proxy.js';
|
|
11
|
-
import { overrideRoutedModelInBody, SYSTEM_ROUTED_MODEL_HEADER } from './request-utils.js';
|
|
11
|
+
import { extractRequestedService, overrideRoutedModelInBody, SYSTEM_ROUTED_MODEL_HEADER } from './request-utils.js';
|
|
12
12
|
function makePeer(seed, providers) {
|
|
13
13
|
const repeated = (seed.repeat(40) + 'a'.repeat(40)).slice(0, 40);
|
|
14
14
|
return {
|
|
@@ -423,7 +423,7 @@ test('selectCandidatePeersForRouting can still include peers without service pro
|
|
|
423
423
|
assert.equal(result.candidatePeers.length, 1);
|
|
424
424
|
assert.equal(result.candidatePeers[0]?.peerId, peerWithoutMetadata.peerId);
|
|
425
425
|
});
|
|
426
|
-
test('model-only request routes to the highest-
|
|
426
|
+
test('model-only request routes to the highest-ranked canonical service match', async () => {
|
|
427
427
|
const lower = makePeer('a', ['anthropic']);
|
|
428
428
|
lower.reputationScore = 40;
|
|
429
429
|
lower.providerServiceApiProtocols = {
|
|
@@ -471,6 +471,7 @@ test('conversation routing keeps the actual peer as a soft preference and fails
|
|
|
471
471
|
let preferredReachable = true;
|
|
472
472
|
proxy._node.sendRequest = async (peer, request) => {
|
|
473
473
|
attempts.push(peer.peerId);
|
|
474
|
+
assert.equal(request.headers['x-vpr-session-id'], undefined);
|
|
474
475
|
assert.equal(request.headers['x-antstation-session-id'], undefined);
|
|
475
476
|
assert.equal(request.headers['x-antseed-prefer-peer'], undefined);
|
|
476
477
|
return {
|
|
@@ -481,7 +482,7 @@ test('conversation routing keeps the actual peer as a soft preference and fails
|
|
|
481
482
|
};
|
|
482
483
|
};
|
|
483
484
|
const conversationHeaders = {
|
|
484
|
-
'x-
|
|
485
|
+
'x-vpr-session-id': 'conversation-soft-affinity',
|
|
485
486
|
'x-antseed-prefer-peer': preferred.peerId,
|
|
486
487
|
};
|
|
487
488
|
await invokeProxy(proxy, makeProxyRequest({
|
|
@@ -492,22 +493,22 @@ test('conversation routing keeps the actual peer as a soft preference and fails
|
|
|
492
493
|
preferredReachable = false;
|
|
493
494
|
attempts.length = 0;
|
|
494
495
|
await invokeProxy(proxy, makeProxyRequest({
|
|
495
|
-
headers: { 'x-
|
|
496
|
+
headers: { 'x-vpr-session-id': 'conversation-soft-affinity' },
|
|
496
497
|
body: { model: 'kimi-k3', messages: [{ role: 'user', content: 'again' }] },
|
|
497
498
|
}));
|
|
498
499
|
assert.deepEqual(attempts, [preferred.peerId, rankedFirst.peerId]);
|
|
499
|
-
const stored = proxy._conversations.get('
|
|
500
|
+
const stored = proxy._conversations.get('vpr:conversation-soft-affinity');
|
|
500
501
|
assert.equal(stored?.pinnedModel, `${rankedFirst.peerId}@Kimi K3`);
|
|
501
502
|
assert.equal(stored?.lastModel, `${rankedFirst.peerId}@Kimi K3`);
|
|
502
503
|
attempts.length = 0;
|
|
503
504
|
await invokeProxy(proxy, makeProxyRequest({
|
|
504
|
-
headers: { 'x-
|
|
505
|
+
headers: { 'x-vpr-session-id': 'conversation-soft-affinity' },
|
|
505
506
|
body: { model: 'kimi-k3', messages: [{ role: 'user', content: 'third' }] },
|
|
506
507
|
}));
|
|
507
508
|
assert.equal(attempts[0], rankedFirst.peerId);
|
|
508
509
|
const route = await invokeProxy(proxy, makeProxyRequest({
|
|
509
510
|
method: 'GET',
|
|
510
|
-
path: `/_antseed/conversations/${encodeURIComponent('
|
|
511
|
+
path: `/_antseed/conversations/${encodeURIComponent('vpr:conversation-soft-affinity')}`,
|
|
511
512
|
}));
|
|
512
513
|
assert.equal(route.statusCode, 200);
|
|
513
514
|
assert.equal(JSON.parse(route.body).conversation.lastModel, `${rankedFirst.peerId}@Kimi K3`);
|
|
@@ -1958,6 +1959,29 @@ function parseJsonBody(body) {
|
|
|
1958
1959
|
return JSON.parse(new TextDecoder().decode(body));
|
|
1959
1960
|
}
|
|
1960
1961
|
const jsonHeaders = { 'content-type': 'application/json' };
|
|
1962
|
+
test('extractRequestedService reads the model from multipart image edits', () => {
|
|
1963
|
+
const boundary = 'image-edit-boundary';
|
|
1964
|
+
const body = new TextEncoder().encode([
|
|
1965
|
+
`--${boundary}`,
|
|
1966
|
+
'Content-Disposition: form-data; name="model"',
|
|
1967
|
+
'',
|
|
1968
|
+
'gpt-image-2',
|
|
1969
|
+
`--${boundary}`,
|
|
1970
|
+
'Content-Disposition: form-data; name="image"; filename="source.png"',
|
|
1971
|
+
'Content-Type: image/png',
|
|
1972
|
+
'',
|
|
1973
|
+
'image-bytes',
|
|
1974
|
+
`--${boundary}--`,
|
|
1975
|
+
'',
|
|
1976
|
+
].join('\r\n'));
|
|
1977
|
+
assert.equal(extractRequestedService({
|
|
1978
|
+
requestId: 'request-1',
|
|
1979
|
+
method: 'POST',
|
|
1980
|
+
path: '/v1/images/edits',
|
|
1981
|
+
headers: { 'content-type': `multipart/form-data; boundary=${boundary}` },
|
|
1982
|
+
body,
|
|
1983
|
+
}), 'gpt-image-2');
|
|
1984
|
+
});
|
|
1961
1985
|
test('parsePeerPinnedService parses 40-char hex peer prefixes', () => {
|
|
1962
1986
|
assert.deepEqual(parsePeerPinnedService(`${validPeerId}@claude-sonnet-4-5`), {
|
|
1963
1987
|
peerId: validPeerId,
|
|
@@ -2511,4 +2535,69 @@ test('sweepStaleStateTmpFiles: removes aged temp files, keeps fresh and unrelate
|
|
|
2511
2535
|
test('sweepStaleStateTmpFiles: tolerates a missing directory', async () => {
|
|
2512
2536
|
await sweepStaleStateTmpFiles(join(tmpdir(), 'antseed-does-not-exist', randomUUID()));
|
|
2513
2537
|
});
|
|
2538
|
+
// ---------- Deposit watcher control plane ----------
|
|
2539
|
+
test('deposits/status reports no watcher until one is attached', async () => {
|
|
2540
|
+
const proxy = new BuyerProxy({
|
|
2541
|
+
port: 0,
|
|
2542
|
+
dataDir: '/tmp/antseed-test',
|
|
2543
|
+
node: { router: null },
|
|
2544
|
+
});
|
|
2545
|
+
const res = await invokeProxy(proxy, makeProxyRequest({ method: 'GET', path: '/_antseed/deposits/status' }));
|
|
2546
|
+
assert.equal(res.statusCode, 200);
|
|
2547
|
+
assert.deepEqual(JSON.parse(res.body), { ok: true, watcher: false, status: null });
|
|
2548
|
+
});
|
|
2549
|
+
test('deposits/watch returns 503 when no watcher is attached', async () => {
|
|
2550
|
+
const proxy = new BuyerProxy({
|
|
2551
|
+
port: 0,
|
|
2552
|
+
dataDir: '/tmp/antseed-test',
|
|
2553
|
+
node: { router: null },
|
|
2554
|
+
});
|
|
2555
|
+
const res = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/deposits/watch', body: { mode: 'active' } }));
|
|
2556
|
+
assert.equal(res.statusCode, 503);
|
|
2557
|
+
assert.equal(JSON.parse(res.body).ok, false);
|
|
2558
|
+
});
|
|
2559
|
+
test('deposits/watch promotes and demotes an attached watcher and returns its status', async () => {
|
|
2560
|
+
const proxy = new BuyerProxy({
|
|
2561
|
+
port: 0,
|
|
2562
|
+
dataDir: '/tmp/antseed-test',
|
|
2563
|
+
node: { router: null },
|
|
2564
|
+
});
|
|
2565
|
+
const calls = [];
|
|
2566
|
+
const fakeStatus = {
|
|
2567
|
+
mode: 'idle',
|
|
2568
|
+
address: '0x' + 'ab'.repeat(20),
|
|
2569
|
+
usdcBalance: '0',
|
|
2570
|
+
sweepInFlight: false,
|
|
2571
|
+
lastEvent: null,
|
|
2572
|
+
};
|
|
2573
|
+
proxy.setDepositWatcher({
|
|
2574
|
+
promote: () => { calls.push('promote'); },
|
|
2575
|
+
demote: () => { calls.push('demote'); },
|
|
2576
|
+
status: () => fakeStatus,
|
|
2577
|
+
});
|
|
2578
|
+
const active = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/deposits/watch', body: { mode: 'active' } }));
|
|
2579
|
+
assert.equal(active.statusCode, 200);
|
|
2580
|
+
assert.deepEqual(JSON.parse(active.body), { ok: true, status: fakeStatus });
|
|
2581
|
+
const background = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/deposits/watch', body: { mode: 'background' } }));
|
|
2582
|
+
assert.equal(background.statusCode, 200);
|
|
2583
|
+
assert.deepEqual(calls, ['promote', 'demote']);
|
|
2584
|
+
const invalid = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/deposits/watch', body: { mode: 'nonsense' } }));
|
|
2585
|
+
assert.equal(invalid.statusCode, 400);
|
|
2586
|
+
assert.deepEqual(calls, ['promote', 'demote']);
|
|
2587
|
+
const status = await invokeProxy(proxy, makeProxyRequest({ method: 'GET', path: '/_antseed/deposits/status' }));
|
|
2588
|
+
assert.deepEqual(JSON.parse(status.body), { ok: true, watcher: true, status: fakeStatus });
|
|
2589
|
+
});
|
|
2590
|
+
test('getSweepReceipt returns cached relayer receipts case-insensitively', () => {
|
|
2591
|
+
const proxy = new BuyerProxy({
|
|
2592
|
+
port: 0,
|
|
2593
|
+
dataDir: '/tmp/antseed-test',
|
|
2594
|
+
node: { router: null },
|
|
2595
|
+
});
|
|
2596
|
+
const nonce = '0x' + 'AB'.repeat(32);
|
|
2597
|
+
const receipt = { authNonce: nonce.toLowerCase(), status: 'confirmed', txHash: '0x' + '12'.repeat(32) };
|
|
2598
|
+
proxy._sweepReceipts.set(nonce.toLowerCase(), receipt);
|
|
2599
|
+
assert.equal(proxy.getSweepReceipt(nonce), receipt);
|
|
2600
|
+
assert.equal(proxy.getSweepReceipt(nonce.toLowerCase()), receipt);
|
|
2601
|
+
assert.equal(proxy.getSweepReceipt('0x' + '00'.repeat(32)), null);
|
|
2602
|
+
});
|
|
2514
2603
|
//# sourceMappingURL=buyer-proxy.test.js.map
|