@antseed/cli 0.1.168 → 0.1.170

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 (165) hide show
  1. package/README.md +4 -0
  2. package/dist/buyer-identities/loader.d.ts +28 -0
  3. package/dist/buyer-identities/loader.d.ts.map +1 -0
  4. package/dist/buyer-identities/loader.js +58 -0
  5. package/dist/buyer-identities/loader.js.map +1 -0
  6. package/dist/buyer-identities/store.d.ts +41 -0
  7. package/dist/buyer-identities/store.d.ts.map +1 -0
  8. package/dist/buyer-identities/store.js +171 -0
  9. package/dist/buyer-identities/store.js.map +1 -0
  10. package/dist/buyer-identities/store.test.d.ts +2 -0
  11. package/dist/buyer-identities/store.test.d.ts.map +1 -0
  12. package/dist/buyer-identities/store.test.js +122 -0
  13. package/dist/buyer-identities/store.test.js.map +1 -0
  14. package/dist/cli/commands/buyer/channel-withdraw.d.ts.map +1 -1
  15. package/dist/cli/commands/buyer/channel-withdraw.js +9 -4
  16. package/dist/cli/commands/buyer/channel-withdraw.js.map +1 -1
  17. package/dist/cli/commands/buyer/channel-withdraw.test.js +69 -0
  18. package/dist/cli/commands/buyer/channel-withdraw.test.js.map +1 -1
  19. package/dist/cli/commands/buyer/channels.js +1 -1
  20. package/dist/cli/commands/buyer/channels.js.map +1 -1
  21. package/dist/cli/commands/buyer/identity.d.ts +3 -0
  22. package/dist/cli/commands/buyer/identity.d.ts.map +1 -0
  23. package/dist/cli/commands/buyer/identity.js +115 -0
  24. package/dist/cli/commands/buyer/identity.js.map +1 -0
  25. package/dist/cli/commands/buyer/index.d.ts.map +1 -1
  26. package/dist/cli/commands/buyer/index.js +2 -0
  27. package/dist/cli/commands/buyer/index.js.map +1 -1
  28. package/dist/cli/commands/buyer/start.d.ts.map +1 -1
  29. package/dist/cli/commands/buyer/start.js +30 -2
  30. package/dist/cli/commands/buyer/start.js.map +1 -1
  31. package/dist/cli/commands/gateway/index.d.ts +3 -0
  32. package/dist/cli/commands/gateway/index.d.ts.map +1 -0
  33. package/dist/cli/commands/gateway/index.js +43 -0
  34. package/dist/cli/commands/gateway/index.js.map +1 -0
  35. package/dist/cli/commands/gateway/key.d.ts +3 -0
  36. package/dist/cli/commands/gateway/key.d.ts.map +1 -0
  37. package/dist/cli/commands/gateway/key.js +240 -0
  38. package/dist/cli/commands/gateway/key.js.map +1 -0
  39. package/dist/cli/commands/gateway/shared.d.ts +29 -0
  40. package/dist/cli/commands/gateway/shared.d.ts.map +1 -0
  41. package/dist/cli/commands/gateway/shared.js +90 -0
  42. package/dist/cli/commands/gateway/shared.js.map +1 -0
  43. package/dist/cli/commands/seller/setup.d.ts.map +1 -1
  44. package/dist/cli/commands/seller/setup.js +4 -0
  45. package/dist/cli/commands/seller/setup.js.map +1 -1
  46. package/dist/cli/commands/seller/start.d.ts.map +1 -1
  47. package/dist/cli/commands/seller/start.js +8 -2
  48. package/dist/cli/commands/seller/start.js.map +1 -1
  49. package/dist/cli/commands/seller/start.test.js +20 -0
  50. package/dist/cli/commands/seller/start.test.js.map +1 -1
  51. package/dist/cli/commands/tunnel/start.d.ts.map +1 -1
  52. package/dist/cli/commands/tunnel/start.js +25 -14
  53. package/dist/cli/commands/tunnel/start.js.map +1 -1
  54. package/dist/cli/index.js +2 -0
  55. package/dist/cli/index.js.map +1 -1
  56. package/dist/gateway/accounting.d.ts +45 -0
  57. package/dist/gateway/accounting.d.ts.map +1 -0
  58. package/dist/gateway/accounting.js +94 -0
  59. package/dist/gateway/accounting.js.map +1 -0
  60. package/dist/gateway/cdp-auth.d.ts +12 -0
  61. package/dist/gateway/cdp-auth.d.ts.map +1 -0
  62. package/dist/gateway/cdp-auth.js +53 -0
  63. package/dist/gateway/cdp-auth.js.map +1 -0
  64. package/dist/gateway/cdp-auth.test.d.ts +2 -0
  65. package/dist/gateway/cdp-auth.test.d.ts.map +1 -0
  66. package/dist/gateway/cdp-auth.test.js +96 -0
  67. package/dist/gateway/cdp-auth.test.js.map +1 -0
  68. package/dist/gateway/gateway.test.d.ts.map +1 -0
  69. package/dist/gateway/gateway.test.js +709 -0
  70. package/dist/gateway/gateway.test.js.map +1 -0
  71. package/dist/gateway/keys.d.ts +13 -0
  72. package/dist/gateway/keys.d.ts.map +1 -0
  73. package/dist/gateway/keys.js +24 -0
  74. package/dist/gateway/keys.js.map +1 -0
  75. package/dist/gateway/limits.d.ts +25 -0
  76. package/dist/gateway/limits.d.ts.map +1 -0
  77. package/dist/gateway/limits.js +45 -0
  78. package/dist/gateway/limits.js.map +1 -0
  79. package/dist/gateway/money.d.ts +9 -0
  80. package/dist/gateway/money.d.ts.map +1 -0
  81. package/dist/gateway/money.js +37 -0
  82. package/dist/gateway/money.js.map +1 -0
  83. package/dist/gateway/runtime.d.ts +38 -0
  84. package/dist/gateway/runtime.d.ts.map +1 -0
  85. package/dist/gateway/runtime.js +144 -0
  86. package/dist/gateway/runtime.js.map +1 -0
  87. package/dist/gateway/server.d.ts +49 -0
  88. package/dist/gateway/server.d.ts.map +1 -0
  89. package/dist/gateway/server.js +460 -0
  90. package/dist/gateway/server.js.map +1 -0
  91. package/dist/gateway/spend-feed.d.ts +32 -0
  92. package/dist/gateway/spend-feed.d.ts.map +1 -0
  93. package/dist/gateway/spend-feed.js +86 -0
  94. package/dist/gateway/spend-feed.js.map +1 -0
  95. package/dist/gateway/store.d.ts +114 -0
  96. package/dist/gateway/store.d.ts.map +1 -0
  97. package/dist/gateway/store.js +253 -0
  98. package/dist/gateway/store.js.map +1 -0
  99. package/dist/gateway/x402.d.ts +95 -0
  100. package/dist/gateway/x402.d.ts.map +1 -0
  101. package/dist/gateway/x402.js +120 -0
  102. package/dist/gateway/x402.js.map +1 -0
  103. package/dist/plugins/registry.d.ts.map +1 -1
  104. package/dist/plugins/registry.js +2 -0
  105. package/dist/plugins/registry.js.map +1 -1
  106. package/dist/proxy/buyer-proxy.d.ts +15 -0
  107. package/dist/proxy/buyer-proxy.d.ts.map +1 -1
  108. package/dist/proxy/buyer-proxy.js +157 -15
  109. package/dist/proxy/buyer-proxy.js.map +1 -1
  110. package/dist/proxy/buyer-proxy.test.js +157 -1
  111. package/dist/proxy/buyer-proxy.test.js.map +1 -1
  112. package/dist/proxy/native-video-proxy.d.ts +34 -0
  113. package/dist/proxy/native-video-proxy.d.ts.map +1 -0
  114. package/dist/proxy/native-video-proxy.js +41 -0
  115. package/dist/proxy/native-video-proxy.js.map +1 -0
  116. package/dist/proxy/native-video-proxy.test.d.ts +2 -0
  117. package/dist/proxy/native-video-proxy.test.d.ts.map +1 -0
  118. package/dist/proxy/native-video-proxy.test.js +23 -0
  119. package/dist/proxy/native-video-proxy.test.js.map +1 -0
  120. package/dist/proxy/network-models.d.ts +3 -2
  121. package/dist/proxy/network-models.d.ts.map +1 -1
  122. package/dist/proxy/network-models.js +3 -0
  123. package/dist/proxy/network-models.js.map +1 -1
  124. package/dist/proxy/network-models.test.js +13 -0
  125. package/dist/proxy/network-models.test.js.map +1 -1
  126. package/dist/proxy/request-utils.d.ts +3 -0
  127. package/dist/proxy/request-utils.d.ts.map +1 -1
  128. package/dist/proxy/request-utils.js +6 -1
  129. package/dist/proxy/request-utils.js.map +1 -1
  130. package/dist/proxy/resource-routes.d.ts +20 -0
  131. package/dist/proxy/resource-routes.d.ts.map +1 -0
  132. package/dist/proxy/resource-routes.js +48 -0
  133. package/dist/proxy/resource-routes.js.map +1 -0
  134. package/dist/proxy/resource-routes.test.d.ts +2 -0
  135. package/dist/proxy/resource-routes.test.d.ts.map +1 -0
  136. package/dist/proxy/resource-routes.test.js +21 -0
  137. package/dist/proxy/resource-routes.test.js.map +1 -0
  138. package/dist/proxy/routing.d.ts.map +1 -1
  139. package/dist/proxy/routing.js +5 -2
  140. package/dist/proxy/routing.js.map +1 -1
  141. package/dist/proxy/service-api-adapter.d.ts +1 -1
  142. package/dist/proxy/service-api-adapter.d.ts.map +1 -1
  143. package/dist/proxy/service-api-adapter.js +1 -1
  144. package/dist/proxy/service-api-adapter.js.map +1 -1
  145. package/dist/proxy/spend-attribution.d.ts +63 -0
  146. package/dist/proxy/spend-attribution.d.ts.map +1 -0
  147. package/dist/proxy/spend-attribution.js +97 -0
  148. package/dist/proxy/spend-attribution.js.map +1 -0
  149. package/dist/proxy/video-download.d.ts +7 -0
  150. package/dist/proxy/video-download.d.ts.map +1 -0
  151. package/dist/proxy/video-download.js +66 -0
  152. package/dist/proxy/video-download.js.map +1 -0
  153. package/dist/proxy/video-download.test.d.ts +2 -0
  154. package/dist/proxy/video-download.test.d.ts.map +1 -0
  155. package/dist/proxy/video-download.test.js +108 -0
  156. package/dist/proxy/video-download.test.js.map +1 -0
  157. package/package.json +9 -7
  158. package/dist/tunnel/gateway.d.ts +0 -16
  159. package/dist/tunnel/gateway.d.ts.map +0 -1
  160. package/dist/tunnel/gateway.js +0 -146
  161. package/dist/tunnel/gateway.js.map +0 -1
  162. package/dist/tunnel/gateway.test.d.ts.map +0 -1
  163. package/dist/tunnel/gateway.test.js +0 -89
  164. package/dist/tunnel/gateway.test.js.map +0 -1
  165. /package/dist/{tunnel → gateway}/gateway.test.d.ts +0 -0
