@mokoconsulting/mcp-mokosuite 1.0.0 → 1.1.0-dev.372
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/.mokogit/workflows/auto-bump.yml +8 -0
- package/.mokogit/workflows/auto-release.yml +184 -71
- package/.mokogit/workflows/cascade-dev.yml +128 -60
- package/.mokogit/workflows/changelog-validation.yml +41 -25
- package/.mokogit/workflows/ci-issue-reporter.yml +1 -1
- package/.mokogit/workflows/cleanup.yml +3 -3
- package/.mokogit/workflows/custom-workflow-sync-trigger.yml +83 -0
- package/.mokogit/workflows/issue-branch.yml +1 -1
- package/.mokogit/workflows/npm-build-test.yml +103 -19
- package/.mokogit/workflows/npm-prerelease.yml +174 -0
- package/.mokogit/workflows/npm-publish.yml +44 -17
- package/.mokogit/workflows/pr-check.yml +181 -53
- package/.mokogit/workflows/pre-release.yml +174 -35
- package/.mokogit/workflows/rc-revert.yml +1 -1
- package/.mokogit/workflows/version-set.yml +4 -4
- package/AGENTS.md +39 -0
- package/CHANGELOG.md +46 -10
- package/dist/index.js +28 -1
- package/docs/API.md +54 -45
- package/package.json +1 -1
- package/src/index.ts +59 -1
- package/.mokogit/workflows/ci-generic.yml +0 -203
- package/.mokogit/workflows/repo-health.yml +0 -700
- package/.mokogit/workflows/standards-compliance.yml +0 -2507
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
# INGROUP: MokoCLI.Release
|
|
8
8
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
|
9
9
|
# PATH: /.mokogit/workflows/pre-release.yml
|
|
10
|
-
# VERSION: 06.
|
|
11
|
-
# BRIEF: Batched pre-release on nightly schedule + manual dispatch (rolling dev/rc asset, build-metadata version — #404)
|
|
10
|
+
# VERSION: 06.01.00
|
|
11
|
+
# BRIEF: Batched pre-release on nightly schedule + manual dispatch (rolling dev/rc asset, build-metadata version — #404); non-Joomla MokoGIT release + cosmetic release-notes are best-effort. npm publishing moved OUT of this universal workflow into Template-NPM's dedicated npm-publish.yml so it can't leak into non-npm repos via sync.
|
|
12
12
|
|
|
13
13
|
name: "Universal: Pre-Release"
|
|
14
14
|
|
|
@@ -19,8 +19,32 @@ name: "Universal: Pre-Release"
|
|
|
19
19
|
# as BUILD METADATA (version_build_metadata.php) instead of a committed bump, and
|
|
20
20
|
# the asset is published under a FIXED rolling name replaced in place.
|
|
21
21
|
on:
|
|
22
|
+
# Dev-channel release on every merge to dev. Uses build-metadata versioning
|
|
23
|
+
# (version_build_metadata.php) so nothing is committed back to dev — the #404
|
|
24
|
+
# manifest <version> merge-conflict problem does NOT return. paths-ignore is a
|
|
25
|
+
# non-code denylist: a push touching only docs / CI config / repo metadata does
|
|
26
|
+
# not build. The concurrency guard below collapses a burst of merges into one.
|
|
27
|
+
push:
|
|
28
|
+
branches:
|
|
29
|
+
- dev
|
|
30
|
+
paths-ignore:
|
|
31
|
+
- '.mokogit/**'
|
|
32
|
+
- '.gitea/**'
|
|
33
|
+
- '.github/**'
|
|
34
|
+
- '.vscode/**'
|
|
35
|
+
- '.idea/**'
|
|
36
|
+
- 'docs/**'
|
|
37
|
+
- 'wiki/**'
|
|
38
|
+
- '**/*.md'
|
|
39
|
+
- '.editorconfig'
|
|
40
|
+
- '.gitignore'
|
|
41
|
+
- '.gitattributes'
|
|
42
|
+
- '.gitmodules'
|
|
43
|
+
- '.gitmessage'
|
|
44
|
+
- 'phpstan.neon'
|
|
45
|
+
- 'LICENSE'
|
|
22
46
|
schedule:
|
|
23
|
-
# Nightly at 07:00 UTC —
|
|
47
|
+
# Nightly at 07:00 UTC — safety-net batch for any dev merge a push build missed.
|
|
24
48
|
- cron: '0 7 * * *'
|
|
25
49
|
workflow_dispatch:
|
|
26
50
|
inputs:
|
|
@@ -40,6 +64,14 @@ on:
|
|
|
40
64
|
default: dev
|
|
41
65
|
type: string
|
|
42
66
|
|
|
67
|
+
concurrency:
|
|
68
|
+
# Per channel + ref: a burst of dev merges collapses to the latest build. A
|
|
69
|
+
# manual alpha/beta/rc dispatch has its own group, so it is never cancelled by
|
|
70
|
+
# a dev push (and vice versa); the nightly runs on the default-branch ref, a
|
|
71
|
+
# different group again.
|
|
72
|
+
group: pre-release-${{ github.ref }}-${{ inputs.stability || 'development' }}
|
|
73
|
+
cancel-in-progress: true
|
|
74
|
+
|
|
43
75
|
permissions:
|
|
44
76
|
contents: write
|
|
45
77
|
|
|
@@ -53,14 +85,15 @@ jobs:
|
|
|
53
85
|
name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
|
|
54
86
|
runs-on: release
|
|
55
87
|
# Skip on template repos (Template-*) — they scaffold other repos and do not release.
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
88
|
+
# Triggers: push to dev (per-merge dev-channel build), the nightly schedule
|
|
89
|
+
# (safety-net batch), and manual workflow_dispatch. The push trigger is scoped
|
|
90
|
+
# to `dev` + a non-code paths-ignore, so a template-sync/doc-only push can't
|
|
91
|
+
# reach it (the ~150x storm on chore/mokoonyx-template-sync-* stays prevented).
|
|
92
|
+
# As defence-in-depth we also refuse a manual dispatch aimed at a bot/sync ref.
|
|
61
93
|
if: >-
|
|
62
94
|
!startsWith(github.event.repository.name, 'Template-') &&
|
|
63
95
|
(
|
|
96
|
+
github.event_name == 'push' ||
|
|
64
97
|
github.event_name == 'schedule' ||
|
|
65
98
|
(
|
|
66
99
|
github.event_name == 'workflow_dispatch' &&
|
|
@@ -73,9 +106,9 @@ jobs:
|
|
|
73
106
|
)
|
|
74
107
|
|
|
75
108
|
steps:
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
#
|
|
109
|
+
# Build ref: the dispatch input if given, else `dev` — which is correct for
|
|
110
|
+
# both a push (only `dev` triggers it) and the nightly schedule (where
|
|
111
|
+
# github.ref_name would otherwise be the default branch, main).
|
|
79
112
|
- name: Resolve build ref
|
|
80
113
|
id: ref
|
|
81
114
|
run: echo "ref=${{ inputs.ref || 'dev' }}" >> "$GITHUB_OUTPUT"
|
|
@@ -88,9 +121,32 @@ jobs:
|
|
|
88
121
|
ref: ${{ steps.ref.outputs.ref }}
|
|
89
122
|
submodules: recursive
|
|
90
123
|
|
|
91
|
-
- name: Update submodules to
|
|
124
|
+
- name: Update submodules to latest
|
|
125
|
+
# Skip cleanly on submodule-less repos. The old step was a silent no-op:
|
|
126
|
+
# `2>/dev/null || true` swallowed every error and the in-step `git pull`
|
|
127
|
+
# used an UN-credentialed remote, so private submodules never actually
|
|
128
|
+
# updated. Credential the private remote, then update each submodule to its
|
|
129
|
+
# tracked branch (default: the submodule's default branch) so the built
|
|
130
|
+
# package contains the latest submodule content. Per #404 this is
|
|
131
|
+
# working-tree only (no commit) — the package is built from the working tree.
|
|
132
|
+
run: |
|
|
133
|
+
if [ -f .gitmodules ]; then
|
|
134
|
+
git config --global url."https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@git.mokoconsulting.tech/".insteadOf "https://git.mokoconsulting.tech/"
|
|
135
|
+
git submodule sync --recursive
|
|
136
|
+
git submodule update --init --remote --recursive
|
|
137
|
+
else
|
|
138
|
+
echo "No .gitmodules — skipping submodule update."
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
- name: Isolate workspace (remove foreign/stale checkouts)
|
|
92
142
|
run: |
|
|
93
|
-
|
|
143
|
+
find . -name .git -not -path './.git' -prune -print | while read nested; do
|
|
144
|
+
dir=$(dirname "$nested")
|
|
145
|
+
[ "$dir" = "." ] && continue
|
|
146
|
+
git ls-files --error-unmatch "$dir" >/dev/null 2>&1 && continue
|
|
147
|
+
echo "::warning::Removing untracked nested checkout: $dir"
|
|
148
|
+
rm -rf "$dir"
|
|
149
|
+
done
|
|
94
150
|
|
|
95
151
|
- name: Setup MokoCLI tools
|
|
96
152
|
env:
|
|
@@ -100,19 +156,39 @@ jobs:
|
|
|
100
156
|
# Use pre-installed /opt/mokocli if available (updated by cron every 6h)
|
|
101
157
|
if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/cli/manifest_element.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
|
|
102
158
|
echo Using pre-installed /opt/mokocli
|
|
159
|
+
# Keep the pre-installed copy current with mokocli main (auto-refresh; soft — never fails the run).
|
|
160
|
+
if git -C /opt/mokocli rev-parse --git-dir >/dev/null 2>&1; then
|
|
161
|
+
git -C /opt/mokocli pull --ff-only --quiet 2>/dev/null || echo " (mokocli git refresh skipped)"
|
|
162
|
+
REFRESH_TOK="$(printf '%s' "${MOKO_CLONE_TOKEN}" | tr -d '[:space:]')"
|
|
163
|
+
if [ -n "${REFRESH_TOK}" ] && command -v composer >/dev/null 2>&1; then
|
|
164
|
+
composer config -g http-basic.git.mokoconsulting.tech x-access-token "${REFRESH_TOK}" 2>/dev/null || true
|
|
165
|
+
( cd /opt/mokocli && composer install --no-dev --no-interaction --no-progress --optimize-autoloader --quiet ) 2>/dev/null || echo " (mokocli composer refresh skipped)"
|
|
166
|
+
fi
|
|
167
|
+
fi
|
|
103
168
|
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
|
|
104
169
|
else
|
|
105
170
|
echo Falling back to fresh clone
|
|
171
|
+
# Strip any stray whitespace/newline a pasted secret may carry, and mask
|
|
172
|
+
# it in the logs. Without the trim + quoted URL below, a trailing newline
|
|
173
|
+
# in MOKOGIT_TOKEN word-splits CLONE_URL so git clone sees extra
|
|
174
|
+
# positionals and dies with "fatal: Too many arguments".
|
|
175
|
+
MOKO_CLONE_TOKEN="$(printf '%s' "${MOKO_CLONE_TOKEN}" | tr -d '[:space:]')"
|
|
176
|
+
echo "::add-mask::${MOKO_CLONE_TOKEN}"
|
|
177
|
+
if [ -z "${MOKO_CLONE_TOKEN}" ]; then
|
|
178
|
+
echo "::error::MOKOGIT_TOKEN secret is empty — cannot clone mokocli fallback." >&2
|
|
179
|
+
exit 1
|
|
180
|
+
fi
|
|
106
181
|
if ! command -v composer > /dev/null 2>&1; then
|
|
107
182
|
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
|
|
108
183
|
fi
|
|
109
184
|
rm -rf /tmp/mokocli
|
|
110
|
-
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
|
|
111
|
-
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
|
|
185
|
+
CLONE_URL="https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git"
|
|
186
|
+
git clone --depth 1 --branch main --quiet "${CLONE_URL}" /tmp/mokocli
|
|
112
187
|
cd /tmp/mokocli
|
|
113
188
|
# Authenticate to the MokoGIT private Composer registry (git.mokoconsulting.tech)
|
|
114
189
|
# declared in mokocli composer.json (preferred-install=dist) before installing deps.
|
|
115
190
|
composer config -g http-basic.git.mokoconsulting.tech x-access-token "${MOKO_CLONE_TOKEN}"
|
|
191
|
+
export COMPOSER_AUTH="{\"http-basic\":{\"git.mokoconsulting.tech\":{\"username\":\"x-access-token\",\"password\":\"${MOKO_CLONE_TOKEN}\"}}}"
|
|
116
192
|
composer install --no-dev --no-interaction --no-progress --optimize-autoloader --quiet
|
|
117
193
|
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
|
|
118
194
|
fi
|
|
@@ -266,6 +342,11 @@ jobs:
|
|
|
266
342
|
--path . --version "$VERSION" --branch "${{ steps.ref.outputs.ref }}" --stability "$STABILITY" 2>/dev/null || true
|
|
267
343
|
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
|
268
344
|
|
|
345
|
+
# Emit a Joomla schema-update stub for each shipped extension's own
|
|
346
|
+
# version so #__schemas tracks the manifest (fixes "database out of date").
|
|
347
|
+
# No-op (|| true) on non-Joomla repos, which have no sql/updates dirs.
|
|
348
|
+
php ${MOKO_CLI}/schema_stub.php --path . || true
|
|
349
|
+
|
|
269
350
|
# Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml
|
|
270
351
|
php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true
|
|
271
352
|
|
|
@@ -302,20 +383,61 @@ jobs:
|
|
|
302
383
|
|
|
303
384
|
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
|
|
304
385
|
|
|
386
|
+
# npm publishing is NOT part of this universal workflow — it lives only in
|
|
387
|
+
# Template-NPM (.mokogit/workflows/npm-publish.yml) so it cannot leak into
|
|
388
|
+
# Joomla / non-npm repos via template sync. Joomla-family repos attach a zip
|
|
389
|
+
# below; npm repos publish from their own dedicated workflow.
|
|
390
|
+
# Release API operations (create / notes / upload / cascade delete) run
|
|
391
|
+
# against THIS repo, so they authenticate with the built-in per-run
|
|
392
|
+
# github.token (contents: write) — NOT the org MOKOGIT_TOKEN. That keeps
|
|
393
|
+
# MOKOGIT_TOKEN least-privilege (read-only, used only for the cross-repo
|
|
394
|
+
# mokocli / submodule clones above) while same-repo release writes stay
|
|
395
|
+
# scoped to the run — matching auto-release.yml (Template-Generic #188).
|
|
396
|
+
# github.token carries no trailing newline, but trim + mask anyway for
|
|
397
|
+
# Authorization-header hygiene.
|
|
398
|
+
- name: Sanitize release API token
|
|
399
|
+
if: steps.eligibility.outputs.proceed == 'true'
|
|
400
|
+
run: |
|
|
401
|
+
T="$(printf '%s' "${{ github.token }}" | tr -d '[:space:]')"
|
|
402
|
+
echo "::add-mask::${T}"
|
|
403
|
+
echo "MOKO_TOKEN=${T}" >> "$GITHUB_ENV"
|
|
404
|
+
|
|
305
405
|
- name: Create release
|
|
306
406
|
id: release
|
|
307
407
|
if: steps.eligibility.outputs.proceed == 'true'
|
|
408
|
+
# The MokoGIT release is the deliverable ONLY for the Joomla family. npm/mcp/
|
|
409
|
+
# client publish their real artifact upstream (npm) and merely mirror a
|
|
410
|
+
# release here, so a MokoGIT release failure must not fail those runs.
|
|
411
|
+
# release_create.php can fail its delete-then-recreate of the rolling dev/rc
|
|
412
|
+
# release (DELETE returns HTTP 200 but the release lingers), which turns an
|
|
413
|
+
# already-successful npm publish red. We branch on family INSIDE the run
|
|
414
|
+
# script (this act runner rejects `continue-on-error: ${{ ... }}` — it only
|
|
415
|
+
# accepts a literal bool): Joomla stays fatal; non-Joomla swallows a release
|
|
416
|
+
# failure so a green npm publish stays green.
|
|
308
417
|
run: |
|
|
309
418
|
TAG="${{ steps.meta.outputs.tag }}"
|
|
310
419
|
VERSION="${{ steps.meta.outputs.version }}"
|
|
311
420
|
API_BASE="${GIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
421
|
+
if [ "${{ steps.eligibility.outputs.is_joomla_family }}" = "true" ]; then
|
|
422
|
+
php ${MOKO_CLI}/release_create.php \
|
|
423
|
+
--path . --version "$VERSION" --tag "$TAG" \
|
|
424
|
+
--token "${MOKO_TOKEN}" --api-base "$API_BASE" \
|
|
425
|
+
--repo "${GIT_REPO}" --branch "${{ steps.ref.outputs.ref }}" --prerelease
|
|
426
|
+
else
|
|
427
|
+
php ${MOKO_CLI}/release_create.php \
|
|
428
|
+
--path . --version "$VERSION" --tag "$TAG" \
|
|
429
|
+
--token "${MOKO_TOKEN}" --api-base "$API_BASE" \
|
|
430
|
+
--repo "${GIT_REPO}" --branch "${{ steps.ref.outputs.ref }}" --prerelease \
|
|
431
|
+
|| echo "::warning::MokoGIT release failed — non-fatal for npm/mcp/client (npm is the real artifact)"
|
|
432
|
+
fi
|
|
316
433
|
|
|
317
434
|
- name: Update release notes from CHANGELOG.md
|
|
318
435
|
if: steps.eligibility.outputs.proceed == 'true'
|
|
436
|
+
# Release notes are cosmetic. The release-body PATCH can 403 on this MokoGIT
|
|
437
|
+
# instance (the per-run github.token lacks release-write), and for npm/mcp/
|
|
438
|
+
# client the published package is the real artifact — never let this fail
|
|
439
|
+
# the run. (Matches the cascade step's best-effort contract.)
|
|
440
|
+
continue-on-error: true
|
|
319
441
|
run: |
|
|
320
442
|
TAG="${{ steps.meta.outputs.tag }}"
|
|
321
443
|
VERSION="${{ steps.meta.outputs.version }}"
|
|
@@ -332,7 +454,7 @@ jobs:
|
|
|
332
454
|
fi
|
|
333
455
|
|
|
334
456
|
# Update release body via API
|
|
335
|
-
RELEASE_ID=$(curl -sf -H "Authorization: token ${
|
|
457
|
+
RELEASE_ID=$(curl -sf -H "Authorization: token ${MOKO_TOKEN}" \
|
|
336
458
|
"${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
|
337
459
|
|
|
338
460
|
if [ -n "$RELEASE_ID" ]; then
|
|
@@ -344,7 +466,7 @@ jobs:
|
|
|
344
466
|
'${API_BASE}/releases/${RELEASE_ID}',
|
|
345
467
|
data=payload, method='PATCH',
|
|
346
468
|
headers={
|
|
347
|
-
'Authorization': 'token ${
|
|
469
|
+
'Authorization': 'token ${MOKO_TOKEN}',
|
|
348
470
|
'Content-Type': 'application/json'
|
|
349
471
|
})
|
|
350
472
|
urllib.request.urlopen(req)
|
|
@@ -352,6 +474,25 @@ jobs:
|
|
|
352
474
|
echo "Release notes updated from CHANGELOG.md"
|
|
353
475
|
fi
|
|
354
476
|
|
|
477
|
+
# Build-time PHP dependency vendoring (universal, guarded). If a package ships a
|
|
478
|
+
# composer.json, fetch its runtime deps into that package's vendor dir BEFORE
|
|
479
|
+
# release_package.php zips the working tree — so a component can declare a PHP
|
|
480
|
+
# library (e.g. dompdf) via composer.json + composer.lock instead of committing
|
|
481
|
+
# vendor/. No-op for any repo without a package composer.json; non-fatal so a
|
|
482
|
+
# composer failure can never block a release (it packages whatever vendor exists).
|
|
483
|
+
- name: Vendor PHP dependencies (build-time)
|
|
484
|
+
if: steps.eligibility.outputs.proceed == 'true'
|
|
485
|
+
continue-on-error: true
|
|
486
|
+
run: |
|
|
487
|
+
command -v composer >/dev/null 2>&1 || { sudo apt-get update -qq && sudo apt-get install -y -qq composer >/dev/null 2>&1 || true; }
|
|
488
|
+
for cj in $(find source/packages -maxdepth 2 -name composer.json 2>/dev/null); do
|
|
489
|
+
dir="$(dirname "$cj")"
|
|
490
|
+
echo "::notice::composer install (no-dev) in ${dir}"
|
|
491
|
+
composer install --no-dev --no-interaction --no-progress --optimize-autoloader --working-dir="$dir" \
|
|
492
|
+
|| echo "::warning::composer install failed in ${dir} — packaging existing vendor"
|
|
493
|
+
done
|
|
494
|
+
true
|
|
495
|
+
|
|
355
496
|
# Joomla-family only: mokocli release_package.php builds the extension zip
|
|
356
497
|
# (+ sha256, attach). npm/mcp/client package/publish in their platform shim
|
|
357
498
|
# (trust boundary, ADR #124), so this PHP zip step is skipped for them.
|
|
@@ -365,31 +506,29 @@ jobs:
|
|
|
365
506
|
TAG="${{ steps.meta.outputs.tag }}"
|
|
366
507
|
STABILITY="${{ steps.meta.outputs.stability }}"
|
|
367
508
|
API_BASE="${GIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
|
368
|
-
#
|
|
369
|
-
# (pkg_<ext>-dev.zip / -rc.zip) replaced in place
|
|
370
|
-
#
|
|
371
|
-
#
|
|
372
|
-
#
|
|
509
|
+
# Publish the pre-release asset under a VERSIONED rolling name
|
|
510
|
+
# (pkg_<ext>-<version>-dev.zip / -rc.zip) replaced in place each build. The
|
|
511
|
+
# release version is stable within a base version, so the asset URL stays
|
|
512
|
+
# stable while the version is visible in the filename. The in-package
|
|
513
|
+
# manifest <version> still advances (build metadata) so the updater keeps
|
|
514
|
+
# detecting new builds.
|
|
373
515
|
php ${MOKO_CLI}/release_package.php \
|
|
374
516
|
--path . --version "$VERSION" --tag "$TAG" \
|
|
375
|
-
--token "${
|
|
517
|
+
--token "${MOKO_TOKEN}" --api-base "$API_BASE" \
|
|
376
518
|
--repo "${GIT_REPO}" --output /tmp \
|
|
377
519
|
--stability "$STABILITY" --rolling || true
|
|
378
520
|
|
|
379
|
-
# updates.xml is generated
|
|
380
|
-
#
|
|
381
|
-
#
|
|
382
|
-
# publishes
|
|
383
|
-
# the server-side generator (MokoGIT license server) needs the equivalent
|
|
384
|
-
# change so the served updates.xml references the fixed filename, not the old
|
|
385
|
-
# versioned one. See #404 report — flagged for human/server-side follow-up.
|
|
521
|
+
# updates.xml <downloadurl> is generated server-side by MokoGIT (the license
|
|
522
|
+
# server; being moved to MokoSuiteLicensing). It must reference the VERSIONED
|
|
523
|
+
# rolling asset (pkg_<ext>-<version>-dev.zip) that release_package.php now
|
|
524
|
+
# publishes — keep the server-side generator in sync with that filename.
|
|
386
525
|
|
|
387
526
|
- name: "Delete lesser pre-release channels (cascade)"
|
|
388
527
|
if: steps.eligibility.outputs.proceed == 'true'
|
|
389
528
|
continue-on-error: true
|
|
390
529
|
run: |
|
|
391
530
|
API_BASE="${GIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
|
392
|
-
TOKEN="${
|
|
531
|
+
TOKEN="${MOKO_TOKEN}"
|
|
393
532
|
|
|
394
533
|
php ${MOKO_CLI}/release_cascade.php \
|
|
395
534
|
--stability "${{ steps.meta.outputs.stability }}" \
|
|
@@ -34,7 +34,7 @@ jobs:
|
|
|
34
34
|
BRANCH: ${{ github.event.pull_request.head.ref }}
|
|
35
35
|
REPO: ${{ github.repository }}
|
|
36
36
|
GIT_URL: ${{ vars.MOKOGIT_URL || 'https://git.mokoconsulting.tech' }}
|
|
37
|
-
TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
|
37
|
+
TOKEN: ${{ secrets.MOKOGIT_TOKEN || github.token }}
|
|
38
38
|
run: |
|
|
39
39
|
set -euo pipefail
|
|
40
40
|
# BRANCH is attacker-controlled (PR head ref). Strict allowlist before ANY use.
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
# FILE INFORMATION
|
|
6
6
|
# DEFGROUP: MokoGIT.Workflow.Template
|
|
7
7
|
# INGROUP: MokoCLI.CI
|
|
8
|
-
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-
|
|
8
|
+
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
|
9
9
|
# PATH: /.mokogit/workflows/version-set.yml
|
|
10
|
-
# VERSION: 01.
|
|
11
|
-
# BRIEF: Set or reset the
|
|
10
|
+
# VERSION: 01.01.00
|
|
11
|
+
# BRIEF: Set or reset the project version across all version-bearing files (README / CHANGELOG / file-header blocks universal; the Joomla manifest step is guarded and no-ops on non-Joomla repos)
|
|
12
12
|
|
|
13
|
-
name: "
|
|
13
|
+
name: "Universal: Set Version"
|
|
14
14
|
|
|
15
15
|
on:
|
|
16
16
|
workflow_dispatch:
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Copyright 2026 Moko Consulting
|
|
3
|
+
SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
BRIEF: Shared AI-agent instruction source of truth for this repository.
|
|
5
|
+
This file is the single point of truth; CLAUDE.md / GEMINI.md / MOKOAI.md point here.
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# mcp-mokosuite — Agent Instructions
|
|
9
|
+
|
|
10
|
+
`mcp-mokosuite` is an MCP (Model Context Protocol) server for MokoSuite. It
|
|
11
|
+
exposes MokoSuite operations to AI agents over the Model Context Protocol.
|
|
12
|
+
|
|
13
|
+
> Read by every AI agent (Claude Code, Gemini CLI, MokoAI). CLAUDE.md / GEMINI.md / MOKOAI.md point here.
|
|
14
|
+
|
|
15
|
+
## Platform & Layout
|
|
16
|
+
|
|
17
|
+
- Platform: MCP server (Node.js).
|
|
18
|
+
- Language: TypeScript.
|
|
19
|
+
- Source dir: `src/`.
|
|
20
|
+
- TypeScript config: `tsconfig.json`.
|
|
21
|
+
- Example runtime config: `config.example.json`.
|
|
22
|
+
- Docs: `docs/`.
|
|
23
|
+
- Notable files: `package.json`, `Makefile`, `CODE_OF_CONDUCT.md`.
|
|
24
|
+
|
|
25
|
+
## Commands
|
|
26
|
+
|
|
27
|
+
- `npm install` — install dependencies.
|
|
28
|
+
- `npm run build` — compile TypeScript (see `package.json` scripts).
|
|
29
|
+
- `make` — see `Makefile` for the repo's build/lint targets.
|
|
30
|
+
|
|
31
|
+
## Rules
|
|
32
|
+
|
|
33
|
+
- Never commit `.claude/`, `.gemini/`, `.mokoai/`, `.mcp.json`, `TODO.md`, or
|
|
34
|
+
minified/compiled assets.
|
|
35
|
+
- Workflow / automation config lives under `.mokogit/`.
|
|
36
|
+
- Attribute all machine-authored commits with `Authored-by: Moko Consulting`.
|
|
37
|
+
- Do not commit real credentials; copy `config.example.json` to a local,
|
|
38
|
+
ignored config.
|
|
39
|
+
- Standards: MokoCLI — https://git.mokoconsulting.tech/MokoConsulting/mokocli/wiki/Home
|
package/CHANGELOG.md
CHANGED
|
@@ -1,18 +1,54 @@
|
|
|
1
|
+
# Changelog
|
|
1
2
|
|
|
3
|
+
All notable changes to this project are documented here, following [Keep a Changelog](https://keepachangelog.com/) and MokoStandards `XX.YY.ZZ` versioning.
|
|
2
4
|
|
|
3
5
|
## [Unreleased]
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
- Branding: Git -> MokoGIT in workflow DEFGROUP tags, committer identity (git-actions[bot] -> mokogit-actions[bot]), and platform prose. Functional tokens (GIT_TOKEN/GITHUB_TOKEN, GIT_ACTIONS_*, vars.GITEA_*, git.mokoconsulting.tech) preserved.
|
|
7
|
+
## [01.01.00] - 2026-08-15
|
|
7
8
|
|
|
8
|
-
###
|
|
9
|
-
-
|
|
9
|
+
### Added
|
|
10
|
+
- **Layer 1 (Extensions Store / MokoSuiteExtensions)** — 5 MCP tools against the
|
|
11
|
+
`/v1/mokosuiteextensions` base path:
|
|
12
|
+
- Reads: `store_catalog_list` (`GET /catalog`), `store_catalog_get`
|
|
13
|
+
(`GET /catalog/{id}`).
|
|
14
|
+
- Writes: `store_install` (`POST /catalog/{id}/install`), `store_update`
|
|
15
|
+
(`POST /catalog/{id}/update`), `store_uninstall`
|
|
16
|
+
(`POST /catalog/{id}/uninstall`, `confirm: true` guard — destructive).
|
|
17
|
+
- Bumped the MCP server advertised version to `1.1.0`.
|
|
18
|
+
|
|
19
|
+
### Note
|
|
20
|
+
- The store write routes (`/install`, `/update`, `/uninstall`) are being added to
|
|
21
|
+
the store REST API in parallel and may not be live on a site until that ships and
|
|
22
|
+
deploys. The read routes exist. The tools are defined now regardless.
|
|
10
23
|
|
|
11
|
-
|
|
12
|
-
- Migrated all workflow and template paths from `.github/` to `.mokogit/`
|
|
13
|
-
- Template source paths updated: `templates/git/` to `templates/mokogit/`
|
|
14
|
-
- HCL definition files removed -- Template repos are now the canonical source
|
|
24
|
+
## [01.00.00] - 2026-07-26
|
|
15
25
|
|
|
16
26
|
### Added
|
|
17
|
-
-
|
|
18
|
-
-
|
|
27
|
+
- **Foundation** — the real MokoSuite MCP server (de-templated from Template-NPM):
|
|
28
|
+
- Multi-site connection model with two credential tiers per site — a Joomla Web
|
|
29
|
+
Services Bearer token for read/CRUD, plus an optional `health_api_token` + HQ
|
|
30
|
+
RSA private key for the remote-control tier (`src/types.ts`).
|
|
31
|
+
- RSA remote-control request signing reproducing HQ's
|
|
32
|
+
`X-MokoSuite-Signature` / `-Timestamp` / `-Key-Version` scheme (RSA-2048 over
|
|
33
|
+
`domain|timestamp|token`, SHA-256, 300 s window) (`src/signing.ts`).
|
|
34
|
+
- Joomla Web Services HTTP client with a signed `remoteControl()` path alongside
|
|
35
|
+
the token-auth methods (`src/client.ts`).
|
|
36
|
+
- **Layer 0 (Platform / MokoSuiteClient)** — 24 MCP tools:
|
|
37
|
+
- Foundation: `mokosuite_ping`, `list_connections`, `api_request`.
|
|
38
|
+
- Reads: `client_get_health`, `client_get_dashboard`, `client_list_extensions`,
|
|
39
|
+
`client_list_plugins`, `client_list_snapshots`, `client_list_users`,
|
|
40
|
+
`client_export_users`.
|
|
41
|
+
- Actions: `client_clear_cache`, `client_check_updates`, `client_install_extension`,
|
|
42
|
+
`client_toggle_plugin`, `client_create_snapshot`, `client_reset_snapshot`,
|
|
43
|
+
`client_sync_push`.
|
|
44
|
+
- Remote-control (RSA-signed; `confirm: true` guard on destructive fleet-wide ops):
|
|
45
|
+
`client_provision_reset`, `client_remote_login`, `client_users_reset_passwords`,
|
|
46
|
+
`client_users_reset_2fa`, `client_users_disable_all`, `client_users_enable_all`,
|
|
47
|
+
`client_users_force_logout`.
|
|
48
|
+
- Published `@mokoconsulting/mcp-mokosuite@1.0.0` to npmjs.org and the internal
|
|
49
|
+
git.mokoconsulting.tech registry.
|
|
50
|
+
|
|
51
|
+
### Verified
|
|
52
|
+
- The read tier (`get_health`, `get_dashboard`, `list_extensions`, `list_plugins`,
|
|
53
|
+
`list_snapshots`) returns live data from `suite.dev.mokoconsulting.tech`
|
|
54
|
+
(`/api/index.php/v1/mokosuiteclient/*`).
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,8 @@ import { ApiClient } from './client.js';
|
|
|
21
21
|
import { hasRemoteControl } from './signing.js';
|
|
22
22
|
/** Base path for MokoSuiteClient Web Services (appended after /api/index.php). */
|
|
23
23
|
const MSC = '/v1/mokosuiteclient';
|
|
24
|
+
/** Base path for MokoSuiteExtensions (store) Web Services (appended after /api/index.php). */
|
|
25
|
+
const MSE = '/v1/mokosuiteextensions';
|
|
24
26
|
let config;
|
|
25
27
|
function clientFor(connection) {
|
|
26
28
|
return new ApiClient(getConnection(config, connection));
|
|
@@ -51,7 +53,7 @@ function requireConfirm(confirm) {
|
|
|
51
53
|
// ── Server ──────────────────────────────────────────────────────────────
|
|
52
54
|
const server = new McpServer({
|
|
53
55
|
name: 'mcp-mokosuite',
|
|
54
|
-
version: '1.
|
|
56
|
+
version: '1.1.0',
|
|
55
57
|
});
|
|
56
58
|
// ════════════════════════════════════════════════════════════════════════
|
|
57
59
|
// FOUNDATION — connectivity & generic access
|
|
@@ -168,6 +170,31 @@ server.tool('client_users_force_logout', 'Terminate ALL user sessions on a MokoS
|
|
|
168
170
|
return guard;
|
|
169
171
|
return formatResponse(await clientFor(connection).remoteControl('POST', `${MSC}/users/force-logout`));
|
|
170
172
|
});
|
|
173
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
174
|
+
// LAYER 1 — Extensions Store (MokoSuiteExtensions)
|
|
175
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
176
|
+
server.tool('store_catalog_list', 'List the products available in the MokoSuite Extensions store catalog for a site.', { ...ConnectionParam }, async ({ connection }) => formatResponse(await clientFor(connection).get(`${MSE}/catalog`)));
|
|
177
|
+
server.tool('store_catalog_get', 'Get a single MokoSuite Extensions store product by its catalog id.', {
|
|
178
|
+
id: z.number().describe('Catalog product id'),
|
|
179
|
+
...ConnectionParam,
|
|
180
|
+
}, async ({ id, connection }) => formatResponse(await clientFor(connection).get(`${MSE}/catalog/${id}`)));
|
|
181
|
+
server.tool('store_install', 'Install a MokoSuite Extensions store product on a site by its catalog id.', {
|
|
182
|
+
id: z.number().describe('Catalog product id to install'),
|
|
183
|
+
...ConnectionParam,
|
|
184
|
+
}, async ({ id, connection }) => formatResponse(await clientFor(connection).post(`${MSE}/catalog/${id}/install`)));
|
|
185
|
+
server.tool('store_update', 'Update an installed MokoSuite Extensions store product on a site by its catalog id.', {
|
|
186
|
+
id: z.number().describe('Catalog product id to update'),
|
|
187
|
+
...ConnectionParam,
|
|
188
|
+
}, async ({ id, connection }) => formatResponse(await clientFor(connection).post(`${MSE}/catalog/${id}/update`)));
|
|
189
|
+
server.tool('store_uninstall', 'Uninstall a MokoSuite Extensions store product from a site by its catalog id. Destructive — requires `confirm: true`.', {
|
|
190
|
+
id: z.number().describe('Catalog product id to uninstall'),
|
|
191
|
+
confirm: z.boolean().describe('Must be true — uninstalling removes the product from the target site'),
|
|
192
|
+
...ConnectionParam,
|
|
193
|
+
}, async ({ id, confirm, connection }) => {
|
|
194
|
+
if (!confirm)
|
|
195
|
+
return text('Refused: set `confirm: true` to uninstall this store product.');
|
|
196
|
+
return formatResponse(await clientFor(connection).post(`${MSE}/catalog/${id}/uninstall`));
|
|
197
|
+
});
|
|
171
198
|
// ── Start Server ────────────────────────────────────────────────────────
|
|
172
199
|
async function main() {
|
|
173
200
|
config = await loadConfig();
|
package/docs/API.md
CHANGED
|
@@ -11,53 +11,62 @@ BRIEF: MCP tool reference documentation
|
|
|
11
11
|
|
|
12
12
|
# API Reference
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
|
33
|
-
|
|
34
|
-
| `
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
|
50
|
-
|
|
51
|
-
| `
|
|
52
|
-
| `
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
14
|
+
Every tool accepts an optional `connection` parameter naming a MokoSuite site/connection from your config (`~/.mcp_mokosuite.json`); omit it to use the default. Tool endpoints target the MokoSuiteClient Web Services under `/api/index.php/v1/mokosuiteclient/`.
|
|
15
|
+
|
|
16
|
+
Two credential tiers apply per connection:
|
|
17
|
+
- **Read / CRUD** — the site's Joomla Web Services Bearer token (`apiToken`).
|
|
18
|
+
- **Remote-control** (RSA-signed) — the site's `healthApiToken` + HQ's `rsaPrivateKeyPath`. Tools in that tier fail with a clear message if those aren't configured.
|
|
19
|
+
|
|
20
|
+
## Foundation
|
|
21
|
+
|
|
22
|
+
| Tool | Parameters | Description |
|
|
23
|
+
|------|------------|-------------|
|
|
24
|
+
| `mokosuite_ping` | `connection?` | Check a site's reachability (calls its dashboard endpoint, reports HTTP status). |
|
|
25
|
+
| `list_connections` | — | List configured sites, their role, and which credential tiers are available. |
|
|
26
|
+
| `api_request` | `method`, `endpoint`, `body?`, `params?`, `connection?` | Raw request to any endpoint relative to `/api/index.php` (escape hatch). |
|
|
27
|
+
|
|
28
|
+
## Layer 0 — Platform (MokoSuiteClient)
|
|
29
|
+
|
|
30
|
+
### Reads (Joomla API token)
|
|
31
|
+
|
|
32
|
+
| Tool | Parameters | Endpoint |
|
|
33
|
+
|------|------------|----------|
|
|
34
|
+
| `client_get_health` | `connection?` | `GET /v1/mokosuiteclient/health` — full 16-check diagnostics |
|
|
35
|
+
| `client_get_dashboard` | `connection?` | `GET .../dashboard` — health summary, versions, plugin states |
|
|
36
|
+
| `client_list_extensions` | `connection?` | `GET .../extensions` — installed + update info |
|
|
37
|
+
| `client_list_plugins` | `connection?` | `GET .../plugins` — Moko feature plugins + state |
|
|
38
|
+
| `client_list_snapshots` | `connection?` | `GET .../snapshot` |
|
|
39
|
+
| `client_list_users` | `connection?` | `GET .../users` |
|
|
40
|
+
| `client_export_users` | `connection?` | `GET .../users/export` |
|
|
41
|
+
|
|
42
|
+
### Actions (Joomla API token, `core.manage`)
|
|
43
|
+
|
|
44
|
+
| Tool | Parameters | Endpoint |
|
|
45
|
+
|------|------------|----------|
|
|
46
|
+
| `client_clear_cache` | `connection?` | `POST .../cache` |
|
|
47
|
+
| `client_check_updates` | `connection?` | `POST .../update` |
|
|
48
|
+
| `client_install_extension` | `url`, `connection?` | `POST .../install` — install from remote ZIP (64 MB cap) |
|
|
49
|
+
| `client_toggle_plugin` | `extension_id`, `connection?` | `POST .../plugins` (`task=toggle`) |
|
|
50
|
+
| `client_create_snapshot` | `label?`, `connection?` | `POST .../snapshot` |
|
|
51
|
+
| `client_reset_snapshot` | `name`, `connection?` | `POST .../reset` — restore a baseline |
|
|
52
|
+
| `client_sync_push` | `target?`, `connection?` | `POST .../sync` |
|
|
53
|
+
|
|
54
|
+
### Remote-control (RSA-signed; requires HQ credentials)
|
|
55
|
+
|
|
56
|
+
Destructive fleet-wide tools require `confirm: true`.
|
|
57
|
+
|
|
58
|
+
| Tool | Parameters | Endpoint |
|
|
59
|
+
|------|------------|----------|
|
|
60
|
+
| `client_provision_reset` | `confirm`, `connection?` | `POST .../provision-reset` |
|
|
61
|
+
| `client_remote_login` | `connection?` | `POST .../remote-login` — one-time login URL (60 s TTL) |
|
|
62
|
+
| `client_users_reset_passwords` | `confirm`, `connection?` | `POST .../users/reset-passwords` |
|
|
63
|
+
| `client_users_reset_2fa` | `confirm`, `connection?` | `POST .../users/reset-2fa` |
|
|
64
|
+
| `client_users_disable_all` | `confirm`, `connection?` | `POST .../users/disable-all` |
|
|
65
|
+
| `client_users_enable_all` | `confirm`, `connection?` | `POST .../users/enable-all` |
|
|
66
|
+
| `client_users_force_logout` | `confirm`, `connection?` | `POST .../users/force-logout` |
|
|
58
67
|
|
|
59
68
|
## Revision History
|
|
60
69
|
|
|
61
70
|
| Date | Version | Author | Notes |
|
|
62
71
|
| --- | --- | --- | --- |
|
|
63
|
-
| 2026-
|
|
72
|
+
| 2026-07-26 | 01.00.00 | Moko Consulting | Foundation + Layer 0 tool reference (24 tools) |
|