@evomap/evolver-proxy 2.0.0-beta.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 (82) hide show
  1. package/README.md +49 -0
  2. package/dist/bin/envFile.d.ts +10 -0
  3. package/dist/bin/envFile.js +68 -0
  4. package/dist/bin/evolver-llm-proxy.d.ts +2 -0
  5. package/dist/bin/evolver-llm-proxy.js +111 -0
  6. package/dist/bin/evolver-proxy.d.ts +83 -0
  7. package/dist/bin/evolver-proxy.js +511 -0
  8. package/dist/bin/proxySettings.d.ts +15 -0
  9. package/dist/bin/proxySettings.js +84 -0
  10. package/dist/bin/proxyStorePath.d.ts +1 -0
  11. package/dist/bin/proxyStorePath.js +16 -0
  12. package/dist/daemon/atpConsent.d.ts +13 -0
  13. package/dist/daemon/atpConsent.js +60 -0
  14. package/dist/daemon/ipcConfig.d.ts +1 -0
  15. package/dist/daemon/ipcConfig.js +13 -0
  16. package/dist/daemon/proxyDaemon.d.ts +191 -0
  17. package/dist/daemon/proxyDaemon.js +1015 -0
  18. package/dist/daemon/selectHub.d.ts +16 -0
  19. package/dist/daemon/selectHub.js +30 -0
  20. package/dist/index.d.ts +8 -0
  21. package/dist/index.js +8 -0
  22. package/dist/lifecycle/deployGuard.d.ts +46 -0
  23. package/dist/lifecycle/deployGuard.js +53 -0
  24. package/dist/lifecycle/legacyNodeId.d.ts +96 -0
  25. package/dist/lifecycle/legacyNodeId.js +163 -0
  26. package/dist/lifecycle/manager.d.ts +106 -0
  27. package/dist/lifecycle/manager.js +390 -0
  28. package/dist/llm/bodyCapture.d.ts +31 -0
  29. package/dist/llm/bodyCapture.js +293 -0
  30. package/dist/llm/index.d.ts +3 -0
  31. package/dist/llm/index.js +3 -0
  32. package/dist/llm/server.d.ts +35 -0
  33. package/dist/llm/server.js +359 -0
  34. package/dist/llm/traceBackfill.d.ts +53 -0
  35. package/dist/llm/traceBackfill.js +525 -0
  36. package/dist/llm/traceConfig.d.ts +7 -0
  37. package/dist/llm/traceConfig.js +44 -0
  38. package/dist/llm/traceControl.d.ts +16 -0
  39. package/dist/llm/traceControl.js +85 -0
  40. package/dist/llm/traceEnvelope.d.ts +75 -0
  41. package/dist/llm/traceEnvelope.js +286 -0
  42. package/dist/llm/traceSink.d.ts +61 -0
  43. package/dist/llm/traceSink.js +278 -0
  44. package/dist/llm/traceUploadPayload.d.ts +14 -0
  45. package/dist/llm/traceUploadPayload.js +27 -0
  46. package/dist/llm/upstream.d.ts +68 -0
  47. package/dist/llm/upstream.js +491 -0
  48. package/dist/private/adapterLoader.d.ts +46 -0
  49. package/dist/private/adapterLoader.js +62 -0
  50. package/dist/private/privateRuntimeSmokeOptions.d.ts +15 -0
  51. package/dist/private/privateRuntimeSmokeOptions.js +132 -0
  52. package/dist/router/cachePassthrough.d.ts +3 -0
  53. package/dist/router/cachePassthrough.js +13 -0
  54. package/dist/router/features.d.ts +9 -0
  55. package/dist/router/features.js +52 -0
  56. package/dist/router/index.d.ts +6 -0
  57. package/dist/router/index.js +6 -0
  58. package/dist/router/messagesRoute.d.ts +138 -0
  59. package/dist/router/messagesRoute.js +753 -0
  60. package/dist/router/modelRouter.d.ts +49 -0
  61. package/dist/router/modelRouter.js +66 -0
  62. package/dist/router/providerRoutes.d.ts +42 -0
  63. package/dist/router/providerRoutes.js +1579 -0
  64. package/dist/router/sseScan.d.ts +56 -0
  65. package/dist/router/sseScan.js +543 -0
  66. package/dist/selfUpdate/executor.d.ts +90 -0
  67. package/dist/selfUpdate/executor.js +179 -0
  68. package/dist/selfUpdate/failureCodes.d.ts +33 -0
  69. package/dist/selfUpdate/failureCodes.js +84 -0
  70. package/dist/selfUpdate/index.d.ts +5 -0
  71. package/dist/selfUpdate/index.js +5 -0
  72. package/dist/selfUpdate/lastUpdate.d.ts +43 -0
  73. package/dist/selfUpdate/lastUpdate.js +195 -0
  74. package/dist/selfUpdate/policy.d.ts +3 -0
  75. package/dist/selfUpdate/policy.js +9 -0
  76. package/dist/selfUpdate/releaseBinary.d.ts +56 -0
  77. package/dist/selfUpdate/releaseBinary.js +498 -0
  78. package/dist/selfUpdate/version.d.ts +2 -0
  79. package/dist/selfUpdate/version.js +14 -0
  80. package/dist/sync/engine.d.ts +65 -0
  81. package/dist/sync/engine.js +461 -0
  82. package/package.json +41 -0
