From 2342fd141633204db6095855625254ec6bd3551a Mon Sep 17 00:00:00 2001 From: Will Killian Date: Thu, 14 May 2026 09:06:58 -0400 Subject: [PATCH 1/7] ci: fold OpenClaw checks into Node workflow Signed-off-by: Will Killian --- .github/workflows/ci.yaml | 16 +---- .github/workflows/ci_changes.yml | 4 +- .github/workflows/ci_node.yml | 50 ++++++++++++++- .github/workflows/ci_openclaw.yml | 97 ----------------------------- docs/contribute/testing-and-docs.md | 5 +- justfile | 1 + 6 files changed, 56 insertions(+), 117 deletions(-) delete mode 100644 .github/workflows/ci_openclaw.yml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0c85376e0..5373de229 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -197,17 +197,7 @@ jobs: ref_type: ${{ github.ref_type }} ref_name: ${{ github.ref_name }} run_package: ${{ needs.ci_changes.outputs.run_node_package == 'true' }} - - ci_openclaw: - name: OpenClaw - needs: [prepare, ci_changes, ci_check] - uses: ./.github/workflows/ci_openclaw.yml - if: ${{ needs.ci_check.result == 'success' && needs.ci_changes.outputs.run_openclaw == 'true' }} - permissions: - contents: read - with: - ref_type: ${{ github.ref_type }} - ref_name: ${{ github.ref_name }} + run_openclaw: ${{ needs.ci_changes.outputs.run_openclaw == 'true' }} ci_python: name: Python @@ -247,7 +237,6 @@ jobs: - ci_rust - ci_go - ci_node - - ci_openclaw - ci_python - ci_wasm if: ${{ always() && !cancelled() && needs.prepare.result == 'success' && ! fromJSON(needs.prepare.outputs.has_skip_ci_label) }} @@ -263,7 +252,6 @@ jobs: RUST_RESULT: ${{ needs.ci_rust.result }} GO_RESULT: ${{ needs.ci_go.result }} NODE_RESULT: ${{ needs.ci_node.result }} - OPENCLAW_RESULT: ${{ needs.ci_openclaw.result }} PYTHON_RESULT: ${{ needs.ci_python.result }} WEBASSEMBLY_RESULT: ${{ needs.ci_wasm.result }} publish_docs: ${{ needs.prepare.outputs.publish_docs }} @@ -302,13 +290,11 @@ jobs: if [[ "$publish_packages" == "true" ]]; then require_success "Rust" "$RUST_RESULT" require_success "Node.js" "$NODE_RESULT" - require_success "OpenClaw" "$OPENCLAW_RESULT" require_success "Python" "$PYTHON_RESULT" require_success "WebAssembly" "$WEBASSEMBLY_RESULT" else allow_success_or_skipped "Rust" "$RUST_RESULT" allow_success_or_skipped "Node.js" "$NODE_RESULT" - allow_success_or_skipped "OpenClaw" "$OPENCLAW_RESULT" allow_success_or_skipped "Python" "$PYTHON_RESULT" allow_success_or_skipped "WebAssembly" "$WEBASSEMBLY_RESULT" fi diff --git a/.github/workflows/ci_changes.yml b/.github/workflows/ci_changes.yml index 5ead5d56a..6785992a6 100644 --- a/.github/workflows/ci_changes.yml +++ b/.github/workflows/ci_changes.yml @@ -76,9 +76,9 @@ jobs: run_dependencies: ${{ inputs.full_ci || steps.filter.outputs.dependencies == 'true' }} run_docs: ${{ (inputs.full_ci || startsWith(inputs.ref_name, 'pull-request/')) && (inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.docs == 'true') }} run_go: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.go == 'true' }} - run_node: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.node == 'true' }} + run_node: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.node == 'true' || steps.filter.outputs.openclaw == 'true' }} run_node_package: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.node_package == 'true' }} - run_openclaw: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.openclaw == 'true' }} + run_openclaw: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.node == 'true' || steps.filter.outputs.openclaw == 'true' }} run_python: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.python == 'true' }} run_python_package: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.python_package == 'true' }} run_rust: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.rust == 'true' }} diff --git a/.github/workflows/ci_node.yml b/.github/workflows/ci_node.yml index 42ba4577d..65a99e6e8 100644 --- a/.github/workflows/ci_node.yml +++ b/.github/workflows/ci_node.yml @@ -19,6 +19,11 @@ on: required: false default: true type: boolean + run_openclaw: + description: 'Whether to run OpenClaw integration checks' + required: false + default: false + type: boolean secrets: CODECOV_TOKEN: required: false @@ -90,6 +95,11 @@ jobs: working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }} run: just --set ci true --set output_dir "${{ github.workspace }}" test-node + - name: Run OpenClaw integration checks + if: ${{ inputs.run_openclaw }} + working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }} + run: just --set ci true test-openclaw + - name: Upload Node coverage to Codecov uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6 if: ${{ !startsWith(matrix.platform, 'windows') }} @@ -104,7 +114,7 @@ jobs: Package: name: Package (${{ matrix.platform }}) needs: [Test] - if: ${{ inputs.run_package && !cancelled() && needs.Test.result == 'success' }} + if: ${{ (inputs.run_package || inputs.run_openclaw) && !cancelled() && needs.Test.result == 'success' }} runs-on: ${{ matrix.runner }} timeout-minutes: 30 permissions: @@ -167,11 +177,13 @@ jobs: tool: just@${{ steps.ci-config.outputs.just_version }} - name: Create packaging output directory + if: ${{ inputs.run_package }} run: | set -e mkdir -p "${{ env.NEMO_FLOW_CI_WORKSPACE_TMP }}/npm" - name: Derive Node package version + if: ${{ inputs.run_package }} working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }} run: | set -e @@ -185,6 +197,7 @@ jobs: printf 'NEMO_FLOW_PACKAGE_VERSION=%s\n' "$version" >> "$GITHUB_ENV" - name: Package Node + if: ${{ inputs.run_package }} working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }} run: | set -e @@ -196,15 +209,48 @@ jobs: - name: Upload npm package artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + if: ${{ inputs.run_package }} with: name: npm-${{ matrix.platform }} path: ${{ env.NEMO_FLOW_CI_WORKSPACE_TMP }}/npm/*.tgz if-no-files-found: error + - name: Derive OpenClaw package version + if: ${{ inputs.run_openclaw && matrix.platform == 'linux-amd64' }} + working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }} + run: | + set -e + version="$(node -e 'const fs = require("fs"); const pkg = JSON.parse(fs.readFileSync("integrations/openclaw/package.json", "utf8")); if (!pkg.version) { throw new Error("integrations/openclaw/package.json missing version field"); } console.log(pkg.version);')" + sha="${GITHUB_SHA::8}" + if [ "${{ inputs.ref_type }}" = "tag" ]; then + version="${{ inputs.ref_name }}" + else + version="${version}-${sha}" + fi + printf 'NEMO_FLOW_OPENCLAW_PACKAGE_VERSION=%s\n' "$version" >> "$GITHUB_ENV" + + - name: Package OpenClaw plugin + if: ${{ inputs.run_openclaw && matrix.platform == 'linux-amd64' }} + working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }} + run: | + set -euo pipefail + just \ + --set output_dir "${NEMO_FLOW_CI_WORKSPACE_TMP}" \ + --set ref_name "${NEMO_FLOW_OPENCLAW_PACKAGE_VERSION}" \ + package-openclaw + + - name: Upload OpenClaw plugin artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + if: ${{ inputs.run_openclaw && matrix.platform == 'linux-amd64' }} + with: + name: openclaw-npm + path: ${{ env.NEMO_FLOW_CI_WORKSPACE_TMP }}/openclaw/*.tgz + if-no-files-found: error + Consolidate: name: Consolidate needs: [Package] - if: ${{ !cancelled() && needs.Package.result == 'success' }} + if: ${{ inputs.run_package && !cancelled() && needs.Package.result == 'success' }} runs-on: ubuntu-latest timeout-minutes: 30 permissions: diff --git a/.github/workflows/ci_openclaw.yml b/.github/workflows/ci_openclaw.yml deleted file mode 100644 index 5af12e359..000000000 --- a/.github/workflows/ci_openclaw.yml +++ /dev/null @@ -1,97 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -name: OpenClaw - -on: - workflow_call: - inputs: - ref_type: - description: 'The ref type from the triggering workflow' - required: true - type: string - ref_name: - description: 'The ref name from the triggering workflow' - required: true - type: string - -defaults: - run: - shell: bash - -env: - GH_TOKEN: "${{ github.token }}" - GIT_COMMIT: "${{ github.sha }}" - NEMO_FLOW_CI_WORKSPACE: "${{ github.workspace }}" - NEMO_FLOW_CI_WORKSPACE_TMP: "${{ github.workspace }}/ci/tmp" - UV_PYTHON_DOWNLOADS: never - -jobs: - Test: - name: Test - runs-on: ubuntu-latest - timeout-minutes: 30 - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Load CI tool versions - id: ci-config - uses: ./.github/actions/load-ci-tool-versions - - - uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4 - with: - cache: false - toolchain: ${{ steps.ci-config.outputs.rust_version }} - - - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - with: - shared-key: nemo-flow-rust-${{ runner.os }}-${{ runner.arch }}-${{ steps.ci-config.outputs.rust_version }} - workspaces: . -> target - cache-all-crates: true - cache-bin: false - save-if: false - - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 - with: - node-version: ${{ steps.ci-config.outputs.node_version }} - - - uses: taiki-e/install-action@5939f3337e40968c39aa70f5ecb1417a92fb25a0 # v2.75.15 - with: - tool: just@${{ steps.ci-config.outputs.just_version }} - - - name: Run OpenClaw integration checks - working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }} - run: just --set ci true test-openclaw - - - name: Derive OpenClaw package version - working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }} - run: | - set -e - version="$(node -e 'const fs = require("fs"); const pkg = JSON.parse(fs.readFileSync("integrations/openclaw/package.json", "utf8")); if (!pkg.version) { throw new Error("integrations/openclaw/package.json missing version field"); } console.log(pkg.version);')" - sha="${GITHUB_SHA::8}" - if [ "${{ inputs.ref_type }}" = "tag" ]; then - version="${{ inputs.ref_name }}" - else - version="${version}-${sha}" - fi - printf 'NEMO_FLOW_PACKAGE_VERSION=%s\n' "$version" >> "$GITHUB_ENV" - - - name: Package OpenClaw plugin - working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }} - run: | - set -euo pipefail - just \ - --set output_dir "${NEMO_FLOW_CI_WORKSPACE_TMP}" \ - --set ref_name "${NEMO_FLOW_PACKAGE_VERSION}" \ - package-openclaw - - - name: Upload OpenClaw plugin artifact - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: openclaw-npm - path: ${{ env.NEMO_FLOW_CI_WORKSPACE_TMP }}/openclaw/*.tgz - if-no-files-found: error diff --git a/docs/contribute/testing-and-docs.md b/docs/contribute/testing-and-docs.md index 634dd95cf..a094c8a0f 100644 --- a/docs/contribute/testing-and-docs.md +++ b/docs/contribute/testing-and-docs.md @@ -23,6 +23,7 @@ just ci=true test-rust just test-python just test-go just test-node +just test-openclaw just test-wasm ``` @@ -62,11 +63,13 @@ uv run pytest ### Node.js Run the Node.js validation loop when a change touches the NAPI binding or -JavaScript package surface. +JavaScript package surface. Run the OpenClaw target when Node changes can affect +the OpenClaw plugin or when touching `integrations/openclaw`. ```bash npm install --ignore-scripts npm test --workspace=nemo-flow-node +just test-openclaw ``` ## Documentation Checklist diff --git a/justfile b/justfile index 24015536e..d78daa1ce 100644 --- a/justfile +++ b/justfile @@ -918,6 +918,7 @@ test-openclaw: fi npm run typecheck --workspace=nemo-flow-openclaw npm test --workspace=nemo-flow-openclaw + npm run test:live --workspace=nemo-flow-openclaw npm run pack:check --workspace=nemo-flow-openclaw # --set [output_dir=] [ci=true|false] From 0738b4cb496a74de7b0acf6e49130e3c48d3798b Mon Sep 17 00:00:00 2001 From: Will Killian Date: Thu, 14 May 2026 09:49:28 -0400 Subject: [PATCH 2/7] ci: make OpenClaw live smoke cross-platform Signed-off-by: Will Killian --- integrations/openclaw/package.json | 2 +- integrations/openclaw/scripts/test-live.mjs | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 integrations/openclaw/scripts/test-live.mjs diff --git a/integrations/openclaw/package.json b/integrations/openclaw/package.json index c25633dda..61182919e 100644 --- a/integrations/openclaw/package.json +++ b/integrations/openclaw/package.json @@ -52,7 +52,7 @@ "prepack": "npm run build", "typecheck": "tsc -p tsconfig.json --noEmit", "test": "npm run build:test && node --test \".test-dist/src/__tests__/*.test.js\"", - "test:live": "npm run build:test && NEMO_FLOW_OPENCLAW_LIVE_SMOKE=1 node --test \".test-dist/src/__tests__/live-smoke.test.js\"" + "test:live": "npm run build:test && node scripts/test-live.mjs" }, "peerDependencies": { "openclaw": ">=2026.5.6" diff --git a/integrations/openclaw/scripts/test-live.mjs b/integrations/openclaw/scripts/test-live.mjs new file mode 100644 index 000000000..8d6af0c87 --- /dev/null +++ b/integrations/openclaw/scripts/test-live.mjs @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { spawnSync } from "node:child_process"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); + +const result = spawnSync(process.execPath, ["--test", ".test-dist/src/__tests__/live-smoke.test.js"], { + cwd: packageRoot, + env: { + ...process.env, + NEMO_FLOW_OPENCLAW_LIVE_SMOKE: "1", + }, + stdio: "inherit", +}); + +process.exit(result.status ?? 1); From 2b77a69a184dbbbdfb8fa0e8bf7e7f34e0e1f979 Mon Sep 17 00:00:00 2001 From: Will Killian Date: Thu, 14 May 2026 09:53:02 -0400 Subject: [PATCH 3/7] test: move OpenClaw tests out of src Signed-off-by: Will Killian --- integrations/openclaw/README.md | 5 +++-- integrations/openclaw/package.json | 2 +- integrations/openclaw/scripts/check-pack-payload.mjs | 4 ++-- integrations/openclaw/scripts/test-live.mjs | 2 +- .../openclaw/{src/__tests__ => test}/config.test.ts | 6 +++--- .../{src/__tests__ => test}/failure-model.test.ts | 6 +++--- .../openclaw/{src/__tests__ => test}/gateway-status.ts | 2 +- .../{src/__tests__ => test}/hooks-backend.test.ts | 8 ++++---- .../openclaw/{src/__tests__ => test}/live-smoke.test.ts | 4 ++-- .../openclaw/{src/__tests__ => test}/llm-replay.test.ts | 6 +++--- .../openclaw/{src/__tests__ => test}/tool-replay.test.ts | 6 +++--- integrations/openclaw/tsconfig.build.json | 2 +- integrations/openclaw/tsconfig.test.json | 6 ++++++ 13 files changed, 33 insertions(+), 26 deletions(-) rename integrations/openclaw/{src/__tests__ => test}/config.test.ts (99%) rename integrations/openclaw/{src/__tests__ => test}/failure-model.test.ts (94%) rename integrations/openclaw/{src/__tests__ => test}/gateway-status.ts (94%) rename integrations/openclaw/{src/__tests__ => test}/hooks-backend.test.ts (98%) rename integrations/openclaw/{src/__tests__ => test}/live-smoke.test.ts (98%) rename integrations/openclaw/{src/__tests__ => test}/llm-replay.test.ts (99%) rename integrations/openclaw/{src/__tests__ => test}/tool-replay.test.ts (97%) diff --git a/integrations/openclaw/README.md b/integrations/openclaw/README.md index d4ceae053..fdaa1c366 100644 --- a/integrations/openclaw/README.md +++ b/integrations/openclaw/README.md @@ -226,8 +226,9 @@ npm run pack:check --workspace=nemo-flow-openclaw `npm run build --workspace=nemo-flow-openclaw` emits production files under `integrations/openclaw/dist/`. Tests compile to -`integrations/openclaw/.test-dist/` so test artifacts do not enter the -installable package. +`integrations/openclaw/.test-dist/` from the sibling +`integrations/openclaw/test/` directory so test artifacts do not enter the +installable package or production source tree. The optional live smoke test requires a working installed `nemo-flow-node` binding: diff --git a/integrations/openclaw/package.json b/integrations/openclaw/package.json index 61182919e..ca5b48d73 100644 --- a/integrations/openclaw/package.json +++ b/integrations/openclaw/package.json @@ -51,7 +51,7 @@ "pack:check": "node scripts/check-pack-payload.mjs", "prepack": "npm run build", "typecheck": "tsc -p tsconfig.json --noEmit", - "test": "npm run build:test && node --test \".test-dist/src/__tests__/*.test.js\"", + "test": "npm run build:test && node --test \".test-dist/test/*.test.js\"", "test:live": "npm run build:test && node scripts/test-live.mjs" }, "peerDependencies": { diff --git a/integrations/openclaw/scripts/check-pack-payload.mjs b/integrations/openclaw/scripts/check-pack-payload.mjs index 0e56d0534..57a87716b 100644 --- a/integrations/openclaw/scripts/check-pack-payload.mjs +++ b/integrations/openclaw/scripts/check-pack-payload.mjs @@ -82,7 +82,7 @@ const packInfo = JSON.parse(pack.stdout)[0]; assert(packInfo, "npm pack did not return package metadata"); const productionSources = walkFiles("src").filter( - (file) => file.endsWith(".ts") && !file.includes("/__tests__/") && !file.endsWith(".test.ts"), + (file) => file.endsWith(".ts") && !file.endsWith(".test.ts"), ); const packedFiles = new Set(packInfo.files.map((file) => normalizePackagePath(file.path))); const packageJson = JSON.parse(readFileSync(path.join(packageRoot, "package.json"), "utf8")); @@ -129,7 +129,7 @@ assert(packageJson.openclaw?.build?.openclawVersion, "openclaw.build.openclawVer assert(packageJson.openclaw?.build?.pluginSdkVersion, "openclaw.build.pluginSdkVersion is required"); for (const file of packedFiles) { - assert(!file.includes("__tests__"), `packed package includes test artifact ${file}`); + assert(!file.startsWith("test/"), `packed package includes test artifact ${file}`); assert(!file.startsWith(".test-dist/"), `packed package includes test output ${file}`); assert(!file.endsWith(".map"), `packed package includes source/declaration map ${file}`); } diff --git a/integrations/openclaw/scripts/test-live.mjs b/integrations/openclaw/scripts/test-live.mjs index 8d6af0c87..4407cce60 100644 --- a/integrations/openclaw/scripts/test-live.mjs +++ b/integrations/openclaw/scripts/test-live.mjs @@ -9,7 +9,7 @@ import { fileURLToPath } from "node:url"; const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); -const result = spawnSync(process.execPath, ["--test", ".test-dist/src/__tests__/live-smoke.test.js"], { +const result = spawnSync(process.execPath, ["--test", ".test-dist/test/live-smoke.test.js"], { cwd: packageRoot, env: { ...process.env, diff --git a/integrations/openclaw/src/__tests__/config.test.ts b/integrations/openclaw/test/config.test.ts similarity index 99% rename from integrations/openclaw/src/__tests__/config.test.ts rename to integrations/openclaw/test/config.test.ts index 29ec63156..1817f1083 100644 --- a/integrations/openclaw/src/__tests__/config.test.ts +++ b/integrations/openclaw/test/config.test.ts @@ -15,14 +15,14 @@ import { NEMO_FLOW_OPENCLAW_JSON_SCHEMA, nemoFlowConfigSchema, parseConfig, -} from "../config.js"; +} from "../src/config.js"; import { defaultNemoFlowModuleLoader, type NemoFlowModuleLoader, type NemoFlowModules, type NemoFlowRuntimeModule, -} from "../modules.js"; -import { registerNemoFlowPlugin } from "../runtime-state.js"; +} from "../src/modules.js"; +import { registerNemoFlowPlugin } from "../src/runtime-state.js"; import type { OpenClawPluginApi, PluginLogger } from "openclaw/plugin-sdk/plugin-entry"; import { callGatewayStatus, type TestGatewayMethodHandler } from "./gateway-status.js"; diff --git a/integrations/openclaw/src/__tests__/failure-model.test.ts b/integrations/openclaw/test/failure-model.test.ts similarity index 94% rename from integrations/openclaw/src/__tests__/failure-model.test.ts rename to integrations/openclaw/test/failure-model.test.ts index 424b7f464..6333da790 100644 --- a/integrations/openclaw/src/__tests__/failure-model.test.ts +++ b/integrations/openclaw/test/failure-model.test.ts @@ -7,9 +7,9 @@ import assert from "node:assert/strict"; import { describe, it } from "node:test"; -import { parseConfig } from "../config.js"; -import { HookReplayBackend } from "../hooks-backend.js"; -import type { NemoFlowRuntimeModule } from "../modules.js"; +import { parseConfig } from "../src/config.js"; +import { HookReplayBackend } from "../src/hooks-backend.js"; +import type { NemoFlowRuntimeModule } from "../src/modules.js"; import type { PluginLogger } from "openclaw/plugin-sdk/plugin-entry"; describe("Replay failure model", () => { diff --git a/integrations/openclaw/src/__tests__/gateway-status.ts b/integrations/openclaw/test/gateway-status.ts similarity index 94% rename from integrations/openclaw/src/__tests__/gateway-status.ts rename to integrations/openclaw/test/gateway-status.ts index 2cc71b4e1..85e6a6281 100644 --- a/integrations/openclaw/src/__tests__/gateway-status.ts +++ b/integrations/openclaw/test/gateway-status.ts @@ -10,7 +10,7 @@ import assert from "node:assert/strict"; import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"; -import type { NemoFlowHealthSnapshot } from "../health.js"; +import type { NemoFlowHealthSnapshot } from "../src/health.js"; export type TestGatewayMethodHandler = Parameters[1]; diff --git a/integrations/openclaw/src/__tests__/hooks-backend.test.ts b/integrations/openclaw/test/hooks-backend.test.ts similarity index 98% rename from integrations/openclaw/src/__tests__/hooks-backend.test.ts rename to integrations/openclaw/test/hooks-backend.test.ts index 49f16ae08..c5ea603d9 100644 --- a/integrations/openclaw/src/__tests__/hooks-backend.test.ts +++ b/integrations/openclaw/test/hooks-backend.test.ts @@ -7,10 +7,10 @@ import assert from "node:assert/strict"; import { describe, it } from "node:test"; -import { parseConfig } from "../config.js"; -import { errorToJson, toJsonRecord } from "../hook-replay/marks.js"; -import { HookReplayBackend } from "../hooks-backend.js"; -import type { NemoFlowRuntimeModule } from "../modules.js"; +import { parseConfig } from "../src/config.js"; +import { errorToJson, toJsonRecord } from "../src/hook-replay/marks.js"; +import { HookReplayBackend } from "../src/hooks-backend.js"; +import type { NemoFlowRuntimeModule } from "../src/modules.js"; import type { PluginLogger } from "openclaw/plugin-sdk/plugin-entry"; describe("HookReplayBackend", () => { diff --git a/integrations/openclaw/src/__tests__/live-smoke.test.ts b/integrations/openclaw/test/live-smoke.test.ts similarity index 98% rename from integrations/openclaw/src/__tests__/live-smoke.test.ts rename to integrations/openclaw/test/live-smoke.test.ts index 3583def33..0cfbc0e86 100644 --- a/integrations/openclaw/src/__tests__/live-smoke.test.ts +++ b/integrations/openclaw/test/live-smoke.test.ts @@ -10,12 +10,12 @@ import * as os from "node:os"; import * as path from "node:path"; import { it } from "node:test"; -import { registerNemoFlowPlugin } from "../runtime-state.js"; +import { registerNemoFlowPlugin } from "../src/runtime-state.js"; import { defaultNemoFlowModuleLoader, type NemoFlowModuleLoader, type NemoFlowModules, -} from "../modules.js"; +} from "../src/modules.js"; import type { OpenClawPluginApi, PluginLogger } from "openclaw/plugin-sdk/plugin-entry"; import { callGatewayStatus, type TestGatewayMethodHandler } from "./gateway-status.js"; diff --git a/integrations/openclaw/src/__tests__/llm-replay.test.ts b/integrations/openclaw/test/llm-replay.test.ts similarity index 99% rename from integrations/openclaw/src/__tests__/llm-replay.test.ts rename to integrations/openclaw/test/llm-replay.test.ts index 24353d9c7..601419b86 100644 --- a/integrations/openclaw/src/__tests__/llm-replay.test.ts +++ b/integrations/openclaw/test/llm-replay.test.ts @@ -7,9 +7,9 @@ import assert from "node:assert/strict"; import { describe, it } from "node:test"; -import { parseConfig } from "../config.js"; -import { HookReplayBackend } from "../hooks-backend.js"; -import type { NemoFlowRuntimeModule } from "../modules.js"; +import { parseConfig } from "../src/config.js"; +import { HookReplayBackend } from "../src/hooks-backend.js"; +import type { NemoFlowRuntimeModule } from "../src/modules.js"; import type { PluginLogger } from "openclaw/plugin-sdk/plugin-entry"; describe("LLM replay", () => { diff --git a/integrations/openclaw/src/__tests__/tool-replay.test.ts b/integrations/openclaw/test/tool-replay.test.ts similarity index 97% rename from integrations/openclaw/src/__tests__/tool-replay.test.ts rename to integrations/openclaw/test/tool-replay.test.ts index 08c3a5aa5..23b83fcec 100644 --- a/integrations/openclaw/src/__tests__/tool-replay.test.ts +++ b/integrations/openclaw/test/tool-replay.test.ts @@ -7,9 +7,9 @@ import assert from "node:assert/strict"; import { describe, it } from "node:test"; -import { parseConfig } from "../config.js"; -import { HookReplayBackend } from "../hooks-backend.js"; -import type { NemoFlowRuntimeModule } from "../modules.js"; +import { parseConfig } from "../src/config.js"; +import { HookReplayBackend } from "../src/hooks-backend.js"; +import type { NemoFlowRuntimeModule } from "../src/modules.js"; import type { PluginLogger } from "openclaw/plugin-sdk/plugin-entry"; describe("Tool replay", () => { diff --git a/integrations/openclaw/tsconfig.build.json b/integrations/openclaw/tsconfig.build.json index bb9825546..5b2d5d342 100644 --- a/integrations/openclaw/tsconfig.build.json +++ b/integrations/openclaw/tsconfig.build.json @@ -8,7 +8,7 @@ "outDir": "dist" }, "exclude": [ - "src/__tests__/**", + "test/**", "src/**/*.test.ts", ".test-dist/**", "dist/**" diff --git a/integrations/openclaw/tsconfig.test.json b/integrations/openclaw/tsconfig.test.json index 2338c352e..5c7a7c9e9 100644 --- a/integrations/openclaw/tsconfig.test.json +++ b/integrations/openclaw/tsconfig.test.json @@ -10,5 +10,11 @@ "exclude": [ ".test-dist/**", "dist/**" + ], + "include": [ + "index.ts", + "src/**/*.ts", + "src/**/*.d.ts", + "test/**/*.ts" ] } From cfd3893c8aa2f523cd0fd5f0cb8a731b90734c4b Mon Sep 17 00:00:00 2001 From: Will Killian <2007799+willkill07@users.noreply.github.com> Date: Thu, 14 May 2026 14:50:28 -0400 Subject: [PATCH 4/7] Apply suggestion from @willkill07 Signed-off-by: Will Killian <2007799+willkill07@users.noreply.github.com> --- .github/workflows/ci_node.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_node.yml b/.github/workflows/ci_node.yml index 65a99e6e8..543cd37e4 100644 --- a/.github/workflows/ci_node.yml +++ b/.github/workflows/ci_node.yml @@ -225,7 +225,7 @@ jobs: if [ "${{ inputs.ref_type }}" = "tag" ]; then version="${{ inputs.ref_name }}" else - version="${version}-${sha}" + version="${version}+${sha}" fi printf 'NEMO_FLOW_OPENCLAW_PACKAGE_VERSION=%s\n' "$version" >> "$GITHUB_ENV" From a5d8b2efe70d999bf4279b946dd4198b22b47527 Mon Sep 17 00:00:00 2001 From: Will Killian <2007799+willkill07@users.noreply.github.com> Date: Thu, 14 May 2026 14:58:25 -0400 Subject: [PATCH 5/7] Update .github/workflows/ci_node.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Will Killian <2007799+willkill07@users.noreply.github.com> --- .github/workflows/ci_node.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_node.yml b/.github/workflows/ci_node.yml index 543cd37e4..f773b2496 100644 --- a/.github/workflows/ci_node.yml +++ b/.github/workflows/ci_node.yml @@ -114,7 +114,7 @@ jobs: Package: name: Package (${{ matrix.platform }}) needs: [Test] - if: ${{ (inputs.run_package || inputs.run_openclaw) && !cancelled() && needs.Test.result == 'success' }} + if: ${{ !cancelled() && needs.Test.result == 'success' && (inputs.run_package || (inputs.run_openclaw && matrix.platform == 'linux-amd64')) }} runs-on: ${{ matrix.runner }} timeout-minutes: 30 permissions: From 34981bac7a3a4fdec4690fc563ae9b99c173068b Mon Sep 17 00:00:00 2001 From: Will Killian Date: Thu, 14 May 2026 15:22:00 -0400 Subject: [PATCH 6/7] ci: split OpenClaw package job Signed-off-by: Will Killian --- .github/workflows/ci_node.yml | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci_node.yml b/.github/workflows/ci_node.yml index f773b2496..05c9d8acc 100644 --- a/.github/workflows/ci_node.yml +++ b/.github/workflows/ci_node.yml @@ -114,7 +114,7 @@ jobs: Package: name: Package (${{ matrix.platform }}) needs: [Test] - if: ${{ !cancelled() && needs.Test.result == 'success' && (inputs.run_package || (inputs.run_openclaw && matrix.platform == 'linux-amd64')) }} + if: ${{ inputs.run_package && !cancelled() && needs.Test.result == 'success' }} runs-on: ${{ matrix.runner }} timeout-minutes: 30 permissions: @@ -215,8 +215,31 @@ jobs: path: ${{ env.NEMO_FLOW_CI_WORKSPACE_TMP }}/npm/*.tgz if-no-files-found: error + PackageOpenClaw: + name: Package OpenClaw plugin + needs: [Test] + if: ${{ inputs.run_openclaw && !cancelled() && needs.Test.result == 'success' }} + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Load CI tool versions + id: ci-config + uses: ./.github/actions/load-ci-tool-versions + + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 + with: + node-version: ${{ steps.ci-config.outputs.node_version }} + + - uses: taiki-e/install-action@5939f3337e40968c39aa70f5ecb1417a92fb25a0 # v2.75.15 + with: + tool: just@${{ steps.ci-config.outputs.just_version }} + - name: Derive OpenClaw package version - if: ${{ inputs.run_openclaw && matrix.platform == 'linux-amd64' }} working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }} run: | set -e @@ -230,7 +253,6 @@ jobs: printf 'NEMO_FLOW_OPENCLAW_PACKAGE_VERSION=%s\n' "$version" >> "$GITHUB_ENV" - name: Package OpenClaw plugin - if: ${{ inputs.run_openclaw && matrix.platform == 'linux-amd64' }} working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }} run: | set -euo pipefail @@ -241,7 +263,6 @@ jobs: - name: Upload OpenClaw plugin artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - if: ${{ inputs.run_openclaw && matrix.platform == 'linux-amd64' }} with: name: openclaw-npm path: ${{ env.NEMO_FLOW_CI_WORKSPACE_TMP }}/openclaw/*.tgz From f529f147618b948f38e115f2d87ad32fc05deaf6 Mon Sep 17 00:00:00 2001 From: Will Killian Date: Thu, 14 May 2026 15:38:08 -0400 Subject: [PATCH 7/7] ci: build node before OpenClaw package Signed-off-by: Will Killian --- .github/workflows/ci_node.yml | 14 ++++++++++++++ justfile | 11 +++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_node.yml b/.github/workflows/ci_node.yml index 05c9d8acc..5ea89d1ae 100644 --- a/.github/workflows/ci_node.yml +++ b/.github/workflows/ci_node.yml @@ -231,6 +231,19 @@ jobs: id: ci-config uses: ./.github/actions/load-ci-tool-versions + - uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4 + with: + cache: false + toolchain: ${{ steps.ci-config.outputs.rust_version }} + + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 + with: + shared-key: nemo-flow-rust-openclaw-package-${{ runner.os }}-${{ runner.arch }}-${{ steps.ci-config.outputs.rust_version }} + workspaces: . -> target + cache-all-crates: true + cache-bin: false + save-if: false + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version: ${{ steps.ci-config.outputs.node_version }} @@ -257,6 +270,7 @@ jobs: run: | set -euo pipefail just \ + --set ci true \ --set output_dir "${NEMO_FLOW_CI_WORKSPACE_TMP}" \ --set ref_name "${NEMO_FLOW_OPENCLAW_PACKAGE_VERSION}" \ package-openclaw diff --git a/justfile b/justfile index f9a778f73..e16cf77c8 100644 --- a/justfile +++ b/justfile @@ -1089,7 +1089,7 @@ package-node: exit 1 fi -# --set [output_dir=] [ref_name=] +# --set [output_dir=] [ref_name=] [ci=true|false] package-openclaw: #!/usr/bin/env bash {{ bash_helpers }} @@ -1103,15 +1103,22 @@ package-openclaw: version="$(read_npm_package_version integrations/openclaw/package.json)" package_version="${version}+${sha}" echo "Non-release build: appending commit hash to version" + set_npm_package_version crates/node/package.json package-lock.json "$package_version" crates/node set_npm_package_version integrations/openclaw/package.json package-lock.json "$package_version" integrations/openclaw set_npm_package_dependency_version integrations/openclaw/package.json package-lock.json integrations/openclaw nemo-flow-node "$package_version" else package_version="{{ ref_name }}" echo "Using explicit version {{ ref_name }}" + set_npm_package_version crates/node/package.json package-lock.json "$package_version" crates/node set_npm_package_version integrations/openclaw/package.json package-lock.json "$package_version" integrations/openclaw set_npm_package_dependency_version integrations/openclaw/package.json package-lock.json integrations/openclaw nemo-flow-node "$package_version" fi - npm install --workspace=nemo-flow-openclaw --ignore-scripts + npm install --workspace=nemo-flow-node --workspace=nemo-flow-openclaw --ignore-scripts + if is_true "{{ ci }}"; then + npm run build-debug --workspace=nemo-flow-node + else + npm run build --workspace=nemo-flow-node + fi npm pack --workspace=nemo-flow-openclaw --pack-destination "$package_dir" shopt -s nullglob packages=("$package_dir"/*.tgz)