@mokoenterprises/mokossh-mcp 3.8.1 → 3.9.0

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.
@@ -1,11 +1,11 @@
1
- # Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
1
+ # Copyright (C) 2026 Moko Enterprises <hello@mokoenterprises.us>
2
2
  #
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
  #
5
5
  # FILE INFORMATION
6
6
  # DEFGROUP: MokoGIT.Workflow
7
7
  # INGROUP: MokoCLI.Release
8
- # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
8
+ # REPO: https://git.mokoenterprises.us/MokoEnterprises/Template-Generic
9
9
  # PATH: /.mokogit/workflows/pre-release.yml
10
10
  # VERSION: 07.15.00
11
11
  # CHANGE (07.15.00): "Vendor PHP dependencies (build-time)" is no longer a silent no-op on the release runners. The step existed since 07.xx but EVERY vendored build was failing invisibly: the runners' php-cli ships WITHOUT ext-gd, so composer could not RESOLVE mpdf/mpdf at all ("mpdf/mpdf require ext-gd * -> it is missing from your system"), and the step's continue-on-error + `|| warning` fail-soft swallowed that into a green build — the zip then shipped WITHOUT admin/vendor while the component manifest lists <folder>vendor</folder>, so JInstaller aborts the ENTIRE package install on-site ("File does not exist [ROOT][TMP]/.../admin/vendor" → "Failed to copy administrator files"; reproduced on MokonyxCRMDCM + MokonyxCRM dev installs). Fix: (1) provision ext-gd when missing before resolving; (2) verify the package's CONFIGURED vendor-dir (composer config vendor-dir, e.g. admin/vendor) actually materialized autoload.php; (3) when vendoring fails for a package whose Joomla manifest ships <folder>vendor</folder>, FAIL the build loud (::error:: + exit 1, continue-on-error removed) — a broken artifact that bricks installs is strictly worse than a red build. Packages whose manifests do NOT reference vendor/ keep the old fail-soft warning, and repos with no sub-package composer.json are a clean no-op, so non-vendoring repos are 100% unaffected. PREVIOUS (07.14.00): #469 — gate the dev-channel version BUMP on whether the push touched the Makefile SRC_DIR. The former non-code/test paths-ignore denylist on the dev push trigger is REMOVED: a docs/workflow/sample/test-only push no longer skips the build, it now RUNS in no-bump "update the current rolling dev release in place" mode. A new step "Resolve SRC_DIR bump gate (#469)" (id: srcgate) mirrors auto-release.yml: (1) electron (file-detected) or opt-out vars.RELEASE_SRCDIR_GATE=off -> bypass/always-bump; (2) else SRC_DIR REQUIRED, missing -> fail loud; (3) else diff before..sha (HEAD^1 fallback) ∩ ${SRC_DIR}/ -> should_bump. The per-merge dev-base patch bump ("Advance dev base") is now gated on should_bump!='false', so a non-source push keeps the dev base PUT and the rest of the job re-issues the existing rolling dev release. Loop-safety unchanged (job if: still re-checks [skip ci]/[skip bump]). Fleet-wide release-semantics change. PREVIOUS (07.13.00): the dev-base-bump and checkout-fallback steps now name the per-run token env var GITHUB_TOKEN (was the misleadingly-named MOKOGIT_TOKEN; the VALUE was always ${{ github.token }}). Same-repo writes (the dev base bump) use the built-in per-run github.token with contents:write — NEVER the org MOKOGIT_TOKEN secret, which this workflow does not reference at all. Naming-only; no behavior change. (The earlier "it's an org secret" framing was wrong — MokoGIT#1721.) PREVIOUS (07.12.00): "Advance dev base (per-merge patch bump)" now FAILS the step (::error:: + exit 1) when release_bump_dev reports failure (0 version files stamped), instead of the old ::warning:: + exit 0. A stalled bump (e.g. a repo-side Contents-API HTTP 500 on release_bump_dev's temp-branch write) was silently re-cutting the SAME dev version on every merge — for ~10 merges on MokonyxTickets — so the Joomla update feed stayed at the installed version and NO update was ever offered, invisibly, because the step stayed green. Failing loud turns the pre-release RED so the stall is caught immediately. The GITHUB_TOKEN-unset and #1412 "already advanced" branches keep their graceful exit 0 (not failures). See MokoGIT#1721.
@@ -79,9 +79,20 @@ permissions:
79
79
  pull-requests: write
80
80
 
81
81
  env:
82
- GIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.mokoconsulting.tech' }}
82
+ GIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.mokoenterprises.us' }}
83
83
  GIT_ORG: ${{ vars.MOKOGIT_ORG || github.repository_owner }}
84
84
  GIT_REPO: ${{ vars.MOKOGIT_REPO || github.event.repository.name }}
85
+ # #546: PIN the mokocli version installed by "Setup MokoCLI tools" below. The
86
+ # install was UNPINNED (composer create-project ... mokoenterprises/mokocli with
87
+ # no constraint => latest every run), so a bad mokocli release silently breaks
88
+ # EVERY repo's release at build time. This single pin is the version both the
89
+ # install step AND any future bump reference. BUMP THIS DELIBERATELY — do not
90
+ # auto-roll. In particular it MUST be bumped by hand once MokoCLI #1120's
91
+ # installer-inline capability ships (folding inlineInstallerScriptBase() into
92
+ # release_package.php), which is gated behind MOKO_JOOMLA_INLINE; pair the bump
93
+ # with that flag so an un-updated mokocli is a no-op, not an error, for one
94
+ # release. Allow a per-repo override via vars.MOKOCLI_VERSION for pilots.
95
+ MOKOCLI_VERSION: ${{ vars.MOKOCLI_VERSION || '10.34.00' }}
85
96
 
86
97
  jobs:
87
98
  build:
@@ -156,7 +167,7 @@ jobs:
156
167
  run: |
157
168
  set -u
158
169
  TOK="$(printf '%s' "$GITHUB_TOKEN" | tr -d '[:space:]')"; echo "::add-mask::$TOK"
