@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,282 @@
|
|
|
1
|
+
import { parseVariables } from './parser';
|
|
2
|
+
import type {
|
|
3
|
+
ResolutionContext,
|
|
4
|
+
ResolveResult,
|
|
5
|
+
TemplateResolveResult,
|
|
6
|
+
VariableReference,
|
|
7
|
+
} from './types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get nested value from object using dot notation
|
|
11
|
+
*
|
|
12
|
+
* @param obj - Object to search
|
|
13
|
+
* @param path - Dot-separated path (e.g., 'dns_registrar.primary_domain')
|
|
14
|
+
* @returns Value if found, undefined otherwise
|
|
15
|
+
*/
|
|
16
|
+
function getNestedValue(obj: Record<string, unknown>, path: string): unknown {
|
|
17
|
+
const parts = path.split('.');
|
|
18
|
+
let current: unknown = obj;
|
|
19
|
+
|
|
20
|
+
for (const part of parts) {
|
|
21
|
+
if (current === null || current === undefined) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
if (typeof current !== 'object') {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
current = (current as Record<string, unknown>)[part];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return current;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Resolve a single variable reference
|
|
35
|
+
*
|
|
36
|
+
* Execution function (Rule 10.1) - may perform database access for capability secrets
|
|
37
|
+
*
|
|
38
|
+
* @param variable - Variable reference to resolve
|
|
39
|
+
* @param context - Resolution context with all data sources
|
|
40
|
+
* @param db - Database connection
|
|
41
|
+
* @returns Resolved value or error
|
|
42
|
+
*/
|
|
43
|
+
export async function resolveVariable(
|
|
44
|
+
variable: VariableReference,
|
|
45
|
+
context: ResolutionContext,
|
|
46
|
+
db: ReturnType<typeof import('../db/client').getDb>,
|
|
47
|
+
): Promise<ResolveResult> {
|
|
48
|
+
switch (variable.type) {
|
|
49
|
+
case 'self': {
|
|
50
|
+
const value = context.selfConfig[variable.path];
|
|
51
|
+
if (value === undefined) {
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
variable: variable.raw,
|
|
55
|
+
error: `Self variable '${variable.path}' not found in module configuration`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return { success: true, value };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
case 'system': {
|
|
62
|
+
const value = context.systemConfig[variable.path];
|
|
63
|
+
if (value === undefined) {
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
variable: variable.raw,
|
|
67
|
+
error: `System variable '${variable.path}' not found`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return { success: true, value };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
case 'system_secret': {
|
|
74
|
+
const value = context.systemSecrets[variable.path];
|
|
75
|
+
if (value === undefined) {
|
|
76
|
+
return {
|
|
77
|
+
success: false,
|
|
78
|
+
variable: variable.raw,
|
|
79
|
+
error: `System secret '${variable.path}' not found`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return { success: true, value };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
case 'secret': {
|
|
86
|
+
const value = context.secrets[variable.path];
|
|
87
|
+
if (value === undefined) {
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
variable: variable.raw,
|
|
91
|
+
error: `Secret '${variable.path}' not found for module ${context.moduleId}`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return { success: true, value };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
case 'capability': {
|
|
98
|
+
// Format: capability_name.path.to.value
|
|
99
|
+
const [capabilityName, ...pathParts] = variable.path.split('.');
|
|
100
|
+
|
|
101
|
+
if (!capabilityName) {
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
variable: variable.raw,
|
|
105
|
+
error:
|
|
106
|
+
'Capability variable must specify capability name (e.g., dns_registrar.primary_domain)',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (pathParts.length === 0) {
|
|
111
|
+
return {
|
|
112
|
+
success: false,
|
|
113
|
+
variable: variable.raw,
|
|
114
|
+
error: `Capability variable must specify data path (e.g., ${capabilityName}.nameserver)`,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const fieldPath = pathParts.join('.');
|
|
119
|
+
|
|
120
|
+
// Check if this field is a secret
|
|
121
|
+
const { isCapabilityFieldSecret, checkCapabilitySecretAccess, getCapabilitySecret } =
|
|
122
|
+
await import('../capabilities/secrets');
|
|
123
|
+
|
|
124
|
+
const isSecret = isCapabilityFieldSecret(capabilityName, fieldPath, db.$client);
|
|
125
|
+
|
|
126
|
+
if (isSecret) {
|
|
127
|
+
// This is a secret - validate access and decrypt
|
|
128
|
+
const canAccess = checkCapabilitySecretAccess(
|
|
129
|
+
context.moduleId,
|
|
130
|
+
capabilityName,
|
|
131
|
+
fieldPath,
|
|
132
|
+
db.$client,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
if (!canAccess) {
|
|
136
|
+
return {
|
|
137
|
+
success: false,
|
|
138
|
+
variable: variable.raw,
|
|
139
|
+
error: `Module '${context.moduleId}' does not have permission to access secret '${fieldPath}' from capability '${capabilityName}'`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const secretValue = await getCapabilitySecret(capabilityName, fieldPath, db.$client);
|
|
145
|
+
return { success: true, value: secretValue };
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
variable: variable.raw,
|
|
150
|
+
error: `Failed to retrieve secret '${fieldPath}' from capability '${capabilityName}': ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Not a secret - access capability data normally
|
|
156
|
+
const capabilityData = context.capabilities[capabilityName];
|
|
157
|
+
if (!capabilityData) {
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
variable: variable.raw,
|
|
161
|
+
error: `Capability '${capabilityName}' not found or not registered`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const value = getNestedValue(capabilityData, fieldPath);
|
|
166
|
+
if (value === undefined) {
|
|
167
|
+
return {
|
|
168
|
+
success: false,
|
|
169
|
+
variable: variable.raw,
|
|
170
|
+
error: `Capability data '${fieldPath}' not found in '${capabilityName}'`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Check if value contains unresolved $self: variable (lazy resolution)
|
|
175
|
+
if (typeof value === 'string' && value.startsWith('$self:')) {
|
|
176
|
+
// Get provider module's config to resolve the variable
|
|
177
|
+
const selfVarPath = value.substring(6); // Remove "$self:" prefix
|
|
178
|
+
|
|
179
|
+
// Get provider module ID
|
|
180
|
+
const providerQuery = db.$client.prepare(
|
|
181
|
+
`SELECT p.id FROM modules p
|
|
182
|
+
JOIN capabilities c ON p.id = c.module_id
|
|
183
|
+
WHERE c.capability_name = ?
|
|
184
|
+
LIMIT 1`,
|
|
185
|
+
);
|
|
186
|
+
const providerResult = providerQuery.get(capabilityName) as { id: string } | undefined;
|
|
187
|
+
|
|
188
|
+
if (!providerResult) {
|
|
189
|
+
return {
|
|
190
|
+
success: false,
|
|
191
|
+
variable: variable.raw,
|
|
192
|
+
error: `Provider module not found for capability '${capabilityName}'`,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Get provider module's config value
|
|
197
|
+
const configQuery = db.$client.prepare(
|
|
198
|
+
'SELECT value FROM module_configs WHERE module_id = ? AND key = ?',
|
|
199
|
+
);
|
|
200
|
+
const configResult = configQuery.get(providerResult.id, selfVarPath) as
|
|
201
|
+
| { value: string }
|
|
202
|
+
| undefined;
|
|
203
|
+
|
|
204
|
+
if (!configResult) {
|
|
205
|
+
return {
|
|
206
|
+
success: false,
|
|
207
|
+
variable: variable.raw,
|
|
208
|
+
error: `Provider module '${providerResult.id}' has not configured '${selfVarPath}' (required by capability '${capabilityName}')`,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return { success: true, value: configResult.value };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Convert value to string
|
|
216
|
+
if (typeof value === 'string') {
|
|
217
|
+
return { success: true, value };
|
|
218
|
+
}
|
|
219
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
220
|
+
return { success: true, value: String(value) };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
success: false,
|
|
225
|
+
variable: variable.raw,
|
|
226
|
+
error: `Capability data '${fieldPath}' is not a primitive value (got ${typeof value})`,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
default: {
|
|
231
|
+
return {
|
|
232
|
+
success: false,
|
|
233
|
+
variable: variable.raw,
|
|
234
|
+
error: `Unknown variable type: ${(variable as VariableReference).type}`,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Resolve all variables in a template
|
|
242
|
+
*
|
|
243
|
+
* Orchestration function (Rule 10.1) - coordinates parsing and resolution
|
|
244
|
+
*
|
|
245
|
+
* @param content - Template content with variables
|
|
246
|
+
* @param context - Resolution context
|
|
247
|
+
* @param db - Database connection
|
|
248
|
+
* @returns Resolved template or errors
|
|
249
|
+
*/
|
|
250
|
+
export async function resolveTemplate(
|
|
251
|
+
content: string,
|
|
252
|
+
context: ResolutionContext,
|
|
253
|
+
db: ReturnType<typeof import('../db/client').getDb>,
|
|
254
|
+
): Promise<TemplateResolveResult> {
|
|
255
|
+
// Parse all variables
|
|
256
|
+
const variables = parseVariables(content);
|
|
257
|
+
|
|
258
|
+
if (variables.length === 0) {
|
|
259
|
+
return { success: true, content };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const errors: Array<{ variable: string; error: string }> = [];
|
|
263
|
+
let resolvedContent = content;
|
|
264
|
+
|
|
265
|
+
// Resolve each variable
|
|
266
|
+
for (const variable of variables) {
|
|
267
|
+
const result = await resolveVariable(variable, context, db);
|
|
268
|
+
|
|
269
|
+
if (!result.success) {
|
|
270
|
+
errors.push({ variable: result.variable, error: result.error });
|
|
271
|
+
} else {
|
|
272
|
+
// Replace all occurrences of this variable
|
|
273
|
+
resolvedContent = resolvedContent.replaceAll(variable.raw, result.value);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (errors.length > 0) {
|
|
278
|
+
return { success: false, errors };
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return { success: true, content: resolvedContent };
|
|
282
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Variable types and interfaces for template resolution
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Variable reference parsed from template
|
|
7
|
+
* Example: $self:container_ip -> { type: 'self', path: 'container_ip', raw: '$self:container_ip' }
|
|
8
|
+
*/
|
|
9
|
+
export interface VariableReference {
|
|
10
|
+
type: 'self' | 'system' | 'system_secret' | 'secret' | 'capability';
|
|
11
|
+
path: string;
|
|
12
|
+
raw: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Resolution context - provides data sources for variable resolution
|
|
17
|
+
*/
|
|
18
|
+
export interface ResolutionContext {
|
|
19
|
+
moduleId: string;
|
|
20
|
+
selfConfig: Record<string, string>;
|
|
21
|
+
systemConfig: Record<string, string>;
|
|
22
|
+
systemSecrets: Record<string, string>;
|
|
23
|
+
secrets: Record<string, string>;
|
|
24
|
+
capabilities: Record<string, Record<string, unknown>>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Variable resolution result
|
|
29
|
+
*/
|
|
30
|
+
export interface ResolveSuccess {
|
|
31
|
+
success: true;
|
|
32
|
+
value: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ResolveError {
|
|
36
|
+
success: false;
|
|
37
|
+
variable: string;
|
|
38
|
+
error: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type ResolveResult = ResolveSuccess | ResolveError;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Template resolution result
|
|
45
|
+
*/
|
|
46
|
+
export interface TemplateResolveSuccess {
|
|
47
|
+
success: true;
|
|
48
|
+
content: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface TemplateResolveError {
|
|
52
|
+
success: false;
|
|
53
|
+
errors: Array<{
|
|
54
|
+
variable: string;
|
|
55
|
+
error: string;
|
|
56
|
+
}>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type TemplateResolveResult = TemplateResolveSuccess | TemplateResolveError;
|