@closedrouter/cli 1.1.7 → 1.1.8
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 +2 -0
- package/dist/commands/agy-model.d.ts +3 -0
- package/dist/commands/agy-model.d.ts.map +1 -0
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/index.js +72 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -203,3 +203,5 @@ Activity shows per-harness requests, tokens, cached tokens, reasoning tokens, la
|
|
|
203
203
|
Other integrations may set the same headers directly. Known client user agents provide fallback attribution; unrecognized clients and older logs appear as `unknown`. Playground requests are tagged `playground`.
|
|
204
204
|
|
|
205
205
|
Request inspection also reports streaming mode, tools offered, tool-result messages supplied, and total message count. These counts describe the request, not which tools the client actually executed. Tool names, arguments, outputs, and message contents are not saved.
|
|
206
|
+
|
|
207
|
+
Antigravity launches register a credential-free custom Gemini-compatible model in `~/.gemini/antigravity-cli/settings.json` under `customModelsConfig.customModels`. Its visible label is `ClosedRouter: <selected model>`, while its internal Gemini transport ID stays compatible with agy. The local adapter continues routing every request to the actual selected pool model. This fixes the previous Gemini placeholder in agy's header. Existing preferences and custom models are preserved; request routing still uses the local model adapter and telemetry relay. Re-run `cr start agy` to choose a different pool model.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agy-model.d.ts","sourceRoot":"","sources":["../../src/commands/agy-model.ts"],"names":[],"mappings":"AAQA,uFAAuF;AACvF,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CA4BrG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAeH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,GAAG,UAAU,CAAC;AACrF,KAAK,OAAO,GAAG,SAAS,CAAC;AAEzB,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,wBAAgB,uBAAuB,CAAC,eAAe,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEzF;AA8BD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAM,EAAO,EACvB,OAAO,SAAY,EACnB,cAAc,CAAC,EAAE,MAAM,EACvB,UAAU,CAAC,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GACxD,UAAU,CAiHZ;AAmBD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAKzF;AA2ED,wBAAsB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDnF"}
|
package/dist/index.js
CHANGED
|
@@ -5197,7 +5197,7 @@ var {
|
|
|
5197
5197
|
} = exports_commander;
|
|
5198
5198
|
|
|
5199
5199
|
// src/index.ts
|
|
5200
|
-
import { readFileSync as
|
|
5200
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
5201
5201
|
|
|
5202
5202
|
// ../node_modules/.bun/@clack+prompts@0.11.0/node_modules/@clack/prompts/dist/index.mjs
|
|
5203
5203
|
import { stripVTControlCharacters as S2 } from "node:util";
|
|
@@ -8645,9 +8645,9 @@ to scan and import local subscriptions!`, "Pool Empty");
|
|
|
8645
8645
|
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
8646
8646
|
import { spawn as spawn3 } from "node:child_process";
|
|
8647
8647
|
import { execFileSync } from "node:child_process";
|
|
8648
|
-
import { mkdirSync as mkdirSync2, readFileSync as
|
|
8648
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "node:fs";
|
|
8649
8649
|
import { homedir as homedir9 } from "node:os";
|
|
8650
|
-
import { dirname, join as
|
|
8650
|
+
import { dirname, join as join10 } from "node:path";
|
|
8651
8651
|
|
|
8652
8652
|
// src/bridge/launch-proxy.ts
|
|
8653
8653
|
import { randomUUID } from "node:crypto";
|
|
@@ -8778,6 +8778,47 @@ async function startAgyModelProxy(gatewayUrl, apiKey, model) {
|
|
|
8778
8778
|
};
|
|
8779
8779
|
}
|
|
8780
8780
|
|
|
8781
|
+
// src/commands/agy-model.ts
|
|
8782
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
8783
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, renameSync, statSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
8784
|
+
import { join as join9 } from "node:path";
|
|
8785
|
+
function record(value) {
|
|
8786
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8787
|
+
}
|
|
8788
|
+
function registerAgyLaunchModel(homeDir, model, transportModel) {
|
|
8789
|
+
const path = join9(homeDir, ".gemini", "antigravity-cli", "settings.json");
|
|
8790
|
+
const settings = JSON.parse(readFileSync8(path, "utf8"));
|
|
8791
|
+
if (!record(settings) || settings.modelProvider !== "gemini") {
|
|
8792
|
+
throw new Error(`Set "modelProvider": "gemini" in ${path} before starting Antigravity through ClosedRouter.`);
|
|
8793
|
+
}
|
|
8794
|
+
const config = settings.customModelsConfig ?? {};
|
|
8795
|
+
if (!record(config))
|
|
8796
|
+
throw new Error(`customModelsConfig must be an object in ${path}.`);
|
|
8797
|
+
const models = config.customModels ?? {};
|
|
8798
|
+
if (!record(models))
|
|
8799
|
+
throw new Error(`customModelsConfig.customModels must be an object in ${path}.`);
|
|
8800
|
+
const label = `ClosedRouter: ${model}`;
|
|
8801
|
+
const entry = { modelName: transportModel, apiProvider: "gemini" };
|
|
8802
|
+
const existing = models[label];
|
|
8803
|
+
if (existing !== undefined) {
|
|
8804
|
+
if (!record(existing) || existing.modelName !== transportModel || existing.apiProvider !== "gemini" || Object.keys(existing).some((key) => !["modelName", "apiProvider"].includes(key))) {
|
|
8805
|
+
throw new Error(`The custom model '${label}' already has different settings in ${path}. Rename that entry before launching it with ClosedRouter.`);
|
|
8806
|
+
}
|
|
8807
|
+
return label;
|
|
8808
|
+
}
|
|
8809
|
+
settings.customModelsConfig = { ...config, customModels: { ...models, [label]: entry } };
|
|
8810
|
+
const temporary = `${path}.closedrouter-${randomUUID2()}.tmp`;
|
|
8811
|
+
try {
|
|
8812
|
+
writeFileSync2(temporary, `${JSON.stringify(settings, null, 2)}
|
|
8813
|
+
`, { flag: "wx", mode: statSync(path).mode & 511 });
|
|
8814
|
+
renameSync(temporary, path);
|
|
8815
|
+
} finally {
|
|
8816
|
+
if (existsSync9(temporary))
|
|
8817
|
+
unlinkSync(temporary);
|
|
8818
|
+
}
|
|
8819
|
+
return label;
|
|
8820
|
+
}
|
|
8821
|
+
|
|
8781
8822
|
// src/commands/start.ts
|
|
8782
8823
|
function isGatewayModelAvailable(availableModels, model) {
|
|
8783
8824
|
return availableModels.includes(model);
|
|
@@ -8825,9 +8866,9 @@ function buildLaunchPlan(tool, serverUrl, apiKey, model, toolArgs = [], homeDir
|
|
|
8825
8866
|
}
|
|
8826
8867
|
if (tool === "codex") {
|
|
8827
8868
|
assertNoManagedCodexArgs(toolArgs);
|
|
8828
|
-
const codexHome = process.env.CODEX_HOME ||
|
|
8869
|
+
const codexHome = process.env.CODEX_HOME || join10(homeDir, ".codex");
|
|
8829
8870
|
const profileName = "closedrouter-launch";
|
|
8830
|
-
const configPath =
|
|
8871
|
+
const configPath = join10(codexHome, `${profileName}.config.toml`);
|
|
8831
8872
|
const configLines = [
|
|
8832
8873
|
`model = ${tomlString(model)}`,
|
|
8833
8874
|
`model_provider = ${tomlString(profileName)}`,
|
|
@@ -8853,7 +8894,7 @@ function buildLaunchPlan(tool, serverUrl, apiKey, model, toolArgs = [], homeDir
|
|
|
8853
8894
|
env.GEMINI_API_KEY = apiKey;
|
|
8854
8895
|
env.GEMINI_MODEL = model;
|
|
8855
8896
|
env.GOOGLE_GEMINI_BASE_URL = baseUrl;
|
|
8856
|
-
const configPath =
|
|
8897
|
+
const configPath = join10(homeDir, ".closedrouter", "gemini-launch.settings.json");
|
|
8857
8898
|
env.GEMINI_CLI_SYSTEM_SETTINGS_PATH = configPath;
|
|
8858
8899
|
return {
|
|
8859
8900
|
tool,
|
|
@@ -8918,7 +8959,7 @@ function buildLaunchPlan(tool, serverUrl, apiKey, model, toolArgs = [], homeDir
|
|
|
8918
8959
|
function writeOwnedConfig(file) {
|
|
8919
8960
|
mkdirSync2(dirname(file.path), { recursive: true, mode: 448 });
|
|
8920
8961
|
try {
|
|
8921
|
-
const existing =
|
|
8962
|
+
const existing = readFileSync9(file.path, "utf8");
|
|
8922
8963
|
if (file.path.endsWith(".config.toml") && !existing.includes('name = "ClosedRouter"')) {
|
|
8923
8964
|
throw new Error(`Refusing to replace an existing Codex profile at ${file.path}.`);
|
|
8924
8965
|
}
|
|
@@ -8926,7 +8967,7 @@ function writeOwnedConfig(file) {
|
|
|
8926
8967
|
if (error instanceof Error && error.message.startsWith("Refusing to replace"))
|
|
8927
8968
|
throw error;
|
|
8928
8969
|
}
|
|
8929
|
-
|
|
8970
|
+
writeFileSync3(file.path, file.contents, { encoding: "utf8", mode: 384 });
|
|
8930
8971
|
}
|
|
8931
8972
|
function modelId(model) {
|
|
8932
8973
|
return String(model.id || model.name || "");
|
|
@@ -8935,18 +8976,32 @@ function modelSupportsHarness(model, tool) {
|
|
|
8935
8976
|
const parameters = model.supported_parameters;
|
|
8936
8977
|
return (model.status === undefined || model.status === "available") && (model.available_tool_account_count === undefined || model.available_tool_account_count > 0) && (!Array.isArray(parameters) || parameters.includes("tools"));
|
|
8937
8978
|
}
|
|
8938
|
-
function
|
|
8979
|
+
function discoverAgyTransportModel(command, apiKey) {
|
|
8980
|
+
const output = execFileSync(command, ["models"], {
|
|
8981
|
+
encoding: "utf8",
|
|
8982
|
+
timeout: 1e4,
|
|
8983
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
8984
|
+
env: { ...process.env, GEMINI_API_KEY: apiKey, GOOGLE_GEMINI_BASE_URL: "http://127.0.0.1:1" }
|
|
8985
|
+
});
|
|
8986
|
+
const models = output.split(/\r?\n/).map((line) => line.split("\t")[0]).filter((id) => /^gemini-[\w.-]+-(?:high|medium|low)$/.test(id)).map((id) => id.replace(/-(?:high|medium|low)$/, ""));
|
|
8987
|
+
if (!models.length)
|
|
8988
|
+
throw new Error("Antigravity did not report any Gemini API models. Update agy and select Gemini API key mode.");
|
|
8989
|
+
return models.includes("gemini-3.8-flash") ? "gemini-3.8-flash" : models[0];
|
|
8990
|
+
}
|
|
8991
|
+
function assertAgyCustomModel(command, label, apiKey) {
|
|
8939
8992
|
try {
|
|
8940
|
-
const output = execFileSync(command, ["models"], { encoding: "utf8", timeout: 1e4, stdio: ["ignore", "pipe", "ignore"] });
|
|
8941
|
-
|
|
8993
|
+
const output = execFileSync(command, ["models"], { encoding: "utf8", timeout: 1e4, stdio: ["ignore", "pipe", "ignore"], env: { ...process.env, GEMINI_API_KEY: apiKey, GOOGLE_GEMINI_BASE_URL: "http://127.0.0.1:1" } });
|
|
8994
|
+
if (output.split(/\r?\n/).some((line) => line.split("\t")[0] === label))
|
|
8995
|
+
return;
|
|
8942
8996
|
} catch {
|
|
8943
8997
|
throw new Error("Could not list Antigravity CLI models. Install or update `agy`, then try `cr start agy` again.");
|
|
8944
8998
|
}
|
|
8999
|
+
throw new Error(`This version of agy did not recognize '${label}'. Update agy to use ClosedRouter's accurate model display.`);
|
|
8945
9000
|
}
|
|
8946
9001
|
function assertAgyGeminiMode(homeDir) {
|
|
8947
|
-
const settingsPath =
|
|
9002
|
+
const settingsPath = join10(homeDir, ".gemini", "antigravity-cli", "settings.json");
|
|
8948
9003
|
try {
|
|
8949
|
-
const settings = JSON.parse(
|
|
9004
|
+
const settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
|
|
8950
9005
|
if (settings?.modelProvider === "gemini")
|
|
8951
9006
|
return;
|
|
8952
9007
|
} catch {}
|
|
@@ -9019,11 +9074,9 @@ async function startCommand(options = {}) {
|
|
|
9019
9074
|
}
|
|
9020
9075
|
if (tool === "agy")
|
|
9021
9076
|
assertAgyGeminiMode(homedir9());
|
|
9022
|
-
const
|
|
9023
|
-
if (
|
|
9024
|
-
|
|
9025
|
-
}
|
|
9026
|
-
const recognizedModel = supportedAgyModels ? supportedAgyModels.has("gemini-3.8-flash") ? "gemini-3.8-flash" : [...supportedAgyModels][0] : undefined;
|
|
9077
|
+
const recognizedModel = tool === "agy" ? registerAgyLaunchModel(homedir9(), model, discoverAgyTransportModel(options.executablePath || "agy", apiKey)) : undefined;
|
|
9078
|
+
if (recognizedModel)
|
|
9079
|
+
assertAgyCustomModel(options.executablePath || "agy", recognizedModel, apiKey);
|
|
9027
9080
|
const relay = await startLaunchProxy(serverUrl, apiKey, tool);
|
|
9028
9081
|
let agyProxy;
|
|
9029
9082
|
try {
|
|
@@ -9085,7 +9138,7 @@ process.on("warning", (warning) => {
|
|
|
9085
9138
|
console.warn(warning.name, warning.message);
|
|
9086
9139
|
});
|
|
9087
9140
|
var program2 = new Command2;
|
|
9088
|
-
var cliVersion = JSON.parse(
|
|
9141
|
+
var cliVersion = JSON.parse(readFileSync10(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
9089
9142
|
program2.name("closedrouter").description("ClosedRouter CLI: Import local AI subscriptions & API keys into your shared token pool").version(cliVersion).option("-s, --server <url>", "ClosedRouter server base URL (default: http://localhost:8000)").action(async (options) => {
|
|
9090
9143
|
await onboardCommand({ server: options.server });
|
|
9091
9144
|
});
|
package/package.json
CHANGED