nexo-code 0.1.2 → 0.1.4
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/nexo.mjs +76 -23
- package/package.json +1 -1
package/nexo.mjs
CHANGED
|
@@ -14550,7 +14550,7 @@ var require_cross_spawn = __commonJS({
|
|
|
14550
14550
|
var cp = __require("child_process");
|
|
14551
14551
|
var parse4 = require_parse();
|
|
14552
14552
|
var enoent = require_enoent();
|
|
14553
|
-
function
|
|
14553
|
+
function spawn6(command, args, options) {
|
|
14554
14554
|
const parsed = parse4(command, args, options);
|
|
14555
14555
|
const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
|
|
14556
14556
|
enoent.hookChildProcess(spawned, parsed);
|
|
@@ -14562,8 +14562,8 @@ var require_cross_spawn = __commonJS({
|
|
|
14562
14562
|
result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
|
|
14563
14563
|
return result;
|
|
14564
14564
|
}
|
|
14565
|
-
module.exports =
|
|
14566
|
-
module.exports.spawn =
|
|
14565
|
+
module.exports = spawn6;
|
|
14566
|
+
module.exports.spawn = spawn6;
|
|
14567
14567
|
module.exports.sync = spawnSync3;
|
|
14568
14568
|
module.exports._parse = parse4;
|
|
14569
14569
|
module.exports._enoent = enoent;
|
|
@@ -14690,7 +14690,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
14690
14690
|
import { join } from "node:path";
|
|
14691
14691
|
|
|
14692
14692
|
// dist/version.js
|
|
14693
|
-
var VERSION = "0.1.
|
|
14693
|
+
var VERSION = "0.1.4";
|
|
14694
14694
|
|
|
14695
14695
|
// dist/update.js
|
|
14696
14696
|
var PACKAGE = "nexo-code";
|
|
@@ -35138,6 +35138,8 @@ var SpeakSchema = external_exports.strictObject({
|
|
|
35138
35138
|
var loginEmail = external_exports.string().trim().toLowerCase().pipe(external_exports.email().max(320));
|
|
35139
35139
|
var EmailLoginStartSchema = external_exports.strictObject({ email: loginEmail, adult: external_exports.literal(true) });
|
|
35140
35140
|
var EmailLoginVerifySchema = external_exports.strictObject({ email: loginEmail, code: external_exports.string().trim().regex(/^\d{6}$/, "six digits") });
|
|
35141
|
+
var DeviceApproveSchema = external_exports.strictObject({ userCode: external_exports.string().trim().toUpperCase().regex(/^[A-Z]{4}-[A-Z]{4}$/, "a code like ABCD-EFGH") });
|
|
35142
|
+
var DevicePollSchema = external_exports.strictObject({ deviceCode: external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/) });
|
|
35141
35143
|
|
|
35142
35144
|
// ../../packages/core/dist/sse.js
|
|
35143
35145
|
var MAX_SSE_FRAME_CHARS = 2e6;
|
|
@@ -35894,6 +35896,7 @@ function formatCost(micros2) {
|
|
|
35894
35896
|
const shown = micros2 === 0 ? "0%" : pct < 0.1 ? "under 0.1%" : pct < 10 ? `${pct.toFixed(1)}%` : `${Math.round(pct)}%`;
|
|
35895
35897
|
return `${shown} of your ${shareOf.label}`;
|
|
35896
35898
|
}
|
|
35899
|
+
var tierLabel = (tier) => tier === "strong" ? "Strong" : tier === "cheap" ? "Auto" : tier;
|
|
35897
35900
|
function formatUsd(micros2) {
|
|
35898
35901
|
const dollars = micros2 / 1e6;
|
|
35899
35902
|
return dollars !== 0 && Math.abs(dollars) < 0.01 ? `$${dollars.toFixed(4)}` : `$${dollars.toFixed(2)}`;
|
|
@@ -37269,7 +37272,7 @@ function summaryLine(s, r, opts = {}) {
|
|
|
37269
37272
|
const cached2 = r.tokens.cacheRead + r.tokens.cacheWrite;
|
|
37270
37273
|
const parts = [
|
|
37271
37274
|
...opts.quiet && r.outcome === "done" ? [] : [status],
|
|
37272
|
-
s.accent(r.tier),
|
|
37275
|
+
s.accent(tierLabel(r.tier)),
|
|
37273
37276
|
`${formatTokens(r.tokens.input + cached2)} in${cached2 ? ` (${formatTokens(cached2)} cached)` : ""} / ${formatTokens(r.tokens.output)} out`,
|
|
37274
37277
|
formatCost(r.costMicros),
|
|
37275
37278
|
`${(r.elapsedMs / 1e3).toFixed(1)}s`
|
|
@@ -37503,7 +37506,7 @@ async function runCascade(task2, deps, opts, state = newCascadeState()) {
|
|
|
37503
37506
|
}
|
|
37504
37507
|
const reason = stoppedEarly ? "retry_limit" : "checks_failed";
|
|
37505
37508
|
state.escalated = reason;
|
|
37506
|
-
deps.log(s.accent(`${s.sym.busy}
|
|
37509
|
+
deps.log(s.accent(`${s.sym.busy} switching to the Strong model (${stoppedEarly ? "the first try stopped early and checks fail" : "checks failed"})`));
|
|
37507
37510
|
let second;
|
|
37508
37511
|
try {
|
|
37509
37512
|
second = await attempt(escalationPrompt(task2, first, checks), "strong", reason);
|
|
@@ -37514,7 +37517,7 @@ async function runCascade(task2, deps, opts, state = newCascadeState()) {
|
|
|
37514
37517
|
throw err;
|
|
37515
37518
|
attempts.pop();
|
|
37516
37519
|
state.escalated = null;
|
|
37517
|
-
deps.log(s.warning(`${s.sym.warn} ${err.message} Trying again
|
|
37520
|
+
deps.log(s.warning(`${s.sym.warn} ${err.message} Trying again with Auto and the failure output.`));
|
|
37518
37521
|
second = await attempt(escalationPrompt(task2, first, checks), "cheap");
|
|
37519
37522
|
}
|
|
37520
37523
|
const secondStopped = stoppedAt(second.outcome);
|
|
@@ -37554,7 +37557,7 @@ function cascadeLine(s, r, state) {
|
|
|
37554
37557
|
const testFiles = files.filter((f) => TEST_FILE.test(f));
|
|
37555
37558
|
const label = LABEL[r.outcome];
|
|
37556
37559
|
const status = r.outcome === "passed" || r.outcome === "done_unchecked" ? s.success(`${s.sym.ok} ${label}`) : r.outcome === "failed" || r.outcome === "checks_failed" ? s.error(`${s.sym.fail} ${label}`) : s.warning(`${s.sym.warn} ${label}`);
|
|
37557
|
-
const tiers = ran.map((a) => a.tier).join("\u2192") || r.attempts[0]?.tier
|
|
37560
|
+
const tiers = ran.map((a) => tierLabel(a.tier)).join("\u2192") || tierLabel(r.attempts[0]?.tier ?? "cheap");
|
|
37558
37561
|
const why2 = r.escalated ? ` (escalated: ${r.escalated.replace("_", " ")})` : state?.firstReason ? ` (${state.firstReason.replace("_", " ")})` : "";
|
|
37559
37562
|
const parts = [
|
|
37560
37563
|
status,
|
|
@@ -37665,6 +37668,7 @@ async function errorFromResponse(res) {
|
|
|
37665
37668
|
}
|
|
37666
37669
|
|
|
37667
37670
|
// dist/commands.js
|
|
37671
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
37668
37672
|
import { join as join15 } from "node:path";
|
|
37669
37673
|
|
|
37670
37674
|
// dist/project.js
|
|
@@ -44247,7 +44251,8 @@ async function login(io, opts) {
|
|
|
44247
44251
|
io.err(`${s.error(s.sym.fail)} Use an https:// API URL (plain http is only allowed for localhost).`);
|
|
44248
44252
|
return 2;
|
|
44249
44253
|
}
|
|
44250
|
-
const
|
|
44254
|
+
const terminal = !opts.token && io.interactive && io.readLine;
|
|
44255
|
+
const token = !terminal ? await io.readSecret(`Paste your Nexo token (input hidden): `) : opts.email ? await emailSignIn(io, apiUrl) : await browserSignIn(io, apiUrl);
|
|
44251
44256
|
if (token === null)
|
|
44252
44257
|
return 1;
|
|
44253
44258
|
if (!TOKEN2.test(token)) {
|
|
@@ -44261,6 +44266,50 @@ async function login(io, opts) {
|
|
|
44261
44266
|
io.out(s.dim(` user ${me.userId}`));
|
|
44262
44267
|
return 0;
|
|
44263
44268
|
}
|
|
44269
|
+
async function browserSignIn(io, apiUrl) {
|
|
44270
|
+
const { style: s } = io;
|
|
44271
|
+
const post = (path2, body) => (io.fetch ?? fetch)(new URL(path2, apiUrl), { method: "POST", headers: { "content-type": "application/json", "x-nexo-client": "cli" }, body: JSON.stringify(body), signal: io.signal });
|
|
44272
|
+
const res = await post("/v1/auth/device/start", {});
|
|
44273
|
+
if (!res.ok)
|
|
44274
|
+
throw await errorFromResponse(res);
|
|
44275
|
+
const start = await res.json();
|
|
44276
|
+
io.err(`${s.bold("Sign in to Nexo")} ${s.dim("(Ctrl+C to cancel)")}`);
|
|
44277
|
+
io.err(`Your code: ${s.bold(s.primary(start.userCode))} ${s.dim("(check that the browser shows the same)")}`);
|
|
44278
|
+
const opened = /^https:\/\//.test(start.verifyUrl) && (io.openUrl ?? openInBrowser)(start.verifyUrl);
|
|
44279
|
+
io.err(opened ? s.dim(`Opening your browser. If it did not open, go to ${start.verifyUrl}`) : `Open this page to approve: ${start.verifyUrl}`);
|
|
44280
|
+
io.err(s.dim(`${s.sym.busy} waiting for you to approve in the browser`));
|
|
44281
|
+
const until = Date.now() + start.expiresInMs;
|
|
44282
|
+
while (Date.now() < until) {
|
|
44283
|
+
await new Promise((resolve3) => {
|
|
44284
|
+
const t = setTimeout(resolve3, Math.max(1e3, start.intervalMs));
|
|
44285
|
+
io.signal.addEventListener("abort", () => (clearTimeout(t), resolve3()), { once: true });
|
|
44286
|
+
});
|
|
44287
|
+
if (io.signal.aborted)
|
|
44288
|
+
return null;
|
|
44289
|
+
const r = await post("/v1/auth/device/poll", { deviceCode: start.deviceCode }).catch(() => null);
|
|
44290
|
+
if (!r)
|
|
44291
|
+
continue;
|
|
44292
|
+
const body = await r.json().catch(() => ({}));
|
|
44293
|
+
if (body.status === "done" && body.token)
|
|
44294
|
+
return body.token;
|
|
44295
|
+
if (body.status === "expired" || r.status === 410)
|
|
44296
|
+
break;
|
|
44297
|
+
}
|
|
44298
|
+
io.err(`${s.error(s.sym.fail)} The sign-in was not approved in time. Run nexo login again.`);
|
|
44299
|
+
return null;
|
|
44300
|
+
}
|
|
44301
|
+
function openInBrowser(url2) {
|
|
44302
|
+
try {
|
|
44303
|
+
const [cmd, args] = process.platform === "win32" ? ["rundll32.exe", ["url.dll,FileProtocolHandler", url2]] : process.platform === "darwin" ? ["open", [url2]] : ["xdg-open", [url2]];
|
|
44304
|
+
const child = spawn3(cmd, args, { stdio: "ignore", detached: true, windowsHide: true });
|
|
44305
|
+
child.on("error", () => {
|
|
44306
|
+
});
|
|
44307
|
+
child.unref();
|
|
44308
|
+
return true;
|
|
44309
|
+
} catch {
|
|
44310
|
+
return false;
|
|
44311
|
+
}
|
|
44312
|
+
}
|
|
44264
44313
|
async function emailSignIn(io, apiUrl) {
|
|
44265
44314
|
const { style: s } = io;
|
|
44266
44315
|
io.err(`${s.bold("Sign in to Nexo")} ${s.dim("(Ctrl+C to cancel)")}`);
|
|
@@ -44341,7 +44390,7 @@ async function logout(io) {
|
|
|
44341
44390
|
}
|
|
44342
44391
|
|
|
44343
44392
|
// dist/statusline.js
|
|
44344
|
-
import { spawn as
|
|
44393
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
44345
44394
|
var TIMEOUT_MS2 = 2e3;
|
|
44346
44395
|
var MAX_CHARS = 160;
|
|
44347
44396
|
function runStatusLine(command, input2, cwd) {
|
|
@@ -44357,7 +44406,7 @@ function runStatusLine(command, input2, cwd) {
|
|
|
44357
44406
|
};
|
|
44358
44407
|
let child;
|
|
44359
44408
|
try {
|
|
44360
|
-
child =
|
|
44409
|
+
child = spawn4(command, { cwd, shell: true, windowsHide: true, stdio: ["pipe", "pipe", "ignore"] });
|
|
44361
44410
|
} catch {
|
|
44362
44411
|
return end(null);
|
|
44363
44412
|
}
|
|
@@ -44661,7 +44710,7 @@ function skillsText(skills) {
|
|
|
44661
44710
|
}
|
|
44662
44711
|
|
|
44663
44712
|
// dist/notify.js
|
|
44664
|
-
import { spawn as
|
|
44713
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
44665
44714
|
import { join as join18 } from "node:path";
|
|
44666
44715
|
var NOTIFY_AFTER_MS = 3e4;
|
|
44667
44716
|
function notificationCommand(title, body, platform = process.platform) {
|
|
@@ -44691,7 +44740,7 @@ function notify(write2, title, body) {
|
|
|
44691
44740
|
if (!cmd)
|
|
44692
44741
|
return;
|
|
44693
44742
|
try {
|
|
44694
|
-
const child =
|
|
44743
|
+
const child = spawn5(cmd[0], cmd.slice(1), { stdio: "ignore", detached: true, windowsHide: true });
|
|
44695
44744
|
child.on("error", () => {
|
|
44696
44745
|
});
|
|
44697
44746
|
child.unref();
|
|
@@ -44759,7 +44808,7 @@ function header(s, opts) {
|
|
|
44759
44808
|
const logo = logoRows(opts);
|
|
44760
44809
|
const text2 = [
|
|
44761
44810
|
`${s.bold("Nexo Code")} ${s.dim(`v${opts.version}`)}`,
|
|
44762
|
-
s.dim(`
|
|
44811
|
+
s.dim(`Auto picks the model ${s.sym.dot} /strong for hard problems ${s.sym.dot} /help`),
|
|
44763
44812
|
s.dim(opts.root),
|
|
44764
44813
|
""
|
|
44765
44814
|
];
|
|
@@ -45212,8 +45261,9 @@ function ttyLineReader(s, color, stdin = process.stdin, out = process.stderr, op
|
|
|
45212
45261
|
}
|
|
45213
45262
|
draw();
|
|
45214
45263
|
};
|
|
45264
|
+
const height = renderBox(s, state, columns(), status, "").lines.length;
|
|
45215
45265
|
out.write(`
|
|
45216
|
-
${CSI}?2004h`);
|
|
45266
|
+
${CSI}?2004h${CSI}999B${height > 1 ? `${CSI}${height - 1}A` : ""}\r`);
|
|
45217
45267
|
out.on?.("resize", draw);
|
|
45218
45268
|
stdin.setRawMode(true);
|
|
45219
45269
|
stdin.resume();
|
|
@@ -45430,7 +45480,7 @@ async function chat(io, opts) {
|
|
|
45430
45480
|
let team = null;
|
|
45431
45481
|
if (!checks.length)
|
|
45432
45482
|
io.err(s.dim(` Tip: add checks to ${PROJECT_FILE} so changes are tested.`));
|
|
45433
|
-
const modelName = () => pinned?.name ?? tier;
|
|
45483
|
+
const modelName = () => pinned?.name ?? tierLabel(tier);
|
|
45434
45484
|
let customStatus = null;
|
|
45435
45485
|
const refreshStatus = async () => {
|
|
45436
45486
|
const command = io.settings?.statusLine;
|
|
@@ -45495,7 +45545,7 @@ async function chat(io, opts) {
|
|
|
45495
45545
|
if (io.settings?.statusLine)
|
|
45496
45546
|
await refreshStatus();
|
|
45497
45547
|
for (; ; ) {
|
|
45498
|
-
const prefix = `${mode === "plan" ? s.warning("plan ") : ""}${pinned ? s.accent(`${pinned.name} `) : tier === "strong" ? s.accent("
|
|
45548
|
+
const prefix = `${mode === "plan" ? s.warning("plan ") : ""}${pinned ? s.accent(`${pinned.name} `) : tier === "strong" ? s.accent("Strong ") : ""}`;
|
|
45499
45549
|
const line2 = await io.readLine(`
|
|
45500
45550
|
${prefix}${s.primary("\u203A")} `, statusLine(), () => {
|
|
45501
45551
|
mode = nextMode(mode);
|
|
@@ -45537,7 +45587,7 @@ ${prefix}${s.primary("\u203A")} `, statusLine(), () => {
|
|
|
45537
45587
|
if (command === "/strong") {
|
|
45538
45588
|
pinned = null;
|
|
45539
45589
|
tier = tier === "strong" ? "cheap" : "strong";
|
|
45540
|
-
io.err(s.dim(`${s.sym.dot}
|
|
45590
|
+
io.err(s.dim(`${s.sym.dot} ${tier === "strong" ? "Using the Strong model (uses more of your plan; /strong again to go back to Auto)" : "Back to Auto: Nexo picks the model"}.`));
|
|
45541
45591
|
continue;
|
|
45542
45592
|
}
|
|
45543
45593
|
if (verb === "/theme" || command === "/vim" || verb === "/statusline") {
|
|
@@ -46105,7 +46155,7 @@ ${answer}` : ""}`
|
|
|
46105
46155
|
if (err instanceof NexoError && err.code === "RATE_LIMITED") {
|
|
46106
46156
|
const quota = (err.retryAfterMs ?? 0) > QUOTA_WAIT_MS;
|
|
46107
46157
|
const wait = err.retryAfterMs ? formatWait(err.retryAfterMs) : "a minute";
|
|
46108
|
-
io.err(s.dim(quota ? `
|
|
46158
|
+
io.err(s.dim(quota ? ` Your ${tier === "strong" ? "Strong model " : ""}limit is used up for ${wait}.${tier === "strong" ? " Type /strong to go back to Auto." : ""}` : ` Wait ${wait} and send your message again.`));
|
|
46109
46159
|
}
|
|
46110
46160
|
if (!(err instanceof NexoError))
|
|
46111
46161
|
throw err;
|
|
@@ -46477,7 +46527,7 @@ Usage:
|
|
|
46477
46527
|
nexo remote Take tasks from the Nexo app (Code) in this folder: they run here, and
|
|
46478
46528
|
every file change and command is approved on your phone
|
|
46479
46529
|
nexo models The models you can put in a team (nexo run --team)
|
|
46480
|
-
nexo login Sign in
|
|
46530
|
+
nexo login Sign in: approve in your browser (--email for a code by email, --token to paste one)
|
|
46481
46531
|
nexo auth status Show login and this month's usage against your cap
|
|
46482
46532
|
nexo logout Revoke the token and remove it from this machine
|
|
46483
46533
|
nexo update Install the latest Nexo Code (npm install -g nexo-code@latest)
|
|
@@ -46580,6 +46630,7 @@ async function run2(argv, io) {
|
|
|
46580
46630
|
output: { type: "string" },
|
|
46581
46631
|
"api-url": { type: "string" },
|
|
46582
46632
|
token: { type: "boolean" },
|
|
46633
|
+
email: { type: "boolean" },
|
|
46583
46634
|
"no-color": { type: "boolean" },
|
|
46584
46635
|
debug: { type: "boolean" }
|
|
46585
46636
|
}
|
|
@@ -46672,7 +46723,7 @@ async function run2(argv, io) {
|
|
|
46672
46723
|
});
|
|
46673
46724
|
}
|
|
46674
46725
|
if (cmd === "update") {
|
|
46675
|
-
const npm =
|
|
46726
|
+
const npm = process.platform === "win32" ? spawnSync2(`npm.cmd install -g --prefer-online ${PACKAGE}@latest`, { stdio: "inherit", shell: true }) : spawnSync2("npm", ["install", "-g", "--prefer-online", `${PACKAGE}@latest`], { stdio: "inherit" });
|
|
46676
46727
|
if (npm.error) {
|
|
46677
46728
|
io.err(`${io.style.error(io.style.sym.fail)} Could not run npm. Install the update yourself: npm install -g ${PACKAGE}@latest`);
|
|
46678
46729
|
return 1;
|
|
@@ -46693,8 +46744,10 @@ async function run2(argv, io) {
|
|
|
46693
46744
|
}
|
|
46694
46745
|
return remote(io, budget);
|
|
46695
46746
|
}
|
|
46747
|
+
if (cmd === "login" && process.platform === "win32" && true)
|
|
46748
|
+
dropBlockedLauncher();
|
|
46696
46749
|
if (cmd === "login")
|
|
46697
|
-
return login(io, { ...values["api-url"] ? { apiUrl: values["api-url"] } : {}, ...values.token ? { token: true } : {} });
|
|
46750
|
+
return login(io, { ...values["api-url"] ? { apiUrl: values["api-url"] } : {}, ...values.token ? { token: true } : {}, ...values.email ? { email: true } : {} });
|
|
46698
46751
|
if (cmd === "models")
|
|
46699
46752
|
return listModels(io);
|
|
46700
46753
|
if (cmd === "logout")
|
|
@@ -46776,7 +46829,7 @@ function dropBlockedLauncher() {
|
|
|
46776
46829
|
const policy = spawnSync2("powershell.exe", ["-NoProfile", "-Command", "Get-ExecutionPolicy"], { encoding: "utf8", windowsHide: true }).stdout.trim();
|
|
46777
46830
|
if (policy !== "Restricted" && policy !== "AllSigned")
|
|
46778
46831
|
return;
|
|
46779
|
-
const prefix = spawnSync2("npm.cmd
|
|
46832
|
+
const prefix = spawnSync2("npm.cmd prefix -g", { encoding: "utf8", shell: true, windowsHide: true }).stdout.trim();
|
|
46780
46833
|
if (prefix)
|
|
46781
46834
|
rmSync(join19(prefix, "nexo.ps1"), { force: true });
|
|
46782
46835
|
} catch {
|