arkaos 4.34.0 → 4.35.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/THE-ARKAOS-GUIDE.md +1 -1
- package/VERSION +1 -1
- package/bin/arka-menubar.py +407 -0
- package/config/install-profiles.json +75 -0
- package/core/runtime/opencode.py +113 -0
- package/core/runtime/registry.py +6 -1
- package/harness/codex/AGENTS.md +1 -1
- package/harness/copilot/copilot-instructions.md +1 -1
- package/harness/cursor/rules/arkaos.mdc +2 -2
- package/harness/gemini/GEMINI.md +1 -1
- package/harness/opencode/AGENTS.md +1 -1
- package/harness/opencode/agents/arka-architect-gabriel.md +12 -0
- package/harness/opencode/agents/arka-brand-director-valentina.md +12 -0
- package/harness/opencode/agents/arka-cfo-helena.md +12 -0
- package/harness/opencode/agents/arka-chief-of-staff-afonso.md +12 -0
- package/harness/opencode/agents/arka-community-strategist-beatriz.md +12 -0
- package/harness/opencode/agents/arka-content-strategist-rafael.md +12 -0
- package/harness/opencode/agents/arka-conversion-strategist-ines.md +12 -0
- package/harness/opencode/agents/arka-coo-sofia.md +12 -0
- package/harness/opencode/agents/arka-copy-director-eduardo.md +12 -0
- package/harness/opencode/agents/arka-cqo-marta.md +12 -0
- package/harness/opencode/agents/arka-cto-marco.md +12 -0
- package/harness/opencode/agents/arka-design-ops-lead-iris.md +12 -0
- package/harness/opencode/agents/arka-ecom-director-ricardo.md +12 -0
- package/harness/opencode/agents/arka-knowledge-director-clara.md +12 -0
- package/harness/opencode/agents/arka-leadership-director-rodrigo.md +12 -0
- package/harness/opencode/agents/arka-marketing-director-luna.md +12 -0
- package/harness/opencode/agents/arka-ops-lead-daniel.md +12 -0
- package/harness/opencode/agents/arka-pm-director-carolina.md +12 -0
- package/harness/opencode/agents/arka-revops-lead-vicente.md +12 -0
- package/harness/opencode/agents/arka-saas-strategist-tiago.md +12 -0
- package/harness/opencode/agents/arka-sales-director-miguel.md +12 -0
- package/harness/opencode/agents/arka-strategy-director-tomas.md +12 -0
- package/harness/opencode/agents/arka-tech-director-francisca.md +12 -0
- package/harness/opencode/agents/arka-tech-lead-paulo.md +12 -0
- package/harness/opencode/agents/arka-video-producer-simao.md +12 -0
- package/harness/opencode/commands/arka-brand.md +9 -0
- package/harness/opencode/commands/arka-community.md +9 -0
- package/harness/opencode/commands/arka-content.md +9 -0
- package/harness/opencode/commands/arka-dev.md +9 -0
- package/harness/opencode/commands/arka-ecom.md +9 -0
- package/harness/opencode/commands/arka-fin.md +9 -0
- package/harness/opencode/commands/arka-kb.md +9 -0
- package/harness/opencode/commands/arka-landing.md +9 -0
- package/harness/opencode/commands/arka-lead.md +9 -0
- package/harness/opencode/commands/arka-mkt.md +9 -0
- package/harness/opencode/commands/arka-ops.md +9 -0
- package/harness/opencode/commands/arka-org.md +9 -0
- package/harness/opencode/commands/arka-pm.md +9 -0
- package/harness/opencode/commands/arka-saas.md +9 -0
- package/harness/opencode/commands/arka-sales.md +9 -0
- package/harness/opencode/commands/arka-strat.md +9 -0
- package/harness/opencode/opencode.json +16 -0
- package/harness/zed/.rules +1 -1
- package/installer/adapters/opencode.js +118 -0
- package/installer/cli.js +31 -1
- package/installer/detect-runtime.js +18 -0
- package/installer/doctor.js +202 -2
- package/installer/index.js +174 -88
- package/installer/menubar.js +262 -0
- package/installer/product-stats.js +93 -0
- package/installer/profile.js +75 -0
- package/installer/prompts.js +303 -41
- package/installer/services.js +487 -0
- package/installer/ui.js +190 -0
- package/installer/update.js +212 -95
- package/knowledge/skills-manifest.json +1 -1
- package/package.json +6 -1
- package/pyproject.toml +1 -1
- package/scripts/harness_gen.py +98 -1
package/installer/prompts.js
CHANGED
|
@@ -2,12 +2,31 @@
|
|
|
2
2
|
* Interactive prompts for ArkaOS installer.
|
|
3
3
|
* Asks user for directories, language, market, preferences.
|
|
4
4
|
* Nothing is hardcoded — everything comes from the user.
|
|
5
|
+
*
|
|
6
|
+
* Foundation PR-3: the wizard renders through @clack/prompts when the
|
|
7
|
+
* terminal supports it (see installer/ui.js) and falls back to the
|
|
8
|
+
* original readline flow otherwise. Both paths return the same config
|
|
9
|
+
* shape, now including `installProfile` (persisted by index.js;
|
|
10
|
+
* provisioning of the non-essential tiers is PR-4).
|
|
5
11
|
*/
|
|
6
12
|
|
|
7
13
|
import { createInterface } from "node:readline";
|
|
8
14
|
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
-
import { join } from "node:path";
|
|
15
|
+
import { join, dirname } from "node:path";
|
|
10
16
|
import { homedir } from "node:os";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
import { getUi } from "./ui.js";
|
|
19
|
+
import {
|
|
20
|
+
DEFAULT_PROFILE,
|
|
21
|
+
INSTALL_PROFILES,
|
|
22
|
+
PROFILE_HINTS,
|
|
23
|
+
normalizeProfileFlag,
|
|
24
|
+
} from "./profile.js";
|
|
25
|
+
|
|
26
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
const VERSION = JSON.parse(
|
|
28
|
+
readFileSync(join(__dirname, "..", "package.json"), "utf-8"),
|
|
29
|
+
).version;
|
|
11
30
|
|
|
12
31
|
// Readline interface is lazily created so headless upgrade runs can
|
|
13
32
|
// return without ever opening stdin. Eagerly constructing the interface
|
|
@@ -64,6 +83,36 @@ function askChoice(question, options) {
|
|
|
64
83
|
// the user where their install lives.
|
|
65
84
|
const DEFAULT_INSTALL_DIR = join(homedir(), ".arkaos");
|
|
66
85
|
|
|
86
|
+
// Shared option lists — one source for both the clack and readline wizards.
|
|
87
|
+
const LANGUAGE_OPTIONS = [
|
|
88
|
+
{ label: "English", value: "en" },
|
|
89
|
+
{ label: "Português", value: "pt" },
|
|
90
|
+
{ label: "Español", value: "es" },
|
|
91
|
+
{ label: "Français", value: "fr" },
|
|
92
|
+
{ label: "Deutsch", value: "de" },
|
|
93
|
+
{ label: "Italiano", value: "it" },
|
|
94
|
+
{ label: "中文 (Chinese)", value: "zh" },
|
|
95
|
+
{ label: "日本語 (Japanese)", value: "ja" },
|
|
96
|
+
{ label: "한국어 (Korean)", value: "ko" },
|
|
97
|
+
{ label: "Other", value: "other" },
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
const ROLE_OPTIONS = [
|
|
101
|
+
{ label: "Developer / Engineer", value: "developer" },
|
|
102
|
+
{ label: "Founder / CEO", value: "founder" },
|
|
103
|
+
{ label: "Marketing / Growth", value: "marketing" },
|
|
104
|
+
{ label: "Product Manager", value: "product" },
|
|
105
|
+
{ label: "Designer", value: "designer" },
|
|
106
|
+
{ label: "Consultant / Agency", value: "consultant" },
|
|
107
|
+
{ label: "Other", value: "other" },
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
const PROFILE_OPTIONS = INSTALL_PROFILES.map((value) => ({
|
|
111
|
+
value,
|
|
112
|
+
label: value,
|
|
113
|
+
hint: PROFILE_HINTS[value],
|
|
114
|
+
}));
|
|
115
|
+
|
|
67
116
|
// When called on an upgrade and a valid profile.json already exists,
|
|
68
117
|
// honor the rule documented in .claude/rules/node-installer.md:
|
|
69
118
|
//
|
|
@@ -81,6 +130,10 @@ const DEFAULT_INSTALL_DIR = join(homedir(), ".arkaos");
|
|
|
81
130
|
//
|
|
82
131
|
// - User metadata fields (language, market, role, company,
|
|
83
132
|
// projectsDir, vaultPath) come from profile.json.
|
|
133
|
+
// - installProfile is carried from profile.json when present and
|
|
134
|
+
// valid; older profiles without the field default to "essential"
|
|
135
|
+
// (Foundation PR-3 — the profile only gates provisioning in PR-4,
|
|
136
|
+
// so defaulting is safe for existing installs).
|
|
84
137
|
// - installDir is the directory we found the profile in.
|
|
85
138
|
// - Feature flags (installDashboard, installKnowledge,
|
|
86
139
|
// installTranscription) default to false on upgrade — an upgrade
|
|
@@ -93,7 +146,7 @@ const DEFAULT_INSTALL_DIR = join(homedir(), ".arkaos");
|
|
|
93
146
|
// Returns null when the short-circuit conditions are not met, which
|
|
94
147
|
// falls through to the interactive wizard below (fresh install or
|
|
95
148
|
// upgrade with a corrupt/missing profile).
|
|
96
|
-
function loadExistingProfileConfig(installDir) {
|
|
149
|
+
export function loadExistingProfileConfig(installDir) {
|
|
97
150
|
const profilePath = join(installDir, "profile.json");
|
|
98
151
|
if (!existsSync(profilePath)) return null;
|
|
99
152
|
try {
|
|
@@ -110,6 +163,8 @@ function loadExistingProfileConfig(installDir) {
|
|
|
110
163
|
projectsDir: profile.projectsDir || join(homedir(), "Projects"),
|
|
111
164
|
vaultPath: profile.vaultPath || "",
|
|
112
165
|
installDir,
|
|
166
|
+
installProfile:
|
|
167
|
+
normalizeProfileFlag(profile.installProfile) || DEFAULT_PROFILE,
|
|
113
168
|
// Upgrades preserve the existing install footprint; they do not
|
|
114
169
|
// re-opt-in to optional features. A user who wants to flip a
|
|
115
170
|
// feature flag on upgrade can re-run with the wizard.
|
|
@@ -130,7 +185,7 @@ function loadExistingProfileConfig(installDir) {
|
|
|
130
185
|
}
|
|
131
186
|
}
|
|
132
187
|
|
|
133
|
-
export async function runSetupPrompts(isUpgrade = false) {
|
|
188
|
+
export async function runSetupPrompts(isUpgrade = false, opts = {}) {
|
|
134
189
|
// Headless upgrade short-circuit. See loadExistingProfileConfig.
|
|
135
190
|
if (isUpgrade) {
|
|
136
191
|
const existing = loadExistingProfileConfig(DEFAULT_INSTALL_DIR);
|
|
@@ -145,6 +200,7 @@ export async function runSetupPrompts(isUpgrade = false) {
|
|
|
145
200
|
Role: ${existing.role}
|
|
146
201
|
Company: ${existing.company || "(not set)"}
|
|
147
202
|
Install dir: ${existing.installDir}
|
|
203
|
+
Profile: ${existing.installProfile}
|
|
148
204
|
|
|
149
205
|
Optional features are NOT re-installed on upgrade.
|
|
150
206
|
Re-run without --force to flip feature flags interactively.
|
|
@@ -153,6 +209,200 @@ export async function runSetupPrompts(isUpgrade = false) {
|
|
|
153
209
|
}
|
|
154
210
|
}
|
|
155
211
|
|
|
212
|
+
// A valid --profile flag skips the profile question in both wizards.
|
|
213
|
+
const profileFlag = normalizeProfileFlag(opts.profileFlag);
|
|
214
|
+
|
|
215
|
+
const ui = await getUi();
|
|
216
|
+
if (ui.isFancy()) {
|
|
217
|
+
return runClackWizard(ui, profileFlag);
|
|
218
|
+
}
|
|
219
|
+
return runReadlineWizard(profileFlag);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ── Fancy wizard (@clack/prompts) ─────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
async function runClackWizard(ui, profileFlag) {
|
|
225
|
+
const p = ui.clack;
|
|
226
|
+
const pc = ui.colors;
|
|
227
|
+
|
|
228
|
+
// Ctrl+C anywhere cancels the whole install, mirroring the readline
|
|
229
|
+
// wizard's "Installation cancelled." exit.
|
|
230
|
+
const guard = (value) => {
|
|
231
|
+
if (p.isCancel(value)) {
|
|
232
|
+
p.cancel("Installation cancelled.");
|
|
233
|
+
process.exit(0);
|
|
234
|
+
}
|
|
235
|
+
return value;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
p.intro(pc.inverse(pc.bold(` ▲ ARKA OS v${VERSION} — Setup `)));
|
|
239
|
+
|
|
240
|
+
const config = {};
|
|
241
|
+
|
|
242
|
+
// ── Identity ──
|
|
243
|
+
p.log.step(pc.bold("Identity"));
|
|
244
|
+
config.language = guard(
|
|
245
|
+
await p.select({
|
|
246
|
+
message: "What is your primary language?",
|
|
247
|
+
options: LANGUAGE_OPTIONS.map(({ label, value }) => ({ value, label })),
|
|
248
|
+
initialValue: "en",
|
|
249
|
+
}),
|
|
250
|
+
);
|
|
251
|
+
if (config.language === "other") {
|
|
252
|
+
config.language =
|
|
253
|
+
String(
|
|
254
|
+
guard(
|
|
255
|
+
await p.text({
|
|
256
|
+
message: "Enter language code",
|
|
257
|
+
placeholder: "e.g., nl, pl, ru",
|
|
258
|
+
}),
|
|
259
|
+
) || "",
|
|
260
|
+
).trim() || "en";
|
|
261
|
+
}
|
|
262
|
+
config.market = String(
|
|
263
|
+
guard(
|
|
264
|
+
await p.text({
|
|
265
|
+
message: "What is your primary market/country?",
|
|
266
|
+
placeholder: "e.g., United States, Portugal, Global",
|
|
267
|
+
}),
|
|
268
|
+
) || "",
|
|
269
|
+
).trim();
|
|
270
|
+
config.role = guard(
|
|
271
|
+
await p.select({
|
|
272
|
+
message: "What best describes your role?",
|
|
273
|
+
options: ROLE_OPTIONS.map(({ label, value }) => ({ value, label })),
|
|
274
|
+
initialValue: "developer",
|
|
275
|
+
}),
|
|
276
|
+
);
|
|
277
|
+
config.company = String(
|
|
278
|
+
guard(
|
|
279
|
+
await p.text({
|
|
280
|
+
message: "Company or organization name (optional)",
|
|
281
|
+
defaultValue: "",
|
|
282
|
+
}),
|
|
283
|
+
) || "",
|
|
284
|
+
).trim();
|
|
285
|
+
|
|
286
|
+
// ── Directories ──
|
|
287
|
+
p.log.step(pc.bold("Directories"));
|
|
288
|
+
config.projectsDir =
|
|
289
|
+
String(
|
|
290
|
+
guard(
|
|
291
|
+
await p.text({
|
|
292
|
+
message: "Where are your projects?",
|
|
293
|
+
initialValue: join(homedir(), "Projects"),
|
|
294
|
+
}),
|
|
295
|
+
) || "",
|
|
296
|
+
).trim() || join(homedir(), "Projects");
|
|
297
|
+
config.vaultPath = String(
|
|
298
|
+
guard(
|
|
299
|
+
await p.text({
|
|
300
|
+
message: "Where is your Obsidian vault? (leave empty if none)",
|
|
301
|
+
defaultValue: "",
|
|
302
|
+
}),
|
|
303
|
+
) || "",
|
|
304
|
+
).trim();
|
|
305
|
+
if (config.vaultPath && !existsSync(config.vaultPath)) {
|
|
306
|
+
const create = guard(
|
|
307
|
+
await p.confirm({
|
|
308
|
+
message: `Directory not found: ${config.vaultPath} — create it?`,
|
|
309
|
+
initialValue: false,
|
|
310
|
+
}),
|
|
311
|
+
);
|
|
312
|
+
if (!create) config.vaultPath = "";
|
|
313
|
+
}
|
|
314
|
+
config.installDir =
|
|
315
|
+
String(
|
|
316
|
+
guard(
|
|
317
|
+
await p.text({
|
|
318
|
+
message: "ArkaOS data directory",
|
|
319
|
+
initialValue: DEFAULT_INSTALL_DIR,
|
|
320
|
+
}),
|
|
321
|
+
) || "",
|
|
322
|
+
).trim() || DEFAULT_INSTALL_DIR;
|
|
323
|
+
|
|
324
|
+
// ── Installation profile ──
|
|
325
|
+
p.log.step(pc.bold("Installation profile"));
|
|
326
|
+
if (profileFlag) {
|
|
327
|
+
config.installProfile = profileFlag;
|
|
328
|
+
p.log.info(`Profile: ${profileFlag} (from --profile)`);
|
|
329
|
+
} else {
|
|
330
|
+
config.installProfile = guard(
|
|
331
|
+
await p.select({
|
|
332
|
+
message: "Which installation profile?",
|
|
333
|
+
options: PROFILE_OPTIONS,
|
|
334
|
+
initialValue: DEFAULT_PROFILE,
|
|
335
|
+
}),
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ── Optional features ──
|
|
340
|
+
p.log.step(pc.bold("Optional features"));
|
|
341
|
+
config.installDashboard = guard(
|
|
342
|
+
await p.confirm({
|
|
343
|
+
message: "Install monitoring dashboard?",
|
|
344
|
+
initialValue: true,
|
|
345
|
+
}),
|
|
346
|
+
);
|
|
347
|
+
config.installKnowledge = guard(
|
|
348
|
+
await p.confirm({
|
|
349
|
+
message: "Install knowledge base (vector DB)?",
|
|
350
|
+
initialValue: true,
|
|
351
|
+
}),
|
|
352
|
+
);
|
|
353
|
+
config.installTranscription = guard(
|
|
354
|
+
await p.confirm({
|
|
355
|
+
message: "Install audio transcription (Whisper)?",
|
|
356
|
+
initialValue: false,
|
|
357
|
+
}),
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
// ── API keys (optional) ──
|
|
361
|
+
p.log.step(pc.bold("API keys (optional, can be configured later)"));
|
|
362
|
+
config.openaiKey = String(
|
|
363
|
+
guard(
|
|
364
|
+
await p.password({
|
|
365
|
+
message: "OpenAI API key (Whisper, embeddings — Enter to skip)",
|
|
366
|
+
mask: "•",
|
|
367
|
+
}),
|
|
368
|
+
) || "",
|
|
369
|
+
).trim();
|
|
370
|
+
config.googleKey = String(
|
|
371
|
+
guard(
|
|
372
|
+
await p.password({
|
|
373
|
+
message: "Google API key (Gemini, Nano Banana — Enter to skip)",
|
|
374
|
+
mask: "•",
|
|
375
|
+
}),
|
|
376
|
+
) || "",
|
|
377
|
+
).trim();
|
|
378
|
+
config.falKey = String(
|
|
379
|
+
guard(
|
|
380
|
+
await p.password({
|
|
381
|
+
message: "fal.ai API key (image/video generation — Enter to skip)",
|
|
382
|
+
mask: "•",
|
|
383
|
+
}),
|
|
384
|
+
) || "",
|
|
385
|
+
).trim();
|
|
386
|
+
|
|
387
|
+
// ── Summary + confirm ──
|
|
388
|
+
p.note(formatSummary(config), "Configuration Summary");
|
|
389
|
+
const confirmed = guard(
|
|
390
|
+
await p.confirm({
|
|
391
|
+
message: "Proceed with this configuration?",
|
|
392
|
+
initialValue: true,
|
|
393
|
+
}),
|
|
394
|
+
);
|
|
395
|
+
if (!confirmed) {
|
|
396
|
+
p.cancel("Installation cancelled.");
|
|
397
|
+
process.exit(0);
|
|
398
|
+
}
|
|
399
|
+
p.outro("Configuration complete — installing.");
|
|
400
|
+
return config;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// ── Plain wizard (readline fallback) ──────────────────────────────────────
|
|
404
|
+
|
|
405
|
+
async function runReadlineWizard(profileFlag) {
|
|
156
406
|
console.log(`
|
|
157
407
|
╔══════════════════════════════════════════════════════╗
|
|
158
408
|
║ ArkaOS Setup — Let's configure your environment ║
|
|
@@ -162,18 +412,10 @@ export async function runSetupPrompts(isUpgrade = false) {
|
|
|
162
412
|
const config = {};
|
|
163
413
|
|
|
164
414
|
// ── Language ──
|
|
165
|
-
config.language = await askChoice(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
{ label: "Français", value: "fr" },
|
|
170
|
-
{ label: "Deutsch", value: "de" },
|
|
171
|
-
{ label: "Italiano", value: "it" },
|
|
172
|
-
{ label: "中文 (Chinese)", value: "zh" },
|
|
173
|
-
{ label: "日本語 (Japanese)", value: "ja" },
|
|
174
|
-
{ label: "한국어 (Korean)", value: "ko" },
|
|
175
|
-
{ label: "Other", value: "other" },
|
|
176
|
-
]);
|
|
415
|
+
config.language = await askChoice(
|
|
416
|
+
"What is your primary language?",
|
|
417
|
+
LANGUAGE_OPTIONS,
|
|
418
|
+
);
|
|
177
419
|
|
|
178
420
|
if (config.language === "other") {
|
|
179
421
|
config.language = await ask("Enter language code (e.g., nl, pl, ru)");
|
|
@@ -187,15 +429,7 @@ export async function runSetupPrompts(isUpgrade = false) {
|
|
|
187
429
|
}
|
|
188
430
|
|
|
189
431
|
// ── Role ──
|
|
190
|
-
config.role = await askChoice("What best describes your role?",
|
|
191
|
-
{ label: "Developer / Engineer", value: "developer" },
|
|
192
|
-
{ label: "Founder / CEO", value: "founder" },
|
|
193
|
-
{ label: "Marketing / Growth", value: "marketing" },
|
|
194
|
-
{ label: "Product Manager", value: "product" },
|
|
195
|
-
{ label: "Designer", value: "designer" },
|
|
196
|
-
{ label: "Consultant / Agency", value: "consultant" },
|
|
197
|
-
{ label: "Other", value: "other" },
|
|
198
|
-
]);
|
|
432
|
+
config.role = await askChoice("What best describes your role?", ROLE_OPTIONS);
|
|
199
433
|
|
|
200
434
|
// ── Company ──
|
|
201
435
|
config.company = await ask("Company or organization name (optional)", "");
|
|
@@ -220,9 +454,25 @@ export async function runSetupPrompts(isUpgrade = false) {
|
|
|
220
454
|
|
|
221
455
|
config.installDir = await ask(
|
|
222
456
|
"ArkaOS data directory",
|
|
223
|
-
|
|
457
|
+
DEFAULT_INSTALL_DIR
|
|
224
458
|
);
|
|
225
459
|
|
|
460
|
+
// ── Installation profile ──
|
|
461
|
+
console.log("\n ── Installation Profile ──\n");
|
|
462
|
+
|
|
463
|
+
if (profileFlag) {
|
|
464
|
+
config.installProfile = profileFlag;
|
|
465
|
+
console.log(` Profile: ${profileFlag} (from --profile)`);
|
|
466
|
+
} else {
|
|
467
|
+
config.installProfile = await askChoice(
|
|
468
|
+
"Which installation profile?",
|
|
469
|
+
PROFILE_OPTIONS.map(({ value, hint }) => ({
|
|
470
|
+
value,
|
|
471
|
+
label: `${value} — ${hint}`,
|
|
472
|
+
})),
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
|
|
226
476
|
// ── Features ──
|
|
227
477
|
console.log("\n ── Optional Features ──\n");
|
|
228
478
|
|
|
@@ -241,32 +491,44 @@ export async function runSetupPrompts(isUpgrade = false) {
|
|
|
241
491
|
console.log(`
|
|
242
492
|
── Configuration Summary ──
|
|
243
493
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
Projects dir: ${config.projectsDir}
|
|
249
|
-
Obsidian vault: ${config.vaultPath || "(none)"}
|
|
250
|
-
Install dir: ${config.installDir}
|
|
251
|
-
Dashboard: ${config.installDashboard ? "Yes" : "No"}
|
|
252
|
-
Knowledge DB: ${config.installKnowledge ? "Yes" : "No"}
|
|
253
|
-
Transcription: ${config.installTranscription ? "Yes" : "No"}
|
|
254
|
-
OpenAI key: ${config.openaiKey ? "configured" : "not set"}
|
|
255
|
-
Google key: ${config.googleKey ? "configured" : "not set"}
|
|
256
|
-
fal.ai key: ${config.falKey ? "configured" : "not set"}
|
|
494
|
+
${formatSummary(config)
|
|
495
|
+
.split("\n")
|
|
496
|
+
.map((l) => ` ${l}`)
|
|
497
|
+
.join("\n")}
|
|
257
498
|
`);
|
|
258
499
|
|
|
259
500
|
const confirmed = await askYN("Proceed with this configuration?", true);
|
|
260
501
|
if (!confirmed) {
|
|
261
502
|
console.log("\n Installation cancelled.\n");
|
|
262
|
-
rl
|
|
503
|
+
rl?.close();
|
|
263
504
|
process.exit(0);
|
|
264
505
|
}
|
|
265
506
|
|
|
266
|
-
rl
|
|
507
|
+
rl?.close();
|
|
267
508
|
return config;
|
|
268
509
|
}
|
|
269
510
|
|
|
511
|
+
function formatSummary(config) {
|
|
512
|
+
return [
|
|
513
|
+
`Language: ${config.language}`,
|
|
514
|
+
`Market: ${config.market || "(not set)"}`,
|
|
515
|
+
`Role: ${config.role}`,
|
|
516
|
+
`Company: ${config.company || "(not set)"}`,
|
|
517
|
+
`Projects dir: ${config.projectsDir}`,
|
|
518
|
+
`Obsidian vault: ${config.vaultPath || "(none)"}`,
|
|
519
|
+
`Install dir: ${config.installDir}`,
|
|
520
|
+
`Profile: ${config.installProfile}`,
|
|
521
|
+
`Dashboard: ${config.installDashboard ? "Yes" : "No"}`,
|
|
522
|
+
`Knowledge DB: ${config.installKnowledge ? "Yes" : "No"}`,
|
|
523
|
+
`Transcription: ${config.installTranscription ? "Yes" : "No"}`,
|
|
524
|
+
`OpenAI key: ${config.openaiKey ? "configured" : "not set"}`,
|
|
525
|
+
`Google key: ${config.googleKey ? "configured" : "not set"}`,
|
|
526
|
+
`fal.ai key: ${config.falKey ? "configured" : "not set"}`,
|
|
527
|
+
].join("\n");
|
|
528
|
+
}
|
|
529
|
+
|
|
270
530
|
export function closePrompts() {
|
|
271
|
-
|
|
531
|
+
// rl is only created when the readline wizard actually asked a
|
|
532
|
+
// question; a fancy/clack or short-circuited run leaves it null.
|
|
533
|
+
try { rl?.close(); } catch { /* already closed */ }
|
|
272
534
|
}
|