@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,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for shell completion
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from 'bun:test';
|
|
6
|
+
import { getCompletions } from './completion';
|
|
7
|
+
|
|
8
|
+
describe('getCompletions', () => {
|
|
9
|
+
it('completes top-level commands', async () => {
|
|
10
|
+
const suggestions = await getCompletions(['m'], 0);
|
|
11
|
+
expect(suggestions).toContain('module');
|
|
12
|
+
expect(suggestions).toContain('machine');
|
|
13
|
+
expect(suggestions).not.toContain('service');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('completes service subcommands', async () => {
|
|
17
|
+
const suggestions = await getCompletions(['service', 'v'], 1);
|
|
18
|
+
expect(suggestions).toContain('verify');
|
|
19
|
+
expect(suggestions).not.toContain('add');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('completes service add providers', async () => {
|
|
23
|
+
const suggestions = await getCompletions(['service', 'add', 'p'], 2);
|
|
24
|
+
expect(suggestions).toContain('proxmox');
|
|
25
|
+
expect(suggestions).not.toContain('digitalocean');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns empty array for unknown context', async () => {
|
|
29
|
+
const suggestions = await getCompletions(['unknown', 'command'], 2);
|
|
30
|
+
expect(suggestions).toEqual([]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('handles completion at different positions', async () => {
|
|
34
|
+
const suggestions = await getCompletions(['module', 'li'], 1);
|
|
35
|
+
expect(suggestions).toContain('list');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell Completion Support
|
|
3
|
+
* Provides tab completion suggestions for bash/zsh
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { eq } from 'drizzle-orm';
|
|
7
|
+
import { getDb } from '../db/client';
|
|
8
|
+
import { capabilities, modules } from '../db/schema';
|
|
9
|
+
import type { ModuleManifest } from '../manifest/schema';
|
|
10
|
+
import { listBackups } from '../services/backup-metadata';
|
|
11
|
+
import { listBackupStorages } from '../services/backup-storage';
|
|
12
|
+
import { listContainerServices } from '../services/container-service';
|
|
13
|
+
import { listMachines } from '../services/machine-pool';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get completion suggestions based on current command context
|
|
17
|
+
*
|
|
18
|
+
* @param words - Array of words typed so far
|
|
19
|
+
* @param current - Index of word being completed
|
|
20
|
+
* @returns Array of completion suggestions
|
|
21
|
+
*/
|
|
22
|
+
export async function getCompletions(words: string[], current: number): Promise<string[]> {
|
|
23
|
+
// Remove 'celilo' from words if present
|
|
24
|
+
const args = words[0] === 'celilo' ? words.slice(1) : words;
|
|
25
|
+
const currentIndex = words[0] === 'celilo' ? current - 1 : current;
|
|
26
|
+
|
|
27
|
+
// Complete top-level commands
|
|
28
|
+
// currentIndex === 0 means we're completing the first word (the command)
|
|
29
|
+
if (currentIndex === 0) {
|
|
30
|
+
const commands = [
|
|
31
|
+
'backup',
|
|
32
|
+
'capability',
|
|
33
|
+
'help',
|
|
34
|
+
'hook',
|
|
35
|
+
'package',
|
|
36
|
+
'module',
|
|
37
|
+
'service',
|
|
38
|
+
'storage',
|
|
39
|
+
'machine',
|
|
40
|
+
'system',
|
|
41
|
+
'ipam',
|
|
42
|
+
'completion',
|
|
43
|
+
'status',
|
|
44
|
+
'version',
|
|
45
|
+
];
|
|
46
|
+
return filterSuggestions(commands, args[0] || '');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const command = args[0];
|
|
50
|
+
|
|
51
|
+
// Capability subcommands
|
|
52
|
+
if (command === 'capability' && currentIndex === 1) {
|
|
53
|
+
const subcommands = ['list', 'info'];
|
|
54
|
+
return filterSuggestions(subcommands, args[1] || '');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Capability info - complete with capability names
|
|
58
|
+
if (command === 'capability' && args[1] === 'info' && currentIndex === 2) {
|
|
59
|
+
const db = getDb();
|
|
60
|
+
const capabilityRows = db
|
|
61
|
+
.select({ name: capabilities.capabilityName })
|
|
62
|
+
.from(capabilities)
|
|
63
|
+
.all();
|
|
64
|
+
const capabilityNames = capabilityRows.map((c) => c.name);
|
|
65
|
+
return filterSuggestions(capabilityNames, args[2] || '');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Hook subcommands
|
|
69
|
+
if (command === 'hook' && currentIndex === 1) {
|
|
70
|
+
const subcommands = ['run'];
|
|
71
|
+
return filterSuggestions(subcommands, args[1] || '');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Hook run - complete with module IDs
|
|
75
|
+
if (command === 'hook' && args[1] === 'run' && currentIndex === 2) {
|
|
76
|
+
const db = getDb();
|
|
77
|
+
const moduleRows = db.select({ id: modules.id }).from(modules).all();
|
|
78
|
+
const moduleIds = moduleRows.map((p) => p.id);
|
|
79
|
+
return filterSuggestions(moduleIds, args[2] || '');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Hook run <module-id> - complete with hook names from manifest
|
|
83
|
+
if (command === 'hook' && args[1] === 'run' && currentIndex === 3) {
|
|
84
|
+
const db = getDb();
|
|
85
|
+
const module = db
|
|
86
|
+
.select()
|
|
87
|
+
.from(modules)
|
|
88
|
+
.where(eq(modules.id, args[2] || ''))
|
|
89
|
+
.get();
|
|
90
|
+
if (module?.manifestData) {
|
|
91
|
+
const manifest = module.manifestData as ModuleManifest;
|
|
92
|
+
const hookNames = Object.keys(manifest.hooks || {});
|
|
93
|
+
return filterSuggestions(hookNames, args[3] || '');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Module subcommands
|
|
98
|
+
if (command === 'module' && currentIndex === 1) {
|
|
99
|
+
const subcommands = [
|
|
100
|
+
'import',
|
|
101
|
+
'list',
|
|
102
|
+
'remove',
|
|
103
|
+
'update',
|
|
104
|
+
'audit',
|
|
105
|
+
'config',
|
|
106
|
+
'show-config',
|
|
107
|
+
'show-zone',
|
|
108
|
+
'build',
|
|
109
|
+
'generate',
|
|
110
|
+
'deploy',
|
|
111
|
+
'destroy',
|
|
112
|
+
'health',
|
|
113
|
+
'logs',
|
|
114
|
+
'run-hook',
|
|
115
|
+
'secret',
|
|
116
|
+
'status',
|
|
117
|
+
'types',
|
|
118
|
+
'validate',
|
|
119
|
+
];
|
|
120
|
+
return filterSuggestions(subcommands, args[1] || '');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Module config subcommands (celilo module config set/get)
|
|
124
|
+
if (command === 'module' && args[1] === 'config' && currentIndex === 2) {
|
|
125
|
+
const subcommands = ['set', 'get'];
|
|
126
|
+
return filterSuggestions(subcommands, args[2] || '');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Module types subcommands (celilo module types generate/check)
|
|
130
|
+
if (command === 'module' && args[1] === 'types' && currentIndex === 2) {
|
|
131
|
+
const subcommands = ['generate', 'check'];
|
|
132
|
+
return filterSuggestions(subcommands, args[2] || '');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Module config set/get - complete with module IDs
|
|
136
|
+
if (
|
|
137
|
+
command === 'module' &&
|
|
138
|
+
args[1] === 'config' &&
|
|
139
|
+
(args[2] === 'set' || args[2] === 'get') &&
|
|
140
|
+
currentIndex === 3
|
|
141
|
+
) {
|
|
142
|
+
const db = getDb();
|
|
143
|
+
const modulesList = db.select({ id: modules.id }).from(modules).all();
|
|
144
|
+
const moduleIds = modulesList.map((p) => p.id);
|
|
145
|
+
return filterSuggestions(moduleIds, args[3] || '');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Module config set/get <module-id> - complete with config variable names
|
|
149
|
+
if (
|
|
150
|
+
command === 'module' &&
|
|
151
|
+
args[1] === 'config' &&
|
|
152
|
+
(args[2] === 'set' || args[2] === 'get') &&
|
|
153
|
+
currentIndex === 4
|
|
154
|
+
) {
|
|
155
|
+
const db = getDb();
|
|
156
|
+
const module = db
|
|
157
|
+
.select()
|
|
158
|
+
.from(modules)
|
|
159
|
+
.where(eq(modules.id, args[3] || ''))
|
|
160
|
+
.get();
|
|
161
|
+
if (module?.manifestData) {
|
|
162
|
+
const manifest = module.manifestData as ModuleManifest;
|
|
163
|
+
const varNames = (manifest.variables?.owns || [])
|
|
164
|
+
.filter((v) => v.source === 'user' || !v.source)
|
|
165
|
+
.map((v) => v.name);
|
|
166
|
+
return filterSuggestions(varNames, args[4] || '');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Module secret subcommands (celilo module secret set/list)
|
|
171
|
+
if (command === 'module' && args[1] === 'secret' && currentIndex === 2) {
|
|
172
|
+
const subcommands = ['set', 'list'];
|
|
173
|
+
return filterSuggestions(subcommands, args[2] || '');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Module secret set/list - complete with module IDs
|
|
177
|
+
if (
|
|
178
|
+
command === 'module' &&
|
|
179
|
+
args[1] === 'secret' &&
|
|
180
|
+
(args[2] === 'set' || args[2] === 'list') &&
|
|
181
|
+
currentIndex === 3
|
|
182
|
+
) {
|
|
183
|
+
const db = getDb();
|
|
184
|
+
const modulesList = db.select({ id: modules.id }).from(modules).all();
|
|
185
|
+
const moduleIds = modulesList.map((p) => p.id);
|
|
186
|
+
return filterSuggestions(moduleIds, args[3] || '');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Module secret set <module-id> - complete with secret names
|
|
190
|
+
if (command === 'module' && args[1] === 'secret' && args[2] === 'set' && currentIndex === 4) {
|
|
191
|
+
const db = getDb();
|
|
192
|
+
const module = db
|
|
193
|
+
.select()
|
|
194
|
+
.from(modules)
|
|
195
|
+
.where(eq(modules.id, args[3] || ''))
|
|
196
|
+
.get();
|
|
197
|
+
if (module?.manifestData) {
|
|
198
|
+
const manifest = module.manifestData as ModuleManifest;
|
|
199
|
+
const secretNames = (manifest.secrets?.declares || []).map((s) => s.name);
|
|
200
|
+
return filterSuggestions(secretNames, args[4] || '');
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Service subcommands
|
|
205
|
+
if (command === 'service' && currentIndex === 1) {
|
|
206
|
+
const subcommands = ['add', 'list', 'verify', 'reconfigure', 'remove', 'config'];
|
|
207
|
+
return filterSuggestions(subcommands, args[1] || '');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Service add providers
|
|
211
|
+
if (command === 'service' && args[1] === 'add' && currentIndex === 2) {
|
|
212
|
+
const providers = ['proxmox', 'digitalocean'];
|
|
213
|
+
return filterSuggestions(providers, args[2] || '');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Service verify - complete with service IDs
|
|
217
|
+
if (command === 'service' && args[1] === 'verify' && currentIndex === 2) {
|
|
218
|
+
const services = await listContainerServices();
|
|
219
|
+
const serviceIds = services.map((s) => s.serviceId);
|
|
220
|
+
return filterSuggestions(serviceIds, args[2] || '');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Service reconfigure - complete with service IDs
|
|
224
|
+
if (command === 'service' && args[1] === 'reconfigure' && currentIndex === 2) {
|
|
225
|
+
const services = await listContainerServices();
|
|
226
|
+
const serviceIds = services.map((s) => s.serviceId);
|
|
227
|
+
return filterSuggestions(serviceIds, args[2] || '');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Service remove - complete with service IDs
|
|
231
|
+
if (command === 'service' && args[1] === 'remove' && currentIndex === 2) {
|
|
232
|
+
const services = await listContainerServices();
|
|
233
|
+
const serviceIds = services.map((s) => s.serviceId);
|
|
234
|
+
return filterSuggestions(serviceIds, args[2] || '');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Service config operations
|
|
238
|
+
if (command === 'service' && args[1] === 'config' && currentIndex === 2) {
|
|
239
|
+
const operations = ['get', 'set'];
|
|
240
|
+
return filterSuggestions(operations, args[2] || '');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Service config get - complete with service IDs
|
|
244
|
+
if (command === 'service' && args[1] === 'config' && args[2] === 'get' && currentIndex === 3) {
|
|
245
|
+
const services = await listContainerServices();
|
|
246
|
+
const serviceIds = services.map((s) => s.serviceId);
|
|
247
|
+
return filterSuggestions(serviceIds, args[3] || '');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Service config get - complete with config keys
|
|
251
|
+
if (command === 'service' && args[1] === 'config' && args[2] === 'get' && currentIndex === 4) {
|
|
252
|
+
const configKeys = ['name', 'zones', 'providerConfig'];
|
|
253
|
+
return filterSuggestions(configKeys, args[4] || '');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Service config set - complete with service IDs
|
|
257
|
+
if (command === 'service' && args[1] === 'config' && args[2] === 'set' && currentIndex === 3) {
|
|
258
|
+
const services = await listContainerServices();
|
|
259
|
+
const serviceIds = services.map((s) => s.serviceId);
|
|
260
|
+
return filterSuggestions(serviceIds, args[3] || '');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Service config set - complete with config keys
|
|
264
|
+
if (command === 'service' && args[1] === 'config' && args[2] === 'set' && currentIndex === 4) {
|
|
265
|
+
const configKeys = ['name', 'zones', 'providerConfig'];
|
|
266
|
+
return filterSuggestions(configKeys, args[4] || '');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Machine subcommands
|
|
270
|
+
if (command === 'machine' && currentIndex === 1) {
|
|
271
|
+
const subcommands = ['add', 'list', 'status', 'remove', 'earmark', 'detect'];
|
|
272
|
+
return filterSuggestions(subcommands, args[1] || '');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Machine remove/earmark - complete with machine hostnames and IPs
|
|
276
|
+
if (
|
|
277
|
+
command === 'machine' &&
|
|
278
|
+
(args[1] === 'remove' || args[1] === 'earmark') &&
|
|
279
|
+
currentIndex === 2
|
|
280
|
+
) {
|
|
281
|
+
const machineList = await listMachines();
|
|
282
|
+
const identifiers = [
|
|
283
|
+
...machineList.map((m) => m.hostname),
|
|
284
|
+
...machineList.map((m) => m.ipAddress),
|
|
285
|
+
...machineList.flatMap((m) => m.interfaces.map((i) => i.ipAddress)),
|
|
286
|
+
];
|
|
287
|
+
return filterSuggestions(identifiers, args[2] || '');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Machine earmark - complete with module IDs at position 3
|
|
291
|
+
if (command === 'machine' && args[1] === 'earmark' && currentIndex === 3) {
|
|
292
|
+
const db = getDb();
|
|
293
|
+
const moduleRows = db.select().from(modules).all();
|
|
294
|
+
const moduleIds = moduleRows.map((p: { id: string }) => p.id);
|
|
295
|
+
return filterSuggestions(moduleIds, args[3] || '');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Module commands that take module ID directly at position 2
|
|
299
|
+
if (command === 'module' && currentIndex === 2) {
|
|
300
|
+
const moduleCommands = [
|
|
301
|
+
'generate',
|
|
302
|
+
'deploy',
|
|
303
|
+
'destroy',
|
|
304
|
+
'logs',
|
|
305
|
+
'remove',
|
|
306
|
+
'build',
|
|
307
|
+
'run-hook',
|
|
308
|
+
'status',
|
|
309
|
+
];
|
|
310
|
+
if (moduleCommands.includes(args[1] || '')) {
|
|
311
|
+
const db = getDb();
|
|
312
|
+
const moduleRows = db.select().from(modules).all();
|
|
313
|
+
const moduleIds = moduleRows.map((p: { id: string }) => p.id);
|
|
314
|
+
return filterSuggestions(moduleIds, args[2] || '');
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Module run-hook <module-id> - complete with hook names from manifest
|
|
319
|
+
if (command === 'module' && args[1] === 'run-hook' && currentIndex === 3) {
|
|
320
|
+
const db = getDb();
|
|
321
|
+
const module = db
|
|
322
|
+
.select()
|
|
323
|
+
.from(modules)
|
|
324
|
+
.where(eq(modules.id, args[2] || ''))
|
|
325
|
+
.get();
|
|
326
|
+
if (module?.manifestData) {
|
|
327
|
+
const manifest = module.manifestData as ModuleManifest;
|
|
328
|
+
const hookNames = Object.keys(manifest.hooks || {});
|
|
329
|
+
return filterSuggestions(hookNames, args[3] || '');
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Storage subcommands
|
|
334
|
+
if (command === 'storage' && currentIndex === 1) {
|
|
335
|
+
const subcommands = ['add', 'list', 'remove', 'verify', 'set-default'];
|
|
336
|
+
return filterSuggestions(subcommands, args[1] || '');
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Storage add providers
|
|
340
|
+
if (command === 'storage' && args[1] === 'add' && currentIndex === 2) {
|
|
341
|
+
const providers = ['local', 's3'];
|
|
342
|
+
return filterSuggestions(providers, args[2] || '');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Storage remove/verify/set-default - complete with storage IDs
|
|
346
|
+
if (
|
|
347
|
+
command === 'storage' &&
|
|
348
|
+
(args[1] === 'remove' || args[1] === 'verify' || args[1] === 'set-default') &&
|
|
349
|
+
currentIndex === 2
|
|
350
|
+
) {
|
|
351
|
+
const storages = listBackupStorages();
|
|
352
|
+
const storageIds = storages.map((s) => s.storageId);
|
|
353
|
+
return filterSuggestions(storageIds, args[2] || '');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Backup subcommands
|
|
357
|
+
if (command === 'backup' && currentIndex === 1) {
|
|
358
|
+
const subcommands = ['create', 'list', 'restore', 'delete', 'prune', 'name', 'import'];
|
|
359
|
+
return filterSuggestions(subcommands, args[1] || '');
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Backup create/list/prune - complete with module IDs
|
|
363
|
+
if (
|
|
364
|
+
command === 'backup' &&
|
|
365
|
+
(args[1] === 'create' || args[1] === 'list' || args[1] === 'prune') &&
|
|
366
|
+
currentIndex === 2
|
|
367
|
+
) {
|
|
368
|
+
const db = getDb();
|
|
369
|
+
const moduleRows = db.select({ id: modules.id }).from(modules).all();
|
|
370
|
+
const moduleIds = moduleRows.map((p: { id: string }) => p.id);
|
|
371
|
+
return filterSuggestions(moduleIds, args[2] || '');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Backup restore/delete/name - complete with short backup IDs and names
|
|
375
|
+
if (
|
|
376
|
+
command === 'backup' &&
|
|
377
|
+
(args[1] === 'restore' || args[1] === 'delete' || args[1] === 'name') &&
|
|
378
|
+
currentIndex === 2
|
|
379
|
+
) {
|
|
380
|
+
const backupList = listBackups({ limit: 50 });
|
|
381
|
+
const suggestions = backupList.map((b) => b.id.substring(0, 8));
|
|
382
|
+
for (const b of backupList) {
|
|
383
|
+
if (b.name) suggestions.push(b.name);
|
|
384
|
+
}
|
|
385
|
+
return filterSuggestions(suggestions, args[2] || '');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// IPAM subcommands
|
|
389
|
+
if (command === 'ipam' && currentIndex === 1) {
|
|
390
|
+
const subcommands = ['show', 'list-allocations', 'vmid', 'ip'];
|
|
391
|
+
return filterSuggestions(subcommands, args[1] || '');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Completion subcommands
|
|
395
|
+
if (command === 'completion' && currentIndex === 1) {
|
|
396
|
+
const subcommands = ['bash', 'zsh'];
|
|
397
|
+
return filterSuggestions(subcommands, args[1] || '');
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// System subcommands
|
|
401
|
+
if (command === 'system' && currentIndex === 1) {
|
|
402
|
+
const subcommands = ['init', 'config', 'secret', 'vault-password'];
|
|
403
|
+
return filterSuggestions(subcommands, args[1] || '');
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// System config operations
|
|
407
|
+
if (command === 'system' && args[1] === 'config' && currentIndex === 2) {
|
|
408
|
+
const operations = ['get', 'set'];
|
|
409
|
+
return filterSuggestions(operations, args[2] || '');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// System secret operations
|
|
413
|
+
if (command === 'system' && args[1] === 'secret' && currentIndex === 2) {
|
|
414
|
+
const operations = ['get', 'set'];
|
|
415
|
+
return filterSuggestions(operations, args[2] || '');
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return [];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Filter suggestions based on current input
|
|
423
|
+
*/
|
|
424
|
+
function filterSuggestions(options: string[], current: string): string[] {
|
|
425
|
+
if (!current) return options;
|
|
426
|
+
return options.filter((opt) => opt.startsWith(current));
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Generate bash completion script
|
|
431
|
+
*/
|
|
432
|
+
export function generateBashCompletion(): string {
|
|
433
|
+
return `# Celilo bash completion
|
|
434
|
+
_celilo_completion() {
|
|
435
|
+
local cur prev words cword
|
|
436
|
+
_init_completion || return
|
|
437
|
+
|
|
438
|
+
# Get completions from celilo
|
|
439
|
+
local completions=$(celilo --get-completions "\${COMP_WORDS[@]}" "\${COMP_CWORD}")
|
|
440
|
+
|
|
441
|
+
# Apply completions
|
|
442
|
+
COMPREPLY=( $(compgen -W "$completions" -- "$cur") )
|
|
443
|
+
return 0
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
complete -F _celilo_completion celilo
|
|
447
|
+
`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Generate zsh completion script
|
|
452
|
+
*/
|
|
453
|
+
export function generateZshCompletion(): string {
|
|
454
|
+
return `#compdef celilo
|
|
455
|
+
|
|
456
|
+
_celilo() {
|
|
457
|
+
local -a completions
|
|
458
|
+
local -a words_array
|
|
459
|
+
local current_word
|
|
460
|
+
|
|
461
|
+
# Get current word index (CURRENT is 1-based)
|
|
462
|
+
current_word=\${CURRENT}
|
|
463
|
+
|
|
464
|
+
# Get words from command line
|
|
465
|
+
words_array=("\${words[@]}")
|
|
466
|
+
|
|
467
|
+
# Call celilo to get completions (returns newline-separated list)
|
|
468
|
+
local result
|
|
469
|
+
result=$(celilo --get-completions "\${words_array[@]}" "$current_word" 2>/dev/null)
|
|
470
|
+
|
|
471
|
+
# Split result into array by newlines
|
|
472
|
+
completions=("\${(@f)result}")
|
|
473
|
+
|
|
474
|
+
# Provide completions
|
|
475
|
+
if (( \${#completions} > 0 )); then
|
|
476
|
+
compadd -a completions
|
|
477
|
+
fi
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
compdef _celilo celilo
|
|
481
|
+
`;
|
|
482
|
+
}
|