@lunaroute/cli 0.2.3 → 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 +88 -12
- package/package.json +1 -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();
|
|
@@ -922,8 +928,25 @@ async function runSetup(harness, opts, deps = {
|
|
|
922
928
|
);
|
|
923
929
|
return 1;
|
|
924
930
|
}
|
|
925
|
-
|
|
931
|
+
let stored = loadProfile(opts.profile);
|
|
926
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
|
+
}
|
|
927
950
|
const routingUrlRaw = opts.routingUrl || stored?.routing_url || settings.routing_url;
|
|
928
951
|
if (!routingUrlRaw) {
|
|
929
952
|
console.error("No routing URL in profile; pass --routing-url.");
|
|
@@ -949,10 +972,12 @@ async function runSetup(harness, opts, deps = {
|
|
|
949
972
|
}
|
|
950
973
|
let models;
|
|
951
974
|
try {
|
|
952
|
-
models = await fetchModels(routingUrl);
|
|
975
|
+
models = await fetchModels(routingUrl, creds.routing_key);
|
|
953
976
|
} catch (err) {
|
|
954
|
-
console.error(
|
|
955
|
-
|
|
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 = [];
|
|
956
981
|
}
|
|
957
982
|
const ctx = {
|
|
958
983
|
routingUrl,
|
|
@@ -981,6 +1006,7 @@ Wrote: ${summary.written.join(", ")}`);
|
|
|
981
1006
|
}
|
|
982
1007
|
}
|
|
983
1008
|
var NOT_LOGGED_IN = 'Not logged in. Run "lunaroute login" first.';
|
|
1009
|
+
var LR_KEY_RE = /^lr_[A-Za-z0-9_-]{8,128}$/;
|
|
984
1010
|
var OPENCODE_LOGIN_HINT = "Or re-run and choose the extension \u2014 it logs in via /connect inside opencode.";
|
|
985
1011
|
var PI_LOGIN_HINT = "Or re-run and choose the extension \u2014 login happens via /login lunaroute inside pi.";
|
|
986
1012
|
async function requireCreds(opts, deps, stored, hint) {
|
|
@@ -1014,6 +1040,38 @@ async function requireCreds(opts, deps, stored, hint) {
|
|
|
1014
1040
|
}
|
|
1015
1041
|
async function runPiSetup(opts, stored, routingUrl, deps) {
|
|
1016
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
|
+
}
|
|
1017
1075
|
if (opts.extension) {
|
|
1018
1076
|
const ok = await deps.confirm(
|
|
1019
1077
|
"Install the LunaRoute Pi extension + MCP adapter via 'pi install'?",
|
|
@@ -1178,7 +1236,7 @@ async function runOpencodeSetup(stored, settings, routingUrl, opts, deps) {
|
|
|
1178
1236
|
async function applyOpencodeProvider(creds, routingUrl, write) {
|
|
1179
1237
|
let models;
|
|
1180
1238
|
try {
|
|
1181
|
-
models = await fetchModels(routingUrl);
|
|
1239
|
+
models = await fetchModels(routingUrl, creds.routing_key);
|
|
1182
1240
|
} catch (err) {
|
|
1183
1241
|
console.error(`Could not fetch the model catalog: ${err instanceof Error ? err.message : err}`);
|
|
1184
1242
|
return 1;
|
|
@@ -1275,7 +1333,7 @@ async function launchPi(spawn) {
|
|
|
1275
1333
|
async function applyPiModels(creds, routingUrl, write) {
|
|
1276
1334
|
let models;
|
|
1277
1335
|
try {
|
|
1278
|
-
models = await fetchModels(routingUrl);
|
|
1336
|
+
models = await fetchModels(routingUrl, creds.routing_key);
|
|
1279
1337
|
} catch (err) {
|
|
1280
1338
|
console.error(`Could not fetch the model catalog: ${err instanceof Error ? err.message : err}`);
|
|
1281
1339
|
return 1;
|
|
@@ -1316,6 +1374,8 @@ async function runModels(profile, opts) {
|
|
|
1316
1374
|
}
|
|
1317
1375
|
if (opts.json) {
|
|
1318
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");
|
|
1319
1379
|
} else {
|
|
1320
1380
|
for (const id of ids) console.log(id);
|
|
1321
1381
|
}
|
|
@@ -1350,6 +1410,10 @@ async function runPricing(profile, opts) {
|
|
|
1350
1410
|
console.log(JSON.stringify(rows, null, 2));
|
|
1351
1411
|
return 0;
|
|
1352
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
|
+
}
|
|
1353
1417
|
const cell = (n) => n === void 0 ? "\u2014" : String(n);
|
|
1354
1418
|
const table = renderTable(
|
|
1355
1419
|
["MODEL", "INPUT (cr/M)", "OUTPUT (cr/M)", "CACHED (cr/M)"],
|
|
@@ -1384,6 +1448,9 @@ async function runUsage(profile, opts) {
|
|
|
1384
1448
|
console.log(
|
|
1385
1449
|
`Wallet: available ${w.available_credits} (balance ${w.balance_credits}, reserved ${w.reserved_credits})`
|
|
1386
1450
|
);
|
|
1451
|
+
if (w.available_credits === 0) {
|
|
1452
|
+
console.log("// wallet is empty \u2014 add credits in the dashboard \u2192 Billing");
|
|
1453
|
+
}
|
|
1387
1454
|
console.log("");
|
|
1388
1455
|
const table = renderTable(
|
|
1389
1456
|
["TIME", "MODEL", "IN", "OUT", "CACHED", "\u0394CREDITS", "BALANCE"],
|
|
@@ -1872,7 +1939,7 @@ async function runRun(harness, opts, deps = realDeps) {
|
|
|
1872
1939
|
if (!model) {
|
|
1873
1940
|
let models;
|
|
1874
1941
|
try {
|
|
1875
|
-
models = await deps.fetchModels(creds.routing_url);
|
|
1942
|
+
models = await deps.fetchModels(creds.routing_url, creds.routing_key);
|
|
1876
1943
|
} catch (err) {
|
|
1877
1944
|
console.error(`Could not fetch the model catalog: ${err instanceof Error ? err.message : err}`);
|
|
1878
1945
|
return 1;
|
|
@@ -1905,6 +1972,14 @@ async function runRun(harness, opts, deps = realDeps) {
|
|
|
1905
1972
|
}
|
|
1906
1973
|
|
|
1907
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
|
+
}
|
|
1908
1983
|
var program = new Command();
|
|
1909
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");
|
|
1910
1985
|
program.command("login").description("Authorize this device via the browser and store a routing key.").action(async () => {
|
|
@@ -1916,14 +1991,15 @@ program.command("whoami").description("Show the signed-in user and organization.
|
|
|
1916
1991
|
program.command("logout").description("Remove stored credentials for the active profile.").action(() => {
|
|
1917
1992
|
logout(program.opts().profile);
|
|
1918
1993
|
});
|
|
1919
|
-
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) => {
|
|
1920
1995
|
const code = await runSetup(harness, {
|
|
1921
1996
|
profile: program.opts().profile,
|
|
1922
1997
|
print: opts.print,
|
|
1923
1998
|
routingUrl: opts.routingUrl,
|
|
1924
1999
|
yes: opts.yes,
|
|
1925
2000
|
extension: opts.extension,
|
|
1926
|
-
models: opts.models
|
|
2001
|
+
models: opts.models,
|
|
2002
|
+
key: opts.key
|
|
1927
2003
|
});
|
|
1928
2004
|
if (code !== 0) process.exit(code);
|
|
1929
2005
|
});
|