create-vellaveto 4.0.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.
@@ -0,0 +1,232 @@
1
+ /**
2
+ * TOML configuration generator.
3
+ *
4
+ * Output must match vellaveto-server/src/setup_wizard.rs generate_config_toml()
5
+ * (lines ~1596-1844) so configs are interchangeable between the web wizard and
6
+ * this CLI wizard.
7
+ */
8
+ import { escapeTomlString } from "../utils.js";
9
+ export function generateToml(state) {
10
+ let toml = "";
11
+ // Header
12
+ toml += "# Vellaveto Configuration\n";
13
+ toml += "# Generated by the setup wizard\n\n";
14
+ // API key note
15
+ if (state.apiKey) {
16
+ toml +=
17
+ "# API key configured via wizard — set VELLAVETO_API_KEY environment variable\n\n";
18
+ }
19
+ // CORS origins
20
+ if (state.corsOrigins.length > 0) {
21
+ toml +=
22
+ "# Allowed CORS origins (also settable via VELLAVETO_CORS_ORIGINS env var)\n";
23
+ toml += "allowed_origins = [";
24
+ for (let i = 0; i < state.corsOrigins.length; i++) {
25
+ if (i > 0)
26
+ toml += ", ";
27
+ toml += `"${escapeTomlString(state.corsOrigins[i])}"`;
28
+ }
29
+ toml += "]\n\n";
30
+ }
31
+ // Policies
32
+ toml += "# ─── Policies ───────────────────────────────────────────────\n\n";
33
+ toml += generatePolicyPreset(state.policyPreset);
34
+ // Detection
35
+ toml += "# ─── Detection ──────────────────────────────────────────────\n\n";
36
+ toml += "[injection]\n";
37
+ toml += `enabled = ${state.injectionEnabled}\n`;
38
+ if (state.injectionEnabled) {
39
+ toml += `blocking = ${state.injectionBlocking}\n`;
40
+ }
41
+ toml += "\n";
42
+ toml += "[dlp]\n";
43
+ toml += `enabled = ${state.dlpEnabled}\n`;
44
+ if (state.dlpEnabled) {
45
+ toml += `blocking = ${state.dlpBlocking}\n`;
46
+ }
47
+ toml += "\n";
48
+ if (state.behavioralEnabled) {
49
+ toml += "[behavioral]\n";
50
+ toml += "enabled = true\n\n";
51
+ }
52
+ // Audit
53
+ toml += "# ─── Audit ──────────────────────────────────────────────────\n\n";
54
+ toml += "[audit]\n";
55
+ toml += `redaction_level = "${escapeTomlString(state.redactionLevel)}"\n`;
56
+ if (state.checkpointInterval > 0) {
57
+ toml += `checkpoint_interval = ${state.checkpointInterval}\n`;
58
+ }
59
+ toml += "\n";
60
+ // Audit export
61
+ if (state.auditExportFormat !== "none") {
62
+ toml += "[audit_export]\n";
63
+ toml += `format = "${escapeTomlString(state.auditExportFormat)}"\n`;
64
+ if (state.auditExportTarget) {
65
+ toml += `target = "${escapeTomlString(state.auditExportTarget)}"\n`;
66
+ }
67
+ toml += "\n";
68
+ }
69
+ // Compliance
70
+ const hasCompliance = state.complianceFrameworks.length > 0;
71
+ if (hasCompliance) {
72
+ toml +=
73
+ "# ─── Compliance ─────────────────────────────────────────────\n\n";
74
+ toml += "[compliance]\n";
75
+ if (state.complianceFrameworks.includes("eu_ai_act")) {
76
+ toml += "\n[compliance.eu_ai_act]\n";
77
+ toml += "enabled = true\n";
78
+ toml += "transparency_marking = true\n";
79
+ }
80
+ if (state.complianceFrameworks.includes("soc2")) {
81
+ toml += "\n[compliance.soc2]\n";
82
+ toml += "enabled = true\n";
83
+ }
84
+ if (state.complianceFrameworks.includes("iso42001")) {
85
+ toml += "\n[compliance.iso42001]\n";
86
+ toml += "enabled = true\n";
87
+ }
88
+ if (state.complianceFrameworks.includes("nis2")) {
89
+ toml +=
90
+ "\n# NIS2 compliance enabled — incident reporting and supply chain checks active\n";
91
+ }
92
+ if (state.complianceFrameworks.includes("dora")) {
93
+ toml +=
94
+ "\n# DORA compliance enabled — ICT risk management and incident tracking active\n";
95
+ }
96
+ toml += "\n";
97
+ }
98
+ return toml;
99
+ }
100
+ function generatePolicyPreset(preset) {
101
+ switch (preset) {
102
+ case "strict":
103
+ return generateStrictPreset();
104
+ case "balanced":
105
+ return generateBalancedPreset();
106
+ case "permissive":
107
+ return generatePermissivePreset();
108
+ }
109
+ }
110
+ function generateStrictPreset() {
111
+ let toml = "";
112
+ toml +=
113
+ "# Policy preset: Strict (deny-by-default, maximum security)\n\n";
114
+ // Default deny
115
+ toml += "[[policies]]\n";
116
+ toml += 'id = "default-deny"\n';
117
+ toml += 'name = "Default deny all"\n';
118
+ toml += 'policy_type = "Deny"\n';
119
+ toml += "priority = 0\n";
120
+ toml += 'tool = "*"\n';
121
+ toml += 'function = "*"\n\n';
122
+ // Block credentials
123
+ toml += "[[policies]]\n";
124
+ toml += 'id = "block-credentials"\n';
125
+ toml += 'name = "Block credential access"\n';
126
+ toml += 'policy_type = "Deny"\n';
127
+ toml += "priority = 100\n";
128
+ toml += 'tool = "*"\n';
129
+ toml += 'function = "*"\n\n';
130
+ toml += "[policies.path_rules]\n";
131
+ toml +=
132
+ 'blocked_patterns = ["**/.env", "**/*.key", "**/*.pem", "**/credentials*", "**/.ssh/**", "**/.aws/**"]\n\n';
133
+ // Block exfiltration
134
+ toml += "[[policies]]\n";
135
+ toml += 'id = "block-exfiltration"\n';
136
+ toml += 'name = "Block data exfiltration"\n';
137
+ toml += 'policy_type = "Deny"\n';
138
+ toml += "priority = 100\n";
139
+ toml += 'tool = "*"\n';
140
+ toml += 'function = "*"\n\n';
141
+ toml += "[policies.network_rules]\n";
142
+ toml +=
143
+ 'blocked_domains = ["*.pastebin.com", "*.transfer.sh", "*.ngrok.io"]\n\n';
144
+ // Require approval for destructive
145
+ toml += "[[policies]]\n";
146
+ toml += 'id = "approve-destructive"\n';
147
+ toml += 'name = "Require approval for destructive operations"\n';
148
+ toml += 'policy_type = "RequireApproval"\n';
149
+ toml += "priority = 50\n";
150
+ toml += 'tool = "*"\n';
151
+ toml += 'function = "*"\n\n';
152
+ toml += "[policies.path_rules]\n";
153
+ toml += 'write_patterns = ["**/*"]\n\n';
154
+ return toml;
155
+ }
156
+ function generateBalancedPreset() {
157
+ let toml = "";
158
+ toml +=
159
+ "# Policy preset: Balanced (deny-by-default, read allowed, writes require approval)\n\n";
160
+ // Default deny
161
+ toml += "[[policies]]\n";
162
+ toml += 'id = "default-deny"\n';
163
+ toml += 'name = "Default deny all"\n';
164
+ toml += 'policy_type = "Deny"\n';
165
+ toml += "priority = 0\n";
166
+ toml += 'tool = "*"\n';
167
+ toml += 'function = "*"\n\n';
168
+ // Block credentials
169
+ toml += "[[policies]]\n";
170
+ toml += 'id = "block-credentials"\n';
171
+ toml += 'name = "Block credential access"\n';
172
+ toml += 'policy_type = "Deny"\n';
173
+ toml += "priority = 100\n";
174
+ toml += 'tool = "*"\n';
175
+ toml += 'function = "*"\n\n';
176
+ toml += "[policies.path_rules]\n";
177
+ toml +=
178
+ 'blocked_patterns = ["**/.env", "**/*.key", "**/*.pem", "**/credentials*", "**/.ssh/**", "**/.aws/**"]\n\n';
179
+ // Allow reads
180
+ toml += "[[policies]]\n";
181
+ toml += 'id = "allow-reads"\n';
182
+ toml += 'name = "Allow file reads"\n';
183
+ toml += 'policy_type = "Allow"\n';
184
+ toml += "priority = 50\n";
185
+ toml += 'tool = "*"\n';
186
+ toml += 'function = "read*"\n\n';
187
+ // Require approval for writes
188
+ toml += "[[policies]]\n";
189
+ toml += 'id = "approve-writes"\n';
190
+ toml += 'name = "Require approval for file writes"\n';
191
+ toml += 'policy_type = "RequireApproval"\n';
192
+ toml += "priority = 50\n";
193
+ toml += 'tool = "*"\n';
194
+ toml += 'function = "write*"\n\n';
195
+ return toml;
196
+ }
197
+ function generatePermissivePreset() {
198
+ let toml = "";
199
+ toml +=
200
+ "# Policy preset: Permissive (allow-by-default, block credentials and exfiltration)\n\n";
201
+ // Default allow
202
+ toml += "[[policies]]\n";
203
+ toml += 'id = "default-allow"\n';
204
+ toml += 'name = "Default allow all"\n';
205
+ toml += 'policy_type = "Allow"\n';
206
+ toml += "priority = 0\n";
207
+ toml += 'tool = "*"\n';
208
+ toml += 'function = "*"\n\n';
209
+ // Block credentials
210
+ toml += "[[policies]]\n";
211
+ toml += 'id = "block-credentials"\n';
212
+ toml += 'name = "Block credential access"\n';
213
+ toml += 'policy_type = "Deny"\n';
214
+ toml += "priority = 100\n";
215
+ toml += 'tool = "*"\n';
216
+ toml += 'function = "*"\n\n';
217
+ toml += "[policies.path_rules]\n";
218
+ toml +=
219
+ 'blocked_patterns = ["**/.env", "**/*.key", "**/*.pem", "**/credentials*", "**/.ssh/**", "**/.aws/**"]\n\n';
220
+ // Block exfiltration
221
+ toml += "[[policies]]\n";
222
+ toml += 'id = "block-exfiltration"\n';
223
+ toml += 'name = "Block data exfiltration"\n';
224
+ toml += 'policy_type = "Deny"\n';
225
+ toml += "priority = 100\n";
226
+ toml += 'tool = "*"\n';
227
+ toml += 'function = "*"\n\n';
228
+ toml += "[policies.network_rules]\n";
229
+ toml +=
230
+ 'blocked_domains = ["*.pastebin.com", "*.transfer.sh", "*.ngrok.io"]\n\n';
231
+ return toml;
232
+ }
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * create-vellaveto — CLI setup wizard for Vellaveto MCP Tool Firewall
4
+ *
5
+ * Usage:
6
+ * npx create-vellaveto Interactive wizard (creates ./vellaveto/)
7
+ * npx create-vellaveto my-project Write files to ./my-project/
8
+ * npx create-vellaveto --help Show help
9
+ * npx create-vellaveto --version Show version
10
+ */
11
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * create-vellaveto — CLI setup wizard for Vellaveto MCP Tool Firewall
4
+ *
5
+ * Usage:
6
+ * npx create-vellaveto Interactive wizard (creates ./vellaveto/)
7
+ * npx create-vellaveto my-project Write files to ./my-project/
8
+ * npx create-vellaveto --help Show help
9
+ * npx create-vellaveto --version Show version
10
+ */
11
+ import { VERSION } from "./constants.js";
12
+ import { runWizard } from "./wizard.js";
13
+ function printHelp() {
14
+ console.log(`
15
+ create-vellaveto v${VERSION}
16
+
17
+ Setup wizard for Vellaveto — MCP Tool Firewall
18
+
19
+ Usage:
20
+ npx create-vellaveto [project-directory]
21
+
22
+ Arguments:
23
+ project-directory Directory to create (default: vellaveto)
24
+
25
+ Options:
26
+ --help, -h Show this help message
27
+ --version, -v Show version number
28
+
29
+ Examples:
30
+ npx create-vellaveto # Creates ./vellaveto/ with all files
31
+ npx create-vellaveto my-project # Creates ./my-project/ with all files
32
+ `);
33
+ }
34
+ function main() {
35
+ const args = process.argv.slice(2);
36
+ let projectDir;
37
+ for (let i = 0; i < args.length; i++) {
38
+ const arg = args[i];
39
+ switch (arg) {
40
+ case "--help":
41
+ case "-h":
42
+ printHelp();
43
+ process.exit(0);
44
+ break;
45
+ case "--version":
46
+ case "-v":
47
+ console.log(VERSION);
48
+ process.exit(0);
49
+ break;
50
+ // Keep --output as hidden alias for backwards compat
51
+ case "--output":
52
+ case "-o":
53
+ i++;
54
+ if (i >= args.length) {
55
+ console.error("Error: --output requires a directory path");
56
+ process.exit(1);
57
+ }
58
+ projectDir = args[i];
59
+ break;
60
+ default:
61
+ if (arg.startsWith("-")) {
62
+ console.error(`Unknown option: ${arg}`);
63
+ console.error("Run with --help for usage information.");
64
+ process.exit(1);
65
+ }
66
+ projectDir = arg;
67
+ }
68
+ }
69
+ runWizard(projectDir).catch((err) => {
70
+ console.error("Fatal error:", err);
71
+ process.exit(1);
72
+ });
73
+ }
74
+ main();
@@ -0,0 +1,2 @@
1
+ import type { WizardState } from "../types.js";
2
+ export declare function auditStep(state: WizardState): Promise<void>;
@@ -0,0 +1,109 @@
1
+ import * as p from "@clack/prompts";
2
+ export async function auditStep(state) {
3
+ const redaction = await p.select({
4
+ message: "Audit log redaction level",
5
+ options: [
6
+ {
7
+ value: "off",
8
+ label: "Off",
9
+ hint: "No redaction — full parameters logged (dev only)",
10
+ },
11
+ {
12
+ value: "low",
13
+ label: "Low (recommended)",
14
+ hint: "Redact known secrets (API keys, tokens)",
15
+ },
16
+ {
17
+ value: "high",
18
+ label: "High",
19
+ hint: "Redact all parameter values",
20
+ },
21
+ ],
22
+ initialValue: "low",
23
+ });
24
+ if (p.isCancel(redaction)) {
25
+ p.cancel("Setup cancelled.");
26
+ process.exit(0);
27
+ }
28
+ state.redactionLevel = redaction;
29
+ const format = await p.select({
30
+ message: "Audit export format",
31
+ options: [
32
+ {
33
+ value: "none",
34
+ label: "None",
35
+ hint: "Local file logging only",
36
+ },
37
+ {
38
+ value: "jsonl",
39
+ label: "JSONL",
40
+ hint: "JSON Lines — one event per line",
41
+ },
42
+ {
43
+ value: "cef",
44
+ label: "CEF",
45
+ hint: "Common Event Format — for SIEM integration",
46
+ },
47
+ {
48
+ value: "webhook",
49
+ label: "Webhook",
50
+ hint: "POST events to an HTTP endpoint",
51
+ },
52
+ ],
53
+ });
54
+ if (p.isCancel(format)) {
55
+ p.cancel("Setup cancelled.");
56
+ process.exit(0);
57
+ }
58
+ state.auditExportFormat = format;
59
+ if (format === "webhook") {
60
+ const target = await p.text({
61
+ message: "Webhook URL",
62
+ placeholder: "https://example.com/vellaveto-events",
63
+ validate(value) {
64
+ try {
65
+ const url = new URL(value);
66
+ if (url.protocol !== "https:" && url.protocol !== "http:") {
67
+ return "URL must use http:// or https://";
68
+ }
69
+ }
70
+ catch {
71
+ return "Please enter a valid URL";
72
+ }
73
+ },
74
+ });
75
+ if (p.isCancel(target)) {
76
+ p.cancel("Setup cancelled.");
77
+ process.exit(0);
78
+ }
79
+ state.auditExportTarget = target;
80
+ }
81
+ else if (format === "jsonl" || format === "cef") {
82
+ const target = await p.text({
83
+ message: "Export file path",
84
+ placeholder: `/var/log/vellaveto/export.${format}`,
85
+ defaultValue: `/var/log/vellaveto/export.${format}`,
86
+ });
87
+ if (p.isCancel(target)) {
88
+ p.cancel("Setup cancelled.");
89
+ process.exit(0);
90
+ }
91
+ state.auditExportTarget = target;
92
+ }
93
+ const checkpoint = await p.text({
94
+ message: "Audit checkpoint interval (seconds, 0 to disable)",
95
+ placeholder: "300",
96
+ defaultValue: "300",
97
+ validate(value) {
98
+ const n = Number(value);
99
+ if (isNaN(n) || n < 0 || !Number.isInteger(n)) {
100
+ return "Must be a non-negative integer";
101
+ }
102
+ },
103
+ });
104
+ if (p.isCancel(checkpoint)) {
105
+ p.cancel("Setup cancelled.");
106
+ process.exit(0);
107
+ }
108
+ state.checkpointInterval = Number(checkpoint);
109
+ }
@@ -0,0 +1,2 @@
1
+ import type { WizardState } from "../types.js";
2
+ export declare function complianceStep(state: WizardState): Promise<void>;
@@ -0,0 +1,26 @@
1
+ import * as p from "@clack/prompts";
2
+ const FRAMEWORKS = [
3
+ { value: "eu_ai_act", label: "EU AI Act", hint: "Transparency marking, risk assessment" },
4
+ { value: "nis2", label: "NIS2", hint: "Incident reporting, supply chain checks" },
5
+ { value: "dora", label: "DORA", hint: "ICT risk management, incident tracking" },
6
+ { value: "soc2", label: "SOC 2", hint: "Trust service criteria auditing" },
7
+ { value: "iso42001", label: "ISO 42001", hint: "AI management system standard" },
8
+ ];
9
+ export async function complianceStep(state) {
10
+ p.log.info("Select which compliance frameworks to enable:");
11
+ const selected = [];
12
+ for (const fw of FRAMEWORKS) {
13
+ const enabled = await p.confirm({
14
+ message: `${fw.label} — ${fw.hint}`,
15
+ initialValue: false,
16
+ });
17
+ if (p.isCancel(enabled)) {
18
+ p.cancel("Setup cancelled.");
19
+ process.exit(0);
20
+ }
21
+ if (enabled) {
22
+ selected.push(fw.value);
23
+ }
24
+ }
25
+ state.complianceFrameworks = selected;
26
+ }
@@ -0,0 +1,2 @@
1
+ import type { WizardState } from "../types.js";
2
+ export declare function detectionStep(state: WizardState): Promise<void>;
@@ -0,0 +1,52 @@
1
+ import * as p from "@clack/prompts";
2
+ export async function detectionStep(state) {
3
+ const injectionEnabled = await p.confirm({
4
+ message: "Enable injection detection?",
5
+ initialValue: true,
6
+ });
7
+ if (p.isCancel(injectionEnabled)) {
8
+ p.cancel("Setup cancelled.");
9
+ process.exit(0);
10
+ }
11
+ state.injectionEnabled = injectionEnabled;
12
+ if (injectionEnabled) {
13
+ const injectionBlocking = await p.confirm({
14
+ message: "Block requests with detected injections? (vs. log-only)",
15
+ initialValue: false,
16
+ });
17
+ if (p.isCancel(injectionBlocking)) {
18
+ p.cancel("Setup cancelled.");
19
+ process.exit(0);
20
+ }
21
+ state.injectionBlocking = injectionBlocking;
22
+ }
23
+ const dlpEnabled = await p.confirm({
24
+ message: "Enable DLP (Data Loss Prevention) scanning?",
25
+ initialValue: true,
26
+ });
27
+ if (p.isCancel(dlpEnabled)) {
28
+ p.cancel("Setup cancelled.");
29
+ process.exit(0);
30
+ }
31
+ state.dlpEnabled = dlpEnabled;
32
+ if (dlpEnabled) {
33
+ const dlpBlocking = await p.confirm({
34
+ message: "Block requests with DLP findings? (vs. log-only)",
35
+ initialValue: false,
36
+ });
37
+ if (p.isCancel(dlpBlocking)) {
38
+ p.cancel("Setup cancelled.");
39
+ process.exit(0);
40
+ }
41
+ state.dlpBlocking = dlpBlocking;
42
+ }
43
+ const behavioralEnabled = await p.confirm({
44
+ message: "Enable behavioral anomaly detection?",
45
+ initialValue: false,
46
+ });
47
+ if (p.isCancel(behavioralEnabled)) {
48
+ p.cancel("Setup cancelled.");
49
+ process.exit(0);
50
+ }
51
+ state.behavioralEnabled = behavioralEnabled;
52
+ }
@@ -0,0 +1,2 @@
1
+ import type { WizardState } from "../types.js";
2
+ export declare function policiesStep(state: WizardState): Promise<void>;
@@ -0,0 +1,29 @@
1
+ import * as p from "@clack/prompts";
2
+ export async function policiesStep(state) {
3
+ const preset = await p.select({
4
+ message: "Select a policy preset",
5
+ options: [
6
+ {
7
+ value: "strict",
8
+ label: "Strict",
9
+ hint: "Deny-by-default. Block credentials + exfiltration. Destructive ops require approval.",
10
+ },
11
+ {
12
+ value: "balanced",
13
+ label: "Balanced (recommended)",
14
+ hint: "Deny-by-default. Block credentials. Reads allowed, writes require approval.",
15
+ },
16
+ {
17
+ value: "permissive",
18
+ label: "Permissive",
19
+ hint: "Allow-by-default. Only block credentials and exfiltration attempts.",
20
+ },
21
+ ],
22
+ initialValue: "balanced",
23
+ });
24
+ if (p.isCancel(preset)) {
25
+ p.cancel("Setup cancelled.");
26
+ process.exit(0);
27
+ }
28
+ state.policyPreset = preset;
29
+ }
@@ -0,0 +1,2 @@
1
+ import type { WizardState } from "../types.js";
2
+ export declare function sdkStep(state: WizardState): Promise<void>;
@@ -0,0 +1,25 @@
1
+ import * as p from "@clack/prompts";
2
+ import pc from "picocolors";
3
+ import { generateSnippet, installCommand } from "../generators/snippets.js";
4
+ export async function sdkStep(state) {
5
+ const language = await p.select({
6
+ message: "Show integration snippet for which SDK?",
7
+ options: [
8
+ { value: "python", label: "Python", hint: "pip install vellaveto" },
9
+ { value: "typescript", label: "TypeScript", hint: "npm install vellaveto" },
10
+ { value: "go", label: "Go", hint: "go get vellaveto" },
11
+ { value: "java", label: "Java", hint: "Maven / Gradle" },
12
+ { value: "skip", label: "Skip", hint: "No snippet needed" },
13
+ ],
14
+ });
15
+ if (p.isCancel(language)) {
16
+ p.cancel("Setup cancelled.");
17
+ process.exit(0);
18
+ }
19
+ state.sdkLanguage = language;
20
+ if (language !== "skip") {
21
+ const install = installCommand(language);
22
+ const snippet = generateSnippet(language, state.apiKey);
23
+ p.note(`${pc.dim("Install:")}\n${install}\n\n${pc.dim("Usage:")}\n${snippet}`, `${language.charAt(0).toUpperCase() + language.slice(1)} SDK`);
24
+ }
25
+ }
@@ -0,0 +1,2 @@
1
+ import type { WizardState } from "../types.js";
2
+ export declare function securityStep(state: WizardState): Promise<void>;
@@ -0,0 +1,61 @@
1
+ import * as p from "@clack/prompts";
2
+ import pc from "picocolors";
3
+ import { generateApiKey, isValidOrigin } from "../utils.js";
4
+ export async function securityStep(state) {
5
+ const generatedKey = generateApiKey();
6
+ p.log.info(`Generated API key: ${pc.cyan(generatedKey)}`);
7
+ const useGenerated = await p.confirm({
8
+ message: "Use this API key?",
9
+ initialValue: true,
10
+ });
11
+ if (p.isCancel(useGenerated)) {
12
+ p.cancel("Setup cancelled.");
13
+ process.exit(0);
14
+ }
15
+ if (useGenerated) {
16
+ state.apiKey = generatedKey;
17
+ }
18
+ else {
19
+ const customKey = await p.text({
20
+ message: "Enter your API key",
21
+ validate(value) {
22
+ if (value.length < 8)
23
+ return "API key must be at least 8 characters";
24
+ },
25
+ });
26
+ if (p.isCancel(customKey)) {
27
+ p.cancel("Setup cancelled.");
28
+ process.exit(0);
29
+ }
30
+ state.apiKey = customKey;
31
+ }
32
+ const corsInput = await p.text({
33
+ message: "Allowed CORS origins (comma-separated, or * for all)",
34
+ placeholder: "http://localhost:3000",
35
+ defaultValue: "",
36
+ });
37
+ if (p.isCancel(corsInput)) {
38
+ p.cancel("Setup cancelled.");
39
+ process.exit(0);
40
+ }
41
+ if (corsInput.trim()) {
42
+ const origins = corsInput
43
+ .split(",")
44
+ .map((s) => s.trim())
45
+ .filter(Boolean);
46
+ const invalid = origins.filter((o) => !isValidOrigin(o));
47
+ if (invalid.length > 0) {
48
+ p.log.warn(`Skipping invalid origins: ${invalid.join(", ")}`);
49
+ }
50
+ state.corsOrigins = origins.filter((o) => isValidOrigin(o));
51
+ }
52
+ const anonymous = await p.confirm({
53
+ message: "Allow anonymous (unauthenticated) evaluate requests?",
54
+ initialValue: false,
55
+ });
56
+ if (p.isCancel(anonymous)) {
57
+ p.cancel("Setup cancelled.");
58
+ process.exit(0);
59
+ }
60
+ state.anonymousMode = anonymous;
61
+ }
@@ -0,0 +1,2 @@
1
+ import type { WizardState } from "../types.js";
2
+ export declare function welcomeStep(state: WizardState, projectDir: string | undefined): Promise<string>;