package/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # @evomap/evolver-proxy
2
+
3
+ The local proxy daemon: it relays LLM traffic and syncs the agent mailbox with an EvoMap hub.
4
+
5
+ ## Targeting a hub
6
+
7
+ The proxy talks to a hub in one of two modes, selected by `EVOMAP_HUB_MODE`.
8
+
9
+ ### Public mode (default)
10
+
11
+ ```
12
+ EVOMAP_HUB_URL=https://your-hub node packages/evolver-proxy/dist/bin/evolver-proxy.js
13
+ ```
14
+
15
+ Public mode sends no private credential. It works against a public hub. Against a self-hosted
16
+ **private** hub it can only complete the no-auth `/a2a/hello` handshake; every authenticated
17
+ `/a2a/*` call (heartbeat, mailbox, publish) is rejected with `401 a2a_auth_required`.
18
+
19
+ When an authenticated call fails the proxy now records an actionable hint in `sync:last_error` /
20
+ `hub:auth_status` (surfaced in the daemon status snapshot), keyed off the hub error code so it does
21
+ not misdirect:
22
+
23
+ - `a2a_auth_required` (the hub demanded a credential the public adapter never sends) points at a
24
+ private hub, so the hint tells you to switch to private mode.
25
+ - any other auth rejection (the node secret was sent but refused) points at a public-hub credential
26
+ problem, so the hint tells you to re-register the node.
27
+
28
+ See issue #314.
29
+
30
+ ### Private mode (self-hosted / enterprise hub)
31
+
32
+ Private mode authenticates every `/a2a/*` call with a bearer token, so it needs two things:
33
+
34
+ 1. An **enterprise token** via `EVOMAP_ENTERPRISE_TOKEN` (also accepts `EVOMAP_PRIVATE_HUB_TOKEN`
35
+ or `PHUB_ENTERPRISE_TOKEN`).
36
+ 2. The **private adapter** package `@evomap/evolver-adapter-private`. This ships in a separate
37
+ enterprise repo and is **not** vendored in this checkout. Install/link it, or point
38
+ `EVOMAP_PRIVATE_ADAPTER_MODULE` at a local build of a module that exports `connectPrivateHub`.
39
+
40
+ ```
41
+ EVOMAP_HUB_MODE=private \
42
+ EVOMAP_HUB_URL=http://127.0.0.1:4010 \
43
+ EVOMAP_ENTERPRISE_TOKEN=<token> \
44
+ EVOMAP_PRIVATE_ADAPTER_MODULE=/abs/path/to/adapter-private \
45
+ node packages/evolver-proxy/dist/bin/evolver-proxy.js
46
+ ```
47
+
48
+ Without the adapter, private mode fails fast at startup with a message naming the missing module.
49
+ This checkout cannot target a private hub until that external adapter is available.
@@ -0,0 +1,10 @@
1
+ export interface EnvFileLoadResult {
2
+ loaded: boolean;
3
+ path?: string;
4
+ keys: string[];
5
+ error?: string;
6
+ }
7
+ export declare function expandHomePath(path: string): string;
8
+ export declare function parseEnvFile(raw: string): Record<string, string>;
9
+ export declare function loadEnvFile(path: string, env?: Record<string, string | undefined>): EnvFileLoadResult;
10
+ export declare function loadEnvFileFromEnv(env?: Record<string, string | undefined>): EnvFileLoadResult;
@@ -0,0 +1,68 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { isAbsolute, join } from 'node:path';
4
+ const ENV_FILE_KEY = 'EVOLVER_ENV_FILE';
5
+ export function expandHomePath(path) {
6
+ if (path === '~')
7
+ return homedir();
8
+ if (path.startsWith('~/'))
9
+ return join(homedir(), path.slice(2));
10
+ return path;
11
+ }
12
+ export function parseEnvFile(raw) {
13
+ const out = {};
14
+ for (const line of raw.split(/\r?\n/)) {
15
+ const trimmed = line.trim();
16
+ if (!trimmed || trimmed.startsWith('#'))
17
+ continue;
18
+ const body = trimmed.startsWith('export ') ? trimmed.slice('export '.length).trimStart() : trimmed;
19
+ const eq = body.indexOf('=');
20
+ if (eq <= 0)
21
+ continue;
22
+ const key = body.slice(0, eq).trim();
23
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
24
+ continue;
25
+ out[key] = unquoteEnvValue(body.slice(eq + 1).trim());
26
+ }
27
+ return out;
28
+ }
29
+ export function loadEnvFile(path, env = process.env) {
30
+ const resolved = isAbsolute(path) ? path : expandHomePath(path);
31
+ try {
32
+ const parsed = parseEnvFile(readFileSync(resolved, 'utf8'));
33
+ const keys = Object.keys(parsed);
34
+ for (const key of keys) {
35
+ if (key === ENV_FILE_KEY)
36
+ continue;
37
+ env[key] = parsed[key];
38
+ }
39
+ return { loaded: true, path: resolved, keys };
40
+ }
41
+ catch (err) {
42
+ return { loaded: false, path: resolved, keys: [], error: err instanceof Error ? err.message : String(err) };
43
+ }
44
+ }
45
+ export function loadEnvFileFromEnv(env = process.env) {
46
+ const path = env[ENV_FILE_KEY]?.trim();
47
+ if (!path)
48
+ return { loaded: false, keys: [] };
49
+ return loadEnvFile(path, env);
50
+ }
51
+ function unquoteEnvValue(value) {
52
+ if (value.length >= 2) {
53
+ const first = value[0];
54
+ const last = value[value.length - 1];
55
+ if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
56
+ const inner = value.slice(1, -1);
57
+ return first === '"' ? inner.replace(/\\(["\\nrt])/g, (_match, ch) => {
58
+ switch (ch) {
59
+ case 'n': return '\n';
60
+ case 'r': return '\r';
61
+ case 't': return '\t';
62
+ default: return ch;
63
+ }
64
+ }) : inner;
65
+ }
66
+ }
67
+ return value;
68
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function runLlmProxyCli(): void;
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ // Standalone LLM proxy entry: HTTP listen shell + tier router + trace capture. Deliberately independent of
3
+ // the mailbox/hub daemon (bin/evolver-proxy.ts) — capturing LLM traces must not require a hub credential.
4
+ //
5
+ // Client wiring: export ANTHROPIC_BASE_URL=http://127.0.0.1:<port>
6
+ // export ANTHROPIC_AUTH_TOKEN=<token printed below>
7
+ // Upstream creds: ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN in THIS process's env (read per request).
8
+ // Routing (opt-in):EVOMAP_ROUTER_ENABLED=1 + EVOMAP_MODEL_{CHEAP,MID,EXPENSIVE} (no IDs are ever hardcoded).
9
+ // Trace material: NDJSON under EVOLVER_LLM_TRACE_DIR (default $EVOLVER_HOME/proxy/traces or ~/.evomap/proxy/traces);
10
+ // EVOLVER_LLM_TRACE=0 disables; legacy EVOMAP_PROXY_TRACE off values are also honored.
11
+ // Hub analysis: EVOLVER_LLM_TRACE_PROFILE_ANALYSIS=1 requires EVOLVER_LLM_TRACE_HUB_PUBLIC_KEY and writes
12
+ // AES-GCM envelopes whose per-row DEK is RSA-OAEP-SHA256 wrapped for the Hub.
13
+ import { randomBytes } from 'node:crypto';
14
+ import { realpathSync } from 'node:fs';
15
+ import { fileURLToPath } from 'node:url';
16
+ import { daemon, events, mailbox } from '@evomap/evolver-core';
17
+ import { buildMessagesHandler } from '../router/messagesRoute.js';
18
+ import { buildProviderRoutes } from '../router/providerRoutes.js';
19
+ import { makeAnthropicUpstream, makeGeminiUpstream, makeOllamaUpstream, makeOpenAIUpstream, makeVertexUpstream } from '../llm/upstream.js';
20
+ import { JsonlTraceSink } from '../llm/traceSink.js';
21
+ import { traceCollectionEnabled } from '../llm/traceConfig.js';
22
+ import { LlmProxyServer } from '../llm/server.js';
23
+ import { getCurrentVersion } from '../selfUpdate/version.js';
24
+ import { resolveProxyStorePath } from './proxyStorePath.js';
25
+ async function main(uninstallUnhandledRejectionGuard = () => undefined) {
26
+ const env = process.env;
27
+ const token = env['EVOLVER_LLM_TOKEN'] ?? randomBytes(24).toString('hex');
28
+ const traceEnabled = traceCollectionEnabled(env);
29
+ const traceDir = events.tracesDir();
30
+ let traceMailboxStore = null;
31
+ if (traceEnabled && env['EVOLVER_LLM_TRACE_MAILBOX'] !== '0') {
32
+ try {
33
+ const storePath = resolveProxyStorePath(env);
34
+ traceMailboxStore = new mailbox.MailboxStore({ path: storePath });
35
+ }
36
+ catch (err) {
37
+ process.stderr.write(`[evolver-llm-proxy] trace mailbox disabled: ${err instanceof Error ? err.message : String(err)}\n`);
38
+ }
39
+ }
40
+ const sink = traceEnabled ? new JsonlTraceSink({
41
+ dir: traceDir,
42
+ producerVersion: getCurrentVersion(),
43
+ ...(traceMailboxStore ? { mailboxStore: traceMailboxStore } : {}),
44
+ }) : null;
45
+ const anthropicProxy = makeAnthropicUpstream();
46
+ const openAIProxy = makeOpenAIUpstream();
47
+ const geminiProxy = makeGeminiUpstream();
48
+ const ollamaProxy = makeOllamaUpstream();
49
+ const vertexProxy = makeVertexUpstream();
50
+ const handler = buildMessagesHandler({
51
+ anthropicProxy,
52
+ ...(sink ? { onTrace: sink.write } : {}),
53
+ });
54
+ const routes = buildProviderRoutes({
55
+ anthropicProxy,
56
+ openAIProxy,
57
+ geminiProxy,
58
+ ollamaProxy,
59
+ vertexProxy,
60
+ ...(sink ? { onTrace: sink.write } : {}),
61
+ });
62
+ const server = new LlmProxyServer({ handler, token, routes });
63
+ const { url } = await server.start();
64
+ process.stdout.write(`[evolver-llm-proxy] url=${url} token=${token} trace=${sink ? traceDir : 'off'}\n`);
65
+ let shuttingDown = false;
66
+ const shutdown = () => {
67
+ if (shuttingDown)
68
+ return;
69
+ shuttingDown = true;
70
+ let exitCode = 0;
71
+ void server.stop()
72
+ .catch((err) => {
73
+ exitCode = 1;
74
+ process.stderr.write(`[evolver-llm-proxy] shutdown failed: ${err instanceof Error ? err.message : String(err)}\n`);
75
+ })
76
+ .finally(() => {
77
+ try {
78
+ traceMailboxStore?.close();
79
+ }
80
+ catch (err) {
81
+ exitCode = 1;
82
+ process.stderr.write(`[evolver-llm-proxy] trace mailbox close failed: ${err instanceof Error ? err.message : String(err)}\n`);
83
+ }
84
+ uninstallUnhandledRejectionGuard();
85
+ process.exit(exitCode);
86
+ });
87
+ };
88
+ process.on('SIGINT', shutdown);
89
+ process.on('SIGTERM', shutdown);
90
+ }
91
+ export function runLlmProxyCli() {
92
+ const uninstallUnhandledRejectionGuard = daemon.installUnhandledRejectionWindow();
93
+ main(uninstallUnhandledRejectionGuard).catch((e) => {
94
+ uninstallUnhandledRejectionGuard();
95
+ process.stderr.write(`[evolver-llm-proxy] fatal: ${e instanceof Error ? e.message : String(e)}\n`);
96
+ process.exit(1);
97
+ });
98
+ }
99
+ if (isDirectRun(import.meta.url, process.argv[1])) {
100
+ runLlmProxyCli();
101
+ }
102
+ function isDirectRun(metaUrl, argv1) {
103
+ if (!argv1)
104
+ return false;
105
+ try {
106
+ return realpathSync(fileURLToPath(metaUrl)) === realpathSync(argv1);
107
+ }
108
+ catch {
109
+ return false;
110
+ }
111
+ }
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+ import { mailbox } from '@evomap/evolver-core';
3
+ import { type SelfUpdatePolicy } from '../selfUpdate/policy.js';
4
+ import { type ReleaseBinaryOptions } from '../selfUpdate/releaseBinary.js';
5
+ import type { AtpProxyClient, ProxyDaemonDeps, ProxyTickReport } from '../daemon/proxyDaemon.js';
6
+ import type { HelloLifecycleMode, HelloResult, HeartbeatOptions, HeartbeatResult } from '../lifecycle/manager.js';
7
+ import type { InboundResult } from '../sync/engine.js';
8
+ export declare function proxyUsage(): string;
9
+ export declare function runProxyCli(): void;
10
+ type PrivateAdapterImporter = (specifier: string) => Promise<unknown>;
11
+ export interface RuntimeDeps {
12
+ mode: 'public' | 'private';
13
+ hubUrl: string;
14
+ senderId: () => string | undefined;
15
+ store: mailbox.MailboxStore;
16
+ env?: Record<string, string | undefined>;
17
+ now?: () => number;
18
+ privateImporter?: PrivateAdapterImporter;
19
+ }
20
+ export type PublicNodeSecretSource = 'env' | 'store' | 'hub_rotate' | 'legacy_file';
21
+ export interface PublicNodeSecretSelection {
22
+ nodeSecret: string | undefined;
23
+ nodeSecretVersion?: number;
24
+ source: PublicNodeSecretSource;
25
+ storeSecret?: string;
26
+ }
27
+ export interface HubRuntime {
28
+ hub: ProxyDaemonDeps['hub'];
29
+ hello: (opts: {
30
+ rotate: boolean;
31
+ evolverVersion?: string;
32
+ }) => Promise<HelloResult>;
33
+ heartbeat: (opts?: HeartbeatOptions) => Promise<HeartbeatResult>;
34
+ helloMode?: HelloLifecycleMode;
35
+ atp?: AtpProxyClient;
36
+ }
37
+ interface ProxyLoopDaemon {
38
+ tick: () => Promise<ProxyTickReport>;
39
+ nextDelay: (last: InboundResult) => number;
40
+ sleep?: (delayMs: number) => Promise<void>;
41
+ setWakeHandler?: (wake: (() => void) | undefined) => void;
42
+ }
43
+ interface ProxyLoopLogger {
44
+ write: (chunk: string) => unknown;
45
+ }
46
+ export interface ProxyLoopOptions {
47
+ minDelayMs?: number;
48
+ errorDelayMs?: number;
49
+ maxIterations?: number;
50
+ sleep?: (delayMs: number) => Promise<void>;
51
+ logger?: ProxyLoopLogger;
52
+ /** 连续 tick throw 或 resolved fatal-candidate 达该值则抛错退出交 supervisor 重启; 默认 10; 瞬态成功会清零。 */
53
+ maxConsecutiveTickFailures?: number;
54
+ }
55
+ export declare function runProxyLoop(daemon: ProxyLoopDaemon, options?: ProxyLoopOptions): Promise<void>;
56
+ interface CreateProxyDaemonDepsOptions {
57
+ runtime: HubRuntime;
58
+ store: mailbox.MailboxStore;
59
+ ipcToken: string;
60
+ ipcPort?: number;
61
+ evolverVersion: string;
62
+ selfUpdatePolicy: SelfUpdatePolicy;
63
+ env?: NodeJS.ProcessEnv;
64
+ selfUpdateOverrides?: ReleaseBinaryOptions & {
65
+ restart?: () => void;
66
+ };
67
+ }
68
+ export declare function createProxyDaemonDeps(options: CreateProxyDaemonDepsOptions): ProxyDaemonDeps;
69
+ export declare function createSelfUpdateDeps(policy: SelfUpdatePolicy, currentVersion: string, env?: NodeJS.ProcessEnv, overrides?: ReleaseBinaryOptions & {
70
+ restart?: () => void;
71
+ }): ProxyDaemonDeps['selfUpdate'];
72
+ export declare function resolvePublicNodeSecret(deps: RuntimeDeps): PublicNodeSecretSelection;
73
+ /**
74
+ * Hub rejected the cached node_secret as diverged (v1 a2aProtocol.js L1983-2017). Clear every
75
+ * durable copy — store keys + on-disk legacy files — so the next start re-acquires cleanly via an
76
+ * unauthenticated hello. The in-memory secret is already dropped by LegacyAuthShim before this runs.
77
+ */
78
+ export declare function clearDivergedPublicNodeSecret(store: mailbox.MailboxStore, env?: NodeJS.ProcessEnv): void;
79
+ export declare function persistSelectedPublicNodeSecret(store: mailbox.MailboxStore, selection: PublicNodeSecretSelection): void;
80
+ export declare function persistPublicNodeSecretVersion(store: mailbox.MailboxStore, selection: PublicNodeSecretSelection, version: number | undefined): void;
81
+ export declare function connectHubRuntime(deps: RuntimeDeps): Promise<HubRuntime>;
82
+ export declare function resolveLegacyNodeSecret(envNodeSecret: string | undefined, storedNodeSecret: string | undefined, storedSource: string | undefined): string | undefined;
83
+ export {};