jishushell 0.4.24-beta.2 → 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.
Files changed (167) hide show
  1. package/INSTALL-NOTICE +11 -0
  2. package/apps/browserless-chromium-container.yaml +78 -0
  3. package/apps/hermes-container.yaml +36 -2
  4. package/apps/ollama-binary.yaml +45 -8
  5. package/apps/ollama-cpu-container.yaml +8 -1
  6. package/apps/ollama-with-hollama-binary.yaml +45 -8
  7. package/apps/openclaw-binary.yaml +30 -1
  8. package/apps/openclaw-container.yaml +37 -2
  9. package/apps/openclaw-with-ollama-container.yaml +11 -2
  10. package/apps/openclaw-with-searxng-container.yaml +22 -2
  11. package/apps/openwebui-container.yaml +45 -1
  12. package/apps/playwright-container.yaml +7 -1
  13. package/apps/searxng-container.yaml +54 -4
  14. package/dist/cli/app.js +12 -2
  15. package/dist/cli/app.js.map +1 -1
  16. package/dist/cli/doctor.d.ts +12 -12
  17. package/dist/cli/doctor.js +242 -55
  18. package/dist/cli/doctor.js.map +1 -1
  19. package/dist/cli/llm.d.ts +4 -3
  20. package/dist/cli/llm.js +4 -3
  21. package/dist/cli/llm.js.map +1 -1
  22. package/dist/cli/panel.d.ts +6 -5
  23. package/dist/cli/panel.js +10 -9
  24. package/dist/cli/panel.js.map +1 -1
  25. package/dist/control.d.ts +7 -6
  26. package/dist/control.js +7 -6
  27. package/dist/control.js.map +1 -1
  28. package/dist/routes/agent-apps.d.ts +1 -1
  29. package/dist/routes/agent-apps.js +1 -1
  30. package/dist/routes/apps.js +44 -11
  31. package/dist/routes/apps.js.map +1 -1
  32. package/dist/routes/auth.js +3 -0
  33. package/dist/routes/auth.js.map +1 -1
  34. package/dist/routes/instances.js +787 -16
  35. package/dist/routes/instances.js.map +1 -1
  36. package/dist/routes/llm.js +24 -35
  37. package/dist/routes/llm.js.map +1 -1
  38. package/dist/routes/setup.js +1 -1
  39. package/dist/routes/setup.js.map +1 -1
  40. package/dist/server.d.ts +9 -0
  41. package/dist/server.js +410 -17
  42. package/dist/server.js.map +1 -1
  43. package/dist/services/agent-apps/catalog.js +4 -3
  44. package/dist/services/agent-apps/catalog.js.map +1 -1
  45. package/dist/services/agent-apps/index.d.ts +1 -1
  46. package/dist/services/agent-apps/index.js +1 -1
  47. package/dist/services/agent-apps/installers/adapter.d.ts +1 -1
  48. package/dist/services/agent-apps/installers/adapter.js +1 -1
  49. package/dist/services/agent-apps/installers/shell-script.d.ts +1 -1
  50. package/dist/services/agent-apps/installers/shell-script.js +3 -3
  51. package/dist/services/agent-apps/installers/shell-script.js.map +1 -1
  52. package/dist/services/agent-apps/types.d.ts +2 -2
  53. package/dist/services/agent-apps/types.js +1 -1
  54. package/dist/services/app/app-manager.d.ts +24 -1
  55. package/dist/services/app/app-manager.js +490 -102
  56. package/dist/services/app/app-manager.js.map +1 -1
  57. package/dist/services/app/hermes-agent-manager.js +6 -4
  58. package/dist/services/app/hermes-agent-manager.js.map +1 -1
  59. package/dist/services/app/provide-resolver.d.ts +29 -0
  60. package/dist/services/app/provide-resolver.js +112 -0
  61. package/dist/services/app/provide-resolver.js.map +1 -0
  62. package/dist/services/capability-endpoint-validator.d.ts +41 -0
  63. package/dist/services/capability-endpoint-validator.js +104 -0
  64. package/dist/services/capability-endpoint-validator.js.map +1 -0
  65. package/dist/services/capability-health.d.ts +16 -0
  66. package/dist/services/capability-health.js +121 -0
  67. package/dist/services/capability-health.js.map +1 -0
  68. package/dist/services/capability-registry.d.ts +106 -0
  69. package/dist/services/capability-registry.js +313 -0
  70. package/dist/services/capability-registry.js.map +1 -0
  71. package/dist/services/connection-apply.d.ts +89 -0
  72. package/dist/services/connection-apply.js +421 -0
  73. package/dist/services/connection-apply.js.map +1 -0
  74. package/dist/services/connection-resolver.d.ts +65 -0
  75. package/dist/services/connection-resolver.js +281 -0
  76. package/dist/services/connection-resolver.js.map +1 -0
  77. package/dist/services/connection-transactor.d.ts +37 -0
  78. package/dist/services/connection-transactor.js +341 -0
  79. package/dist/services/connection-transactor.js.map +1 -0
  80. package/dist/services/instance-manager.d.ts +13 -0
  81. package/dist/services/instance-manager.js +137 -23
  82. package/dist/services/instance-manager.js.map +1 -1
  83. package/dist/services/llm-proxy/index.d.ts +16 -2
  84. package/dist/services/llm-proxy/index.js +48 -44
  85. package/dist/services/llm-proxy/index.js.map +1 -1
  86. package/dist/services/llm-proxy/probe.d.ts +6 -0
  87. package/dist/services/llm-proxy/probe.js +85 -0
  88. package/dist/services/llm-proxy/probe.js.map +1 -0
  89. package/dist/services/llm-proxy/ssrf.d.ts +1 -0
  90. package/dist/services/llm-proxy/ssrf.js +18 -7
  91. package/dist/services/llm-proxy/ssrf.js.map +1 -1
  92. package/dist/services/nomad-manager.js +375 -16
  93. package/dist/services/nomad-manager.js.map +1 -1
  94. package/dist/services/process-manager.js +1 -1
  95. package/dist/services/process-manager.js.map +1 -1
  96. package/dist/services/runtime/adapters/hermes.d.ts +30 -1
  97. package/dist/services/runtime/adapters/hermes.js +218 -5
  98. package/dist/services/runtime/adapters/hermes.js.map +1 -1
  99. package/dist/services/runtime/adapters/openclaw-mcporter.d.ts +45 -0
  100. package/dist/services/runtime/adapters/openclaw-mcporter.js +108 -0
  101. package/dist/services/runtime/adapters/openclaw-mcporter.js.map +1 -0
  102. package/dist/services/runtime/adapters/openclaw.d.ts +87 -0
  103. package/dist/services/runtime/adapters/openclaw.js +250 -2
  104. package/dist/services/runtime/adapters/openclaw.js.map +1 -1
  105. package/dist/services/runtime/mcp-shims/firewall.d.ts +26 -0
  106. package/dist/services/runtime/mcp-shims/firewall.js +129 -0
  107. package/dist/services/runtime/mcp-shims/firewall.js.map +1 -0
  108. package/dist/services/runtime/mcp-shims/searxng-shim.d.ts +27 -0
  109. package/dist/services/runtime/mcp-shims/searxng-shim.js +125 -0
  110. package/dist/services/runtime/mcp-shims/searxng-shim.js.map +1 -0
  111. package/dist/services/runtime/mcp-shims/write-mcp-entry.d.ts +83 -0
  112. package/dist/services/runtime/mcp-shims/write-mcp-entry.js +127 -0
  113. package/dist/services/runtime/mcp-shims/write-mcp-entry.js.map +1 -0
  114. package/dist/services/runtime/migrations.d.ts +8 -0
  115. package/dist/services/runtime/migrations.js +100 -0
  116. package/dist/services/runtime/migrations.js.map +1 -1
  117. package/dist/services/runtime/types.d.ts +15 -0
  118. package/dist/services/setup-manager.js +6 -6
  119. package/dist/services/setup-manager.js.map +1 -1
  120. package/dist/services/suggestions.d.ts +27 -0
  121. package/dist/services/suggestions.js +133 -0
  122. package/dist/services/suggestions.js.map +1 -0
  123. package/dist/services/task-registry.js +4 -2
  124. package/dist/services/task-registry.js.map +1 -1
  125. package/dist/services/telemetry/device-fingerprint.d.ts +1 -1
  126. package/dist/services/telemetry/device-fingerprint.js +1 -1
  127. package/dist/services/types-shim.d.ts +16 -0
  128. package/dist/services/types-shim.js +2 -0
  129. package/dist/services/types-shim.js.map +1 -0
  130. package/dist/types.d.ts +169 -1
  131. package/dist/utils/instance-lock.d.ts +22 -0
  132. package/dist/utils/instance-lock.js +48 -0
  133. package/dist/utils/instance-lock.js.map +1 -0
  134. package/dist/utils/safe-json.js +55 -22
  135. package/dist/utils/safe-json.js.map +1 -1
  136. package/install/jishu-install.sh +323 -26
  137. package/install/jishu-uninstall.sh +353 -20
  138. package/package.json +3 -1
  139. package/public/assets/Dashboard-rkWp-CXd.js +1 -0
  140. package/public/assets/{HermesChatPanel-D6JI6lLY.js → HermesChatPanel-_GHoklgo.js} +1 -1
  141. package/public/assets/HermesConfigForm-anDnwUp_.js +4 -0
  142. package/public/assets/{InitPassword-CFTKsED4.js → InitPassword-ZU9_-hDr.js} +1 -1
  143. package/public/assets/InstanceDetail-CN0FH1aw.js +92 -0
  144. package/public/assets/{Login-KB9qrtM0.js → Login-BItXqYAJ.js} +1 -1
  145. package/public/assets/NewInstance-BousE6kY.js +1 -0
  146. package/public/assets/ProviderRecommendations-DFYj7Fb6.js +1 -0
  147. package/public/assets/Settings-Bttc6QmM.js +1 -0
  148. package/public/assets/Setup-Bsxx1zgj.js +1 -0
  149. package/public/assets/{WeixinLoginPanel-gca0QTic.js → WeixinLoginPanel-DPZpAKgO.js} +2 -2
  150. package/public/assets/index-8xZy1z5k.css +1 -0
  151. package/public/assets/index-Dw3HhUYE.js +19 -0
  152. package/public/assets/providers-DtNXh9JD.js +1 -0
  153. package/public/assets/registry-5s2UB6is.js +2 -0
  154. package/public/index.html +2 -2
  155. package/scripts/check-app-spec.mjs +443 -0
  156. package/scripts/check-i18n.mjs +154 -0
  157. package/scripts/run.sh +4 -4
  158. package/public/assets/Dashboard-rh9qpYRR.js +0 -1
  159. package/public/assets/HermesConfigForm-DcbSemaj.js +0 -4
  160. package/public/assets/InstanceDetail-BhNIKA6Z.js +0 -91
  161. package/public/assets/NewInstance-CxkO8Hlq.js +0 -1
  162. package/public/assets/Settings-BVWJvOkU.js +0 -1
  163. package/public/assets/Setup-X-lzuaUT.js +0 -1
  164. package/public/assets/index-C8B0cFJM.js +0 -19
  165. package/public/assets/index-CPhVFEsx.css +0 -1
  166. package/public/assets/providers-V-vwrExZ.js +0 -1
  167. package/public/assets/registry-fVUSujib.js +0 -2
