@celilo/cli 0.1.5 → 0.1.6
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/0004_caddy_hostname_list.sql +25 -0
- package/drizzle/meta/_journal.json +14 -0
- package/package.json +9 -2
- package/src/ansible/inventory.test.ts +3 -2
- package/src/ansible/inventory.ts +5 -1
- package/src/capabilities/public-web-helpers.test.ts +2 -2
- package/src/capabilities/public-web-publish.test.ts +34 -1
- package/src/cli/cli.test.ts +2 -2
- package/src/cli/command-registry.ts +146 -3
- package/src/cli/command-tree-parser.test.ts +1 -1
- package/src/cli/command-tree-parser.ts +9 -8
- package/src/cli/commands/hook-run.ts +15 -66
- package/src/cli/commands/module-audit.ts +14 -44
- package/src/cli/commands/module-deploy.ts +4 -1
- package/src/cli/commands/module-import-registry.test.ts +115 -0
- package/src/cli/commands/module-import.ts +106 -22
- package/src/cli/commands/module-publish.test.ts +235 -0
- package/src/cli/commands/module-publish.ts +234 -0
- package/src/cli/commands/module-remove.ts +82 -2
- package/src/cli/commands/module-search.ts +57 -0
- package/src/cli/commands/module-secret-get.ts +59 -0
- package/src/cli/commands/module-terraform-unlock.ts +57 -0
- package/src/cli/commands/module-verify.test.ts +59 -0
- package/src/cli/commands/module-verify.ts +53 -0
- package/src/cli/commands/status.ts +30 -20
- package/src/cli/commands/system-audit.test.ts +138 -0
- package/src/cli/commands/system-audit.ts +571 -0
- package/src/cli/commands/system-update.ts +391 -0
- package/src/cli/completion.ts +15 -1
- package/src/cli/fuel-gauge.ts +68 -3
- package/src/cli/generate-zsh-completion.ts +13 -3
- package/src/cli/index.ts +112 -5
- package/src/cli/parser.ts +11 -0
- package/src/cli/prompts.ts +36 -5
- package/src/cli/tui/audit-state.test.ts +246 -0
- package/src/cli/tui/audit-state.ts +525 -0
- package/src/cli/tui/audit-tui.test.tsx +135 -0
- package/src/cli/tui/audit-tui.tsx +624 -0
- package/src/cli/tui/celebration.tsx +29 -0
- package/src/cli/tui/clipboard.test.ts +94 -0
- package/src/cli/tui/clipboard.ts +101 -0
- package/src/cli/tui/icons.ts +22 -0
- package/src/cli/tui/keybar.tsx +65 -0
- package/src/cli/tui/keymap.test.ts +105 -0
- package/src/cli/tui/keymap.ts +70 -0
- package/src/cli/tui/modals/analyzing.tsx +75 -0
- package/src/cli/tui/modals/celebration.tsx +44 -0
- package/src/cli/tui/modals/reaudit-prompt.tsx +35 -0
- package/src/cli/tui/modals/remediate.tsx +44 -0
- package/src/cli/tui/modals.test.ts +137 -0
- package/src/cli/tui/mouse.test.ts +78 -0
- package/src/cli/tui/mouse.ts +114 -0
- package/src/cli/tui/panes/categories.tsx +62 -0
- package/src/cli/tui/panes/command-log.tsx +87 -0
- package/src/cli/tui/panes/detail.tsx +175 -0
- package/src/cli/tui/panes/findings.tsx +97 -0
- package/src/cli/tui/panes/summary.tsx +64 -0
- package/src/cli/tui/spawn.ts +130 -0
- package/src/cli/tui/theme.ts +42 -0
- package/src/cli/tui/wrap.test.ts +43 -0
- package/src/cli/tui/wrap.ts +45 -0
- package/src/cli/types.ts +5 -0
- package/src/db/client.ts +55 -2
- package/src/db/schema.ts +26 -17
- package/src/hooks/capability-loader.ts +133 -73
- package/src/hooks/define-hook.test.ts +9 -1
- package/src/hooks/executor.ts +22 -1
- package/src/hooks/load-hook-config.test.ts +165 -0
- package/src/hooks/load-hook-config.ts +60 -0
- package/src/hooks/logger.ts +42 -12
- package/src/hooks/run-named-hook.ts +128 -0
- package/src/hooks/types.ts +19 -0
- package/src/manifest/ensure-schema.test.ts +115 -0
- package/src/manifest/schema.ts +76 -0
- package/src/module/import.ts +20 -12
- package/src/module/packaging/build.ts +85 -16
- package/src/module/packaging/release-metadata.test.ts +103 -0
- package/src/module/packaging/release-metadata.ts +145 -0
- package/src/registry/client.test.ts +228 -0
- package/src/registry/client.ts +157 -0
- package/src/services/audit/backups.test.ts +233 -0
- package/src/services/audit/backups.ts +128 -0
- package/src/services/audit/capability-abi.test.ts +153 -0
- package/src/services/audit/capability-abi.ts +204 -0
- package/src/services/audit/cli-version.test.ts +60 -0
- package/src/services/audit/cli-version.ts +87 -0
- package/src/services/audit/health.test.ts +84 -0
- package/src/services/audit/health.ts +43 -0
- package/src/services/audit/index.test.ts +99 -0
- package/src/services/audit/index.ts +118 -0
- package/src/services/audit/machines-reachable.test.ts +87 -0
- package/src/services/audit/machines-reachable.ts +87 -0
- package/src/services/audit/module-configs.test.ts +131 -0
- package/src/services/audit/module-configs.ts +80 -0
- package/src/services/audit/module-versions.test.ts +99 -0
- package/src/services/audit/module-versions.ts +154 -0
- package/src/services/audit/schema.test.ts +68 -0
- package/src/services/audit/schema.ts +115 -0
- package/src/services/audit/secrets-decryptable.test.ts +82 -0
- package/src/services/audit/secrets-decryptable.ts +97 -0
- package/src/services/audit/services-credentials.test.ts +54 -0
- package/src/services/audit/services-credentials.ts +64 -0
- package/src/services/audit/services-reachable.test.ts +60 -0
- package/src/services/audit/services-reachable.ts +64 -0
- package/src/services/audit/terraform-plan.test.ts +127 -0
- package/src/services/audit/terraform-plan.ts +153 -0
- package/src/services/audit/types.test.ts +36 -0
- package/src/services/audit/types.ts +90 -0
- package/src/services/audit/unconfigured-modules.test.ts +48 -0
- package/src/services/audit/unconfigured-modules.ts +71 -0
- package/src/services/audit/undeployed-modules.test.ts +66 -0
- package/src/services/audit/undeployed-modules.ts +72 -0
- package/src/services/build-stream.ts +122 -122
- package/src/services/config-interview.ts +407 -2
- package/src/services/deploy-ansible.ts +73 -7
- package/src/services/deploy-preflight.ts +45 -4
- package/src/services/deploy-terraform.ts +31 -24
- package/src/services/deploy-validation.ts +167 -23
- package/src/services/ensure-interview.test.ts +245 -0
- package/src/services/health-runner.ts +110 -38
- package/src/services/module-build.ts +11 -13
- package/src/services/module-deploy.ts +370 -59
- package/src/services/ssh-key-manager.test.ts +1 -1
- package/src/services/ssh-key-manager.ts +3 -2
- package/src/services/terraform-env.ts +62 -0
- package/src/services/update/dep-graph.test.ts +214 -0
- package/src/services/update/dep-graph.ts +215 -0
- package/src/services/update/orchestrator.test.ts +463 -0
- package/src/services/update/orchestrator.ts +359 -0
- package/src/services/update/progress.ts +49 -0
- package/src/services/update/self-update.test.ts +68 -0
- package/src/services/update/self-update.ts +57 -0
- package/src/services/update/types.ts +94 -0
- package/src/templates/generator.test.ts +1 -1
- package/src/templates/generator.ts +42 -1
- package/src/test-utils/completion-harness.test.ts +1 -1
- package/src/test-utils/completion-harness.ts +4 -4
- package/src/variables/capability-self-ref.test.ts +203 -0
- package/src/variables/context.ts +49 -1
- package/src/variables/declarative-derivation.test.ts +306 -0
- package/src/variables/declarative-derivation.ts +4 -2
- package/src/variables/parser.test.ts +56 -1
- package/src/variables/parser.ts +47 -6
- package/src/variables/resolver.ts +27 -9
- package/tsconfig.json +1 -0
|
@@ -13,40 +13,40 @@ import type { ModuleManifest } from '../../manifest/schema';
|
|
|
13
13
|
import type { CommandResult } from '../types';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* Determine module status
|
|
16
|
+
* Determine module status for display.
|
|
17
|
+
*
|
|
18
|
+
* Uses the DB state as the primary source of truth.
|
|
19
|
+
* Falls back to filesystem / config inspection for IMPORTED/CONFIGURED modules
|
|
20
|
+
* that haven't yet been deployed.
|
|
17
21
|
*/
|
|
18
22
|
async function determineModuleStatus(
|
|
19
23
|
_moduleId: string,
|
|
20
24
|
manifest: ModuleManifest,
|
|
21
25
|
configs: (typeof moduleConfigs.$inferSelect)[],
|
|
22
26
|
generatedPath: string,
|
|
27
|
+
dbState: string,
|
|
23
28
|
): Promise<{
|
|
24
|
-
status: 'IMPORTED' | 'CONFIGURED' | 'GENERATED' | 'DEPLOYED' | 'NEEDS_UPDATE';
|
|
29
|
+
status: 'IMPORTED' | 'CONFIGURED' | 'GENERATED' | 'DEPLOYED' | 'VERIFIED' | 'NEEDS_UPDATE';
|
|
25
30
|
missingCount?: number;
|
|
26
31
|
}> {
|
|
27
|
-
//
|
|
32
|
+
// Deployed states come directly from the DB — don't infer from filesystem
|
|
33
|
+
if (dbState === 'VERIFIED') return { status: 'VERIFIED' };
|
|
34
|
+
if (dbState === 'INSTALLED') return { status: 'DEPLOYED' };
|
|
35
|
+
|
|
36
|
+
// For pre-deploy states, derive from filesystem / config
|
|
28
37
|
if (existsSync(generatedPath)) {
|
|
29
|
-
// TODO: Add DEPLOYED and NEEDS_UPDATE detection
|
|
30
|
-
// For now, if generated exists, it's GENERATED
|
|
31
38
|
return { status: 'GENERATED' };
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
// Check if all required variables are set
|
|
35
41
|
const requiredVars = manifest.variables?.owns?.filter((v) => v.required) || [];
|
|
36
42
|
const configMap = new Map(configs.map((c) => [c.key, true]));
|
|
37
|
-
|
|
38
43
|
const missingVars = requiredVars.filter((v) => !configMap.has(v.name));
|
|
39
44
|
|
|
40
|
-
if (missingVars.length === 0 && requiredVars.length > 0) {
|
|
41
|
-
return { status: 'CONFIGURED' };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
45
|
if (missingVars.length > 0) {
|
|
45
46
|
return { status: 'IMPORTED', missingCount: missingVars.length };
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
if (configs.length > 0) {
|
|
49
|
+
if (requiredVars.length > 0 || configs.length > 0) {
|
|
50
50
|
return { status: 'CONFIGURED' };
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -116,10 +116,16 @@ export async function handleStatus(): Promise<CommandResult> {
|
|
|
116
116
|
manifest,
|
|
117
117
|
moduleConfigsList,
|
|
118
118
|
generatedPath,
|
|
119
|
+
module.state,
|
|
119
120
|
);
|
|
120
121
|
|
|
121
122
|
// Status icon
|
|
122
|
-
const icon =
|
|
123
|
+
const icon =
|
|
124
|
+
statusInfo.status === 'VERIFIED' ||
|
|
125
|
+
statusInfo.status === 'DEPLOYED' ||
|
|
126
|
+
statusInfo.status === 'GENERATED'
|
|
127
|
+
? '✓'
|
|
128
|
+
: '⚠';
|
|
123
129
|
|
|
124
130
|
lines.push(` ${icon} ${module.id} (v${module.version})`);
|
|
125
131
|
lines.push(` Status: ${statusInfo.status}`);
|
|
@@ -169,7 +175,11 @@ export async function handleStatus(): Promise<CommandResult> {
|
|
|
169
175
|
}
|
|
170
176
|
|
|
171
177
|
// Show when last generated (if applicable)
|
|
172
|
-
if (
|
|
178
|
+
if (
|
|
179
|
+
statusInfo.status === 'GENERATED' ||
|
|
180
|
+
statusInfo.status === 'DEPLOYED' ||
|
|
181
|
+
statusInfo.status === 'VERIFIED'
|
|
182
|
+
) {
|
|
173
183
|
try {
|
|
174
184
|
const stats = await stat(generatedPath);
|
|
175
185
|
const age = Date.now() - stats.mtimeMs;
|
|
@@ -197,11 +207,11 @@ export async function handleStatus(): Promise<CommandResult> {
|
|
|
197
207
|
|
|
198
208
|
// Legend
|
|
199
209
|
lines.push('Legend:');
|
|
200
|
-
lines.push(' IMPORTED
|
|
201
|
-
lines.push(' CONFIGURED
|
|
202
|
-
lines.push(' GENERATED
|
|
203
|
-
lines.push(' DEPLOYED
|
|
204
|
-
lines.push('
|
|
210
|
+
lines.push(' IMPORTED - Module imported, not configured');
|
|
211
|
+
lines.push(' CONFIGURED - Configuration complete, not generated');
|
|
212
|
+
lines.push(' GENERATED - Infrastructure code generated, not deployed');
|
|
213
|
+
lines.push(' DEPLOYED - Deployed (Ansible complete, health check pending)');
|
|
214
|
+
lines.push(' VERIFIED - Deployed and health checks passed');
|
|
205
215
|
|
|
206
216
|
return {
|
|
207
217
|
success: true,
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import type { SystemAuditReport } from '../../services/audit';
|
|
3
|
+
import { formatReport } from './system-audit';
|
|
4
|
+
|
|
5
|
+
const baseReport = (overrides: Partial<SystemAuditReport> = {}): SystemAuditReport => ({
|
|
6
|
+
version: 1,
|
|
7
|
+
verdict: 'READY',
|
|
8
|
+
generatedAt: '2026-04-25T00:00:00.000Z',
|
|
9
|
+
findings: [],
|
|
10
|
+
...overrides,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('formatReport', () => {
|
|
14
|
+
test('READY report calls out no drift', () => {
|
|
15
|
+
const out = formatReport(baseReport());
|
|
16
|
+
expect(out).toContain('READY');
|
|
17
|
+
expect(out).toContain('No drift detected');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('DRIFT report lists each finding with remediation', () => {
|
|
21
|
+
const out = formatReport(
|
|
22
|
+
baseReport({
|
|
23
|
+
verdict: 'DRIFT',
|
|
24
|
+
findings: [
|
|
25
|
+
{
|
|
26
|
+
category: 'cli_version',
|
|
27
|
+
severity: 'drift',
|
|
28
|
+
code: 'cli_version_drift',
|
|
29
|
+
message: '@celilo/cli 0.1.5 → 0.1.7 available',
|
|
30
|
+
remediation: '`celilo system update` will self-update first',
|
|
31
|
+
subject: 'system',
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
expect(out).toContain('DRIFT (1 finding)');
|
|
38
|
+
expect(out).toContain('@celilo/cli 0.1.5 → 0.1.7');
|
|
39
|
+
expect(out).toContain('→ `celilo system update`');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('BLOCKED report puts blocked findings first', () => {
|
|
43
|
+
const out = formatReport(
|
|
44
|
+
baseReport({
|
|
45
|
+
verdict: 'BLOCKED',
|
|
46
|
+
findings: [
|
|
47
|
+
{
|
|
48
|
+
category: 'cli_version',
|
|
49
|
+
severity: 'drift',
|
|
50
|
+
code: 'cli_version_drift',
|
|
51
|
+
message: 'cli is old',
|
|
52
|
+
subject: 'system',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
category: 'schema',
|
|
56
|
+
severity: 'blocked',
|
|
57
|
+
code: 'schema_pending_migrations',
|
|
58
|
+
message: '2 pending DB migrations',
|
|
59
|
+
subject: 'system',
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const blockedIdx = out.indexOf('BLOCKED • schema');
|
|
66
|
+
const driftIdx = out.indexOf('DRIFT • cli_version');
|
|
67
|
+
expect(blockedIdx).toBeGreaterThanOrEqual(0);
|
|
68
|
+
expect(driftIdx).toBeGreaterThan(blockedIdx);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('groups findings by category', () => {
|
|
72
|
+
const out = formatReport(
|
|
73
|
+
baseReport({
|
|
74
|
+
verdict: 'DRIFT',
|
|
75
|
+
findings: [
|
|
76
|
+
{
|
|
77
|
+
category: 'module_versions',
|
|
78
|
+
severity: 'drift',
|
|
79
|
+
code: 'x',
|
|
80
|
+
message: 'caddy: 1.0 → 1.1',
|
|
81
|
+
subject: 'caddy',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
category: 'module_versions',
|
|
85
|
+
severity: 'drift',
|
|
86
|
+
code: 'x',
|
|
87
|
+
message: 'iptables: 1.0 → 1.1',
|
|
88
|
+
subject: 'iptables',
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
expect(out).toContain('DRIFT • module_versions (2)');
|
|
95
|
+
expect(out).toContain('caddy: 1.0 → 1.1');
|
|
96
|
+
expect(out).toContain('iptables: 1.0 → 1.1');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('renders details on indented lines', () => {
|
|
100
|
+
const out = formatReport(
|
|
101
|
+
baseReport({
|
|
102
|
+
verdict: 'BLOCKED',
|
|
103
|
+
findings: [
|
|
104
|
+
{
|
|
105
|
+
category: 'schema',
|
|
106
|
+
severity: 'blocked',
|
|
107
|
+
code: 'schema_pending_migrations',
|
|
108
|
+
message: '2 pending DB migrations',
|
|
109
|
+
details: ' • 0001_zones\n • 0002_backups',
|
|
110
|
+
subject: 'system',
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
expect(out).toContain('0001_zones');
|
|
117
|
+
expect(out).toContain('0002_backups');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('singular wording for exactly one finding', () => {
|
|
121
|
+
const out = formatReport(
|
|
122
|
+
baseReport({
|
|
123
|
+
verdict: 'DRIFT',
|
|
124
|
+
findings: [
|
|
125
|
+
{
|
|
126
|
+
category: 'cli_version',
|
|
127
|
+
severity: 'drift',
|
|
128
|
+
code: 'x',
|
|
129
|
+
message: 'cli is old',
|
|
130
|
+
subject: 'system',
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
}),
|
|
134
|
+
);
|
|
135
|
+
expect(out).toContain('DRIFT (1 finding)');
|
|
136
|
+
expect(out).not.toContain('1 findings');
|
|
137
|
+
});
|
|
138
|
+
});
|