@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.
Files changed (267) hide show
  1. package/README.md +1566 -0
  2. package/bin/celilo +16 -0
  3. package/drizzle/0000_complex_puma.sql +179 -0
  4. package/drizzle/0001_dizzy_wolfpack.sql +2 -0
  5. package/drizzle/0002_web_routes.sql +16 -0
  6. package/drizzle/0003_backup_storage.sql +32 -0
  7. package/drizzle/meta/0000_snapshot.json +1151 -0
  8. package/drizzle/meta/0001_snapshot.json +1167 -0
  9. package/drizzle/meta/0002_snapshot.json +1257 -0
  10. package/drizzle/meta/_journal.json +27 -0
  11. package/package.json +64 -0
  12. package/schemas/system_config.json +106 -0
  13. package/src/__integration__/container-services-cli.integration.test.ts +246 -0
  14. package/src/ansible/dependencies.test.ts +309 -0
  15. package/src/ansible/dependencies.ts +896 -0
  16. package/src/ansible/inventory.test.ts +463 -0
  17. package/src/ansible/inventory.ts +445 -0
  18. package/src/ansible/secrets.ts +222 -0
  19. package/src/ansible/validation.test.ts +92 -0
  20. package/src/ansible/validation.ts +272 -0
  21. package/src/api-clients/digitalocean.ts +94 -0
  22. package/src/api-clients/proxmox.ts +655 -0
  23. package/src/capabilities/logging-wrapper.test.ts +217 -0
  24. package/src/capabilities/lookup.test.ts +149 -0
  25. package/src/capabilities/lookup.ts +89 -0
  26. package/src/capabilities/public-web-helpers.test.ts +198 -0
  27. package/src/capabilities/public-web-publish.test.ts +458 -0
  28. package/src/capabilities/registration.test.ts +395 -0
  29. package/src/capabilities/registration.ts +200 -0
  30. package/src/capabilities/route-validation.test.ts +121 -0
  31. package/src/capabilities/route-validation.ts +96 -0
  32. package/src/capabilities/secret-ref.test.ts +313 -0
  33. package/src/capabilities/secret-validation.ts +157 -0
  34. package/src/capabilities/secrets.test.ts +750 -0
  35. package/src/capabilities/secrets.ts +244 -0
  36. package/src/capabilities/validation.test.ts +613 -0
  37. package/src/capabilities/validation.ts +160 -0
  38. package/src/capabilities/well-known.test.ts +238 -0
  39. package/src/capabilities/well-known.ts +222 -0
  40. package/src/cli/cli.test.ts +654 -0
  41. package/src/cli/command-registry.ts +742 -0
  42. package/src/cli/command-tree-parser.test.ts +180 -0
  43. package/src/cli/command-tree-parser.ts +193 -0
  44. package/src/cli/commands/backup-create.ts +137 -0
  45. package/src/cli/commands/backup-delete.ts +74 -0
  46. package/src/cli/commands/backup-import.ts +97 -0
  47. package/src/cli/commands/backup-list.ts +132 -0
  48. package/src/cli/commands/backup-name.ts +73 -0
  49. package/src/cli/commands/backup-prune.ts +98 -0
  50. package/src/cli/commands/backup-restore.ts +122 -0
  51. package/src/cli/commands/capability-info.ts +121 -0
  52. package/src/cli/commands/capability-list.ts +47 -0
  53. package/src/cli/commands/completion.ts +87 -0
  54. package/src/cli/commands/hook-run.ts +176 -0
  55. package/src/cli/commands/ipam.ts +607 -0
  56. package/src/cli/commands/machine-add.ts +235 -0
  57. package/src/cli/commands/machine-earmark.ts +82 -0
  58. package/src/cli/commands/machine-list.ts +77 -0
  59. package/src/cli/commands/machine-remove.ts +90 -0
  60. package/src/cli/commands/machine-status.ts +131 -0
  61. package/src/cli/commands/module-audit.ts +51 -0
  62. package/src/cli/commands/module-build.ts +60 -0
  63. package/src/cli/commands/module-config.ts +170 -0
  64. package/src/cli/commands/module-deploy.ts +71 -0
  65. package/src/cli/commands/module-generate.ts +236 -0
  66. package/src/cli/commands/module-health.ts +108 -0
  67. package/src/cli/commands/module-import.ts +80 -0
  68. package/src/cli/commands/module-list.ts +43 -0
  69. package/src/cli/commands/module-logs.ts +73 -0
  70. package/src/cli/commands/module-remove.ts +162 -0
  71. package/src/cli/commands/module-show.ts +208 -0
  72. package/src/cli/commands/module-status.ts +131 -0
  73. package/src/cli/commands/module-types.ts +189 -0
  74. package/src/cli/commands/module-upgrade.ts +192 -0
  75. package/src/cli/commands/package.ts +68 -0
  76. package/src/cli/commands/secret-list.ts +99 -0
  77. package/src/cli/commands/secret-set.ts +134 -0
  78. package/src/cli/commands/service-add-digitalocean.ts +133 -0
  79. package/src/cli/commands/service-add-proxmox.ts +342 -0
  80. package/src/cli/commands/service-config-get.ts +83 -0
  81. package/src/cli/commands/service-config-set.ts +145 -0
  82. package/src/cli/commands/service-list.ts +74 -0
  83. package/src/cli/commands/service-reconfigure.ts +230 -0
  84. package/src/cli/commands/service-remove.ts +103 -0
  85. package/src/cli/commands/service-verify.ts +240 -0
  86. package/src/cli/commands/status.ts +216 -0
  87. package/src/cli/commands/storage-add-local.ts +106 -0
  88. package/src/cli/commands/storage-add-s3.ts +114 -0
  89. package/src/cli/commands/storage-list.ts +72 -0
  90. package/src/cli/commands/storage-remove.ts +54 -0
  91. package/src/cli/commands/storage-set-default.ts +44 -0
  92. package/src/cli/commands/storage-verify.ts +54 -0
  93. package/src/cli/commands/system-config.ts +168 -0
  94. package/src/cli/commands/system-init.ts +314 -0
  95. package/src/cli/commands/system-secret-get.ts +98 -0
  96. package/src/cli/commands/system-secret-set.ts +76 -0
  97. package/src/cli/commands/system-vault-password.ts +34 -0
  98. package/src/cli/completion.test.ts +37 -0
  99. package/src/cli/completion.ts +482 -0
  100. package/src/cli/fuel-gauge.test.ts +208 -0
  101. package/src/cli/fuel-gauge.ts +405 -0
  102. package/src/cli/generate-zsh-completion.test.ts +95 -0
  103. package/src/cli/generate-zsh-completion.ts +497 -0
  104. package/src/cli/index.ts +1583 -0
  105. package/src/cli/interactive-config.test.ts +201 -0
  106. package/src/cli/interactive-config.ts +62 -0
  107. package/src/cli/parser.test.ts +227 -0
  108. package/src/cli/parser.ts +244 -0
  109. package/src/cli/prompts.test.ts +33 -0
  110. package/src/cli/prompts.ts +121 -0
  111. package/src/cli/types.ts +38 -0
  112. package/src/cli/validators.test.ts +235 -0
  113. package/src/cli/validators.ts +188 -0
  114. package/src/config/env.ts +41 -0
  115. package/src/config/paths.test.ts +172 -0
  116. package/src/config/paths.ts +108 -0
  117. package/src/db/client.ts +190 -0
  118. package/src/db/migrate.ts +30 -0
  119. package/src/db/schema.test.ts +221 -0
  120. package/src/db/schema.ts +434 -0
  121. package/src/hooks/capability-loader-firewall.test.ts +246 -0
  122. package/src/hooks/capability-loader.test.ts +100 -0
  123. package/src/hooks/capability-loader.ts +520 -0
  124. package/src/hooks/define-hook.test.ts +488 -0
  125. package/src/hooks/executor.test.ts +462 -0
  126. package/src/hooks/executor.ts +469 -0
  127. package/src/hooks/logger.test.ts +54 -0
  128. package/src/hooks/logger.ts +95 -0
  129. package/src/hooks/test-fixtures/failing-hook.ts +13 -0
  130. package/src/hooks/test-fixtures/no-default-hook.ts +6 -0
  131. package/src/hooks/test-fixtures/success-hook.ts +20 -0
  132. package/src/hooks/test-fixtures/unbranded-hook.ts +11 -0
  133. package/src/hooks/test-fixtures/void-hook.ts +13 -0
  134. package/src/hooks/types.ts +89 -0
  135. package/src/infrastructure/property-extractor.test.ts +194 -0
  136. package/src/infrastructure/property-extractor.ts +151 -0
  137. package/src/ipam/allocator.test.ts +442 -0
  138. package/src/ipam/allocator.ts +369 -0
  139. package/src/ipam/auto-allocator.test.ts +247 -0
  140. package/src/ipam/auto-allocator.ts +270 -0
  141. package/src/ipam/subnet-parser.test.ts +107 -0
  142. package/src/ipam/subnet-parser.ts +136 -0
  143. package/src/manifest/contracts/index.ts +61 -0
  144. package/src/manifest/contracts/v1.ts +118 -0
  145. package/src/manifest/json-schema-roundtrip.test.ts +99 -0
  146. package/src/manifest/schema.ts +367 -0
  147. package/src/manifest/template-validator.test.ts +231 -0
  148. package/src/manifest/template-validator.ts +322 -0
  149. package/src/manifest/validate.test.ts +1180 -0
  150. package/src/manifest/validate.ts +415 -0
  151. package/src/module/import.test.ts +355 -0
  152. package/src/module/import.ts +676 -0
  153. package/src/module/packaging/audit.ts +169 -0
  154. package/src/module/packaging/build.ts +228 -0
  155. package/src/module/packaging/checksum.ts +41 -0
  156. package/src/module/packaging/extract.ts +234 -0
  157. package/src/module/packaging/signature.ts +47 -0
  158. package/src/secrets/encryption.test.ts +284 -0
  159. package/src/secrets/encryption.ts +162 -0
  160. package/src/secrets/generators.test.ts +112 -0
  161. package/src/secrets/generators.ts +127 -0
  162. package/src/secrets/master-key.test.ts +159 -0
  163. package/src/secrets/master-key.ts +114 -0
  164. package/src/secrets/storage.test.ts +115 -0
  165. package/src/secrets/storage.ts +106 -0
  166. package/src/secrets/vault.test.ts +35 -0
  167. package/src/secrets/vault.ts +42 -0
  168. package/src/services/backup-create.ts +532 -0
  169. package/src/services/backup-metadata.ts +198 -0
  170. package/src/services/backup-restore.ts +229 -0
  171. package/src/services/backup-retention.ts +84 -0
  172. package/src/services/backup-storage.ts +281 -0
  173. package/src/services/build-stream.test.ts +122 -0
  174. package/src/services/build-stream.ts +201 -0
  175. package/src/services/config-interview.ts +694 -0
  176. package/src/services/container-service.test.ts +298 -0
  177. package/src/services/container-service.ts +401 -0
  178. package/src/services/cross-module-data-manager.test.ts +405 -0
  179. package/src/services/cross-module-data-manager.ts +412 -0
  180. package/src/services/deploy-ansible.ts +88 -0
  181. package/src/services/deploy-planner.ts +153 -0
  182. package/src/services/deploy-preflight.ts +274 -0
  183. package/src/services/deploy-ssh.ts +131 -0
  184. package/src/services/deploy-terraform.test.ts +55 -0
  185. package/src/services/deploy-terraform.ts +445 -0
  186. package/src/services/deploy-validation.ts +311 -0
  187. package/src/services/dns-auto-register.ts +211 -0
  188. package/src/services/health-runner.ts +184 -0
  189. package/src/services/infrastructure-selector.test.ts +485 -0
  190. package/src/services/infrastructure-selector.ts +245 -0
  191. package/src/services/infrastructure-variable-resolver.test.ts +751 -0
  192. package/src/services/infrastructure-variable-resolver.ts +234 -0
  193. package/src/services/machine-detector.ts +328 -0
  194. package/src/services/machine-pool.test.ts +405 -0
  195. package/src/services/machine-pool.ts +316 -0
  196. package/src/services/manifest-validation.ts +120 -0
  197. package/src/services/module-build.test.ts +290 -0
  198. package/src/services/module-build.ts +431 -0
  199. package/src/services/module-config.test.ts +237 -0
  200. package/src/services/module-config.ts +298 -0
  201. package/src/services/module-deploy.ts +862 -0
  202. package/src/services/module-types-drift.test.ts +73 -0
  203. package/src/services/module-types-generator.test.ts +288 -0
  204. package/src/services/module-types-generator.ts +189 -0
  205. package/src/services/proxmox-state-recovery.ts +140 -0
  206. package/src/services/schema-validation.ts +155 -0
  207. package/src/services/secret-schema-loader.test.ts +311 -0
  208. package/src/services/secret-schema-loader.ts +239 -0
  209. package/src/services/ssh-key-manager.test.ts +283 -0
  210. package/src/services/ssh-key-manager.ts +193 -0
  211. package/src/services/storage-providers/local.ts +105 -0
  212. package/src/services/storage-providers/s3.ts +182 -0
  213. package/src/services/storage-providers/types.ts +24 -0
  214. package/src/services/system-config-schema-types.ts +25 -0
  215. package/src/services/system-config-validator.test.ts +160 -0
  216. package/src/services/system-config-validator.ts +74 -0
  217. package/src/services/system-init.test.ts +153 -0
  218. package/src/services/system-init.ts +253 -0
  219. package/src/services/terraform-safety.ts +174 -0
  220. package/src/services/zone-detector.test.ts +110 -0
  221. package/src/services/zone-detector.ts +102 -0
  222. package/src/services/zone-policy.test.ts +97 -0
  223. package/src/services/zone-policy.ts +126 -0
  224. package/src/templates/generator.test.ts +645 -0
  225. package/src/templates/generator.ts +1119 -0
  226. package/src/templates/types.ts +62 -0
  227. package/src/test-utils/INTERACTIVE_PROMPTS.md +167 -0
  228. package/src/test-utils/cli-context-interactive.test.ts +152 -0
  229. package/src/test-utils/cli-context-server.test.ts +66 -0
  230. package/src/test-utils/cli-context.test.ts +273 -0
  231. package/src/test-utils/cli-context.ts +677 -0
  232. package/src/test-utils/cli-result.test.ts +282 -0
  233. package/src/test-utils/cli-result.ts +241 -0
  234. package/src/test-utils/cli.ts +55 -0
  235. package/src/test-utils/completion-harness.test.ts +126 -0
  236. package/src/test-utils/completion-harness.ts +82 -0
  237. package/src/test-utils/database.test.ts +182 -0
  238. package/src/test-utils/database.ts +126 -0
  239. package/src/test-utils/filesystem.test.ts +208 -0
  240. package/src/test-utils/filesystem.ts +142 -0
  241. package/src/test-utils/fixtures.test.ts +123 -0
  242. package/src/test-utils/fixtures.ts +160 -0
  243. package/src/test-utils/golden-diff.ts +197 -0
  244. package/src/test-utils/index.ts +77 -0
  245. package/src/test-utils/integration.ts +81 -0
  246. package/src/test-utils/module-fixtures.ts +468 -0
  247. package/src/test-utils/modules.test.ts +144 -0
  248. package/src/test-utils/modules.ts +183 -0
  249. package/src/test-utils/setup-test-db.ts +90 -0
  250. package/src/test-utils/value-extractor.test.ts +231 -0
  251. package/src/test-utils/value-extractor.ts +228 -0
  252. package/src/types/infrastructure.ts +157 -0
  253. package/src/utils/shell.test.ts +365 -0
  254. package/src/utils/shell.ts +159 -0
  255. package/src/validation/schemas.ts +166 -0
  256. package/src/variables/ansible-resolver.test.ts +142 -0
  257. package/src/variables/ansible-resolver.ts +69 -0
  258. package/src/variables/capability-self-ref.test.ts +220 -0
  259. package/src/variables/context.test.ts +1265 -0
  260. package/src/variables/context.ts +624 -0
  261. package/src/variables/declarative-derivation.test.ts +743 -0
  262. package/src/variables/declarative-derivation.ts +200 -0
  263. package/src/variables/parser.test.ts +231 -0
  264. package/src/variables/parser.ts +76 -0
  265. package/src/variables/resolver.test.ts +458 -0
  266. package/src/variables/resolver.ts +282 -0
  267. 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;