@antseed/dashboard 0.1.1

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 (45) hide show
  1. package/README.md +45 -0
  2. package/dist/api/routes.d.ts +84 -0
  3. package/dist/api/routes.d.ts.map +1 -0
  4. package/dist/api/routes.js +454 -0
  5. package/dist/api/routes.js.map +1 -0
  6. package/dist/api/routes.test.d.ts +2 -0
  7. package/dist/api/routes.test.d.ts.map +1 -0
  8. package/dist/api/routes.test.js +77 -0
  9. package/dist/api/routes.test.js.map +1 -0
  10. package/dist/api/websocket.d.ts +25 -0
  11. package/dist/api/websocket.d.ts.map +1 -0
  12. package/dist/api/websocket.js +51 -0
  13. package/dist/api/websocket.js.map +1 -0
  14. package/dist/config-io.d.ts +7 -0
  15. package/dist/config-io.d.ts.map +1 -0
  16. package/dist/config-io.js +22 -0
  17. package/dist/config-io.js.map +1 -0
  18. package/dist/dht-query-service.d.ts +52 -0
  19. package/dist/dht-query-service.d.ts.map +1 -0
  20. package/dist/dht-query-service.js +276 -0
  21. package/dist/dht-query-service.js.map +1 -0
  22. package/dist/dht-query-service.test.d.ts +2 -0
  23. package/dist/dht-query-service.test.d.ts.map +1 -0
  24. package/dist/dht-query-service.test.js +77 -0
  25. package/dist/dht-query-service.test.js.map +1 -0
  26. package/dist/index.d.ts +7 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +4 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/server.d.ts +20 -0
  31. package/dist/server.d.ts.map +1 -0
  32. package/dist/server.js +65 -0
  33. package/dist/server.js.map +1 -0
  34. package/dist/status.d.ts +9 -0
  35. package/dist/status.d.ts.map +1 -0
  36. package/dist/status.js +97 -0
  37. package/dist/status.js.map +1 -0
  38. package/dist/types.d.ts +76 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +2 -0
  41. package/dist/types.js.map +1 -0
  42. package/dist-web/assets/index-CsvLvJ8Z.css +1 -0
  43. package/dist-web/assets/index-Da6V1FWz.js +142 -0
  44. package/dist-web/index.html +13 -0
  45. package/package.json +43 -0
