create-oke 0.10.3 → 0.11.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 +2 -2
- package/package.json +2 -2
- package/src/ai-setup/apply.ts +113 -17
- package/src/ai-setup/catalog.ts +3 -3
- package/src/ai-setup/from-pref.ts +1 -1
- package/src/ai-setup/prompts.ts +1 -1
- package/src/cli.test.ts +183 -142
- package/src/cli.ts +190 -32
- package/src/create-defaults.test.ts +21 -15
- package/src/create-defaults.ts +33 -13
- package/src/customize-flow.test.ts +30 -35
- package/src/customize-flow.ts +68 -229
- package/src/drivers-catalog.ts +42 -48
- package/src/local-okengine.test.ts +50 -41
- package/src/local-okengine.ts +28 -14
- package/src/locales.test.ts +128 -0
- package/src/locales.ts +321 -0
- package/src/scaffold.ts +277 -33
- package/src/templates.ts +2 -8
- package/src/transform.test.ts +96 -36
- package/src/transform.ts +285 -110
- package/templates/advanced/.env.example +23 -3
- package/templates/advanced/README.md +1 -1
- package/templates/advanced/drizzle.config.ts +10 -13
- package/templates/advanced/oke.config.ts +12 -57
- package/templates/advanced/package.json +3 -1
- package/templates/advanced/src/app.ts +3 -10
- package/templates/advanced/src/core.ts +58 -0
- package/templates/advanced/src/db/schema.decl.ts +1 -1
- package/templates/advanced/src/db/seed/index.ts +2 -2
- package/templates/advanced/src/flows/notes/index.ts +1 -13
- package/templates/advanced/src/locales/index.ts +6 -0
- package/templates/advanced/tests/advanced.test.ts +1 -1
- package/templates/standard/.env.example +23 -3
- package/templates/standard/README.md +1 -1
- package/templates/standard/drizzle.config.ts +10 -13
- package/templates/standard/oke.config.ts +10 -56
- package/templates/standard/package.json +3 -1
- package/templates/standard/src/app.ts +3 -10
- package/templates/standard/src/core.ts +55 -0
- package/templates/standard/src/db/schema.decl.ts +1 -1
- package/templates/standard/src/db/seed/index.ts +2 -2
- package/templates/standard/src/flows/notes/index.ts +1 -10
- package/templates/standard/src/locales/index.ts +6 -0
- package/templates/standard/tests/standard.test.ts +1 -1
- package/templates/advanced/src/core/channels.ts +0 -13
- package/templates/advanced/src/core/gates.ts +0 -12
- package/templates/advanced/src/core/index.ts +0 -12
- package/templates/advanced/src/core/store.ts +0 -8
- package/templates/advanced/src/core/vault.ts +0 -7
- package/templates/advanced/src/locales/ar.ts +0 -18
- package/templates/standard/src/core/channels.ts +0 -13
- package/templates/standard/src/core/gates.ts +0 -12
- package/templates/standard/src/core/index.ts +0 -12
- package/templates/standard/src/core/store.ts +0 -5
- package/templates/standard/src/core/vault.ts +0 -7
- package/templates/standard/src/locales/ar.ts +0 -18
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Unit tests for
|
|
2
|
+
* Unit tests for Docker-first customize assembly (no Clack prompts).
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { describe, expect, test } from "bun:test";
|
|
6
6
|
import {
|
|
7
7
|
EMAIL_CHOICES,
|
|
8
8
|
INDEX_CHOICES,
|
|
9
|
-
|
|
10
|
-
TEMPLATE_LOCAL,
|
|
9
|
+
TEMPLATE_DEV,
|
|
11
10
|
customizeFacetsFor,
|
|
12
11
|
} from "./drivers-catalog.ts";
|
|
13
12
|
import { assembleDriverDefaults, pinsFromSides, recommendedAiApply } from "./customize-flow.ts";
|
|
@@ -37,7 +36,6 @@ describe("catalog labels", () => {
|
|
|
37
36
|
"none",
|
|
38
37
|
"memory",
|
|
39
38
|
"pgvector",
|
|
40
|
-
"libsql",
|
|
41
39
|
"meilisearch",
|
|
42
40
|
]);
|
|
43
41
|
});
|
|
@@ -52,7 +50,7 @@ describe("recommendedAiApply", () => {
|
|
|
52
50
|
test("returns llama.cpp (openai-compatible) with curated ai/ model", () => {
|
|
53
51
|
const apply = recommendedAiApply();
|
|
54
52
|
expect(apply.driver).toBe("openai-compatible");
|
|
55
|
-
expect(apply.chatModel).toBe("
|
|
53
|
+
expect(apply.chatModel).toBe("granite3.3:2b");
|
|
56
54
|
expect(apply.baseUrl).toContain("8080");
|
|
57
55
|
expect(apply.image).toContain("llama.cpp");
|
|
58
56
|
expect(apply.image).not.toContain("latest");
|
|
@@ -61,50 +59,47 @@ describe("recommendedAiApply", () => {
|
|
|
61
59
|
});
|
|
62
60
|
|
|
63
61
|
describe("pinsFromSides", () => {
|
|
64
|
-
test("prod
|
|
65
|
-
expect(pinsFromSides("
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
test: "memory",
|
|
62
|
+
test("prod mirrors dev by default", () => {
|
|
63
|
+
expect(pinsFromSides("postgres", "pglite")).toEqual({
|
|
64
|
+
dev: "postgres",
|
|
65
|
+
test: "pglite",
|
|
69
66
|
prod: "postgres",
|
|
70
67
|
});
|
|
71
68
|
});
|
|
72
69
|
});
|
|
73
70
|
|
|
74
71
|
describe("assembleDriverDefaults", () => {
|
|
75
|
-
test("
|
|
76
|
-
const drivers = assembleDriverDefaults(
|
|
77
|
-
"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
expect(drivers.store.sql.
|
|
83
|
-
expect(drivers.store.
|
|
84
|
-
expect(drivers.
|
|
85
|
-
expect(drivers.store.
|
|
72
|
+
test("picked sql + defaults for unpicked facets", () => {
|
|
73
|
+
const drivers = assembleDriverDefaults({
|
|
74
|
+
sql: "postgres",
|
|
75
|
+
kv: "redis",
|
|
76
|
+
files: "s3",
|
|
77
|
+
signal: "redis",
|
|
78
|
+
});
|
|
79
|
+
expect(drivers.store.sql.dev).toBe("postgres");
|
|
80
|
+
expect(drivers.store.sql.test).toBe("pglite");
|
|
81
|
+
expect(drivers.store.sql.prod).toBe("postgres");
|
|
82
|
+
expect(drivers.store.kv.dev).toBe("redis");
|
|
83
|
+
expect(drivers.signal.dev).toBe("redis");
|
|
84
|
+
expect(drivers.vault.dev).toBe(TEMPLATE_DEV.vault);
|
|
86
85
|
});
|
|
87
86
|
|
|
88
|
-
test("
|
|
89
|
-
const drivers = assembleDriverDefaults(
|
|
90
|
-
expect(drivers.store.sql.
|
|
91
|
-
expect(drivers.store.
|
|
92
|
-
expect(drivers.store.sql.
|
|
93
|
-
// Unpicked facets fall back to catalog defaults.
|
|
94
|
-
expect(drivers.store.kv.local).toBe(TEMPLATE_LOCAL.kv);
|
|
95
|
-
expect(drivers.store.kv.docker).toBe(TEMPLATE_DOCKER_PROD.kv);
|
|
87
|
+
test("empty pick → template Docker-first defaults", () => {
|
|
88
|
+
const drivers = assembleDriverDefaults({});
|
|
89
|
+
expect(drivers.store.sql.dev).toBe(TEMPLATE_DEV.sql);
|
|
90
|
+
expect(drivers.store.kv.dev).toBe(TEMPLATE_DEV.kv);
|
|
91
|
+
expect(drivers.store.sql.test).toBe("pglite");
|
|
96
92
|
});
|
|
97
93
|
|
|
98
94
|
test("index stays null for none / unset", () => {
|
|
99
|
-
expect(assembleDriverDefaults(
|
|
100
|
-
expect(assembleDriverDefaults(
|
|
95
|
+
expect(assembleDriverDefaults({ sql: "postgres" }).store.index).toBeNull();
|
|
96
|
+
expect(assembleDriverDefaults({ index: "none" }).store.index).toBeNull();
|
|
101
97
|
});
|
|
102
98
|
|
|
103
|
-
test("index driver
|
|
104
|
-
const drivers = assembleDriverDefaults(
|
|
99
|
+
test("index driver fills both runtime columns", () => {
|
|
100
|
+
const drivers = assembleDriverDefaults({ index: "meilisearch" });
|
|
105
101
|
expect(drivers.store.index).toEqual({
|
|
106
|
-
|
|
107
|
-
docker: "meilisearch",
|
|
102
|
+
dev: "meilisearch",
|
|
108
103
|
test: "memory",
|
|
109
104
|
prod: "meilisearch",
|
|
110
105
|
});
|
package/src/customize-flow.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Customize wizard —
|
|
3
|
-
* then ask whether to customize the other (defaults if no).
|
|
2
|
+
* Customize wizard — walk Docker-first facets once, assemble `{ dev, test, prod }`.
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
|
-
import { confirm, isCancel } from "@clack/prompts";
|
|
7
5
|
import { toCreateDefaults, type CreateDefaults, type EnvDriverPins } from "./create-defaults.ts";
|
|
8
6
|
import {
|
|
9
7
|
CLOCK_CHOICES,
|
|
@@ -13,8 +11,7 @@ import {
|
|
|
13
11
|
KV_CHOICES,
|
|
14
12
|
SIGNAL_CHOICES,
|
|
15
13
|
SQL_CHOICES,
|
|
16
|
-
|
|
17
|
-
TEMPLATE_LOCAL,
|
|
14
|
+
TEMPLATE_DEV,
|
|
18
15
|
TEMPLATE_TEST,
|
|
19
16
|
VAULT_CHOICES,
|
|
20
17
|
AI_PROVIDERS,
|
|
@@ -25,7 +22,7 @@ import {
|
|
|
25
22
|
aiDriverForProvider,
|
|
26
23
|
customizeFacetsFor,
|
|
27
24
|
pinsDockerReady,
|
|
28
|
-
|
|
25
|
+
pinsEnv,
|
|
29
26
|
type AiProviderId,
|
|
30
27
|
type CustomizeFacetId,
|
|
31
28
|
type DriverChoice,
|
|
@@ -39,10 +36,7 @@ import { WIZARD_BACK, selectWithBack, type WizardBack } from "./wizard-select.ts
|
|
|
39
36
|
export type { WizardBack };
|
|
40
37
|
export { WIZARD_BACK };
|
|
41
38
|
|
|
42
|
-
/**
|
|
43
|
-
export type CustomizeSide = "local" | "docker";
|
|
44
|
-
|
|
45
|
-
/** Partial pins accumulated per side before merge. */
|
|
39
|
+
/** Partial pins accumulated before merge. */
|
|
46
40
|
export type SidePins = {
|
|
47
41
|
sql?: string;
|
|
48
42
|
kv?: string;
|
|
@@ -56,14 +50,14 @@ export type SidePins = {
|
|
|
56
50
|
};
|
|
57
51
|
|
|
58
52
|
/**
|
|
59
|
-
*
|
|
53
|
+
* Assemble `{ dev, test, prod }` pins (prod mirrors dev).
|
|
60
54
|
*
|
|
61
|
-
* @param
|
|
62
|
-
* @param docker - Docker driver (copied to prod)
|
|
55
|
+
* @param dev - Dev driver
|
|
63
56
|
* @param test - Test pin
|
|
57
|
+
* @param prod - Prod pin (defaults to `dev`)
|
|
64
58
|
*/
|
|
65
|
-
export function pinsFromSides(
|
|
66
|
-
return
|
|
59
|
+
export function pinsFromSides(dev: string, test: string, prod: string = dev): EnvDriverPins {
|
|
60
|
+
return pinsEnv(dev, test, prod);
|
|
67
61
|
}
|
|
68
62
|
|
|
69
63
|
/**
|
|
@@ -76,69 +70,25 @@ function indexEnabled(value: string | null | undefined): value is string {
|
|
|
76
70
|
}
|
|
77
71
|
|
|
78
72
|
/**
|
|
79
|
-
* Fill missing
|
|
73
|
+
* Fill missing facets from template Docker-first defaults.
|
|
80
74
|
*
|
|
81
|
-
* @param
|
|
82
|
-
* @param picked - Values chosen for that side
|
|
83
|
-
* @param other - Optional values if the user also customized the other side
|
|
75
|
+
* @param picked - Values chosen during customize
|
|
84
76
|
*/
|
|
85
|
-
export function assembleDriverDefaults(
|
|
86
|
-
|
|
87
|
-
picked
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const sql = pinsFromSides(
|
|
94
|
-
localSrc.sql ?? TEMPLATE_LOCAL.sql,
|
|
95
|
-
dockerSrc.sql ?? TEMPLATE_DOCKER_PROD.sql,
|
|
96
|
-
TEMPLATE_TEST.sql,
|
|
97
|
-
);
|
|
98
|
-
const kv = pinsFromSides(
|
|
99
|
-
localSrc.kv ?? TEMPLATE_LOCAL.kv,
|
|
100
|
-
dockerSrc.kv ?? TEMPLATE_DOCKER_PROD.kv,
|
|
101
|
-
TEMPLATE_TEST.kv,
|
|
102
|
-
);
|
|
103
|
-
const files = pinsFromSides(
|
|
104
|
-
localSrc.files ?? TEMPLATE_LOCAL.files,
|
|
105
|
-
dockerSrc.files ?? TEMPLATE_DOCKER_PROD.files,
|
|
106
|
-
TEMPLATE_TEST.files,
|
|
107
|
-
);
|
|
108
|
-
const indexLocal = localSrc.index;
|
|
109
|
-
const indexDocker = dockerSrc.index;
|
|
110
|
-
const index =
|
|
111
|
-
indexEnabled(indexLocal) || indexEnabled(indexDocker)
|
|
112
|
-
? pinsFromSides(
|
|
113
|
-
indexEnabled(indexLocal) ? indexLocal : "memory",
|
|
114
|
-
indexEnabled(indexDocker) ? indexDocker : "meilisearch",
|
|
115
|
-
"memory",
|
|
116
|
-
)
|
|
117
|
-
: null;
|
|
77
|
+
export function assembleDriverDefaults(picked: SidePins): CreateDefaults["drivers"] {
|
|
78
|
+
const sql = pinsFromSides(picked.sql ?? TEMPLATE_DEV.sql, TEMPLATE_TEST.sql);
|
|
79
|
+
const kv = pinsFromSides(picked.kv ?? TEMPLATE_DEV.kv, TEMPLATE_TEST.kv);
|
|
80
|
+
const files = pinsFromSides(picked.files ?? TEMPLATE_DEV.files, TEMPLATE_TEST.files);
|
|
81
|
+
const index = indexEnabled(picked.index)
|
|
82
|
+
? pinsFromSides(picked.index, "memory", picked.index)
|
|
83
|
+
: null;
|
|
118
84
|
|
|
119
85
|
return {
|
|
120
86
|
store: { sql, kv, files, index },
|
|
121
|
-
signal: pinsFromSides(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
TEMPLATE_TEST.signal,
|
|
125
|
-
),
|
|
126
|
-
clock: pinsFromSides(
|
|
127
|
-
localSrc.clock ?? TEMPLATE_LOCAL.clock,
|
|
128
|
-
dockerSrc.clock ?? TEMPLATE_DOCKER_PROD.clock,
|
|
129
|
-
TEMPLATE_TEST.clock,
|
|
130
|
-
),
|
|
131
|
-
vault: pinsFromSides(
|
|
132
|
-
localSrc.vault ?? TEMPLATE_LOCAL.vault,
|
|
133
|
-
dockerSrc.vault ?? TEMPLATE_DOCKER_PROD.vault,
|
|
134
|
-
TEMPLATE_TEST.vault,
|
|
135
|
-
),
|
|
87
|
+
signal: pinsFromSides(picked.signal ?? TEMPLATE_DEV.signal, TEMPLATE_TEST.signal),
|
|
88
|
+
clock: pinsFromSides(picked.clock ?? TEMPLATE_DEV.clock, TEMPLATE_TEST.clock),
|
|
89
|
+
vault: pinsFromSides(picked.vault ?? TEMPLATE_DEV.vault, TEMPLATE_TEST.vault),
|
|
136
90
|
channel: {
|
|
137
|
-
email: pinsFromSides(
|
|
138
|
-
localSrc.email ?? TEMPLATE_LOCAL.email,
|
|
139
|
-
dockerSrc.email ?? TEMPLATE_DOCKER_PROD.email,
|
|
140
|
-
TEMPLATE_TEST.email,
|
|
141
|
-
),
|
|
91
|
+
email: pinsFromSides(picked.email ?? TEMPLATE_DEV.email, TEMPLATE_TEST.email),
|
|
142
92
|
},
|
|
143
93
|
ai: null,
|
|
144
94
|
};
|
|
@@ -146,13 +96,13 @@ export function assembleDriverDefaults(
|
|
|
146
96
|
|
|
147
97
|
/**
|
|
148
98
|
* Balanced llama.cpp defaults for "AI setup → Recommended" (no quiz).
|
|
149
|
-
*
|
|
99
|
+
* openai-compatible + curated Docker Hub `ai/` model (`granite3.3:2b`).
|
|
150
100
|
*/
|
|
151
101
|
export function recommendedAiApply(): AiSetupApplyInput {
|
|
152
102
|
return {
|
|
153
103
|
driver: "openai-compatible",
|
|
154
104
|
baseUrl: process.env.OKE_AI_URL ?? "http://127.0.0.1:8080/v1",
|
|
155
|
-
chatModel: "
|
|
105
|
+
chatModel: "granite3.3:2b",
|
|
156
106
|
visionModel: null,
|
|
157
107
|
embedModel: null,
|
|
158
108
|
image: LLAMA_CPP_IMAGE,
|
|
@@ -201,56 +151,14 @@ export function withLocalAiImage(input: AiSetupApplyInput, provider: string): Ai
|
|
|
201
151
|
export async function askCustomizeFlow(
|
|
202
152
|
template: TemplateId,
|
|
203
153
|
): Promise<CreateDefaults | WizardBack | null> {
|
|
204
|
-
const primaryValue = await selectWithBack(
|
|
205
|
-
"Customize drivers for",
|
|
206
|
-
[
|
|
207
|
-
{
|
|
208
|
-
value: "local",
|
|
209
|
-
label: "Local",
|
|
210
|
-
hint: "oke mode local — sqlite / memory / fs …",
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
value: "docker",
|
|
214
|
-
label: "Docker",
|
|
215
|
-
hint: "oke mode docker — postgres / redis / s3 …",
|
|
216
|
-
},
|
|
217
|
-
],
|
|
218
|
-
template === "advanced" ? "docker" : "local",
|
|
219
|
-
{ allowBack: true },
|
|
220
|
-
);
|
|
221
|
-
if (primaryValue === null) return null;
|
|
222
|
-
if (primaryValue === WIZARD_BACK) return WIZARD_BACK;
|
|
223
|
-
const primary = primaryValue as CustomizeSide;
|
|
224
|
-
|
|
225
154
|
const facets = customizeFacetsFor(template);
|
|
226
|
-
const primaryPins = await
|
|
155
|
+
const primaryPins = await walkFacets(facets);
|
|
227
156
|
if (primaryPins === null) return null;
|
|
228
157
|
if (primaryPins === WIZARD_BACK) return WIZARD_BACK;
|
|
229
158
|
|
|
230
|
-
const
|
|
231
|
-
const alsoOther = await confirm({
|
|
232
|
-
message:
|
|
233
|
-
otherSide === "local" ? "Also customize local drivers?" : "Also customize docker drivers?",
|
|
234
|
-
initialValue: false,
|
|
235
|
-
});
|
|
236
|
-
if (isCancel(alsoOther)) return null;
|
|
237
|
-
|
|
238
|
-
let otherPins: SidePins | null = null;
|
|
239
|
-
if (alsoOther) {
|
|
240
|
-
const walked = await walkFacetsForSide(facets, otherSide);
|
|
241
|
-
if (walked === null) return null;
|
|
242
|
-
if (walked === WIZARD_BACK) {
|
|
243
|
-
// Back from other side → treat as "no, use defaults"
|
|
244
|
-
otherPins = null;
|
|
245
|
-
} else {
|
|
246
|
-
otherPins = walked;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const drivers = assembleDriverDefaults(primary, primaryPins, otherPins);
|
|
251
|
-
const profile = primary === "docker" ? "docker-ready" : "local-only";
|
|
159
|
+
const drivers = assembleDriverDefaults(primaryPins);
|
|
252
160
|
|
|
253
|
-
// AI — after env
|
|
161
|
+
// AI — after env pass
|
|
254
162
|
let aiPins: EnvDriverPins | null = null;
|
|
255
163
|
let aiPref: CreateDefaults["ai"] = { enabled: false, provider: null, driver: null };
|
|
256
164
|
let aiApply: AiSetupApplyInput | null = null;
|
|
@@ -262,7 +170,7 @@ export async function askCustomizeFlow(
|
|
|
262
170
|
{
|
|
263
171
|
value: "recommended",
|
|
264
172
|
label: "Recommended",
|
|
265
|
-
hint: "llama.cpp ·
|
|
173
|
+
hint: "llama.cpp · openai-compatible · granite3.3:2b",
|
|
266
174
|
},
|
|
267
175
|
{
|
|
268
176
|
value: "customize",
|
|
@@ -280,15 +188,11 @@ export async function askCustomizeFlow(
|
|
|
280
188
|
);
|
|
281
189
|
if (aiSetup === null) return null;
|
|
282
190
|
if (aiSetup === WIZARD_BACK) {
|
|
283
|
-
// Back from AI → leave AI off (same as Off) and finish customize.
|
|
284
191
|
break;
|
|
285
192
|
}
|
|
286
193
|
if (aiSetup === "off") break;
|
|
287
194
|
if (aiSetup === "recommended") {
|
|
288
|
-
aiPins =
|
|
289
|
-
primary === "local" && !alsoOther
|
|
290
|
-
? pinsLocalOnly("openai-compatible", "openai-compatible", "mock")
|
|
291
|
-
: pinsDockerReady("openai-compatible", "openai-compatible", "mock");
|
|
195
|
+
aiPins = pinsDockerReady("openai-compatible", "mock");
|
|
292
196
|
aiApply = recommendedAiApply();
|
|
293
197
|
aiPref = aiPrefWithModels(
|
|
294
198
|
{
|
|
@@ -301,91 +205,60 @@ export async function askCustomizeFlow(
|
|
|
301
205
|
break;
|
|
302
206
|
}
|
|
303
207
|
|
|
304
|
-
// customize — local provider, optional docker provider, then model wizard
|
|
305
208
|
const options = AI_PROVIDERS.map((p) => ({ value: p.value, label: p.label }));
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
"llama-cpp",
|
|
312
|
-
{ allowBack: true },
|
|
313
|
-
);
|
|
314
|
-
if (localProviderValue === null) return null;
|
|
315
|
-
if (localProviderValue === WIZARD_BACK) continue aiSetup;
|
|
316
|
-
|
|
317
|
-
const localProvider = localProviderValue as AiProviderId;
|
|
318
|
-
const localDriver = aiDriverForProvider(localProvider);
|
|
319
|
-
let dockerDriver = localDriver === "mock" ? "mock" : localDriver;
|
|
320
|
-
let dockerProvider: AiProviderId = localProvider;
|
|
321
|
-
|
|
322
|
-
if (askDockerAi) {
|
|
323
|
-
const dockerProviderValue = await selectWithBack(
|
|
324
|
-
"AI Provider — docker",
|
|
325
|
-
options,
|
|
326
|
-
"llama-cpp",
|
|
327
|
-
{ allowBack: true },
|
|
328
|
-
);
|
|
329
|
-
if (dockerProviderValue === null) return null;
|
|
330
|
-
if (dockerProviderValue === WIZARD_BACK) continue providers;
|
|
331
|
-
dockerProvider = dockerProviderValue as AiProviderId;
|
|
332
|
-
dockerDriver = aiDriverForProvider(dockerProvider);
|
|
333
|
-
if (dockerDriver === "mock") dockerDriver = "mock";
|
|
334
|
-
}
|
|
209
|
+
const providerValue = await selectWithBack("AI Provider", options, "llama-cpp", {
|
|
210
|
+
allowBack: true,
|
|
211
|
+
});
|
|
212
|
+
if (providerValue === null) return null;
|
|
213
|
+
if (providerValue === WIZARD_BACK) continue;
|
|
335
214
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
: pinsDockerReady(localDriver, dockerDriver, "mock");
|
|
215
|
+
const provider = providerValue as AiProviderId;
|
|
216
|
+
const driver = aiDriverForProvider(provider);
|
|
217
|
+
aiPins = pinsDockerReady(driver, "mock");
|
|
340
218
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const picked = await askAiSetup({ provider: setupProvider });
|
|
348
|
-
if (picked === null) return null;
|
|
349
|
-
aiApply = withLocalAiImage(picked, askDockerAi ? dockerProvider : localProvider);
|
|
350
|
-
} else {
|
|
351
|
-
aiApply = { driver: "mock" };
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
aiPref = aiPrefWithModels(
|
|
355
|
-
{
|
|
356
|
-
enabled: true,
|
|
357
|
-
provider: setupProvider,
|
|
358
|
-
driver: aiPins.local,
|
|
359
|
-
},
|
|
360
|
-
aiApply,
|
|
361
|
-
);
|
|
362
|
-
break aiSetup;
|
|
219
|
+
if (provider !== "mock") {
|
|
220
|
+
const picked = await askAiSetup({ provider });
|
|
221
|
+
if (picked === null) return null;
|
|
222
|
+
aiApply = withLocalAiImage(picked, provider);
|
|
223
|
+
} else {
|
|
224
|
+
aiApply = { driver: "mock" };
|
|
363
225
|
}
|
|
226
|
+
|
|
227
|
+
aiPref = aiPrefWithModels(
|
|
228
|
+
{
|
|
229
|
+
enabled: true,
|
|
230
|
+
provider,
|
|
231
|
+
driver: aiPins.dev,
|
|
232
|
+
},
|
|
233
|
+
aiApply,
|
|
234
|
+
);
|
|
235
|
+
break;
|
|
364
236
|
}
|
|
365
237
|
|
|
366
238
|
return toCreateDefaults({
|
|
367
239
|
template,
|
|
368
|
-
profile,
|
|
240
|
+
profile: "docker-ready",
|
|
369
241
|
drivers: { ...drivers, ai: aiPins },
|
|
370
242
|
ai: aiPref,
|
|
243
|
+
// Locales / PgDog are asked once in the main wizard (not per customize pass).
|
|
244
|
+
locales: [],
|
|
245
|
+
pgdog: false,
|
|
371
246
|
});
|
|
372
247
|
}
|
|
373
248
|
|
|
374
249
|
/**
|
|
375
|
-
* Walk facet prompts for
|
|
250
|
+
* Walk facet prompts for the Docker-first runtime.
|
|
376
251
|
*
|
|
377
252
|
* @param facets - Facet ids for the template
|
|
378
|
-
* @param side - local or docker
|
|
379
253
|
*/
|
|
380
|
-
async function
|
|
254
|
+
async function walkFacets(
|
|
381
255
|
facets: readonly CustomizeFacetId[],
|
|
382
|
-
side: CustomizeSide,
|
|
383
256
|
): Promise<SidePins | WizardBack | null> {
|
|
384
257
|
const out: SidePins = {};
|
|
385
258
|
let i = 0;
|
|
386
259
|
while (i < facets.length) {
|
|
387
260
|
const facet = facets[i]!;
|
|
388
|
-
const result = await askOneFacet(facet,
|
|
261
|
+
const result = await askOneFacet(facet, out);
|
|
389
262
|
if (result === null) return null;
|
|
390
263
|
if (result === WIZARD_BACK) {
|
|
391
264
|
if (i === 0) return WIZARD_BACK;
|
|
@@ -399,87 +272,53 @@ async function walkFacetsForSide(
|
|
|
399
272
|
|
|
400
273
|
async function askOneFacet(
|
|
401
274
|
facet: CustomizeFacetId,
|
|
402
|
-
side: CustomizeSide,
|
|
403
275
|
state: SidePins,
|
|
404
276
|
): Promise<true | WizardBack | null> {
|
|
405
|
-
const suffix = side === "local" ? "local" : "docker";
|
|
406
277
|
switch (facet) {
|
|
407
278
|
case "sql": {
|
|
408
|
-
const v = await askSideChoice(
|
|
409
|
-
`store.sql — ${suffix}`,
|
|
410
|
-
SQL_CHOICES,
|
|
411
|
-
side === "local" ? TEMPLATE_LOCAL.sql : TEMPLATE_DOCKER_PROD.sql,
|
|
412
|
-
);
|
|
279
|
+
const v = await askSideChoice("store.sql — dev/prod", SQL_CHOICES, TEMPLATE_DEV.sql);
|
|
413
280
|
if (v === null || v === WIZARD_BACK) return v;
|
|
414
281
|
state.sql = v;
|
|
415
282
|
return true;
|
|
416
283
|
}
|
|
417
284
|
case "kv": {
|
|
418
|
-
const v = await askSideChoice(
|
|
419
|
-
`store.kv — ${suffix}`,
|
|
420
|
-
KV_CHOICES,
|
|
421
|
-
side === "local" ? TEMPLATE_LOCAL.kv : TEMPLATE_DOCKER_PROD.kv,
|
|
422
|
-
);
|
|
285
|
+
const v = await askSideChoice("store.kv — dev/prod", KV_CHOICES, TEMPLATE_DEV.kv);
|
|
423
286
|
if (v === null || v === WIZARD_BACK) return v;
|
|
424
287
|
state.kv = v;
|
|
425
288
|
return true;
|
|
426
289
|
}
|
|
427
290
|
case "files": {
|
|
428
|
-
const v = await askSideChoice(
|
|
429
|
-
`store.files — ${suffix}`,
|
|
430
|
-
FILES_CHOICES,
|
|
431
|
-
side === "local" ? TEMPLATE_LOCAL.files : TEMPLATE_DOCKER_PROD.files,
|
|
432
|
-
);
|
|
291
|
+
const v = await askSideChoice("store.files — dev/prod", FILES_CHOICES, TEMPLATE_DEV.files);
|
|
433
292
|
if (v === null || v === WIZARD_BACK) return v;
|
|
434
293
|
state.files = v;
|
|
435
294
|
return true;
|
|
436
295
|
}
|
|
437
296
|
case "index": {
|
|
438
|
-
const v = await askSideChoice(
|
|
439
|
-
`store.index — ${suffix}`,
|
|
440
|
-
INDEX_CHOICES,
|
|
441
|
-
side === "local" ? "none" : "meilisearch",
|
|
442
|
-
);
|
|
297
|
+
const v = await askSideChoice("store.index — dev/prod", INDEX_CHOICES, "meilisearch");
|
|
443
298
|
if (v === null || v === WIZARD_BACK) return v;
|
|
444
299
|
state.index = v;
|
|
445
300
|
return true;
|
|
446
301
|
}
|
|
447
302
|
case "signal": {
|
|
448
|
-
const v = await askSideChoice(
|
|
449
|
-
`signal — ${suffix}`,
|
|
450
|
-
SIGNAL_CHOICES,
|
|
451
|
-
side === "local" ? TEMPLATE_LOCAL.signal : TEMPLATE_DOCKER_PROD.signal,
|
|
452
|
-
);
|
|
303
|
+
const v = await askSideChoice("signal — dev/prod", SIGNAL_CHOICES, TEMPLATE_DEV.signal);
|
|
453
304
|
if (v === null || v === WIZARD_BACK) return v;
|
|
454
305
|
state.signal = v;
|
|
455
306
|
return true;
|
|
456
307
|
}
|
|
457
308
|
case "clock": {
|
|
458
|
-
const v = await askSideChoice(
|
|
459
|
-
`clock — ${suffix}`,
|
|
460
|
-
CLOCK_CHOICES,
|
|
461
|
-
side === "local" ? TEMPLATE_LOCAL.clock : TEMPLATE_DOCKER_PROD.clock,
|
|
462
|
-
);
|
|
309
|
+
const v = await askSideChoice("clock — dev/prod", CLOCK_CHOICES, TEMPLATE_DEV.clock);
|
|
463
310
|
if (v === null || v === WIZARD_BACK) return v;
|
|
464
311
|
state.clock = v;
|
|
465
312
|
return true;
|
|
466
313
|
}
|
|
467
314
|
case "vault": {
|
|
468
|
-
const v = await askSideChoice(
|
|
469
|
-
`vault — ${suffix}`,
|
|
470
|
-
VAULT_CHOICES,
|
|
471
|
-
side === "local" ? TEMPLATE_LOCAL.vault : TEMPLATE_DOCKER_PROD.vault,
|
|
472
|
-
);
|
|
315
|
+
const v = await askSideChoice("vault — dev/prod", VAULT_CHOICES, TEMPLATE_DEV.vault);
|
|
473
316
|
if (v === null || v === WIZARD_BACK) return v;
|
|
474
317
|
state.vault = v;
|
|
475
318
|
return true;
|
|
476
319
|
}
|
|
477
320
|
case "email": {
|
|
478
|
-
const v = await askSideChoice(
|
|
479
|
-
`channel.email — ${suffix}`,
|
|
480
|
-
EMAIL_CHOICES,
|
|
481
|
-
side === "local" ? TEMPLATE_LOCAL.email : TEMPLATE_DOCKER_PROD.email,
|
|
482
|
-
);
|
|
321
|
+
const v = await askSideChoice("channel.email — dev/prod", EMAIL_CHOICES, TEMPLATE_DEV.email);
|
|
483
322
|
if (v === null || v === WIZARD_BACK) return v;
|
|
484
323
|
state.email = v;
|
|
485
324
|
return true;
|