@celilo/cli 0.5.0-alpha.1 → 0.5.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/drizzle/0009_dns_registrations.sql +13 -0
  2. package/drizzle/0010_dns_internal_records.sql +12 -0
  3. package/drizzle/0011_backups_name.sql +1 -0
  4. package/drizzle/meta/_journal.json +22 -1
  5. package/package.json +3 -3
  6. package/src/ansible/inventory.test.ts +10 -10
  7. package/src/ansible/validation.test.ts +25 -15
  8. package/src/api-clients/proxmox.test.ts +211 -1
  9. package/src/api-clients/proxmox.ts +399 -8
  10. package/src/cli/command-registry.ts +83 -6
  11. package/src/cli/commands/backup-delete.ts +10 -7
  12. package/src/cli/commands/backup-import.ts +11 -8
  13. package/src/cli/commands/backup-restore.ts +11 -8
  14. package/src/cli/commands/dns.ts +57 -0
  15. package/src/cli/commands/events.test.ts +4 -4
  16. package/src/cli/commands/events.ts +89 -24
  17. package/src/cli/commands/machine-add.ts +178 -163
  18. package/src/cli/commands/machine-remove.ts +10 -7
  19. package/src/cli/commands/module-config.test.ts +78 -0
  20. package/src/cli/commands/module-config.ts +18 -3
  21. package/src/cli/commands/module-import.ts +9 -5
  22. package/src/cli/commands/module-publish.ts +24 -0
  23. package/src/cli/commands/module-remove.ts +20 -9
  24. package/src/cli/commands/module-status.ts +15 -0
  25. package/src/cli/commands/module-upgrade.test.ts +37 -0
  26. package/src/cli/commands/module-upgrade.ts +26 -6
  27. package/src/cli/commands/proxmox-node-list.ts +101 -0
  28. package/src/cli/commands/proxmox-template-selection.ts +16 -15
  29. package/src/cli/commands/proxmox-vm-template-build.ts +171 -0
  30. package/src/cli/commands/publish/alpha.test.ts +26 -0
  31. package/src/cli/commands/publish/alpha.ts +23 -0
  32. package/src/cli/commands/publish/types.ts +7 -2
  33. package/src/cli/commands/publish/workspace.ts +11 -1
  34. package/src/cli/commands/restore.ts +29 -0
  35. package/src/cli/commands/service-add-digitalocean.ts +120 -109
  36. package/src/cli/commands/service-add-proxmox.ts +283 -209
  37. package/src/cli/commands/service-reconfigure.test.ts +115 -0
  38. package/src/cli/commands/service-reconfigure.ts +252 -129
  39. package/src/cli/commands/service-remove.ts +19 -13
  40. package/src/cli/commands/service-verify.ts +9 -10
  41. package/src/cli/commands/storage-add-local.ts +120 -107
  42. package/src/cli/commands/storage-add-s3.ts +145 -131
  43. package/src/cli/commands/storage-remove.ts +11 -8
  44. package/src/cli/commands/system-doctor.ts +135 -40
  45. package/src/cli/commands/system-init.ts +119 -128
  46. package/src/cli/commands/system-migrate.test.ts +40 -0
  47. package/src/cli/commands/system-migrate.ts +65 -0
  48. package/src/cli/completion.ts +23 -0
  49. package/src/cli/index.ts +91 -7
  50. package/src/cli/service-credential.ts +54 -0
  51. package/src/config/paths.test.ts +61 -48
  52. package/src/db/client.ts +15 -146
  53. package/src/db/migrate.ts +14 -6
  54. package/src/db/schema-introspection.ts +88 -0
  55. package/src/db/schema.ts +74 -0
  56. package/src/hooks/capability-loader-firewall.test.ts +3 -3
  57. package/src/hooks/capability-loader.ts +43 -2
  58. package/src/hooks/run-named-hook.ts +28 -2
  59. package/src/hooks/types.ts +2 -1
  60. package/src/infrastructure/property-extractor.test.ts +15 -0
  61. package/src/infrastructure/property-extractor.ts +12 -0
  62. package/src/manifest/contracts/v1.ts +16 -0
  63. package/src/manifest/schema.ts +17 -0
  64. package/src/manifest/validate.test.ts +53 -0
  65. package/src/services/bus-interview.test.ts +2 -2
  66. package/src/services/bus-interview.ts +232 -0
  67. package/src/services/bus-secret-flow.test.ts +2 -2
  68. package/src/services/celilo-mgmt-hooks.test.ts +3 -2
  69. package/src/services/deploy-preflight.ts +25 -0
  70. package/src/services/deploy-validation.test.ts +54 -4
  71. package/src/services/deploy-validation.ts +27 -36
  72. package/src/services/dns-internal-records.test.ts +126 -0
  73. package/src/services/dns-internal-records.ts +119 -0
  74. package/src/services/dns-provider-backfill.test.ts +2 -2
  75. package/src/services/dns-provider-backfill.ts +14 -2
  76. package/src/services/dns-registrations.test.ts +120 -0
  77. package/src/services/dns-registrations.ts +108 -0
  78. package/src/services/events-daemon.test.ts +59 -0
  79. package/src/services/events-daemon.ts +191 -57
  80. package/src/services/fleet-checks.test.ts +508 -0
  81. package/src/services/fleet-checks.ts +678 -0
  82. package/src/services/module-build.test.ts +43 -38
  83. package/src/services/module-config.ts +12 -0
  84. package/src/services/module-deploy.ts +7 -6
  85. package/src/services/module-subscriptions.test.ts +88 -0
  86. package/src/services/module-subscriptions.ts +50 -1
  87. package/src/services/module-validator/bundled-deps.test.ts +55 -0
  88. package/src/services/module-validator/bundled-deps.ts +115 -0
  89. package/src/services/module-validator/capability-versions.test.ts +1 -1
  90. package/src/services/placement-reconcile.test.ts +86 -0
  91. package/src/services/placement-reconcile.ts +108 -0
  92. package/src/services/programmatic-responder.ts +34 -0
  93. package/src/services/terminal-responder.ts +113 -0
  94. package/src/templates/generator.test.ts +92 -12
  95. package/src/templates/generator.ts +165 -80
  96. package/src/test-utils/fixtures.test.ts +1 -1
  97. package/src/test-utils/integration-guard.ts +33 -0
  98. package/src/types/infrastructure.ts +6 -0
  99. package/src/variables/computed/computed-integration.test.ts +3 -3
  100. package/src/variables/computed/computed.test.ts +5 -5
  101. package/src/variables/declarative-derivation.test.ts +6 -6
