@celilo/cli 0.1.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/README.md +1566 -0
- package/bin/celilo +16 -0
- package/drizzle/0000_complex_puma.sql +179 -0
- package/drizzle/0001_dizzy_wolfpack.sql +2 -0
- package/drizzle/0002_web_routes.sql +16 -0
- package/drizzle/0003_backup_storage.sql +32 -0
- package/drizzle/meta/0000_snapshot.json +1151 -0
- package/drizzle/meta/0001_snapshot.json +1167 -0
- package/drizzle/meta/0002_snapshot.json +1257 -0
- package/drizzle/meta/_journal.json +27 -0
- package/package.json +64 -0
- package/schemas/system_config.json +106 -0
- package/src/__integration__/container-services-cli.integration.test.ts +246 -0
- package/src/ansible/dependencies.test.ts +309 -0
- package/src/ansible/dependencies.ts +896 -0
- package/src/ansible/inventory.test.ts +463 -0
- package/src/ansible/inventory.ts +445 -0
- package/src/ansible/secrets.ts +222 -0
- package/src/ansible/validation.test.ts +92 -0
- package/src/ansible/validation.ts +272 -0
- package/src/api-clients/digitalocean.ts +94 -0
- package/src/api-clients/proxmox.ts +655 -0
- package/src/capabilities/logging-wrapper.test.ts +217 -0
- package/src/capabilities/lookup.test.ts +149 -0
- package/src/capabilities/lookup.ts +89 -0
- package/src/capabilities/public-web-helpers.test.ts +198 -0
- package/src/capabilities/public-web-publish.test.ts +458 -0
- package/src/capabilities/registration.test.ts +395 -0
- package/src/capabilities/registration.ts +200 -0
- package/src/capabilities/route-validation.test.ts +121 -0
- package/src/capabilities/route-validation.ts +96 -0
- package/src/capabilities/secret-ref.test.ts +313 -0
- package/src/capabilities/secret-validation.ts +157 -0
- package/src/capabilities/secrets.test.ts +750 -0
- package/src/capabilities/secrets.ts +244 -0
- package/src/capabilities/validation.test.ts +613 -0
- package/src/capabilities/validation.ts +160 -0
- package/src/capabilities/well-known.test.ts +238 -0
- package/src/capabilities/well-known.ts +222 -0
- package/src/cli/cli.test.ts +654 -0
- package/src/cli/command-registry.ts +742 -0
- package/src/cli/command-tree-parser.test.ts +180 -0
- package/src/cli/command-tree-parser.ts +193 -0
- package/src/cli/commands/backup-create.ts +137 -0
- package/src/cli/commands/backup-delete.ts +74 -0
- package/src/cli/commands/backup-import.ts +97 -0
- package/src/cli/commands/backup-list.ts +132 -0
- package/src/cli/commands/backup-name.ts +73 -0
- package/src/cli/commands/backup-prune.ts +98 -0
- package/src/cli/commands/backup-restore.ts +122 -0
- package/src/cli/commands/capability-info.ts +121 -0
- package/src/cli/commands/capability-list.ts +47 -0
- package/src/cli/commands/completion.ts +87 -0
- package/src/cli/commands/hook-run.ts +176 -0
- package/src/cli/commands/ipam.ts +607 -0
- package/src/cli/commands/machine-add.ts +235 -0
- package/src/cli/commands/machine-earmark.ts +82 -0
- package/src/cli/commands/machine-list.ts +77 -0
- package/src/cli/commands/machine-remove.ts +90 -0
- package/src/cli/commands/machine-status.ts +131 -0
- package/src/cli/commands/module-audit.ts +51 -0
- package/src/cli/commands/module-build.ts +60 -0
- package/src/cli/commands/module-config.ts +170 -0
- package/src/cli/commands/module-deploy.ts +71 -0
- package/src/cli/commands/module-generate.ts +236 -0
- package/src/cli/commands/module-health.ts +108 -0
- package/src/cli/commands/module-import.ts +80 -0
- package/src/cli/commands/module-list.ts +43 -0
- package/src/cli/commands/module-logs.ts +73 -0
- package/src/cli/commands/module-remove.ts +162 -0
- package/src/cli/commands/module-show.ts +208 -0
- package/src/cli/commands/module-status.ts +131 -0
- package/src/cli/commands/module-types.ts +189 -0
- package/src/cli/commands/module-upgrade.ts +192 -0
- package/src/cli/commands/package.ts +68 -0
- package/src/cli/commands/secret-list.ts +99 -0
- package/src/cli/commands/secret-set.ts +134 -0
- package/src/cli/commands/service-add-digitalocean.ts +133 -0
- package/src/cli/commands/service-add-proxmox.ts +342 -0
- package/src/cli/commands/service-config-get.ts +83 -0
- package/src/cli/commands/service-config-set.ts +145 -0
- package/src/cli/commands/service-list.ts +74 -0
- package/src/cli/commands/service-reconfigure.ts +230 -0
- package/src/cli/commands/service-remove.ts +103 -0
- package/src/cli/commands/service-verify.ts +240 -0
- package/src/cli/commands/status.ts +216 -0
- package/src/cli/commands/storage-add-local.ts +106 -0
- package/src/cli/commands/storage-add-s3.ts +114 -0
- package/src/cli/commands/storage-list.ts +72 -0
- package/src/cli/commands/storage-remove.ts +54 -0
- package/src/cli/commands/storage-set-default.ts +44 -0
- package/src/cli/commands/storage-verify.ts +54 -0
- package/src/cli/commands/system-config.ts +168 -0
- package/src/cli/commands/system-init.ts +314 -0
- package/src/cli/commands/system-secret-get.ts +98 -0
- package/src/cli/commands/system-secret-set.ts +76 -0
- package/src/cli/commands/system-vault-password.ts +34 -0
- package/src/cli/completion.test.ts +37 -0
- package/src/cli/completion.ts +482 -0
- package/src/cli/fuel-gauge.test.ts +208 -0
- package/src/cli/fuel-gauge.ts +405 -0
- package/src/cli/generate-zsh-completion.test.ts +95 -0
- package/src/cli/generate-zsh-completion.ts +497 -0
- package/src/cli/index.ts +1583 -0
- package/src/cli/interactive-config.test.ts +201 -0
- package/src/cli/interactive-config.ts +62 -0
- package/src/cli/parser.test.ts +227 -0
- package/src/cli/parser.ts +244 -0
- package/src/cli/prompts.test.ts +33 -0
- package/src/cli/prompts.ts +121 -0
- package/src/cli/types.ts +38 -0
- package/src/cli/validators.test.ts +235 -0
- package/src/cli/validators.ts +188 -0
- package/src/config/env.ts +41 -0
- package/src/config/paths.test.ts +172 -0
- package/src/config/paths.ts +108 -0
- package/src/db/client.ts +190 -0
- package/src/db/migrate.ts +30 -0
- package/src/db/schema.test.ts +221 -0
- package/src/db/schema.ts +434 -0
- package/src/hooks/capability-loader-firewall.test.ts +246 -0
- package/src/hooks/capability-loader.test.ts +100 -0
- package/src/hooks/capability-loader.ts +520 -0
- package/src/hooks/define-hook.test.ts +488 -0
- package/src/hooks/executor.test.ts +462 -0
- package/src/hooks/executor.ts +469 -0
- package/src/hooks/logger.test.ts +54 -0
- package/src/hooks/logger.ts +95 -0
- package/src/hooks/test-fixtures/failing-hook.ts +13 -0
- package/src/hooks/test-fixtures/no-default-hook.ts +6 -0
- package/src/hooks/test-fixtures/success-hook.ts +20 -0
- package/src/hooks/test-fixtures/unbranded-hook.ts +11 -0
- package/src/hooks/test-fixtures/void-hook.ts +13 -0
- package/src/hooks/types.ts +89 -0
- package/src/infrastructure/property-extractor.test.ts +194 -0
- package/src/infrastructure/property-extractor.ts +151 -0
- package/src/ipam/allocator.test.ts +442 -0
- package/src/ipam/allocator.ts +369 -0
- package/src/ipam/auto-allocator.test.ts +247 -0
- package/src/ipam/auto-allocator.ts +270 -0
- package/src/ipam/subnet-parser.test.ts +107 -0
- package/src/ipam/subnet-parser.ts +136 -0
- package/src/manifest/contracts/index.ts +61 -0
- package/src/manifest/contracts/v1.ts +118 -0
- package/src/manifest/json-schema-roundtrip.test.ts +99 -0
- package/src/manifest/schema.ts +367 -0
- package/src/manifest/template-validator.test.ts +231 -0
- package/src/manifest/template-validator.ts +322 -0
- package/src/manifest/validate.test.ts +1180 -0
- package/src/manifest/validate.ts +415 -0
- package/src/module/import.test.ts +355 -0
- package/src/module/import.ts +676 -0
- package/src/module/packaging/audit.ts +169 -0
- package/src/module/packaging/build.ts +228 -0
- package/src/module/packaging/checksum.ts +41 -0
- package/src/module/packaging/extract.ts +234 -0
- package/src/module/packaging/signature.ts +47 -0
- package/src/secrets/encryption.test.ts +284 -0
- package/src/secrets/encryption.ts +162 -0
- package/src/secrets/generators.test.ts +112 -0
- package/src/secrets/generators.ts +127 -0
- package/src/secrets/master-key.test.ts +159 -0
- package/src/secrets/master-key.ts +114 -0
- package/src/secrets/storage.test.ts +115 -0
- package/src/secrets/storage.ts +106 -0
- package/src/secrets/vault.test.ts +35 -0
- package/src/secrets/vault.ts +42 -0
- package/src/services/backup-create.ts +532 -0
- package/src/services/backup-metadata.ts +198 -0
- package/src/services/backup-restore.ts +229 -0
- package/src/services/backup-retention.ts +84 -0
- package/src/services/backup-storage.ts +281 -0
- package/src/services/build-stream.test.ts +122 -0
- package/src/services/build-stream.ts +201 -0
- package/src/services/config-interview.ts +694 -0
- package/src/services/container-service.test.ts +298 -0
- package/src/services/container-service.ts +401 -0
- package/src/services/cross-module-data-manager.test.ts +405 -0
- package/src/services/cross-module-data-manager.ts +412 -0
- package/src/services/deploy-ansible.ts +88 -0
- package/src/services/deploy-planner.ts +153 -0
- package/src/services/deploy-preflight.ts +274 -0
- package/src/services/deploy-ssh.ts +131 -0
- package/src/services/deploy-terraform.test.ts +55 -0
- package/src/services/deploy-terraform.ts +445 -0
- package/src/services/deploy-validation.ts +311 -0
- package/src/services/dns-auto-register.ts +211 -0
- package/src/services/health-runner.ts +184 -0
- package/src/services/infrastructure-selector.test.ts +485 -0
- package/src/services/infrastructure-selector.ts +245 -0
- package/src/services/infrastructure-variable-resolver.test.ts +751 -0
- package/src/services/infrastructure-variable-resolver.ts +234 -0
- package/src/services/machine-detector.ts +328 -0
- package/src/services/machine-pool.test.ts +405 -0
- package/src/services/machine-pool.ts +316 -0
- package/src/services/manifest-validation.ts +120 -0
- package/src/services/module-build.test.ts +290 -0
- package/src/services/module-build.ts +431 -0
- package/src/services/module-config.test.ts +237 -0
- package/src/services/module-config.ts +298 -0
- package/src/services/module-deploy.ts +862 -0
- package/src/services/module-types-drift.test.ts +73 -0
- package/src/services/module-types-generator.test.ts +288 -0
- package/src/services/module-types-generator.ts +189 -0
- package/src/services/proxmox-state-recovery.ts +140 -0
- package/src/services/schema-validation.ts +155 -0
- package/src/services/secret-schema-loader.test.ts +311 -0
- package/src/services/secret-schema-loader.ts +239 -0
- package/src/services/ssh-key-manager.test.ts +283 -0
- package/src/services/ssh-key-manager.ts +193 -0
- package/src/services/storage-providers/local.ts +105 -0
- package/src/services/storage-providers/s3.ts +182 -0
- package/src/services/storage-providers/types.ts +24 -0
- package/src/services/system-config-schema-types.ts +25 -0
- package/src/services/system-config-validator.test.ts +160 -0
- package/src/services/system-config-validator.ts +74 -0
- package/src/services/system-init.test.ts +153 -0
- package/src/services/system-init.ts +253 -0
- package/src/services/terraform-safety.ts +174 -0
- package/src/services/zone-detector.test.ts +110 -0
- package/src/services/zone-detector.ts +102 -0
- package/src/services/zone-policy.test.ts +97 -0
- package/src/services/zone-policy.ts +126 -0
- package/src/templates/generator.test.ts +645 -0
- package/src/templates/generator.ts +1119 -0
- package/src/templates/types.ts +62 -0
- package/src/test-utils/INTERACTIVE_PROMPTS.md +167 -0
- package/src/test-utils/cli-context-interactive.test.ts +152 -0
- package/src/test-utils/cli-context-server.test.ts +66 -0
- package/src/test-utils/cli-context.test.ts +273 -0
- package/src/test-utils/cli-context.ts +677 -0
- package/src/test-utils/cli-result.test.ts +282 -0
- package/src/test-utils/cli-result.ts +241 -0
- package/src/test-utils/cli.ts +55 -0
- package/src/test-utils/completion-harness.test.ts +126 -0
- package/src/test-utils/completion-harness.ts +82 -0
- package/src/test-utils/database.test.ts +182 -0
- package/src/test-utils/database.ts +126 -0
- package/src/test-utils/filesystem.test.ts +208 -0
- package/src/test-utils/filesystem.ts +142 -0
- package/src/test-utils/fixtures.test.ts +123 -0
- package/src/test-utils/fixtures.ts +160 -0
- package/src/test-utils/golden-diff.ts +197 -0
- package/src/test-utils/index.ts +77 -0
- package/src/test-utils/integration.ts +81 -0
- package/src/test-utils/module-fixtures.ts +468 -0
- package/src/test-utils/modules.test.ts +144 -0
- package/src/test-utils/modules.ts +183 -0
- package/src/test-utils/setup-test-db.ts +90 -0
- package/src/test-utils/value-extractor.test.ts +231 -0
- package/src/test-utils/value-extractor.ts +228 -0
- package/src/types/infrastructure.ts +157 -0
- package/src/utils/shell.test.ts +365 -0
- package/src/utils/shell.ts +159 -0
- package/src/validation/schemas.ts +166 -0
- package/src/variables/ansible-resolver.test.ts +142 -0
- package/src/variables/ansible-resolver.ts +69 -0
- package/src/variables/capability-self-ref.test.ts +220 -0
- package/src/variables/context.test.ts +1265 -0
- package/src/variables/context.ts +624 -0
- package/src/variables/declarative-derivation.test.ts +743 -0
- package/src/variables/declarative-derivation.ts +200 -0
- package/src/variables/parser.test.ts +231 -0
- package/src/variables/parser.ts +76 -0
- package/src/variables/resolver.test.ts +458 -0
- package/src/variables/resolver.ts +282 -0
- package/src/variables/types.ts +59 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rich Zsh Completion Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates a full zsh completion file from the command registry.
|
|
5
|
+
* Produces native zsh completions with descriptions, flags, dynamic
|
|
6
|
+
* database-backed argument completions, and nested command hierarchies.
|
|
7
|
+
*
|
|
8
|
+
* This is the ONLY source for zsh completions. The generated file
|
|
9
|
+
* should never be hand-edited.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ArgDef, CommandDef, FlagDef } from './command-registry';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Escape a string for use in zsh completion descriptions.
|
|
16
|
+
* Colons and backslashes need escaping in _describe arrays.
|
|
17
|
+
*/
|
|
18
|
+
function escapeZshDescription(text: string): string {
|
|
19
|
+
return text.replace(/\\/g, '\\\\').replace(/:/g, '\\:');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Convert a command path to a zsh function name.
|
|
24
|
+
* e.g., ["module", "config"] → "_celilo_module_config"
|
|
25
|
+
*/
|
|
26
|
+
function functionName(path: string[]): string {
|
|
27
|
+
return `_celilo${path.length > 0 ? `_${path.join('_')}` : ''}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Generate the _arguments line for flags
|
|
32
|
+
*/
|
|
33
|
+
function generateFlagArgs(flags: FlagDef[]): string[] {
|
|
34
|
+
return flags.map((flag) => {
|
|
35
|
+
const desc = escapeZshDescription(flag.description);
|
|
36
|
+
if (flag.takesValue) {
|
|
37
|
+
const hint = flag.valueHint ?? '';
|
|
38
|
+
// If valueHint is a zsh completer function (starts with _), use directly
|
|
39
|
+
// Otherwise treat as space-separated literal values
|
|
40
|
+
if (hint.startsWith('_')) {
|
|
41
|
+
return `'--${flag.name}[${desc}]:${flag.name}:${hint}'`;
|
|
42
|
+
}
|
|
43
|
+
if (hint) {
|
|
44
|
+
return `'--${flag.name}[${desc}]:${flag.name}:(${hint})'`;
|
|
45
|
+
}
|
|
46
|
+
return `'--${flag.name}[${desc}]:${flag.name}:'`;
|
|
47
|
+
}
|
|
48
|
+
return `'--${flag.name}[${desc}]'`;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get the zsh completion action for a dynamic completion source
|
|
54
|
+
*/
|
|
55
|
+
function completionAction(completion: string): string {
|
|
56
|
+
switch (completion) {
|
|
57
|
+
case 'module_ids':
|
|
58
|
+
return '_celilo_module_ids';
|
|
59
|
+
case 'service_ids':
|
|
60
|
+
return '_celilo_service_ids';
|
|
61
|
+
case 'machine_hostnames':
|
|
62
|
+
return '_celilo_machine_hostnames';
|
|
63
|
+
case 'capability_names':
|
|
64
|
+
return '_celilo_capability_names';
|
|
65
|
+
case 'config_keys':
|
|
66
|
+
return '_celilo_config_keys';
|
|
67
|
+
case 'system_config_keys':
|
|
68
|
+
return '_celilo_system_config_keys';
|
|
69
|
+
case 'system_secret_keys':
|
|
70
|
+
return '_celilo_system_secret_keys';
|
|
71
|
+
case 'storage_ids':
|
|
72
|
+
return '_celilo_storage_ids';
|
|
73
|
+
case 'backup_ids':
|
|
74
|
+
return '_celilo_backup_ids';
|
|
75
|
+
case 'files':
|
|
76
|
+
return '_files';
|
|
77
|
+
case 'directories':
|
|
78
|
+
return '_files -/';
|
|
79
|
+
default:
|
|
80
|
+
return '';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Generate positional argument specs for _arguments
|
|
86
|
+
*/
|
|
87
|
+
function generatePositionalArgs(args: ArgDef[]): string[] {
|
|
88
|
+
return args.map((arg, i) => {
|
|
89
|
+
const pos = arg.variadic ? '*' : String(i + 1);
|
|
90
|
+
const desc = escapeZshDescription(arg.description);
|
|
91
|
+
if (arg.completion) {
|
|
92
|
+
const action = completionAction(arg.completion);
|
|
93
|
+
return `'${pos}:${desc}:${action}'`;
|
|
94
|
+
}
|
|
95
|
+
return `'${pos}:${desc}:_files'`;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Generate a leaf command's completion (args + flags, no subcommands)
|
|
101
|
+
*/
|
|
102
|
+
function generateLeafArgs(cmd: CommandDef): string {
|
|
103
|
+
const parts: string[] = [];
|
|
104
|
+
|
|
105
|
+
if (cmd.flags && cmd.flags.length > 0) {
|
|
106
|
+
parts.push(...generateFlagArgs(cmd.flags));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (cmd.args && cmd.args.length > 0) {
|
|
110
|
+
parts.push(...generatePositionalArgs(cmd.args));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (parts.length === 0) {
|
|
114
|
+
return ' # No additional arguments';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (parts.length === 1) {
|
|
118
|
+
return ` _arguments ${parts[0]}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return ` _arguments \\\n ${parts.join(' \\\n ')}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Generate a command function that has subcommands
|
|
126
|
+
*/
|
|
127
|
+
function generateCommandFunction(cmd: CommandDef, path: string[], lines: string[]): void {
|
|
128
|
+
const fnName = functionName(path);
|
|
129
|
+
const subcommands = cmd.subcommands ?? [];
|
|
130
|
+
|
|
131
|
+
lines.push(`${fnName}() {`);
|
|
132
|
+
lines.push(' local curcontext="$curcontext" state line');
|
|
133
|
+
lines.push(' typeset -A opt_args');
|
|
134
|
+
lines.push('');
|
|
135
|
+
lines.push(' _arguments -C \\');
|
|
136
|
+
lines.push(` '1: :${fnName}_commands' \\`);
|
|
137
|
+
lines.push(" '*::arg:->args'");
|
|
138
|
+
lines.push('');
|
|
139
|
+
lines.push(' case $state in');
|
|
140
|
+
lines.push(' args)');
|
|
141
|
+
lines.push(' case $line[1] in');
|
|
142
|
+
|
|
143
|
+
for (const sub of subcommands) {
|
|
144
|
+
if (sub.subcommands && sub.subcommands.length > 0) {
|
|
145
|
+
// Sub has its own subcommands — dispatch to its function
|
|
146
|
+
lines.push(` ${sub.name})`);
|
|
147
|
+
lines.push(` ${functionName([...path, sub.name])}`);
|
|
148
|
+
lines.push(' ;;');
|
|
149
|
+
} else {
|
|
150
|
+
// Leaf subcommand
|
|
151
|
+
lines.push(` ${sub.name})`);
|
|
152
|
+
lines.push(generateLeafArgs(sub));
|
|
153
|
+
lines.push(' ;;');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
lines.push(' esac');
|
|
158
|
+
lines.push(' ;;');
|
|
159
|
+
lines.push(' esac');
|
|
160
|
+
lines.push('}');
|
|
161
|
+
lines.push('');
|
|
162
|
+
|
|
163
|
+
// Generate the _commands list function
|
|
164
|
+
lines.push(`${fnName}_commands() {`);
|
|
165
|
+
lines.push(' local commands=(');
|
|
166
|
+
for (const sub of subcommands) {
|
|
167
|
+
lines.push(` '${sub.name}:${escapeZshDescription(sub.description)}'`);
|
|
168
|
+
}
|
|
169
|
+
lines.push(' )');
|
|
170
|
+
lines.push(" _describe 'command' commands");
|
|
171
|
+
lines.push('}');
|
|
172
|
+
lines.push('');
|
|
173
|
+
|
|
174
|
+
// Recurse into subcommands that have their own subcommands
|
|
175
|
+
for (const sub of subcommands) {
|
|
176
|
+
if (sub.subcommands && sub.subcommands.length > 0) {
|
|
177
|
+
generateCommandFunction(sub, [...path, sub.name], lines);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Collect all dynamic completion sources used across the command tree
|
|
184
|
+
*/
|
|
185
|
+
function collectCompletionSources(commands: CommandDef[]): Set<string> {
|
|
186
|
+
const sources = new Set<string>();
|
|
187
|
+
|
|
188
|
+
function walk(cmd: CommandDef): void {
|
|
189
|
+
if (cmd.args) {
|
|
190
|
+
for (const arg of cmd.args) {
|
|
191
|
+
if (arg.completion && !['files', 'directories'].includes(arg.completion)) {
|
|
192
|
+
sources.add(arg.completion);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (cmd.subcommands) {
|
|
197
|
+
for (const sub of cmd.subcommands) {
|
|
198
|
+
walk(sub);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
for (const cmd of commands) {
|
|
204
|
+
walk(cmd);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return sources;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Generate the dynamic completion helper functions
|
|
212
|
+
*/
|
|
213
|
+
function generateDynamicCompletions(sources: Set<string>): string[] {
|
|
214
|
+
const lines: string[] = [];
|
|
215
|
+
|
|
216
|
+
if (sources.has('module_ids')) {
|
|
217
|
+
lines.push(`# Dynamic completions - module IDs
|
|
218
|
+
_celilo_module_ids() {
|
|
219
|
+
local -a module_ids
|
|
220
|
+
local db_path="\${CELILO_DB_PATH:-$HOME/Library/Application Support/celilo/celilo.db}"
|
|
221
|
+
|
|
222
|
+
if [[ -f "$db_path" ]] && command -v sqlite3 >/dev/null 2>&1; then
|
|
223
|
+
module_ids=(\${(f)"$(sqlite3 "$db_path" "SELECT id FROM modules;" 2>/dev/null)"})
|
|
224
|
+
if [[ \${#module_ids[@]} -gt 0 ]]; then
|
|
225
|
+
_describe 'module id' module_ids
|
|
226
|
+
return
|
|
227
|
+
fi
|
|
228
|
+
fi
|
|
229
|
+
|
|
230
|
+
_message 'module-id'
|
|
231
|
+
}
|
|
232
|
+
`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (sources.has('service_ids')) {
|
|
236
|
+
lines.push(`# Dynamic completions - service IDs
|
|
237
|
+
_celilo_service_ids() {
|
|
238
|
+
local -a service_ids
|
|
239
|
+
local db_path="\${CELILO_DB_PATH:-$HOME/Library/Application Support/celilo/celilo.db}"
|
|
240
|
+
|
|
241
|
+
if [[ -f "$db_path" ]] && command -v sqlite3 >/dev/null 2>&1; then
|
|
242
|
+
while IFS='|' read -r id name; do
|
|
243
|
+
service_ids+=("$id:$name")
|
|
244
|
+
done < <(sqlite3 "$db_path" "SELECT service_id, name FROM container_services;" 2>/dev/null)
|
|
245
|
+
if [[ \${#service_ids[@]} -gt 0 ]]; then
|
|
246
|
+
_describe 'service id' service_ids
|
|
247
|
+
return
|
|
248
|
+
fi
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
_message 'service-id'
|
|
252
|
+
}
|
|
253
|
+
`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (sources.has('machine_hostnames')) {
|
|
257
|
+
lines.push(`# Dynamic completions - machine hostnames
|
|
258
|
+
_celilo_machine_hostnames() {
|
|
259
|
+
local -a hostnames
|
|
260
|
+
local db_path="\${CELILO_DB_PATH:-$HOME/Library/Application Support/celilo/celilo.db}"
|
|
261
|
+
|
|
262
|
+
if [[ -f "$db_path" ]] && command -v sqlite3 >/dev/null 2>&1; then
|
|
263
|
+
hostnames=(\${(f)"$(sqlite3 "$db_path" "SELECT hostname FROM machines;" 2>/dev/null)"})
|
|
264
|
+
if [[ \${#hostnames[@]} -gt 0 ]]; then
|
|
265
|
+
_describe 'hostname' hostnames
|
|
266
|
+
return
|
|
267
|
+
fi
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
_message 'hostname'
|
|
271
|
+
}
|
|
272
|
+
`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (sources.has('capability_names')) {
|
|
276
|
+
lines.push(`# Dynamic completions - capability names
|
|
277
|
+
_celilo_capability_names() {
|
|
278
|
+
local -a capability_names
|
|
279
|
+
local db_path="\${CELILO_DB_PATH:-$HOME/Library/Application Support/celilo/celilo.db}"
|
|
280
|
+
|
|
281
|
+
if [[ -f "$db_path" ]] && command -v sqlite3 >/dev/null 2>&1; then
|
|
282
|
+
while IFS='|' read -r name module_id; do
|
|
283
|
+
capability_names+=("$name:provided by $module_id")
|
|
284
|
+
done < <(sqlite3 "$db_path" "SELECT capability_name, module_id FROM capabilities;" 2>/dev/null)
|
|
285
|
+
if [[ \${#capability_names[@]} -gt 0 ]]; then
|
|
286
|
+
_describe 'capability name' capability_names
|
|
287
|
+
return
|
|
288
|
+
fi
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
_message 'capability-name'
|
|
292
|
+
}
|
|
293
|
+
`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (sources.has('config_keys')) {
|
|
297
|
+
lines.push(`# Dynamic completions - module config keys (from manifest)
|
|
298
|
+
_celilo_config_keys() {
|
|
299
|
+
local module_id=$line[1]
|
|
300
|
+
local db_path="\${CELILO_DB_PATH:-$HOME/Library/Application Support/celilo/celilo.db}"
|
|
301
|
+
|
|
302
|
+
if [[ -n "$module_id" ]] && [[ -f "$db_path" ]] && command -v sqlite3 >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
|
|
303
|
+
local manifest_json=$(sqlite3 "$db_path" "SELECT manifest_data FROM modules WHERE id = '$module_id';" 2>/dev/null)
|
|
304
|
+
if [[ -n "$manifest_json" ]]; then
|
|
305
|
+
local -a config_keys
|
|
306
|
+
while IFS='|' read -r key desc required; do
|
|
307
|
+
local desc_text="$desc"
|
|
308
|
+
if [[ "$required" == "true" ]]; then
|
|
309
|
+
desc_text="$desc (required)"
|
|
310
|
+
fi
|
|
311
|
+
config_keys+=("$key:$desc_text")
|
|
312
|
+
done < <(echo "$manifest_json" | jq -r '.variables.owns[]? | "\\(.name)|\\(.description // "No description")|\\(.required // false)"' 2>/dev/null)
|
|
313
|
+
if [[ \${#config_keys[@]} -gt 0 ]]; then
|
|
314
|
+
_describe 'config key' config_keys
|
|
315
|
+
return 0
|
|
316
|
+
fi
|
|
317
|
+
fi
|
|
318
|
+
fi
|
|
319
|
+
|
|
320
|
+
_message 'config-key'
|
|
321
|
+
}
|
|
322
|
+
`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (sources.has('system_config_keys')) {
|
|
326
|
+
lines.push(`# Dynamic completions - system config keys
|
|
327
|
+
_celilo_system_config_keys() {
|
|
328
|
+
local config_keys=(
|
|
329
|
+
'proxmox.api_url:Proxmox API URL'
|
|
330
|
+
'proxmox.default_target_node:Default Proxmox node'
|
|
331
|
+
'proxmox.lxc_template:LXC container template'
|
|
332
|
+
'network.bridge:Network bridge name'
|
|
333
|
+
'network.dmz.subnet:DMZ subnet'
|
|
334
|
+
'network.dmz.vlan:DMZ VLAN ID'
|
|
335
|
+
'network.dmz.gateway:DMZ gateway IP'
|
|
336
|
+
'network.app.subnet:App subnet'
|
|
337
|
+
'network.app.vlan:App VLAN ID'
|
|
338
|
+
'network.app.gateway:App gateway IP'
|
|
339
|
+
'network.secure.subnet:Secure subnet'
|
|
340
|
+
'network.secure.vlan:Secure VLAN ID'
|
|
341
|
+
'network.secure.gateway:Secure gateway IP'
|
|
342
|
+
'network.internal.subnet:Internal subnet'
|
|
343
|
+
'network.internal.gateway:Internal gateway IP'
|
|
344
|
+
'dns.primary:Primary DNS server'
|
|
345
|
+
'dns.fallback:Fallback DNS servers'
|
|
346
|
+
'routing.internal_gateway:Internal gateway IP'
|
|
347
|
+
'ssh.public_key:SSH public key'
|
|
348
|
+
)
|
|
349
|
+
_describe 'config key' config_keys
|
|
350
|
+
}
|
|
351
|
+
`);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (sources.has('system_secret_keys')) {
|
|
355
|
+
lines.push(`# Dynamic completions - system secret keys
|
|
356
|
+
_celilo_system_secret_keys() {
|
|
357
|
+
local secret_keys=(
|
|
358
|
+
'proxmox.root_password:Proxmox root password'
|
|
359
|
+
)
|
|
360
|
+
_describe 'secret key' secret_keys
|
|
361
|
+
}
|
|
362
|
+
`);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (sources.has('storage_ids')) {
|
|
366
|
+
lines.push(`# Dynamic completions - backup storage IDs
|
|
367
|
+
_celilo_storage_ids() {
|
|
368
|
+
local -a storage_ids
|
|
369
|
+
local db_path="\${CELILO_DB_PATH:-$HOME/Library/Application Support/celilo/celilo.db}"
|
|
370
|
+
|
|
371
|
+
if [[ -f "$db_path" ]] && command -v sqlite3 >/dev/null 2>&1; then
|
|
372
|
+
while IFS='|' read -r id name; do
|
|
373
|
+
storage_ids+=("$id:$name")
|
|
374
|
+
done < <(sqlite3 "$db_path" "SELECT storage_id, name FROM backup_storages;" 2>/dev/null)
|
|
375
|
+
if [[ \${#storage_ids[@]} -gt 0 ]]; then
|
|
376
|
+
_describe 'storage id' storage_ids
|
|
377
|
+
return
|
|
378
|
+
fi
|
|
379
|
+
fi
|
|
380
|
+
|
|
381
|
+
_message 'storage-id'
|
|
382
|
+
}
|
|
383
|
+
`);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (sources.has('backup_ids')) {
|
|
387
|
+
lines.push(`# Dynamic completions - backup IDs and names
|
|
388
|
+
_celilo_backup_ids() {
|
|
389
|
+
local -a backup_ids
|
|
390
|
+
local db_path="\${CELILO_DB_PATH:-$HOME/Library/Application Support/celilo/celilo.db}"
|
|
391
|
+
|
|
392
|
+
if [[ -f "$db_path" ]] && command -v sqlite3 >/dev/null 2>&1; then
|
|
393
|
+
while IFS='|' read -r id module_id backup_type started_at name; do
|
|
394
|
+
local short_id="\${id:0:8}"
|
|
395
|
+
local label="\${module_id:-system}"
|
|
396
|
+
if [[ "$backup_type" == "system_state" ]]; then label="system"; fi
|
|
397
|
+
local date="\${started_at}"
|
|
398
|
+
backup_ids+=("$short_id:$label $date")
|
|
399
|
+
if [[ -n "$name" ]]; then
|
|
400
|
+
backup_ids+=("$name:$label $date")
|
|
401
|
+
fi
|
|
402
|
+
done < <(sqlite3 "$db_path" "SELECT id, module_id, backup_type, datetime(started_at, 'unixepoch'), name FROM backups ORDER BY started_at DESC LIMIT 50;" 2>/dev/null)
|
|
403
|
+
if [[ \${#backup_ids[@]} -gt 0 ]]; then
|
|
404
|
+
_describe 'backup id' backup_ids
|
|
405
|
+
return
|
|
406
|
+
fi
|
|
407
|
+
fi
|
|
408
|
+
|
|
409
|
+
_message 'backup-id or name'
|
|
410
|
+
}
|
|
411
|
+
`);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return lines;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Generate the complete rich zsh completion script
|
|
419
|
+
*
|
|
420
|
+
* @param commands - Command tree from the registry
|
|
421
|
+
* @returns Complete zsh completion script as a string
|
|
422
|
+
*/
|
|
423
|
+
export function generateRichZshCompletion(commands: CommandDef[]): string {
|
|
424
|
+
const lines: string[] = [];
|
|
425
|
+
|
|
426
|
+
// Header
|
|
427
|
+
lines.push('#compdef celilo');
|
|
428
|
+
lines.push('');
|
|
429
|
+
lines.push('# Auto-generated by: celilo completion zsh');
|
|
430
|
+
lines.push('# Do not edit manually. Regenerate with:');
|
|
431
|
+
lines.push('# celilo completion zsh > ~/.zsh/completions/_celilo');
|
|
432
|
+
lines.push('');
|
|
433
|
+
|
|
434
|
+
// Main _celilo function
|
|
435
|
+
lines.push('_celilo() {');
|
|
436
|
+
lines.push(' local curcontext="$curcontext" state line');
|
|
437
|
+
lines.push(' typeset -A opt_args');
|
|
438
|
+
lines.push('');
|
|
439
|
+
lines.push(' _arguments -C \\');
|
|
440
|
+
lines.push(" '1: :_celilo_commands' \\");
|
|
441
|
+
lines.push(" '*::arg:->args'");
|
|
442
|
+
lines.push('');
|
|
443
|
+
lines.push(' case $state in');
|
|
444
|
+
lines.push(' args)');
|
|
445
|
+
lines.push(' case $line[1] in');
|
|
446
|
+
|
|
447
|
+
for (const cmd of commands) {
|
|
448
|
+
if (cmd.subcommands && cmd.subcommands.length > 0) {
|
|
449
|
+
lines.push(` ${cmd.name})`);
|
|
450
|
+
lines.push(` _celilo_${cmd.name}`);
|
|
451
|
+
lines.push(' ;;');
|
|
452
|
+
} else if (cmd.args || cmd.flags) {
|
|
453
|
+
lines.push(` ${cmd.name})`);
|
|
454
|
+
lines.push(generateLeafArgs(cmd));
|
|
455
|
+
lines.push(' ;;');
|
|
456
|
+
} else {
|
|
457
|
+
lines.push(` ${cmd.name})`);
|
|
458
|
+
lines.push(' # No additional arguments');
|
|
459
|
+
lines.push(' ;;');
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
lines.push(' esac');
|
|
464
|
+
lines.push(' ;;');
|
|
465
|
+
lines.push(' esac');
|
|
466
|
+
lines.push('}');
|
|
467
|
+
lines.push('');
|
|
468
|
+
|
|
469
|
+
// Top-level commands list
|
|
470
|
+
lines.push('_celilo_commands() {');
|
|
471
|
+
lines.push(' local commands=(');
|
|
472
|
+
for (const cmd of commands) {
|
|
473
|
+
lines.push(` '${cmd.name}:${escapeZshDescription(cmd.description)}'`);
|
|
474
|
+
}
|
|
475
|
+
lines.push(' )');
|
|
476
|
+
lines.push(" _describe 'command' commands");
|
|
477
|
+
lines.push('}');
|
|
478
|
+
lines.push('');
|
|
479
|
+
|
|
480
|
+
// Generate functions for each command with subcommands
|
|
481
|
+
for (const cmd of commands) {
|
|
482
|
+
if (cmd.subcommands && cmd.subcommands.length > 0) {
|
|
483
|
+
generateCommandFunction(cmd, [cmd.name], lines);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Generate dynamic completion helpers
|
|
488
|
+
const sources = collectCompletionSources(commands);
|
|
489
|
+
const dynamicLines = generateDynamicCompletions(sources);
|
|
490
|
+
lines.push(...dynamicLines);
|
|
491
|
+
|
|
492
|
+
// Footer
|
|
493
|
+
lines.push('_celilo "$@"');
|
|
494
|
+
lines.push('');
|
|
495
|
+
|
|
496
|
+
return lines.join('\n');
|
|
497
|
+
}
|