@closedrouter/cli 1.1.6 → 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 +12 -0
- package/dist/bridge/launch-proxy.d.ts +7 -0
- package/dist/bridge/launch-proxy.d.ts.map +1 -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 +157 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -193,3 +193,15 @@ print(response.choices[0].message.content)
|
|
|
193
193
|
## License
|
|
194
194
|
|
|
195
195
|
MIT © ClosedRouter Contributors
|
|
196
|
+
|
|
197
|
+
### Harness telemetry
|
|
198
|
+
|
|
199
|
+
`cr start` attributes each model request to the launched client and a fresh launch UUID. A streaming loopback relay adds `X-ClosedRouter-Harness` and `X-ClosedRouter-Launch-ID` for all supported clients, including clients without custom-header settings. Keep the launcher running for the duration of the client session. Re-run `cr start` to create a new session.
|
|
200
|
+
|
|
201
|
+
Activity shows per-harness requests, tokens, cached tokens, reasoning tokens, latency, errors, and launch counts for the selected period. Search request logs by harness or launch UUID; open a request to inspect its attribution and routing attempts. The relay does not retain request or response bodies, keys, arguments, project paths, or hostnames. These client-supplied labels are diagnostic metadata, not an authorization boundary.
|
|
202
|
+
|
|
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
|
+
|
|
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,7 @@
|
|
|
1
|
+
/** Streaming transport relay: inject attribution for clients with no custom-header setting. */
|
|
2
|
+
export declare function startLaunchProxy(gatewayUrl: string, apiKey: string, harness: string): Promise<{
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
launchId: `${string}-${string}-${string}-${string}-${string}`;
|
|
5
|
+
close: () => Promise<void>;
|
|
6
|
+
}>;
|
|
7
|
+
//# sourceMappingURL=launch-proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch-proxy.d.ts","sourceRoot":"","sources":["../../src/bridge/launch-proxy.ts"],"names":[],"mappings":"AAMA,+FAA+F;AAC/F,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;GAqDzF"}
|
|
@@ -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,21 +8645,96 @@ 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
|
-
// src/
|
|
8653
|
-
import {
|
|
8654
|
-
import { request as httpRequest } from "node:http";
|
|
8652
|
+
// src/bridge/launch-proxy.ts
|
|
8653
|
+
import { randomUUID } from "node:crypto";
|
|
8654
|
+
import { createServer as createServer2, request as httpRequest } from "node:http";
|
|
8655
8655
|
import { request as httpsRequest } from "node:https";
|
|
8656
|
+
var HOP_HEADERS = new Set(["connection", "keep-alive", "proxy-authenticate", "proxy-authorization", "te", "trailer", "transfer-encoding", "upgrade", "host"]);
|
|
8657
|
+
async function startLaunchProxy(gatewayUrl, apiKey, harness) {
|
|
8658
|
+
const upstream = new URL(gatewayUrl.replace(/\/+$/, "") + "/");
|
|
8659
|
+
if (!["http:", "https:"].includes(upstream.protocol) || upstream.username || upstream.password) {
|
|
8660
|
+
throw new Error("ClosedRouter server URL must use HTTP or HTTPS without embedded credentials.");
|
|
8661
|
+
}
|
|
8662
|
+
const launchId = randomUUID();
|
|
8663
|
+
const transport = upstream.protocol === "https:" ? httpsRequest : httpRequest;
|
|
8664
|
+
const server = createServer2((incoming, outgoing) => {
|
|
8665
|
+
const authorized = incoming.headers.authorization === `Bearer ${apiKey}` || incoming.headers["x-api-key"] === apiKey || incoming.headers["x-goog-api-key"] === apiKey;
|
|
8666
|
+
if (!authorized) {
|
|
8667
|
+
outgoing.writeHead(401).end();
|
|
8668
|
+
return;
|
|
8669
|
+
}
|
|
8670
|
+
if (!incoming.url?.startsWith("/") || incoming.url.startsWith("//")) {
|
|
8671
|
+
outgoing.writeHead(400).end();
|
|
8672
|
+
return;
|
|
8673
|
+
}
|
|
8674
|
+
const target = new URL(upstream);
|
|
8675
|
+
const local = new URL(incoming.url, "http://localhost");
|
|
8676
|
+
target.pathname = upstream.pathname.replace(/\/$/, "") + local.pathname;
|
|
8677
|
+
target.search = local.search;
|
|
8678
|
+
const headers = { ...incoming.headers };
|
|
8679
|
+
const connectionHeaders = String(headers.connection || "").split(",").map((x) => x.trim().toLowerCase());
|
|
8680
|
+
for (const key of Object.keys(headers)) {
|
|
8681
|
+
if (HOP_HEADERS.has(key) || connectionHeaders.includes(key) || key.startsWith("x-closedrouter-harness") || key === "x-closedrouter-launch-id")
|
|
8682
|
+
delete headers[key];
|
|
8683
|
+
}
|
|
8684
|
+
headers["x-closedrouter-harness"] = harness;
|
|
8685
|
+
headers["x-closedrouter-launch-id"] = launchId;
|
|
8686
|
+
const request = transport(target, { method: incoming.method, headers }, (response) => {
|
|
8687
|
+
const responseHeaders = { ...response.headers };
|
|
8688
|
+
for (const key of HOP_HEADERS)
|
|
8689
|
+
delete responseHeaders[key];
|
|
8690
|
+
outgoing.writeHead(response.statusCode || 502, responseHeaders);
|
|
8691
|
+
response.on("error", () => outgoing.destroy());
|
|
8692
|
+
response.pipe(outgoing);
|
|
8693
|
+
});
|
|
8694
|
+
request.on("error", () => {
|
|
8695
|
+
if (!outgoing.headersSent)
|
|
8696
|
+
outgoing.writeHead(502).end(JSON.stringify({ error: { message: "ClosedRouter connection failed." } }));
|
|
8697
|
+
else
|
|
8698
|
+
outgoing.destroy();
|
|
8699
|
+
});
|
|
8700
|
+
incoming.on("aborted", () => request.destroy());
|
|
8701
|
+
outgoing.on("close", () => {
|
|
8702
|
+
if (!outgoing.writableFinished)
|
|
8703
|
+
request.destroy();
|
|
8704
|
+
});
|
|
8705
|
+
incoming.pipe(request);
|
|
8706
|
+
});
|
|
8707
|
+
await new Promise((resolve, reject) => {
|
|
8708
|
+
server.once("error", reject);
|
|
8709
|
+
server.listen(0, "127.0.0.1", () => {
|
|
8710
|
+
server.off("error", reject);
|
|
8711
|
+
resolve();
|
|
8712
|
+
});
|
|
8713
|
+
});
|
|
8714
|
+
const address = server.address();
|
|
8715
|
+
if (!address || typeof address === "string")
|
|
8716
|
+
throw new Error("Could not bind the launch relay.");
|
|
8717
|
+
return {
|
|
8718
|
+
baseUrl: `http://127.0.0.1:${address.port}`,
|
|
8719
|
+
launchId,
|
|
8720
|
+
close: () => new Promise((resolve, reject) => {
|
|
8721
|
+
server.close((error) => error ? reject(error) : resolve());
|
|
8722
|
+
server.closeAllConnections();
|
|
8723
|
+
})
|
|
8724
|
+
};
|
|
8725
|
+
}
|
|
8726
|
+
|
|
8727
|
+
// src/commands/agy-proxy.ts
|
|
8728
|
+
import { createServer as createServer3 } from "node:http";
|
|
8729
|
+
import { request as httpRequest2 } from "node:http";
|
|
8730
|
+
import { request as httpsRequest2 } from "node:https";
|
|
8656
8731
|
async function startAgyModelProxy(gatewayUrl, apiKey, model) {
|
|
8657
8732
|
const upstream = new URL(gatewayUrl.replace(/\/+$/, "") + "/");
|
|
8658
8733
|
if (!["https:", "http:"].includes(upstream.protocol)) {
|
|
8659
8734
|
throw new Error("ClosedRouter server URL must use HTTP or HTTPS.");
|
|
8660
8735
|
}
|
|
8661
|
-
const requestUpstream = upstream.protocol === "https:" ?
|
|
8662
|
-
const server =
|
|
8736
|
+
const requestUpstream = upstream.protocol === "https:" ? httpsRequest2 : httpRequest2;
|
|
8737
|
+
const server = createServer3((incoming, outgoing) => {
|
|
8663
8738
|
if (incoming.headers["x-goog-api-key"] !== apiKey) {
|
|
8664
8739
|
outgoing.writeHead(401).end();
|
|
8665
8740
|
return;
|
|
@@ -8703,6 +8778,47 @@ async function startAgyModelProxy(gatewayUrl, apiKey, model) {
|
|
|
8703
8778
|
};
|
|
8704
8779
|
}
|
|
8705
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
|
+
|
|
8706
8822
|
// src/commands/start.ts
|
|
8707
8823
|
function isGatewayModelAvailable(availableModels, model) {
|
|
8708
8824
|
return availableModels.includes(model);
|
|
@@ -8750,9 +8866,9 @@ function buildLaunchPlan(tool, serverUrl, apiKey, model, toolArgs = [], homeDir
|
|
|
8750
8866
|
}
|
|
8751
8867
|
if (tool === "codex") {
|
|
8752
8868
|
assertNoManagedCodexArgs(toolArgs);
|
|
8753
|
-
const codexHome = process.env.CODEX_HOME ||
|
|
8869
|
+
const codexHome = process.env.CODEX_HOME || join10(homeDir, ".codex");
|
|
8754
8870
|
const profileName = "closedrouter-launch";
|
|
8755
|
-
const configPath =
|
|
8871
|
+
const configPath = join10(codexHome, `${profileName}.config.toml`);
|
|
8756
8872
|
const configLines = [
|
|
8757
8873
|
`model = ${tomlString(model)}`,
|
|
8758
8874
|
`model_provider = ${tomlString(profileName)}`,
|
|
@@ -8778,7 +8894,7 @@ function buildLaunchPlan(tool, serverUrl, apiKey, model, toolArgs = [], homeDir
|
|
|
8778
8894
|
env.GEMINI_API_KEY = apiKey;
|
|
8779
8895
|
env.GEMINI_MODEL = model;
|
|
8780
8896
|
env.GOOGLE_GEMINI_BASE_URL = baseUrl;
|
|
8781
|
-
const configPath =
|
|
8897
|
+
const configPath = join10(homeDir, ".closedrouter", "gemini-launch.settings.json");
|
|
8782
8898
|
env.GEMINI_CLI_SYSTEM_SETTINGS_PATH = configPath;
|
|
8783
8899
|
return {
|
|
8784
8900
|
tool,
|
|
@@ -8843,7 +8959,7 @@ function buildLaunchPlan(tool, serverUrl, apiKey, model, toolArgs = [], homeDir
|
|
|
8843
8959
|
function writeOwnedConfig(file) {
|
|
8844
8960
|
mkdirSync2(dirname(file.path), { recursive: true, mode: 448 });
|
|
8845
8961
|
try {
|
|
8846
|
-
const existing =
|
|
8962
|
+
const existing = readFileSync9(file.path, "utf8");
|
|
8847
8963
|
if (file.path.endsWith(".config.toml") && !existing.includes('name = "ClosedRouter"')) {
|
|
8848
8964
|
throw new Error(`Refusing to replace an existing Codex profile at ${file.path}.`);
|
|
8849
8965
|
}
|
|
@@ -8851,7 +8967,7 @@ function writeOwnedConfig(file) {
|
|
|
8851
8967
|
if (error instanceof Error && error.message.startsWith("Refusing to replace"))
|
|
8852
8968
|
throw error;
|
|
8853
8969
|
}
|
|
8854
|
-
|
|
8970
|
+
writeFileSync3(file.path, file.contents, { encoding: "utf8", mode: 384 });
|
|
8855
8971
|
}
|
|
8856
8972
|
function modelId(model) {
|
|
8857
8973
|
return String(model.id || model.name || "");
|
|
@@ -8860,18 +8976,32 @@ function modelSupportsHarness(model, tool) {
|
|
|
8860
8976
|
const parameters = model.supported_parameters;
|
|
8861
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"));
|
|
8862
8978
|
}
|
|
8863
|
-
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) {
|
|
8864
8992
|
try {
|
|
8865
|
-
const output = execFileSync(command, ["models"], { encoding: "utf8", timeout: 1e4, stdio: ["ignore", "pipe", "ignore"] });
|
|
8866
|
-
|
|
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;
|
|
8867
8996
|
} catch {
|
|
8868
8997
|
throw new Error("Could not list Antigravity CLI models. Install or update `agy`, then try `cr start agy` again.");
|
|
8869
8998
|
}
|
|
8999
|
+
throw new Error(`This version of agy did not recognize '${label}'. Update agy to use ClosedRouter's accurate model display.`);
|
|
8870
9000
|
}
|
|
8871
9001
|
function assertAgyGeminiMode(homeDir) {
|
|
8872
|
-
const settingsPath =
|
|
9002
|
+
const settingsPath = join10(homeDir, ".gemini", "antigravity-cli", "settings.json");
|
|
8873
9003
|
try {
|
|
8874
|
-
const settings = JSON.parse(
|
|
9004
|
+
const settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
|
|
8875
9005
|
if (settings?.modelProvider === "gemini")
|
|
8876
9006
|
return;
|
|
8877
9007
|
} catch {}
|
|
@@ -8944,14 +9074,14 @@ async function startCommand(options = {}) {
|
|
|
8944
9074
|
}
|
|
8945
9075
|
if (tool === "agy")
|
|
8946
9076
|
assertAgyGeminiMode(homedir9());
|
|
8947
|
-
const
|
|
8948
|
-
if (
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
const agyProxy = tool === "agy" ? await startAgyModelProxy(serverUrl, apiKey, model) : 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);
|
|
9080
|
+
const relay = await startLaunchProxy(serverUrl, apiKey, tool);
|
|
9081
|
+
let agyProxy;
|
|
8953
9082
|
try {
|
|
8954
|
-
|
|
9083
|
+
agyProxy = tool === "agy" ? await startAgyModelProxy(relay.baseUrl, apiKey, model) : undefined;
|
|
9084
|
+
const plan = buildLaunchPlan(tool, relay.baseUrl, apiKey, model, options.toolArgs, homedir9(), options.executablePath, agyProxy && recognizedModel ? { baseUrl: agyProxy.baseUrl, recognizedModel } : undefined);
|
|
8955
9085
|
if (plan.configFile)
|
|
8956
9086
|
writeOwnedConfig(plan.configFile);
|
|
8957
9087
|
M2.info(`${import_picocolors12.default.green("Starting")} ${import_picocolors12.default.bold(TOOL_OPTIONS.find((option) => option.value === tool)?.label || tool)} with ${import_picocolors12.default.cyan(model)} through ${import_picocolors12.default.dim(serverUrl)}`);
|
|
@@ -8959,6 +9089,7 @@ async function startCommand(options = {}) {
|
|
|
8959
9089
|
process.exitCode = exitCode;
|
|
8960
9090
|
} finally {
|
|
8961
9091
|
await agyProxy?.close();
|
|
9092
|
+
await relay.close();
|
|
8962
9093
|
}
|
|
8963
9094
|
}
|
|
8964
9095
|
|
|
@@ -9007,7 +9138,7 @@ process.on("warning", (warning) => {
|
|
|
9007
9138
|
console.warn(warning.name, warning.message);
|
|
9008
9139
|
});
|
|
9009
9140
|
var program2 = new Command2;
|
|
9010
|
-
var cliVersion = JSON.parse(
|
|
9141
|
+
var cliVersion = JSON.parse(readFileSync10(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
9011
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) => {
|
|
9012
9143
|
await onboardCommand({ server: options.server });
|
|
9013
9144
|
});
|
package/package.json
CHANGED