@novastorm-ai/cli 0.0.1
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/dist/bin/nova.js +12 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-FYSTZ6K6.js +231 -0
- package/dist/chunk-NFNZMCLQ.js +1753 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +21 -0
- package/dist/package-3YCVE5UE.js +43 -0
- package/dist/setup-3KREUXRO.js +7 -0
- package/package.json +37 -0
package/dist/bin/nova.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
__require
|
|
10
|
+
};
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
// src/setup.ts
|
|
2
|
+
import * as fs2 from "fs/promises";
|
|
3
|
+
import * as path2 from "path";
|
|
4
|
+
import { select, password } from "@inquirer/prompts";
|
|
5
|
+
import TOML2 from "@iarna/toml";
|
|
6
|
+
|
|
7
|
+
// src/config.ts
|
|
8
|
+
import * as fs from "fs/promises";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
import TOML from "@iarna/toml";
|
|
11
|
+
import {
|
|
12
|
+
ConfigError,
|
|
13
|
+
DEFAULT_CONFIG
|
|
14
|
+
} from "@novastorm-ai/core";
|
|
15
|
+
var NOVA_TOML = "nova.toml";
|
|
16
|
+
var LOCAL_CONFIG_PATH = path.join(".nova", "config.toml");
|
|
17
|
+
function deepMerge(target, source) {
|
|
18
|
+
const result = { ...target };
|
|
19
|
+
for (const key of Object.keys(source)) {
|
|
20
|
+
const srcVal = source[key];
|
|
21
|
+
if (srcVal === void 0) continue;
|
|
22
|
+
const tgtVal = result[key];
|
|
23
|
+
if (typeof srcVal === "object" && srcVal !== null && !Array.isArray(srcVal) && typeof tgtVal === "object" && tgtVal !== null && !Array.isArray(tgtVal)) {
|
|
24
|
+
result[key] = deepMerge(
|
|
25
|
+
tgtVal,
|
|
26
|
+
srcVal
|
|
27
|
+
);
|
|
28
|
+
} else {
|
|
29
|
+
result[key] = srcVal;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
async function readTomlFile(filePath) {
|
|
35
|
+
let content;
|
|
36
|
+
try {
|
|
37
|
+
content = await fs.readFile(filePath, "utf-8");
|
|
38
|
+
} catch {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
return TOML.parse(content);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
45
|
+
throw new ConfigError(`Invalid TOML in ${filePath}: ${message}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function validateRelativePath(value, field) {
|
|
49
|
+
if (value.includes("..")) {
|
|
50
|
+
throw new ConfigError(
|
|
51
|
+
`Path traversal not allowed in ${field}: "${value}"`,
|
|
52
|
+
field
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
if (path.isAbsolute(value)) {
|
|
56
|
+
throw new ConfigError(
|
|
57
|
+
`Absolute paths not allowed in ${field}: "${value}". Use a relative path.`,
|
|
58
|
+
field
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function validate(config) {
|
|
63
|
+
if (config.project.port < 0 || config.project.port > 65535) {
|
|
64
|
+
throw new ConfigError(
|
|
65
|
+
`Invalid port number: ${config.project.port}. Must be between 0 and 65535.`,
|
|
66
|
+
"project.port"
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
const validProviders = ["openrouter", "anthropic", "openai", "ollama", "claude-cli"];
|
|
70
|
+
if (!validProviders.includes(config.apiKeys.provider)) {
|
|
71
|
+
throw new ConfigError(
|
|
72
|
+
`Invalid provider: ${config.apiKeys.provider}. Must be one of: ${validProviders.join(", ")}`,
|
|
73
|
+
"apiKeys.provider"
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
const validEngines = ["web", "whisper"];
|
|
77
|
+
if (!validEngines.includes(config.voice.engine)) {
|
|
78
|
+
throw new ConfigError(
|
|
79
|
+
`Invalid voice engine: ${config.voice.engine}. Must be one of: ${validEngines.join(", ")}`,
|
|
80
|
+
"voice.engine"
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
if (typeof config.telemetry.enabled !== "boolean") {
|
|
84
|
+
throw new ConfigError(
|
|
85
|
+
`Invalid telemetry.enabled: must be a boolean.`,
|
|
86
|
+
"telemetry.enabled"
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (config.project.frontend !== void 0) {
|
|
90
|
+
validateRelativePath(config.project.frontend, "project.frontend");
|
|
91
|
+
}
|
|
92
|
+
if (config.project.backends !== void 0) {
|
|
93
|
+
if (!Array.isArray(config.project.backends)) {
|
|
94
|
+
throw new ConfigError(
|
|
95
|
+
"project.backends must be an array of strings.",
|
|
96
|
+
"project.backends"
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
for (const backend of config.project.backends) {
|
|
100
|
+
if (typeof backend !== "string") {
|
|
101
|
+
throw new ConfigError(
|
|
102
|
+
"Each entry in project.backends must be a string.",
|
|
103
|
+
"project.backends"
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
validateRelativePath(backend, "project.backends");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function diffFromDefaults(config) {
|
|
111
|
+
const result = {};
|
|
112
|
+
const defaults = DEFAULT_CONFIG;
|
|
113
|
+
const input = config;
|
|
114
|
+
for (const section of Object.keys(input)) {
|
|
115
|
+
const sectionValues = input[section];
|
|
116
|
+
if (!sectionValues || typeof sectionValues !== "object") continue;
|
|
117
|
+
const defaultSection = defaults[section];
|
|
118
|
+
const diff = {};
|
|
119
|
+
for (const [key, value] of Object.entries(sectionValues)) {
|
|
120
|
+
if (defaultSection && defaultSection[key] === value) continue;
|
|
121
|
+
diff[key] = value;
|
|
122
|
+
}
|
|
123
|
+
if (Object.keys(diff).length > 0) {
|
|
124
|
+
result[section] = diff;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
var ConfigReader = class {
|
|
130
|
+
async read(projectPath) {
|
|
131
|
+
const projectTomlPath = path.join(projectPath, NOVA_TOML);
|
|
132
|
+
const localTomlPath = path.join(projectPath, LOCAL_CONFIG_PATH);
|
|
133
|
+
const projectData = await readTomlFile(projectTomlPath);
|
|
134
|
+
const localData = await readTomlFile(localTomlPath);
|
|
135
|
+
let merged = deepMerge(
|
|
136
|
+
DEFAULT_CONFIG,
|
|
137
|
+
projectData
|
|
138
|
+
);
|
|
139
|
+
merged = deepMerge(merged, localData);
|
|
140
|
+
const envApiKey = process.env["NOVA_API_KEY"];
|
|
141
|
+
if (envApiKey !== void 0) {
|
|
142
|
+
const apiKeys = merged["apiKeys"];
|
|
143
|
+
apiKeys["key"] = envApiKey;
|
|
144
|
+
}
|
|
145
|
+
const config = merged;
|
|
146
|
+
validate(config);
|
|
147
|
+
return config;
|
|
148
|
+
}
|
|
149
|
+
async write(projectPath, config) {
|
|
150
|
+
const diff = diffFromDefaults(config);
|
|
151
|
+
const tomlString = TOML.stringify(diff);
|
|
152
|
+
const filePath = path.join(projectPath, NOVA_TOML);
|
|
153
|
+
await fs.writeFile(filePath, tomlString, "utf-8");
|
|
154
|
+
}
|
|
155
|
+
async writeLocal(projectPath, config) {
|
|
156
|
+
const diff = diffFromDefaults(config);
|
|
157
|
+
const tomlString = TOML.stringify(diff);
|
|
158
|
+
const filePath = path.join(projectPath, LOCAL_CONFIG_PATH);
|
|
159
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
160
|
+
await fs.writeFile(filePath, tomlString, "utf-8");
|
|
161
|
+
}
|
|
162
|
+
async exists(projectPath) {
|
|
163
|
+
try {
|
|
164
|
+
await fs.stat(path.join(projectPath, NOVA_TOML));
|
|
165
|
+
return true;
|
|
166
|
+
} catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// src/setup.ts
|
|
173
|
+
import { DEFAULT_CONFIG as DEFAULT_CONFIG2 } from "@novastorm-ai/core";
|
|
174
|
+
var NOVA_DIR = ".nova";
|
|
175
|
+
var LOCAL_CONFIG = "config.toml";
|
|
176
|
+
async function runSetup(projectPath) {
|
|
177
|
+
const cwd = projectPath ?? process.cwd();
|
|
178
|
+
console.log("Welcome to Nova Architect setup!\n");
|
|
179
|
+
let provider;
|
|
180
|
+
let apiKey;
|
|
181
|
+
try {
|
|
182
|
+
provider = await select({
|
|
183
|
+
message: "Select your AI provider:",
|
|
184
|
+
choices: [
|
|
185
|
+
{ name: "Claude CLI (uses your Claude Max/Pro subscription)", value: "claude-cli" },
|
|
186
|
+
{ name: "OpenRouter (recommended \u2014 access to all models)", value: "openrouter" },
|
|
187
|
+
{ name: "Anthropic", value: "anthropic" },
|
|
188
|
+
{ name: "OpenAI", value: "openai" },
|
|
189
|
+
{ name: "Ollama (free, local)", value: "ollama" }
|
|
190
|
+
]
|
|
191
|
+
});
|
|
192
|
+
console.log(`Selected provider: ${provider}`);
|
|
193
|
+
if (provider !== "ollama" && provider !== "claude-cli") {
|
|
194
|
+
apiKey = await password({
|
|
195
|
+
message: `Enter your ${provider} API key:`,
|
|
196
|
+
mask: "*"
|
|
197
|
+
});
|
|
198
|
+
if (!apiKey || apiKey.trim().length === 0) {
|
|
199
|
+
console.log("No API key provided. You can set it later in .nova/config.toml");
|
|
200
|
+
apiKey = void 0;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch {
|
|
204
|
+
console.log("\nSetup cancelled.");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const novaDir = path2.join(cwd, NOVA_DIR);
|
|
208
|
+
await fs2.mkdir(novaDir, { recursive: true });
|
|
209
|
+
const localConfig = {
|
|
210
|
+
apiKeys: { provider }
|
|
211
|
+
};
|
|
212
|
+
if (apiKey) {
|
|
213
|
+
localConfig["apiKeys"]["key"] = apiKey;
|
|
214
|
+
}
|
|
215
|
+
const localConfigPath = path2.join(novaDir, LOCAL_CONFIG);
|
|
216
|
+
await fs2.writeFile(localConfigPath, TOML2.stringify(localConfig), "utf-8");
|
|
217
|
+
console.log(`
|
|
218
|
+
Saved provider config to ${localConfigPath}`);
|
|
219
|
+
const configReader = new ConfigReader();
|
|
220
|
+
const exists = await configReader.exists(cwd);
|
|
221
|
+
if (!exists) {
|
|
222
|
+
await configReader.write(cwd, DEFAULT_CONFIG2);
|
|
223
|
+
console.log(`Created ${path2.join(cwd, "nova.toml")} with default configuration.`);
|
|
224
|
+
}
|
|
225
|
+
console.log("\nSetup complete!");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export {
|
|
229
|
+
ConfigReader,
|
|
230
|
+
runSetup
|
|
231
|
+
};
|