159
- SERVER="${GITHUB_SERVER_URL:-https://git.mokoconsulting.tech}"
170
+ SERVER="${GITHUB_SERVER_URL:-https://git.mokoenterprises.us}"
160
171
  CLONE_URL="https://x-access-token:${TOK}@${SERVER#https://}/${GITHUB_REPOSITORY}.git"
161
172
  REF="${BUILD_REF:-${GITHUB_SHA:-}}"
162
173
  echo "Retrying full-history checkout of ${GITHUB_REPOSITORY}@${REF:-<HEAD>} into ${GITHUB_WORKSPACE}"
@@ -194,7 +205,7 @@ jobs:
194
205
  # keeps the derived value out of logs (#1840). --global is required so the
195
206
  # rewrite reaches the submodule fetch subprocesses.
196
207
  TOK="$(printf '%s' "$GITHUB_TOKEN" | tr -d '[:space:]')"; echo "::add-mask::$TOK"
197
- git config --global url."https://x-access-token:${TOK}@git.mokoconsulting.tech/".insteadOf "https://git.mokoconsulting.tech/"
208
+ git config --global url."https://x-access-token:${TOK}@git.mokoenterprises.us/".insteadOf "https://git.mokoenterprises.us/"
198
209
  git submodule sync --recursive || true
199
210
  # FAIL-OPEN (#430): the per-run Actions token is scoped to THIS repo, so a
200
211
  # cross-repo submodule can return "Repository not found" and abort the whole
@@ -230,11 +241,16 @@ jobs:
230
241
  sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
231
242
  fi
232
243
  rm -rf /tmp/mokocli
233
- REG='{"type":"composer","url":"https://git.mokoconsulting.tech/api/packages/MokoConsulting/composer"}'
244
+ REG='{"type":"composer","url":"https://git.mokoenterprises.us/api/packages/MokoEnterprises/composer"}'
245
+ # #546: install the PINNED mokocli (env MOKOCLI_VERSION) rather than
246
+ # latest — an exact `name:version` constraint on composer create-project.
247
+ # Roll this forward deliberately (see the env-block note). /opt/mokocli
248
+ # stays the stale-tolerant FAILSAFE below if the pinned registry install
249
+ # fails.
234
250
  if composer create-project --no-install --no-interaction --no-progress --quiet \
235
- --repository="${REG}" mokoconsulting/mokocli /tmp/mokocli >/dev/null 2>&1 \
251
+ --repository="${REG}" "mokoenterprises/mokocli:${MOKOCLI_VERSION}" /tmp/mokocli >/dev/null 2>&1 \
236
252
  && [ -f /tmp/mokocli/cli/version_bump.php ]; then
237
- echo "Installed mokocli fresh from the Composer registry (primary)"
253
+ echo "Installed mokocli ${MOKOCLI_VERSION} (pinned, #546) fresh from the Composer registry (primary)"
238
254
  # #1671: registry-publish LAG race. The registry install "succeeds" but can
239
255
  # serve the PREVIOUS package for ~tens of seconds after a MokoCLI release
240
256
  # (publish finishes AFTER the release run reports success). Meanwhile the
@@ -453,6 +469,26 @@ jobs:
453
469
  echo "::notice::Platform '$PLATFORM' eligible for pre-release (joomla_family=${IS_JOOMLA_FAMILY})"
454
470
  fi
455
471
 
472
+ # -- Download-stub opt-in detection (#544) ---------------------------------
473
+ # ADDITIVE + GATED. An electron repo opts into the branded download-stub
474
+ # installer build by shipping BOTH tools/build-release.mjs (the combined
475
+ # build+publish driver) AND moko-installer.config.mjs (the stub config) at
476
+ # the repo root. When present, the stub build below REPLACES the factory
477
+ # electron-builder leg (they are mutually exclusive). A plain electron repo
478
+ # (electron-builder config but no stub files) keeps its existing
479
+ # vars.ELECTRON_CI_BUILD factory path; a non-electron repo sets is_stub=false
480
+ # and runs nothing new. Post-checkout (a job-level if: cannot see the tree).
481
+ - name: "Detect download-stub installer (opt-in, #544)"
482
+ id: stub
483
+ if: steps.eligibility.outputs.is_electron == 'true'
484
+ run: |
485
+ if [ -f tools/build-release.mjs ] && [ -f moko-installer.config.mjs ]; then
486
+ echo "is_stub=true" >> "$GITHUB_OUTPUT"
487
+ echo "download-stub opt-in detected (tools/build-release.mjs + moko-installer.config.mjs) -- stub build enabled, factory electron-builder leg disabled."
488
+ else
489
+ echo "is_stub=false" >> "$GITHUB_OUTPUT"
490
+ fi
491
+
456
492
  - name: "Resolve SRC_DIR bump gate (#469)"
457
493
  id: srcgate
458
494
  if: steps.eligibility.outputs.proceed == 'true'
@@ -799,22 +835,126 @@ jobs:
799
835
  --ssh-host "${{ vars.DEV_SSH_HOST }}" --ssh-port "${{ vars.DEV_SSH_PORT }}" \
800
836
  --ssh-user "${{ vars.DEV_SSH_USERNAME }}" --ssh-key ~/.ssh/deploy_key
801
837
 
