okengine 0.2.8 → 0.3.4
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/AGENTS.md +17 -15
- package/README.md +59 -31
- package/package.json +11 -16
- package/site/content/docs/ai/llms-txt.mdx +54 -0
- package/site/content/docs/ai/mcp.mdx +123 -0
- package/site/content/docs/ai/meta.json +5 -0
- package/site/content/docs/ai/skills.mdx +53 -0
- package/site/content/docs/console/access.mdx +29 -0
- package/site/content/docs/console/ai.mdx +35 -0
- package/site/content/docs/console/architecture.mdx +35 -0
- package/site/content/docs/console/channels.mdx +37 -0
- package/site/content/docs/console/clock.mdx +31 -0
- package/site/content/docs/console/flows.mdx +31 -0
- package/site/content/docs/console/gates.mdx +35 -0
- package/site/content/docs/console/manifest-diff.mdx +34 -0
- package/site/content/docs/console/meta.json +23 -0
- package/site/content/docs/console/overview.mdx +40 -0
- package/site/content/docs/console/plugins.mdx +41 -0
- package/site/content/docs/console/privacy.mdx +32 -0
- package/site/content/docs/console/runs.mdx +40 -0
- package/site/content/docs/console/signals.mdx +31 -0
- package/site/content/docs/console/store.mdx +32 -0
- package/site/content/docs/console/tenancy.mdx +32 -0
- package/site/content/docs/console/traces.mdx +34 -0
- package/site/content/docs/console/vault.mdx +37 -0
- package/site/content/docs/elements/ai.mdx +180 -0
- package/site/content/docs/elements/channel.mdx +167 -0
- package/site/content/docs/elements/clock.mdx +182 -0
- package/site/content/docs/elements/flow.mdx +288 -0
- package/site/content/docs/elements/gate.mdx +171 -0
- package/site/content/docs/elements/meta.json +5 -0
- package/site/content/docs/elements/signal.mdx +171 -0
- package/site/content/docs/elements/store.mdx +320 -0
- package/site/content/docs/elements/vault.mdx +263 -0
- package/site/content/docs/get-started/basic-usage.mdx +124 -0
- package/site/content/docs/get-started/comparison.mdx +65 -0
- package/site/content/docs/get-started/installation.mdx +113 -0
- package/site/content/docs/get-started/introduction.mdx +123 -0
- package/site/content/docs/get-started/meta.json +5 -0
- package/site/content/docs/index.mdx +63 -0
- package/site/content/docs/meta.json +5 -0
- package/site/content/docs/plugins/compression.mdx +60 -0
- package/site/content/docs/plugins/cors.mdx +98 -0
- package/site/content/docs/plugins/csrf.mdx +96 -0
- package/site/content/docs/plugins/ip-allowlist.mdx +93 -0
- package/site/content/docs/plugins/maintenance-mode.mdx +101 -0
- package/site/content/docs/plugins/meta.json +15 -0
- package/site/content/docs/plugins/security-headers.mdx +136 -0
- package/site/content/docs/reference/cli.md +101 -0
- package/site/content/docs/reference/configuration.mdx +159 -0
- package/site/content/docs/reference/environment-variables.mdx +87 -0
- package/site/content/docs/reference/errors.mdx +80 -0
- package/site/content/docs/reference/fx.mdx +117 -0
- package/site/content/docs/reference/meta.json +5 -0
- package/site/content/docs/reference/plugins.mdx +249 -0
- package/site/content/docs/reference/security.md +63 -0
- package/src/auth/auth.test.ts +3 -0
- package/src/cli/ask-dev-mode.ts +1 -1
- package/src/cli/db.ts +87 -17
- package/src/cli/dev-db-push.test.ts +32 -2
- package/src/cli/dev-schema-sync.test.ts +66 -0
- package/src/cli/dev-schema-sync.ts +139 -0
- package/src/cli/dev.test.ts +123 -1
- package/src/cli/dev.ts +150 -8
- package/src/cli/doc-staleness.test.ts +4 -4
- package/src/cli/docker-cli.test.ts +20 -0
- package/src/cli/docker.ts +10 -0
- package/src/cli/drizzle-env.test.ts +67 -0
- package/src/cli/drizzle-env.ts +78 -0
- package/src/cli/ensure-drizzle-config.ts +50 -0
- package/src/cli/hero-meta.test.ts +1 -1
- package/src/cli/load-config.ts +6 -0
- package/src/cli/mode.ts +24 -4
- package/src/cli/openbao-bootstrap.test.ts +149 -0
- package/src/cli/openbao-bootstrap.ts +314 -0
- package/src/cli/openbao-restart.integration.test.ts +136 -0
- package/src/cli/ports.test.ts +7 -5
- package/src/cli/ports.ts +6 -2
- package/src/cli/resolve-dev-sql-env.test.ts +48 -0
- package/src/cli/resolve-dev-sql-env.ts +42 -0
- package/src/cli/stack.ts +7 -4
- package/src/cli/vault-cmd.ts +63 -0
- package/src/client/types.ts +7 -1
- package/src/compiler/extract.test.ts +40 -0
- package/src/compiler/extract.ts +123 -1
- package/src/compiler/fixtures/skyport/oke.config.ts +2 -2
- package/src/compiler/fixtures/skyport.expected.json +1 -1
- package/src/compiler/response.ts +12 -0
- package/src/config/define-config.test.ts +4 -6
- package/src/config/index.ts +2 -15
- package/src/console/server/app.ts +2 -0
- package/src/console/server/vault.ts +21 -6
- package/src/docker/compose.ts +112 -16
- package/src/docker/derive.ts +7 -1
- package/src/docker/docker.test.ts +103 -0
- package/src/docker/index.ts +11 -1
- package/src/docker/recipes/index.ts +3 -2
- package/src/docker/recipes/openbao.ts +47 -0
- package/src/docker/recipes/redis.ts +5 -1
- package/src/docker/recipes/rustfs.ts +2 -3
- package/src/docker/stack-id.test.ts +43 -8
- package/src/docker/stack-id.ts +99 -20
- package/src/docker/stack.ts +36 -4
- package/src/docker/types.ts +3 -0
- package/src/docs-origin.ts +4 -4
- package/src/drivers/drizzle-dialect.test.ts +20 -0
- package/src/drivers/drizzle-dialect.ts +37 -0
- package/src/drivers/index.ts +1 -2
- package/src/drivers/memory.ts +278 -39
- package/src/drivers/s3.ts +10 -1
- package/src/drivers/vault-driver-removal.test.ts +55 -0
- package/src/drivers/vault-openbao.test.ts +97 -0
- package/src/drivers/vault-openbao.ts +102 -35
- package/src/drivers/vault-types.ts +3 -10
- package/src/elements/store/declare.ts +4 -1
- package/src/elements/store/resource-list-docs.fixture.ts +56 -0
- package/src/elements/store/resource-list-docs.test.ts +79 -0
- package/src/elements/store/resource.test.ts +253 -0
- package/src/elements/store/resource.ts +786 -0
- package/src/elements/store/sql-condition.test.ts +132 -0
- package/src/elements/store/sql-condition.ts +284 -46
- package/src/elements/store/sql-session.test.ts +86 -1
- package/src/elements/store/sql-session.ts +187 -27
- package/src/elements/store/table.ts +34 -4
- package/src/elements/store.ts +16 -0
- package/src/elements/vault/runtime.ts +1 -1
- package/src/elements/vault.test.ts +1 -28
- package/src/elements/vault.ts +1 -1
- package/src/kernel/app.ts +59 -25
- package/src/kernel/boot-bind/channel.test.ts +60 -0
- package/src/kernel/boot-bind/channel.ts +64 -2
- package/src/kernel/boot-bind/store.test.ts +10 -1
- package/src/kernel/boot-bind/store.ts +49 -2
- package/src/kernel/boot.test.ts +0 -1
- package/src/kernel/boot.ts +1 -1
- package/src/kernel/edge.test.ts +68 -0
- package/src/kernel/errors.registry.test.ts +1 -1
- package/src/kernel/flow.ts +8 -0
- package/src/kernel/fx.test.ts +23 -3
- package/src/kernel/fx.ts +115 -18
- package/src/kernel/hooks.test.ts +33 -0
- package/src/kernel/hooks.ts +22 -0
- package/src/kernel/index.ts +7 -0
- package/src/kernel/on.ts +44 -3
- package/src/kernel/plugin.ts +33 -3
- package/src/kernel/registry-isolation.test.ts +74 -0
- package/src/kernel/registry.ts +22 -1
- package/src/kernel/triggers.ts +59 -0
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/fixtures/skyport.manifest.json +1 -1
- package/src/manifest/index.ts +1 -1
- package/src/manifest/types.ts +1 -1
- package/src/manifest/validate.ts +2 -2
- package/src/plugins/compression.test.ts +127 -0
- package/src/plugins/compression.ts +94 -0
- package/src/plugins/config-source.test.ts +204 -0
- package/src/plugins/config-source.ts +209 -0
- package/src/plugins/cors.test.ts +172 -0
- package/src/plugins/cors.ts +153 -0
- package/src/plugins/csrf.test.ts +102 -0
- package/src/plugins/csrf.ts +86 -0
- package/src/plugins/headers.ts +54 -0
- package/src/plugins/index.ts +26 -0
- package/src/plugins/ip-allowlist.test.ts +139 -0
- package/src/plugins/ip-allowlist.ts +129 -0
- package/src/plugins/maintenance-mode.test.ts +91 -0
- package/src/plugins/maintenance-mode.ts +85 -0
- package/src/plugins/security-headers.test.ts +243 -0
- package/src/plugins/security-headers.ts +255 -0
- package/src/release/measure.ts +1 -2
- package/src/test/create-test-app.ts +14 -2
- package/docs/spec/console.md +0 -762
- package/docs/spec/example.md +0 -1374
- package/docs/spec/four-applications.md +0 -1376
- package/docs/spec/unified-theory.md +0 -498
- package/src/cli/doc-drift.test.ts +0 -147
- package/src/cli/doc-drift.ts +0 -401
- package/src/cli/doctor-diff-examples.ts +0 -90
- package/src/drivers/vault-sops.ts +0 -246
- /package/{spec/manifest.v1.schema.json → manifest.v1.schema.json} +0 -0
package/src/cli/dev.ts
CHANGED
|
@@ -13,7 +13,9 @@ import type { ConsoleState } from "../console/server/state.ts";
|
|
|
13
13
|
import {
|
|
14
14
|
DEFAULT_DOCKER_DIR,
|
|
15
15
|
deriveInfrastructure,
|
|
16
|
+
loadExistingStackControls,
|
|
16
17
|
loadExistingStackCredentials,
|
|
18
|
+
resolveExtraPorts,
|
|
17
19
|
stackAppSlug,
|
|
18
20
|
stackInstanceId,
|
|
19
21
|
writeDerivedFiles,
|
|
@@ -21,7 +23,7 @@ import {
|
|
|
21
23
|
} from "../docker/index.ts";
|
|
22
24
|
import type { Manifest } from "../manifest/types.ts";
|
|
23
25
|
import type { McpContext } from "../mcp/tools.ts";
|
|
24
|
-
import { APP_PORT, CONSOLE_PORT, MCP_PORT } from "../runtime/types.ts";
|
|
26
|
+
import { APP_PORT, CONSOLE_PORT, DOCS_MCP_PORT, MCP_PORT } from "../runtime/types.ts";
|
|
25
27
|
import {
|
|
26
28
|
formatDevBanner,
|
|
27
29
|
formatDevLogSeparator,
|
|
@@ -90,6 +92,12 @@ export interface DevServeMcpOptions {
|
|
|
90
92
|
readonly hostname?: string;
|
|
91
93
|
}
|
|
92
94
|
|
|
95
|
+
/** Options for {@link DevOptions.serveDocsMcp}. */
|
|
96
|
+
export interface DevServeDocsMcpOptions {
|
|
97
|
+
readonly port: number;
|
|
98
|
+
readonly hostname?: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
93
101
|
/** Live `oke dev` session — returned when {@link DevOptions.keepAlive} is false. */
|
|
94
102
|
export interface DevSession {
|
|
95
103
|
readonly plan: DevPlan;
|
|
@@ -99,6 +107,10 @@ export interface DevSession {
|
|
|
99
107
|
readonly consolePort: number;
|
|
100
108
|
readonly mcpPort: number;
|
|
101
109
|
readonly mcpUrl: URL | null;
|
|
110
|
+
/** Docs MCP listen port (read-only, unauthenticated). */
|
|
111
|
+
readonly docsMcpPort: number;
|
|
112
|
+
/** Docs MCP base URL — null when the surface was skipped. */
|
|
113
|
+
readonly docsMcpUrl: URL | null;
|
|
102
114
|
/** Shared with Console — mint `oke-mcp` tokens against this store/secret. */
|
|
103
115
|
readonly secret: string | null;
|
|
104
116
|
readonly sessions: SessionStore | null;
|
|
@@ -106,6 +118,13 @@ export interface DevSession {
|
|
|
106
118
|
readonly consoleState: ConsoleState | null;
|
|
107
119
|
}
|
|
108
120
|
|
|
121
|
+
/** Minimal filesystem watcher contract, injectable for deterministic tests. */
|
|
122
|
+
export type DevWatchFn = (
|
|
123
|
+
path: string,
|
|
124
|
+
options: { readonly recursive: true },
|
|
125
|
+
listener: (event: "rename" | "change", filename: string | null) => void,
|
|
126
|
+
) => { close(): void };
|
|
127
|
+
|
|
109
128
|
/** Options for {@link runDev}. */
|
|
110
129
|
export interface DevOptions {
|
|
111
130
|
readonly cwd?: string;
|
|
@@ -134,6 +153,8 @@ export interface DevOptions {
|
|
|
134
153
|
* @param filename - Relative path from the watcher
|
|
135
154
|
*/
|
|
136
155
|
readonly onDbAutoPush?: (filename: string) => void;
|
|
156
|
+
/** Watch project source changes (injectable for tests). */
|
|
157
|
+
readonly watchFs?: DevWatchFn;
|
|
137
158
|
readonly images?: Readonly<Record<string, string>>;
|
|
138
159
|
readonly credentials?: DeriveOptions["credentials"];
|
|
139
160
|
readonly write?: (text: string) => void;
|
|
@@ -156,6 +177,7 @@ export interface DevOptions {
|
|
|
156
177
|
readonly appPort?: number;
|
|
157
178
|
readonly consolePort?: number;
|
|
158
179
|
readonly mcpPort?: number;
|
|
180
|
+
readonly docsMcpPort?: number;
|
|
159
181
|
/**
|
|
160
182
|
* Boot compose (injectable).
|
|
161
183
|
*
|
|
@@ -176,6 +198,12 @@ export interface DevOptions {
|
|
|
176
198
|
* Default: {@link serveMcp} on :6535.
|
|
177
199
|
*/
|
|
178
200
|
readonly serveMcp?: (options: DevServeMcpOptions) => Promise<DevMcpHandle>;
|
|
201
|
+
/**
|
|
202
|
+
* Serve the read-only docs MCP (injectable).
|
|
203
|
+
* Default: {@link serveDocsMcp} on :6536. Boot failure skips the surface —
|
|
204
|
+
* docs search must never take `oke dev` down.
|
|
205
|
+
*/
|
|
206
|
+
readonly serveDocsMcp?: (options: DevServeDocsMcpOptions) => Promise<DevMcpHandle>;
|
|
179
207
|
/**
|
|
180
208
|
* Boot + serve the app entry (injectable).
|
|
181
209
|
* Default: spawn `bun --hot` on the shipped {@link ./dev-app-runner.ts}
|
|
@@ -199,6 +227,7 @@ export interface DevPlan {
|
|
|
199
227
|
readonly appPort: number;
|
|
200
228
|
readonly consolePort: number;
|
|
201
229
|
readonly mcpPort: number;
|
|
230
|
+
readonly docsMcpPort: number;
|
|
202
231
|
readonly stackRoles: readonly string[] | null;
|
|
203
232
|
readonly composeFiles: readonly string[] | null;
|
|
204
233
|
readonly stackEnv: Readonly<Record<string, string>> | null;
|
|
@@ -223,24 +252,29 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
223
252
|
const preferredApp = options.appPort ?? Number(Bun.env.PORT ?? APP_PORT);
|
|
224
253
|
const preferredConsole = options.consolePort ?? CONSOLE_PORT;
|
|
225
254
|
const preferredMcp = options.mcpPort ?? MCP_PORT;
|
|
255
|
+
const preferredDocsMcp = options.docsMcpPort ?? DOCS_MCP_PORT;
|
|
226
256
|
// Explicit overrides (incl. `0` ephemeral) skip probing; otherwise +1 until free.
|
|
227
257
|
const ports =
|
|
228
258
|
options.appPort !== undefined ||
|
|
229
259
|
options.consolePort !== undefined ||
|
|
230
|
-
options.mcpPort !== undefined
|
|
260
|
+
options.mcpPort !== undefined ||
|
|
261
|
+
options.docsMcpPort !== undefined
|
|
231
262
|
? {
|
|
232
263
|
app: preferredApp,
|
|
233
264
|
console: preferredConsole,
|
|
234
265
|
mcp: preferredMcp,
|
|
266
|
+
docsMcp: preferredDocsMcp,
|
|
235
267
|
}
|
|
236
268
|
: await resolveDevPorts({
|
|
237
269
|
app: preferredApp,
|
|
238
270
|
console: preferredConsole,
|
|
239
271
|
mcp: preferredMcp,
|
|
272
|
+
docsMcp: preferredDocsMcp,
|
|
240
273
|
});
|
|
241
274
|
const appPort = ports.app;
|
|
242
275
|
const consolePort = ports.console;
|
|
243
276
|
const mcpPort = ports.mcp;
|
|
277
|
+
const docsMcpPort = ports.docsMcp;
|
|
244
278
|
const keepAlive = options.keepAlive ?? true;
|
|
245
279
|
|
|
246
280
|
let entry = options.entry;
|
|
@@ -344,6 +378,7 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
344
378
|
const appSlug = stackAppSlug(cwd);
|
|
345
379
|
const reusedCreds =
|
|
346
380
|
options.credentials ?? (await loadExistingStackCredentials(cwd, stackRoles));
|
|
381
|
+
const controls = await loadExistingStackControls(cwd);
|
|
347
382
|
const derived = deriveInfrastructure({
|
|
348
383
|
images,
|
|
349
384
|
app: appSlug,
|
|
@@ -352,6 +387,7 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
352
387
|
composeDir,
|
|
353
388
|
instanceId,
|
|
354
389
|
...(reusedCreds ? { credentials: reusedCreds } : {}),
|
|
390
|
+
...(controls ? { controls } : {}),
|
|
355
391
|
host: "127.0.0.1",
|
|
356
392
|
});
|
|
357
393
|
// Layer 4 is user-owned — include only if the file already exists.
|
|
@@ -383,6 +419,27 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
383
419
|
});
|
|
384
420
|
// Compose files live under docker/; cwd for compose is that directory.
|
|
385
421
|
await up(composeFiles, dockerOut);
|
|
422
|
+
|
|
423
|
+
// Real OpenBao vault: init once / unseal every start, then expose the
|
|
424
|
+
// least-privilege app token to the app + Console (never root).
|
|
425
|
+
const vaultSpec = derived.specs.find((s) => s.role === "vault");
|
|
426
|
+
if (vaultSpec) {
|
|
427
|
+
const { ensureOpenBao, openbaoStackEnv } = await import("./openbao-bootstrap.ts");
|
|
428
|
+
const vaultNames = await tryLoadVaultSecretNames(cwd);
|
|
429
|
+
const boot = await ensureOpenBao({
|
|
430
|
+
cwd,
|
|
431
|
+
url: `http://127.0.0.1:${vaultSpec.hostPort}`,
|
|
432
|
+
names: vaultNames,
|
|
433
|
+
});
|
|
434
|
+
Object.assign(stackEnv!, openbaoStackEnv(boot));
|
|
435
|
+
const envPath = resolve(dockerOut, ".env.docker");
|
|
436
|
+
const current = await Bun.file(envPath).text();
|
|
437
|
+
const tokenLine = `OKE_VAULT_TOKEN=${boot.appToken}`;
|
|
438
|
+
const next = /OKE_VAULT_TOKEN=.*/.test(current)
|
|
439
|
+
? current.replace(/OKE_VAULT_TOKEN=.*/, tokenLine)
|
|
440
|
+
: `${current.trimEnd()}\nOKE_VAULT_TOKEN=${boot.appToken}\nOKE_VAULT_MOUNT=secret\n`;
|
|
441
|
+
await Bun.write(envPath, next);
|
|
442
|
+
}
|
|
386
443
|
} catch (err) {
|
|
387
444
|
const msg = err instanceof Error ? err.message : String(err);
|
|
388
445
|
console.error(msg);
|
|
@@ -392,8 +449,15 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
392
449
|
const roleLabel = (role: string): string => {
|
|
393
450
|
if (role === "store.sql") return "postgres";
|
|
394
451
|
if (role === "store.kv") return "redis";
|
|
452
|
+
if (role === "store.files") return "files";
|
|
453
|
+
if (role === "channel.email") return "mail";
|
|
395
454
|
return role;
|
|
396
455
|
};
|
|
456
|
+
const extraLabel = (role: string, containerPort: number): string => {
|
|
457
|
+
if (role === "channel.email" && containerPort === 8025) return "mail-ui";
|
|
458
|
+
if (role === "store.files" && containerPort === 9001) return "files-ui";
|
|
459
|
+
return `${roleLabel(role)}+${containerPort}`;
|
|
460
|
+
};
|
|
397
461
|
const appDrivers = [
|
|
398
462
|
...(stackRoles.includes("store.sql") ? [stackSqlDriver] : []),
|
|
399
463
|
...(stackRoles.includes("store.kv") ? [stackKvDriver] : []),
|
|
@@ -401,10 +465,13 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
401
465
|
write(
|
|
402
466
|
formatStackSummary({
|
|
403
467
|
project: `oke-${appSlug}`,
|
|
404
|
-
services: derived.specs.
|
|
405
|
-
label: roleLabel(spec.role),
|
|
406
|
-
|
|
407
|
-
|
|
468
|
+
services: derived.specs.flatMap((spec) => [
|
|
469
|
+
{ label: roleLabel(spec.role), hostPort: spec.hostPort },
|
|
470
|
+
...resolveExtraPorts(spec, { images, instanceId }).map((p) => ({
|
|
471
|
+
label: extraLabel(spec.role, p.containerPort),
|
|
472
|
+
hostPort: p.hostPort,
|
|
473
|
+
})),
|
|
474
|
+
]),
|
|
408
475
|
appDrivers,
|
|
409
476
|
}),
|
|
410
477
|
);
|
|
@@ -416,6 +483,7 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
416
483
|
appPort,
|
|
417
484
|
consolePort,
|
|
418
485
|
mcpPort,
|
|
486
|
+
docsMcpPort,
|
|
419
487
|
stackRoles,
|
|
420
488
|
composeFiles,
|
|
421
489
|
stackEnv,
|
|
@@ -452,6 +520,28 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
452
520
|
|
|
453
521
|
if (options.dryRun) return { code: 0, plan };
|
|
454
522
|
|
|
523
|
+
// One-shot schema sync for the session profile (both local and docker):
|
|
524
|
+
// emits schema.generated.ts for the active dialect, then pushes via
|
|
525
|
+
// drizzle-kit. Data planes stay isolated — session flags never rewrite
|
|
526
|
+
// `.oke/mode`. Watch auto-push below remains local-only.
|
|
527
|
+
if (!options.noDbPush) {
|
|
528
|
+
const { syncDevSchema } = await import("./dev-schema-sync.ts");
|
|
529
|
+
try {
|
|
530
|
+
const result = await syncDevSchema(cwd, mode, { write });
|
|
531
|
+
write(
|
|
532
|
+
formatStatusLine(
|
|
533
|
+
`oke db push (${mode} · ${result.dialect}) ${result.code === 0 ? "ok" : "failed"}`,
|
|
534
|
+
),
|
|
535
|
+
);
|
|
536
|
+
} catch (err) {
|
|
537
|
+
write(
|
|
538
|
+
formatStatusLine(
|
|
539
|
+
`oke db push (${mode}) skipped — ${err instanceof Error ? err.message : String(err)}`,
|
|
540
|
+
),
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
455
545
|
const autoPushEnabled = resolveDevAutoPush({
|
|
456
546
|
noDbPush: options.noDbPush,
|
|
457
547
|
docker: Boolean(stackEnv),
|
|
@@ -542,6 +632,24 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
542
632
|
};
|
|
543
633
|
});
|
|
544
634
|
|
|
635
|
+
const serveDocsMcpSurface =
|
|
636
|
+
options.serveDocsMcp ??
|
|
637
|
+
(async (docsOptions) => {
|
|
638
|
+
const { serveDocsMcp } = await import("../mcp/docs-server.ts");
|
|
639
|
+
const server = await serveDocsMcp({
|
|
640
|
+
port: docsOptions.port,
|
|
641
|
+
hostname: docsOptions.hostname ?? "127.0.0.1",
|
|
642
|
+
});
|
|
643
|
+
write(formatServiceLine("Docs MCP", `http://127.0.0.1:${server.port}`));
|
|
644
|
+
return {
|
|
645
|
+
port: server.port,
|
|
646
|
+
url: server.url,
|
|
647
|
+
stop() {
|
|
648
|
+
server.stop(true);
|
|
649
|
+
},
|
|
650
|
+
};
|
|
651
|
+
});
|
|
652
|
+
|
|
545
653
|
const regen =
|
|
546
654
|
options.regenClient ??
|
|
547
655
|
(async (appUrl) => {
|
|
@@ -581,8 +689,21 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
581
689
|
});
|
|
582
690
|
}
|
|
583
691
|
|
|
692
|
+
// Docs MCP is independent of Console state — read-only, no sessions/secret.
|
|
693
|
+
// Boot failure (missing docs content, busy port) skips it, never kills dev.
|
|
694
|
+
let docsMcpServer: DevMcpHandle | null = null;
|
|
695
|
+
try {
|
|
696
|
+
docsMcpServer = await serveDocsMcpSurface({ port: docsMcpPort, hostname: "127.0.0.1" });
|
|
697
|
+
} catch (err) {
|
|
698
|
+
write(
|
|
699
|
+
formatStatusLine(`Docs MCP skipped — ${err instanceof Error ? err.message : String(err)}`),
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
|
|
584
703
|
const boundMcpPort = mcpServer?.port ?? mcpPort;
|
|
585
704
|
const mcpUrl = mcpServer?.url ?? null;
|
|
705
|
+
const boundDocsMcpPort = docsMcpServer?.port ?? docsMcpPort;
|
|
706
|
+
const docsMcpUrl = docsMcpServer?.url ?? null;
|
|
586
707
|
|
|
587
708
|
const appUrl = app.url?.origin ?? `http://127.0.0.1:${boundAppPort}`;
|
|
588
709
|
await regen(appUrl);
|
|
@@ -591,7 +712,7 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
591
712
|
options.dbPush ??
|
|
592
713
|
(async (projectCwd: string) => {
|
|
593
714
|
const configPath = await resolveDrizzleConfigPath(projectCwd);
|
|
594
|
-
return runPush(configPath, (t) => write(t));
|
|
715
|
+
return runPush(configPath, (t) => write(t), { cwd: projectCwd, env: mode });
|
|
595
716
|
});
|
|
596
717
|
|
|
597
718
|
let lastSchemaFilename = "";
|
|
@@ -614,7 +735,9 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
614
735
|
|
|
615
736
|
write(formatDevLogSeparator());
|
|
616
737
|
|
|
617
|
-
const
|
|
738
|
+
const watchFs: DevWatchFn =
|
|
739
|
+
options.watchFs ?? ((path, watchOptions, listener) => watch(path, watchOptions, listener));
|
|
740
|
+
const watcher = watchFs(resolve(cwd, "src"), { recursive: true }, (_event, filename) => {
|
|
618
741
|
void regen(appUrl);
|
|
619
742
|
// Only live-extract when the host did not pin a Manifest (tests).
|
|
620
743
|
if (options.manifest === undefined) {
|
|
@@ -635,6 +758,7 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
635
758
|
app.stop();
|
|
636
759
|
consoleServer.stop();
|
|
637
760
|
mcpServer?.stop();
|
|
761
|
+
docsMcpServer?.stop();
|
|
638
762
|
process.off("SIGINT", onSignal);
|
|
639
763
|
process.off("SIGTERM", onSignal);
|
|
640
764
|
};
|
|
@@ -652,12 +776,15 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
652
776
|
appPort: boundAppPort,
|
|
653
777
|
consolePort: boundConsolePort,
|
|
654
778
|
mcpPort: boundMcpPort,
|
|
779
|
+
docsMcpPort: boundDocsMcpPort,
|
|
655
780
|
},
|
|
656
781
|
stop,
|
|
657
782
|
appPort: boundAppPort,
|
|
658
783
|
consolePort: boundConsolePort,
|
|
659
784
|
mcpPort: boundMcpPort,
|
|
660
785
|
mcpUrl,
|
|
786
|
+
docsMcpPort: boundDocsMcpPort,
|
|
787
|
+
docsMcpUrl,
|
|
661
788
|
secret,
|
|
662
789
|
sessions,
|
|
663
790
|
consoleState,
|
|
@@ -674,6 +801,21 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
|
|
|
674
801
|
return { code: 0, plan: session.plan, session };
|
|
675
802
|
}
|
|
676
803
|
|
|
804
|
+
/**
|
|
805
|
+
* Declared vault secret names from the project Manifest (policy scope).
|
|
806
|
+
*
|
|
807
|
+
* @param cwd - Project root
|
|
808
|
+
*/
|
|
809
|
+
async function tryLoadVaultSecretNames(cwd: string): Promise<readonly string[]> {
|
|
810
|
+
try {
|
|
811
|
+
const { extractManifest } = await import("../compiler/extract.ts");
|
|
812
|
+
const manifest = await extractManifest({ rootDir: cwd });
|
|
813
|
+
return Object.keys(manifest.vault ?? {});
|
|
814
|
+
} catch {
|
|
815
|
+
return [];
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
677
819
|
/**
|
|
678
820
|
* True when a Manifest has no declared flows (scaffold / empty extract).
|
|
679
821
|
*
|
|
@@ -48,20 +48,20 @@ function assertZeroGitGrep(pattern: string, ignorePrefixes: readonly string[] =
|
|
|
48
48
|
|
|
49
49
|
describe("legacy stack-mode removal gate", () => {
|
|
50
50
|
test("tracked tree has zero legacy docker-mode flag outside changelog", () => {
|
|
51
|
-
assertZeroGitGrep(FORBIDDEN_STACK_FLAG, ["
|
|
51
|
+
assertZeroGitGrep(FORBIDDEN_STACK_FLAG, ["changelog.md:"]);
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
test("tracked tree has zero legacy compose env filename outside changelog", () => {
|
|
55
|
-
assertZeroGitGrep(FORBIDDEN_ENV_STACK, ["
|
|
55
|
+
assertZeroGitGrep(FORBIDDEN_ENV_STACK, ["changelog.md:"]);
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
test("tracked tree has zero legacy OKE env outside changelog", () => {
|
|
59
|
-
assertZeroGitGrep(FORBIDDEN_LEGACY_ENV, ["
|
|
59
|
+
assertZeroGitGrep(FORBIDDEN_LEGACY_ENV, ["changelog.md:"]);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
test("tracked tree has zero legacy vault helper outside changelog + upgrade", () => {
|
|
63
63
|
assertZeroGitGrep(FORBIDDEN_FROM_STACK, [
|
|
64
|
-
"
|
|
64
|
+
"changelog.md:",
|
|
65
65
|
"src/upgrade/codemods.ts:",
|
|
66
66
|
"src/upgrade/codemods.test.ts:",
|
|
67
67
|
]);
|
|
@@ -63,6 +63,26 @@ describe("oke stack", () => {
|
|
|
63
63
|
expect(out).toContain("postgres");
|
|
64
64
|
expect(await Bun.file(join(dir, "compose.yml")).exists()).toBe(false);
|
|
65
65
|
});
|
|
66
|
+
|
|
67
|
+
test("uses per-project host ports (not raw recipe defaults)", async () => {
|
|
68
|
+
const { stackInstanceId, hostPortForInstance } = await import("../docker/index.ts");
|
|
69
|
+
const dir = await mkdtemp(join(tmpdir(), "oke-cli-stack-ports-"));
|
|
70
|
+
let out = "";
|
|
71
|
+
const code = await runStackPreview({
|
|
72
|
+
cwd: dir,
|
|
73
|
+
images: {
|
|
74
|
+
"channel.email": "axllent/mailpit:v1.22.3",
|
|
75
|
+
},
|
|
76
|
+
write: (t) => {
|
|
77
|
+
out += t;
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
expect(code).toBe(0);
|
|
81
|
+
const id = stackInstanceId(dir);
|
|
82
|
+
const smtp = hostPortForInstance("channel.email", 1025, id);
|
|
83
|
+
expect(out).toContain(`${smtp}:1025`);
|
|
84
|
+
expect(out).not.toContain("1025:1025");
|
|
85
|
+
});
|
|
66
86
|
});
|
|
67
87
|
|
|
68
88
|
describe("oke images pin", () => {
|
package/src/cli/docker.ts
CHANGED
|
@@ -88,6 +88,16 @@ export async function runDockerDerive(
|
|
|
88
88
|
await writeDerivedFiles(result, outDir, { writeStackEnv: false });
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
const vaultSpec = result.specs.find((s) => s.role === "vault");
|
|
92
|
+
if (vaultSpec) {
|
|
93
|
+
const rel = composeDir === "." ? ".oke/openbao/" : `${composeDir}/../.oke/openbao/`;
|
|
94
|
+
write(
|
|
95
|
+
`oke docker: OpenBao first boot — bring the stack up (e.g. \`oke dev -d\`), and oke ` +
|
|
96
|
+
`will initialize + unseal it once. Root/unseal material stays on the host at ${rel} ` +
|
|
97
|
+
`(0600, gitignored) — back it up; losing it loses every secret permanently.\n`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
91
101
|
write(`oke docker: wrote ${result.files.length} file(s) → ${composeDir}/\n`);
|
|
92
102
|
for (const f of result.files) write(` ${composeDir}/${f.path}\n`);
|
|
93
103
|
write(
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* drizzle-kit env resolution — dialect from configured driver, not URL sniffing.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
6
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import type { OkeConfig } from "../config/index.ts";
|
|
10
|
+
import { resolveDrizzleKitEnv } from "./drizzle-env.ts";
|
|
11
|
+
|
|
12
|
+
const dirs: string[] = [];
|
|
13
|
+
|
|
14
|
+
afterEach(async () => {
|
|
15
|
+
await Promise.all(dirs.splice(0).map((d) => rm(d, { recursive: true, force: true })));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
async function tempDir(): Promise<string> {
|
|
19
|
+
const dir = await mkdtemp(join(tmpdir(), "oke-drizzle-env-"));
|
|
20
|
+
dirs.push(dir);
|
|
21
|
+
return dir;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const dualConfig = {
|
|
25
|
+
drivers: {
|
|
26
|
+
store: {
|
|
27
|
+
sql: { local: "sqlite", docker: "postgres", prod: "postgres", test: "memory" },
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
} as unknown as OkeConfig;
|
|
31
|
+
|
|
32
|
+
describe("resolveDrizzleKitEnv", () => {
|
|
33
|
+
test("local env → sqlite dialect + sqlite url", async () => {
|
|
34
|
+
const dir = await tempDir();
|
|
35
|
+
const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "local");
|
|
36
|
+
expect(ctx.dialect).toBe("sqlite");
|
|
37
|
+
expect(ctx.overlay.OKE_DRIZZLE_DIALECT).toBe("sqlite");
|
|
38
|
+
expect(ctx.overlay.OKE_SQLITE_URL).toContain(".oke/app.sqlite");
|
|
39
|
+
expect(ctx.overlay.DATABASE_URL).toBeUndefined();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("docker env → postgresql dialect + DATABASE_URL from compose env", async () => {
|
|
43
|
+
const dir = await tempDir();
|
|
44
|
+
await mkdir(join(dir, "docker"), { recursive: true });
|
|
45
|
+
await writeFile(
|
|
46
|
+
join(dir, "docker", ".env.docker"),
|
|
47
|
+
"DATABASE_URL=postgres://u:p@127.0.0.1:5432/oke\n",
|
|
48
|
+
);
|
|
49
|
+
const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "docker");
|
|
50
|
+
expect(ctx.dialect).toBe("postgresql");
|
|
51
|
+
expect(ctx.overlay.OKE_DRIZZLE_DIALECT).toBe("postgresql");
|
|
52
|
+
expect(ctx.overlay.DATABASE_URL).toBe("postgres://u:p@127.0.0.1:5432/oke");
|
|
53
|
+
expect(ctx.overlay.OKE_SQLITE_URL).toBeUndefined();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("docker dialect is postgresql even when DATABASE_URL unset (no URL guessing)", async () => {
|
|
57
|
+
const dir = await tempDir();
|
|
58
|
+
const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "docker");
|
|
59
|
+
expect(ctx.dialect).toBe("postgresql");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("unsupported driver id errors clearly", async () => {
|
|
63
|
+
const dir = await tempDir();
|
|
64
|
+
const bad = { drivers: { store: { sql: { local: "memory" } } } } as unknown as OkeConfig;
|
|
65
|
+
await expect(resolveDrizzleKitEnv(dir, bad, "local")).rejects.toThrow(/not supported/);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* drizzle-kit child-env overlay — resolved from the configured `store.sql`
|
|
3
|
+
* driver for the active env (never guessed from `DATABASE_URL` presence).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { resolveDriverId, type ConfigEnv, type OkeConfig } from "../config/index.ts";
|
|
7
|
+
import {
|
|
8
|
+
drizzleDialectFromSqlDriver,
|
|
9
|
+
type OkeDrizzleKitDialect,
|
|
10
|
+
} from "../drivers/drizzle-dialect.ts";
|
|
11
|
+
import { parseDotenv } from "../drivers/vault-dotenv-parse.ts";
|
|
12
|
+
import { resolveComposeEnvPath } from "../elements/vault/chain.ts";
|
|
13
|
+
|
|
14
|
+
/** Resolved drizzle env context for one project env. */
|
|
15
|
+
export interface DrizzleKitEnvContext {
|
|
16
|
+
/** Active env used for driver resolution. */
|
|
17
|
+
readonly env: ConfigEnv;
|
|
18
|
+
/** drizzle-kit dialect (from `SQL_DRIVER_TO_DRIZZLE_DIALECT`). */
|
|
19
|
+
readonly dialect: OkeDrizzleKitDialect;
|
|
20
|
+
/** Env overlay for the drizzle-kit child process. */
|
|
21
|
+
readonly overlay: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Load `docker/.env.docker` (or legacy `.env.docker`) as a name→value map.
|
|
26
|
+
*
|
|
27
|
+
* @param cwd - Project root
|
|
28
|
+
*/
|
|
29
|
+
async function readComposeEnv(cwd: string): Promise<Map<string, string>> {
|
|
30
|
+
const { path } = resolveComposeEnvPath(cwd);
|
|
31
|
+
const file = Bun.file(path);
|
|
32
|
+
if (!(await file.exists())) return new Map();
|
|
33
|
+
try {
|
|
34
|
+
return parseDotenv(await file.text());
|
|
35
|
+
} catch {
|
|
36
|
+
return new Map();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Resolve the drizzle-kit env context for a project + env.
|
|
42
|
+
*
|
|
43
|
+
* Dialect comes from the configured driver via the exhaustive
|
|
44
|
+
* `SQL_DRIVER_TO_DRIZZLE_DIALECT` map — a missing/unsupported driver id is a
|
|
45
|
+
* compile-time gap there, and a clear runtime error here (never a silent
|
|
46
|
+
* sqlite fallback).
|
|
47
|
+
*
|
|
48
|
+
* @param cwd - Project root
|
|
49
|
+
* @param config - Loaded `oke.config.ts` (null → sqlite/local defaults)
|
|
50
|
+
* @param env - Active env (`local` | `docker` for dev tooling)
|
|
51
|
+
*/
|
|
52
|
+
export async function resolveDrizzleKitEnv(
|
|
53
|
+
cwd: string,
|
|
54
|
+
config: OkeConfig | null | undefined,
|
|
55
|
+
env: ConfigEnv,
|
|
56
|
+
): Promise<DrizzleKitEnvContext> {
|
|
57
|
+
const driverId = resolveDriverId(config?.drivers?.store?.sql, env) ?? "sqlite";
|
|
58
|
+
if (driverId !== "sqlite" && driverId !== "postgres") {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`oke db: store.sql driver "${driverId}" is not supported by drizzle-kit (sqlite | postgres)`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const dialect = drizzleDialectFromSqlDriver(driverId);
|
|
64
|
+
|
|
65
|
+
const compose = env === "docker" ? await readComposeEnv(cwd) : new Map<string, string>();
|
|
66
|
+
|
|
67
|
+
const overlay: Record<string, string> = { OKE_DRIZZLE_DIALECT: dialect };
|
|
68
|
+
if (dialect === "postgresql") {
|
|
69
|
+
const url =
|
|
70
|
+
process.env.DATABASE_URL ?? compose.get("DATABASE_URL") ?? compose.get("OKE_STORE_SQL_URL");
|
|
71
|
+
if (url !== undefined) overlay.DATABASE_URL = url;
|
|
72
|
+
} else {
|
|
73
|
+
const url =
|
|
74
|
+
process.env.OKE_SQLITE_URL ?? compose.get("OKE_SQLITE_URL") ?? "file:.oke/app.sqlite";
|
|
75
|
+
overlay.OKE_SQLITE_URL = url;
|
|
76
|
+
}
|
|
77
|
+
return { env, dialect, overlay };
|
|
78
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure a mode-aware `drizzle.config.ts` exists — created when absent,
|
|
3
|
+
* never overwritten when the user customised it.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
|
|
8
|
+
/** Mode-aware template (matches `packages/create-oke/template/drizzle.config.ts`). */
|
|
9
|
+
const DRIZZLE_CONFIG_TEMPLATE = `import { defineConfig } from "drizzle-kit";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Domain schema sync for \`oke db push|generate|migrate\`.
|
|
13
|
+
*
|
|
14
|
+
* Dialect comes from OKE's \`store.sql\` driver map (resolved by the CLI and
|
|
15
|
+
* injected as \`OKE_DRIZZLE_DIALECT\`) — never inferred from \`DATABASE_URL\`
|
|
16
|
+
* presence. \`src/schema.generated.ts\` is emitted from \`src/schema.decl.ts\`
|
|
17
|
+
* for the active dialect as a pre-step of \`oke db\` / \`oke dev\`.
|
|
18
|
+
*
|
|
19
|
+
* Versioned prod migrations land in \`./drizzle\` — never mix with \`.oke/\`.
|
|
20
|
+
*/
|
|
21
|
+
const dialect = (process.env.OKE_DRIZZLE_DIALECT ?? "sqlite") as "sqlite" | "postgresql";
|
|
22
|
+
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
dialect,
|
|
25
|
+
schema: "./src/schema.generated.ts",
|
|
26
|
+
out: "./drizzle",
|
|
27
|
+
dbCredentials: {
|
|
28
|
+
url:
|
|
29
|
+
dialect === "postgresql"
|
|
30
|
+
? (process.env.DATABASE_URL ?? process.env.OKE_STORE_SQL_URL!)
|
|
31
|
+
: (process.env.OKE_SQLITE_URL ?? "file:.oke/app.sqlite"),
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Create `drizzle.config.ts` at the project root when it does not exist.
|
|
38
|
+
* Returns the absolute path and whether it was created.
|
|
39
|
+
*
|
|
40
|
+
* @param cwd - Project root
|
|
41
|
+
*/
|
|
42
|
+
export async function ensureDrizzleConfig(
|
|
43
|
+
cwd: string,
|
|
44
|
+
): Promise<{ readonly path: string; readonly created: boolean }> {
|
|
45
|
+
const path = resolve(cwd, "drizzle.config.ts");
|
|
46
|
+
const file = Bun.file(path);
|
|
47
|
+
if (await file.exists()) return { path, created: false };
|
|
48
|
+
await Bun.write(path, DRIZZLE_CONFIG_TEMPLATE);
|
|
49
|
+
return { path, created: true };
|
|
50
|
+
}
|
|
@@ -20,7 +20,7 @@ const SAMPLE: OkeConfig = {
|
|
|
20
20
|
},
|
|
21
21
|
signal: { local: "memory", docker: "postgres", prod: "postgres" },
|
|
22
22
|
clock: { local: "memory", docker: "postgres", prod: "postgres" },
|
|
23
|
-
vault: { local: "dotenv", docker: "
|
|
23
|
+
vault: { local: "dotenv", docker: "openbao", prod: "openbao" },
|
|
24
24
|
channel: {
|
|
25
25
|
email: { local: "console", docker: "smtp", prod: "smtp" },
|
|
26
26
|
},
|
package/src/cli/load-config.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type { Manifest } from "../manifest/types.ts";
|
|
|
10
10
|
const DEFAULT_SQL_IMAGE = "postgres:18-alpine";
|
|
11
11
|
const DEFAULT_PGVECTOR_IMAGE = "pgvector/pgvector:pg17";
|
|
12
12
|
const DEFAULT_KV_IMAGE = "redis:8-alpine";
|
|
13
|
+
const DEFAULT_VAULT_IMAGE = "openbao/openbao:2.6.1";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Extract protocol id from a driver ref.
|
|
@@ -66,6 +67,11 @@ export function defaultImagesFromConfig(config: OkeConfig): Readonly<Record<stri
|
|
|
66
67
|
out["store.kv"] = DEFAULT_KV_IMAGE;
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
const vault = dockerOrProdId(config.drivers?.vault);
|
|
71
|
+
if (vault === "openbao") {
|
|
72
|
+
out.vault = DEFAULT_VAULT_IMAGE;
|
|
73
|
+
}
|
|
74
|
+
|
|
69
75
|
return out;
|
|
70
76
|
}
|
|
71
77
|
|