@@ -0,0 +1,77 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import Fastify from 'fastify';
4
+ import { mkdtemp, readFile, rm } from 'node:fs/promises';
5
+ import { join } from 'node:path';
6
+ import { tmpdir } from 'node:os';
7
+ import { registerApiRoutes } from './routes.js';
8
+ function makeConfig() {
9
+ return {
10
+ identity: {
11
+ displayName: 'Dashboard Test Node',
12
+ },
13
+ seller: {
14
+ enabledProviders: ['anthropic'],
15
+ reserveFloor: 10,
16
+ maxConcurrentBuyers: 5,
17
+ pricing: {
18
+ defaults: {
19
+ inputUsdPerMillion: 10,
20
+ outputUsdPerMillion: 20,
21
+ },
22
+ },
23
+ },
24
+ buyer: {
25
+ preferredProviders: ['anthropic'],
26
+ maxPricing: {
27
+ defaults: {
28
+ inputUsdPerMillion: 30,
29
+ outputUsdPerMillion: 60,
30
+ },
31
+ },
32
+ minPeerReputation: 50,
33
+ proxyPort: 8377,
34
+ },
35
+ network: {
36
+ bootstrapNodes: [],
37
+ },
38
+ payments: {
39
+ preferredMethod: 'crypto',
40
+ platformFeeRate: 0.05,
41
+ },
42
+ providers: [],
43
+ plugins: [],
44
+ };
45
+ }
46
+ test('PUT /api/config writes to injected config path and persists payments updates', async (t) => {
47
+ const tempDir = await mkdtemp(join(tmpdir(), 'dashboard-routes-test-'));
48
+ const configPath = join(tempDir, 'custom-config.json');
49
+ const app = Fastify();
50
+ t.after(async () => {
51
+ await app.close();
52
+ await rm(tempDir, { recursive: true, force: true });
53
+ });
54
+ await registerApiRoutes(app, makeConfig(), undefined, configPath);
55
+ const response = await app.inject({
56
+ method: 'PUT',
57
+ url: '/api/config',
58
+ payload: {
59
+ payments: {
60
+ preferredMethod: 'crypto',
61
+ platformFeeRate: 0.12,
62
+ crypto: {
63
+ chainId: 'base-sepolia',
64
+ rpcUrl: 'http://127.0.0.1:8545',
65
+ escrowContractAddress: '0xabc',
66
+ usdcContractAddress: '0xdef',
67
+ },
68
+ },
69
+ },
70
+ });
71
+ assert.equal(response.statusCode, 200);
72
+ const savedRaw = await readFile(configPath, 'utf-8');
73
+ const saved = JSON.parse(savedRaw);
74
+ assert.equal(saved.payments.platformFeeRate, 0.12);
75
+ assert.equal(saved.payments.crypto?.rpcUrl, 'http://127.0.0.1:8545');
76
+ });
77
+ //# sourceMappingURL=routes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.test.js","sourceRoot":"","sources":["../../src/api/routes.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { FastifyInstance } from 'fastify';
2
+ /** Event types pushed to the dashboard via WebSocket */
3
+ export type WsEventType = 'peer_connected' | 'peer_disconnected' | 'session_started' | 'session_ended' | 'earnings_update' | 'status_change' | 'network_peers_updated';
4
+ export interface WsEvent {
5
+ type: WsEventType;
6
+ data: unknown;
7
+ timestamp: number;
8
+ }
9
+ /**
10
+ * Register the WebSocket endpoint on the Fastify server.
11
+ * Dashboard connects to ws://localhost:{port}/ws
12
+ */
13
+ export declare function registerWebSocket(app: FastifyInstance): Promise<void>;
14
+ /**
15
+ * Broadcast an event to all connected dashboard WebSocket clients.
16
+ * Called by the daemon when state changes occur.
17
+ *
18
+ * @param event - The event to broadcast
19
+ */
20
+ export declare function broadcastEvent(event: WsEvent): void;
21
+ /**
22
+ * Get the number of connected dashboard clients.
23
+ */
24
+ export declare function getConnectedClientCount(): number;
25
+ //# sourceMappingURL=websocket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../src/api/websocket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI1C,wDAAwD;AACxD,MAAM,MAAM,WAAW,GACnB,gBAAgB,GAChB,mBAAmB,GACnB,iBAAiB,GACjB,eAAe,GACf,iBAAiB,GACjB,eAAe,GACf,uBAAuB,CAAC;AAE5B,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB3E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAWnD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD"}
@@ -0,0 +1,51 @@
1
+ import fastifyWebsocket from '@fastify/websocket';
2
+ /** Set of connected WebSocket clients */
3
+ const clients = new Set();
4
+ /**
5
+ * Register the WebSocket endpoint on the Fastify server.
6
+ * Dashboard connects to ws://localhost:{port}/ws
7
+ */
8
+ export async function registerWebSocket(app) {
9
+ await app.register(fastifyWebsocket);
10
+ app.get('/ws', { websocket: true }, (socket, _req) => {
11
+ clients.add(socket);
12
+ socket.on('close', () => {
13
+ clients.delete(socket);
14
+ });
15
+ socket.on('error', () => {
16
+ clients.delete(socket);
17
+ });
18
+ // Send initial connection confirmation
19
+ socket.send(JSON.stringify({
20
+ type: 'status_change',
21
+ data: { connected: true },
22
+ timestamp: Date.now(),
23
+ }));
24
+ });
25
+ }
26
+ /**
27
+ * Broadcast an event to all connected dashboard WebSocket clients.
28
+ * Called by the daemon when state changes occur.
29
+ *
30
+ * @param event - The event to broadcast
31
+ */
32
+ export function broadcastEvent(event) {
33
+ const message = JSON.stringify(event);
34
+ for (const client of clients) {
35
+ if (client.readyState === client.OPEN) {
36
+ try {
37
+ client.send(message);
38
+ }
39
+ catch {
40
+ clients.delete(client);
41
+ }
42
+ }
43
+ }
44
+ }
45
+ /**
46
+ * Get the number of connected dashboard clients.
47
+ */
48
+ export function getConnectedClientCount() {
49
+ return clients.size;
50
+ }
51
+ //# sourceMappingURL=websocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../src/api/websocket.ts"],"names":[],"mappings":"AACA,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAmBlD,yCAAyC;AACzC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAa,CAAC;AAErC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAoB;IAC1D,MAAM,GAAG,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAErC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE;QACnD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAEpB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;QAEH,uCAAuC;QACvC,MAAM,CAAC,IAAI,CACT,IAAI,CAAC,SAAS,CAAC;YACb,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;YACzB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACJ,CAAC,CACrB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACtC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC;YACtC,IAAI,CAAC;gBACH,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO,OAAO,CAAC,IAAI,CAAC;AACtB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { DashboardConfig } from './types.js';
2
+ /**
3
+ * Persist the dashboard config to disk.
4
+ * Creates the directory if it doesn't exist.
5
+ */
6
+ export declare function saveConfig(configPath: string, config: DashboardConfig): Promise<void>;
7
+ //# sourceMappingURL=config-io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-io.d.ts","sourceRoot":"","sources":["../src/config-io.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAYlD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAI3F"}
@@ -0,0 +1,22 @@
1
+ import { writeFile, mkdir } from 'node:fs/promises';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ /**
5
+ * Resolve a config path, expanding ~ to the user's home directory.
6
+ */
7
+ function resolveConfigPath(configPath) {
8
+ if (configPath.startsWith('~')) {
9
+ return resolve(homedir(), configPath.slice(2));
10
+ }
11
+ return resolve(configPath);
12
+ }
13
+ /**
14
+ * Persist the dashboard config to disk.
15
+ * Creates the directory if it doesn't exist.
16
+ */
17
+ export async function saveConfig(configPath, config) {
18
+ const resolved = resolveConfigPath(configPath);
19
+ await mkdir(dirname(resolved), { recursive: true });
20
+ await writeFile(resolved, JSON.stringify(config, null, 2), 'utf-8');
21
+ }
22
+ //# sourceMappingURL=config-io.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-io.js","sourceRoot":"","sources":["../src/config-io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC;;GAEG;AACH,SAAS,iBAAiB,CAAC,UAAkB;IAC3C,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,OAAO,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAAkB,EAAE,MAAuB;IAC1E,MAAM,QAAQ,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAC/C,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC"}
@@ -0,0 +1,52 @@
1
+ import type { DashboardConfig } from './types.js';
2
+ import type { PeerMetadata } from '@antseed/node';
3
+ export interface NetworkPeer {
4
+ peerId: string;
5
+ host: string;
6
+ port: number;
7
+ providers: string[];
8
+ inputUsdPerMillion: number;
9
+ outputUsdPerMillion: number;
10
+ capacityMsgPerHour: number;
11
+ reputation: number;
12
+ lastSeen: number;
13
+ source: 'dht' | 'daemon';
14
+ }
15
+ export interface NetworkStats {
16
+ totalPeers: number;
17
+ dhtNodeCount: number;
18
+ dhtHealthy: boolean;
19
+ lastScanAt: number | null;
20
+ totalLookups: number;
21
+ successfulLookups: number;
22
+ lookupSuccessRate: number;
23
+ averageLookupLatencyMs: number;
24
+ healthReason: string;
25
+ }
26
+ export declare function resolveDiscoveryProviders(config: Pick<DashboardConfig, 'seller' | 'buyer'>): string[];
27
+ export declare function resolveNetworkPeerProviders(metadata: Pick<PeerMetadata, 'providers'> | null | undefined, existingProviders: string[] | undefined, discoveredTopic: string): string[];
28
+ export declare function resolveMetadataSummaryPricing(metadata: Pick<PeerMetadata, 'providers'> | null | undefined, preferredProviders?: string[]): {
29
+ inputUsdPerMillion: number;
30
+ outputUsdPerMillion: number;
31
+ };
32
+ export declare class DHTQueryService {
33
+ private readonly config;
34
+ private dhtNode;
35
+ private healthMonitor;
36
+ private readonly metadataResolver;
37
+ private readonly peers;
38
+ private readonly events;
39
+ private scanTimer;
40
+ private lastScanAt;
41
+ private running;
42
+ constructor(config: DashboardConfig);
43
+ private resolveSummaryPricing;
44
+ start(): Promise<void>;
45
+ stop(): Promise<void>;
46
+ scanNow(): Promise<void>;
47
+ getNetworkPeers(): NetworkPeer[];
48
+ getNetworkStats(): NetworkStats;
49
+ onPeersUpdated(callback: (peers: NetworkPeer[]) => void): void;
50
+ offPeersUpdated(callback: (peers: NetworkPeer[]) => void): void;
51
+ }
52
+ //# sourceMappingURL=dht-query-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dht-query-service.d.ts","sourceRoot":"","sources":["../src/dht-query-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAOlD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;CACtB;AAkDD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,OAAO,CAAC,GAChD,MAAM,EAAE,CAsBV;AAmBD,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,EAC5D,iBAAiB,EAAE,MAAM,EAAE,GAAG,SAAS,EACvC,eAAe,EAAE,MAAM,GACtB,MAAM,EAAE,CAoBV;AAED,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,EAC5D,kBAAkB,GAAE,MAAM,EAAO,GAChC;IAAE,kBAAkB,EAAE,MAAM,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAA;CAAE,CAiB7D;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAiD;IAClF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,eAAe;IAInC,OAAO,CAAC,qBAAqB;IAOvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyCtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBrB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAoE9B,eAAe,IAAI,WAAW,EAAE;IAIhC,eAAe,IAAI,YAAY;IA2B/B,cAAc,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,IAAI,GAAG,IAAI;IAI9D,eAAe,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,IAAI,GAAG,IAAI;CAGhE"}
@@ -0,0 +1,276 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { randomBytes } from 'node:crypto';
3
+ import { DHTNode, DEFAULT_DHT_CONFIG, topicToInfoHash, providerTopic } from '@antseed/node/discovery';
4
+ import { DHTHealthMonitor } from '@antseed/node/discovery';
5
+ import { DEFAULT_HEALTH_THRESHOLDS } from '@antseed/node/discovery';
6
+ import { HttpMetadataResolver } from '@antseed/node/discovery';
7
+ import { mergeBootstrapNodes, OFFICIAL_BOOTSTRAP_NODES, toBootstrapConfig, parseBootstrapList } from '@antseed/node/discovery';
8
+ import { toPeerId } from '@antseed/node';
9
+ const SCAN_INTERVAL_MS = 30_000;
10
+ const DEFAULT_DISCOVERY_PROVIDERS = [
11
+ 'anthropic',
12
+ 'openai',
13
+ 'google',
14
+ 'claude-code',
15
+ 'claude-oauth',
16
+ 'openrouter',
17
+ 'local-llm',
18
+ ];
19
+ const PROVIDER_ALIAS_MAP = {
20
+ '@antseed/provider-anthropic': 'anthropic',
21
+ 'antseed-provider-anthropic': 'anthropic',
22
+ '@antseed/provider-claude-code': 'claude-code',
23
+ 'antseed-provider-claude-code': 'claude-code',
24
+ '@antseed/provider-claude-oauth': 'claude-oauth',
25
+ 'antseed-provider-claude-oauth': 'claude-oauth',
26
+ '@antseed/provider-openrouter': 'openrouter',
27
+ 'antseed-provider-openrouter': 'openrouter',
28
+ '@antseed/provider-local-llm': 'local-llm',
29
+ 'antseed-provider-local-llm': 'local-llm',
30
+ };
31
+ function normalizeProviderTopicName(value) {
32
+ if (typeof value !== 'string') {
33
+ return null;
34
+ }
35
+ const raw = value.trim().toLowerCase();
36
+ if (!raw) {
37
+ return null;
38
+ }
39
+ const alias = PROVIDER_ALIAS_MAP[raw];
40
+ if (alias) {
41
+ return alias;
42
+ }
43
+ if (raw.startsWith('@antseed/provider-')) {
44
+ return raw.slice('@antseed/provider-'.length);
45
+ }
46
+ if (raw.startsWith('antseed-provider-')) {
47
+ return raw.slice('antseed-provider-'.length);
48
+ }
49
+ return raw;
50
+ }
51
+ export function resolveDiscoveryProviders(config) {
52
+ const topics = new Set();
53
+ for (const candidate of config.seller.enabledProviders) {
54
+ const normalized = normalizeProviderTopicName(candidate);
55
+ if (normalized) {
56
+ topics.add(normalized);
57
+ }
58
+ }
59
+ for (const candidate of config.buyer.preferredProviders) {
60
+ const normalized = normalizeProviderTopicName(candidate);
61
+ if (normalized) {
62
+ topics.add(normalized);
63
+ }
64
+ }
65
+ for (const candidate of DEFAULT_DISCOVERY_PROVIDERS) {
66
+ topics.add(candidate);
67
+ }
68
+ return Array.from(topics);
69
+ }
70
+ function providerNamesFromMetadata(metadata) {
71
+ if (!metadata?.providers || metadata.providers.length === 0) {
72
+ return [];
73
+ }
74
+ const providers = new Set();
75
+ for (const entry of metadata.providers) {
76
+ const normalized = normalizeProviderTopicName(entry.provider);
77
+ if (normalized) {
78
+ providers.add(normalized);
79
+ }
80
+ }
81
+ return Array.from(providers);
82
+ }
83
+ export function resolveNetworkPeerProviders(metadata, existingProviders, discoveredTopic) {
84
+ // Prefer explicit provider list from peer metadata when available.
85
+ const fromMetadata = providerNamesFromMetadata(metadata);
86
+ if (fromMetadata.length > 0) {
87
+ return fromMetadata;
88
+ }
89
+ // Otherwise accumulate prior inferred topics and current lookup topic.
90
+ const providers = new Set();
91
+ for (const provider of existingProviders ?? []) {
92
+ const normalized = normalizeProviderTopicName(provider);
93
+ if (normalized) {
94
+ providers.add(normalized);
95
+ }
96
+ }
97
+ const normalizedTopic = normalizeProviderTopicName(discoveredTopic);
98
+ if (normalizedTopic) {
99
+ providers.add(normalizedTopic);
100
+ }
101
+ return Array.from(providers);
102
+ }
103
+ export function resolveMetadataSummaryPricing(metadata, preferredProviders = []) {
104
+ if (!metadata?.providers || metadata.providers.length === 0) {
105
+ return { inputUsdPerMillion: 0, outputUsdPerMillion: 0 };
106
+ }
107
+ const selectedProvider = preferredProviders.length > 0
108
+ ? metadata.providers.find((provider) => preferredProviders.includes(provider.provider)) ?? metadata.providers[0]
109
+ : metadata.providers[0];
110
+ if (!selectedProvider) {
111
+ return { inputUsdPerMillion: 0, outputUsdPerMillion: 0 };
112
+ }
113
+ return {
114
+ inputUsdPerMillion: selectedProvider.defaultPricing.inputUsdPerMillion,
115
+ outputUsdPerMillion: selectedProvider.defaultPricing.outputUsdPerMillion,
116
+ };
117
+ }
118
+ export class DHTQueryService {
119
+ config;
120
+ dhtNode = null;
121
+ healthMonitor = null;
122
+ metadataResolver = new HttpMetadataResolver({ timeoutMs: 5000 });
123
+ peers = new Map();
124
+ events = new EventEmitter();
125
+ scanTimer;
126
+ lastScanAt = null;
127
+ running = false;
128
+ constructor(config) {
129
+ this.config = config;
130
+ }
131
+ resolveSummaryPricing(metadata) {
132
+ return resolveMetadataSummaryPricing(metadata, this.config.buyer?.preferredProviders ?? []);
133
+ }
134
+ async start() {
135
+ if (this.running)
136
+ return;
137
+ // Generate a random peerId for the read-only DHT node
138
+ const randomId = randomBytes(32).toString('hex');
139
+ const peerId = toPeerId(randomId);
140
+ const userBootstrap = this.config.network?.bootstrapNodes?.length
141
+ ? parseBootstrapList(this.config.network.bootstrapNodes)
142
+ : [];
143
+ const allBootstrap = toBootstrapConfig(mergeBootstrapNodes(OFFICIAL_BOOTSTRAP_NODES, userBootstrap));
144
+ this.dhtNode = new DHTNode({
145
+ peerId,
146
+ ...DEFAULT_DHT_CONFIG,
147
+ port: 0, // OS-assigned port — read-only, no announce
148
+ bootstrapNodes: allBootstrap,
149
+ allowPrivateIPs: true, // Allow local/private peers for development
150
+ });
151
+ await this.dhtNode.start();
152
+ // Dashboard DHT visibility is read-only and often runs in small local networks.
153
+ // Use a less strict health threshold than the full node runtime monitor.
154
+ this.healthMonitor = new DHTHealthMonitor(() => this.dhtNode?.getNodeCount() ?? 0, {
155
+ ...DEFAULT_HEALTH_THRESHOLDS,
156
+ minNodeCount: 1,
157
+ minLookupSuccessRate: 0.2,
158
+ maxAvgLookupLatencyMs: 30_000,
159
+ });
160
+ this.running = true;
161
+ // Initial scan
162
+ this.scanNow().catch(() => { });
163
+ // Periodic scans
164
+ this.scanTimer = setInterval(() => {
165
+ this.scanNow().catch(() => { });
166
+ }, SCAN_INTERVAL_MS);
167
+ }
168
+ async stop() {
169
+ if (!this.running)
170
+ return;
171
+ this.running = false;
172
+ if (this.scanTimer) {
173
+ clearInterval(this.scanTimer);
174
+ this.scanTimer = undefined;
175
+ }
176
+ if (this.dhtNode) {
177
+ await this.dhtNode.stop();
178
+ this.dhtNode = null;
179
+ }
180
+ this.healthMonitor = null;
181
+ this.peers.clear();
182
+ }
183
+ async scanNow() {
184
+ if (!this.dhtNode || !this.healthMonitor)
185
+ return;
186
+ const topics = resolveDiscoveryProviders(this.config);
187
+ const discoveredPeers = new Map();
188
+ for (const name of topics) {
189
+ const topic = providerTopic(name);
190
+ const infoHash = topicToInfoHash(topic);
191
+ const startTime = Date.now();
192
+ try {
193
+ const endpoints = await this.dhtNode.lookup(infoHash);
194
+ const latency = Date.now() - startTime;
195
+ this.healthMonitor.recordLookup(endpoints.length > 0, latency);
196
+ for (const ep of endpoints) {
197
+ // Try to resolve metadata for richer peer info
198
+ let metadata = null;
199
+ try {
200
+ metadata = await this.metadataResolver.resolve(ep);
201
+ }
202
+ catch {
203
+ // Metadata resolution failed — use basic info
204
+ }
205
+ const peerId = metadata?.peerId ?? `${ep.host}:${ep.port}`;
206
+ const existing = discoveredPeers.get(peerId);
207
+ const providers = resolveNetworkPeerProviders(metadata, existing?.providers, name);
208
+ // Extract summary pricing and capacity from metadata
209
+ const summaryPricing = this.resolveSummaryPricing(metadata);
210
+ let capacityMsgPerHour = 0;
211
+ if (metadata?.providers) {
212
+ for (const pa of metadata.providers) {
213
+ capacityMsgPerHour += pa.maxConcurrency * 60;
214
+ }
215
+ }
216
+ discoveredPeers.set(peerId, {
217
+ peerId,
218
+ host: ep.host,
219
+ port: ep.port,
220
+ providers,
221
+ inputUsdPerMillion: existing?.inputUsdPerMillion || summaryPricing.inputUsdPerMillion,
222
+ outputUsdPerMillion: existing?.outputUsdPerMillion || summaryPricing.outputUsdPerMillion,
223
+ capacityMsgPerHour: existing?.capacityMsgPerHour || capacityMsgPerHour,
224
+ reputation: metadata ? 100 : 50, // Reputation scale is 0-100
225
+ lastSeen: Date.now(),
226
+ source: 'dht',
227
+ });
228
+ }
229
+ }
230
+ catch {
231
+ this.healthMonitor.recordLookup(false, Date.now() - startTime);
232
+ }
233
+ }
234
+ // Update cache
235
+ this.peers.clear();
236
+ for (const [id, peer] of discoveredPeers) {
237
+ this.peers.set(id, peer);
238
+ }
239
+ this.lastScanAt = Date.now();
240
+ this.events.emit('peers_updated', this.getNetworkPeers());
241
+ }
242
+ getNetworkPeers() {
243
+ return Array.from(this.peers.values());
244
+ }
245
+ getNetworkStats() {
246
+ const snapshot = this.healthMonitor?.getSnapshot();
247
+ const totalLookups = snapshot?.totalLookups ?? 0;
248
+ const successfulLookups = snapshot?.successfulLookups ?? 0;
249
+ const successRate = totalLookups > 0 ? successfulLookups / totalLookups : 0;
250
+ const nodeCount = snapshot?.nodeCount ?? 0;
251
+ const discoveredPeers = this.peers.size;
252
+ // If peers are actively discovered, consider DHT usable even if strict thresholds fail.
253
+ const dhtHealthy = Boolean(snapshot?.isHealthy) || discoveredPeers > 0 || nodeCount > 0;
254
+ const healthReason = dhtHealthy
255
+ ? `ok (nodes=${nodeCount}, peers=${discoveredPeers}, successRate=${(successRate * 100).toFixed(0)}%)`
256
+ : `insufficient activity (nodes=${nodeCount}, peers=${discoveredPeers}, lookups=${totalLookups})`;
257
+ return {
258
+ totalPeers: discoveredPeers,
259
+ dhtNodeCount: nodeCount,
260
+ dhtHealthy,
261
+ lastScanAt: this.lastScanAt,
262
+ totalLookups,
263
+ successfulLookups,
264
+ lookupSuccessRate: successRate,
265
+ averageLookupLatencyMs: snapshot?.averageLookupLatencyMs ?? 0,
266
+ healthReason,
267
+ };
268
+ }
269
+ onPeersUpdated(callback) {
270
+ this.events.on('peers_updated', callback);
271
+ }
272
+ offPeersUpdated(callback) {
273
+ this.events.off('peers_updated', callback);
274
+ }
275
+ }
276
+ //# sourceMappingURL=dht-query-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dht-query-service.js","sourceRoot":"","sources":["../src/dht-query-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dht-query-service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dht-query-service.test.d.ts","sourceRoot":"","sources":["../src/dht-query-service.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,77 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { resolveDiscoveryProviders, resolveMetadataSummaryPricing, resolveNetworkPeerProviders } from './dht-query-service.js';
4
+ test('metadata default pricing maps to input/output USD per million', () => {
5
+ const pricing = resolveMetadataSummaryPricing({
6
+ providers: [
7
+ {
8
+ provider: 'anthropic',
9
+ models: ['claude-sonnet-4-5-20250929'],
10
+ defaultPricing: {
11
+ inputUsdPerMillion: 11,
12
+ outputUsdPerMillion: 33,
13
+ },
14
+ maxConcurrency: 5,
15
+ currentLoad: 0,
16
+ },
17
+ ],
18
+ });
19
+ assert.equal(pricing.inputUsdPerMillion, 11);
20
+ assert.equal(pricing.outputUsdPerMillion, 33);
21
+ });
22
+ test('missing model-specific pricing still resolves provider defaults', () => {
23
+ const pricing = resolveMetadataSummaryPricing({
24
+ providers: [
25
+ {
26
+ provider: 'openai',
27
+ models: ['gpt-4o', 'gpt-4o-mini'],
28
+ defaultPricing: {
29
+ inputUsdPerMillion: 7,
30
+ outputUsdPerMillion: 21,
31
+ },
32
+ maxConcurrency: 8,
33
+ currentLoad: 0,
34
+ },
35
+ ],
36
+ }, ['openai']);
37
+ assert.equal(pricing.inputUsdPerMillion, 7);
38
+ assert.equal(pricing.outputUsdPerMillion, 21);
39
+ });
40
+ test('discovery providers include buyer preferred and known defaults', () => {
41
+ const providers = resolveDiscoveryProviders({
42
+ seller: { enabledProviders: [] },
43
+ buyer: { preferredProviders: ['claude-code'] },
44
+ });
45
+ assert.ok(providers.includes('claude-code'));
46
+ assert.ok(providers.includes('anthropic'));
47
+ assert.ok(providers.includes('openrouter'));
48
+ });
49
+ test('discovery providers normalize package aliases and dedupe', () => {
50
+ const providers = resolveDiscoveryProviders({
51
+ seller: { enabledProviders: ['@antseed/provider-claude-code', 'claude-code'] },
52
+ buyer: { preferredProviders: ['antseed-provider-openrouter', 'openrouter'] },
53
+ });
54
+ const claudeCodeCount = providers.filter((p) => p === 'claude-code').length;
55
+ const openrouterCount = providers.filter((p) => p === 'openrouter').length;
56
+ assert.equal(claudeCodeCount, 1);
57
+ assert.equal(openrouterCount, 1);
58
+ });
59
+ test('network peer providers prefer metadata providers over topic inference', () => {
60
+ const providers = resolveNetworkPeerProviders({
61
+ providers: [
62
+ {
63
+ provider: 'claude-code',
64
+ models: ['x'],
65
+ defaultPricing: { inputUsdPerMillion: 0, outputUsdPerMillion: 0 },
66
+ maxConcurrency: 1,
67
+ currentLoad: 0,
68
+ },
69
+ ],
70
+ }, ['local-llm'], 'local-llm');
71
+ assert.deepEqual(providers, ['claude-code']);
72
+ });
73
+ test('network peer providers fallback accumulates inferred topics when metadata is unavailable', () => {
74
+ const providers = resolveNetworkPeerProviders(null, ['@antseed/provider-openrouter'], 'openrouter');
75
+ assert.deepEqual(providers, ['openrouter']);
76
+ });
77
+ //# sourceMappingURL=dht-query-service.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dht-query-service.test.js","sourceRoot":"","sources":["../src/dht-query-service.test.ts"],"names":[],"mappings":"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"}