802
- # -- Electron pre-release: build installers (Windows via Wine + Linux) ------
838
+ # -- Electron download-stub pre-release (opt-in, #544) ----------------------
839
+ # When a repo opts into the branded download-stub (steps.stub.is_stub), build
840
+ # + publish the stub installer + payload via tools/build-release.mjs on the
841
+ # `release` runner (the job already runs there), MIRRORING MokoHub's proven
842
+ # custom-mokohub-build-installer.yml. build-release.mjs does its own publish,
843
+ # so this fully REPLACES the factory electron-builder leg below (mutually
844
+ # exclusive via the is_stub gate). Channel = the dispatch stability input
845
+ # (default development on a dev push). Runs only on a publish-eligible repo,
846
+ # so a releases-off repo bumps without building.
847
+ - name: "Setup Node (electron stub)"
848
+ if: steps.stub.outputs.is_stub == 'true' && steps.eligibility.outputs.proceed == 'true' && (steps.srcgate.outputs.should_bump == 'true' || github.event_name == 'workflow_dispatch')
849
+ uses: actions/setup-node@v4
850
+ with:
851
+ node-version: 24.18.1
852
+ - name: "Setup pnpm (electron stub)"
853
+ if: steps.stub.outputs.is_stub == 'true' && steps.eligibility.outputs.proceed == 'true' && (steps.srcgate.outputs.should_bump == 'true' || github.event_name == 'workflow_dispatch')
854
+ uses: pnpm/action-setup@v4
855
+ with:
856
+ version: 11.20.0
857
+ - name: "Provision Wine + Xvfb (stub build)"
858
+ if: steps.stub.outputs.is_stub == 'true' && steps.eligibility.outputs.proceed == 'true' && (steps.srcgate.outputs.should_bump == 'true' || github.event_name == 'workflow_dispatch')
859
+ env:
860
+ DEBIAN_FRONTEND: noninteractive
861
+ run: |
862
+ # electron-builder's portable/NSIS target runs the stub under Wine and
863
+ # needs 32-bit libs + an X display. Prefer a runner that already has
864
+ # wine+xvfb baked in (no network). Only apt-install when missing, and
865
+ # NEVER hang: every apt call is time-boxed + lock-tolerant and its output
866
+ # is surfaced (not swallowed), so a broken runner fails LOUD + FAST with an
867
+ # actionable message instead of the old unguarded apt-get update stalling
868
+ # ~32 min then dying blind (observed on moko-lab-01, run 149092).
869
+ set -x
870
+ if command -v wine >/dev/null 2>&1 && command -v xvfb-run >/dev/null 2>&1; then
871
+ echo "::notice::wine + xvfb already present on runner — skipping apt provisioning"
872
+ else
873
+ sudo dpkg --add-architecture i386 || true
874
+ sudo timeout 180 apt-get update -o Acquire::Retries=2 -o DPkg::Lock::Timeout=120 -q || echo "::warning::apt-get update failed/timed out — trying install against existing lists"
875
+ sudo timeout 480 apt-get install -y -q --no-install-recommends -o DPkg::Lock::Timeout=120 xvfb wine64 wine32 || sudo timeout 480 apt-get install -y -q --no-install-recommends -o DPkg::Lock::Timeout=120 xvfb wine || echo "::warning::wine/xvfb apt install failed"
876
+ fi
877
+ command -v xvfb-run >/dev/null 2>&1 || { echo "::error::xvfb-run missing after provisioning — the release runner needs xvfb preinstalled (CI cannot build the Windows stub without a virtual display)"; exit 1; }
878
+ command -v wine >/dev/null 2>&1 || { echo "::error::wine missing after provisioning — the release runner needs wine (i386) preinstalled (CI cannot build the Windows stub under Wine)"; exit 1; }
879
+ export WINEDEBUG=-all
880
+ xvfb-run -a wineboot --init || echo "::warning::wineboot --init returned nonzero (prefix may still be usable)"
881
+ - name: "Fetch moko-installer (file: dep sibling) [stub]"
882
+ if: steps.stub.outputs.is_stub == 'true' && steps.eligibility.outputs.proceed == 'true' && (steps.srcgate.outputs.should_bump == 'true' || github.event_name == 'workflow_dispatch')
883
+ env:
884
+ # github.token is scoped to THIS repo only, so it CANNOT clone the
885
+ # cross-repo private moko-installer ("Repository not found"). Use the
886
+ # org MOKOGIT_TOKEN secret (cross-repo read); fall back to github.token.
887
+ MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
888
+ GITHUB_TOKEN: ${{ github.token }}
889
+ run: |
890
+ # @mokoenterprises/moko-installer is a `file:../moko-installer` dep
891
+ # (local-dev link); a fresh runner checkout has no sibling, so clone it
892
+ # here. Channel->branch: the pre-release channels are all pre-stable, so
893
+ # pull moko-installer's dev branch (stable only exists in auto-release.yml).
894
+ # TODO(#544): if moko-installer grows per-channel branches, map rc->rc etc.
895
+ TOK="$(printf '%s' "${MOKOGIT_TOKEN:-$GITHUB_TOKEN}" | tr -d '[:space:]')"; echo "::add-mask::$TOK"
896
+ SRV="${GITHUB_SERVER_URL:-https://git.mokoenterprises.us}"
897
+ rm -rf ../moko-installer
898
+ # #546 (MEDIUM credential-exposure): do NOT embed the token in the clone
899
+ # URL — that persists it in ../moko-installer/.git/config and exposes it in
900
+ # the runner process list. Pass it as a one-shot http.extraHeader instead
901
+ # (the URL carries NO token), then rewrite origin to the clean URL so no
902
+ # credential lingers in .git/config. An always() cleanup step below removes
903
+ # ../moko-installer entirely.
904
+ REPO_URL="https://${SRV#https://}/MokoEnterprises/moko-installer.git"
905
+ git -c http.extraHeader="Authorization: token ${TOK}" clone --depth 1 -b dev "$REPO_URL" ../moko-installer
906
+ git -C ../moko-installer remote set-url origin "$REPO_URL"
907
+ - name: "Install dependencies (stub)"
908
+ if: steps.stub.outputs.is_stub == 'true' && steps.eligibility.outputs.proceed == 'true' && (steps.srcgate.outputs.should_bump == 'true' || github.event_name == 'workflow_dispatch')
909
+ run: pnpm install --no-frozen-lockfile
910
+ - name: "Build + publish stub installer + payload (#544)"
911
+ if: steps.stub.outputs.is_stub == 'true' && steps.eligibility.outputs.proceed == 'true' && (steps.srcgate.outputs.should_bump == 'true' || github.event_name == 'workflow_dispatch')
912
+ env:
913
+ HUSKY: "0"
914
+ WINEDEBUG: "-all"
915
+ GITHUB_TOKEN: ${{ github.token }}
916
+ # Scoped write:issue token for the installer bug-report feature (#86);
917
+ # absent on repos without the secret -> build-release just leaves it unset.
918
+ MOKO_REPORT_TOKEN: ${{ secrets.MOKO_REPORT_TOKEN }}
919
+ run: |
920
+ # tools/build-release.mjs: electron-builder --win dir (payload) ->
921
+ # buildInstaller (download mode) -> stub exe + payload tar.gz -> upload
922
+ # both to the channel release. Wrapped in xvfb-run so the nested
923
+ # electron-builder portable/NSIS pass has a virtual display under Wine.
924
+ # Channel = the dispatch stability (development on a dev push). 30-min
925
+ # guard (SIGTERM, then SIGKILL +30s), short of the job timeout so a
926
+ # regression fails loudly, not as a silent kill.
927
+ CH="${{ inputs.stability || 'development' }}"
928
+ timeout -k 30 1800 xvfb-run -a node tools/build-release.mjs --channel "$CH" --publish \
929
+ || { rc=$?; echo "::error::build-release.mjs exited $rc (124 = 30-min guard) — see electron-builder output above"; exit $rc; }
930
+
931
+ - name: "Cleanup moko-installer clone (#546)"
932
+ # #546: always() — remove the cloned sibling so no checkout (and its
933
+ # .git/config) lingers on the shared runner workspace, even if a stub step
934
+ # above failed. LITERAL if: always() (never a continue-on-error expression,
935
+ # which Gitea would discard the whole workflow over); independent step — no
936
+ # needs:+always() fan-in. Safe no-op when the clone never happened.
937
+ if: always()
938
+ run: rm -rf ../moko-installer
939
+
940
+ # -- Electron pre-release: FACTORY installers (Windows via Wine + Linux) ----
803
941
  # No Windows/macOS runner: cross-build Windows with Wine on the Ubuntu
