@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.
Files changed (179) hide show
  1. package/.env.example +15 -0
  2. package/README.md +169 -0
  3. package/dist/cli/commands/balance.d.ts +3 -0
  4. package/dist/cli/commands/balance.d.ts.map +1 -0
  5. package/dist/cli/commands/balance.js +64 -0
  6. package/dist/cli/commands/balance.js.map +1 -0
  7. package/dist/cli/commands/browse.d.ts +7 -0
  8. package/dist/cli/commands/browse.d.ts.map +1 -0
  9. package/dist/cli/commands/browse.js +100 -0
  10. package/dist/cli/commands/browse.js.map +1 -0
  11. package/dist/cli/commands/config.d.ts +20 -0
  12. package/dist/cli/commands/config.d.ts.map +1 -0
  13. package/dist/cli/commands/config.js +239 -0
  14. package/dist/cli/commands/config.js.map +1 -0
  15. package/dist/cli/commands/connect.d.ts +14 -0
  16. package/dist/cli/commands/connect.d.ts.map +1 -0
  17. package/dist/cli/commands/connect.js +298 -0
  18. package/dist/cli/commands/connect.js.map +1 -0
  19. package/dist/cli/commands/connect.test.d.ts +2 -0
  20. package/dist/cli/commands/connect.test.d.ts.map +1 -0
  21. package/dist/cli/commands/connect.test.js +54 -0
  22. package/dist/cli/commands/connect.test.js.map +1 -0
  23. package/dist/cli/commands/dashboard.d.ts +6 -0
  24. package/dist/cli/commands/dashboard.d.ts.map +1 -0
  25. package/dist/cli/commands/dashboard.js +48 -0
  26. package/dist/cli/commands/dashboard.js.map +1 -0
  27. package/dist/cli/commands/deposit.d.ts +3 -0
  28. package/dist/cli/commands/deposit.d.ts.map +1 -0
  29. package/dist/cli/commands/deposit.js +48 -0
  30. package/dist/cli/commands/deposit.js.map +1 -0
  31. package/dist/cli/commands/dev.d.ts +3 -0
  32. package/dist/cli/commands/dev.d.ts.map +1 -0
  33. package/dist/cli/commands/dev.js +94 -0
  34. package/dist/cli/commands/dev.js.map +1 -0
  35. package/dist/cli/commands/init.d.ts +3 -0
  36. package/dist/cli/commands/init.d.ts.map +1 -0
  37. package/dist/cli/commands/init.js +91 -0
  38. package/dist/cli/commands/init.js.map +1 -0
  39. package/dist/cli/commands/plugin-create.d.ts +11 -0
  40. package/dist/cli/commands/plugin-create.d.ts.map +1 -0
  41. package/dist/cli/commands/plugin-create.js +201 -0
  42. package/dist/cli/commands/plugin-create.js.map +1 -0
  43. package/dist/cli/commands/plugin-create.test.d.ts +2 -0
  44. package/dist/cli/commands/plugin-create.test.d.ts.map +1 -0
  45. package/dist/cli/commands/plugin-create.test.js +53 -0
  46. package/dist/cli/commands/plugin-create.test.js.map +1 -0
  47. package/dist/cli/commands/plugin.d.ts +3 -0
  48. package/dist/cli/commands/plugin.d.ts.map +1 -0
  49. package/dist/cli/commands/plugin.js +279 -0
  50. package/dist/cli/commands/plugin.js.map +1 -0
  51. package/dist/cli/commands/plugin.test.d.ts +2 -0
  52. package/dist/cli/commands/plugin.test.d.ts.map +1 -0
  53. package/dist/cli/commands/plugin.test.js +53 -0
  54. package/dist/cli/commands/plugin.test.js.map +1 -0
  55. package/dist/cli/commands/profile.d.ts +10 -0
  56. package/dist/cli/commands/profile.d.ts.map +1 -0
  57. package/dist/cli/commands/profile.js +89 -0
  58. package/dist/cli/commands/profile.js.map +1 -0
  59. package/dist/cli/commands/seed.d.ts +11 -0
  60. package/dist/cli/commands/seed.d.ts.map +1 -0
  61. package/dist/cli/commands/seed.js +397 -0
  62. package/dist/cli/commands/seed.js.map +1 -0
  63. package/dist/cli/commands/seed.test.d.ts +2 -0
  64. package/dist/cli/commands/seed.test.d.ts.map +1 -0
  65. package/dist/cli/commands/seed.test.js +57 -0
  66. package/dist/cli/commands/seed.test.js.map +1 -0
  67. package/dist/cli/commands/status.d.ts +8 -0
  68. package/dist/cli/commands/status.d.ts.map +1 -0
  69. package/dist/cli/commands/status.js +55 -0
  70. package/dist/cli/commands/status.js.map +1 -0
  71. package/dist/cli/commands/types.d.ts +14 -0
  72. package/dist/cli/commands/types.d.ts.map +1 -0
  73. package/dist/cli/commands/types.js +41 -0
  74. package/dist/cli/commands/types.js.map +1 -0
  75. package/dist/cli/commands/withdraw.d.ts +3 -0
  76. package/dist/cli/commands/withdraw.d.ts.map +1 -0
  77. package/dist/cli/commands/withdraw.js +48 -0
  78. package/dist/cli/commands/withdraw.js.map +1 -0
  79. package/dist/cli/formatters.d.ts +29 -0
  80. package/dist/cli/formatters.d.ts.map +1 -0
  81. package/dist/cli/formatters.js +67 -0
  82. package/dist/cli/formatters.js.map +1 -0
  83. package/dist/cli/index.d.ts +3 -0
  84. package/dist/cli/index.d.ts.map +1 -0
  85. package/dist/cli/index.js +41 -0
  86. package/dist/cli/index.js.map +1 -0
  87. package/dist/cli/shutdown.d.ts +11 -0
  88. package/dist/cli/shutdown.d.ts.map +1 -0
  89. package/dist/cli/shutdown.js +34 -0
  90. package/dist/cli/shutdown.js.map +1 -0
  91. package/dist/config/defaults.d.ts +6 -0
  92. package/dist/config/defaults.d.ts.map +1 -0
  93. package/dist/config/defaults.js +48 -0
  94. package/dist/config/defaults.js.map +1 -0
  95. package/dist/config/effective.d.ts +26 -0
  96. package/dist/config/effective.d.ts.map +1 -0
  97. package/dist/config/effective.js +84 -0
  98. package/dist/config/effective.js.map +1 -0
  99. package/dist/config/effective.test.d.ts +2 -0
  100. package/dist/config/effective.test.d.ts.map +1 -0
  101. package/dist/config/effective.test.js +65 -0
  102. package/dist/config/effective.test.js.map +1 -0
  103. package/dist/config/loader.d.ts +12 -0
  104. package/dist/config/loader.d.ts.map +1 -0
  105. package/dist/config/loader.js +212 -0
  106. package/dist/config/loader.js.map +1 -0
  107. package/dist/config/loader.test.d.ts +2 -0
  108. package/dist/config/loader.test.d.ts.map +1 -0
  109. package/dist/config/loader.test.js +77 -0
  110. package/dist/config/loader.test.js.map +1 -0
  111. package/dist/config/types.d.ts +133 -0
  112. package/dist/config/types.d.ts.map +1 -0
  113. package/dist/config/types.js +2 -0
  114. package/dist/config/types.js.map +1 -0
  115. package/dist/config/validation.d.ts +10 -0
  116. package/dist/config/validation.d.ts.map +1 -0
  117. package/dist/config/validation.js +50 -0
  118. package/dist/config/validation.js.map +1 -0
  119. package/dist/env/load-env.d.ts +6 -0
  120. package/dist/env/load-env.d.ts.map +1 -0
  121. package/dist/env/load-env.js +18 -0
  122. package/dist/env/load-env.js.map +1 -0
  123. package/dist/plugins/loader.d.ts +7 -0
  124. package/dist/plugins/loader.d.ts.map +1 -0
  125. package/dist/plugins/loader.js +70 -0
  126. package/dist/plugins/loader.js.map +1 -0
  127. package/dist/plugins/manager.d.ts +11 -0
  128. package/dist/plugins/manager.d.ts.map +1 -0
  129. package/dist/plugins/manager.js +52 -0
  130. package/dist/plugins/manager.js.map +1 -0
  131. package/dist/plugins/registry.d.ts +8 -0
  132. package/dist/plugins/registry.d.ts.map +1 -0
  133. package/dist/plugins/registry.js +39 -0
  134. package/dist/plugins/registry.js.map +1 -0
  135. package/dist/proxy/buyer-proxy.d.ts +30 -0
  136. package/dist/proxy/buyer-proxy.d.ts.map +1 -0
  137. package/dist/proxy/buyer-proxy.js +488 -0
  138. package/dist/proxy/buyer-proxy.js.map +1 -0
  139. package/dist/status/node-status.d.ts +22 -0
  140. package/dist/status/node-status.d.ts.map +1 -0
  141. package/dist/status/node-status.js +83 -0
  142. package/dist/status/node-status.js.map +1 -0
  143. package/package.json +39 -0
  144. package/src/cli/commands/balance.ts +77 -0
  145. package/src/cli/commands/browse.ts +113 -0
  146. package/src/cli/commands/config.ts +271 -0
  147. package/src/cli/commands/connect.test.ts +69 -0
  148. package/src/cli/commands/connect.ts +342 -0
  149. package/src/cli/commands/dashboard.ts +59 -0
  150. package/src/cli/commands/deposit.ts +61 -0
  151. package/src/cli/commands/dev.ts +107 -0
  152. package/src/cli/commands/init.ts +99 -0
  153. package/src/cli/commands/plugin-create.test.ts +60 -0
  154. package/src/cli/commands/plugin-create.ts +230 -0
  155. package/src/cli/commands/plugin.test.ts +55 -0
  156. package/src/cli/commands/plugin.ts +295 -0
  157. package/src/cli/commands/profile.ts +95 -0
  158. package/src/cli/commands/seed.test.ts +70 -0
  159. package/src/cli/commands/seed.ts +447 -0
  160. package/src/cli/commands/status.ts +73 -0
  161. package/src/cli/commands/types.ts +56 -0
  162. package/src/cli/commands/withdraw.ts +61 -0
  163. package/src/cli/formatters.ts +64 -0
  164. package/src/cli/index.ts +46 -0
  165. package/src/cli/shutdown.ts +38 -0
  166. package/src/config/defaults.ts +49 -0
  167. package/src/config/effective.test.ts +80 -0
  168. package/src/config/effective.ts +119 -0
  169. package/src/config/loader.test.ts +95 -0
  170. package/src/config/loader.ts +251 -0
  171. package/src/config/types.ts +139 -0
  172. package/src/config/validation.ts +78 -0
  173. package/src/env/load-env.ts +20 -0
  174. package/src/plugins/loader.ts +96 -0
  175. package/src/plugins/manager.ts +66 -0
  176. package/src/plugins/registry.ts +45 -0
  177. package/src/proxy/buyer-proxy.ts +604 -0
  178. package/src/status/node-status.ts +105 -0
  179. 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,8 @@
1
+ export interface TrustedPlugin {
2
+ name: string;
3
+ type: 'provider' | 'router';
4
+ description: string;
5
+ package: string;
6
+ }
7
+ export declare const TRUSTED_PLUGINS: TrustedPlugin[];
8
+ //# sourceMappingURL=registry.d.ts.map
@@ -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