@evomap/evolver-proxy 2.0.0-beta.19 → 2.0.0-beta.22
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 +12 -0
- package/dist/bin/evolver-proxy.js +257 -56
- package/dist/daemon/proxyDaemon.d.ts +12 -0
- package/dist/daemon/proxyDaemon.js +313 -18
- package/dist/daemon/systemdNotifier.d.ts +2 -0
- package/dist/daemon/systemdNotifier.js +11 -1
- package/dist/llm/upstream.js +54 -7
- package/dist/private/accountAssetCompatibility.d.ts +1 -0
- package/dist/private/accountAssetCompatibility.js +3 -3
- package/dist/private/adapterLoader.js +4 -3
- package/dist/router/messagesRoute.js +9 -3
- package/dist/router/providerRoutes.js +7 -3
- package/dist/selfUpdate/bootstrap.d.ts +162 -0
- package/dist/selfUpdate/bootstrap.js +3524 -0
- package/dist/selfUpdate/bootstrapReadiness.d.ts +9 -0
- package/dist/selfUpdate/bootstrapReadiness.js +153 -0
- package/dist/selfUpdate/builtinKey.d.ts +4 -0
- package/dist/selfUpdate/builtinKey.js +16 -0
- package/dist/selfUpdate/controllerLifecycleAuthority.d.ts +45 -0
- package/dist/selfUpdate/controllerLifecycleAuthority.js +61 -0
- package/dist/selfUpdate/executor.d.ts +18 -7
- package/dist/selfUpdate/executor.js +159 -59
- package/dist/selfUpdate/failureCodes.d.ts +4 -0
- package/dist/selfUpdate/failureCodes.js +7 -0
- package/dist/selfUpdate/index.d.ts +2 -1
- package/dist/selfUpdate/index.js +2 -1
- package/dist/selfUpdate/migration.d.ts +158 -0
- package/dist/selfUpdate/migration.js +2672 -0
- package/dist/selfUpdate/policy.d.ts +19 -2
- package/dist/selfUpdate/policy.js +76 -2
- package/dist/selfUpdate/recoveryChildStartGate.d.ts +29 -0
- package/dist/selfUpdate/recoveryChildStartGate.js +319 -0
- package/dist/selfUpdate/releaseBinary.d.ts +3 -0
- package/dist/selfUpdate/releaseBinary.js +50 -4
- package/dist/selfUpdate/transaction.d.ts +8 -0
- package/dist/selfUpdate/transaction.js +166 -18
- package/dist/selfUpdate/unixController.d.ts +8 -0
- package/dist/selfUpdate/unixController.js +366 -38
- package/dist/selfUpdate/windowsController.d.ts +14 -2
- package/dist/selfUpdate/windowsController.js +484 -103
- package/dist/selfUpdate/windowsUpdater.d.ts +25 -0
- package/dist/selfUpdate/windowsUpdater.js +174 -7
- package/package.json +3 -3
package/dist/llm/upstream.js
CHANGED
|
@@ -39,15 +39,44 @@ function isOpenAiMode(upstreamMode) {
|
|
|
39
39
|
* Warn once per process to avoid per-request spam.
|
|
40
40
|
*/
|
|
41
41
|
let warnedDeprecatedOpenAICompatible = false;
|
|
42
|
+
function hasDeprecatedOpenAICompatibleConfig(env) {
|
|
43
|
+
return [
|
|
44
|
+
env['EVOLVER_OPENAI_COMPATIBLE_BASE_URLS'],
|
|
45
|
+
env['EVOMAP_OPENAI_COMPATIBLE_BASE_URLS'],
|
|
46
|
+
].some((value) => typeof value === 'string' && value.trim() !== '');
|
|
47
|
+
}
|
|
48
|
+
function explicitOpenAIBaseUrl(env) {
|
|
49
|
+
return env['EVOLVER_LLM_OPENAI_BASE_URL'] || env['EVOMAP_OPENAI_BASE_URL'] || env['OPENAI_BASE_URL'];
|
|
50
|
+
}
|
|
51
|
+
function hasCanonicalOpenAIMigrationPair(env) {
|
|
52
|
+
const baseUrl = env['EVOLVER_LLM_OPENAI_BASE_URL']?.trim();
|
|
53
|
+
if (!baseUrl || !env['EVOLVER_LLM_OPENAI_API_KEY']?.trim())
|
|
54
|
+
return false;
|
|
55
|
+
try {
|
|
56
|
+
normalizeOpenAIBaseUrl(baseUrl);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
42
63
|
function warnDeprecatedOpenAICompatible(env) {
|
|
43
|
-
|
|
44
|
-
if (!deprecatedValue || warnedDeprecatedOpenAICompatible)
|
|
64
|
+
if (!hasDeprecatedOpenAICompatibleConfig(env) || warnedDeprecatedOpenAICompatible)
|
|
45
65
|
return;
|
|
46
66
|
warnedDeprecatedOpenAICompatible = true;
|
|
47
67
|
console.warn('[proxy] DEPRECATED: EVOMAP_OPENAI_COMPATIBLE_BASE_URLS / EVOLVER_OPENAI_COMPATIBLE_BASE_URLS is ignored for routing. ' +
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
68
|
+
'Manual migration is required: explicitly bind one official https://*.api.openai.com/v1 endpoint and its credential ' +
|
|
69
|
+
'with EVOLVER_LLM_OPENAI_BASE_URL and EVOLVER_LLM_OPENAI_API_KEY. ' +
|
|
70
|
+
'LiteLLM, OpenRouter, Azure OpenAI, MiniMax, DeepSeek, Moonshot, and other custom OpenAI-compatible hosts have no drop-in V2 route. ' +
|
|
71
|
+
'Do not reuse their credentials as an OpenAI credential; keep that workload outside this proxy until a supported provider-specific route exists.');
|
|
72
|
+
}
|
|
73
|
+
function assertDeprecatedOpenAICompatibleMigrated(env) {
|
|
74
|
+
warnDeprecatedOpenAICompatible(env);
|
|
75
|
+
if (hasDeprecatedOpenAICompatibleConfig(env) && !hasCanonicalOpenAIMigrationPair(env)) {
|
|
76
|
+
throw new Error('[proxy] manual migration of deprecated OpenAI-compatible base URLs requires an explicit '
|
|
77
|
+
+ 'EVOLVER_LLM_OPENAI_BASE_URL and '
|
|
78
|
+
+ 'EVOLVER_LLM_OPENAI_API_KEY migration pair before the OpenAI route can start');
|
|
79
|
+
}
|
|
51
80
|
}
|
|
52
81
|
/** Test helper: reset once-warn latch (unit tests only). */
|
|
53
82
|
function resetDeprecatedOpenAICompatibleWarning() {
|
|
@@ -77,9 +106,23 @@ function normalizeOpenAIBaseUrl(raw) {
|
|
|
77
106
|
}
|
|
78
107
|
return value;
|
|
79
108
|
}
|
|
109
|
+
function openAIBaseUrlIdentity(raw) {
|
|
110
|
+
return new URL(normalizeOpenAIBaseUrl(raw)).href;
|
|
111
|
+
}
|
|
112
|
+
function assertDeprecatedOpenAIRequestBaseBound(env, requestBaseUrl) {
|
|
113
|
+
if (!requestBaseUrl || !hasDeprecatedOpenAICompatibleConfig(env))
|
|
114
|
+
return;
|
|
115
|
+
const canonicalBaseUrl = env['EVOLVER_LLM_OPENAI_BASE_URL'];
|
|
116
|
+
if (typeof canonicalBaseUrl !== 'string'
|
|
117
|
+
|| openAIBaseUrlIdentity(requestBaseUrl) !== openAIBaseUrlIdentity(canonicalBaseUrl)) {
|
|
118
|
+
throw new Error('[proxy] request-scoped OpenAI base URL must match EVOLVER_LLM_OPENAI_BASE_URL '
|
|
119
|
+
+ 'while deprecated OpenAI-compatible routing remains configured');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
80
122
|
export function resolveOpenAIUpstreamUrl(env = process.env) {
|
|
81
|
-
|
|
82
|
-
|
|
123
|
+
assertDeprecatedOpenAICompatibleMigrated(env);
|
|
124
|
+
const explicitBaseUrl = explicitOpenAIBaseUrl(env);
|
|
125
|
+
return normalizeOpenAIBaseUrl(explicitBaseUrl || DEFAULT_OPENAI_UPSTREAM_URL);
|
|
83
126
|
}
|
|
84
127
|
function pathForOpenAIBase(path) {
|
|
85
128
|
if (path === '/v1')
|
|
@@ -478,6 +521,10 @@ export function makeOpenAIUpstream(opts = {}) {
|
|
|
478
521
|
const headersTimeoutMs = opts.headersTimeoutMs ?? DEFAULT_HEADERS_TIMEOUT_MS;
|
|
479
522
|
return async (path, body, callOpts) => {
|
|
480
523
|
const env = opts.env ?? process.env;
|
|
524
|
+
// A request-scoped base override is routing data, not evidence that the daemon's
|
|
525
|
+
// legacy provider credential/base pair was deliberately migrated.
|
|
526
|
+
assertDeprecatedOpenAICompatibleMigrated(env);
|
|
527
|
+
assertDeprecatedOpenAIRequestBaseBound(env, callOpts.baseUrl);
|
|
481
528
|
const baseUrl = callOpts.baseUrl ? normalizeOpenAIBaseUrl(callOpts.baseUrl) : resolveOpenAIUpstreamUrl(env);
|
|
482
529
|
return fetchUpstream(`${baseUrl}${path}`, body, callOpts, buildOpenAIHeaders(callOpts.inboundHeaders, env), fetchImpl, headersTimeoutMs, 'openai', (headers) => contentTypeIncludes(headers, 'text/event-stream'));
|
|
483
530
|
};
|
|
@@ -25,4 +25,5 @@ interface PrivateAccountAssetCompatibilityOptions {
|
|
|
25
25
|
* future adapter's native implementation.
|
|
26
26
|
*/
|
|
27
27
|
export declare function withPrivateAccountAssetCompatibility<T extends object>(hubCapability: T, opts: PrivateAccountAssetCompatibilityOptions): T & PrivateAccountAssetHub;
|
|
28
|
+
export declare function normalizePrivateHubBaseUrl(raw: string, env: Record<string, string | undefined>): string;
|
|
28
29
|
export {};
|
|
@@ -159,14 +159,14 @@ function malformedPublishedPage() {
|
|
|
159
159
|
context: `GET ${PRIVATE_PUBLISHED_ASSETS_PATH}`,
|
|
160
160
|
});
|
|
161
161
|
}
|
|
162
|
-
function normalizePrivateHubBaseUrl(raw, env) {
|
|
162
|
+
export function normalizePrivateHubBaseUrl(raw, env) {
|
|
163
163
|
const normalized = raw.trim().replace(/\/+$/, '');
|
|
164
164
|
assertPrivateCompatibilityUrlSecure(normalized, env);
|
|
165
165
|
const parsed = new URL(normalized);
|
|
166
|
-
if (parsed.username || parsed.password ||
|
|
166
|
+
if (parsed.username || parsed.password || normalized.includes('?') || normalized.includes('#')) {
|
|
167
167
|
throw new Error('Private Hub URL must not contain credentials, query parameters, or a fragment');
|
|
168
168
|
}
|
|
169
|
-
return
|
|
169
|
+
return parsed.href.replace(/[/]+$/, '');
|
|
170
170
|
}
|
|
171
171
|
function assertPrivateCompatibilityUrlSecure(url, env) {
|
|
172
172
|
let parsed;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { hub as hubNs } from '@evomap/evolver-core';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
|
-
import { withPrivateAccountAssetCompatibility, } from './accountAssetCompatibility.js';
|
|
3
|
+
import { normalizePrivateHubBaseUrl, withPrivateAccountAssetCompatibility, } from './accountAssetCompatibility.js';
|
|
4
4
|
const DEFAULT_PRIVATE_ADAPTER_MODULE = '@evomap/evolver-adapter-private';
|
|
5
5
|
export function resolvePrivateEnterpriseToken(env) {
|
|
6
6
|
return firstEnv(env, 'EVOMAP_ENTERPRISE_TOKEN', 'EVOMAP_PRIVATE_HUB_TOKEN', 'PHUB_ENTERPRISE_TOKEN', 'PRIVATE_HUB_ENTERPRISE_TOKEN');
|
|
@@ -33,12 +33,13 @@ export async function connectPrivateProxyHub(opts) {
|
|
|
33
33
|
if (!token && !usableInvitationToken && !nodeSecret) {
|
|
34
34
|
throw new Error('EVOMAP_HUB_MODE=private 需要 A2A_NODE_SECRET / EVOMAP_NODE_SECRET、A2A_INVITATION_TOKEN 或 EVOMAP_ENTERPRISE_TOKEN');
|
|
35
35
|
}
|
|
36
|
+
const hubUrl = normalizePrivateHubBaseUrl(opts.hubUrl, opts.env);
|
|
36
37
|
const moduleName = opts.env['EVOMAP_PRIVATE_ADAPTER_MODULE']?.trim() || DEFAULT_PRIVATE_ADAPTER_MODULE;
|
|
37
38
|
const connectPrivateHub = await loadConnectPrivateHub(moduleName, opts.importer ?? ((specifier) => import(specifier)));
|
|
38
39
|
const now = opts.now ?? (() => Date.now());
|
|
39
40
|
const subject = resolvePrivateEnterpriseSubject(opts.env);
|
|
40
41
|
const baseConnectionOptions = {
|
|
41
|
-
hubUrl
|
|
42
|
+
hubUrl,
|
|
42
43
|
senderId: opts.senderId,
|
|
43
44
|
env: opts.env,
|
|
44
45
|
now,
|
|
@@ -79,7 +80,7 @@ export async function connectPrivateProxyHub(opts) {
|
|
|
79
80
|
hub.agentDirectory = hubNs.unsupportedAgentDirectoryCapability('private_hub_agent_directory_not_supported');
|
|
80
81
|
}
|
|
81
82
|
const compatibleHub = withPrivateAccountAssetCompatibility(hub, {
|
|
82
|
-
baseUrl:
|
|
83
|
+
baseUrl: hubUrl,
|
|
83
84
|
auth,
|
|
84
85
|
senderId: opts.senderId,
|
|
85
86
|
env: opts.env,
|
|
@@ -235,9 +235,15 @@ function sessionIdFromUserField(value) {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
if (parsed && typeof parsed === 'object') {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
238
|
+
// Accept both spellings. The archive-side session key (agentic-trace-pipeline
|
|
239
|
+
// derive_session_key) reads `session_id ?? sessionId` from this same field, so a
|
|
240
|
+
// camelCase producer yields a `cc::<sid>` key upstream while we recorded null —
|
|
241
|
+
// the two datasets then silently fail to join. Both spellings still pass through
|
|
242
|
+
// safePlainSessionId; this widens the accepted key name, never the value guard.
|
|
243
|
+
const raw = parsed['session_id']
|
|
244
|
+
?? parsed['sessionId'];
|
|
245
|
+
if (typeof raw === 'string' && raw.length > 0)
|
|
246
|
+
return safePlainSessionId(raw);
|
|
241
247
|
}
|
|
242
248
|
return '';
|
|
243
249
|
}
|
|
@@ -998,9 +998,13 @@ function sessionIdFromUserField(value) {
|
|
|
998
998
|
}
|
|
999
999
|
}
|
|
1000
1000
|
if (parsed && typeof parsed === 'object') {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1001
|
+
// Accept both spellings — see the matching note in messagesRoute.ts. The
|
|
1002
|
+
// archive-side session key reads `session_id ?? sessionId` from this field, so
|
|
1003
|
+
// dropping camelCase here costs a join, not just a field.
|
|
1004
|
+
const raw = parsed['session_id']
|
|
1005
|
+
?? parsed['sessionId'];
|
|
1006
|
+
if (typeof raw === 'string' && raw.length > 0)
|
|
1007
|
+
return safePlainSessionId(raw);
|
|
1004
1008
|
}
|
|
1005
1009
|
return '';
|
|
1006
1010
|
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { util } from '@evomap/evolver-core';
|
|
3
|
+
import { type MigrationOptions } from './migration.js';
|
|
4
|
+
export declare const RECOVERY_CONTROLLER_LIFECYCLE_OWNER_CAPABILITY_ENV = "EVOLVER_INTERNAL_RECOVERY_CONTROLLER_LIFECYCLE_OWNER";
|
|
5
|
+
type BootstrapProcessKill = (pid: number, signal: NodeJS.Signals | 0) => boolean;
|
|
6
|
+
type BootstrapSkipReason = 'already_supervised' | 'already_bootstrapped' | 'unsupported_install_shape' | 'policy_not_auto' | 'bootstrap_disabled' | 'ci_environment' | 'container_environment' | 'recent_failure' | 'migration_ambiguous' | 'bootstrap_attempt_invalid' | 'bootstrap_intent_pending' | 'bootstrap_attempt_pending';
|
|
7
|
+
export interface BootstrapDecision {
|
|
8
|
+
proceed: boolean;
|
|
9
|
+
reason?: BootstrapSkipReason;
|
|
10
|
+
}
|
|
11
|
+
export interface BootstrapOutcome {
|
|
12
|
+
ok: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 'bootstrapped' / 'bootstrapped_lock_release_unconfirmed' / 'migrated' on success;
|
|
15
|
+
* failure/skip reason otherwise. Migration failures record 'migration_failed' /
|
|
16
|
+
* 'migration_timeout' (both cooldown-worthy).
|
|
17
|
+
*/
|
|
18
|
+
reason: string;
|
|
19
|
+
detail?: string;
|
|
20
|
+
/** The child may own manager or IPC state, so the foreground proxy must exit. */
|
|
21
|
+
requiresForegroundExit?: true;
|
|
22
|
+
}
|
|
23
|
+
export interface BootstrapRunOptions {
|
|
24
|
+
env: NodeJS.ProcessEnv;
|
|
25
|
+
platform?: NodeJS.Platform;
|
|
26
|
+
execPath?: string;
|
|
27
|
+
argv1?: string;
|
|
28
|
+
/** Parent force-timeout; it must exceed the child's transaction budget. */
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
/** Absolute child deadline offset. Production uses the complete transaction budget. */
|
|
31
|
+
transactionBudgetMs?: number;
|
|
32
|
+
/** Bounded wait for OS confirmation after force-terminating the process tree. */
|
|
33
|
+
terminationGraceMs?: number;
|
|
34
|
+
now?: number;
|
|
35
|
+
exists?: (path: string) => boolean;
|
|
36
|
+
readFile?: (path: string) => string;
|
|
37
|
+
writeFile?: (path: string, content: string) => void;
|
|
38
|
+
spawnFn?: typeof spawn;
|
|
39
|
+
treeKillSpawnFn?: typeof spawn;
|
|
40
|
+
processKill?: BootstrapProcessKill;
|
|
41
|
+
/** Test-only failure seam; production publication always uses the strict exclusive writer. */
|
|
42
|
+
beforeIntentPublish?: () => void;
|
|
43
|
+
/** Test-only crash seam for each durable initial-publication boundary. */
|
|
44
|
+
afterIntentPublicationStep?: (step: 'create' | 'partial_write' | 'file_fsync' | 'link' | 'directory_fsync', path: string) => void;
|
|
45
|
+
/** Test-only crash seams around the atomic terminal/clear state transitions. */
|
|
46
|
+
beforeIntentTerminalFsync?: (path: string) => void;
|
|
47
|
+
afterIntentTerminalPublish?: () => void;
|
|
48
|
+
afterIntentClearRename?: () => void;
|
|
49
|
+
/** Test-only trust seams. Production always uses native owner/DACL validation. */
|
|
50
|
+
assertIntentDirectoryTrust?: (directory: string) => void;
|
|
51
|
+
assertIntentFileTrust?: (path: string) => void;
|
|
52
|
+
assertLegacyProofDirectoryTrust?: (directory: string) => void;
|
|
53
|
+
assertLegacyProofFileTrust?: (path: string) => void;
|
|
54
|
+
afterLegacyProofRead?: (path: string) => void;
|
|
55
|
+
/** Test-only process identity seams; production uses fresh native core observations. */
|
|
56
|
+
readRegistrationProcessStartIdentity?: (pid: number) => util.FileLockProcessStartIdentity | null;
|
|
57
|
+
registrationOwnerProcessStatus?: (owner: Pick<util.FileLockOwnerRecord, 'pid' | 'processStartIdentity'>) => util.FileLockOwnerProcessStatus;
|
|
58
|
+
registrationPublisherProcessStatus?: (publisher: Readonly<{
|
|
59
|
+
pid: number;
|
|
60
|
+
token: string;
|
|
61
|
+
processIdentityDigest: string;
|
|
62
|
+
}>) => util.FileLockOwnerProcessStatus;
|
|
63
|
+
/**
|
|
64
|
+
* Extra seams forwarded to the one-time npm/JS → standalone migration (migration.ts).
|
|
65
|
+
* Bootstrap-level seams (exists/readFile/writeFile/spawnFn/now/execPath) win when both
|
|
66
|
+
* are supplied, so the decision and the migration observe the same injected world.
|
|
67
|
+
*/
|
|
68
|
+
migration?: MigrationOptions;
|
|
69
|
+
}
|
|
70
|
+
/** Lifecycle state dir mirror of evolver-cli lifecyclePaths (kept dependency-free across packages). */
|
|
71
|
+
export declare function resolveBootstrapStateDir(env: NodeJS.ProcessEnv): string;
|
|
72
|
+
export declare function withRecoveryControllerLifecycleOwnerCapability(env: NodeJS.ProcessEnv, owner: util.FileLockOwnerRecord): NodeJS.ProcessEnv;
|
|
73
|
+
export interface PreparedRecoveryControllerLifecycleOwnerCapability {
|
|
74
|
+
env: NodeJS.ProcessEnv;
|
|
75
|
+
startupAckToken: string;
|
|
76
|
+
}
|
|
77
|
+
export declare function prepareRecoveryControllerLifecycleOwnerCapability(env: NodeJS.ProcessEnv, owner: util.FileLockOwnerRecord): PreparedRecoveryControllerLifecycleOwnerCapability;
|
|
78
|
+
export declare function clearRecoveryControllerLifecycleOwnerCapability(env: NodeJS.ProcessEnv): void;
|
|
79
|
+
export declare function publishRecoveryControllerLifecycleStartupAttestation(env: NodeJS.ProcessEnv, descriptor?: number): boolean;
|
|
80
|
+
export declare function lifecycleBootstrapStatePresent(env: NodeJS.ProcessEnv, exists?: (path: string) => boolean): boolean;
|
|
81
|
+
export type BootstrapDurableStateOptions = Pick<BootstrapRunOptions, 'exists' | 'readFile' | 'assertIntentFileTrust' | 'assertLegacyProofDirectoryTrust' | 'assertLegacyProofFileTrust' | 'afterLegacyProofRead' | 'registrationOwnerProcessStatus' | 'now'> & {
|
|
82
|
+
/** Exact owner currently holding the shared lifecycle mutation lock. */
|
|
83
|
+
expectedRecoveryOwner?: util.FileLockOwnerRecord;
|
|
84
|
+
};
|
|
85
|
+
export interface LifecycleBootstrapOwnerLease {
|
|
86
|
+
readonly path: string;
|
|
87
|
+
readonly owner: util.FileLockOwnerRecord;
|
|
88
|
+
assertOwned(): void;
|
|
89
|
+
armProcess(pid: number): util.FileLockOwnerRecord;
|
|
90
|
+
disarmProcess(): void;
|
|
91
|
+
retainProcess(): void;
|
|
92
|
+
transferToProcess(pid: number): util.FileLockOwnerRecord;
|
|
93
|
+
release(): void;
|
|
94
|
+
}
|
|
95
|
+
export declare function assertSupervisedLifecycleBootstrapState(env: NodeJS.ProcessEnv, options?: BootstrapDurableStateOptions & {
|
|
96
|
+
requireLifecycleState?: boolean;
|
|
97
|
+
/** Test-only parent identity seam; production always binds to process.ppid. */
|
|
98
|
+
recoveryControllerParentPid?: number;
|
|
99
|
+
}): void;
|
|
100
|
+
/**
|
|
101
|
+
* Revalidate the narrow parent-owned activation window used by a newly launched recovery
|
|
102
|
+
* controller. This deliberately rejects committed supervision: callers use it only as delegated
|
|
103
|
+
* authority while the lifecycle parent still owns the mutation lock and is waiting for readiness.
|
|
104
|
+
*/
|
|
105
|
+
export declare function assertActiveSupervisedLifecycleBootstrapDelegation(env: NodeJS.ProcessEnv, options?: BootstrapDurableStateOptions): void;
|
|
106
|
+
/**
|
|
107
|
+
* Revalidate a transaction-bound launcher before any self-update operation. Unlike the startup
|
|
108
|
+
* assertion above, this never accepts the narrow activating window used to publish readiness.
|
|
109
|
+
*/
|
|
110
|
+
export declare function assertCommittedLifecycleBootstrapState(env: NodeJS.ProcessEnv, options?: BootstrapDurableStateOptions): void;
|
|
111
|
+
/**
|
|
112
|
+
* Serialize self-update with every lifecycle bootstrap, recovery, and manual-transition writer.
|
|
113
|
+
* Acquisition is deliberately fail-fast: a heartbeat must not block the daemon while another
|
|
114
|
+
* lifecycle owner is active. Transaction-bound launchers revalidate the committed receipt under
|
|
115
|
+
* the exact acquired generation; legacy launchers still hold and recheck the shared owner lock.
|
|
116
|
+
*/
|
|
117
|
+
export declare function acquireLifecycleBootstrapOwnerLease(env: NodeJS.ProcessEnv, lockOptions?: {
|
|
118
|
+
maxTries?: number;
|
|
119
|
+
waitMs?: number;
|
|
120
|
+
}): LifecycleBootstrapOwnerLease;
|
|
121
|
+
export declare function looksLikeContainer(exists: (path: string) => boolean, readFile: (path: string) => string): boolean;
|
|
122
|
+
/** True when a recent bootstrap/migration attempt failed within the cooldown window. */
|
|
123
|
+
export declare function recentBootstrapFailure(env: NodeJS.ProcessEnv, optionsOrReadFile: Pick<BootstrapRunOptions, 'exists' | 'readFile' | 'assertIntentFileTrust'> | ((path: string) => string), now: number): boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Decide whether an unsupervised (degraded) startup should attempt first-run bootstrap.
|
|
126
|
+
* Pure — filesystem access is injectable for tests.
|
|
127
|
+
*/
|
|
128
|
+
export declare function shouldBootstrap(env: NodeJS.ProcessEnv, platform?: NodeJS.Platform, options?: Pick<BootstrapRunOptions, 'exists' | 'readFile' | 'now' | 'execPath' | 'assertIntentFileTrust'>): BootstrapDecision;
|
|
129
|
+
/**
|
|
130
|
+
* Resolve the `lifecycle bootstrap` invocation for the current install shape: standalone binary,
|
|
131
|
+
* CLI entry through node, or the npm-installed @evomap/evolver-cli sibling. Returns undefined when
|
|
132
|
+
* no CLI can be located. The caller may continue only after confirming durable state is clean.
|
|
133
|
+
*/
|
|
134
|
+
export declare function resolveBootstrapCliInvocation(options?: Pick<BootstrapRunOptions, 'execPath' | 'argv1' | 'exists'>): {
|
|
135
|
+
command: string;
|
|
136
|
+
args: string[];
|
|
137
|
+
} | undefined;
|
|
138
|
+
/** Best-effort attempt marker; never throws — bootstrap bookkeeping must not break startup. */
|
|
139
|
+
export declare function recordBootstrapAttempt(env: NodeJS.ProcessEnv, outcome: BootstrapOutcome, options?: Pick<BootstrapRunOptions, 'writeFile' | 'now'>): void;
|
|
140
|
+
/** Spawn `evolver lifecycle bootstrap` and await its result within a bounded timeout. */
|
|
141
|
+
export declare function runBootstrap(options: BootstrapRunOptions): Promise<BootstrapOutcome>;
|
|
142
|
+
export type DegradedStartupBootstrapResult = {
|
|
143
|
+
disposition: 'continue';
|
|
144
|
+
handedOver: false;
|
|
145
|
+
message: string;
|
|
146
|
+
} | {
|
|
147
|
+
disposition: 'handoff';
|
|
148
|
+
handedOver: true;
|
|
149
|
+
exitCode: 0;
|
|
150
|
+
message: string;
|
|
151
|
+
} | {
|
|
152
|
+
disposition: 'fail_closed';
|
|
153
|
+
handedOver: false;
|
|
154
|
+
exitCode: 1;
|
|
155
|
+
message: string;
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Orchestrate bootstrap for a degraded (default-auto, unsupervised) startup: decide, attempt,
|
|
159
|
+
* record, and produce the operator message. Never throws.
|
|
160
|
+
*/
|
|
161
|
+
export declare function bootstrapDegradedSelfUpdateStartup(env: NodeJS.ProcessEnv, platform?: NodeJS.Platform, options?: Omit<BootstrapRunOptions, 'env' | 'platform'>): Promise<DegradedStartupBootstrapResult>;
|
|
162
|
+
export {};
|