crewly 1.20.173 → 1.20.175

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/config/skills/orchestrator/restart-crewly/SKILL.md +11 -23
  2. package/config/skills/orchestrator/restart-crewly/execute.sh +7 -5
  3. package/dist/backend/backend/src/constants.d.ts +64 -0
  4. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  5. package/dist/backend/backend/src/constants.js +64 -0
  6. package/dist/backend/backend/src/constants.js.map +1 -1
  7. package/dist/backend/backend/src/controllers/system/system-control.controller.d.ts +67 -0
  8. package/dist/backend/backend/src/controllers/system/system-control.controller.d.ts.map +1 -0
  9. package/dist/backend/backend/src/controllers/system/system-control.controller.js +183 -0
  10. package/dist/backend/backend/src/controllers/system/system-control.controller.js.map +1 -0
  11. package/dist/backend/backend/src/controllers/system/system.controller.d.ts +0 -13
  12. package/dist/backend/backend/src/controllers/system/system.controller.d.ts.map +1 -1
  13. package/dist/backend/backend/src/controllers/system/system.controller.js +0 -87
  14. package/dist/backend/backend/src/controllers/system/system.controller.js.map +1 -1
  15. package/dist/backend/backend/src/index.d.ts +7 -0
  16. package/dist/backend/backend/src/index.d.ts.map +1 -1
  17. package/dist/backend/backend/src/index.js +84 -1
  18. package/dist/backend/backend/src/index.js.map +1 -1
  19. package/dist/backend/backend/src/routes/modules/system.routes.d.ts.map +1 -1
  20. package/dist/backend/backend/src/routes/modules/system.routes.js +4 -2
  21. package/dist/backend/backend/src/routes/modules/system.routes.js.map +1 -1
  22. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
  23. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +7 -0
  24. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
  25. package/dist/backend/backend/src/services/harness/login-broker.service.d.ts +6 -0
  26. package/dist/backend/backend/src/services/harness/login-broker.service.d.ts.map +1 -1
  27. package/dist/backend/backend/src/services/harness/login-broker.service.js +32 -1
  28. package/dist/backend/backend/src/services/harness/login-broker.service.js.map +1 -1
  29. package/dist/backend/backend/src/services/system/auto-update.service.d.ts +58 -0
  30. package/dist/backend/backend/src/services/system/auto-update.service.d.ts.map +1 -1
  31. package/dist/backend/backend/src/services/system/auto-update.service.js +128 -52
  32. package/dist/backend/backend/src/services/system/auto-update.service.js.map +1 -1
  33. package/dist/backend/backend/src/services/system/auto-update.utils.d.ts +7 -0
  34. package/dist/backend/backend/src/services/system/auto-update.utils.d.ts.map +1 -1
  35. package/dist/backend/backend/src/services/system/auto-update.utils.js +14 -0
  36. package/dist/backend/backend/src/services/system/auto-update.utils.js.map +1 -1
  37. package/dist/backend/backend/src/services/system/restart-replacement.d.ts +78 -0
  38. package/dist/backend/backend/src/services/system/restart-replacement.d.ts.map +1 -0
  39. package/dist/backend/backend/src/services/system/restart-replacement.js +109 -0
  40. package/dist/backend/backend/src/services/system/restart-replacement.js.map +1 -0
  41. package/dist/backend/backend/src/services/system/supervisor-detect.d.ts +78 -0
  42. package/dist/backend/backend/src/services/system/supervisor-detect.d.ts.map +1 -0
  43. package/dist/backend/backend/src/services/system/supervisor-detect.js +171 -0
  44. package/dist/backend/backend/src/services/system/supervisor-detect.js.map +1 -0
  45. package/dist/backend/backend/src/services/system/system-control.service.d.ts +337 -0
  46. package/dist/backend/backend/src/services/system/system-control.service.d.ts.map +1 -0
  47. package/dist/backend/backend/src/services/system/system-control.service.js +574 -0
  48. package/dist/backend/backend/src/services/system/system-control.service.js.map +1 -0
  49. package/dist/backend/build-info.json +2 -2
  50. package/dist/cli/backend/src/constants.d.ts +64 -0
  51. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  52. package/dist/cli/backend/src/constants.js +64 -0
  53. package/dist/cli/backend/src/constants.js.map +1 -1
  54. package/dist/cli/backend/src/services/harness/login-broker.service.d.ts +6 -0
  55. package/dist/cli/backend/src/services/harness/login-broker.service.d.ts.map +1 -1
  56. package/dist/cli/backend/src/services/harness/login-broker.service.js +32 -1
  57. package/dist/cli/backend/src/services/harness/login-broker.service.js.map +1 -1
  58. package/dist/cli/backend/src/services/system/auto-update.utils.d.ts +7 -0
  59. package/dist/cli/backend/src/services/system/auto-update.utils.d.ts.map +1 -1
  60. package/dist/cli/backend/src/services/system/auto-update.utils.js +14 -0
  61. package/dist/cli/backend/src/services/system/auto-update.utils.js.map +1 -1
  62. package/frontend/dist/assets/{index-36e85ac8.js → index-0726afab.js} +281 -281
  63. package/frontend/dist/assets/{index-15501404.css → index-0c5449b5.css} +1 -1
  64. package/frontend/dist/index.html +2 -2
  65. package/package.json +1 -1
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Who brings the backend back after it exits?
3
+ *
4
+ * The owner's Restart / Upgrade buttons must never leave a machine down, so
5
+ * before exiting the backend needs to know whether something will relaunch
6
+ * it (specs/2026-10-01-upgrade-restart-controls.md):
7
+ *
8
+ * - `crewly start` (the CLI restart loop) respawns the backend when it exits
9
+ * with RESTART_REQUESTED (120). That is the parent in every supported run
10
+ * mode: foreground, the macOS login item (`~/.crewly/crewly-start.command`),
11
+ * the systemd user unit (`crewly.service`) and the desktop app. The outer
12
+ * layer is reported too, because it decides what happens if the CLI itself
13
+ * dies.
14
+ * - PM2 restarts the app on exit by default.
15
+ * - A backend started some other way (directly under systemd or launchd,
16
+ * a bare `node dist/...`) may or may not come back; that is "unknown", and
17
+ * "none" when there is clearly no supervisor at all.
18
+ *
19
+ * Pure: the environment and process lookups are injected.
20
+ *
21
+ * @module services/system/supervisor-detect
22
+ */
23
+ import { execFileSync } from 'child_process';
24
+ import { SYSTEM_CONTROL_CONSTANTS } from '../../constants.js';
25
+ import { hasRestartSupervisor, readProcessCommandLine } from './auto-update.utils.js';
26
+ /**
27
+ * Whether the environment says we run inside a systemd unit.
28
+ *
29
+ * @param env - Environment
30
+ * @param commandLines - Parent / grandparent command lines
31
+ * @returns True under systemd
32
+ */
33
+ function isUnderSystemd(env, commandLines) {
34
+ if (env.INVOCATION_ID)
35
+ return true;
36
+ return commandLines.some((c) => !!c && c.includes(SYSTEM_CONTROL_CONSTANTS.SYSTEMD_UNIT));
37
+ }
38
+ /**
39
+ * Whether launchd started us as a job (not via Terminal). launchd sets
40
+ * XPC_SERVICE_NAME to the job label; shells in Terminal get `0` or an
41
+ * `application.*` name.
42
+ *
43
+ * @param env - Environment
44
+ * @param platform - Platform
45
+ * @returns True for a launchd job
46
+ */
47
+ function isLaunchdJob(env, platform) {
48
+ if (platform !== 'darwin')
49
+ return false;
50
+ const label = env.XPC_SERVICE_NAME;
51
+ return !!label && label !== '0' && !label.startsWith('application.');
52
+ }
53
+ /**
54
+ * Work out what relaunches the backend.
55
+ *
56
+ * @param probe - Environment and process lookups
57
+ * @returns Supervisor info
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * detectSupervisor({ env: { CREWLY_RESTART_SUPERVISOR: 'cli-start', INVOCATION_ID: 'x' }, platform: 'linux',
62
+ * parentCommandLine: () => null, grandparentCommandLine: () => null });
63
+ * // { kind: 'crewly-start', outer: 'systemd', willRelaunch: 'yes', ... }
64
+ * ```
65
+ */
66
+ export function detectSupervisor(probe) {
67
+ const { env, platform } = probe;
68
+ let parent;
69
+ let grandparent;
70
+ const readParent = () => (parent === undefined ? (parent = probe.parentCommandLine()) : parent);
71
+ const readGrandparent = () => grandparent === undefined ? (grandparent = probe.grandparentCommandLine()) : grandparent;
72
+ const cliStart = hasRestartSupervisor(env, readParent);
73
+ if (cliStart) {
74
+ const gp = readGrandparent();
75
+ let outer = null;
76
+ if (isUnderSystemd(env, [gp]))
77
+ outer = 'systemd';
78
+ else if (gp && gp.includes(SYSTEM_CONTROL_CONSTANTS.LOGIN_WRAPPER_SCRIPT))
79
+ outer = 'login-wrapper';
80
+ else if (isLaunchdJob(env, platform))
81
+ outer = 'launchd';
82
+ const outerText = outer === 'systemd'
83
+ ? ' systemd keeps crewly start itself running.'
84
+ : outer === 'login-wrapper'
85
+ ? ` The ${SYSTEM_CONTROL_CONSTANTS.LOGIN_WRAPPER_SCRIPT} login item keeps crewly start itself running.`
86
+ : outer === 'launchd'
87
+ ? ' launchd keeps crewly start itself running.'
88
+ : '';
89
+ return {
90
+ kind: 'crewly-start',
91
+ outer,
92
+ willRelaunch: 'yes',
93
+ detail: `crewly start restarts the backend when it exits.${outerText}`,
94
+ };
95
+ }
96
+ // PM2 (the legacy ecosystem.config.js) restarts the app on exit by default.
97
+ if (env.pm_id !== undefined || env.PM2_HOME !== undefined) {
98
+ return {
99
+ kind: 'pm2',
100
+ outer: null,
101
+ willRelaunch: 'yes',
102
+ detail: 'PM2 restarts the backend when it exits.',
103
+ };
104
+ }
105
+ const p = readParent();
106
+ if (isUnderSystemd(env, [p])) {
107
+ return {
108
+ kind: 'systemd',
109
+ outer: null,
110
+ willRelaunch: 'unknown',
111
+ detail: 'Runs directly under systemd; it comes back only if the unit has Restart= set.',
112
+ };
113
+ }
114
+ if (isLaunchdJob(env, platform)) {
115
+ return {
116
+ kind: 'launchd',
117
+ outer: null,
118
+ willRelaunch: 'unknown',
119
+ detail: 'Runs directly under launchd; it comes back only if the job has KeepAlive set.',
120
+ };
121
+ }
122
+ if (p === null) {
123
+ return {
124
+ kind: 'unknown',
125
+ outer: null,
126
+ willRelaunch: 'unknown',
127
+ detail: 'Could not tell what started Crewly.',
128
+ };
129
+ }
130
+ return {
131
+ kind: 'none',
132
+ outer: null,
133
+ willRelaunch: 'no',
134
+ detail: 'Nothing will relaunch Crewly on its own; a restart starts a new copy before this one exits.',
135
+ };
136
+ }
137
+ /**
138
+ * Parent pid of a process (POSIX, via `ps`).
139
+ *
140
+ * @param pid - Process id
141
+ * @returns The parent pid, or null
142
+ */
143
+ export function readParentPid(pid) {
144
+ if (process.platform === 'win32' || !Number.isInteger(pid) || pid <= 1)
145
+ return null;
146
+ try {
147
+ const out = execFileSync('ps', ['-o', 'ppid=', '-p', String(pid)], { encoding: 'utf-8', timeout: 2000 }).trim();
148
+ const ppid = Number.parseInt(out, 10);
149
+ return Number.isInteger(ppid) && ppid > 1 ? ppid : null;
150
+ }
151
+ catch {
152
+ return null;
153
+ }
154
+ }
155
+ /**
156
+ * Detect the supervisor of the running process.
157
+ *
158
+ * @returns Supervisor info
159
+ */
160
+ export function detectRunningSupervisor() {
161
+ return detectSupervisor({
162
+ env: process.env,
163
+ platform: process.platform,
164
+ parentCommandLine: () => readProcessCommandLine(process.ppid),
165
+ grandparentCommandLine: () => {
166
+ const gp = readParentPid(process.ppid);
167
+ return gp ? readProcessCommandLine(gp) : null;
168
+ },
169
+ });
170
+ }
171
+ //# sourceMappingURL=supervisor-detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supervisor-detect.js","sourceRoot":"","sources":["../../../../../../backend/src/services/system/supervisor-detect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,337 @@
1
+ /**
2
+ * System Control Service — the owner's Upgrade and Restart buttons.
3
+ *
4
+ * Jenkins-style "safe restart" for Crewly
5
+ * (specs/2026-10-01-upgrade-restart-controls.md):
6
+ *
7
+ * - **Status.** Running version, latest on npm, install kind, who relaunches
8
+ * the backend, agents mid-turn, and the current / last action.
9
+ * - **Upgrade** (`when: idle | now`). npm global installs only. It reuses the
10
+ * AutoUpdateService install path (same prefix, same npm, same verify), then
11
+ * restarts. A source checkout is refused: it is updated with git.
12
+ * - **Restart** (`when: idle | now`). The graceful drained shutdown, exiting
13
+ * with RESTART_REQUESTED. When nothing is known to relaunch the backend, a
14
+ * detached launcher is started first so the machine never stays down.
15
+ * - **When idle** waits until no agent is mid-turn, capped (30 min), then
16
+ * goes ahead. Asking again with `now` cuts the wait short.
17
+ * - **One at a time.** A second request while one is running is refused,
18
+ * as is any request while a shutdown is already under way.
19
+ * - **Survives the restart.** Progress and outcome are kept in
20
+ * `<crewlyHome>/system-action.json`; the next boot settles the record
21
+ * ("Restarted at …", "Upgraded to x.y.z", or what went wrong), so the
22
+ * dashboard can show it after it reconnects.
23
+ *
24
+ * All I/O goes through {@link SystemControlDeps}.
25
+ *
26
+ * @module services/system/system-control.service
27
+ */
28
+ import { SYSTEM_CONTROL_CONSTANTS } from '../../constants.js';
29
+ import type { InstallAttempt } from './auto-update.service.js';
30
+ import type { InstallInfo } from './auto-update.utils.js';
31
+ import type { SupervisorInfo } from './supervisor-detect.js';
32
+ /** `when` choice of the buttons. */
33
+ export type SystemActionWhen = (typeof SYSTEM_CONTROL_CONSTANTS.WHEN_VALUES)[number];
34
+ /** Which button was pressed. */
35
+ export type SystemActionKind = 'upgrade' | 'restart';
36
+ /** Where an action is. */
37
+ export type SystemActionStatus = 'waiting-idle' | 'installing' | 'restarting' | 'completed' | 'failed' | 'interrupted';
38
+ /** Install kind as the dashboard sees it. */
39
+ export type PublicInstallKind = 'npm-global' | 'dev-checkout' | 'other';
40
+ /** How the backend comes back after it exits. */
41
+ export type RelaunchMethod = 'supervisor' | 'replacement';
42
+ /** One upgrade or restart, persisted across the restart. */
43
+ export interface SystemActionRecord {
44
+ id: string;
45
+ kind: SystemActionKind;
46
+ when: SystemActionWhen;
47
+ status: SystemActionStatus;
48
+ /** Who pressed it (e.g. `dashboard from 192.168.1.20`) */
49
+ requestedBy: string;
50
+ requestedAt: string;
51
+ updatedAt: string;
52
+ /** Version running when requested */
53
+ fromVersion: string | null;
54
+ /** Upgrade target */
55
+ toVersion: string | null;
56
+ /** Process that accepted the request */
57
+ pid: number;
58
+ /** How the backend is brought back */
59
+ relaunch: RelaunchMethod | null;
60
+ /** Owner-readable progress / outcome */
61
+ message: string;
62
+ /** Agents mid-turn the last time the idle wait looked */
63
+ waitingFor?: string[];
64
+ /** When the idle wait ended and why */
65
+ idleWaitEndedBy?: 'idle' | 'cap' | 'now';
66
+ /** Settled at (completed / failed / interrupted) */
67
+ completedAt?: string;
68
+ /** Version running when settled */
69
+ resultVersion?: string | null;
70
+ }
71
+ /** An agent mid-turn. */
72
+ export interface BusyAgentInfo {
73
+ session: string;
74
+ since?: string;
75
+ messagePreview?: string;
76
+ }
77
+ /** `GET /api/system/update-status` body. */
78
+ export interface UpdateStatus {
79
+ currentVersion: string | null;
80
+ latestVersion: string | null;
81
+ updateAvailable: boolean;
82
+ installKind: PublicInstallKind;
83
+ packageRoot: string | null;
84
+ /** Upgrade possible right now */
85
+ canUpgrade: boolean;
86
+ /** Why the Upgrade button is disabled (null when enabled) */
87
+ upgradeBlockedReason: string | null;
88
+ /** Restart possible right now */
89
+ canRestart: boolean;
90
+ /** Why the Restart button is disabled (null when enabled) */
91
+ restartBlockedReason: string | null;
92
+ supervisor: SupervisorInfo;
93
+ relaunch: RelaunchMethod;
94
+ /** Agents mid-turn now */
95
+ busyAgents: BusyAgentInfo[];
96
+ /** Some upgrade / restart / shutdown is under way */
97
+ inProgress: boolean;
98
+ /** Current or last action */
99
+ action: SystemActionRecord | null;
100
+ /** Changes on every boot; the dashboard uses it to see the backend came back */
101
+ bootId: string;
102
+ startedAt: string;
103
+ }
104
+ /** Refusal from a request. */
105
+ export interface SystemActionRefusal {
106
+ ok: false;
107
+ httpStatus: number;
108
+ code: string;
109
+ error: string;
110
+ }
111
+ /** Accepted request. */
112
+ export interface SystemActionAccepted {
113
+ ok: true;
114
+ action: SystemActionRecord;
115
+ /** True when an "idle" wait was cut short by a later "now" */
116
+ escalated?: boolean;
117
+ }
118
+ /** The install half the service borrows from AutoUpdateService. */
119
+ export interface UpgradeInstaller {
120
+ installVersion(version: string): Promise<InstallAttempt>;
121
+ writeUpgradeMarker(fromVersion: string, toVersion: string): void;
122
+ clearUpgradeMarker(): void;
123
+ isBusy(): boolean;
124
+ appendLogLine(line: string): void;
125
+ }
126
+ /** Minimal logger. */
127
+ export interface SystemControlLogger {
128
+ info(message: string, meta?: Record<string, unknown>): void;
129
+ warn(message: string, meta?: Record<string, unknown>): void;
130
+ error(message: string, meta?: Record<string, unknown>): void;
131
+ }
132
+ /** Everything the service touches outside itself. */
133
+ export interface SystemControlDeps {
134
+ crewlyHome: string;
135
+ install: InstallInfo;
136
+ currentVersion: string | null;
137
+ pid: number;
138
+ bootId: string;
139
+ startedAt: string;
140
+ /** Who relaunches the backend (evaluated when needed) */
141
+ getSupervisor: () => SupervisorInfo;
142
+ /** Latest version on npm, accepting a cached answer younger than maxAgeMs */
143
+ fetchLatestVersion: (maxAgeMs: number) => Promise<string | null>;
144
+ /** Agents mid-turn */
145
+ getBusyAgents: () => BusyAgentInfo[];
146
+ /** A shutdown / drain is already under way */
147
+ isShutdownInProgress: () => boolean;
148
+ /** The AutoUpdateService install path (null when it did not start) */
149
+ getInstaller: () => UpgradeInstaller | null;
150
+ /** Run the graceful drained shutdown with RESTART_REQUESTED; false when no handler */
151
+ requestGracefulRestart: (reason: string) => boolean;
152
+ /** Last resort when no graceful handler exists */
153
+ exit: (code: number) => void;
154
+ /** Start the detached launcher that brings the backend back */
155
+ spawnReplacement: (supervisorUnknown: boolean) => void;
156
+ logger: SystemControlLogger;
157
+ now: () => number;
158
+ sleep: (ms: number) => Promise<void>;
159
+ }
160
+ /** Options for the two requests. */
161
+ export interface SystemActionRequest {
162
+ when: SystemActionWhen;
163
+ actor: string;
164
+ }
165
+ /**
166
+ * Map the AutoUpdate install kind onto the public one.
167
+ *
168
+ * @param install - Install info
169
+ * @returns Public kind
170
+ */
171
+ export declare function toPublicInstallKind(install: InstallInfo): PublicInstallKind;
172
+ /**
173
+ * Read the persisted action record.
174
+ *
175
+ * @param file - State file
176
+ * @returns The record, or null when missing / malformed
177
+ */
178
+ export declare function readActionRecord(file: string): SystemActionRecord | null;
179
+ /**
180
+ * Write the action record atomically.
181
+ *
182
+ * @param file - State file
183
+ * @param record - Record
184
+ */
185
+ export declare function writeActionRecord(file: string, record: SystemActionRecord): void;
186
+ /**
187
+ * Upgrade / restart controller for the running backend.
188
+ */
189
+ export declare class SystemControlService {
190
+ private readonly deps;
191
+ private static instance;
192
+ private action;
193
+ private readonly stateFile;
194
+ /** Resolves the current idle wait early ("now" pressed while waiting) */
195
+ private cutIdleWaitShort;
196
+ /**
197
+ * @param deps - Injected I/O
198
+ */
199
+ constructor(deps: SystemControlDeps);
200
+ /**
201
+ * Register the process-wide instance.
202
+ *
203
+ * @param service - Instance, or null to clear
204
+ */
205
+ static setInstance(service: SystemControlService | null): void;
206
+ /**
207
+ * The process-wide instance, when the server registered one.
208
+ *
209
+ * @returns The instance or null
210
+ */
211
+ static getInstance(): SystemControlService | null;
212
+ /**
213
+ * Boot: settle the record a previous process left behind, so the
214
+ * dashboard can say "Restarted at …" / "Upgraded to x.y.z" after it
215
+ * reconnects.
216
+ */
217
+ handleBoot(): void;
218
+ /**
219
+ * Whether an upgrade or restart from this process is still running.
220
+ *
221
+ * @returns True while one is
222
+ */
223
+ isActionInProgress(): boolean;
224
+ /**
225
+ * The current or last action (this boot, or the one settled at boot).
226
+ *
227
+ * @returns The record or null
228
+ */
229
+ getAction(): SystemActionRecord | null;
230
+ /**
231
+ * How the backend comes back after an exit.
232
+ *
233
+ * @param supervisor - Supervisor info
234
+ * @returns supervisor or replacement
235
+ */
236
+ private relaunchMethod;
237
+ /**
238
+ * Status for the dashboard.
239
+ *
240
+ * @param options - `refresh` re-asks the registry when the cached answer is over a minute old
241
+ * @returns Status
242
+ */
243
+ getStatus(options?: {
244
+ refresh?: boolean;
245
+ }): Promise<UpdateStatus>;
246
+ /**
247
+ * Why nothing new can start right now (null when it can).
248
+ *
249
+ * @returns Owner-readable reason or null
250
+ */
251
+ private inProgressReason;
252
+ /**
253
+ * Busy agents, never throwing.
254
+ *
255
+ * @returns Agents mid-turn
256
+ */
257
+ private safeBusyAgents;
258
+ /**
259
+ * Escalate a running "when idle" wait of the same kind to "now".
260
+ *
261
+ * @param kind - Requested kind
262
+ * @param request - Request
263
+ * @returns Accepted answer, or null when this is not an escalation
264
+ */
265
+ private tryEscalate;
266
+ /**
267
+ * Refusal for "something is already running", or null.
268
+ *
269
+ * @returns Refusal or null
270
+ */
271
+ private busyRefusal;
272
+ /**
273
+ * Owner pressed "Upgrade".
274
+ *
275
+ * @param request - When, and who
276
+ * @returns Accepted (work continues in the background) or a refusal
277
+ */
278
+ requestUpgrade(request: SystemActionRequest): Promise<SystemActionAccepted | SystemActionRefusal>;
279
+ /**
280
+ * Owner pressed "Restart".
281
+ *
282
+ * @param request - When, and who
283
+ * @returns Accepted (work continues in the background) or a refusal
284
+ */
285
+ requestRestart(request: SystemActionRequest): Promise<SystemActionAccepted | SystemActionRefusal>;
286
+ /**
287
+ * Create and persist a new action.
288
+ *
289
+ * @param kind - upgrade or restart
290
+ * @param request - Request
291
+ * @param toVersion - Upgrade target
292
+ * @returns The record
293
+ */
294
+ private begin;
295
+ /**
296
+ * Upgrade body: (wait for idle) → install → verify → marker → restart.
297
+ *
298
+ * @param installer - AutoUpdate install path
299
+ * @param version - Target
300
+ */
301
+ private runUpgrade;
302
+ /**
303
+ * Restart body: (wait for idle) → restart.
304
+ */
305
+ private runRestart;
306
+ /**
307
+ * Make sure something brings the backend back, then run the graceful
308
+ * restart.
309
+ *
310
+ * @param reason - Shutdown reason (logs)
311
+ * @param message - Owner-readable progress
312
+ * @returns False when the restart could not be started
313
+ */
314
+ private restartNow;
315
+ /**
316
+ * Wait until no agent is mid-turn, at most IDLE_WAIT_CAP_MS; a later "now"
317
+ * request ends the wait at once.
318
+ */
319
+ private waitForIdle;
320
+ /**
321
+ * Mark the current action failed.
322
+ *
323
+ * @param message - Owner-readable reason
324
+ */
325
+ private fail;
326
+ /**
327
+ * Merge a patch into the current action and persist it.
328
+ *
329
+ * @param patch - Fields to change
330
+ */
331
+ private update;
332
+ /**
333
+ * Write the current action (best-effort).
334
+ */
335
+ private persist;
336
+ }
337
+ //# sourceMappingURL=system-control.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-control.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/system/system-control.service.ts"],"names":[],"mappings":"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"}