@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,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module types command
|
|
3
|
+
*
|
|
4
|
+
* Generates TypeScript `<module>/celilo/types.d.ts` files from a
|
|
5
|
+
* module's `variables.owns` / `variables.imports` declarations in
|
|
6
|
+
* manifest.yml. The generated file exposes a `<ModuleName>Config`
|
|
7
|
+
* interface that hook scripts import via `defineHook<ConfigT, ...>`.
|
|
8
|
+
*
|
|
9
|
+
* See `design/TECHNICAL_DESIGN_HOOK_API_V2.md` D2 for the design rationale.
|
|
10
|
+
*
|
|
11
|
+
* Subcommands:
|
|
12
|
+
* celilo module types generate <module-dir> — write types.d.ts
|
|
13
|
+
* celilo module types check <module-dir> — CI drift check
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { existsSync } from 'node:fs';
|
|
17
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
18
|
+
import { dirname, join, resolve } from 'node:path';
|
|
19
|
+
import type { ModuleManifest } from '../../manifest/schema';
|
|
20
|
+
import { validateManifest } from '../../manifest/validate';
|
|
21
|
+
import { generateModuleTypes } from '../../services/module-types-generator';
|
|
22
|
+
import { getArg, validateRequiredArgs } from '../parser';
|
|
23
|
+
import type { CommandResult } from '../types';
|
|
24
|
+
|
|
25
|
+
const TYPES_FILE_RELATIVE_PATH = join('celilo', 'types.d.ts');
|
|
26
|
+
|
|
27
|
+
type LoadManifestResult =
|
|
28
|
+
| { ok: true; manifest: ModuleManifest }
|
|
29
|
+
| { ok: false; failure: CommandResult };
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Load and validate a module's manifest.yml from a directory path.
|
|
33
|
+
*
|
|
34
|
+
* Returns a discriminated result: either the parsed manifest on success,
|
|
35
|
+
* or a CommandResult describing the failure that callers can return
|
|
36
|
+
* directly.
|
|
37
|
+
*/
|
|
38
|
+
async function loadManifest(modulePath: string): Promise<LoadManifestResult> {
|
|
39
|
+
const absolute = resolve(modulePath);
|
|
40
|
+
if (!existsSync(absolute)) {
|
|
41
|
+
return {
|
|
42
|
+
ok: false,
|
|
43
|
+
failure: {
|
|
44
|
+
success: false,
|
|
45
|
+
error: `Module directory does not exist: ${modulePath}`,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const manifestPath = join(absolute, 'manifest.yml');
|
|
51
|
+
if (!existsSync(manifestPath)) {
|
|
52
|
+
return {
|
|
53
|
+
ok: false,
|
|
54
|
+
failure: {
|
|
55
|
+
success: false,
|
|
56
|
+
error: `No manifest.yml found in ${modulePath}`,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const yamlContent = await readFile(manifestPath, 'utf-8');
|
|
62
|
+
const result = validateManifest(yamlContent);
|
|
63
|
+
if (!result.success) {
|
|
64
|
+
const errorMessages = result.errors.map((e) => ` ${e.path}: ${e.message}`).join('\n');
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
failure: {
|
|
68
|
+
success: false,
|
|
69
|
+
error: `Manifest validation failed:\n${errorMessages}`,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { ok: true, manifest: result.data };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Handle `celilo module types generate <module-dir>`.
|
|
79
|
+
*
|
|
80
|
+
* Reads the manifest, generates the types file content, writes it to
|
|
81
|
+
* `<module-dir>/celilo/types.d.ts`, creating the `celilo/`
|
|
82
|
+
* subdirectory if needed.
|
|
83
|
+
*/
|
|
84
|
+
export async function handleModuleTypesGenerate(args: string[]): Promise<CommandResult> {
|
|
85
|
+
const err = validateRequiredArgs(args, 1);
|
|
86
|
+
if (err) {
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
error: `${err}\n\nUsage: celilo module types generate <module-dir>`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const modulePath = getArg(args, 0);
|
|
94
|
+
if (!modulePath) {
|
|
95
|
+
return {
|
|
96
|
+
success: false,
|
|
97
|
+
error: 'Module directory is required\n\nUsage: celilo module types generate <module-dir>',
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const loaded = await loadManifest(modulePath);
|
|
102
|
+
if (!loaded.ok) return loaded.failure;
|
|
103
|
+
|
|
104
|
+
const content = generateModuleTypes(loaded.manifest);
|
|
105
|
+
const outputPath = resolve(modulePath, TYPES_FILE_RELATIVE_PATH);
|
|
106
|
+
|
|
107
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
108
|
+
await writeFile(outputPath, content, 'utf-8');
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
success: true,
|
|
112
|
+
message: `Generated ${outputPath}`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Handle `celilo module types check <module-dir>`.
|
|
118
|
+
*
|
|
119
|
+
* Regenerates the expected types file content in-memory and compares it
|
|
120
|
+
* byte-for-byte to the committed file. Fails if they differ or if the
|
|
121
|
+
* committed file is missing. Used as a CI drift check.
|
|
122
|
+
*/
|
|
123
|
+
export async function handleModuleTypesCheck(args: string[]): Promise<CommandResult> {
|
|
124
|
+
const err = validateRequiredArgs(args, 1);
|
|
125
|
+
if (err) {
|
|
126
|
+
return {
|
|
127
|
+
success: false,
|
|
128
|
+
error: `${err}\n\nUsage: celilo module types check <module-dir>`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const modulePath = getArg(args, 0);
|
|
133
|
+
if (!modulePath) {
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
error: 'Module directory is required\n\nUsage: celilo module types check <module-dir>',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const loaded = await loadManifest(modulePath);
|
|
141
|
+
if (!loaded.ok) return loaded.failure;
|
|
142
|
+
|
|
143
|
+
const expected = generateModuleTypes(loaded.manifest);
|
|
144
|
+
const outputPath = resolve(modulePath, TYPES_FILE_RELATIVE_PATH);
|
|
145
|
+
|
|
146
|
+
if (!existsSync(outputPath)) {
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
error: `Types file missing: ${outputPath}\n\nRun 'celilo module types generate ${modulePath}' to create it.`,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const actual = await readFile(outputPath, 'utf-8');
|
|
154
|
+
if (actual !== expected) {
|
|
155
|
+
return {
|
|
156
|
+
success: false,
|
|
157
|
+
error: `Types file is stale: ${outputPath}\n\nIt does not match what would be generated from the current manifest.yml.\nRun 'celilo module types generate ${modulePath}' to regenerate.`,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
success: true,
|
|
163
|
+
message: `Types file is in sync: ${outputPath}`,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Silent generator used as the belt-and-suspenders step in
|
|
169
|
+
* `celilo module import`. Writes types to the imported module's
|
|
170
|
+
* directory and swallows errors with a warning — failing to regenerate
|
|
171
|
+
* types should not abort an import.
|
|
172
|
+
*
|
|
173
|
+
* Returns a string describing what was done (for logging), or `null` if
|
|
174
|
+
* the operation was skipped or failed silently.
|
|
175
|
+
*/
|
|
176
|
+
export async function generateTypesForImportedModule(modulePath: string): Promise<string | null> {
|
|
177
|
+
try {
|
|
178
|
+
const loaded = await loadManifest(modulePath);
|
|
179
|
+
if (!loaded.ok) return null;
|
|
180
|
+
|
|
181
|
+
const content = generateModuleTypes(loaded.manifest);
|
|
182
|
+
const outputPath = resolve(modulePath, TYPES_FILE_RELATIVE_PATH);
|
|
183
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
184
|
+
await writeFile(outputPath, content, 'utf-8');
|
|
185
|
+
return outputPath;
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module update command
|
|
3
|
+
*
|
|
4
|
+
* Updates module code (manifest, scripts, templates) while preserving state
|
|
5
|
+
* (configs, secrets, infrastructure, capabilities).
|
|
6
|
+
*
|
|
7
|
+
* Usage: celilo module update <path>
|
|
8
|
+
*
|
|
9
|
+
* The module ID is read from the manifest at the given path.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { cpSync, existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
13
|
+
import { join, resolve } from 'node:path';
|
|
14
|
+
import { eq } from 'drizzle-orm';
|
|
15
|
+
import { parse as parseYaml } from 'yaml';
|
|
16
|
+
import { registerModuleCapabilities } from '../../capabilities/registration';
|
|
17
|
+
import { getDb } from '../../db/client';
|
|
18
|
+
import { capabilities, modules } from '../../db/schema';
|
|
19
|
+
import { ModuleManifestSchema } from '../../manifest/schema';
|
|
20
|
+
import type { ModuleManifest } from '../../manifest/schema';
|
|
21
|
+
import { cleanupTempDir, extractPackage } from '../../module/packaging/extract';
|
|
22
|
+
import { log } from '../prompts';
|
|
23
|
+
import type { CommandResult } from '../types';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Upgrade a single module from a source path
|
|
27
|
+
*/
|
|
28
|
+
async function upgradeOne(
|
|
29
|
+
sourcePath: string,
|
|
30
|
+
db: ReturnType<typeof getDb>,
|
|
31
|
+
flags: Record<string, string | boolean> = {},
|
|
32
|
+
): Promise<{ moduleId: string; success: boolean; error?: string }> {
|
|
33
|
+
const originalCwd = process.env.CELILO_ORIGINAL_CWD || process.cwd();
|
|
34
|
+
const importPath = resolve(originalCwd, sourcePath);
|
|
35
|
+
if (!existsSync(importPath)) {
|
|
36
|
+
return { moduleId: sourcePath, success: false, error: `Source path not found: ${importPath}` };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Handle .netapp packages: extract to temp dir
|
|
40
|
+
let actualPath = importPath;
|
|
41
|
+
let tempDir: string | null = null;
|
|
42
|
+
|
|
43
|
+
if (importPath.endsWith('.netapp')) {
|
|
44
|
+
const extractResult = await extractPackage(importPath);
|
|
45
|
+
if (!extractResult.success || !extractResult.tempDir) {
|
|
46
|
+
return {
|
|
47
|
+
moduleId: sourcePath,
|
|
48
|
+
success: false,
|
|
49
|
+
error: extractResult.error || 'Failed to extract package',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
tempDir = extractResult.tempDir;
|
|
53
|
+
actualPath = tempDir;
|
|
54
|
+
|
|
55
|
+
// Skip signature verification if --skip-verify
|
|
56
|
+
if (flags['skip-verify'] !== true) {
|
|
57
|
+
const { verifyPackageIntegrity } = await import('../../module/packaging/extract');
|
|
58
|
+
const verifyResult = await verifyPackageIntegrity(tempDir);
|
|
59
|
+
if (!verifyResult.success) {
|
|
60
|
+
await cleanupTempDir(tempDir);
|
|
61
|
+
return {
|
|
62
|
+
moduleId: sourcePath,
|
|
63
|
+
success: false,
|
|
64
|
+
error: verifyResult.error || 'Package verification failed',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
log.warn('Skipping package signature verification (--skip-verify)');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const manifestPath = join(actualPath, 'manifest.yml');
|
|
73
|
+
if (!existsSync(manifestPath)) {
|
|
74
|
+
if (tempDir) await cleanupTempDir(tempDir);
|
|
75
|
+
return {
|
|
76
|
+
moduleId: sourcePath,
|
|
77
|
+
success: false,
|
|
78
|
+
error: `No manifest.yml found at ${actualPath}`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let newManifest: ModuleManifest;
|
|
83
|
+
try {
|
|
84
|
+
const raw = readFileSync(manifestPath, 'utf-8');
|
|
85
|
+
const parsed = parseYaml(raw);
|
|
86
|
+
newManifest = ModuleManifestSchema.parse(parsed);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
if (tempDir) await cleanupTempDir(tempDir);
|
|
89
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
90
|
+
return { moduleId: sourcePath, success: false, error: `Invalid manifest: ${msg}` };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const moduleId = newManifest.id;
|
|
94
|
+
|
|
95
|
+
const module = db.select().from(modules).where(eq(modules.id, moduleId)).get();
|
|
96
|
+
if (!module) {
|
|
97
|
+
return {
|
|
98
|
+
moduleId,
|
|
99
|
+
success: false,
|
|
100
|
+
error: `Module '${moduleId}' is not installed. Use 'celilo module import ${sourcePath}' first.`,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const oldManifest = module.manifestData as ModuleManifest;
|
|
105
|
+
log.info(`Upgrading ${moduleId}: v${oldManifest.version} → v${newManifest.version}`);
|
|
106
|
+
|
|
107
|
+
// Copy new module files, preserving generated output and state
|
|
108
|
+
const installedPath = module.sourcePath;
|
|
109
|
+
const preserveDirs = new Set(['generated', 'screenshots', 'cookies.json']);
|
|
110
|
+
|
|
111
|
+
const skipDirs = new Set(['.git', 'node_modules', '.next', '.cache']);
|
|
112
|
+
const entries = readdirSync(actualPath);
|
|
113
|
+
for (const entry of entries) {
|
|
114
|
+
if (preserveDirs.has(entry) || skipDirs.has(entry)) continue;
|
|
115
|
+
const src = join(actualPath, entry);
|
|
116
|
+
const dest = join(installedPath, entry);
|
|
117
|
+
cpSync(src, dest, { recursive: true, force: true });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Clean up temp dir if we extracted a .netapp
|
|
121
|
+
if (tempDir) await cleanupTempDir(tempDir);
|
|
122
|
+
|
|
123
|
+
// Update manifest in database
|
|
124
|
+
db.update(modules)
|
|
125
|
+
.set({
|
|
126
|
+
manifestData: newManifest as unknown as Record<string, unknown>,
|
|
127
|
+
version: newManifest.version,
|
|
128
|
+
name: newManifest.name,
|
|
129
|
+
})
|
|
130
|
+
.where(eq(modules.id, moduleId))
|
|
131
|
+
.run();
|
|
132
|
+
|
|
133
|
+
// Re-register capabilities
|
|
134
|
+
db.delete(capabilities).where(eq(capabilities.moduleId, moduleId)).run();
|
|
135
|
+
|
|
136
|
+
if (newManifest.provides?.capabilities && newManifest.provides.capabilities.length > 0) {
|
|
137
|
+
const regResult = await registerModuleCapabilities(moduleId, newManifest, db.$client);
|
|
138
|
+
if (!regResult.success) {
|
|
139
|
+
log.warn(` ${moduleId}: capability re-registration warning: ${regResult.error}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
log.success(`Upgraded ${moduleId} (v${oldManifest.version} → v${newManifest.version})`);
|
|
144
|
+
return { moduleId, success: true };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Handle module upgrade command
|
|
149
|
+
*
|
|
150
|
+
* @param args - Command arguments: [path, path, ...]
|
|
151
|
+
* @returns Command result
|
|
152
|
+
*/
|
|
153
|
+
export async function handleModuleUpgrade(
|
|
154
|
+
args: string[],
|
|
155
|
+
flags: Record<string, string | boolean> = {},
|
|
156
|
+
): Promise<CommandResult> {
|
|
157
|
+
if (args.length === 0) {
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
error: 'At least one module path is required\n\nUsage: celilo module update <path> [path...]',
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const db = getDb();
|
|
165
|
+
const results: Array<{ moduleId: string; success: boolean; error?: string }> = [];
|
|
166
|
+
|
|
167
|
+
for (const path of args) {
|
|
168
|
+
const result = await upgradeOne(path, db, flags);
|
|
169
|
+
results.push(result);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const succeeded = results.filter((r) => r.success);
|
|
173
|
+
const failed = results.filter((r) => !r.success);
|
|
174
|
+
|
|
175
|
+
if (failed.length > 0) {
|
|
176
|
+
const errors = failed.map((r) => ` ${r.moduleId}: ${r.error}`).join('\n');
|
|
177
|
+
if (succeeded.length > 0) {
|
|
178
|
+
const names = succeeded.map((r) => r.moduleId).join(', ');
|
|
179
|
+
return {
|
|
180
|
+
success: false,
|
|
181
|
+
error: `Upgraded ${succeeded.length} module(s): ${names}\n\nFailed ${failed.length}:\n${errors}`,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return { success: false, error: `Upgrade failed:\n${errors}` };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const names = succeeded.map((r) => r.moduleId).join(', ');
|
|
188
|
+
return {
|
|
189
|
+
success: true,
|
|
190
|
+
message: `Successfully updated ${succeeded.length} module(s): ${names}`,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package command
|
|
3
|
+
*
|
|
4
|
+
* Creates distributable .netapp packages from module source directories
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
import { buildModule } from '../../module/packaging/build';
|
|
9
|
+
import { getArg, getFlag, validateRequiredArgs } from '../parser';
|
|
10
|
+
import type { CommandResult } from '../types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Handle package command
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* celilo package <source-directory> [--output <path>]
|
|
17
|
+
*
|
|
18
|
+
* @param args - Command arguments
|
|
19
|
+
* @param flags - Command flags
|
|
20
|
+
* @returns Command result
|
|
21
|
+
*/
|
|
22
|
+
export async function handlePackage(
|
|
23
|
+
args: string[],
|
|
24
|
+
flags: Record<string, string | boolean>,
|
|
25
|
+
): Promise<CommandResult> {
|
|
26
|
+
// Validate arguments
|
|
27
|
+
const error = validateRequiredArgs(args, 1);
|
|
28
|
+
if (error) {
|
|
29
|
+
return {
|
|
30
|
+
success: false,
|
|
31
|
+
error: `${error}\n\nUsage:\n celilo package <source-directory> [--output <path>]`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const sourceDir = getArg(args, 0);
|
|
36
|
+
if (!sourceDir) {
|
|
37
|
+
return {
|
|
38
|
+
success: false,
|
|
39
|
+
error: 'Source directory is required',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Resolve paths relative to the user's original cwd, not the backend directory
|
|
44
|
+
const originalCwd = process.env.CELILO_ORIGINAL_CWD || process.cwd();
|
|
45
|
+
const resolvedSourceDir = resolve(originalCwd, sourceDir);
|
|
46
|
+
const outputPath = getFlag(flags, 'output');
|
|
47
|
+
const resolvedOutputPath = outputPath ? resolve(originalCwd, outputPath as string) : undefined;
|
|
48
|
+
|
|
49
|
+
const result = await buildModule({
|
|
50
|
+
sourceDir: resolvedSourceDir,
|
|
51
|
+
outputPath: resolvedOutputPath,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (!result.success) {
|
|
55
|
+
return {
|
|
56
|
+
success: false,
|
|
57
|
+
error: result.error || 'Package creation failed',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
success: true,
|
|
63
|
+
message: `Successfully created module package: ${result.packagePath}`,
|
|
64
|
+
data: {
|
|
65
|
+
packagePath: result.packagePath,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret list command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { eq } from 'drizzle-orm';
|
|
6
|
+
import { getDb } from '../../db/client';
|
|
7
|
+
import { modules, secrets } from '../../db/schema';
|
|
8
|
+
import { getArg, validateRequiredArgs } from '../parser';
|
|
9
|
+
import type { CommandResult } from '../types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Handle secret list command
|
|
13
|
+
*
|
|
14
|
+
* Usage: celilo secret list <module-id>
|
|
15
|
+
*
|
|
16
|
+
* Lists all secrets declared in the module manifest and shows which ones are configured.
|
|
17
|
+
*
|
|
18
|
+
* @param args - Command arguments
|
|
19
|
+
* @returns Command result
|
|
20
|
+
*/
|
|
21
|
+
export async function handleSecretList(args: string[]): Promise<CommandResult> {
|
|
22
|
+
// Validate arguments
|
|
23
|
+
const error = validateRequiredArgs(args, 1);
|
|
24
|
+
if (error) {
|
|
25
|
+
return {
|
|
26
|
+
success: false,
|
|
27
|
+
error: `${error}\n\nUsage: celilo secret list <module-id>`,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const moduleId = getArg(args, 0);
|
|
32
|
+
if (!moduleId) {
|
|
33
|
+
return {
|
|
34
|
+
success: false,
|
|
35
|
+
error: 'Module ID is required',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const db = getDb();
|
|
40
|
+
|
|
41
|
+
// Check if module exists
|
|
42
|
+
const module = db.select().from(modules).where(eq(modules.id, moduleId)).get();
|
|
43
|
+
if (!module) {
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
error: `Module not found: ${moduleId}`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Get declared secrets from manifest
|
|
51
|
+
const manifest = module.manifestData as Record<string, unknown>;
|
|
52
|
+
const secretsSection = manifest.secrets as
|
|
53
|
+
| {
|
|
54
|
+
declares?: Array<{ name: string; type?: string; required?: boolean; description?: string }>;
|
|
55
|
+
}
|
|
56
|
+
| undefined;
|
|
57
|
+
const declaredSecrets = secretsSection?.declares || [];
|
|
58
|
+
|
|
59
|
+
if (declaredSecrets.length === 0) {
|
|
60
|
+
return {
|
|
61
|
+
success: true,
|
|
62
|
+
message: `Module '${moduleId}' declares no secrets`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Get configured secrets from database
|
|
67
|
+
const configuredSecrets = db
|
|
68
|
+
.select({ name: secrets.name })
|
|
69
|
+
.from(secrets)
|
|
70
|
+
.where(eq(secrets.moduleId, moduleId))
|
|
71
|
+
.all();
|
|
72
|
+
|
|
73
|
+
const configuredNames = new Set(configuredSecrets.map((s) => s.name));
|
|
74
|
+
|
|
75
|
+
// Build output
|
|
76
|
+
const lines: string[] = [`\nSecrets for module '${moduleId}':\n`];
|
|
77
|
+
|
|
78
|
+
for (const secret of declaredSecrets) {
|
|
79
|
+
const isConfigured = configuredNames.has(secret.name);
|
|
80
|
+
const status = isConfigured ? '✓' : '✗';
|
|
81
|
+
const statusText = isConfigured ? 'configured' : 'not configured';
|
|
82
|
+
const requiredText = secret.required ? ' (required)' : '';
|
|
83
|
+
|
|
84
|
+
lines.push(` ${status} ${secret.name} - ${statusText}${requiredText}`);
|
|
85
|
+
if (secret.description) {
|
|
86
|
+
lines.push(` ${secret.description}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Summary
|
|
91
|
+
const configuredCount = declaredSecrets.filter((s) => configuredNames.has(s.name)).length;
|
|
92
|
+
const totalCount = declaredSecrets.length;
|
|
93
|
+
lines.push(`\nConfigured: ${configuredCount}/${totalCount}`);
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
message: lines.join('\n'),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret set command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { and, eq } from 'drizzle-orm';
|
|
6
|
+
import { getDb } from '../../db/client';
|
|
7
|
+
import { modules, secrets } from '../../db/schema';
|
|
8
|
+
import { encryptSecret } from '../../secrets/encryption';
|
|
9
|
+
import { getOrCreateMasterKey } from '../../secrets/master-key';
|
|
10
|
+
import { getArg, validateRequiredArgs } from '../parser';
|
|
11
|
+
import type { CommandResult } from '../types';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Handle secret set command
|
|
15
|
+
*
|
|
16
|
+
* Usage: celilo secret set <module-id> <name> <value>
|
|
17
|
+
*
|
|
18
|
+
* @param args - Command arguments
|
|
19
|
+
* @returns Command result
|
|
20
|
+
*/
|
|
21
|
+
export async function handleSecretSet(args: string[]): Promise<CommandResult> {
|
|
22
|
+
// Validate arguments
|
|
23
|
+
const error = validateRequiredArgs(args, 3);
|
|
24
|
+
if (error) {
|
|
25
|
+
return {
|
|
26
|
+
success: false,
|
|
27
|
+
error: `${error}\n\nUsage: celilo secret set <module-id> <name> <value>`,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const moduleId = getArg(args, 0);
|
|
32
|
+
const name = getArg(args, 1);
|
|
33
|
+
const value = getArg(args, 2);
|
|
34
|
+
|
|
35
|
+
if (!moduleId || !name || !value) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
error: 'Module ID, secret name, and value are required',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const db = getDb();
|
|
43
|
+
|
|
44
|
+
// Check if module exists
|
|
45
|
+
const module = db.select().from(modules).where(eq(modules.id, moduleId)).get();
|
|
46
|
+
if (!module) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
error: `Module not found: ${moduleId}`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Validate secret name against manifest
|
|
54
|
+
const manifest = module.manifestData as Record<string, unknown>;
|
|
55
|
+
const secretsSection = manifest.secrets as
|
|
56
|
+
| { declares?: Array<{ name: string; type?: string; required?: boolean }> }
|
|
57
|
+
| undefined;
|
|
58
|
+
const declaredSecrets = secretsSection?.declares || [];
|
|
59
|
+
|
|
60
|
+
// Check if secret is declared in manifest
|
|
61
|
+
const declaredSecret = declaredSecrets.find((s) => s.name === name);
|
|
62
|
+
if (!declaredSecret) {
|
|
63
|
+
const validSecrets = declaredSecrets.map((s) => s.name).join(', ');
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
error: `Invalid secret '${name}' for module ${moduleId}.\n\nValid secrets: ${validSecrets || '(none declared)'}`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Get or create master key
|
|
71
|
+
let masterKey: Buffer;
|
|
72
|
+
try {
|
|
73
|
+
masterKey = await getOrCreateMasterKey();
|
|
74
|
+
} catch (err) {
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
error: 'Failed to access master key',
|
|
78
|
+
details: err,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Encrypt secret
|
|
83
|
+
let encrypted: { encryptedValue: string; iv: string; authTag: string };
|
|
84
|
+
try {
|
|
85
|
+
encrypted = encryptSecret(value, masterKey);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
error: 'Failed to encrypt secret',
|
|
90
|
+
details: err,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check if secret already exists
|
|
95
|
+
const existingSecret = db
|
|
96
|
+
.select()
|
|
97
|
+
.from(secrets)
|
|
98
|
+
.where(and(eq(secrets.moduleId, moduleId), eq(secrets.name, name)))
|
|
99
|
+
.get();
|
|
100
|
+
|
|
101
|
+
if (existingSecret) {
|
|
102
|
+
// Update existing secret
|
|
103
|
+
db.update(secrets)
|
|
104
|
+
.set({
|
|
105
|
+
encryptedValue: encrypted.encryptedValue,
|
|
106
|
+
iv: encrypted.iv,
|
|
107
|
+
authTag: encrypted.authTag,
|
|
108
|
+
updatedAt: new Date(),
|
|
109
|
+
})
|
|
110
|
+
.where(eq(secrets.id, existingSecret.id))
|
|
111
|
+
.run();
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
success: true,
|
|
115
|
+
message: `Updated secret for ${moduleId}: ${name}`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Insert new secret
|
|
120
|
+
db.insert(secrets)
|
|
121
|
+
.values({
|
|
122
|
+
moduleId,
|
|
123
|
+
name,
|
|
124
|
+
encryptedValue: encrypted.encryptedValue,
|
|
125
|
+
iv: encrypted.iv,
|
|
126
|
+
authTag: encrypted.authTag,
|
|
127
|
+
})
|
|
128
|
+
.run();
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
success: true,
|
|
132
|
+
message: `Set secret for ${moduleId}: ${name}`,
|
|
133
|
+
};
|
|
134
|
+
}
|