jishushell 0.4.10 → 0.4.17

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 (100) hide show
  1. package/INSTALL-NOTICE +10 -12
  2. package/dist/cli/app.d.ts +3 -0
  3. package/dist/cli/app.js +156 -0
  4. package/dist/cli/app.js.map +1 -0
  5. package/dist/{doctor.d.ts → cli/doctor.d.ts} +6 -1
  6. package/dist/{doctor.js → cli/doctor.js} +343 -14
  7. package/dist/cli/doctor.js.map +1 -0
  8. package/dist/cli/helpers.d.ts +4 -0
  9. package/dist/cli/helpers.js +32 -0
  10. package/dist/cli/helpers.js.map +1 -0
  11. package/dist/cli/job.d.ts +3 -0
  12. package/dist/cli/job.js +260 -0
  13. package/dist/cli/job.js.map +1 -0
  14. package/dist/cli/llm.d.ts +24 -0
  15. package/dist/cli/llm.js +593 -0
  16. package/dist/cli/llm.js.map +1 -0
  17. package/dist/cli/openclaw.d.ts +12 -0
  18. package/dist/cli/openclaw.js +156 -0
  19. package/dist/cli/openclaw.js.map +1 -0
  20. package/dist/cli/panel.d.ts +25 -0
  21. package/dist/cli/panel.js +734 -0
  22. package/dist/cli/panel.js.map +1 -0
  23. package/dist/cli.js +67 -326
  24. package/dist/cli.js.map +1 -1
  25. package/dist/config.d.ts +1 -0
  26. package/dist/config.js +11 -4
  27. package/dist/config.js.map +1 -1
  28. package/dist/control.d.ts +13 -41
  29. package/dist/control.js +12 -1355
  30. package/dist/control.js.map +1 -1
  31. package/dist/routes/apps.d.ts +3 -0
  32. package/dist/routes/apps.js +99 -0
  33. package/dist/routes/apps.js.map +1 -0
  34. package/dist/routes/instances.js +12 -6
  35. package/dist/routes/instances.js.map +1 -1
  36. package/dist/routes/llm.d.ts +15 -0
  37. package/dist/routes/llm.js +246 -0
  38. package/dist/routes/llm.js.map +1 -0
  39. package/dist/routes/setup.js +29 -2
  40. package/dist/routes/setup.js.map +1 -1
  41. package/dist/routes/system.js +31 -6
  42. package/dist/routes/system.js.map +1 -1
  43. package/dist/server.js +40 -4
  44. package/dist/server.js.map +1 -1
  45. package/dist/services/app-compiler.d.ts +15 -0
  46. package/dist/services/app-compiler.js +169 -0
  47. package/dist/services/app-compiler.js.map +1 -0
  48. package/dist/services/app-manager.d.ts +17 -0
  49. package/dist/services/app-manager.js +168 -0
  50. package/dist/services/app-manager.js.map +1 -0
  51. package/dist/services/instance-manager.d.ts +51 -3
  52. package/dist/services/instance-manager.js +233 -30
  53. package/dist/services/instance-manager.js.map +1 -1
  54. package/dist/services/job-manager.d.ts +22 -0
  55. package/dist/services/job-manager.js +102 -0
  56. package/dist/services/job-manager.js.map +1 -0
  57. package/dist/services/llm-proxy/adapters.js +5 -1
  58. package/dist/services/llm-proxy/adapters.js.map +1 -1
  59. package/dist/services/llm-proxy/index.d.ts +30 -0
  60. package/dist/services/llm-proxy/index.js +71 -1
  61. package/dist/services/llm-proxy/index.js.map +1 -1
  62. package/dist/services/llm-proxy/ssrf.js +1 -1
  63. package/dist/services/llm-proxy/ssrf.js.map +1 -1
  64. package/dist/services/nomad-manager.js +192 -29
  65. package/dist/services/nomad-manager.js.map +1 -1
  66. package/dist/services/panel-manager.d.ts +40 -0
  67. package/dist/services/panel-manager.js +346 -0
  68. package/dist/services/panel-manager.js.map +1 -0
  69. package/dist/services/process-manager.js +20 -7
  70. package/dist/services/process-manager.js.map +1 -1
  71. package/dist/services/setup-manager.js +316 -31
  72. package/dist/services/setup-manager.js.map +1 -1
  73. package/dist/services/update-manager.d.ts +47 -0
  74. package/dist/services/update-manager.js +305 -0
  75. package/dist/services/update-manager.js.map +1 -0
  76. package/dist/types.d.ts +62 -0
  77. package/install/jishu-install.sh +279 -37
  78. package/install/post-install.sh +64 -5
  79. package/package.json +6 -2
  80. package/public/assets/Dashboard-CQsp1Mr9.js +1 -0
  81. package/public/assets/InitPassword-BEC8SE4A.js +1 -0
  82. package/public/assets/InstanceDetail-B5wTgNEg.js +17 -0
  83. package/public/assets/{Login-CUoEZOWR.js → Login-D1Bt-Lyk.js} +1 -1
  84. package/public/assets/NewInstance-GQzm3K9D.js +1 -0
  85. package/public/assets/Settings-ByjGlqhP.js +1 -0
  86. package/public/assets/Setup-cMF21Y-8.js +1 -0
  87. package/public/assets/index-B6qQP4mH.css +1 -0
  88. package/public/assets/index-BuTQtuNy.js +16 -0
  89. package/public/assets/{providers-lBSOjUWy.js → providers-V-vwrExZ.js} +1 -1
  90. package/public/index.html +2 -2
  91. package/dist/doctor.js.map +0 -1
  92. package/install/jishu-install-china.sh +0 -3092
  93. package/public/assets/Dashboard-DhsrzJ4F.js +0 -1
  94. package/public/assets/InitPassword-BjubiVdd.js +0 -1
  95. package/public/assets/InstanceDetail-DMcywsof.js +0 -17
  96. package/public/assets/NewInstance-Bk0G4EiJ.js +0 -1
  97. package/public/assets/Settings-D5tHL_h5.js +0 -1
  98. package/public/assets/Setup-4t6E3Rut.js +0 -1
  99. package/public/assets/index-BJ47MWpF.css +0 -1
  100. package/public/assets/index-DbX85irc.js +0 -16
