@evomap/evolver-proxy 2.0.0-beta.9 → 2.0.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/dist/bin/evolver-proxy.d.ts +26 -2
- package/dist/bin/evolver-proxy.js +284 -51
- package/dist/daemon/atpConsent.js +5 -2
- package/dist/daemon/collaborationFacade.js +23 -13
- package/dist/daemon/proxyDaemon.d.ts +52 -0
- package/dist/daemon/proxyDaemon.js +1067 -24
- package/dist/daemon/publishRecallVerifier.d.ts +114 -0
- package/dist/daemon/publishRecallVerifier.js +495 -0
- package/dist/daemon/selectHub.js +5 -3
- package/dist/daemon/systemdNotifier.d.ts +46 -0
- package/dist/daemon/systemdNotifier.js +153 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/lifecycle/claimNudge.d.ts +20 -0
- package/dist/lifecycle/claimNudge.js +124 -0
- package/dist/lifecycle/manager.d.ts +4 -0
- package/dist/lifecycle/manager.js +15 -2
- package/dist/llm/server.js +24 -4
- package/dist/llm/upstream.d.ts +5 -1
- package/dist/llm/upstream.js +24 -1
- package/dist/private/accountAssetCompatibility.d.ts +29 -0
- package/dist/private/accountAssetCompatibility.js +196 -0
- package/dist/private/adapterLoader.d.ts +21 -1
- package/dist/private/adapterLoader.js +242 -7
- package/dist/private/nodeCredentialStore.d.ts +23 -0
- package/dist/private/nodeCredentialStore.js +210 -0
- package/dist/selfUpdate/bootstrap.d.ts +69 -0
- package/dist/selfUpdate/bootstrap.js +282 -0
- package/dist/selfUpdate/builtinKey.d.ts +4 -0
- package/dist/selfUpdate/builtinKey.js +16 -0
- package/dist/selfUpdate/executor.d.ts +1 -1
- package/dist/selfUpdate/executor.js +1 -1
- package/dist/selfUpdate/failureCodes.d.ts +4 -0
- package/dist/selfUpdate/failureCodes.js +7 -0
- package/dist/selfUpdate/migration.d.ts +93 -0
- package/dist/selfUpdate/migration.js +315 -0
- package/dist/selfUpdate/policy.d.ts +19 -2
- package/dist/selfUpdate/policy.js +82 -2
- package/dist/selfUpdate/releaseBinary.js +4 -1
- package/dist/sync/engine.d.ts +12 -0
- package/dist/sync/engine.js +255 -64
- package/package.json +7 -4
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { mailbox } from '@evomap/evolver-core';
|
|
3
|
+
import { PrivateNodeCredentialStore } from '../private/nodeCredentialStore.js';
|
|
3
4
|
import { loadEnvFileFromEnv } from './envFile.js';
|
|
4
5
|
import { type SelfUpdatePolicy } from '../selfUpdate/policy.js';
|
|
5
6
|
import { type ReleaseBinaryOptions } from '../selfUpdate/releaseBinary.js';
|
|
@@ -11,8 +12,9 @@ import type { AtpProxyClient, ProxyDaemonDeps, ProxyTickReport } from '../daemon
|
|
|
11
12
|
import type { HelloLifecycleMode, HelloResult, HeartbeatOptions, HeartbeatResult } from '../lifecycle/manager.js';
|
|
12
13
|
import type { InboundResult } from '../sync/engine.js';
|
|
13
14
|
/** evolver-proxy 系统级 daemon 入口(M6-7). EVOMAP_HUB_MODE/URL/NODE_SECRET 选址. */
|
|
14
|
-
interface RunProxyMainOptions {
|
|
15
|
+
export interface RunProxyMainOptions {
|
|
15
16
|
environmentPrepared?: boolean;
|
|
17
|
+
recoveryPrepared?: SelfUpdateRecoveryResult;
|
|
16
18
|
}
|
|
17
19
|
export declare function runProxyMain(options?: RunProxyMainOptions): Promise<void>;
|
|
18
20
|
export declare function recoverBoundDurableSelfUpdate(options: Omit<SelfUpdateRecoveryOptions, 'beforeJournalMutation'>): Promise<SelfUpdateRecoveryResult>;
|
|
@@ -45,7 +47,8 @@ export interface RunProxyCliOptions {
|
|
|
45
47
|
env?: NodeJS.ProcessEnv;
|
|
46
48
|
platform?: NodeJS.Platform;
|
|
47
49
|
processExecPath?: string;
|
|
48
|
-
runMain?: () => Promise<void>;
|
|
50
|
+
runMain?: (options?: RunProxyMainOptions) => Promise<void>;
|
|
51
|
+
recoverStartup?: typeof recoverBoundDurableSelfUpdate;
|
|
49
52
|
runUnixRecoveryController?: typeof maybeRunUnixRecoveryController;
|
|
50
53
|
runWindowsRecoveryController?: typeof maybeRunWindowsRecoveryController;
|
|
51
54
|
runWindowsUpdaterWorker?: typeof maybeRunWindowsUpdaterWorkerFromArgv;
|
|
@@ -73,14 +76,22 @@ export interface RuntimeDeps {
|
|
|
73
76
|
env?: Record<string, string | undefined>;
|
|
74
77
|
now?: () => number;
|
|
75
78
|
privateImporter?: PrivateAdapterImporter;
|
|
79
|
+
privateNodeCredentialStore?: Pick<PrivateNodeCredentialStore, 'read' | 'write'>;
|
|
76
80
|
}
|
|
77
81
|
export type PublicNodeSecretSource = 'env' | 'store' | 'hub_rotate' | 'legacy_file';
|
|
78
82
|
export interface PublicNodeSecretSelection {
|
|
79
83
|
nodeSecret: string | undefined;
|
|
84
|
+
nodeId?: string;
|
|
80
85
|
nodeSecretVersion?: number;
|
|
81
86
|
source: PublicNodeSecretSource;
|
|
82
87
|
storeSecret?: string;
|
|
83
88
|
}
|
|
89
|
+
export interface VerifiedPublicSender {
|
|
90
|
+
senderId: () => string | undefined;
|
|
91
|
+
adopt: (nodeId: string) => void;
|
|
92
|
+
}
|
|
93
|
+
export declare function createVerifiedPublicSender(initialNodeId?: string): VerifiedPublicSender;
|
|
94
|
+
export declare function adoptVerifiedPublicNodeId(store: mailbox.MailboxStore, selection: PublicNodeSecretSelection, sender: VerifiedPublicSender, nodeId: string): void;
|
|
84
95
|
export interface HubRuntime {
|
|
85
96
|
hub: ProxyDaemonDeps['hub'];
|
|
86
97
|
hello: (opts: {
|
|
@@ -96,6 +107,7 @@ interface ProxyLoopDaemon {
|
|
|
96
107
|
nextDelay: (last: InboundResult) => number;
|
|
97
108
|
sleep?: (delayMs: number) => Promise<void>;
|
|
98
109
|
setWakeHandler?: (wake: (() => void) | undefined) => void;
|
|
110
|
+
setExpectedNextTick?: (delayMs: number | undefined) => void;
|
|
99
111
|
}
|
|
100
112
|
interface ProxyLoopLogger {
|
|
101
113
|
write: (chunk: string) => unknown;
|
|
@@ -110,9 +122,20 @@ export interface ProxyLoopOptions {
|
|
|
110
122
|
maxConsecutiveTickFailures?: number;
|
|
111
123
|
}
|
|
112
124
|
export declare function runProxyLoop(daemon: ProxyLoopDaemon, options?: ProxyLoopOptions): Promise<void>;
|
|
125
|
+
export declare function runManagedProxyLoop(options: {
|
|
126
|
+
daemon: ProxyLoopDaemon & StartupStoppableDaemon;
|
|
127
|
+
store: StartupClosableStore;
|
|
128
|
+
notifier: {
|
|
129
|
+
readyOrThrow(): Promise<void>;
|
|
130
|
+
stop(): void;
|
|
131
|
+
};
|
|
132
|
+
runLoop?: (daemon: ProxyLoopDaemon, options?: ProxyLoopOptions) => Promise<void>;
|
|
133
|
+
logger?: ProxyLoopLogger;
|
|
134
|
+
}): Promise<void>;
|
|
113
135
|
interface CreateProxyDaemonDepsOptions {
|
|
114
136
|
runtime: HubRuntime;
|
|
115
137
|
store: mailbox.MailboxStore;
|
|
138
|
+
hubMode?: 'public' | 'private';
|
|
116
139
|
ipcToken: string;
|
|
117
140
|
ipcPort?: number;
|
|
118
141
|
evolverVersion: string;
|
|
@@ -139,6 +162,7 @@ export declare function resolvePublicNodeSecret(deps: RuntimeDeps): PublicNodeSe
|
|
|
139
162
|
*/
|
|
140
163
|
export declare function clearDivergedPublicNodeSecret(store: mailbox.MailboxStore, env?: NodeJS.ProcessEnv): void;
|
|
141
164
|
export declare function persistSelectedPublicNodeSecret(store: mailbox.MailboxStore, selection: PublicNodeSecretSelection): void;
|
|
165
|
+
export declare function persistRotatedPublicNodeCredentials(store: mailbox.MailboxStore, selection: PublicNodeSecretSelection, secret: string, version: number | undefined): void;
|
|
142
166
|
export declare function persistPublicNodeSecretVersion(store: mailbox.MailboxStore, selection: PublicNodeSecretSelection, version: number | undefined): void;
|
|
143
167
|
export declare function connectHubRuntime(deps: RuntimeDeps): Promise<HubRuntime>;
|
|
144
168
|
export declare function resolveLegacyNodeSecret(envNodeSecret: string | undefined, storedNodeSecret: string | undefined, storedSource: string | undefined): string | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
3
3
|
import { readFileSync, realpathSync, rmSync } from 'node:fs';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import { join, resolve, win32 } from 'node:path';
|
|
@@ -10,14 +10,19 @@ import { ProxyDaemon } from '../daemon/proxyDaemon.js';
|
|
|
10
10
|
import { resolveHubMode, resolveHubUrl } from '../daemon/selectHub.js';
|
|
11
11
|
import { resolveIpcPort } from '../daemon/ipcConfig.js';
|
|
12
12
|
import { traceCollectionEnabled } from '../llm/traceConfig.js';
|
|
13
|
-
import { connectPrivateProxyHub } from '../private/adapterLoader.js';
|
|
13
|
+
import { connectPrivateProxyHub, resolvePrivateEnterpriseToken, resolvePrivateInvitationToken, resolvePrivateNodeSecret, } from '../private/adapterLoader.js';
|
|
14
|
+
import { PrivateNodeCredentialStore, PrivateNodeCredentialReadError, } from '../private/nodeCredentialStore.js';
|
|
14
15
|
import { createAtpOrderConsentGate } from '../daemon/atpConsent.js';
|
|
16
|
+
import { SystemdNotifier } from '../daemon/systemdNotifier.js';
|
|
15
17
|
import { resolveProxyStorePath } from './proxyStorePath.js';
|
|
16
18
|
import { publishProxySettings } from './proxySettings.js';
|
|
17
19
|
import { expandHomePath, loadEnvFileFromEnv } from './envFile.js';
|
|
18
|
-
import { resolveProxyNodeId } from '../lifecycle/legacyNodeId.js';
|
|
20
|
+
import { readLegacyNodeId, resolveProxyNodeId } from '../lifecycle/legacyNodeId.js';
|
|
21
|
+
import { createClaimNudge, wrapHelloWithClaimNudge } from '../lifecycle/claimNudge.js';
|
|
22
|
+
import { bootstrapDegradedSelfUpdateStartup } from '../selfUpdate/bootstrap.js';
|
|
19
23
|
import { getCurrentVersion } from '../selfUpdate/version.js';
|
|
20
|
-
import {
|
|
24
|
+
import { resolveEffectiveSelfUpdatePolicy, selfUpdateSupervisorAttested, } from '../selfUpdate/policy.js';
|
|
25
|
+
import { resolveSelfUpdatePublicKey } from '../selfUpdate/builtinKey.js';
|
|
21
26
|
import { atomicReplaceExecutable, downloadGithubReleaseArtifact, resolveGithubReleaseManifest, resolveSelfUpdateTarget, } from '../selfUpdate/releaseBinary.js';
|
|
22
27
|
import { beginDurableSelfUpdate, confirmDurableSelfUpdate, recoverDurableSelfUpdate, rollbackDurableSelfUpdate, } from '../selfUpdate/transaction.js';
|
|
23
28
|
import { SELF_UPDATE_FAILURE_CODES } from '../selfUpdate/failureCodes.js';
|
|
@@ -30,19 +35,23 @@ export async function runProxyMain(options = {}) {
|
|
|
30
35
|
process.stdout.write(proxyUsage());
|
|
31
36
|
return;
|
|
32
37
|
}
|
|
33
|
-
if (!options.environmentPrepared) {
|
|
34
|
-
const envFile = loadProxyEnvFile(process.env);
|
|
35
|
-
if (envFile.error)
|
|
36
|
-
process.stderr.write(`[evolver-proxy] failed to load EVOLVER_ENV_FILE: ${safeLoopMessage(envFile.error)}\n`);
|
|
37
|
-
}
|
|
38
38
|
// Recovery must run before any hub/store/runtime initialization. In particular,
|
|
39
|
-
// a broken store must not prevent a pending-health update from restoring the old binary.
|
|
40
|
-
const recovery =
|
|
39
|
+
// a broken store or env-file pointer must not prevent a pending-health update from restoring the old binary.
|
|
40
|
+
const recovery = options.recoveryPrepared
|
|
41
|
+
?? await recoverBoundDurableSelfUpdate({
|
|
42
|
+
env: proxyRecoveryEnvironment(process.env),
|
|
43
|
+
processExecPath: process.execPath,
|
|
44
|
+
});
|
|
41
45
|
if (recovery.outcome === 'blocked') {
|
|
42
46
|
throw new Error(`self_update_recovery_blocked:${recovery.failureCode ?? 'unknown'}`);
|
|
43
47
|
}
|
|
44
48
|
if (recovery.restartRequired)
|
|
45
49
|
process.exit(78);
|
|
50
|
+
if (!options.environmentPrepared) {
|
|
51
|
+
const envFile = loadProxyEnvFile(process.env);
|
|
52
|
+
if (envFile.error)
|
|
53
|
+
throw new Error(`failed to load EVOLVER_ENV_FILE: ${safeLoopMessage(envFile.error)}`);
|
|
54
|
+
}
|
|
46
55
|
let storePath;
|
|
47
56
|
let store;
|
|
48
57
|
let proxyDaemon;
|
|
@@ -76,7 +85,23 @@ export async function runProxyMain(options = {}) {
|
|
|
76
85
|
// same owner. See lifecycle/legacyNodeId.ts for the duplicate-node rationale.
|
|
77
86
|
const senderId = () => resolveProxyNodeId({ storedNodeId: store.getState('node_id'), configuredNodeId });
|
|
78
87
|
evolverVersion = getCurrentVersion();
|
|
79
|
-
|
|
88
|
+
// Default (unset) 'auto' without a durable supervisor attestation degrades to 'off' so
|
|
89
|
+
// unsupervised foreground runs keep starting; explicit 'auto' stays fail-closed at assembly below.
|
|
90
|
+
const effectiveSelfUpdate = resolveEffectiveSelfUpdatePolicy(process.env);
|
|
91
|
+
selfUpdatePolicy = effectiveSelfUpdate.policy;
|
|
92
|
+
if (effectiveSelfUpdate.degraded) {
|
|
93
|
+
// First-run bootstrap: try to register our own user-level durable launcher so a bare
|
|
94
|
+
// `npm install` run becomes permanently self-updating. Any failure/skip keeps the degraded
|
|
95
|
+
// 'off' startup; success hands restart ownership to the service manager.
|
|
96
|
+
const bootstrap = await bootstrapDegradedSelfUpdateStartup(process.env, process.platform);
|
|
97
|
+
if (bootstrap.handedOver) {
|
|
98
|
+
// Exit cleanly so the just-activated service instance can bind the IPC port; systemd
|
|
99
|
+
// RestartSec / launchd ThrottleInterval absorb the short hand-off window if we lose the race.
|
|
100
|
+
process.stdout.write(`${bootstrap.message}\n`);
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}
|
|
103
|
+
process.stderr.write(`${bootstrap.message}\n`);
|
|
104
|
+
}
|
|
80
105
|
const proxyStartedAt = new Date().toISOString();
|
|
81
106
|
publishLocalProxySettings = () => {
|
|
82
107
|
if (!proxySettingsState.url)
|
|
@@ -92,11 +117,21 @@ export async function runProxyMain(options = {}) {
|
|
|
92
117
|
},
|
|
93
118
|
});
|
|
94
119
|
};
|
|
95
|
-
const
|
|
120
|
+
const privateNodeCredentialStore = mode === 'private'
|
|
121
|
+
? new PrivateNodeCredentialStore(storePath)
|
|
122
|
+
: undefined;
|
|
123
|
+
const runtime = await connectHubRuntime({
|
|
124
|
+
mode,
|
|
125
|
+
hubUrl,
|
|
126
|
+
senderId,
|
|
127
|
+
store,
|
|
128
|
+
...(privateNodeCredentialStore ? { privateNodeCredentialStore } : {}),
|
|
129
|
+
});
|
|
96
130
|
proxyDaemon = new ProxyDaemon({
|
|
97
131
|
...createProxyDaemonDeps({
|
|
98
132
|
runtime,
|
|
99
133
|
store,
|
|
134
|
+
hubMode: mode,
|
|
100
135
|
ipcToken,
|
|
101
136
|
...(ipcPort !== undefined ? { ipcPort } : {}),
|
|
102
137
|
evolverVersion,
|
|
@@ -138,7 +173,16 @@ export async function runProxyMain(options = {}) {
|
|
|
138
173
|
proxySettingsState.url = `http://127.0.0.1:${port}`;
|
|
139
174
|
publishLocalProxySettings();
|
|
140
175
|
process.stdout.write(`[evolver-proxy] mode=${mode} hub=${hubUrl} ipc=127.0.0.1:${port} v=${evolverVersion} self-update=${selfUpdatePolicy}\n`);
|
|
141
|
-
|
|
176
|
+
const systemdNotifier = new SystemdNotifier({
|
|
177
|
+
env: process.env,
|
|
178
|
+
health: () => proxyDaemon.health(),
|
|
179
|
+
});
|
|
180
|
+
await runManagedProxyLoop({
|
|
181
|
+
daemon: proxyDaemon,
|
|
182
|
+
store: store,
|
|
183
|
+
notifier: systemdNotifier,
|
|
184
|
+
logger: process.stderr,
|
|
185
|
+
});
|
|
142
186
|
}
|
|
143
187
|
export async function recoverBoundDurableSelfUpdate(options) {
|
|
144
188
|
return recoverDurableSelfUpdate({
|
|
@@ -150,14 +194,27 @@ export async function recoverBoundDurableSelfUpdate(options) {
|
|
|
150
194
|
}
|
|
151
195
|
export function loadProxyEnvFile(env) {
|
|
152
196
|
const supervisor = env['EVOLVER_SELF_UPDATE_SUPERVISOR'];
|
|
197
|
+
const lifecycleStateDir = env['EVOLVER_LIFECYCLE_STATE_DIR'];
|
|
153
198
|
const stateDir = env['EVOLVER_SELF_UPDATE_STATE_DIR'];
|
|
154
199
|
const targetPath = env['EVOLVER_SELF_UPDATE_TARGET_PATH'];
|
|
200
|
+
const systemRootBindings = Object.entries(env)
|
|
201
|
+
.filter(([key]) => key.toLowerCase() === 'systemroot');
|
|
155
202
|
const result = loadEnvFileFromEnv(env);
|
|
203
|
+
for (const key of Object.keys(env)) {
|
|
204
|
+
if (key.toLowerCase() === 'systemroot')
|
|
205
|
+
delete env[key];
|
|
206
|
+
}
|
|
207
|
+
for (const [key, value] of systemRootBindings) {
|
|
208
|
+
if (value !== undefined)
|
|
209
|
+
env[key] = value;
|
|
210
|
+
}
|
|
156
211
|
if (supervisor === undefined) {
|
|
157
212
|
delete env['EVOLVER_SELF_UPDATE_SUPERVISOR'];
|
|
158
213
|
}
|
|
159
214
|
else {
|
|
160
215
|
env['EVOLVER_SELF_UPDATE_SUPERVISOR'] = supervisor;
|
|
216
|
+
if (lifecycleStateDir !== undefined)
|
|
217
|
+
env['EVOLVER_LIFECYCLE_STATE_DIR'] = lifecycleStateDir;
|
|
161
218
|
if (stateDir !== undefined)
|
|
162
219
|
env['EVOLVER_SELF_UPDATE_STATE_DIR'] = stateDir;
|
|
163
220
|
if (targetPath !== undefined)
|
|
@@ -165,6 +222,11 @@ export function loadProxyEnvFile(env) {
|
|
|
165
222
|
}
|
|
166
223
|
return result;
|
|
167
224
|
}
|
|
225
|
+
function proxyRecoveryEnvironment(env) {
|
|
226
|
+
const recoveryEnv = { ...env };
|
|
227
|
+
loadProxyEnvFile(recoveryEnv);
|
|
228
|
+
return recoveryEnv;
|
|
229
|
+
}
|
|
168
230
|
function finalizeRecoveryTelemetry(store, recovery) {
|
|
169
231
|
try {
|
|
170
232
|
finalizeSelfUpdateRecoveryLastUpdate(store, recovery);
|
|
@@ -302,6 +364,10 @@ export function prepareProxyCliEnvironment(argv, env) {
|
|
|
302
364
|
if (options.envFile)
|
|
303
365
|
env['EVOLVER_ENV_FILE'] = options.envFile;
|
|
304
366
|
const envFile = loadProxyEnvFile(env);
|
|
367
|
+
applyProxyCliPathOptions(options, env);
|
|
368
|
+
return { options, envFile };
|
|
369
|
+
}
|
|
370
|
+
function applyProxyCliPathOptions(options, env) {
|
|
305
371
|
if (options.home) {
|
|
306
372
|
env['EVOMAP_DIR'] = options.home;
|
|
307
373
|
env['EVOLVER_HOME'] = options.home;
|
|
@@ -320,7 +386,6 @@ export function prepareProxyCliEnvironment(argv, env) {
|
|
|
320
386
|
env['EVOLVER_PROXY_STORE'] = options.store;
|
|
321
387
|
if (options.settings)
|
|
322
388
|
env['EVOLVER_PROXY_SETTINGS_FILE'] = options.settings;
|
|
323
|
-
return { options, envFile };
|
|
324
389
|
}
|
|
325
390
|
export async function runProxyCli(options = {}) {
|
|
326
391
|
const argv = options.argv ?? process.argv.slice(2);
|
|
@@ -356,11 +421,28 @@ export async function runProxyCli(options = {}) {
|
|
|
356
421
|
process.stdout.write(proxyUsage(argv[0] === 'proxy' ? 'evolver proxy' : 'evolver-proxy'));
|
|
357
422
|
return 0;
|
|
358
423
|
}
|
|
424
|
+
if (cliOptions.envFile)
|
|
425
|
+
env['EVOLVER_ENV_FILE'] = cliOptions.envFile;
|
|
426
|
+
applyProxyCliPathOptions(cliOptions, env);
|
|
427
|
+
const recovery = options.recoverStartup || !options.runMain
|
|
428
|
+
? await (options.recoverStartup ?? recoverBoundDurableSelfUpdate)({
|
|
429
|
+
env: proxyRecoveryEnvironment(env),
|
|
430
|
+
processExecPath: options.processExecPath ?? process.execPath,
|
|
431
|
+
})
|
|
432
|
+
: undefined;
|
|
433
|
+
if (recovery?.outcome === 'blocked') {
|
|
434
|
+
throw new Error(`self_update_recovery_blocked:${recovery.failureCode ?? 'unknown'}`);
|
|
435
|
+
}
|
|
436
|
+
if (recovery?.restartRequired)
|
|
437
|
+
return 78;
|
|
359
438
|
const prepared = prepareProxyCliEnvironment(argv, env);
|
|
360
439
|
if (prepared.envFile.error) {
|
|
361
|
-
|
|
440
|
+
throw new Error(`failed to load EVOLVER_ENV_FILE: ${safeLoopMessage(prepared.envFile.error)}`);
|
|
362
441
|
}
|
|
363
|
-
await (options.runMain ??
|
|
442
|
+
await (options.runMain ?? runProxyMain)({
|
|
443
|
+
environmentPrepared: true,
|
|
444
|
+
...(recovery ? { recoveryPrepared: recovery } : {}),
|
|
445
|
+
});
|
|
364
446
|
return 0;
|
|
365
447
|
}
|
|
366
448
|
catch (error) {
|
|
@@ -376,6 +458,20 @@ if (isDirectRun(import.meta.url, process.argv[1])) {
|
|
|
376
458
|
process.exitCode = exitCode;
|
|
377
459
|
});
|
|
378
460
|
}
|
|
461
|
+
export function createVerifiedPublicSender(initialNodeId) {
|
|
462
|
+
let verifiedNodeId = initialNodeId;
|
|
463
|
+
return {
|
|
464
|
+
senderId: () => verifiedNodeId,
|
|
465
|
+
adopt: (nodeId) => {
|
|
466
|
+
verifiedNodeId = nodeId;
|
|
467
|
+
},
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
export function adoptVerifiedPublicNodeId(store, selection, sender, nodeId) {
|
|
471
|
+
store.setState('node_id', nodeId);
|
|
472
|
+
selection.nodeId = nodeId;
|
|
473
|
+
sender.adopt(nodeId);
|
|
474
|
+
}
|
|
379
475
|
export async function runProxyLoop(daemon, options = {}) {
|
|
380
476
|
const minDelayMs = options.minDelayMs ?? 1_000;
|
|
381
477
|
const errorDelayMs = options.errorDelayMs ?? 5_000;
|
|
@@ -391,6 +487,7 @@ export async function runProxyLoop(daemon, options = {}) {
|
|
|
391
487
|
let consecutiveTickFailures = 0;
|
|
392
488
|
try {
|
|
393
489
|
for (let iteration = 0; iteration < maxIterations; iteration += 1) {
|
|
490
|
+
daemon.setExpectedNextTick?.(undefined);
|
|
394
491
|
let delayMs = errorDelayMs;
|
|
395
492
|
let tickHealthy = false;
|
|
396
493
|
let exitForResolvedFailure;
|
|
@@ -430,7 +527,9 @@ export async function runProxyLoop(daemon, options = {}) {
|
|
|
430
527
|
break;
|
|
431
528
|
if (tickHealthy) {
|
|
432
529
|
// Healthy idle: wake-interruptible so new outbound/inbound work re-ticks promptly.
|
|
433
|
-
|
|
530
|
+
const healthyDelayMs = Math.max(minDelayMs, delayMs);
|
|
531
|
+
daemon.setExpectedNextTick?.(healthyDelayMs);
|
|
532
|
+
await sleepUntilDelayOrWake(healthyDelayMs, sleep, setWakeHandler);
|
|
434
533
|
}
|
|
435
534
|
else {
|
|
436
535
|
// Error / fatal-candidate backoff: NON-interruptible. Otherwise wakeRunner()
|
|
@@ -442,15 +541,33 @@ export async function runProxyLoop(daemon, options = {}) {
|
|
|
442
541
|
}
|
|
443
542
|
}
|
|
444
543
|
finally {
|
|
544
|
+
daemon.setExpectedNextTick?.(undefined);
|
|
445
545
|
if (!useDaemonSleep)
|
|
446
546
|
daemon.setWakeHandler?.(undefined);
|
|
447
547
|
}
|
|
448
548
|
}
|
|
549
|
+
export async function runManagedProxyLoop(options) {
|
|
550
|
+
try {
|
|
551
|
+
await options.notifier.readyOrThrow();
|
|
552
|
+
await (options.runLoop ?? runProxyLoop)(options.daemon, options.logger ? { logger: options.logger } : {});
|
|
553
|
+
}
|
|
554
|
+
finally {
|
|
555
|
+
try {
|
|
556
|
+
options.notifier.stop();
|
|
557
|
+
}
|
|
558
|
+
finally {
|
|
559
|
+
await closeStartupResources({ store: options.store, daemon: options.daemon });
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
449
563
|
export function createProxyDaemonDeps(options) {
|
|
450
564
|
const selfUpdate = createSelfUpdateDeps(options.selfUpdatePolicy, options.evolverVersion, options.env ?? process.env, options.selfUpdateOverrides);
|
|
451
|
-
const
|
|
565
|
+
const env = options.env ?? process.env;
|
|
566
|
+
const traceBackfill = resolveTraceBackfillConfig(env);
|
|
567
|
+
const heartbeatIntervalMs = positiveIntegerEnv(env['HEARTBEAT_INTERVAL_MS']);
|
|
452
568
|
return {
|
|
453
569
|
hub: options.runtime.hub,
|
|
570
|
+
...(options.hubMode ? { hubMode: options.hubMode } : {}),
|
|
454
571
|
store: options.store,
|
|
455
572
|
ipcToken: options.ipcToken,
|
|
456
573
|
...(options.ipcPort !== undefined ? { ipcPort: options.ipcPort } : {}),
|
|
@@ -461,11 +578,19 @@ export function createProxyDaemonDeps(options) {
|
|
|
461
578
|
evolverVersion: options.evolverVersion,
|
|
462
579
|
hello: options.runtime.hello,
|
|
463
580
|
heartbeat: options.runtime.heartbeat,
|
|
581
|
+
...(heartbeatIntervalMs !== undefined ? { heartbeatIntervalMs } : {}),
|
|
464
582
|
...(options.runtime.helloMode ? { helloMode: options.runtime.helloMode } : {}),
|
|
465
583
|
...(selfUpdate ? { selfUpdate } : {}),
|
|
466
584
|
...(traceBackfill ? { traceBackfill } : {}),
|
|
467
585
|
};
|
|
468
586
|
}
|
|
587
|
+
function positiveIntegerEnv(value) {
|
|
588
|
+
const trimmed = value?.trim();
|
|
589
|
+
if (!trimmed || !/^\d+$/.test(trimmed))
|
|
590
|
+
return undefined;
|
|
591
|
+
const parsed = Number(trimmed);
|
|
592
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined;
|
|
593
|
+
}
|
|
469
594
|
function resolveTraceBackfillConfig(env) {
|
|
470
595
|
if (!traceCollectionEnabled(env))
|
|
471
596
|
return undefined;
|
|
@@ -594,7 +719,7 @@ export function createSelfUpdateDeps(policy, currentVersion, env = process.env,
|
|
|
594
719
|
if (!supervisorAttested && !selfUpdateSupervisorAttested(env)) {
|
|
595
720
|
throw new Error('self_update_supervisor_required');
|
|
596
721
|
}
|
|
597
|
-
const publicKey = env
|
|
722
|
+
const publicKey = resolveSelfUpdatePublicKey(env);
|
|
598
723
|
if (!publicKey)
|
|
599
724
|
throw new Error('self_update_public_key_required');
|
|
600
725
|
const releaseOpts = {
|
|
@@ -668,36 +793,81 @@ function canonicalExecutablePath(path) {
|
|
|
668
793
|
.replace(/^\\\\\?\\/i, '');
|
|
669
794
|
return win32.normalize(withoutNamespace).toLowerCase();
|
|
670
795
|
}
|
|
671
|
-
function selfUpdateSupervisorAttested(env) {
|
|
672
|
-
const supervisor = env['EVOLVER_SELF_UPDATE_SUPERVISOR']?.trim();
|
|
673
|
-
return supervisor === 'systemd'
|
|
674
|
-
|| supervisor === 'launchd'
|
|
675
|
-
|| supervisor === 'windows-scheduled-task';
|
|
676
|
-
}
|
|
677
796
|
export function resolvePublicNodeSecret(deps) {
|
|
678
|
-
const
|
|
679
|
-
// version env precedence MUST mirror the node_secret precedence above (EVOMAP-first) so an operator
|
|
680
|
-
// who sets both env pairs always resolves a matched (secret, version). v2 standardizes on EVOMAP_*-first
|
|
681
|
-
// for BOTH secret and version; v1 uses A2A_*-first but pairs the two identically. Do not flip one alone.
|
|
682
|
-
const envNodeSecretVersion = parseNodeSecretVersion(process.env['EVOMAP_NODE_SECRET_VERSION'] ?? process.env['A2A_NODE_SECRET_VERSION']);
|
|
797
|
+
const explicit = resolveExplicitPublicNodeCredentials(process.env);
|
|
683
798
|
const storedNodeSecret = deps.store.getState('node_secret');
|
|
799
|
+
const storedNodeId = deps.store.getState('node_id')?.trim() || undefined;
|
|
684
800
|
const storedSource = deps.store.getState('node_secret_source');
|
|
685
801
|
const storedNodeSecretVersion = parseNodeSecretVersion(deps.store.getState('node_secret_version'));
|
|
686
|
-
const storeSecret =
|
|
687
|
-
|
|
688
|
-
|
|
802
|
+
const storeSecret = storedSource?.startsWith('pending_')
|
|
803
|
+
? undefined
|
|
804
|
+
: storedNodeSecret && isNodeSecret(storedNodeSecret) ? storedNodeSecret : undefined;
|
|
805
|
+
const legacy = readLegacyNodeSecret(process.env);
|
|
806
|
+
const pairedStoreNodeId = storedNodeId
|
|
807
|
+
?? (explicit.nodeSecret === storeSecret ? explicit.nodeId : undefined)
|
|
808
|
+
?? (legacy && legacy.nodeSecret === storeSecret ? legacy.nodeId : undefined);
|
|
809
|
+
const completeExplicitOverridesOrphan = Boolean(explicit.nodeId
|
|
810
|
+
&& explicit.nodeSecret
|
|
811
|
+
&& explicit.nodeSecret !== storeSecret
|
|
812
|
+
&& pairedStoreNodeId !== explicit.nodeId);
|
|
813
|
+
if (storedSource === 'hub_rotate' && storeSecret && !completeExplicitOverridesOrphan) {
|
|
814
|
+
return {
|
|
815
|
+
nodeSecret: storeSecret,
|
|
816
|
+
...(pairedStoreNodeId ? { nodeId: pairedStoreNodeId } : {}),
|
|
817
|
+
nodeSecretVersion: storedNodeSecretVersion,
|
|
818
|
+
source: 'hub_rotate',
|
|
819
|
+
storeSecret,
|
|
820
|
+
};
|
|
689
821
|
}
|
|
690
|
-
if (
|
|
691
|
-
const
|
|
692
|
-
|
|
822
|
+
if (explicit.nodeSecret) {
|
|
823
|
+
const pairedNodeId = explicit.nodeId
|
|
824
|
+
?? (legacy?.nodeSecret === explicit.nodeSecret ? legacy.nodeId : undefined);
|
|
825
|
+
const pairedStoreVersion = explicit.nodeSecret === storeSecret ? storedNodeSecretVersion : undefined;
|
|
826
|
+
return {
|
|
827
|
+
nodeSecret: explicit.nodeSecret,
|
|
828
|
+
...(pairedNodeId ? { nodeId: pairedNodeId } : {}),
|
|
829
|
+
nodeSecretVersion: explicit.nodeSecretVersion ?? pairedStoreVersion,
|
|
830
|
+
source: 'env',
|
|
831
|
+
storeSecret,
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
if (storeSecret) {
|
|
835
|
+
return {
|
|
836
|
+
nodeSecret: storeSecret,
|
|
837
|
+
...(pairedStoreNodeId ? { nodeId: pairedStoreNodeId } : {}),
|
|
838
|
+
nodeSecretVersion: storedNodeSecretVersion,
|
|
839
|
+
source: 'store',
|
|
840
|
+
storeSecret,
|
|
841
|
+
};
|
|
693
842
|
}
|
|
694
|
-
if (storeSecret)
|
|
695
|
-
return { nodeSecret: storeSecret, nodeSecretVersion: storedNodeSecretVersion, source: 'store', storeSecret };
|
|
696
|
-
const legacy = readLegacyNodeSecret(process.env);
|
|
697
843
|
if (legacy)
|
|
698
844
|
return { ...legacy, source: 'legacy_file' };
|
|
699
845
|
return { nodeSecret: undefined, source: 'store' };
|
|
700
846
|
}
|
|
847
|
+
function resolveExplicitPublicNodeCredentials(env) {
|
|
848
|
+
const evomap = publicCredentialNamespace(env, 'EVOMAP');
|
|
849
|
+
const a2a = publicCredentialNamespace(env, 'A2A');
|
|
850
|
+
if (evomap.nodeId && evomap.nodeSecret)
|
|
851
|
+
return evomap;
|
|
852
|
+
if (a2a.nodeId && a2a.nodeSecret)
|
|
853
|
+
return a2a;
|
|
854
|
+
if (evomap.nodeId && a2a.nodeId && evomap.nodeId === a2a.nodeId) {
|
|
855
|
+
return evomap.nodeSecret ? evomap : a2a.nodeSecret ? a2a : {};
|
|
856
|
+
}
|
|
857
|
+
if (evomap.nodeId || a2a.nodeId)
|
|
858
|
+
return {};
|
|
859
|
+
return evomap.nodeSecret ? evomap : a2a.nodeSecret ? a2a : {};
|
|
860
|
+
}
|
|
861
|
+
function publicCredentialNamespace(env, prefix) {
|
|
862
|
+
const nodeId = env[`${prefix}_NODE_ID`]?.trim() || undefined;
|
|
863
|
+
const nodeSecret = env[`${prefix}_NODE_SECRET`]?.trim() || undefined;
|
|
864
|
+
const nodeSecretVersion = parseNodeSecretVersion(env[`${prefix}_NODE_SECRET_VERSION`]);
|
|
865
|
+
return {
|
|
866
|
+
...(nodeId ? { nodeId } : {}),
|
|
867
|
+
...(nodeSecret ? { nodeSecret } : {}),
|
|
868
|
+
...(nodeSecretVersion !== undefined ? { nodeSecretVersion } : {}),
|
|
869
|
+
};
|
|
870
|
+
}
|
|
701
871
|
function setOptionalStoreState(store, key, value) {
|
|
702
872
|
store.setState(key, value ?? '');
|
|
703
873
|
}
|
|
@@ -732,8 +902,13 @@ function readLegacyNodeSecret(env = process.env) {
|
|
|
732
902
|
const nodeSecret = readTrimmedFile(join(home, 'node_secret'));
|
|
733
903
|
if (!nodeSecret || !isNodeSecret(nodeSecret))
|
|
734
904
|
continue;
|
|
905
|
+
const nodeId = readLegacyNodeId({ candidates: [join(home, 'node_id')] });
|
|
735
906
|
const nodeSecretVersion = parseNodeSecretVersion(readTrimmedFile(join(home, 'node_secret_version')));
|
|
736
|
-
return {
|
|
907
|
+
return {
|
|
908
|
+
...(nodeId ? { nodeId } : {}),
|
|
909
|
+
nodeSecret,
|
|
910
|
+
...(nodeSecretVersion !== undefined ? { nodeSecretVersion } : {}),
|
|
911
|
+
};
|
|
737
912
|
}
|
|
738
913
|
return undefined;
|
|
739
914
|
}
|
|
@@ -768,9 +943,20 @@ export function clearDivergedPublicNodeSecret(store, env = process.env) {
|
|
|
768
943
|
export function persistSelectedPublicNodeSecret(store, selection) {
|
|
769
944
|
if (selection.source !== 'legacy_file' || !selection.nodeSecret)
|
|
770
945
|
return;
|
|
946
|
+
store.setState('node_secret_source', 'pending_legacy');
|
|
947
|
+
if (selection.nodeId)
|
|
948
|
+
store.setState('node_id', selection.nodeId);
|
|
771
949
|
store.setState('node_secret', selection.nodeSecret);
|
|
772
|
-
store.setState('node_secret_source', 'legacy_file');
|
|
773
950
|
setOptionalStoreState(store, 'node_secret_version', selection.nodeSecretVersion !== undefined ? String(selection.nodeSecretVersion) : undefined);
|
|
951
|
+
store.setState('node_secret_source', 'legacy_file');
|
|
952
|
+
}
|
|
953
|
+
export function persistRotatedPublicNodeCredentials(store, selection, secret, version) {
|
|
954
|
+
store.setState('node_secret_source', 'pending_rotate');
|
|
955
|
+
if (selection.nodeId)
|
|
956
|
+
store.setState('node_id', selection.nodeId);
|
|
957
|
+
store.setState('node_secret', secret);
|
|
958
|
+
setOptionalStoreState(store, 'node_secret_version', version !== undefined ? String(version) : undefined);
|
|
959
|
+
store.setState('node_secret_source', 'hub_rotate');
|
|
774
960
|
}
|
|
775
961
|
export function persistPublicNodeSecretVersion(store, selection, version) {
|
|
776
962
|
const currentStoreSecret = store.getState('node_secret');
|
|
@@ -801,31 +987,59 @@ function isDirectRun(metaUrl, argv1) {
|
|
|
801
987
|
}
|
|
802
988
|
export async function connectHubRuntime(deps) {
|
|
803
989
|
if (deps.mode === 'private') {
|
|
804
|
-
const
|
|
990
|
+
const storedInvitationFingerprint = deps.store.getState('private_invitation_fingerprint')?.trim();
|
|
991
|
+
const runtimeEnv = deps.env ?? process.env;
|
|
992
|
+
let storedNodeSecret;
|
|
993
|
+
try {
|
|
994
|
+
storedNodeSecret = deps.privateNodeCredentialStore?.read();
|
|
995
|
+
}
|
|
996
|
+
catch (error) {
|
|
997
|
+
if (!(error instanceof PrivateNodeCredentialReadError)
|
|
998
|
+
|| !hasPrivateEnrollmentFallback(runtimeEnv, storedInvitationFingerprint)) {
|
|
999
|
+
throw error;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
const runtime = await connectPrivateProxyHub({
|
|
805
1003
|
hubUrl: deps.hubUrl,
|
|
806
1004
|
senderId: deps.senderId,
|
|
807
|
-
env:
|
|
1005
|
+
env: runtimeEnv,
|
|
1006
|
+
...(storedNodeSecret ? { storedNodeSecret } : {}),
|
|
1007
|
+
...(storedInvitationFingerprint ? { storedInvitationFingerprint } : {}),
|
|
1008
|
+
...(deps.privateNodeCredentialStore ? {
|
|
1009
|
+
onNodeSecretAdopted: (nodeSecret) => {
|
|
1010
|
+
deps.privateNodeCredentialStore?.write(nodeSecret);
|
|
1011
|
+
deps.store.setState('private_node_secret_source', 'hub_enrollment');
|
|
1012
|
+
},
|
|
1013
|
+
} : {}),
|
|
1014
|
+
onInvitationRedeemed: (fingerprint) => {
|
|
1015
|
+
deps.store.setState('private_invitation_fingerprint', fingerprint);
|
|
1016
|
+
},
|
|
808
1017
|
...(deps.now ? { now: deps.now } : {}),
|
|
809
1018
|
...(deps.privateImporter ? { importer: deps.privateImporter } : {}),
|
|
810
1019
|
});
|
|
811
|
-
return {
|
|
1020
|
+
return {
|
|
1021
|
+
hub: runtime.hub,
|
|
1022
|
+
hello: runtime.hello,
|
|
1023
|
+
heartbeat: (opts) => runtime.hub.heartbeat(opts),
|
|
1024
|
+
helloMode: 'enterprise_token',
|
|
1025
|
+
};
|
|
812
1026
|
}
|
|
813
1027
|
const selection = resolvePublicNodeSecret(deps);
|
|
814
1028
|
const { nodeSecret, nodeSecretVersion } = selection;
|
|
815
1029
|
if (!nodeSecret)
|
|
816
1030
|
throw new Error('public legacy 模式需 EVOMAP_NODE_SECRET');
|
|
817
1031
|
persistSelectedPublicNodeSecret(deps.store, selection);
|
|
1032
|
+
const verifiedSender = createVerifiedPublicSender(selection.nodeId);
|
|
1033
|
+
const senderId = verifiedSender.senderId;
|
|
818
1034
|
const { hub, auth } = connectPublicHub({
|
|
819
1035
|
hubUrl: deps.hubUrl,
|
|
820
1036
|
authMode: 'legacy',
|
|
821
1037
|
nodeSecret,
|
|
822
1038
|
...(nodeSecretVersion !== undefined ? { nodeSecretVersion } : {}),
|
|
823
|
-
senderId
|
|
1039
|
+
senderId,
|
|
824
1040
|
antiAbuse: { source: 'evolver-proxy', proxyPortConfigured: true },
|
|
825
1041
|
onNodeSecretRotated: (secret, version) => {
|
|
826
|
-
deps.store
|
|
827
|
-
deps.store.setState('node_secret_source', 'hub_rotate');
|
|
828
|
-
setOptionalStoreState(deps.store, 'node_secret_version', version !== undefined ? String(version) : undefined);
|
|
1042
|
+
persistRotatedPublicNodeCredentials(deps.store, selection, secret, version);
|
|
829
1043
|
},
|
|
830
1044
|
onNodeSecretVersionUpdated: (version) => {
|
|
831
1045
|
persistPublicNodeSecretVersion(deps.store, selection, version);
|
|
@@ -836,13 +1050,32 @@ export async function connectHubRuntime(deps) {
|
|
|
836
1050
|
clearDivergedPublicNodeSecret(deps.store, process.env);
|
|
837
1051
|
},
|
|
838
1052
|
});
|
|
1053
|
+
const hello = wrapHelloWithClaimNudge(async (opts) => {
|
|
1054
|
+
const result = await hub.hello(opts);
|
|
1055
|
+
if (result.nodeId)
|
|
1056
|
+
adoptVerifiedPublicNodeId(deps.store, selection, verifiedSender, result.nodeId);
|
|
1057
|
+
return result;
|
|
1058
|
+
}, createClaimNudge({
|
|
1059
|
+
store: deps.store,
|
|
1060
|
+
hubUrl: deps.hubUrl,
|
|
1061
|
+
env: deps.env ?? process.env,
|
|
1062
|
+
...(deps.now ? { now: deps.now } : {}),
|
|
1063
|
+
}));
|
|
839
1064
|
return {
|
|
840
1065
|
hub,
|
|
841
|
-
atp: new AtpHubClient({ baseUrl: deps.hubUrl, auth, fetchFn: globalFetchLike, senderId
|
|
842
|
-
hello
|
|
1066
|
+
atp: new AtpHubClient({ baseUrl: deps.hubUrl, auth, fetchFn: globalFetchLike, senderId }),
|
|
1067
|
+
hello,
|
|
843
1068
|
heartbeat: (opts) => hub.heartbeat(opts),
|
|
844
1069
|
};
|
|
845
1070
|
}
|
|
1071
|
+
function hasPrivateEnrollmentFallback(env, storedInvitationFingerprint) {
|
|
1072
|
+
if (resolvePrivateNodeSecret(env) || resolvePrivateEnterpriseToken(env))
|
|
1073
|
+
return true;
|
|
1074
|
+
const invitationToken = resolvePrivateInvitationToken(env);
|
|
1075
|
+
if (!invitationToken)
|
|
1076
|
+
return false;
|
|
1077
|
+
return createHash('sha256').update(invitationToken).digest('hex') !== storedInvitationFingerprint;
|
|
1078
|
+
}
|
|
846
1079
|
export function resolveLegacyNodeSecret(envNodeSecret, storedNodeSecret, storedSource) {
|
|
847
1080
|
if (envNodeSecret)
|
|
848
1081
|
return envNodeSecret;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
2
3
|
import { join } from 'node:path';
|
|
3
|
-
import { events } from '@evomap/evolver-core';
|
|
4
4
|
export class AtpProxySpendConsentError extends Error {
|
|
5
5
|
source;
|
|
6
6
|
constructor(source) {
|
|
@@ -10,7 +10,10 @@ export class AtpProxySpendConsentError extends Error {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
function atpProxyConsentPath(env = process.env) {
|
|
13
|
-
const home = env['EVOMAP_DIR']
|
|
13
|
+
const home = [env['EVOMAP_DIR'], env['EVOLVER_HOME'], env['EVOMAP_HOME']]
|
|
14
|
+
.map((value) => value?.trim())
|
|
15
|
+
.find((value) => Boolean(value))
|
|
16
|
+
?? join(homedir(), '.evomap');
|
|
14
17
|
return join(home, 'evolution', 'atp-autobuy-ack.json');
|
|
15
18
|
}
|
|
16
19
|
export function getAtpProxyConsent(env = process.env, ackPath = atpProxyConsentPath(env)) {
|