diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..1928d66 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,42 @@ +name: Publish codio-authoring plugin +on: push + +env: + BINARY_REPOSITORY_URL: ${{ secrets.BINARY_REPOSITORY_URL }} + BINARY_REPOSITORY_CREDENTIALS: ${{ secrets.BINARY_REPOSITORY_USER }}:${{ secrets.BINARY_REPOSITORY_PASSWORD }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: build + run: ./build.sh "$GITHUB_SHA" + + - name: Upload artifact + shell: bash + run: | + upload () { + local result_file=( codio-authoring-opencode-plugin-*.tar.gz ) + local git_hash=${1} + + local url="${BINARY_REPOSITORY_URL}/component-builds/codio-authoring-opencode-plugin-${git_hash}.tar.gz" + + curl --fail -i -u"${BINARY_REPOSITORY_CREDENTIALS}" -T "${result_file[0]}" "${url}" + } + upload "${GITHUB_SHA}" + + - name: Slack + uses: codio/codio-slack-action@master + if: always() + with: + slack_hook_url: ${{ secrets.SLACK_WEBHOOK_URL }} + message: " for ${{ github.repository }} by ${{ github.actor }} has ${{ job.status }} on branch ${{ github.ref_name }}" + success: ${{ job.status }} diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..80daa7f --- /dev/null +++ b/build.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -xe + +commit=$1 + +bun install --frozen-lockfile + +bun run build:deploy + +rm -rf _staging +mkdir -p _staging/plugins +cp dist/index.js _staging/plugins/codio-authoring.js +cp -r src/skills _staging/skills + +tar -czf "codio-authoring-opencode-plugin-${commit}.tar.gz" -C _staging . +rm -rf _staging diff --git a/bun.lock b/bun.lock index f03232a..c5a75af 100644 --- a/bun.lock +++ b/bun.lock @@ -12,7 +12,6 @@ "@biomejs/biome": "2.4.11", "@types/node": "^24.6.1", "bun-types": "1.3.12", - "codio-api-js": "^0.18.0", "json-2-csv": "^5.5.10", "typescript": "^5.9.3", "zod": "~4.1.0", @@ -41,14 +40,6 @@ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.11", "", { "os": "win32", "cpu": "x64" }, "sha512-A8D3JM/00C2KQgUV3oj8Ba15EHEYwebAGCy5Sf9GAjr5Y3+kJIYOiESoqRDeuRZueuMdCsbLZIUqmPhpYXJE9A=="], - "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], - - "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], - - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - - "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], - "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="], "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="], @@ -67,172 +58,32 @@ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@tsconfig/node10": ["@tsconfig/node10@1.0.12", "", {}, "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ=="], - - "@tsconfig/node12": ["@tsconfig/node12@1.0.11", "", {}, "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="], - - "@tsconfig/node14": ["@tsconfig/node14@1.0.3", "", {}, "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="], - - "@tsconfig/node16": ["@tsconfig/node16@1.0.4", "", {}, "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="], - - "@types/glob": ["@types/glob@7.2.0", "", { "dependencies": { "@types/minimatch": "*", "@types/node": "*" } }, "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA=="], - - "@types/minimatch": ["@types/minimatch@6.0.0", "", { "dependencies": { "minimatch": "*" } }, "sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA=="], - "@types/node": ["@types/node@24.12.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA=="], - "acorn": ["acorn@8.17.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg=="], - - "acorn-walk": ["acorn-walk@8.3.5", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw=="], - - "arg": ["arg@4.1.3", "", {}, "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="], - - "array-differ": ["array-differ@1.0.0", "", {}, "sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ=="], - - "array-union": ["array-union@1.0.2", "", { "dependencies": { "array-uniq": "^1.0.1" } }, "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng=="], - - "array-uniq": ["array-uniq@1.0.3", "", {}, "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q=="], - - "arrify": ["arrify@1.0.1", "", {}, "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA=="], - - "asap": ["asap@2.0.6", "", {}, "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="], - - "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], - - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - - "bent": ["bent@7.3.12", "", { "dependencies": { "bytesish": "^0.4.1", "caseless": "~0.12.0", "is-stream": "^2.0.0" } }, "sha512-T3yrKnVGB63zRuoco/7Ybl7BwwGZR0lceoVG5XmQyMIH9s19SV5m+a8qam4if0zQuAmOQTyPTPmsQBdAorGK3w=="], - - "brace-expansion": ["brace-expansion@1.1.15", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg=="], - - "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], - "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], - "bytesish": ["bytesish@0.4.4", "", {}, "sha512-i4uu6M4zuMUiyfZN4RU2+i9+peJh//pXhd9x1oSe1LBkZ3LEbCoygu8W0bXTukU1Jme2txKuotpCZRaC3FLxcQ=="], - - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], - - "caseless": ["caseless@0.12.0", "", {}, "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="], - - "chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="], - - "codio-api-js": ["codio-api-js@0.18.0", "", { "dependencies": { "bent": "^7.3.12", "form-data": "^4.0.0", "glob-promise": "^4.2.0", "https": "^1.0.0", "lodash": "^4.17.21", "object-hash": "^2.2.0", "querystring": "^0.2.1", "recursive-copy": "^2.0.11", "simple-zstd": "^1.3.1", "tar": "^6.1.0", "ts-node": "^10.9.1", "uuid": "^8.3.2", "yaml": "^1.10.2" } }, "sha512-rJh0zGimRvJcER615tVmcNyE7moBQSmq56Pi1BiWbOelRVX0Jf4MlFgJHdL7ARZHoOY1MRiZdw5mpjWRA6EHiw=="], - - "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], - - "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - - "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], - - "create-require": ["create-require@1.1.1", "", {}, "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="], - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], "deeks": ["deeks@3.2.1", "", {}, "sha512-D/o0k3pCG1aI1cxb/dDiWmtMc4Rh7ZQBybXpfMsw9Rbtqwg8kUA9SpYkWcw0pAUjZSnPm8MluctiS0o68r69jQ=="], - "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], - "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "diff": ["diff@4.0.4", "", {}, "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ=="], - "doc-path": ["doc-path@4.1.4", "", {}, "sha512-yw5D++UCIB6a033PvQaUvSpW2QuKW0+DOId763n0Q4z3brxS7G8oQr8yBQ1nQFkognKrAVrV6I55TLeU9cfXTg=="], - "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], - - "duplex-maker": ["duplex-maker@1.0.0", "", {}, "sha512-KoHuzggxg7f+vvjqOHfXxaQYI1POzBm+ah0eec7YDssZmbt6QFBI8d1nl5GQwAgR2f+VQCPvyvZtmWWqWuFtlA=="], - - "duplexify": ["duplexify@3.7.1", "", { "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g=="], - "effect": ["effect@4.0.0-beta.59", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-xyUDLeHSe8d6lWGOvR6Fgn2HL6gYeTZ/S4Jzk9uc4ZUxMPPsNZlNXrvk0C7/utQFzeX7uAWcVnG2BjbA0SRoAA=="], - "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], - - "errno": ["errno@0.1.8", "", { "dependencies": { "prr": "~1.0.1" }, "bin": { "errno": "cli.js" } }, "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A=="], - - "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], - - "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - - "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], - - "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], - "fast-check": ["fast-check@4.8.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg=="], "find-my-way-ts": ["find-my-way-ts@0.1.6", "", {}, "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA=="], - "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], - - "fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="], - - "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], - - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - - "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], - - "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - - "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - - "glob-promise": ["glob-promise@4.2.2", "", { "dependencies": { "@types/glob": "^7.1.3" }, "peerDependencies": { "glob": "^7.1.6" } }, "sha512-xcUzJ8NWN5bktoTIX7eOclO1Npxd/dyVqUJxlLIDasT4C7KZyqlPIwkdJ0Ypiy3p2ZKahTjK4M9uC3sNSfNMzw=="], - - "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], - - "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], - - "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], - - "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], - - "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], - - "https": ["https@1.0.0", "", {}, "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg=="], - - "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], - - "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - "ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="], - "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], - - "is-zst": ["is-zst@1.0.0", "", {}, "sha512-ZA5lvshKAl8z30dX7saXLpVhpsq3d2EHK9uf7qtUjnOtdw4XBpAoWb2RvZ5kyoaebdoidnGI0g2hn9Z7ObPbww=="], - - "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "json-2-csv": ["json-2-csv@5.5.11", "", { "dependencies": { "deeks": "3.2.1", "doc-path": "4.1.4" } }, "sha512-kVuwgVL7rfad9ETf02ZZxJPuMR5ZSUn139+T34BfmVxYhb/IsAIm/LzEeQ8YLJmXfuQ5z7LUAFrgPZZM6VLJPw=="], - "junk": ["junk@1.0.3", "", {}, "sha512-3KF80UaaSSxo8jVnRYtMKNGFOoVPBdkkVPsw+Ad0y4oxKXPduS6G6iHkrf69yJVff/VAaYXkV42rtZ7daJxU3w=="], - "kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="], - "lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="], - - "make-error": ["make-error@1.3.6", "", {}, "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="], - - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], - - "maximatch": ["maximatch@0.1.0", "", { "dependencies": { "array-differ": "^1.0.0", "array-union": "^1.0.1", "arrify": "^1.0.0", "minimatch": "^3.0.0" } }, "sha512-9ORVtDUFk4u/NFfo0vG/ND/z7UQCVZBL539YW0+U1I7H1BkZwizcPx5foFv7LCPcBnm2U6RjFnQOsIvN4/Vm2A=="], - - "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - - "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - - "minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - - "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - - "minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="], - - "minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="], - - "mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="], - "msgpackr": ["msgpackr@1.11.12", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg=="], "msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="], @@ -241,108 +92,26 @@ "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], - "object-hash": ["object-hash@2.2.0", "", {}, "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="], - - "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], - - "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "peek-stream": ["peek-stream@1.1.3", "", { "dependencies": { "buffer-from": "^1.0.0", "duplexify": "^3.5.0", "through2": "^2.0.3" } }, "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA=="], - - "pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], - - "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], - - "process-streams": ["process-streams@1.0.3", "", { "dependencies": { "duplex-maker": "^1.0.0" } }, "sha512-xkIaM5vYnyekB88WyET78YEqXiaJRy0xcvIdE22n+myhvBT7LlLmX6iAtq7jDvVH8CUx2rqQsd32JdRyJMV3NA=="], - - "promise": ["promise@7.3.1", "", { "dependencies": { "asap": "~2.0.3" } }, "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg=="], - - "prr": ["prr@1.0.1", "", {}, "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw=="], - "pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="], - "querystring": ["querystring@0.2.1", "", {}, "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg=="], - - "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], - - "recursive-copy": ["recursive-copy@2.0.14", "", { "dependencies": { "errno": "^0.1.2", "graceful-fs": "^4.1.4", "junk": "^1.0.1", "maximatch": "^0.1.0", "mkdirp": "^0.5.1", "pify": "^2.3.0", "promise": "^7.0.1", "rimraf": "^2.7.1", "slash": "^1.0.0" } }, "sha512-K8WNY8f8naTpfbA+RaXmkaQuD1IeW9EgNEfyGxSqqTQukpVtoOKros9jUqbpEsSw59YOmpd8nCBgtqJZy5nvog=="], - - "rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="], - - "safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "simple-zstd": ["simple-zstd@1.4.2", "", { "dependencies": { "is-zst": "^1.0.0", "peek-stream": "^1.1.3", "process-streams": "^1.0.1", "through2": "^4.0.2" } }, "sha512-kGYEvT33M5XfyQvvW4wxl3eKcWbdbCc1V7OZzuElnaXft0qbVzoIIXHXiCm3JCUki+MZKKmvjl8p2VGLJc5Y/A=="], - - "slash": ["slash@1.0.0", "", {}, "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg=="], - - "stream-shift": ["stream-shift@1.0.3", "", {}, "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ=="], - - "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], - - "tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="], - - "through2": ["through2@4.0.2", "", { "dependencies": { "readable-stream": "3" } }, "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw=="], - "toml": ["toml@4.1.1", "", {}, "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw=="], - "ts-node": ["ts-node@10.9.2", "", { "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", "@tsconfig/node16": "^1.0.2", "acorn": "^8.4.1", "acorn-walk": "^8.1.1", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "peerDependencies": { "@swc/core": ">=1.2.50", "@swc/wasm": ">=1.2.50", "@types/node": "*", "typescript": ">=2.7" }, "optionalPeers": ["@swc/core", "@swc/wasm"], "bin": { "ts-node": "dist/bin.js", "ts-script": "dist/bin-script-deprecated.js", "ts-node-cwd": "dist/bin-cwd.js", "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js" } }, "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], - - "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], - - "v8-compile-cache-lib": ["v8-compile-cache-lib@3.0.1", "", {}, "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="], + "uuid": ["uuid@13.0.2", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - - "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], - - "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], - - "yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="], - - "yn": ["yn@3.1.1", "", {}, "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="], + "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], "zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="], - - "@types/minimatch/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], - - "duplexify/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], - - "effect/uuid": ["uuid@13.0.2", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw=="], - - "effect/yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], - - "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], - - "minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], - - "peek-stream/through2": ["through2@2.0.5", "", { "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ=="], - - "string_decoder/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - - "tar/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], - - "@types/minimatch/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="], - - "duplexify/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], - - "peek-stream/through2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], - - "@types/minimatch/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], - - "peek-stream/through2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], } } diff --git a/package.json b/package.json index 81f9c24..8c69fe1 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ }, "scripts": { "build": "bun build src/index.ts --outdir dist --target node --format esm --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod && tsc --emitDeclarationOnly", + "build:deploy": "bun build src/index.ts --outdir dist --target node --format esm --minify && tsc --emitDeclarationOnly", "postinstall": "node ./bin/codio-authoring-register.js || true", "preuninstall": "node ./bin/codio-authoring-unregister.js || true", "test": "bun test", @@ -49,7 +50,6 @@ "@biomejs/biome": "2.4.11", "@types/node": "^24.6.1", "bun-types": "1.3.12", - "codio-api-js": "^0.18.0", "json-2-csv": "^5.5.10", "typescript": "^5.9.3", "zod": "~4.1.0" diff --git a/src/agents/orchestrator.ts b/src/agents/orchestrator.ts index 5a7722a..4b58a26 100644 --- a/src/agents/orchestrator.ts +++ b/src/agents/orchestrator.ts @@ -85,14 +85,6 @@ ${enabledAgents} - \`create_assessment({type, workspace, payload})\` — stamp an assessment JSON with a fresh taskId and write to \`.guides/assessments/\`. Returns \`{ taskId, jsonPath, embedLine }\`. Use after \`@assessment-author\` returns a payload. The returned \`embedLine\` is what you splice into the page markdown. - \`validate_guide({workspace})\` — deterministic structural validation (UUIDs, order arrays, embed references, taskId/filename consistency). Run automatically at the end of new-assignment, import-source, and reorder workflows. Run manually on user request (\`/validate-guide\`). -### Codio platform tools -- \`fetch_course({workspace, courseId, outputDir?, domain?})\` — pull an existing Codio course from the platform into an AI-ready local project. Requires \`CODIO_CLIENT_ID\` and \`CODIO_CLIENT_SECRET\` env vars. Returns \`{ outputDir, manifestPath, moduleCount, assignmentCount }\`. Writes \`course-manifest.json\`, \`course-assignments.csv\`, and extracted assignment content under \`course-content/\`. **Delegate when:** user wants to fetch, pull, import, or export an existing Codio course. -- \`publish_course({manifestPath, dryRun?, force?, module?, assignment?, changelog?, stack?})\` — push course assignments back to the Codio platform using \`course-manifest.json\` as the source of truth. Creates missing courses/modules/assignments automatically. Skips unchanged assignments by content hash. Returns \`{ published, created, unchanged, skipped }\`. **Delegate when:** user wants to publish, push, deploy, or sync assignments to Codio. Always suggest \`dryRun: true\` first to preview. - -### Platform tool routing -- User says "fetch/pull/import/export this course" → \`fetch_course\` -- User says "publish/push/deploy/sync assignments" → \`publish_course\` (dryRun first) -- Typical flow: \`fetch_course\` → edit assignments using authoring tools → \`publish_course\` diff --git a/src/skills/reference-codio-api/SKILL.md b/src/skills/reference-codio-api/SKILL.md deleted file mode 100644 index a39a102..0000000 --- a/src/skills/reference-codio-api/SKILL.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -name: reference-codio-api -description: Reference for the Codio platform API tools — fetch_course and publish_course. Course manifest format, output structure, credential requirements, and troubleshooting. Use when working with fetched courses or publishing assignments. ---- - -# Codio API Tools Reference - -## Credentials - -Both tools read credentials from environment variables. Set these on the Codio box before using either tool: - -| Variable | Required | Description | -|---|---|---| -| `CODIO_CLIENT_ID` | Yes | Codio API client ID | -| `CODIO_CLIENT_SECRET` | Yes | Codio API client secret | -| `CODIO_DOMAIN` | No | Codio domain, default `codio.com` | - ---- - -## `fetch_course` — Pull a course from Codio - -Authenticates with the Codio API, downloads the course export, and writes an AI-ready project to the workspace. - -### Inputs - -| Field | Required | Description | -|---|---|---| -| `workspace` | Yes | Absolute path to the Codio workspace root | -| `courseId` | Yes | Codio course ID (32-char hex string) | -| `outputDir` | No | Output folder name, default `course-project` | -| `domain` | No | Codio domain, default `codio.com` | - -### Output - -Returns `{ outputDir, manifestPath, assignmentsCsvPath, moduleCount, assignmentCount }`. - -Writes to `/` inside the workspace: - -``` -/ -├── course-manifest.json ← machine-readable index; source of truth for publish_course -├── course-assignments.csv ← flat CSV for review -└── course-content/ - └── / - └── / - └── .guides/ ← guide pages, assessments, images -``` - ---- - -## `publish_course` — Push assignments back to Codio - -Reads `course-manifest.json` from the project folder and publishes each assignment to Codio. - -### Inputs - -| Field | Required | Description | -|---|---|---| -| `manifestPath` | Yes | Absolute path to `course-manifest.json` | -| `dryRun` | No | Preview actions without making changes | -| `force` | No | Publish all, bypassing change detection | -| `module` | No | Only process this module (name or folder) | -| `assignment` | No | Only process this assignment (name or folder) | -| `changelog` | No | Changelog text applied to all publishes | -| `stack` | No | Force a specific stack ID for all assignments | - -### Output - -Returns `{ published, created, unchanged, skipped }`. - -Writes back any new IDs to `course-manifest.json` immediately after each resource is created. - -### Change detection - -The tool skips existing assignments whose content hash matches the stored `contentHash` in the manifest. Pass `force: true` to bypass. New assignments (no `id`) are always published. - -### Stacks - -- **New assignments** always use the default stack. -- **Existing assignments** preserve their current stack unless `stack` is supplied. - ---- - -## course-manifest.json format - -Created by `fetch_course`, consumed by `publish_course`. Both tools write back IDs as resources are created/resolved. - -```jsonc -{ - "schemaVersion": 2, - "courseId": "c87b4540de108faad55c8cefe7847492", - "exportedAt": "2026-06-12T10:00:00.000Z", - "counts": { "modules": 2, "assignments": 6 }, - "paths": { - "assignmentsCsv": "course-assignments.csv", - "courseContent": "course-content" - }, - "modules": [ - { - "id": "...", // Resolved or created by publish_course; written back - "name": "Module Name", - "folder": "module-name", - "path": "course-content/module-name", - "assignmentCount": 3, - "assignments": [ - { - "id": "...", // Created by publish_course if empty; written back - "name": "Assignment Name", - "folder": "assignment-name", - "path": "course-content/module-name/assignment-name", - "contentHash": "..." // Written after each successful publish; used for change detection - } - ] - } - ] -} -``` - -### Folder slug rules - -Codio uses lowercase slugs: normalise to ASCII, lowercase, replace each non-alphanumeric character with `-`, trim leading/trailing hyphens. Example: `"1.1 – Intro"` → `"1-1---intro"`. - ---- - -## Typical workflow - -``` -fetch_course(courseId) - → course-project/course-manifest.json + course-content/... - -[edit assignment pages using authoring tools] - -publish_course(manifestPath, dryRun: true) ← preview first -publish_course(manifestPath) ← publish for real -``` - ---- - -## Troubleshooting - -| Error | Fix | -|---|---| -| `CODIO_CLIENT_ID ... must be set` | Set env vars on the box | -| `course-manifest.json not found` | Pass the absolute path to the manifest; run from or inside the project folder | -| `path not found` for an assignment | The folder referenced in the manifest doesn't exist — generate content there first | -| `Required command not found: unzip` | Install `unzip` via the box package manager | -| API 401 / auth error | Credentials are invalid or expired | -| API 429 rate limit | Library retries automatically; wait and re-run if still failing | -| `no module ID available` | Module creation failed earlier in the run — check the error above it | diff --git a/src/tools/fetch-course.test.ts b/src/tools/fetch-course.test.ts deleted file mode 100644 index c6da8f2..0000000 --- a/src/tools/fetch-course.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { describe, expect, test } from 'bun:test'; -import { fetchCourseInputSchema } from './fetch-course'; - -describe('fetchCourseInputSchema', () => { - test('accepts valid input', () => { - expect(() => - fetchCourseInputSchema.parse({ workspace: '/ws', courseId: 'abc123' }), - ).not.toThrow(); - }); - - test('rejects missing workspace', () => { - expect(() => - fetchCourseInputSchema.parse({ courseId: 'abc123' }), - ).toThrow(); - }); - - test('rejects missing courseId', () => { - expect(() => - fetchCourseInputSchema.parse({ workspace: '/ws' }), - ).toThrow(); - }); - - test('accepts optional outputDir and domain', () => { - const result = fetchCourseInputSchema.parse({ - workspace: '/ws', - courseId: 'abc123', - outputDir: 'my-course', - domain: 'codio.co.uk', - }); - expect(result.outputDir).toBe('my-course'); - expect(result.domain).toBe('codio.co.uk'); - }); -}); diff --git a/src/tools/fetch-course.ts b/src/tools/fetch-course.ts deleted file mode 100644 index de68fa6..0000000 --- a/src/tools/fetch-course.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { execFile } from 'node:child_process'; -import { - existsSync, - mkdirSync, - readdirSync, - rmSync, - statSync, - unlinkSync, - writeFileSync, -} from 'node:fs'; -import { writeFile, mkdir, readdir } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { dirname, join, relative } from 'node:path'; -import { promisify } from 'node:util'; -import codio from 'codio-api-js'; -import { json2csv } from 'json-2-csv'; -import { z } from 'zod'; - -const execFileAsync = promisify(execFile); - -export const fetchCourseInputSchema = z.object({ - workspace: z.string().min(1).describe('Absolute path to the Codio workspace root'), - courseId: z.string().min(1).describe('Codio course ID (32-char hex)'), - outputDir: z - .string() - .optional() - .describe('Output folder name inside workspace (default: course-project)'), - domain: z.string().optional().describe('Codio domain (default: codio.com)'), -}); - -export type FetchCourseInput = z.infer; - -export interface FetchCourseResult { - outputDir: string; - manifestPath: string; - assignmentsCsvPath: string; - moduleCount: number; - assignmentCount: number; -} - -function codioFolderSlug(value: string): string { - return String(value ?? '') - .normalize('NFKD') - .replace(/ß/g, 'ss').replace(/ẞ/g, 'ss') - .replace(/æ/g, 'ae').replace(/Æ/g, 'ae') - .replace(/œ/g, 'oe').replace(/Œ/g, 'oe') - .replace(/[̀-ͯ]/g, '') - .toLowerCase() - .replace(/[^a-z0-9]/g, '-') - .replace(/^-+|-+$/g, ''); -} - -function toPosixPath(p: string): string { - return p.split('/').join('/'); -} - -function toManifestPath(manifestPath: string, targetPath: string): string { - return toPosixPath(relative(dirname(manifestPath), targetPath) || '.'); -} - -async function extractZip(zipPath: string, destDir: string): Promise { - mkdirSync(destDir, { recursive: true }); - await execFileAsync('unzip', ['-q', zipPath, '-d', destDir]); -} - -function findTarZstFiles(dir: string): string[] { - const results: string[] = []; - for (const name of readdirSync(dir)) { - const full = join(dir, name); - if (statSync(full).isDirectory()) results.push(...findTarZstFiles(full)); - else if (name.endsWith('.tar.zst')) results.push(full); - } - return results; -} - -async function extractTarZstArchives(stagingDir: string): Promise { - for (const archivePath of findTarZstFiles(stagingDir)) { - const destDir = archivePath.replace(/\.tar\.zst$/, ''); - mkdirSync(destDir, { recursive: true }); - await execFileAsync('tar', ['--zstd', '-xf', archivePath, '-C', destDir]); - unlinkSync(archivePath); - } -} - -function removeExportedReadmes(dir: string): void { - if (!existsSync(dir)) return; - for (const name of readdirSync(dir)) { - const full = join(dir, name); - if (statSync(full).isDirectory()) removeExportedReadmes(full); - else if (name.toLowerCase() === 'readme.md') unlinkSync(full); - } -} - -function copyDir(src: string, dst: string): void { - mkdirSync(dst, { recursive: true }); - for (const name of readdirSync(src)) { - const srcPath = join(src, name); - const dstPath = join(dst, name); - if (statSync(srcPath).isDirectory()) copyDir(srcPath, dstPath); - else { - const { copyFileSync } = require('node:fs') as typeof import('node:fs'); - copyFileSync(srcPath, dstPath); - } - } -} - -export async function fetchCourseHandler(raw: unknown): Promise { - const input = fetchCourseInputSchema.parse(raw); - - const clientId = process.env['CODIO_CLIENT_ID']; - const clientSecret = process.env['CODIO_CLIENT_SECRET']; - if (!clientId || !clientSecret) { - throw new Error( - 'CODIO_CLIENT_ID and CODIO_CLIENT_SECRET must be set as environment variables on the box.', - ); - } - - const domain = input.domain ?? process.env['CODIO_DOMAIN'] ?? 'codio.com'; - const outputFolderName = input.outputDir ?? 'course-project'; - const outputDir = join(input.workspace, outputFolderName); - const contentDir = join(outputDir, 'course-content'); - const manifestPath = join(outputDir, 'course-manifest.json'); - const csvPath = join(outputDir, 'course-assignments.csv'); - - // Authenticate and fetch course metadata - codio.v1.setDomain(domain); - await codio.v1.auth(clientId, clientSecret); - const courseInfo = await codio.v1.course.info(input.courseId); - - // Build manifest - const modules = (courseInfo.modules ?? []).map((mod, mi) => { - const moduleName = mod.name ?? `Module ${mi + 1}`; - const moduleFolder = codioFolderSlug(moduleName); - const modulePath = `course-content/${moduleFolder}`; - const assignments = (mod.assignments ?? []).map((asgn, ai) => { - const assignmentName = asgn.name ?? `Assignment ${ai + 1}`; - const assignmentFolder = codioFolderSlug(assignmentName); - return { - id: asgn.id ?? '', - name: assignmentName, - folder: assignmentFolder, - path: `${modulePath}/${assignmentFolder}`, - }; - }); - return { - name: moduleName, - folder: moduleFolder, - path: modulePath, - assignmentCount: assignments.length, - assignments, - }; - }); - - const manifest = { - schemaVersion: 2, - courseId: input.courseId, - exportedAt: new Date().toISOString(), - counts: { - modules: modules.length, - assignments: modules.reduce((c, m) => c + m.assignments.length, 0), - }, - paths: { assignmentsCsv: 'course-assignments.csv', courseContent: 'course-content' }, - modules, - }; - - // Prepare output directories - rmSync(outputDir, { recursive: true, force: true }); - mkdirSync(contentDir, { recursive: true }); - - // Write manifest and CSV - await mkdir(dirname(manifestPath), { recursive: true }); - await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8'); - - const rows = modules.flatMap(m => - m.assignments.map(a => ({ - module: m.name, moduleFolder: m.folder, modulePath: m.path, - name: a.name, id: a.id, assignmentFolder: a.folder, assignmentPath: a.path, - })) - ); - const csv = await json2csv(rows, { emptyFieldValue: '', prependHeader: true }); - await writeFile(csvPath, csv, 'utf8'); - - // Download course source export ZIP into a temp dir - const workDir = join(tmpdir(), `codio-fetch-${Date.now()}`); - const zipPath = join(workDir, 'course-export.zip'); - mkdirSync(workDir, { recursive: true }); - try { - await codio.v1.course.downloadSourceExport(input.courseId, zipPath); - - // Extract ZIP → staging → extract inner .tar.zst archives → copy to course-content - const stagingDir = join(workDir, 'staging'); - await extractZip(zipPath, stagingDir); - await extractTarZstArchives(stagingDir); - removeExportedReadmes(stagingDir); - copyDir(stagingDir, contentDir); - } finally { - rmSync(workDir, { recursive: true, force: true }); - } - - return { - outputDir, - manifestPath, - assignmentsCsvPath: csvPath, - moduleCount: manifest.counts.modules, - assignmentCount: manifest.counts.assignments, - }; -} diff --git a/src/tools/index.ts b/src/tools/index.ts index 23a10b8..3a5c23c 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,8 +1,6 @@ import { tool } from '@opencode-ai/plugin'; import { createAssessmentHandler } from './create-assessment'; import { createPageHandler, createPageInputSchema } from './create-page'; -import { fetchCourseHandler, fetchCourseInputSchema } from './fetch-course'; -import { publishCourseHandler, publishCourseInputSchema } from './publish-course'; import { validateGuideHandler, validateGuideInputSchema, @@ -55,28 +53,6 @@ export const validate_guide = tool({ }, }); -// ── fetch_course ───────────────────────────────────────────────────────────── - -export const fetch_course = tool({ - description: - 'Pull an existing Codio course from the platform into an AI-ready local project. Authenticates via CODIO_CLIENT_ID/SECRET env vars, downloads the course export, extracts assignment content, and writes course-manifest.json + course-content/. Returns { outputDir, manifestPath, moduleCount, assignmentCount }.', - args: fetchCourseInputSchema.shape, - async execute(args) { - return JSON.stringify(await fetchCourseHandler(args)); - }, -}); - -// ── publish_course ──────────────────────────────────────────────────────────── - -export const publish_course = tool({ - description: - 'Publish Codio course assignments to the platform. Reads course-manifest.json, creates missing courses/modules/assignments, and publishes each one. Skips unchanged assignments by content hash. Returns { published, created, unchanged, skipped }.', - args: publishCourseInputSchema.shape, - async execute(args) { - return JSON.stringify(await publishCourseHandler(args)); - }, -}); - // ── aggregation ────────────────────────────────────────────────────────────── // Annotated as Record to avoid the inferred type referencing @@ -87,6 +63,4 @@ export const tools: Record = { create_page, create_assessment, validate_guide, - fetch_course, - publish_course, }; diff --git a/src/tools/publish-course.test.ts b/src/tools/publish-course.test.ts deleted file mode 100644 index 5410d2e..0000000 --- a/src/tools/publish-course.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { describe, expect, test } from 'bun:test'; -import { publishCourseInputSchema } from './publish-course'; - -describe('publishCourseInputSchema', () => { - test('accepts valid input', () => { - expect(() => - publishCourseInputSchema.parse({ manifestPath: '/ws/my-course/course-manifest.json' }), - ).not.toThrow(); - }); - - test('rejects missing manifestPath', () => { - expect(() => publishCourseInputSchema.parse({})).toThrow(); - }); - - test('rejects empty manifestPath', () => { - expect(() => publishCourseInputSchema.parse({ manifestPath: '' })).toThrow(); - }); - - test('accepts all optional fields', () => { - const result = publishCourseInputSchema.parse({ - manifestPath: '/ws/my-course/course-manifest.json', - dryRun: true, - force: false, - module: 'Basic Skills', - assignment: 'Printing', - changelog: 'Updated content', - stack: 'e0195698-d647-4490-8834-350583b532eb:latest', - }); - expect(result.dryRun).toBe(true); - expect(result.module).toBe('Basic Skills'); - }); -}); diff --git a/src/tools/publish-course.ts b/src/tools/publish-course.ts deleted file mode 100644 index c22450f..0000000 --- a/src/tools/publish-course.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { createHash } from 'node:crypto'; -import { - existsSync, - readdirSync, - readFileSync, - statSync, - writeFileSync, -} from 'node:fs'; -import { join, relative } from 'node:path'; -import codio from 'codio-api-js'; -import { z } from 'zod'; - -const DEFAULT_STACK = 'e0195698-d647-4490-8834-350583b532eb:latest'; - -export const publishCourseInputSchema = z.object({ - manifestPath: z.string().min(1).describe('Absolute path to course-manifest.json'), - dryRun: z.boolean().optional().describe('Preview actions without making changes'), - force: z.boolean().optional().describe('Publish all, bypassing change detection'), - module: z.string().optional().describe('Only process this module (name or folder)'), - assignment: z.string().optional().describe('Only process this assignment (name or folder)'), - changelog: z.string().optional().describe('Changelog message for all publishes'), - stack: z.string().optional().describe('Force a specific stack ID for all assignments'), -}); - -export type PublishCourseInput = z.infer; - -export interface PublishCourseResult { - published: number; - created: number; - unchanged: number; - skipped: number; - dryRun: boolean; -} - -interface ManifestAssignment { - id?: string; - name: string; - folder: string; - path: string; - contentHash?: string; -} - -interface ManifestModule { - id?: string; - name: string; - folder: string; - path: string; - assignments: ManifestAssignment[]; -} - -interface Manifest { - courseId?: string; - courseName?: string; - start?: string; - timezone?: string; - modules: ManifestModule[]; -} - -function saveManifest(manifestPath: string, manifest: Manifest): void { - writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8'); -} - -function findZip(dir: string): string | null { - const zip = readdirSync(dir).find(f => f.endsWith('.zip')); - return zip ? join(dir, zip) : null; -} - -function walkFiles(dir: string): string[] { - const results: string[] = []; - for (const entry of readdirSync(dir, { withFileTypes: true })) { - const full = join(dir, entry.name); - if (entry.isDirectory()) results.push(...walkFiles(full)); - else results.push(full); - } - return results; -} - -function computeContentHash(dir: string): string { - const hash = createHash('sha256'); - const zipPath = findZip(dir); - if (zipPath) { - hash.update(readFileSync(zipPath)); - } else { - for (const file of walkFiles(dir).sort()) { - hash.update(relative(dir, file)); - hash.update('\0'); - hash.update(readFileSync(file)); - hash.update('\0'); - } - } - return hash.digest('hex').slice(0, 16); -} - -export async function publishCourseHandler(raw: unknown): Promise { - const input = publishCourseInputSchema.parse(raw); - - const clientId = process.env['CODIO_CLIENT_ID']; - const clientSecret = process.env['CODIO_CLIENT_SECRET']; - if (!clientId || !clientSecret) { - throw new Error( - 'CODIO_CLIENT_ID and CODIO_CLIENT_SECRET must be set as environment variables on the box.', - ); - } - - if (!existsSync(input.manifestPath)) { - throw new Error(`course-manifest.json not found at ${input.manifestPath}`); - } - - const domain = process.env['CODIO_DOMAIN'] ?? 'codio.com'; - codio.v1.setDomain(domain); - await codio.v1.auth(clientId, clientSecret); - - const manifest: Manifest = JSON.parse(readFileSync(input.manifestPath, 'utf8')); - const manifestDir = join(input.manifestPath, '..'); - - // Create course if missing - if (!manifest.courseId) { - const courseName = manifest.courseName ?? 'Codio Course'; - if (input.dryRun) { - console.log(`[DRY RUN] Would create course: "${courseName}"`); - } else { - const start = manifest.start ?? new Date().toISOString().replace(/T.*/, 'T00:00:00.000Z'); - const timezone = manifest.timezone ?? 'UTC'; - manifest.courseId = await codio.v1.course.createCourse({ name: courseName, start, timezone }); - if (!manifest.courseName) manifest.courseName = courseName; - saveManifest(input.manifestPath, manifest); - } - } - - // Fetch existing module IDs to avoid duplicates - const existingModuleIds = new Map(); - if (manifest.courseId && !input.dryRun) { - try { - const courseInfo = await codio.v1.course.info(manifest.courseId); - for (const m of (courseInfo.modules ?? [])) { - if (m.id && m.name) existingModuleIds.set(m.name, m.id); - } - } catch { - // Non-fatal — proceed without existing module info - } - } - - let published = 0, unchanged = 0, created = 0, skipped = 0; - - for (const mod of manifest.modules) { - if (input.module && mod.name !== input.module && mod.folder !== input.module) continue; - - if (!mod.id) { - const knownId = existingModuleIds.get(mod.name); - if (knownId) { - mod.id = knownId; - saveManifest(input.manifestPath, manifest); - } else if (!input.dryRun && mod.assignments.some(a => !a.id)) { - mod.id = await codio.v1.course.createModule(manifest.courseId!, mod.name); - saveManifest(input.manifestPath, manifest); - created++; - } - } - - for (const asgn of mod.assignments) { - if (input.assignment && asgn.name !== input.assignment && asgn.folder !== input.assignment) - continue; - - const asgnAbsPath = join(manifestDir, asgn.path); - if (!existsSync(asgnAbsPath)) { - skipped++; continue; - } - - const isNew = !asgn.id; - - if (isNew) { - if (input.dryRun) { published++; continue; } - if (!mod.id) { skipped++; continue; } - asgn.id = await codio.v1.assignment.createAssignment(manifest.courseId!, { - moduleId: mod.id, - settings: { name: asgn.name }, - }); - saveManifest(input.manifestPath, manifest); - created++; - } - - if (!isNew && !input.force) { - const currentHash = computeContentHash(asgnAbsPath); - if (currentHash === asgn.contentHash) { unchanged++; continue; } - } - - const stackToUse = input.stack ?? (isNew ? DEFAULT_STACK : undefined); - if (input.dryRun) { published++; continue; } - - const changelog = input.changelog ?? `Published ${asgn.name}`; - const publishOpts = stackToUse ? { changelog, stack: stackToUse } : { changelog }; - const zipPath = findZip(asgnAbsPath); - - try { - if (zipPath) { - await codio.v1.assignment.publishArchive(manifest.courseId!, asgn.id!, zipPath, publishOpts); - } else { - await codio.v1.assignment.publish(manifest.courseId!, asgn.id!, asgnAbsPath, publishOpts); - } - asgn.contentHash = computeContentHash(asgnAbsPath); - saveManifest(input.manifestPath, manifest); - published++; - } catch { - skipped++; - } - } - } - - return { published, created, unchanged, skipped, dryRun: input.dryRun ?? false }; -} diff --git a/src/types/codio-api-js.d.ts b/src/types/codio-api-js.d.ts deleted file mode 100644 index 761ee17..0000000 --- a/src/types/codio-api-js.d.ts +++ /dev/null @@ -1,46 +0,0 @@ -declare module 'codio-api-js' { - interface AssignmentInfo { - id?: string; - name?: string; - } - interface ModuleInfo { - id?: string; - name?: string; - assignments?: AssignmentInfo[]; - } - interface CourseInfo { - modules?: ModuleInfo[]; - } - interface CreateCourseOpts { - name: string; - start: string; - timezone: string; - } - interface CreateAssignmentOpts { - moduleId: string; - settings: { name: string }; - } - interface PublishOpts { - changelog?: string; - stack?: string; - } - - const codio: { - v1: { - setDomain(domain: string): void; - auth(clientId: string, clientSecret: string): Promise; - course: { - info(courseId: string): Promise; - downloadSourceExport(courseId: string, zipPath: string): Promise; - createCourse(opts: CreateCourseOpts): Promise; - createModule(courseId: string, name: string): Promise; - }; - assignment: { - createAssignment(courseId: string, opts: CreateAssignmentOpts): Promise; - publish(courseId: string, assignmentId: string, contentPath: string, opts: PublishOpts): Promise; - publishArchive(courseId: string, assignmentId: string, zipPath: string, opts: PublishOpts): Promise; - }; - }; - }; - export default codio; -}