@lakphy/local-router 0.5.6 → 0.5.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/config.schema.json +2 -2
- package/dist/cli.js +1095 -436
- package/dist/entry.js +436 -238
- package/dist/web/assets/{index-0-b0NcMV.js → index-B-4HJAZa.js} +69 -69
- package/dist/web/assets/index-DrH6dT5r.css +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-_8dgANhJ.css +0 -2
package/dist/cli.js
CHANGED
|
@@ -8708,6 +8708,8 @@ var init_errors = __esm(() => {
|
|
|
8708
8708
|
APPLY_FAILED: 8,
|
|
8709
8709
|
UPSTREAM_UNREACHABLE: 9,
|
|
8710
8710
|
INTERACTIVE_REQUIRED: 10,
|
|
8711
|
+
TARGET_NOT_FOUND: 3,
|
|
8712
|
+
TARGET_UNREACHABLE: 9,
|
|
8711
8713
|
UNKNOWN_ERROR: 1
|
|
8712
8714
|
};
|
|
8713
8715
|
CliError = class CliError extends Error {
|
|
@@ -8750,6 +8752,9 @@ function extractGlobalFlags(args) {
|
|
|
8750
8752
|
let yes = false;
|
|
8751
8753
|
let jsonAlias = false;
|
|
8752
8754
|
let explain = false;
|
|
8755
|
+
let targetUrl;
|
|
8756
|
+
let targetHost;
|
|
8757
|
+
let targetPortRaw;
|
|
8753
8758
|
for (let i = 0;i < args.length; i++) {
|
|
8754
8759
|
const a = args[i];
|
|
8755
8760
|
if (!a)
|
|
@@ -8796,6 +8801,21 @@ function extractGlobalFlags(args) {
|
|
|
8796
8801
|
explain = true;
|
|
8797
8802
|
continue;
|
|
8798
8803
|
}
|
|
8804
|
+
if (a === "--url" || a.startsWith("--url=")) {
|
|
8805
|
+
targetUrl = a.startsWith("--url=") ? a.slice("--url=".length) : args[i + 1];
|
|
8806
|
+
rest.push(a);
|
|
8807
|
+
continue;
|
|
8808
|
+
}
|
|
8809
|
+
if (a === "--host" || a.startsWith("--host=")) {
|
|
8810
|
+
targetHost = a.startsWith("--host=") ? a.slice("--host=".length) : args[i + 1];
|
|
8811
|
+
rest.push(a);
|
|
8812
|
+
continue;
|
|
8813
|
+
}
|
|
8814
|
+
if (a === "--port" || a.startsWith("--port=")) {
|
|
8815
|
+
targetPortRaw = a.startsWith("--port=") ? a.slice("--port=".length) : args[i + 1];
|
|
8816
|
+
rest.push(a);
|
|
8817
|
+
continue;
|
|
8818
|
+
}
|
|
8799
8819
|
rest.push(a);
|
|
8800
8820
|
}
|
|
8801
8821
|
const envFormat = pickOutput(process.env.LOCAL_ROUTER_FORMAT);
|
|
@@ -8813,6 +8833,16 @@ function extractGlobalFlags(args) {
|
|
|
8813
8833
|
} else {
|
|
8814
8834
|
output = envFormat ?? "markdown";
|
|
8815
8835
|
}
|
|
8836
|
+
let targetPort;
|
|
8837
|
+
if (targetPortRaw !== undefined) {
|
|
8838
|
+
targetPort = Number.parseInt(targetPortRaw, 10);
|
|
8839
|
+
if (!Number.isFinite(targetPort) || targetPort <= 0 || targetPort > 65535) {
|
|
8840
|
+
throw new CliError("USAGE_ERROR", `\u65E0\u6548\u7AEF\u53E3: ${targetPortRaw}`, {
|
|
8841
|
+
hint: "\u7AEF\u53E3\u8303\u56F4 1-65535"
|
|
8842
|
+
});
|
|
8843
|
+
}
|
|
8844
|
+
}
|
|
8845
|
+
const target = targetUrl !== undefined || targetHost !== undefined || targetPort !== undefined ? { url: targetUrl, host: targetHost, port: targetPort } : undefined;
|
|
8816
8846
|
return {
|
|
8817
8847
|
flags: {
|
|
8818
8848
|
output,
|
|
@@ -8821,7 +8851,8 @@ function extractGlobalFlags(args) {
|
|
|
8821
8851
|
noColor: noColor || !!process.env.NO_COLOR,
|
|
8822
8852
|
noInteractive: noInteractive || process.env.LOCAL_ROUTER_NO_INTERACTIVE === "1",
|
|
8823
8853
|
yes,
|
|
8824
|
-
explain: explain || process.env.LOCAL_ROUTER_EXPLAIN === "1"
|
|
8854
|
+
explain: explain || process.env.LOCAL_ROUTER_EXPLAIN === "1",
|
|
8855
|
+
target
|
|
8825
8856
|
},
|
|
8826
8857
|
rest
|
|
8827
8858
|
};
|
|
@@ -9179,257 +9210,6 @@ var init_output = __esm(() => {
|
|
|
9179
9210
|
init_global_flags();
|
|
9180
9211
|
});
|
|
9181
9212
|
|
|
9182
|
-
// src/cli/runtime.ts
|
|
9183
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
9184
|
-
import { homedir as homedir2 } from "os";
|
|
9185
|
-
import { join as join3, resolve as resolve2 } from "path";
|
|
9186
|
-
function getRuntimeDirs() {
|
|
9187
|
-
const override = process.env.LOCAL_ROUTER_RUNTIME_DIR;
|
|
9188
|
-
const root = override?.trim() ? override.trim() : join3(homedir2(), ".local-router");
|
|
9189
|
-
return {
|
|
9190
|
-
root,
|
|
9191
|
-
run: join3(root, "run"),
|
|
9192
|
-
logs: join3(root, "logs")
|
|
9193
|
-
};
|
|
9194
|
-
}
|
|
9195
|
-
function getRuntimeFiles() {
|
|
9196
|
-
const dirs = getRuntimeDirs();
|
|
9197
|
-
return {
|
|
9198
|
-
pid: join3(dirs.run, "local-router.pid"),
|
|
9199
|
-
state: join3(dirs.run, "status.json"),
|
|
9200
|
-
daemonLog: join3(dirs.logs, "daemon.log")
|
|
9201
|
-
};
|
|
9202
|
-
}
|
|
9203
|
-
function ensureRuntimeDirs() {
|
|
9204
|
-
const dirs = getRuntimeDirs();
|
|
9205
|
-
mkdirSync3(dirs.root, { recursive: true });
|
|
9206
|
-
mkdirSync3(dirs.run, { recursive: true });
|
|
9207
|
-
mkdirSync3(dirs.logs, { recursive: true });
|
|
9208
|
-
}
|
|
9209
|
-
function writeRuntimeState(state) {
|
|
9210
|
-
ensureRuntimeDirs();
|
|
9211
|
-
const files = getRuntimeFiles();
|
|
9212
|
-
writeFileSync3(files.pid, `${state.pid}
|
|
9213
|
-
`, "utf-8");
|
|
9214
|
-
writeFileSync3(files.state, JSON.stringify(state, null, 2), "utf-8");
|
|
9215
|
-
}
|
|
9216
|
-
function readRuntimeState() {
|
|
9217
|
-
const files = getRuntimeFiles();
|
|
9218
|
-
if (!existsSync2(files.state)) {
|
|
9219
|
-
return null;
|
|
9220
|
-
}
|
|
9221
|
-
try {
|
|
9222
|
-
return JSON.parse(readFileSync4(files.state, "utf-8"));
|
|
9223
|
-
} catch {
|
|
9224
|
-
return null;
|
|
9225
|
-
}
|
|
9226
|
-
}
|
|
9227
|
-
function clearRuntimeFiles() {
|
|
9228
|
-
const files = getRuntimeFiles();
|
|
9229
|
-
rmSync(files.pid, { force: true });
|
|
9230
|
-
rmSync(files.state, { force: true });
|
|
9231
|
-
}
|
|
9232
|
-
function resolveConfigArgPath(pathValue) {
|
|
9233
|
-
return resolve2(pathValue);
|
|
9234
|
-
}
|
|
9235
|
-
var init_runtime = () => {};
|
|
9236
|
-
|
|
9237
|
-
// src/cli/autostart.ts
|
|
9238
|
-
import { execSync } from "child_process";
|
|
9239
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
9240
|
-
import { homedir as homedir3, platform } from "os";
|
|
9241
|
-
import { dirname as dirname2, join as join4 } from "path";
|
|
9242
|
-
function getDaemonLogPath() {
|
|
9243
|
-
return getRuntimeDirs().logs + "/daemon.log";
|
|
9244
|
-
}
|
|
9245
|
-
function getLaunchAgentPath() {
|
|
9246
|
-
return join4(homedir3(), "Library", "LaunchAgents", `${LABEL}.plist`);
|
|
9247
|
-
}
|
|
9248
|
-
function buildPlist(opts) {
|
|
9249
|
-
const logPath = getDaemonLogPath();
|
|
9250
|
-
const args = [opts.execPath, ...opts.args].map((a) => ` <string>${escapeXml(a)}</string>`).join(`
|
|
9251
|
-
`);
|
|
9252
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
9253
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
9254
|
-
<plist version="1.0">
|
|
9255
|
-
<dict>
|
|
9256
|
-
<key>Label</key>
|
|
9257
|
-
<string>${escapeXml(opts.label)}</string>
|
|
9258
|
-
<key>ProgramArguments</key>
|
|
9259
|
-
<array>
|
|
9260
|
-
${args}
|
|
9261
|
-
</array>
|
|
9262
|
-
<key>RunAtLoad</key>
|
|
9263
|
-
<true/>
|
|
9264
|
-
<key>KeepAlive</key>
|
|
9265
|
-
<false/>
|
|
9266
|
-
<key>StandardOutPath</key>
|
|
9267
|
-
<string>${escapeXml(logPath)}</string>
|
|
9268
|
-
<key>StandardErrorPath</key>
|
|
9269
|
-
<string>${escapeXml(logPath)}</string>
|
|
9270
|
-
</dict>
|
|
9271
|
-
</plist>
|
|
9272
|
-
`;
|
|
9273
|
-
}
|
|
9274
|
-
function escapeXml(s) {
|
|
9275
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
9276
|
-
}
|
|
9277
|
-
function createMacosManager() {
|
|
9278
|
-
const plistPath = getLaunchAgentPath();
|
|
9279
|
-
return {
|
|
9280
|
-
platform: "macos",
|
|
9281
|
-
async isInstalled() {
|
|
9282
|
-
return existsSync3(plistPath);
|
|
9283
|
-
},
|
|
9284
|
-
async install(opts) {
|
|
9285
|
-
const dir = dirname2(plistPath);
|
|
9286
|
-
if (!existsSync3(dir))
|
|
9287
|
-
mkdirSync4(dir, { recursive: true });
|
|
9288
|
-
writeFileSync4(plistPath, buildPlist(opts), "utf-8");
|
|
9289
|
-
try {
|
|
9290
|
-
execSync(`launchctl bootout gui/$(id -u) ${plistPath} 2>/dev/null`, { stdio: "ignore" });
|
|
9291
|
-
} catch {}
|
|
9292
|
-
execSync(`launchctl bootstrap gui/$(id -u) ${plistPath}`, { stdio: "ignore" });
|
|
9293
|
-
},
|
|
9294
|
-
async uninstall() {
|
|
9295
|
-
if (!existsSync3(plistPath))
|
|
9296
|
-
return;
|
|
9297
|
-
try {
|
|
9298
|
-
execSync(`launchctl bootout gui/$(id -u) ${plistPath}`, { stdio: "ignore" });
|
|
9299
|
-
} catch {}
|
|
9300
|
-
rmSync2(plistPath, { force: true });
|
|
9301
|
-
},
|
|
9302
|
-
getServicePath() {
|
|
9303
|
-
return plistPath;
|
|
9304
|
-
}
|
|
9305
|
-
};
|
|
9306
|
-
}
|
|
9307
|
-
function getSystemdUnitPath() {
|
|
9308
|
-
return join4(homedir3(), ".config", "systemd", "user", "local-router.service");
|
|
9309
|
-
}
|
|
9310
|
-
function buildUnit(opts) {
|
|
9311
|
-
const logPath = getDaemonLogPath();
|
|
9312
|
-
const execStart = [opts.execPath, ...opts.args].join(" ");
|
|
9313
|
-
return `[Unit]
|
|
9314
|
-
Description=Local Router API Gateway
|
|
9315
|
-
After=network-online.target
|
|
9316
|
-
|
|
9317
|
-
[Service]
|
|
9318
|
-
Type=simple
|
|
9319
|
-
ExecStart=${execStart}
|
|
9320
|
-
Restart=on-failure
|
|
9321
|
-
RestartSec=5
|
|
9322
|
-
StandardOutput=append:${logPath}
|
|
9323
|
-
StandardError=append:${logPath}
|
|
9324
|
-
|
|
9325
|
-
[Install]
|
|
9326
|
-
WantedBy=default.target
|
|
9327
|
-
`;
|
|
9328
|
-
}
|
|
9329
|
-
function createLinuxManager() {
|
|
9330
|
-
const unitPath = getSystemdUnitPath();
|
|
9331
|
-
return {
|
|
9332
|
-
platform: "linux",
|
|
9333
|
-
async isInstalled() {
|
|
9334
|
-
if (!existsSync3(unitPath))
|
|
9335
|
-
return false;
|
|
9336
|
-
try {
|
|
9337
|
-
const out = execSync("systemctl --user is-enabled local-router 2>/dev/null", {
|
|
9338
|
-
encoding: "utf-8"
|
|
9339
|
-
}).trim();
|
|
9340
|
-
return out === "enabled";
|
|
9341
|
-
} catch {
|
|
9342
|
-
return false;
|
|
9343
|
-
}
|
|
9344
|
-
},
|
|
9345
|
-
async install(opts) {
|
|
9346
|
-
const dir = dirname2(unitPath);
|
|
9347
|
-
if (!existsSync3(dir))
|
|
9348
|
-
mkdirSync4(dir, { recursive: true });
|
|
9349
|
-
writeFileSync4(unitPath, buildUnit(opts), "utf-8");
|
|
9350
|
-
execSync("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
9351
|
-
execSync("systemctl --user enable local-router", { stdio: "ignore" });
|
|
9352
|
-
},
|
|
9353
|
-
async uninstall() {
|
|
9354
|
-
try {
|
|
9355
|
-
execSync("systemctl --user disable local-router", { stdio: "ignore" });
|
|
9356
|
-
} catch {}
|
|
9357
|
-
rmSync2(unitPath, { force: true });
|
|
9358
|
-
try {
|
|
9359
|
-
execSync("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
9360
|
-
} catch {}
|
|
9361
|
-
},
|
|
9362
|
-
getServicePath() {
|
|
9363
|
-
return unitPath;
|
|
9364
|
-
}
|
|
9365
|
-
};
|
|
9366
|
-
}
|
|
9367
|
-
function createWindowsManager() {
|
|
9368
|
-
return {
|
|
9369
|
-
platform: "windows",
|
|
9370
|
-
async isInstalled() {
|
|
9371
|
-
try {
|
|
9372
|
-
execSync(`reg query "${WIN_REG_KEY}" /v ${WIN_REG_VALUE}`, { stdio: "ignore" });
|
|
9373
|
-
return true;
|
|
9374
|
-
} catch {
|
|
9375
|
-
return false;
|
|
9376
|
-
}
|
|
9377
|
-
},
|
|
9378
|
-
async install(opts) {
|
|
9379
|
-
const cmd = [opts.execPath, ...opts.args].map((a) => `"${a}"`).join(" ");
|
|
9380
|
-
execSync(`reg add "${WIN_REG_KEY}" /v ${WIN_REG_VALUE} /t REG_SZ /d "${cmd}" /f`, {
|
|
9381
|
-
stdio: "ignore"
|
|
9382
|
-
});
|
|
9383
|
-
},
|
|
9384
|
-
async uninstall() {
|
|
9385
|
-
try {
|
|
9386
|
-
execSync(`reg delete "${WIN_REG_KEY}" /v ${WIN_REG_VALUE} /f`, { stdio: "ignore" });
|
|
9387
|
-
} catch {}
|
|
9388
|
-
},
|
|
9389
|
-
getServicePath() {
|
|
9390
|
-
return `${WIN_REG_KEY}\\${WIN_REG_VALUE}`;
|
|
9391
|
-
}
|
|
9392
|
-
};
|
|
9393
|
-
}
|
|
9394
|
-
function createUnsupportedManager() {
|
|
9395
|
-
return {
|
|
9396
|
-
platform: "unsupported",
|
|
9397
|
-
async isInstalled() {
|
|
9398
|
-
return false;
|
|
9399
|
-
},
|
|
9400
|
-
async install() {
|
|
9401
|
-
throw new Error("\u5F53\u524D\u5E73\u53F0\u4E0D\u652F\u6301\u81EA\u542F\u52A8");
|
|
9402
|
-
},
|
|
9403
|
-
async uninstall() {
|
|
9404
|
-
throw new Error("\u5F53\u524D\u5E73\u53F0\u4E0D\u652F\u6301\u81EA\u542F\u52A8");
|
|
9405
|
-
},
|
|
9406
|
-
getServicePath() {
|
|
9407
|
-
return "";
|
|
9408
|
-
}
|
|
9409
|
-
};
|
|
9410
|
-
}
|
|
9411
|
-
function createAutostartManager() {
|
|
9412
|
-
const p = platform();
|
|
9413
|
-
if (p === "darwin")
|
|
9414
|
-
return createMacosManager();
|
|
9415
|
-
if (p === "linux")
|
|
9416
|
-
return createLinuxManager();
|
|
9417
|
-
if (p === "win32")
|
|
9418
|
-
return createWindowsManager();
|
|
9419
|
-
return createUnsupportedManager();
|
|
9420
|
-
}
|
|
9421
|
-
function getAutostartExecArgs() {
|
|
9422
|
-
const script = process.argv[1] ?? "dist/cli.js";
|
|
9423
|
-
return {
|
|
9424
|
-
execPath: process.execPath,
|
|
9425
|
-
args: [script, "__run-server", "--mode", "daemon"]
|
|
9426
|
-
};
|
|
9427
|
-
}
|
|
9428
|
-
var LABEL = "com.lakphy.local-router", WIN_REG_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", WIN_REG_VALUE = "LocalRouter";
|
|
9429
|
-
var init_autostart = __esm(() => {
|
|
9430
|
-
init_runtime();
|
|
9431
|
-
});
|
|
9432
|
-
|
|
9433
9213
|
// node_modules/.bun/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/dist/index.mjs
|
|
9434
9214
|
function getErrorMessage(error) {
|
|
9435
9215
|
if (error == null) {
|
|
@@ -27547,14 +27327,14 @@ async function delay(delayInMs, options) {
|
|
|
27547
27327
|
return Promise.resolve();
|
|
27548
27328
|
}
|
|
27549
27329
|
const signal = options == null ? undefined : options.abortSignal;
|
|
27550
|
-
return new Promise((
|
|
27330
|
+
return new Promise((resolve2, reject) => {
|
|
27551
27331
|
if (signal == null ? undefined : signal.aborted) {
|
|
27552
27332
|
reject(createAbortError());
|
|
27553
27333
|
return;
|
|
27554
27334
|
}
|
|
27555
27335
|
const timeoutId = setTimeout(() => {
|
|
27556
27336
|
cleanup();
|
|
27557
|
-
|
|
27337
|
+
resolve2();
|
|
27558
27338
|
}, delayInMs);
|
|
27559
27339
|
const cleanup = () => {
|
|
27560
27340
|
clearTimeout(timeoutId);
|
|
@@ -29012,7 +28792,7 @@ function createProviderToolFactoryWithOutputSchema({
|
|
|
29012
28792
|
supportsDeferredResults
|
|
29013
28793
|
});
|
|
29014
28794
|
}
|
|
29015
|
-
async function
|
|
28795
|
+
async function resolve2(value) {
|
|
29016
28796
|
if (typeof value === "function") {
|
|
29017
28797
|
value = value();
|
|
29018
28798
|
}
|
|
@@ -29051,13 +28831,13 @@ var DelayedPromise = class {
|
|
|
29051
28831
|
if (this._promise) {
|
|
29052
28832
|
return this._promise;
|
|
29053
28833
|
}
|
|
29054
|
-
this._promise = new Promise((
|
|
28834
|
+
this._promise = new Promise((resolve2, reject) => {
|
|
29055
28835
|
if (this.status.type === "resolved") {
|
|
29056
|
-
|
|
28836
|
+
resolve2(this.status.value);
|
|
29057
28837
|
} else if (this.status.type === "rejected") {
|
|
29058
28838
|
reject(this.status.error);
|
|
29059
28839
|
}
|
|
29060
|
-
this._resolve =
|
|
28840
|
+
this._resolve = resolve2;
|
|
29061
28841
|
this._reject = reject;
|
|
29062
28842
|
});
|
|
29063
28843
|
return this._promise;
|
|
@@ -31515,11 +31295,11 @@ var VERSION2 = "3.0.58", anthropicErrorDataSchema, anthropicFailedResponseHandle
|
|
|
31515
31295
|
betas,
|
|
31516
31296
|
headers
|
|
31517
31297
|
}) {
|
|
31518
|
-
return combineHeaders(await
|
|
31298
|
+
return combineHeaders(await resolve2(this.config.headers), headers, betas.size > 0 ? { "anthropic-beta": Array.from(betas).join(",") } : {});
|
|
31519
31299
|
}
|
|
31520
31300
|
async getBetasFromHeaders(requestHeaders) {
|
|
31521
31301
|
var _a16, _b16;
|
|
31522
|
-
const configHeaders = await
|
|
31302
|
+
const configHeaders = await resolve2(this.config.headers);
|
|
31523
31303
|
const configBetaHeader = (_a16 = configHeaders["anthropic-beta"]) != null ? _a16 : "";
|
|
31524
31304
|
const requestBetaHeader = (_b16 = requestHeaders == null ? undefined : requestHeaders["anthropic-beta"]) != null ? _b16 : "";
|
|
31525
31305
|
return new Set([
|
|
@@ -42350,7 +42130,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
42350
42130
|
try {
|
|
42351
42131
|
const { value } = await getFromApi({
|
|
42352
42132
|
url: `${this.config.baseURL}/config`,
|
|
42353
|
-
headers: await
|
|
42133
|
+
headers: await resolve2(this.config.headers()),
|
|
42354
42134
|
successfulResponseHandler: createJsonResponseHandler(gatewayAvailableModelsResponseSchema),
|
|
42355
42135
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
42356
42136
|
errorSchema: exports_external.any(),
|
|
@@ -42368,7 +42148,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
42368
42148
|
const baseUrl = new URL(this.config.baseURL);
|
|
42369
42149
|
const { value } = await getFromApi({
|
|
42370
42150
|
url: `${baseUrl.origin}/v1/credits`,
|
|
42371
|
-
headers: await
|
|
42151
|
+
headers: await resolve2(this.config.headers()),
|
|
42372
42152
|
successfulResponseHandler: createJsonResponseHandler(gatewayCreditsResponseSchema),
|
|
42373
42153
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
42374
42154
|
errorSchema: exports_external.any(),
|
|
@@ -42401,7 +42181,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
42401
42181
|
async doGenerate(options) {
|
|
42402
42182
|
const { args, warnings } = await this.getArgs(options);
|
|
42403
42183
|
const { abortSignal } = options;
|
|
42404
|
-
const resolvedHeaders = await
|
|
42184
|
+
const resolvedHeaders = await resolve2(this.config.headers());
|
|
42405
42185
|
try {
|
|
42406
42186
|
const {
|
|
42407
42187
|
responseHeaders,
|
|
@@ -42409,7 +42189,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
42409
42189
|
rawValue: rawResponse
|
|
42410
42190
|
} = await postJsonToApi({
|
|
42411
42191
|
url: this.getUrl(),
|
|
42412
|
-
headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, false), await
|
|
42192
|
+
headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, false), await resolve2(this.config.o11yHeaders)),
|
|
42413
42193
|
body: args,
|
|
42414
42194
|
successfulResponseHandler: createJsonResponseHandler(exports_external.any()),
|
|
42415
42195
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
@@ -42432,11 +42212,11 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
42432
42212
|
async doStream(options) {
|
|
42433
42213
|
const { args, warnings } = await this.getArgs(options);
|
|
42434
42214
|
const { abortSignal } = options;
|
|
42435
|
-
const resolvedHeaders = await
|
|
42215
|
+
const resolvedHeaders = await resolve2(this.config.headers());
|
|
42436
42216
|
try {
|
|
42437
42217
|
const { value: response, responseHeaders } = await postJsonToApi({
|
|
42438
42218
|
url: this.getUrl(),
|
|
42439
|
-
headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, true), await
|
|
42219
|
+
headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, true), await resolve2(this.config.o11yHeaders)),
|
|
42440
42220
|
body: args,
|
|
42441
42221
|
successfulResponseHandler: createEventSourceResponseHandler(exports_external.any()),
|
|
42442
42222
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
@@ -42521,7 +42301,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
42521
42301
|
providerOptions
|
|
42522
42302
|
}) {
|
|
42523
42303
|
var _a92;
|
|
42524
|
-
const resolvedHeaders = await
|
|
42304
|
+
const resolvedHeaders = await resolve2(this.config.headers());
|
|
42525
42305
|
try {
|
|
42526
42306
|
const {
|
|
42527
42307
|
responseHeaders,
|
|
@@ -42529,7 +42309,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
42529
42309
|
rawValue
|
|
42530
42310
|
} = await postJsonToApi({
|
|
42531
42311
|
url: this.getUrl(),
|
|
42532
|
-
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await
|
|
42312
|
+
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve2(this.config.o11yHeaders)),
|
|
42533
42313
|
body: {
|
|
42534
42314
|
values,
|
|
42535
42315
|
...providerOptions ? { providerOptions } : {}
|
|
@@ -42585,7 +42365,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
42585
42365
|
abortSignal
|
|
42586
42366
|
}) {
|
|
42587
42367
|
var _a92, _b92, _c, _d;
|
|
42588
|
-
const resolvedHeaders = await
|
|
42368
|
+
const resolvedHeaders = await resolve2(this.config.headers());
|
|
42589
42369
|
try {
|
|
42590
42370
|
const {
|
|
42591
42371
|
responseHeaders,
|
|
@@ -42593,7 +42373,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
42593
42373
|
rawValue
|
|
42594
42374
|
} = await postJsonToApi({
|
|
42595
42375
|
url: this.getUrl(),
|
|
42596
|
-
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await
|
|
42376
|
+
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve2(this.config.o11yHeaders)),
|
|
42597
42377
|
body: {
|
|
42598
42378
|
prompt,
|
|
42599
42379
|
n,
|
|
@@ -42668,11 +42448,11 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
42668
42448
|
abortSignal
|
|
42669
42449
|
}) {
|
|
42670
42450
|
var _a92;
|
|
42671
|
-
const resolvedHeaders = await
|
|
42451
|
+
const resolvedHeaders = await resolve2(this.config.headers());
|
|
42672
42452
|
try {
|
|
42673
42453
|
const { responseHeaders, value: responseBody } = await postJsonToApi({
|
|
42674
42454
|
url: this.getUrl(),
|
|
42675
|
-
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await
|
|
42455
|
+
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve2(this.config.o11yHeaders), { accept: "text/event-stream" }),
|
|
42676
42456
|
body: {
|
|
42677
42457
|
prompt,
|
|
42678
42458
|
n,
|
|
@@ -46662,8 +46442,8 @@ function writeToServerResponse({
|
|
|
46662
46442
|
break;
|
|
46663
46443
|
const canContinue = response.write(value);
|
|
46664
46444
|
if (!canContinue) {
|
|
46665
|
-
await new Promise((
|
|
46666
|
-
response.once("drain",
|
|
46445
|
+
await new Promise((resolve3) => {
|
|
46446
|
+
response.once("drain", resolve3);
|
|
46667
46447
|
});
|
|
46668
46448
|
}
|
|
46669
46449
|
}
|
|
@@ -47424,15 +47204,15 @@ async function consumeStream({
|
|
|
47424
47204
|
}
|
|
47425
47205
|
}
|
|
47426
47206
|
function createResolvablePromise() {
|
|
47427
|
-
let
|
|
47207
|
+
let resolve3;
|
|
47428
47208
|
let reject;
|
|
47429
47209
|
const promise2 = new Promise((res, rej) => {
|
|
47430
|
-
|
|
47210
|
+
resolve3 = res;
|
|
47431
47211
|
reject = rej;
|
|
47432
47212
|
});
|
|
47433
47213
|
return {
|
|
47434
47214
|
promise: promise2,
|
|
47435
|
-
resolve:
|
|
47215
|
+
resolve: resolve3,
|
|
47436
47216
|
reject
|
|
47437
47217
|
};
|
|
47438
47218
|
}
|
|
@@ -48014,7 +47794,7 @@ var import_api, import_api2, __defProp2, __export2 = (target, all) => {
|
|
|
48014
47794
|
const schema = asSchema(inputSchema);
|
|
48015
47795
|
return {
|
|
48016
47796
|
name: "object",
|
|
48017
|
-
responseFormat:
|
|
47797
|
+
responseFormat: resolve2(schema.jsonSchema).then((jsonSchema2) => ({
|
|
48018
47798
|
type: "json",
|
|
48019
47799
|
schema: jsonSchema2,
|
|
48020
47800
|
...name21 != null && { name: name21 },
|
|
@@ -48075,7 +47855,7 @@ var import_api, import_api2, __defProp2, __export2 = (target, all) => {
|
|
|
48075
47855
|
const elementSchema = asSchema(inputElementSchema);
|
|
48076
47856
|
return {
|
|
48077
47857
|
name: "array",
|
|
48078
|
-
responseFormat:
|
|
47858
|
+
responseFormat: resolve2(elementSchema.jsonSchema).then((jsonSchema2) => {
|
|
48079
47859
|
const { $schema, ...itemSchema } = jsonSchema2;
|
|
48080
47860
|
return {
|
|
48081
47861
|
type: "json",
|
|
@@ -52573,7 +52353,7 @@ var init_path = () => {};
|
|
|
52573
52353
|
var ENCODINGS, ENCODINGS_ORDERED_KEYS, DEFAULT_DOCUMENT = "index.html", serveStatic = (options) => {
|
|
52574
52354
|
const root = options.root ?? "./";
|
|
52575
52355
|
const optionPath = options.path;
|
|
52576
|
-
const
|
|
52356
|
+
const join3 = options.join ?? defaultJoin;
|
|
52577
52357
|
return async (c, next) => {
|
|
52578
52358
|
if (c.finalized) {
|
|
52579
52359
|
return next();
|
|
@@ -52592,9 +52372,9 @@ var ENCODINGS, ENCODINGS_ORDERED_KEYS, DEFAULT_DOCUMENT = "index.html", serveSta
|
|
|
52592
52372
|
return next();
|
|
52593
52373
|
}
|
|
52594
52374
|
}
|
|
52595
|
-
let path =
|
|
52375
|
+
let path = join3(root, !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename) : filename);
|
|
52596
52376
|
if (options.isDir && await options.isDir(path)) {
|
|
52597
|
-
path =
|
|
52377
|
+
path = join3(path, DEFAULT_DOCUMENT);
|
|
52598
52378
|
}
|
|
52599
52379
|
const getContent = options.getContent;
|
|
52600
52380
|
let content = await getContent(path, c);
|
|
@@ -52642,7 +52422,7 @@ var init_serve_static = __esm(() => {
|
|
|
52642
52422
|
|
|
52643
52423
|
// node_modules/.bun/hono@4.12.5/node_modules/hono/dist/adapter/bun/serve-static.js
|
|
52644
52424
|
import { stat } from "fs/promises";
|
|
52645
|
-
import { join as
|
|
52425
|
+
import { join as join3 } from "path";
|
|
52646
52426
|
var serveStatic2 = (options) => {
|
|
52647
52427
|
return async function serveStatic22(c, next) {
|
|
52648
52428
|
const getContent = async (path) => {
|
|
@@ -52660,7 +52440,7 @@ var serveStatic2 = (options) => {
|
|
|
52660
52440
|
return serveStatic({
|
|
52661
52441
|
...options,
|
|
52662
52442
|
getContent,
|
|
52663
|
-
join:
|
|
52443
|
+
join: join3,
|
|
52664
52444
|
isDir
|
|
52665
52445
|
})(c, next);
|
|
52666
52446
|
};
|
|
@@ -52826,6 +52606,286 @@ var init_bun = __esm(() => {
|
|
|
52826
52606
|
init_server();
|
|
52827
52607
|
});
|
|
52828
52608
|
|
|
52609
|
+
// src/cli/asset-paths.ts
|
|
52610
|
+
async function findExisting(...candidates) {
|
|
52611
|
+
for (const url2 of candidates) {
|
|
52612
|
+
try {
|
|
52613
|
+
const exists = await Bun.file(url2).exists();
|
|
52614
|
+
if (exists)
|
|
52615
|
+
return url2;
|
|
52616
|
+
} catch {}
|
|
52617
|
+
}
|
|
52618
|
+
return null;
|
|
52619
|
+
}
|
|
52620
|
+
async function readPackageJson() {
|
|
52621
|
+
const url2 = await findExisting(new URL("../../package.json", import.meta.url), new URL("../package.json", import.meta.url), new URL("../../../package.json", import.meta.url));
|
|
52622
|
+
if (!url2)
|
|
52623
|
+
return null;
|
|
52624
|
+
try {
|
|
52625
|
+
return await Bun.file(url2).json();
|
|
52626
|
+
} catch {
|
|
52627
|
+
return null;
|
|
52628
|
+
}
|
|
52629
|
+
}
|
|
52630
|
+
async function findConfigSchemaUrl() {
|
|
52631
|
+
return findExisting(new URL("../../config.schema.json", import.meta.url), new URL("../config.schema.json", import.meta.url), new URL("../../../config.schema.json", import.meta.url));
|
|
52632
|
+
}
|
|
52633
|
+
async function readVersionString() {
|
|
52634
|
+
const pkg = await readPackageJson();
|
|
52635
|
+
return typeof pkg?.version === "string" ? pkg.version : "unknown";
|
|
52636
|
+
}
|
|
52637
|
+
|
|
52638
|
+
// src/cli/runtime.ts
|
|
52639
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
52640
|
+
import { homedir as homedir2 } from "os";
|
|
52641
|
+
import { join as join4, resolve as resolve3 } from "path";
|
|
52642
|
+
function getRuntimeDirs() {
|
|
52643
|
+
const override = process.env.LOCAL_ROUTER_RUNTIME_DIR;
|
|
52644
|
+
const root = override?.trim() ? override.trim() : join4(homedir2(), ".local-router");
|
|
52645
|
+
return {
|
|
52646
|
+
root,
|
|
52647
|
+
run: join4(root, "run"),
|
|
52648
|
+
logs: join4(root, "logs")
|
|
52649
|
+
};
|
|
52650
|
+
}
|
|
52651
|
+
function getRuntimeFiles() {
|
|
52652
|
+
const dirs = getRuntimeDirs();
|
|
52653
|
+
return {
|
|
52654
|
+
pid: join4(dirs.run, "local-router.pid"),
|
|
52655
|
+
state: join4(dirs.run, "status.json"),
|
|
52656
|
+
daemonLog: join4(dirs.logs, "daemon.log")
|
|
52657
|
+
};
|
|
52658
|
+
}
|
|
52659
|
+
function ensureRuntimeDirs() {
|
|
52660
|
+
const dirs = getRuntimeDirs();
|
|
52661
|
+
mkdirSync3(dirs.root, { recursive: true });
|
|
52662
|
+
mkdirSync3(dirs.run, { recursive: true });
|
|
52663
|
+
mkdirSync3(dirs.logs, { recursive: true });
|
|
52664
|
+
}
|
|
52665
|
+
function writeRuntimeState(state) {
|
|
52666
|
+
ensureRuntimeDirs();
|
|
52667
|
+
const files = getRuntimeFiles();
|
|
52668
|
+
writeFileSync3(files.pid, `${state.pid}
|
|
52669
|
+
`, "utf-8");
|
|
52670
|
+
writeFileSync3(files.state, JSON.stringify(state, null, 2), "utf-8");
|
|
52671
|
+
}
|
|
52672
|
+
function readRuntimeState() {
|
|
52673
|
+
const files = getRuntimeFiles();
|
|
52674
|
+
if (!existsSync2(files.state)) {
|
|
52675
|
+
return null;
|
|
52676
|
+
}
|
|
52677
|
+
try {
|
|
52678
|
+
return JSON.parse(readFileSync4(files.state, "utf-8"));
|
|
52679
|
+
} catch {
|
|
52680
|
+
return null;
|
|
52681
|
+
}
|
|
52682
|
+
}
|
|
52683
|
+
function clearRuntimeFiles() {
|
|
52684
|
+
const files = getRuntimeFiles();
|
|
52685
|
+
rmSync(files.pid, { force: true });
|
|
52686
|
+
rmSync(files.state, { force: true });
|
|
52687
|
+
}
|
|
52688
|
+
function resolveConfigArgPath(pathValue) {
|
|
52689
|
+
return resolve3(pathValue);
|
|
52690
|
+
}
|
|
52691
|
+
var init_runtime = () => {};
|
|
52692
|
+
|
|
52693
|
+
// src/cli/autostart.ts
|
|
52694
|
+
import { execSync } from "child_process";
|
|
52695
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
52696
|
+
import { homedir as homedir3, platform } from "os";
|
|
52697
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
52698
|
+
function getDaemonLogPath() {
|
|
52699
|
+
return getRuntimeDirs().logs + "/daemon.log";
|
|
52700
|
+
}
|
|
52701
|
+
function getLaunchAgentPath() {
|
|
52702
|
+
return join5(homedir3(), "Library", "LaunchAgents", `${LABEL}.plist`);
|
|
52703
|
+
}
|
|
52704
|
+
function buildPlist(opts) {
|
|
52705
|
+
const logPath = getDaemonLogPath();
|
|
52706
|
+
const args = [opts.execPath, ...opts.args].map((a) => ` <string>${escapeXml(a)}</string>`).join(`
|
|
52707
|
+
`);
|
|
52708
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
52709
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
52710
|
+
<plist version="1.0">
|
|
52711
|
+
<dict>
|
|
52712
|
+
<key>Label</key>
|
|
52713
|
+
<string>${escapeXml(opts.label)}</string>
|
|
52714
|
+
<key>ProgramArguments</key>
|
|
52715
|
+
<array>
|
|
52716
|
+
${args}
|
|
52717
|
+
</array>
|
|
52718
|
+
<key>RunAtLoad</key>
|
|
52719
|
+
<true/>
|
|
52720
|
+
<key>KeepAlive</key>
|
|
52721
|
+
<false/>
|
|
52722
|
+
<key>StandardOutPath</key>
|
|
52723
|
+
<string>${escapeXml(logPath)}</string>
|
|
52724
|
+
<key>StandardErrorPath</key>
|
|
52725
|
+
<string>${escapeXml(logPath)}</string>
|
|
52726
|
+
</dict>
|
|
52727
|
+
</plist>
|
|
52728
|
+
`;
|
|
52729
|
+
}
|
|
52730
|
+
function escapeXml(s) {
|
|
52731
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
52732
|
+
}
|
|
52733
|
+
function createMacosManager() {
|
|
52734
|
+
const plistPath = getLaunchAgentPath();
|
|
52735
|
+
return {
|
|
52736
|
+
platform: "macos",
|
|
52737
|
+
async isInstalled() {
|
|
52738
|
+
return existsSync3(plistPath);
|
|
52739
|
+
},
|
|
52740
|
+
async install(opts) {
|
|
52741
|
+
const dir = dirname3(plistPath);
|
|
52742
|
+
if (!existsSync3(dir))
|
|
52743
|
+
mkdirSync4(dir, { recursive: true });
|
|
52744
|
+
writeFileSync4(plistPath, buildPlist(opts), "utf-8");
|
|
52745
|
+
try {
|
|
52746
|
+
execSync(`launchctl bootout gui/$(id -u) ${plistPath} 2>/dev/null`, { stdio: "ignore" });
|
|
52747
|
+
} catch {}
|
|
52748
|
+
execSync(`launchctl bootstrap gui/$(id -u) ${plistPath}`, { stdio: "ignore" });
|
|
52749
|
+
},
|
|
52750
|
+
async uninstall() {
|
|
52751
|
+
if (!existsSync3(plistPath))
|
|
52752
|
+
return;
|
|
52753
|
+
try {
|
|
52754
|
+
execSync(`launchctl bootout gui/$(id -u) ${plistPath}`, { stdio: "ignore" });
|
|
52755
|
+
} catch {}
|
|
52756
|
+
rmSync2(plistPath, { force: true });
|
|
52757
|
+
},
|
|
52758
|
+
getServicePath() {
|
|
52759
|
+
return plistPath;
|
|
52760
|
+
}
|
|
52761
|
+
};
|
|
52762
|
+
}
|
|
52763
|
+
function getSystemdUnitPath() {
|
|
52764
|
+
return join5(homedir3(), ".config", "systemd", "user", "local-router.service");
|
|
52765
|
+
}
|
|
52766
|
+
function buildUnit(opts) {
|
|
52767
|
+
const logPath = getDaemonLogPath();
|
|
52768
|
+
const execStart = [opts.execPath, ...opts.args].join(" ");
|
|
52769
|
+
return `[Unit]
|
|
52770
|
+
Description=Local Router API Gateway
|
|
52771
|
+
After=network-online.target
|
|
52772
|
+
|
|
52773
|
+
[Service]
|
|
52774
|
+
Type=simple
|
|
52775
|
+
ExecStart=${execStart}
|
|
52776
|
+
Restart=on-failure
|
|
52777
|
+
RestartSec=5
|
|
52778
|
+
StandardOutput=append:${logPath}
|
|
52779
|
+
StandardError=append:${logPath}
|
|
52780
|
+
|
|
52781
|
+
[Install]
|
|
52782
|
+
WantedBy=default.target
|
|
52783
|
+
`;
|
|
52784
|
+
}
|
|
52785
|
+
function createLinuxManager() {
|
|
52786
|
+
const unitPath = getSystemdUnitPath();
|
|
52787
|
+
return {
|
|
52788
|
+
platform: "linux",
|
|
52789
|
+
async isInstalled() {
|
|
52790
|
+
if (!existsSync3(unitPath))
|
|
52791
|
+
return false;
|
|
52792
|
+
try {
|
|
52793
|
+
const out = execSync("systemctl --user is-enabled local-router 2>/dev/null", {
|
|
52794
|
+
encoding: "utf-8"
|
|
52795
|
+
}).trim();
|
|
52796
|
+
return out === "enabled";
|
|
52797
|
+
} catch {
|
|
52798
|
+
return false;
|
|
52799
|
+
}
|
|
52800
|
+
},
|
|
52801
|
+
async install(opts) {
|
|
52802
|
+
const dir = dirname3(unitPath);
|
|
52803
|
+
if (!existsSync3(dir))
|
|
52804
|
+
mkdirSync4(dir, { recursive: true });
|
|
52805
|
+
writeFileSync4(unitPath, buildUnit(opts), "utf-8");
|
|
52806
|
+
execSync("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
52807
|
+
execSync("systemctl --user enable local-router", { stdio: "ignore" });
|
|
52808
|
+
},
|
|
52809
|
+
async uninstall() {
|
|
52810
|
+
try {
|
|
52811
|
+
execSync("systemctl --user disable local-router", { stdio: "ignore" });
|
|
52812
|
+
} catch {}
|
|
52813
|
+
rmSync2(unitPath, { force: true });
|
|
52814
|
+
try {
|
|
52815
|
+
execSync("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
52816
|
+
} catch {}
|
|
52817
|
+
},
|
|
52818
|
+
getServicePath() {
|
|
52819
|
+
return unitPath;
|
|
52820
|
+
}
|
|
52821
|
+
};
|
|
52822
|
+
}
|
|
52823
|
+
function createWindowsManager() {
|
|
52824
|
+
return {
|
|
52825
|
+
platform: "windows",
|
|
52826
|
+
async isInstalled() {
|
|
52827
|
+
try {
|
|
52828
|
+
execSync(`reg query "${WIN_REG_KEY}" /v ${WIN_REG_VALUE}`, { stdio: "ignore" });
|
|
52829
|
+
return true;
|
|
52830
|
+
} catch {
|
|
52831
|
+
return false;
|
|
52832
|
+
}
|
|
52833
|
+
},
|
|
52834
|
+
async install(opts) {
|
|
52835
|
+
const cmd = [opts.execPath, ...opts.args].map((a) => `"${a}"`).join(" ");
|
|
52836
|
+
execSync(`reg add "${WIN_REG_KEY}" /v ${WIN_REG_VALUE} /t REG_SZ /d "${cmd}" /f`, {
|
|
52837
|
+
stdio: "ignore"
|
|
52838
|
+
});
|
|
52839
|
+
},
|
|
52840
|
+
async uninstall() {
|
|
52841
|
+
try {
|
|
52842
|
+
execSync(`reg delete "${WIN_REG_KEY}" /v ${WIN_REG_VALUE} /f`, { stdio: "ignore" });
|
|
52843
|
+
} catch {}
|
|
52844
|
+
},
|
|
52845
|
+
getServicePath() {
|
|
52846
|
+
return `${WIN_REG_KEY}\\${WIN_REG_VALUE}`;
|
|
52847
|
+
}
|
|
52848
|
+
};
|
|
52849
|
+
}
|
|
52850
|
+
function createUnsupportedManager() {
|
|
52851
|
+
return {
|
|
52852
|
+
platform: "unsupported",
|
|
52853
|
+
async isInstalled() {
|
|
52854
|
+
return false;
|
|
52855
|
+
},
|
|
52856
|
+
async install() {
|
|
52857
|
+
throw new Error("\u5F53\u524D\u5E73\u53F0\u4E0D\u652F\u6301\u81EA\u542F\u52A8");
|
|
52858
|
+
},
|
|
52859
|
+
async uninstall() {
|
|
52860
|
+
throw new Error("\u5F53\u524D\u5E73\u53F0\u4E0D\u652F\u6301\u81EA\u542F\u52A8");
|
|
52861
|
+
},
|
|
52862
|
+
getServicePath() {
|
|
52863
|
+
return "";
|
|
52864
|
+
}
|
|
52865
|
+
};
|
|
52866
|
+
}
|
|
52867
|
+
function createAutostartManager() {
|
|
52868
|
+
const p = platform();
|
|
52869
|
+
if (p === "darwin")
|
|
52870
|
+
return createMacosManager();
|
|
52871
|
+
if (p === "linux")
|
|
52872
|
+
return createLinuxManager();
|
|
52873
|
+
if (p === "win32")
|
|
52874
|
+
return createWindowsManager();
|
|
52875
|
+
return createUnsupportedManager();
|
|
52876
|
+
}
|
|
52877
|
+
function getAutostartExecArgs() {
|
|
52878
|
+
const script = process.argv[1] ?? "dist/cli.js";
|
|
52879
|
+
return {
|
|
52880
|
+
execPath: process.execPath,
|
|
52881
|
+
args: [script, "__run-server", "--mode", "daemon"]
|
|
52882
|
+
};
|
|
52883
|
+
}
|
|
52884
|
+
var LABEL = "com.lakphy.local-router", WIN_REG_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", WIN_REG_VALUE = "LocalRouter";
|
|
52885
|
+
var init_autostart = __esm(() => {
|
|
52886
|
+
init_runtime();
|
|
52887
|
+
});
|
|
52888
|
+
|
|
52829
52889
|
// src/config-store.ts
|
|
52830
52890
|
import { writeFileSync as writeFileSync5 } from "fs";
|
|
52831
52891
|
import { resolve as resolve4 } from "path";
|
|
@@ -57455,6 +57515,97 @@ var init_openapi = __esm(() => {
|
|
|
57455
57515
|
}
|
|
57456
57516
|
}
|
|
57457
57517
|
},
|
|
57518
|
+
"/api/models": {
|
|
57519
|
+
get: {
|
|
57520
|
+
tags: ["Health"],
|
|
57521
|
+
summary: "\u55C5\u63A2\u53EF\u7528\u6A21\u578B\u8DEF\u7531",
|
|
57522
|
+
description: "\u8FD4\u56DE\u672C\u673A\u6307\u5B9A\u534F\u8BAE\u4E0B\u53EF\u7528\u7684\u6A21\u578B\u8DEF\u7531\u522B\u540D\uFF08routes[protocol] \u7684 key\uFF0C\u6392\u9664 * \u901A\u914D\uFF09\u3002\u4F9B\u5C40\u57DF\u7F51\u5185\u5176\u4ED6 local-router \u63A2\u6D4B\u4F7F\u7528\u3002",
|
|
57523
|
+
parameters: [
|
|
57524
|
+
{
|
|
57525
|
+
name: "protocol",
|
|
57526
|
+
in: "query",
|
|
57527
|
+
required: true,
|
|
57528
|
+
schema: {
|
|
57529
|
+
type: "string",
|
|
57530
|
+
enum: ["openai-completions", "openai-responses", "anthropic-messages"]
|
|
57531
|
+
},
|
|
57532
|
+
description: "\u534F\u8BAE\u7C7B\u578B"
|
|
57533
|
+
}
|
|
57534
|
+
],
|
|
57535
|
+
responses: {
|
|
57536
|
+
"200": {
|
|
57537
|
+
description: "\u53EF\u7528\u6A21\u578B\u8DEF\u7531\u5217\u8868",
|
|
57538
|
+
content: {
|
|
57539
|
+
"application/json": {
|
|
57540
|
+
schema: {
|
|
57541
|
+
type: "object",
|
|
57542
|
+
properties: {
|
|
57543
|
+
protocol: { type: "string", example: "anthropic-messages" },
|
|
57544
|
+
models: {
|
|
57545
|
+
type: "array",
|
|
57546
|
+
items: { type: "string" },
|
|
57547
|
+
example: ["claude-3-5-sonnet", "claude-3-opus"]
|
|
57548
|
+
}
|
|
57549
|
+
}
|
|
57550
|
+
}
|
|
57551
|
+
}
|
|
57552
|
+
}
|
|
57553
|
+
},
|
|
57554
|
+
"400": { description: "\u534F\u8BAE\u53C2\u6570\u7F3A\u5931\u6216\u65E0\u6548" }
|
|
57555
|
+
}
|
|
57556
|
+
}
|
|
57557
|
+
},
|
|
57558
|
+
"/api/providers/discover": {
|
|
57559
|
+
get: {
|
|
57560
|
+
tags: ["Health"],
|
|
57561
|
+
summary: "\u53D1\u73B0\u5C40\u57DF\u7F51 local-router \u7684\u6A21\u578B",
|
|
57562
|
+
description: "\u7531\u672C\u673A server \u4EE3\u4E3A\u8BF7\u6C42\u5BF9\u7AEF local-router \u7684 /api/models\uFF08\u89C4\u907F\u6D4F\u89C8\u5668\u8DE8\u57DF\uFF09\uFF0C\u8FD4\u56DE\u5BF9\u7AEF\u67D0\u534F\u8BAE\u4E0B\u7684\u53EF\u7528\u6A21\u578B\u8DEF\u7531\u3002",
|
|
57563
|
+
parameters: [
|
|
57564
|
+
{
|
|
57565
|
+
name: "ip",
|
|
57566
|
+
in: "query",
|
|
57567
|
+
required: true,
|
|
57568
|
+
schema: { type: "string" },
|
|
57569
|
+
description: "\u5BF9\u7AEF IP"
|
|
57570
|
+
},
|
|
57571
|
+
{
|
|
57572
|
+
name: "port",
|
|
57573
|
+
in: "query",
|
|
57574
|
+
required: false,
|
|
57575
|
+
schema: { type: "string", default: "4099" },
|
|
57576
|
+
description: "\u5BF9\u7AEF\u7AEF\u53E3\uFF0C\u9ED8\u8BA4 4099"
|
|
57577
|
+
},
|
|
57578
|
+
{
|
|
57579
|
+
name: "protocol",
|
|
57580
|
+
in: "query",
|
|
57581
|
+
required: true,
|
|
57582
|
+
schema: {
|
|
57583
|
+
type: "string",
|
|
57584
|
+
enum: ["openai-completions", "openai-responses", "anthropic-messages"]
|
|
57585
|
+
},
|
|
57586
|
+
description: "\u534F\u8BAE\u7C7B\u578B"
|
|
57587
|
+
}
|
|
57588
|
+
],
|
|
57589
|
+
responses: {
|
|
57590
|
+
"200": {
|
|
57591
|
+
description: "\u5BF9\u7AEF\u53EF\u7528\u6A21\u578B\u8DEF\u7531\u5217\u8868",
|
|
57592
|
+
content: {
|
|
57593
|
+
"application/json": {
|
|
57594
|
+
schema: {
|
|
57595
|
+
type: "object",
|
|
57596
|
+
properties: {
|
|
57597
|
+
protocol: { type: "string", example: "anthropic-messages" },
|
|
57598
|
+
models: { type: "array", items: { type: "string" } }
|
|
57599
|
+
}
|
|
57600
|
+
}
|
|
57601
|
+
}
|
|
57602
|
+
}
|
|
57603
|
+
},
|
|
57604
|
+
"400": { description: "\u53C2\u6570\u7F3A\u5931" },
|
|
57605
|
+
"502": { description: "\u65E0\u6CD5\u8FDE\u63A5\u5BF9\u7AEF\u6216\u5BF9\u7AEF\u8FD4\u56DE\u9519\u8BEF" }
|
|
57606
|
+
}
|
|
57607
|
+
}
|
|
57608
|
+
},
|
|
57458
57609
|
"/api/metrics/logs": {
|
|
57459
57610
|
get: {
|
|
57460
57611
|
tags: ["Health"],
|
|
@@ -59234,6 +59385,13 @@ function createServerAddressInfo(listenHost, port) {
|
|
|
59234
59385
|
// src/index.ts
|
|
59235
59386
|
import { readFileSync as readFileSync8 } from "fs";
|
|
59236
59387
|
import { dirname as dirname4, resolve as resolve8 } from "path";
|
|
59388
|
+
function readRestartCriticalServerFields(config2) {
|
|
59389
|
+
return {
|
|
59390
|
+
host: config2.server?.host ?? "0.0.0.0",
|
|
59391
|
+
port: config2.server?.port ?? 4099,
|
|
59392
|
+
idleTimeout: config2.server?.idleTimeout ?? 0
|
|
59393
|
+
};
|
|
59394
|
+
}
|
|
59237
59395
|
function printIntegrationGuide(config2, listen = {}) {
|
|
59238
59396
|
const host = listen.host ?? process.env.HOST ?? "0.0.0.0";
|
|
59239
59397
|
const parsedPort = listen.port ?? Number.parseInt(process.env.PORT ?? "4099", 10);
|
|
@@ -59313,7 +59471,7 @@ function createChatProxyModel(providerName, providerConfig, model) {
|
|
|
59313
59471
|
throw new Error(`\u6682\u4E0D\u652F\u6301\u7684 provider \u7C7B\u578B: ${providerConfig.type}`);
|
|
59314
59472
|
}
|
|
59315
59473
|
}
|
|
59316
|
-
function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
59474
|
+
function createAdminApiRoutes(store, pluginManager, registerCleanup, serverControl, restartLogStorageTask, serviceVersion) {
|
|
59317
59475
|
const api2 = new Hono2;
|
|
59318
59476
|
const cryptoSessions = new Map;
|
|
59319
59477
|
const CRYPTO_SESSION_TTL_MS = 2 * 60 * 1000;
|
|
@@ -59353,7 +59511,16 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
59353
59511
|
}
|
|
59354
59512
|
cryptoSessions.clear();
|
|
59355
59513
|
});
|
|
59356
|
-
api2.get("/health", (c) =>
|
|
59514
|
+
api2.get("/health", (c) => {
|
|
59515
|
+
const addr = serverControl?.current ?? readRestartCriticalServerFields(store.get());
|
|
59516
|
+
return c.json({
|
|
59517
|
+
status: "ok",
|
|
59518
|
+
service: "local-router",
|
|
59519
|
+
version: serviceVersion ?? "unknown",
|
|
59520
|
+
host: addr.host,
|
|
59521
|
+
port: addr.port
|
|
59522
|
+
});
|
|
59523
|
+
});
|
|
59357
59524
|
api2.post("/crypto/handshake", async (c) => {
|
|
59358
59525
|
pruneExpiredCryptoSessions();
|
|
59359
59526
|
if (cryptoSessions.size >= CRYPTO_SESSION_MAX) {
|
|
@@ -59426,18 +59593,30 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
59426
59593
|
});
|
|
59427
59594
|
api2.post("/config/apply", async (_c) => {
|
|
59428
59595
|
try {
|
|
59596
|
+
const fallbackBefore = readRestartCriticalServerFields(store.get());
|
|
59597
|
+
const before = serverControl?.current ?? fallbackBefore;
|
|
59429
59598
|
const config2 = store.reload();
|
|
59599
|
+
const after = readRestartCriticalServerFields(config2);
|
|
59430
59600
|
if (config2.log) {
|
|
59431
59601
|
const logBaseDir = resolveLogBaseDir(config2.log);
|
|
59432
59602
|
initLogger(logBaseDir, config2.log);
|
|
59603
|
+
} else {
|
|
59604
|
+
resetLogger();
|
|
59433
59605
|
}
|
|
59606
|
+
restartLogStorageTask?.(config2.log);
|
|
59434
59607
|
const pluginResult = await pluginManager.reloadAll(config2.providers);
|
|
59608
|
+
const restartRequired = before.host !== after.host || before.port !== after.port || before.idleTimeout !== after.idleTimeout;
|
|
59435
59609
|
return _c.json({
|
|
59436
59610
|
ok: true,
|
|
59437
59611
|
summary: {
|
|
59438
59612
|
providers: Object.keys(config2.providers).length,
|
|
59439
59613
|
routes: Object.keys(config2.routes).length
|
|
59440
59614
|
},
|
|
59615
|
+
restartRequired,
|
|
59616
|
+
...restartRequired && {
|
|
59617
|
+
listen: { host: after.host, port: after.port },
|
|
59618
|
+
canRestart: Boolean(serverControl)
|
|
59619
|
+
},
|
|
59441
59620
|
...pluginResult.failures.length > 0 && {
|
|
59442
59621
|
pluginWarnings: pluginResult.failures
|
|
59443
59622
|
}
|
|
@@ -59446,12 +59625,57 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
59446
59625
|
return _c.json({ error: `\u5E94\u7528\u914D\u7F6E\u5931\u8D25: ${err instanceof Error ? err.message : err}` }, 500);
|
|
59447
59626
|
}
|
|
59448
59627
|
});
|
|
59628
|
+
api2.post("/restart", (c) => {
|
|
59629
|
+
if (!serverControl) {
|
|
59630
|
+
return c.json({ error: "\u5F53\u524D\u8FD0\u884C\u65B9\u5F0F\u4E0D\u652F\u6301\u81EA\u52A8\u91CD\u542F\uFF0C\u8BF7\u624B\u52A8\u6267\u884C local-router restart" }, 501);
|
|
59631
|
+
}
|
|
59632
|
+
const { host, port } = readRestartCriticalServerFields(store.get());
|
|
59633
|
+
serverControl.requestRestart();
|
|
59634
|
+
return c.json({ ok: true, listen: { host, port } });
|
|
59635
|
+
});
|
|
59449
59636
|
api2.get("/config/meta", (c) => {
|
|
59450
59637
|
return c.json({
|
|
59451
59638
|
configPath: store.getPath(),
|
|
59452
59639
|
routeTypes: Object.keys(ROUTE_REGISTRY)
|
|
59453
59640
|
});
|
|
59454
59641
|
});
|
|
59642
|
+
api2.get("/models", (c) => {
|
|
59643
|
+
const protocol = c.req.query("protocol");
|
|
59644
|
+
const routeTypes = Object.keys(ROUTE_REGISTRY);
|
|
59645
|
+
if (!protocol || !routeTypes.includes(protocol)) {
|
|
59646
|
+
return c.json({ error: "invalid or missing protocol", routeTypes }, 400);
|
|
59647
|
+
}
|
|
59648
|
+
const routes = store.get().routes[protocol] ?? {};
|
|
59649
|
+
const models = Object.keys(routes).filter((k) => k !== "*");
|
|
59650
|
+
return c.json({ protocol, models });
|
|
59651
|
+
});
|
|
59652
|
+
api2.get("/providers/discover", async (c) => {
|
|
59653
|
+
const ip = c.req.query("ip");
|
|
59654
|
+
const portStr = c.req.query("port") ?? "4099";
|
|
59655
|
+
const protocol = c.req.query("protocol");
|
|
59656
|
+
if (!ip || !protocol) {
|
|
59657
|
+
return c.json({ error: "ip and protocol are required" }, 400);
|
|
59658
|
+
}
|
|
59659
|
+
const port = Number(portStr);
|
|
59660
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
59661
|
+
return c.json({ error: "\u7AEF\u53E3\u65E0\u6548\uFF0C\u5FC5\u987B\u662F 1-65535 \u7684\u6574\u6570" }, 400);
|
|
59662
|
+
}
|
|
59663
|
+
if (!isLoopbackAddress(ip) && !isLanAddress(ip)) {
|
|
59664
|
+
return c.json({ error: "\u4EC5\u652F\u6301\u5C40\u57DF\u7F51\u6216\u672C\u673A IP \u5730\u5740" }, 400);
|
|
59665
|
+
}
|
|
59666
|
+
const url2 = `http://${ip}:${port}/api/models?protocol=${encodeURIComponent(protocol)}`;
|
|
59667
|
+
try {
|
|
59668
|
+
const res = await fetch(url2, { signal: AbortSignal.timeout(5000) });
|
|
59669
|
+
if (!res.ok) {
|
|
59670
|
+
const body = await res.json().catch(() => ({}));
|
|
59671
|
+
return c.json({ error: body.error ?? `remote returned ${res.status}` }, 502);
|
|
59672
|
+
}
|
|
59673
|
+
const data = await res.json();
|
|
59674
|
+
return c.json(data);
|
|
59675
|
+
} catch (err) {
|
|
59676
|
+
return c.json({ error: `\u65E0\u6CD5\u8FDE\u63A5\u5BF9\u7AEF local-router: ${err instanceof Error ? err.message : err}` }, 502);
|
|
59677
|
+
}
|
|
59678
|
+
});
|
|
59455
59679
|
api2.get("/config/schema", (c) => {
|
|
59456
59680
|
try {
|
|
59457
59681
|
return c.json(schemaJson);
|
|
@@ -59954,6 +60178,7 @@ async function proxyAdminToDevServer(c, origin) {
|
|
|
59954
60178
|
}
|
|
59955
60179
|
async function createApp(store, options) {
|
|
59956
60180
|
const config2 = store.get();
|
|
60181
|
+
const serviceVersion = await readVersionString();
|
|
59957
60182
|
console.log(`\u5DF2\u52A0\u8F7D\u914D\u7F6E: ${store.getPath()}`);
|
|
59958
60183
|
if (config2.log) {
|
|
59959
60184
|
const logBaseDir = resolveLogBaseDir(config2.log);
|
|
@@ -59961,8 +60186,14 @@ async function createApp(store, options) {
|
|
|
59961
60186
|
} else {
|
|
59962
60187
|
resetLogger();
|
|
59963
60188
|
}
|
|
59964
|
-
|
|
59965
|
-
|
|
60189
|
+
let stopLogStorageTask = startLogStorageBackgroundTask(config2.log);
|
|
60190
|
+
const restartLogStorageTask = (logConfig) => {
|
|
60191
|
+
try {
|
|
60192
|
+
stopLogStorageTask();
|
|
60193
|
+
} catch {}
|
|
60194
|
+
stopLogStorageTask = startLogStorageBackgroundTask(logConfig);
|
|
60195
|
+
};
|
|
60196
|
+
options?.registerCleanup?.(() => stopLogStorageTask());
|
|
59966
60197
|
const configDir = dirname4(resolve8(store.getPath()));
|
|
59967
60198
|
const pluginManager = new PluginManager(configDir);
|
|
59968
60199
|
const reloadResult = await pluginManager.reloadAll(config2.providers);
|
|
@@ -59981,7 +60212,7 @@ async function createApp(store, options) {
|
|
|
59981
60212
|
app.route(entry.mountPrefix, subApp);
|
|
59982
60213
|
console.log(`\u5DF2\u6CE8\u518C\u8DEF\u7531: ${routeType} -> ${entry.mountPrefix}`);
|
|
59983
60214
|
}
|
|
59984
|
-
app.route("/api", createAdminApiRoutes(store, pluginManager, options?.registerCleanup));
|
|
60215
|
+
app.route("/api", createAdminApiRoutes(store, pluginManager, options?.registerCleanup, options?.serverControl, restartLogStorageTask, serviceVersion));
|
|
59985
60216
|
console.log("\u5DF2\u6CE8\u518C\u7BA1\u7406 API: /api");
|
|
59986
60217
|
app.get("/api/docs", middleware({ url: "/api/openapi.json" }));
|
|
59987
60218
|
app.get("/api/openapi.json", (c) => c.json(openAPISpec));
|
|
@@ -60010,11 +60241,12 @@ async function createApp(store, options) {
|
|
|
60010
60241
|
}
|
|
60011
60242
|
return app;
|
|
60012
60243
|
}
|
|
60013
|
-
async function createAppRuntimeFromConfigPath(configPath, listen) {
|
|
60244
|
+
async function createAppRuntimeFromConfigPath(configPath, listen, serverControl) {
|
|
60014
60245
|
const store = new ConfigStore(configPath);
|
|
60015
60246
|
const cleanups = [];
|
|
60016
60247
|
const app = await createApp(store, {
|
|
60017
60248
|
listen,
|
|
60249
|
+
serverControl,
|
|
60018
60250
|
registerCleanup: (cleanup) => {
|
|
60019
60251
|
cleanups.push(cleanup);
|
|
60020
60252
|
}
|
|
@@ -60035,7 +60267,6 @@ async function createAppRuntimeFromConfigPath(configPath, listen) {
|
|
|
60035
60267
|
}
|
|
60036
60268
|
var ROUTE_REGISTRY;
|
|
60037
60269
|
var init_src = __esm(() => {
|
|
60038
|
-
init_autostart();
|
|
60039
60270
|
init_dist4();
|
|
60040
60271
|
init_dist5();
|
|
60041
60272
|
init_dist6();
|
|
@@ -60043,6 +60274,7 @@ var init_src = __esm(() => {
|
|
|
60043
60274
|
init_dist9();
|
|
60044
60275
|
init_dist10();
|
|
60045
60276
|
init_bun();
|
|
60277
|
+
init_autostart();
|
|
60046
60278
|
init_config();
|
|
60047
60279
|
init_config_store();
|
|
60048
60280
|
init_config_validate();
|
|
@@ -60116,11 +60348,15 @@ function resolveIdleTimeoutSeconds(explicit) {
|
|
|
60116
60348
|
return DEFAULT_IDLE_TIMEOUT_SECONDS;
|
|
60117
60349
|
}
|
|
60118
60350
|
async function startServer(options) {
|
|
60351
|
+
const idleTimeout = resolveIdleTimeoutSeconds(options.idleTimeoutSeconds);
|
|
60352
|
+
const requestRestart = options.requestRestart;
|
|
60119
60353
|
const runtime = await createAppRuntimeFromConfigPath(options.configPath, {
|
|
60120
60354
|
host: options.host,
|
|
60121
60355
|
port: options.port
|
|
60122
|
-
}
|
|
60123
|
-
|
|
60356
|
+
}, requestRestart ? {
|
|
60357
|
+
requestRestart,
|
|
60358
|
+
current: { host: options.host, port: options.port, idleTimeout }
|
|
60359
|
+
} : undefined);
|
|
60124
60360
|
const server = Bun.serve({
|
|
60125
60361
|
fetch: (request, server2) => {
|
|
60126
60362
|
const remoteAddress = server2.requestIP(request)?.address ?? null;
|
|
@@ -60163,6 +60399,7 @@ var exports_process = {};
|
|
|
60163
60399
|
__export(exports_process, {
|
|
60164
60400
|
stopProcess: () => stopProcess,
|
|
60165
60401
|
startDaemon: () => startDaemon,
|
|
60402
|
+
spawnDetachedRestart: () => spawnDetachedRestart,
|
|
60166
60403
|
runServerProcess: () => runServerProcess,
|
|
60167
60404
|
readLogDelta: () => readLogDelta,
|
|
60168
60405
|
parseSharedFlags: () => parseSharedFlags,
|
|
@@ -60320,7 +60557,8 @@ async function runServerProcess(opts) {
|
|
|
60320
60557
|
configPath: ensured.path,
|
|
60321
60558
|
host,
|
|
60322
60559
|
port,
|
|
60323
|
-
idleTimeoutSeconds: Number.isFinite(idleTimeoutSeconds) ? idleTimeoutSeconds : undefined
|
|
60560
|
+
idleTimeoutSeconds: Number.isFinite(idleTimeoutSeconds) ? idleTimeoutSeconds : undefined,
|
|
60561
|
+
requestRestart: () => spawnDetachedRestart({ configPath: ensured.path })
|
|
60324
60562
|
});
|
|
60325
60563
|
} catch (err) {
|
|
60326
60564
|
const code = err?.code;
|
|
@@ -60424,6 +60662,32 @@ async function startDaemon(flags) {
|
|
|
60424
60662
|
throw new Error(`\u540E\u53F0\u542F\u52A8\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u65E5\u5FD7: ${files.daemonLog}
|
|
60425
60663
|
${tail}`);
|
|
60426
60664
|
}
|
|
60665
|
+
function spawnDetachedRestart(opts = {}) {
|
|
60666
|
+
const trigger = () => {
|
|
60667
|
+
try {
|
|
60668
|
+
ensureRuntimeDirs();
|
|
60669
|
+
const files = getRuntimeFiles();
|
|
60670
|
+
const fd = openSync3(files.daemonLog, "a");
|
|
60671
|
+
const cliScript = process.argv[1] ?? "src/cli.ts";
|
|
60672
|
+
const childArgs = [cliScript, "restart"];
|
|
60673
|
+
if (opts.configPath) {
|
|
60674
|
+
childArgs.push("--config", opts.configPath);
|
|
60675
|
+
}
|
|
60676
|
+
const child = Bun.spawn([process.execPath, ...childArgs], {
|
|
60677
|
+
stdin: "ignore",
|
|
60678
|
+
stdout: fd,
|
|
60679
|
+
stderr: fd,
|
|
60680
|
+
detached: true
|
|
60681
|
+
});
|
|
60682
|
+
closeSync3(fd);
|
|
60683
|
+
child.unref();
|
|
60684
|
+
} catch (err) {
|
|
60685
|
+
console.error(`[restart] \u81EA\u91CD\u542F\u89E6\u53D1\u5931\u8D25: ${err instanceof Error ? err.message : err}`);
|
|
60686
|
+
}
|
|
60687
|
+
};
|
|
60688
|
+
const timer = setTimeout(trigger, 400);
|
|
60689
|
+
timer.unref?.();
|
|
60690
|
+
}
|
|
60427
60691
|
async function stopProcess(graceMs = 8000) {
|
|
60428
60692
|
await cleanupIfStale();
|
|
60429
60693
|
const state = readRuntimeState();
|
|
@@ -61066,6 +61330,10 @@ function normalizeOne(flag, raw2) {
|
|
|
61066
61330
|
}
|
|
61067
61331
|
function parseCommandArgs(def, args) {
|
|
61068
61332
|
const options = toParseArgsOptions(def.flags);
|
|
61333
|
+
for (const g of GLOBAL_TARGET_FLAG_NAMES) {
|
|
61334
|
+
if (!(g in options))
|
|
61335
|
+
options[g] = { type: "string" };
|
|
61336
|
+
}
|
|
61069
61337
|
let parsed;
|
|
61070
61338
|
try {
|
|
61071
61339
|
parsed = nodeParseArgs({
|
|
@@ -61077,7 +61345,7 @@ function parseCommandArgs(def, args) {
|
|
|
61077
61345
|
} catch (err) {
|
|
61078
61346
|
throw new CliError("USAGE_ERROR", err instanceof Error ? err.message : String(err));
|
|
61079
61347
|
}
|
|
61080
|
-
const known = new Set(def.flags?.map((f) => f.name) ?? []);
|
|
61348
|
+
const known = new Set([...def.flags?.map((f) => f.name) ?? [], ...GLOBAL_TARGET_FLAG_NAMES]);
|
|
61081
61349
|
for (const a of args) {
|
|
61082
61350
|
if (!a.startsWith("--"))
|
|
61083
61351
|
continue;
|
|
@@ -61145,8 +61413,10 @@ function levenshtein(a, b) {
|
|
|
61145
61413
|
}
|
|
61146
61414
|
return dp[b.length];
|
|
61147
61415
|
}
|
|
61416
|
+
var GLOBAL_TARGET_FLAG_NAMES;
|
|
61148
61417
|
var init_parse_args = __esm(() => {
|
|
61149
61418
|
init_errors();
|
|
61419
|
+
GLOBAL_TARGET_FLAG_NAMES = ["url", "host", "port"];
|
|
61150
61420
|
});
|
|
61151
61421
|
|
|
61152
61422
|
// src/cli/registry.ts
|
|
@@ -61442,9 +61712,298 @@ init_config_apply();
|
|
|
61442
61712
|
init_errors();
|
|
61443
61713
|
init_output();
|
|
61444
61714
|
init_process();
|
|
61715
|
+
import { createInterface as createInterface5 } from "readline/promises";
|
|
61716
|
+
import { parseArgs as parseArgs3 } from "util";
|
|
61717
|
+
|
|
61718
|
+
// src/cli/target.ts
|
|
61719
|
+
init_errors();
|
|
61720
|
+
init_output();
|
|
61721
|
+
init_process();
|
|
61445
61722
|
init_runtime();
|
|
61446
61723
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
61447
|
-
|
|
61724
|
+
var DEFAULT_HOST = "127.0.0.1";
|
|
61725
|
+
var DEFAULT_PORT = 4099;
|
|
61726
|
+
function normalizeHostForUrl(host) {
|
|
61727
|
+
const h = host.trim();
|
|
61728
|
+
if (h === "" || h === "0.0.0.0")
|
|
61729
|
+
return "127.0.0.1";
|
|
61730
|
+
if (h === "::" || h === "::1")
|
|
61731
|
+
return "[::1]";
|
|
61732
|
+
if (h.includes(":") && !h.startsWith("["))
|
|
61733
|
+
return `[${h}]`;
|
|
61734
|
+
return h;
|
|
61735
|
+
}
|
|
61736
|
+
function buildBaseUrl(host, port) {
|
|
61737
|
+
return `http://${normalizeHostForUrl(host)}:${port}`;
|
|
61738
|
+
}
|
|
61739
|
+
async function fingerprint(baseUrl, timeoutMs = 800) {
|
|
61740
|
+
const controller = new AbortController;
|
|
61741
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
61742
|
+
try {
|
|
61743
|
+
const res = await fetch(`${baseUrl}/api/health`, { method: "GET", signal: controller.signal });
|
|
61744
|
+
if (!res.ok)
|
|
61745
|
+
return { ok: false };
|
|
61746
|
+
const body = await res.json();
|
|
61747
|
+
if (body?.service !== "local-router")
|
|
61748
|
+
return { ok: false };
|
|
61749
|
+
return { ok: true, version: body.version, host: body.host, port: body.port };
|
|
61750
|
+
} catch {
|
|
61751
|
+
return { ok: false };
|
|
61752
|
+
} finally {
|
|
61753
|
+
clearTimeout(timer);
|
|
61754
|
+
}
|
|
61755
|
+
}
|
|
61756
|
+
function parseLsofPorts(output) {
|
|
61757
|
+
const ports = new Set;
|
|
61758
|
+
for (const line of output.split(`
|
|
61759
|
+
`)) {
|
|
61760
|
+
if (!line.includes("LISTEN"))
|
|
61761
|
+
continue;
|
|
61762
|
+
const m = line.match(/(?:\*|\[[^\]]+\]|[\d.]+):(\d{2,5})\s*\(LISTEN\)/);
|
|
61763
|
+
if (!m)
|
|
61764
|
+
continue;
|
|
61765
|
+
const port = Number.parseInt(m[1], 10);
|
|
61766
|
+
if (Number.isFinite(port))
|
|
61767
|
+
ports.add(port);
|
|
61768
|
+
}
|
|
61769
|
+
return [...ports];
|
|
61770
|
+
}
|
|
61771
|
+
function parseNetstatPorts(output) {
|
|
61772
|
+
const ports = new Set;
|
|
61773
|
+
for (const line of output.split(`
|
|
61774
|
+
`)) {
|
|
61775
|
+
if (!/LISTEN/i.test(line))
|
|
61776
|
+
continue;
|
|
61777
|
+
const m = line.match(/[.:](\d{2,5})\s+\S+\s+LISTEN/i) ?? line.match(/[.:](\d{2,5})\s+LISTEN/i);
|
|
61778
|
+
if (!m)
|
|
61779
|
+
continue;
|
|
61780
|
+
const port = Number.parseInt(m[1], 10);
|
|
61781
|
+
if (Number.isFinite(port))
|
|
61782
|
+
ports.add(port);
|
|
61783
|
+
}
|
|
61784
|
+
return [...ports];
|
|
61785
|
+
}
|
|
61786
|
+
async function runShellCommand(cmd) {
|
|
61787
|
+
try {
|
|
61788
|
+
const proc = Bun.spawn(cmd, { stdout: "pipe", stderr: "ignore", stdin: "ignore" });
|
|
61789
|
+
const out = await new Response(proc.stdout).text();
|
|
61790
|
+
await proc.exited;
|
|
61791
|
+
return out;
|
|
61792
|
+
} catch {
|
|
61793
|
+
return null;
|
|
61794
|
+
}
|
|
61795
|
+
}
|
|
61796
|
+
async function listListeningPorts() {
|
|
61797
|
+
const ports = new Set;
|
|
61798
|
+
const lsof = await runShellCommand(["lsof", "-nP", "-iTCP", "-sTCP:LISTEN"]);
|
|
61799
|
+
if (lsof) {
|
|
61800
|
+
for (const p of parseLsofPorts(lsof))
|
|
61801
|
+
ports.add(p);
|
|
61802
|
+
}
|
|
61803
|
+
if (ports.size === 0) {
|
|
61804
|
+
const netstat = await runShellCommand(["netstat", "-an"]);
|
|
61805
|
+
if (netstat) {
|
|
61806
|
+
for (const p of parseNetstatPorts(netstat))
|
|
61807
|
+
ports.add(p);
|
|
61808
|
+
}
|
|
61809
|
+
}
|
|
61810
|
+
return [...ports].sort((a, b) => a - b);
|
|
61811
|
+
}
|
|
61812
|
+
async function discoverLocalRouters(timeoutMs = 300) {
|
|
61813
|
+
const ports = await listListeningPorts();
|
|
61814
|
+
const hits = [];
|
|
61815
|
+
const concurrency = 8;
|
|
61816
|
+
let idx = 0;
|
|
61817
|
+
const worker = async () => {
|
|
61818
|
+
while (idx < ports.length) {
|
|
61819
|
+
const port = ports[idx++];
|
|
61820
|
+
const fp = await fingerprint(buildBaseUrl(DEFAULT_HOST, port), timeoutMs);
|
|
61821
|
+
if (fp.ok)
|
|
61822
|
+
hits.push({ port, version: fp.version });
|
|
61823
|
+
}
|
|
61824
|
+
};
|
|
61825
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, ports.length) }, () => worker()));
|
|
61826
|
+
return hits.sort((a, b) => a.port - b.port);
|
|
61827
|
+
}
|
|
61828
|
+
async function fromExplicit(url2, host, port, source) {
|
|
61829
|
+
if (url2) {
|
|
61830
|
+
let u;
|
|
61831
|
+
try {
|
|
61832
|
+
u = new URL(url2);
|
|
61833
|
+
} catch {
|
|
61834
|
+
throw new CliError("USAGE_ERROR", `\u65E0\u6548 URL: ${url2}`, { hint: "\u5F62\u5982 http://127.0.0.1:4099" });
|
|
61835
|
+
}
|
|
61836
|
+
const baseUrl = u.origin;
|
|
61837
|
+
const fp = await fingerprint(baseUrl);
|
|
61838
|
+
if (!fp.ok) {
|
|
61839
|
+
throw new CliError("TARGET_UNREACHABLE", `\u76EE\u6807\u65E0\u6CD5\u8FDE\u901A: ${baseUrl}`, {
|
|
61840
|
+
hint: "\u786E\u8BA4\u5730\u5740\u6B63\u786E\u4E14 local-router \u6B63\u5728\u8BE5\u5730\u5740\u76D1\u542C"
|
|
61841
|
+
});
|
|
61842
|
+
}
|
|
61843
|
+
const portNum = u.port ? Number.parseInt(u.port, 10) : u.protocol === "https:" ? 443 : 80;
|
|
61844
|
+
return { baseUrl, host: u.hostname, port: portNum, version: fp.version, source };
|
|
61845
|
+
}
|
|
61846
|
+
if (port !== undefined || host !== undefined) {
|
|
61847
|
+
const h = host ?? DEFAULT_HOST;
|
|
61848
|
+
const p = port ?? DEFAULT_PORT;
|
|
61849
|
+
const baseUrl = buildBaseUrl(h, p);
|
|
61850
|
+
const fp = await fingerprint(baseUrl);
|
|
61851
|
+
if (!fp.ok) {
|
|
61852
|
+
throw new CliError("TARGET_UNREACHABLE", `\u76EE\u6807\u65E0\u6CD5\u8FDE\u901A: ${baseUrl}`, {
|
|
61853
|
+
hint: "\u786E\u8BA4\u7AEF\u53E3\u6B63\u786E\u4E14 local-router \u6B63\u5728\u76D1\u542C"
|
|
61854
|
+
});
|
|
61855
|
+
}
|
|
61856
|
+
return { baseUrl, host: h, port: p, version: fp.version, source };
|
|
61857
|
+
}
|
|
61858
|
+
return null;
|
|
61859
|
+
}
|
|
61860
|
+
async function promptPort() {
|
|
61861
|
+
const rl = createInterface4({ input: process.stdin, output: process.stdout });
|
|
61862
|
+
try {
|
|
61863
|
+
const answer = (await rl.question(`\u672A\u53D1\u73B0 local-router\uFF0C\u8BF7\u8F93\u5165\u7AEF\u53E3 [${DEFAULT_PORT}]: `)).trim();
|
|
61864
|
+
const port = answer === "" ? DEFAULT_PORT : Number.parseInt(answer, 10);
|
|
61865
|
+
if (!Number.isFinite(port) || port <= 0 || port > 65535) {
|
|
61866
|
+
throw new CliError("USAGE_ERROR", `\u65E0\u6548\u7AEF\u53E3: ${answer}`, { hint: "\u7AEF\u53E3\u8303\u56F4 1-65535" });
|
|
61867
|
+
}
|
|
61868
|
+
return port;
|
|
61869
|
+
} finally {
|
|
61870
|
+
rl.close();
|
|
61871
|
+
}
|
|
61872
|
+
}
|
|
61873
|
+
async function promptSelectPort(found) {
|
|
61874
|
+
const rl = createInterface4({ input: process.stdin, output: process.stdout });
|
|
61875
|
+
try {
|
|
61876
|
+
console.log("\u53D1\u73B0\u591A\u4E2A local-router\uFF1A");
|
|
61877
|
+
found.forEach((f, i) => {
|
|
61878
|
+
console.log(` ${i + 1}) 127.0.0.1:${f.port}${f.version ? ` (v${f.version})` : ""}`);
|
|
61879
|
+
});
|
|
61880
|
+
const answer = (await rl.question("\u8BF7\u8F93\u5165\u5E8F\u53F7: ")).trim();
|
|
61881
|
+
const idx = Number.parseInt(answer, 10) - 1;
|
|
61882
|
+
if (!Number.isFinite(idx) || idx < 0 || idx >= found.length) {
|
|
61883
|
+
throw new CliError("USAGE_ERROR", `\u65E0\u6548\u5E8F\u53F7: ${answer}`);
|
|
61884
|
+
}
|
|
61885
|
+
return found[idx].port;
|
|
61886
|
+
} finally {
|
|
61887
|
+
rl.close();
|
|
61888
|
+
}
|
|
61889
|
+
}
|
|
61890
|
+
function discoveredTarget(found) {
|
|
61891
|
+
return {
|
|
61892
|
+
baseUrl: buildBaseUrl(DEFAULT_HOST, found.port),
|
|
61893
|
+
host: DEFAULT_HOST,
|
|
61894
|
+
port: found.port,
|
|
61895
|
+
version: found.version,
|
|
61896
|
+
source: "discovered"
|
|
61897
|
+
};
|
|
61898
|
+
}
|
|
61899
|
+
async function resolveTarget(flags) {
|
|
61900
|
+
const explicit = await fromExplicit(flags.target?.url, flags.target?.host, flags.target?.port, "flag");
|
|
61901
|
+
if (explicit)
|
|
61902
|
+
return explicit;
|
|
61903
|
+
const envUrl = process.env.LOCAL_ROUTER_URL?.trim() || undefined;
|
|
61904
|
+
const envPortRaw = process.env.LOCAL_ROUTER_PORT?.trim();
|
|
61905
|
+
let envPort;
|
|
61906
|
+
if (envPortRaw) {
|
|
61907
|
+
envPort = Number.parseInt(envPortRaw, 10);
|
|
61908
|
+
if (!Number.isFinite(envPort)) {
|
|
61909
|
+
throw new CliError("USAGE_ERROR", `\u65E0\u6548 LOCAL_ROUTER_PORT: ${envPortRaw}`);
|
|
61910
|
+
}
|
|
61911
|
+
}
|
|
61912
|
+
const env = await fromExplicit(envUrl, undefined, envPort, "env");
|
|
61913
|
+
if (env)
|
|
61914
|
+
return env;
|
|
61915
|
+
const state = readRuntimeState();
|
|
61916
|
+
if (state && isProcessAlive(state.pid)) {
|
|
61917
|
+
const fp = await fingerprint(state.baseUrl);
|
|
61918
|
+
if (fp.ok) {
|
|
61919
|
+
return {
|
|
61920
|
+
baseUrl: state.baseUrl,
|
|
61921
|
+
host: resolveLocalAccessHost(state.host),
|
|
61922
|
+
port: state.port,
|
|
61923
|
+
version: fp.version,
|
|
61924
|
+
source: "runtime"
|
|
61925
|
+
};
|
|
61926
|
+
}
|
|
61927
|
+
}
|
|
61928
|
+
const defaultUrl = buildBaseUrl(DEFAULT_HOST, DEFAULT_PORT);
|
|
61929
|
+
const defFp = await fingerprint(defaultUrl);
|
|
61930
|
+
if (defFp.ok) {
|
|
61931
|
+
return {
|
|
61932
|
+
baseUrl: defaultUrl,
|
|
61933
|
+
host: DEFAULT_HOST,
|
|
61934
|
+
port: DEFAULT_PORT,
|
|
61935
|
+
version: defFp.version,
|
|
61936
|
+
source: "default"
|
|
61937
|
+
};
|
|
61938
|
+
}
|
|
61939
|
+
const found = await discoverLocalRouters();
|
|
61940
|
+
if (found.length === 1) {
|
|
61941
|
+
return discoveredTarget(found[0]);
|
|
61942
|
+
}
|
|
61943
|
+
if (found.length > 1) {
|
|
61944
|
+
if (flags.yes)
|
|
61945
|
+
return discoveredTarget(found[0]);
|
|
61946
|
+
if (flags.noInteractive || !process.stdin.isTTY) {
|
|
61947
|
+
throw new CliError("TARGET_NOT_FOUND", "\u53D1\u73B0\u591A\u4E2A local-router\uFF0C\u8BF7\u7528 --port \u6307\u5B9A", {
|
|
61948
|
+
hint: `\u5019\u9009\u7AEF\u53E3: ${found.map((f) => f.port).join(", ")}`,
|
|
61949
|
+
details: { candidates: found }
|
|
61950
|
+
});
|
|
61951
|
+
}
|
|
61952
|
+
const picked = await promptSelectPort(found);
|
|
61953
|
+
return discoveredTarget(found.find((f) => f.port === picked) ?? { port: picked });
|
|
61954
|
+
}
|
|
61955
|
+
if (!flags.noInteractive && process.stdin.isTTY) {
|
|
61956
|
+
const port = await promptPort();
|
|
61957
|
+
const baseUrl = buildBaseUrl(DEFAULT_HOST, port);
|
|
61958
|
+
const fp = await fingerprint(baseUrl);
|
|
61959
|
+
if (!fp.ok) {
|
|
61960
|
+
throw new CliError("TARGET_UNREACHABLE", `\u7AEF\u53E3 ${port} \u4E0A\u6CA1\u6709 local-router`);
|
|
61961
|
+
}
|
|
61962
|
+
return { baseUrl, host: DEFAULT_HOST, port, version: fp.version, source: "prompt" };
|
|
61963
|
+
}
|
|
61964
|
+
throw new CliError("TARGET_NOT_FOUND", "\u627E\u4E0D\u5230\u53EF\u8FDE\u63A5\u7684 local-router", {
|
|
61965
|
+
hint: "`local-router start` \u542F\u52A8\uFF1B\u6216\u7528 --port <port> / --url <url> \u6307\u5B9A\u76EE\u6807"
|
|
61966
|
+
});
|
|
61967
|
+
}
|
|
61968
|
+
function guessTargetUrl(flags) {
|
|
61969
|
+
if (flags.target?.url) {
|
|
61970
|
+
try {
|
|
61971
|
+
const u = new URL(flags.target.url);
|
|
61972
|
+
const port = u.port ? Number.parseInt(u.port, 10) : u.protocol === "https:" ? 443 : 80;
|
|
61973
|
+
return { baseUrl: u.origin, host: u.hostname, port, running: false };
|
|
61974
|
+
} catch {}
|
|
61975
|
+
}
|
|
61976
|
+
if (flags.target?.port !== undefined || flags.target?.host !== undefined) {
|
|
61977
|
+
const host = flags.target.host ?? DEFAULT_HOST;
|
|
61978
|
+
const port = flags.target.port ?? DEFAULT_PORT;
|
|
61979
|
+
return { baseUrl: buildBaseUrl(host, port), host, port, running: false };
|
|
61980
|
+
}
|
|
61981
|
+
const state = readRuntimeState();
|
|
61982
|
+
if (state && isProcessAlive(state.pid)) {
|
|
61983
|
+
return {
|
|
61984
|
+
baseUrl: state.baseUrl,
|
|
61985
|
+
host: resolveLocalAccessHost(state.host),
|
|
61986
|
+
port: state.port,
|
|
61987
|
+
running: true
|
|
61988
|
+
};
|
|
61989
|
+
}
|
|
61990
|
+
return {
|
|
61991
|
+
baseUrl: buildBaseUrl(DEFAULT_HOST, DEFAULT_PORT),
|
|
61992
|
+
host: DEFAULT_HOST,
|
|
61993
|
+
port: DEFAULT_PORT,
|
|
61994
|
+
running: false
|
|
61995
|
+
};
|
|
61996
|
+
}
|
|
61997
|
+
function targetMetaLine(t) {
|
|
61998
|
+
return `\u2192 ${t.host}:${t.port}${t.version ? ` (v${t.version})` : ""} \xB7 ${t.source}`;
|
|
61999
|
+
}
|
|
62000
|
+
async function requireTarget(ctx) {
|
|
62001
|
+
const t = await resolveTarget(ctx.flags);
|
|
62002
|
+
emitDiagnostic(ctx, targetMetaLine(t));
|
|
62003
|
+
return t;
|
|
62004
|
+
}
|
|
62005
|
+
|
|
62006
|
+
// src/cli/config-command.ts
|
|
61448
62007
|
function readConfig(configArg) {
|
|
61449
62008
|
const path = resolveConfigPath(configArg);
|
|
61450
62009
|
return { path, config: loadConfig(path) };
|
|
@@ -61512,7 +62071,7 @@ async function selectFromList(title, items) {
|
|
|
61512
62071
|
items.forEach((item, i) => {
|
|
61513
62072
|
console.log(` ${i + 1}) ${item}`);
|
|
61514
62073
|
});
|
|
61515
|
-
const rl =
|
|
62074
|
+
const rl = createInterface5({ input: process.stdin, output: process.stdout });
|
|
61516
62075
|
try {
|
|
61517
62076
|
const answer = await rl.question("\u8BF7\u8F93\u5165\u5E8F\u53F7: ");
|
|
61518
62077
|
const idx = Number.parseInt(answer, 10) - 1;
|
|
@@ -61537,6 +62096,7 @@ Commands:
|
|
|
61537
62096
|
config provider list [--json] [--config <path>]
|
|
61538
62097
|
config provider show <name> [--show-secrets] [--config <path>]
|
|
61539
62098
|
config provider add <name> --type <type> --base <url> --api-key <key> --model <name> [--image-input] [--reasoning] [--proxy <url>] [--dry-run] [--config <path>]
|
|
62099
|
+
config provider add-lan <ip> --type <protocol> [--port 4099] [--dry-run] [--config <path>]
|
|
61540
62100
|
config provider set <name> [--base <url>] [--api-key <key>] [--proxy <url>] [--dry-run] [--config <path>]
|
|
61541
62101
|
config provider remove <name> [--force] [--dry-run] [--config <path>]
|
|
61542
62102
|
config provider model list <provider> [--config <path>]
|
|
@@ -61716,6 +62276,89 @@ async function handleProviderAdd(args, flags) {
|
|
|
61716
62276
|
}
|
|
61717
62277
|
});
|
|
61718
62278
|
}
|
|
62279
|
+
async function handleProviderAddLan(args, flags) {
|
|
62280
|
+
return runCommand({
|
|
62281
|
+
command: "config.provider.add-lan",
|
|
62282
|
+
flags,
|
|
62283
|
+
fn: async (ctx) => {
|
|
62284
|
+
const [ip, ...flagArgs] = args;
|
|
62285
|
+
ensureNoFlag("ip", ip);
|
|
62286
|
+
if (!ip) {
|
|
62287
|
+
throw new CliError("USAGE_ERROR", "ip \u5FC5\u586B", {
|
|
62288
|
+
hint: "\u7528\u6CD5: config provider add-lan <ip> --type <protocol> [--port 4099]"
|
|
62289
|
+
});
|
|
62290
|
+
}
|
|
62291
|
+
const parsed = parseArgs3({
|
|
62292
|
+
args: flagArgs,
|
|
62293
|
+
options: {
|
|
62294
|
+
type: { type: "string" },
|
|
62295
|
+
port: { type: "string", default: "4099" },
|
|
62296
|
+
"dry-run": { type: "boolean", default: false },
|
|
62297
|
+
config: { type: "string" }
|
|
62298
|
+
},
|
|
62299
|
+
allowPositionals: true,
|
|
62300
|
+
strict: false
|
|
62301
|
+
});
|
|
62302
|
+
const type = parsed.values.type;
|
|
62303
|
+
if (!type || !providerTypes().includes(type)) {
|
|
62304
|
+
throw new CliError("USAGE_ERROR", "type \u5FC5\u586B\u4E14\u5FC5\u987B\u662F openai-completions/openai-responses/anthropic-messages", { details: { acceptable: providerTypes() } });
|
|
62305
|
+
}
|
|
62306
|
+
const portStr = parsed.values.port ?? "4099";
|
|
62307
|
+
const port = Number(portStr);
|
|
62308
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
62309
|
+
throw new CliError("USAGE_ERROR", `\u7AEF\u53E3\u65E0\u6548: ${portStr}\uFF08\u5FC5\u987B\u662F 1-65535 \u7684\u6574\u6570\uFF09`);
|
|
62310
|
+
}
|
|
62311
|
+
const name21 = `${ip}-${type}`;
|
|
62312
|
+
const { path, config: config2 } = readConfig(parsed.values.config);
|
|
62313
|
+
if (config2.providers[name21]) {
|
|
62314
|
+
throw new CliError("PROVIDER_EXISTS", `provider \u5DF2\u5B58\u5728: ${name21}`, {
|
|
62315
|
+
hint: "\u4F7F\u7528 `config provider set` \u4FEE\u6539\u5B57\u6BB5"
|
|
62316
|
+
});
|
|
62317
|
+
}
|
|
62318
|
+
const url2 = `http://${ip}:${port}/api/models?protocol=${encodeURIComponent(type)}`;
|
|
62319
|
+
let models;
|
|
62320
|
+
try {
|
|
62321
|
+
const res = await fetch(url2, { signal: AbortSignal.timeout(5000) });
|
|
62322
|
+
if (!res.ok) {
|
|
62323
|
+
const body = await res.json().catch(() => ({}));
|
|
62324
|
+
throw new CliError("UPSTREAM_UNREACHABLE", `\u5BF9\u7AEF\u8FD4\u56DE\u9519\u8BEF: ${body.error ?? res.status}`, {
|
|
62325
|
+
details: { url: url2 }
|
|
62326
|
+
});
|
|
62327
|
+
}
|
|
62328
|
+
const data = await res.json();
|
|
62329
|
+
models = Array.isArray(data.models) ? data.models : [];
|
|
62330
|
+
} catch (err) {
|
|
62331
|
+
if (err instanceof CliError)
|
|
62332
|
+
throw err;
|
|
62333
|
+
throw new CliError("UPSTREAM_UNREACHABLE", `\u65E0\u6CD5\u8FDE\u63A5\u5BF9\u7AEF local-router: ${err instanceof Error ? err.message : err}`, { details: { url: url2 } });
|
|
62334
|
+
}
|
|
62335
|
+
if (models.length === 0) {
|
|
62336
|
+
throw new CliError("USAGE_ERROR", `\u5BF9\u7AEF\u5728\u534F\u8BAE "${type}" \u4E0B\u6CA1\u6709\u53EF\u7528\u7684\u6A21\u578B\u8DEF\u7531\uFF0C\u65E0\u6CD5\u521B\u5EFA provider`, { hint: "\u786E\u8BA4\u5BF9\u7AEF\u5DF2\u4E3A\u8BE5\u534F\u8BAE\u914D\u7F6E\u4E86\u5177\u4F53\u6A21\u578B\u8DEF\u7531\uFF08\u800C\u975E\u4EC5 * \u515C\u5E95\uFF09" });
|
|
62337
|
+
}
|
|
62338
|
+
const modelMap = {};
|
|
62339
|
+
for (const m of models) {
|
|
62340
|
+
modelMap[m] = { "image-input": false, reasoning: false };
|
|
62341
|
+
}
|
|
62342
|
+
config2.providers[name21] = {
|
|
62343
|
+
type,
|
|
62344
|
+
base: `http://${ip}:${port}/${type}`,
|
|
62345
|
+
apiKey: "no_key",
|
|
62346
|
+
models: modelMap
|
|
62347
|
+
};
|
|
62348
|
+
const result = applyConfigChange(path, config2, { dryRun: parsed.values["dry-run"] });
|
|
62349
|
+
emitResult(ctx, {
|
|
62350
|
+
command: "config.provider.add-lan",
|
|
62351
|
+
data: { provider: name21, models: models.length, ...result },
|
|
62352
|
+
md: {
|
|
62353
|
+
heading: `config.provider.add-lan \xB7 ${name21} \xB7 ${result.written ? "\u2713" : "dry-run"}`,
|
|
62354
|
+
data: applyResultToMd(result, `provider ${name21} (${models.length} \u4E2A\u6A21\u578B)`),
|
|
62355
|
+
hints: result.written ? ["\u70ED\u52A0\u8F7D: `local-router config apply`"] : ["\u6267\u884C\u5199\u5165: \u53BB\u6389 `--dry-run`"]
|
|
62356
|
+
},
|
|
62357
|
+
text: result.written ? `\u5DF2\u6DFB\u52A0 provider: ${name21}\uFF08\u55C5\u63A2\u5230 ${models.length} \u4E2A\u6A21\u578B\uFF09` : applyResultToText(result)
|
|
62358
|
+
});
|
|
62359
|
+
}
|
|
62360
|
+
});
|
|
62361
|
+
}
|
|
61719
62362
|
async function handleProviderSet(args, flags) {
|
|
61720
62363
|
return runCommand({
|
|
61721
62364
|
command: "config.provider.set",
|
|
@@ -62299,32 +62942,26 @@ async function handleApply(args, flags) {
|
|
|
62299
62942
|
command: "config.apply",
|
|
62300
62943
|
flags,
|
|
62301
62944
|
fn: async (ctx) => {
|
|
62302
|
-
await
|
|
62303
|
-
const
|
|
62304
|
-
if (!state) {
|
|
62305
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u670D\u52A1\u672A\u8FD0\u884C\uFF0C\u65E0\u6CD5 apply", {
|
|
62306
|
-
hint: "\u542F\u52A8: `local-router start --daemon`"
|
|
62307
|
-
});
|
|
62308
|
-
}
|
|
62309
|
-
const res = await fetch(`${state.baseUrl}/api/config/apply`, { method: "POST" });
|
|
62945
|
+
const target = await requireTarget(ctx);
|
|
62946
|
+
const res = await fetch(`${target.baseUrl}/api/config/apply`, { method: "POST" });
|
|
62310
62947
|
if (!res.ok) {
|
|
62311
62948
|
const text2 = await res.text();
|
|
62312
62949
|
throw new CliError("APPLY_FAILED", `apply \u5931\u8D25: ${res.status} ${text2}`, {
|
|
62313
|
-
details: { status: res.status, baseUrl:
|
|
62950
|
+
details: { status: res.status, baseUrl: target.baseUrl }
|
|
62314
62951
|
});
|
|
62315
62952
|
}
|
|
62316
|
-
const healthy = await checkHealth(
|
|
62953
|
+
const healthy = await checkHealth(target.baseUrl);
|
|
62317
62954
|
if (!healthy) {
|
|
62318
|
-
throw new CliError("HEALTH_FAILED", `apply \u540E\u5065\u5EB7\u68C0\u67E5\u5931\u8D25: ${
|
|
62955
|
+
throw new CliError("HEALTH_FAILED", `apply \u540E\u5065\u5EB7\u68C0\u67E5\u5931\u8D25: ${target.baseUrl}`);
|
|
62319
62956
|
}
|
|
62320
62957
|
emitResult(ctx, {
|
|
62321
62958
|
command: "config.apply",
|
|
62322
|
-
data: { ok: true, baseUrl:
|
|
62959
|
+
data: { ok: true, baseUrl: target.baseUrl },
|
|
62323
62960
|
md: {
|
|
62324
62961
|
heading: "config.apply \xB7 \u2713",
|
|
62325
|
-
data: `\u5DF2\u5E94\u7528: \`${
|
|
62962
|
+
data: `\u5DF2\u5E94\u7528: \`${target.baseUrl}\``
|
|
62326
62963
|
},
|
|
62327
|
-
text: `\u914D\u7F6E\u5DF2\u5E94\u7528: ${
|
|
62964
|
+
text: `\u914D\u7F6E\u5DF2\u5E94\u7528: ${target.baseUrl}`
|
|
62328
62965
|
});
|
|
62329
62966
|
}
|
|
62330
62967
|
});
|
|
@@ -62338,6 +62975,8 @@ async function handleProvider(args, flags) {
|
|
|
62338
62975
|
return handleProviderShow(rest, flags);
|
|
62339
62976
|
case "add":
|
|
62340
62977
|
return handleProviderAdd(rest, flags);
|
|
62978
|
+
case "add-lan":
|
|
62979
|
+
return handleProviderAddLan(rest, flags);
|
|
62341
62980
|
case "set":
|
|
62342
62981
|
return handleProviderSet(rest, flags);
|
|
62343
62982
|
case "remove":
|
|
@@ -62445,7 +63084,11 @@ async function dispatchConfig(group, rest, flags) {
|
|
|
62445
63084
|
init_registry();
|
|
62446
63085
|
var PROVIDER_TYPE_ENUM = ["openai-completions", "openai-responses", "anthropic-messages"];
|
|
62447
63086
|
var COMMON_CONFIG_FLAG = { name: "config", type: "string", description: "\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84" };
|
|
62448
|
-
var DRY_RUN_FLAG = {
|
|
63087
|
+
var DRY_RUN_FLAG = {
|
|
63088
|
+
name: "dry-run",
|
|
63089
|
+
type: "boolean",
|
|
63090
|
+
description: "\u53EA\u9884\u89C8 diff\uFF0C\u4E0D\u5199\u5165"
|
|
63091
|
+
};
|
|
62449
63092
|
function forward(prefix) {
|
|
62450
63093
|
return async (args, flags) => cmdConfig([...prefix, ...args], flags);
|
|
62451
63094
|
}
|
|
@@ -62462,10 +63105,7 @@ defineCommand({
|
|
|
62462
63105
|
defineCommand({
|
|
62463
63106
|
name: "config diff",
|
|
62464
63107
|
summary: "\u4E0E\u5907\u4EFD\u6216\u6307\u5B9A\u6587\u4EF6\u5BF9\u6BD4",
|
|
62465
|
-
flags: [
|
|
62466
|
-
{ name: "against", type: "string", description: "\u5907\u4EFD id \u6216\u8DEF\u5F84" },
|
|
62467
|
-
COMMON_CONFIG_FLAG
|
|
62468
|
-
],
|
|
63108
|
+
flags: [{ name: "against", type: "string", description: "\u5907\u4EFD id \u6216\u8DEF\u5F84" }, COMMON_CONFIG_FLAG],
|
|
62469
63109
|
supportsJson: true,
|
|
62470
63110
|
handler: forward(["diff"])
|
|
62471
63111
|
});
|
|
@@ -62579,6 +63219,26 @@ defineCommand({
|
|
|
62579
63219
|
supportsJson: true,
|
|
62580
63220
|
handler: forward(["provider", "add"])
|
|
62581
63221
|
});
|
|
63222
|
+
defineCommand({
|
|
63223
|
+
name: "config provider add-lan",
|
|
63224
|
+
summary: "\u4ECE\u5C40\u57DF\u7F51\u5185\u5176\u4ED6 local-router \u55C5\u63A2\u5E76\u65B0\u589E provider",
|
|
63225
|
+
positionals: [{ name: "ip", required: true, description: "\u5BF9\u7AEF IP" }],
|
|
63226
|
+
flags: [
|
|
63227
|
+
{
|
|
63228
|
+
name: "type",
|
|
63229
|
+
type: "enum",
|
|
63230
|
+
enum: [...PROVIDER_TYPE_ENUM],
|
|
63231
|
+
required: true,
|
|
63232
|
+
description: "\u534F\u8BAE\u7C7B\u578B"
|
|
63233
|
+
},
|
|
63234
|
+
{ name: "port", type: "string", description: "\u5BF9\u7AEF\u7AEF\u53E3\uFF08\u9ED8\u8BA4 4099\uFF09" },
|
|
63235
|
+
DRY_RUN_FLAG,
|
|
63236
|
+
COMMON_CONFIG_FLAG
|
|
63237
|
+
],
|
|
63238
|
+
mutates: true,
|
|
63239
|
+
supportsJson: true,
|
|
63240
|
+
handler: forward(["provider", "add-lan"])
|
|
63241
|
+
});
|
|
62582
63242
|
defineCommand({
|
|
62583
63243
|
name: "config provider set",
|
|
62584
63244
|
summary: "\u4FEE\u6539 provider \u5B57\u6BB5",
|
|
@@ -62664,10 +63324,7 @@ defineCommand({
|
|
|
62664
63324
|
defineCommand({
|
|
62665
63325
|
name: "config route list",
|
|
62666
63326
|
summary: "\u5217\u51FA\u6240\u6709\u8DEF\u7531",
|
|
62667
|
-
flags: [
|
|
62668
|
-
{ name: "entry", type: "string", description: "\u53EA\u770B\u67D0\u5165\u53E3" },
|
|
62669
|
-
COMMON_CONFIG_FLAG
|
|
62670
|
-
],
|
|
63327
|
+
flags: [{ name: "entry", type: "string", description: "\u53EA\u770B\u67D0\u5165\u53E3" }, COMMON_CONFIG_FLAG],
|
|
62671
63328
|
supportsJson: true,
|
|
62672
63329
|
handler: forward(["route", "list"])
|
|
62673
63330
|
});
|
|
@@ -62877,10 +63534,8 @@ defineSchemaCommand({
|
|
|
62877
63534
|
// src/cli/handlers/chat.ts
|
|
62878
63535
|
init_errors();
|
|
62879
63536
|
init_output();
|
|
62880
|
-
init_process();
|
|
62881
63537
|
init_registry();
|
|
62882
|
-
|
|
62883
|
-
import { createInterface as createInterface5 } from "readline/promises";
|
|
63538
|
+
import { createInterface as createInterface6 } from "readline/promises";
|
|
62884
63539
|
defineSchemaCommand({
|
|
62885
63540
|
name: "chat",
|
|
62886
63541
|
summary: "\u4EA4\u4E92\u5F0F REPL\uFF08\u9ED8\u8BA4\u8D70 openai-completions\uFF0C\u6D41\u5F0F\uFF09",
|
|
@@ -62899,25 +63554,19 @@ defineSchemaCommand({
|
|
|
62899
63554
|
{ name: "no-stream", type: "boolean", description: "\u7981\u7528\u6D41\u5F0F\uFF08\u4E00\u6B21\u8FD4\u56DE\uFF09" }
|
|
62900
63555
|
],
|
|
62901
63556
|
fn: async ({ values, ctx }) => {
|
|
62902
|
-
await cleanupIfStale();
|
|
62903
|
-
const state = readRuntimeState();
|
|
62904
|
-
if (!state)
|
|
62905
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u670D\u52A1\u672A\u8FD0\u884C");
|
|
62906
|
-
if (!await checkHealth(state.baseUrl)) {
|
|
62907
|
-
throw new CliError("HEALTH_FAILED", `\u670D\u52A1\u5065\u5EB7\u68C0\u67E5\u5931\u8D25: ${state.baseUrl}`);
|
|
62908
|
-
}
|
|
62909
63557
|
if (!process.stdin.isTTY) {
|
|
62910
63558
|
throw new CliError("INTERACTIVE_REQUIRED", "chat \u9700\u8981 TTY", {
|
|
62911
63559
|
hint: "\u7BA1\u9053\u573A\u666F\u8BF7\u7528 `local-router try`"
|
|
62912
63560
|
});
|
|
62913
63561
|
}
|
|
62914
|
-
const
|
|
63562
|
+
const target = await requireTarget(ctx);
|
|
63563
|
+
const baseUrl = target.baseUrl.replace(/\/+$/, "");
|
|
62915
63564
|
const url2 = `${baseUrl}/openai-completions/v1/chat/completions`;
|
|
62916
63565
|
const messages = [];
|
|
62917
63566
|
if (values.system)
|
|
62918
63567
|
messages.push({ role: "system", content: values.system });
|
|
62919
63568
|
emitDiagnostic(ctx, `chat \u2192 ${values.entry}/${values.model} \xB7 \u8F93\u5165 \`/exit\` \u6216 Ctrl+C \u9000\u51FA \xB7 /reset \u6E05\u7A7A\u4E0A\u4E0B\u6587`);
|
|
62920
|
-
const rl =
|
|
63569
|
+
const rl = createInterface6({ input: process.stdin, output: process.stdout });
|
|
62921
63570
|
try {
|
|
62922
63571
|
while (true) {
|
|
62923
63572
|
const input = (await rl.question("\u203A ")).trim();
|
|
@@ -63367,7 +64016,6 @@ init_config_apply();
|
|
|
63367
64016
|
init_errors();
|
|
63368
64017
|
init_output();
|
|
63369
64018
|
init_registry();
|
|
63370
|
-
init_runtime();
|
|
63371
64019
|
import { spawnSync } from "child_process";
|
|
63372
64020
|
import { copyFileSync, existsSync as existsSync13, mkdtempSync, rmSync as rmSync4 } from "fs";
|
|
63373
64021
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -63447,7 +64095,10 @@ function platformOpen(target) {
|
|
|
63447
64095
|
args = [target];
|
|
63448
64096
|
}
|
|
63449
64097
|
const r = spawnSync(cmd, args, { stdio: "ignore" });
|
|
63450
|
-
return {
|
|
64098
|
+
return {
|
|
64099
|
+
ok: !r.error && (typeof r.status !== "number" || r.status === 0),
|
|
64100
|
+
cmd: `${cmd} ${args.join(" ")}`
|
|
64101
|
+
};
|
|
63451
64102
|
}
|
|
63452
64103
|
defineSchemaCommand({
|
|
63453
64104
|
name: "open",
|
|
@@ -63461,7 +64112,7 @@ defineSchemaCommand({
|
|
|
63461
64112
|
}
|
|
63462
64113
|
],
|
|
63463
64114
|
flags: [{ name: "config", type: "string", description: "\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84" }],
|
|
63464
|
-
fn: ({ positionals, values, ctx }) => {
|
|
64115
|
+
fn: async ({ positionals, values, ctx }) => {
|
|
63465
64116
|
const target = positionals[0];
|
|
63466
64117
|
if (!target) {
|
|
63467
64118
|
throw new CliError("USAGE_ERROR", "\u7528\u6CD5: open <admin|docs|logs-dir|config>");
|
|
@@ -63469,13 +64120,8 @@ defineSchemaCommand({
|
|
|
63469
64120
|
let url2;
|
|
63470
64121
|
let label;
|
|
63471
64122
|
if (target === "admin") {
|
|
63472
|
-
const
|
|
63473
|
-
|
|
63474
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u670D\u52A1\u672A\u8FD0\u884C\uFF0C\u65E0\u6CD5\u6253\u5F00 admin", {
|
|
63475
|
-
hint: "`local-router start --daemon`"
|
|
63476
|
-
});
|
|
63477
|
-
}
|
|
63478
|
-
url2 = `${state.baseUrl}/admin/`;
|
|
64123
|
+
const resolved = await resolveTarget(ctx.flags);
|
|
64124
|
+
url2 = `${resolved.baseUrl}/admin/`;
|
|
63479
64125
|
label = "Web Admin";
|
|
63480
64126
|
} else if (target === "docs") {
|
|
63481
64127
|
url2 = "https://github.com/lakphy/local-router#readme";
|
|
@@ -63527,8 +64173,9 @@ defineSchemaCommand({
|
|
|
63527
64173
|
{ name: "config", type: "string", description: "\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84" }
|
|
63528
64174
|
],
|
|
63529
64175
|
fn: ({ values, ctx }) => {
|
|
63530
|
-
const
|
|
63531
|
-
const baseUrl =
|
|
64176
|
+
const guess = guessTargetUrl(ctx.flags);
|
|
64177
|
+
const baseUrl = guess.baseUrl;
|
|
64178
|
+
const running = guess.running;
|
|
63532
64179
|
const entries = [
|
|
63533
64180
|
{
|
|
63534
64181
|
key: "OPENAI_BASE_URL",
|
|
@@ -63572,12 +64219,12 @@ defineSchemaCommand({
|
|
|
63572
64219
|
data: {
|
|
63573
64220
|
baseUrl,
|
|
63574
64221
|
shell: values.shell,
|
|
63575
|
-
running
|
|
64222
|
+
running,
|
|
63576
64223
|
entries,
|
|
63577
64224
|
script
|
|
63578
64225
|
},
|
|
63579
64226
|
md: {
|
|
63580
|
-
heading: `env \xB7 ${
|
|
64227
|
+
heading: `env \xB7 ${running ? "\u2713 \u8FD0\u884C\u4E2D" : "\u2717 \u672A\u8FD0\u884C\uFF08\u4F7F\u7528\u9ED8\u8BA4 4099\uFF09"}`,
|
|
63581
64228
|
meta: [`baseUrl: \`${baseUrl}\``],
|
|
63582
64229
|
data: [
|
|
63583
64230
|
renderKv(entries.map((e) => ({ key: e.key, value: e.value }))),
|
|
@@ -63877,14 +64524,8 @@ defineSchemaCommand({
|
|
|
63877
64524
|
],
|
|
63878
64525
|
fn: async ({ values, ctx }) => {
|
|
63879
64526
|
const { entry, model, prompt, stream: streamMode, timeout: timeoutSec } = values;
|
|
63880
|
-
await
|
|
63881
|
-
const
|
|
63882
|
-
if (!state) {
|
|
63883
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u670D\u52A1\u672A\u8FD0\u884C", {
|
|
63884
|
-
hint: "`local-router start --daemon`"
|
|
63885
|
-
});
|
|
63886
|
-
}
|
|
63887
|
-
const payload = buildTryPayload(entry, model, prompt, state.baseUrl);
|
|
64527
|
+
const target = await requireTarget(ctx);
|
|
64528
|
+
const payload = buildTryPayload(entry, model, prompt, target.baseUrl);
|
|
63888
64529
|
const controller = new AbortController;
|
|
63889
64530
|
const timer = setTimeout(() => controller.abort(), timeoutSec * 1000);
|
|
63890
64531
|
const startedAt = Date.now();
|
|
@@ -64022,35 +64663,6 @@ defineSchemaCommand({
|
|
|
64022
64663
|
import { readFileSync as readFileSync11 } from "fs";
|
|
64023
64664
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
64024
64665
|
|
|
64025
|
-
// src/cli/asset-paths.ts
|
|
64026
|
-
async function findExisting(...candidates) {
|
|
64027
|
-
for (const url2 of candidates) {
|
|
64028
|
-
try {
|
|
64029
|
-
const exists = await Bun.file(url2).exists();
|
|
64030
|
-
if (exists)
|
|
64031
|
-
return url2;
|
|
64032
|
-
} catch {}
|
|
64033
|
-
}
|
|
64034
|
-
return null;
|
|
64035
|
-
}
|
|
64036
|
-
async function readPackageJson() {
|
|
64037
|
-
const url2 = await findExisting(new URL("../../package.json", import.meta.url), new URL("../package.json", import.meta.url), new URL("../../../package.json", import.meta.url));
|
|
64038
|
-
if (!url2)
|
|
64039
|
-
return null;
|
|
64040
|
-
try {
|
|
64041
|
-
return await Bun.file(url2).json();
|
|
64042
|
-
} catch {
|
|
64043
|
-
return null;
|
|
64044
|
-
}
|
|
64045
|
-
}
|
|
64046
|
-
async function findConfigSchemaUrl() {
|
|
64047
|
-
return findExisting(new URL("../../config.schema.json", import.meta.url), new URL("../config.schema.json", import.meta.url), new URL("../../../config.schema.json", import.meta.url));
|
|
64048
|
-
}
|
|
64049
|
-
async function readVersionString() {
|
|
64050
|
-
const pkg = await readPackageJson();
|
|
64051
|
-
return typeof pkg?.version === "string" ? pkg.version : "unknown";
|
|
64052
|
-
}
|
|
64053
|
-
|
|
64054
64666
|
// src/cli/error-docs.ts
|
|
64055
64667
|
var ERROR_DOCS = {
|
|
64056
64668
|
USAGE_ERROR: {
|
|
@@ -64143,6 +64755,16 @@ var ERROR_DOCS = {
|
|
|
64143
64755
|
cause: "\u547D\u4EE4\u7F3A\u5C11\u5FC5\u586B flag\uFF0C\u672C\u5E94\u5F39\u51FA\u9009\u62E9\u5668\u4F46\u5F53\u524D stdin \u4E0D\u662F TTY \u6216\u663E\u5F0F `--no-interactive`\u3002",
|
|
64144
64756
|
fix: "\u5728\u9519\u8BEF\u7684 `details` \u91CC\u67E5\u770B\u5019\u9009\u9879\uFF0C\u663E\u5F0F\u8865 `--provider/--model` \u7B49\u3002"
|
|
64145
64757
|
},
|
|
64758
|
+
TARGET_NOT_FOUND: {
|
|
64759
|
+
summary: "\u627E\u4E0D\u5230\u53EF\u8FDE\u63A5\u7684 local-router",
|
|
64760
|
+
cause: "\u9ED8\u8BA4\u7AEF\u53E3 4099 \u4E0A\u6CA1\u6709 local-router\uFF0COS \u8FDB\u7A0B\u679A\u4E3E\u4E5F\u672A\u53D1\u73B0\u5176\u5B83\u5B9E\u4F8B\u3002",
|
|
64761
|
+
fix: "`local-router start` \u542F\u52A8\uFF1B\u6216\u7528 `--port <port>` / `--url <url>` \u6307\u5B9A\u76EE\u6807\u3002"
|
|
64762
|
+
},
|
|
64763
|
+
TARGET_UNREACHABLE: {
|
|
64764
|
+
summary: "\u6307\u5B9A\u7684\u76EE\u6807\u65E0\u6CD5\u8FDE\u901A",
|
|
64765
|
+
cause: "`--port` / `--url`\uFF08\u6216\u73AF\u5883\u53D8\u91CF\uFF09\u6307\u5411\u7684\u5730\u5740\u6CA1\u6709\u54CD\u5E94 `/api/health` \u6216\u4E0D\u662F local-router\u3002",
|
|
64766
|
+
fix: "\u786E\u8BA4\u76EE\u6807\u7AEF\u53E3/\u5730\u5740\u6B63\u786E\u4E14 local-router \u6B63\u5728\u8BE5\u5730\u5740\u76D1\u542C\u3002"
|
|
64767
|
+
},
|
|
64146
64768
|
UNKNOWN_ERROR: {
|
|
64147
64769
|
summary: "\u672A\u77E5\u9519\u8BEF",
|
|
64148
64770
|
cause: "\u6CA1\u6709\u5339\u914D\u5230\u4EFB\u4F55 CliError \u7C7B\u578B\u3002",
|
|
@@ -64589,13 +65211,13 @@ defineSchemaCommand({
|
|
|
64589
65211
|
|
|
64590
65212
|
// src/cli/handlers/lifecycle.ts
|
|
64591
65213
|
init_config();
|
|
64592
|
-
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
64593
|
-
import { setTimeout as sleep3 } from "timers/promises";
|
|
64594
65214
|
init_errors();
|
|
64595
65215
|
init_output();
|
|
64596
65216
|
init_process();
|
|
64597
65217
|
init_registry();
|
|
64598
65218
|
init_runtime();
|
|
65219
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
65220
|
+
import { setTimeout as sleep3 } from "timers/promises";
|
|
64599
65221
|
|
|
64600
65222
|
// src/cli/wait.ts
|
|
64601
65223
|
init_errors();
|
|
@@ -64814,34 +65436,34 @@ defineSchemaCommand({
|
|
|
64814
65436
|
fn: async ({ values, ctx }) => {
|
|
64815
65437
|
const retry = Math.max(1, values.retry || 1);
|
|
64816
65438
|
const interval = Math.max(0, values["retry-interval"] || 1);
|
|
64817
|
-
|
|
64818
|
-
const
|
|
64819
|
-
if (!state) {
|
|
64820
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u670D\u52A1\u672A\u8FD0\u884C", {
|
|
64821
|
-
hint: "\u542F\u52A8: `local-router start --daemon`"
|
|
64822
|
-
});
|
|
64823
|
-
}
|
|
65439
|
+
const target = guessTargetUrl(ctx.flags);
|
|
65440
|
+
const baseUrl = target.baseUrl;
|
|
64824
65441
|
let ok = false;
|
|
64825
65442
|
for (let i = 0;i < retry; i++) {
|
|
64826
|
-
ok = await checkHealth(
|
|
65443
|
+
ok = await checkHealth(baseUrl);
|
|
64827
65444
|
if (ok)
|
|
64828
65445
|
break;
|
|
64829
65446
|
if (i < retry - 1)
|
|
64830
65447
|
await sleep3(interval * 1000);
|
|
64831
65448
|
}
|
|
64832
65449
|
if (!ok) {
|
|
64833
|
-
|
|
64834
|
-
|
|
65450
|
+
if (!target.running && !ctx.flags.target) {
|
|
65451
|
+
throw new CliError("SERVICE_NOT_RUNNING", "\u670D\u52A1\u672A\u8FD0\u884C", {
|
|
65452
|
+
hint: "\u542F\u52A8: `local-router start --daemon`"
|
|
65453
|
+
});
|
|
65454
|
+
}
|
|
65455
|
+
throw new CliError("HEALTH_FAILED", `\u5065\u5EB7\u68C0\u67E5\u5931\u8D25: ${baseUrl}/api/health`, {
|
|
65456
|
+
details: { baseUrl, retries: retry }
|
|
64835
65457
|
});
|
|
64836
65458
|
}
|
|
64837
65459
|
emitResult(ctx, {
|
|
64838
65460
|
command: "health",
|
|
64839
|
-
data: { ok: true, baseUrl
|
|
65461
|
+
data: { ok: true, baseUrl },
|
|
64840
65462
|
md: {
|
|
64841
65463
|
heading: "health \xB7 \u2713 ok",
|
|
64842
|
-
meta: [`\u5730\u5740 ${
|
|
65464
|
+
meta: [`\u5730\u5740 ${baseUrl}`]
|
|
64843
65465
|
},
|
|
64844
|
-
text: `\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7: ${
|
|
65466
|
+
text: `\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7: ${baseUrl}`
|
|
64845
65467
|
});
|
|
64846
65468
|
}
|
|
64847
65469
|
});
|
|
@@ -64955,22 +65577,10 @@ defineSchemaCommand({
|
|
|
64955
65577
|
// src/cli/handlers/logs.ts
|
|
64956
65578
|
init_errors();
|
|
64957
65579
|
init_output();
|
|
64958
|
-
init_process();
|
|
64959
65580
|
init_registry();
|
|
64960
|
-
|
|
64961
|
-
|
|
64962
|
-
|
|
64963
|
-
const state = readRuntimeState();
|
|
64964
|
-
if (!state) {
|
|
64965
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u65E5\u5FD7\u67E5\u8BE2\u9700\u8981\u670D\u52A1\u8FD0\u884C", {
|
|
64966
|
-
hint: "`local-router start --daemon`"
|
|
64967
|
-
});
|
|
64968
|
-
}
|
|
64969
|
-
const ok = await checkHealth(state.baseUrl);
|
|
64970
|
-
if (!ok) {
|
|
64971
|
-
throw new CliError("HEALTH_FAILED", `\u670D\u52A1\u5065\u5EB7\u68C0\u67E5\u5931\u8D25: ${state.baseUrl}`);
|
|
64972
|
-
}
|
|
64973
|
-
return { baseUrl: state.baseUrl };
|
|
65581
|
+
async function requireRunning(ctx) {
|
|
65582
|
+
const t = await requireTarget(ctx);
|
|
65583
|
+
return { baseUrl: t.baseUrl };
|
|
64974
65584
|
}
|
|
64975
65585
|
async function fetchJson(url2, init) {
|
|
64976
65586
|
const res = await fetch(url2, init);
|
|
@@ -65018,7 +65628,7 @@ defineSchemaCommand({
|
|
|
65018
65628
|
{ name: "cursor", type: "string", description: "\u5206\u9875\u6E38\u6807" }
|
|
65019
65629
|
],
|
|
65020
65630
|
fn: async ({ values, ctx }) => {
|
|
65021
|
-
const { baseUrl } = await requireRunning();
|
|
65631
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65022
65632
|
const params = new URLSearchParams;
|
|
65023
65633
|
const set2 = (k, v) => {
|
|
65024
65634
|
if (v !== undefined && v !== null && v !== "" && v !== false) {
|
|
@@ -65083,7 +65693,7 @@ defineSchemaCommand({
|
|
|
65083
65693
|
const id = positionals[0];
|
|
65084
65694
|
if (!id)
|
|
65085
65695
|
throw new CliError("USAGE_ERROR", "\u7528\u6CD5: logs event <id>");
|
|
65086
|
-
const { baseUrl } = await requireRunning();
|
|
65696
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65087
65697
|
const params = new URLSearchParams;
|
|
65088
65698
|
if (values["include-stream"])
|
|
65089
65699
|
params.set("includeStream", "true");
|
|
@@ -65129,7 +65739,7 @@ defineSchemaCommand({
|
|
|
65129
65739
|
}
|
|
65130
65740
|
],
|
|
65131
65741
|
fn: async ({ values, ctx }) => {
|
|
65132
|
-
const { baseUrl } = await requireRunning();
|
|
65742
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65133
65743
|
const w = values.window ?? "24h";
|
|
65134
65744
|
const { status, json: json3 } = await fetchJson(`${baseUrl}/api/logs/events?window=${w}&hasError=true&limit=1&sort=time_desc`);
|
|
65135
65745
|
if (status !== 200)
|
|
@@ -65181,7 +65791,7 @@ defineSchemaCommand({
|
|
|
65181
65791
|
}
|
|
65182
65792
|
],
|
|
65183
65793
|
fn: async ({ values, ctx }) => {
|
|
65184
|
-
const { baseUrl } = await requireRunning();
|
|
65794
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65185
65795
|
const w = values.window ?? "24h";
|
|
65186
65796
|
const { status, json: json3 } = await fetchJson(`${baseUrl}/api/metrics/logs?window=${w}`);
|
|
65187
65797
|
if (status !== 200) {
|
|
@@ -65226,7 +65836,7 @@ defineSchemaCommand({
|
|
|
65226
65836
|
supportsJson: true,
|
|
65227
65837
|
requiresRunning: true,
|
|
65228
65838
|
fn: async ({ ctx }) => {
|
|
65229
|
-
const { baseUrl } = await requireRunning();
|
|
65839
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65230
65840
|
const { status, json: json3 } = await fetchJson(`${baseUrl}/api/logs/storage`);
|
|
65231
65841
|
if (status !== 200) {
|
|
65232
65842
|
throw new CliError("UNKNOWN_ERROR", `\u83B7\u53D6 storage \u5931\u8D25: ${status}`, { details: json3 });
|
|
@@ -65255,7 +65865,7 @@ defineSchemaCommand({
|
|
|
65255
65865
|
{ name: "limit", type: "number", default: 50, description: "\u6700\u5927 session \u6570" }
|
|
65256
65866
|
],
|
|
65257
65867
|
fn: async ({ values, ctx }) => {
|
|
65258
|
-
const { baseUrl } = await requireRunning();
|
|
65868
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65259
65869
|
const params = new URLSearchParams;
|
|
65260
65870
|
params.set("window", values.window ?? "24h");
|
|
65261
65871
|
if (values.user)
|
|
@@ -65269,7 +65879,10 @@ defineSchemaCommand({
|
|
|
65269
65879
|
emitResult(ctx, {
|
|
65270
65880
|
command: "logs.sessions",
|
|
65271
65881
|
data: json3,
|
|
65272
|
-
md: {
|
|
65882
|
+
md: {
|
|
65883
|
+
heading: "logs.sessions",
|
|
65884
|
+
data: renderCodeBlock(JSON.stringify(json3, null, 2), "json")
|
|
65885
|
+
}
|
|
65273
65886
|
});
|
|
65274
65887
|
}
|
|
65275
65888
|
});
|
|
@@ -65279,7 +65892,7 @@ defineSchemaCommand({
|
|
|
65279
65892
|
supportsJson: true,
|
|
65280
65893
|
requiresRunning: true,
|
|
65281
65894
|
fn: async ({ ctx }) => {
|
|
65282
|
-
const { baseUrl } = await requireRunning();
|
|
65895
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65283
65896
|
const stream = startStream(ctx, "logs.tail");
|
|
65284
65897
|
const res = await fetch(`${baseUrl}/api/logs/tail`, {
|
|
65285
65898
|
headers: { accept: "text/event-stream" }
|
|
@@ -65341,8 +65954,8 @@ defineSchemaCommand({
|
|
|
65341
65954
|
{ name: "from", type: "string", description: "ISO \u8D77\u70B9" },
|
|
65342
65955
|
{ name: "to", type: "string", description: "ISO \u7EC8\u70B9" }
|
|
65343
65956
|
],
|
|
65344
|
-
fn: async ({ values }) => {
|
|
65345
|
-
const { baseUrl } = await requireRunning();
|
|
65957
|
+
fn: async ({ values, ctx }) => {
|
|
65958
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65346
65959
|
const params = new URLSearchParams;
|
|
65347
65960
|
params.set("format", values.format ?? "jsonl");
|
|
65348
65961
|
params.set("window", values.window ?? "24h");
|
|
@@ -65373,7 +65986,7 @@ defineSchemaCommand({
|
|
|
65373
65986
|
const id = positionals[0];
|
|
65374
65987
|
if (!id)
|
|
65375
65988
|
throw new CliError("USAGE_ERROR", "\u7528\u6CD5: logs replay <event-id>");
|
|
65376
|
-
const { baseUrl } = await requireRunning();
|
|
65989
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65377
65990
|
const detailRes = await fetchJson(`${baseUrl}/api/logs/events/${encodeURIComponent(id)}`);
|
|
65378
65991
|
if (detailRes.status === 404)
|
|
65379
65992
|
throw new CliError("ROUTE_NOT_FOUND", `\u4E8B\u4EF6\u4E0D\u5B58\u5728: ${id}`);
|
|
@@ -65426,7 +66039,6 @@ defineSchemaCommand({
|
|
|
65426
66039
|
init_config();
|
|
65427
66040
|
init_errors();
|
|
65428
66041
|
init_output();
|
|
65429
|
-
init_process();
|
|
65430
66042
|
init_registry();
|
|
65431
66043
|
init_runtime();
|
|
65432
66044
|
import { existsSync as existsSync16, rmSync as rmSync5 } from "fs";
|
|
@@ -65441,15 +66053,9 @@ async function fetchJson2(url2) {
|
|
|
65441
66053
|
} catch {}
|
|
65442
66054
|
return { status: res.status, json: json3 };
|
|
65443
66055
|
}
|
|
65444
|
-
async function requireBaseUrl() {
|
|
65445
|
-
await
|
|
65446
|
-
|
|
65447
|
-
if (!state)
|
|
65448
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u670D\u52A1\u672A\u8FD0\u884C");
|
|
65449
|
-
if (!await checkHealth(state.baseUrl)) {
|
|
65450
|
-
throw new CliError("HEALTH_FAILED", `\u670D\u52A1\u5065\u5EB7\u68C0\u67E5\u5931\u8D25: ${state.baseUrl}`);
|
|
65451
|
-
}
|
|
65452
|
-
return state.baseUrl;
|
|
66056
|
+
async function requireBaseUrl(ctx) {
|
|
66057
|
+
const t = await requireTarget(ctx);
|
|
66058
|
+
return t.baseUrl;
|
|
65453
66059
|
}
|
|
65454
66060
|
defineSchemaCommand({
|
|
65455
66061
|
name: "logs tokens",
|
|
@@ -65466,7 +66072,7 @@ defineSchemaCommand({
|
|
|
65466
66072
|
}
|
|
65467
66073
|
],
|
|
65468
66074
|
fn: async ({ values, ctx }) => {
|
|
65469
|
-
const baseUrl = await requireBaseUrl();
|
|
66075
|
+
const baseUrl = await requireBaseUrl(ctx);
|
|
65470
66076
|
const { status, json: json3 } = await fetchJson2(`${baseUrl}/api/logs/events?window=${values.window}&limit=1`);
|
|
65471
66077
|
if (status !== 200) {
|
|
65472
66078
|
throw new CliError("UNKNOWN_ERROR", `\u67E5\u8BE2\u5931\u8D25: ${status}`, { details: json3 });
|
|
@@ -65531,7 +66137,7 @@ defineSchemaCommand({
|
|
|
65531
66137
|
}
|
|
65532
66138
|
],
|
|
65533
66139
|
fn: async ({ values, ctx }) => {
|
|
65534
|
-
const baseUrl = await requireBaseUrl();
|
|
66140
|
+
const baseUrl = await requireBaseUrl(ctx);
|
|
65535
66141
|
let rateTable = {};
|
|
65536
66142
|
if (values["rate-table"]) {
|
|
65537
66143
|
try {
|
|
@@ -66021,6 +66627,59 @@ defineSchemaCommand({
|
|
|
66021
66627
|
}
|
|
66022
66628
|
});
|
|
66023
66629
|
|
|
66630
|
+
// src/cli/handlers/target.ts
|
|
66631
|
+
init_output();
|
|
66632
|
+
init_registry();
|
|
66633
|
+
var SOURCE_LABEL = {
|
|
66634
|
+
flag: "\u547D\u4EE4\u884C --port/--url/--host",
|
|
66635
|
+
env: "\u73AF\u5883\u53D8\u91CF LOCAL_ROUTER_URL/PORT",
|
|
66636
|
+
runtime: "\u672C\u673A daemon (status.json)",
|
|
66637
|
+
default: "\u9ED8\u8BA4\u7AEF\u53E3 4099",
|
|
66638
|
+
discovered: "OS \u8FDB\u7A0B\u679A\u4E3E\u53D1\u73B0",
|
|
66639
|
+
prompt: "\u4EA4\u4E92\u8F93\u5165"
|
|
66640
|
+
};
|
|
66641
|
+
defineSchemaCommand({
|
|
66642
|
+
name: "target",
|
|
66643
|
+
summary: "\u663E\u793A\u5F53\u524D\u89E3\u6790\u5230\u7684 local-router \u76EE\u6807\uFF08\u7AEF\u53E3/\u6765\u6E90/\u7248\u672C\uFF09",
|
|
66644
|
+
supportsJson: true,
|
|
66645
|
+
requiresRunning: false,
|
|
66646
|
+
flags: [],
|
|
66647
|
+
fn: async ({ ctx }) => {
|
|
66648
|
+
const t = await resolveTarget(ctx.flags);
|
|
66649
|
+
const candidates = ctx.flags.verbose ? await discoverLocalRouters() : [];
|
|
66650
|
+
emitResult(ctx, {
|
|
66651
|
+
command: "target",
|
|
66652
|
+
data: {
|
|
66653
|
+
baseUrl: t.baseUrl,
|
|
66654
|
+
host: t.host,
|
|
66655
|
+
port: t.port,
|
|
66656
|
+
version: t.version ?? null,
|
|
66657
|
+
source: t.source,
|
|
66658
|
+
candidates: ctx.flags.verbose ? candidates.map((c) => ({ port: c.port, version: c.version ?? null })) : undefined
|
|
66659
|
+
},
|
|
66660
|
+
md: {
|
|
66661
|
+
heading: `target \xB7 ${t.host}:${t.port}${t.version ? ` (v${t.version})` : ""}`,
|
|
66662
|
+
meta: [`\u6765\u6E90: ${SOURCE_LABEL[t.source] ?? t.source}`],
|
|
66663
|
+
data: [
|
|
66664
|
+
renderKv([
|
|
66665
|
+
{ key: "baseUrl", value: t.baseUrl },
|
|
66666
|
+
{ key: "host", value: t.host },
|
|
66667
|
+
{ key: "port", value: t.port },
|
|
66668
|
+
{ key: "version", value: t.version ?? "unknown" },
|
|
66669
|
+
{ key: "source", value: t.source }
|
|
66670
|
+
]),
|
|
66671
|
+
ctx.flags.verbose ? `**\u53D1\u73B0\u7684\u5B9E\u4F8B**
|
|
66672
|
+
|
|
66673
|
+
${candidates.length > 0 ? renderTable(["port", "version"], candidates.map((c) => [String(c.port), c.version ?? "?"])) : "\uFF08\u672A\u53D1\u73B0\u5176\u5B83\u5B9E\u4F8B\uFF09"}` : ""
|
|
66674
|
+
].filter(Boolean).join(`
|
|
66675
|
+
|
|
66676
|
+
`)
|
|
66677
|
+
},
|
|
66678
|
+
text: t.baseUrl
|
|
66679
|
+
});
|
|
66680
|
+
}
|
|
66681
|
+
});
|
|
66682
|
+
|
|
66024
66683
|
// src/cli.ts
|
|
66025
66684
|
init_output();
|
|
66026
66685
|
init_parse_args();
|