@visorcraft/idlehands 1.1.10 → 1.1.12
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/agent.js +4 -0
- package/dist/agent.js.map +1 -1
- package/dist/anton/controller.js +172 -3
- package/dist/anton/controller.js.map +1 -1
- package/dist/anton/lock.js +31 -2
- package/dist/anton/lock.js.map +1 -1
- package/dist/bot/commands.js +34 -9
- package/dist/bot/commands.js.map +1 -1
- package/dist/bot/dir-guard.js +93 -0
- package/dist/bot/dir-guard.js.map +1 -0
- package/dist/bot/discord.js +61 -16
- package/dist/bot/discord.js.map +1 -1
- package/dist/bot/session-manager.js +30 -10
- package/dist/bot/session-manager.js.map +1 -1
- package/dist/bot/telegram.js +42 -4
- package/dist/bot/telegram.js.map +1 -1
- package/dist/cli/args.js +7 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/commands/anton.js +1 -0
- package/dist/cli/commands/anton.js.map +1 -1
- package/dist/cli/runtime-cmds.js +168 -127
- package/dist/cli/runtime-cmds.js.map +1 -1
- package/dist/runtime/health.js +103 -0
- package/dist/runtime/health.js.map +1 -0
- package/dist/tools.js +40 -8
- package/dist/tools.js.map +1 -1
- package/dist/tui/controller.js +32 -5
- package/dist/tui/controller.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/runtime-cmds.js
CHANGED
|
@@ -2,6 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import readline from 'node:readline/promises';
|
|
3
3
|
import { stdin as input, stdout as output } from 'node:process';
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { probeModelsEndpoint, waitForModelsReady } from '../runtime/health.js';
|
|
5
6
|
import { loadRuntimes, saveRuntimes, validateRuntimes, redactConfig, bootstrapRuntimes, interpolateTemplate, } from '../runtime/store.js';
|
|
6
7
|
import { configDir, shellEscape } from '../utils.js';
|
|
7
8
|
import { firstToken } from './command-utils.js';
|
|
@@ -37,18 +38,17 @@ function runHostCommand(host, command, timeoutSec = 5) {
|
|
|
37
38
|
if (host.transport === 'local')
|
|
38
39
|
return runLocalCommand(command, timeoutSec);
|
|
39
40
|
const target = `${host.connection.user ? `${host.connection.user}@` : ''}${host.connection.host ?? ''}`;
|
|
40
|
-
const
|
|
41
|
+
const sshParts = [
|
|
41
42
|
'ssh',
|
|
42
43
|
'-o', 'BatchMode=yes',
|
|
43
44
|
'-o', `ConnectTimeout=${timeoutSec}`,
|
|
44
45
|
];
|
|
45
46
|
if (host.connection.port)
|
|
46
|
-
|
|
47
|
+
sshParts.push('-p', String(host.connection.port));
|
|
47
48
|
if (host.connection.key_path)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return runLocalCommand(sshArgs.join(' '), timeoutSec + 1);
|
|
49
|
+
sshParts.push('-i', shellEscape(host.connection.key_path));
|
|
50
|
+
sshParts.push(shellEscape(target), '--', 'bash', '-lc', shellEscape(command));
|
|
51
|
+
return runLocalCommand(sshParts.join(' '), timeoutSec + 1);
|
|
52
52
|
}
|
|
53
53
|
function usage(kind) {
|
|
54
54
|
console.log(`Usage:\n idlehands ${kind}\n idlehands ${kind} show <id>\n idlehands ${kind} add\n idlehands ${kind} edit <id>\n idlehands ${kind} remove <id>\n idlehands ${kind} validate\n idlehands ${kind} test <id>\n idlehands ${kind} doctor`);
|
|
@@ -744,8 +744,11 @@ export async function runSelectSubcommand(args, _config) {
|
|
|
744
744
|
const force = !!args.force;
|
|
745
745
|
const restart = !!args.restart;
|
|
746
746
|
const forceRestart = force || restart;
|
|
747
|
+
const waitReady = !!(args['wait-ready'] ?? args.wait_ready);
|
|
748
|
+
const waitTimeoutSecRaw = Number(args['wait-timeout'] ?? args.wait_timeout ?? args.timeout ?? 0);
|
|
749
|
+
const waitTimeoutSec = Number.isFinite(waitTimeoutSecRaw) && waitTimeoutSecRaw > 0 ? waitTimeoutSecRaw : undefined;
|
|
747
750
|
if (!modelId) {
|
|
748
|
-
console.log('Usage: idlehands select --model <id> [--backend <id>] [--host <id>] [--dry-run] [--json] [--force] [--restart]');
|
|
751
|
+
console.log('Usage: idlehands select --model <id> [--backend <id>] [--host <id>] [--dry-run] [--json] [--force] [--restart] [--wait-ready] [--wait-timeout <sec>]');
|
|
749
752
|
console.log(' idlehands select status');
|
|
750
753
|
return;
|
|
751
754
|
}
|
|
@@ -807,26 +810,65 @@ export async function runSelectSubcommand(args, _config) {
|
|
|
807
810
|
},
|
|
808
811
|
force,
|
|
809
812
|
});
|
|
813
|
+
let executedPlan = result;
|
|
810
814
|
let execResult = await executeWithRenderer(result);
|
|
811
815
|
// Reuse-probe fallback: if reuse validation fails, force restart automatically.
|
|
812
816
|
if (!execResult.ok && result.reuse && !forceRestart) {
|
|
813
817
|
console.error('Reuse health check failed. Retrying with forced restart...');
|
|
814
818
|
const restartPlan = plan({ modelId, backendOverride, hostOverride, mode: 'live', forceRestart: true }, rtConfig, active);
|
|
815
819
|
if (restartPlan.ok) {
|
|
820
|
+
executedPlan = restartPlan;
|
|
816
821
|
await applyDynamicProbeDefaults(restartPlan, rtConfig, runOnHost);
|
|
817
822
|
execResult = await executeWithRenderer(restartPlan);
|
|
818
823
|
}
|
|
819
824
|
}
|
|
825
|
+
const readyChecks = [];
|
|
826
|
+
let readyOk = true;
|
|
827
|
+
if (execResult.ok && waitReady) {
|
|
828
|
+
const timeoutSec = waitTimeoutSec ?? (executedPlan.model.launch.probe_timeout_sec ?? 60);
|
|
829
|
+
for (const resolvedHost of executedPlan.hosts) {
|
|
830
|
+
const hostCfg = rtConfig.hosts.find((h) => h.id === resolvedHost.id);
|
|
831
|
+
if (!hostCfg)
|
|
832
|
+
continue;
|
|
833
|
+
const port = executedPlan.model.runtime_defaults?.port ?? 8080;
|
|
834
|
+
process.stdout.write(` Waiting for /v1/models on ${resolvedHost.id}:${port}...`);
|
|
835
|
+
const ready = await waitForModelsReady(runOnHost, hostCfg, port, {
|
|
836
|
+
timeoutMs: timeoutSec * 1000,
|
|
837
|
+
intervalMs: executedPlan.model.launch.probe_interval_ms ?? 1500,
|
|
838
|
+
});
|
|
839
|
+
readyChecks.push({
|
|
840
|
+
hostId: resolvedHost.id,
|
|
841
|
+
ok: ready.ok,
|
|
842
|
+
attempts: ready.attempts,
|
|
843
|
+
reason: ready.reason,
|
|
844
|
+
status: ready.last.status,
|
|
845
|
+
httpCode: ready.last.httpCode,
|
|
846
|
+
modelIds: ready.last.modelIds,
|
|
847
|
+
});
|
|
848
|
+
if (ready.ok) {
|
|
849
|
+
process.stdout.write(' ✓\n');
|
|
850
|
+
}
|
|
851
|
+
else {
|
|
852
|
+
process.stdout.write(' ✗\n');
|
|
853
|
+
if (ready.reason)
|
|
854
|
+
process.stdout.write(` ${ready.reason}\n`);
|
|
855
|
+
readyOk = false;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
820
859
|
iface.close();
|
|
821
860
|
if (jsonOut) {
|
|
822
|
-
console.log(JSON.stringify(
|
|
861
|
+
console.log(JSON.stringify({
|
|
862
|
+
execute: execResult,
|
|
863
|
+
waitReady: waitReady ? { ok: readyOk, checks: readyChecks } : undefined,
|
|
864
|
+
}, null, 2));
|
|
823
865
|
}
|
|
824
866
|
else if (execResult.ok) {
|
|
825
867
|
if (execResult.reused) {
|
|
826
868
|
console.log('Runtime already active and healthy. No changes needed.');
|
|
827
869
|
}
|
|
828
870
|
else {
|
|
829
|
-
console.log(`Runtime switched to "${
|
|
871
|
+
console.log(`Runtime switched to "${executedPlan.model.display_name}" successfully.`);
|
|
830
872
|
}
|
|
831
873
|
// Show the derived endpoint so the user knows where requests will go
|
|
832
874
|
const { loadActiveRuntime: loadAR } = await import('../runtime/executor.js');
|
|
@@ -834,6 +876,10 @@ export async function runSelectSubcommand(args, _config) {
|
|
|
834
876
|
if (activeNow?.endpoint) {
|
|
835
877
|
console.log(`Endpoint: ${activeNow.endpoint}`);
|
|
836
878
|
}
|
|
879
|
+
if (waitReady && !readyOk) {
|
|
880
|
+
console.error('Wait-ready failed: server did not become ready in time.');
|
|
881
|
+
process.exitCode = 1;
|
|
882
|
+
}
|
|
837
883
|
}
|
|
838
884
|
else {
|
|
839
885
|
console.error(`Execution failed: ${execResult.error || 'unknown error'}`);
|
|
@@ -847,27 +893,9 @@ const YELLOW = '\x1b[33m';
|
|
|
847
893
|
const DIM = '\x1b[2m';
|
|
848
894
|
const BOLD = '\x1b[1m';
|
|
849
895
|
const RESET = '\x1b[0m';
|
|
850
|
-
function parseCurlTagged(stdout) {
|
|
851
|
-
const m = stdout.match(/\n__HTTP__:(\d{3})\s*$/);
|
|
852
|
-
if (!m)
|
|
853
|
-
return { code: null, body: stdout.trim() };
|
|
854
|
-
const code = Number(m[1]);
|
|
855
|
-
const body = stdout.slice(0, m.index).trim();
|
|
856
|
-
return { code: Number.isFinite(code) ? code : null, body };
|
|
857
|
-
}
|
|
858
|
-
function classifyProbe(exitCode, httpCode) {
|
|
859
|
-
if (httpCode === 200)
|
|
860
|
-
return 'ready';
|
|
861
|
-
if (httpCode === 503)
|
|
862
|
-
return 'loading';
|
|
863
|
-
if (exitCode === 7 || exitCode === 28)
|
|
864
|
-
return 'down';
|
|
865
|
-
if (exitCode !== 0)
|
|
866
|
-
return 'down';
|
|
867
|
-
return 'unknown';
|
|
868
|
-
}
|
|
869
896
|
export async function runHealthSubcommand(args, _config) {
|
|
870
897
|
const { runOnHost } = await import('../runtime/executor.js');
|
|
898
|
+
const jsonOut = !!args.json;
|
|
871
899
|
let runtimes;
|
|
872
900
|
try {
|
|
873
901
|
runtimes = await loadRuntimes();
|
|
@@ -887,7 +915,6 @@ export async function runHealthSubcommand(args, _config) {
|
|
|
887
915
|
const trimmed = input.trim();
|
|
888
916
|
if (!trimmed)
|
|
889
917
|
return null;
|
|
890
|
-
// Try range format: "8000-8100"
|
|
891
918
|
if (/^\d+-\d+$/.test(trimmed)) {
|
|
892
919
|
const [start, end] = trimmed.split('-').map(Number);
|
|
893
920
|
if (start > end || start < 1 || end > 65535)
|
|
@@ -897,13 +924,11 @@ export async function runHealthSubcommand(args, _config) {
|
|
|
897
924
|
ports.push(p);
|
|
898
925
|
return ports;
|
|
899
926
|
}
|
|
900
|
-
// Try comma-separated: "8080,8081,8082"
|
|
901
927
|
if (trimmed.includes(',')) {
|
|
902
928
|
const parts = trimmed.split(',').map((s) => s.trim()).filter(Boolean);
|
|
903
929
|
const ports = parts.map(Number).filter((p) => Number.isFinite(p) && p >= 1 && p <= 65535);
|
|
904
930
|
return ports.length > 0 ? ports : null;
|
|
905
931
|
}
|
|
906
|
-
// Single port
|
|
907
932
|
const port = Number(trimmed);
|
|
908
933
|
if (Number.isFinite(port) && port >= 1 && port <= 65535)
|
|
909
934
|
return [port];
|
|
@@ -915,86 +940,114 @@ export async function runHealthSubcommand(args, _config) {
|
|
|
915
940
|
return;
|
|
916
941
|
}
|
|
917
942
|
let anyFailed = false;
|
|
918
|
-
|
|
919
|
-
|
|
943
|
+
const report = {
|
|
944
|
+
ok: true,
|
|
945
|
+
generatedAt: new Date().toISOString(),
|
|
946
|
+
hosts: [],
|
|
947
|
+
configuredModels: [],
|
|
948
|
+
discovery: {
|
|
949
|
+
ports: [],
|
|
950
|
+
hosts: [],
|
|
951
|
+
},
|
|
952
|
+
};
|
|
953
|
+
if (!jsonOut)
|
|
954
|
+
console.log(`\n${BOLD}Hosts${RESET}`);
|
|
920
955
|
for (const host of enabledHosts) {
|
|
921
956
|
const label = host.transport === 'ssh'
|
|
922
957
|
? `${host.id} (${host.connection.user ? host.connection.user + '@' : ''}${host.connection.host ?? '?'})`
|
|
923
958
|
: `${host.id} (local)`;
|
|
924
959
|
const cmd = host.health.check_cmd;
|
|
925
960
|
const timeoutMs = (host.health.timeout_sec ?? 5) * 1000;
|
|
926
|
-
|
|
961
|
+
if (!jsonOut)
|
|
962
|
+
process.stdout.write(` ${label}... `);
|
|
927
963
|
const result = await runOnHost(cmd, host, timeoutMs);
|
|
964
|
+
report.hosts.push({
|
|
965
|
+
id: host.id,
|
|
966
|
+
ok: result.exitCode === 0,
|
|
967
|
+
exitCode: result.exitCode,
|
|
968
|
+
stdout: result.stdout ?? '',
|
|
969
|
+
stderr: result.stderr ?? '',
|
|
970
|
+
});
|
|
928
971
|
if (result.exitCode === 0) {
|
|
929
|
-
|
|
972
|
+
if (!jsonOut)
|
|
973
|
+
console.log(`${GREEN}✓${RESET}`);
|
|
930
974
|
}
|
|
931
975
|
else {
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
976
|
+
if (!jsonOut) {
|
|
977
|
+
console.log(`${RED}✗${RESET}`);
|
|
978
|
+
if (result.stderr.trim()) {
|
|
979
|
+
for (const line of result.stderr.trim().split('\n').slice(0, 4)) {
|
|
980
|
+
console.log(` ${DIM}${line}${RESET}`);
|
|
981
|
+
}
|
|
936
982
|
}
|
|
937
983
|
}
|
|
938
984
|
anyFailed = true;
|
|
939
985
|
}
|
|
940
986
|
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
const timeoutMs = (model.launch.probe_timeout_sec ?? 60) * 1000;
|
|
987
|
+
if (enabledModels.length > 0 && !jsonOut) {
|
|
988
|
+
console.log(`\n${BOLD}Configured Models${RESET}`);
|
|
989
|
+
}
|
|
990
|
+
for (const model of enabledModels) {
|
|
991
|
+
const targetHosts = model.host_policy === 'any'
|
|
992
|
+
? enabledHosts
|
|
993
|
+
: enabledHosts.filter((h) => model.host_policy.includes(h.id));
|
|
994
|
+
const backend = model.backend_policy === 'any'
|
|
995
|
+
? enabledBackends[0] ?? null
|
|
996
|
+
: enabledBackends.find((b) => model.backend_policy.includes(b.id)) ?? null;
|
|
997
|
+
for (const host of targetHosts) {
|
|
998
|
+
const port = String(model.runtime_defaults?.port ?? 8080);
|
|
999
|
+
const backendArgs = backend?.args?.map((a) => shellEscape(a)).join(' ') ?? '';
|
|
1000
|
+
const backendEnv = backend?.env
|
|
1001
|
+
? Object.entries(backend.env).map(([k, v]) => `${k}=${shellEscape(String(v))}`).join(' ')
|
|
1002
|
+
: '';
|
|
1003
|
+
const vars = {
|
|
1004
|
+
source: model.source,
|
|
1005
|
+
port,
|
|
1006
|
+
host: host.connection.host ?? host.id,
|
|
1007
|
+
backend_args: backendArgs,
|
|
1008
|
+
backend_env: backendEnv,
|
|
1009
|
+
model_id: model.id,
|
|
1010
|
+
host_id: host.id,
|
|
1011
|
+
backend_id: backend?.id ?? '',
|
|
1012
|
+
};
|
|
1013
|
+
let probeCmd;
|
|
1014
|
+
try {
|
|
1015
|
+
probeCmd = interpolateTemplate(model.launch.probe_cmd, vars);
|
|
1016
|
+
}
|
|
1017
|
+
catch {
|
|
1018
|
+
probeCmd = model.launch.probe_cmd;
|
|
1019
|
+
}
|
|
1020
|
+
const timeoutMs = (model.launch.probe_timeout_sec ?? 60) * 1000;
|
|
1021
|
+
if (!jsonOut)
|
|
977
1022
|
process.stdout.write(` ${model.display_name} on ${host.id}... `);
|
|
978
|
-
|
|
979
|
-
|
|
1023
|
+
const result = await runOnHost(probeCmd, host, timeoutMs);
|
|
1024
|
+
const detail = (result.stderr || result.stdout || '').trim();
|
|
1025
|
+
report.configuredModels.push({
|
|
1026
|
+
modelId: model.id,
|
|
1027
|
+
hostId: host.id,
|
|
1028
|
+
ok: result.exitCode === 0,
|
|
1029
|
+
exitCode: result.exitCode,
|
|
1030
|
+
detail,
|
|
1031
|
+
});
|
|
1032
|
+
if (result.exitCode === 0) {
|
|
1033
|
+
if (!jsonOut) {
|
|
980
1034
|
const body = result.stdout.trim();
|
|
981
1035
|
console.log(`${GREEN}✓${RESET}${body ? ` ${DIM}${body.split('\n')[0].slice(0, 80)}${RESET}` : ''}`);
|
|
982
1036
|
}
|
|
983
|
-
|
|
1037
|
+
}
|
|
1038
|
+
else {
|
|
1039
|
+
if (!jsonOut) {
|
|
984
1040
|
console.log(`${RED}✗${RESET}`);
|
|
985
|
-
const detail = (result.stderr || result.stdout).trim();
|
|
986
1041
|
if (detail) {
|
|
987
1042
|
for (const line of detail.split('\n').slice(0, 4)) {
|
|
988
1043
|
console.log(` ${DIM}${line}${RESET}`);
|
|
989
1044
|
}
|
|
990
1045
|
}
|
|
991
|
-
anyFailed = true;
|
|
992
1046
|
}
|
|
1047
|
+
anyFailed = true;
|
|
993
1048
|
}
|
|
994
1049
|
}
|
|
995
1050
|
}
|
|
996
|
-
// ── Discovery: what is actually loaded (configured + common ports) ──
|
|
997
|
-
console.log(`\n${BOLD}Loaded (discovered)${RESET}`);
|
|
998
1051
|
let candidatePorts;
|
|
999
1052
|
if (scanPortsOverride) {
|
|
1000
1053
|
candidatePorts = scanPortsOverride;
|
|
@@ -1005,57 +1058,45 @@ export async function runHealthSubcommand(args, _config) {
|
|
|
1005
1058
|
configuredPorts.add(p);
|
|
1006
1059
|
candidatePorts = Array.from(configuredPorts).sort((a, b) => a - b);
|
|
1007
1060
|
}
|
|
1061
|
+
report.discovery.ports = candidatePorts;
|
|
1008
1062
|
const configuredModelIds = new Set(enabledModels.map((m) => m.id));
|
|
1063
|
+
if (!jsonOut)
|
|
1064
|
+
console.log(`\n${BOLD}Discovered Servers (/v1/models + /health)${RESET}`);
|
|
1009
1065
|
for (const host of enabledHosts) {
|
|
1010
|
-
|
|
1066
|
+
const hostEntry = { hostId: host.id, services: [] };
|
|
1067
|
+
if (!jsonOut)
|
|
1068
|
+
console.log(` ${host.id}:`);
|
|
1011
1069
|
for (const port of candidatePorts) {
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
if (status !== 'ready') {
|
|
1031
|
-
const healthCmd = `curl -sS -m 3 -o - -w "\\n__HTTP__:%{http_code}" http://127.0.0.1:${port}/health`;
|
|
1032
|
-
const healthRes = await runOnHost(healthCmd, host, 5000);
|
|
1033
|
-
const parsedHealth = parseCurlTagged(healthRes.stdout ?? '');
|
|
1034
|
-
const healthStatus = classifyProbe(healthRes.exitCode, parsedHealth.code);
|
|
1035
|
-
// Prefer the more informative status.
|
|
1036
|
-
if (healthStatus === 'ready' || healthStatus === 'loading') {
|
|
1037
|
-
status = healthStatus;
|
|
1038
|
-
httpCode = parsedHealth.code;
|
|
1039
|
-
}
|
|
1040
|
-
else if (status === 'unknown') {
|
|
1041
|
-
status = healthStatus;
|
|
1042
|
-
httpCode = parsedHealth.code;
|
|
1043
|
-
}
|
|
1070
|
+
const probe = await probeModelsEndpoint(runOnHost, host, port, 5000);
|
|
1071
|
+
if (probe.status === 'down')
|
|
1072
|
+
continue; // concise display + compact JSON
|
|
1073
|
+
hostEntry.services.push({
|
|
1074
|
+
port,
|
|
1075
|
+
status: probe.status,
|
|
1076
|
+
httpCode: probe.httpCode,
|
|
1077
|
+
modelIds: probe.modelIds,
|
|
1078
|
+
stderr: probe.stderr,
|
|
1079
|
+
exitCode: probe.exitCode,
|
|
1080
|
+
});
|
|
1081
|
+
if (!jsonOut) {
|
|
1082
|
+
const icon = probe.status === 'ready' ? `${GREEN}✓${RESET}` : probe.status === 'loading' ? `${YELLOW}~${RESET}` : `${DIM}?${RESET}`;
|
|
1083
|
+
const extras = probe.modelIds.filter((id) => !configuredModelIds.has(id));
|
|
1084
|
+
const idsText = probe.modelIds.length ? ` models=${probe.modelIds.join(', ')}` : '';
|
|
1085
|
+
const extrasText = extras.length ? ` ${YELLOW}(extra/unconfigured: ${extras.join(', ')})${RESET}` : '';
|
|
1086
|
+
const httpText = probe.httpCode != null ? ` http=${probe.httpCode}` : '';
|
|
1087
|
+
console.log(` ${icon} :${port} ${probe.status}${httpText}${idsText}${extrasText}`);
|
|
1044
1088
|
}
|
|
1045
|
-
if (status === 'down')
|
|
1046
|
-
continue; // keep output concise
|
|
1047
|
-
const icon = status === 'ready' ? `${GREEN}✓${RESET}` : status === 'loading' ? `${YELLOW}~${RESET}` : `${DIM}?${RESET}`;
|
|
1048
|
-
const statusWord = status === 'ready' ? 'ready' : status === 'loading' ? 'loading' : 'unknown';
|
|
1049
|
-
const extras = modelIds.filter((id) => !configuredModelIds.has(id));
|
|
1050
|
-
const idsText = modelIds.length ? ` models=${modelIds.join(', ')}` : '';
|
|
1051
|
-
const extrasText = extras.length ? ` ${YELLOW}(extra/unconfigured: ${extras.join(', ')})${RESET}` : '';
|
|
1052
|
-
const httpText = httpCode != null ? ` http=${httpCode}` : '';
|
|
1053
|
-
console.log(` ${icon} :${port} ${statusWord}${httpText}${idsText}${extrasText}`);
|
|
1054
1089
|
}
|
|
1090
|
+
report.discovery.hosts.push(hostEntry);
|
|
1055
1091
|
}
|
|
1056
|
-
|
|
1057
|
-
if (
|
|
1058
|
-
|
|
1092
|
+
report.ok = !anyFailed;
|
|
1093
|
+
if (jsonOut) {
|
|
1094
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1059
1095
|
}
|
|
1096
|
+
else {
|
|
1097
|
+
console.log();
|
|
1098
|
+
}
|
|
1099
|
+
if (anyFailed)
|
|
1100
|
+
process.exitCode = 1;
|
|
1060
1101
|
}
|
|
1061
1102
|
//# sourceMappingURL=runtime-cmds.js.map
|