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.
- package/INSTALL-NOTICE +10 -12
- package/dist/cli/app.d.ts +3 -0
- package/dist/cli/app.js +156 -0
- package/dist/cli/app.js.map +1 -0
- package/dist/{doctor.d.ts → cli/doctor.d.ts} +6 -1
- package/dist/{doctor.js → cli/doctor.js} +343 -14
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/helpers.d.ts +4 -0
- package/dist/cli/helpers.js +32 -0
- package/dist/cli/helpers.js.map +1 -0
- package/dist/cli/job.d.ts +3 -0
- package/dist/cli/job.js +260 -0
- package/dist/cli/job.js.map +1 -0
- package/dist/cli/llm.d.ts +24 -0
- package/dist/cli/llm.js +593 -0
- package/dist/cli/llm.js.map +1 -0
- package/dist/cli/openclaw.d.ts +12 -0
- package/dist/cli/openclaw.js +156 -0
- package/dist/cli/openclaw.js.map +1 -0
- package/dist/cli/panel.d.ts +25 -0
- package/dist/cli/panel.js +734 -0
- package/dist/cli/panel.js.map +1 -0
- package/dist/cli.js +67 -326
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +11 -4
- package/dist/config.js.map +1 -1
- package/dist/control.d.ts +13 -41
- package/dist/control.js +12 -1355
- package/dist/control.js.map +1 -1
- package/dist/routes/apps.d.ts +3 -0
- package/dist/routes/apps.js +99 -0
- package/dist/routes/apps.js.map +1 -0
- package/dist/routes/instances.js +12 -6
- package/dist/routes/instances.js.map +1 -1
- package/dist/routes/llm.d.ts +15 -0
- package/dist/routes/llm.js +246 -0
- package/dist/routes/llm.js.map +1 -0
- package/dist/routes/setup.js +29 -2
- package/dist/routes/setup.js.map +1 -1
- package/dist/routes/system.js +31 -6
- package/dist/routes/system.js.map +1 -1
- package/dist/server.js +40 -4
- package/dist/server.js.map +1 -1
- package/dist/services/app-compiler.d.ts +15 -0
- package/dist/services/app-compiler.js +169 -0
- package/dist/services/app-compiler.js.map +1 -0
- package/dist/services/app-manager.d.ts +17 -0
- package/dist/services/app-manager.js +168 -0
- package/dist/services/app-manager.js.map +1 -0
- package/dist/services/instance-manager.d.ts +51 -3
- package/dist/services/instance-manager.js +233 -30
- package/dist/services/instance-manager.js.map +1 -1
- package/dist/services/job-manager.d.ts +22 -0
- package/dist/services/job-manager.js +102 -0
- package/dist/services/job-manager.js.map +1 -0
- package/dist/services/llm-proxy/adapters.js +5 -1
- package/dist/services/llm-proxy/adapters.js.map +1 -1
- package/dist/services/llm-proxy/index.d.ts +30 -0
- package/dist/services/llm-proxy/index.js +71 -1
- package/dist/services/llm-proxy/index.js.map +1 -1
- package/dist/services/llm-proxy/ssrf.js +1 -1
- package/dist/services/llm-proxy/ssrf.js.map +1 -1
- package/dist/services/nomad-manager.js +192 -29
- package/dist/services/nomad-manager.js.map +1 -1
- package/dist/services/panel-manager.d.ts +40 -0
- package/dist/services/panel-manager.js +346 -0
- package/dist/services/panel-manager.js.map +1 -0
- package/dist/services/process-manager.js +20 -7
- package/dist/services/process-manager.js.map +1 -1
- package/dist/services/setup-manager.js +316 -31
- package/dist/services/setup-manager.js.map +1 -1
- package/dist/services/update-manager.d.ts +47 -0
- package/dist/services/update-manager.js +305 -0
- package/dist/services/update-manager.js.map +1 -0
- package/dist/types.d.ts +62 -0
- package/install/jishu-install.sh +279 -37
- package/install/post-install.sh +64 -5
- package/package.json +6 -2
- package/public/assets/Dashboard-CQsp1Mr9.js +1 -0
- package/public/assets/InitPassword-BEC8SE4A.js +1 -0
- package/public/assets/InstanceDetail-B5wTgNEg.js +17 -0
- package/public/assets/{Login-CUoEZOWR.js → Login-D1Bt-Lyk.js} +1 -1
- package/public/assets/NewInstance-GQzm3K9D.js +1 -0
- package/public/assets/Settings-ByjGlqhP.js +1 -0
- package/public/assets/Setup-cMF21Y-8.js +1 -0
- package/public/assets/index-B6qQP4mH.css +1 -0
- package/public/assets/index-BuTQtuNy.js +16 -0
- package/public/assets/{providers-lBSOjUWy.js → providers-V-vwrExZ.js} +1 -1
- package/public/index.html +2 -2
- package/dist/doctor.js.map +0 -1
- package/install/jishu-install-china.sh +0 -3092
- package/public/assets/Dashboard-DhsrzJ4F.js +0 -1
- package/public/assets/InitPassword-BjubiVdd.js +0 -1
- package/public/assets/InstanceDetail-DMcywsof.js +0 -17
- package/public/assets/NewInstance-Bk0G4EiJ.js +0 -1
- package/public/assets/Settings-D5tHL_h5.js +0 -1
- package/public/assets/Setup-4t6E3Rut.js +0 -1
- package/public/assets/index-BJ47MWpF.css +0 -1
- package/public/assets/index-DbX85irc.js +0 -16
package/install/jishu-install.sh
CHANGED
|
@@ -118,7 +118,11 @@ is_promptable() {
|
|
|
118
118
|
if [[ "${NO_PROMPT:-0}" == "1" ]]; then
|
|
119
119
|
return 1
|
|
120
120
|
fi
|
|
121
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
#
|
|
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 [[
|
|
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
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
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
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
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
|
|
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
|
|
2673
|
+
ui_info "Installing ${jishushell_pkg_spec} from ${NPM_REGISTRY}..."
|
|
2452
2674
|
else
|
|
2453
|
-
ui_info "Installing
|
|
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
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
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
|
|
2484
|
-
if ! log_cmd "$npm_bin" install -g
|
|
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
|
|
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://
|
|
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
|
-
|
|
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
|
-
|
|
2881
|
-
|
|
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}
|
|
2952
|
-
echo -e " ${MUTED} URL : https://github.com/hashicorp/nomad${NC}"
|
|
2953
|
-
echo -e " ${MUTED} License :
|
|
2954
|
-
echo -e " ${MUTED} https://github.com/hashicorp/nomad/blob/
|
|
2955
|
-
echo -e " ${MUTED}
|
|
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}"
|
package/install/post-install.sh
CHANGED
|
@@ -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/
|
|
13
|
-
|
|
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
|
|
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
|
-
|
|
116
|
-
|
|
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.
|
|
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};
|