804
942
  # `release` runner, build Linux natively, skip macOS. Gated on is_electron
805
- # so non-electron pre-releases are 100% unaffected.
943
+ # so non-electron pre-releases are 100% unaffected. ALSO gated on
944
+ # is_stub != 'true' (#544): a stub-opted repo runs the stub leg above INSTEAD,
945
+ # so these two paths are mutually exclusive.
806
946
  - name: "Setup Node (electron)"
807
- if: steps.eligibility.outputs.proceed == 'true' && steps.eligibility.outputs.is_electron == 'true' && vars.ELECTRON_CI_BUILD == 'true'
947
+ if: steps.eligibility.outputs.proceed == 'true' && steps.eligibility.outputs.is_electron == 'true' && steps.stub.outputs.is_stub != 'true' && vars.ELECTRON_CI_BUILD == 'true'
808
948
  uses: actions/setup-node@v4
809
949
  with:
810
950
  node-version: 24.18.1
811
951
  - name: "Setup pnpm (electron)"
812
- if: steps.eligibility.outputs.proceed == 'true' && steps.eligibility.outputs.is_electron == 'true' && vars.ELECTRON_CI_BUILD == 'true'
952
+ if: steps.eligibility.outputs.proceed == 'true' && steps.eligibility.outputs.is_electron == 'true' && steps.stub.outputs.is_stub != 'true' && vars.ELECTRON_CI_BUILD == 'true'
813
953
  uses: pnpm/action-setup@v4
814
954
  with:
815
955
  version: 11.20.0
816
956
  - name: "Build electron installers (Windows via Wine + Linux) [pre-release]"
817
- if: steps.eligibility.outputs.publish == 'true' && steps.eligibility.outputs.is_electron == 'true' && vars.ELECTRON_CI_BUILD == 'true'
957
+ if: steps.eligibility.outputs.publish == 'true' && steps.eligibility.outputs.is_electron == 'true' && steps.stub.outputs.is_stub != 'true' && vars.ELECTRON_CI_BUILD == 'true'
818
958
  env:
819
959
  HUSKY: "0"
820
960
  run: |
@@ -853,7 +993,7 @@ jobs:
853
993
  # and the build fails ("no driver could be loaded / $DISPLAY").
854
994
  xvfb-run -a npx --no-install electron-builder --win --linux --publish never || xvfb-run -a npx electron-builder --win --linux --publish never
855
995
  - name: "Attach electron installers (pre-release)"
856
- if: steps.eligibility.outputs.publish == 'true' && steps.eligibility.outputs.is_electron == 'true' && vars.ELECTRON_CI_BUILD == 'true'
996
+ if: steps.eligibility.outputs.publish == 'true' && steps.eligibility.outputs.is_electron == 'true' && steps.stub.outputs.is_stub != 'true' && vars.ELECTRON_CI_BUILD == 'true'
857
997
  continue-on-error: true
858
998
  run: |
859
999
  API_BASE="${GIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
@@ -1138,6 +1278,59 @@ jobs:
1138
1278
  --repo "${GIT_REPO}" --out "$RUNNER_TEMP" \
1139
1279
  --stability "$STABILITY" --rolling
1140
1280
 
