@agentteams/runner 0.0.104 → 0.0.105

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.
@@ -1,18 +1,20 @@
1
1
  import { platform as getPlatform } from 'node:os';
2
- import { getAutostartStatus, restartAutostartService, scheduleWindowsTaskRestart } from './autostart.js';
2
+ import { getAutostartStatus, registerWindowsTask, restartAutostartService, scheduleWindowsTaskRestart } from './autostart.js';
3
3
  import { logger } from './logger.js';
4
+ import { acknowledgePreparedRestartHandoff, waitForPreparedRestartHandoff, type RestartExecutionResult, type RestartHandoffLaunch, type RestartHandoffPreparation } from './restart-handoff.js';
4
5
  type RunningDaemonStatus = {
5
6
  running: boolean;
6
7
  pid: number | null;
7
8
  };
8
9
  type DetachedChildProcess = {
10
+ pid?: number;
9
11
  unref: () => void;
10
12
  };
11
13
  type RestartDeps = {
12
14
  getDaemonStatus?: () => Promise<RunningDaemonStatus>;
13
15
  getAutostartStatus?: typeof getAutostartStatus;
14
16
  restartAutostartService?: typeof restartAutostartService;
15
- spawnDetachedDaemon?: () => DetachedChildProcess | void;
17
+ spawnDetachedDaemon?: (launch?: RestartHandoffLaunch) => DetachedChildProcess | void;
16
18
  kill?: typeof process.kill;
17
19
  sleep?: (milliseconds: number) => Promise<void>;
18
20
  logger?: Pick<typeof logger, 'info'>;
@@ -20,9 +22,17 @@ type RestartDeps = {
20
22
  type ExecuteRestartDeps = {
21
23
  getAutostartStatus?: typeof getAutostartStatus;
22
24
  scheduleWindowsTaskRestart?: typeof scheduleWindowsTaskRestart;
23
- spawnDetachedDaemon?: () => DetachedChildProcess | void;
25
+ registerWindowsTask?: typeof registerWindowsTask;
26
+ prepareDetachedDaemon?: () => Promise<RestartHandoffPreparation>;
27
+ spawnDetachedDaemon?: (launch?: RestartHandoffLaunch) => DetachedChildProcess | void;
28
+ acknowledgeRestart?: () => Promise<void>;
29
+ acknowledgePreparedHandoff?: typeof acknowledgePreparedRestartHandoff;
30
+ config?: {
31
+ daemonToken: string;
32
+ apiUrl: string;
33
+ };
24
34
  platform?: typeof getPlatform;
25
- processExit?: (code: number) => never;
35
+ processExit?: (code: number) => void;
26
36
  logger?: Pick<typeof logger, 'info'>;
27
37
  };
28
38
  type WaitForStartDeps = {
@@ -32,7 +42,16 @@ type WaitForStartDeps = {
32
42
  timeoutMs?: number;
33
43
  };
34
44
  export declare const waitForDaemonToStart: (deps?: WaitForStartDeps) => Promise<RunningDaemonStatus>;
35
- export declare const spawnDetachedDaemon: () => DetachedChildProcess | void;
36
- export declare const executeRestartRequest: (deps?: ExecuteRestartDeps) => void;
45
+ export declare const spawnDetachedDaemon: (launch?: RestartHandoffLaunch) => DetachedChildProcess | void;
46
+ type PrepareDetachedRestartDeps = {
47
+ spawnDetachedDaemon?: (launch: RestartHandoffLaunch) => DetachedChildProcess | void;
48
+ waitForPreparedRestartHandoff?: typeof waitForPreparedRestartHandoff;
49
+ unlink?: (path: string) => Promise<void>;
50
+ handoffId?: string;
51
+ parentPid?: number;
52
+ markerPath?: string;
53
+ };
54
+ export declare const prepareDetachedRestartHandoff: (deps?: PrepareDetachedRestartDeps) => Promise<RestartHandoffPreparation>;
55
+ export declare const executeRestartRequest: (deps?: ExecuteRestartDeps) => Promise<RestartExecutionResult>;
37
56
  export declare const restartDaemon: (deps?: RestartDeps) => Promise<void>;
38
57
  export {};
@@ -1,9 +1,12 @@
1
1
  import { setTimeout as delay } from 'node:timers/promises';
2
+ import { randomUUID } from 'node:crypto';
2
3
  import { platform as getPlatform } from 'node:os';
3
- import { getAutostartStatus, launchWindowsHiddenDaemon, restartAutostartService, scheduleWindowsTaskRestart, } from './autostart.js';
4
+ import { getAutostartStatus, launchWindowsHiddenDaemon, registerWindowsTask, restartAutostartService, scheduleWindowsTaskRestart, } from './autostart.js';
4
5
  import { logger } from './logger.js';
5
6
  import { getDaemonStatus } from './pid.js';
6
7
  import { spawnExecutable } from './executable.js';
8
+ import { acknowledgePreparedRestartHandoff, buildRestartHandoffEnv, getRestartHandoffPath, waitForPreparedRestartHandoff, } from './restart-handoff.js';
9
+ import { promises as fs } from 'node:fs';
7
10
  const restartPollIntervalMs = 100;
8
11
  const stopTimeoutMs = 10_000;
9
12
  const startTimeoutMs = 15_000;
@@ -36,62 +39,219 @@ const waitForDaemonToStop = async (pid, deps) => {
36
39
  }
37
40
  throw new Error(`Timed out waiting for AgentRunner process ${pid} to stop.`);
38
41
  };
39
- export const spawnDetachedDaemon = () => {
42
+ export const spawnDetachedDaemon = (launch) => {
43
+ const env = launch ? buildRestartHandoffEnv(launch) : process.env;
40
44
  // On Windows, use the dedicated hidden PowerShell launcher so the `.cmd`
41
45
  // shim never creates a visible console host.
42
46
  if (getPlatform() === 'win32') {
43
- launchWindowsHiddenDaemon();
47
+ launchWindowsHiddenDaemon({ env });
44
48
  return;
45
49
  }
46
50
  const child = spawnExecutable('agentrunner', ['start'], {
47
51
  detached: true,
48
52
  stdio: 'ignore',
49
- env: process.env,
53
+ env,
50
54
  cwd: process.cwd(),
51
55
  });
52
56
  child.unref();
53
57
  return child;
54
58
  };
59
+ export const prepareDetachedRestartHandoff = async (deps = {}) => {
60
+ const handoffId = deps.handoffId ?? randomUUID();
61
+ const parentPid = deps.parentPid ?? process.pid;
62
+ const markerPath = deps.markerPath ?? getRestartHandoffPath(handoffId);
63
+ const launch = { handoffId, parentPid, markerPath };
64
+ try {
65
+ await (deps.unlink ?? fs.unlink)(markerPath);
66
+ }
67
+ catch {
68
+ // Missing or stale marker is fine.
69
+ }
70
+ let child;
71
+ try {
72
+ child = (deps.spawnDetachedDaemon ?? spawnDetachedDaemon)(launch);
73
+ }
74
+ catch (error) {
75
+ return {
76
+ status: 'retryable-failure',
77
+ handoffId,
78
+ replacementReady: false,
79
+ acknowledged: false,
80
+ retryableFailure: true,
81
+ reason: 'replacement-preparation-failed',
82
+ error: error instanceof Error ? error.message : String(error),
83
+ };
84
+ }
85
+ if (!child?.pid) {
86
+ return {
87
+ status: 'retryable-failure',
88
+ handoffId,
89
+ replacementReady: false,
90
+ acknowledged: false,
91
+ retryableFailure: true,
92
+ reason: 'replacement-preparation-failed',
93
+ error: 'Replacement runner process did not expose a PID.',
94
+ };
95
+ }
96
+ return (deps.waitForPreparedRestartHandoff ?? waitForPreparedRestartHandoff)(launch);
97
+ };
55
98
  // Run from within the daemon itself when a web restart request is received.
56
99
  // We can't call restartDaemon() here because that would SIGTERM our own PID
57
100
  // before we get a chance to spawn the replacement; instead we either exit and
58
101
  // let the OS supervisor restart us, or spawn a replacement and exit cleanly.
59
- export const executeRestartRequest = (deps = {}) => {
102
+ export const executeRestartRequest = async (deps = {}) => {
60
103
  const resolvedGetAutostartStatus = deps.getAutostartStatus ?? getAutostartStatus;
61
104
  const resolvedScheduleWindowsTaskRestart = deps.scheduleWindowsTaskRestart ?? scheduleWindowsTaskRestart;
105
+ const resolvedRegisterWindowsTask = deps.registerWindowsTask ?? registerWindowsTask;
62
106
  const resolvedSpawnDetachedDaemon = deps.spawnDetachedDaemon ?? spawnDetachedDaemon;
107
+ const acknowledgeRestart = deps.acknowledgeRestart ?? (async () => undefined);
108
+ const acknowledgePreparedHandoff = deps.acknowledgePreparedHandoff ?? acknowledgePreparedRestartHandoff;
63
109
  const resolvedPlatform = (deps.platform ?? getPlatform)();
64
110
  const exitProcess = deps.processExit ?? ((code) => process.exit(code));
65
111
  const resolvedLogger = deps.logger ?? logger;
66
- const autostartStatus = resolvedGetAutostartStatus();
67
- if (autostartStatus.registered && autostartStatus.platform === 'task-scheduler') {
68
- resolvedLogger.info('Restart requested — scheduling explicit Task Scheduler restart');
69
- const scheduled = resolvedScheduleWindowsTaskRestart();
70
- if (!scheduled) {
71
- // The restart helper was not created. Exiting now would kill this runner
72
- // with nothing to bring it back up, so keep the daemon alive instead — the
73
- // failure is already logged by scheduleWindowsTaskRestart.
74
- resolvedLogger.info('Restart helper creation failed — keeping the current runner alive instead of exiting');
75
- return;
112
+ let autostartStatus = resolvedGetAutostartStatus();
113
+ if (resolvedPlatform === 'win32' || autostartStatus.platform === 'task-scheduler') {
114
+ if (!autostartStatus.registered) {
115
+ if (!deps.config) {
116
+ return {
117
+ status: 'retryable-failure',
118
+ handoffId: randomUUID(),
119
+ replacementReady: false,
120
+ acknowledged: false,
121
+ retryableFailure: true,
122
+ reason: 'autostart-repair-failed',
123
+ error: 'Windows Task Scheduler autostart is missing and runtime configuration is unavailable.',
124
+ };
125
+ }
126
+ try {
127
+ resolvedLogger.info('Windows Task Scheduler autostart is missing — repairing it before restart');
128
+ await resolvedRegisterWindowsTask({ token: deps.config.daemonToken, apiUrl: deps.config.apiUrl }, { startImmediately: false });
129
+ autostartStatus = { registered: true, platform: 'task-scheduler' };
130
+ }
131
+ catch (error) {
132
+ return {
133
+ status: 'retryable-failure',
134
+ handoffId: randomUUID(),
135
+ replacementReady: false,
136
+ acknowledged: false,
137
+ retryableFailure: true,
138
+ reason: 'autostart-repair-failed',
139
+ error: error instanceof Error ? error.message : String(error),
140
+ };
141
+ }
142
+ }
143
+ resolvedLogger.info('Restart requested — preparing an out-of-job Task Scheduler handoff');
144
+ const preparation = await resolvedScheduleWindowsTaskRestart();
145
+ if (preparation.status === 'retryable-failure') {
146
+ resolvedLogger.info('Restart handoff preparation failed — keeping the current runner alive for retry');
147
+ return preparation;
148
+ }
149
+ try {
150
+ await acknowledgeRestart();
151
+ }
152
+ catch (error) {
153
+ return {
154
+ status: 'retryable-failure',
155
+ handoffId: preparation.handoffId,
156
+ replacementReady: false,
157
+ acknowledged: false,
158
+ retryableFailure: true,
159
+ reason: 'acknowledgement-failed',
160
+ error: error instanceof Error ? error.message : String(error),
161
+ };
162
+ }
163
+ if (!(await acknowledgePreparedHandoff(preparation))) {
164
+ return {
165
+ status: 'retryable-failure',
166
+ handoffId: preparation.handoffId,
167
+ replacementReady: false,
168
+ acknowledged: false,
169
+ retryableFailure: true,
170
+ reason: 'replacement-confirmation-failed',
171
+ error: 'The prepared Windows restart helper was no longer available after acknowledgement.',
172
+ };
76
173
  }
77
174
  exitProcess(0);
78
- return;
175
+ return {
176
+ status: 'acknowledged',
177
+ handoffId: preparation.handoffId,
178
+ replacementReady: true,
179
+ acknowledged: true,
180
+ retryableFailure: false,
181
+ };
79
182
  }
80
183
  const supervisedRespawn = autostartStatus.registered && (autostartStatus.platform === 'launchd' || autostartStatus.platform === 'systemd');
81
184
  if (supervisedRespawn) {
185
+ const handoffId = randomUUID();
186
+ try {
187
+ await acknowledgeRestart();
188
+ }
189
+ catch (error) {
190
+ return {
191
+ status: 'retryable-failure',
192
+ handoffId,
193
+ replacementReady: false,
194
+ acknowledged: false,
195
+ retryableFailure: true,
196
+ reason: 'acknowledgement-failed',
197
+ error: error instanceof Error ? error.message : String(error),
198
+ };
199
+ }
82
200
  resolvedLogger.info('Restart requested — exiting non-zero so the OS supervisor restarts the daemon', {
83
201
  platform: autostartStatus.platform,
84
202
  });
85
203
  exitProcess(1);
86
- return;
204
+ return {
205
+ status: 'acknowledged',
206
+ handoffId,
207
+ replacementReady: true,
208
+ acknowledged: true,
209
+ retryableFailure: false,
210
+ };
87
211
  }
88
- resolvedLogger.info('Restart requested — spawning new daemon and exiting', {
212
+ resolvedLogger.info('Restart requested — preparing a detached replacement runner', {
89
213
  platform: autostartStatus.platform,
90
214
  registered: autostartStatus.registered,
91
215
  osPlatform: resolvedPlatform,
92
216
  });
93
- resolvedSpawnDetachedDaemon();
217
+ const preparation = await (deps.prepareDetachedDaemon ??
218
+ (() => prepareDetachedRestartHandoff({ spawnDetachedDaemon: resolvedSpawnDetachedDaemon })))();
219
+ if (preparation.status === 'retryable-failure') {
220
+ return preparation;
221
+ }
222
+ try {
223
+ await acknowledgeRestart();
224
+ }
225
+ catch (error) {
226
+ return {
227
+ status: 'retryable-failure',
228
+ handoffId: preparation.handoffId,
229
+ replacementReady: false,
230
+ acknowledged: false,
231
+ retryableFailure: true,
232
+ reason: 'acknowledgement-failed',
233
+ error: error instanceof Error ? error.message : String(error),
234
+ };
235
+ }
236
+ if (!(await acknowledgePreparedHandoff(preparation))) {
237
+ return {
238
+ status: 'retryable-failure',
239
+ handoffId: preparation.handoffId,
240
+ replacementReady: false,
241
+ acknowledged: false,
242
+ retryableFailure: true,
243
+ reason: 'replacement-confirmation-failed',
244
+ error: 'The prepared replacement runner was no longer available after acknowledgement.',
245
+ };
246
+ }
94
247
  exitProcess(0);
248
+ return {
249
+ status: 'acknowledged',
250
+ handoffId: preparation.handoffId,
251
+ replacementReady: true,
252
+ acknowledged: true,
253
+ retryableFailure: false,
254
+ };
95
255
  };
96
256
  export const restartDaemon = async (deps = {}) => {
97
257
  const resolvedGetDaemonStatus = deps.getDaemonStatus ?? getDaemonStatus;
@@ -1 +1 @@
1
- {"version":3,"file":"daemon-control.js","sourceRoot":"","sources":["../src/daemon-control.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"daemon-control.js","sourceRoot":"","sources":["../src/daemon-control.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,IAAI,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,mBAAmB,EACnB,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACL,iCAAiC,EACjC,sBAAsB,EACtB,qBAAqB,EACrB,6BAA6B,GAI9B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AAoCzC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAClC,MAAM,aAAa,GAAG,MAAM,CAAC;AAC7B,MAAM,cAAc,GAAG,MAAM,CAAC;AAS9B,wEAAwE;AACxE,2EAA2E;AAC3E,8EAA8E;AAC9E,oEAAoE;AACpE,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,EAAE,OAAyB,EAAE,EAAgC,EAAE;IACtG,MAAM,uBAAuB,GAAG,IAAI,CAAC,eAAe,IAAI,eAAe,CAAC;IACxE,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,YAAoB,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;IACpF,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,cAAc,CAAC;IAEnD,MAAM,QAAQ,GAAG,WAAW,EAAE,GAAG,SAAS,CAAC;IAC3C,IAAI,MAAM,GAAG,MAAM,uBAAuB,EAAE,CAAC;IAC7C,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,WAAW,EAAE,GAAG,QAAQ,EAAE,CAAC;QACnD,MAAM,aAAa,CAAC,qBAAqB,CAAC,CAAC;QAC3C,MAAM,GAAG,MAAM,uBAAuB,EAAE,CAAC;IAC3C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,mBAAmB,GAAG,KAAK,EAC/B,GAAW,EACX,IAAuE,EACxD,EAAE;IACjB,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAE1B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,aAAa,CAAC;IAC5C,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAC5C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,6CAA6C,GAAG,WAAW,CAAC,CAAC;AAC/E,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAA6B,EAA+B,EAAE;IAChG,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;IAClE,yEAAyE;IACzE,6CAA6C;IAC7C,IAAI,WAAW,EAAE,KAAK,OAAO,EAAE,CAAC;QAC9B,yBAAyB,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACnC,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,eAAe,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC,EAAE;QACtD,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,QAAQ;QACf,GAAG;QACH,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;KACnB,CAAC,CAAC;IACH,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAWF,MAAM,CAAC,MAAM,6BAA6B,GAAG,KAAK,EAChD,OAAmC,EAAE,EACD,EAAE;IACtC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,UAAU,EAAE,CAAC;IACjD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC;IAChD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,qBAAqB,CAAC,SAAS,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;IAEpD,IAAI,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;IACrC,CAAC;IAED,IAAI,KAAkC,CAAC;IACvC,IAAI,CAAC;QACH,KAAK,GAAG,CAAC,IAAI,CAAC,mBAAmB,IAAI,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;IACpE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,MAAM,EAAE,mBAAmB;YAC3B,SAAS;YACT,gBAAgB,EAAE,KAAK;YACvB,YAAY,EAAE,KAAK;YACnB,gBAAgB,EAAE,IAAI;YACtB,MAAM,EAAE,gCAAgC;YACxC,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC9D,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;QAChB,OAAO;YACL,MAAM,EAAE,mBAAmB;YAC3B,SAAS;YACT,gBAAgB,EAAE,KAAK;YACvB,YAAY,EAAE,KAAK;YACnB,gBAAgB,EAAE,IAAI;YACtB,MAAM,EAAE,gCAAgC;YACxC,KAAK,EAAE,kDAAkD;SAC1D,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,6BAA6B,IAAI,6BAA6B,CAAC,CAAC,MAAM,CAAC,CAAC;AACvF,CAAC,CAAC;AAEF,4EAA4E;AAC5E,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,EAAE,OAA2B,EAAE,EAAmC,EAAE;IAC5G,MAAM,0BAA0B,GAAG,IAAI,CAAC,kBAAkB,IAAI,kBAAkB,CAAC;IACjF,MAAM,kCAAkC,GAAG,IAAI,CAAC,0BAA0B,IAAI,0BAA0B,CAAC;IACzG,MAAM,2BAA2B,GAAG,IAAI,CAAC,mBAAmB,IAAI,mBAAmB,CAAC;IACpF,MAAM,2BAA2B,GAAG,IAAI,CAAC,mBAAmB,IAAI,mBAAmB,CAAC;IACpF,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC;IAC9E,MAAM,0BAA0B,GAAG,IAAI,CAAC,0BAA0B,IAAI,iCAAiC,CAAC;IACxG,MAAM,gBAAgB,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAC,EAAE,CAAC;IAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/E,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC;IAE7C,IAAI,eAAe,GAAG,0BAA0B,EAAE,CAAC;IACnD,IAAI,gBAAgB,KAAK,OAAO,IAAI,eAAe,CAAC,QAAQ,KAAK,gBAAgB,EAAE,CAAC;QAClF,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,CAAC;YAChC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjB,OAAO;oBACL,MAAM,EAAE,mBAAmB;oBAC3B,SAAS,EAAE,UAAU,EAAE;oBACvB,gBAAgB,EAAE,KAAK;oBACvB,YAAY,EAAE,KAAK;oBACnB,gBAAgB,EAAE,IAAI;oBACtB,MAAM,EAAE,yBAAyB;oBACjC,KAAK,EAAE,uFAAuF;iBAC/F,CAAC;YACJ,CAAC;YAED,IAAI,CAAC;gBACH,cAAc,CAAC,IAAI,CAAC,2EAA2E,CAAC,CAAC;gBACjG,MAAM,2BAA2B,CAC/B,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EAC9D,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAC5B,CAAC;gBACF,eAAe,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;YACrE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO;oBACL,MAAM,EAAE,mBAAmB;oBAC3B,SAAS,EAAE,UAAU,EAAE;oBACvB,gBAAgB,EAAE,KAAK;oBACvB,YAAY,EAAE,KAAK;oBACnB,gBAAgB,EAAE,IAAI;oBACtB,MAAM,EAAE,yBAAyB;oBACjC,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;iBAC9D,CAAC;YACJ,CAAC;QACH,CAAC;QAED,cAAc,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAC;QAC1F,MAAM,WAAW,GAAG,MAAM,kCAAkC,EAAE,CAAC;QAC/D,IAAI,WAAW,CAAC,MAAM,KAAK,mBAAmB,EAAE,CAAC;YAC/C,cAAc,CAAC,IAAI,CAAC,iFAAiF,CAAC,CAAC;YACvG,OAAO,WAAW,CAAC;QACrB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,kBAAkB,EAAE,CAAC;QAC7B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,MAAM,EAAE,mBAAmB;gBAC3B,SAAS,EAAE,WAAW,CAAC,SAAS;gBAChC,gBAAgB,EAAE,KAAK;gBACvB,YAAY,EAAE,KAAK;gBACnB,gBAAgB,EAAE,IAAI;gBACtB,MAAM,EAAE,wBAAwB;gBAChC,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC9D,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,CAAC,MAAM,0BAA0B,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;YACrD,OAAO;gBACL,MAAM,EAAE,mBAAmB;gBAC3B,SAAS,EAAE,WAAW,CAAC,SAAS;gBAChC,gBAAgB,EAAE,KAAK;gBACvB,YAAY,EAAE,KAAK;gBACnB,gBAAgB,EAAE,IAAI;gBACtB,MAAM,EAAE,iCAAiC;gBACzC,KAAK,EAAE,oFAAoF;aAC5F,CAAC;QACJ,CAAC;QAED,WAAW,CAAC,CAAC,CAAC,CAAC;QACf,OAAO;YACL,MAAM,EAAE,cAAc;YACtB,SAAS,EAAE,WAAW,CAAC,SAAS;YAChC,gBAAgB,EAAE,IAAI;YACtB,YAAY,EAAE,IAAI;YAClB,gBAAgB,EAAE,KAAK;SACxB,CAAC;IACJ,CAAC;IACD,MAAM,iBAAiB,GACrB,eAAe,CAAC,UAAU,IAAI,CAAC,eAAe,CAAC,QAAQ,KAAK,SAAS,IAAI,eAAe,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC;IAEnH,IAAI,iBAAiB,EAAE,CAAC;QACtB,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,kBAAkB,EAAE,CAAC;QAC7B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,MAAM,EAAE,mBAAmB;gBAC3B,SAAS;gBACT,gBAAgB,EAAE,KAAK;gBACvB,YAAY,EAAE,KAAK;gBACnB,gBAAgB,EAAE,IAAI;gBACtB,MAAM,EAAE,wBAAwB;gBAChC,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC9D,CAAC;QACJ,CAAC;QACD,cAAc,CAAC,IAAI,CAAC,+EAA+E,EAAE;YACnG,QAAQ,EAAE,eAAe,CAAC,QAAQ;SACnC,CAAC,CAAC;QACH,WAAW,CAAC,CAAC,CAAC,CAAC;QACf,OAAO;YACL,MAAM,EAAE,cAAc;YACtB,SAAS;YACT,gBAAgB,EAAE,IAAI;YACtB,YAAY,EAAE,IAAI;YAClB,gBAAgB,EAAE,KAAK;SACxB,CAAC;IACJ,CAAC;IAED,cAAc,CAAC,IAAI,CAAC,6DAA6D,EAAE;QACjF,QAAQ,EAAE,eAAe,CAAC,QAAQ;QAClC,UAAU,EAAE,eAAe,CAAC,UAAU;QACtC,UAAU,EAAE,gBAAgB;KAC7B,CAAC,CAAC;IACH,MAAM,WAAW,GAAG,MAAM,CACxB,IAAI,CAAC,qBAAqB;QAC1B,CAAC,GAAG,EAAE,CAAC,6BAA6B,CAAC,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,CAAC,CAAC,CAC5F,EAAE,CAAC;IACJ,IAAI,WAAW,CAAC,MAAM,KAAK,mBAAmB,EAAE,CAAC;QAC/C,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,kBAAkB,EAAE,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,MAAM,EAAE,mBAAmB;YAC3B,SAAS,EAAE,WAAW,CAAC,SAAS;YAChC,gBAAgB,EAAE,KAAK;YACvB,YAAY,EAAE,KAAK;YACnB,gBAAgB,EAAE,IAAI;YACtB,MAAM,EAAE,wBAAwB;YAChC,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC9D,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,0BAA0B,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;QACrD,OAAO;YACL,MAAM,EAAE,mBAAmB;YAC3B,SAAS,EAAE,WAAW,CAAC,SAAS;YAChC,gBAAgB,EAAE,KAAK;YACvB,YAAY,EAAE,KAAK;YACnB,gBAAgB,EAAE,IAAI;YACtB,MAAM,EAAE,iCAAiC;YACzC,KAAK,EAAE,gFAAgF;SACxF,CAAC;IACJ,CAAC;IACD,WAAW,CAAC,CAAC,CAAC,CAAC;IACf,OAAO;QACL,MAAM,EAAE,cAAc;QACtB,SAAS,EAAE,WAAW,CAAC,SAAS;QAChC,gBAAgB,EAAE,IAAI;QACtB,YAAY,EAAE,IAAI;QAClB,gBAAgB,EAAE,KAAK;KACxB,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAAE,OAAoB,EAAE,EAAiB,EAAE;IAC3E,MAAM,uBAAuB,GAAG,IAAI,CAAC,eAAe,IAAI,eAAe,CAAC;IACxE,MAAM,0BAA0B,GAAG,IAAI,CAAC,kBAAkB,IAAI,kBAAkB,CAAC;IACjF,MAAM,+BAA+B,GAAG,IAAI,CAAC,uBAAuB,IAAI,uBAAuB,CAAC;IAChG,MAAM,2BAA2B,GAAG,IAAI,CAAC,mBAAmB,IAAI,mBAAmB,CAAC;IACpF,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7D,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,YAAoB,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;IACpF,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC;IAE7C,MAAM,eAAe,GAAG,0BAA0B,EAAE,CAAC;IACrD,IAAI,eAAe,CAAC,UAAU,IAAI,eAAe,CAAC,QAAQ,KAAK,gBAAgB,EAAE,CAAC;QAChF,cAAc,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;QACjF,MAAM,+BAA+B,EAAE,CAAC;QACxC,OAAO;IACT,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,uBAAuB,EAAE,CAAC;IACrD,IAAI,YAAY,CAAC,OAAO,IAAI,YAAY,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;QACtD,cAAc,CAAC,IAAI,CAAC,qCAAqC,EAAE,EAAE,GAAG,EAAE,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC;QACtF,MAAM,mBAAmB,CAAC,YAAY,CAAC,GAAG,EAAE;YAC1C,eAAe,EAAE,uBAAuB;YACxC,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE,aAAa;SACrB,CAAC,CAAC;IACL,CAAC;IAED,IAAI,eAAe,CAAC,UAAU,EAAE,CAAC;QAC/B,cAAc,CAAC,IAAI,CAAC,yDAAyD,EAAE;YAC7E,QAAQ,EAAE,eAAe,CAAC,QAAQ;SACnC,CAAC,CAAC;QACH,MAAM,+BAA+B,EAAE,CAAC;QACxC,OAAO;IACT,CAAC;IAED,cAAc,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;IACzF,2BAA2B,EAAE,CAAC;AAChC,CAAC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import test from 'node:test';
3
- import { executeRestartRequest, restartDaemon, waitForDaemonToStart } from './daemon-control.js';
3
+ import { executeRestartRequest, prepareDetachedRestartHandoff, restartDaemon, waitForDaemonToStart, } from './daemon-control.js';
4
4
  test('restartDaemon stops running daemon and restarts via autostart when registered', async () => {
5
5
  const signals = [];
6
6
  let statusChecks = 0;
@@ -99,97 +99,280 @@ test('waitForDaemonToStart gives up after the deadline and reports not running',
99
99
  // Stops polling once now() passes the deadline instead of looping forever.
100
100
  assert.ok(checks >= 1);
101
101
  });
102
- test('executeRestartRequest exits non-zero when supervised by launchd', () => {
102
+ test('executeRestartRequest exits non-zero when supervised by launchd', async () => {
103
103
  const exitCodes = [];
104
104
  let spawned = false;
105
- executeRestartRequest({
105
+ await executeRestartRequest({
106
106
  getAutostartStatus: () => ({ registered: true, platform: 'launchd' }),
107
107
  spawnDetachedDaemon: () => {
108
108
  spawned = true;
109
109
  },
110
110
  platform: () => 'darwin',
111
- processExit: ((code) => {
111
+ processExit: (code) => {
112
112
  exitCodes.push(code);
113
- return undefined;
114
- }),
113
+ },
115
114
  logger: { info: () => undefined },
116
115
  });
117
116
  assert.deepEqual(exitCodes, [1]);
118
117
  assert.equal(spawned, false);
119
118
  });
120
- test('executeRestartRequest exits non-zero when supervised by systemd', () => {
119
+ test('executeRestartRequest exits non-zero when supervised by systemd', async () => {
121
120
  const exitCodes = [];
122
121
  let spawned = false;
123
- executeRestartRequest({
122
+ await executeRestartRequest({
124
123
  getAutostartStatus: () => ({ registered: true, platform: 'systemd' }),
125
124
  spawnDetachedDaemon: () => {
126
125
  spawned = true;
127
126
  },
128
127
  platform: () => 'linux',
129
- processExit: ((code) => {
128
+ processExit: (code) => {
130
129
  exitCodes.push(code);
131
- return undefined;
132
- }),
130
+ },
133
131
  logger: { info: () => undefined },
134
132
  });
135
133
  assert.deepEqual(exitCodes, [1]);
136
134
  assert.equal(spawned, false);
137
135
  });
138
- test('executeRestartRequest schedules an explicit restart and exits cleanly for Windows Task Scheduler', () => {
136
+ test('executeRestartRequest schedules an explicit restart and exits cleanly for Windows Task Scheduler', async () => {
139
137
  const exitCodes = [];
140
138
  let spawned = false;
141
139
  let scheduled = false;
142
- executeRestartRequest({
140
+ await executeRestartRequest({
143
141
  getAutostartStatus: () => ({ registered: true, platform: 'task-scheduler' }),
144
- scheduleWindowsTaskRestart: () => {
142
+ scheduleWindowsTaskRestart: async () => {
145
143
  scheduled = true;
146
- return true;
144
+ return {
145
+ status: 'prepared',
146
+ handoffId: 'windows-success',
147
+ markerPath: '/tmp/restart-handoff-windows-success.json',
148
+ replacementPid: 9002,
149
+ replacementReady: true,
150
+ acknowledged: false,
151
+ retryableFailure: false,
152
+ };
147
153
  },
148
154
  spawnDetachedDaemon: () => {
149
155
  spawned = true;
150
156
  },
151
157
  platform: () => 'win32',
152
- processExit: ((code) => {
158
+ acknowledgePreparedHandoff: async () => true,
159
+ processExit: (code) => {
153
160
  exitCodes.push(code);
154
- return undefined;
155
- }),
161
+ },
156
162
  logger: { info: () => undefined },
157
163
  });
158
164
  assert.equal(spawned, false);
159
165
  assert.equal(scheduled, true);
160
166
  assert.deepEqual(exitCodes, [0]);
161
167
  });
162
- test('executeRestartRequest keeps the daemon alive when the Windows restart helper fails to schedule', () => {
168
+ test('executeRestartRequest keeps the daemon alive when the Windows restart helper fails to schedule', async () => {
163
169
  const exitCodes = [];
164
- executeRestartRequest({
170
+ await executeRestartRequest({
165
171
  getAutostartStatus: () => ({ registered: true, platform: 'task-scheduler' }),
166
172
  // Helper creation failed — must NOT exit, or the runner dies with no replacement.
167
- scheduleWindowsTaskRestart: () => false,
173
+ scheduleWindowsTaskRestart: async () => ({
174
+ status: 'retryable-failure',
175
+ handoffId: 'windows-failure',
176
+ replacementReady: false,
177
+ acknowledged: false,
178
+ retryableFailure: true,
179
+ reason: 'helper-preparation-failed',
180
+ error: 'helper creation failed',
181
+ }),
168
182
  platform: () => 'win32',
169
- processExit: ((code) => {
183
+ processExit: (code) => {
170
184
  exitCodes.push(code);
171
- return undefined;
172
- }),
185
+ },
173
186
  logger: { info: () => undefined },
174
187
  });
175
188
  assert.deepEqual(exitCodes, [], 'daemon must not exit when the restart helper could not be created');
176
189
  });
177
- test('executeRestartRequest spawns a detached daemon when running manually on macOS', () => {
178
- const exitCodes = [];
179
- let spawned = false;
180
- executeRestartRequest({
190
+ test('executeRestartRequest acks and exits only after a manual replacement is prepared', async () => {
191
+ const events = [];
192
+ await executeRestartRequest({
181
193
  getAutostartStatus: () => ({ registered: false, platform: 'launchd' }),
182
- spawnDetachedDaemon: () => {
183
- spawned = true;
194
+ prepareDetachedDaemon: async () => {
195
+ events.push('prepared');
196
+ return {
197
+ status: 'prepared',
198
+ handoffId: 'manual-success',
199
+ markerPath: '/tmp/restart-handoff-manual-success.json',
200
+ replacementPid: 9002,
201
+ replacementReady: true,
202
+ acknowledged: false,
203
+ retryableFailure: false,
204
+ };
205
+ },
206
+ acknowledgeRestart: async () => {
207
+ events.push('ack');
208
+ },
209
+ acknowledgePreparedHandoff: async () => {
210
+ events.push('commit');
211
+ return true;
212
+ },
213
+ platform: () => 'darwin',
214
+ processExit: (code) => {
215
+ events.push(`exit:${code}`);
216
+ },
217
+ logger: { info: () => undefined },
218
+ });
219
+ assert.deepEqual(events, ['prepared', 'ack', 'commit', 'exit:0']);
220
+ });
221
+ test('executeRestartRequest keeps the daemon alive when the prepared standby disappears during acknowledgement', async () => {
222
+ const events = [];
223
+ const result = await executeRestartRequest({
224
+ getAutostartStatus: () => ({ registered: false, platform: 'manual' }),
225
+ prepareDetachedDaemon: async () => ({
226
+ status: 'prepared',
227
+ handoffId: 'manual-disappeared',
228
+ markerPath: '/tmp/restart-handoff-manual-disappeared.json',
229
+ replacementPid: 9002,
230
+ replacementReady: true,
231
+ acknowledged: false,
232
+ retryableFailure: false,
233
+ }),
234
+ acknowledgeRestart: async () => {
235
+ events.push('ack');
236
+ },
237
+ acknowledgePreparedHandoff: async () => {
238
+ events.push('commit-failed');
239
+ return false;
240
+ },
241
+ processExit: (code) => {
242
+ events.push(`exit:${code}`);
184
243
  },
185
244
  platform: () => 'darwin',
186
- processExit: ((code) => {
245
+ logger: { info: () => undefined },
246
+ });
247
+ assert.deepEqual(events, ['ack', 'commit-failed']);
248
+ assert.equal(result.status, 'retryable-failure');
249
+ });
250
+ test('prepareDetachedRestartHandoff reports spawn failure without waiting for readiness', async () => {
251
+ let waited = false;
252
+ const result = await prepareDetachedRestartHandoff({
253
+ handoffId: 'manual-spawn-failure',
254
+ parentPid: 4321,
255
+ markerPath: '/tmp/restart-handoff.json',
256
+ unlink: async () => undefined,
257
+ spawnDetachedDaemon: () => {
258
+ throw new Error('spawn failed');
259
+ },
260
+ waitForPreparedRestartHandoff: async () => {
261
+ waited = true;
262
+ throw new Error('should not wait');
263
+ },
264
+ });
265
+ assert.equal(result.status, 'retryable-failure');
266
+ assert.equal(result.reason, 'replacement-preparation-failed');
267
+ assert.equal(waited, false);
268
+ });
269
+ test('executeRestartRequest keeps supervised runners alive when acknowledgement fails', async () => {
270
+ const exitCodes = [];
271
+ const result = await executeRestartRequest({
272
+ getAutostartStatus: () => ({ registered: true, platform: 'systemd' }),
273
+ acknowledgeRestart: async () => {
274
+ throw new Error('network down');
275
+ },
276
+ platform: () => 'linux',
277
+ processExit: (code) => {
187
278
  exitCodes.push(code);
188
- return undefined;
279
+ },
280
+ logger: { info: () => undefined },
281
+ });
282
+ assert.equal(result.status, 'retryable-failure');
283
+ assert.equal(result.reason, 'acknowledgement-failed');
284
+ assert.deepEqual(exitCodes, []);
285
+ });
286
+ test('executeRestartRequest preserves the current runner and request when the Windows helper reports a post-create failure', async () => {
287
+ const events = [];
288
+ const result = await executeRestartRequest({
289
+ getAutostartStatus: () => ({ registered: true, platform: 'task-scheduler' }),
290
+ scheduleWindowsTaskRestart: async () => ({
291
+ status: 'retryable-failure',
292
+ handoffId: 'handoff-query-failure',
293
+ replacementReady: false,
294
+ acknowledged: false,
295
+ retryableFailure: true,
296
+ reason: 'helper-preparation-failed',
297
+ error: 'schtasks /Query failed with exit 1',
189
298
  }),
299
+ acknowledgeRestart: async () => {
300
+ events.push('ack');
301
+ },
302
+ spawnDetachedDaemon: () => {
303
+ events.push('spawn');
304
+ },
305
+ processExit: (code) => {
306
+ events.push(`exit:${code}`);
307
+ },
308
+ platform: () => 'win32',
190
309
  logger: { info: () => undefined },
191
310
  });
192
- assert.equal(spawned, true);
193
- assert.deepEqual(exitCodes, [0]);
311
+ assert.deepEqual(events, [], 'a post-create helper failure must not ack or terminate the current runner');
312
+ assert.equal(result.status, 'retryable-failure');
313
+ });
314
+ test('executeRestartRequest repairs a missing Windows scheduled task before preparing handoff', async () => {
315
+ const events = [];
316
+ const result = await executeRestartRequest({
317
+ getAutostartStatus: () => ({ registered: false, platform: 'task-scheduler' }),
318
+ registerWindowsTask: async () => {
319
+ events.push('register');
320
+ return { registered: true, servicePath: 'task.xml', platform: 'task-scheduler' };
321
+ },
322
+ scheduleWindowsTaskRestart: async () => {
323
+ events.push('prepare');
324
+ return {
325
+ status: 'retryable-failure',
326
+ handoffId: 'handoff-after-repair',
327
+ replacementReady: false,
328
+ acknowledged: false,
329
+ retryableFailure: true,
330
+ reason: 'helper-preparation-failed',
331
+ error: 'replacement did not become ready',
332
+ };
333
+ },
334
+ acknowledgeRestart: async () => {
335
+ events.push('ack');
336
+ },
337
+ spawnDetachedDaemon: () => {
338
+ events.push('spawn');
339
+ },
340
+ processExit: (code) => {
341
+ events.push(`exit:${code}`);
342
+ },
343
+ platform: () => 'win32',
344
+ config: { daemonToken: 'secret-token', apiUrl: 'https://api.example' },
345
+ logger: { info: () => undefined },
346
+ });
347
+ assert.deepEqual(events, ['register', 'prepare']);
348
+ assert.equal(result.status, 'retryable-failure');
349
+ });
350
+ test('executeRestartRequest does not ack or exit when a manual replacement never becomes ready', async () => {
351
+ const events = [];
352
+ const result = await executeRestartRequest({
353
+ getAutostartStatus: () => ({ registered: false, platform: 'manual' }),
354
+ prepareDetachedDaemon: async () => ({
355
+ status: 'retryable-failure',
356
+ handoffId: 'manual-timeout',
357
+ replacementReady: false,
358
+ acknowledged: false,
359
+ retryableFailure: true,
360
+ reason: 'replacement-preparation-failed',
361
+ error: 'replacement readiness timed out',
362
+ }),
363
+ acknowledgeRestart: async () => {
364
+ events.push('ack');
365
+ },
366
+ spawnDetachedDaemon: () => {
367
+ events.push('spawn');
368
+ },
369
+ processExit: (code) => {
370
+ events.push(`exit:${code}`);
371
+ },
372
+ platform: () => 'darwin',
373
+ logger: { info: () => undefined },
374
+ });
375
+ assert.deepEqual(events, [], 'spawn success alone must not ack or terminate the current runner');
376
+ assert.equal(result.status, 'retryable-failure');
194
377
  });
195
378
  //# sourceMappingURL=daemon-control.test.js.map