@@ -1,6 +1,7 @@
1
1
  import { request as httpRequest } from "http";
2
2
  import { request as httpsRequest } from "https";
3
3
  import { lookup } from "dns/promises";
4
+ import { isIP } from "net";
4
5
  import { Readable } from "stream";
5
6
  // SSRF protection — private IP ranges
6
7
  const PRIVATE_HOST_PATTERNS = [
@@ -18,8 +19,14 @@ const PRIVATE_HOST_PATTERNS = [
18
19
  ];
19
20
  export const LOCAL_PROVIDER_IDS = new Set(["ollama", "vllm", "sglang", "custom"]);
20
21
  const LOCALHOST_HOSTS = new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
21
- function isPrivateHost(hostname) {
22
+ function isLinkLocalHost(hostname) {
23
+ const cleaned = hostname.replace(/^\[|\]$/g, "").toLowerCase();
24
+ return cleaned.startsWith("169.254.") || cleaned.startsWith("fe80:");
25
+ }
26
+ export function isPrivateHost(hostname) {
22
27
  const cleaned = hostname.replace(/^\[|\]$/g, "");
28
+ if (isIP(cleaned) && isPrivateIP(cleaned))
29
+ return true;
23
30
  return PRIVATE_HOST_PATTERNS.some((p) => p.test(cleaned));
24
31
  }
25
32
  function isPrivateIP(ip) {
@@ -85,21 +92,22 @@ function isPrivateIP(ip) {
85
92
  export async function validateUpstreamUrl(urlStr, providerId) {
86
93
  try {
87
94
  const url = new URL(urlStr);
95
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
96
+ throw new Error("upstream URL must use http:// or https://");
97
+ }
88
98
  const isLocalHost = LOCALHOST_HOSTS.has(url.hostname);
89
99
  if (LOCAL_PROVIDER_IDS.has(providerId)) {
90
100
  // Local AI providers (ollama/vllm/sglang): allow localhost and private
91
101
  // networks, but block cloud metadata endpoints (169.254.x.x link-local).
92
102
  if (isLocalHost)
93
103
  return null;
94
- if (url.hostname.startsWith("169.254.")) {
104
+ if (isLinkLocalHost(url.hostname)) {
95
105
  throw new Error(`Local provider URL must not target link-local/metadata address ${url.hostname}`);
96
106
  }
97
107
  // Allow private LAN addresses (192.168.x, 10.x, 172.16-31.x) — admin explicitly configured
98
108
  return null;
99
109
  }
100
- // Even for remote providers, if destination is localhost, skip DNS pinning
101
- // but still allow it (reverse-proxy setups). Do NOT skip SSRF for
102
- // non-localhost destinations just because providerId looks local.
110
+ // Preserve explicit localhost reverse-proxy setups for remote providers.
103
111
  if (isLocalHost)
104
112
  return null;
105
113
  if (isPrivateHost(url.hostname)) {
@@ -119,9 +127,12 @@ export async function validateUpstreamUrl(urlStr, providerId) {
119
127
  }
120
128
  }
121
129
  catch (err) {
122
- if (err.message.includes("private address") || err.message.includes("private IP"))
130
+ if (err.message.includes("private address") ||
131
+ err.message.includes("private IP") ||
132
+ err.message.includes("must use http") ||
133
+ err.message.includes("link-local"))
123
134
  throw err;
124
- throw new Error(`invalid upstream URL: ${urlStr}`);
135
+ throw new Error("invalid upstream URL");
125
136
  }
126
137
  }
127
138
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ssrf.js","sourceRoot":"","sources":["../../../src/services/llm-proxy/ssrf.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ssrf.js","sourceRoot":"","sources":["../../../src/services/llm-proxy/ssrf.ts"],"names":[],"mappings":"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"}
@@ -415,6 +415,20 @@ function getInstanceAgentType(instanceId) {
415
415
  }
416
416
  }
417
417
  function wrapNomadJob(jid, groupName, task) {
418
+ // Adapters declare port reservations on `task.Resources.Networks` (legacy
419
+ // schema). The docker driver's `Config.ports = [<label>]` lookup, however,
420
+ // resolves labels against the TaskGroup-level `Networks` block. Move (not
421
+ // copy) the network block so the docker driver can find the port and so
422
+ // HostNetwork ("external") is honored — without this, ports publish to
423
+ // 127.0.0.1 by default. Keeping it on both levels would make Nomad reject
424
+ // the job with "port label already in use".
425
+ const taskNetworks = Array.isArray(task?.Resources?.Networks)
426
+ ? task.Resources.Networks
427
+ : [];
428
+ const groupNetworks = taskNetworks.length > 0 ? taskNetworks.map((n) => ({ ...n })) : undefined;
429
+ if (groupNetworks && task?.Resources && typeof task.Resources === "object") {
430
+ delete task.Resources.Networks;
431
+ }
418
432
  return {
419
433
  Job: {
420
434
  ID: jid,
@@ -425,6 +439,7 @@ function wrapNomadJob(jid, groupName, task) {
425
439
  TaskGroups: [{
426
440
  Name: groupName,
427
441
  Count: 1,
442
+ ...(groupNetworks ? { Networks: groupNetworks } : {}),
428
443
  RestartPolicy: {
429
444
  Attempts: 3,
430
445
  Interval: 300000000000,
@@ -457,6 +472,7 @@ async function buildJob(instanceId) {
457
472
  if (legacyManager) {
458
473
  const runtime = legacyManager.buildRuntime(instanceId);
459
474
  const task = legacyManager.buildNomadTask(instanceId, runtime, jid);
475
+ await injectConnectionsRuntimeEnv(instanceId, task);
460
476
  return wrapNomadJob(jid, legacyManager.nomadTaskGroupName(), task);
461
477
  }
462
478
  // Pure adapter dispatch — no more `isHermesInstance()` / kind literals.
@@ -466,11 +482,54 @@ async function buildJob(instanceId) {
466
482
  throw new Error(`Runtime adapter "${agentType}" does not implement buildNomadTask(); cannot schedule Nomad job`);
467
483
  }
468
484
  const task = await adapter.buildNomadTask(instanceId);
485
+ await injectConnectionsRuntimeEnv(instanceId, task);
469
486
  // Task group name mirrors the agentType. Log/status helpers resolve the
470
487
  // Nomad task name via resolveTaskName(instanceId) → adapter.nomadTaskName.
471
488
  const groupName = agentType;
472
489
  return wrapNomadJob(jid, groupName, task);
473
490
  }
491
+ /**
492
+ * Re-resolve `instance.connections` against the live capability registry
493
+ * and merge the resulting env into the freshly-built Nomad task. Idempotent
494
+ * — empty meta.connections short-circuits to a no-op.
495
+ *
496
+ * Resolving at start time (rather than reading the frozen
497
+ * `instance.json["connections-env"]` written by PUT /connections) means
498
+ * provider port / address changes in the registry propagate on next
499
+ * restart without requiring the user to re-bind. Failures here are
500
+ * logged but never block start: a missing required binding still surfaces
501
+ * via the Connections UI status badge.
502
+ */
503
+ async function injectConnectionsRuntimeEnv(instanceId, task) {
504
+ try {
505
+ const meta = getInstance(instanceId);
506
+ const connections = meta?.connections;
507
+ if (!meta || !connections || Object.keys(connections).length === 0)
508
+ return;
509
+ const { loadCapabilitySpecForLegacyInstance } = await import("./runtime/migrations.js");
510
+ const spec = loadCapabilitySpecForLegacyInstance(meta);
511
+ if (!spec)
512
+ return;
513
+ const { resolveConnections } = await import("./connection-resolver.js");
514
+ const { resolved } = resolveConnections(spec, { connections }, "preCreate");
515
+ if (resolved.length === 0)
516
+ return;
517
+ const { RUNTIME_HOOKS } = await import("./connection-apply.js");
518
+ const merged = {};
519
+ for (const binding of resolved) {
520
+ const hook = RUNTIME_HOOKS[binding.category];
521
+ if (!hook)
522
+ continue;
523
+ Object.assign(merged, await hook(meta, binding));
524
+ }
525
+ if (Object.keys(merged).length === 0)
526
+ return;
527
+ task.Env = { ...(task.Env ?? {}), ...merged };
528
+ }
529
+ catch (e) {
530
+ console.warn(`[nomad] connections runtime env merge failed for ${instanceId}: ${e?.message ?? e}`);
531
+ }
532
+ }
474
533
  async function getRunningAlloc(instanceId) {
475
534
  const jid = jobId(instanceId);
476
535
  try {
@@ -607,7 +666,7 @@ export async function getStatus(instanceId) {
607
666
  async function phaseRunningCheck(instanceId) {
608
667
  const status = await getStatus(instanceId);
609
668
  if (status.status === "running") {
610
- return { ok: false, error: "Instance is already running" };
669
+ return { ok: false, error: "Instance is already running", code: "INSTANCE_ALREADY_RUNNING" };
611
670
  }
612
671
  return { ok: true };
613
672
  }
@@ -674,6 +733,81 @@ async function phasePreStartHook(adapter, instanceId) {
674
733
  return { ok: false, error: e?.message || String(e) };
675
734
  }
676
735
  }
736
+ /**
737
+ * §17 / PR 9 — re-render adapter-managed connection config from the
738
+ * current capability registry before each instance start.
739
+ *
740
+ * Without this hook, env values (like `SEARCH_API_BASE_URL` =
741
+ * `http://<host>:<port>/search`) are frozen into the adapter's config
742
+ * files at PUT /connections time. When the host IP changes (DHCP
743
+ * renewal, pi reboot picking up a new lease, network move) or a
744
+ * provider gets re-deployed at a different host:port, the consumer
745
+ * keeps trying the stale address and search/llm/etc. silently fail.
746
+ *
747
+ * What this does on every start:
748
+ * 1. Read connections from instance.json
749
+ * 2. Re-resolve them in `runtime` mode (tolerant: ambiguous/missing
750
+ * becomes empty resolved instead of throwing — start should still
751
+ * proceed even if one binding can't be re-rendered)
752
+ * 3. Collect env via the same persist hooks PUT /connections uses
753
+ * 4. Call adapter.applyConnectionEnv with the fresh env so the
754
+ * adapter rewrites its config files (mcp_servers / openclaw.json /
755
+ * etc.) with the current address
756
+ *
757
+ * Failures here are logged but never block start: a stale config is
758
+ * better than no start. If something is genuinely wrong with the
759
+ * registry, the user will see a connection error in the UI on next
760
+ * use — at which point they can re-bind manually.
761
+ */
762
+ async function phaseRefreshConnections(adapter, instanceId) {
763
+ if (!adapter.applyConnectionEnv)
764
+ return;
765
+ try {
766
+ const meta = getInstance(instanceId);
767
+ const connections = meta?.connections;
768
+ if (!meta || !connections || Object.keys(connections).length === 0)
769
+ return;
770
+ const { loadCapabilitySpecForLegacyInstance } = await import("./runtime/migrations.js");
771
+ const spec = loadCapabilitySpecForLegacyInstance(meta);
772
+ if (!spec)
773
+ return;
774
+ const { resolveConnections } = await import("./connection-resolver.js");
775
+ const { resolved } = resolveConnections(spec, { connections }, "preCreate");
776
+ if (resolved.length === 0)
777
+ return;
778
+ const { PERSIST_HOOKS } = await import("./connection-apply.js");
779
+ const merged = {};
780
+ const seenEnvKeys = new Set();
781
+ // Accumulate env across all resolved bindings via a stub
782
+ // writeConnectionEnv that just collects into `merged`. We don't
783
+ // run the real persist hooks here because those would re-write
784
+ // generic-app `connections-env` (already handled by
785
+ // injectConnectionsRuntimeEnv); we only want the env so we can
786
+ // pass it to adapter.applyConnectionEnv below.
787
+ const stubCtx = {
788
+ registry: await import("./capability-registry.js"),
789
+ adapter: { applyConnectionEnv: undefined },
790
+ async writeConnectionEnv(_inst, env) {
791
+ for (const [k, v] of Object.entries(env)) {
792
+ merged[k] = v;
793
+ seenEnvKeys.add(k);
794
+ }
795
+ },
796
+ };
797
+ for (const binding of resolved) {
798
+ const hook = PERSIST_HOOKS[binding.category];
799
+ if (!hook)
800
+ continue;
801
+ await hook(meta, binding, stubCtx);
802
+ }
803
+ if (seenEnvKeys.size === 0)
804
+ return;
805
+ await adapter.applyConnectionEnv(instanceId, merged);
806
+ }
807
+ catch (e) {
808
+ console.warn(`[nomad] connections refresh failed for ${instanceId}: ${e?.message ?? e}`);
809
+ }
810
+ }
677
811
  /**
678
812
  * Phase 5: submit to Nomad with a single retry on port race. Between our
679
813
  * earlier host probe and Docker's actual bind another process could have
@@ -741,8 +875,12 @@ export async function startInstance(instanceId) {
741
875
  return { ok: false, phase, ...rest };
742
876
  };
743
877
  const running = await phaseRunningCheck(instanceId);
744
- if (!running.ok)
745
- return failed("running_check", { error: running.error });
878
+ if (!running.ok) {
879
+ const extra = { error: running.error };
880
+ if (running.code)
881
+ extra.code = running.code;
882
+ return failed("running_check", extra);
883
+ }
746
884
  const legacyManager = await getLegacyAppManager(instanceId);
747
885
  if (legacyManager) {
748
886
  const prep = await legacyManager.prepareStart(instanceId);
@@ -761,6 +899,11 @@ export async function startInstance(instanceId) {
761
899
  const home = await phaseHomeConflict(instanceId, adapter.findInstancesSharingHome?.(instanceId) ?? []);
762
900
  if (!home.ok)
763
901
  return failed("home_conflict", { error: home.error });
902
+ // PR 9 — refresh adapter-managed connection config from current
903
+ // capability registry before adapter pre-start. Best-effort: never
904
+ // blocks start (any failure is logged and we proceed with the
905
+ // existing on-disk config). See phaseRefreshConnections doc.
906
+ await phaseRefreshConnections(adapter, instanceId);
764
907
  const hook = await phasePreStartHook(adapter, instanceId);
765
908
  if (!hook.ok) {
766
909
  const extra = { error: hook.error };
@@ -777,12 +920,65 @@ export async function startInstance(instanceId) {
777
920
  const submit = await phaseSubmit(instanceId, port.portAllocation);
778
921
  if (!submit.ok)
779
922
  return failed("submit", { error: submit.error });
923
+ // Auto-register capability providers for legacy instances so they appear
924
+ // in the Connections UI alongside app-installed apps. App-dir installed
925
+ // apps short-circuit at the top of this function and don't reach here.
926
+ await registerLegacyCapabilitiesTopLevel(instanceId);
780
927
  return {
781
928
  ok: true,
782
929
  eval_id: submit.evalId,
783
930
  ...(submit.portAllocation ? { port_allocation: submit.portAllocation } : {}),
784
931
  };
785
932
  }
933
+ /**
934
+ * Best-effort capability registration for legacy (non-app-installed)
935
+ * hermes/openclaw instances. Loaded synthetic spec via the migrations
936
+ * helper; failures are logged but never block start/stop.
937
+ */
938
+ async function registerLegacyCapabilitiesTopLevel(instanceId) {
939
+ try {
940
+ const meta = getInstance(instanceId);
941
+ if (!meta)
942
+ return;
943
+ const { loadCapabilitySpecForLegacyInstance } = await import("./runtime/migrations.js");
944
+ const synthSpec = loadCapabilitySpecForLegacyInstance(meta);
945
+ if (!synthSpec?.provides?.length)
946
+ return;
947
+ // The synthetic spec's `name` is the yaml template's display name
948
+ // ("Hermes Agent" / "OpenClaw Container"). For Connections-tab UX we
949
+ // want the candidate to surface the user's instance name (e.g. "h",
950
+ // "claw11"), so override before handing it to registerCapabilities.
951
+ const instanceName = typeof meta.name === "string" && meta.name
952
+ ? meta.name
953
+ : instanceId;
954
+ const namedSpec = { ...synthSpec, name: instanceName };
955
+ // The synthetic spec has `tasks: []`, so `resolveProvideEndpoint`
956
+ // can't compute the actual gateway port and falls back to the yaml's
957
+ // declared port (e.g. openclaw default 18789). Legacy instances may
958
+ // have been allocated a different port at creation time (port
959
+ // collision avoidance). Pass the live `getGatewayPort` so the
960
+ // capability registry advertises the port consumers can actually
961
+ // reach.
962
+ const portOverride = getGatewayPort(instanceId);
963
+ const { registerCapabilities } = await import("./app/app-manager.js");
964
+ registerCapabilities(instanceId, namedSpec, portOverride > 0 ? portOverride : undefined);
965
+ }
966
+ catch (e) {
967
+ console.warn(`[legacy-capabilities] register failed for ${instanceId}: ${e?.message ?? e}`);
968
+ }
969
+ }
970
+ async function unregisterLegacyCapabilitiesTopLevel(instanceId, purge) {
971
+ try {
972
+ const { markCapabilitiesStopped, unregisterCapabilities } = await import("./app/app-manager.js");
973
+ if (purge)
974
+ unregisterCapabilities(instanceId);
975
+ else
976
+ markCapabilitiesStopped(instanceId);
977
+ }
978
+ catch (e) {
979
+ console.warn(`[legacy-capabilities] unregister failed for ${instanceId}: ${e?.message ?? e}`);
980
+ }
981
+ }
786
982
  export async function stopInstance(instanceId, purge = false) {
787
983
  const jid = jobId(instanceId);
788
984
  try {
@@ -794,10 +990,14 @@ export async function stopInstance(instanceId, purge = false) {
794
990
  }
795
991
  catch { /* ignore */ }
796
992
  }
993
+ await unregisterLegacyCapabilitiesTopLevel(instanceId, purge);
797
994
  return { ok: true };
798
995
  }
799
- if (resp.status === 404)
996
+ if (resp.status === 404) {
997
+ // Already stopped — still mark capabilities stopped so the UI reflects state.
998
+ await unregisterLegacyCapabilitiesTopLevel(instanceId, purge);
800
999
  return { ok: false, error: "Instance is not running" };
1000
+ }
801
1001
  return { ok: false, error: await resp.text() };
802
1002
  }
803
1003
  catch (e) {
@@ -828,6 +1028,11 @@ export async function restartInstance(instanceId) {
828
1028
  const meta = getInstance(instanceId);
829
1029
  const agentType = resolveAgentType(meta);
830
1030
  const adapter = getAdapter(agentType);
1031
+ // PR 9 — refresh connection-derived config (mcp_servers /
1032
+ // openclaw.json plugins) from current capability registry so
1033
+ // host IP changes propagate on restart without manual re-bind.
1034
+ // Symmetric with the same call in startInstance.
1035
+ await phaseRefreshConnections(adapter, instanceId);
831
1036
  if (adapter.hooks?.onBeforeStart) {
832
1037
  await adapter.hooks.onBeforeStart({ instanceId });
833
1038
  }
@@ -841,8 +1046,12 @@ export async function restartInstance(instanceId) {
841
1046
  TaskName: resolveTaskName(instanceId),
842
1047
  AllTasks: false,
843
1048
  });
844
- if (resp.ok)
1049
+ if (resp.ok) {
1050
+ // Re-register capabilities — yaml provides may have changed since
1051
+ // the last start (e.g. a panel upgrade adding `llm-agent`).
1052
+ await registerLegacyCapabilitiesTopLevel(instanceId);
845
1053
  return { ok: true, alloc_id: alloc.ID };
1054
+ }
846
1055
  // Non-2xx from the restart endpoint falls through to stop+start
847
1056
  const errText = await resp.text();
848
1057
  console.warn(`[nomad] Native restart failed for ${instanceId} (HTTP ${resp.status}): ${errText} — falling back to stop+start`);
@@ -1228,7 +1437,8 @@ var UnifiedNomadJobs;
1228
1437
  return nomadConfigDeclaresHostNetwork("external") ? "external" : undefined;
1229
1438
  }
1230
1439
  function specRequiresExternalHostNetwork(spec) {
1231
- return spec.tasks.some((task) => (task.ports ?? []).some((port) => (port.visibility ?? "external") !== "internal"));
1440
+ return spec.tasks.some((task) => task.runtime !== "container"
1441
+ && (task.ports ?? []).some((port) => (port.visibility ?? "external") !== "internal"));
1232
1442
  }
1233
1443
  async function validateRequiredHostNetworks(spec) {
1234
1444
  if (!specRequiresExternalHostNetwork(spec))
@@ -1268,7 +1478,18 @@ var UnifiedNomadJobs;
1268
1478
  Label: portLabel(task.name, port.name),
1269
1479
  Value: port.host_port ?? port.port,
1270
1480
  ...(task.runtime === "container" ? { To: port.container_port ?? port.port } : {}),
1271
- ...(hostNetworkForPort(port) ? { HostNetwork: hostNetworkForPort(port) } : {}),
1481
+ // Attach the named host_network for any externally-visible port —
1482
+ // including container tasks. Without it, Nomad falls back to
1483
+ // HostNetwork="default" (loopback) and the docker driver publishes
1484
+ // to 127.0.0.1, breaking cross-container consumers (e.g. OpenWebUI
1485
+ // calling hermes / openclaw via the connections page). The earlier
1486
+ // restriction to non-container tasks was overcautious — our task
1487
+ // groups use host networking (Mode: "") rather than Nomad bridge
1488
+ // mode, so attaching host_network is safe and is exactly what
1489
+ // searxng-container has been doing in practice all along.
1490
+ ...(hostNetworkForPort(port)
1491
+ ? { HostNetwork: hostNetworkForPort(port) }
1492
+ : {}),
1272
1493
  }));
1273
1494
  }
1274
1495
  // ── Health check → Nomad service check builder ────────────────────────────
@@ -2013,12 +2234,52 @@ var UnifiedNomadJobs;
2013
2234
  const src = v.source.replace(/^~(?=\/|$)/, homedir());
2014
2235
  return `${src}:${v.target}${v.readonly ? ":ro" : ":rw"}`;
2015
2236
  });
2237
+ // Resolve container task user. On Linux we default to the panel
2238
+ // process's host uid:gid so bind-mounted data dirs (owned by the panel
2239
+ // user, typically `pi`) are writable without forcing the container to
2240
+ // run as root and without needing chown / DAC_OVERRIDE gymnastics.
2241
+ // yaml override `user: "<uid>:<gid>"` wins; explicit `user: "root"` or
2242
+ // `user: "0:0"` keeps the image's root default.
2243
+ //
2244
+ // On macOS we skip the host-uid default. Docker on Mac runs inside a
2245
+ // Linux VM (Colima/Docker Desktop) with its own uid namespace — host
2246
+ // uids like 501 (the standard macOS first-user) are virtualised away
2247
+ // by virtiofs and almost never exist in the image's /etc/passwd. Some
2248
+ // images crash hard when started as an unknown uid: e.g. browserless
2249
+ // calls Node.js `os.userInfo()` very early, which throws
2250
+ // `uv_os_get_passwd returned ENOENT` and the container exits before
2251
+ // the port ever binds. Letting the image's default USER directive
2252
+ // take effect is correct on Mac; users who do need bind-mount
2253
+ // ownership control can still set yaml `user:` explicitly.
2254
+ const containerUser = (() => {
2255
+ if (task.runtime !== "container")
2256
+ return undefined;
2257
+ const declared = typeof task.user === "string" ? task.user.trim() : "";
2258
+ if (declared === "host" || declared === "") {
2259
+ if (process.platform === "darwin")
2260
+ return undefined;
2261
+ const uid = process.getuid?.() ?? 1000;
2262
+ const gid = process.getgid?.() ?? 1000;
2263
+ return `${uid}:${gid}`;
2264
+ }
2265
+ return declared;
2266
+ })();
2016
2267
  const taskDef = {
2017
2268
  Name: task.name,
2018
2269
  Driver: "docker",
2270
+ ...(containerUser ? { User: containerUser } : {}),
2019
2271
  Config: {
2020
2272
  image,
2021
2273
  force_pull: false,
2274
+ // Nomad's docker driver default `image_pull_timeout` is 5 minutes;
2275
+ // on Raspberry Pi or other constrained networks a 1+ GiB image
2276
+ // (Open WebUI, OpenClaw, Hermes) can exceed that and the alloc
2277
+ // restart-loops with "Failed to pull: context deadline exceeded"
2278
+ // before it ever starts. Raise to 15 minutes — long enough for
2279
+ // realistic Pi-class pulls, short enough that a genuinely
2280
+ // unreachable registry still surfaces as a failure within a
2281
+ // bounded window.
2282
+ image_pull_timeout: "15m",
2022
2283
  ...(task.command ? { command: String(task.command) } : {}),
2023
2284
  args,
2024
2285
  ...(publishedPorts.length > 0 ? { ports: publishedPorts } : {}),
@@ -2602,6 +2863,10 @@ var UnifiedNomadJobs;
2602
2863
  if (nomadStopped) {
2603
2864
  const allocsStopped = await waitForAllocationsToStop(liveAllocIds);
2604
2865
  if (!allocsStopped) {
2866
+ const lingeringAlloc = await getRunningAlloc(appId);
2867
+ if (!lingeringAlloc) {
2868
+ return { ok: true };
2869
+ }
2605
2870
  return { ok: false, error: `App '${appId}' allocations did not stop in time` };
2606
2871
  }
2607
2872
  return { ok: true };
@@ -3155,16 +3420,87 @@ var UnifiedNomadJobs;
3155
3420
  return getGenericJobStatus(nomadJobId);
3156
3421
  }
3157
3422
  UnifiedNomadJobs.getInstanceStatus = getInstanceStatus;
3423
+ /**
3424
+ * Capability registration shim for **legacy** (non-app-installed)
3425
+ * hermes/openclaw instances. Loads the synthetic spec via
3426
+ * loadCapabilitySpecForLegacyInstance and routes provides through the
3427
+ * app-manager registry helpers, so legacy instances surface in
3428
+ * Connections candidate lists like app-installed ones.
3429
+ *
3430
+ * Errors are swallowed and logged — capability registration is best-
3431
+ * effort; a failure here must not block start/stop.
3432
+ */
3433
+ async function registerLegacyCapabilities(instanceId) {
3434
+ try {
3435
+ const meta = getInstance(instanceId);
3436
+ if (!meta)
3437
+ return;
3438
+ const { loadCapabilitySpecForLegacyInstance } = await import("./runtime/migrations.js");
3439
+ const synthSpec = loadCapabilitySpecForLegacyInstance(meta);
3440
+ if (!synthSpec?.provides?.length)
3441
+ return;
3442
+ // Same instance-name override + portOverride passthrough as
3443
+ // registerLegacyCapabilitiesTopLevel — Connections candidates should
3444
+ // show the user's instance name and advertise the actually-allocated
3445
+ // gateway port (not the yaml default).
3446
+ const instanceName = typeof meta.name === "string" && meta.name
3447
+ ? meta.name
3448
+ : instanceId;
3449
+ const namedSpec = { ...synthSpec, name: instanceName };
3450
+ const portOverride = getGatewayPort(instanceId);
3451
+ const { registerCapabilities } = await import("./app/app-manager.js");
3452
+ registerCapabilities(instanceId, namedSpec, portOverride > 0 ? portOverride : undefined);
3453
+ }
3454
+ catch (e) {
3455
+ console.warn(`[legacy-capabilities] register failed for ${instanceId}: ${e?.message ?? e}`);
3456
+ }
3457
+ }
3458
+ async function unregisterLegacyCapabilities(instanceId, purge) {
3459
+ try {
3460
+ const { markCapabilitiesStopped, unregisterCapabilities } = await import("./app/app-manager.js");
3461
+ if (purge) {
3462
+ unregisterCapabilities(instanceId);
3463
+ }
3464
+ else {
3465
+ markCapabilitiesStopped(instanceId);
3466
+ }
3467
+ }
3468
+ catch (e) {
3469
+ console.warn(`[legacy-capabilities] unregister failed for ${instanceId}: ${e?.message ?? e}`);
3470
+ }
3471
+ }
3158
3472
  async function startInstance(nomadJobId) {
3159
3473
  const instanceBackedApp = await getInstanceBackedInstalledApp(nomadJobId);
3160
3474
  if (instanceBackedApp) {
3475
+ // PR 3 sub-step 3d: switch to resolveConnections in runtime mode so
3476
+ // missing required producers / ambiguous prefix candidates throw with
3477
+ // structured codes (412 / 409 / 400). Read the live instance.json so
3478
+ // UI bindings persisted via PUT /connections (PR 4) drive the
3479
+ // resolution; fall back to a stub `{ connections: {} }` when the
3480
+ // instance file isn't readable yet.
3161
3481
  let extraEnv = {};
3162
3482
  try {
3163
- const { resolveRequires } = await import("./app/app-manager.js");
3164
- extraEnv = resolveRequires(instanceBackedApp.spec);
3483
+ const { resolveConnections, resolvedToLegacyEnv } = await import("./connection-resolver.js");
3484
+ const legacyInstanceManager = await import("./instance-manager.js");
3485
+ const meta = legacyInstanceManager.getInstance(nomadJobId);
3486
+ const instance = { connections: meta?.connections ?? {} };
3487
+ // Validate in runtime mode so missing required / ambiguous still throws
3488
+ // with structured error codes (412 / 409 / 400) before we touch Nomad.
3489
+ const { resolved } = resolveConnections(instanceBackedApp.spec, instance, "runtime");
3490
+ // Render the full RUNTIME_HOOKS env (covers llm/search/browser/mcp)
3491
+ // rather than just the default-category subset, so apply: openai-env
3492
+ // consumers (e.g. OpenWebUI) self-heal across provider port changes.
3493
+ const { renderRuntimeConnectionsEnv } = await import("./connection-apply.js");
3494
+ const runtimeEnv = await renderRuntimeConnectionsEnv(instanceBackedApp.spec, { id: nomadJobId, connections: instance.connections });
3495
+ extraEnv = { ...resolvedToLegacyEnv(resolved), ...runtimeEnv };
3165
3496
  }
3166
3497
  catch (e) {
3167
- return { ok: false, error: e.message };
3498
+ return {
3499
+ ok: false,
3500
+ error: e.message,
3501
+ ...(e.code ? { code: e.code } : {}),
3502
+ ...(typeof e.statusCode === "number" ? { statusCode: e.statusCode } : {}),
3503
+ };
3168
3504
  }
3169
3505
  const depCheck = await checkDependencies(instanceBackedApp.spec);
3170
3506
  if (!depCheck.ok) {
@@ -3189,10 +3525,17 @@ var UnifiedNomadJobs;
3189
3525
  return { ok: false, error: `App '${nomadJobId}' 必须通过 app-manager 启动` };
3190
3526
  }
3191
3527
  if (existsSync(instanceMetaPath(nomadJobId))) {
3192
- return instanceScheduler.startInstance(nomadJobId);
3528
+ const result = await instanceScheduler.startInstance(nomadJobId);
3529
+ if (result.ok)
3530
+ await registerLegacyCapabilities(nomadJobId);
3531
+ return result;
3193
3532
  }
3194
3533
  if (nomadJobId.startsWith(OPENCLAW_PREFIX)) {
3195
- return instanceScheduler.startInstance(nomadJobId.slice(OPENCLAW_PREFIX.length));
3534
+ const inner = nomadJobId.slice(OPENCLAW_PREFIX.length);
3535
+ const result = await instanceScheduler.startInstance(inner);
3536
+ if (result.ok)
3537
+ await registerLegacyCapabilities(inner);
3538
+ return result;
3196
3539
  }
3197
3540
  if (!isAppJob(nomadJobId)) {
3198
3541
  return { ok: false, error: `Cannot start unmanaged job "${nomadJobId}"` };
@@ -3213,10 +3556,19 @@ var UnifiedNomadJobs;
3213
3556
  return { ok: false, error: `App '${nomadJobId}' 必须通过 app-manager 停止` };
3214
3557
  }
3215
3558
  if (existsSync(instanceMetaPath(nomadJobId))) {
3216
- return instanceScheduler.stopInstance(nomadJobId, purge);
3559
+ const result = await instanceScheduler.stopInstance(nomadJobId, purge);
3560
+ if (result.ok || result.error?.includes("not running") || result.error?.includes("not found")) {
3561
+ await unregisterLegacyCapabilities(nomadJobId, purge);
3562
+ }
3563
+ return result;
3217
3564
  }
3218
3565
  if (nomadJobId.startsWith(OPENCLAW_PREFIX)) {
3219
- return instanceScheduler.stopInstance(nomadJobId.slice(OPENCLAW_PREFIX.length), purge);
3566
+ const inner = nomadJobId.slice(OPENCLAW_PREFIX.length);
3567
+ const result = await instanceScheduler.stopInstance(inner, purge);
3568
+ if (result.ok || result.error?.includes("not running") || result.error?.includes("not found")) {
3569
+ await unregisterLegacyCapabilities(inner, purge);
3570
+ }
3571
+ return result;
3220
3572
  }
3221
3573
  try {
3222
3574
  const resp = await nomadDelete(`/v1/job/${nomadJobId}?purge=${purge}`);
@@ -3239,10 +3591,17 @@ var UnifiedNomadJobs;
3239
3591
  return { ok: false, error: `App '${nomadJobId}' 必须通过 app-manager 重启` };
3240
3592
  }
3241
3593
  if (existsSync(instanceMetaPath(nomadJobId))) {
3242
- return instanceScheduler.restartInstance(nomadJobId);
3594
+ const result = await instanceScheduler.restartInstance(nomadJobId);
3595
+ if (result.ok)
3596
+ await registerLegacyCapabilities(nomadJobId);
3597
+ return result;
3243
3598
  }
3244
3599
  if (nomadJobId.startsWith(OPENCLAW_PREFIX)) {
3245
- return instanceScheduler.restartInstance(nomadJobId.slice(OPENCLAW_PREFIX.length));
3600
+ const inner = nomadJobId.slice(OPENCLAW_PREFIX.length);
3601
+ const result = await instanceScheduler.restartInstance(inner);
3602
+ if (result.ok)
3603
+ await registerLegacyCapabilities(inner);
3604
+ return result;
3246
3605
  }
3247
3606
  if (!isAppJob(nomadJobId)) {
3248
3607
  return { ok: false, error: `Cannot restart unmanaged job "${nomadJobId}"` };