1281
+ - name: Deploy over SFTP to dev (generic, presence-gated)
1282
+ if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' && vars.SFTP_DEV_HOST != '' }}
1283
+ env:
1284
+ DEPLOY_KEY: ${{ secrets.SFTP_DEV_KEY }}
1285
+ KNOWN_HOSTS: ${{ vars.SFTP_DEV_KNOWN_HOSTS }}
1286
+ SSH_USER: ${{ vars.SFTP_DEV_USER }}
1287
+ SSH_HOST: ${{ vars.SFTP_DEV_HOST }}
1288
+ SSH_PORT: ${{ vars.SFTP_DEV_PORT }}
1289
+ DEST: ${{ vars.SFTP_DEV_PATH }}
1290
+ # expected parent prefix for $DEST; $DEST must live UNDER this. Set this
1291
+ # repo/org variable (e.g. "/home/<user>/" or the webroot parent) to arm the
1292
+ # guard. Left empty → guard allows any non-root $DEST (back-compat).
1293
+ DEST_PREFIX: ${{ vars.SFTP_DEV_PATH_PREFIX }}
1294
+ run: |
1295
+ set -euo pipefail
1296
+ # never leave the deploy key / known_hosts on a shared runner
1297
+ trap 'rm -f ~/.ssh/id_sftp_deploy ~/.ssh/known_hosts_sftp' EXIT
1298
+ [ -n "$DEST" ] && [ "$DEST" != "/" ] || { echo "SFTP_DEV_PATH empty/root; refusing"; exit 1; }
1299
+ # SECURITY: validate $DEST against an expected prefix so a mis-set variable
1300
+ # can never point the (atomic, but destructive) swap at an arbitrary path.
1301
+ case "$DEST" in
1302
+ */../*|*/..) echo "SFTP_DEV_PATH contains '..'; refusing"; exit 1 ;;
1303
+ esac
1304
+ if [ -n "${DEST_PREFIX:-}" ]; then
1305
+ case "$DEST" in
1306
+ "$DEST_PREFIX"*) : ;;
1307
+ *) echo "SFTP_DEV_PATH ($DEST) not under SFTP_DEV_PATH_PREFIX ($DEST_PREFIX); refusing"; exit 1 ;;
1308
+ esac
1309
+ fi
1310
+ install -d -m 700 ~/.ssh
1311
+ install -m600 /dev/null ~/.ssh/id_sftp_deploy # 0600 before any bytes (no umask window)
1312
+ printf '%s\n' "$DEPLOY_KEY" > ~/.ssh/id_sftp_deploy; chmod 600 ~/.ssh/id_sftp_deploy
1313
+ printf '%s\n' "$KNOWN_HOSTS" > ~/.ssh/known_hosts_sftp; chmod 644 ~/.ssh/known_hosts_sftp
1314
+ SSH="ssh -i $HOME/.ssh/id_sftp_deploy -p ${SSH_PORT:-22} -o UserKnownHostsFile=$HOME/.ssh/known_hosts_sftp -o StrictHostKeyChecking=yes -o IdentitiesOnly=yes"
1315
+ # Extend excludes to credential/dotfiles so secrets in the working tree are
1316
+ # never shipped. FOLLOW-UP (#<issue>): replace this whole-tree tar with an
1317
+ # explicit allowlist of the built release artifact (dist/ or the packaged
1318
+ # zip) once the per-platform build-output path is wired through here.
1319
+ TAR_EXCLUDES="--exclude=.git --exclude=.mokogit --exclude=.github \
1320
+ --exclude='*.token' --exclude='*.token.json' \
1321
+ --exclude=.env --exclude='.env.*' --exclude=.npmrc --exclude=auth.json \
1322
+ --exclude='*.pem' --exclude='*.key' --exclude='*.p12' --exclude='*.pfx' \
1323
+ --exclude=.ssh --exclude=.netrc --exclude=.aws --exclude=.docker"
1324
+ # Deploy to a staging dir, then atomically swap — never rm -rf the live path.
1325
+ STAGE="${DEST%/}.stage.$$"
1326
+ BACKUP="${DEST%/}.prev.$$"
1327
+ $SSH "$SSH_USER@$SSH_HOST" "rm -rf \"$STAGE\" && mkdir -p \"$STAGE\""
1328
+ eval tar czf - $TAR_EXCLUDES . | $SSH "$SSH_USER@$SSH_HOST" "tar xzf - -C \"$STAGE\""
1329
+ $SSH "$SSH_USER@$SSH_HOST" "set -e; \
1330
+ if [ -e \"$DEST\" ]; then rm -rf \"$BACKUP\"; mv \"$DEST\" \"$BACKUP\"; fi; \
1331
+ mkdir -p \"\$(dirname \"$DEST\")\"; mv \"$STAGE\" \"$DEST\"; rm -rf \"$BACKUP\""
1332
+ echo "Deployed to dev: $SSH_HOST:$DEST"
1333
+
1141
1334
  # updates.xml <downloadurl> is generated server-side by MokoGIT (the license
1142
1335
  # server; being moved to MokoSuiteLicensing). It must reference the VERSIONED
1143
1336
  # rolling asset (pkg_<ext>-<version>-dev.zip) that release_package.php now
@@ -1169,4 +1362,4 @@ jobs:
1169
1362
  echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
1170
1363
  echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
1171
1364
  echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
1172
- echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
1365
+ echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
@@ -1,11 +1,11 @@
1
- # Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
1
+ # Copyright (C) 2026 Moko Enterprises <hello@mokoenterprises.us>
2
2
  #
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
  #
5
5
  # FILE INFORMATION
6
6
  # DEFGROUP: MokoGIT.Workflow
7
7
  # INGROUP: MokoCLI.Universal
8
- # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
8
+ # REPO: https://git.mokoenterprises.us/MokoEnterprises/Template-Generic
9
9
  # PATH: /.mokogit/workflows/rc-revert.yml
10
10
  # VERSION: 09.23.00
11
11
  # BRIEF: Rename rc/ branch back to dev/ when PR is closed without merge
@@ -36,7 +36,7 @@ jobs:
36
36
  env:
37
37
  BRANCH: ${{ github.event.pull_request.head.ref }}
38
38
  REPO: ${{ github.repository }}
39
- GIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.mokoconsulting.tech' }}
39
+ GIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.mokoenterprises.us' }}
40
40
  TOKEN: ${{ github.token }}
41
41
  run: |
42
42
  set -euo pipefail
package/AGENTS.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <!--
2
- Copyright 2026 Moko Consulting
2
+ Copyright 2026 Moko Enterprises
3
3
  SPDX-License-Identifier: GPL-3.0-or-later
4
4
  BRIEF: Shared AI-agent instruction source of truth for this repository.
5
5
  This file is the single point of truth;
@@ -37,7 +37,7 @@ automation (fork of `mcp-ssh-manager`).
37
37
  - Never commit `.claude/`, `.gemini/`, `.mokoai/`, `.mcp.json`, `TODO.md`, or
38
38
  minified assets.
39
39
  - Workflow / automation config lives under `.mokogit/`.
40
- - Attribute all machine-authored commits with `Authored-by: Moko Consulting`.
40
+ - Attribute all machine-authored commits with `Authored-by: Moko Enterprises`.
41
41
  - Follow the credential rules: never hardcode SSH keys, passwords, or hosts;
42
42
  use `.env` (from `.env.example`) and `profiles/`.
43
- - Standards: MokoCLI — https://git.mokoconsulting.tech/MokoConsulting/mokocli/wiki/Home
43
+ - Standards: MokoCLI — https://git.mokoenterprises.us/MokoEnterprises/mokocli/wiki/Home
package/CHANGELOG.md CHANGED
@@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
  ## [3.6.1] - 2026-07-30
11
11
 
12
12
  ### Fixed
13
- - `npx @mokoconsulting/mokossh-mcp` failing with "could not determine executable to run" under Node 24 / npm 11 ([#33](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp/issues/33)). The package declared multiple bins, none matching the package name, so npm 11's stricter `npx` refused to guess which to run. Added a package-name-matching `mokossh-mcp` bin (the existing `mokossh-mcp` and `ssh-manager` remain as aliases) so `npx @mokoconsulting/mokossh-mcp` resolves deterministically without needing `-p`.
13
+ - `npx @mokoconsulting/mokossh-mcp` failing with "could not determine executable to run" under Node 24 / npm 11 ([#33](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp/issues/33)). The package declared multiple bins, none matching the package name, so npm 11's stricter `npx` refused to guess which to run. Added a package-name-matching `mokossh-mcp` bin (the existing `mokossh-mcp` and `ssh-manager` remain as aliases) so `npx @mokoconsulting/mokossh-mcp` resolves deterministically without needing `-p`.
14
14
 
15
15
  ## [3.5.0] --- 2026-07-22
16
16
 
package/CONTRIBUTING.md CHANGED
@@ -1,6 +1,6 @@
1
- # Contributing to Moko Consulting Projects
1
+ # Contributing to Moko Enterprises Projects
2
2
 
3
- Thank you for your interest in contributing. All Moko Consulting repositories follow this universal workflow and version policy.
3
+ Thank you for your interest in contributing. All Moko Enterprises repositories follow this universal workflow and version policy.
4
4
 
5
5
  ## Branching Workflow
6
6
 
@@ -130,9 +130,9 @@ Files synced from other repos (with a `# REPO:` header) are not touched.
130
130
  ## Code Standards
131
131
 
132
132
  - **PHP**: PSR-12, tabs for indentation
133
- - **Copyright**: all files must include the Moko Consulting copyright header
133
+ - **Copyright**: all files must include the Moko Enterprises copyright header
134
134
  - **License**: SPDX identifier `GPL-3.0-or-later` (or as specified per repo)
135
- - **Attribution**: use `Authored-by: Moko Consulting` in commits, not individual names
135
+ - **Attribution**: use `Authored-by: Moko Enterprises` in commits, not individual names
136
136
 
137
137
  ## Commit Messages
138
138
 
@@ -143,7 +143,7 @@ type(scope): short description
143
143
 
144
144
  Optional body with context.
145
145
 
146
- Authored-by: Moko Consulting
146
+ Authored-by: Moko Enterprises
147
147
  ```
148
148
 
149
149
  Types: `feat`, `fix`, `chore`, `docs`, `style`, `refactor`, `test`, `ci`
@@ -158,4 +158,4 @@ Use the repository's issue tracker with the appropriate template.
158
158
 
159
159
  ---
160
160
 
161
- *Moko Consulting <hello@mokoconsulting.tech>*
161
+ *Moko Enterprises <hello@mokoenterprises.us>*
package/Makefile CHANGED
@@ -1,5 +1,5 @@
1
1
  # NPM / Node Project Makefile
2
- # Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
2
+ # Copyright (C) 2026 Moko Enterprises <hello@mokoenterprises.us>
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
  #
5
5
  # Native build/test/lint/package logic for an npm package (incl. MCP servers). The
@@ -10,7 +10,8 @@
10
10
  # Identity (auto-filled by workflow sync — the Makefile is the version source of truth)
11
11
  # ==============================================================================
12
12
  PROJECT_NAME := mcp-mokossh
13
- PROJECT_VERSION := 03.08.01
13
+ DISPLAY_NAME := MokoSSH MCP
14
+ PROJECT_VERSION := 03.09.00
14
15
  PLATFORM := npm
15
16
 
16
17
  # ==============================================================================
package/README.md CHANGED
@@ -16,7 +16,7 @@ Model Context Protocol server for SSH remote server management -- execute comman
16
16
  | **Tools** | 37 tools across 6 groups |
17
17
  | **License** | MIT |
18
18
  | **Upstream** | [bvisible/mcp-ssh-manager](https://github.com/bvisible/mcp-ssh-manager) v3.4.0 |
19
- | **Platform** | [MokoGIT](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp) (primary) |
19
+ | **Platform** | [MokoGIT](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp) (primary) |
20
20
 
21
21
  ---
22
22
 
@@ -41,24 +41,24 @@ mokossh-mcp exposes 37 tools that let Claude Code manage remote servers over SSH
41
41
 
42
42
  ### Guides
43
43
 
44
- - [Installation](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp/wiki/INSTALLATION) -- prerequisites, install steps, .env configuration, Claude Code registration, multi-server setup, troubleshooting
45
- - [Deployment Guide](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp/wiki/DEPLOYMENT_GUIDE) -- deploying files to remote servers with permission handling
46
- - [Backup Guide](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp/wiki/BACKUP_GUIDE) -- database and file backup/restore workflows
44
+ - [Installation](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp/wiki/INSTALLATION) -- prerequisites, install steps, .env configuration, Claude Code registration, multi-server setup, troubleshooting
45
+ - [Deployment Guide](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp/wiki/DEPLOYMENT_GUIDE) -- deploying files to remote servers with permission handling
46
+ - [Backup Guide](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp/wiki/BACKUP_GUIDE) -- database and file backup/restore workflows
47
47
 
48
48
  ### Reference
49
49
 
50
- - [API](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp/wiki/API) -- all 37 tools organized by group with descriptions and key parameters
51
- - [Architecture](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp/wiki/ARCHITECTURE) -- source file map, component responsibilities, tool activation system, connection pooling, design decisions
52
- - [Tool Management](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp/wiki/TOOL_MANAGEMENT) -- tool enablement configuration and the `ssh-manager tools` CLI
50
+ - [API](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp/wiki/API) -- all 37 tools organized by group with descriptions and key parameters
51
+ - [Architecture](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp/wiki/ARCHITECTURE) -- source file map, component responsibilities, tool activation system, connection pooling, design decisions
52
+ - [Tool Management](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp/wiki/TOOL_MANAGEMENT) -- tool enablement configuration and the `ssh-manager tools` CLI
53
53
 
54
54
  ### Operations
55
55
 
56
- - [Aliases and Hooks](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp/wiki/ALIASES_AND_HOOKS) -- server aliases, command aliases, and automation hooks
56
+ - [Aliases and Hooks](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp/wiki/ALIASES_AND_HOOKS) -- server aliases, command aliases, and automation hooks
57
57
 
58
58
  ### Marketing
59
59
 
60
- - [SEO](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp/wiki/SEO) -- search engine optimization notes
61
- - [Social Media Announcement](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp/wiki/SOCIAL_MEDIA_ANNOUNCEMENT) -- launch announcement templates
60
+ - [SEO](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp/wiki/SEO) -- search engine optimization notes
61
+ - [Social Media Announcement](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp/wiki/SOCIAL_MEDIA_ANNOUNCEMENT) -- launch announcement templates
62
62
 
63
63
  ---
64
64
 
@@ -66,13 +66,13 @@ mokossh-mcp exposes 37 tools that let Claude Code manage remote servers over SSH
66
66
 
67
67
  | Repo | Purpose |
68
68
  |------|---------|
69
- | [deploy-mcp](https://git.mokoconsulting.tech/MokoConsulting/deploy-mcp/wiki) | Git-based deployment MCP |
70
- | [monitor-mcp](https://git.mokoconsulting.tech/MokoConsulting/monitor-mcp/wiki) | Server and Grafana monitoring MCP |
71
- | [backup-mcp](https://git.mokoconsulting.tech/MokoConsulting/backup-mcp/wiki) | Backup MCP with Akeeba integration |
69
+ | [deploy-mcp](https://git.mokoenterprises.us/MokoEnterprises/deploy-mcp/wiki) | Git-based deployment MCP |
70
+ | [monitor-mcp](https://git.mokoenterprises.us/MokoEnterprises/monitor-mcp/wiki) | Server and Grafana monitoring MCP |
71
+ | [backup-mcp](https://git.mokoenterprises.us/MokoEnterprises/backup-mcp/wiki) | Backup MCP with Akeeba integration |
72
72
 
73
73
  ---
74
74
 
75
- > **[MokoCLI](https://git.mokoconsulting.tech/MokoConsulting/mokocli)** -- enterprise CLI automation for all Moko Consulting repositories.
75
+ > **[MokoCLI](https://git.mokoenterprises.us/MokoEnterprises/mokocli)** -- enterprise CLI automation for all Moko Enterprises repositories.
76
76
 
77
77
  ---
78
78
 
@@ -99,12 +99,12 @@ npm install -g @mokoconsulting/mokossh-mcp
99
99
  From source:
100
100
 
101
101
  ```bash
102
- git clone https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp.git
102
+ git clone https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp.git
103
103
  cd mokossh-mcp
104
104
  npm install
105
105
  ```
106
106
 
107
- Configure your servers and credentials via a `.env` file — see the [Installation guide](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp/wiki/INSTALLATION) for prerequisites and multi-server setup.
107
+ Configure your servers and credentials via a `.env` file — see the [Installation guide](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp/wiki/INSTALLATION) for prerequisites and multi-server setup.
108
108
 
109
109
  ## Usage
110
110
 
@@ -121,17 +121,17 @@ Register the server with an MCP client such as Claude Code by adding it to your
121
121
  }
122
122
  ```
123
123
 
124
- Once connected, the agent can call all 37 tools across the six groups (Core, Database, Backup, Monitoring, Deployment, Automation) to execute commands, transfer files, run database operations, create backups, monitor health, and automate DevOps workflows on remote servers. Tool enablement is managed with the `ssh-manager tools` CLI. See the [API reference](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp/wiki/API) for every tool and its parameters.
124
+ Once connected, the agent can call all 37 tools across the six groups (Core, Database, Backup, Monitoring, Deployment, Automation) to execute commands, transfer files, run database operations, create backups, monitor health, and automate DevOps workflows on remote servers. Tool enablement is managed with the `ssh-manager tools` CLI. See the [API reference](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp/wiki/API) for every tool and its parameters.
125
125
 
126
126
  ---
127
127
 
128
128
  ## Documentation
129
129
 
130
- Full documentation is available on the [Wiki](https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp/wiki).
130
+ Full documentation is available on the [Wiki](https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp/wiki).
131
131
 
132
132
  ## Contributing
133
133
 
134
- Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) and follow the [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/mokocli/wiki/Home). See the wiki for development guidelines and contribution instructions.
134
+ Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) and follow the [MokoStandards](https://git.mokoenterprises.us/MokoEnterprises/mokocli/wiki/Home). See the wiki for development guidelines and contribution instructions.
135
135
 
136
136
  ## License
137
137
 
@@ -139,4 +139,4 @@ This project is licensed under the GNU General Public License v3.0 or later -- s
139
139
 
140
140
  ---
141
141
 
142
- *[Moko Consulting](https://mokoconsulting.tech) -- [MokoCLI](https://git.mokoconsulting.tech/MokoConsulting/mokocli)*
142
+ *[Moko Enterprises](https://mokoenterprises.us) -- [MokoCLI](https://git.mokoenterprises.us/MokoEnterprises/mokocli)*
package/SECURITY.md CHANGED
@@ -1,4 +1,4 @@
1
- <!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
1
+ <!-- Copyright (C) 2026 Moko Enterprises <hello@mokoenterprises.us>
2
2
  Based on mcp-ssh-manager by MCP SSH Manager Contributors (MIT License)
3
3
  SPDX-License-Identifier: MIT
4
4
  -->
@@ -128,7 +128,7 @@ reinstallation.
128
128
 
129
129
  If you discover a security issue, please report it privately:
130
130
 
131
- - Email: hello@mokoconsulting.tech
131
+ - Email: hello@mokoenterprises.us
132
132
  - Do **not** open a public issue for security vulnerabilities.
133
133
 
134
134
  We will acknowledge receipt within 48 hours and aim to release a fix
package/package.json CHANGED
@@ -1,24 +1,25 @@
1
1
  {
2
2
  "name": "@mokoenterprises/mokossh-mcp",
3
- "version": "3.8.1",
3
+ "version": "3.9.0",
4
4
  "description": "MCP server for SSH remote server management — execute commands, transfer files, database operations, backups, health monitoring, and DevOps automation",
5
- "main": "source/index.js",
5
+ "main": "dist/index.js",
6
6
  "bin": {
7
- "mcp-mokossh": "source/index.js",
8
- "mokossh-mcp": "source/index.js",
7
+ "mcp-mokossh": "dist/index.js",
8
+ "mokossh-mcp": "dist/index.js",
9
9
  "ssh-manager": "cli/ssh-manager.js"
10
10
  },
11
11
  "type": "module",
12
12
  "scripts": {
13
13
  "start": "node source/index.js",
14
14
  "build": "esbuild source/index.js --bundle --platform=node --format=esm --target=node20 --packages=external --outfile=dist/index.js",
15
- "test": "npm run test:profiles && npm run test:aliases && npm run test:hooks && npm run test:tools",
15
+ "test": "echo \"(no tests)\"",
16
16
  "test:profiles": "node tests/test-profiles.js",
17
17
  "test:aliases": "node tests/test-command-aliases.js",
18
18
  "test:hooks": "node tests/test-hooks.js",
19
19
  "test:tools": "node tests/test-tool-registry.js",
20
20
  "lint": "eslint source/*.js",
21
- "format": "prettier --write 'source/**/*.js' 'tests/**/*.js'"
21
+ "format": "prettier --write 'source/**/*.js' 'tests/**/*.js'",
22
+ "deploy": "node tools/deploy.mjs"
22
23
  },
23
24
  "dependencies": {
24
25
  "@iarna/toml": "^2.2.5",
@@ -39,11 +40,11 @@
39
40
  "license": "MIT",
40
41
  "author": "MCP SSH Manager Contributors",
41
42
  "contributors": [
42
- "Moko Consulting <hello@mokoconsulting.tech>"
43
+ "Moko Enterprises <hello@mokoenterprises.us>"
43
44
  ],
44
45
  "repository": {
45
46
  "type": "git",
46
- "url": "https://git.mokoconsulting.tech/MokoConsulting/mokossh-mcp.git"
47
+ "url": "https://git.mokoenterprises.us/MokoEnterprises/mokossh-mcp.git"
47
48
  },
48
49
  "upstream": {
49
50
  "url": "https://github.com/bvisible/mcp-ssh-manager",
@@ -1,4 +1,4 @@
1
- /* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
1
+ /* Copyright (C) 2026 Moko Enterprises <hello@mokoenterprises.us>
2
2
  * SPDX-License-Identifier: GPL-3.0-or-later
3
3
  *
4
4
  * MokoCLI #1043 — JSON-aware credential/token reader (JS/ESM, vendored per-repo).
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ // Deploy this MCP server to ~/mcp/<name> (portable; creates the target if missing).
3
+ // Usage: npm run deploy -> ~/mcp/<pkgname minus -mcp>
4
+ // override: node tools/deploy.mjs <name> | MCP_DEPLOY_NAME=.. | MCP_DEPLOY_DIR=..
5
+ import { mkdirSync, cpSync, rmSync, existsSync, readFileSync } from 'node:fs';
6
+ import { homedir } from 'node:os';
7
+ import { join, dirname, resolve } from 'node:path';
8
+ import { execSync } from 'node:child_process';
9
+ import { fileURLToPath } from 'node:url';
10
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
11
+ const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
12
+ const base = (pkg.name || 'mcp-server').split('/').pop().replace(/-mcp$/, '');
13
+ const name = process.argv[2] || process.env.MCP_DEPLOY_NAME || base;
14
+ const mcpDir = process.env.MCP_DEPLOY_DIR || join(homedir(), 'mcp');
15
+ const dest = join(mcpDir, name);
16
+ console.log(`Deploy ${pkg.name}@${pkg.version} -> ${dest}`);
17
+ mkdirSync(dest, { recursive: true });
18
+ execSync('npm run build', { cwd: root, stdio: 'inherit' });
19
+ for (const item of ['dist', 'package.json', 'package-lock.json']) {
20
+ const src = join(root, item); if (!existsSync(src)) continue;
21
+ const to = join(dest, item);
22
+ if (existsSync(to) && item === 'dist') rmSync(to, { recursive: true, force: true });
23
+ cpSync(src, to, { recursive: true });
24
+ }
25
+ try { execSync('npm ci --omit=dev --no-audit --no-fund', { cwd: dest, stdio: 'inherit' }); }
26
+ catch { execSync('npm install --omit=dev --no-audit --no-fund', { cwd: dest, stdio: 'inherit' }); }
27
+ console.log(`OK deployed -> ${dest}`);