@@ -118,7 +118,11 @@ is_promptable() {
118
118
  if [[ "${NO_PROMPT:-0}" == "1" ]]; then
119
119
  return 1
120
120
  fi
121
- if [[ -r /dev/tty && -w /dev/tty ]]; then
121
+ # Web-triggered upgrades never have an interactive TTY
122
+ if [[ "${JISHUSHELL_WEB_UPDATE:-0}" == "1" ]]; then
123
+ return 1
124
+ fi
125
+ if ( : <> /dev/tty ) 2>/dev/null; then
122
126
  return 0
123
127
  fi
124
128
  return 1
@@ -161,7 +165,7 @@ JISHU_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd 2>/dev/null |
161
165
  # ──── BEGIN VERSIONS ────
162
166
  NODE_VERSION="${JISHU_NODE_VERSION:-22}"
163
167
  NVM_VERSION="${JISHU_NVM_VERSION:-0.40.4}"
164
- NOMAD_VERSION="${JISHU_NOMAD_VERSION:-1.11.3}"
168
+ NOMAD_VERSION="${JISHU_NOMAD_VERSION:-1.6.5}"
165
169
  JISHUSHELL_PORT="${JISHUSHELL_PORT:-8090}"
166
170
 
167
171
  # ──── NPM Registry Configuration ────
@@ -223,6 +227,11 @@ SKIP_NOMAD="${SKIP_NOMAD:-0}"
223
227
  SKIP_OPENCLAW="${SKIP_OPENCLAW:-0}" # default=0 (install); use --skip 4 or --skip-openclaw to skip
224
228
  SKIP_JISHUSHELL="${SKIP_JISHUSHELL:-0}" # 1=skip install_jishushell
225
229
  SKIP_JISHUSHELL_SERVICE="${SKIP_JISHUSHELL_SERVICE:-0}" # 1=skip service registration
230
+ JISHUSHELL_NPM_VERSION="${JISHUSHELL_NPM_VERSION:-latest}" # jishushell npm package version
231
+ JISHUSHELL_VERSION_OVERRIDE=0
232
+ if [[ "${JISHUSHELL_NPM_VERSION}" != "latest" ]]; then
233
+ JISHUSHELL_VERSION_OVERRIDE=1
234
+ fi
226
235
  OPENCLAW_NPM_VERSION="${OPENCLAW_NPM_VERSION:-latest}" # openclaw npm package version
227
236
  OPENCLAW_DOCKER_TAG="${OPENCLAW_DOCKER_TAG:-ghcr.io/x-aijishu/openclaw-runtime:latest}" # pre-built image from registry
228
237
  OPENCLAW_IMAGE="" # set dynamically after pull/build
@@ -448,6 +457,10 @@ check_sudo() {
448
457
 
449
458
  if ! sudo -n true 2>/dev/null; then
450
459
  ui_info "Some steps require sudo — you may be prompted for your password."
460
+ if ! is_promptable; then
461
+ ui_error "Failed to obtain sudo privileges (no interactive TTY available)"
462
+ exit 1
463
+ fi
451
464
  if ! sudo -v </dev/tty; then
452
465
  ui_error "Failed to obtain sudo privileges"
453
466
  exit 1
@@ -1413,12 +1426,24 @@ install_nomad() {
1413
1426
  if [[ -z "$current_version" ]]; then
1414
1427
  ui_warn "Nomad at ${local_bin} is not functional (wrong arch or corrupt) — reinstalling..."
1415
1428
  rm -f "$local_bin"
1429
+ elif [[ "$current_version" == "$NOMAD_VERSION" ]]; then
1430
+ ui_success "Nomad already at target version: v${current_version} → ${local_bin}"
1431
+ _ensure_jishushell_bin_in_path
1432
+ return 0
1416
1433
  elif version_gte "$current_version" "$NOMAD_VERSION"; then
1417
- ui_success "Nomad already installed: v${current_version} ${local_bin}"
1434
+ # current > target (the == case was handled above). JishuShell
1435
+ # pins Nomad to a specific version on purpose (license downgrade
1436
+ # from BSL 1.1 to MPL 2.0). Raft state is not backward compatible
1437
+ # across the jump, so we auto-migrate: download + verify the new
1438
+ # binary first (safe-first), then stop services, back up the old
1439
+ # data_dir, wipe it, clean orphaned containers, and swap the
1440
+ # binary. JishuShell re-bootstraps ACL and resubmits jobs from
1441
+ # on-disk instance configs on the next start.
1442
+ _migrate_nomad_to_target "$current_version" || return 1
1418
1443
  _ensure_jishushell_bin_in_path
1419
1444
  return 0
1420
1445
  else
1421
- ui_warn "Nomad version too old: v${current_version} (need >= v${NOMAD_VERSION}) — upgrading..."
1446
+ ui_warn "Nomad version too old: v${current_version} (need v${NOMAD_VERSION}) — upgrading..."
1422
1447
  rm -f "$local_bin"
1423
1448
  fi
1424
1449
  fi
@@ -1504,6 +1529,180 @@ REPO
1504
1529
  return 1
1505
1530
  }
1506
1531
 
1532
+ # Auto-migrate from a higher Nomad version (e.g. 1.11.3 BSL) back to the
1533
+ # jishushell target (1.6.5 MPL). Called when install_nomad detects a local
1534
+ # binary whose semver is > NOMAD_VERSION. The migration is destructive to
1535
+ # Nomad's raft state (schema is not backward compatible) but preserves
1536
+ # instance configs under ~/.jishushell/instances/*, which is what jishushell
1537
+ # uses to resubmit jobs after reboot. A single tar.gz snapshot of the old
1538
+ # data_dir is kept under ~/.jishushell/nomad/backups/ for forensic inspection
1539
+ # — it is not a user-recovery mechanism (the schema can't be replayed).
1540
+ _migrate_nomad_to_target() {
1541
+ local current_version="$1"
1542
+ local local_bin="${JISHUSHELL_BIN_DIR}/nomad"
1543
+
1544
+ if [[ "$DRY_RUN" == "1" ]]; then
1545
+ ui_info "[dry-run] Would migrate Nomad v${current_version} → v${NOMAD_VERSION}:"
1546
+ ui_info "[dry-run] 1. Stage + verify new binary in /tmp"
1547
+ ui_info "[dry-run] 2. Stop jishushell + nomad services"
1548
+ ui_info "[dry-run] 3. Tar backup ${JISHUSHELL_HOME}/nomad/data → nomad/backups/data-<ts>.tar.gz"
1549
+ ui_info "[dry-run] 4. Wipe raft state + nomad.env files (schema incompatible)"
1550
+ ui_info "[dry-run] 5. Remove orphaned gateway-<alloc> containers"
1551
+ ui_info "[dry-run] 6. Swap binary into ${local_bin}"
1552
+ return 0
1553
+ fi
1554
+
1555
+ ui_warn "Nomad v${current_version} > target v${NOMAD_VERSION} — auto-migrating (BSL → MPL)..."
1556
+ ui_info " Raft state is not backward-compatible; allocation history will be reset."
1557
+ ui_info " Instance configs under ${JISHUSHELL_HOME}/instances/ are preserved."
1558
+
1559
+ # ── Stage 1: download + verify new binary before touching anything ────
1560
+ local stage_dir
1561
+ stage_dir="$(mktemp -d)" || { ui_error "mktemp failed"; return 1; }
1562
+ # shellcheck disable=SC2064
1563
+ trap "rm -rf '$stage_dir'" RETURN
1564
+
1565
+ local platform
1566
+ platform="$(uname -s | tr '[:upper:]' '[:lower:]')"
1567
+ local download_url="https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_${platform}_${ARCH}.zip"
1568
+ local sums_url="https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_SHA256SUMS"
1569
+
1570
+ ui_info "Staging Nomad v${NOMAD_VERSION} (${platform}/${ARCH})..."
1571
+ if ! retry_net "Download Nomad binary" 3 curl -fsSL "$download_url" -o "${stage_dir}/nomad.zip"; then
1572
+ ui_error "Failed to download Nomad v${NOMAD_VERSION} — keeping existing v${current_version}"
1573
+ return 1
1574
+ fi
1575
+ if ! retry_net "Download Nomad checksums" 3 curl -fsSL "$sums_url" -o "${stage_dir}/SHA256SUMS"; then
1576
+ ui_error "Failed to download Nomad checksum file — aborting migration for security"
1577
+ return 1
1578
+ fi
1579
+
1580
+ local expected_hash actual_hash
1581
+ expected_hash="$(grep "nomad_${NOMAD_VERSION}_${platform}_${ARCH}.zip" "${stage_dir}/SHA256SUMS" | awk '{print $1}')"
1582
+ if [[ -z "$expected_hash" ]]; then
1583
+ ui_error "No checksum entry for nomad_${NOMAD_VERSION}_${platform}_${ARCH}.zip — aborting"
1584
+ return 1
1585
+ fi
1586
+ if command -v sha256sum &>/dev/null; then
1587
+ actual_hash="$(sha256sum "${stage_dir}/nomad.zip" | awk '{print $1}')"
1588
+ else
1589
+ actual_hash="$(shasum -a 256 "${stage_dir}/nomad.zip" | awk '{print $1}')"
1590
+ fi
1591
+ if [[ "$expected_hash" != "$actual_hash" ]]; then
1592
+ ui_error "Nomad checksum mismatch — download may have been tampered with!"
1593
+ ui_error " Expected: $expected_hash"
1594
+ ui_error " Got: $actual_hash"
1595
+ return 1
1596
+ fi
1597
+ ui_info "Checksum verified ✓"
1598
+
1599
+ if ! command -v unzip &>/dev/null; then
1600
+ ui_info "Installing unzip..."
1601
+ pkg_install unzip >/dev/null 2>&1
1602
+ fi
1603
+ if ! unzip -o "${stage_dir}/nomad.zip" nomad -d "${stage_dir}" >/dev/null 2>&1; then
1604
+ if ! unzip -o "${stage_dir}/nomad.zip" -d "${stage_dir}" >/dev/null 2>&1; then
1605
+ ui_error "Failed to extract staged Nomad archive"
1606
+ return 1
1607
+ fi
1608
+ fi
1609
+ chmod 755 "${stage_dir}/nomad" 2>/dev/null
1610
+
1611
+ local staged_version
1612
+ staged_version="$("${stage_dir}/nomad" version 2>/dev/null | head -n1 | extract_semver || echo "")"
1613
+ if [[ "$staged_version" != "$NOMAD_VERSION" ]]; then
1614
+ ui_error "Staged binary reports v${staged_version:-unknown}, expected v${NOMAD_VERSION} — aborting"
1615
+ return 1
1616
+ fi
1617
+ ui_success "Staged new Nomad binary v${staged_version}"
1618
+
1619
+ # ── Stage 2: destructive state changes begin ──────────────────────────
1620
+ ui_info "Stopping services..."
1621
+ ${SUDO} systemctl stop jishushell 2>/dev/null || true
1622
+ ${SUDO} systemctl stop nomad 2>/dev/null || true
1623
+ # pkill -f 'nomad agent' matches its own cmdline ("pkill -f nomad agent"
1624
+ # literally contains the pattern) and self-terminates before reaching the
1625
+ # real nomad process. Use pgrep -x nomad instead (exact proc-name match,
1626
+ # pgrep's own comm is "pgrep" not "nomad").
1627
+ local nomad_pids
1628
+ nomad_pids="$(pgrep -x nomad 2>/dev/null | tr '\n' ' ')"
1629
+ if [[ -n "$nomad_pids" ]]; then
1630
+ # shellcheck disable=SC2086
1631
+ ${SUDO} kill -TERM $nomad_pids 2>/dev/null || kill -TERM $nomad_pids 2>/dev/null || true
1632
+ sleep 2
1633
+ nomad_pids="$(pgrep -x nomad 2>/dev/null | tr '\n' ' ')"
1634
+ if [[ -n "$nomad_pids" ]]; then
1635
+ # shellcheck disable=SC2086
1636
+ ${SUDO} kill -KILL $nomad_pids 2>/dev/null || kill -KILL $nomad_pids 2>/dev/null || true
1637
+ fi
1638
+ fi
1639
+
1640
+ # ── Stage 3: tar backup (single snapshot, overwrite any previous) ─────
1641
+ local backup_file=""
1642
+ local backup_dir="${JISHUSHELL_HOME}/nomad/backups"
1643
+ if [[ -d "${JISHUSHELL_HOME}/nomad/data" ]]; then
1644
+ mkdir -p "$backup_dir"
1645
+ local ts
1646
+ ts="$(date +%Y%m%d-%H%M%S)"
1647
+ backup_file="${backup_dir}/data-${ts}.tar.gz"
1648
+ ui_info "Backing up raft state → ${backup_file}"
1649
+ if ! tar czf "$backup_file" -C "${JISHUSHELL_HOME}/nomad" data 2>/dev/null; then
1650
+ ui_warn "Backup tar failed — continuing (raft state will still be wiped)"
1651
+ backup_file=""
1652
+ else
1653
+ # Keep only the most recent snapshot to avoid unbounded disk growth
1654
+ ls -t "${backup_dir}"/data-*.tar.gz 2>/dev/null | tail -n +2 | xargs -r rm -f
1655
+ fi
1656
+ fi
1657
+
1658
+ # ── Stage 4: wipe raft state + env files ─────────────────────────────
1659
+ ${SUDO} rm -rf "${JISHUSHELL_HOME}/nomad/data"
1660
+ rm -f "${JISHUSHELL_HOME}/nomad.env"
1661
+ ${SUDO} rm -f /etc/jishushell/nomad.env
1662
+
1663
+ # ── Stage 5: orphaned gateway containers (alloc ids gone with raft) ──
1664
+ # sudo npm install -g runs postinstall as the invoking user (typically pi),
1665
+ # whose login shell may not have docker group access — the legacy install
1666
+ # only granted docker to the nomad.service via SupplementaryGroups, not to
1667
+ # the login shell. Try unprivileged first, fall back to sudo docker so this
1668
+ # step works regardless of group membership.
1669
+ if command -v docker &>/dev/null; then
1670
+ local _docker="docker"
1671
+ if ! docker ps >/dev/null 2>&1; then
1672
+ _docker="${SUDO} docker"
1673
+ fi
1674
+ local gw_containers
1675
+ gw_containers="$($_docker ps -a --format '{{.Names}}' 2>/dev/null | grep '^gateway-' || true)"
1676
+ if [[ -n "$gw_containers" ]]; then
1677
+ local gw_count
1678
+ gw_count="$(echo "$gw_containers" | wc -l)"
1679
+ echo "$gw_containers" | xargs -r $_docker rm -f >/dev/null 2>&1 || true
1680
+ ui_info "Removed ${gw_count} orphaned gateway container(s)"
1681
+ fi
1682
+ fi
1683
+
1684
+ # ── Stage 6: swap binary into place (atomic via temp name + rename) ──
1685
+ mkdir -p "${JISHUSHELL_BIN_DIR}"
1686
+ local dest_tmp="${local_bin}.tmp.$$"
1687
+ if ! cp "${stage_dir}/nomad" "$dest_tmp"; then
1688
+ ui_error "Failed to copy new Nomad binary into place"
1689
+ [[ -n "$backup_file" ]] && ui_error " Backup preserved at: ${backup_file}"
1690
+ return 1
1691
+ fi
1692
+ chmod 755 "$dest_tmp"
1693
+ if ! mv -f "$dest_tmp" "$local_bin"; then
1694
+ ui_error "Failed to swap Nomad binary"
1695
+ [[ -n "$backup_file" ]] && ui_error " Backup preserved at: ${backup_file}"
1696
+ rm -f "$dest_tmp"
1697
+ return 1
1698
+ fi
1699
+
1700
+ ui_success "Nomad migrated to v${NOMAD_VERSION}"
1701
+ [[ -n "$backup_file" ]] && ui_info " Backup (forensic, not self-recovery): ${backup_file}"
1702
+ ui_info " JishuShell will re-bootstrap ACL and resubmit jobs from instance configs on next start."
1703
+ return 0
1704
+ }
1705
+
1507
1706
  _install_nomad_binary() {
1508
1707
  local dest="${JISHUSHELL_BIN_DIR}/nomad"
1509
1708
 
@@ -1849,14 +2048,17 @@ install_nomad_systemd() {
1849
2048
 
1850
2049
  _ensure_nomad_hcl
1851
2050
 
2051
+ # Nomad 1.6.5's docker driver fingerprint requires euid==0 (PR #18197 lifted
2052
+ # the root requirement only in 1.7+, which is BSL). The panel stays as the
2053
+ # installing user via a separate unit; it talks to this agent over HTTP so
2054
+ # ~/.jishushell/nomad/data/ can be root-owned without breaking anything.
1852
2055
  local service_content="[Unit]
1853
2056
  Description=Nomad Agent
1854
2057
  After=network-online.target docker.service
1855
2058
  Wants=network-online.target
1856
2059
 
1857
2060
  [Service]
1858
- User=${REAL_USER}
1859
- SupplementaryGroups=docker
2061
+ User=root
1860
2062
  Type=simple
1861
2063
  EnvironmentFile=-/etc/jishushell/nomad.env
1862
2064
  ExecStart=${nomad_bin} agent -config=${config_file}
@@ -1879,8 +2081,12 @@ WantedBy=multi-user.target"
1879
2081
  need_reload=1
1880
2082
  fi
1881
2083
 
1882
- # Ensure Nomad data dirs are owned by the real user before the service starts
2084
+ # Keep the real user owning ~/.jishushell except for Nomad's own state,
2085
+ # which must be root-owned because the agent runs as root for driver fingerprinting.
1883
2086
  chown -R "${REAL_USER}:${REAL_GID:-${REAL_USER}}" "${JISHUSHELL_HOME}" 2>/dev/null || true
2087
+ if [[ -d "${nomad_config_dir}/data" ]]; then
2088
+ ${SUDO} chown -R root:root "${nomad_config_dir}/data" 2>/dev/null || true
2089
+ fi
1884
2090
 
1885
2091
  if [[ $need_reload -eq 1 ]]; then
1886
2092
  ${SUDO} systemctl daemon-reload
@@ -2211,6 +2417,14 @@ _prompt_openclaw_skip() {
2211
2417
  esac
2212
2418
  }
2213
2419
 
2420
+ jishushell_package_spec() {
2421
+ if [[ "${JISHUSHELL_VERSION_OVERRIDE}" == "1" || "${JISHUSHELL_NPM_VERSION}" != "latest" ]]; then
2422
+ printf 'jishushell@%s' "${JISHUSHELL_NPM_VERSION}"
2423
+ return 0
2424
+ fi
2425
+ printf 'jishushell'
2426
+ }
2427
+
2214
2428
  # show_install_plan [--with-jishushell]
2215
2429
  show_install_plan() {
2216
2430
  local with_jishushell=0
@@ -2226,17 +2440,19 @@ show_install_plan() {
2226
2440
  ui_kv "Node.js" "$(if [[ $SKIP_NODE -eq 1 ]]; then echo 'skip'; else echo "v${NODE_VERSION} via nvm v${NVM_VERSION}"; fi)"
2227
2441
  ui_kv "Docker" "$(if [[ $SKIP_DOCKER -eq 1 ]]; then echo 'skip'; else echo 'latest stable'; fi)"
2228
2442
  ui_kv "Nomad" "$(if [[ $SKIP_NOMAD -eq 1 ]]; then echo 'skip'; else echo "v${NOMAD_VERSION}"; fi)"
2229
- ui_kv "OpenClaw" "$(if [[ \"${SKIP_OPENCLAW}\" == \"1\" ]]; then echo 'skip'; else echo \"docker pull ${OPENCLAW_DOCKER_TAG}\"; fi)"
2443
+ ui_kv "OpenClaw" "$(if [[ "${SKIP_OPENCLAW}" == "1" ]]; then echo 'skip'; else echo "docker pull ${OPENCLAW_DOCKER_TAG}"; fi)"
2230
2444
  if [[ $with_jishushell -eq 1 ]]; then
2231
2445
  local _plan_jishu
2232
2446
  if [[ $SKIP_JISHUSHELL -eq 1 ]]; then
2233
2447
  _plan_jishu="skip"
2234
2448
  else
2235
2449
  local _plan_tgz=""
2236
- for _c in "${JISHU_SCRIPT_DIR}"/jishushell-*.tgz; do
2237
- [[ -f "$_c" ]] && { _plan_tgz="$(basename "$_c")"; break; }
2238
- done
2239
- _plan_jishu="${_plan_tgz:+npm install -g ${_plan_tgz} (local)}${_plan_tgz:-npm install -g jishushell}"
2450
+ if [[ "${JISHUSHELL_VERSION_OVERRIDE}" != "1" && "${JISHUSHELL_NPM_VERSION}" == "latest" ]]; then
2451
+ for _c in "${JISHU_SCRIPT_DIR}"/jishushell-*.tgz; do
2452
+ [[ -f "$_c" ]] && { _plan_tgz="$(basename "$_c")"; break; }
2453
+ done
2454
+ fi
2455
+ _plan_jishu="${_plan_tgz:+npm install -g ${_plan_tgz} (local)}${_plan_tgz:-npm install -g $(jishushell_package_spec)}"
2240
2456
  fi
2241
2457
  ui_kv "JishuShell" "$_plan_jishu"
2242
2458
  ui_kv "JishuShell service" "$(if [[ $SKIP_JISHUSHELL_SERVICE -eq 1 ]]; then echo 'skip'; else echo 'register autostart'; fi)"
@@ -2401,16 +2617,20 @@ install_jishushell() {
2401
2617
  fi
2402
2618
 
2403
2619
  if [[ "$DRY_RUN" == "1" ]]; then
2620
+ local jishushell_pkg_spec
2621
+ jishushell_pkg_spec="$(jishushell_package_spec)"
2404
2622
  local _dry_reg=""
2405
2623
  [[ -n "${NPM_REGISTRY:-}" ]] && _dry_reg=" --registry ${NPM_REGISTRY}"
2406
2624
  local _dry_tgz=""
2407
- for _c in "${JISHU_SCRIPT_DIR}"/jishushell-*.tgz; do
2408
- [[ -f "$_c" ]] && { _dry_tgz="$_c"; break; }
2409
- done
2625
+ if [[ "${JISHUSHELL_VERSION_OVERRIDE}" != "1" && "${JISHUSHELL_NPM_VERSION}" == "latest" ]]; then
2626
+ for _c in "${JISHU_SCRIPT_DIR}"/jishushell-*.tgz; do
2627
+ [[ -f "$_c" ]] && { _dry_tgz="$_c"; break; }
2628
+ done
2629
+ fi
2410
2630
  if [[ -n "$_dry_tgz" ]]; then
2411
2631
  ui_info "[dry-run] Would: npm install -g ${_dry_tgz} (local package)"
2412
2632
  else
2413
- ui_info "[dry-run] Would: npm install -g jishushell${_dry_reg}"
2633
+ ui_info "[dry-run] Would: npm install -g ${jishushell_pkg_spec}${_dry_reg}"
2414
2634
  fi
2415
2635
  ui_info "[dry-run] Would write wrapper: ${JISHUSHELL_BIN_DIR}/jishushell-panel-start"
2416
2636
  return 0
@@ -2441,6 +2661,8 @@ install_jishushell() {
2441
2661
  return 1
2442
2662
  fi
2443
2663
 
2664
+ local jishushell_pkg_spec
2665
+ jishushell_pkg_spec="$(jishushell_package_spec)"
2444
2666
  local npm_registry_args=()
2445
2667
  if [[ -n "${NPM_REGISTRY:-}" ]]; then
2446
2668
  if [[ ! "$NPM_REGISTRY" =~ ^https?:// ]]; then
@@ -2448,9 +2670,9 @@ install_jishushell() {
2448
2670
  return 1
2449
2671
  fi
2450
2672
  npm_registry_args=("--registry" "${NPM_REGISTRY}")
2451
- ui_info "Installing jishushell from ${NPM_REGISTRY}..."
2673
+ ui_info "Installing ${jishushell_pkg_spec} from ${NPM_REGISTRY}..."
2452
2674
  else
2453
- ui_info "Installing jishushell from public npm registry..."
2675
+ ui_info "Installing ${jishushell_pkg_spec} from public npm registry..."
2454
2676
  fi
2455
2677
 
2456
2678
  # When jishushell is already installed (e.g. running as npm postinstall hook),
@@ -2459,12 +2681,14 @@ install_jishushell() {
2459
2681
  # Prefer a local .tgz package in the same directory as this script.
2460
2682
  local tgz_path=""
2461
2683
  local _tgz_candidate
2462
- for _tgz_candidate in "${JISHU_SCRIPT_DIR}"/jishushell-*.tgz; do
2463
- if [[ -f "$_tgz_candidate" ]]; then
2464
- tgz_path="$_tgz_candidate"
2465
- break
2466
- fi
2467
- done
2684
+ if [[ "${JISHUSHELL_VERSION_OVERRIDE}" != "1" && "${JISHUSHELL_NPM_VERSION}" == "latest" ]]; then
2685
+ for _tgz_candidate in "${JISHU_SCRIPT_DIR}"/jishushell-*.tgz; do
2686
+ if [[ -f "$_tgz_candidate" ]]; then
2687
+ tgz_path="$_tgz_candidate"
2688
+ break
2689
+ fi
2690
+ done
2691
+ fi
2468
2692
 
2469
2693
  # Export a sentinel so post-install.sh (triggered by npm's postinstall
2470
2694
  # lifecycle hook) knows it was launched from inside jishu-install.sh and
@@ -2480,10 +2704,10 @@ install_jishushell() {
2480
2704
  return 1
2481
2705
  fi
2482
2706
  else
2483
- log_detail "[$(date '+%H:%M:%S')] ${npm_bin} install -g jishushell ${npm_registry_args[*]:-}"
2484
- if ! log_cmd "$npm_bin" install -g jishushell ${npm_registry_args[@]+"${npm_registry_args[@]}"}; then
2707
+ log_detail "[$(date '+%H:%M:%S')] ${npm_bin} install -g ${jishushell_pkg_spec} ${npm_registry_args[*]:-}"
2708
+ if ! log_cmd "$npm_bin" install -g "${jishushell_pkg_spec}" "${npm_registry_args[@]}"; then
2485
2709
  unset JISHU_RUNNING_IN_INSTALLER
2486
- ui_error "npm install -g jishushell failed"
2710
+ ui_error "npm install -g ${jishushell_pkg_spec} failed"
2487
2711
  return 1
2488
2712
  fi
2489
2713
  fi
@@ -2787,8 +3011,17 @@ parse_args() {
2787
3011
  shift
2788
3012
  OPENCLAW_NPM_VERSION="${1:?--openclaw-version requires a version argument (e.g. 3.24)}"
2789
3013
  ;;
3014
+ --openclaw-docker-tag)
3015
+ shift
3016
+ OPENCLAW_DOCKER_TAG="${1:?--openclaw-docker-tag requires a tag argument (e.g. ghcr.io/x-aijishu/openclaw-runtime:2026.4.9)}"
3017
+ ;;
2790
3018
  --skip-jishushell) SKIP_JISHUSHELL=1 ;;
2791
3019
  --skip-jishushell-service) SKIP_JISHUSHELL_SERVICE=1 ;;
3020
+ --jishushell-version)
3021
+ shift
3022
+ JISHUSHELL_NPM_VERSION="${1:?--jishushell-version requires a version argument (e.g. 0.4.9)}"
3023
+ JISHUSHELL_VERSION_OVERRIDE=1
3024
+ ;;
2792
3025
  --skip)
2793
3026
  shift
2794
3027
  IFS=',' read -ra _steps <<< "${1:-}"
@@ -2854,10 +3087,16 @@ Options:
2854
3087
  --skip-docker Skip step 2: Docker installation
2855
3088
  --skip-nomad Skip step 3: Nomad installation
2856
3089
  --skip-openclaw Skip step 4: OpenClaw installation
3090
+ --openclaw-docker-tag <tag>
3091
+ Pull a specific OpenClaw image tag
3092
+ (e.g. --openclaw-docker-tag ghcr.io/x-aijishu/openclaw-runtime:2026.4.9)
2857
3093
  --skip-jishushell Skip step 5: JishuShell installation
2858
3094
  --skip-jishushell-service Skip step 6: JishuShell service registration
3095
+ --jishushell-version <ver>
3096
+ Install a specific jishushell version
3097
+ (e.g. --jishushell-version 0.4.9)
2859
3098
  --registry <url> Use a custom npm registry for all installs
2860
- (e.g. --registry http://10.188.0.22:4873/)
3099
+ (e.g. --registry http://127.0.0.1:4873/)
2861
3100
  --yes, -y Skip all confirmation prompts
2862
3101
  --help, -h Show this help message
2863
3102
 
@@ -2873,12 +3112,16 @@ Environment variables:
2873
3112
  JISHU_NODE_VERSION Specify Node.js major version (default: ${NODE_VERSION})
2874
3113
  JISHU_NVM_VERSION Specify nvm version (default: ${NVM_VERSION})
2875
3114
  JISHU_NOMAD_VERSION Specify Nomad version (default: ${NOMAD_VERSION})
3115
+ JISHUSHELL_NPM_VERSION
3116
+ Specify jishushell npm package version (default: latest)
2876
3117
  OPENCLAW_NPM_VERSION Specify openclaw npm package version (default: latest)
2877
- OPENCLAW_DOCKER_TAG Override built Docker image tag (default: jishushell-base:v1)
3118
+ OPENCLAW_DOCKER_TAG Override OpenClaw Docker image tag (default: ${OPENCLAW_DOCKER_TAG})
2878
3119
  NPM_REGISTRY Custom npm registry URL (same as --registry flag)
2879
3120
 
2880
- OpenClaw version flag (equivalent to OPENCLAW_NPM_VERSION env var):
2881
- --openclaw-version <ver> Install a specific openclaw version, e.g. --openclaw-version 3.24
3121
+ Version flags:
3122
+ --jishushell-version <ver> Install a specific jishushell version, e.g. --jishushell-version 0.4.9
3123
+ --openclaw-version <ver> Install a specific openclaw version, e.g. --openclaw-version 3.24
3124
+ --openclaw-docker-tag <tag> Pull a specific OpenClaw image tag, e.g. --openclaw-docker-tag ghcr.io/x-aijishu/openclaw-runtime:2026.4.9
2882
3125
  NO_PROMPT Set to 1 to skip interactive prompts
2883
3126
  VERBOSE Set to 1 for verbose output
2884
3127
 
@@ -2948,12 +3191,11 @@ _prompt_install_confirm() {
2948
3191
  fi
2949
3192
  if [[ $SKIP_NOMAD -eq 0 ]]; then
2950
3193
  echo -e " ${BOLD}Nomad${NC}"
2951
- echo -e " ${MUTED} Nomad v${NOMAD_VERSION}+ (>= 1.7.0)${NC}"
2952
- echo -e " ${MUTED} URL : https://github.com/hashicorp/nomad${NC}"
2953
- echo -e " ${MUTED} License : Business Source License 1.1 (BSL 1.1)${NC}"
2954
- echo -e " ${MUTED} https://github.com/hashicorp/nomad/blob/main/LICENSE${NC}"
2955
- echo -e " ${MUTED} Licensor: International Business Machines Corporation (IBM)${NC}"
2956
- echo -e " ${MUTED} Work : Nomad Version 1.7.0 or later. (c) 2024 IBM Corp.${NC}"
3194
+ echo -e " ${MUTED} Nomad v${NOMAD_VERSION} (last MPL 2.0 release in the 1.6.x line)${NC}"
3195
+ echo -e " ${MUTED} URL : https://github.com/hashicorp/nomad/tree/v${NOMAD_VERSION}${NC}"
3196
+ echo -e " ${MUTED} License : Mozilla Public License 2.0${NC}"
3197
+ echo -e " ${MUTED} https://github.com/hashicorp/nomad/blob/v${NOMAD_VERSION}/LICENSE${NC}"
3198
+ echo -e " ${MUTED} Author : HashiCorp, Inc.${NC}"
2957
3199
  echo ""
2958
3200
  fi
2959
3201
  echo -e " ${ACCENT}─────────────────────────────────────────────────────────${NC}"
@@ -6,11 +6,17 @@ if [[ "${CI:-}" == "true" || -n "${CI_PIPELINE_ID:-}" ]]; then
6
6
  exit 0
7
7
  fi
8
8
 
9
+ _post_install_has_tty() {
10
+ ( : > /dev/tty ) 2>/dev/null
11
+ }
12
+
9
13
  # Only run when installed globally via `npm install -g jishushell`.
10
14
  # Local installs (npm install, npm run build) skip post-install entirely.
11
15
  if [[ -n "${npm_lifecycle_event:-}" && "${npm_config_global:-false}" != "true" ]]; then
12
- _tty=/dev/tty
13
- [[ -w "$_tty" ]] || _tty=/dev/stderr
16
+ _tty=/dev/stderr
17
+ if _post_install_has_tty; then
18
+ _tty=/dev/tty
19
+ fi
14
20
  {
15
21
  echo ""
16
22
  echo " JishuShell requires global installation."
@@ -90,13 +96,60 @@ export JISHUSHELL_SKIP_NPM_INSTALL=1
90
96
  # Parse any extra args forwarded via npm (e.g. --dry-run, --yes, --skip-docker)
91
97
  parse_args "$@"
92
98
 
99
+ # ── Non-interactive upgrade mode ──────────────────────────────────────────────
100
+ # Web-triggered `npm install -g jishushell` runs without a controlling TTY, so
101
+ # postinstall must not block on sudo prompts or fail the package upgrade solely
102
+ # because privileged refresh steps cannot be confirmed interactively.
103
+ POST_INSTALL_BEST_EFFORT=0
104
+ configure_postinstall_mode() {
105
+ # Web-triggered upgrade: npm was spawned by the panel backend without a
106
+ # controlling TTY. Skip all privileged steps so npm can exit cleanly.
107
+ if [[ "${JISHUSHELL_WEB_UPDATE:-0}" == "1" ]]; then
108
+ POST_INSTALL_BEST_EFFORT=1
109
+ NO_PROMPT=1
110
+ SUDO=""
111
+ SKIP_DOCKER=1
112
+ SKIP_NOMAD=1
113
+ SKIP_OPENCLAW=1
114
+ SKIP_JISHUSHELL_SERVICE=1
115
+ return 0
116
+ fi
117
+
118
+ if [[ $EUID -eq 0 ]]; then
119
+ SUDO=""
120
+ return 0
121
+ fi
122
+
123
+ if ! command -v sudo &>/dev/null; then
124
+ return 0
125
+ fi
126
+
127
+ if sudo -n true 2>/dev/null; then
128
+ SUDO="sudo"
129
+ NO_PROMPT=1
130
+ return 0
131
+ fi
132
+
133
+ if _post_install_has_tty; then
134
+ return 0
135
+ fi
136
+
137
+ POST_INSTALL_BEST_EFFORT=1
138
+ NO_PROMPT=1
139
+ SUDO=""
140
+ SKIP_DOCKER=1
141
+ SKIP_NOMAD=1
142
+ SKIP_OPENCLAW=1
143
+ SKIP_JISHUSHELL_SERVICE=1
144
+ }
145
+
93
146
  # ── Log setup ─────────────────────────────────────────────────────────────────
94
147
  # npm captures lifecycle script stdout/stderr and only shows it on error.
95
148
  # Writing to /dev/tty bypasses that capture so progress is visible in real time.
96
149
  # The log file is always written regardless of tty availability.
97
150
  mkdir -p "${REAL_HOME}/.jishushell"
98
151
  LOG_FILE="${REAL_HOME}/.jishushell/post-install-$(date +%Y-%m-%d-%H-%M-%S)-$$.log"
99
- if [[ -w /dev/tty ]] && echo -n '' > /dev/tty 2>/dev/null; then
152
+ if _post_install_has_tty; then
100
153
  # tee writes to $LOG_FILE; its stdout (>/dev/tty) goes straight to the terminal,
101
154
  # bypassing npm's stdout capture entirely.
102
155
  exec > >(tee -a "$LOG_FILE" >/dev/tty) 2>&1
@@ -112,8 +165,14 @@ echo ""
112
165
  # ── Run install steps ─────────────────────────────────────────────────────────
113
166
  detect_os
114
167
  detect_arch
115
- check_sudo
116
- ensure_prerequisites
168
+ configure_postinstall_mode
169
+ if [[ "$POST_INSTALL_BEST_EFFORT" == "1" ]]; then
170
+ ui_warn "No interactive sudo is available during npm post-install — skipping privileged runtime checks and service refresh."
171
+ ui_info "The JishuShell package files were updated. If you need to reconcile system components later, run: jishushell install --yes"
172
+ else
173
+ check_sudo
174
+ ensure_prerequisites
175
+ fi
117
176
  run_install_components --with-jishushell || true
118
177
  _rc=$?
119
178
  show_summary --with-jishushell
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jishushell",
3
- "version": "0.4.10",
3
+ "version": "0.4.17",
4
4
  "description": "JishuShell - Multi-Agent Framework Management",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,9 +25,12 @@
25
25
  "test": "vitest run",
26
26
  "test:unit:backend": "vitest run tests/unit",
27
27
  "test:integration": "vitest run tests/integration",
28
+ "test:e2e:real": "npm run build:backend && vitest run --config vitest.e2e-real.config.ts",
29
+ "test:e2e:real:verbose": "npm run build:backend && vitest run --config vitest.e2e-real.config.ts --reporter=verbose",
28
30
  "test:watch": "vitest",
29
31
  "test:coverage": "vitest run --coverage",
30
32
  "test:ci": "npm run build:backend && vitest run --coverage && cd frontend && npm install && npm run test",
33
+ "test:ci:full": "npm run test:ci && vitest run --config vitest.e2e-real.config.ts",
31
34
  "prepublishOnly": "npm run build && chmod +x dist/cli.js",
32
35
  "postinstall": "bash install/post-install.sh",
33
36
  "preuninstall": "bash install/post-uninstall.sh",
@@ -51,7 +54,8 @@
51
54
  "bcryptjs": "^2.4.3",
52
55
  "fastify": "^5.2.0",
53
56
  "jsonwebtoken": "^9.0.2",
54
- "systeminformation": "^5.23.0"
57
+ "systeminformation": "^5.23.0",
58
+ "yaml": "^2.8.3"
55
59
  },
56
60
  "devDependencies": {
57
61
  "@types/bcryptjs": "^2.4.6",
@@ -0,0 +1 @@
1
+ import{h as B,j as e,L as E,k,l as M,m as R,n as K,o as L,q as A,t as I,v as T,w as W}from"./index-BuTQtuNy.js";import{r as m,u as D}from"./vendor-react-B1-3Yrt-.js";import{u as P}from"./usePolling-CK0DfI4h.js";import{u as C}from"./vendor-i18n-CfW0RvgE.js";function q(t){if(!t)return"-";const l=Math.floor(t/86400),d=Math.floor(t%86400/3600),r=Math.floor(t%3600/60);return l>0?`${l}d ${d}h`:d>0?`${d}h ${r}m`:`${r}m`}function G({status:t}){const{t:l}=C(),r={running:{cls:"bg-emerald-500/10 text-emerald-400 border border-emerald-500/20",labelKey:"status.running"},pending:{cls:"bg-amber-500/10 text-amber-400 border border-amber-500/20",labelKey:"status.starting"},failed:{cls:"bg-red-500/10 text-red-400 border border-red-500/20",labelKey:"status.failed"},dead:{cls:"bg-red-500/10 text-red-400 border border-red-500/20",labelKey:"status.crashed"}}[t]||{cls:"bg-[var(--card)] text-muted border border-[var(--border)]",labelKey:"status.stopped"};return e.jsx("span",{className:`inline-flex items-center text-xs px-2 py-0.5 rounded-full font-medium ${r.cls}`,children:l(r.labelKey)})}function z(){const{t}=C(["dashboard","common"]),[l,d]=m.useState([]),[r,$]=m.useState(null),[h,v]=m.useState(""),[y,j]=m.useState(""),[p,N]=m.useState(!1),g=D(),{showToast:c}=B(),u=()=>{K().then(s=>{d(s),j("")}).catch(s=>j(s.message||t("common:error.loadFailed"))),L().then($).catch(()=>{})};P(u,1e4);const _=async()=>{if(window.confirm(t("engine.restartConfirm"))){N(!0);try{await A(),c(t("engine.restarted"),"success"),setTimeout(u,2e3)}catch(s){c(s.message||t("engine.restartFailed"),"error")}finally{N(!1)}}},f=async(s,a,i)=>{s.stopPropagation(),v(`${i}-${a}`);try{let n=null;a==="start"&&(n=await I(i)),a==="stop"&&(n=await T(i)),a==="restart"&&(n=await W(i)),c(t(`common:action.${a}Done`),"success");const o=n==null?void 0:n.port_allocation;o&&typeof o.from=="number"&&typeof o.to=="number"&&o.from!==o.to&&c(t("common:toast.portReallocated",{defaultValue:"端口 {{from}} 被占用,已自动切换到 {{to}}",from:o.from,to:o.to}),"info"),setTimeout(u,1e3)}catch(n){c(n.message||t("common:error.operationFailed"),"error")}finally{v("")}},F=l.filter(s=>{var a;return((a=s.service)==null?void 0:a.status)==="running"}).length,b=!!r&&r.disk.percent>90,S=r?[{label:t("stats.runningInstances"),value:`${F} / ${l.length}`,sub:r.nomad_running?t("stats.engineRunning"):t("stats.engineStopped"),subColor:r.nomad_running?"text-emerald-400":"text-red-400",icon:e.jsxs("svg",{className:"w-4 h-4",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.5,children:[e.jsx("rect",{x:"2",y:"3",width:"20",height:"14",rx:"2"}),e.jsx("line",{x1:"8",y1:"21",x2:"16",y2:"21"}),e.jsx("line",{x1:"12",y1:"17",x2:"12",y2:"21"})]}),iconColor:"text-[#0066FF]",glowColor:"rgba(0,102,255,0.12)",accent:"border-l-2 border-l-[#0066FF]"},{label:t("stats.cpu"),value:`${r.cpu_percent}%`,sub:r.temperature?`${r.temperature}°C`:null,icon:e.jsxs("svg",{className:"w-4 h-4",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.5,children:[e.jsx("rect",{x:"4",y:"4",width:"16",height:"16",rx:"2"}),e.jsx("rect",{x:"9",y:"9",width:"6",height:"6"}),e.jsx("line",{x1:"9",y1:"2",x2:"9",y2:"4"}),e.jsx("line",{x1:"15",y1:"2",x2:"15",y2:"4"}),e.jsx("line",{x1:"9",y1:"20",x2:"9",y2:"22"}),e.jsx("line",{x1:"15",y1:"20",x2:"15",y2:"22"}),e.jsx("line",{x1:"20",y1:"9",x2:"22",y2:"9"}),e.jsx("line",{x1:"20",y1:"15",x2:"22",y2:"15"}),e.jsx("line",{x1:"2",y1:"9",x2:"4",y2:"9"}),e.jsx("line",{x1:"2",y1:"15",x2:"4",y2:"15"})]}),iconColor:"text-[#0066FF]",glowColor:"rgba(0,102,255,0.12)",accent:"border-l-2 border-l-[#0066FF]",warn:r.cpu_percent>90},{label:t("stats.memory"),value:`${r.memory.percent}%`,sub:`${r.memory.used_mb}MB / ${r.memory.total_mb}MB`,icon:e.jsx("svg",{className:"w-4 h-4",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.5,children:e.jsx("path",{d:"M6 19v-8m4 8v-4m4 4v-6m4 6v-2"})}),iconColor:"text-[#00D4AA]",glowColor:"rgba(0,212,170,0.12)",accent:"border-l-2 border-l-[#00D4AA]"},{label:t("stats.disk"),value:`${r.disk.percent}%`,sub:`${r.disk.used_gb}GB / ${r.disk.total_gb}GB`,icon:e.jsxs("svg",{className:"w-4 h-4",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.5,children:[e.jsx("ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}),e.jsx("path",{d:"M3 5v14a9 3 0 0018 0V5"}),e.jsx("line",{x1:"12",y1:"8",x2:"12",y2:"22"})]}),iconColor:b?"text-red-400":"text-[#0066FF]",glowColor:b?"rgba(239,68,68,0.12)":"rgba(0,102,255,0.12)",accent:b?"border-l-2 border-l-red-400":"border-l-2 border-l-[#0066FF]",warn:b}]:[];return e.jsxs("div",{className:"p-4 max-w-5xl mx-auto",children:[e.jsxs("div",{className:"mb-4 flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-base font-semibold text-foreground",children:t("title")}),e.jsx("p",{className:"text-xs text-muted mt-0.5",children:t("subtitle")})]}),e.jsx(E,{})]}),r&&e.jsx("div",{className:"grid grid-cols-2 lg:grid-cols-4 gap-3 mb-4",children:S.map(s=>e.jsxs("div",{className:`bg-[var(--card)] border border-[var(--border)] rounded-lg p-3 relative overflow-hidden hover:border-[var(--border-hover)] hover:bg-[var(--card-hover)] transition-all duration-200 ${s.accent}`,children:[e.jsx("div",{className:"absolute top-0 right-0 w-16 h-16 rounded-full opacity-60 pointer-events-none",style:{background:`radial-gradient(circle, ${s.glowColor} 0%, transparent 70%)`}}),e.jsxs("div",{className:"flex items-center justify-between mb-1.5",children:[e.jsx("span",{className:"text-[11px] text-muted",children:s.label}),e.jsx("span",{className:`${s.iconColor} opacity-80`,children:s.icon})]}),e.jsx("div",{className:`text-lg font-semibold ${s.warn?"text-red-400":"text-foreground"}`,children:s.value}),s.sub&&e.jsx("div",{className:`text-[11px] mt-0.5 truncate ${s.subColor??"text-muted"}`,children:s.sub})]},s.label))}),e.jsxs("div",{className:"bg-[var(--card)] border border-[var(--border)] rounded-xl overflow-hidden",children:[e.jsxs("div",{className:"px-4 py-2.5 border-b border-[var(--border)] flex items-center justify-between",children:[e.jsx("h2",{className:"text-sm font-medium text-foreground",children:t("instances.title")}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{onClick:()=>g("/instances/new"),className:"bg-[#0066FF] text-white px-3 py-1.5 rounded-md text-xs font-medium hover:bg-[#0066FF]/90 transition-all duration-200 shadow-[0_0_12px_rgba(0,102,255,0.3)]",children:t("instances.new")}),e.jsx("button",{onClick:()=>g("/instances/new?import=true"),className:"px-3 py-1.5 rounded-md text-xs font-medium text-muted border border-[var(--border)] bg-[var(--card)] hover:bg-[var(--card-hover)] hover:text-foreground transition-all duration-200",children:t("instances.import")}),e.jsxs("button",{onClick:_,disabled:p,title:t("instances.restartEngineTitle"),className:"flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-xs font-medium text-red-400 border border-red-500/20 bg-red-500/5 hover:bg-red-500/15 disabled:opacity-40 disabled:cursor-not-allowed transition-all duration-200",children:[e.jsx(k,{className:`w-3 h-3 ${p?"animate-spin":""}`}),t(p?"instances.restarting":"instances.restartEngine")]})]})]}),y?e.jsxs("div",{className:"text-center py-12 px-4",children:[e.jsx("p",{className:"text-sm text-red-400 mb-2",children:t("instances.loadError",{error:y})}),e.jsx("button",{onClick:u,className:"text-xs text-muted hover:text-foreground underline",children:t("common:action.retry")})]}):l.length===0?e.jsxs("div",{className:"text-center py-12 px-4",children:[e.jsx("div",{className:"text-muted opacity-40 mb-3",children:e.jsxs("svg",{className:"w-8 h-8 mx-auto",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1,strokeLinecap:"round",strokeLinejoin:"round",children:[e.jsx("rect",{x:"2",y:"3",width:"20",height:"14",rx:"2"}),e.jsx("line",{x1:"8",y1:"21",x2:"16",y2:"21"}),e.jsx("line",{x1:"12",y1:"17",x2:"12",y2:"21"})]})}),e.jsx("p",{className:"text-sm text-muted mb-1",children:t("instances.empty")}),e.jsx("p",{className:"text-xs text-muted opacity-60",children:t("instances.emptyHint")})]}):e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"text-xs text-muted border-b border-[var(--border)]",children:[e.jsx("th",{className:"text-left font-medium px-4 py-2",children:t("table.name")}),e.jsx("th",{className:"text-left font-medium px-4 py-2",children:t("table.status")}),e.jsx("th",{className:"text-left font-medium px-4 py-2 hidden sm:table-cell",children:t("table.uptime")}),e.jsx("th",{className:"text-left font-medium px-4 py-2 hidden md:table-cell",children:t("table.memory")}),e.jsx("th",{className:"text-right font-medium px-4 py-2",children:t("table.actions")})]})}),e.jsx("tbody",{className:"divide-y divide-[var(--border)]",children:l.map(s=>{var n,o,w;const a=((n=s.service)==null?void 0:n.status)||"stopped",i=a==="running";return e.jsxs("tr",{className:"hover:bg-[var(--card-hover)] cursor-pointer transition-colors duration-150",onClick:()=>g(`/instances/${s.id}`),children:[e.jsxs("td",{className:"px-4 py-2.5",children:[e.jsx("div",{className:"font-medium text-foreground",children:s.name}),e.jsx("div",{className:"text-xs text-muted font-mono",children:s.id})]}),e.jsx("td",{className:"px-4 py-2.5",children:e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx(G,{status:a}),((o=s.auto_backup)==null?void 0:o.enabled)&&e.jsx("span",{className:`text-xs leading-none ${s.auto_backup.last_backup_ok===!1?"text-red-400":"text-green-400"}`,title:s.auto_backup.last_backup_ok===!1?"Backup failed":"Backup OK",children:s.auto_backup.last_backup_ok===!1?"⚠":"●"})]})}),e.jsx("td",{className:"px-4 py-2.5 text-muted hidden sm:table-cell font-mono text-xs",children:i?q(s.service.uptime):"-"}),e.jsx("td",{className:"px-4 py-2.5 text-muted hidden md:table-cell font-mono text-xs",children:(w=s.service)!=null&&w.memory_mb?`${s.service.memory_mb} MB`:"-"}),e.jsx("td",{className:"px-4 py-2.5 text-right",children:e.jsx("div",{className:"inline-flex items-center gap-1",children:i||a==="pending"?e.jsxs(e.Fragment,{children:[e.jsx("button",{title:t("common:action.restart"),onClick:x=>f(x,"restart",s.id),disabled:!!h,className:"p-1.5 rounded-md text-muted hover:text-foreground hover:bg-[var(--card-hover)] disabled:opacity-30 transition-colors duration-150",children:e.jsx(k,{className:"w-3.5 h-3.5"})}),e.jsx("button",{title:t("common:action.stop"),onClick:x=>f(x,"stop",s.id),disabled:!!h,className:"p-1.5 rounded-md text-muted hover:text-red-400 hover:bg-red-500/10 disabled:opacity-30 transition-colors duration-150",children:e.jsx(M,{className:"w-3.5 h-3.5"})})]}):e.jsx("button",{title:t("common:action.start"),onClick:x=>f(x,"start",s.id),disabled:!!h,className:"p-1.5 rounded-md text-muted hover:text-emerald-400 hover:bg-emerald-500/10 disabled:opacity-30 transition-colors duration-150",children:e.jsx(R,{className:"w-3.5 h-3.5"})})})})]},s.id)})})]})]})]})}export{z as default};
@@ -0,0 +1 @@
1
+ import{j as e,L as j,T as v,I as w,a as N,c as y,s as F}from"./index-BuTQtuNy.js";import{r}from"./vendor-react-B1-3Yrt-.js";import{u as S}from"./vendor-i18n-CfW0RvgE.js";const x="w-full bg-[var(--input-bg)] border border-[var(--border)] rounded-md px-3 py-2.5 text-sm text-foreground placeholder-[var(--muted)] focus:outline-none focus:ring-1 focus:ring-[#0066FF]/60 focus:border-[#0066FF]/60 transition-colors duration-200";function C({onDone:h}){const{t}=S("auth"),[s,p]=r.useState(""),[n,b]=r.useState(""),[o,f]=r.useState(!1),[l,d]=r.useState(""),[c,m]=r.useState(!1),u=s.length>=8&&s===n,g=async a=>{if(a.preventDefault(),!!u){d(""),m(!0);try{const i=await y(s);F(i.token),h()}catch(i){d(i.message||t("init.failed"))}finally{m(!1)}}};return e.jsxs("div",{className:"min-h-screen flex items-center justify-center p-4 bg-background relative overflow-hidden",children:[e.jsx("div",{className:"grid-bg absolute inset-0 pointer-events-none"}),e.jsx("div",{className:"absolute top-1/3 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[500px] h-[300px] rounded-full bg-[#0066FF]/[0.07] blur-[90px] pointer-events-none"}),e.jsx("div",{className:"absolute top-4 right-4 z-20",children:e.jsx(j,{})}),e.jsxs("div",{className:"w-full max-w-sm relative z-10",children:[e.jsxs("div",{className:"text-center mb-8",children:[e.jsx(v,{className:"w-40 h-40 mx-auto mb-4 object-contain"}),e.jsx("p",{className:"text-sm text-muted mt-1",children:t("init.title")})]}),e.jsxs("form",{onSubmit:g,className:"space-y-4",children:[l&&e.jsx("div",{className:"bg-red-500/10 border border-red-500/20 text-red-400 text-sm rounded-lg px-3 py-2.5",children:l}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium text-muted mb-1.5",children:t("init.password")}),e.jsxs("div",{className:"relative",children:[e.jsx("input",{type:o?"text":"password",value:s,onChange:a=>p(a.target.value),placeholder:t("init.passwordPlaceholder"),className:`${x} pr-10`,autoFocus:!0}),e.jsx("button",{type:"button",onClick:()=>f(!o),className:"absolute right-3 top-1/2 -translate-y-1/2 text-muted hover:text-foreground transition-colors duration-200",children:o?e.jsx(w,{}):e.jsx(N,{})})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium text-muted mb-1.5",children:t("init.confirm")}),e.jsx("input",{type:o?"text":"password",value:n,onChange:a=>b(a.target.value),placeholder:t("init.confirmPlaceholder"),className:x}),s&&n&&s!==n&&e.jsx("p",{className:"text-red-400 text-xs mt-1.5",children:t("init.mismatch")})]}),e.jsx("button",{type:"submit",disabled:c||!u,className:"w-full bg-[#0066FF] text-white rounded-md py-2.5 text-sm font-medium hover:bg-[#0066FF]/90 disabled:opacity-40 disabled:cursor-not-allowed transition-all duration-200 shadow-[0_0_20px_rgba(0,102,255,0.3)] hover:shadow-[0_0_28px_rgba(0,102,255,0.45)]",children:c?e.jsxs("span",{className:"inline-flex items-center gap-2",children:[e.jsx("span",{className:"w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin"}),t("init.loading")]}):t("init.submit")})]})]})]})}export{C as default};