@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.
Files changed (71) hide show
  1. package/README.md +38 -19
  2. package/dist/cli/commands/buyer/activity.d.ts +3 -0
  3. package/dist/cli/commands/buyer/activity.d.ts.map +1 -0
  4. package/dist/cli/commands/buyer/activity.js +555 -0
  5. package/dist/cli/commands/buyer/activity.js.map +1 -0
  6. package/dist/cli/commands/buyer/daemon.d.ts +13 -0
  7. package/dist/cli/commands/buyer/daemon.d.ts.map +1 -0
  8. package/dist/cli/commands/buyer/daemon.js +39 -0
  9. package/dist/cli/commands/buyer/daemon.js.map +1 -0
  10. package/dist/cli/commands/buyer/deposit.d.ts +8 -0
  11. package/dist/cli/commands/buyer/deposit.d.ts.map +1 -1
  12. package/dist/cli/commands/buyer/deposit.js +338 -25
  13. package/dist/cli/commands/buyer/deposit.js.map +1 -1
  14. package/dist/cli/commands/buyer/index.d.ts.map +1 -1
  15. package/dist/cli/commands/buyer/index.js +2 -0
  16. package/dist/cli/commands/buyer/index.js.map +1 -1
  17. package/dist/cli/commands/buyer/start.d.ts.map +1 -1
  18. package/dist/cli/commands/buyer/start.js +44 -2
  19. package/dist/cli/commands/buyer/start.js.map +1 -1
  20. package/dist/cli/commands/buyer/sweep.d.ts.map +1 -1
  21. package/dist/cli/commands/buyer/sweep.js +17 -17
  22. package/dist/cli/commands/buyer/sweep.js.map +1 -1
  23. package/dist/cli/commands/payments.d.ts.map +1 -1
  24. package/dist/cli/commands/payments.js +29 -34
  25. package/dist/cli/commands/payments.js.map +1 -1
  26. package/dist/cli/commands/seller/start.d.ts.map +1 -1
  27. package/dist/cli/commands/seller/start.js +4 -1
  28. package/dist/cli/commands/seller/start.js.map +1 -1
  29. package/dist/cli/commands/seller/start.test.js +15 -0
  30. package/dist/cli/commands/seller/start.test.js.map +1 -1
  31. package/dist/cli/index.js +2 -0
  32. package/dist/cli/index.js.map +1 -1
  33. package/dist/config/defaults.d.ts.map +1 -1
  34. package/dist/config/defaults.js +1 -0
  35. package/dist/config/defaults.js.map +1 -1
  36. package/dist/config/loader.d.ts.map +1 -1
  37. package/dist/config/loader.js +2 -0
  38. package/dist/config/loader.js.map +1 -1
  39. package/dist/config/loader.test.js +18 -0
  40. package/dist/config/loader.test.js.map +1 -1
  41. package/dist/config/types.d.ts +7 -0
  42. package/dist/config/types.d.ts.map +1 -1
  43. package/dist/generated/baked-defaults.d.ts +15 -0
  44. package/dist/generated/baked-defaults.d.ts.map +1 -0
  45. package/dist/generated/baked-defaults.js +15 -0
  46. package/dist/generated/baked-defaults.js.map +1 -0
  47. package/dist/plugins/manager.d.ts +1 -0
  48. package/dist/plugins/manager.d.ts.map +1 -1
  49. package/dist/plugins/manager.js +15 -4
  50. package/dist/plugins/manager.js.map +1 -1
  51. package/dist/plugins/manager.test.d.ts +2 -0
  52. package/dist/plugins/manager.test.d.ts.map +1 -0
  53. package/dist/plugins/manager.test.js +15 -0
  54. package/dist/plugins/manager.test.js.map +1 -0
  55. package/dist/proxy/buyer-proxy.d.ts +12 -1
  56. package/dist/proxy/buyer-proxy.d.ts.map +1 -1
  57. package/dist/proxy/buyer-proxy.js +75 -2
  58. package/dist/proxy/buyer-proxy.js.map +1 -1
  59. package/dist/proxy/buyer-proxy.test.js +96 -7
  60. package/dist/proxy/buyer-proxy.test.js.map +1 -1
  61. package/dist/proxy/deposit-watcher.d.ts +125 -0
  62. package/dist/proxy/deposit-watcher.d.ts.map +1 -0
  63. package/dist/proxy/deposit-watcher.js +306 -0
  64. package/dist/proxy/deposit-watcher.js.map +1 -0
  65. package/dist/proxy/deposit-watcher.test.d.ts +2 -0
  66. package/dist/proxy/deposit-watcher.test.d.ts.map +1 -0
  67. package/dist/proxy/deposit-watcher.test.js +173 -0
  68. package/dist/proxy/deposit-watcher.test.js.map +1 -0
  69. package/dist/proxy/request-utils.js +4 -3
  70. package/dist/proxy/request-utils.js.map +1 -1
  71. 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-reputation canonical service match', async () => {
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-antstation-session-id': 'conversation-soft-affinity',
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-antstation-session-id': 'conversation-soft-affinity' },
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('antstation:conversation-soft-affinity');
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-antstation-session-id': 'conversation-soft-affinity' },
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('antstation:conversation-soft-affinity')}`,
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