@@ -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,12 +114,14 @@ 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;
117
121
  private _configWatchDebounce;
118
122
  private _routingPreferences;
119
123
  private _stateWriteChain;
124
+ private readonly _resourceRoutes;
120
125
  private _cachedPeers;
121
126
  private _cacheLastUpdatedAtMs;
122
127
  private _cacheMutationEpoch;
@@ -157,9 +162,18 @@ export declare class BuyerProxy {
157
162
  * deltas (buyer- and seller-initiated auth both advance the cumulative).
158
163
  */
159
164
  private readonly _requestConversations;
165
+ /** Signed spend for requests a local gateway tagged, polled over the control plane. */
166
+ private readonly _spendAttribution;
160
167
  constructor(config: BuyerProxyConfig);
161
168
  /** Roll a signed spend delta into the conversation that triggered it. */
162
169
  private _attributeSpend;
170
+ /**
171
+ * Identity named by a request: null for the default, the name when it is
172
+ * (or can be) loaded, undefined when no stored identity has that name.
173
+ */
174
+ private _resolveBuyerIdentity;
175
+ /** `?identity=` on control-plane reads; undefined when it names no stored identity. */
176
+ private _controlPlaneIdentity;
163
177
  /**
164
178
  * Remember which chat a request belongs to for the life of the request plus
165
179
  * a grace window — the seller-initiated auth path can land just after the
@@ -184,6 +198,7 @@ export declare class BuyerProxy {
184
198
  private _markModelActivity;
185
199
  /** Serialised read-modify-write to buyer.state.json. Returns the queued write promise. */
186
200
  private _mergeStateFile;
201
+ private _persistResourceRoutes;
187
202
  private _writeStateFile;
188
203
  private _startIncrementalDiscoverySweep;
189
204
  private _startBackgroundRefresh;
@@ -1 +1 @@
1
- {"version":3,"file":"buyer-proxy.d.ts","sourceRoot":"","sources":["../../src/proxy/buyer-proxy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"buyer-proxy.d.ts","sourceRoot":"","sources":["../../src/proxy/buyer-proxy.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,23 @@
1
+ import { nativeVideoRoute } from '@antseed/api-adapter';
2
+ import { ResourceRoutes } from './resource-routes.js';
3
+ import { prepareVideoRequest, recordVideoAcceptance } from './native-video-proxy.js';
4
+ import { downloadVideo } from './video-download.js';
1
5
  import { createServer } from 'node:http';
2
6
  import { randomUUID } from 'node:crypto';
3
7
  import { watchFile, unwatchFile } from 'node:fs';
4
8
  import { readFile, writeFile, rename, mkdir, readdir, stat, unlink } from 'node:fs/promises';
5
9
  import { join } from 'node:path';
6
- import { ANTSEED_BUYER_FAULT_ERROR_CODE, ANTSEED_FAULT_ATTRIBUTION_HEADER, ANTSEED_ATTEST_PATH, adaptPeerFaultErrorResponse, computeTrustScore, decodeSweepRequest, faultAttributionOf, faultCodeOf, isModelRouteEligible, modelRouteTotalPrice, normalizedModelReputationScore, peerSupportsCooperativeClose, rankModelRoutes, sanitizePeerDisplayName, } from '@antseed/node';
10
+ import { ANTSEED_BUYER_FAULT_ERROR_CODE, ANTSEED_FAULT_ATTRIBUTION_HEADER, ANTSEED_ATTEST_PATH, adaptPeerFaultErrorResponse, computeTrustScore, decodeSweepRequest, faultAttributionOf, faultCodeOf, isModelRouteEligible, modelRouteTotalPrice, normalizedModelReputationScore, peerSupportsCooperativeClose, DEFAULT_BUYER_IDENTITY, rankModelRoutes, sanitizePeerDisplayName, } from '@antseed/node';
7
11
  import { canonicalModelKey } from '@antseed/node/model-identity';
8
12
  import { createStreamingAdapter, detectRequestServiceApiProtocol, transformRequest, transformResponse, } from './service-api-adapter.js';
9
- import { DEBUG, log, extractRequestedService, summarizeRequestShape, summarizeErrorResponse, requestWantsStreaming, parsePeerPinnedService, rewritePeerPinnedServiceInBody, substituteRoutedModelAlias, overrideRoutedModelInBody, ROUTED_MODEL_ALIAS, SYSTEM_PROXY_SOURCE_HEADER, SYSTEM_ROUTED_MODEL_HEADER, normalizePeerId, } from './request-utils.js';
13
+ import { DEBUG, log, extractRequestedService, summarizeRequestShape, summarizeErrorResponse, requestWantsStreaming, parsePeerPinnedService, rewritePeerPinnedServiceInBody, substituteRoutedModelAlias, overrideRoutedModelInBody, ROUTED_MODEL_ALIAS, SYSTEM_PROXY_SOURCE_HEADER, SYSTEM_ROUTED_MODEL_HEADER, BUYER_IDENTITY_HEADER, normalizePeerId, } from './request-utils.js';
14
+ import { BuyerIdentityLoader } from '../buyer-identities/loader.js';
10
15
  import { buildNetworkModels, effectiveModelReputationScore, parseModelTypeFilter, } from './network-models.js';
11
16
  import { findMissingRequiredParameters, findUnannouncedRequestParameters, findAdvertisedServiceOffer, findAdvertisedServiceProtocols, getExplicitProviderOverride, getExplicitPeerIdOverride, parseRequiredParametersHeader, REQUIRED_PARAMETERS_HEADER, resolvePeerRoutePlan, selectCandidatePeersForRouting, } from './routing.js';
12
17
  import { computeResponseTelemetry, attachAntseedTelemetryHeaders, attachStreamingAntseedHeaders, } from './telemetry.js';
13
18
  import { DEFAULT_BUYER_PEER_REFRESH_INTERVAL_MS } from '../config/defaults.js';
14
19
  import { extractConversationIdentity, extractFirstUserSnippet, isCompletionRequestPath, isTitleGenerationRequest, parseRequestBodyObject, } from './conversation-identity.js';
20
+ import { SPEND_ATTRIBUTION_HEADER, SpendAttributionFeed, parseSpendAttributionTag } from './spend-attribution.js';
15
21
  import { ConversationStore } from './conversation-store.js';
16
22
  import { recordPeerFailureEntry, clearPeerHealthEntry, isCoolingDown, parsePersistedPeerHealth, prunePeerHealth, serializePeerHealth, reasonEscalates, } from './peer-health.js';
17
23
  import { PeerAttributionTracker, HEARTBEAT_MS } from './peer-attribution.js';
@@ -277,6 +283,14 @@ export function sanitizePeerBuyerFaultMarker(response) {
277
283
  ? { ...response, body: Buffer.from(JSON.stringify(parsed)) }
278
284
  : response;
279
285
  }
286
+ function buyerFaultStatusCode(faultCode) {
287
+ switch (faultCode) {
288
+ case 'invalid-request': return 400;
289
+ case 'buyer-budget-too-low': return 422;
290
+ case 'buyer-reserve-topup-timeout': return 504;
291
+ default: return 503;
292
+ }
293
+ }
280
294
  /**
281
295
  * Inject the buyer-known peerId into a 402 payment_required JSON body.
282
296
  * The seller doesn't include its own peerId (and shouldn't — self-reported
@@ -595,6 +609,10 @@ export async function mergeJsonStateFile(stateDir, stateFile, patch) {
595
609
  throw err;
596
610
  }
597
611
  }
612
+ function sendUnknownBuyerIdentity(res) {
613
+ res.writeHead(404, { 'content-type': 'application/json' });
614
+ res.end(JSON.stringify({ ok: false, error: 'Unknown buyer identity' }));
615
+ }
598
616
  export class BuyerProxy {
599
617
  _server;
600
618
  _node;
@@ -623,12 +641,14 @@ export class BuyerProxy {
623
641
  */
624
642
  _lastModelActivityAt = 0;
625
643
  _verifier;
644
+ _buyerIdentities;
626
645
  _teeVerification;
627
646
  _teeControl;
628
647
  _stateWatchDebounce = null;
629
648
  _configWatchDebounce = null;
630
649
  _routingPreferences;
631
650
  _stateWriteChain = Promise.resolve();
651
+ _resourceRoutes = new ResourceRoutes();
632
652
  _cachedPeers = [];
633
653
  _cacheLastUpdatedAtMs = 0;
634
654
  _cacheMutationEpoch = 0;
@@ -669,9 +689,12 @@ export class BuyerProxy {
669
689
  * deltas (buyer- and seller-initiated auth both advance the cumulative).
670
690
  */
671
691
  _requestConversations = new Map();
692
+ /** Signed spend for requests a local gateway tagged, polled over the control plane. */
693
+ _spendAttribution = new SpendAttributionFeed();
672
694
  constructor(config) {
673
695
  this._node = config.node;
674
696
  this._verifier = config.verifier;
697
+ this._buyerIdentities = config.buyerIdentities ?? new BuyerIdentityLoader(config.node, config.dataDir);
675
698
  this._teeVerification = new TeeVerification(config.verifier);
676
699
  this._teeControl = new TeeControl(this._teeVerification.sessionId);
677
700
  this._port = config.port;
@@ -714,6 +737,7 @@ export class BuyerProxy {
714
737
  if (typeof spendEventNode.on === 'function') {
715
738
  spendEventNode.on('payment:spend', (event) => {
716
739
  this._attributeSpend(event);
740
+ this._spendAttribution.record(event);
717
741
  });
718
742
  }
719
743
  const eventNode = this._node;
@@ -741,6 +765,25 @@ export class BuyerProxy {
741
765
  }, !entry.counted);
742
766
  entry.counted = true;
743
767
  }
768
+ /**
769
+ * Identity named by a request: null for the default, the name when it is
770
+ * (or can be) loaded, undefined when no stored identity has that name.
771
+ */
772
+ async _resolveBuyerIdentity(header) {
773
+ const name = header?.trim().toLowerCase() ?? '';
774
+ if (!name || name === DEFAULT_BUYER_IDENTITY)
775
+ return null;
776
+ const loaded = await this._buyerIdentities.ensure(name).catch((err) => {
777
+ log(`Buyer identity "${name}" failed to load: ${err instanceof Error ? err.message : String(err)}`);
778
+ return false;
779
+ });
780
+ return loaded ? name : undefined;
781
+ }
782
+ /** `?identity=` on control-plane reads; undefined when it names no stored identity. */
783
+ async _controlPlaneIdentity(path) {
784
+ const param = new URL(path, 'http://localhost').searchParams.get('identity');
785
+ return this._resolveBuyerIdentity(param ?? undefined);
786
+ }
744
787
  /**
745
788
  * Remember which chat a request belongs to for the life of the request plus
746
789
  * a grace window — the seller-initiated auth path can land just after the
@@ -778,6 +821,11 @@ export class BuyerProxy {
778
821
  // startup route from the warm cache without blocking on DHT discovery.
779
822
  // The background refresh still runs to pick up fresh peers and IP changes.
780
823
  await this._hydratePeersFromStateFile();
824
+ try {
825
+ const state = JSON.parse(await readFile(this._stateFile, 'utf8'));
826
+ this._resourceRoutes.hydrate(state.resourceRoutes);
827
+ }
828
+ catch { }
781
829
  // Adopt persisted session overrides (peer pin, default routed model) so
782
830
  // they survive daemon restart. A --peer CLI flag beats the persisted pin
783
831
  // at startup; runtime `connection set` writes still take over via the
@@ -868,7 +916,10 @@ export class BuyerProxy {
868
916
  await this._writeStateFile('stopped');
869
917
  await this._conversations.flush();
870
918
  return new Promise((resolve) => {
871
- this._server.close(() => resolve());
919
+ this._server.close(() => {
920
+ this._spendAttribution.close();
921
+ resolve();
922
+ });
872
923
  });
873
924
  }
874
925
  _watchStateFile() {
@@ -969,6 +1020,11 @@ export class BuyerProxy {
969
1020
  }).catch(() => { });
970
1021
  return this._stateWriteChain;
971
1022
  }
1023
+ async _persistResourceRoutes() {
1024
+ const write = this._stateWriteChain.then(() => mergeJsonStateFile(this._stateDir, this._stateFile, { resourceRoutes: this._resourceRoutes.snapshot() }));
1025
+ this._stateWriteChain = write.catch(() => { });
1026
+ return write;
1027
+ }
972
1028
  async _writeStateFile(state) {
973
1029
  // When stopping, preserve whatever session overrides are already
974
1030
  // in the file — the debounce may have been cancelled before
@@ -1682,11 +1738,20 @@ export class BuyerProxy {
1682
1738
  }
1683
1739
  return;
1684
1740
  }
1741
+ if (path.startsWith('/_antseed/buyer-identities') && method === 'GET') {
1742
+ await this._buyerIdentities.loadAll();
1743
+ res.writeHead(200, { 'content-type': 'application/json' });
1744
+ res.end(JSON.stringify({ ok: true, identities: this._node.buyerIdentities() }));
1745
+ return;
1746
+ }
1685
1747
  if (path.startsWith('/_antseed/channels') && method === 'GET') {
1686
1748
  const all = /[?&]all=1/.test(path);
1749
+ const identity = await this._controlPlaneIdentity(path);
1750
+ if (identity === undefined)
1751
+ return sendUnknownBuyerIdentity(res);
1687
1752
  const channels = all
1688
- ? this._node.getAllBuyerChannels()
1689
- : this._node.getActiveBuyerChannels();
1753
+ ? this._node.getAllBuyerChannels(identity ?? undefined)
1754
+ : this._node.getActiveBuyerChannels(identity ?? undefined);
1690
1755
  const peers = await this._getPeers();
1691
1756
  const peersById = new Map(peers.map((peer) => [peer.peerId, peer]));
1692
1757
  const channelsWithCapabilities = channels.map((channel) => {
@@ -1701,16 +1766,30 @@ export class BuyerProxy {
1701
1766
  res.end(JSON.stringify({ ok: true, channels: channelsWithCapabilities }));
1702
1767
  return;
1703
1768
  }
1769
+ // Unauthenticated like every /_antseed route: it relies on the buyer
1770
+ // listening on 127.0.0.1 only, since it exposes per-seller spend and tags.
1771
+ if (path.startsWith('/_antseed/attributed-spend') && method === 'GET') {
1772
+ const after = Number(new URL(path, 'http://localhost').searchParams.get('after') ?? '0');
1773
+ res.writeHead(200, { 'content-type': 'application/json' });
1774
+ res.end(JSON.stringify({ ok: true, ...this._spendAttribution.page(Number.isFinite(after) ? after : 0) }));
1775
+ return;
1776
+ }
1704
1777
  if (path.startsWith('/_antseed/buyer-usage') && method === 'GET') {
1705
- const totals = this._node.getBuyerUsageTotals();
1778
+ const identity = await this._controlPlaneIdentity(path);
1779
+ if (identity === undefined)
1780
+ return sendUnknownBuyerIdentity(res);
1781
+ const totals = this._node.getBuyerUsageTotals(identity ?? undefined);
1706
1782
  res.writeHead(200, { 'content-type': 'application/json' });
1707
1783
  res.end(JSON.stringify({ ok: true, totals, lastActivityAt: this._lastModelActivityAt || null }));
1708
1784
  return;
1709
1785
  }
1710
- const meteringMatch = path.match(/^\/_antseed\/metering\/(.+)$/);
1786
+ const meteringMatch = path.match(/^\/_antseed\/metering\/([^?]+)/);
1711
1787
  if (meteringMatch && method === 'GET') {
1712
1788
  const sellerPeerId = decodeURIComponent(meteringMatch[1]);
1713
- const stats = this._node.getMeteringStatsByPeer(sellerPeerId);
1789
+ const identity = await this._controlPlaneIdentity(path);
1790
+ if (identity === undefined)
1791
+ return sendUnknownBuyerIdentity(res);
1792
+ const stats = this._node.getMeteringStatsByPeer(sellerPeerId, identity ?? undefined);
1714
1793
  res.writeHead(200, { 'content-type': 'application/json' });
1715
1794
  res.end(JSON.stringify(stats));
1716
1795
  return;
@@ -1764,11 +1843,14 @@ export class BuyerProxy {
1764
1843
  }
1765
1844
  let peerId;
1766
1845
  let includeAuth = true;
1846
+ let identityParam;
1767
1847
  try {
1768
1848
  const body = JSON.parse(Buffer.concat(chunks).toString());
1769
1849
  peerId = String(body.peerId ?? '');
1770
1850
  if (body.includeAuth === false)
1771
1851
  includeAuth = false;
1852
+ if (typeof body.identity === 'string')
1853
+ identityParam = body.identity;
1772
1854
  }
1773
1855
  catch {
1774
1856
  res.writeHead(400, { 'content-type': 'application/json' });
@@ -1781,7 +1863,10 @@ export class BuyerProxy {
1781
1863
  return;
1782
1864
  }
1783
1865
  try {
1784
- const result = await this._node.requestChannelClose(peerId, { includeAuth });
1866
+ const identity = await this._resolveBuyerIdentity(identityParam);
1867
+ if (identity === undefined)
1868
+ throw new Error('Unknown buyer identity');
1869
+ const result = await this._node.requestChannelClose(peerId, { includeAuth, ...(identity ? { buyerIdentity: identity } : {}) });
1785
1870
  log(`Cooperative close of ${result.channelId.slice(0, 18)}... with ${peerId.slice(0, 12)}...: ` +
1786
1871
  `${result.status}${result.code ? ` (${result.code})` : ''}`);
1787
1872
  res.writeHead(200, { 'content-type': 'application/json' });
@@ -1911,7 +1996,7 @@ export class BuyerProxy {
1911
1996
  res.writeHead(400, responseHeaders);
1912
1997
  res.end(JSON.stringify({
1913
1998
  error: {
1914
- message: `Unknown model type "${url.searchParams.get('type') ?? ''}" — expected "text", "images", or "decisions".`,
1999
+ message: `Unknown model type "${url.searchParams.get('type') ?? ''}" — expected "text", "images", "decisions", or "videos".`,
1915
2000
  type: 'invalid_request_error',
1916
2001
  param: 'type',
1917
2002
  },
@@ -1933,7 +2018,9 @@ export class BuyerProxy {
1933
2018
  }
1934
2019
  // Only proxy known API paths — reject everything else with 404
1935
2020
  const normalizedPath = path.split('?')[0]?.trim().toLowerCase() ?? '/';
1936
- const isKnownApiPath = normalizedPath.startsWith('/v1/messages') ||
2021
+ const nativeVideo = nativeVideoRoute({ method, path });
2022
+ const isKnownApiPath = nativeVideo !== null ||
2023
+ normalizedPath.startsWith('/v1/messages') ||
1937
2024
  normalizedPath.startsWith('/v1/chat/completions') ||
1938
2025
  normalizedPath.startsWith('/v1/responses') ||
1939
2026
  normalizedPath.startsWith('/v1/images/generations') ||
@@ -1986,6 +2073,29 @@ export class BuyerProxy {
1986
2073
  // it never reaches a seller.
1987
2074
  const systemRoutedModel = headers[SYSTEM_ROUTED_MODEL_HEADER] === '1';
1988
2075
  delete headers[SYSTEM_ROUTED_MODEL_HEADER];
2076
+ // Internal marker from the API-key gateway: report this request's signed
2077
+ // spend under the gateway's tag. Stripped so it never reaches a seller.
2078
+ const spendAttributionTag = parseSpendAttributionTag(headers[SPEND_ATTRIBUTION_HEADER]);
2079
+ delete headers[SPEND_ATTRIBUTION_HEADER];
2080
+ // The paying identity rides on the internal headers through routing, like
2081
+ // a peer pin, and is stripped when the request is built for the seller.
2082
+ const buyerIdentity = await this._resolveBuyerIdentity(headers[BUYER_IDENTITY_HEADER]);
2083
+ if (buyerIdentity === undefined) {
2084
+ res.writeHead(400, { 'content-type': 'application/json' });
2085
+ res.end(JSON.stringify({
2086
+ error: {
2087
+ type: 'invalid_request_error',
2088
+ code: 'unknown_buyer_identity',
2089
+ message: `${BUYER_IDENTITY_HEADER} names no stored buyer identity. Create one with \`antseed buyer identity create <name>\`.`,
2090
+ param: BUYER_IDENTITY_HEADER,
2091
+ },
2092
+ }));
2093
+ return;
2094
+ }
2095
+ if (buyerIdentity)
2096
+ headers[BUYER_IDENTITY_HEADER] = buyerIdentity;
2097
+ else
2098
+ delete headers[BUYER_IDENTITY_HEADER];
1989
2099
  let serializedReq = {
1990
2100
  requestId: randomUUID(),
1991
2101
  method,
@@ -1993,6 +2103,8 @@ export class BuyerProxy {
1993
2103
  headers,
1994
2104
  body: new Uint8Array(body),
1995
2105
  };
2106
+ if (spendAttributionTag)
2107
+ this._spendAttribution.track(serializedReq.requestId, spendAttributionTag);
1996
2108
  const requiredParameters = parseRequiredParametersHeader(serializedReq.headers[REQUIRED_PARAMETERS_HEADER]);
1997
2109
  if (requiredParameters === null) {
1998
2110
  res.writeHead(400, { 'content-type': 'application/json' });
@@ -2006,6 +2118,16 @@ export class BuyerProxy {
2006
2118
  }));
2007
2119
  return;
2008
2120
  }
2121
+ if (nativeVideo) {
2122
+ // Re-classify with the body: some APIs (Venice) carry the job ID in it.
2123
+ const prepared = prepareVideoRequest(nativeVideoRoute(serializedReq) ?? nativeVideo, serializedReq.headers, this._resourceRoutes);
2124
+ if ('error' in prepared) {
2125
+ res.writeHead(prepared.error.statusCode, { 'content-type': 'application/json' });
2126
+ res.end(JSON.stringify(prepared.error.body));
2127
+ return;
2128
+ }
2129
+ serializedReq = { ...serializedReq, headers: prepared.headers };
2130
+ }
2009
2131
  // Snapshot the session overrides before any await so a concurrent
2010
2132
  // _reloadSessionOverrides() cannot change routing mid-request.
2011
2133
  const effectivePinnedPeer = this._pinnedPeer;
@@ -2325,6 +2447,13 @@ export class BuyerProxy {
2325
2447
  }
2326
2448
  return;
2327
2449
  }
2450
+ // Never retry a video on another seller: a create may already have
2451
+ // started (and been charged), and follow-ups only exist on one seller.
2452
+ if (nativeVideo) {
2453
+ res.writeHead(result.statusCode, result.responseHeaders);
2454
+ res.end(result.responseBody);
2455
+ return;
2456
+ }
2328
2457
  lastRetry = result;
2329
2458
  if (result.responseHeaders[ANTSEED_FAULT_ATTRIBUTION_HEADER]?.toLowerCase() === 'buyer') {
2330
2459
  res.writeHead(result.statusCode, result.responseHeaders);
@@ -2593,7 +2722,7 @@ export class BuyerProxy {
2593
2722
  + `did not announce for this service: ${unannounced.join(', ')} — the upstream may ignore or reject them`);
2594
2723
  }
2595
2724
  }
2596
- const { 'x-antseed-pin-peer': _pinPeer, 'x-antseed-prefer-peer': _preferPeer, [REQUIRED_PARAMETERS_HEADER]: _requiredParameters, 'x-vpr-session-id': _vprSession,
2725
+ const { 'x-antseed-pin-peer': _pinPeer, 'x-antseed-prefer-peer': _preferPeer, [BUYER_IDENTITY_HEADER]: buyerIdentity, [REQUIRED_PARAMETERS_HEADER]: _requiredParameters, 'x-vpr-session-id': _vprSession,
2597
2726
  // Legacy desktop builds (pre AntStation → VPR rename) still send this.
2598
2727
  'x-antstation-session-id': _antstationSession, ...headersForPeer } = serializedReq.headers;
2599
2728
  let requestForPeer = {
@@ -2661,7 +2790,13 @@ export class BuyerProxy {
2661
2790
  log(`Routing to peer ${selectedPeer.peerId.slice(0, 12)}...`);
2662
2791
  this._markModelActivity();
2663
2792
  // Forward through P2P
2664
- const wantsStreaming = clientWantsStreaming;
2793
+ const videoRoute = nativeVideoRoute(requestForPeer);
2794
+ // Video responses are JSON job objects or a streamed MP4, never SSE.
2795
+ const wantsStreaming = clientWantsStreaming && !videoRoute;
2796
+ if (videoRoute?.action === 'retrieve') {
2797
+ await downloadVideo(requestForPeer, res, (request, callbacks, signal) => this._node.sendRequestStream(selectedPeer, request, callbacks, { signal, pinned: true }), requestSignal);
2798
+ return { done: true };
2799
+ }
2665
2800
  const peerResponseProtocol = selectedRoutePlan.selection?.targetProtocol ?? requestProtocol;
2666
2801
  const adaptPeerResponse = (response) => adaptPeerFaultErrorResponse(response, peerResponseProtocol, { pinned });
2667
2802
  const startTime = Date.now();
@@ -2701,7 +2836,7 @@ export class BuyerProxy {
2701
2836
  }
2702
2837
  }
2703
2838
  },
2704
- }, { signal: requestSignal, pinned });
2839
+ }, { signal: requestSignal, pinned, ...(buyerIdentity ? { buyerIdentity } : {}) });
2705
2840
  let responseForClient = adaptBuyerFaultErrorResponse(response, requestProtocol);
2706
2841
  responseForClient = adaptPeerResponse(responseForClient);
2707
2842
  if (!streamed
@@ -2768,10 +2903,17 @@ export class BuyerProxy {
2768
2903
  const upstreamResponse = await this._node.sendRequest(selectedPeer, requestForPeer, {
2769
2904
  signal: requestSignal,
2770
2905
  pinned,
2906
+ ...(buyerIdentity ? { buyerIdentity } : {}),
2771
2907
  });
2772
2908
  if (upstreamResponse.statusCode >= 400 && !adaptResponse) {
2773
2909
  log(`Upstream raw error detail: ${summarizeErrorResponse(upstreamResponse)}`);
2774
2910
  }
2911
+ // Only the seller that accepted a video job knows its ID. Remember and
2912
+ // persist job ID -> seller so later retrieve requests (even
2913
+ // after a proxy restart) are pinned back to that seller.
2914
+ if (videoRoute && recordVideoAcceptance(videoRoute, upstreamResponse, { peerId: selectedPeer.peerId, provider: selectedRoutePlan.provider, service: requestedService }, this._resourceRoutes)) {
2915
+ await this._persistResourceRoutes().catch(error => console.error('[proxy] Accepted video route was not persisted:', error));
2916
+ }
2775
2917
  let response = adaptBuyerFaultErrorResponse(upstreamResponse, requestProtocol);
2776
2918
  response = adaptPeerResponse(response);
2777
2919
  if (adaptResponse
@@ -2879,7 +3021,7 @@ export class BuyerProxy {
2879
3021
  if (fault === 'buyer') {
2880
3022
  const buyerResponse = adaptBuyerFaultErrorResponse({
2881
3023
  requestId: requestForPeer.requestId,
2882
- statusCode: 503,
3024
+ statusCode: buyerFaultStatusCode(faultCode),
2883
3025
  headers: {
2884
3026
  'content-type': 'application/json',
2885
3027
  [ANTSEED_FAULT_ATTRIBUTION_HEADER]: 'buyer',