From 1bba631d9494f6bead1b9ef63e502264b4cc7bc3 Mon Sep 17 00:00:00 2001 From: rafaelscosta Date: Wed, 13 May 2026 10:21:12 -0300 Subject: [PATCH 1/3] fix: add legacy aiox-core wrapper [Story 124.8] --- .github/workflows/npm-publish.yml | 129 ++++++++++++++++-- compat/aiox-core/README.md | 13 ++ compat/aiox-core/bin/aiox-core.js | 17 +++ compat/aiox-core/package.json | 36 +++++ .../STORY-124.8-legacy-aiox-core-wrapper.md | 36 +++++ 5 files changed, 221 insertions(+), 10 deletions(-) create mode 100644 compat/aiox-core/README.md create mode 100755 compat/aiox-core/bin/aiox-core.js create mode 100644 compat/aiox-core/package.json create mode 100644 docs/stories/epic-124-aiox-squads-scope-migration/STORY-124.8-legacy-aiox-core-wrapper.md diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index a5b14b3a96..959e2e789b 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -27,6 +27,7 @@ on: options: - all - core + - aiox-core - aiox-install - aiox-pro-cli - installer @@ -99,9 +100,9 @@ jobs: # Default to all packages on release/tag events if [ -z "$SELECTED" ] || [ "$SELECTED" = "all" ]; then - PACKAGES="core,aiox-install,aiox-pro-cli,installer" + PACKAGES="core,aiox-core,aiox-install,aiox-pro-cli,installer" elif [ "$SELECTED" = "aiox-core" ]; then - PACKAGES="core" + PACKAGES="aiox-core" else PACKAGES="$SELECTED" fi @@ -147,7 +148,7 @@ jobs: publish: needs: [test, build] - if: ${{ contains(needs.build.outputs.packages, 'core') }} + if: ${{ contains(format(',{0},', needs.build.outputs.packages), ',core,') }} runs-on: ubuntu-latest steps: - name: Checkout code @@ -233,7 +234,7 @@ jobs: publish_workspace_packages: needs: [test, build] - if: ${{ contains(needs.build.outputs.packages, 'aiox-install') || contains(needs.build.outputs.packages, 'aiox-pro-cli') || contains(needs.build.outputs.packages, 'installer') }} + if: ${{ contains(format(',{0},', needs.build.outputs.packages), ',aiox-install,') || contains(format(',{0},', needs.build.outputs.packages), ',aiox-pro-cli,') || contains(format(',{0},', needs.build.outputs.packages), ',installer,') }} runs-on: ubuntu-latest strategy: fail-fast: false @@ -352,19 +353,119 @@ jobs: echo "❌ Smoke test timeout for ${{ steps.pkg.outputs.name }}" exit 1 + publish_legacy_aiox_core: + needs: [test, build] + if: ${{ contains(format(',{0},', needs.build.outputs.packages), ',aiox-core,') }} + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: ${{ env.NODE_VERSION }} + registry-url: ${{ env.NPM_REGISTRY }} + + - name: Get legacy package metadata + id: pkg + run: | + PACKAGE_JSON="./compat/aiox-core/package.json" + NAME=$(node -p "require('${PACKAGE_JSON}').name") + VERSION=$(node -p "require('${PACKAGE_JSON}').version") + echo "name=$NAME" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> $GITHUB_OUTPUT + if [ "$NAME" != "aiox-core" ]; then + echo "::error::Expected legacy package name aiox-core, got $NAME" + exit 1 + fi + + - name: Set publishing tag + id: publish-tag + run: | + if [ "${{ github.event.inputs.publish_mode }}" = "stable" ] || [ "${{ github.event_name }}" = "release" ] || [ "${{ github.event_name }}" = "push" ]; then + echo "tag=latest" >> $GITHUB_OUTPUT + elif [ "${{ github.event.inputs.publish_mode }}" = "beta" ]; then + echo "tag=beta" >> $GITHUB_OUTPUT + else + echo "tag=preview" >> $GITHUB_OUTPUT + fi + + - name: Check if already published + id: should-publish + run: | + PKG_NAME="${{ steps.pkg.outputs.name }}" + VERSION="${{ steps.pkg.outputs.version }}" + + if npm view "${PKG_NAME}@${VERSION}" version > /dev/null 2>&1; then + echo "Version ${VERSION} already exists for ${PKG_NAME}" + echo "should_publish=false" >> $GITHUB_OUTPUT + else + echo "Package ${PKG_NAME}@${VERSION} not found in registry" + echo "should_publish=true" >> $GITHUB_OUTPUT + fi + + - name: Publish legacy aiox-core wrapper + if: steps.should-publish.outputs.should_publish == 'true' + working-directory: compat/aiox-core + run: | + publish_status=1 + for token in "$NPM_TOKEN_AIOX_SQUADS" "$NPM_TOKEN"; do + if [ -z "$token" ]; then + continue + fi + echo "//registry.npmjs.org/:_authToken=${token}" > .npmrc + if npm publish --tag ${{ steps.publish-tag.outputs.tag }} --access public; then + publish_status=0 + break + fi + publish_status=$? + done + rm -f .npmrc + if [ "$publish_status" -ne 0 ]; then + echo "::error::Unable to publish aiox-core. Refresh NPM_TOKEN with owner access to the legacy package." + exit "$publish_status" + fi + echo "✅ Published aiox-core@${{ steps.pkg.outputs.version }} with tag ${{ steps.publish-tag.outputs.tag }}" + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_TOKEN_AIOX_SQUADS: ${{ secrets.NPM_TOKEN_AIOX_SQUADS }} + + - name: Smoke test legacy npx + if: steps.should-publish.outputs.should_publish == 'true' + env: + PKG_SPEC: aiox-core@${{ steps.pkg.outputs.version }} + run: | + for i in 1 2 3 4 5 6; do + sleep 15 + if npm view "${PKG_SPEC}" version > /dev/null 2>&1; then + SMOKE_DIR=$(mktemp -d) + if (cd "${SMOKE_DIR}" && npx --yes "${PKG_SPEC}" --version >/dev/null 2>&1); then + rm -rf "${SMOKE_DIR}" + echo "✅ Smoke test passed: npx --yes ${PKG_SPEC} --version works" + exit 0 + fi + rm -rf "${SMOKE_DIR}" + fi + echo "⏳ Waiting for npm propagation... (attempt $i/6)" + done + echo "❌ Smoke test timeout for ${PKG_SPEC}" + exit 1 + notify: - needs: [build, publish, publish_workspace_packages] + needs: [build, publish, publish_workspace_packages, publish_legacy_aiox_core] if: always() runs-on: ubuntu-latest steps: - name: Notify completion run: | - if [ "${{ needs.publish.result }}" = "failure" ] || [ "${{ needs.publish_workspace_packages.result }}" = "failure" ]; then + if [ "${{ needs.publish.result }}" = "failure" ] || [ "${{ needs.publish_workspace_packages.result }}" = "failure" ] || [ "${{ needs.publish_legacy_aiox_core.result }}" = "failure" ]; then echo "❌ Publishing failed" exit 1 fi - if [ "${{ needs.publish.result }}" = "cancelled" ] || [ "${{ needs.publish_workspace_packages.result }}" = "cancelled" ]; then + if [ "${{ needs.publish.result }}" = "cancelled" ] || [ "${{ needs.publish_workspace_packages.result }}" = "cancelled" ] || [ "${{ needs.publish_legacy_aiox_core.result }}" = "cancelled" ]; then echo "❌ Publishing was cancelled" exit 1 fi @@ -383,11 +484,19 @@ jobs: fi fi + if [ "${{ needs.publish_legacy_aiox_core.result }}" = "success" ]; then + if [ -n "$PUBLISHED_PACKAGES" ]; then + PUBLISHED_PACKAGES="$PUBLISHED_PACKAGES, aiox-core legacy wrapper" + else + PUBLISHED_PACKAGES="aiox-core legacy wrapper" + fi + fi + if [ -n "$PUBLISHED_PACKAGES" ]; then echo "🎉 Successfully published to NPM:" echo " Packages: $PUBLISHED_PACKAGES" echo " Registry: ${{ env.NPM_REGISTRY }}" - elif [ "${{ needs.publish.result }}" = "skipped" ] && [ "${{ needs.publish_workspace_packages.result }}" = "skipped" ]; then + elif [ "${{ needs.publish.result }}" = "skipped" ] && [ "${{ needs.publish_workspace_packages.result }}" = "skipped" ] && [ "${{ needs.publish_legacy_aiox_core.result }}" = "skipped" ]; then echo "⏭️ No packages needed publishing (already up-to-date)" else echo "❌ Publishing failed" @@ -395,8 +504,8 @@ jobs: fi create-release-notes: - needs: [build, publish, publish_workspace_packages] - if: github.event_name == 'release' && (needs.publish.result == 'success' || needs.publish_workspace_packages.result == 'success') + needs: [build, publish, publish_workspace_packages, publish_legacy_aiox_core] + if: github.event_name == 'release' && (needs.publish.result == 'success' || needs.publish_workspace_packages.result == 'success' || needs.publish_legacy_aiox_core.result == 'success') runs-on: ubuntu-latest permissions: contents: write diff --git a/compat/aiox-core/README.md b/compat/aiox-core/README.md new file mode 100644 index 0000000000..0af117435f --- /dev/null +++ b/compat/aiox-core/README.md @@ -0,0 +1,13 @@ +# aiox-core compatibility package + +`aiox-core` is the legacy npm package name for AIOX Core. + +The canonical package is now `@aiox-squads/core`. This compatibility package +keeps existing commands such as `npx aiox-core@latest install` working by +delegating to the canonical package. + +Recommended new command: + +```bash +npx -y -p @aiox-squads/core@latest aiox install +``` diff --git a/compat/aiox-core/bin/aiox-core.js b/compat/aiox-core/bin/aiox-core.js new file mode 100755 index 0000000000..ac902fd15f --- /dev/null +++ b/compat/aiox-core/bin/aiox-core.js @@ -0,0 +1,17 @@ +#!/usr/bin/env node +'use strict'; + +const path = require('path'); + +const binByName = { + aiox: 'aiox.js', + 'aiox-core': 'aiox.js', + 'aiox-minimal': 'aiox-minimal.js', + 'aiox-graph': 'aiox-graph.js', + 'aiox-delegate': 'aiox-delegate.js', +}; + +const invokedAs = path.basename(process.argv[1] || 'aiox-core'); +const targetBin = binByName[invokedAs] || 'aiox.js'; + +require(`@aiox-squads/core/bin/${targetBin}`); diff --git a/compat/aiox-core/package.json b/compat/aiox-core/package.json new file mode 100644 index 0000000000..4841477e12 --- /dev/null +++ b/compat/aiox-core/package.json @@ -0,0 +1,36 @@ +{ + "name": "aiox-core", + "version": "5.2.2", + "description": "Compatibility wrapper for @aiox-squads/core.", + "license": "MIT", + "bin": { + "aiox": "bin/aiox-core.js", + "aiox-core": "bin/aiox-core.js", + "aiox-minimal": "bin/aiox-core.js", + "aiox-graph": "bin/aiox-core.js", + "aiox-delegate": "bin/aiox-core.js" + }, + "files": [ + "bin/", + "README.md" + ], + "dependencies": { + "@aiox-squads/core": "5.2.2" + }, + "engines": { + "node": ">=18" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/SynkraAI/aiox-core.git", + "directory": "compat/aiox-core" + }, + "bugs": { + "url": "https://github.com/SynkraAI/aiox-core/issues" + }, + "homepage": "https://github.com/SynkraAI/aiox-core#readme", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + } +} diff --git a/docs/stories/epic-124-aiox-squads-scope-migration/STORY-124.8-legacy-aiox-core-wrapper.md b/docs/stories/epic-124-aiox-squads-scope-migration/STORY-124.8-legacy-aiox-core-wrapper.md new file mode 100644 index 0000000000..7a0c3ea7be --- /dev/null +++ b/docs/stories/epic-124-aiox-squads-scope-migration/STORY-124.8-legacy-aiox-core-wrapper.md @@ -0,0 +1,36 @@ +# Story 124.8: Legacy `aiox-core` Wrapper + +## Status + +Ready for Review + +## Story + +As an AIOX Pro student using older installation instructions, +I want `npx aiox-core@latest install` to execute the current canonical installer, +so that legacy documentation and cached support messages do not route me to stale Pro activation code. + +## Acceptance Criteria + +- [x] Publishable compatibility package `aiox-core` delegates to `@aiox-squads/core`. +- [x] Legacy bins `aiox`, `aiox-core`, `aiox-minimal`, `aiox-graph`, and `aiox-delegate` are preserved. +- [x] The NPM publish workflow can publish the legacy package using the existing unscoped package token. +- [x] Local pack and smoke validation prove `npx aiox-core@latest install` will resolve to the canonical core after publish. + +## Tasks + +- [x] Add a minimal legacy package outside the root workspace. +- [x] Wire the NPM workflow selection for `aiox-core`. +- [x] Validate the wrapper locally before publish. + +## File List + +- `compat/aiox-core/package.json` +- `compat/aiox-core/bin/aiox-core.js` +- `compat/aiox-core/README.md` +- `.github/workflows/npm-publish.yml` +- `docs/stories/epic-124-aiox-squads-scope-migration/STORY-124.8-legacy-aiox-core-wrapper.md` + +## Dev Notes + +The package intentionally lives outside `packages/*` so root workspace lockfiles do not need to change. It depends on the exact canonical core version that should serve legacy users. From 6bf7c0fce6f6cde183a50f11c6e42d41c7521691 Mon Sep 17 00:00:00 2001 From: rafaelscosta Date: Wed, 13 May 2026 11:19:05 -0300 Subject: [PATCH 2/3] fix: preserve legacy publish failure status [Story 124.8] --- .github/workflows/npm-publish.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 959e2e789b..ebe66cfb56 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -418,8 +418,9 @@ jobs: if npm publish --tag ${{ steps.publish-tag.outputs.tag }} --access public; then publish_status=0 break + else + publish_status=$? fi - publish_status=$? done rm -f .npmrc if [ "$publish_status" -ne 0 ]; then From 88b20f13c7d8a837e4a38763798413afe03bfccc Mon Sep 17 00:00:00 2001 From: rafaelscosta Date: Wed, 13 May 2026 11:28:24 -0300 Subject: [PATCH 3/3] fix: report legacy publish only when published [Story 124.8] --- .github/workflows/npm-publish.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index ebe66cfb56..e385546dc0 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -357,6 +357,8 @@ jobs: needs: [test, build] if: ${{ contains(format(',{0},', needs.build.outputs.packages), ',aiox-core,') }} runs-on: ubuntu-latest + outputs: + published: ${{ steps.should-publish.outputs.should_publish }} steps: - name: Checkout code uses: actions/checkout@v6 @@ -485,7 +487,7 @@ jobs: fi fi - if [ "${{ needs.publish_legacy_aiox_core.result }}" = "success" ]; then + if [ "${{ needs.publish_legacy_aiox_core.outputs.published }}" = "true" ]; then if [ -n "$PUBLISHED_PACKAGES" ]; then PUBLISHED_PACKAGES="$PUBLISHED_PACKAGES, aiox-core legacy wrapper" else @@ -506,7 +508,7 @@ jobs: create-release-notes: needs: [build, publish, publish_workspace_packages, publish_legacy_aiox_core] - if: github.event_name == 'release' && (needs.publish.result == 'success' || needs.publish_workspace_packages.result == 'success' || needs.publish_legacy_aiox_core.result == 'success') + if: github.event_name == 'release' && (needs.publish.result == 'success' || needs.publish_workspace_packages.result == 'success' || needs.publish_legacy_aiox_core.outputs.published == 'true') runs-on: ubuntu-latest permissions: contents: write