@khorsheed/dsh-ankh-guard 0.1.1 → 0.2.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/CHANGELOG.md +24 -0
- package/README.en.md +75 -29
- package/README.i18n.yaml +2 -2
- package/README.md +74 -29
- package/lib/cli.js +2349 -182
- package/lib/client.js +257 -0
- package/lib/exit-agent.js +5 -2
- package/lib/index.js +721 -38
- package/lib/invariant.js +1 -1
- package/lib/preflight-runner.js +125 -47
- package/lib/processes-BjZgJjQr.js +344 -0
- package/lib/restart-context-D6nISh28.js +1245 -0
- package/lib/restart-context-DUyExi9O.js +1245 -0
- package/lib/{state-Dhx9VG44.js → state-4f7yny39.js} +60 -13
- package/lib/state-CZMypGkB.js +323 -0
- package/lib/test-seam-DnvLWTeO.js +119 -0
- package/lib/test-seam-cli.js +24 -0
- package/lib/test-seam-dwvaKjRp.js +459 -0
- package/lib/test-seam.js +2 -0
- package/lib/types/browser-handoff.d.ts +55 -0
- package/lib/types/browser-handoff.js +489 -0
- package/lib/types/cli.d.ts +34 -4
- package/lib/types/cli.js +1421 -166
- package/lib/types/client/index.d.ts +15 -0
- package/lib/types/client/index.js +264 -0
- package/lib/types/deployment-proof.d.ts +24 -0
- package/lib/types/deployment-proof.js +314 -0
- package/lib/types/exit-agent.js +2 -0
- package/lib/types/git.d.ts +12 -3
- package/lib/types/git.js +69 -7
- package/lib/types/index.d.ts +66 -3
- package/lib/types/index.js +157 -39
- package/lib/types/launch-spec.d.ts +263 -0
- package/lib/types/launch-spec.js +823 -0
- package/lib/types/preflight-runner.d.ts +23 -12
- package/lib/types/preflight-runner.js +152 -57
- package/lib/types/processes.d.ts +38 -6
- package/lib/types/processes.js +236 -10
- package/lib/types/restart-context.d.ts +50 -0
- package/lib/types/restart-context.js +106 -0
- package/lib/types/restart-request.d.ts +32 -0
- package/lib/types/restart-request.js +128 -0
- package/lib/types/state-files.d.ts +30 -0
- package/lib/types/state-files.js +55 -0
- package/lib/types/state.d.ts +29 -2
- package/lib/types/state.js +52 -7
- package/lib/types/temp-artifact.d.ts +15 -0
- package/lib/types/temp-artifact.js +17 -0
- package/lib/types/test-seam-cli.d.ts +3 -0
- package/lib/types/test-seam-cli.js +27 -0
- package/lib/types/test-seam.d.ts +55 -0
- package/lib/types/test-seam.js +112 -0
- package/lib/types/transition.d.ts +118 -0
- package/lib/types/transition.js +717 -0
- package/package.json +30 -11
- package/scripts/dsh-watchdog.sh +1388 -80
- package/scripts/install-launchd.sh +43 -5
- package/scripts/install-systemd.sh +43 -5
- package/scripts/on-install.js +1 -1
- package/skills/dsh-self-restart-guard/SKILL.md +38 -12
- package/lib/processes-hCAmwma-.js +0 -127
- package/lib/restart-context-DmnQXNf-.js +0 -421
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import type { TransitionReference } from './transition.ts';
|
|
2
|
+
export type CutoverRecoveryPolicy = 'restore-previous' | 'wait-for-user';
|
|
3
|
+
export type BrowserHandoffPolicy = 'required' | 'off';
|
|
4
|
+
export type LaunchRole = 'target' | 'previous';
|
|
5
|
+
export type CutoverControlAction = 'abort' | 'restore-previous';
|
|
6
|
+
export type PreflightSurface = 'source' | 'built';
|
|
7
|
+
/**
|
|
8
|
+
* Immutable execution binding for composition preflight. The runner and host
|
|
9
|
+
* installation are explicit because the guarded checkout and the successor's
|
|
10
|
+
* npm toolchain may be different module graphs.
|
|
11
|
+
*/
|
|
12
|
+
export interface LaunchPreflightSpec {
|
|
13
|
+
version: 1;
|
|
14
|
+
surface: PreflightSurface;
|
|
15
|
+
runnerExecutable: string;
|
|
16
|
+
runnerRuntimeArgs: string[];
|
|
17
|
+
runnerPath: string;
|
|
18
|
+
runnerSha256: string;
|
|
19
|
+
/** package.json used as createRequire()'s anchor for a built successor. */
|
|
20
|
+
installAnchor: string;
|
|
21
|
+
installAnchorSha256: string;
|
|
22
|
+
hostPackageVersion: string;
|
|
23
|
+
/** Full digest of the launch command this preflight contract approves. */
|
|
24
|
+
targetCommandSha256: string;
|
|
25
|
+
/** One-shot candidate command that validates the target CLI/argv contract. */
|
|
26
|
+
candidateProbeCommand?: string;
|
|
27
|
+
candidateProbeSha256?: string;
|
|
28
|
+
/** Guard binds and runs this caller assertion; it does not derive the probe. */
|
|
29
|
+
candidateProbeProvenance?: 'caller-supplied';
|
|
30
|
+
}
|
|
31
|
+
/** Everything needed to launch one supervised instance. The state directory is the transaction anchor. */
|
|
32
|
+
export interface LaunchSpec {
|
|
33
|
+
version: 1;
|
|
34
|
+
command: string;
|
|
35
|
+
port: number;
|
|
36
|
+
home: string;
|
|
37
|
+
/** Repository whose HEAD is bound to the guard credential and rollback. */
|
|
38
|
+
credentialRepo: string;
|
|
39
|
+
/** dsh host checkout used by preflight and exported as DSH_HARNESS. */
|
|
40
|
+
harnessRoot: string;
|
|
41
|
+
profile: string;
|
|
42
|
+
/** Absent only on launch state written before execution binding existed. */
|
|
43
|
+
preflight?: LaunchPreflightSpec;
|
|
44
|
+
}
|
|
45
|
+
export interface StableLaunchState {
|
|
46
|
+
version: 1;
|
|
47
|
+
mode: 'stable';
|
|
48
|
+
active: LaunchSpec;
|
|
49
|
+
}
|
|
50
|
+
export interface CutoverLaunchState {
|
|
51
|
+
version: 1;
|
|
52
|
+
mode: 'cutover';
|
|
53
|
+
cutoverId: string;
|
|
54
|
+
selected: LaunchRole;
|
|
55
|
+
previous: LaunchSpec;
|
|
56
|
+
target: LaunchSpec;
|
|
57
|
+
/** Immutable, cutover-scoped filesystem transition prepared before takeover. */
|
|
58
|
+
transition?: TransitionReference;
|
|
59
|
+
}
|
|
60
|
+
export type LaunchState = StableLaunchState | CutoverLaunchState;
|
|
61
|
+
export type CutoverPhase = 'prepared' | 'supervisor-starting' | 'supervisor-ready' | 'target-starting' | 'target-retrying' | 'restoring' | 'ready' | 'restored' | 'awaiting-user' | 'prepare-failed';
|
|
62
|
+
export interface LaunchSpecSummary {
|
|
63
|
+
commandSha256: string;
|
|
64
|
+
port: number;
|
|
65
|
+
home: string;
|
|
66
|
+
credentialRepo: string;
|
|
67
|
+
harnessRoot: string;
|
|
68
|
+
profile: string;
|
|
69
|
+
preflight?: {
|
|
70
|
+
surface: PreflightSurface;
|
|
71
|
+
runnerExecutable: string;
|
|
72
|
+
runnerRuntimeArgs: string[];
|
|
73
|
+
runnerPath: string;
|
|
74
|
+
runnerSha256: string;
|
|
75
|
+
installAnchor: string;
|
|
76
|
+
installAnchorSha256: string;
|
|
77
|
+
hostPackageVersion: string;
|
|
78
|
+
targetCommandSha256: string;
|
|
79
|
+
candidateProbeSha256?: string;
|
|
80
|
+
candidateProbeProvenance?: 'caller-supplied';
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export interface CutoverAttempt {
|
|
84
|
+
role: LaunchRole;
|
|
85
|
+
number: number;
|
|
86
|
+
childPid: number;
|
|
87
|
+
childStartToken?: string;
|
|
88
|
+
startedAt: number;
|
|
89
|
+
outcome?: 'failed' | 'ready';
|
|
90
|
+
detail?: string;
|
|
91
|
+
}
|
|
92
|
+
/** PID identity proof safe to persist in the redacted operational receipt. */
|
|
93
|
+
export interface CutoverProcessOwnership {
|
|
94
|
+
childPid: number;
|
|
95
|
+
childStartToken: string;
|
|
96
|
+
listenerPid: number;
|
|
97
|
+
listenerStartToken: string;
|
|
98
|
+
}
|
|
99
|
+
export interface CutoverBrowserHandoff {
|
|
100
|
+
required: boolean;
|
|
101
|
+
status: 'pending' | 'acknowledged' | 'fallback-opened' | 'off' | 'failed' | 'not-required';
|
|
102
|
+
channel?: 'original-tab' | 'fallback-tab';
|
|
103
|
+
authentication?: 'existing-cookie' | 'launch-url';
|
|
104
|
+
authority?: string;
|
|
105
|
+
acknowledgedAt?: number;
|
|
106
|
+
}
|
|
107
|
+
export interface LaunchCutoverReceipt {
|
|
108
|
+
version: 1;
|
|
109
|
+
id: string;
|
|
110
|
+
phase: CutoverPhase;
|
|
111
|
+
preparedAt: number;
|
|
112
|
+
updatedAt: number;
|
|
113
|
+
initiator?: string;
|
|
114
|
+
previous: LaunchSpecSummary;
|
|
115
|
+
target: LaunchSpecSummary;
|
|
116
|
+
/** Redacted transition evidence; the plan paths live only in launch state. */
|
|
117
|
+
transition?: {
|
|
118
|
+
planSha256: string;
|
|
119
|
+
operationCount: number;
|
|
120
|
+
phase: 'pending' | 'applied' | 'rolled-back' | 'failed';
|
|
121
|
+
failureOperation?: 'apply' | 'rollback';
|
|
122
|
+
detail?: string;
|
|
123
|
+
};
|
|
124
|
+
supervisor: {
|
|
125
|
+
previousPid: number;
|
|
126
|
+
previousStartToken: string;
|
|
127
|
+
targetDriverPid?: number;
|
|
128
|
+
targetDriverStartToken?: string;
|
|
129
|
+
targetPid?: number;
|
|
130
|
+
targetStartToken?: string;
|
|
131
|
+
previousRetirement?: 'yielded' | 'identity-gone' | 'forced';
|
|
132
|
+
};
|
|
133
|
+
child: {
|
|
134
|
+
previousPid?: number;
|
|
135
|
+
targetPid?: number;
|
|
136
|
+
restoredPid?: number;
|
|
137
|
+
};
|
|
138
|
+
ownership: {
|
|
139
|
+
previous: CutoverProcessOwnership;
|
|
140
|
+
target?: CutoverProcessOwnership;
|
|
141
|
+
restored?: CutoverProcessOwnership;
|
|
142
|
+
};
|
|
143
|
+
readiness?: {
|
|
144
|
+
role: LaunchRole;
|
|
145
|
+
childPid: number;
|
|
146
|
+
listenerPid: number;
|
|
147
|
+
stableWindowMs: number;
|
|
148
|
+
retryCount: number;
|
|
149
|
+
};
|
|
150
|
+
authentication: {
|
|
151
|
+
transportStatus?: number;
|
|
152
|
+
launchUrlObserved?: boolean;
|
|
153
|
+
exchangeStatus?: number;
|
|
154
|
+
authenticatedStatus?: number;
|
|
155
|
+
/** Compatibility summary; detailed browser evidence lives in browserHandoff. */
|
|
156
|
+
browserHandoff: CutoverBrowserHandoff['status'];
|
|
157
|
+
};
|
|
158
|
+
/** Browser acknowledgement is independent of server transport/readiness/canary. */
|
|
159
|
+
browserHandoff: CutoverBrowserHandoff;
|
|
160
|
+
/** Candidate and composition checks completed before the previous host stopped. */
|
|
161
|
+
preflight?: {
|
|
162
|
+
surface: PreflightSurface;
|
|
163
|
+
runnerExecutable: string;
|
|
164
|
+
runnerRuntimeArgs: string[];
|
|
165
|
+
runnerPath: string;
|
|
166
|
+
runnerSha256: string;
|
|
167
|
+
installAnchor: string;
|
|
168
|
+
installAnchorSha256: string;
|
|
169
|
+
hostPackageVersion: string;
|
|
170
|
+
targetCommandSha256: string;
|
|
171
|
+
candidateProbeSha256: string;
|
|
172
|
+
candidateProbeProvenance: 'caller-supplied';
|
|
173
|
+
candidateProbe: 'pass';
|
|
174
|
+
composition: 'pass';
|
|
175
|
+
};
|
|
176
|
+
/** Target evidence is retained even if recovery later becomes terminal. */
|
|
177
|
+
targetValidation?: {
|
|
178
|
+
readiness?: LaunchCutoverReceipt['readiness'];
|
|
179
|
+
canary?: {
|
|
180
|
+
outcome: 'pass' | 'fail';
|
|
181
|
+
detail?: string;
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
canary?: {
|
|
185
|
+
outcome: 'pass' | 'fail';
|
|
186
|
+
detail?: string;
|
|
187
|
+
};
|
|
188
|
+
attempts: CutoverAttempt[];
|
|
189
|
+
failureCount: {
|
|
190
|
+
target: number;
|
|
191
|
+
previous: number;
|
|
192
|
+
};
|
|
193
|
+
recovery: {
|
|
194
|
+
policy: CutoverRecoveryPolicy;
|
|
195
|
+
result: 'pending' | 'not-needed' | 'restored' | 'waiting-for-user' | 'prepare-failed';
|
|
196
|
+
detail?: string;
|
|
197
|
+
validation?: {
|
|
198
|
+
readiness?: LaunchCutoverReceipt['readiness'];
|
|
199
|
+
canary?: {
|
|
200
|
+
outcome: 'pass' | 'fail' | 'skipped';
|
|
201
|
+
detail?: string;
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
events: Array<{
|
|
206
|
+
at: number;
|
|
207
|
+
kind: string;
|
|
208
|
+
detail?: string;
|
|
209
|
+
}>;
|
|
210
|
+
}
|
|
211
|
+
export interface CutoverControlRequest {
|
|
212
|
+
version: 1;
|
|
213
|
+
cutoverId: string;
|
|
214
|
+
action: CutoverControlAction;
|
|
215
|
+
requestedAt: number;
|
|
216
|
+
}
|
|
217
|
+
export declare function commandSha256(command: string): string;
|
|
218
|
+
/** Read the selected launch state, or null for a deployment predating this protocol. */
|
|
219
|
+
export declare function readLaunchState(stateDir: string): LaunchState | null;
|
|
220
|
+
/** The exact spec a newly starting supervisor must honor. */
|
|
221
|
+
export declare function selectedLaunchSpec(state: LaunchState): LaunchSpec;
|
|
222
|
+
/** Persist an ordinary, non-transactional active spec. */
|
|
223
|
+
export declare function writeStableLaunchSpec(stateDir: string, spec: LaunchSpec, ifAbsent?: boolean): boolean;
|
|
224
|
+
export declare function summarizeLaunchSpec(spec: LaunchSpec): LaunchSpecSummary;
|
|
225
|
+
/** Safe operator view: preserve topology and selection without printing commands. */
|
|
226
|
+
export declare function summarizeLaunchState(state: LaunchState | null): unknown;
|
|
227
|
+
/** Read the durable receipt, or null when absent/malformed. */
|
|
228
|
+
export declare function readCutoverReceipt(stateDir: string): LaunchCutoverReceipt | null;
|
|
229
|
+
/** Persist an operator control request for the current watchdog to consume. */
|
|
230
|
+
export declare function writeCutoverControl(stateDir: string, cutoverId: string, action: CutoverControlAction, now: number): CutoverControlRequest;
|
|
231
|
+
export declare function readCutoverControl(stateDir: string): CutoverControlRequest | null;
|
|
232
|
+
export declare function clearCutoverControl(stateDir: string, cutoverId: string): void;
|
|
233
|
+
/**
|
|
234
|
+
* Prepare one cutover. The receipt lands first; the single launch-state rename
|
|
235
|
+
* is the commit point selecting target. A crash before that rename leaves the
|
|
236
|
+
* previous stable state authoritative and the old supervisor untouched.
|
|
237
|
+
*/
|
|
238
|
+
export declare function prepareLaunchCutover(stateDir: string, input: {
|
|
239
|
+
id: string;
|
|
240
|
+
previous: LaunchSpec;
|
|
241
|
+
target: LaunchSpec;
|
|
242
|
+
recoveryPolicy: CutoverRecoveryPolicy;
|
|
243
|
+
browserHandoff: BrowserHandoffPolicy;
|
|
244
|
+
previousSupervisorPid: number;
|
|
245
|
+
previousSupervisorStartToken: string;
|
|
246
|
+
previousOwnership: CutoverProcessOwnership;
|
|
247
|
+
transition?: TransitionReference;
|
|
248
|
+
initiator?: string;
|
|
249
|
+
now: number;
|
|
250
|
+
}): LaunchCutoverReceipt;
|
|
251
|
+
/**
|
|
252
|
+
* Apply one watchdog event. Arguments are deliberately credential-free; the
|
|
253
|
+
* launch URL itself never crosses this boundary or lands in the receipt.
|
|
254
|
+
*/
|
|
255
|
+
export declare function recordCutoverEvent(stateDir: string, id: string, kind: string, args: readonly string[], now: number): LaunchCutoverReceipt;
|
|
256
|
+
/** A restarted instance must not wake/report while the watchdog is still proving the cutover. */
|
|
257
|
+
export declare function cutoverBlocksWake(stateDir: string): boolean;
|
|
258
|
+
/** The active transaction's full specs plus its redacted policy receipt. */
|
|
259
|
+
export declare function activeCutover(stateDir: string): {
|
|
260
|
+
state: CutoverLaunchState;
|
|
261
|
+
receipt: LaunchCutoverReceipt;
|
|
262
|
+
} | null;
|
|
263
|
+
//# sourceMappingURL=launch-spec.d.ts.map
|