@antseed/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +15 -0
- package/README.md +169 -0
- package/dist/cli/commands/balance.d.ts +3 -0
- package/dist/cli/commands/balance.d.ts.map +1 -0
- package/dist/cli/commands/balance.js +64 -0
- package/dist/cli/commands/balance.js.map +1 -0
- package/dist/cli/commands/browse.d.ts +7 -0
- package/dist/cli/commands/browse.d.ts.map +1 -0
- package/dist/cli/commands/browse.js +100 -0
- package/dist/cli/commands/browse.js.map +1 -0
- package/dist/cli/commands/config.d.ts +20 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +239 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/connect.d.ts +14 -0
- package/dist/cli/commands/connect.d.ts.map +1 -0
- package/dist/cli/commands/connect.js +298 -0
- package/dist/cli/commands/connect.js.map +1 -0
- package/dist/cli/commands/connect.test.d.ts +2 -0
- package/dist/cli/commands/connect.test.d.ts.map +1 -0
- package/dist/cli/commands/connect.test.js +54 -0
- package/dist/cli/commands/connect.test.js.map +1 -0
- package/dist/cli/commands/dashboard.d.ts +6 -0
- package/dist/cli/commands/dashboard.d.ts.map +1 -0
- package/dist/cli/commands/dashboard.js +48 -0
- package/dist/cli/commands/dashboard.js.map +1 -0
- package/dist/cli/commands/deposit.d.ts +3 -0
- package/dist/cli/commands/deposit.d.ts.map +1 -0
- package/dist/cli/commands/deposit.js +48 -0
- package/dist/cli/commands/deposit.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +3 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +94 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/init.d.ts +3 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +91 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/plugin-create.d.ts +11 -0
- package/dist/cli/commands/plugin-create.d.ts.map +1 -0
- package/dist/cli/commands/plugin-create.js +201 -0
- package/dist/cli/commands/plugin-create.js.map +1 -0
- package/dist/cli/commands/plugin-create.test.d.ts +2 -0
- package/dist/cli/commands/plugin-create.test.d.ts.map +1 -0
- package/dist/cli/commands/plugin-create.test.js +53 -0
- package/dist/cli/commands/plugin-create.test.js.map +1 -0
- package/dist/cli/commands/plugin.d.ts +3 -0
- package/dist/cli/commands/plugin.d.ts.map +1 -0
- package/dist/cli/commands/plugin.js +279 -0
- package/dist/cli/commands/plugin.js.map +1 -0
- package/dist/cli/commands/plugin.test.d.ts +2 -0
- package/dist/cli/commands/plugin.test.d.ts.map +1 -0
- package/dist/cli/commands/plugin.test.js +53 -0
- package/dist/cli/commands/plugin.test.js.map +1 -0
- package/dist/cli/commands/profile.d.ts +10 -0
- package/dist/cli/commands/profile.d.ts.map +1 -0
- package/dist/cli/commands/profile.js +89 -0
- package/dist/cli/commands/profile.js.map +1 -0
- package/dist/cli/commands/seed.d.ts +11 -0
- package/dist/cli/commands/seed.d.ts.map +1 -0
- package/dist/cli/commands/seed.js +397 -0
- package/dist/cli/commands/seed.js.map +1 -0
- package/dist/cli/commands/seed.test.d.ts +2 -0
- package/dist/cli/commands/seed.test.d.ts.map +1 -0
- package/dist/cli/commands/seed.test.js +57 -0
- package/dist/cli/commands/seed.test.js.map +1 -0
- package/dist/cli/commands/status.d.ts +8 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +55 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/types.d.ts +14 -0
- package/dist/cli/commands/types.d.ts.map +1 -0
- package/dist/cli/commands/types.js +41 -0
- package/dist/cli/commands/types.js.map +1 -0
- package/dist/cli/commands/withdraw.d.ts +3 -0
- package/dist/cli/commands/withdraw.d.ts.map +1 -0
- package/dist/cli/commands/withdraw.js +48 -0
- package/dist/cli/commands/withdraw.js.map +1 -0
- package/dist/cli/formatters.d.ts +29 -0
- package/dist/cli/formatters.d.ts.map +1 -0
- package/dist/cli/formatters.js +67 -0
- package/dist/cli/formatters.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +41 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/shutdown.d.ts +11 -0
- package/dist/cli/shutdown.d.ts.map +1 -0
- package/dist/cli/shutdown.js +34 -0
- package/dist/cli/shutdown.js.map +1 -0
- package/dist/config/defaults.d.ts +6 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +48 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/effective.d.ts +26 -0
- package/dist/config/effective.d.ts.map +1 -0
- package/dist/config/effective.js +84 -0
- package/dist/config/effective.js.map +1 -0
- package/dist/config/effective.test.d.ts +2 -0
- package/dist/config/effective.test.d.ts.map +1 -0
- package/dist/config/effective.test.js +65 -0
- package/dist/config/effective.test.js.map +1 -0
- package/dist/config/loader.d.ts +12 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +212 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/loader.test.d.ts +2 -0
- package/dist/config/loader.test.d.ts.map +1 -0
- package/dist/config/loader.test.js +77 -0
- package/dist/config/loader.test.js.map +1 -0
- package/dist/config/types.d.ts +133 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/validation.d.ts +10 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +50 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/env/load-env.d.ts +6 -0
- package/dist/env/load-env.d.ts.map +1 -0
- package/dist/env/load-env.js +18 -0
- package/dist/env/load-env.js.map +1 -0
- package/dist/plugins/loader.d.ts +7 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +70 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/manager.d.ts +11 -0
- package/dist/plugins/manager.d.ts.map +1 -0
- package/dist/plugins/manager.js +52 -0
- package/dist/plugins/manager.js.map +1 -0
- package/dist/plugins/registry.d.ts +8 -0
- package/dist/plugins/registry.d.ts.map +1 -0
- package/dist/plugins/registry.js +39 -0
- package/dist/plugins/registry.js.map +1 -0
- package/dist/proxy/buyer-proxy.d.ts +30 -0
- package/dist/proxy/buyer-proxy.d.ts.map +1 -0
- package/dist/proxy/buyer-proxy.js +488 -0
- package/dist/proxy/buyer-proxy.js.map +1 -0
- package/dist/status/node-status.d.ts +22 -0
- package/dist/status/node-status.d.ts.map +1 -0
- package/dist/status/node-status.js +83 -0
- package/dist/status/node-status.js.map +1 -0
- package/package.json +39 -0
- package/src/cli/commands/balance.ts +77 -0
- package/src/cli/commands/browse.ts +113 -0
- package/src/cli/commands/config.ts +271 -0
- package/src/cli/commands/connect.test.ts +69 -0
- package/src/cli/commands/connect.ts +342 -0
- package/src/cli/commands/dashboard.ts +59 -0
- package/src/cli/commands/deposit.ts +61 -0
- package/src/cli/commands/dev.ts +107 -0
- package/src/cli/commands/init.ts +99 -0
- package/src/cli/commands/plugin-create.test.ts +60 -0
- package/src/cli/commands/plugin-create.ts +230 -0
- package/src/cli/commands/plugin.test.ts +55 -0
- package/src/cli/commands/plugin.ts +295 -0
- package/src/cli/commands/profile.ts +95 -0
- package/src/cli/commands/seed.test.ts +70 -0
- package/src/cli/commands/seed.ts +447 -0
- package/src/cli/commands/status.ts +73 -0
- package/src/cli/commands/types.ts +56 -0
- package/src/cli/commands/withdraw.ts +61 -0
- package/src/cli/formatters.ts +64 -0
- package/src/cli/index.ts +46 -0
- package/src/cli/shutdown.ts +38 -0
- package/src/config/defaults.ts +49 -0
- package/src/config/effective.test.ts +80 -0
- package/src/config/effective.ts +119 -0
- package/src/config/loader.test.ts +95 -0
- package/src/config/loader.ts +251 -0
- package/src/config/types.ts +139 -0
- package/src/config/validation.ts +78 -0
- package/src/env/load-env.ts +20 -0
- package/src/plugins/loader.ts +96 -0
- package/src/plugins/manager.ts +66 -0
- package/src/plugins/registry.ts +45 -0
- package/src/proxy/buyer-proxy.ts +604 -0
- package/src/status/node-status.ts +105 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/plugins/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const PLUGINS_DIR: string;
|
|
2
|
+
export declare function getPluginsDir(): string;
|
|
3
|
+
export interface InstalledPlugin {
|
|
4
|
+
name: string;
|
|
5
|
+
package: string;
|
|
6
|
+
version: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function installPlugin(packageName: string): Promise<void>;
|
|
9
|
+
export declare function removePlugin(packageName: string): Promise<void>;
|
|
10
|
+
export declare function listInstalledPlugins(): Promise<InstalledPlugin[]>;
|
|
11
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/plugins/manager.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,WAAW,QAAyC,CAAA;AAOjE,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB;AAiBD,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtE;AAED,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrE;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAcvE"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { mkdir, readFile, writeFile, access } from 'node:fs/promises';
|
|
4
|
+
import { constants, existsSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { homedir } from 'node:os';
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
export const PLUGINS_DIR = join(homedir(), '.antseed', 'plugins');
|
|
9
|
+
function resolvePluginsDir() {
|
|
10
|
+
if (existsSync(PLUGINS_DIR))
|
|
11
|
+
return PLUGINS_DIR;
|
|
12
|
+
return PLUGINS_DIR;
|
|
13
|
+
}
|
|
14
|
+
export function getPluginsDir() {
|
|
15
|
+
return resolvePluginsDir();
|
|
16
|
+
}
|
|
17
|
+
async function ensurePluginsDir() {
|
|
18
|
+
const pluginsDir = resolvePluginsDir();
|
|
19
|
+
const pluginsPackageJson = join(pluginsDir, 'package.json');
|
|
20
|
+
await mkdir(pluginsDir, { recursive: true });
|
|
21
|
+
try {
|
|
22
|
+
await access(pluginsPackageJson, constants.R_OK);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
await writeFile(pluginsPackageJson, JSON.stringify({ name: 'antseed-plugins', version: '1.0.0', private: true, dependencies: {} }, null, 2), 'utf-8');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function installPlugin(packageName) {
|
|
29
|
+
await ensurePluginsDir();
|
|
30
|
+
await execFileAsync('npm', ['install', '--ignore-scripts', packageName], { cwd: getPluginsDir() });
|
|
31
|
+
}
|
|
32
|
+
export async function removePlugin(packageName) {
|
|
33
|
+
await ensurePluginsDir();
|
|
34
|
+
await execFileAsync('npm', ['uninstall', packageName], { cwd: getPluginsDir() });
|
|
35
|
+
}
|
|
36
|
+
export async function listInstalledPlugins() {
|
|
37
|
+
await ensurePluginsDir();
|
|
38
|
+
try {
|
|
39
|
+
const raw = await readFile(join(getPluginsDir(), 'package.json'), 'utf-8');
|
|
40
|
+
const pkg = JSON.parse(raw);
|
|
41
|
+
const deps = pkg.dependencies ?? {};
|
|
42
|
+
return Object.entries(deps).map(([pkgName, version]) => ({
|
|
43
|
+
name: pkgName,
|
|
44
|
+
package: pkgName,
|
|
45
|
+
version: version,
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/plugins/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAEjC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;AAEzC,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;AAEjE,SAAS,iBAAiB;IACxB,IAAI,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,WAAW,CAAA;IAC/C,OAAO,WAAW,CAAA;AACpB,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,iBAAiB,EAAE,CAAA;AAC5B,CAAC;AAQD,KAAK,UAAU,gBAAgB;IAC7B,MAAM,UAAU,GAAG,iBAAiB,EAAE,CAAA;IACtC,MAAM,kBAAkB,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAA;IAC3D,MAAM,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,kBAAkB,EAAE,SAAS,CAAC,IAAI,CAAC,CAAA;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,SAAS,CACb,kBAAkB,EAClB,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EACvG,OAAO,CACR,CAAA;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,WAAmB;IACrD,MAAM,gBAAgB,EAAE,CAAA;IACxB,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,SAAS,EAAE,kBAAkB,EAAE,WAAW,CAAC,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,EAAE,CAAC,CAAA;AACpG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,WAAmB;IACpD,MAAM,gBAAgB,EAAE,CAAA;IACxB,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,EAAE,CAAC,CAAA;AAClF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,MAAM,gBAAgB,EAAE,CAAA;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAA;QAC1E,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA8C,CAAA;QACxE,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,IAAI,EAAE,CAAA;QACnC,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;YACvD,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,OAAO;YAChB,OAAO,EAAE,OAAO;SACjB,CAAC,CAAC,CAAA;IACL,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/plugins/registry.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,eAAO,MAAM,eAAe,EAAE,aAAa,EAqC1C,CAAA"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const TRUSTED_PLUGINS = [
|
|
2
|
+
{
|
|
3
|
+
name: 'anthropic',
|
|
4
|
+
type: 'provider',
|
|
5
|
+
description: 'Sell Anthropic API capacity (API key)',
|
|
6
|
+
package: '@antseed/provider-anthropic',
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: 'claude-code',
|
|
10
|
+
type: 'provider',
|
|
11
|
+
description: 'Sell Claude capacity via Claude Code keychain',
|
|
12
|
+
package: '@antseed/provider-claude-code',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: 'openrouter',
|
|
16
|
+
type: 'provider',
|
|
17
|
+
description: 'Sell via OpenRouter (multi-model)',
|
|
18
|
+
package: '@antseed/provider-openrouter',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'local-llm',
|
|
22
|
+
type: 'provider',
|
|
23
|
+
description: 'Sell local LLM capacity (Ollama, llama.cpp)',
|
|
24
|
+
package: '@antseed/provider-local-llm',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'local-proxy',
|
|
28
|
+
type: 'router',
|
|
29
|
+
description: 'Local HTTP proxy for Claude Code, Aider, Codex',
|
|
30
|
+
package: '@antseed/router-local-proxy',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'local-chat',
|
|
34
|
+
type: 'router',
|
|
35
|
+
description: 'Local desktop chat router',
|
|
36
|
+
package: '@antseed/router-local-chat',
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/plugins/registry.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,eAAe,GAAoB;IAC9C;QACE,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,uCAAuC;QACpD,OAAO,EAAE,6BAA6B;KACvC;IACD;QACE,IAAI,EAAE,aAAa;QACnB,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,+CAA+C;QAC5D,OAAO,EAAE,+BAA+B;KACzC;IACD;QACE,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,mCAAmC;QAChD,OAAO,EAAE,8BAA8B;KACxC;IACD;QACE,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,6CAA6C;QAC1D,OAAO,EAAE,6BAA6B;KACvC;IACD;QACE,IAAI,EAAE,aAAa;QACnB,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,gDAAgD;QAC7D,OAAO,EAAE,6BAA6B;KACvC;IACD;QACE,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,2BAA2B;QACxC,OAAO,EAAE,4BAA4B;KACtC;CACF,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AntseedNode } from '@antseed/node';
|
|
2
|
+
export interface BuyerProxyConfig {
|
|
3
|
+
port: number;
|
|
4
|
+
node: AntseedNode;
|
|
5
|
+
/** How long to cache discovered peers before re-querying DHT (ms). Default: 30000 */
|
|
6
|
+
peerCacheTtlMs?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Local HTTP proxy that forwards requests to P2P sellers.
|
|
10
|
+
*
|
|
11
|
+
* Tools like Claude CLI set ANTHROPIC_BASE_URL=http://localhost:8377
|
|
12
|
+
* and the proxy transparently routes their API calls through the
|
|
13
|
+
* Antseed P2P network.
|
|
14
|
+
*/
|
|
15
|
+
export declare class BuyerProxy {
|
|
16
|
+
private readonly _server;
|
|
17
|
+
private readonly _node;
|
|
18
|
+
private readonly _port;
|
|
19
|
+
private readonly _peerCacheTtlMs;
|
|
20
|
+
private _cachedPeers;
|
|
21
|
+
private _cacheTimestamp;
|
|
22
|
+
constructor(config: BuyerProxyConfig);
|
|
23
|
+
start(): Promise<void>;
|
|
24
|
+
stop(): Promise<void>;
|
|
25
|
+
private _readLocalSeederFallback;
|
|
26
|
+
private _getPeers;
|
|
27
|
+
private _formatPeerSelectionDiagnostics;
|
|
28
|
+
private _handleRequest;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=buyer-proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buyer-proxy.d.ts","sourceRoot":"","sources":["../../src/proxy/buyer-proxy.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAGhD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,WAAW,CAAA;IACjB,qFAAqF;IACrF,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AA6UD;;;;;;GAMG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IAExC,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,eAAe,CAAI;gBAEf,MAAM,EAAE,gBAAgB;IAe9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAUtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAMb,wBAAwB;YAyDxB,SAAS;IAsBvB,OAAO,CAAC,+BAA+B;YAuBzB,cAAc;CA4G7B"}
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { homedir } from 'node:os';
|
|
6
|
+
const DAEMON_STATE_FILE = join(homedir(), '.antseed', 'daemon.state.json');
|
|
7
|
+
const DEBUG = () => ['1', 'true', 'yes', 'on'].includes((process.env['ANTSEED_DEBUG'] ?? '').trim().toLowerCase());
|
|
8
|
+
function log(...args) {
|
|
9
|
+
if (DEBUG())
|
|
10
|
+
console.log('[proxy]', ...args);
|
|
11
|
+
}
|
|
12
|
+
function parseTokenCount(value) {
|
|
13
|
+
const parsed = Number(value);
|
|
14
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
return Math.floor(parsed);
|
|
18
|
+
}
|
|
19
|
+
function parseUsageObject(value) {
|
|
20
|
+
if (!value || typeof value !== 'object') {
|
|
21
|
+
return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
22
|
+
}
|
|
23
|
+
const usage = value;
|
|
24
|
+
const total = parseTokenCount(usage.totalTokens ?? usage.total_tokens ?? usage.total_token_count);
|
|
25
|
+
let input = parseTokenCount(usage.inputTokens
|
|
26
|
+
?? usage.input_tokens
|
|
27
|
+
?? usage.promptTokens
|
|
28
|
+
?? usage.prompt_tokens
|
|
29
|
+
?? usage.input_token_count
|
|
30
|
+
?? usage.prompt_token_count
|
|
31
|
+
?? usage.cache_creation_input_tokens
|
|
32
|
+
?? usage.cache_read_input_tokens);
|
|
33
|
+
let output = parseTokenCount(usage.outputTokens
|
|
34
|
+
?? usage.output_tokens
|
|
35
|
+
?? usage.completionTokens
|
|
36
|
+
?? usage.completion_tokens
|
|
37
|
+
?? usage.output_token_count
|
|
38
|
+
?? usage.completion_token_count);
|
|
39
|
+
if (total > 0) {
|
|
40
|
+
if (input === 0 && output === 0) {
|
|
41
|
+
output = total;
|
|
42
|
+
}
|
|
43
|
+
else if (output === 0 && input > 0 && total >= input) {
|
|
44
|
+
output = total - input;
|
|
45
|
+
}
|
|
46
|
+
else if (input === 0 && output > 0 && total >= output) {
|
|
47
|
+
input = total - output;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
inputTokens: input,
|
|
52
|
+
outputTokens: output,
|
|
53
|
+
totalTokens: input + output,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function estimateTokensFromBytes(inputBytes, outputBytes) {
|
|
57
|
+
const inputTokens = Math.max(1, Math.round(Math.max(0, inputBytes) / 4));
|
|
58
|
+
const outputTokens = Math.max(1, Math.round(Math.max(0, outputBytes) / 4));
|
|
59
|
+
return {
|
|
60
|
+
inputTokens,
|
|
61
|
+
outputTokens,
|
|
62
|
+
totalTokens: inputTokens + outputTokens,
|
|
63
|
+
source: 'estimated',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function parseSseUsage(body) {
|
|
67
|
+
const text = new TextDecoder().decode(body);
|
|
68
|
+
const lines = text.split('\n');
|
|
69
|
+
let inputTokens = 0;
|
|
70
|
+
let outputTokens = 0;
|
|
71
|
+
let totalTokens = 0;
|
|
72
|
+
for (const line of lines) {
|
|
73
|
+
const trimmed = line.trim();
|
|
74
|
+
if (!trimmed.startsWith('data:'))
|
|
75
|
+
continue;
|
|
76
|
+
const payload = trimmed.slice(5).trim();
|
|
77
|
+
if (payload.length === 0 || payload === '[DONE]')
|
|
78
|
+
continue;
|
|
79
|
+
let parsed;
|
|
80
|
+
try {
|
|
81
|
+
parsed = JSON.parse(payload);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const directUsage = parseUsageObject(parsed.usage);
|
|
87
|
+
if (directUsage.totalTokens > 0) {
|
|
88
|
+
inputTokens = Math.max(inputTokens, directUsage.inputTokens);
|
|
89
|
+
outputTokens = Math.max(outputTokens, directUsage.outputTokens);
|
|
90
|
+
totalTokens = Math.max(totalTokens, directUsage.totalTokens);
|
|
91
|
+
}
|
|
92
|
+
const message = parsed.message;
|
|
93
|
+
const messageUsage = parseUsageObject(message && typeof message === 'object' ? message.usage : undefined);
|
|
94
|
+
if (messageUsage.totalTokens > 0) {
|
|
95
|
+
inputTokens = Math.max(inputTokens, messageUsage.inputTokens);
|
|
96
|
+
outputTokens = Math.max(outputTokens, messageUsage.outputTokens);
|
|
97
|
+
totalTokens = Math.max(totalTokens, messageUsage.totalTokens);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (totalTokens <= 0) {
|
|
101
|
+
totalTokens = inputTokens + outputTokens;
|
|
102
|
+
}
|
|
103
|
+
return { inputTokens, outputTokens, totalTokens };
|
|
104
|
+
}
|
|
105
|
+
function parseJsonUsage(body) {
|
|
106
|
+
try {
|
|
107
|
+
const parsed = JSON.parse(new TextDecoder().decode(body));
|
|
108
|
+
const direct = parseUsageObject(parsed.usage);
|
|
109
|
+
if (direct.totalTokens > 0) {
|
|
110
|
+
return direct;
|
|
111
|
+
}
|
|
112
|
+
const message = parsed.message;
|
|
113
|
+
if (message && typeof message === 'object') {
|
|
114
|
+
const nested = parseUsageObject(message.usage);
|
|
115
|
+
if (nested.totalTokens > 0) {
|
|
116
|
+
return nested;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const result = parsed.result;
|
|
120
|
+
if (result && typeof result === 'object') {
|
|
121
|
+
const nested = parseUsageObject(result.usage);
|
|
122
|
+
if (nested.totalTokens > 0) {
|
|
123
|
+
return nested;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function pickProviderForPeer(peer, request) {
|
|
133
|
+
const explicit = request.headers['x-antseed-provider']?.trim();
|
|
134
|
+
if (explicit && explicit.length > 0) {
|
|
135
|
+
return explicit.toLowerCase();
|
|
136
|
+
}
|
|
137
|
+
if (request.path.startsWith('/v1/messages') && peer.providers.includes('anthropic')) {
|
|
138
|
+
return 'anthropic';
|
|
139
|
+
}
|
|
140
|
+
const first = peer.providers[0]?.trim();
|
|
141
|
+
if (first && first.length > 0) {
|
|
142
|
+
return first.toLowerCase();
|
|
143
|
+
}
|
|
144
|
+
return 'unknown';
|
|
145
|
+
}
|
|
146
|
+
function extractRequestedModel(request) {
|
|
147
|
+
const contentType = (request.headers['content-type'] ?? request.headers['Content-Type'] ?? '').toLowerCase();
|
|
148
|
+
if (!contentType.includes('application/json')) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const parsed = JSON.parse(new TextDecoder().decode(request.body));
|
|
153
|
+
const model = parsed.model;
|
|
154
|
+
if (typeof model === 'string' && model.trim().length > 0) {
|
|
155
|
+
return model.trim();
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function resolvePeerPricing(peer, provider, model) {
|
|
164
|
+
const providerPricing = peer.providerPricing?.[provider];
|
|
165
|
+
if (providerPricing) {
|
|
166
|
+
if (model && providerPricing.models?.[model]) {
|
|
167
|
+
return {
|
|
168
|
+
inputUsdPerMillion: Number.isFinite(providerPricing.models[model].inputUsdPerMillion)
|
|
169
|
+
? providerPricing.models[model].inputUsdPerMillion
|
|
170
|
+
: null,
|
|
171
|
+
outputUsdPerMillion: Number.isFinite(providerPricing.models[model].outputUsdPerMillion)
|
|
172
|
+
? providerPricing.models[model].outputUsdPerMillion
|
|
173
|
+
: null,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
inputUsdPerMillion: Number.isFinite(providerPricing.defaults.inputUsdPerMillion)
|
|
178
|
+
? providerPricing.defaults.inputUsdPerMillion
|
|
179
|
+
: null,
|
|
180
|
+
outputUsdPerMillion: Number.isFinite(providerPricing.defaults.outputUsdPerMillion)
|
|
181
|
+
? providerPricing.defaults.outputUsdPerMillion
|
|
182
|
+
: null,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const input = peer.defaultInputUsdPerMillion;
|
|
186
|
+
const output = peer.defaultOutputUsdPerMillion;
|
|
187
|
+
return {
|
|
188
|
+
inputUsdPerMillion: Number.isFinite(input) ? input : null,
|
|
189
|
+
outputUsdPerMillion: Number.isFinite(output) ? output : null,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function computeResponseTelemetry(request, responseHeaders, responseBody, selectedPeer) {
|
|
193
|
+
const provider = pickProviderForPeer(selectedPeer, request);
|
|
194
|
+
const model = extractRequestedModel(request);
|
|
195
|
+
const pricing = resolvePeerPricing(selectedPeer, provider, model);
|
|
196
|
+
const contentType = (responseHeaders['content-type'] ?? '').toLowerCase();
|
|
197
|
+
const usageFromBody = contentType.includes('text/event-stream')
|
|
198
|
+
? parseSseUsage(responseBody)
|
|
199
|
+
: parseJsonUsage(responseBody);
|
|
200
|
+
let usage;
|
|
201
|
+
if (usageFromBody.totalTokens > 0) {
|
|
202
|
+
usage = {
|
|
203
|
+
inputTokens: usageFromBody.inputTokens,
|
|
204
|
+
outputTokens: usageFromBody.outputTokens,
|
|
205
|
+
totalTokens: usageFromBody.totalTokens,
|
|
206
|
+
source: 'usage',
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
usage = estimateTokensFromBytes(request.body.length, responseBody.length);
|
|
211
|
+
}
|
|
212
|
+
let estimatedCostUsd = null;
|
|
213
|
+
if (pricing.inputUsdPerMillion !== null &&
|
|
214
|
+
pricing.outputUsdPerMillion !== null &&
|
|
215
|
+
Number.isFinite(pricing.inputUsdPerMillion) &&
|
|
216
|
+
Number.isFinite(pricing.outputUsdPerMillion)) {
|
|
217
|
+
estimatedCostUsd =
|
|
218
|
+
(usage.inputTokens * pricing.inputUsdPerMillion + usage.outputTokens * pricing.outputUsdPerMillion) / 1_000_000;
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
usage,
|
|
222
|
+
pricing: {
|
|
223
|
+
provider,
|
|
224
|
+
model,
|
|
225
|
+
inputUsdPerMillion: pricing.inputUsdPerMillion,
|
|
226
|
+
outputUsdPerMillion: pricing.outputUsdPerMillion,
|
|
227
|
+
},
|
|
228
|
+
estimatedCostUsd,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
function attachAntseedTelemetryHeaders(upstreamHeaders, selectedPeer, telemetry, requestId, latencyMs) {
|
|
232
|
+
const headers = { ...upstreamHeaders };
|
|
233
|
+
headers['x-antseed-request-id'] = requestId;
|
|
234
|
+
headers['x-antseed-latency-ms'] = String(Math.max(0, Math.floor(latencyMs)));
|
|
235
|
+
headers['x-antseed-peer-id'] = selectedPeer.peerId;
|
|
236
|
+
if (selectedPeer.publicAddress) {
|
|
237
|
+
headers['x-antseed-peer-address'] = selectedPeer.publicAddress;
|
|
238
|
+
}
|
|
239
|
+
if (selectedPeer.providers.length > 0) {
|
|
240
|
+
headers['x-antseed-peer-providers'] = selectedPeer.providers.join(',');
|
|
241
|
+
}
|
|
242
|
+
if (typeof selectedPeer.reputationScore === 'number' && Number.isFinite(selectedPeer.reputationScore)) {
|
|
243
|
+
headers['x-antseed-peer-reputation'] = String(selectedPeer.reputationScore);
|
|
244
|
+
}
|
|
245
|
+
if (typeof selectedPeer.trustScore === 'number' && Number.isFinite(selectedPeer.trustScore)) {
|
|
246
|
+
headers['x-antseed-peer-trust-score'] = String(selectedPeer.trustScore);
|
|
247
|
+
}
|
|
248
|
+
if (typeof selectedPeer.currentLoad === 'number' && Number.isFinite(selectedPeer.currentLoad)) {
|
|
249
|
+
headers['x-antseed-peer-current-load'] = String(selectedPeer.currentLoad);
|
|
250
|
+
}
|
|
251
|
+
if (typeof selectedPeer.maxConcurrency === 'number' && Number.isFinite(selectedPeer.maxConcurrency)) {
|
|
252
|
+
headers['x-antseed-peer-max-concurrency'] = String(selectedPeer.maxConcurrency);
|
|
253
|
+
}
|
|
254
|
+
headers['x-antseed-provider'] = telemetry.pricing.provider;
|
|
255
|
+
if (telemetry.pricing.model) {
|
|
256
|
+
headers['x-antseed-model'] = telemetry.pricing.model;
|
|
257
|
+
}
|
|
258
|
+
if (telemetry.pricing.inputUsdPerMillion !== null) {
|
|
259
|
+
headers['x-antseed-input-usd-per-million'] = String(telemetry.pricing.inputUsdPerMillion);
|
|
260
|
+
}
|
|
261
|
+
if (telemetry.pricing.outputUsdPerMillion !== null) {
|
|
262
|
+
headers['x-antseed-output-usd-per-million'] = String(telemetry.pricing.outputUsdPerMillion);
|
|
263
|
+
}
|
|
264
|
+
headers['x-antseed-token-source'] = telemetry.usage.source;
|
|
265
|
+
headers['x-antseed-input-tokens'] = String(telemetry.usage.inputTokens);
|
|
266
|
+
headers['x-antseed-output-tokens'] = String(telemetry.usage.outputTokens);
|
|
267
|
+
headers['x-antseed-total-tokens'] = String(telemetry.usage.totalTokens);
|
|
268
|
+
if (telemetry.estimatedCostUsd !== null && Number.isFinite(telemetry.estimatedCostUsd)) {
|
|
269
|
+
headers['x-antseed-estimated-cost-usd'] = telemetry.estimatedCostUsd.toFixed(6);
|
|
270
|
+
}
|
|
271
|
+
return headers;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Local HTTP proxy that forwards requests to P2P sellers.
|
|
275
|
+
*
|
|
276
|
+
* Tools like Claude CLI set ANTHROPIC_BASE_URL=http://localhost:8377
|
|
277
|
+
* and the proxy transparently routes their API calls through the
|
|
278
|
+
* Antseed P2P network.
|
|
279
|
+
*/
|
|
280
|
+
export class BuyerProxy {
|
|
281
|
+
_server;
|
|
282
|
+
_node;
|
|
283
|
+
_port;
|
|
284
|
+
_peerCacheTtlMs;
|
|
285
|
+
_cachedPeers = [];
|
|
286
|
+
_cacheTimestamp = 0;
|
|
287
|
+
constructor(config) {
|
|
288
|
+
this._node = config.node;
|
|
289
|
+
this._port = config.port;
|
|
290
|
+
this._peerCacheTtlMs = config.peerCacheTtlMs ?? 30_000;
|
|
291
|
+
this._server = createServer((req, res) => {
|
|
292
|
+
this._handleRequest(req, res).catch((err) => {
|
|
293
|
+
log('Unhandled error:', err);
|
|
294
|
+
if (!res.headersSent) {
|
|
295
|
+
res.writeHead(502, { 'content-type': 'text/plain' });
|
|
296
|
+
}
|
|
297
|
+
res.end(`Proxy error: ${err instanceof Error ? err.message : String(err)}`);
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
async start() {
|
|
302
|
+
return new Promise((resolve, reject) => {
|
|
303
|
+
this._server.once('error', reject);
|
|
304
|
+
this._server.listen(this._port, '127.0.0.1', () => {
|
|
305
|
+
this._server.removeListener('error', reject);
|
|
306
|
+
resolve();
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
async stop() {
|
|
311
|
+
return new Promise((resolve) => {
|
|
312
|
+
this._server.close(() => resolve());
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
async _readLocalSeederFallback() {
|
|
316
|
+
try {
|
|
317
|
+
const raw = await readFile(DAEMON_STATE_FILE, 'utf-8');
|
|
318
|
+
const parsed = JSON.parse(raw);
|
|
319
|
+
if (parsed.state !== 'seeding')
|
|
320
|
+
return null;
|
|
321
|
+
if (typeof parsed.peerId !== 'string' || !/^[0-9a-f]{64}$/i.test(parsed.peerId))
|
|
322
|
+
return null;
|
|
323
|
+
const signalingPort = Number(parsed.signalingPort);
|
|
324
|
+
if (!Number.isFinite(signalingPort) || signalingPort <= 0 || signalingPort > 65535)
|
|
325
|
+
return null;
|
|
326
|
+
const pid = Number(parsed.pid);
|
|
327
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
328
|
+
try {
|
|
329
|
+
process.kill(Math.floor(pid), 0);
|
|
330
|
+
}
|
|
331
|
+
catch {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const providers = typeof parsed.provider === 'string' && parsed.provider.trim().length > 0
|
|
336
|
+
? [parsed.provider.trim()]
|
|
337
|
+
: [];
|
|
338
|
+
const defaultInputUsdPerMillion = Number(parsed.defaultInputUsdPerMillion);
|
|
339
|
+
const defaultOutputUsdPerMillion = Number(parsed.defaultOutputUsdPerMillion);
|
|
340
|
+
const providerPricing = parsed.providerPricing && typeof parsed.providerPricing === 'object'
|
|
341
|
+
? parsed.providerPricing
|
|
342
|
+
: undefined;
|
|
343
|
+
const peerId = parsed.peerId.toLowerCase();
|
|
344
|
+
if (this._node.peerId && this._node.peerId.toLowerCase() === peerId) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
peerId: peerId,
|
|
349
|
+
lastSeen: Date.now(),
|
|
350
|
+
publicAddress: `127.0.0.1:${Math.floor(signalingPort)}`,
|
|
351
|
+
providers,
|
|
352
|
+
defaultInputUsdPerMillion: Number.isFinite(defaultInputUsdPerMillion) ? defaultInputUsdPerMillion : 0,
|
|
353
|
+
defaultOutputUsdPerMillion: Number.isFinite(defaultOutputUsdPerMillion) ? defaultOutputUsdPerMillion : 0,
|
|
354
|
+
...(providerPricing ? { providerPricing } : {}),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
catch {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
async _getPeers() {
|
|
362
|
+
const now = Date.now();
|
|
363
|
+
if (this._cachedPeers.length > 0 && now - this._cacheTimestamp < this._peerCacheTtlMs) {
|
|
364
|
+
return this._cachedPeers;
|
|
365
|
+
}
|
|
366
|
+
log('Discovering peers via DHT...');
|
|
367
|
+
let peers = await this._node.discoverPeers();
|
|
368
|
+
const localSeeder = await this._readLocalSeederFallback();
|
|
369
|
+
if (localSeeder && !peers.some((peer) => peer.peerId === localSeeder.peerId)) {
|
|
370
|
+
peers = [localSeeder, ...peers];
|
|
371
|
+
log(`Added local seeder fallback ${localSeeder.peerId.slice(0, 12)}... @ ${localSeeder.publicAddress}`);
|
|
372
|
+
}
|
|
373
|
+
if (peers.length > 0) {
|
|
374
|
+
this._cachedPeers = peers;
|
|
375
|
+
this._cacheTimestamp = now;
|
|
376
|
+
log(`Found ${peers.length} peer(s)`);
|
|
377
|
+
}
|
|
378
|
+
return peers;
|
|
379
|
+
}
|
|
380
|
+
_formatPeerSelectionDiagnostics(peers) {
|
|
381
|
+
if (peers.length === 0) {
|
|
382
|
+
return 'No peers discovered.';
|
|
383
|
+
}
|
|
384
|
+
const summarize = (peer) => {
|
|
385
|
+
const providers = peer.providers
|
|
386
|
+
.map((provider) => provider.trim())
|
|
387
|
+
.filter((provider) => provider.length > 0);
|
|
388
|
+
const trust = Number.isFinite(peer.trustScore) ? String(peer.trustScore) : 'n/a';
|
|
389
|
+
const rep = Number.isFinite(peer.reputationScore) ? String(peer.reputationScore) : 'n/a';
|
|
390
|
+
const onChain = Number.isFinite(peer.onChainReputation) ? String(peer.onChainReputation) : 'n/a';
|
|
391
|
+
const input = Number.isFinite(peer.defaultInputUsdPerMillion) ? String(peer.defaultInputUsdPerMillion) : 'n/a';
|
|
392
|
+
const output = Number.isFinite(peer.defaultOutputUsdPerMillion) ? String(peer.defaultOutputUsdPerMillion) : 'n/a';
|
|
393
|
+
return `${peer.peerId.slice(0, 8)} providers=[${providers.join(',') || 'none'}] trust=${trust} rep=${rep} onchain=${onChain} in=${input} out=${output}`;
|
|
394
|
+
};
|
|
395
|
+
const samples = peers.slice(0, 5).map((peer) => summarize(peer)).join(' | ');
|
|
396
|
+
const suffix = peers.length > 5 ? ` (+${peers.length - 5} more)` : '';
|
|
397
|
+
return `Discovered ${peers.length} peer(s): ${samples}${suffix}`;
|
|
398
|
+
}
|
|
399
|
+
async _handleRequest(req, res) {
|
|
400
|
+
const method = req.method ?? 'GET';
|
|
401
|
+
const path = req.url ?? '/';
|
|
402
|
+
log(`${method} ${path}`);
|
|
403
|
+
// Collect request body
|
|
404
|
+
const chunks = [];
|
|
405
|
+
for await (const chunk of req) {
|
|
406
|
+
chunks.push(chunk);
|
|
407
|
+
}
|
|
408
|
+
const body = Buffer.concat(chunks);
|
|
409
|
+
// Build serialized request
|
|
410
|
+
const headers = {};
|
|
411
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
412
|
+
if (typeof value === 'string') {
|
|
413
|
+
headers[key] = value;
|
|
414
|
+
}
|
|
415
|
+
else if (Array.isArray(value)) {
|
|
416
|
+
headers[key] = value.join(', ');
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
// Remove host header (points to localhost, not the seller)
|
|
420
|
+
delete headers['host'];
|
|
421
|
+
const serializedReq = {
|
|
422
|
+
requestId: randomUUID(),
|
|
423
|
+
method,
|
|
424
|
+
path,
|
|
425
|
+
headers,
|
|
426
|
+
body: new Uint8Array(body),
|
|
427
|
+
};
|
|
428
|
+
// Discover peers
|
|
429
|
+
const peers = await this._getPeers();
|
|
430
|
+
if (peers.length === 0) {
|
|
431
|
+
log('No sellers available');
|
|
432
|
+
res.writeHead(502, { 'content-type': 'text/plain' });
|
|
433
|
+
res.end('No sellers available on the network. Is a seeder running?');
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
// Use router to select peer
|
|
437
|
+
const router = this._node.router;
|
|
438
|
+
const selectedPeer = router
|
|
439
|
+
? router.selectPeer(serializedReq, peers)
|
|
440
|
+
: peers[0] ?? null;
|
|
441
|
+
if (!selectedPeer) {
|
|
442
|
+
const diagnostics = this._formatPeerSelectionDiagnostics(peers);
|
|
443
|
+
log('Router could not select a peer.', diagnostics);
|
|
444
|
+
res.writeHead(502, { 'content-type': 'text/plain' });
|
|
445
|
+
res.end(`Router could not select a suitable peer. ${diagnostics}`);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
log(`Routing to peer ${selectedPeer.peerId.slice(0, 12)}...`);
|
|
449
|
+
// Forward through P2P
|
|
450
|
+
const startTime = Date.now();
|
|
451
|
+
try {
|
|
452
|
+
const response = await this._node.sendRequest(selectedPeer, serializedReq);
|
|
453
|
+
const latencyMs = Date.now() - startTime;
|
|
454
|
+
log(`Response: ${response.statusCode} (${latencyMs}ms, ${response.body.length} bytes)`);
|
|
455
|
+
const telemetry = computeResponseTelemetry(serializedReq, response.headers, response.body, selectedPeer);
|
|
456
|
+
const responseHeaders = attachAntseedTelemetryHeaders(response.headers, selectedPeer, telemetry, serializedReq.requestId, latencyMs);
|
|
457
|
+
// Report result to router for learning
|
|
458
|
+
if (router) {
|
|
459
|
+
router.onResult(selectedPeer, {
|
|
460
|
+
success: response.statusCode < 500,
|
|
461
|
+
latencyMs,
|
|
462
|
+
tokens: telemetry.usage.totalTokens,
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
// Forward response headers and body to the HTTP client
|
|
466
|
+
res.writeHead(response.statusCode, responseHeaders);
|
|
467
|
+
res.end(Buffer.from(response.body));
|
|
468
|
+
}
|
|
469
|
+
catch (err) {
|
|
470
|
+
const latencyMs = Date.now() - startTime;
|
|
471
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
472
|
+
log(`Request failed after ${latencyMs}ms: ${message}`);
|
|
473
|
+
if (router) {
|
|
474
|
+
router.onResult(selectedPeer, {
|
|
475
|
+
success: false,
|
|
476
|
+
latencyMs,
|
|
477
|
+
tokens: 0,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
// Invalidate peer cache on connection errors so next request re-discovers
|
|
481
|
+
this._cachedPeers = [];
|
|
482
|
+
this._cacheTimestamp = 0;
|
|
483
|
+
res.writeHead(502, { 'content-type': 'text/plain' });
|
|
484
|
+
res.end(`P2P request failed: ${message}`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
//# sourceMappingURL=buyer-proxy.js.map
|