diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 903ea473f..6b1461011 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,7 +9,7 @@ on: description: Package to publish (single-package selections are dry-run only) required: true type: choice - options: [all, surface, sdk, runtime-linux-x64] + options: [all, surface, sdk, runtime-linux-x64, relayflows] default: all version: description: Version bump type @@ -108,7 +108,26 @@ jobs: ./node_modules/.bin/tsc node scripts/make-cli-executable.mjs - name: Pack and assert SDK + id: sdk run: node scripts/pack-release.mjs sdk + # relayflows only re-exposes the SDK's CLI under the unscoped name, so it + # builds against the packed SDK the same way the SDK builds against the + # packed surface — and it's cheap enough to smoke-test for real here + # rather than only asserting the tarball's shape in pack-release.mjs. + - name: Build relayflows CLI wrapper against packed SDK + env: + SDK_TARBALL: ${{ steps.sdk.outputs.tarball }} + working-directory: packages/relayflows + run: | + npm install --no-save --package-lock=false --ignore-scripts "$SDK_TARBALL" + test ! -L node_modules/@relayflows/sdk + report=$(node bin/flows.js check --json ../../testdata/hello-deterministic.flow.yaml) + echo "$report" | node -e ' + const report = JSON.parse(require("fs").readFileSync(0, "utf8")); + if (report.ok !== true) { console.error(report); process.exit(1); } + ' + - name: Pack and assert relayflows CLI wrapper + run: node scripts/pack-release.mjs relayflows - name: Build relayflowd working-directory: kernel run: cargo build --locked --release -p relayflowd @@ -152,13 +171,19 @@ jobs: with: name: build-output path: dist/build-output + # relayflows is the one unscoped package, so `npm pack` names its tarball + # `relayflows-.tgz`, not `relayflows-relayflows-.tgz`. - name: Restore built packages env: NEW_VERSION: ${{ needs.build.outputs.new_version }} run: | - for package in surface sdk runtime-linux-x64; do - tar -xzf "dist/build-output/relayflows-${package}-${NEW_VERSION}.tgz" \ - --strip-components=1 -C "packages/$package" + for package in surface sdk runtime-linux-x64 relayflows; do + if [[ "$package" == relayflows ]]; then + tarball="relayflows-${NEW_VERSION}.tgz" + else + tarball="relayflows-${package}-${NEW_VERSION}.tgz" + fi + tar -xzf "dist/build-output/$tarball" --strip-components=1 -C "packages/$package" done # Repack and check EVERYTHING before the first publish. Tar archives # preserve executable bits across Actions artifact upload/download. @@ -167,18 +192,24 @@ jobs: node scripts/pack-release.mjs surface node scripts/pack-release.mjs sdk node scripts/pack-release.mjs runtime-linux-x64 - - name: Publish to NPM (surface before SDK) + node scripts/pack-release.mjs relayflows + - name: Publish to NPM (surface before SDK, relayflows last) env: PACKAGE: ${{ inputs.package }} NEW_VERSION: ${{ needs.build.outputs.new_version }} DRY_RUN: ${{ inputs.dry_run }} NPM_TAG: ${{ inputs.tag }} run: | - for package in surface sdk runtime-linux-x64; do + for package in surface sdk runtime-linux-x64 relayflows; do if [[ "$PACKAGE" != all && "$PACKAGE" != "$package" ]]; then continue; fi + if [[ "$package" == relayflows ]]; then + tarball="relayflows-${NEW_VERSION}.tgz" + else + tarball="relayflows-${package}-${NEW_VERSION}.tgz" + fi args=() if [[ "$DRY_RUN" == true ]]; then args+=(--dry-run); fi - npm publish "dist/publish/relayflows-${package}-${NEW_VERSION}.tgz" \ + npm publish "dist/publish/$tarball" \ --access public --provenance --ignore-scripts --tag "$NPM_TAG" "${args[@]}" done - name: Regenerate release lockfiles @@ -188,17 +219,24 @@ jobs: run: | npm install --prefix packages/surface --package-lock-only --ignore-scripts npm install --prefix packages/sdk --package-lock-only --ignore-scripts --save-exact "@relayflows/surface@$NEW_VERSION" + npm install --prefix packages/relayflows --package-lock-only --ignore-scripts --save-exact "@relayflows/sdk@$NEW_VERSION" node --input-type=module - <<'NODE' import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; - const lock = JSON.parse(readFileSync('packages/sdk/package-lock.json', 'utf8')); - const surface = lock.packages['node_modules/@relayflows/surface']; - assert.equal(surface.version, process.env.NEW_VERSION); - assert.match(surface.resolved, /^https:\/\/registry\.npmjs\.org\//); - assert(!surface.link, 'release lockfile must resolve the published surface'); + const version = process.env.NEW_VERSION; + const check = (lockPath, depNodeModulesPath) => { + const lock = JSON.parse(readFileSync(lockPath, 'utf8')); + const dep = lock.packages[depNodeModulesPath]; + assert.equal(dep.version, version); + assert.match(dep.resolved, /^https:\/\/registry\.npmjs\.org\//); + assert(!dep.link, `release lockfile must resolve the published dependency (${lockPath})`); + }; + check('packages/sdk/package-lock.json', 'node_modules/@relayflows/surface'); + check('packages/relayflows/package-lock.json', 'node_modules/@relayflows/sdk'); NODE npm ci --prefix packages/surface --dry-run --ignore-scripts npm ci --prefix packages/sdk --dry-run --ignore-scripts + npm ci --prefix packages/relayflows --dry-run --ignore-scripts - name: Commit version bump and create tag if: ${{ !inputs.dry_run }} env: @@ -209,7 +247,8 @@ jobs: git config user.email 'actions@github.com' git add packages/surface/package.json packages/surface/package-lock.json \ packages/sdk/package.json packages/sdk/package-lock.json \ - packages/runtime-linux-x64/package.json + packages/runtime-linux-x64/package.json \ + packages/relayflows/package.json packages/relayflows/package-lock.json if ! git diff --staged --quiet; then git commit -m "chore(release): v${NEW_VERSION}" fi diff --git a/README.md b/README.md index 257773825..cccf073af 100644 --- a/README.md +++ b/README.md @@ -38,23 +38,15 @@ can run autonomously over days and weeks. Every agent session is observable and - Pull request review pipeline with different agents looking at the pull request from different angles (security, optimization etc) and agents communicate when needed to reach consensus — [`examples/pr-review-pipeline/`](examples/pr-review-pipeline/) - Dependency upgrade bot: deterministic check flags a dependency out of date which fires an agent who does the upgrade in a sandbox. This upgrade is gated on another agent verifying the entire application with computer use in another sandbox. If completely verified a pull request is opened up — [`examples/dependency-upgrade-bot/`](examples/dependency-upgrade-bot/) -## Cloud review swarm - -Every pull request launches the cloud review swarm. Repository administrators -must configure two Actions secrets. The workflow fails during preflight, in -seconds and before submitting a run, when either is absent. - -| Secret | What it is | How to obtain it | -|---|---|---| -| `RELAY_WORKSPACE_KEY` | Selects the messaging workspace the swarm runs in. | `agent-relay workspace key --reveal-secrets` | -| `CLOUD_API_KEY` | The Cloud API key for workflow invocation. | Follow `AgentWorkforce/cloud` → `docs/runbooks/relay-ci-workflow-credential.md`, profile `workflow-invoke` | - -`CLOUD_API_URL` is not secret; the workflow defaults it and it can be -overridden with a repository variable of the same name. - -A workspace key alone cannot run the swarm. `agent-relay cloud run` authenticates -to the Cloud API using `CLOUD_API_KEY`: the workspace key is read only by the -resolver that picks a messaging workspace, while the API key authenticates -`POST /api/v1/workflows/prepare` — which `--sync-code` requires, and `--sync-code` -is how the swarm receives the PR diff. Given no API key, the CLI falls back to an -interactive device login that no runner can approve and exits after the grant expires. + +# Get Started + +Installation: +``` +npm install -g relayflows +``` + +Give your agent a skill to write a flow: +``` +npx skills add https://github.com/agentworkforce/skills --skill writing-relayflows +``` diff --git a/packages/relayflows/README.md b/packages/relayflows/README.md new file mode 100644 index 000000000..54888a9d3 --- /dev/null +++ b/packages/relayflows/README.md @@ -0,0 +1,20 @@ +# relayflows + +The `flows` CLI, published under the unscoped name so the install is just: + +```sh +npm install -g relayflows +``` + +This package carries no logic of its own. It declares `@relayflows/sdk` as a +normal dependency and `bin/flows.js` forwards argv and stdio straight to that +dependency's `dist/cli.js`, resolved by filesystem path from this package's +own `node_modules` rather than by import specifier — the SDK's package +`exports` doesn't list that subpath, so a bare `import` would be refused. + +Versioned and released in lockstep with `@relayflows/sdk`: the two always +carry the same version number, and this package pins its dependency to that +exact version rather than a range, so `npm install -g relayflows` always +resolves the SDK build it shipped with. + +See `@relayflows/sdk` and `docs/SURFACE.md` for what the CLI actually does. diff --git a/packages/relayflows/bin/flows.js b/packages/relayflows/bin/flows.js new file mode 100755 index 000000000..585c4bf1d --- /dev/null +++ b/packages/relayflows/bin/flows.js @@ -0,0 +1,23 @@ +#!/usr/bin/env node +import { spawnSync } from 'node:child_process'; +import { existsSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +// Not `import '@relayflows/sdk/dist/cli.js'`: that subpath isn't in the +// SDK's package "exports", so specifier resolution would refuse it. Locating +// the installed dependency's real CLI file directly sidesteps that — this +// package's only job is finding it and forwarding argv/stdio. +const packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..'); +const sdkCli = join(packageRoot, 'node_modules', '@relayflows', 'sdk', 'dist', 'cli.js'); + +if (!existsSync(sdkCli)) { + process.stderr.write( + `relayflows: could not find @relayflows/sdk at ${sdkCli}\n` + + 'Reinstall with `npm install -g relayflows`.\n', + ); + process.exit(1); +} + +const result = spawnSync(process.execPath, [sdkCli, ...process.argv.slice(2)], { stdio: 'inherit' }); +process.exit(result.status ?? 1); diff --git a/packages/relayflows/package-lock.json b/packages/relayflows/package-lock.json new file mode 100644 index 000000000..1e787f6d8 --- /dev/null +++ b/packages/relayflows/package-lock.json @@ -0,0 +1,161 @@ +{ + "name": "relayflows", + "version": "2.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "relayflows", + "version": "2.0.1", + "license": "Apache-2.0", + "dependencies": { + "@relayflows/sdk": "2.0.1" + }, + "bin": { + "flows": "bin/flows.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@relayflows/sdk": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@relayflows/sdk/-/sdk-2.0.1.tgz", + "integrity": "sha512-veI1dFVBXtSTrOjKrUJ6mv4mHUtt005SoAHoIq5st3ZmOEDLuiJEp+r0G1dum5z7wgFngCJI0WHOy0UD1jSGPw==", + "license": "Apache-2.0", + "dependencies": { + "@relayflows/surface": "2.0.1", + "@types/js-yaml": "^4.0.9", + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "js-yaml": "^5.4.1", + "yaml": "^2.5.1" + }, + "bin": { + "flows": "dist/cli.js" + } + }, + "node_modules/@relayflows/surface": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@relayflows/surface/-/surface-2.0.1.tgz", + "integrity": "sha512-I8ItRJhUHSfW8Tl33SvgExc7AAmtD4Qg/IiMBLOzg7BucZ5nOJfqvsaibfM03C7krBT+HI7ok3sg2VcCK+eyBA==", + "license": "Apache-2.0" + }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "license": "MIT" + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "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/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", + "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "funding": [ + { + "type": "github", + "url": "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/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/js-yaml": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.1.tgz", + "integrity": "sha512-28R/k+NAjeuf7+CKlTxWZVExJGwVVLwY06DgEnOMz2gEpfNkDcD7QvyiVPT0xy0XXhU8vHsd4Ot42OOPdJG7dQ==", + "funding": [ + { + "type": "github", + "url": "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/sponsors/puzrin" + }, + { + "type": "github", + "url": "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/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.mjs" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "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/sponsors/eemeli" + } + } + } +} diff --git a/packages/relayflows/package.json b/packages/relayflows/package.json new file mode 100644 index 000000000..dd7aa0c5b --- /dev/null +++ b/packages/relayflows/package.json @@ -0,0 +1,24 @@ +{ + "name": "relayflows", + "version": "2.0.1", + "description": "Relayflow v2 CLI. Installs the `flows` command — a thin wrapper around @relayflows/sdk, published under the unscoped name for `npm install -g relayflows`.", + "type": "module", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "git+https://github.com/AgentWorkforce/flows.git", + "directory": "packages/relayflows" + }, + "files": [ + "bin" + ], + "bin": { + "flows": "./bin/flows.js" + }, + "dependencies": { + "@relayflows/sdk": "2.0.1" + }, + "engines": { + "node": ">=20" + } +} diff --git a/scripts/pack-release.mjs b/scripts/pack-release.mjs index 0808748ef..09a583c56 100644 --- a/scripts/pack-release.mjs +++ b/scripts/pack-release.mjs @@ -5,7 +5,10 @@ import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; const [name, output = 'dist/publish'] = process.argv.slice(2); -assert(['surface', 'sdk', 'runtime-linux-x64'].includes(name), 'unknown release package'); +assert(['surface', 'sdk', 'runtime-linux-x64', 'relayflows'].includes(name), 'unknown release package'); +// Every release package is scoped (@relayflows/) except the CLI alias, +// which is published unscoped so `npm install -g relayflows` names it directly. +const expectedName = name === 'relayflows' ? 'relayflows' : `@relayflows/${name}`; const directory = resolve(`packages/${name}`); const destination = resolve(output); mkdirSync(destination, { recursive: true }); @@ -18,7 +21,7 @@ try { execFileSync('tar', ['-xzf', archive, '-C', unpacked]); const root = join(unpacked, 'package'); const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')); - assert.equal(pkg.name, `@relayflows/${name}`); + assert.equal(pkg.name, expectedName); const expected = JSON.parse(readFileSync('packages/sdk/package.json', 'utf8')).version; assert.equal(pkg.version, expected, 'package version differs from SDK anchor'); for (const type of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) { @@ -29,7 +32,9 @@ try { } const required = name === 'runtime-linux-x64' ? ['bin/relayflowd', 'bin/flows'] - : ['dist/index.js', 'dist/index.d.ts']; + : name === 'relayflows' + ? ['bin/flows.js'] + : ['dist/index.js', 'dist/index.d.ts']; if (name === 'surface') required.push('dist/runtime.js', 'dist/runtime.d.ts'); if (name === 'sdk') required.push('dist/cli.js'); for (const file of required) { diff --git a/scripts/publish.test.mjs b/scripts/publish.test.mjs index a32db7e54..a303bf6fa 100644 --- a/scripts/publish.test.mjs +++ b/scripts/publish.test.mjs @@ -1,6 +1,6 @@ import { test } from 'node:test'; import assert from 'node:assert/strict'; -import { spawnSync } from 'node:child_process'; +import { execFileSync, spawnSync } from 'node:child_process'; import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; @@ -11,7 +11,7 @@ const read = (path) => JSON.parse(readFileSync(path, 'utf8')); function fixture(t) { const root = mkdtempSync(join(tmpdir(), 'flows-publish-test-')); t.after(() => rmSync(root, { recursive: true, force: true })); - for (const name of ['surface', 'sdk', 'runtime-linux-x64']) { + for (const name of ['surface', 'sdk', 'runtime-linux-x64', 'relayflows']) { mkdirSync(join(root, 'packages', name), { recursive: true }); const path = join(root, 'packages', name, 'package.json'); cpSync(`packages/${name}/package.json`, path); @@ -47,7 +47,7 @@ test('one SDK anchor rewrites all internal dependency types and preserves extern writeFileSync(path, JSON.stringify(pkg)); const result = version(root, { CUSTOM_VERSION: '3.0.0-rc.2' }); assert.equal(result.status, 0, result.stderr); - for (const name of ['sdk', 'surface', 'runtime-linux-x64']) { + for (const name of ['sdk', 'surface', 'runtime-linux-x64', 'relayflows']) { assert.equal(read(join(root, 'packages', name, 'package.json')).version, '3.0.0-rc.2'); } const updated = read(path); @@ -56,6 +56,8 @@ test('one SDK anchor rewrites all internal dependency types and preserves extern } assert.equal(updated.optionalDependencies['@relayflows/runtime-linux-x64'], '3.0.0-rc.2'); assert.equal(updated.dependencies.yaml, pkg.dependencies.yaml); + const relayflows = read(join(root, 'packages/relayflows/package.json')); + assert.equal(relayflows.dependencies['@relayflows/sdk'], '3.0.0-rc.2'); }); test('prerelease bumps use the SDK anchor and output the resolved version', (t) => { @@ -101,6 +103,34 @@ test('actual npm tarballs reject missing dist and local dependencies, then accep assert.match(built.stdout, /PACK_OK @relayflows\/surface@2.0.0/); }); +test('relayflows tarball publishes unscoped and rejects a missing bin', (t) => { + const root = fixture(t); + // fixture() pins every package to '2.0.0' independently; align relayflows's + // committed dependency pin the way version-packages.mjs would for a real + // release, so this test exercises the bin/executable checks, not the + // (separately real) local-dependency-drift assertion. + const path = join(root, 'packages/relayflows/package.json'); + const pkg = read(path); + pkg.dependencies['@relayflows/sdk'] = '2.0.0'; + writeFileSync(path, JSON.stringify(pkg)); + const run = () => spawnSync(process.execPath, [packScript, 'relayflows'], { cwd: root, encoding: 'utf8' }); + const missing = run(); + assert.notEqual(missing.status, 0); + assert.match(missing.stderr, /missing package\/bin\/flows.js/); + const bin = join(root, 'packages/relayflows/bin'); + mkdirSync(bin); + writeFileSync(join(bin, 'flows.js'), '#!/usr/bin/env node\n'); + const built = run(); + // Not yet executable: same failure pack-release.mjs gives for surface/sdk/runtime. + assert.notEqual(built.status, 0); + assert.match(built.stderr, /non-executable \.\/bin\/flows\.js/); + execFileSync('chmod', ['+x', join(bin, 'flows.js')]); + const executable = run(); + assert.equal(executable.status, 0, executable.stderr); + // Unscoped — not `@relayflows/relayflows`, unlike every other release package. + assert.match(executable.stdout, /PACK_OK relayflows@2.0.0/); +}); + test('runtime tarball refuses an unstaged binary package', (t) => { const root = fixture(t); const result = spawnSync(process.execPath, [packScript, 'runtime-linux-x64'], { diff --git a/scripts/version-packages.mjs b/scripts/version-packages.mjs index 47a518cec..99d696d5e 100644 --- a/scripts/version-packages.mjs +++ b/scripts/version-packages.mjs @@ -2,7 +2,7 @@ import { readFileSync, writeFileSync } from 'node:fs'; import { execFileSync } from 'node:child_process'; // The SDK is the version anchor; no package independently computes a bump. -const paths = ['surface', 'sdk', 'runtime-linux-x64'].map((name) => `packages/${name}/package.json`); +const paths = ['surface', 'sdk', 'runtime-linux-x64', 'relayflows'].map((name) => `packages/${name}/package.json`); if (process.env.CUSTOM_VERSION && !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(process.env.CUSTOM_VERSION)) { throw new Error('custom_version must be a semantic version'); }