@@ -3,16 +3,16 @@
3
3
  * Configure a Digital Ocean VPS service
4
4
  */
5
5
 
6
- import * as p from '@clack/prompts';
7
6
  import type { NetworkZone } from '../../db/schema';
7
+ import { askMultiselect, askText, withInterviewSession } from '../../services/bus-interview';
8
8
  import {
9
9
  addContainerService,
10
10
  testConnection as testServiceConnection,
11
11
  updateVerificationStatus,
12
12
  } from '../../services/container-service';
13
- import { celiloIntro, celiloOutro, promptPassword, promptText } from '../prompts';
13
+ import { celiloIntro, celiloOutro } from '../prompts';
14
+ import { resolveServiceCredential } from '../service-credential';
14
15
  import type { CommandResult } from '../types';
15
- import { validateRequired } from '../validators';
16
16
 
17
17
  /**
18
18
  * Handle service add digitalocean command
@@ -22,112 +22,123 @@ import { validateRequired } from '../validators';
22
22
  */
23
23
  export async function handleServiceAddDigitalOcean(
24
24
  _args: string[],
25
- _flags: Record<string, boolean | string> = {},
25
+ flags: Record<string, boolean | string> = {},
26
26
  ): Promise<CommandResult> {
27
- try {
28
- celiloIntro('Add Digital Ocean VPS Service');
29
-
30
- // Prompt for service configuration
31
- const name = await promptText({
32
- message: 'Human-readable name:',
33
- defaultValue: 'Digital Ocean VPS',
34
- placeholder: 'Digital Ocean VPS',
35
- validate: validateRequired('Service name'),
36
- });
37
-
38
- // Prompt for zones (multi-select)
39
- const zones = await p.multiselect<NetworkZone>({
40
- message: 'Zones this service can provision to:',
41
- options: [
42
- { value: 'internal' as NetworkZone, label: 'internal' },
43
- { value: 'dmz' as NetworkZone, label: 'dmz' },
44
- { value: 'app' as NetworkZone, label: 'app' },
45
- { value: 'secure' as NetworkZone, label: 'secure' },
46
- { value: 'external' as NetworkZone, label: 'external' },
47
- ],
48
- required: true,
49
- initialValues: ['external' as NetworkZone], // Digital Ocean is typically for external-facing services
50
- });
51
-
52
- if (p.isCancel(zones)) {
53
- p.cancel('Operation cancelled');
54
- return { success: false, error: 'Cancelled by user' };
27
+ // Non-secret prompts route through the bus interview (ISS-0127); the DO API
28
+ // token is a service credential that travels by flag/env only (D7).
29
+ const scope = 'service-add:digitalocean';
30
+
31
+ return withInterviewSession(async () => {
32
+ try {
33
+ celiloIntro('Add Digital Ocean VPS Service');
34
+
35
+ const name = await askText({
36
+ scope,
37
+ key: 'name',
38
+ message: 'Human-readable name',
39
+ defaultValue: 'Digital Ocean VPS',
40
+ placeholder: 'Digital Ocean VPS',
41
+ required: true,
42
+ });
43
+
44
+ const zones = await askMultiselect({
45
+ scope,
46
+ key: 'zones',
47
+ message: 'Zones this service can provision to',
48
+ options: [
49
+ { value: 'internal', label: 'internal' },
50
+ { value: 'dmz', label: 'dmz' },
51
+ { value: 'app', label: 'app' },
52
+ { value: 'secure', label: 'secure' },
53
+ { value: 'external', label: 'external' },
54
+ ],
55
+ required: true,
56
+ });
57
+
58
+ console.log('\nDigital Ocean Configuration');
59
+ console.log('──────────────────────────');
60
+
61
+ // API token is a service credential — flag/env only (D7).
62
+ const apiToken = await resolveServiceCredential({
63
+ field: 'API token (Personal Access Token)',
64
+ flag: 'api-token',
65
+ envVar: 'DIGITALOCEAN_API_TOKEN',
66
+ flagValue: flags['api-token'],
67
+ });
68
+
69
+ const defaultRegion = await askText({
70
+ scope,
71
+ key: 'default_region',
72
+ message: 'Default region',
73
+ defaultValue: 'nyc3',
74
+ placeholder: 'nyc3',
75
+ required: true,
76
+ });
77
+
78
+ const defaultSize = await askText({
79
+ scope,
80
+ key: 'default_size',
81
+ message: 'Default droplet size',
82
+ defaultValue: 's-1vcpu-1gb',
83
+ placeholder: 's-1vcpu-1gb',
84
+ required: true,
85
+ });
86
+
87
+ const defaultImage = await askText({
88
+ scope,
89
+ key: 'default_image',
90
+ message: 'Default image',
91
+ defaultValue: 'ubuntu-22-04-x64',
92
+ placeholder: 'ubuntu-22-04-x64',
93
+ required: true,
94
+ });
95
+
96
+ // Store the service first so we can test it
97
+ const service = await addContainerService({
98
+ name,
99
+ providerName: 'digitalocean',
100
+ zones: zones as unknown as NetworkZone[],
101
+ apiCredentials: {
102
+ api_token: apiToken,
103
+ },
104
+ providerConfig: {
105
+ default_region: defaultRegion,
106
+ default_size: defaultSize,
107
+ default_image: defaultImage,
108
+ },
109
+ });
110
+
111
+ // Test connection
112
+ console.log('\nTesting connection...');
113
+ const testResult = await testServiceConnection(service);
114
+ await updateVerificationStatus(service.id, testResult);
115
+
116
+ if (!testResult.success) {
117
+ console.log(`✗ Connection test failed: ${testResult.message}`);
118
+ console.log(
119
+ '\nService saved but not verified. The service will not be used until verification succeeds.',
120
+ );
121
+
122
+ celiloOutro(
123
+ `Service '${service.serviceId}' (${name}) added but not verified.\n\nNext steps:\n - Fix the connection issue\n - Re-verify: celilo service verify ${service.serviceId}\n - Check status: celilo service list`,
124
+ );
125
+ } else {
126
+ console.log(`✓ ${testResult.message}`);
127
+
128
+ celiloOutro(
129
+ `Service '${service.serviceId}' (${name}) added and verified successfully!\n\nService ID: ${service.serviceId}\n\nNext steps:\n - Generate module: celilo module generate <module-id>\n - List services: celilo service list`,
130
+ );
131
+ }
132
+
133
+ return {
134
+ success: true,
135
+ message: `Added Digital Ocean service: ${service.id}`,
136
+ };
137
+ } catch (error) {
138
+ return {
139
+ success: false,
140
+ error: `Failed to add Digital Ocean service: ${error instanceof Error ? error.message : String(error)}`,
141
+ };
55
142
  }
56
-
57
- console.log('\nDigital Ocean Configuration');
58
- console.log('──────────────────────────');
59
-
60
- const apiToken = await promptPassword({
61
- message: 'API Token (Personal Access Token):',
62
- validate: validateRequired('API token'),
63
- });
64
-
65
- const defaultRegion = await promptText({
66
- message: 'Default region:',
67
- defaultValue: 'nyc3',
68
- placeholder: 'nyc3',
69
- validate: validateRequired('Region'),
70
- });
71
-
72
- const defaultSize = await promptText({
73
- message: 'Default droplet size:',
74
- defaultValue: 's-1vcpu-1gb',
75
- placeholder: 's-1vcpu-1gb',
76
- validate: validateRequired('Droplet size'),
77
- });
78
-
79
- const defaultImage = await promptText({
80
- message: 'Default image:',
81
- defaultValue: 'ubuntu-22-04-x64',
82
- placeholder: 'ubuntu-22-04-x64',
83
- validate: validateRequired('Image'),
84
- });
85
-
86
- // Store the service first so we can test it
87
- const service = await addContainerService({
88
- name,
89
- providerName: 'digitalocean',
90
- zones: zones as NetworkZone[],
91
- apiCredentials: {
92
- api_token: apiToken,
93
- },
94
- providerConfig: {
95
- default_region: defaultRegion,
96
- default_size: defaultSize,
97
- default_image: defaultImage,
98
- },
99
- });
100
-
101
- // Test connection
102
- console.log('\nTesting connection...');
103
- const testResult = await testServiceConnection(service);
104
- await updateVerificationStatus(service.id, testResult);
105
-
106
- if (!testResult.success) {
107
- console.log(`✗ Connection test failed: ${testResult.message}`);
108
- console.log(
109
- '\nService saved but not verified. The service will not be used until verification succeeds.',
110
- );
111
-
112
- celiloOutro(
113
- `Service '${service.serviceId}' (${name}) added but not verified.\n\nNext steps:\n - Fix the connection issue\n - Re-verify: celilo service verify ${service.serviceId}\n - Check status: celilo service list`,
114
- );
115
- } else {
116
- console.log(`✓ ${testResult.message}`);
117
-
118
- celiloOutro(
119
- `Service '${service.serviceId}' (${name}) added and verified successfully!\n\nService ID: ${service.serviceId}\n\nNext steps:\n - Generate module: celilo module generate <module-id>\n - List services: celilo service list`,
120
- );
121
- }
122
-
123
- return {
124
- success: true,
125
- message: `Added Digital Ocean service: ${service.id}`,
126
- };
127
- } catch (error) {
128
- return {
129
- success: false,
130
- error: `Failed to add Digital Ocean service: ${error instanceof Error ? error.message : String(error)}`,
131
- };
132
- }
143
+ });
133
144
  }