@antseed/cli 0.1.196 → 0.1.197

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 (109) hide show
  1. package/dist/cli/commands/buyer/start.d.ts.map +1 -1
  2. package/dist/cli/commands/buyer/start.js +1 -0
  3. package/dist/cli/commands/buyer/start.js.map +1 -1
  4. package/dist/cli/commands/gateway/activity.d.ts.map +1 -1
  5. package/dist/cli/commands/gateway/activity.js +26 -5
  6. package/dist/cli/commands/gateway/activity.js.map +1 -1
  7. package/dist/config/types.d.ts +2 -0
  8. package/dist/config/types.d.ts.map +1 -1
  9. package/dist/gateway/accounting.d.ts +118 -6
  10. package/dist/gateway/accounting.d.ts.map +1 -1
  11. package/dist/gateway/accounting.js +319 -21
  12. package/dist/gateway/accounting.js.map +1 -1
  13. package/dist/gateway/console-api/handlers/favorites.d.ts +9 -0
  14. package/dist/gateway/console-api/handlers/favorites.d.ts.map +1 -0
  15. package/dist/gateway/console-api/handlers/favorites.js +44 -0
  16. package/dist/gateway/console-api/handlers/favorites.js.map +1 -0
  17. package/dist/gateway/console-api/handlers/favorites.test.d.ts +2 -0
  18. package/dist/gateway/console-api/handlers/favorites.test.d.ts.map +1 -0
  19. package/dist/gateway/console-api/handlers/favorites.test.js +41 -0
  20. package/dist/gateway/console-api/handlers/favorites.test.js.map +1 -0
  21. package/dist/gateway/console-api/handlers/index.d.ts.map +1 -1
  22. package/dist/gateway/console-api/handlers/index.js +2 -0
  23. package/dist/gateway/console-api/handlers/index.js.map +1 -1
  24. package/dist/gateway/console-api/handlers/network-mapping.d.ts +7 -1
  25. package/dist/gateway/console-api/handlers/network-mapping.d.ts.map +1 -1
  26. package/dist/gateway/console-api/handlers/network-mapping.js +44 -0
  27. package/dist/gateway/console-api/handlers/network-mapping.js.map +1 -1
  28. package/dist/gateway/console-api/handlers/network.d.ts.map +1 -1
  29. package/dist/gateway/console-api/handlers/network.js +3 -1
  30. package/dist/gateway/console-api/handlers/network.js.map +1 -1
  31. package/dist/gateway/console-api/handlers/network.test.js +39 -4
  32. package/dist/gateway/console-api/handlers/network.test.js.map +1 -1
  33. package/dist/gateway/console-api/handlers/usage.d.ts.map +1 -1
  34. package/dist/gateway/console-api/handlers/usage.js +2 -1
  35. package/dist/gateway/console-api/handlers/usage.js.map +1 -1
  36. package/dist/gateway/console-api/types.d.ts +69 -1
  37. package/dist/gateway/console-api/types.d.ts.map +1 -1
  38. package/dist/gateway/console-api/types.js.map +1 -1
  39. package/dist/gateway/gateway.test.js +829 -3
  40. package/dist/gateway/gateway.test.js.map +1 -1
  41. package/dist/gateway/request-errors.test.js +30 -0
  42. package/dist/gateway/request-errors.test.js.map +1 -1
  43. package/dist/gateway/routing-through-gateway.test.js +86 -4
  44. package/dist/gateway/routing-through-gateway.test.js.map +1 -1
  45. package/dist/gateway/runtime.d.ts.map +1 -1
  46. package/dist/gateway/runtime.js +4 -0
  47. package/dist/gateway/runtime.js.map +1 -1
  48. package/dist/gateway/server.d.ts +20 -0
  49. package/dist/gateway/server.d.ts.map +1 -1
  50. package/dist/gateway/server.js +178 -25
  51. package/dist/gateway/server.js.map +1 -1
  52. package/dist/gateway/services/network.d.ts +6 -1
  53. package/dist/gateway/services/network.d.ts.map +1 -1
  54. package/dist/gateway/services/network.js +12 -1
  55. package/dist/gateway/services/network.js.map +1 -1
  56. package/dist/gateway/services/usage.d.ts +2 -0
  57. package/dist/gateway/services/usage.d.ts.map +1 -1
  58. package/dist/gateway/services/usage.js +18 -4
  59. package/dist/gateway/services/usage.js.map +1 -1
  60. package/dist/gateway/spend-feed.d.ts +4 -1
  61. package/dist/gateway/spend-feed.d.ts.map +1 -1
  62. package/dist/gateway/spend-feed.js +7 -0
  63. package/dist/gateway/spend-feed.js.map +1 -1
  64. package/dist/gateway/store-data.test.js +1 -1
  65. package/dist/gateway/store-data.test.js.map +1 -1
  66. package/dist/gateway/store-migrations.d.ts.map +1 -1
  67. package/dist/gateway/store-migrations.js +35 -0
  68. package/dist/gateway/store-migrations.js.map +1 -1
  69. package/dist/gateway/store.d.ts +59 -1
  70. package/dist/gateway/store.d.ts.map +1 -1
  71. package/dist/gateway/store.js +124 -19
  72. package/dist/gateway/store.js.map +1 -1
  73. package/dist/gateway/video-jobs.d.ts +12 -0
  74. package/dist/gateway/video-jobs.d.ts.map +1 -0
  75. package/dist/gateway/video-jobs.js +24 -0
  76. package/dist/gateway/video-jobs.js.map +1 -0
  77. package/dist/proxy/buyer-proxy.d.ts +15 -0
  78. package/dist/proxy/buyer-proxy.d.ts.map +1 -1
  79. package/dist/proxy/buyer-proxy.js +92 -13
  80. package/dist/proxy/buyer-proxy.js.map +1 -1
  81. package/dist/proxy/buyer-proxy.test.js +158 -1
  82. package/dist/proxy/buyer-proxy.test.js.map +1 -1
  83. package/dist/proxy/request-utils.d.ts +4 -0
  84. package/dist/proxy/request-utils.d.ts.map +1 -1
  85. package/dist/proxy/request-utils.js +4 -0
  86. package/dist/proxy/request-utils.js.map +1 -1
  87. package/dist/proxy/resource-routes.d.ts +2 -0
  88. package/dist/proxy/resource-routes.d.ts.map +1 -1
  89. package/dist/proxy/resource-routes.js +4 -0
  90. package/dist/proxy/resource-routes.js.map +1 -1
  91. package/dist/proxy/spend-attribution.d.ts +17 -1
  92. package/dist/proxy/spend-attribution.d.ts.map +1 -1
  93. package/dist/proxy/spend-attribution.js +45 -10
  94. package/dist/proxy/spend-attribution.js.map +1 -1
  95. package/dist/proxy/spend-attribution.test.d.ts +2 -0
  96. package/dist/proxy/spend-attribution.test.d.ts.map +1 -0
  97. package/dist/proxy/spend-attribution.test.js +63 -0
  98. package/dist/proxy/spend-attribution.test.js.map +1 -0
  99. package/dist/proxy/telemetry.d.ts +14 -0
  100. package/dist/proxy/telemetry.d.ts.map +1 -1
  101. package/dist/proxy/telemetry.js +23 -3
  102. package/dist/proxy/telemetry.js.map +1 -1
  103. package/dist/proxy/video-download.d.ts +9 -1
  104. package/dist/proxy/video-download.d.ts.map +1 -1
  105. package/dist/proxy/video-download.js +41 -6
  106. package/dist/proxy/video-download.js.map +1 -1
  107. package/dist/proxy/video-download.test.js +33 -3
  108. package/dist/proxy/video-download.test.js.map +1 -1
  109. package/package.json +7 -7
