@lakphy/local-router 0.5.7 → 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/dist/cli.js +570 -143
- package/dist/entry.js +76 -6
- package/dist/web/assets/index-B-4HJAZa.js +192 -0
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/assets/index-D1WTE7QU.js +0 -192
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
|
};
|
|
@@ -52575,6 +52606,35 @@ var init_bun = __esm(() => {
|
|
|
52575
52606
|
init_server();
|
|
52576
52607
|
});
|
|
52577
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
|
+
|
|
52578
52638
|
// src/cli/runtime.ts
|
|
52579
52639
|
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
52580
52640
|
import { homedir as homedir2 } from "os";
|
|
@@ -59325,6 +59385,13 @@ function createServerAddressInfo(listenHost, port) {
|
|
|
59325
59385
|
// src/index.ts
|
|
59326
59386
|
import { readFileSync as readFileSync8 } from "fs";
|
|
59327
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
|
+
}
|
|
59328
59395
|
function printIntegrationGuide(config2, listen = {}) {
|
|
59329
59396
|
const host = listen.host ?? process.env.HOST ?? "0.0.0.0";
|
|
59330
59397
|
const parsedPort = listen.port ?? Number.parseInt(process.env.PORT ?? "4099", 10);
|
|
@@ -59404,7 +59471,7 @@ function createChatProxyModel(providerName, providerConfig, model) {
|
|
|
59404
59471
|
throw new Error(`\u6682\u4E0D\u652F\u6301\u7684 provider \u7C7B\u578B: ${providerConfig.type}`);
|
|
59405
59472
|
}
|
|
59406
59473
|
}
|
|
59407
|
-
function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
59474
|
+
function createAdminApiRoutes(store, pluginManager, registerCleanup, serverControl, restartLogStorageTask, serviceVersion) {
|
|
59408
59475
|
const api2 = new Hono2;
|
|
59409
59476
|
const cryptoSessions = new Map;
|
|
59410
59477
|
const CRYPTO_SESSION_TTL_MS = 2 * 60 * 1000;
|
|
@@ -59444,7 +59511,16 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
59444
59511
|
}
|
|
59445
59512
|
cryptoSessions.clear();
|
|
59446
59513
|
});
|
|
59447
|
-
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
|
+
});
|
|
59448
59524
|
api2.post("/crypto/handshake", async (c) => {
|
|
59449
59525
|
pruneExpiredCryptoSessions();
|
|
59450
59526
|
if (cryptoSessions.size >= CRYPTO_SESSION_MAX) {
|
|
@@ -59517,18 +59593,30 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
59517
59593
|
});
|
|
59518
59594
|
api2.post("/config/apply", async (_c) => {
|
|
59519
59595
|
try {
|
|
59596
|
+
const fallbackBefore = readRestartCriticalServerFields(store.get());
|
|
59597
|
+
const before = serverControl?.current ?? fallbackBefore;
|
|
59520
59598
|
const config2 = store.reload();
|
|
59599
|
+
const after = readRestartCriticalServerFields(config2);
|
|
59521
59600
|
if (config2.log) {
|
|
59522
59601
|
const logBaseDir = resolveLogBaseDir(config2.log);
|
|
59523
59602
|
initLogger(logBaseDir, config2.log);
|
|
59603
|
+
} else {
|
|
59604
|
+
resetLogger();
|
|
59524
59605
|
}
|
|
59606
|
+
restartLogStorageTask?.(config2.log);
|
|
59525
59607
|
const pluginResult = await pluginManager.reloadAll(config2.providers);
|
|
59608
|
+
const restartRequired = before.host !== after.host || before.port !== after.port || before.idleTimeout !== after.idleTimeout;
|
|
59526
59609
|
return _c.json({
|
|
59527
59610
|
ok: true,
|
|
59528
59611
|
summary: {
|
|
59529
59612
|
providers: Object.keys(config2.providers).length,
|
|
59530
59613
|
routes: Object.keys(config2.routes).length
|
|
59531
59614
|
},
|
|
59615
|
+
restartRequired,
|
|
59616
|
+
...restartRequired && {
|
|
59617
|
+
listen: { host: after.host, port: after.port },
|
|
59618
|
+
canRestart: Boolean(serverControl)
|
|
59619
|
+
},
|
|
59532
59620
|
...pluginResult.failures.length > 0 && {
|
|
59533
59621
|
pluginWarnings: pluginResult.failures
|
|
59534
59622
|
}
|
|
@@ -59537,6 +59625,14 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
59537
59625
|
return _c.json({ error: `\u5E94\u7528\u914D\u7F6E\u5931\u8D25: ${err instanceof Error ? err.message : err}` }, 500);
|
|
59538
59626
|
}
|
|
59539
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
|
+
});
|
|
59540
59636
|
api2.get("/config/meta", (c) => {
|
|
59541
59637
|
return c.json({
|
|
59542
59638
|
configPath: store.getPath(),
|
|
@@ -60082,6 +60178,7 @@ async function proxyAdminToDevServer(c, origin) {
|
|
|
60082
60178
|
}
|
|
60083
60179
|
async function createApp(store, options) {
|
|
60084
60180
|
const config2 = store.get();
|
|
60181
|
+
const serviceVersion = await readVersionString();
|
|
60085
60182
|
console.log(`\u5DF2\u52A0\u8F7D\u914D\u7F6E: ${store.getPath()}`);
|
|
60086
60183
|
if (config2.log) {
|
|
60087
60184
|
const logBaseDir = resolveLogBaseDir(config2.log);
|
|
@@ -60089,8 +60186,14 @@ async function createApp(store, options) {
|
|
|
60089
60186
|
} else {
|
|
60090
60187
|
resetLogger();
|
|
60091
60188
|
}
|
|
60092
|
-
|
|
60093
|
-
|
|
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());
|
|
60094
60197
|
const configDir = dirname4(resolve8(store.getPath()));
|
|
60095
60198
|
const pluginManager = new PluginManager(configDir);
|
|
60096
60199
|
const reloadResult = await pluginManager.reloadAll(config2.providers);
|
|
@@ -60109,7 +60212,7 @@ async function createApp(store, options) {
|
|
|
60109
60212
|
app.route(entry.mountPrefix, subApp);
|
|
60110
60213
|
console.log(`\u5DF2\u6CE8\u518C\u8DEF\u7531: ${routeType} -> ${entry.mountPrefix}`);
|
|
60111
60214
|
}
|
|
60112
|
-
app.route("/api", createAdminApiRoutes(store, pluginManager, options?.registerCleanup));
|
|
60215
|
+
app.route("/api", createAdminApiRoutes(store, pluginManager, options?.registerCleanup, options?.serverControl, restartLogStorageTask, serviceVersion));
|
|
60113
60216
|
console.log("\u5DF2\u6CE8\u518C\u7BA1\u7406 API: /api");
|
|
60114
60217
|
app.get("/api/docs", middleware({ url: "/api/openapi.json" }));
|
|
60115
60218
|
app.get("/api/openapi.json", (c) => c.json(openAPISpec));
|
|
@@ -60138,11 +60241,12 @@ async function createApp(store, options) {
|
|
|
60138
60241
|
}
|
|
60139
60242
|
return app;
|
|
60140
60243
|
}
|
|
60141
|
-
async function createAppRuntimeFromConfigPath(configPath, listen) {
|
|
60244
|
+
async function createAppRuntimeFromConfigPath(configPath, listen, serverControl) {
|
|
60142
60245
|
const store = new ConfigStore(configPath);
|
|
60143
60246
|
const cleanups = [];
|
|
60144
60247
|
const app = await createApp(store, {
|
|
60145
60248
|
listen,
|
|
60249
|
+
serverControl,
|
|
60146
60250
|
registerCleanup: (cleanup) => {
|
|
60147
60251
|
cleanups.push(cleanup);
|
|
60148
60252
|
}
|
|
@@ -60244,11 +60348,15 @@ function resolveIdleTimeoutSeconds(explicit) {
|
|
|
60244
60348
|
return DEFAULT_IDLE_TIMEOUT_SECONDS;
|
|
60245
60349
|
}
|
|
60246
60350
|
async function startServer(options) {
|
|
60351
|
+
const idleTimeout = resolveIdleTimeoutSeconds(options.idleTimeoutSeconds);
|
|
60352
|
+
const requestRestart = options.requestRestart;
|
|
60247
60353
|
const runtime = await createAppRuntimeFromConfigPath(options.configPath, {
|
|
60248
60354
|
host: options.host,
|
|
60249
60355
|
port: options.port
|
|
60250
|
-
}
|
|
60251
|
-
|
|
60356
|
+
}, requestRestart ? {
|
|
60357
|
+
requestRestart,
|
|
60358
|
+
current: { host: options.host, port: options.port, idleTimeout }
|
|
60359
|
+
} : undefined);
|
|
60252
60360
|
const server = Bun.serve({
|
|
60253
60361
|
fetch: (request, server2) => {
|
|
60254
60362
|
const remoteAddress = server2.requestIP(request)?.address ?? null;
|
|
@@ -60291,6 +60399,7 @@ var exports_process = {};
|
|
|
60291
60399
|
__export(exports_process, {
|
|
60292
60400
|
stopProcess: () => stopProcess,
|
|
60293
60401
|
startDaemon: () => startDaemon,
|
|
60402
|
+
spawnDetachedRestart: () => spawnDetachedRestart,
|
|
60294
60403
|
runServerProcess: () => runServerProcess,
|
|
60295
60404
|
readLogDelta: () => readLogDelta,
|
|
60296
60405
|
parseSharedFlags: () => parseSharedFlags,
|
|
@@ -60448,7 +60557,8 @@ async function runServerProcess(opts) {
|
|
|
60448
60557
|
configPath: ensured.path,
|
|
60449
60558
|
host,
|
|
60450
60559
|
port,
|
|
60451
|
-
idleTimeoutSeconds: Number.isFinite(idleTimeoutSeconds) ? idleTimeoutSeconds : undefined
|
|
60560
|
+
idleTimeoutSeconds: Number.isFinite(idleTimeoutSeconds) ? idleTimeoutSeconds : undefined,
|
|
60561
|
+
requestRestart: () => spawnDetachedRestart({ configPath: ensured.path })
|
|
60452
60562
|
});
|
|
60453
60563
|
} catch (err) {
|
|
60454
60564
|
const code = err?.code;
|
|
@@ -60552,6 +60662,32 @@ async function startDaemon(flags) {
|
|
|
60552
60662
|
throw new Error(`\u540E\u53F0\u542F\u52A8\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u65E5\u5FD7: ${files.daemonLog}
|
|
60553
60663
|
${tail}`);
|
|
60554
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
|
+
}
|
|
60555
60691
|
async function stopProcess(graceMs = 8000) {
|
|
60556
60692
|
await cleanupIfStale();
|
|
60557
60693
|
const state = readRuntimeState();
|
|
@@ -61194,6 +61330,10 @@ function normalizeOne(flag, raw2) {
|
|
|
61194
61330
|
}
|
|
61195
61331
|
function parseCommandArgs(def, args) {
|
|
61196
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
|
+
}
|
|
61197
61337
|
let parsed;
|
|
61198
61338
|
try {
|
|
61199
61339
|
parsed = nodeParseArgs({
|
|
@@ -61205,7 +61345,7 @@ function parseCommandArgs(def, args) {
|
|
|
61205
61345
|
} catch (err) {
|
|
61206
61346
|
throw new CliError("USAGE_ERROR", err instanceof Error ? err.message : String(err));
|
|
61207
61347
|
}
|
|
61208
|
-
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]);
|
|
61209
61349
|
for (const a of args) {
|
|
61210
61350
|
if (!a.startsWith("--"))
|
|
61211
61351
|
continue;
|
|
@@ -61273,8 +61413,10 @@ function levenshtein(a, b) {
|
|
|
61273
61413
|
}
|
|
61274
61414
|
return dp[b.length];
|
|
61275
61415
|
}
|
|
61416
|
+
var GLOBAL_TARGET_FLAG_NAMES;
|
|
61276
61417
|
var init_parse_args = __esm(() => {
|
|
61277
61418
|
init_errors();
|
|
61419
|
+
GLOBAL_TARGET_FLAG_NAMES = ["url", "host", "port"];
|
|
61278
61420
|
});
|
|
61279
61421
|
|
|
61280
61422
|
// src/cli/registry.ts
|
|
@@ -61570,9 +61712,298 @@ init_config_apply();
|
|
|
61570
61712
|
init_errors();
|
|
61571
61713
|
init_output();
|
|
61572
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();
|
|
61573
61722
|
init_runtime();
|
|
61574
61723
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
61575
|
-
|
|
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
|
|
61576
62007
|
function readConfig(configArg) {
|
|
61577
62008
|
const path = resolveConfigPath(configArg);
|
|
61578
62009
|
return { path, config: loadConfig(path) };
|
|
@@ -61640,7 +62071,7 @@ async function selectFromList(title, items) {
|
|
|
61640
62071
|
items.forEach((item, i) => {
|
|
61641
62072
|
console.log(` ${i + 1}) ${item}`);
|
|
61642
62073
|
});
|
|
61643
|
-
const rl =
|
|
62074
|
+
const rl = createInterface5({ input: process.stdin, output: process.stdout });
|
|
61644
62075
|
try {
|
|
61645
62076
|
const answer = await rl.question("\u8BF7\u8F93\u5165\u5E8F\u53F7: ");
|
|
61646
62077
|
const idx = Number.parseInt(answer, 10) - 1;
|
|
@@ -62511,32 +62942,26 @@ async function handleApply(args, flags) {
|
|
|
62511
62942
|
command: "config.apply",
|
|
62512
62943
|
flags,
|
|
62513
62944
|
fn: async (ctx) => {
|
|
62514
|
-
await
|
|
62515
|
-
const
|
|
62516
|
-
if (!state) {
|
|
62517
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u670D\u52A1\u672A\u8FD0\u884C\uFF0C\u65E0\u6CD5 apply", {
|
|
62518
|
-
hint: "\u542F\u52A8: `local-router start --daemon`"
|
|
62519
|
-
});
|
|
62520
|
-
}
|
|
62521
|
-
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" });
|
|
62522
62947
|
if (!res.ok) {
|
|
62523
62948
|
const text2 = await res.text();
|
|
62524
62949
|
throw new CliError("APPLY_FAILED", `apply \u5931\u8D25: ${res.status} ${text2}`, {
|
|
62525
|
-
details: { status: res.status, baseUrl:
|
|
62950
|
+
details: { status: res.status, baseUrl: target.baseUrl }
|
|
62526
62951
|
});
|
|
62527
62952
|
}
|
|
62528
|
-
const healthy = await checkHealth(
|
|
62953
|
+
const healthy = await checkHealth(target.baseUrl);
|
|
62529
62954
|
if (!healthy) {
|
|
62530
|
-
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}`);
|
|
62531
62956
|
}
|
|
62532
62957
|
emitResult(ctx, {
|
|
62533
62958
|
command: "config.apply",
|
|
62534
|
-
data: { ok: true, baseUrl:
|
|
62959
|
+
data: { ok: true, baseUrl: target.baseUrl },
|
|
62535
62960
|
md: {
|
|
62536
62961
|
heading: "config.apply \xB7 \u2713",
|
|
62537
|
-
data: `\u5DF2\u5E94\u7528: \`${
|
|
62962
|
+
data: `\u5DF2\u5E94\u7528: \`${target.baseUrl}\``
|
|
62538
62963
|
},
|
|
62539
|
-
text: `\u914D\u7F6E\u5DF2\u5E94\u7528: ${
|
|
62964
|
+
text: `\u914D\u7F6E\u5DF2\u5E94\u7528: ${target.baseUrl}`
|
|
62540
62965
|
});
|
|
62541
62966
|
}
|
|
62542
62967
|
});
|
|
@@ -63109,10 +63534,8 @@ defineSchemaCommand({
|
|
|
63109
63534
|
// src/cli/handlers/chat.ts
|
|
63110
63535
|
init_errors();
|
|
63111
63536
|
init_output();
|
|
63112
|
-
init_process();
|
|
63113
63537
|
init_registry();
|
|
63114
|
-
|
|
63115
|
-
import { createInterface as createInterface5 } from "readline/promises";
|
|
63538
|
+
import { createInterface as createInterface6 } from "readline/promises";
|
|
63116
63539
|
defineSchemaCommand({
|
|
63117
63540
|
name: "chat",
|
|
63118
63541
|
summary: "\u4EA4\u4E92\u5F0F REPL\uFF08\u9ED8\u8BA4\u8D70 openai-completions\uFF0C\u6D41\u5F0F\uFF09",
|
|
@@ -63131,25 +63554,19 @@ defineSchemaCommand({
|
|
|
63131
63554
|
{ name: "no-stream", type: "boolean", description: "\u7981\u7528\u6D41\u5F0F\uFF08\u4E00\u6B21\u8FD4\u56DE\uFF09" }
|
|
63132
63555
|
],
|
|
63133
63556
|
fn: async ({ values, ctx }) => {
|
|
63134
|
-
await cleanupIfStale();
|
|
63135
|
-
const state = readRuntimeState();
|
|
63136
|
-
if (!state)
|
|
63137
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u670D\u52A1\u672A\u8FD0\u884C");
|
|
63138
|
-
if (!await checkHealth(state.baseUrl)) {
|
|
63139
|
-
throw new CliError("HEALTH_FAILED", `\u670D\u52A1\u5065\u5EB7\u68C0\u67E5\u5931\u8D25: ${state.baseUrl}`);
|
|
63140
|
-
}
|
|
63141
63557
|
if (!process.stdin.isTTY) {
|
|
63142
63558
|
throw new CliError("INTERACTIVE_REQUIRED", "chat \u9700\u8981 TTY", {
|
|
63143
63559
|
hint: "\u7BA1\u9053\u573A\u666F\u8BF7\u7528 `local-router try`"
|
|
63144
63560
|
});
|
|
63145
63561
|
}
|
|
63146
|
-
const
|
|
63562
|
+
const target = await requireTarget(ctx);
|
|
63563
|
+
const baseUrl = target.baseUrl.replace(/\/+$/, "");
|
|
63147
63564
|
const url2 = `${baseUrl}/openai-completions/v1/chat/completions`;
|
|
63148
63565
|
const messages = [];
|
|
63149
63566
|
if (values.system)
|
|
63150
63567
|
messages.push({ role: "system", content: values.system });
|
|
63151
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`);
|
|
63152
|
-
const rl =
|
|
63569
|
+
const rl = createInterface6({ input: process.stdin, output: process.stdout });
|
|
63153
63570
|
try {
|
|
63154
63571
|
while (true) {
|
|
63155
63572
|
const input = (await rl.question("\u203A ")).trim();
|
|
@@ -63599,7 +64016,6 @@ init_config_apply();
|
|
|
63599
64016
|
init_errors();
|
|
63600
64017
|
init_output();
|
|
63601
64018
|
init_registry();
|
|
63602
|
-
init_runtime();
|
|
63603
64019
|
import { spawnSync } from "child_process";
|
|
63604
64020
|
import { copyFileSync, existsSync as existsSync13, mkdtempSync, rmSync as rmSync4 } from "fs";
|
|
63605
64021
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -63679,7 +64095,10 @@ function platformOpen(target) {
|
|
|
63679
64095
|
args = [target];
|
|
63680
64096
|
}
|
|
63681
64097
|
const r = spawnSync(cmd, args, { stdio: "ignore" });
|
|
63682
|
-
return {
|
|
64098
|
+
return {
|
|
64099
|
+
ok: !r.error && (typeof r.status !== "number" || r.status === 0),
|
|
64100
|
+
cmd: `${cmd} ${args.join(" ")}`
|
|
64101
|
+
};
|
|
63683
64102
|
}
|
|
63684
64103
|
defineSchemaCommand({
|
|
63685
64104
|
name: "open",
|
|
@@ -63693,7 +64112,7 @@ defineSchemaCommand({
|
|
|
63693
64112
|
}
|
|
63694
64113
|
],
|
|
63695
64114
|
flags: [{ name: "config", type: "string", description: "\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84" }],
|
|
63696
|
-
fn: ({ positionals, values, ctx }) => {
|
|
64115
|
+
fn: async ({ positionals, values, ctx }) => {
|
|
63697
64116
|
const target = positionals[0];
|
|
63698
64117
|
if (!target) {
|
|
63699
64118
|
throw new CliError("USAGE_ERROR", "\u7528\u6CD5: open <admin|docs|logs-dir|config>");
|
|
@@ -63701,13 +64120,8 @@ defineSchemaCommand({
|
|
|
63701
64120
|
let url2;
|
|
63702
64121
|
let label;
|
|
63703
64122
|
if (target === "admin") {
|
|
63704
|
-
const
|
|
63705
|
-
|
|
63706
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u670D\u52A1\u672A\u8FD0\u884C\uFF0C\u65E0\u6CD5\u6253\u5F00 admin", {
|
|
63707
|
-
hint: "`local-router start --daemon`"
|
|
63708
|
-
});
|
|
63709
|
-
}
|
|
63710
|
-
url2 = `${state.baseUrl}/admin/`;
|
|
64123
|
+
const resolved = await resolveTarget(ctx.flags);
|
|
64124
|
+
url2 = `${resolved.baseUrl}/admin/`;
|
|
63711
64125
|
label = "Web Admin";
|
|
63712
64126
|
} else if (target === "docs") {
|
|
63713
64127
|
url2 = "https://github.com/lakphy/local-router#readme";
|
|
@@ -63759,8 +64173,9 @@ defineSchemaCommand({
|
|
|
63759
64173
|
{ name: "config", type: "string", description: "\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84" }
|
|
63760
64174
|
],
|
|
63761
64175
|
fn: ({ values, ctx }) => {
|
|
63762
|
-
const
|
|
63763
|
-
const baseUrl =
|
|
64176
|
+
const guess = guessTargetUrl(ctx.flags);
|
|
64177
|
+
const baseUrl = guess.baseUrl;
|
|
64178
|
+
const running = guess.running;
|
|
63764
64179
|
const entries = [
|
|
63765
64180
|
{
|
|
63766
64181
|
key: "OPENAI_BASE_URL",
|
|
@@ -63804,12 +64219,12 @@ defineSchemaCommand({
|
|
|
63804
64219
|
data: {
|
|
63805
64220
|
baseUrl,
|
|
63806
64221
|
shell: values.shell,
|
|
63807
|
-
running
|
|
64222
|
+
running,
|
|
63808
64223
|
entries,
|
|
63809
64224
|
script
|
|
63810
64225
|
},
|
|
63811
64226
|
md: {
|
|
63812
|
-
heading: `env \xB7 ${
|
|
64227
|
+
heading: `env \xB7 ${running ? "\u2713 \u8FD0\u884C\u4E2D" : "\u2717 \u672A\u8FD0\u884C\uFF08\u4F7F\u7528\u9ED8\u8BA4 4099\uFF09"}`,
|
|
63813
64228
|
meta: [`baseUrl: \`${baseUrl}\``],
|
|
63814
64229
|
data: [
|
|
63815
64230
|
renderKv(entries.map((e) => ({ key: e.key, value: e.value }))),
|
|
@@ -64109,14 +64524,8 @@ defineSchemaCommand({
|
|
|
64109
64524
|
],
|
|
64110
64525
|
fn: async ({ values, ctx }) => {
|
|
64111
64526
|
const { entry, model, prompt, stream: streamMode, timeout: timeoutSec } = values;
|
|
64112
|
-
await
|
|
64113
|
-
const
|
|
64114
|
-
if (!state) {
|
|
64115
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u670D\u52A1\u672A\u8FD0\u884C", {
|
|
64116
|
-
hint: "`local-router start --daemon`"
|
|
64117
|
-
});
|
|
64118
|
-
}
|
|
64119
|
-
const payload = buildTryPayload(entry, model, prompt, state.baseUrl);
|
|
64527
|
+
const target = await requireTarget(ctx);
|
|
64528
|
+
const payload = buildTryPayload(entry, model, prompt, target.baseUrl);
|
|
64120
64529
|
const controller = new AbortController;
|
|
64121
64530
|
const timer = setTimeout(() => controller.abort(), timeoutSec * 1000);
|
|
64122
64531
|
const startedAt = Date.now();
|
|
@@ -64254,35 +64663,6 @@ defineSchemaCommand({
|
|
|
64254
64663
|
import { readFileSync as readFileSync11 } from "fs";
|
|
64255
64664
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
64256
64665
|
|
|
64257
|
-
// src/cli/asset-paths.ts
|
|
64258
|
-
async function findExisting(...candidates) {
|
|
64259
|
-
for (const url2 of candidates) {
|
|
64260
|
-
try {
|
|
64261
|
-
const exists = await Bun.file(url2).exists();
|
|
64262
|
-
if (exists)
|
|
64263
|
-
return url2;
|
|
64264
|
-
} catch {}
|
|
64265
|
-
}
|
|
64266
|
-
return null;
|
|
64267
|
-
}
|
|
64268
|
-
async function readPackageJson() {
|
|
64269
|
-
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));
|
|
64270
|
-
if (!url2)
|
|
64271
|
-
return null;
|
|
64272
|
-
try {
|
|
64273
|
-
return await Bun.file(url2).json();
|
|
64274
|
-
} catch {
|
|
64275
|
-
return null;
|
|
64276
|
-
}
|
|
64277
|
-
}
|
|
64278
|
-
async function findConfigSchemaUrl() {
|
|
64279
|
-
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));
|
|
64280
|
-
}
|
|
64281
|
-
async function readVersionString() {
|
|
64282
|
-
const pkg = await readPackageJson();
|
|
64283
|
-
return typeof pkg?.version === "string" ? pkg.version : "unknown";
|
|
64284
|
-
}
|
|
64285
|
-
|
|
64286
64666
|
// src/cli/error-docs.ts
|
|
64287
64667
|
var ERROR_DOCS = {
|
|
64288
64668
|
USAGE_ERROR: {
|
|
@@ -64375,6 +64755,16 @@ var ERROR_DOCS = {
|
|
|
64375
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",
|
|
64376
64756
|
fix: "\u5728\u9519\u8BEF\u7684 `details` \u91CC\u67E5\u770B\u5019\u9009\u9879\uFF0C\u663E\u5F0F\u8865 `--provider/--model` \u7B49\u3002"
|
|
64377
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
|
+
},
|
|
64378
64768
|
UNKNOWN_ERROR: {
|
|
64379
64769
|
summary: "\u672A\u77E5\u9519\u8BEF",
|
|
64380
64770
|
cause: "\u6CA1\u6709\u5339\u914D\u5230\u4EFB\u4F55 CliError \u7C7B\u578B\u3002",
|
|
@@ -64821,13 +65211,13 @@ defineSchemaCommand({
|
|
|
64821
65211
|
|
|
64822
65212
|
// src/cli/handlers/lifecycle.ts
|
|
64823
65213
|
init_config();
|
|
64824
|
-
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
64825
|
-
import { setTimeout as sleep3 } from "timers/promises";
|
|
64826
65214
|
init_errors();
|
|
64827
65215
|
init_output();
|
|
64828
65216
|
init_process();
|
|
64829
65217
|
init_registry();
|
|
64830
65218
|
init_runtime();
|
|
65219
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
65220
|
+
import { setTimeout as sleep3 } from "timers/promises";
|
|
64831
65221
|
|
|
64832
65222
|
// src/cli/wait.ts
|
|
64833
65223
|
init_errors();
|
|
@@ -65046,34 +65436,34 @@ defineSchemaCommand({
|
|
|
65046
65436
|
fn: async ({ values, ctx }) => {
|
|
65047
65437
|
const retry = Math.max(1, values.retry || 1);
|
|
65048
65438
|
const interval = Math.max(0, values["retry-interval"] || 1);
|
|
65049
|
-
|
|
65050
|
-
const
|
|
65051
|
-
if (!state) {
|
|
65052
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u670D\u52A1\u672A\u8FD0\u884C", {
|
|
65053
|
-
hint: "\u542F\u52A8: `local-router start --daemon`"
|
|
65054
|
-
});
|
|
65055
|
-
}
|
|
65439
|
+
const target = guessTargetUrl(ctx.flags);
|
|
65440
|
+
const baseUrl = target.baseUrl;
|
|
65056
65441
|
let ok = false;
|
|
65057
65442
|
for (let i = 0;i < retry; i++) {
|
|
65058
|
-
ok = await checkHealth(
|
|
65443
|
+
ok = await checkHealth(baseUrl);
|
|
65059
65444
|
if (ok)
|
|
65060
65445
|
break;
|
|
65061
65446
|
if (i < retry - 1)
|
|
65062
65447
|
await sleep3(interval * 1000);
|
|
65063
65448
|
}
|
|
65064
65449
|
if (!ok) {
|
|
65065
|
-
|
|
65066
|
-
|
|
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 }
|
|
65067
65457
|
});
|
|
65068
65458
|
}
|
|
65069
65459
|
emitResult(ctx, {
|
|
65070
65460
|
command: "health",
|
|
65071
|
-
data: { ok: true, baseUrl
|
|
65461
|
+
data: { ok: true, baseUrl },
|
|
65072
65462
|
md: {
|
|
65073
65463
|
heading: "health \xB7 \u2713 ok",
|
|
65074
|
-
meta: [`\u5730\u5740 ${
|
|
65464
|
+
meta: [`\u5730\u5740 ${baseUrl}`]
|
|
65075
65465
|
},
|
|
65076
|
-
text: `\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7: ${
|
|
65466
|
+
text: `\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7: ${baseUrl}`
|
|
65077
65467
|
});
|
|
65078
65468
|
}
|
|
65079
65469
|
});
|
|
@@ -65187,22 +65577,10 @@ defineSchemaCommand({
|
|
|
65187
65577
|
// src/cli/handlers/logs.ts
|
|
65188
65578
|
init_errors();
|
|
65189
65579
|
init_output();
|
|
65190
|
-
init_process();
|
|
65191
65580
|
init_registry();
|
|
65192
|
-
|
|
65193
|
-
|
|
65194
|
-
|
|
65195
|
-
const state = readRuntimeState();
|
|
65196
|
-
if (!state) {
|
|
65197
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u65E5\u5FD7\u67E5\u8BE2\u9700\u8981\u670D\u52A1\u8FD0\u884C", {
|
|
65198
|
-
hint: "`local-router start --daemon`"
|
|
65199
|
-
});
|
|
65200
|
-
}
|
|
65201
|
-
const ok = await checkHealth(state.baseUrl);
|
|
65202
|
-
if (!ok) {
|
|
65203
|
-
throw new CliError("HEALTH_FAILED", `\u670D\u52A1\u5065\u5EB7\u68C0\u67E5\u5931\u8D25: ${state.baseUrl}`);
|
|
65204
|
-
}
|
|
65205
|
-
return { baseUrl: state.baseUrl };
|
|
65581
|
+
async function requireRunning(ctx) {
|
|
65582
|
+
const t = await requireTarget(ctx);
|
|
65583
|
+
return { baseUrl: t.baseUrl };
|
|
65206
65584
|
}
|
|
65207
65585
|
async function fetchJson(url2, init) {
|
|
65208
65586
|
const res = await fetch(url2, init);
|
|
@@ -65250,7 +65628,7 @@ defineSchemaCommand({
|
|
|
65250
65628
|
{ name: "cursor", type: "string", description: "\u5206\u9875\u6E38\u6807" }
|
|
65251
65629
|
],
|
|
65252
65630
|
fn: async ({ values, ctx }) => {
|
|
65253
|
-
const { baseUrl } = await requireRunning();
|
|
65631
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65254
65632
|
const params = new URLSearchParams;
|
|
65255
65633
|
const set2 = (k, v) => {
|
|
65256
65634
|
if (v !== undefined && v !== null && v !== "" && v !== false) {
|
|
@@ -65315,7 +65693,7 @@ defineSchemaCommand({
|
|
|
65315
65693
|
const id = positionals[0];
|
|
65316
65694
|
if (!id)
|
|
65317
65695
|
throw new CliError("USAGE_ERROR", "\u7528\u6CD5: logs event <id>");
|
|
65318
|
-
const { baseUrl } = await requireRunning();
|
|
65696
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65319
65697
|
const params = new URLSearchParams;
|
|
65320
65698
|
if (values["include-stream"])
|
|
65321
65699
|
params.set("includeStream", "true");
|
|
@@ -65361,7 +65739,7 @@ defineSchemaCommand({
|
|
|
65361
65739
|
}
|
|
65362
65740
|
],
|
|
65363
65741
|
fn: async ({ values, ctx }) => {
|
|
65364
|
-
const { baseUrl } = await requireRunning();
|
|
65742
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65365
65743
|
const w = values.window ?? "24h";
|
|
65366
65744
|
const { status, json: json3 } = await fetchJson(`${baseUrl}/api/logs/events?window=${w}&hasError=true&limit=1&sort=time_desc`);
|
|
65367
65745
|
if (status !== 200)
|
|
@@ -65413,7 +65791,7 @@ defineSchemaCommand({
|
|
|
65413
65791
|
}
|
|
65414
65792
|
],
|
|
65415
65793
|
fn: async ({ values, ctx }) => {
|
|
65416
|
-
const { baseUrl } = await requireRunning();
|
|
65794
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65417
65795
|
const w = values.window ?? "24h";
|
|
65418
65796
|
const { status, json: json3 } = await fetchJson(`${baseUrl}/api/metrics/logs?window=${w}`);
|
|
65419
65797
|
if (status !== 200) {
|
|
@@ -65458,7 +65836,7 @@ defineSchemaCommand({
|
|
|
65458
65836
|
supportsJson: true,
|
|
65459
65837
|
requiresRunning: true,
|
|
65460
65838
|
fn: async ({ ctx }) => {
|
|
65461
|
-
const { baseUrl } = await requireRunning();
|
|
65839
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65462
65840
|
const { status, json: json3 } = await fetchJson(`${baseUrl}/api/logs/storage`);
|
|
65463
65841
|
if (status !== 200) {
|
|
65464
65842
|
throw new CliError("UNKNOWN_ERROR", `\u83B7\u53D6 storage \u5931\u8D25: ${status}`, { details: json3 });
|
|
@@ -65487,7 +65865,7 @@ defineSchemaCommand({
|
|
|
65487
65865
|
{ name: "limit", type: "number", default: 50, description: "\u6700\u5927 session \u6570" }
|
|
65488
65866
|
],
|
|
65489
65867
|
fn: async ({ values, ctx }) => {
|
|
65490
|
-
const { baseUrl } = await requireRunning();
|
|
65868
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65491
65869
|
const params = new URLSearchParams;
|
|
65492
65870
|
params.set("window", values.window ?? "24h");
|
|
65493
65871
|
if (values.user)
|
|
@@ -65501,7 +65879,10 @@ defineSchemaCommand({
|
|
|
65501
65879
|
emitResult(ctx, {
|
|
65502
65880
|
command: "logs.sessions",
|
|
65503
65881
|
data: json3,
|
|
65504
|
-
md: {
|
|
65882
|
+
md: {
|
|
65883
|
+
heading: "logs.sessions",
|
|
65884
|
+
data: renderCodeBlock(JSON.stringify(json3, null, 2), "json")
|
|
65885
|
+
}
|
|
65505
65886
|
});
|
|
65506
65887
|
}
|
|
65507
65888
|
});
|
|
@@ -65511,7 +65892,7 @@ defineSchemaCommand({
|
|
|
65511
65892
|
supportsJson: true,
|
|
65512
65893
|
requiresRunning: true,
|
|
65513
65894
|
fn: async ({ ctx }) => {
|
|
65514
|
-
const { baseUrl } = await requireRunning();
|
|
65895
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65515
65896
|
const stream = startStream(ctx, "logs.tail");
|
|
65516
65897
|
const res = await fetch(`${baseUrl}/api/logs/tail`, {
|
|
65517
65898
|
headers: { accept: "text/event-stream" }
|
|
@@ -65573,8 +65954,8 @@ defineSchemaCommand({
|
|
|
65573
65954
|
{ name: "from", type: "string", description: "ISO \u8D77\u70B9" },
|
|
65574
65955
|
{ name: "to", type: "string", description: "ISO \u7EC8\u70B9" }
|
|
65575
65956
|
],
|
|
65576
|
-
fn: async ({ values }) => {
|
|
65577
|
-
const { baseUrl } = await requireRunning();
|
|
65957
|
+
fn: async ({ values, ctx }) => {
|
|
65958
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65578
65959
|
const params = new URLSearchParams;
|
|
65579
65960
|
params.set("format", values.format ?? "jsonl");
|
|
65580
65961
|
params.set("window", values.window ?? "24h");
|
|
@@ -65605,7 +65986,7 @@ defineSchemaCommand({
|
|
|
65605
65986
|
const id = positionals[0];
|
|
65606
65987
|
if (!id)
|
|
65607
65988
|
throw new CliError("USAGE_ERROR", "\u7528\u6CD5: logs replay <event-id>");
|
|
65608
|
-
const { baseUrl } = await requireRunning();
|
|
65989
|
+
const { baseUrl } = await requireRunning(ctx);
|
|
65609
65990
|
const detailRes = await fetchJson(`${baseUrl}/api/logs/events/${encodeURIComponent(id)}`);
|
|
65610
65991
|
if (detailRes.status === 404)
|
|
65611
65992
|
throw new CliError("ROUTE_NOT_FOUND", `\u4E8B\u4EF6\u4E0D\u5B58\u5728: ${id}`);
|
|
@@ -65658,7 +66039,6 @@ defineSchemaCommand({
|
|
|
65658
66039
|
init_config();
|
|
65659
66040
|
init_errors();
|
|
65660
66041
|
init_output();
|
|
65661
|
-
init_process();
|
|
65662
66042
|
init_registry();
|
|
65663
66043
|
init_runtime();
|
|
65664
66044
|
import { existsSync as existsSync16, rmSync as rmSync5 } from "fs";
|
|
@@ -65673,15 +66053,9 @@ async function fetchJson2(url2) {
|
|
|
65673
66053
|
} catch {}
|
|
65674
66054
|
return { status: res.status, json: json3 };
|
|
65675
66055
|
}
|
|
65676
|
-
async function requireBaseUrl() {
|
|
65677
|
-
await
|
|
65678
|
-
|
|
65679
|
-
if (!state)
|
|
65680
|
-
throw new CliError("SERVICE_NOT_RUNNING", "\u670D\u52A1\u672A\u8FD0\u884C");
|
|
65681
|
-
if (!await checkHealth(state.baseUrl)) {
|
|
65682
|
-
throw new CliError("HEALTH_FAILED", `\u670D\u52A1\u5065\u5EB7\u68C0\u67E5\u5931\u8D25: ${state.baseUrl}`);
|
|
65683
|
-
}
|
|
65684
|
-
return state.baseUrl;
|
|
66056
|
+
async function requireBaseUrl(ctx) {
|
|
66057
|
+
const t = await requireTarget(ctx);
|
|
66058
|
+
return t.baseUrl;
|
|
65685
66059
|
}
|
|
65686
66060
|
defineSchemaCommand({
|
|
65687
66061
|
name: "logs tokens",
|
|
@@ -65698,7 +66072,7 @@ defineSchemaCommand({
|
|
|
65698
66072
|
}
|
|
65699
66073
|
],
|
|
65700
66074
|
fn: async ({ values, ctx }) => {
|
|
65701
|
-
const baseUrl = await requireBaseUrl();
|
|
66075
|
+
const baseUrl = await requireBaseUrl(ctx);
|
|
65702
66076
|
const { status, json: json3 } = await fetchJson2(`${baseUrl}/api/logs/events?window=${values.window}&limit=1`);
|
|
65703
66077
|
if (status !== 200) {
|
|
65704
66078
|
throw new CliError("UNKNOWN_ERROR", `\u67E5\u8BE2\u5931\u8D25: ${status}`, { details: json3 });
|
|
@@ -65763,7 +66137,7 @@ defineSchemaCommand({
|
|
|
65763
66137
|
}
|
|
65764
66138
|
],
|
|
65765
66139
|
fn: async ({ values, ctx }) => {
|
|
65766
|
-
const baseUrl = await requireBaseUrl();
|
|
66140
|
+
const baseUrl = await requireBaseUrl(ctx);
|
|
65767
66141
|
let rateTable = {};
|
|
65768
66142
|
if (values["rate-table"]) {
|
|
65769
66143
|
try {
|
|
@@ -66253,6 +66627,59 @@ defineSchemaCommand({
|
|
|
66253
66627
|
}
|
|
66254
66628
|
});
|
|
66255
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
|
+
|
|
66256
66683
|
// src/cli.ts
|
|
66257
66684
|
init_output();
|
|
66258
66685
|
init_parse_args();
|