jishushell 0.4.24 → 0.4.30
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/INSTALL-NOTICE +11 -0
- package/apps/browserless-chromium-container.yaml +78 -0
- package/apps/hermes-container.yaml +36 -2
- package/apps/ollama-binary.yaml +91 -90
- package/apps/ollama-cpu-container.yaml +8 -1
- package/apps/ollama-with-hollama-binary.yaml +91 -90
- package/apps/openclaw-binary.yaml +30 -1
- package/apps/openclaw-container.yaml +37 -2
- package/apps/openclaw-with-ollama-container.yaml +11 -2
- package/apps/openclaw-with-searxng-container.yaml +22 -2
- package/apps/openwebui-container.yaml +45 -1
- package/apps/playwright-container.yaml +7 -1
- package/apps/searxng-container.yaml +54 -4
- package/dist/cli/app.js +79 -9
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/doctor.d.ts +12 -12
- package/dist/cli/doctor.js +242 -55
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/llm.d.ts +4 -3
- package/dist/cli/llm.js +4 -3
- package/dist/cli/llm.js.map +1 -1
- package/dist/cli/panel.d.ts +6 -5
- package/dist/cli/panel.js +10 -9
- package/dist/cli/panel.js.map +1 -1
- package/dist/control.d.ts +7 -6
- package/dist/control.js +7 -6
- package/dist/control.js.map +1 -1
- package/dist/routes/agent-apps.d.ts +1 -1
- package/dist/routes/agent-apps.js +1 -1
- package/dist/routes/apps.js +44 -11
- package/dist/routes/apps.js.map +1 -1
- package/dist/routes/auth.js +3 -0
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/instances.js +787 -16
- package/dist/routes/instances.js.map +1 -1
- package/dist/routes/llm.js +24 -35
- package/dist/routes/llm.js.map +1 -1
- package/dist/routes/setup.js +1 -1
- package/dist/routes/setup.js.map +1 -1
- package/dist/server.d.ts +9 -0
- package/dist/server.js +410 -17
- package/dist/server.js.map +1 -1
- package/dist/services/agent-apps/catalog.js +4 -3
- package/dist/services/agent-apps/catalog.js.map +1 -1
- package/dist/services/agent-apps/index.d.ts +1 -1
- package/dist/services/agent-apps/index.js +1 -1
- package/dist/services/agent-apps/installers/adapter.d.ts +1 -1
- package/dist/services/agent-apps/installers/adapter.js +1 -1
- package/dist/services/agent-apps/installers/shell-script.d.ts +1 -1
- package/dist/services/agent-apps/installers/shell-script.js +3 -3
- package/dist/services/agent-apps/installers/shell-script.js.map +1 -1
- package/dist/services/agent-apps/types.d.ts +2 -2
- package/dist/services/agent-apps/types.js +1 -1
- package/dist/services/app/app-manager.d.ts +24 -1
- package/dist/services/app/app-manager.js +664 -116
- package/dist/services/app/app-manager.js.map +1 -1
- package/dist/services/app/hermes-agent-manager.js +6 -4
- package/dist/services/app/hermes-agent-manager.js.map +1 -1
- package/dist/services/app/provide-resolver.d.ts +29 -0
- package/dist/services/app/provide-resolver.js +112 -0
- package/dist/services/app/provide-resolver.js.map +1 -0
- package/dist/services/capability-endpoint-validator.d.ts +41 -0
- package/dist/services/capability-endpoint-validator.js +104 -0
- package/dist/services/capability-endpoint-validator.js.map +1 -0
- package/dist/services/capability-health.d.ts +16 -0
- package/dist/services/capability-health.js +121 -0
- package/dist/services/capability-health.js.map +1 -0
- package/dist/services/capability-registry.d.ts +106 -0
- package/dist/services/capability-registry.js +313 -0
- package/dist/services/capability-registry.js.map +1 -0
- package/dist/services/connection-apply.d.ts +89 -0
- package/dist/services/connection-apply.js +421 -0
- package/dist/services/connection-apply.js.map +1 -0
- package/dist/services/connection-resolver.d.ts +65 -0
- package/dist/services/connection-resolver.js +281 -0
- package/dist/services/connection-resolver.js.map +1 -0
- package/dist/services/connection-transactor.d.ts +37 -0
- package/dist/services/connection-transactor.js +341 -0
- package/dist/services/connection-transactor.js.map +1 -0
- package/dist/services/instance-manager.d.ts +13 -0
- package/dist/services/instance-manager.js +137 -23
- package/dist/services/instance-manager.js.map +1 -1
- package/dist/services/llm-proxy/index.d.ts +16 -2
- package/dist/services/llm-proxy/index.js +48 -44
- package/dist/services/llm-proxy/index.js.map +1 -1
- package/dist/services/llm-proxy/probe.d.ts +6 -0
- package/dist/services/llm-proxy/probe.js +85 -0
- package/dist/services/llm-proxy/probe.js.map +1 -0
- package/dist/services/llm-proxy/ssrf.d.ts +1 -0
- package/dist/services/llm-proxy/ssrf.js +18 -7
- package/dist/services/llm-proxy/ssrf.js.map +1 -1
- package/dist/services/nomad-manager.js +375 -16
- package/dist/services/nomad-manager.js.map +1 -1
- package/dist/services/process-manager.js +1 -1
- package/dist/services/process-manager.js.map +1 -1
- package/dist/services/runtime/adapters/hermes.d.ts +30 -1
- package/dist/services/runtime/adapters/hermes.js +218 -5
- package/dist/services/runtime/adapters/hermes.js.map +1 -1
- package/dist/services/runtime/adapters/openclaw-mcporter.d.ts +45 -0
- package/dist/services/runtime/adapters/openclaw-mcporter.js +108 -0
- package/dist/services/runtime/adapters/openclaw-mcporter.js.map +1 -0
- package/dist/services/runtime/adapters/openclaw.d.ts +87 -0
- package/dist/services/runtime/adapters/openclaw.js +250 -2
- package/dist/services/runtime/adapters/openclaw.js.map +1 -1
- package/dist/services/runtime/mcp-shims/firewall.d.ts +26 -0
- package/dist/services/runtime/mcp-shims/firewall.js +129 -0
- package/dist/services/runtime/mcp-shims/firewall.js.map +1 -0
- package/dist/services/runtime/mcp-shims/searxng-shim.d.ts +27 -0
- package/dist/services/runtime/mcp-shims/searxng-shim.js +125 -0
- package/dist/services/runtime/mcp-shims/searxng-shim.js.map +1 -0
- package/dist/services/runtime/mcp-shims/write-mcp-entry.d.ts +83 -0
- package/dist/services/runtime/mcp-shims/write-mcp-entry.js +127 -0
- package/dist/services/runtime/mcp-shims/write-mcp-entry.js.map +1 -0
- package/dist/services/runtime/migrations.d.ts +8 -0
- package/dist/services/runtime/migrations.js +100 -0
- package/dist/services/runtime/migrations.js.map +1 -1
- package/dist/services/runtime/types.d.ts +15 -0
- package/dist/services/setup-manager.js +6 -6
- package/dist/services/setup-manager.js.map +1 -1
- package/dist/services/suggestions.d.ts +27 -0
- package/dist/services/suggestions.js +133 -0
- package/dist/services/suggestions.js.map +1 -0
- package/dist/services/task-registry.js +4 -2
- package/dist/services/task-registry.js.map +1 -1
- package/dist/services/telemetry/device-fingerprint.d.ts +1 -1
- package/dist/services/telemetry/device-fingerprint.js +1 -1
- package/dist/services/types-shim.d.ts +16 -0
- package/dist/services/types-shim.js +2 -0
- package/dist/services/types-shim.js.map +1 -0
- package/dist/types.d.ts +171 -1
- package/dist/utils/instance-lock.d.ts +22 -0
- package/dist/utils/instance-lock.js +48 -0
- package/dist/utils/instance-lock.js.map +1 -0
- package/dist/utils/safe-json.js +55 -22
- package/dist/utils/safe-json.js.map +1 -1
- package/install/jishu-install.sh +323 -27
- package/install/jishu-uninstall.sh +353 -20
- package/package.json +3 -1
- package/public/assets/Dashboard-rkWp-CXd.js +1 -0
- package/public/assets/{HermesChatPanel-mFSureyc.js → HermesChatPanel-_GHoklgo.js} +1 -1
- package/public/assets/HermesConfigForm-anDnwUp_.js +4 -0
- package/public/assets/{InitPassword-CVA8wQA6.js → InitPassword-ZU9_-hDr.js} +1 -1
- package/public/assets/InstanceDetail-CN0FH1aw.js +92 -0
- package/public/assets/{Login-BWsZH2mu.js → Login-BItXqYAJ.js} +1 -1
- package/public/assets/NewInstance-BousE6kY.js +1 -0
- package/public/assets/ProviderRecommendations-DFYj7Fb6.js +1 -0
- package/public/assets/Settings-Bttc6QmM.js +1 -0
- package/public/assets/Setup-Bsxx1zgj.js +1 -0
- package/public/assets/{WeixinLoginPanel-CnjR8xMu.js → WeixinLoginPanel-DPZpAKgO.js} +2 -2
- package/public/assets/index-8xZy1z5k.css +1 -0
- package/public/assets/index-Dw3HhUYE.js +19 -0
- package/public/assets/providers-DtNXh9JD.js +1 -0
- package/public/assets/registry-5s2UB6is.js +2 -0
- package/public/index.html +2 -2
- package/scripts/check-app-spec.mjs +443 -0
- package/scripts/check-i18n.mjs +154 -0
- package/scripts/run.sh +4 -4
- package/public/assets/Dashboard-B-JoOjBQ.js +0 -1
- package/public/assets/HermesConfigForm-DvR05LK1.js +0 -4
- package/public/assets/InstanceDetail-DcZW2QGO.js +0 -91
- package/public/assets/NewInstance-BCIrAd86.js +0 -1
- package/public/assets/Settings-xkDcduFz.js +0 -1
- package/public/assets/Setup-Cfuwj4gV.js +0 -1
- package/public/assets/index-CPhVFEsx.css +0 -1
- package/public/assets/index-DQsM6Joa.js +0 -19
- package/public/assets/providers-V-vwrExZ.js +0 -1
- package/public/assets/registry-B4UFJdpA.js +0 -2
package/dist/cli/doctor.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* JishuShell Doctor —
|
|
2
|
+
* JishuShell Doctor — environment diagnostics and auto-fix.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* jishushell doctor
|
|
6
|
-
* jishushell doctor --fix
|
|
4
|
+
* Usage:
|
|
5
|
+
* jishushell doctor inspect all components
|
|
6
|
+
* jishushell doctor --fix inspect and attempt to auto-repair
|
|
7
7
|
*
|
|
8
|
-
* Nomad
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
8
|
+
* Nomad checks follow install/jishu-install.sh and src/services/setup-manager.ts:
|
|
9
|
+
* - binary: ~/.jishushell/bin/nomad (v1.6.5)
|
|
10
|
+
* - config: ~/.jishushell/nomad/nomad.hcl
|
|
11
|
+
* - data dir: ~/.jishushell/nomad/data/
|
|
12
|
+
* - alloc dir: ~/.jishushell/nomad/data/alloc/
|
|
13
|
+
* - log file: ~/.jishushell/nomad/nomad.log
|
|
14
14
|
* - HTTP API: localhost:4646 (ACL enabled)
|
|
15
15
|
* - systemd: /etc/systemd/system/nomad.service
|
|
16
16
|
* - macOS: ~/Library/LaunchAgents/com.jishushell.nomad.plist
|
|
@@ -26,7 +26,7 @@ import { buildDockerClientEnv, managedColimaDockerHost } from "../utils/docker-h
|
|
|
26
26
|
import { loadNomadToken } from "../services/setup-manager.js";
|
|
27
27
|
import { ensureDirContainer, writeConfigFile, writeSecretFile } from "../utils/fs.js";
|
|
28
28
|
import { LOG_FILE as PANEL_LOG_FILE } from "../services/panel-manager.js";
|
|
29
|
-
// ── ANSI
|
|
29
|
+
// ── ANSI colours (auto-disabled when stdout is not a TTY) ───────────────────
|
|
30
30
|
const isTTY = process.stdout.isTTY ?? false;
|
|
31
31
|
const c = {
|
|
32
32
|
bold: (s) => isTTY ? `\x1b[1m${s}\x1b[0m` : s,
|
|
@@ -60,7 +60,7 @@ function detectNomadExternalInterface(loopbackIface) {
|
|
|
60
60
|
return "";
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
// ──
|
|
63
|
+
// ── Internal helpers ────────────────────────────────────────────────────────
|
|
64
64
|
function httpGet(url, timeoutMs = 3000) {
|
|
65
65
|
return new Promise((resolve, reject) => {
|
|
66
66
|
const req = http.get(url, { timeout: timeoutMs }, (res) => {
|
|
@@ -119,7 +119,7 @@ function isPortListening(port) {
|
|
|
119
119
|
return false;
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
-
// ── Nomad
|
|
122
|
+
// ── Nomad path constants (kept in sync with setup-manager.ts) ───────────────
|
|
123
123
|
const NOMAD_BIN_DIR = join(JISHUSHELL_HOME, "bin");
|
|
124
124
|
const NOMAD_LOCAL_BIN = join(NOMAD_BIN_DIR, "nomad");
|
|
125
125
|
const NOMAD_DIR = join(JISHUSHELL_HOME, "nomad");
|
|
@@ -133,7 +133,7 @@ function nomadEnabled() {
|
|
|
133
133
|
return getPanelConfig().service_manager === "nomad";
|
|
134
134
|
}
|
|
135
135
|
// ════════════════════════════════════════════════════════════════════════════
|
|
136
|
-
//
|
|
136
|
+
// Category: system environment
|
|
137
137
|
// ════════════════════════════════════════════════════════════════════════════
|
|
138
138
|
const checkNode = {
|
|
139
139
|
id: "node", label: "Node.js 版本", category: "系统环境", severity: "error",
|
|
@@ -183,10 +183,24 @@ const checkDockerDaemon = {
|
|
|
183
183
|
const colimaEnv = { ...process.env, COLIMA_HOME: colimaDir };
|
|
184
184
|
const dockerEnv = { ...process.env, DOCKER_HOST: managedColimaDockerHost(JISHUSHELL_HOME) };
|
|
185
185
|
execFileSync("mkdir", ["-p", colimaDir], { timeout: 5000 });
|
|
186
|
+
// `--network-host-addresses` (added upstream as b8e7dd5) is the
|
|
187
|
+
// load-bearing flag on macOS: without it Colima won't expose Mac
|
|
188
|
+
// host IPs (e.g. en0) into the VM, so Nomad's docker driver — which
|
|
189
|
+
// requests port publishing on `host_network = "external"` (en0) —
|
|
190
|
+
// can't bind that address inside the VM and silently produces
|
|
191
|
+
// containers with empty `Ports: {}`. The result on the host is
|
|
192
|
+
// ECONNREFUSED on every gateway port and the panel proxy returns
|
|
193
|
+
// 502 forever. Do NOT replace this with `--network-address`; that
|
|
194
|
+
// is a different flag (assigns a static IP to the VM) and does not
|
|
195
|
+
// fix the host-port-publishing path.
|
|
196
|
+
// `--arch aarch64` is explicit so we don't fall back to qemu on
|
|
197
|
+
// Apple Silicon when colima can't auto-detect (5-10x slower).
|
|
186
198
|
execFileSync("colima", ["start", "jishushell",
|
|
187
|
-
"--vm-type", "vz", "--mount-type", "virtiofs",
|
|
188
|
-
"--
|
|
189
|
-
|
|
199
|
+
"--vm-type", "vz", "--mount-type", "virtiofs",
|
|
200
|
+
"--network-host-addresses",
|
|
201
|
+
"--arch", "aarch64",
|
|
202
|
+
"--cpu", "2", "--memory", "4", "--disk", "60",
|
|
203
|
+
], { stdio: "ignore", timeout: 180000, env: colimaEnv });
|
|
190
204
|
await new Promise((r) => setTimeout(r, 3000));
|
|
191
205
|
try {
|
|
192
206
|
execFileSync("docker", ["info"], { stdio: "ignore", timeout: 6000, env: dockerEnv });
|
|
@@ -286,7 +300,7 @@ const checkMemory = {
|
|
|
286
300
|
},
|
|
287
301
|
};
|
|
288
302
|
// ════════════════════════════════════════════════════════════════════════════
|
|
289
|
-
//
|
|
303
|
+
// Category: configuration
|
|
290
304
|
// ════════════════════════════════════════════════════════════════════════════
|
|
291
305
|
const checkConfigDir = {
|
|
292
306
|
id: "config-dir", label: "配置目录", category: "配置", severity: "error",
|
|
@@ -436,7 +450,7 @@ const checkFilePermissions = {
|
|
|
436
450
|
},
|
|
437
451
|
};
|
|
438
452
|
// ════════════════════════════════════════════════════════════════════════════
|
|
439
|
-
//
|
|
453
|
+
// Category: service status
|
|
440
454
|
// ════════════════════════════════════════════════════════════════════════════
|
|
441
455
|
const checkPanel = {
|
|
442
456
|
id: "panel", label: "面板服务", category: "服务状态", severity: "warning",
|
|
@@ -503,20 +517,20 @@ const checkSystemdEnabled = {
|
|
|
503
517
|
},
|
|
504
518
|
};
|
|
505
519
|
// ════════════════════════════════════════════════════════════════════════════
|
|
506
|
-
//
|
|
520
|
+
// Category: Nomad
|
|
507
521
|
// ════════════════════════════════════════════════════════════════════════════
|
|
508
522
|
/**
|
|
509
|
-
*
|
|
510
|
-
*
|
|
511
|
-
*
|
|
512
|
-
*
|
|
523
|
+
* Check the Nomad binary (~/.jishushell/bin/nomad).
|
|
524
|
+
* Source of truth: install/jishu-install.sh _install_nomad_binary().
|
|
525
|
+
* Install path: JISHUSHELL_BIN_DIR=${JISHUSHELL_HOME}/bin/nomad
|
|
526
|
+
* Required version: NOMAD_VERSION=1.6.5
|
|
513
527
|
*/
|
|
514
528
|
const checkNomadBin = {
|
|
515
529
|
id: "nomad-bin", label: "Nomad 二进制", category: "Nomad", severity: "error",
|
|
516
530
|
async check() {
|
|
517
531
|
if (!nomadEnabled())
|
|
518
532
|
return { ok: true, detail: "未使用 Nomad(已跳过)" };
|
|
519
|
-
//
|
|
533
|
+
// Prefer the local install path.
|
|
520
534
|
const binPath = existsSync(NOMAD_LOCAL_BIN) ? NOMAD_LOCAL_BIN : "nomad";
|
|
521
535
|
try {
|
|
522
536
|
const versionOut = execFileSync(binPath, ["version"], { encoding: "utf-8", timeout: 5000 }).trim();
|
|
@@ -528,7 +542,7 @@ const checkNomadBin = {
|
|
|
528
542
|
c.dim(` ├─ 路径: ${binPath === "nomad" ? "(系统 PATH)" : NOMAD_LOCAL_BIN}`),
|
|
529
543
|
c.dim(` └─ ${NOMAD_BIN_DIR} → `) + pathMark,
|
|
530
544
|
];
|
|
531
|
-
//
|
|
545
|
+
// Version check.
|
|
532
546
|
const [maj, min, pat] = version.split(".").map(Number);
|
|
533
547
|
const [rMaj, rMin, rPat] = NOMAD_MIN_VER.split(".").map(Number);
|
|
534
548
|
const vOk = maj > rMaj || (maj === rMaj && (min > rMin || (min === rMin && pat >= rPat)));
|
|
@@ -557,9 +571,9 @@ const checkNomadBin = {
|
|
|
557
571
|
},
|
|
558
572
|
};
|
|
559
573
|
/**
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
* nomad.hcl
|
|
574
|
+
* Check the Nomad config file and data directory.
|
|
575
|
+
* Source of truth: install/jishu-install.sh _ensure_nomad_hcl().
|
|
576
|
+
* Key nomad.hcl fields: data_dir, bind_addr, server, client, acl { enabled = true }.
|
|
563
577
|
*/
|
|
564
578
|
const checkNomadConfig = {
|
|
565
579
|
id: "nomad-config", label: "Nomad 配置", category: "Nomad", severity: "error",
|
|
@@ -579,7 +593,7 @@ const checkNomadConfig = {
|
|
|
579
593
|
if (!existsSync(NOMAD_DATA_DIR) || !existsSync(NOMAD_ALLOC_DIR)) {
|
|
580
594
|
return { ok: false, detail: "数据目录不完整", fixable: true, extras };
|
|
581
595
|
}
|
|
582
|
-
//
|
|
596
|
+
// Validate the ACL config.
|
|
583
597
|
try {
|
|
584
598
|
const content = readFileSync(NOMAD_CONFIG, "utf-8");
|
|
585
599
|
if (!content.includes("acl") || !content.includes("enabled = true")) {
|
|
@@ -594,7 +608,7 @@ const checkNomadConfig = {
|
|
|
594
608
|
ensureDirContainer(NOMAD_DATA_DIR);
|
|
595
609
|
ensureDirContainer(NOMAD_ALLOC_DIR);
|
|
596
610
|
if (!existsSync(NOMAD_CONFIG)) {
|
|
597
|
-
//
|
|
611
|
+
// Template is kept in sync with install/jishu-install.sh _ensure_nomad_hcl().
|
|
598
612
|
const loopbackIface = process.platform === "darwin" ? "lo0" : "lo";
|
|
599
613
|
const externalIface = detectNomadExternalInterface(loopbackIface);
|
|
600
614
|
const externalHostNetworkBlock = externalIface
|
|
@@ -659,9 +673,9 @@ acl {
|
|
|
659
673
|
},
|
|
660
674
|
};
|
|
661
675
|
/**
|
|
662
|
-
*
|
|
663
|
-
*
|
|
664
|
-
*
|
|
676
|
+
* Check the Nomad agent runtime state (port 4646 + HTTP API + ACL token).
|
|
677
|
+
* Source of truth: install/jishu-install.sh start_nomad() / install_nomad_systemd().
|
|
678
|
+
* Start command: sudo nohup nomad agent -config=~/.jishushell/nomad/nomad.hcl > nomad.log 2>&1 &
|
|
665
679
|
*/
|
|
666
680
|
const checkNomadAgent = {
|
|
667
681
|
id: "nomad-agent", label: "Nomad Agent", category: "Nomad", severity: "error",
|
|
@@ -703,7 +717,7 @@ const checkNomadAgent = {
|
|
|
703
717
|
}
|
|
704
718
|
},
|
|
705
719
|
async fix() {
|
|
706
|
-
// 1.
|
|
720
|
+
// 1. Try systemctl.
|
|
707
721
|
try {
|
|
708
722
|
execSync("sudo systemctl start nomad", { stdio: "ignore", timeout: 15000 });
|
|
709
723
|
await new Promise((r) => setTimeout(r, 3000));
|
|
@@ -711,7 +725,7 @@ const checkNomadAgent = {
|
|
|
711
725
|
return { ok: true, message: "Nomad Agent 已通过 systemctl 启动" };
|
|
712
726
|
}
|
|
713
727
|
catch { }
|
|
714
|
-
// 2.
|
|
728
|
+
// 2. Fall back to nohup (matches install/jishu-install.sh start_nomad()).
|
|
715
729
|
if (existsSync(NOMAD_LOCAL_BIN) && existsSync(NOMAD_CONFIG)) {
|
|
716
730
|
try {
|
|
717
731
|
execSync(`sudo nohup "${NOMAD_LOCAL_BIN}" agent -config="${NOMAD_CONFIG}" > "${NOMAD_LOG}" 2>&1 &`, { stdio: "ignore", timeout: 5000 });
|
|
@@ -728,9 +742,104 @@ const checkNomadAgent = {
|
|
|
728
742
|
},
|
|
729
743
|
};
|
|
730
744
|
/**
|
|
731
|
-
*
|
|
732
|
-
*
|
|
733
|
-
*
|
|
745
|
+
* Verify Nomad's docker driver fingerprint is healthy.
|
|
746
|
+
*
|
|
747
|
+
* Boot ordering on macOS: launchd fires every LaunchAgent in parallel, so
|
|
748
|
+
* Nomad often starts before Colima finishes booting the VM. When Nomad
|
|
749
|
+
* polls the docker daemon at startup and gets a connection error, it
|
|
750
|
+
* caches `Drivers.docker.Healthy=False` indefinitely — every subsequent
|
|
751
|
+
* job submission then bounces with `missing drivers: 1`, even after
|
|
752
|
+
* Colima is fully up. The only thing that clears the cache is restarting
|
|
753
|
+
* Nomad. This check detects that exact stuck state and offers a one-click
|
|
754
|
+
* `launchctl kickstart` (or `systemctl restart nomad` on Linux) fix.
|
|
755
|
+
*/
|
|
756
|
+
const checkNomadDockerDriver = {
|
|
757
|
+
id: "nomad-docker-driver", label: "Nomad docker 驱动健康", category: "Nomad", severity: "error",
|
|
758
|
+
async check() {
|
|
759
|
+
if (!nomadEnabled())
|
|
760
|
+
return { ok: true, detail: "未使用 Nomad(已跳过)" };
|
|
761
|
+
if (!isPortListening(4646))
|
|
762
|
+
return { ok: true, detail: "Nomad 未运行(已跳过,先看 Nomad Agent 检查)" };
|
|
763
|
+
loadNomadToken();
|
|
764
|
+
const nomadAddr = process.env.NOMAD_ADDR || "http://127.0.0.1:4646";
|
|
765
|
+
const token = getNomadToken();
|
|
766
|
+
try {
|
|
767
|
+
const nodesResp = token
|
|
768
|
+
? await httpGetWithHeaders(`${nomadAddr}/v1/nodes`, { "X-Nomad-Token": token }, 3000)
|
|
769
|
+
: await httpGet(`${nomadAddr}/v1/nodes`, 3000);
|
|
770
|
+
if (nodesResp.status !== 200)
|
|
771
|
+
return { ok: true, detail: `无法读取 nodes 列表(HTTP ${nodesResp.status}),已跳过` };
|
|
772
|
+
const nodes = JSON.parse(nodesResp.body);
|
|
773
|
+
if (!nodes.length)
|
|
774
|
+
return { ok: true, detail: "没有 Nomad 节点(已跳过)" };
|
|
775
|
+
const nodeId = nodes[0].ID;
|
|
776
|
+
const detailResp = token
|
|
777
|
+
? await httpGetWithHeaders(`${nomadAddr}/v1/node/${nodeId}`, { "X-Nomad-Token": token }, 3000)
|
|
778
|
+
: await httpGet(`${nomadAddr}/v1/node/${nodeId}`, 3000);
|
|
779
|
+
if (detailResp.status !== 200)
|
|
780
|
+
return { ok: true, detail: `无法读取 node 详情(HTTP ${detailResp.status}),已跳过` };
|
|
781
|
+
const node = JSON.parse(detailResp.body);
|
|
782
|
+
const docker = node?.Drivers?.docker ?? {};
|
|
783
|
+
// Healthy → all good. Not detected → docker isn't installed at all,
|
|
784
|
+
// covered by the earlier checkDockerDaemon. Only flag the bad-cache
|
|
785
|
+
// case: Nomad sees docker as detected-but-unhealthy AND we can
|
|
786
|
+
// actually reach docker daemon ourselves.
|
|
787
|
+
if (docker.Healthy === true)
|
|
788
|
+
return { ok: true, detail: "docker 驱动正常" };
|
|
789
|
+
if (docker.Detected !== true)
|
|
790
|
+
return { ok: true, detail: "docker 驱动未启用(已跳过)" };
|
|
791
|
+
const dockerEnv = process.platform === "darwin"
|
|
792
|
+
? { ...process.env, DOCKER_HOST: managedColimaDockerHost(JISHUSHELL_HOME) }
|
|
793
|
+
: process.env;
|
|
794
|
+
let dockerReachable = false;
|
|
795
|
+
try {
|
|
796
|
+
execFileSync("docker", ["info"], { stdio: "ignore", timeout: 6000, env: dockerEnv });
|
|
797
|
+
dockerReachable = true;
|
|
798
|
+
}
|
|
799
|
+
catch { /* leave false */ }
|
|
800
|
+
if (!dockerReachable) {
|
|
801
|
+
return { ok: false, detail: "docker 驱动 unhealthy 且本地 docker 也不通(先修 Docker 守护进程)", fixable: false };
|
|
802
|
+
}
|
|
803
|
+
const desc = docker.HealthDescription?.slice(0, 80) ?? "";
|
|
804
|
+
return {
|
|
805
|
+
ok: false,
|
|
806
|
+
detail: `docker 驱动 unhealthy 但 docker 实际可达(${desc || "Nomad fingerprint 缓存过期"})`,
|
|
807
|
+
hint: "重启 Nomad 让其重新探测 docker 驱动",
|
|
808
|
+
fixable: true,
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
catch (e) {
|
|
812
|
+
return { ok: true, detail: `查询失败(已跳过): ${e.message?.slice?.(0, 60) || e}` };
|
|
813
|
+
}
|
|
814
|
+
},
|
|
815
|
+
async fix() {
|
|
816
|
+
if (process.platform === "darwin") {
|
|
817
|
+
try {
|
|
818
|
+
const uid = process.getuid ? process.getuid() : 0;
|
|
819
|
+
execFileSync("launchctl", ["kickstart", "-k", `gui/${uid}/com.jishushell.nomad`], {
|
|
820
|
+
stdio: "ignore", timeout: 10000,
|
|
821
|
+
});
|
|
822
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
823
|
+
return { ok: true, message: "已 kickstart Nomad,等数秒后再次运行 doctor 验证" };
|
|
824
|
+
}
|
|
825
|
+
catch (e) {
|
|
826
|
+
return { ok: false, message: `launchctl kickstart 失败: ${e.message}` };
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
try {
|
|
830
|
+
execSync("sudo systemctl restart nomad", { stdio: "ignore", timeout: 15000 });
|
|
831
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
832
|
+
return { ok: true, message: "已通过 systemctl 重启 Nomad" };
|
|
833
|
+
}
|
|
834
|
+
catch (e) {
|
|
835
|
+
return { ok: false, message: `systemctl restart 失败: ${e.message}` };
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
};
|
|
839
|
+
/**
|
|
840
|
+
* Check whether nomad.service is registered and enabled for boot.
|
|
841
|
+
* Source of truth: install/jishu-install.sh install_nomad_systemd() / _install_nomad_launchd().
|
|
842
|
+
* Linux service file: /etc/systemd/system/nomad.service
|
|
734
843
|
* macOS plist: ~/Library/LaunchAgents/com.jishushell.nomad.plist
|
|
735
844
|
*/
|
|
736
845
|
const checkNomadSystemd = {
|
|
@@ -768,8 +877,8 @@ const checkNomadSystemd = {
|
|
|
768
877
|
},
|
|
769
878
|
};
|
|
770
879
|
/**
|
|
771
|
-
*
|
|
772
|
-
* API: GET /v1/jobs
|
|
880
|
+
* List jobs in the Nomad cluster and their runtime status.
|
|
881
|
+
* API: GET /v1/jobs (requires X-Nomad-Token).
|
|
773
882
|
*/
|
|
774
883
|
const checkNomadJobs = {
|
|
775
884
|
id: "nomad-jobs", label: "Nomad Jobs", category: "Nomad", severity: "warning",
|
|
@@ -808,7 +917,7 @@ const checkNomadJobs = {
|
|
|
808
917
|
},
|
|
809
918
|
};
|
|
810
919
|
// ════════════════════════════════════════════════════════════════════════════
|
|
811
|
-
//
|
|
920
|
+
// Category: AI components
|
|
812
921
|
// ════════════════════════════════════════════════════════════════════════════
|
|
813
922
|
const checkOpenclawPkg = {
|
|
814
923
|
id: "openclaw-pkg", label: "OpenClaw 运行时", category: "AI 组件", severity: "warning",
|
|
@@ -816,7 +925,7 @@ const checkOpenclawPkg = {
|
|
|
816
925
|
const oclawBin = join(JISHUSHELL_HOME, "packages/openclaw/bin/openclaw");
|
|
817
926
|
const oclawPkgJson = join(JISHUSHELL_HOME, "packages/openclaw/lib/node_modules/openclaw/package.json");
|
|
818
927
|
const localBinOk = existsSync(oclawBin);
|
|
819
|
-
// Docker
|
|
928
|
+
// A Docker image is also a valid runtime (matches setup-manager logic).
|
|
820
929
|
const imageTag = getPanelConfig().openclaw_image || DEFAULT_OPENCLAW_DOCKER_IMAGE;
|
|
821
930
|
let dockerImageOk = false;
|
|
822
931
|
try {
|
|
@@ -878,7 +987,83 @@ const checkOpenclawImage = {
|
|
|
878
987
|
},
|
|
879
988
|
};
|
|
880
989
|
// ════════════════════════════════════════════════════════════════════════════
|
|
881
|
-
//
|
|
990
|
+
// Category: connections (capability registry health)
|
|
991
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
992
|
+
/**
|
|
993
|
+
* Reads `capability-registry.json` and reports the count of providers by
|
|
994
|
+
* status, plus any obvious data-residue (duplicate `(instanceId,
|
|
995
|
+
* capability)` rows, orphan entries pointing at instances that no longer
|
|
996
|
+
* exist, etc.). Aimed at catching the kind of registry drift that would
|
|
997
|
+
* surface in the Connections UI as ghost candidates.
|
|
998
|
+
*/
|
|
999
|
+
const checkConnectionsRegistry = {
|
|
1000
|
+
id: "connections-registry", label: "连接 APP 注册表", category: "AI 组件", severity: "warning",
|
|
1001
|
+
async check() {
|
|
1002
|
+
const registryPath = join(JISHUSHELL_HOME, "apps", "capability-registry.json");
|
|
1003
|
+
if (!existsSync(registryPath)) {
|
|
1004
|
+
return { ok: true, detail: "尚无注册表(第一次启动应用前为正常状态)" };
|
|
1005
|
+
}
|
|
1006
|
+
let raw;
|
|
1007
|
+
try {
|
|
1008
|
+
raw = JSON.parse(readFileSync(registryPath, "utf-8"));
|
|
1009
|
+
}
|
|
1010
|
+
catch (e) {
|
|
1011
|
+
return {
|
|
1012
|
+
ok: false,
|
|
1013
|
+
detail: `capability-registry.json 解析失败:${e.message}`,
|
|
1014
|
+
hint: "备份后删除 ~/.jishushell/apps/capability-registry.json,下次启动会重建",
|
|
1015
|
+
fixable: false,
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
const providers = raw?.providersByCapability ?? {};
|
|
1019
|
+
let running = 0;
|
|
1020
|
+
let stopped = 0;
|
|
1021
|
+
let unknown = 0;
|
|
1022
|
+
const dupKeys = new Set();
|
|
1023
|
+
const dupHits = [];
|
|
1024
|
+
for (const [cap, list] of Object.entries(providers)) {
|
|
1025
|
+
if (!Array.isArray(list))
|
|
1026
|
+
continue;
|
|
1027
|
+
const seen = new Set();
|
|
1028
|
+
for (const e of list) {
|
|
1029
|
+
const status = String(e?.status ?? "unknown");
|
|
1030
|
+
if (status === "running")
|
|
1031
|
+
running += 1;
|
|
1032
|
+
else if (status === "stopped")
|
|
1033
|
+
stopped += 1;
|
|
1034
|
+
else
|
|
1035
|
+
unknown += 1;
|
|
1036
|
+
const key = `${e?.instanceId}::${cap}`;
|
|
1037
|
+
if (seen.has(key) && !dupKeys.has(key)) {
|
|
1038
|
+
dupKeys.add(key);
|
|
1039
|
+
dupHits.push(key);
|
|
1040
|
+
}
|
|
1041
|
+
seen.add(key);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
const summary = `running=${running}, stopped=${stopped}${unknown ? `, unknown=${unknown}` : ""}`;
|
|
1045
|
+
if (dupHits.length > 0) {
|
|
1046
|
+
const sample = dupHits.slice(0, 3).join(", ");
|
|
1047
|
+
return {
|
|
1048
|
+
ok: false,
|
|
1049
|
+
detail: `${summary};检测到 ${dupHits.length} 条重复注册(${sample}${dupHits.length > 3 ? ", …" : ""})`,
|
|
1050
|
+
hint: "通常无害(dedupe 在读取时进行),如需清理可在面板中重启相关实例触发重新注册",
|
|
1051
|
+
fixable: false,
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
if (unknown > 0) {
|
|
1055
|
+
return {
|
|
1056
|
+
ok: false,
|
|
1057
|
+
detail: `${summary};存在 ${unknown} 条 unknown 状态条目`,
|
|
1058
|
+
hint: "重启 jishushell 会让启动重建把它们标记为 stopped/running",
|
|
1059
|
+
fixable: false,
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
return { ok: true, detail: summary };
|
|
1063
|
+
},
|
|
1064
|
+
};
|
|
1065
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
1066
|
+
// Category: network
|
|
882
1067
|
// ════════════════════════════════════════════════════════════════════════════
|
|
883
1068
|
const checkNetwork = {
|
|
884
1069
|
id: "network", label: "网络连通性", category: "网络", severity: "warning",
|
|
@@ -899,39 +1084,41 @@ const checkNetwork = {
|
|
|
899
1084
|
},
|
|
900
1085
|
};
|
|
901
1086
|
// ════════════════════════════════════════════════════════════════════════════
|
|
902
|
-
//
|
|
1087
|
+
// All check items (in execution order).
|
|
903
1088
|
// ════════════════════════════════════════════════════════════════════════════
|
|
904
1089
|
export const ALL_CHECKS = [
|
|
905
|
-
//
|
|
1090
|
+
// System environment
|
|
906
1091
|
checkNode,
|
|
907
1092
|
checkDockerBin,
|
|
908
1093
|
checkDockerDaemon,
|
|
909
1094
|
checkDockerGroup,
|
|
910
1095
|
checkDiskSpace,
|
|
911
1096
|
checkMemory,
|
|
912
|
-
//
|
|
1097
|
+
// Configuration
|
|
913
1098
|
checkConfigDir,
|
|
914
1099
|
checkPanelConfig,
|
|
915
1100
|
checkJwtSecret,
|
|
916
1101
|
checkAuth,
|
|
917
1102
|
checkFilePermissions,
|
|
918
|
-
//
|
|
1103
|
+
// Service status
|
|
919
1104
|
checkPanel,
|
|
920
1105
|
checkSystemdEnabled,
|
|
921
1106
|
// Nomad
|
|
922
1107
|
checkNomadBin,
|
|
923
1108
|
checkNomadConfig,
|
|
924
1109
|
checkNomadAgent,
|
|
1110
|
+
checkNomadDockerDriver,
|
|
925
1111
|
checkNomadSystemd,
|
|
926
1112
|
checkNomadJobs,
|
|
927
|
-
// AI
|
|
1113
|
+
// AI components
|
|
928
1114
|
checkOpenclawPkg,
|
|
929
1115
|
checkOpenclawImage,
|
|
930
|
-
|
|
1116
|
+
checkConnectionsRegistry,
|
|
1117
|
+
// Network
|
|
931
1118
|
checkNetwork,
|
|
932
1119
|
];
|
|
933
1120
|
// ════════════════════════════════════════════════════════════════════════════
|
|
934
|
-
//
|
|
1121
|
+
// Output formatting
|
|
935
1122
|
// ════════════════════════════════════════════════════════════════════════════
|
|
936
1123
|
function formatRow(check, result) {
|
|
937
1124
|
const isWarn = !result.ok && check.severity === "warning";
|
|
@@ -941,10 +1128,10 @@ function formatRow(check, result) {
|
|
|
941
1128
|
return ` ${icon} ${label}${tag} ${c.dim(result.detail)}`;
|
|
942
1129
|
}
|
|
943
1130
|
// ════════════════════════════════════════════════════════════════════════════
|
|
944
|
-
//
|
|
1131
|
+
// Main entry
|
|
945
1132
|
// ════════════════════════════════════════════════════════════════════════════
|
|
946
1133
|
/**
|
|
947
|
-
*
|
|
1134
|
+
* Run every diagnostic check and return whether all error-level checks pass.
|
|
948
1135
|
*/
|
|
949
1136
|
export async function runDoctorChecks(opts = {}) {
|
|
950
1137
|
const { fix = false } = opts;
|
|
@@ -997,7 +1184,7 @@ export async function runDoctorChecks(opts = {}) {
|
|
|
997
1184
|
}
|
|
998
1185
|
log("");
|
|
999
1186
|
}
|
|
1000
|
-
//
|
|
1187
|
+
// Summary
|
|
1001
1188
|
const errors = ALL_CHECKS.filter((ch) => !results.get(ch.id).ok && ch.severity === "error");
|
|
1002
1189
|
const warnings = ALL_CHECKS.filter((ch) => !results.get(ch.id).ok && ch.severity === "warning");
|
|
1003
1190
|
log(c.dim(" ────────────────────────────────────────────────"));
|
|
@@ -1031,7 +1218,7 @@ Options:
|
|
|
1031
1218
|
`);
|
|
1032
1219
|
}
|
|
1033
1220
|
// ════════════════════════════════════════════════════════════════════════════
|
|
1034
|
-
// AI
|
|
1221
|
+
// AI diagnostics
|
|
1035
1222
|
// ════════════════════════════════════════════════════════════════════════════
|
|
1036
1223
|
/** Strip ANSI escape codes from a string. */
|
|
1037
1224
|
function stripAnsi(s) {
|