@lunaroute/cli 0.2.2 → 0.2.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/dist/index.js +152 -69
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Command, InvalidArgumentError, Option } from "commander";
|
|
5
5
|
|
|
6
6
|
// package.json
|
|
7
|
-
var version = "0.2.
|
|
7
|
+
var version = "0.2.4";
|
|
8
8
|
|
|
9
9
|
// src/login.ts
|
|
10
10
|
import { createServer } from "http";
|
|
@@ -359,10 +359,16 @@ function logout(profile) {
|
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
// src/catalog.ts
|
|
362
|
-
async function fetchModels(routingUrl) {
|
|
362
|
+
async function fetchModels(routingUrl, key) {
|
|
363
363
|
const url = `${routingUrl}/v1/models`;
|
|
364
|
-
const
|
|
364
|
+
const headers = key ? { Authorization: `Bearer ${key}` } : {};
|
|
365
|
+
const res = await fetch(url, { method: "GET", headers });
|
|
365
366
|
if (!res.ok) {
|
|
367
|
+
if (res.status === 401) {
|
|
368
|
+
throw new Error(
|
|
369
|
+
key ? `the routing service rejected that key (HTTP 401) \u2014 check that you copied the whole lr_ key` : `the routing service requires a key for /v1/models (HTTP 401) \u2014 run "lunaroute login" or pass --key`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
366
372
|
throw new Error(`failed to fetch models: HTTP ${res.status} from ${url}`);
|
|
367
373
|
}
|
|
368
374
|
const body = await res.json();
|
|
@@ -536,8 +542,31 @@ async function promptInput(question, opts = {}) {
|
|
|
536
542
|
return value === "" && opts.default !== void 0 ? opts.default : value;
|
|
537
543
|
}
|
|
538
544
|
|
|
539
|
-
// src/
|
|
540
|
-
import { spawn } from "child_process";
|
|
545
|
+
// src/spawn.ts
|
|
546
|
+
import { spawn as nodeSpawn } from "child_process";
|
|
547
|
+
import { spawn as crossSpawn } from "cross-spawn";
|
|
548
|
+
function makeRealSpawn(platform = process.platform) {
|
|
549
|
+
if (platform === "win32") {
|
|
550
|
+
return (command, args, env) => crossSpawn(command, args, { stdio: "inherit", env });
|
|
551
|
+
}
|
|
552
|
+
return (command, args, env) => nodeSpawn(command, args, { stdio: "inherit", env });
|
|
553
|
+
}
|
|
554
|
+
function describeSpawnError(command, installHint, err) {
|
|
555
|
+
const code = err?.code;
|
|
556
|
+
if (code === "ENOENT") {
|
|
557
|
+
return {
|
|
558
|
+
message: `Error: "${command}" not found on PATH. ${installHint} (exit 127)`,
|
|
559
|
+
exitCode: 127
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
if (code === "EINVAL") {
|
|
563
|
+
return {
|
|
564
|
+
message: `Error: "${command}" could not be launched \u2014 Node on Windows refuses to execute npm .cmd shims directly (EINVAL). The tool is likely installed; try running it manually. (exit 126)`,
|
|
565
|
+
exitCode: 126
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
return { message: err instanceof Error ? err.message : String(err), exitCode: 1 };
|
|
569
|
+
}
|
|
541
570
|
|
|
542
571
|
// src/setup/paths.ts
|
|
543
572
|
import { execSync } from "child_process";
|
|
@@ -885,7 +914,7 @@ var ADAPTERS = {
|
|
|
885
914
|
var PI_INSTALL_PACKAGES = ["npm:@lunaroute/pi-extension", "npm:pi-mcp-adapter"];
|
|
886
915
|
async function runSetup(harness, opts, deps = {
|
|
887
916
|
confirm,
|
|
888
|
-
spawn: (
|
|
917
|
+
spawn: makeRealSpawn(),
|
|
889
918
|
runLogin
|
|
890
919
|
}) {
|
|
891
920
|
if (harness === "pi" && opts.extension && opts.models) {
|
|
@@ -899,8 +928,25 @@ async function runSetup(harness, opts, deps = {
|
|
|
899
928
|
);
|
|
900
929
|
return 1;
|
|
901
930
|
}
|
|
902
|
-
|
|
931
|
+
let stored = loadProfile(opts.profile);
|
|
903
932
|
const settings = resolveSettings(opts.profile);
|
|
933
|
+
if (opts.key) {
|
|
934
|
+
if (!LR_KEY_RE.test(opts.key)) {
|
|
935
|
+
console.error(
|
|
936
|
+
"Invalid key: a LunaRoute routing key starts with lr_ (copy the whole key from the dashboard)."
|
|
937
|
+
);
|
|
938
|
+
return 1;
|
|
939
|
+
}
|
|
940
|
+
saveProfile(opts.profile, {
|
|
941
|
+
api_url: settings.api_url,
|
|
942
|
+
routing_url: opts.routingUrl || settings.routing_url,
|
|
943
|
+
front_url: settings.front_url,
|
|
944
|
+
org_id: stored?.org_id ?? "",
|
|
945
|
+
user_email: stored?.user_email ?? "",
|
|
946
|
+
routing_key: opts.key
|
|
947
|
+
});
|
|
948
|
+
stored = loadProfile(opts.profile);
|
|
949
|
+
}
|
|
904
950
|
const routingUrlRaw = opts.routingUrl || stored?.routing_url || settings.routing_url;
|
|
905
951
|
if (!routingUrlRaw) {
|
|
906
952
|
console.error("No routing URL in profile; pass --routing-url.");
|
|
@@ -926,10 +972,12 @@ async function runSetup(harness, opts, deps = {
|
|
|
926
972
|
}
|
|
927
973
|
let models;
|
|
928
974
|
try {
|
|
929
|
-
models = await fetchModels(routingUrl);
|
|
975
|
+
models = await fetchModels(routingUrl, creds.routing_key);
|
|
930
976
|
} catch (err) {
|
|
931
|
-
console.error(
|
|
932
|
-
|
|
977
|
+
console.error(
|
|
978
|
+
`Could not fetch the model catalog (${err instanceof Error ? err.message : err}) \u2014 continuing; model ids show as <model> until it answers.`
|
|
979
|
+
);
|
|
980
|
+
models = [];
|
|
933
981
|
}
|
|
934
982
|
const ctx = {
|
|
935
983
|
routingUrl,
|
|
@@ -958,6 +1006,7 @@ Wrote: ${summary.written.join(", ")}`);
|
|
|
958
1006
|
}
|
|
959
1007
|
}
|
|
960
1008
|
var NOT_LOGGED_IN = 'Not logged in. Run "lunaroute login" first.';
|
|
1009
|
+
var LR_KEY_RE = /^lr_[A-Za-z0-9_-]{8,128}$/;
|
|
961
1010
|
var OPENCODE_LOGIN_HINT = "Or re-run and choose the extension \u2014 it logs in via /connect inside opencode.";
|
|
962
1011
|
var PI_LOGIN_HINT = "Or re-run and choose the extension \u2014 login happens via /login lunaroute inside pi.";
|
|
963
1012
|
async function requireCreds(opts, deps, stored, hint) {
|
|
@@ -991,6 +1040,38 @@ async function requireCreds(opts, deps, stored, hint) {
|
|
|
991
1040
|
}
|
|
992
1041
|
async function runPiSetup(opts, stored, routingUrl, deps) {
|
|
993
1042
|
try {
|
|
1043
|
+
if (opts.key) {
|
|
1044
|
+
const creds2 = await requireCreds(opts, deps, stored);
|
|
1045
|
+
if (!creds2) return 1;
|
|
1046
|
+
let models = [];
|
|
1047
|
+
let modelsSynced = true;
|
|
1048
|
+
try {
|
|
1049
|
+
models = await fetchModels(routingUrl, creds2.routing_key);
|
|
1050
|
+
} catch (err) {
|
|
1051
|
+
modelsSynced = false;
|
|
1052
|
+
console.warn(
|
|
1053
|
+
`Note: ${err instanceof Error ? err.message : err}`
|
|
1054
|
+
);
|
|
1055
|
+
console.warn(
|
|
1056
|
+
"Writing the provider block with no models. Your organization has no models enabled yet \u2014 contact hello@lunaroute.com to get them turned on, then re-run this command to sync them."
|
|
1057
|
+
);
|
|
1058
|
+
}
|
|
1059
|
+
const plan = buildPlan2({
|
|
1060
|
+
routingUrl,
|
|
1061
|
+
orgId: creds2.org_id,
|
|
1062
|
+
models,
|
|
1063
|
+
keyEnvVar: "LUNAROUTE_API_KEY"
|
|
1064
|
+
});
|
|
1065
|
+
const write = await deps.confirm(
|
|
1066
|
+
"Merge LunaRoute provider into ~/.pi/agent/models.json (backup kept, other providers preserved)?",
|
|
1067
|
+
{ yes: opts.yes }
|
|
1068
|
+
);
|
|
1069
|
+
const code = await applyAndReport(plan, creds2.routing_key, write);
|
|
1070
|
+
if (code === 0 && modelsSynced) {
|
|
1071
|
+
console.log("\nDone. Start pi \u2014 it is signed in. Pick a model with /model.");
|
|
1072
|
+
}
|
|
1073
|
+
return code;
|
|
1074
|
+
}
|
|
994
1075
|
if (opts.extension) {
|
|
995
1076
|
const ok = await deps.confirm(
|
|
996
1077
|
"Install the LunaRoute Pi extension + MCP adapter via 'pi install'?",
|
|
@@ -1075,23 +1156,20 @@ function hermesConfigSetArgs(routingUrl, key) {
|
|
|
1075
1156
|
["config", "set", "LUNAROUTE_API_KEY", key]
|
|
1076
1157
|
];
|
|
1077
1158
|
}
|
|
1078
|
-
async function runHermesConfigSets(
|
|
1159
|
+
async function runHermesConfigSets(spawn, routingUrl, key) {
|
|
1079
1160
|
const commands = hermesConfigSetArgs(routingUrl, key);
|
|
1080
1161
|
const setKeys = commands.map((c) => c[2]);
|
|
1081
1162
|
for (let i = 0; i < commands.length; i++) {
|
|
1082
1163
|
const code = await new Promise((resolve) => {
|
|
1083
|
-
const child =
|
|
1164
|
+
const child = spawn("hermes", commands[i]);
|
|
1084
1165
|
child.on("error", (err) => {
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
}
|
|
1093
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
1094
|
-
resolve(1);
|
|
1166
|
+
const { message, exitCode } = describeSpawnError(
|
|
1167
|
+
"hermes",
|
|
1168
|
+
"Install Hermes Agent first: https://hermes-agent.nousresearch.com",
|
|
1169
|
+
err
|
|
1170
|
+
);
|
|
1171
|
+
console.error(message);
|
|
1172
|
+
resolve(exitCode);
|
|
1095
1173
|
});
|
|
1096
1174
|
child.on("exit", (code2) => {
|
|
1097
1175
|
resolve(typeof code2 === "number" ? code2 : 1);
|
|
@@ -1158,7 +1236,7 @@ async function runOpencodeSetup(stored, settings, routingUrl, opts, deps) {
|
|
|
1158
1236
|
async function applyOpencodeProvider(creds, routingUrl, write) {
|
|
1159
1237
|
let models;
|
|
1160
1238
|
try {
|
|
1161
|
-
models = await fetchModels(routingUrl);
|
|
1239
|
+
models = await fetchModels(routingUrl, creds.routing_key);
|
|
1162
1240
|
} catch (err) {
|
|
1163
1241
|
console.error(`Could not fetch the model catalog: ${err instanceof Error ? err.message : err}`);
|
|
1164
1242
|
return 1;
|
|
@@ -1185,18 +1263,17 @@ Wrote: ${summary.written.join(", ")}`);
|
|
|
1185
1263
|
return 1;
|
|
1186
1264
|
}
|
|
1187
1265
|
}
|
|
1188
|
-
async function launchOpencode(
|
|
1266
|
+
async function launchOpencode(spawn) {
|
|
1189
1267
|
return new Promise((resolve) => {
|
|
1190
|
-
const child =
|
|
1268
|
+
const child = spawn("opencode", []);
|
|
1191
1269
|
child.on("error", (err) => {
|
|
1192
|
-
const
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
resolve(1);
|
|
1270
|
+
const { message, exitCode } = describeSpawnError(
|
|
1271
|
+
"opencode",
|
|
1272
|
+
"Install OpenCode first: https://opencode.ai",
|
|
1273
|
+
err
|
|
1274
|
+
);
|
|
1275
|
+
console.error(message);
|
|
1276
|
+
resolve(exitCode);
|
|
1200
1277
|
});
|
|
1201
1278
|
child.on("exit", (code) => {
|
|
1202
1279
|
resolve(typeof code === "number" ? code : 1);
|
|
@@ -1208,14 +1285,13 @@ async function installPiExtension(deps, opts) {
|
|
|
1208
1285
|
const code = await new Promise((resolve) => {
|
|
1209
1286
|
const child = deps.spawn("pi", ["install", pkg]);
|
|
1210
1287
|
child.on("error", (err) => {
|
|
1211
|
-
const
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
resolve(1);
|
|
1288
|
+
const { message, exitCode } = describeSpawnError(
|
|
1289
|
+
"pi",
|
|
1290
|
+
"Install pi first: https://pi.dev",
|
|
1291
|
+
err
|
|
1292
|
+
);
|
|
1293
|
+
console.error(message);
|
|
1294
|
+
resolve(exitCode);
|
|
1219
1295
|
});
|
|
1220
1296
|
child.on("exit", (code2) => {
|
|
1221
1297
|
resolve(typeof code2 === "number" ? code2 : 1);
|
|
@@ -1237,18 +1313,17 @@ async function installPiExtension(deps, opts) {
|
|
|
1237
1313
|
console.log(" 2. /model \u2014 pick a lunaroute/* model (models auto-sync from /v1/models).");
|
|
1238
1314
|
return 0;
|
|
1239
1315
|
}
|
|
1240
|
-
async function launchPi(
|
|
1316
|
+
async function launchPi(spawn) {
|
|
1241
1317
|
return new Promise((resolve) => {
|
|
1242
|
-
const child =
|
|
1318
|
+
const child = spawn("pi", []);
|
|
1243
1319
|
child.on("error", (err) => {
|
|
1244
|
-
const
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
resolve(1);
|
|
1320
|
+
const { message, exitCode } = describeSpawnError(
|
|
1321
|
+
"pi",
|
|
1322
|
+
"Install pi first: https://pi.dev",
|
|
1323
|
+
err
|
|
1324
|
+
);
|
|
1325
|
+
console.error(message);
|
|
1326
|
+
resolve(exitCode);
|
|
1252
1327
|
});
|
|
1253
1328
|
child.on("exit", (code) => {
|
|
1254
1329
|
resolve(typeof code === "number" ? code : 1);
|
|
@@ -1258,7 +1333,7 @@ async function launchPi(spawn3) {
|
|
|
1258
1333
|
async function applyPiModels(creds, routingUrl, write) {
|
|
1259
1334
|
let models;
|
|
1260
1335
|
try {
|
|
1261
|
-
models = await fetchModels(routingUrl);
|
|
1336
|
+
models = await fetchModels(routingUrl, creds.routing_key);
|
|
1262
1337
|
} catch (err) {
|
|
1263
1338
|
console.error(`Could not fetch the model catalog: ${err instanceof Error ? err.message : err}`);
|
|
1264
1339
|
return 1;
|
|
@@ -1299,6 +1374,8 @@ async function runModels(profile, opts) {
|
|
|
1299
1374
|
}
|
|
1300
1375
|
if (opts.json) {
|
|
1301
1376
|
console.log(JSON.stringify(ids, null, 2));
|
|
1377
|
+
} else if (ids.length === 0) {
|
|
1378
|
+
console.log("// no models enabled for your org yet \u2014 contact hello@lunaroute.com to get models enabled");
|
|
1302
1379
|
} else {
|
|
1303
1380
|
for (const id of ids) console.log(id);
|
|
1304
1381
|
}
|
|
@@ -1333,6 +1410,10 @@ async function runPricing(profile, opts) {
|
|
|
1333
1410
|
console.log(JSON.stringify(rows, null, 2));
|
|
1334
1411
|
return 0;
|
|
1335
1412
|
}
|
|
1413
|
+
if (rows.length === 0) {
|
|
1414
|
+
console.log("// no models enabled for your org yet \u2014 contact hello@lunaroute.com to get models enabled");
|
|
1415
|
+
return 0;
|
|
1416
|
+
}
|
|
1336
1417
|
const cell = (n) => n === void 0 ? "\u2014" : String(n);
|
|
1337
1418
|
const table = renderTable(
|
|
1338
1419
|
["MODEL", "INPUT (cr/M)", "OUTPUT (cr/M)", "CACHED (cr/M)"],
|
|
@@ -1367,6 +1448,9 @@ async function runUsage(profile, opts) {
|
|
|
1367
1448
|
console.log(
|
|
1368
1449
|
`Wallet: available ${w.available_credits} (balance ${w.balance_credits}, reserved ${w.reserved_credits})`
|
|
1369
1450
|
);
|
|
1451
|
+
if (w.available_credits === 0) {
|
|
1452
|
+
console.log("// wallet is empty \u2014 add credits in the dashboard \u2192 Billing");
|
|
1453
|
+
}
|
|
1370
1454
|
console.log("");
|
|
1371
1455
|
const table = renderTable(
|
|
1372
1456
|
["TIME", "MODEL", "IN", "OUT", "CACHED", "\u0394CREDITS", "BALANCE"],
|
|
@@ -1791,9 +1875,6 @@ async function runSkillInstall(profile, opts) {
|
|
|
1791
1875
|
return 0;
|
|
1792
1876
|
}
|
|
1793
1877
|
|
|
1794
|
-
// src/commands/run.ts
|
|
1795
|
-
import { spawn as spawn2 } from "child_process";
|
|
1796
|
-
|
|
1797
1878
|
// src/run/adapters/claudeCode.ts
|
|
1798
1879
|
function buildRunSpec(ctx) {
|
|
1799
1880
|
return {
|
|
@@ -1840,7 +1921,7 @@ var INSTALL_HINT = {
|
|
|
1840
1921
|
codex: "Install Codex: https://github.com/openai/codex#install"
|
|
1841
1922
|
};
|
|
1842
1923
|
var realDeps = {
|
|
1843
|
-
spawn: (
|
|
1924
|
+
spawn: makeRealSpawn(),
|
|
1844
1925
|
fetchModels
|
|
1845
1926
|
};
|
|
1846
1927
|
async function runRun(harness, opts, deps = realDeps) {
|
|
@@ -1858,7 +1939,7 @@ async function runRun(harness, opts, deps = realDeps) {
|
|
|
1858
1939
|
if (!model) {
|
|
1859
1940
|
let models;
|
|
1860
1941
|
try {
|
|
1861
|
-
models = await deps.fetchModels(creds.routing_url);
|
|
1942
|
+
models = await deps.fetchModels(creds.routing_url, creds.routing_key);
|
|
1862
1943
|
} catch (err) {
|
|
1863
1944
|
console.error(`Could not fetch the model catalog: ${err instanceof Error ? err.message : err}`);
|
|
1864
1945
|
return 1;
|
|
@@ -1880,16 +1961,9 @@ async function runRun(harness, opts, deps = realDeps) {
|
|
|
1880
1961
|
const child = deps.spawn(spec.command, args, env);
|
|
1881
1962
|
return new Promise((resolve) => {
|
|
1882
1963
|
child.on("error", (err) => {
|
|
1883
|
-
const
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
`Error: "${spec.command}" not found on PATH. ${INSTALL_HINT[harness]} (exit 127)`
|
|
1887
|
-
);
|
|
1888
|
-
resolve(127);
|
|
1889
|
-
return;
|
|
1890
|
-
}
|
|
1891
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
1892
|
-
resolve(1);
|
|
1964
|
+
const { message, exitCode } = describeSpawnError(spec.command, INSTALL_HINT[harness], err);
|
|
1965
|
+
console.error(message);
|
|
1966
|
+
resolve(exitCode);
|
|
1893
1967
|
});
|
|
1894
1968
|
child.on("exit", (code) => {
|
|
1895
1969
|
resolve(typeof code === "number" ? code : 1);
|
|
@@ -1898,6 +1972,14 @@ async function runRun(harness, opts, deps = realDeps) {
|
|
|
1898
1972
|
}
|
|
1899
1973
|
|
|
1900
1974
|
// src/index.ts
|
|
1975
|
+
var MIN_NODE_MAJOR = 20;
|
|
1976
|
+
var nodeMajor = Number.parseInt(process.versions.node.split(".")[0] ?? "0", 10);
|
|
1977
|
+
if (nodeMajor < MIN_NODE_MAJOR) {
|
|
1978
|
+
console.error(
|
|
1979
|
+
`LunaRoute CLI requires Node.js ${MIN_NODE_MAJOR}+ \u2014 you're running ${process.versions.node}. Upgrade from https://nodejs.org/en/download and try again.`
|
|
1980
|
+
);
|
|
1981
|
+
process.exit(1);
|
|
1982
|
+
}
|
|
1901
1983
|
var program = new Command();
|
|
1902
1984
|
program.name("lunaroute").description("LunaRoute CLI \u2014 configure coding harnesses and manage your account.").version(version).option("-p, --profile <name>", "credential profile to use", "default");
|
|
1903
1985
|
program.command("login").description("Authorize this device via the browser and store a routing key.").action(async () => {
|
|
@@ -1909,14 +1991,15 @@ program.command("whoami").description("Show the signed-in user and organization.
|
|
|
1909
1991
|
program.command("logout").description("Remove stored credentials for the active profile.").action(() => {
|
|
1910
1992
|
logout(program.opts().profile);
|
|
1911
1993
|
});
|
|
1912
|
-
program.command("setup <harness>").description("Configure a coding harness (opencode | pi | hermes | claude-code | copilot-cli | openclaw | generic).").option("--print", "print the config instead of writing files", false).option("--routing-url <url>", "override the routing base URL").option("--yes", "accept all confirmation prompts without a TTY (for scripts)", false).option("--extension", "pi only: jump straight to installing the Pi extension + MCP adapter").option("--models", "pi only: jump straight to writing the models.json provider block").action(async (harness, opts) => {
|
|
1994
|
+
program.command("setup <harness>").description("Configure a coding harness (opencode | pi | hermes | claude-code | copilot-cli | openclaw | generic).").option("--print", "print the config instead of writing files", false).option("--routing-url <url>", "override the routing base URL").option("--yes", "accept all confirmation prompts without a TTY (for scripts)", false).option("--extension", "pi only: jump straight to installing the Pi extension + MCP adapter").option("--models", "pi only: jump straight to writing the models.json provider block").option("--key <key>", "a LunaRoute routing key (lr_\u2026), e.g. from the dashboard's onboarding page \u2014 stores it and skips the browser login").action(async (harness, opts) => {
|
|
1913
1995
|
const code = await runSetup(harness, {
|
|
1914
1996
|
profile: program.opts().profile,
|
|
1915
1997
|
print: opts.print,
|
|
1916
1998
|
routingUrl: opts.routingUrl,
|
|
1917
1999
|
yes: opts.yes,
|
|
1918
2000
|
extension: opts.extension,
|
|
1919
|
-
models: opts.models
|
|
2001
|
+
models: opts.models,
|
|
2002
|
+
key: opts.key
|
|
1920
2003
|
});
|
|
1921
2004
|
if (code !== 0) process.exit(code);
|
|
1922
2005
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunaroute/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "LunaRoute CLI — configure coding harnesses and manage your LunaRoute account.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,10 +30,12 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
32
32
|
"commander": "^15.0.0",
|
|
33
|
+
"cross-spawn": "^7.0.6",
|
|
33
34
|
"open": "^11.0.1",
|
|
34
35
|
"zod": "^4.4.3"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
38
|
+
"@types/cross-spawn": "^6.0.6",
|
|
37
39
|
"@types/node": "^26.2.0",
|
|
38
40
|
"tsup": "^8.3.0",
|
|
39
41
|
"tsx": "^4.23.12",
|