@lunaroute/cli 0.2.5 → 0.2.7
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/README.md +4 -4
- package/dist/index.js +148 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,18 +15,18 @@ Requires Node.js 20+.
|
|
|
15
15
|
## Quickstart
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
|
-
lunaroute setup pi # or: opencode | claude-code | copilot-cli | generic
|
|
18
|
+
lunaroute setup pi # or: opencode | claude-code | codex | copilot-cli | generic
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
One command from a fresh machine: `setup` asks before touching anything — for `pi` it first offers to install the LunaRoute Pi extension (recommended — models then auto-sync, and you sign in with `/login lunaroute` inside pi), then offers to start pi for you. `opencode` works the same way with its extension (`/connect` to sign in). Decline an extension — or pick a harness whose config needs a key — and `setup` offers to run `lunaroute login` right there (it opens your browser) before writing. The fallback config finishes with an `export LUNAROUTE_API_KEY=…` line — run it in your shell, then start your agent and it routes through LunaRoute.
|
|
22
22
|
|
|
23
|
-
Already have a key from the dashboard? `lunaroute setup pi --key lr_… --yes` is the whole onboarding: it checks the key against the catalog, installs the Pi extension, and stores the key in `~/.pi/agent/auth.json`, so pi starts signed in with models syncing from `/v1/models`. No models.json edit and no shell export. A stale `providers.lunaroute` block from an older CLI is removed in the same run (backup kept).
|
|
23
|
+
Already have a key from the dashboard? `lunaroute setup pi --key lr_… --yes` is the whole onboarding: it checks the key against the catalog, installs the Pi extension, and stores the key in `~/.pi/agent/auth.json`, so pi starts signed in with models syncing from `/v1/models`. No models.json edit and no shell export. A stale `providers.lunaroute` block from an older CLI is removed in the same run (backup kept). `lunaroute setup opencode --key lr_… --yes` works the same way: it installs the OpenCode extension and stores the key in `~/.local/share/opencode/auth.json`, so opencode starts signed in — no `/connect` needed.
|
|
24
24
|
|
|
25
25
|
Prefer a script? `setup pi --extension` / `--models` pick a flow, `--yes` accepts all prompts, and `--print` previews without writing.
|
|
26
26
|
|
|
27
27
|
## What it enables
|
|
28
28
|
|
|
29
|
-
- **One-command setup** — `lunaroute setup` writes the right config for `pi`, `claude-code`, `opencode`, `copilot-cli`, or `generic` (any OpenAI-compatible base URL swap).
|
|
29
|
+
- **One-command setup** — `lunaroute setup` writes the right config for `pi`, `claude-code`, `codex`, `opencode`, `copilot-cli`, or `generic` (any OpenAI-compatible base URL swap).
|
|
30
30
|
- **Session launches** — `lunaroute run claude` or `lunaroute run codex` starts a session wired to LunaRoute; `--model` picks from the catalog, everything after `--` passes through to the harness.
|
|
31
31
|
- **The catalog, in your terminal** — `models`, `pricing`, and `usage` show what's available, what it costs (credits per million tokens), and your wallet balance plus recent ledger entries.
|
|
32
32
|
- **Project memory** — `memory search` and `memory read` query your past agent conversations for the current project. `lunaroute mcp` exposes the same tools as a stdio MCP server, and `skill install` wires the memory skill + MCP into Claude Code.
|
|
@@ -38,7 +38,7 @@ Prefer a script? `setup pi --extension` / `--models` pick a flow, `--yes` accept
|
|
|
38
38
|
| `lunaroute login` | Authorize this device via the browser and store a routing key |
|
|
39
39
|
| `lunaroute whoami` | Show the signed-in user and organization |
|
|
40
40
|
| `lunaroute logout` | Remove stored credentials |
|
|
41
|
-
| `lunaroute setup <harness>` | Configure `opencode` \| `pi` \| `openclaw` \| `claude-code` \| `copilot-cli` \| `generic`. pi setup is interactive (asks before writing); `--extension` / `--models` pick a flow, `--yes` accepts prompts, `--print` previews |
|
|
41
|
+
| `lunaroute setup <harness>` | Configure `opencode` \| `pi` \| `openclaw` \| `claude-code` \| `codex` \| `copilot-cli` \| `generic`. pi setup is interactive (asks before writing); `--extension` / `--models` pick a flow, `--yes` accepts prompts, `--print` previews |
|
|
42
42
|
| `lunaroute run <harness>` | Launch `claude` \| `claude-code` \| `codex` on LunaRoute for this session |
|
|
43
43
|
| `lunaroute models` | List available LunaRoute models |
|
|
44
44
|
| `lunaroute pricing` | Per-model pricing (credits per million tokens) |
|
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.7";
|
|
8
8
|
|
|
9
9
|
// src/login.ts
|
|
10
10
|
import { createServer } from "http";
|
|
@@ -591,9 +591,17 @@ function configHome() {
|
|
|
591
591
|
function opencodeConfigPath() {
|
|
592
592
|
return join2(configHome(), "opencode", "opencode.json");
|
|
593
593
|
}
|
|
594
|
+
function opencodeAuthPath() {
|
|
595
|
+
const dataHome = process.env.XDG_DATA_HOME || join2(homedir2(), ".local", "share");
|
|
596
|
+
return join2(dataHome, "opencode", "auth.json");
|
|
597
|
+
}
|
|
594
598
|
function openclawConfigPath() {
|
|
595
599
|
return join2(homedir2(), ".openclaw", "openclaw.json");
|
|
596
600
|
}
|
|
601
|
+
function codexProfilePath() {
|
|
602
|
+
const home = process.env.CODEX_HOME || join2(homedir2(), ".codex");
|
|
603
|
+
return join2(home, "lunaroute.config.toml");
|
|
604
|
+
}
|
|
597
605
|
function piModelsPath() {
|
|
598
606
|
return join2(homedir2(), ".pi", "agent", "models.json");
|
|
599
607
|
}
|
|
@@ -708,6 +716,25 @@ function providerPlan(ctx) {
|
|
|
708
716
|
]
|
|
709
717
|
};
|
|
710
718
|
}
|
|
719
|
+
function buildKeyPlan(key) {
|
|
720
|
+
return {
|
|
721
|
+
fileWrites: [
|
|
722
|
+
{
|
|
723
|
+
kind: "json",
|
|
724
|
+
path: opencodeAuthPath(),
|
|
725
|
+
merge: (existing) => {
|
|
726
|
+
const obj = existing ?? {};
|
|
727
|
+
delete obj["lunaroute/"];
|
|
728
|
+
return { ...obj, lunaroute: { type: "api", key } };
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
],
|
|
732
|
+
exports: [],
|
|
733
|
+
notes: [
|
|
734
|
+
"opencode: key stored in ~/.local/share/opencode/auth.json \u2014 start opencode (or restart it), then /models to pick a lunaroute/* model."
|
|
735
|
+
]
|
|
736
|
+
};
|
|
737
|
+
}
|
|
711
738
|
|
|
712
739
|
// src/setup/adapters/pi.ts
|
|
713
740
|
import { readFileSync as readFileSync3 } from "fs";
|
|
@@ -811,7 +838,7 @@ function buildStaleProviderCleanupPlan() {
|
|
|
811
838
|
};
|
|
812
839
|
}
|
|
813
840
|
var KEY_EXPIRES_MS = 10 * 365 * 24 * 60 * 60 * 1e3;
|
|
814
|
-
function
|
|
841
|
+
function buildKeyPlan2(key) {
|
|
815
842
|
return {
|
|
816
843
|
fileWrites: [
|
|
817
844
|
{
|
|
@@ -862,8 +889,57 @@ function buildPlan3(ctx) {
|
|
|
862
889
|
};
|
|
863
890
|
}
|
|
864
891
|
|
|
865
|
-
// src/setup/
|
|
892
|
+
// src/setup/routing-url.ts
|
|
893
|
+
function validatedRoutingUrl(routingUrl) {
|
|
894
|
+
function fail2(reason) {
|
|
895
|
+
throw new Error(
|
|
896
|
+
`routingUrl rejected (reason: ${reason}; <redacted URL>) \u2014 check --routing-url or the profile's routing URL`
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
if (!/^https?:\/\/[^/?#]/.test(routingUrl)) fail2("invalid-url");
|
|
900
|
+
if (/[?#]/.test(routingUrl)) fail2("query-or-fragment");
|
|
901
|
+
if (routingUrl !== routingUrl.trim()) fail2("whitespace");
|
|
902
|
+
if (routingUrl.endsWith("/")) fail2("trailing-slash");
|
|
903
|
+
let parsed;
|
|
904
|
+
try {
|
|
905
|
+
parsed = new URL(routingUrl);
|
|
906
|
+
} catch {
|
|
907
|
+
fail2("invalid-url");
|
|
908
|
+
}
|
|
909
|
+
if (parsed.username !== "" || parsed.password !== "") fail2("userinfo");
|
|
910
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") fail2("invalid-url");
|
|
911
|
+
if (parsed.hostname === "") fail2("invalid-url");
|
|
912
|
+
return routingUrl;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// src/setup/adapters/codex.ts
|
|
866
916
|
function buildPlan4(ctx) {
|
|
917
|
+
const base = validatedRoutingUrl(ctx.routingUrl);
|
|
918
|
+
const model = ctx.models[0]?.id ?? "<model>";
|
|
919
|
+
const content = [
|
|
920
|
+
`model = "${model}"`,
|
|
921
|
+
'model_provider = "lunaroute"',
|
|
922
|
+
"",
|
|
923
|
+
"[model_providers.lunaroute]",
|
|
924
|
+
'name = "LunaRoute"',
|
|
925
|
+
`base_url = "${base}/v1"`,
|
|
926
|
+
'env_key = "LUNAROUTE_API_KEY"',
|
|
927
|
+
'wire_api = "responses"'
|
|
928
|
+
].join("\n") + "\n";
|
|
929
|
+
return {
|
|
930
|
+
fileWrites: [{ kind: "text", path: codexProfilePath(), content }],
|
|
931
|
+
exports: [{ name: ctx.keyEnvVar, value: "__KEY__" }],
|
|
932
|
+
notes: [
|
|
933
|
+
"\nCodex: add the export above to your shell profile, then start Codex with `codex --profile lunaroute`.",
|
|
934
|
+
`Change model to any of: ${ctx.models.map((m) => m.id).join(", ")}, or pass \`--model <id>\` for one session.`,
|
|
935
|
+
"The key rides the Authorization: Bearer header via env_key; LunaRoute authenticates the lr_ key at the edge and never forwards it upstream.",
|
|
936
|
+
"One file: the model and the named provider live in the profile file \u2014 your ~/.codex/config.toml is untouched."
|
|
937
|
+
]
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// src/setup/adapters/copilotCli.ts
|
|
942
|
+
function buildPlan5(ctx) {
|
|
867
943
|
const firstModel = ctx.models[0]?.id ?? "<model>";
|
|
868
944
|
return {
|
|
869
945
|
fileWrites: [],
|
|
@@ -889,7 +965,7 @@ function buildPlan4(ctx) {
|
|
|
889
965
|
}
|
|
890
966
|
|
|
891
967
|
// src/setup/adapters/generic.ts
|
|
892
|
-
function
|
|
968
|
+
function buildPlan6(ctx) {
|
|
893
969
|
return {
|
|
894
970
|
fileWrites: [],
|
|
895
971
|
exports: [{ name: ctx.keyEnvVar, value: "__KEY__" }],
|
|
@@ -904,34 +980,11 @@ function buildPlan5(ctx) {
|
|
|
904
980
|
};
|
|
905
981
|
}
|
|
906
982
|
|
|
907
|
-
// src/setup/routing-url.ts
|
|
908
|
-
function validatedRoutingUrl(routingUrl) {
|
|
909
|
-
function fail2(reason) {
|
|
910
|
-
throw new Error(
|
|
911
|
-
`routingUrl rejected (reason: ${reason}; <redacted URL>) \u2014 check --routing-url or the profile's routing URL`
|
|
912
|
-
);
|
|
913
|
-
}
|
|
914
|
-
if (!/^https?:\/\/[^/?#]/.test(routingUrl)) fail2("invalid-url");
|
|
915
|
-
if (/[?#]/.test(routingUrl)) fail2("query-or-fragment");
|
|
916
|
-
if (routingUrl !== routingUrl.trim()) fail2("whitespace");
|
|
917
|
-
if (routingUrl.endsWith("/")) fail2("trailing-slash");
|
|
918
|
-
let parsed;
|
|
919
|
-
try {
|
|
920
|
-
parsed = new URL(routingUrl);
|
|
921
|
-
} catch {
|
|
922
|
-
fail2("invalid-url");
|
|
923
|
-
}
|
|
924
|
-
if (parsed.username !== "" || parsed.password !== "") fail2("userinfo");
|
|
925
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") fail2("invalid-url");
|
|
926
|
-
if (parsed.hostname === "") fail2("invalid-url");
|
|
927
|
-
return routingUrl;
|
|
928
|
-
}
|
|
929
|
-
|
|
930
983
|
// src/setup/adapters/openclaw.ts
|
|
931
984
|
function asRecord(v) {
|
|
932
985
|
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
|
|
933
986
|
}
|
|
934
|
-
function
|
|
987
|
+
function buildPlan7(ctx) {
|
|
935
988
|
const base = validatedRoutingUrl(ctx.routingUrl);
|
|
936
989
|
const models = ctx.models.map((m) => ({ id: m.id, name: m.id }));
|
|
937
990
|
return {
|
|
@@ -991,10 +1044,11 @@ openclaw: restart the gateway, then \`openclaw models list\` and \`openclaw mode
|
|
|
991
1044
|
var ADAPTERS = {
|
|
992
1045
|
opencode: buildPlan,
|
|
993
1046
|
pi: buildPlan2,
|
|
994
|
-
openclaw:
|
|
1047
|
+
openclaw: buildPlan7,
|
|
995
1048
|
"claude-code": buildPlan3,
|
|
996
|
-
|
|
997
|
-
|
|
1049
|
+
codex: buildPlan4,
|
|
1050
|
+
"copilot-cli": buildPlan5,
|
|
1051
|
+
generic: buildPlan6
|
|
998
1052
|
};
|
|
999
1053
|
var PI_INSTALL_PACKAGES = ["npm:@lunaroute/pi-extension"];
|
|
1000
1054
|
var PI_INSTALL_HINT = "Install pi first: npm install -g --ignore-scripts @earendil-works/pi-coding-agent (https://pi.dev)";
|
|
@@ -1008,7 +1062,7 @@ async function runSetup(harness, opts, deps = {
|
|
|
1008
1062
|
return 1;
|
|
1009
1063
|
}
|
|
1010
1064
|
if (opts.key && (opts.extension || opts.models)) {
|
|
1011
|
-
console.error("--key runs the full pi flow; drop --extension/--models.");
|
|
1065
|
+
console.error("--key runs the full pi or opencode flow; drop --extension/--models.");
|
|
1012
1066
|
return 1;
|
|
1013
1067
|
}
|
|
1014
1068
|
const adapter = ADAPTERS[harness];
|
|
@@ -1089,7 +1143,7 @@ async function runSetup(harness, opts, deps = {
|
|
|
1089
1143
|
};
|
|
1090
1144
|
let plan;
|
|
1091
1145
|
try {
|
|
1092
|
-
plan = harness === "pi" && opts.key ?
|
|
1146
|
+
plan = harness === "pi" && opts.key ? buildKeyPlan2("<your-api-key>") : adapter(ctx);
|
|
1093
1147
|
} catch (err) {
|
|
1094
1148
|
console.error(err instanceof Error ? err.message : err);
|
|
1095
1149
|
return 1;
|
|
@@ -1166,7 +1220,7 @@ async function runPiSetup(opts, stored, routingUrl, deps) {
|
|
|
1166
1220
|
console.log("\nSkipped. Inside pi, run /login lunaroute to sign in.");
|
|
1167
1221
|
return 0;
|
|
1168
1222
|
}
|
|
1169
|
-
const plan = combinePlans(
|
|
1223
|
+
const plan = combinePlans(buildKeyPlan2(creds2.routing_key), stalePiProviderExists() ? buildStaleProviderCleanupPlan() : null);
|
|
1170
1224
|
const wcode = await applyAndReport(plan, creds2.routing_key, true);
|
|
1171
1225
|
if (wcode !== 0) return wcode;
|
|
1172
1226
|
console.log("\nDone. Start pi \u2014 it is signed in. Pick a model with /model.");
|
|
@@ -1291,11 +1345,48 @@ async function runHermesConfigSets(spawn, routingUrl, key) {
|
|
|
1291
1345
|
async function runOpencodeSetup(stored, settings, routingUrl, opts, deps) {
|
|
1292
1346
|
try {
|
|
1293
1347
|
if (opts.print) {
|
|
1348
|
+
const plan = combinePlans(
|
|
1349
|
+
buildPlan(
|
|
1350
|
+
{ routingUrl, orgId: settings.org_id, models: [], keyEnvVar: "LUNAROUTE_API_KEY" },
|
|
1351
|
+
{ extension: true }
|
|
1352
|
+
),
|
|
1353
|
+
opts.key ? buildKeyPlan("<your-api-key>") : null
|
|
1354
|
+
);
|
|
1355
|
+
return applyAndReport(plan, stored?.routing_key ?? "", false);
|
|
1356
|
+
}
|
|
1357
|
+
if (opts.key) {
|
|
1358
|
+
const creds2 = await requireCreds(opts, deps, stored);
|
|
1359
|
+
if (!creds2) return 1;
|
|
1360
|
+
const install = await deps.confirm("Install the LunaRoute OpenCode extension?", {
|
|
1361
|
+
yes: opts.yes
|
|
1362
|
+
});
|
|
1363
|
+
if (!install) {
|
|
1364
|
+
return applyOpencodeProvider(
|
|
1365
|
+
creds2,
|
|
1366
|
+
routingUrl,
|
|
1367
|
+
await deps.confirm("Merge LunaRoute provider into opencode.json (backup kept, other providers preserved)?", {
|
|
1368
|
+
yes: opts.yes
|
|
1369
|
+
})
|
|
1370
|
+
);
|
|
1371
|
+
}
|
|
1294
1372
|
const plan = buildPlan(
|
|
1295
1373
|
{ routingUrl, orgId: settings.org_id, models: [], keyEnvVar: "LUNAROUTE_API_KEY" },
|
|
1296
1374
|
{ extension: true }
|
|
1297
1375
|
);
|
|
1298
|
-
|
|
1376
|
+
const code = await applyAndReport(plan, creds2.routing_key, true);
|
|
1377
|
+
if (code !== 0) return code;
|
|
1378
|
+
const store = await deps.confirm(
|
|
1379
|
+
"Store the key in ~/.local/share/opencode/auth.json (replaces any existing LunaRoute login; other credentials preserved)?",
|
|
1380
|
+
{ yes: opts.yes }
|
|
1381
|
+
);
|
|
1382
|
+
if (!store) {
|
|
1383
|
+
console.log("\nSkipped. Inside opencode, run /connect to sign in.");
|
|
1384
|
+
return 0;
|
|
1385
|
+
}
|
|
1386
|
+
const wcode = await applyAndReport(buildKeyPlan(creds2.routing_key), creds2.routing_key, true);
|
|
1387
|
+
if (wcode !== 0) return wcode;
|
|
1388
|
+
console.log("\nDone. Start opencode \u2014 it is signed in. Pick a model with /models.");
|
|
1389
|
+
return 0;
|
|
1299
1390
|
}
|
|
1300
1391
|
if (await deps.confirm(
|
|
1301
1392
|
"Install the LunaRoute OpenCode extension (recommended \u2014 /connect login, models auto-sync)?",
|
|
@@ -2014,12 +2105,19 @@ function buildRunSpec(ctx) {
|
|
|
2014
2105
|
|
|
2015
2106
|
// src/run/adapters/codex.ts
|
|
2016
2107
|
function buildRunSpec2(ctx) {
|
|
2108
|
+
const provider = `{name="LunaRoute",base_url="${ctx.routingUrl}/v1",env_key="LUNAROUTE_API_KEY",wire_api="responses"}`;
|
|
2017
2109
|
return {
|
|
2018
2110
|
command: "codex",
|
|
2019
|
-
args: [
|
|
2111
|
+
args: [
|
|
2112
|
+
"-c",
|
|
2113
|
+
'model_provider="lunaroute"',
|
|
2114
|
+
"-c",
|
|
2115
|
+
`model_providers.lunaroute=${provider}`,
|
|
2116
|
+
"-c",
|
|
2117
|
+
`model="${ctx.model}"`
|
|
2118
|
+
],
|
|
2020
2119
|
env: {
|
|
2021
|
-
|
|
2022
|
-
OPENAI_API_KEY: ctx.apiKey
|
|
2120
|
+
LUNAROUTE_API_KEY: ctx.apiKey
|
|
2023
2121
|
}
|
|
2024
2122
|
};
|
|
2025
2123
|
}
|
|
@@ -2106,7 +2204,7 @@ program.command("whoami").description("Show the signed-in user and organization.
|
|
|
2106
2204
|
program.command("logout").description("Remove stored credentials for the active profile.").action(() => {
|
|
2107
2205
|
logout(program.opts().profile);
|
|
2108
2206
|
});
|
|
2109
|
-
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").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) => {
|
|
2207
|
+
program.command("setup <harness>").description("Configure a coding harness (opencode | pi | hermes | claude-code | codex | 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").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) => {
|
|
2110
2208
|
const code = await runSetup(harness, {
|
|
2111
2209
|
profile: program.opts().profile,
|
|
2112
2210
|
print: opts.print,
|
|
@@ -2118,14 +2216,19 @@ program.command("setup <harness>").description("Configure a coding harness (open
|
|
|
2118
2216
|
});
|
|
2119
2217
|
if (code !== 0) process.exit(code);
|
|
2120
2218
|
});
|
|
2121
|
-
program.command("run <harness>").description("Launch a coding harness (claude | claude-code | codex) configured to use LunaRoute for this session.").option("--model <id>", "model id to launch on (default: first model in the catalog)").
|
|
2122
|
-
const
|
|
2123
|
-
const
|
|
2124
|
-
const
|
|
2125
|
-
|
|
2219
|
+
program.command("run <harness>").description("Launch a coding harness (claude | claude-code | codex) configured to use LunaRoute for this session.").option("--model <id>", "model id to launch on (default: first model in the catalog)").argument("[extra...]").action(async (harness, extra, opts) => {
|
|
2220
|
+
const sep = process.argv.indexOf("--");
|
|
2221
|
+
const post = sep !== -1 ? process.argv.slice(sep + 1) : [];
|
|
2222
|
+
const before = post.length <= extra.length ? extra.slice(0, extra.length - post.length) : extra;
|
|
2223
|
+
if (before.length > 0) {
|
|
2224
|
+
console.error(
|
|
2225
|
+
`Unexpected argument "${before[0]}" \u2014 harness flags go after "--": lunaroute run ${harness} -- ${before.join(" ")}`
|
|
2226
|
+
);
|
|
2227
|
+
process.exit(1);
|
|
2228
|
+
}
|
|
2126
2229
|
const code = await runRun(
|
|
2127
2230
|
harness,
|
|
2128
|
-
{ profile: program.opts().profile, model: opts.model, passthrough }
|
|
2231
|
+
{ profile: program.opts().profile, model: opts.model, passthrough: post }
|
|
2129
2232
|
);
|
|
2130
2233
|
if (code !== 0) process.exit(code);
|
|
2131
2234
|
});
|