@celilo/cli 0.1.4 → 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 +9 -8
- package/src/ansible/inventory.ts +9 -7
- package/src/capabilities/public-web-helpers.test.ts +2 -2
- package/src/capabilities/public-web-publish.test.ts +45 -12
- package/src/capabilities/registration.test.ts +6 -6
- package/src/capabilities/well-known.test.ts +2 -2
- package/src/capabilities/well-known.ts +5 -5
- 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-show.ts +1 -1
- 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.test.ts +3 -3
- package/src/db/schema.ts +26 -17
- package/src/hooks/capability-loader.ts +135 -72
- package/src/hooks/define-hook.test.ts +11 -3
- 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/manifest/template-validator.test.ts +1 -1
- package/src/manifest/template-validator.ts +1 -1
- package/src/manifest/validate.test.ts +1 -1
- package/src/module/import.ts +20 -12
- package/src/module/packaging/build.ts +121 -25
- 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-planner.ts +5 -5
- 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/dns-auto-register.ts +4 -4
- package/src/services/ensure-interview.test.ts +245 -0
- package/src/services/health-runner.ts +110 -38
- package/src/services/infrastructure-variable-resolver.test.ts +1 -1
- package/src/services/infrastructure-variable-resolver.ts +3 -3
- package/src/services/module-build.ts +11 -13
- package/src/services/module-deploy.ts +372 -61
- package/src/services/proxmox-state-recovery.ts +6 -6
- 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 +3 -3
- package/src/templates/generator.ts +43 -2
- 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.test.ts +31 -31
- package/src/variables/context.ts +65 -17
- package/src/variables/declarative-derivation.test.ts +306 -0
- package/src/variables/declarative-derivation.ts +4 -2
- package/src/variables/parser.test.ts +64 -9
- package/src/variables/parser.ts +47 -6
- package/src/variables/resolver.test.ts +14 -14
- package/src/variables/resolver.ts +27 -9
- package/src/variables/types.ts +1 -1
- package/tsconfig.json +1 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- Caddy hostname list refactor (CADDY_HOSTNAME_LIST design).
|
|
2
|
+
-- web_routes used to carry `subdomain` + `custom_domain` and a flat
|
|
3
|
+
-- unique index on `path`. The new model has a single `hostname` FQDN
|
|
4
|
+
-- and a (hostname, path) unique index. Phase 0 / no production users
|
|
5
|
+
-- means this is a destructive migration — modules repopulate routes
|
|
6
|
+
-- on their next deploy after the migration runs.
|
|
7
|
+
DROP TABLE IF EXISTS `web_routes`;
|
|
8
|
+
--> statement-breakpoint
|
|
9
|
+
CREATE TABLE `web_routes` (
|
|
10
|
+
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
11
|
+
`slug` text NOT NULL,
|
|
12
|
+
`module_id` text NOT NULL,
|
|
13
|
+
`type` text NOT NULL,
|
|
14
|
+
`path` text NOT NULL,
|
|
15
|
+
`hostname` text NOT NULL,
|
|
16
|
+
`target_host` text,
|
|
17
|
+
`target_port` integer,
|
|
18
|
+
`websocket` integer DEFAULT false NOT NULL,
|
|
19
|
+
`content_hash` text,
|
|
20
|
+
`created_at` integer DEFAULT (unixepoch()) NOT NULL,
|
|
21
|
+
`updated_at` integer DEFAULT (unixepoch()) NOT NULL,
|
|
22
|
+
FOREIGN KEY (`module_id`) REFERENCES `modules`(`id`) ON UPDATE no action ON DELETE cascade
|
|
23
|
+
);
|
|
24
|
+
--> statement-breakpoint
|
|
25
|
+
CREATE UNIQUE INDEX `web_routes_hostname_path_idx` ON `web_routes` (`hostname`,`path`);
|
|
@@ -22,6 +22,20 @@
|
|
|
22
22
|
"when": 1774881303520,
|
|
23
23
|
"tag": "0002_web_routes",
|
|
24
24
|
"breakpoints": true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"idx": 3,
|
|
28
|
+
"version": "6",
|
|
29
|
+
"when": 1774924800000,
|
|
30
|
+
"tag": "0003_backup_storage",
|
|
31
|
+
"breakpoints": true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"idx": 4,
|
|
35
|
+
"version": "6",
|
|
36
|
+
"when": 1777680000000,
|
|
37
|
+
"tag": "0004_caddy_hostname_list",
|
|
38
|
+
"breakpoints": true
|
|
25
39
|
}
|
|
26
40
|
]
|
|
27
41
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@celilo/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Celilo — home lab orchestration CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -46,10 +46,15 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@aws-sdk/client-s3": "^3.1024.0",
|
|
49
|
+
"@celilo/capabilities": "^0.1.2",
|
|
50
|
+
"@celilo/cli-display": "0.1.0",
|
|
49
51
|
"@clack/prompts": "^1.1.0",
|
|
50
|
-
"@celilo/capabilities": "^0.1.1",
|
|
51
52
|
"ajv": "^8.18.0",
|
|
52
53
|
"drizzle-orm": "^0.36.4",
|
|
54
|
+
"ink": "^7.0.1",
|
|
55
|
+
"ink-spinner": "^5.0.0",
|
|
56
|
+
"ink-text-input": "^6.0.0",
|
|
57
|
+
"react": "^19.2.5",
|
|
53
58
|
"tar": "^7.5.10",
|
|
54
59
|
"xxhash-wasm": "^1.1.0",
|
|
55
60
|
"yaml": "^2.8.2",
|
|
@@ -58,7 +63,9 @@
|
|
|
58
63
|
"devDependencies": {
|
|
59
64
|
"@biomejs/biome": "^1.9.4",
|
|
60
65
|
"@types/bun": "^1.1.14",
|
|
66
|
+
"@types/react": "^19.2.14",
|
|
61
67
|
"drizzle-kit": "^0.30.0",
|
|
68
|
+
"ink-testing-library": "^4.0.0",
|
|
62
69
|
"typescript": "^5.9.3",
|
|
63
70
|
"zod-to-json-schema": "^3.25.2"
|
|
64
71
|
}
|
|
@@ -75,13 +75,14 @@ describe('generateHostsIni', () => {
|
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
test('includes SSH private key file path when provided', () => {
|
|
78
|
+
const keyPath = '/tmp/celilo-ansible-keys/machine-machine-1.key';
|
|
78
79
|
const hosts: InventoryHost[] = [
|
|
79
80
|
{
|
|
80
81
|
hostname: 'machine-host',
|
|
81
82
|
ansibleHost: '192.168.1.100',
|
|
82
83
|
ansibleUser: 'ubuntu',
|
|
83
84
|
groups: ['machines'],
|
|
84
|
-
ansibleSshPrivateKeyFile:
|
|
85
|
+
ansibleSshPrivateKeyFile: keyPath,
|
|
85
86
|
},
|
|
86
87
|
];
|
|
87
88
|
|
|
@@ -89,7 +90,7 @@ describe('generateHostsIni', () => {
|
|
|
89
90
|
|
|
90
91
|
expect(result).toContain('[machines]');
|
|
91
92
|
expect(result).toContain(
|
|
92
|
-
|
|
93
|
+
`machine-host ansible_host=192.168.1.100 ansible_user=ubuntu ansible_ssh_private_key_file=${keyPath}`,
|
|
93
94
|
);
|
|
94
95
|
});
|
|
95
96
|
});
|
|
@@ -99,14 +100,14 @@ describe('generateHostVarsYaml', () => {
|
|
|
99
100
|
const vars = {
|
|
100
101
|
vmid: 2110,
|
|
101
102
|
hostname: 'iot',
|
|
102
|
-
|
|
103
|
+
target_ip: '192.168.0.110/24',
|
|
103
104
|
};
|
|
104
105
|
|
|
105
106
|
const result = generateHostVarsYaml(vars);
|
|
106
107
|
|
|
107
108
|
expect(result).toContain('vmid: 2110');
|
|
108
109
|
expect(result).toContain('hostname: iot');
|
|
109
|
-
expect(result).toContain('
|
|
110
|
+
expect(result).toContain('target_ip: 192.168.0.110/24');
|
|
110
111
|
});
|
|
111
112
|
|
|
112
113
|
test('generates YAML for arrays', () => {
|
|
@@ -289,7 +290,7 @@ describe('Database integration', () => {
|
|
|
289
290
|
{ moduleId: 'homebridge', key: 'hostname', value: 'iot', valueJson: null },
|
|
290
291
|
{
|
|
291
292
|
moduleId: 'homebridge',
|
|
292
|
-
key: '
|
|
293
|
+
key: 'target_ip',
|
|
293
294
|
value: '192.168.0.110/24',
|
|
294
295
|
valueJson: null,
|
|
295
296
|
},
|
|
@@ -301,7 +302,7 @@ describe('Database integration', () => {
|
|
|
301
302
|
|
|
302
303
|
expect(vars.vmid).toBe(2110);
|
|
303
304
|
expect(vars.hostname).toBe('iot');
|
|
304
|
-
expect(vars.
|
|
305
|
+
expect(vars.target_ip).toBe('192.168.0.110/24');
|
|
305
306
|
expect(vars.cores).toBe(1);
|
|
306
307
|
});
|
|
307
308
|
|
|
@@ -404,7 +405,7 @@ describe('Database integration', () => {
|
|
|
404
405
|
db.insert(moduleConfigs)
|
|
405
406
|
.values([
|
|
406
407
|
{ moduleId: 'homebridge', key: 'hostname', value: 'iot' },
|
|
407
|
-
{ moduleId: 'homebridge', key: '
|
|
408
|
+
{ moduleId: 'homebridge', key: 'target_ip', value: '192.168.0.110/24' },
|
|
408
409
|
])
|
|
409
410
|
.run();
|
|
410
411
|
|
|
@@ -446,7 +447,7 @@ describe('Database integration', () => {
|
|
|
446
447
|
db.insert(moduleConfigs)
|
|
447
448
|
.values([
|
|
448
449
|
{ moduleId: 'test', key: 'hostname', value: 'iot' },
|
|
449
|
-
// Missing
|
|
450
|
+
// Missing target_ip or vps_ip for ansible_host
|
|
450
451
|
])
|
|
451
452
|
.run();
|
|
452
453
|
|
package/src/ansible/inventory.ts
CHANGED
|
@@ -66,7 +66,11 @@ export function generateHostsIni(hosts: InventoryHost[]): string {
|
|
|
66
66
|
// Add [all:vars] section with common variables
|
|
67
67
|
lines.push('[all:vars]');
|
|
68
68
|
lines.push('ansible_python_interpreter=/usr/bin/python3');
|
|
69
|
-
|
|
69
|
+
// Disable ControlMaster to avoid Python format string issues with ControlPath
|
|
70
|
+
// ControlPath uses %h, %p, %r placeholders which trigger Python's % formatter
|
|
71
|
+
lines.push(
|
|
72
|
+
'ansible_ssh_common_args=-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ControlMaster=no',
|
|
73
|
+
);
|
|
70
74
|
lines.push('');
|
|
71
75
|
|
|
72
76
|
return lines.join('\n');
|
|
@@ -259,13 +263,11 @@ export function extractInventoryHost(moduleId: string, db: DbClient): InventoryH
|
|
|
259
263
|
}
|
|
260
264
|
|
|
261
265
|
// Auto-derive ansible_host from infrastructure variables
|
|
262
|
-
// Priority:
|
|
263
|
-
if (moduleConfig.
|
|
264
|
-
const slashIndex = moduleConfig.
|
|
266
|
+
// Priority: target_ip > ip.primary > vps_ip (for backward compatibility)
|
|
267
|
+
if (moduleConfig.target_ip) {
|
|
268
|
+
const slashIndex = moduleConfig.target_ip.indexOf('/');
|
|
265
269
|
derived['inventory.ansible_host'] =
|
|
266
|
-
slashIndex === -1
|
|
267
|
-
? moduleConfig.container_ip
|
|
268
|
-
: moduleConfig.container_ip.slice(0, slashIndex);
|
|
270
|
+
slashIndex === -1 ? moduleConfig.target_ip : moduleConfig.target_ip.slice(0, slashIndex);
|
|
269
271
|
} else if (moduleConfig['ip.primary']) {
|
|
270
272
|
derived['inventory.ansible_host'] = moduleConfig['ip.primary'];
|
|
271
273
|
} else if (moduleConfig.vps_ip) {
|
|
@@ -101,11 +101,11 @@ describe('validatePublishStaticSiteRequest', () => {
|
|
|
101
101
|
expect(result.errors).toEqual([]);
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
-
test('accepts a request with optional clientConfig and
|
|
104
|
+
test('accepts a request with optional clientConfig and hostname', () => {
|
|
105
105
|
const result = validatePublishStaticSiteRequest(
|
|
106
106
|
basePublishRequest({
|
|
107
107
|
clientConfig: { FOO: 'bar' },
|
|
108
|
-
|
|
108
|
+
hostname: 'app.iamtheinternet.org',
|
|
109
109
|
}),
|
|
110
110
|
);
|
|
111
111
|
expect(result.valid).toBe(true);
|
|
@@ -53,9 +53,9 @@ function makeRouteOps(): { ops: RouteOps; routes: WebRoute[] } {
|
|
|
53
53
|
moduleId: route.moduleId,
|
|
54
54
|
type: route.type,
|
|
55
55
|
path: route.path,
|
|
56
|
+
hostname: route.hostname,
|
|
56
57
|
targetHost: route.targetHost ?? null,
|
|
57
58
|
targetPort: route.targetPort ?? null,
|
|
58
|
-
subdomain: route.subdomain ?? null,
|
|
59
59
|
websocket: route.websocket ?? false,
|
|
60
60
|
contentHash: route.contentHash ?? null,
|
|
61
61
|
createdAt: existing >= 0 ? routes[existing].createdAt : now,
|
|
@@ -109,13 +109,16 @@ describe('publishStaticSite — clientConfig injection', () => {
|
|
|
109
109
|
moduleId: 'lunacycle',
|
|
110
110
|
logger: noopLogger,
|
|
111
111
|
config: {
|
|
112
|
-
|
|
112
|
+
target_ip: '10.0.10.20/24',
|
|
113
113
|
hostname: 'www',
|
|
114
114
|
primary_domain: 'example.com',
|
|
115
115
|
email: 'admin@example.com',
|
|
116
116
|
},
|
|
117
117
|
secrets: {},
|
|
118
118
|
routeOps: ops,
|
|
119
|
+
hostnames: ['www.example.com'],
|
|
120
|
+
caddyModuleId: 'caddy',
|
|
121
|
+
dnsManagedDomains: ['www.example.com'],
|
|
119
122
|
});
|
|
120
123
|
|
|
121
124
|
const result = await cap.publishStaticSite({
|
|
@@ -150,13 +153,16 @@ describe('publishStaticSite — clientConfig injection', () => {
|
|
|
150
153
|
moduleId: 'lunacycle',
|
|
151
154
|
logger: noopLogger,
|
|
152
155
|
config: {
|
|
153
|
-
|
|
156
|
+
target_ip: '10.0.10.20/24',
|
|
154
157
|
hostname: 'www',
|
|
155
158
|
primary_domain: 'example.com',
|
|
156
159
|
email: 'admin@example.com',
|
|
157
160
|
},
|
|
158
161
|
secrets: {},
|
|
159
162
|
routeOps: ops,
|
|
163
|
+
hostnames: ['www.example.com'],
|
|
164
|
+
caddyModuleId: 'caddy',
|
|
165
|
+
dnsManagedDomains: ['www.example.com'],
|
|
160
166
|
});
|
|
161
167
|
|
|
162
168
|
await cap.publishStaticSite({
|
|
@@ -174,13 +180,16 @@ describe('publishStaticSite — clientConfig injection', () => {
|
|
|
174
180
|
moduleId: 'lunacycle',
|
|
175
181
|
logger: noopLogger,
|
|
176
182
|
config: {
|
|
177
|
-
|
|
183
|
+
target_ip: '10.0.10.20/24',
|
|
178
184
|
hostname: 'www',
|
|
179
185
|
primary_domain: 'example.com',
|
|
180
186
|
email: 'admin@example.com',
|
|
181
187
|
},
|
|
182
188
|
secrets: {},
|
|
183
189
|
routeOps: ops,
|
|
190
|
+
hostnames: ['www.example.com'],
|
|
191
|
+
caddyModuleId: 'caddy',
|
|
192
|
+
dnsManagedDomains: ['www.example.com'],
|
|
184
193
|
});
|
|
185
194
|
|
|
186
195
|
await cap.publishStaticSite({
|
|
@@ -201,13 +210,16 @@ describe('publishStaticSite — clientConfig injection', () => {
|
|
|
201
210
|
moduleId: 'lunacycle',
|
|
202
211
|
logger: noopLogger,
|
|
203
212
|
config: {
|
|
204
|
-
|
|
213
|
+
target_ip: '10.0.10.20/24',
|
|
205
214
|
hostname: 'www',
|
|
206
215
|
primary_domain: 'example.com',
|
|
207
216
|
email: 'admin@example.com',
|
|
208
217
|
},
|
|
209
218
|
secrets: {},
|
|
210
219
|
routeOps: ops,
|
|
220
|
+
hostnames: ['www.example.com'],
|
|
221
|
+
caddyModuleId: 'caddy',
|
|
222
|
+
dnsManagedDomains: ['www.example.com'],
|
|
211
223
|
});
|
|
212
224
|
|
|
213
225
|
await expect(
|
|
@@ -225,13 +237,16 @@ describe('publishStaticSite — clientConfig injection', () => {
|
|
|
225
237
|
moduleId: 'lunacycle',
|
|
226
238
|
logger: noopLogger,
|
|
227
239
|
config: {
|
|
228
|
-
|
|
240
|
+
target_ip: '10.0.10.20/24',
|
|
229
241
|
hostname: 'www',
|
|
230
242
|
primary_domain: 'example.com',
|
|
231
243
|
email: 'admin@example.com',
|
|
232
244
|
},
|
|
233
245
|
secrets: {},
|
|
234
246
|
routeOps: ops,
|
|
247
|
+
hostnames: ['www.example.com'],
|
|
248
|
+
caddyModuleId: 'caddy',
|
|
249
|
+
dnsManagedDomains: ['www.example.com'],
|
|
235
250
|
});
|
|
236
251
|
|
|
237
252
|
await expect(
|
|
@@ -261,13 +276,16 @@ describe('registerReverseProxy', () => {
|
|
|
261
276
|
moduleId: 'lunacycle',
|
|
262
277
|
logger: noopLogger,
|
|
263
278
|
config: {
|
|
264
|
-
|
|
279
|
+
target_ip: '10.0.10.20/24',
|
|
265
280
|
hostname: 'www',
|
|
266
281
|
primary_domain: 'example.com',
|
|
267
282
|
email: 'admin@example.com',
|
|
268
283
|
},
|
|
269
284
|
secrets: {},
|
|
270
285
|
routeOps: ops,
|
|
286
|
+
hostnames: ['www.example.com'],
|
|
287
|
+
caddyModuleId: 'caddy',
|
|
288
|
+
dnsManagedDomains: ['www.example.com'],
|
|
271
289
|
});
|
|
272
290
|
|
|
273
291
|
const result = await cap.registerReverseProxy({
|
|
@@ -294,13 +312,16 @@ describe('registerReverseProxy', () => {
|
|
|
294
312
|
moduleId: 'lunacycle',
|
|
295
313
|
logger: noopLogger,
|
|
296
314
|
config: {
|
|
297
|
-
|
|
315
|
+
target_ip: '10.0.10.20/24',
|
|
298
316
|
hostname: 'www',
|
|
299
317
|
primary_domain: 'example.com',
|
|
300
318
|
email: 'admin@example.com',
|
|
301
319
|
},
|
|
302
320
|
secrets: {},
|
|
303
321
|
routeOps: ops,
|
|
322
|
+
hostnames: ['www.example.com'],
|
|
323
|
+
caddyModuleId: 'caddy',
|
|
324
|
+
dnsManagedDomains: ['www.example.com'],
|
|
304
325
|
});
|
|
305
326
|
|
|
306
327
|
await expect(
|
|
@@ -337,13 +358,16 @@ describe('auto-logging — end-to-end through createPublicWeb', () => {
|
|
|
337
358
|
moduleId: 'lunacycle',
|
|
338
359
|
logger,
|
|
339
360
|
config: {
|
|
340
|
-
|
|
361
|
+
target_ip: '10.0.10.20/24',
|
|
341
362
|
hostname: 'www',
|
|
342
363
|
primary_domain: 'example.com',
|
|
343
364
|
email: 'admin@example.com',
|
|
344
365
|
},
|
|
345
366
|
secrets: {},
|
|
346
367
|
routeOps: ops,
|
|
368
|
+
hostnames: ['www.example.com'],
|
|
369
|
+
caddyModuleId: 'caddy',
|
|
370
|
+
dnsManagedDomains: ['www.example.com'],
|
|
347
371
|
});
|
|
348
372
|
|
|
349
373
|
await cap.register_route({
|
|
@@ -368,13 +392,16 @@ describe('auto-logging — end-to-end through createPublicWeb', () => {
|
|
|
368
392
|
moduleId: 'lunacycle',
|
|
369
393
|
logger,
|
|
370
394
|
config: {
|
|
371
|
-
|
|
395
|
+
target_ip: '10.0.10.20/24',
|
|
372
396
|
hostname: 'www',
|
|
373
397
|
primary_domain: 'example.com',
|
|
374
398
|
email: 'admin@example.com',
|
|
375
399
|
},
|
|
376
400
|
secrets: {},
|
|
377
401
|
routeOps: ops,
|
|
402
|
+
hostnames: ['www.example.com'],
|
|
403
|
+
caddyModuleId: 'caddy',
|
|
404
|
+
dnsManagedDomains: ['www.example.com'],
|
|
378
405
|
});
|
|
379
406
|
|
|
380
407
|
// Bad path triggers the validator inside register_route, which
|
|
@@ -400,13 +427,16 @@ describe('auto-logging — end-to-end through createPublicWeb', () => {
|
|
|
400
427
|
moduleId: 'lunacycle',
|
|
401
428
|
logger,
|
|
402
429
|
config: {
|
|
403
|
-
|
|
430
|
+
target_ip: '10.0.10.20/24',
|
|
404
431
|
hostname: 'www',
|
|
405
432
|
primary_domain: 'example.com',
|
|
406
433
|
email: 'admin@example.com',
|
|
407
434
|
},
|
|
408
435
|
secrets: {},
|
|
409
436
|
routeOps: ops,
|
|
437
|
+
hostnames: ['www.example.com'],
|
|
438
|
+
caddyModuleId: 'caddy',
|
|
439
|
+
dnsManagedDomains: ['www.example.com'],
|
|
410
440
|
});
|
|
411
441
|
|
|
412
442
|
await cap.publishStaticSite({ path: '/lunacycle', sourceDir });
|
|
@@ -432,13 +462,16 @@ describe('auto-logging — end-to-end through createPublicWeb', () => {
|
|
|
432
462
|
moduleId: 'lunacycle',
|
|
433
463
|
logger,
|
|
434
464
|
config: {
|
|
435
|
-
|
|
465
|
+
target_ip: '10.0.10.20/24',
|
|
436
466
|
hostname: 'www',
|
|
437
467
|
primary_domain: 'example.com',
|
|
438
468
|
email: 'admin@example.com',
|
|
439
469
|
},
|
|
440
470
|
secrets: {},
|
|
441
471
|
routeOps: ops,
|
|
472
|
+
hostnames: ['www.example.com'],
|
|
473
|
+
caddyModuleId: 'caddy',
|
|
474
|
+
dnsManagedDomains: ['www.example.com'],
|
|
442
475
|
});
|
|
443
476
|
|
|
444
477
|
await cap.register_route({
|
|
@@ -43,7 +43,7 @@ describe('Capability Registration', () => {
|
|
|
43
43
|
version: '1.0.0',
|
|
44
44
|
data: {
|
|
45
45
|
server: {
|
|
46
|
-
ip: '$self:
|
|
46
|
+
ip: '$self:target_ip',
|
|
47
47
|
port: 443,
|
|
48
48
|
},
|
|
49
49
|
},
|
|
@@ -65,7 +65,7 @@ describe('Capability Registration', () => {
|
|
|
65
65
|
// Variables are preserved, not resolved
|
|
66
66
|
expect(result).toEqual({
|
|
67
67
|
server: {
|
|
68
|
-
ip: '$self:
|
|
68
|
+
ip: '$self:target_ip',
|
|
69
69
|
port: 443,
|
|
70
70
|
},
|
|
71
71
|
});
|
|
@@ -177,7 +177,7 @@ describe('Capability Registration', () => {
|
|
|
177
177
|
name: 'test_capability',
|
|
178
178
|
version: '1.0.0',
|
|
179
179
|
data: {
|
|
180
|
-
variable: '$self:
|
|
180
|
+
variable: '$self:target_ip',
|
|
181
181
|
literal: 'not-a-variable',
|
|
182
182
|
with_dollar: '$100',
|
|
183
183
|
empty: '',
|
|
@@ -198,7 +198,7 @@ describe('Capability Registration', () => {
|
|
|
198
198
|
const result = buildCapabilityData(capability, manifest);
|
|
199
199
|
|
|
200
200
|
expect(result).toEqual({
|
|
201
|
-
variable: '$self:
|
|
201
|
+
variable: '$self:target_ip',
|
|
202
202
|
literal: 'not-a-variable',
|
|
203
203
|
with_dollar: '$100',
|
|
204
204
|
empty: '',
|
|
@@ -337,7 +337,7 @@ describe('Capability Registration', () => {
|
|
|
337
337
|
version: '1.0.0',
|
|
338
338
|
data: {
|
|
339
339
|
server: {
|
|
340
|
-
ip: '$self:
|
|
340
|
+
ip: '$self:target_ip',
|
|
341
341
|
},
|
|
342
342
|
},
|
|
343
343
|
};
|
|
@@ -360,7 +360,7 @@ describe('Capability Registration', () => {
|
|
|
360
360
|
(result as any).server.ip = 'modified';
|
|
361
361
|
|
|
362
362
|
// Original should be unchanged
|
|
363
|
-
expect(capability.data.server.ip).toBe('$self:
|
|
363
|
+
expect(capability.data.server.ip).toBe('$self:target_ip');
|
|
364
364
|
});
|
|
365
365
|
});
|
|
366
366
|
|
|
@@ -170,7 +170,7 @@ describe('Well-Known Capabilities Registry', () => {
|
|
|
170
170
|
expect(schema).toEqual({
|
|
171
171
|
server: {
|
|
172
172
|
ip: {
|
|
173
|
-
primary: '$self:
|
|
173
|
+
primary: '$self:target_ip',
|
|
174
174
|
},
|
|
175
175
|
port: 443,
|
|
176
176
|
},
|
|
@@ -196,7 +196,7 @@ describe('Well-Known Capabilities Registry', () => {
|
|
|
196
196
|
expect(schema).toEqual({
|
|
197
197
|
server: {
|
|
198
198
|
ip: {
|
|
199
|
-
primary: '$self:
|
|
199
|
+
primary: '$self:target_ip',
|
|
200
200
|
},
|
|
201
201
|
port: 9000,
|
|
202
202
|
},
|
|
@@ -41,7 +41,7 @@ export const WELL_KNOWN_CAPABILITIES: Record<string, WellKnownCapability> = {
|
|
|
41
41
|
data_schema: {
|
|
42
42
|
server: {
|
|
43
43
|
ip: {
|
|
44
|
-
primary: '$self:
|
|
44
|
+
primary: '$self:target_ip',
|
|
45
45
|
},
|
|
46
46
|
port: 443,
|
|
47
47
|
},
|
|
@@ -76,7 +76,7 @@ export const WELL_KNOWN_CAPABILITIES: Record<string, WellKnownCapability> = {
|
|
|
76
76
|
data_schema: {
|
|
77
77
|
server: {
|
|
78
78
|
ip: {
|
|
79
|
-
primary: '$self:
|
|
79
|
+
primary: '$self:target_ip',
|
|
80
80
|
},
|
|
81
81
|
port: 53,
|
|
82
82
|
},
|
|
@@ -102,7 +102,7 @@ export const WELL_KNOWN_CAPABILITIES: Record<string, WellKnownCapability> = {
|
|
|
102
102
|
data_schema: {
|
|
103
103
|
server: {
|
|
104
104
|
ip: {
|
|
105
|
-
primary: '$self:
|
|
105
|
+
primary: '$self:target_ip',
|
|
106
106
|
},
|
|
107
107
|
port: 9000,
|
|
108
108
|
},
|
|
@@ -121,12 +121,12 @@ export const WELL_KNOWN_CAPABILITIES: Record<string, WellKnownCapability> = {
|
|
|
121
121
|
data_schema: {
|
|
122
122
|
server: {
|
|
123
123
|
ip: {
|
|
124
|
-
primary: '$self:
|
|
124
|
+
primary: '$self:target_ip',
|
|
125
125
|
},
|
|
126
126
|
port: '$self:port', // Database-specific port
|
|
127
127
|
},
|
|
128
128
|
connection: {
|
|
129
|
-
host: '$self:
|
|
129
|
+
host: '$self:target_ip',
|
|
130
130
|
port: '$self:port',
|
|
131
131
|
name: '$self:database_name',
|
|
132
132
|
},
|
package/src/cli/cli.test.ts
CHANGED
|
@@ -574,12 +574,12 @@ secrets:
|
|
|
574
574
|
});
|
|
575
575
|
|
|
576
576
|
describe('module import', () => {
|
|
577
|
-
test('should error on missing
|
|
577
|
+
test('should error on missing subcommand', async () => {
|
|
578
578
|
const result = await runCli(['node', 'celilo', 'module', 'import']);
|
|
579
579
|
|
|
580
580
|
expect(result.success).toBe(false);
|
|
581
581
|
if (result.success) return;
|
|
582
|
-
expect(result.error).toContain('
|
|
582
|
+
expect(result.error).toContain('Subcommand required');
|
|
583
583
|
});
|
|
584
584
|
|
|
585
585
|
test('should error on non-existent path', async () => {
|