@celilo/cli 0.3.30 → 0.4.0-alpha.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/drizzle/0005_module_operations.sql +12 -0
- package/drizzle/0006_base_module_aspects.sql +15 -0
- package/drizzle/0007_module_systems.sql +17 -0
- package/drizzle/meta/_journal.json +21 -0
- package/package.json +5 -4
- package/schemas/system_config.json +14 -28
- package/src/ansible/inventory.test.ts +46 -62
- package/src/ansible/inventory.ts +48 -25
- package/src/capabilities/registration.ts +25 -7
- package/src/capabilities/validation.test.ts +30 -0
- package/src/capabilities/validation.ts +8 -0
- package/src/cli/backup-rename.test.ts +95 -0
- package/src/cli/cli.test.ts +17 -23
- package/src/cli/command-registry.ts +199 -0
- package/src/cli/commands/backup-list.ts +1 -1
- package/src/cli/commands/events.ts +96 -0
- package/src/cli/commands/machine-add.ts +103 -59
- package/src/cli/commands/module-import.ts +153 -4
- package/src/cli/commands/module-remove.ts +86 -17
- package/src/cli/commands/module-status.ts +6 -2
- package/src/cli/commands/publish/alpha.test.ts +185 -0
- package/src/cli/commands/publish/alpha.ts +226 -0
- package/src/cli/commands/publish/changesets.test.ts +89 -0
- package/src/cli/commands/publish/changesets.ts +144 -0
- package/src/cli/commands/publish/consumer-pins.test.ts +155 -0
- package/src/cli/commands/publish/consumer-pins.ts +149 -0
- package/src/cli/commands/publish/execute.ts +131 -0
- package/src/cli/commands/publish/global-install.test.ts +154 -0
- package/src/cli/commands/publish/global-install.ts +171 -0
- package/src/cli/commands/publish/helpers.ts +227 -0
- package/src/cli/commands/publish/index.ts +365 -0
- package/src/cli/commands/publish/module-registry.test.ts +40 -0
- package/src/cli/commands/publish/module-registry.ts +64 -0
- package/src/cli/commands/publish/plan.ts +107 -0
- package/src/cli/commands/publish/preflight.ts +238 -0
- package/src/cli/commands/publish/types.ts +264 -0
- package/src/cli/commands/publish/workspace.test.ts +323 -0
- package/src/cli/commands/publish/workspace.ts +596 -0
- package/src/cli/commands/restore.ts +126 -0
- package/src/cli/commands/storage-add-local.ts +1 -1
- package/src/cli/commands/storage-add-s3.ts +1 -1
- package/src/cli/commands/subscribers-add.ts +68 -0
- package/src/cli/commands/subscribers-list.ts +48 -0
- package/src/cli/commands/subscribers-remove.ts +38 -0
- package/src/cli/commands/subscribers-serve.ts +77 -0
- package/src/cli/commands/subscribers-status.ts +33 -0
- package/src/cli/commands/subscribers-test.ts +71 -0
- package/src/cli/commands/system-apply-config-equivalence.test.ts +108 -0
- package/src/cli/commands/system-apply-config.test.ts +70 -0
- package/src/cli/commands/system-apply-config.ts +130 -0
- package/src/cli/commands/system-audit.ts +2 -1
- package/src/cli/commands/system-init-deprecation.test.ts +90 -0
- package/src/cli/commands/system-init.ts +36 -70
- package/src/cli/commands/system-update.ts +3 -2
- package/src/cli/completion.ts +22 -1
- package/src/cli/index.ts +214 -6
- package/src/cli/interactive-config.test.ts +19 -0
- package/src/cli/restore-command.test.ts +131 -0
- package/src/db/client.ts +42 -0
- package/src/db/schema.test.ts +13 -16
- package/src/db/schema.ts +161 -9
- package/src/hooks/capability-loader-firewall.test.ts +6 -15
- package/src/hooks/capability-loader.test.ts +2 -3
- package/src/hooks/capability-loader.ts +36 -2
- package/src/hooks/define-hook.test.ts +4 -0
- package/src/hooks/executor.test.ts +18 -0
- package/src/hooks/executor.ts +21 -2
- package/src/hooks/load-hook-config.test.ts +26 -24
- package/src/hooks/load-hook-config.ts +11 -2
- package/src/hooks/run-named-hook.ts +16 -0
- package/src/hooks/types.ts +9 -1
- package/src/manifest/contracts/v1.ts +70 -0
- package/src/manifest/schema.ts +262 -16
- package/src/manifest/validate-privileged.test.ts +84 -0
- package/src/manifest/validate.test.ts +156 -0
- package/src/manifest/validate.ts +69 -0
- package/src/module/import.ts +12 -0
- package/src/services/aspect-approvals.test.ts +231 -0
- package/src/services/aspect-approvals.ts +120 -0
- package/src/services/aspect-runner.test.ts +493 -0
- package/src/services/aspect-runner.ts +438 -0
- package/src/services/aspect-template-resolver.test.ts +101 -0
- package/src/services/aspect-template-resolver.ts +122 -0
- package/src/services/backup-create.ts +104 -25
- package/src/services/backup-envelope-roundtrip.test.ts +199 -0
- package/src/services/backup-in-flight-refusal.test.ts +163 -0
- package/src/services/backup-manifest.test.ts +115 -0
- package/src/services/backup-manifest.ts +163 -0
- package/src/services/backup-restore.ts +154 -19
- package/src/services/build-bus/delivery-events.ts +92 -0
- package/src/services/build-bus/event-factory.ts +54 -0
- package/src/services/build-bus/fan-out.test.ts +279 -0
- package/src/services/build-bus/fan-out.ts +161 -0
- package/src/services/build-bus/hook-dispatch-mgmt.test.ts +157 -0
- package/src/services/build-bus/hook-dispatch.test.ts +207 -0
- package/src/services/build-bus/hook-dispatch.ts +198 -0
- package/src/services/build-bus/hook-dispatcher.ts +115 -0
- package/src/services/build-bus/index.ts +41 -0
- package/src/services/build-bus/receiver-server.test.ts +179 -0
- package/src/services/build-bus/receiver-server.ts +159 -0
- package/src/services/build-bus/status.test.ts +212 -0
- package/src/services/build-bus/status.ts +213 -0
- package/src/services/build-bus/subscriber-store.ts +113 -0
- package/src/services/celilo-events.test.ts +70 -0
- package/src/services/celilo-events.ts +92 -0
- package/src/services/celilo-mgmt-hooks.test.ts +296 -0
- package/src/services/config-interview.ts +13 -95
- package/src/services/cross-module-data-manager.ts +2 -31
- package/src/services/cross-module-read.test.ts +250 -0
- package/src/services/cross-module-read.ts +232 -0
- package/src/services/deploy-validation.ts +7 -0
- package/src/services/deployed-systems.test.ts +235 -0
- package/src/services/deployed-systems.ts +308 -0
- package/src/services/dns-provider-backfill.ts +75 -0
- package/src/services/health-runner.ts +19 -3
- package/src/services/infrastructure-variable-resolver.test.ts +6 -32
- package/src/services/infrastructure-variable-resolver.ts +3 -13
- package/src/services/machine-detector.ts +104 -48
- package/src/services/machine-pool.ts +145 -2
- package/src/services/module-config.ts +78 -120
- package/src/services/module-deploy.ts +113 -40
- package/src/services/module-operations.test.ts +154 -0
- package/src/services/module-operations.ts +154 -0
- package/src/services/module-subscriptions.test.ts +58 -0
- package/src/services/module-subscriptions.ts +24 -1
- package/src/services/module-types-generator.test.ts +3 -3
- package/src/services/module-types-generator.ts +7 -2
- package/src/services/proxmox-reconcile.test.ts +333 -0
- package/src/services/proxmox-reconcile.ts +156 -0
- package/src/services/proxmox-state-recovery.ts +3 -24
- package/src/services/restore-from-file.test.ts +177 -0
- package/src/services/restore-from-file.ts +355 -0
- package/src/services/restore-preflight.test.ts +127 -0
- package/src/services/restore-preflight.ts +118 -0
- package/src/services/storage-providers/s3.ts +10 -2
- package/src/services/system-identity.ts +30 -0
- package/src/services/system-init.test.ts +64 -21
- package/src/services/system-init.ts +28 -26
- package/src/templates/generator.test.ts +7 -16
- package/src/templates/generator.ts +28 -115
- package/src/test-utils/integration.ts +5 -2
- package/src/types/infrastructure.ts +8 -0
- package/src/variables/computed/computed-integration.test.ts +191 -0
- package/src/variables/computed/computed.test.ts +177 -0
- package/src/variables/computed/evaluate.ts +271 -0
- package/src/variables/computed/marker.ts +53 -0
- package/src/variables/computed/parse.ts +262 -0
- package/src/variables/computed/provider-lookup.ts +130 -0
- package/src/variables/context.test.ts +89 -28
- package/src/variables/context.ts +196 -191
- package/src/variables/parser.ts +3 -3
- package/src/variables/resolver.test.ts +61 -0
- package/src/variables/resolver.ts +81 -0
- package/src/variables/types.ts +23 -1
- package/src/services/dns-auto-register.ts +0 -211
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `celilo system apply-config` — headless write to systemConfig.
|
|
3
|
+
*
|
|
4
|
+
* Phase 2 of v2/MANAGEMENT_AS_NETAPP.md: the celilo-mgmt module's
|
|
5
|
+
* on_install hook needs a CLI surface it can shell out to that writes
|
|
6
|
+
* the operator-chosen network/DNS/SSH config without the interactive
|
|
7
|
+
* framing of `celilo system init`. This is that command.
|
|
8
|
+
*
|
|
9
|
+
* Shape:
|
|
10
|
+
*
|
|
11
|
+
* celilo system apply-config <key=value> ...
|
|
12
|
+
* celilo system apply-config --from-stdin # JSON map on stdin
|
|
13
|
+
*
|
|
14
|
+
* No prompts. No "next steps" guidance. Just writes the keys and
|
|
15
|
+
* reports a count. Suitable for module hooks, CI workflows, and any
|
|
16
|
+
* other automation that needs to seed systemConfig at deploy time.
|
|
17
|
+
*
|
|
18
|
+
* `celilo system init --accept-defaults` continues to work and now
|
|
19
|
+
* delegates to this same `initializeSystem()` plumbing — that's the
|
|
20
|
+
* operator-facing surface; this is the automation-facing surface.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { getDb } from '../../db/client';
|
|
24
|
+
import { initializeSystem } from '../../services/system-init';
|
|
25
|
+
import type { CommandResult } from '../types';
|
|
26
|
+
|
|
27
|
+
interface ParsedInput {
|
|
28
|
+
overrides: Record<string, string>;
|
|
29
|
+
errors: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Pure parser for the positional key=value arguments. Exported so the
|
|
34
|
+
* test suite can exercise it without spinning up the CLI.
|
|
35
|
+
*
|
|
36
|
+
* - Keys must contain a literal `=`; bare positionals are errors.
|
|
37
|
+
* - Empty values are allowed (`network.dmz.subnet=`) — caller decides
|
|
38
|
+
* what to do with them (the writer skips them).
|
|
39
|
+
* - Values containing `=` are preserved (split on FIRST `=` only).
|
|
40
|
+
*/
|
|
41
|
+
export function parseKeyValueArgs(args: string[]): ParsedInput {
|
|
42
|
+
const overrides: Record<string, string> = {};
|
|
43
|
+
const errors: string[] = [];
|
|
44
|
+
for (const arg of args) {
|
|
45
|
+
const eqIndex = arg.indexOf('=');
|
|
46
|
+
if (eqIndex <= 0) {
|
|
47
|
+
errors.push(`Expected key=value (got "${arg}"). Use --from-stdin for JSON input.`);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const key = arg.slice(0, eqIndex);
|
|
51
|
+
const value = arg.slice(eqIndex + 1);
|
|
52
|
+
overrides[key] = value;
|
|
53
|
+
}
|
|
54
|
+
return { overrides, errors };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Read JSON config from stdin. Returns the parsed key→value map; the
|
|
59
|
+
* caller validates structure. Values are coerced to strings to match
|
|
60
|
+
* systemConfig's schema (which stores everything as TEXT).
|
|
61
|
+
*/
|
|
62
|
+
async function readJsonStdin(): Promise<Record<string, string>> {
|
|
63
|
+
const chunks: Buffer[] = [];
|
|
64
|
+
for await (const chunk of process.stdin) {
|
|
65
|
+
chunks.push(chunk as Buffer);
|
|
66
|
+
}
|
|
67
|
+
const body = Buffer.concat(chunks).toString('utf-8').trim();
|
|
68
|
+
if (!body) return {};
|
|
69
|
+
const parsed = JSON.parse(body) as unknown;
|
|
70
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
71
|
+
throw new Error('--from-stdin expects a JSON object mapping config keys to values');
|
|
72
|
+
}
|
|
73
|
+
const out: Record<string, string> = {};
|
|
74
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
75
|
+
if (value === null || value === undefined) continue;
|
|
76
|
+
out[key] = String(value);
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function handleSystemApplyConfig(
|
|
82
|
+
args: string[],
|
|
83
|
+
flags: Record<string, boolean | string> = {},
|
|
84
|
+
): Promise<CommandResult> {
|
|
85
|
+
const fromStdin = flags['from-stdin'] === true;
|
|
86
|
+
|
|
87
|
+
let overrides: Record<string, string> = {};
|
|
88
|
+
if (fromStdin) {
|
|
89
|
+
try {
|
|
90
|
+
overrides = await readJsonStdin();
|
|
91
|
+
} catch (err) {
|
|
92
|
+
return {
|
|
93
|
+
success: false,
|
|
94
|
+
error: `Could not read --from-stdin: ${err instanceof Error ? err.message : String(err)}`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
const parsed = parseKeyValueArgs(args);
|
|
99
|
+
if (parsed.errors.length > 0) {
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
error: parsed.errors.join('\n'),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
overrides = parsed.overrides;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (Object.keys(overrides).length === 0) {
|
|
109
|
+
return {
|
|
110
|
+
success: false,
|
|
111
|
+
error:
|
|
112
|
+
'No config values supplied.\n\nUsage:\n celilo system apply-config <key=value> ...\n celilo system apply-config --from-stdin',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const db = getDb();
|
|
117
|
+
try {
|
|
118
|
+
const applied = initializeSystem(db, overrides);
|
|
119
|
+
const writtenCount = Object.keys(applied).length;
|
|
120
|
+
return {
|
|
121
|
+
success: true,
|
|
122
|
+
message: `Applied ${writtenCount} config value(s) to systemConfig.`,
|
|
123
|
+
};
|
|
124
|
+
} catch (err) {
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
error: `Config write failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -52,6 +52,7 @@ import type { TerraformPlanRunner } from '../../services/audit/terraform-plan';
|
|
|
52
52
|
import { getServiceCredentials, listContainerServices } from '../../services/container-service';
|
|
53
53
|
import { runAllHealthChecks } from '../../services/health-runner';
|
|
54
54
|
import { listMachines } from '../../services/machine-pool';
|
|
55
|
+
import { parseStoredConfigValue } from '../../services/module-config';
|
|
55
56
|
import { buildTerraformEnvForModule } from '../../services/terraform-env';
|
|
56
57
|
import { hasFlag } from '../parser';
|
|
57
58
|
import type { CommandResult } from '../types';
|
|
@@ -183,7 +184,7 @@ async function buildAuditDeps(onProgress?: (msg: string) => void) {
|
|
|
183
184
|
const configsByModule = new Map<string, Record<string, unknown>>();
|
|
184
185
|
for (const c of allConfigs) {
|
|
185
186
|
const map = configsByModule.get(c.moduleId) ?? {};
|
|
186
|
-
map[c.key] =
|
|
187
|
+
map[c.key] = parseStoredConfigValue(c);
|
|
187
188
|
configsByModule.set(c.moduleId, map);
|
|
188
189
|
}
|
|
189
190
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 5 of v2/MANAGEMENT_AS_NETAPP.md — confirm `celilo system init`
|
|
3
|
+
* surfaces a deprecation banner pointing at the new paths
|
|
4
|
+
* (bootstrap.sh + `system apply-config`), and that
|
|
5
|
+
* CELILO_SUPPRESS_DEPRECATION=1 silences it.
|
|
6
|
+
*
|
|
7
|
+
* The banner is operator-facing UX; we verify the contract rather than
|
|
8
|
+
* pin specific wording.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
|
12
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
13
|
+
import { tmpdir } from 'node:os';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { handleSystemInit } from './system-init';
|
|
16
|
+
|
|
17
|
+
interface CapturedStream {
|
|
18
|
+
out: string[];
|
|
19
|
+
restore: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function captureStderr(): CapturedStream {
|
|
23
|
+
const original = console.warn;
|
|
24
|
+
const captured: string[] = [];
|
|
25
|
+
console.warn = (...args: unknown[]) => {
|
|
26
|
+
captured.push(args.map(String).join(' '));
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
out: captured,
|
|
30
|
+
restore: () => {
|
|
31
|
+
console.warn = original;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe('celilo system init deprecation banner', () => {
|
|
37
|
+
let tmpDir: string;
|
|
38
|
+
let savedDbPath: string | undefined;
|
|
39
|
+
let savedSuppress: string | undefined;
|
|
40
|
+
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
tmpDir = mkdtempSync(join(tmpdir(), 'celilo-deprecation-test-'));
|
|
43
|
+
savedDbPath = process.env.CELILO_DB_PATH;
|
|
44
|
+
savedSuppress = process.env.CELILO_SUPPRESS_DEPRECATION;
|
|
45
|
+
process.env.CELILO_DB_PATH = join(tmpDir, 'init.db');
|
|
46
|
+
process.env.CELILO_SUPPRESS_DEPRECATION = undefined;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
process.env.CELILO_DB_PATH = savedDbPath;
|
|
51
|
+
process.env.CELILO_SUPPRESS_DEPRECATION = savedSuppress;
|
|
52
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('prints a deprecation banner on stderr by default', async () => {
|
|
56
|
+
const captured = captureStderr();
|
|
57
|
+
try {
|
|
58
|
+
await handleSystemInit([], { 'accept-defaults': true });
|
|
59
|
+
} finally {
|
|
60
|
+
captured.restore();
|
|
61
|
+
}
|
|
62
|
+
const all = captured.out.join('\n');
|
|
63
|
+
expect(all).toContain('deprecated');
|
|
64
|
+
expect(all).toContain('bootstrap.sh');
|
|
65
|
+
expect(all).toContain('apply-config');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('CELILO_SUPPRESS_DEPRECATION=1 silences the banner', async () => {
|
|
69
|
+
process.env.CELILO_SUPPRESS_DEPRECATION = '1';
|
|
70
|
+
const captured = captureStderr();
|
|
71
|
+
try {
|
|
72
|
+
await handleSystemInit([], { 'accept-defaults': true });
|
|
73
|
+
} finally {
|
|
74
|
+
captured.restore();
|
|
75
|
+
}
|
|
76
|
+
const all = captured.out.join('\n');
|
|
77
|
+
expect(all).not.toContain('deprecated');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('the command still executes successfully when the banner fires', async () => {
|
|
81
|
+
const captured = captureStderr();
|
|
82
|
+
let result: Awaited<ReturnType<typeof handleSystemInit>>;
|
|
83
|
+
try {
|
|
84
|
+
result = await handleSystemInit([], { 'accept-defaults': true });
|
|
85
|
+
} finally {
|
|
86
|
+
captured.restore();
|
|
87
|
+
}
|
|
88
|
+
expect(result.success).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -16,12 +16,7 @@ import {
|
|
|
16
16
|
} from '../../services/system-init';
|
|
17
17
|
import { celiloIntro, celiloOutro, promptConfirm, promptText } from '../prompts';
|
|
18
18
|
import type { CommandResult } from '../types';
|
|
19
|
-
import {
|
|
20
|
-
validateIpAddress,
|
|
21
|
-
validateIpAddressList,
|
|
22
|
-
validateRequired,
|
|
23
|
-
validateSubnet,
|
|
24
|
-
} from '../validators';
|
|
19
|
+
import { validateRequired } from '../validators';
|
|
25
20
|
|
|
26
21
|
/**
|
|
27
22
|
* Parse key=value pairs from positional arguments
|
|
@@ -51,6 +46,28 @@ export async function handleSystemInit(
|
|
|
51
46
|
const cliOverrides = parseOverrides(args);
|
|
52
47
|
const db = getDb();
|
|
53
48
|
|
|
49
|
+
// Phase 5 of v2/MANAGEMENT_AS_NETAPP.md — surface that this command
|
|
50
|
+
// is on its way out so operators have time to migrate to the new
|
|
51
|
+
// paths. Keeps working unchanged; the banner just points at the
|
|
52
|
+
// replacements. CELILO_SUPPRESS_DEPRECATION=1 silences for callers
|
|
53
|
+
// that already know (e.g. the celilo-mgmt module's on_install hook
|
|
54
|
+
// — wait, that one shells to apply-config now, but other tooling
|
|
55
|
+
// that legitimately wants the interactive interview can still opt
|
|
56
|
+
// out of the banner).
|
|
57
|
+
if (process.env.CELILO_SUPPRESS_DEPRECATION !== '1') {
|
|
58
|
+
console.warn('⚠ celilo system init is deprecated.');
|
|
59
|
+
console.warn(' Recommended paths:');
|
|
60
|
+
console.warn(
|
|
61
|
+
' • Fresh management server: curl -fsSL https://celilo.computer/bootstrap.sh | bash',
|
|
62
|
+
);
|
|
63
|
+
console.warn(
|
|
64
|
+
' • Headless config write (CI / hooks): celilo system apply-config key=value ...',
|
|
65
|
+
);
|
|
66
|
+
console.warn(
|
|
67
|
+
' This command still works; suppress this banner with CELILO_SUPPRESS_DEPRECATION=1.\n',
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
54
71
|
// Check if already initialized
|
|
55
72
|
const alreadyInitialized = isSystemInitialized(db);
|
|
56
73
|
if (alreadyInitialized && !acceptDefaults && Object.keys(cliOverrides).length === 0) {
|
|
@@ -61,7 +78,7 @@ export async function handleSystemInit(
|
|
|
61
78
|
|
|
62
79
|
try {
|
|
63
80
|
if (acceptDefaults) {
|
|
64
|
-
return
|
|
81
|
+
return initWithDefaults(cliOverrides);
|
|
65
82
|
}
|
|
66
83
|
return await initInteractive(cliOverrides);
|
|
67
84
|
} catch (error) {
|
|
@@ -91,8 +108,10 @@ function initWithDefaults(overrides: Record<string, string> = {}): CommandResult
|
|
|
91
108
|
|
|
92
109
|
const sshKeyDetected = config['ssh.public_key'] !== undefined;
|
|
93
110
|
|
|
94
|
-
console.log('✓
|
|
95
|
-
console.log('
|
|
111
|
+
console.log('✓ Celilo state initialized');
|
|
112
|
+
console.log(' Network addressing is NOT defaulted: the `internal` zone is');
|
|
113
|
+
console.log(' discovered when you deploy celilo-mgmt, and dmz/app/secure');
|
|
114
|
+
console.log(' appear when you deploy a firewall module.');
|
|
96
115
|
|
|
97
116
|
if (sshKeyDetected) {
|
|
98
117
|
console.log('✓ SSH key (auto-detected)');
|
|
@@ -146,65 +165,12 @@ async function initInteractive(cliOverrides: Record<string, string> = {}): Promi
|
|
|
146
165
|
|
|
147
166
|
await celiloIntro('🎛️ Welcome to Celilo System Setup');
|
|
148
167
|
|
|
149
|
-
// Network
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
validate: validateSubnet,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (!has('network.app.subnet')) {
|
|
160
|
-
overrides['network.app.subnet'] = await promptText({
|
|
161
|
-
message: 'App network (internal apps)',
|
|
162
|
-
defaultValue: String(defaults['network.app.subnet']),
|
|
163
|
-
placeholder: String(defaults['network.app.subnet']) || '10.0.20.0/24',
|
|
164
|
-
validate: validateSubnet,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (!has('network.secure.subnet')) {
|
|
169
|
-
overrides['network.secure.subnet'] = await promptText({
|
|
170
|
-
message: 'Secure network (databases, auth)',
|
|
171
|
-
defaultValue: String(defaults['network.secure.subnet']),
|
|
172
|
-
placeholder: String(defaults['network.secure.subnet']) || '10.0.30.0/24',
|
|
173
|
-
validate: validateSubnet,
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (!has('network.internal.subnet')) {
|
|
178
|
-
overrides['network.internal.subnet'] = await promptText({
|
|
179
|
-
message: 'Internal network (home devices)',
|
|
180
|
-
defaultValue: String(defaults['network.internal.subnet']),
|
|
181
|
-
placeholder: String(defaults['network.internal.subnet']) || '192.168.1.0/24',
|
|
182
|
-
validate: validateSubnet,
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// System Settings
|
|
187
|
-
// primary_domain and admin.email are no longer system config — modules
|
|
188
|
-
// (dns_registrar, authentik, caddy) own those values directly. See
|
|
189
|
-
// design/TECHNICAL_DESIGN_MANIFEST_V2.md D9.
|
|
190
|
-
|
|
191
|
-
if (!has('dns.primary')) {
|
|
192
|
-
overrides['dns.primary'] = await promptText({
|
|
193
|
-
message: 'Primary DNS server',
|
|
194
|
-
defaultValue: String(defaults['dns.primary']),
|
|
195
|
-
placeholder: String(defaults['dns.primary']) || '1.1.1.1',
|
|
196
|
-
validate: validateIpAddress,
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (!has('dns.fallback')) {
|
|
201
|
-
overrides['dns.fallback'] = await promptText({
|
|
202
|
-
message: 'Fallback DNS servers (space-separated)',
|
|
203
|
-
defaultValue: String(defaults['dns.fallback']),
|
|
204
|
-
placeholder: String(defaults['dns.fallback']) || '8.8.8.8 1.1.1.1',
|
|
205
|
-
validate: validateIpAddressList,
|
|
206
|
-
});
|
|
207
|
-
}
|
|
168
|
+
// Network and DNS addressing are intentionally NOT prompted here.
|
|
169
|
+
// Network topology is no longer owned by `system init`
|
|
170
|
+
// (v2/NETWORK_CONFIG_TO_FIREWALL.md): the `internal` zone and DNS are
|
|
171
|
+
// discovered when celilo-mgmt is deployed, and `dmz`/`app`/`secure`
|
|
172
|
+
// come from a firewall module. The only thing left to capture
|
|
173
|
+
// interactively is the SSH key celilo uses to reach managed machines.
|
|
208
174
|
|
|
209
175
|
// SSH Key — skip if provided via CLI
|
|
210
176
|
if (!has('ssh.public_key')) {
|
|
@@ -311,8 +277,8 @@ async function initInteractive(cliOverrides: Record<string, string> = {}): Promi
|
|
|
311
277
|
}
|
|
312
278
|
}
|
|
313
279
|
|
|
314
|
-
// Apply configuration
|
|
315
|
-
|
|
280
|
+
// Apply configuration (called for its DB side effects).
|
|
281
|
+
initializeSystem(db, overrides);
|
|
316
282
|
|
|
317
283
|
await celiloOutro('✅ System initialization complete!');
|
|
318
284
|
|
|
@@ -30,6 +30,7 @@ import { fetchLatestCliVersion } from '../../services/audit/cli-version';
|
|
|
30
30
|
import { makeJournalReader, readAppliedMigrations } from '../../services/audit/schema';
|
|
31
31
|
import { createModuleBackup, createSystemStateBackup } from '../../services/backup-create';
|
|
32
32
|
import { runAllHealthChecks, runModuleHealthCheck } from '../../services/health-runner';
|
|
33
|
+
import { parseStoredConfigValue } from '../../services/module-config';
|
|
33
34
|
import { deployModule } from '../../services/module-deploy';
|
|
34
35
|
import { buildModuleGraph } from '../../services/update/dep-graph';
|
|
35
36
|
import {
|
|
@@ -511,7 +512,7 @@ export async function handleSystemUpdate(
|
|
|
511
512
|
const configsByModule = new Map<string, Record<string, unknown>>();
|
|
512
513
|
for (const c of allConfigs) {
|
|
513
514
|
const m = configsByModule.get(c.moduleId) ?? {};
|
|
514
|
-
m[c.key] =
|
|
515
|
+
m[c.key] = parseStoredConfigValue(c);
|
|
515
516
|
configsByModule.set(c.moduleId, m);
|
|
516
517
|
}
|
|
517
518
|
|
|
@@ -741,7 +742,7 @@ export function rebuildAuditDepsForRerun(
|
|
|
741
742
|
const configsByModule = new Map<string, Record<string, unknown>>();
|
|
742
743
|
for (const c of allConfigs) {
|
|
743
744
|
const m = configsByModule.get(c.moduleId) ?? {};
|
|
744
|
-
m[c.key] =
|
|
745
|
+
m[c.key] = parseStoredConfigValue(c);
|
|
745
746
|
configsByModule.set(c.moduleId, m);
|
|
746
747
|
}
|
|
747
748
|
|
package/src/cli/completion.ts
CHANGED
|
@@ -39,9 +39,12 @@ export async function getCompletions(words: string[], current: number): Promise<
|
|
|
39
39
|
'machine',
|
|
40
40
|
'module',
|
|
41
41
|
'package',
|
|
42
|
+
'publish',
|
|
43
|
+
'restore',
|
|
42
44
|
'service',
|
|
43
45
|
'status',
|
|
44
46
|
'storage',
|
|
47
|
+
'subscribers',
|
|
45
48
|
'system',
|
|
46
49
|
'version',
|
|
47
50
|
];
|
|
@@ -76,6 +79,7 @@ export async function getCompletions(words: string[], current: number): Promise<
|
|
|
76
79
|
'list-pending',
|
|
77
80
|
'drain',
|
|
78
81
|
'run',
|
|
82
|
+
'run-hook',
|
|
79
83
|
'emit',
|
|
80
84
|
'ack',
|
|
81
85
|
'fail',
|
|
@@ -130,6 +134,7 @@ export async function getCompletions(words: string[], current: number): Promise<
|
|
|
130
134
|
'update',
|
|
131
135
|
'verify',
|
|
132
136
|
'audit',
|
|
137
|
+
'backup',
|
|
133
138
|
'config',
|
|
134
139
|
'show-config',
|
|
135
140
|
'show-zone',
|
|
@@ -333,6 +338,7 @@ export async function getCompletions(words: string[], current: number): Promise<
|
|
|
333
338
|
'logs',
|
|
334
339
|
'remove',
|
|
335
340
|
'build',
|
|
341
|
+
'backup',
|
|
336
342
|
'run-hook',
|
|
337
343
|
'status',
|
|
338
344
|
'terraform-unlock',
|
|
@@ -362,6 +368,12 @@ export async function getCompletions(words: string[], current: number): Promise<
|
|
|
362
368
|
}
|
|
363
369
|
}
|
|
364
370
|
|
|
371
|
+
// Subscribers subcommands
|
|
372
|
+
if (command === 'subscribers' && currentIndex === 1) {
|
|
373
|
+
const subcommands = ['list', 'add', 'remove', 'test', 'serve', 'status'];
|
|
374
|
+
return filterSuggestions(subcommands, args[1] || '');
|
|
375
|
+
}
|
|
376
|
+
|
|
365
377
|
// Storage subcommands
|
|
366
378
|
if (command === 'storage' && currentIndex === 1) {
|
|
367
379
|
const subcommands = ['add', 'list', 'remove', 'verify', 'set-default'];
|
|
@@ -431,7 +443,16 @@ export async function getCompletions(words: string[], current: number): Promise<
|
|
|
431
443
|
|
|
432
444
|
// System subcommands
|
|
433
445
|
if (command === 'system' && currentIndex === 1) {
|
|
434
|
-
const subcommands = [
|
|
446
|
+
const subcommands = [
|
|
447
|
+
'init',
|
|
448
|
+
'apply-config',
|
|
449
|
+
'config',
|
|
450
|
+
'secret',
|
|
451
|
+
'vault-password',
|
|
452
|
+
'audit',
|
|
453
|
+
'update',
|
|
454
|
+
'doctor',
|
|
455
|
+
];
|
|
435
456
|
return filterSuggestions(subcommands, args[1] || '');
|
|
436
457
|
}
|
|
437
458
|
|