chels-code 0.5.0 → 0.5.2
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/src/cli.js +69 -1
- package/dist/src/engine.js +18 -2
- package/dist/src/ui/App.js +8 -1
- package/dist/src/version.js +1 -1
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { realpathSync } from "node:fs";
|
|
5
|
+
import { writeFile } from "node:fs/promises";
|
|
6
|
+
import { createInterface } from "node:readline";
|
|
7
|
+
import { exec } from "node:child_process";
|
|
5
8
|
import { stdin, stdout } from "node:process";
|
|
6
9
|
import { fileURLToPath } from "node:url";
|
|
7
10
|
import { render } from "ink";
|
|
@@ -74,6 +77,68 @@ function getApiKeys() {
|
|
|
74
77
|
}
|
|
75
78
|
return keys;
|
|
76
79
|
}
|
|
80
|
+
const REQUIRED_API_KEY_VARS = [
|
|
81
|
+
{ variable: "OPENROUTER_API_KEY", label: "OpenRouter" },
|
|
82
|
+
{ variable: "NVIDIA_API_KEY", label: "NVIDIA" },
|
|
83
|
+
{ variable: "GROQ_API_KEY", label: "Groq" },
|
|
84
|
+
];
|
|
85
|
+
async function ensureApiKeys() {
|
|
86
|
+
const missing = REQUIRED_API_KEY_VARS.filter((item) => !process.env[item.variable]?.trim());
|
|
87
|
+
if (missing.length === 0)
|
|
88
|
+
return;
|
|
89
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
90
|
+
try {
|
|
91
|
+
const provided = {};
|
|
92
|
+
for (const item of missing) {
|
|
93
|
+
const answer = await new Promise((resolve) => {
|
|
94
|
+
rl.question(`\n${item.label} API key: `, (response) => resolve(response.trim()));
|
|
95
|
+
});
|
|
96
|
+
if (answer) {
|
|
97
|
+
process.env[item.variable] = answer;
|
|
98
|
+
provided[item.variable] = answer;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (Object.keys(provided).length > 0) {
|
|
102
|
+
const content = Object.entries(provided)
|
|
103
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
104
|
+
.join("\n") + "\n";
|
|
105
|
+
await writeFile(path.join(process.cwd(), ".env"), content, { encoding: "utf8" });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
rl.close();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async function checkAndPromptUpdate() {
|
|
113
|
+
try {
|
|
114
|
+
const response = await fetch("https://registry.npmjs.org/chelscode/latest");
|
|
115
|
+
if (!response.ok)
|
|
116
|
+
return;
|
|
117
|
+
const data = (await response.json());
|
|
118
|
+
const latest = data.version;
|
|
119
|
+
if (!latest || latest === VERSION)
|
|
120
|
+
return;
|
|
121
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
122
|
+
const answer = await new Promise((resolve) => {
|
|
123
|
+
rl.question(`A new version of ChelsCode is available (${latest}). Update now? (y/n): `, (res) => resolve(res.trim()));
|
|
124
|
+
});
|
|
125
|
+
rl.close();
|
|
126
|
+
if (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") {
|
|
127
|
+
await new Promise((resolve, reject) => {
|
|
128
|
+
exec(`npm install -g chelscode@${latest}`, { cwd: process.cwd() }, (error, _stdout, stderr) => {
|
|
129
|
+
if (error)
|
|
130
|
+
reject(error);
|
|
131
|
+
else
|
|
132
|
+
resolve();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
process.exit(0);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Ignore update-check failures.
|
|
140
|
+
}
|
|
141
|
+
}
|
|
77
142
|
async function main() {
|
|
78
143
|
const options = parseOptions(process.argv.slice(2));
|
|
79
144
|
if (options.help) {
|
|
@@ -88,6 +153,8 @@ async function main() {
|
|
|
88
153
|
if (!stdin.isTTY || !stdout.isTTY) {
|
|
89
154
|
throw new Error("ChelsCode needs an interactive terminal.");
|
|
90
155
|
}
|
|
156
|
+
const keysMissing = REQUIRED_API_KEY_VARS.some((item) => !process.env[item.variable]?.trim());
|
|
157
|
+
await ensureApiKeys();
|
|
91
158
|
const apiKeys = getApiKeys();
|
|
92
159
|
const workspace = process.cwd();
|
|
93
160
|
let resumed = null;
|
|
@@ -121,7 +188,8 @@ async function main() {
|
|
|
121
188
|
saveSession(session);
|
|
122
189
|
const restoreTitle = installTerminalTitle("ChelsCode");
|
|
123
190
|
try {
|
|
124
|
-
|
|
191
|
+
await checkAndPromptUpdate();
|
|
192
|
+
const instance = render(_jsx(App, { apiKeys: apiKeys, workspace: workspace, version: VERSION, sessionId: session.id, resume: resumed, missingKeys: keysMissing }), {
|
|
125
193
|
exitOnCtrlC: true,
|
|
126
194
|
kittyKeyboard: { mode: "auto" },
|
|
127
195
|
});
|
package/dist/src/engine.js
CHANGED
|
@@ -62,6 +62,18 @@ export function classifyRequest(request) {
|
|
|
62
62
|
function errorMessage(error) {
|
|
63
63
|
return error instanceof Error ? error.message : String(error);
|
|
64
64
|
}
|
|
65
|
+
function formatElapsed(ms) {
|
|
66
|
+
if (ms < 1000)
|
|
67
|
+
return `${Math.round(ms)}ms`;
|
|
68
|
+
if (ms < 60_000)
|
|
69
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
70
|
+
if (ms < 3_600_000)
|
|
71
|
+
return `${(ms / 60_000).toFixed(1)}m`;
|
|
72
|
+
return `${(ms / 3_600_000).toFixed(1)}h`;
|
|
73
|
+
}
|
|
74
|
+
function statusWithElapsed(status, startMs) {
|
|
75
|
+
return `${status} (${formatElapsed(Date.now() - startMs)})`;
|
|
76
|
+
}
|
|
65
77
|
function responseText(message, provider) {
|
|
66
78
|
if (typeof message.content !== "string" || !message.content.trim()) {
|
|
67
79
|
throw new Error(`${PROVIDERS[provider].label} returned an empty response.`);
|
|
@@ -217,7 +229,8 @@ async function runToolLoop({ provider, apiKey, model, system, user, tools, maxRo
|
|
|
217
229
|
onMade: (item) => appendMade(made, item),
|
|
218
230
|
};
|
|
219
231
|
for (let toolRound = 0; toolRound < maxRounds; toolRound += 1) {
|
|
220
|
-
|
|
232
|
+
const roundStart = Date.now();
|
|
233
|
+
hooks.onStatus(statusWithElapsed(`${PROVIDERS[provider].label} is thinking…`, roundStart));
|
|
221
234
|
let assistantMessage;
|
|
222
235
|
try {
|
|
223
236
|
assistantMessage = await complete(hooks, {
|
|
@@ -365,7 +378,10 @@ export function createEngine({ apiKeys, models, workspace, hooks, effort }) {
|
|
|
365
378
|
else
|
|
366
379
|
failures.push(`${PROVIDERS[PROVIDER_IDS[index]].label}: ${errorMessage(result.reason)}`);
|
|
367
380
|
});
|
|
368
|
-
if (failures.length) {
|
|
381
|
+
if (failures.length > 0) {
|
|
382
|
+
hooks.onStatus(`Some AI analyses failed; continuing with available perspectives: ${failures.join("; ")}`);
|
|
383
|
+
}
|
|
384
|
+
if (perspectives.length === 0) {
|
|
369
385
|
throw new Error(`All three AIs must analyze the task before it can continue.\n${failures.join("\n")}`);
|
|
370
386
|
}
|
|
371
387
|
hooks.onStatus(null);
|
package/dist/src/ui/App.js
CHANGED
|
@@ -27,7 +27,7 @@ function initialEntries(workspace, version, resume) {
|
|
|
27
27
|
.map((entry, index) => ({ ...entry, id: index + 1 }));
|
|
28
28
|
return [header, ...restored];
|
|
29
29
|
}
|
|
30
|
-
export function App({ apiKeys, workspace, version, sessionId, resume, sessionsDir, }) {
|
|
30
|
+
export function App({ apiKeys, workspace, version, sessionId, resume, sessionsDir, missingKeys = false, }) {
|
|
31
31
|
const { exit } = useApp();
|
|
32
32
|
const { columns, rows } = useTerminalSize();
|
|
33
33
|
const [entries, setEntries] = useState(() => (initialEntries(workspace, version, resume)));
|
|
@@ -208,6 +208,13 @@ export function App({ apiKeys, workspace, version, sessionId, resume, sessionsDi
|
|
|
208
208
|
endSession();
|
|
209
209
|
return;
|
|
210
210
|
}
|
|
211
|
+
if (missingKeys) {
|
|
212
|
+
pushEntry({
|
|
213
|
+
kind: "note",
|
|
214
|
+
text: "API keys are not configured. Add OPENROUTER_API_KEY, NVIDIA_API_KEY, and GROQ_API_KEY to your .env or environment before continuing.",
|
|
215
|
+
});
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
211
218
|
if (!engine)
|
|
212
219
|
return;
|
|
213
220
|
busyRef.current = true;
|
package/dist/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.5.
|
|
1
|
+
export const VERSION = "0.5.2";
|