@@ -1453,6 +1453,157 @@ test('native video retrieves use the buyer identity that created the job', async
1453
1453
  { path: '/api/v1/video/retrieve', identity: 'team-a' },
1454
1454
  ]);
1455
1455
  });
1456
+ test('a video create passes the caller spend cap to the buyer and never to the seller', async () => {
1457
+ const peer = makePeer('a', ['venice']);
1458
+ peer.providerServiceApiProtocols = { venice: { services: { 'video-model': ['venice-video'] } } };
1459
+ const proxy = makeBuyerProxyWithPeers([peer], [peer], permissiveRouter());
1460
+ proxy._persistResourceRoutes = async () => { };
1461
+ const sent = [];
1462
+ proxy._node.sendRequest = async (_peer, request, options) => {
1463
+ sent.push({ path: request.path, cap: options?.maxVideoCostUsdc, header: request.headers['x-antseed-max-video-usdc'] });
1464
+ return { requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json', 'x-antseed-video-price-usdc': '650000' }, body: Buffer.from(JSON.stringify({ queue_id: 'job' })) };
1465
+ };
1466
+ for (const cap of ['1500000', 'lots']) {
1467
+ const queued = await invokeProxy(proxy, makeProxyRequest({ path: '/api/v1/video/queue', body: { model: 'video-model', prompt: 'cat', duration: '5s' }, headers: { 'x-antseed-max-video-usdc': cap } }));
1468
+ assert.equal(queued.statusCode, 200, queued.body);
1469
+ // The accepted job's price reaches the caller, so a gateway can keep it reserved until delivery.
1470
+ assert.equal(queued.headers['x-antseed-video-price-usdc'], '650000');
1471
+ }
1472
+ assert.deepEqual(sent, [
1473
+ { path: '/api/v1/video/queue', cap: 1500000n, header: undefined },
1474
+ { path: '/api/v1/video/queue', cap: undefined, header: undefined },
1475
+ ]);
1476
+ });
1477
+ test('an unknown video job is not said to be retired while the saved routes could not be read', async () => {
1478
+ const peer = makePeer('a', ['venice']);
1479
+ peer.providerServiceApiProtocols = { venice: { services: { 'video-model': ['venice-video'] } } };
1480
+ const proxy = makeBuyerProxyWithPeers([peer], [peer], permissiveRouter());
1481
+ const retrieve = () => invokeProxy(proxy, makeProxyRequest({ method: 'POST', path: '/api/v1/video/retrieve', body: { model: 'video-model', queue_id: 'job' } }));
1482
+ const known = await retrieve();
1483
+ assert.equal(known.statusCode, 404);
1484
+ assert.equal(known.headers['x-antseed-video-job-retired'], '1', 'with the routes read, an unknown job can never be charged');
1485
+ proxy._resourceRoutesUnread = true;
1486
+ const unread = await retrieve();
1487
+ assert.equal(unread.statusCode, 404);
1488
+ assert.equal(JSON.parse(unread.body).error.code, 'video_route_not_found');
1489
+ assert.equal(unread.headers['x-antseed-video-job-retired'], undefined);
1490
+ });
1491
+ test('a video job that reports failed is retired: later retrieves never reach the seller, so it cannot be charged', async () => {
1492
+ for (const upstream of [{ status: 'FAILED' }, { status: 'COMPLETED' }]) {
1493
+ const peer = makePeer('a', ['venice']);
1494
+ peer.providerServiceApiProtocols = { venice: { services: { 'video-model': ['venice-video'] } } };
1495
+ const proxy = makeBuyerProxyWithPeers([peer], [peer], permissiveRouter());
1496
+ let persisted = 0;
1497
+ proxy._persistResourceRoutes = async () => { persisted += 1; };
1498
+ proxy._node.sendRequest = async (_peer, request) => ({ requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' }, body: Buffer.from(JSON.stringify({ queue_id: 'job' })) });
1499
+ let retrieves = 0;
1500
+ proxy._node.sendRequestStream = async (_peer, request) => {
1501
+ retrieves += 1;
1502
+ return { requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' }, body: Buffer.from(JSON.stringify(upstream)) };
1503
+ };
1504
+ assert.equal((await invokeProxy(proxy, makeProxyRequest({ path: '/api/v1/video/queue', body: { model: 'video-model', prompt: 'cat', duration: '5s' } }))).statusCode, 200);
1505
+ const retrieve = () => invokeProxy(proxy, makeProxyRequest({ method: 'POST', path: '/api/v1/video/retrieve', body: { model: 'video-model', queue_id: 'job' } }));
1506
+ const failed = await retrieve();
1507
+ assert.equal(failed.statusCode, 200);
1508
+ assert.equal(JSON.parse(failed.body).status, 'failed');
1509
+ assert.equal(failed.headers['x-antseed-video-job-retired'], '1', 'a gateway may release the job');
1510
+ const after = await retrieve();
1511
+ assert.equal(after.statusCode, 404, after.body);
1512
+ assert.equal(JSON.parse(after.body).error.code, 'video_route_not_found');
1513
+ assert.equal(after.headers['x-antseed-video-job-retired'], '1');
1514
+ assert.equal(retrieves, 1, 'the retired job is never sent to the seller again');
1515
+ assert.equal(persisted, 2, 'the route is saved when accepted and again when retired');
1516
+ }
1517
+ });
1518
+ function failingVideoProxy(persist, answer) {
1519
+ const peer = makePeer('a', ['venice']);
1520
+ peer.providerServiceApiProtocols = { venice: { services: { 'video-model': ['venice-video'] } } };
1521
+ const proxy = makeBuyerProxyWithPeers([peer], [peer], permissiveRouter());
1522
+ proxy._persistResourceRoutes = persist;
1523
+ proxy._node.sendRequest = async (_peer, request) => ({ requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' }, body: Buffer.from(JSON.stringify({ queue_id: 'job' })) });
1524
+ const calls = { retrieves: 0 };
1525
+ proxy._node.sendRequestStream = async (_peer, request) => {
1526
+ const body = await answer(++calls.retrieves);
1527
+ return { requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' }, body: Buffer.from(JSON.stringify(body)) };
1528
+ };
1529
+ const create = () => invokeProxy(proxy, makeProxyRequest({ path: '/api/v1/video/queue', body: { model: 'video-model', prompt: 'cat', duration: '5s' } }));
1530
+ const retrieve = () => invokeProxy(proxy, makeProxyRequest({ method: 'POST', path: '/api/v1/video/retrieve', body: { model: 'video-model', queue_id: 'job' } }));
1531
+ return { create, retrieve, calls };
1532
+ }
1533
+ test('a retrieve queued behind one that retires the job as failed never reaches the seller', async () => {
1534
+ const video = failingVideoProxy(async () => { }, async (call) => {
1535
+ if (call === 1)
1536
+ await new Promise((resolve) => setTimeout(resolve, 30));
1537
+ return { status: 'FAILED' };
1538
+ });
1539
+ assert.equal((await video.create()).statusCode, 200);
1540
+ const [first, second] = await Promise.all([video.retrieve(), video.retrieve()]);
1541
+ assert.equal(JSON.parse(first.body).status, 'failed');
1542
+ assert.equal(second.statusCode, 404, second.body);
1543
+ assert.equal(video.calls.retrieves, 1);
1544
+ });
1545
+ test('a retrieve that arrives while a retirement is being saved waits, and keeps the job if the save fails', async () => {
1546
+ let rejectWrite = () => { };
1547
+ let writes = 0;
1548
+ const video = failingVideoProxy(() => {
1549
+ writes += 1;
1550
+ // The create's write succeeds; the retirement's write is held, then fails.
1551
+ return writes === 1 ? Promise.resolve() : new Promise((_resolve, reject) => { rejectWrite = reject; });
1552
+ }, async (call) => (call === 1 ? { status: 'FAILED' } : { status: 'PROCESSING' }));
1553
+ assert.equal((await video.create()).statusCode, 200);
1554
+ const first = video.retrieve();
1555
+ await new Promise((resolve) => setTimeout(resolve, 20));
1556
+ const second = video.retrieve();
1557
+ await new Promise((resolve) => setTimeout(resolve, 20));
1558
+ rejectWrite(new Error('disk full'));
1559
+ const [a, b] = await Promise.all([first, second]);
1560
+ // Neither answer says the job is gone, so a gateway keeps it held; the second poll reached the seller.
1561
+ assert.deepEqual([JSON.parse(a.body).status, JSON.parse(b.body).status], ['unknown', 'processing']);
1562
+ assert.equal(a.headers['x-antseed-video-job-retired'], undefined);
1563
+ assert.equal(b.headers['x-antseed-video-job-retired'], undefined);
1564
+ assert.equal(video.calls.retrieves, 2);
1565
+ });
1566
+ test('a job its seller no longer has is retired; a status still processing is not', async () => {
1567
+ const peer = makePeer('a', ['venice']);
1568
+ peer.providerServiceApiProtocols = { venice: { services: { 'video-model': ['venice-video'] } } };
1569
+ const proxy = makeBuyerProxyWithPeers([peer], [peer], permissiveRouter());
1570
+ proxy._persistResourceRoutes = async () => { };
1571
+ proxy._node.sendRequest = async (_peer, request) => ({ requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' }, body: Buffer.from(JSON.stringify({ queue_id: 'job' })) });
1572
+ const answers = [
1573
+ // A seller cannot release a job by naming the buyer's own code, or by setting the buyer's marker: only the buyer's retirement counts.
1574
+ { statusCode: 200, headers: { 'content-type': 'application/json', 'X-Antseed-Video-Job-Retired': '1' }, body: { status: 'PROCESSING' } },
1575
+ { statusCode: 404, headers: { 'content-type': 'application/json' }, body: { error: { code: 'video_route_not_found' } } },
1576
+ ];
1577
+ proxy._node.sendRequestStream = async (_peer, request) => {
1578
+ const answer = answers.shift();
1579
+ return { requestId: request.requestId, statusCode: answer.statusCode, headers: answer.headers, body: Buffer.from(JSON.stringify(answer.body)) };
1580
+ };
1581
+ assert.equal((await invokeProxy(proxy, makeProxyRequest({ path: '/api/v1/video/queue', body: { model: 'video-model', prompt: 'cat', duration: '5s' } }))).statusCode, 200);
1582
+ const retrieve = () => invokeProxy(proxy, makeProxyRequest({ method: 'POST', path: '/api/v1/video/retrieve', body: { model: 'video-model', queue_id: 'job' } }));
1583
+ const processing = await retrieve();
1584
+ assert.equal(processing.headers['x-antseed-video-job-retired'], undefined);
1585
+ const gone = await retrieve();
1586
+ assert.equal(gone.statusCode, 404);
1587
+ assert.equal(gone.headers['x-antseed-video-job-retired'], '1', 'the buyer retired it: the seller no longer has it');
1588
+ assert.equal((await retrieve()).statusCode, 404);
1589
+ assert.equal(answers.length, 0, 'the retired job is not sent to the seller again');
1590
+ });
1591
+ test('a failed job whose retirement cannot be saved is reported as unknown and kept', async () => {
1592
+ let failWrites = false;
1593
+ const video = failingVideoProxy(async () => { if (failWrites)
1594
+ throw new Error('disk full'); }, async () => ({ status: 'FAILED' }));
1595
+ assert.equal((await video.create()).statusCode, 200);
1596
+ failWrites = true;
1597
+ const unsaved = await video.retrieve();
1598
+ assert.equal(unsaved.statusCode, 200);
1599
+ assert.deepEqual([JSON.parse(unsaved.body).status, JSON.parse(unsaved.body).retryable], ['unknown', true]);
1600
+ assert.equal(unsaved.headers['x-antseed-video-job-retired'], undefined, 'not retired, so a gateway keeps the job held');
1601
+ // The route was put back, so the job is still polled (and retired once the write succeeds).
1602
+ failWrites = false;
1603
+ assert.equal(JSON.parse((await video.retrieve()).body).status, 'failed');
1604
+ assert.equal((await video.retrieve()).statusCode, 404);
1605
+ assert.equal(video.calls.retrieves, 2);
1606
+ });
1456
1607
  test('native resource follow-ups fail when the recorded provider loses support instead of switching providers', async () => {
1457
1608
  for (const provider of ['venice']) {
1458
1609
  const protocol = 'venice-video';
@@ -1562,6 +1713,8 @@ function makeProxyRequest(options) {
1562
1713
  return req;
1563
1714
  }
1564
1715
  function makeProxyResponse() {
1716
+ // Headers set before writeHead merge into it, as on a Node ServerResponse.
1717
+ const pending = {};
1565
1718
  return {
1566
1719
  statusCode: 0,
1567
1720
  headers: {},
@@ -1570,10 +1723,14 @@ function makeProxyResponse() {
1570
1723
  writableEnded: false,
1571
1724
  writeHead(statusCode, headers) {
1572
1725
  this.statusCode = statusCode;
1573
- this.headers = headers;
1726
+ this.headers = { ...pending, ...headers };
1574
1727
  this.headersSent = true;
1575
1728
  return this;
1576
1729
  },
1730
+ setHeader(name, value) {
1731
+ pending[name.toLowerCase()] = value;
1732
+ return this;
1733
+ },
1577
1734
  write(chunk) {
1578
1735
  this.body += Buffer.from(chunk).toString('utf8');
1579
1736
  return true;