@celilo/cli 0.5.0-alpha.1 → 0.5.0-alpha.11
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/drizzle/0009_dns_registrations.sql +13 -0
- package/drizzle/0010_dns_internal_records.sql +12 -0
- package/drizzle/0011_backups_name.sql +1 -0
- package/drizzle/meta/_journal.json +22 -1
- package/package.json +3 -3
- package/src/ansible/inventory.test.ts +10 -10
- package/src/ansible/validation.test.ts +25 -15
- package/src/api-clients/proxmox.test.ts +211 -1
- package/src/api-clients/proxmox.ts +399 -8
- package/src/cli/command-registry.ts +83 -6
- package/src/cli/commands/backup-delete.ts +10 -7
- package/src/cli/commands/backup-import.ts +11 -8
- package/src/cli/commands/backup-restore.ts +11 -8
- package/src/cli/commands/dns.ts +57 -0
- package/src/cli/commands/events.test.ts +4 -4
- package/src/cli/commands/events.ts +89 -24
- package/src/cli/commands/machine-add.ts +178 -163
- package/src/cli/commands/machine-remove.ts +10 -7
- package/src/cli/commands/module-config.test.ts +78 -0
- package/src/cli/commands/module-config.ts +18 -3
- package/src/cli/commands/module-import.ts +9 -5
- package/src/cli/commands/module-publish.ts +24 -0
- package/src/cli/commands/module-remove.ts +20 -9
- package/src/cli/commands/module-status.ts +15 -0
- package/src/cli/commands/module-upgrade.test.ts +37 -0
- package/src/cli/commands/module-upgrade.ts +26 -6
- package/src/cli/commands/proxmox-node-list.ts +101 -0
- package/src/cli/commands/proxmox-template-selection.ts +16 -15
- package/src/cli/commands/proxmox-vm-template-build.ts +171 -0
- package/src/cli/commands/publish/alpha.test.ts +26 -0
- package/src/cli/commands/publish/alpha.ts +23 -0
- package/src/cli/commands/publish/types.ts +7 -2
- package/src/cli/commands/publish/workspace.ts +11 -1
- package/src/cli/commands/restore.ts +29 -0
- package/src/cli/commands/service-add-digitalocean.ts +120 -109
- package/src/cli/commands/service-add-proxmox.ts +283 -209
- package/src/cli/commands/service-reconfigure.test.ts +115 -0
- package/src/cli/commands/service-reconfigure.ts +252 -129
- package/src/cli/commands/service-remove.ts +19 -13
- package/src/cli/commands/service-verify.ts +9 -10
- package/src/cli/commands/storage-add-local.ts +120 -107
- package/src/cli/commands/storage-add-s3.ts +145 -131
- package/src/cli/commands/storage-remove.ts +11 -8
- package/src/cli/commands/system-doctor.ts +135 -40
- package/src/cli/commands/system-init.ts +119 -128
- package/src/cli/commands/system-migrate.test.ts +40 -0
- package/src/cli/commands/system-migrate.ts +65 -0
- package/src/cli/completion.ts +23 -0
- package/src/cli/index.ts +91 -7
- package/src/cli/service-credential.ts +54 -0
- package/src/config/paths.test.ts +61 -48
- package/src/db/client.ts +15 -146
- package/src/db/migrate.ts +14 -6
- package/src/db/schema-introspection.ts +88 -0
- package/src/db/schema.ts +74 -0
- package/src/hooks/capability-loader-firewall.test.ts +3 -3
- package/src/hooks/capability-loader.ts +43 -2
- package/src/hooks/run-named-hook.ts +28 -2
- package/src/hooks/types.ts +2 -1
- package/src/infrastructure/property-extractor.test.ts +15 -0
- package/src/infrastructure/property-extractor.ts +12 -0
- package/src/manifest/contracts/v1.ts +16 -0
- package/src/manifest/schema.ts +17 -0
- package/src/manifest/validate.test.ts +53 -0
- package/src/services/bus-interview.test.ts +2 -2
- package/src/services/bus-interview.ts +232 -0
- package/src/services/bus-secret-flow.test.ts +2 -2
- package/src/services/celilo-mgmt-hooks.test.ts +3 -2
- package/src/services/deploy-preflight.ts +25 -0
- package/src/services/deploy-validation.test.ts +54 -4
- package/src/services/deploy-validation.ts +27 -36
- package/src/services/dns-internal-records.test.ts +126 -0
- package/src/services/dns-internal-records.ts +119 -0
- package/src/services/dns-provider-backfill.test.ts +2 -2
- package/src/services/dns-provider-backfill.ts +14 -2
- package/src/services/dns-registrations.test.ts +120 -0
- package/src/services/dns-registrations.ts +108 -0
- package/src/services/events-daemon.test.ts +59 -0
- package/src/services/events-daemon.ts +191 -57
- package/src/services/fleet-checks.test.ts +508 -0
- package/src/services/fleet-checks.ts +678 -0
- package/src/services/module-build.test.ts +43 -38
- package/src/services/module-config.ts +12 -0
- package/src/services/module-deploy.ts +7 -6
- package/src/services/module-subscriptions.test.ts +88 -0
- package/src/services/module-subscriptions.ts +50 -1
- package/src/services/module-validator/bundled-deps.test.ts +55 -0
- package/src/services/module-validator/bundled-deps.ts +115 -0
- package/src/services/module-validator/capability-versions.test.ts +1 -1
- package/src/services/placement-reconcile.test.ts +86 -0
- package/src/services/placement-reconcile.ts +108 -0
- package/src/services/programmatic-responder.ts +34 -0
- package/src/services/terminal-responder.ts +113 -0
- package/src/templates/generator.test.ts +92 -12
- package/src/templates/generator.ts +165 -80
- package/src/test-utils/fixtures.test.ts +1 -1
- package/src/test-utils/integration-guard.ts +33 -0
- package/src/types/infrastructure.ts +6 -0
- package/src/variables/computed/computed-integration.test.ts +3 -3
- package/src/variables/computed/computed.test.ts +5 -5
- package/src/variables/declarative-derivation.test.ts +6 -6
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* Restores from a backup archive, executing the module's on_restore hook.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import * as p from '@clack/prompts';
|
|
7
6
|
import { formatSize, getBackup } from '../../services/backup-metadata';
|
|
8
7
|
import { restoreModuleBackup, restoreSystemStateBackup } from '../../services/backup-restore';
|
|
9
8
|
import { getBackupStorage } from '../../services/backup-storage';
|
|
9
|
+
import { askConfirm, withInterviewSession } from '../../services/bus-interview';
|
|
10
10
|
import { celiloIntro, celiloOutro } from '../prompts';
|
|
11
11
|
import type { CommandResult } from '../types';
|
|
12
12
|
|
|
@@ -66,13 +66,16 @@ export async function handleBackupRestore(
|
|
|
66
66
|
|
|
67
67
|
// Confirm unless --yes
|
|
68
68
|
if (!flags.yes) {
|
|
69
|
-
const confirmed = await
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
const confirmed = await withInterviewSession(() =>
|
|
70
|
+
askConfirm({
|
|
71
|
+
scope: `backup-restore:${backupId}`,
|
|
72
|
+
key: 'proceed',
|
|
73
|
+
message: 'Proceed with restore?',
|
|
74
|
+
defaultValue: false,
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (!confirmed) {
|
|
76
79
|
return { success: false, error: 'Cancelled by user' };
|
|
77
80
|
}
|
|
78
81
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `celilo dns` — operator views over celilo's DNS bookkeeping.
|
|
3
|
+
*
|
|
4
|
+
* `registrations` lists the dns_registrations ledger: every (provider,
|
|
5
|
+
* fqdn) the framework has registered via dns_registrar.registerHost,
|
|
6
|
+
* with the consumer that asked for it and when it was last re-asserted
|
|
7
|
+
* by the provider's refresh_registrations hook. Read-only; names only
|
|
8
|
+
* (module ids), never UUIDs. See
|
|
9
|
+
* designs/DISPATCHER_DAEMON_AND_TIMER_EVENTS.md (B2b).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { getDb } from '../../db/client';
|
|
13
|
+
import { listDnsRegistrations } from '../../services/dns-registrations';
|
|
14
|
+
import type { CommandResult } from '../types';
|
|
15
|
+
|
|
16
|
+
function formatAge(from: Date | null, now: Date): string {
|
|
17
|
+
if (!from) return 'never';
|
|
18
|
+
const mins = Math.floor((now.getTime() - from.getTime()) / 60_000);
|
|
19
|
+
if (mins < 1) return 'just now';
|
|
20
|
+
if (mins < 60) return `${mins}m ago`;
|
|
21
|
+
const hours = Math.floor(mins / 60);
|
|
22
|
+
if (hours < 48) return `${hours}h ago`;
|
|
23
|
+
return `${Math.floor(hours / 24)}d ago`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function handleDnsRegistrations(
|
|
27
|
+
_args: string[],
|
|
28
|
+
flags: Record<string, string | boolean>,
|
|
29
|
+
): Promise<CommandResult> {
|
|
30
|
+
const db = getDb();
|
|
31
|
+
const provider = typeof flags.provider === 'string' ? flags.provider : undefined;
|
|
32
|
+
const rows = listDnsRegistrations(db, { providerModuleId: provider });
|
|
33
|
+
|
|
34
|
+
if (rows.length === 0) {
|
|
35
|
+
const scope = provider ? ` for provider '${provider}'` : '';
|
|
36
|
+
return {
|
|
37
|
+
success: true,
|
|
38
|
+
message: `No DNS registrations recorded${scope}\n\nRegistrations are recorded when a module registers a hostname via the dns_registrar capability (e.g. during deploy).`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const now = new Date();
|
|
43
|
+
const header = ['FQDN', 'IP', 'PROVIDER', 'CONSUMER', 'REGISTERED', 'REFRESHED'];
|
|
44
|
+
const table = rows.map((r) => [
|
|
45
|
+
r.fqdn,
|
|
46
|
+
r.ip ?? 'auto',
|
|
47
|
+
r.providerModuleId,
|
|
48
|
+
r.consumerModuleId,
|
|
49
|
+
formatAge(r.registeredAt, now),
|
|
50
|
+
formatAge(r.refreshedAt, now),
|
|
51
|
+
]);
|
|
52
|
+
const widths = header.map((h, i) => Math.max(h.length, ...table.map((row) => row[i].length)));
|
|
53
|
+
const render = (row: string[]) => row.map((cell, i) => cell.padEnd(widths[i])).join(' ');
|
|
54
|
+
|
|
55
|
+
const lines = [render(header), ...table.map(render)];
|
|
56
|
+
return { success: true, message: lines.join('\n'), data: rows };
|
|
57
|
+
}
|
|
@@ -165,20 +165,20 @@ describe('celilo events command handlers', () => {
|
|
|
165
165
|
});
|
|
166
166
|
setupBus.close();
|
|
167
167
|
|
|
168
|
-
const res = await handleEventsReply([String(query.id), '"
|
|
168
|
+
const res = await handleEventsReply([String(query.id), '"example.net"'], {});
|
|
169
169
|
expect(res.success).toBe(true);
|
|
170
170
|
if (!res.success) throw new Error('expected success');
|
|
171
171
|
const data = res.data as { status: string; family: string; value: unknown };
|
|
172
172
|
expect(data.status).toBe('replied');
|
|
173
173
|
expect(data.family).toBe('config');
|
|
174
|
-
expect(data.value).toBe('
|
|
174
|
+
expect(data.value).toBe('example.net');
|
|
175
175
|
|
|
176
176
|
const checkBus = openBus({ dbPath, events: defineEvents({}) });
|
|
177
177
|
const replies = checkBus.recentEvents({ type: 'config.required.lunacycle.domain.reply' });
|
|
178
178
|
checkBus.close();
|
|
179
179
|
expect(replies).toHaveLength(1);
|
|
180
180
|
expect(replies[0].replyFor).toBe(query.id);
|
|
181
|
-
expect(replies[0].payload).toEqual({ value: '
|
|
181
|
+
expect(replies[0].payload).toEqual({ value: 'example.net' });
|
|
182
182
|
expect(replies[0].emittedBy).toBe('claude-config-responder');
|
|
183
183
|
});
|
|
184
184
|
|
|
@@ -229,7 +229,7 @@ describe('celilo events command handlers', () => {
|
|
|
229
229
|
setupBus.close();
|
|
230
230
|
|
|
231
231
|
// A bare word isn't valid JSON — the operator must quote strings.
|
|
232
|
-
const res = await handleEventsReply([String(query.id), '
|
|
232
|
+
const res = await handleEventsReply([String(query.id), 'example.net'], {});
|
|
233
233
|
expect(res.success).toBe(false);
|
|
234
234
|
if (res.success) throw new Error('expected failure');
|
|
235
235
|
expect(res.error).toContain('Invalid JSON value');
|
|
@@ -36,7 +36,12 @@ import { runNamedHook } from '../../hooks/run-named-hook';
|
|
|
36
36
|
import type { HookName } from '../../hooks/types';
|
|
37
37
|
import type { ModuleManifest } from '../../manifest/schema';
|
|
38
38
|
import type { EnsureRequiredPayload } from '../../services/bus-interview';
|
|
39
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
installDaemon,
|
|
41
|
+
planDaemonInstall,
|
|
42
|
+
readInstalledUnit,
|
|
43
|
+
uninstallDaemon,
|
|
44
|
+
} from '../../services/events-daemon';
|
|
40
45
|
import { getArg, hasFlag } from '../parser';
|
|
41
46
|
import type { CommandResult } from '../types';
|
|
42
47
|
|
|
@@ -187,6 +192,34 @@ export async function handleEventsListSubscribers(): Promise<CommandResult> {
|
|
|
187
192
|
}
|
|
188
193
|
}
|
|
189
194
|
|
|
195
|
+
/**
|
|
196
|
+
* `celilo events resync-subscriptions` — rebuild the bus `subscribers` table
|
|
197
|
+
* from every deployed module's manifest. The reactive layer is registered at
|
|
198
|
+
* DEPLOY time and lives in the bus, which a restore/migration starts EMPTY — so
|
|
199
|
+
* after a cutover this re-establishes who-reacts-to-what without redeploying
|
|
200
|
+
* every module (ISS-0088). Idempotent.
|
|
201
|
+
*/
|
|
202
|
+
export async function handleEventsResyncSubscriptions(): Promise<CommandResult> {
|
|
203
|
+
try {
|
|
204
|
+
const { resyncAllSubscriptions } = await import('../../services/module-subscriptions');
|
|
205
|
+
const result = resyncAllSubscriptions();
|
|
206
|
+
const lines = [
|
|
207
|
+
`Re-registered ${result.registered} subscription(s) from ${result.modules} deployed module(s).`,
|
|
208
|
+
];
|
|
209
|
+
if (result.failures.length > 0) {
|
|
210
|
+
const detail = result.failures.map((f) => ` ${f.moduleId}: ${f.error}`).join('\n');
|
|
211
|
+
return {
|
|
212
|
+
success: false,
|
|
213
|
+
error: `${result.failures.length} module(s) failed to re-register subscriptions:\n${detail}`,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
lines.push('', 'A running dispatcher will now deliver events to these handlers.');
|
|
217
|
+
return { success: true, message: lines.join('\n'), data: result };
|
|
218
|
+
} catch (err) {
|
|
219
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
190
223
|
export async function handleEventsListPending(
|
|
191
224
|
_args: string[],
|
|
192
225
|
flags: Record<string, string | boolean>,
|
|
@@ -356,7 +389,7 @@ export async function handleEventsRepair(): Promise<CommandResult> {
|
|
|
356
389
|
}
|
|
357
390
|
}
|
|
358
391
|
|
|
359
|
-
const INTERVIEW_FAMILIES = ['config', 'secret', 'ensure', 'aspect'] as const;
|
|
392
|
+
const INTERVIEW_FAMILIES = ['config', 'secret', 'ensure', 'aspect', 'interview'] as const;
|
|
360
393
|
type InterviewFamily = (typeof INTERVIEW_FAMILIES)[number];
|
|
361
394
|
|
|
362
395
|
/** Classify a query event type into its interview family, or null if it isn't one. */
|
|
@@ -365,6 +398,7 @@ function interviewFamily(type: string): InterviewFamily | null {
|
|
|
365
398
|
if (type.startsWith('secret.required.')) return 'secret';
|
|
366
399
|
if (type.startsWith('ensure.required.')) return 'ensure';
|
|
367
400
|
if (type.startsWith('aspect.required.')) return 'aspect';
|
|
401
|
+
if (type.startsWith('interview.required.')) return 'interview';
|
|
368
402
|
return null;
|
|
369
403
|
}
|
|
370
404
|
|
|
@@ -395,6 +429,10 @@ function interviewFamily(type: string): InterviewFamily | null {
|
|
|
395
429
|
* base-module aspect, 'false' refuses it (ISS-0027).
|
|
396
430
|
* Replies { consented }; the deploy records the
|
|
397
431
|
* approval/denial.
|
|
432
|
+
* - interview.required.<scope>.<key> → value is the answer itself, shaped per
|
|
433
|
+
* the question's kind ('"node3"', 'true',
|
|
434
|
+
* '["a","b"]'); replies { value }. The generic
|
|
435
|
+
* operator-command interview family (ISS-0127).
|
|
398
436
|
*/
|
|
399
437
|
export async function handleEventsReply(
|
|
400
438
|
args: string[],
|
|
@@ -406,7 +444,7 @@ export async function handleEventsReply(
|
|
|
406
444
|
return {
|
|
407
445
|
success: false,
|
|
408
446
|
error: `Usage: celilo events reply <query-event-id> <value-json>
|
|
409
|
-
e.g. celilo events reply 42 '"
|
|
447
|
+
e.g. celilo events reply 42 '"example.net"'`,
|
|
410
448
|
};
|
|
411
449
|
}
|
|
412
450
|
const queryId = Number(idArg);
|
|
@@ -421,7 +459,7 @@ export async function handleEventsReply(
|
|
|
421
459
|
return {
|
|
422
460
|
success: false,
|
|
423
461
|
error: `Invalid JSON value: ${err instanceof Error ? err.message : String(err)}
|
|
424
|
-
Encode the answer as JSON, e.g. '"
|
|
462
|
+
Encode the answer as JSON, e.g. '"example.net"', '8080', '["a","b"]'.`,
|
|
425
463
|
};
|
|
426
464
|
}
|
|
427
465
|
|
|
@@ -443,7 +481,7 @@ export async function handleEventsReply(
|
|
|
443
481
|
return {
|
|
444
482
|
success: false,
|
|
445
483
|
error: `Event ${queryId} is type '${query.type}', not an interview query.
|
|
446
|
-
Expected one of: config.required.* / secret.required.* / ensure.required.* / aspect.required.*`,
|
|
484
|
+
Expected one of: config.required.* / secret.required.* / ensure.required.* / aspect.required.* / interview.required.*`,
|
|
447
485
|
};
|
|
448
486
|
}
|
|
449
487
|
|
|
@@ -463,7 +501,7 @@ export async function handleEventsReply(
|
|
|
463
501
|
});
|
|
464
502
|
}
|
|
465
503
|
|
|
466
|
-
if (family === 'config') {
|
|
504
|
+
if (family === 'config' || family === 'interview') {
|
|
467
505
|
bus.emitRaw(`${query.type}.reply`, { value }, { replyFor: queryId, emittedBy });
|
|
468
506
|
return jsonResult({ ok: true, status: 'replied', queryId, type: query.type, family, value });
|
|
469
507
|
}
|
|
@@ -778,27 +816,46 @@ function parseDurationMs(flag: string | boolean | undefined, defaultMs: number):
|
|
|
778
816
|
}
|
|
779
817
|
}
|
|
780
818
|
|
|
819
|
+
function daemonScopeFrom(flags: Record<string, string | boolean>): 'user' | 'system' {
|
|
820
|
+
return flags.system ? 'system' : 'user';
|
|
821
|
+
}
|
|
822
|
+
|
|
781
823
|
/**
|
|
782
|
-
* `celilo events install-daemon` — write a systemd
|
|
783
|
-
*
|
|
784
|
-
*
|
|
785
|
-
*
|
|
786
|
-
*
|
|
824
|
+
* `celilo events install-daemon` — write a systemd unit (Linux) or
|
|
825
|
+
* launchd plist (macOS) that runs the dispatcher under supervision.
|
|
826
|
+
* Default is a per-user unit; `--system` writes the system-scope unit
|
|
827
|
+
* (/etc/systemd/system, or a /Library/LaunchDaemons LaunchDaemon on
|
|
828
|
+
* macOS) that survives logout/reboot and runs as the celilo state-dir
|
|
829
|
+
* owner — the management-plane shape. Writes the file but doesn't
|
|
830
|
+
* touch supervisor state — the operator (or the celilo-mgmt Ansible
|
|
831
|
+
* role) runs the enable/bootstrap steps themselves so any change is
|
|
832
|
+
* visible.
|
|
787
833
|
*/
|
|
788
834
|
export async function handleEventsInstallDaemon(
|
|
789
835
|
_args: string[],
|
|
790
836
|
flags: Record<string, string | boolean>,
|
|
791
837
|
): Promise<CommandResult> {
|
|
792
838
|
try {
|
|
793
|
-
const
|
|
839
|
+
const options = {
|
|
794
840
|
celiloPath: typeof flags['celilo-path'] === 'string' ? flags['celilo-path'] : undefined,
|
|
795
841
|
pollMs: flags['poll-ms'] ? Number(flags['poll-ms']) : undefined,
|
|
796
842
|
concurrency: flags.concurrency ? Number(flags.concurrency) : undefined,
|
|
797
|
-
|
|
843
|
+
scope: daemonScopeFrom(flags),
|
|
844
|
+
};
|
|
845
|
+
// --print: render-only, raw unit content on stdout. The celilo-mgmt
|
|
846
|
+
// Ansible role captures this and does the root-owned write itself
|
|
847
|
+
// (the deb wrapper runs celilo as the unprivileged celilo user, so
|
|
848
|
+
// a direct --system write would EACCES on /etc/systemd/system).
|
|
849
|
+
if (flags.print) {
|
|
850
|
+
const plan = planDaemonInstall(options);
|
|
851
|
+
return { success: true, message: plan.unitContent, rawOutput: true, data: plan };
|
|
852
|
+
}
|
|
853
|
+
const result = installDaemon(options);
|
|
798
854
|
const lines = [
|
|
799
|
-
`Wrote ${result.platform} unit: ${shortenPath(result.unitPath)}`,
|
|
855
|
+
`Wrote ${result.platform} ${result.scope} unit: ${shortenPath(result.unitPath)}`,
|
|
800
856
|
` celilo: ${shortenPath(result.celiloPath)}`,
|
|
801
857
|
` bus DB: ${shortenPath(result.busDbPath)}`,
|
|
858
|
+
...(result.runAsUser ? [` runs as: ${result.runAsUser}`] : []),
|
|
802
859
|
'',
|
|
803
860
|
'Next steps (run these yourself — install does not touch supervisor state):',
|
|
804
861
|
...result.nextSteps.map((s) => ` ${s}`),
|
|
@@ -813,13 +870,16 @@ export async function handleEventsInstallDaemon(
|
|
|
813
870
|
}
|
|
814
871
|
|
|
815
872
|
/**
|
|
816
|
-
* `celilo events uninstall-daemon` — remove the supervisor unit file
|
|
817
|
-
*
|
|
818
|
-
* them.
|
|
873
|
+
* `celilo events uninstall-daemon` — remove the supervisor unit file
|
|
874
|
+
* (`--system` for the system-scope unit). Symmetrical: prints the
|
|
875
|
+
* operator-facing disable steps, doesn't run them.
|
|
819
876
|
*/
|
|
820
|
-
export async function handleEventsUninstallDaemon(
|
|
877
|
+
export async function handleEventsUninstallDaemon(
|
|
878
|
+
_args: string[],
|
|
879
|
+
flags: Record<string, string | boolean>,
|
|
880
|
+
): Promise<CommandResult> {
|
|
821
881
|
try {
|
|
822
|
-
const result = uninstallDaemon();
|
|
882
|
+
const result = uninstallDaemon({ scope: daemonScopeFrom(flags) });
|
|
823
883
|
const lines = [
|
|
824
884
|
result.removed
|
|
825
885
|
? `Removed unit: ${shortenPath(result.unitPath)}`
|
|
@@ -839,15 +899,20 @@ export async function handleEventsUninstallDaemon(): Promise<CommandResult> {
|
|
|
839
899
|
|
|
840
900
|
/**
|
|
841
901
|
* `celilo events show-daemon` — print whatever unit file is currently
|
|
842
|
-
* installed
|
|
843
|
-
* run, without grepping into
|
|
902
|
+
* installed (`--system` for the system-scope unit) so the operator can
|
|
903
|
+
* see exactly what the supervisor will run, without grepping into
|
|
904
|
+
* platform-specific paths.
|
|
844
905
|
*/
|
|
845
|
-
export async function handleEventsShowDaemon(
|
|
846
|
-
|
|
906
|
+
export async function handleEventsShowDaemon(
|
|
907
|
+
_args: string[],
|
|
908
|
+
flags: Record<string, string | boolean>,
|
|
909
|
+
): Promise<CommandResult> {
|
|
910
|
+
const scope = daemonScopeFrom(flags);
|
|
911
|
+
const result = readInstalledUnit({ scope });
|
|
847
912
|
if (!result.exists) {
|
|
848
913
|
return {
|
|
849
914
|
success: true,
|
|
850
|
-
message: `No supervisor unit installed at ${shortenPath(result.path)}\n\nRun \`celilo events install-daemon\` to create one.`,
|
|
915
|
+
message: `No supervisor unit installed at ${shortenPath(result.path)}\n\nRun \`celilo events install-daemon${scope === 'system' ? ' --system' : ''}\` to create one.`,
|
|
851
916
|
};
|
|
852
917
|
}
|
|
853
918
|
return {
|