test: run CLI tests against source (#5904)

## Summary

Make ordinary CLI tests execute the TypeScript source graph instead of
potentially stale `dist/` output, while preserving a small clean-built
lane for genuine compiled-package contracts. Add executable boundaries
so the migration cannot silently regress or leave tests assigned to
overlapping Vitest projects.

## Changes

- Migrate CLI unit and integration tests from compiled internals to
`src/`, including spawned fixture programs.
- Move compiled-artifact assertions into `test/package-contract/` and
clean-build CLI/plugin artifacts before that lane runs.
- Split Vitest into explicit, disjoint source, integration, installer,
package, plugin, support, and live-E2E projects.
- Add `test:fast`, `test:integration`, `test:package`, and
`test:live-e2e` commands.
- Add repository checks that reject ordinary test imports from `dist/`
and derive overlapping project membership from Vitest itself.
- Collect CLI coverage against TypeScript source, remove the old
compiled-coverage signal, and run package contracts in the
build/typecheck gate.
- Tighten three existing test-file size exceptions after the migration
reduced those files.
- Document the test lanes and source/package boundary for contributors.

## Type of Change

- [ ] Code change (feature, bug fix, or refactor)
- [x] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates

- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [ ] Docs updated for user-facing behavior changes
- [x] Docs not applicable — justification: no user-facing runtime
behavior changed; contributor guidance was updated in `AGENTS.md` and
`CONTRIBUTING.md`.
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [x] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification: production source is
unchanged; migrated tests retain their assertions and the complete
non-live suite passes against the new source graph.
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification

- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Git hooks passed during commit and push, or `npx prek run
--from-ref main --to-ref HEAD` passes
- [x] Targeted tests pass for changed behavior
- [x] Full `npm test` passes (broad runtime changes only)
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

Verification details on head `88360d617`:

- `VITEST_MAX_WORKERS=16 npm test`: 808 files passed, 2 skipped; 9,507
tests passed, 18 skipped, after clean CLI/plugin builds.
- `VITEST_MAX_WORKERS=16 npm run test:fast`: 505 files and 5,592 tests
passed with `dist/` absent.
- The clean-built package-contract project passed 13 files and 259
compiled-package assertions.
- The source/package import boundary and disjoint Vitest-project guards
passed in `static-checks`.
- All five source-coverage shards passed; `cli-tests` merged coverage
against `src/**/*.ts` and passed the coverage ratchet; required
aggregate `checks` passed.
- Pre-push CLI typecheck and version/tag synchronization passed.
- CodeQL, both PR review advisors, all self-hosted E2Es, macOS E2E, WSL
E2E, and codebase growth guardrails passed.

---

Signed-off-by: Carlos Villela <cvillela@nvidia.com>

---------

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
This commit is contained in:
Carlos Villela 2026-06-27 13:23:32 -07:00 committed by GitHub
parent a95e851d15
commit 7d6daa2b93
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
321 changed files with 2915 additions and 2666 deletions

View file

@ -27,6 +27,10 @@ runs:
shell: bash
run: npm run build:cli
- name: Verify compiled package contracts
shell: bash
run: npx vitest run --project package-contract
- name: Typecheck CLI + tests
shell: bash
run: npm run typecheck:cli

View file

@ -78,16 +78,6 @@ runs:
- name: Merge CLI coverage
shell: bash
run: |
coverage_include="dist/lib/**/*.js"
# Keep the merge denominator aligned with the shard command during
# the PR #5904 transition. That PR removes this probe after landing.
integration_probe_output=""
if integration_probe_output=$(npx vitest list --project integration --filesOnly 2>&1); then
coverage_include="src/**/*.ts"
elif ! grep -Fqx 'Error: No projects matched the filter "integration".' <<<"$integration_probe_output"; then
printf '%s\n' "$integration_probe_output" >&2
exit 1
fi
npx vitest --mergeReports .vitest-reports \
--reporter=json \
--outputFile.json=coverage/cli/vitest-results.json \
@ -97,7 +87,7 @@ runs:
--coverage.reporter=cobertura \
--coverage.reportsDirectory=coverage/cli \
--coverage.include="bin/**/*.js" \
--coverage.include="$coverage_include" \
--coverage.include="src/**/*.ts" \
--coverage.exclude="test/**/*.js" \
--coverage.exclude="test/**/*.ts"
npx tsx scripts/check-coverage-ratchet.ts coverage/cli/coverage-summary.json ci/coverage-threshold-cli.json "CLI coverage"

View file

@ -66,21 +66,7 @@ runs:
node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })"
npm run build:cli
npx tsx scripts/check-dist-sourcemaps.ts dist
coverage_include="dist/lib/**/*.js"
additional_projects=()
# Bootstrap the source-test migration without executing action code
# from the PR branch. PR #5904 removes this probe once `integration`
# is part of the trusted base configuration.
integration_probe_output=""
if integration_probe_output=$(npx vitest list --project integration --filesOnly 2>&1); then
additional_projects+=(--project integration)
coverage_include="src/**/*.ts"
elif ! grep -Fqx 'Error: No projects matched the filter "integration".' <<<"$integration_probe_output"; then
printf '%s\n' "$integration_probe_output" >&2
exit 1
fi
npx vitest run --project cli \
"${additional_projects[@]}" \
npx vitest run --project cli --project integration \
--shard="${CLI_SHARD}/${CLI_SHARD_COUNT}" \
--reporter=github-actions \
--reporter=blob \
@ -89,7 +75,7 @@ runs:
--coverage.reporter=json-summary \
--coverage.reportsDirectory="coverage/cli/shard-${CLI_SHARD}" \
--coverage.include="bin/**/*.js" \
--coverage.include="$coverage_include" \
--coverage.include="src/**/*.ts" \
--coverage.exclude="test/**/*.js" \
--coverage.exclude="test/**/*.ts"

View file

@ -285,7 +285,7 @@ repos:
- id: test-cli
name: Test (CLI)
entry: >-
bash -c 'node -e "require(\"node:fs\").rmSync(\"dist\", { recursive: true, force: true })" && npm run build:cli && npx tsx scripts/check-dist-sourcemaps.ts dist && npx vitest run --project cli --coverage --coverage.reporter=text-summary --coverage.reporter=json-summary --coverage.reportsDirectory=coverage/cli --coverage.include="bin/**/*.js" --coverage.include="dist/lib/**/*.js" --coverage.exclude="test/**/*.js" --coverage.exclude="test/**/*.ts" && npx tsx scripts/check-coverage-ratchet.ts coverage/cli/coverage-summary.json ci/coverage-threshold-cli.json "CLI coverage"'
bash -c 'node -e "require(\"node:fs\").rmSync(\"dist\", { recursive: true, force: true })" && npm run build:cli && npx tsx scripts/check-dist-sourcemaps.ts dist && npx vitest run --project cli --project integration --coverage --coverage.reporter=text-summary --coverage.reporter=json-summary --coverage.reportsDirectory=coverage/cli --coverage.include="bin/**/*.js" --coverage.include="src/**/*.ts" --coverage.exclude="test/**/*.js" --coverage.exclude="test/**/*.ts" && npx tsx scripts/check-coverage-ratchet.ts coverage/cli/coverage-summary.json ci/coverage-threshold-cli.json "CLI coverage"'
language: system
pass_filenames: false
files: ^(bin/|src/.*\.(ts|tsx|js|mjs|cjs)$|test/.*\.(ts|tsx|js|mjs|cjs)$)

View file

@ -45,6 +45,10 @@ Package-specific guides:
| Build plugin | `cd nemoclaw && npm run build` |
| Watch mode | `cd nemoclaw && npm run dev` |
| Run all tests | `npm test` |
| Run fast source tests | `npm run test:fast` |
| Run integration tests | `npm run test:integration` |
| Run package contracts | `npm run test:package` |
| Run live E2E scenarios | `npm run test:live-e2e` |
| Run plugin tests | `cd nemoclaw && npm test` |
| Run all linters | `make check` |
| Run all hooks manually | `npx prek run --all-files` |
@ -66,16 +70,23 @@ The `bin/` directory uses CommonJS intentionally for the launcher and a few comp
### Testing Strategy
Tests are organized into three Vitest projects defined in `vitest.config.ts`:
Tests are organized into disjoint Vitest projects defined in `vitest.config.ts`:
1. **`cli`** — `test/**/*.test.{js,ts}` — integration tests for CLI behavior
2. **`plugin`** — `nemoclaw/src/**/*.test.ts` — unit tests co-located with source
3. **`e2e-branch-validation`** — `test/e2e/brev-e2e.test.ts` — validates a branch from source on ephemeral Brev instance (requires `BREV_API_TOKEN`)
1. **`cli`** — `src/**/*.test.ts` — CLI unit tests importing source
2. **`integration`** — `test/**/*.test.{js,ts}` — root integration tests importing source; excludes the explicit lanes below
3. **`installer-integration`** — installer tests that spawn real `install.sh` processes
4. **`package-contract`** — `test/package-contract/**/*.test.ts` — the only non-live lane that imports compiled CLI/plugin artifacts
5. **`plugin`** — `nemoclaw/src/**/*.test.ts` — plugin unit tests co-located with source
6. **`e2e-vitest-support`** — fast tests for the E2E fixture/support layer
7. **`e2e-scenarios-live`** — opt-in live scenarios that mutate real external state
8. **`e2e-branch-validation`** — opt-in validation on an ephemeral Brev instance
When writing tests:
- Root-level tests (`test/`) use ESM imports
- Plugin tests use TypeScript and are co-located with their source files
- Import CLI source from ordinary tests. Put genuine compiled-artifact assertions under `test/package-contract/`.
- Keep project globs disjoint; `npm run test:projects:check` derives membership from Vitest and rejects overlap.
- Mock external dependencies; don't call real NVIDIA APIs in unit tests
- E2E tests run on ephemeral Brev cloud instances

View file

@ -120,7 +120,11 @@ These are the primary `make` and `npm` targets for day-to-day development:
| `make lint` | Same as `make check` |
| `make format` | Auto-format TypeScript and Python source |
| `npm run typecheck:cli` | Type-check CLI TypeScript using `tsconfig.cli.json` (`bin/`, `scripts/`, `src/`, `test/`, `nemoclaw-blueprint/scripts/`) |
| `npm test` | Run root-level tests (`test/*.test.js`) |
| `npm test` | Build package artifacts and run every non-live Vitest project |
| `npm run test:fast` | Clean `dist/` and run source CLI, plugin, and E2E-support tests |
| `npm run test:integration` | Clean-build the CLI and run root integration and installer tests |
| `npm run test:package` | Clean-build CLI/plugin artifacts and run compiled-package contracts |
| `npm run test:live-e2e` | Opt into live E2E scenarios (mutates real external state) |
| `cd nemoclaw && npm test` | Run plugin unit tests (Vitest) |
| `npm run docs` | Validate Fern documentation with the pinned Fern CLI version |
| `npm run docs:live` | Serve Fern docs locally with auto-rebuild |

View file

@ -4,14 +4,14 @@
"legacyMaxLines": {
"nemoclaw/src/commands/migration-state.test.ts": 1566,
"src/lib/inference/nim.test.ts": 2068,
"src/lib/onboard/preflight.test.ts": 1905,
"src/lib/onboard/preflight.test.ts": 1904,
"test/channels-add-preset.test.ts": 1871,
"test/generate-openclaw-config.test.ts": 1984,
"test/install-preflight.test.ts": 4006,
"test/nemoclaw-start.test.ts": 5043,
"test/onboard-messaging.test.ts": 2062,
"test/onboard-selection.test.ts": 6888,
"test/onboard-selection.test.ts": 6867,
"test/onboard.test.ts": 4774,
"test/policies.test.ts": 2753
"test/policies.test.ts": 2489
}
}

View file

@ -20,8 +20,13 @@
},
"scripts": {
"preinstall": "node scripts/check-node-version.js",
"test": "vitest run",
"coverage:cli:dist-signal": "tsx scripts/coverage-cli-dist-signal.ts",
"test": "npm run clean:cli && npm --prefix nemoclaw run clean && npm run build:cli && npm --prefix nemoclaw run build && vitest run --project cli --project integration --project installer-integration --project package-contract --project plugin --project e2e-vitest-support",
"test:fast": "npm run clean:cli && vitest run --project cli --project plugin --project e2e-vitest-support",
"test:integration": "npm run clean:cli && npm run build:cli && vitest run --project integration --project installer-integration",
"test:package": "npm run clean:cli && npm --prefix nemoclaw run clean && npm run build:cli && npm --prefix nemoclaw run build && vitest run --project package-contract",
"test:live-e2e": "NEMOCLAW_RUN_E2E_SCENARIOS=1 vitest run --project e2e-scenarios-live",
"test:imports:check": "tsx scripts/checks/no-test-dist-imports.ts",
"test:projects:check": "tsx scripts/checks/vitest-project-overlap.ts",
"check": "npx prek run --all-files",
"checks": "tsx scripts/checks/run.ts",
"lint": "npx @biomejs/biome lint . && npm run checks",
@ -33,6 +38,7 @@
"check:installer-hash": "bash scripts/check-installer-hash.sh",
"typecheck": "tsc -p jsconfig.json",
"build:cli": "tsc -p tsconfig.src.json && node dist/lib/cli/generate-oclif-metadata-manifest.js && if find nemoclaw-blueprint/scripts -name '*.ts' -print -quit | grep -q .; then tsc -p nemoclaw-blueprint/tsconfig.json; fi",
"clean:cli": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
"typecheck:cli": "tsc -p tsconfig.cli.json",
"validate:configs": "tsx scripts/validate-configs.ts",
"type-safety:hotspots": "tsx scripts/type-safety-hotspots.ts",

View file

@ -0,0 +1,187 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import ts from "typescript";
export type Violation = { file: string; line: number; detail: string };
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const SKIP_DIRS = new Set([".git", "coverage", "dist", "node_modules"]);
// These tests intentionally construct fake dist/lib trees; they do not load
// repository build output. The self-audit below prevents this list growing or
// retaining an exemption after the fixture no longer needs one.
const FIXTURE_EXCLUSIONS = new Set([
"test/dist-sourcemaps.test.ts",
"test/install-preflight.test.ts",
"test/stale-dist-check.test.ts",
]);
const EXCLUDED_PREFIXES = [
// Live/branch E2E validates installed artifacts rather than unit-test imports.
"test/e2e/",
"test/e2e-scenario/live/",
// This is the sole non-live lane allowed to import compiled package artifacts.
"test/package-contract/",
];
function repoPath(absolutePath: string): string {
return path.relative(REPO_ROOT, absolutePath).split(path.sep).join("/");
}
export function isScannedTestPath(relativePath: string): boolean {
if (FIXTURE_EXCLUSIONS.has(relativePath)) return false;
if (EXCLUDED_PREFIXES.some((prefix) => relativePath.startsWith(prefix))) return false;
if (relativePath.startsWith("src/")) return /\.(?:test|spec)\.[cm]?[jt]sx?$/.test(relativePath);
return relativePath.startsWith("test/") && /\.[cm]?[jt]sx?$/.test(relativePath);
}
function isScannedTestFile(absolutePath: string): boolean {
return isScannedTestPath(repoPath(absolutePath));
}
function* walk(directory: string): Generator<string> {
if (!existsSync(directory)) return;
for (const entry of readdirSync(directory)) {
if (SKIP_DIRS.has(entry)) continue;
const absolutePath = path.join(directory, entry);
const stats = statSync(absolutePath);
if (stats.isDirectory()) yield* walk(absolutePath);
else if (stats.isFile() && isScannedTestFile(absolutePath)) yield absolutePath;
}
}
function isCompiledInternalSpecifier(specifier: string): boolean {
const normalized = specifier.replaceAll("\\", "/");
return (
/(^|\/)dist\/(?:lib|commands)(?:\/|$)/.test(normalized) ||
/(^|\/)dist\/nemoclaw(?:\.js)?$/.test(normalized)
);
}
export function findCompiledInternalViolations(file: string, source: string): Violation[] {
const sourceFile = ts.createSourceFile(
file,
source,
ts.ScriptTarget.Latest,
true,
file.endsWith("x") ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
);
const violations: Violation[] = [];
function add(node: ts.Node, detail: string): void {
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
violations.push({ file, line: position.line + 1, detail });
}
function checkSpecifier(node: ts.Node, specifier: string): void {
if (isCompiledInternalSpecifier(specifier)) {
add(node, `imports compiled CLI internals from ${JSON.stringify(specifier)}`);
}
}
function visit(node: ts.Node): void {
if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier)) {
checkSpecifier(node.moduleSpecifier, node.moduleSpecifier.text);
} else if (
ts.isExportDeclaration(node) &&
node.moduleSpecifier &&
ts.isStringLiteralLike(node.moduleSpecifier)
) {
checkSpecifier(node.moduleSpecifier, node.moduleSpecifier.text);
} else if (ts.isCallExpression(node)) {
const isRequire = ts.isIdentifier(node.expression) && node.expression.text === "require";
const isDynamicImport = node.expression.kind === ts.SyntaxKind.ImportKeyword;
const isRequireResolve =
ts.isPropertyAccessExpression(node.expression) &&
ts.isIdentifier(node.expression.expression) &&
node.expression.expression.text === "require" &&
node.expression.name.text === "resolve";
const firstArgument = node.arguments[0];
if (
(isRequire || isDynamicImport || isRequireResolve) &&
firstArgument &&
ts.isStringLiteralLike(firstArgument)
) {
checkSpecifier(firstArgument, firstArgument.text);
}
const isPathBuilder =
ts.isPropertyAccessExpression(node.expression) &&
ts.isIdentifier(node.expression.expression) &&
node.expression.expression.text === "path" &&
["join", "resolve"].includes(node.expression.name.text);
if (isPathBuilder) {
const parts = node.arguments.map((argument) =>
ts.isStringLiteralLike(argument) ? argument.text : null,
);
const distIndex = parts.indexOf("dist");
const compiledTarget = distIndex >= 0 ? parts[distIndex + 1] : null;
if (compiledTarget === "lib" || compiledTarget === "commands") {
add(node, `constructs a path into dist/${compiledTarget}`);
} else if (distIndex >= 0 && compiledTarget === "nemoclaw.js") {
add(node, "constructs a path to dist/nemoclaw.js");
}
}
}
ts.forEachChild(node, visit);
}
visit(sourceFile);
for (const match of source.matchAll(/require\([^\n)]*(?:\.\/|\.\.\/)dist\/(?:lib|commands)\//g)) {
const position = sourceFile.getLineAndCharacterOfPosition(match.index);
violations.push({
file,
line: position.line + 1,
detail: "embeds a compiled-internal require in generated test code",
});
}
return violations.filter(
(violation, index, all) =>
all.findIndex(
(candidate) => candidate.file === violation.file && candidate.line === violation.line,
) === index,
);
}
function findViolations(absolutePath: string): Violation[] {
return findCompiledInternalViolations(repoPath(absolutePath), readFileSync(absolutePath, "utf8"));
}
function main(): void {
const staleFixtureExclusions = [...FIXTURE_EXCLUSIONS].filter((relativePath) => {
const absolutePath = path.join(REPO_ROOT, relativePath);
return !existsSync(absolutePath) || findViolations(absolutePath).length === 0;
});
if (staleFixtureExclusions.length > 0) {
console.error("Fixture exclusions must exist and still construct a compiled-internal path:");
for (const relativePath of staleFixtureExclusions) console.error(` ${relativePath}`);
process.exit(1);
}
const violations = [
...walk(path.join(REPO_ROOT, "src")),
...walk(path.join(REPO_ROOT, "test")),
].flatMap(findViolations);
if (violations.length > 0) {
console.error(
"Compiled CLI internals may only be imported by the package-contract test project:",
);
for (const violation of violations) {
console.error(` ${violation.file}:${violation.line} ${violation.detail}`);
}
console.error(
"Import src/ instead, or move a genuine compiled-package contract under test/package-contract/.",
);
process.exit(1);
}
console.log("Test imports respect the source/package boundary.");
}
if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
main();
}

View file

@ -36,6 +36,16 @@ const CHECKS: readonly CheckCommand[] = [
command: TSX,
args: ["scripts/checks/layer-import-boundaries.ts"],
},
{
name: "no-test-dist-imports",
command: TSX,
args: ["scripts/checks/no-test-dist-imports.ts"],
},
{
name: "vitest-project-overlap",
command: TSX,
args: ["scripts/checks/vitest-project-overlap.ts"],
},
];
function main(): void {

View file

@ -0,0 +1,80 @@
// 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, pathToFileURL } from "node:url";
export type ProjectListing = {
projects: Set<string>;
projectsByFile: Map<string, Set<string>>;
};
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const VITEST = path.join(
REPO_ROOT,
"node_modules",
".bin",
process.platform === "win32" ? "vitest.cmd" : "vitest",
);
export function parseProjectListing(output: string): ProjectListing {
const projectsByFile = new Map<string, Set<string>>();
const projects = new Set<string>();
for (const line of output.split(/\r?\n/)) {
if (!line.trim()) continue;
const match = line.match(/^\[([^\]]+)\]\s+(.+)$/);
if (!match) throw new Error(`Could not parse Vitest project listing line: ${line}`);
const [, project, file] = match;
projects.add(project);
const memberships = projectsByFile.get(file) ?? new Set<string>();
memberships.add(project);
projectsByFile.set(file, memberships);
}
return { projects, projectsByFile };
}
export function findProjectOverlaps(
projectsByFile: ReadonlyMap<string, ReadonlySet<string>>,
): Array<[string, ReadonlySet<string>]> {
return [...projectsByFile]
.filter(([, memberships]) => memberships.size > 1)
.sort(([left], [right]) => left.localeCompare(right));
}
function main(): void {
const result = spawnSync(VITEST, ["list", "--filesOnly"], {
cwd: REPO_ROOT,
encoding: "utf8",
env: {
...process.env,
NEMOCLAW_RUN_BRANCH_VALIDATION_E2E: "1",
NEMOCLAW_RUN_E2E_SCENARIOS: "1",
},
});
if (result.error) throw result.error;
if (result.status !== 0) {
process.stderr.write(result.stderr);
process.exit(result.status ?? 1);
}
const { projects, projectsByFile } = parseProjectListing(result.stdout);
const overlaps = findProjectOverlaps(projectsByFile);
if (overlaps.length > 0) {
console.error("Vitest files must belong to exactly one project:");
for (const [file, memberships] of overlaps) {
console.error(` ${file}: ${[...memberships].sort().join(", ")}`);
}
process.exit(1);
}
console.log(
`Vitest project membership is disjoint (${projectsByFile.size} files across ${projects.size} projects).`,
);
}
if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
main();
}

View file

@ -1,93 +0,0 @@
#!/usr/bin/env -S npx tsx
// 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 DEFAULT_REPORT_DIR = "coverage/cli-dist-signal";
const REPO_ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), "..");
type SpawnResult = {
status: number | null;
signal?: NodeJS.Signals | null;
error?: Error;
};
type SpawnSyncLike = (
command: string,
args: string[],
options: { stdio: "inherit"; env: NodeJS.ProcessEnv },
) => SpawnResult;
type RunCliDistCoverageDeps = {
spawn?: SpawnSyncLike;
kill?: typeof process.kill;
env?: NodeJS.ProcessEnv;
repoRoot?: string;
};
export function buildCliDistCoverageArgs(extraArgs: string[] = []): string[] {
return [
"run",
"--project",
"cli",
"--coverage",
"--coverage.provider=v8",
"--coverage.reporter=text-summary",
"--coverage.reporter=json-summary",
"--coverage.reporter=json",
`--coverage.reportsDirectory=${DEFAULT_REPORT_DIR}`,
"--coverage.reportOnFailure",
"--coverage.include=src/**/*.ts",
"--coverage.include=dist/**/*.js",
"--coverage.include=bin/**/*.js",
"--coverage.exclude=**/*.test.ts",
"--coverage.exclude=**/*.test.js",
"--coverage.exclude=node_modules/**",
"--coverage.exclude=nemoclaw/**",
...extraArgs,
];
}
export function resolveLocalVitestBin(repoRoot = REPO_ROOT): string {
const suffix = process.platform === "win32" ? "vitest.cmd" : "vitest";
return path.join(repoRoot, "node_modules", ".bin", suffix);
}
export function runCliDistCoverage(
extraArgs: string[] = [],
deps: RunCliDistCoverageDeps = {},
): number | null {
const args = buildCliDistCoverageArgs(extraArgs);
const result = (deps.spawn ?? spawnSync)(resolveLocalVitestBin(deps.repoRoot), args, {
stdio: "inherit",
env: deps.env ?? process.env,
});
if (result.error) {
throw result.error;
}
if (result.signal) {
(deps.kill ?? process.kill)(process.pid, result.signal);
return null;
}
return result.status ?? 1;
}
export function isDirectExecution(metaUrl: string, argv1: string | undefined): boolean {
if (!argv1) return false;
return path.resolve(fileURLToPath(metaUrl)) === path.resolve(argv1);
}
function main(): void {
const exitCode = runCliDistCoverage(process.argv.slice(2));
if (exitCode !== null) {
process.exitCode = exitCode;
}
}
if (isDirectExecution(import.meta.url, process.argv[1])) {
main();
}

View file

@ -3,7 +3,7 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import ResourcesCommand from "../../dist/commands/resources.js";
import ResourcesCommand from "./resources.js";
const rootDir = process.cwd();

View file

@ -2,9 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
import { describe, expect, it, vi } from "vitest";
import { runFixCoreDns, runSetupDnsProxy } from "../../../../dist/lib/actions/dns/index.js";
import type { CommandResult } from "./index";
import { runFixCoreDns, runSetupDnsProxy } from "./index.js";
function ok(stdout = ""): CommandResult {
return { status: 0, stdout, stderr: "" };

View file

@ -7,9 +7,8 @@ import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } fr
import type { OpenShellStateRpcIssue } from "../adapters/openshell/gateway-drift";
type BackupAll = typeof import("../../../dist/lib/actions/maintenance")["backupAll"];
type UpgradeSandboxes =
typeof import("../../../dist/lib/actions/upgrade-sandboxes")["upgradeSandboxes"];
type BackupAll = typeof import("./maintenance")["backupAll"];
type UpgradeSandboxes = typeof import("./upgrade-sandboxes")["upgradeSandboxes"];
const requireDist = createRequire(import.meta.url);
@ -57,14 +56,14 @@ describe("gateway drift preflight for maintenance actions", () => {
exitSpy = mockExit();
errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
const gatewayDrift = requireDist("../../../dist/lib/adapters/openshell/gateway-drift.js");
const openshellRuntime = requireDist("../../../dist/lib/adapters/openshell/runtime.js");
const registry = requireDist("../../../dist/lib/state/registry.js");
const sandboxState = requireDist("../../../dist/lib/state/sandbox.js");
const sandboxVersion = requireDist("../../../dist/lib/sandbox/version.js");
const upgradeDomain = requireDist("../../../dist/lib/domain/maintenance/upgrade.js");
const rebuild = requireDist("../../../dist/lib/actions/sandbox/rebuild.js");
const gatewayRuntime = requireDist("../../../dist/lib/gateway-runtime-action.js");
const gatewayDrift = requireDist("../adapters/openshell/gateway-drift.js");
const openshellRuntime = requireDist("../adapters/openshell/runtime.js");
const registry = requireDist("../state/registry.js");
const sandboxState = requireDist("../state/sandbox.js");
const sandboxVersion = requireDist("../sandbox/version.js");
const upgradeDomain = requireDist("../domain/maintenance/upgrade.js");
const rebuild = requireDist("./sandbox/rebuild.js");
const gatewayRuntime = requireDist("../gateway-runtime-action.js");
detectPreflightIssueSpy = vi
.spyOn(gatewayDrift, "detectOpenShellStateRpcPreflightIssue")
@ -113,8 +112,8 @@ describe("gateway drift preflight for maintenance actions", () => {
vi.spyOn(rebuild, "rebuildSandbox").mockResolvedValue(undefined),
);
({ backupAll } = requireDist("../../../dist/lib/actions/maintenance.js"));
({ upgradeSandboxes } = requireDist("../../../dist/lib/actions/upgrade-sandboxes.js"));
({ backupAll } = requireDist("./maintenance.js"));
({ upgradeSandboxes } = requireDist("./upgrade-sandboxes.js"));
});
afterEach(() => {
@ -197,7 +196,7 @@ describe("gateway drift preflight for maintenance actions", () => {
});
it("backup-all skips sandboxes that are not in Ready phase", async () => {
const registry = requireDist("../../../dist/lib/state/registry.js");
const registry = requireDist("../state/registry.js");
(registry.listSandboxes as ReturnType<typeof vi.fn>).mockReturnValue({
sandboxes: [
{ name: "alpha", provider: "nvidia-prod", model: "nemotron" },

View file

@ -6,11 +6,15 @@ import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
type ConnectSandbox =
typeof import("../../../../dist/lib/actions/sandbox/connect")["connectSandbox"];
type ConnectSandbox = typeof import("./connect")["connectSandbox"];
const requireDist = createRequire(import.meta.url);
const connectModulePath = "../../../../dist/lib/actions/sandbox/connect.js";
const connectModulePath = "./connect.js";
// Warm the CommonJS source graph outside the first test's timeout. Each harness
// still reloads the entry module after installing its dependency spies.
requireDist(connectModulePath);
delete require.cache[requireDist.resolve(connectModulePath)];
type ConnectHarness = {
captureOpenshellSpy: MockInstance;
@ -65,24 +69,18 @@ function createConnectHarness(options: ConnectHarnessOptions = {}): ConnectHarne
signal: options.spawnSignal ?? null,
} as never)) as never);
const runtime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
const gatewayState = requireDist("../../../../dist/lib/actions/sandbox/gateway-state.js");
const processRecovery = requireDist("../../../../dist/lib/actions/sandbox/process-recovery.js");
const autoPairApproval = requireDist(
"../../../../dist/lib/actions/sandbox/auto-pair-approval.js",
);
const connectVllmPreflight = requireDist(
"../../../../dist/lib/actions/sandbox/connect-vllm-preflight.js",
);
const gatewayFailureClassifier = requireDist(
"../../../../dist/lib/actions/sandbox/gateway-failure-classifier.js",
);
const ollamaProxy = requireDist("../../../../dist/lib/inference/ollama/proxy.js");
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
const registry = requireDist("../../../../dist/lib/state/registry.js");
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
const runtime = requireDist("../../adapters/openshell/runtime.js");
const resolve = requireDist("../../adapters/openshell/resolve.js");
const agentRuntime = requireDist("../../agent/runtime.js");
const gatewayState = requireDist("./gateway-state.js");
const processRecovery = requireDist("./process-recovery.js");
const autoPairApproval = requireDist("./auto-pair-approval.js");
const connectVllmPreflight = requireDist("./connect-vllm-preflight.js");
const gatewayFailureClassifier = requireDist("./gateway-failure-classifier.js");
const ollamaProxy = requireDist("../../inference/ollama/proxy.js");
const sandboxVersion = requireDist("../../sandbox/version.js");
const registry = requireDist("../../state/registry.js");
const sandboxSession = requireDist("../../state/sandbox-session.js");
vi.spyOn(connectVllmPreflight, "preflightVllmModelEnvOrExit").mockImplementation(() => undefined);
vi.spyOn(gatewayState, "ensureLiveSandboxOrExit").mockResolvedValue({
@ -436,7 +434,7 @@ describe("connectSandbox flow", () => {
secretBoundaryReason: "raw-secret",
},
});
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
const agentRuntime = requireDist("../../agent/runtime.js");
vi.spyOn(agentRuntime, "getSessionAgent").mockReturnValue({ name: "hermes" });
vi.spyOn(agentRuntime, "getAgentDisplayName").mockReturnValue("Hermes");
const errorSpy = vi.spyOn(console, "error");
@ -474,7 +472,7 @@ describe("connectSandbox flow", () => {
secretBoundaryReason: "raw-secret",
},
});
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
const agentRuntime = requireDist("../../agent/runtime.js");
vi.spyOn(agentRuntime, "getSessionAgent").mockReturnValue({ name: "hermes" });
vi.spyOn(agentRuntime, "getAgentDisplayName").mockReturnValue("Hermes");
const errorSpy = vi.spyOn(console, "error");
@ -509,7 +507,7 @@ describe("connectSandbox flow", () => {
secretBoundaryReason: "inconclusive",
},
});
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
const agentRuntime = requireDist("../../agent/runtime.js");
vi.spyOn(agentRuntime, "getSessionAgent").mockReturnValue({ name: "hermes" });
vi.spyOn(agentRuntime, "getAgentDisplayName").mockReturnValue("Hermes");
const errorSpy = vi.spyOn(console, "error");

View file

@ -5,11 +5,10 @@ import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
type DestroySandbox =
typeof import("../../../../dist/lib/actions/sandbox/destroy")["destroySandbox"];
type DestroySandbox = typeof import("./destroy")["destroySandbox"];
const requireDist = createRequire(import.meta.url);
const destroyModulePath = "../../../../dist/lib/actions/sandbox/destroy.js";
const destroyModulePath = "./destroy.js";
type DestroyHarness = {
cleanupGatewaySpy: MockInstance;
@ -45,19 +44,17 @@ function createDestroyHarness(options: DestroyHarnessOptions = {}): DestroyHarne
vi.spyOn(console, "error").mockImplementation(() => undefined);
vi.spyOn(console, "warn").mockImplementation(() => undefined);
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
const runtime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
const destroyGateway = requireDist("../../../../dist/lib/actions/sandbox/destroy-gateway.js");
const sandboxProviderCleanup = requireDist(
"../../../../dist/lib/onboard/sandbox-provider-cleanup.js",
);
const nim = requireDist("../../../../dist/lib/inference/nim.js");
const ollamaProxy = requireDist("../../../../dist/lib/inference/ollama/proxy.js");
const tunnelServices = requireDist("../../../../dist/lib/tunnel/services.js");
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
const registry = requireDist("../../../../dist/lib/state/registry.js");
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
const timerControl = requireDist("../../../../dist/lib/shields/timer-control.js");
const resolve = requireDist("../../adapters/openshell/resolve.js");
const runtime = requireDist("../../adapters/openshell/runtime.js");
const destroyGateway = requireDist("./destroy-gateway.js");
const sandboxProviderCleanup = requireDist("../../onboard/sandbox-provider-cleanup.js");
const nim = requireDist("../../inference/nim.js");
const ollamaProxy = requireDist("../../inference/ollama/proxy.js");
const tunnelServices = requireDist("../../tunnel/services.js");
const onboardSession = requireDist("../../state/onboard-session.js");
const registry = requireDist("../../state/registry.js");
const sandboxSession = requireDist("../../state/sandbox-session.js");
const timerControl = requireDist("../../shields/timer-control.js");
vi.spyOn(resolve, "resolveOpenshell").mockReturnValue("/usr/bin/openshell");
vi.spyOn(sandboxSession, "getActiveSandboxSessions").mockReturnValue({

View file

@ -2,12 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
import { describe, expect, it } from "vitest";
import {
getSandboxDockerHealth,
getSandboxDockerRuntime,
} from "../../../../dist/lib/actions/sandbox/docker-health";
import type { SandboxEntry } from "../../../../dist/lib/state/registry";
import type { SandboxEntry } from "../../state/registry";
import { getSandboxDockerHealth, getSandboxDockerRuntime } from "./docker-health";
function fixture({
driver = "docker",

View file

@ -5,11 +5,10 @@ import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
type RunSandboxDoctor =
typeof import("../../../../dist/lib/actions/sandbox/doctor")["runSandboxDoctor"];
type RunSandboxDoctor = typeof import("./doctor")["runSandboxDoctor"];
const requireDist = createRequire(import.meta.url);
const doctorModulePath = "../../../../dist/lib/actions/sandbox/doctor.js";
const doctorModulePath = "./doctor.js";
function createDoctorHarness(): {
captureHostCommandSpy: MockInstance;
@ -23,29 +22,23 @@ function createDoctorHarness(): {
const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
vi.spyOn(console, "error").mockImplementation(() => undefined);
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
const runtime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
const agentDefs = requireDist("../../../../dist/lib/agent/defs.js");
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
const gatewayRuntime = requireDist("../../../../dist/lib/gateway-runtime-action.js");
const health = requireDist("../../../../dist/lib/inference/health.js");
const dockerDriverPlatform = requireDist(
"../../../../dist/lib/onboard/docker-driver-platform.js",
);
const gatewayBinding = requireDist("../../../../dist/lib/onboard/gateway-binding.js");
const sandboxVerificationExec = requireDist(
"../../../../dist/lib/onboard/sandbox-verification-exec.js",
);
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
const shields = requireDist("../../../../dist/lib/shields/index.js");
const registry = requireDist("../../../../dist/lib/state/registry.js");
const statusCommandDeps = requireDist("../../../../dist/lib/status-command-deps.js");
const tunnelServices = requireDist("../../../../dist/lib/tunnel/services.js");
const doctorHostCommand = requireDist(
"../../../../dist/lib/actions/sandbox/doctor-host-command.js",
);
const doctorToolScope = requireDist("../../../../dist/lib/actions/sandbox/doctor-tool-scope.js");
const processRecovery = requireDist("../../../../dist/lib/actions/sandbox/process-recovery.js");
const resolve = requireDist("../../adapters/openshell/resolve.js");
const runtime = requireDist("../../adapters/openshell/runtime.js");
const agentDefs = requireDist("../../agent/defs.js");
const agentRuntime = requireDist("../../agent/runtime.js");
const gatewayRuntime = requireDist("../../gateway-runtime-action.js");
const health = requireDist("../../inference/health.js");
const dockerDriverPlatform = requireDist("../../onboard/docker-driver-platform.js");
const gatewayBinding = requireDist("../../onboard/gateway-binding.js");
const sandboxVerificationExec = requireDist("../../onboard/sandbox-verification-exec.js");
const sandboxVersion = requireDist("../../sandbox/version.js");
const shields = requireDist("../../shields/index.js");
const registry = requireDist("../../state/registry.js");
const statusCommandDeps = requireDist("../../status-command-deps.js");
const tunnelServices = requireDist("../../tunnel/services.js");
const doctorHostCommand = requireDist("./doctor-host-command.js");
const doctorToolScope = requireDist("./doctor-tool-scope.js");
const processRecovery = requireDist("./process-recovery.js");
const getSandboxSpy = vi.spyOn(registry, "getSandbox").mockReturnValue({
name: "alpha",

View file

@ -3,11 +3,11 @@
import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from "vitest";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
import type { OpenShellStateRpcIssue } from "../../adapters/openshell/gateway-drift";
type GatewayStateModule = typeof import("../../../../dist/lib/actions/sandbox/gateway-state");
type GatewayStateModule = typeof import("./gateway-state");
const requireDist = createRequire(import.meta.url);
@ -46,10 +46,10 @@ describe("sandbox gateway state drift guard", () => {
exitSpy = mockExit();
errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
const gatewayDrift = requireDist("../../../../dist/lib/adapters/openshell/gateway-drift.js");
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
const gatewayRuntime = requireDist("../../../../dist/lib/gateway-runtime-action.js");
const registry = requireDist("../../../../dist/lib/state/registry.js");
const gatewayDrift = requireDist("../../adapters/openshell/gateway-drift.js");
const openshellRuntime = requireDist("../../adapters/openshell/runtime.js");
const gatewayRuntime = requireDist("../../gateway-runtime-action.js");
const registry = requireDist("../../state/registry.js");
getSandboxSpy = vi.spyOn(registry, "getSandbox").mockReturnValue(null);
@ -99,7 +99,7 @@ describe("sandbox gateway state drift guard", () => {
removeSandboxSpy,
);
gatewayState = requireDist("../../../../dist/lib/actions/sandbox/gateway-state.js");
gatewayState = requireDist("./gateway-state.js");
});
afterEach(() => {

View file

@ -3,9 +3,9 @@
import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from "vitest";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
type GatewayStateModule = typeof import("../../../../dist/lib/actions/sandbox/gateway-state");
type GatewayStateModule = typeof import("./gateway-state");
const requireDist = createRequire(import.meta.url);
@ -14,13 +14,13 @@ describe("printGatewayLifecycleHint multi-instance hints", () => {
let getSandboxSpy: MockInstance;
beforeEach(async () => {
const registry = requireDist("../../../../dist/lib/state/registry.js");
const registry = requireDist("../../state/registry.js");
getSandboxSpy = vi.spyOn(registry, "getSandbox").mockReturnValue({
name: "instance-a",
gatewayName: "nemoclaw-8080",
gatewayPort: 8080,
});
gatewayState = requireDist("../../../../dist/lib/actions/sandbox/gateway-state.js");
gatewayState = requireDist("./gateway-state.js");
});
afterEach(() => {

View file

@ -1,13 +1,10 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { describe, it, expect } from "vitest";
import { describe, expect, it } from "vitest";
// Import from compiled dist for parity with the other CLI tests in this project.
import {
collectGatewayWedgeDiagnostics,
sanitizeWedgeLogLine,
} from "../../../../dist/lib/actions/sandbox/gateway-wedge-diagnostics";
import { collectGatewayWedgeDiagnostics, sanitizeWedgeLogLine } from "./gateway-wedge-diagnostics";
describe("collectGatewayWedgeDiagnostics — #4710 wedge signature", () => {
it("returns the matching gateway.log lines, trimmed", () => {

View file

@ -6,7 +6,7 @@ import {
ensureHermesDashboardPortForwardIfEnabled,
getHermesDashboardRecoveryConfig,
recoverHermesDashboardProcessIfEnabled,
} from "../../../../dist/lib/actions/sandbox/hermes-dashboard-recovery";
} from "./hermes-dashboard-recovery";
describe("Hermes dashboard recovery helpers", () => {
it("reads recovery config only for enabled Hermes dashboard sandboxes", () => {

View file

@ -7,19 +7,16 @@
// Without this gate, a destructive sandbox rebuild can run and fail late at
// Dockerfile patching.
//
// Why dist + vi.spyOn (matches policy-channel-conflict.test.ts): the source
// policy-channel.ts loads several deps via runtime CommonJS `require()`. In
// this repo's vitest setup, `vi.mock` only intercepts ESM `import`, not plain
// `require()`. We `require()` the COMPILED module + its real compiled
// dependency modules from dist/ (one shared require cache) and `vi.spyOn`
// the dependency exports. Run `npm run build:cli` first.
// policy-channel.ts loads several dependencies through CommonJS `require()`.
// Load the source module and its dependencies through the shared source hook
// so `vi.spyOn` observes one require cache without depending on a CLI build.
import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
const requireDist = createRequire(import.meta.url);
const D = (p: string) => requireDist(`../../../../dist/lib/${p}`);
const requireSource = createRequire(import.meta.url);
const D = (p: string) => requireSource(`../../${p}`);
const registry = D("state/registry.js");
const providers = D("onboard/providers.js");

View file

@ -12,7 +12,7 @@ import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
const requireDist = createRequire(import.meta.url);
const D = (p: string) => requireDist(`../../../../dist/lib/${p}`);
const D = (p: string) => requireDist(`../../${p}`);
const registry = D("state/registry.js");
const defs = D("agent/defs.js");

View file

@ -6,14 +6,9 @@
// assert only on the mocked module boundaries — never on the private helper
// names — so they survive a refactor of the internal conflict-check plumbing.
//
// Why dist + vi.spyOn (the rebuild-shields-finally.test.ts pattern): the source
// policy-channel.ts loads several deps via runtime CommonJS `require()`
// (../../onboard, ../../onboard/providers, ./rebuild, ../../runner, ...). In
// this repo's vitest setup, `vi.mock` only intercepts ESM `import`, not plain
// `require()`, and those modules do extensionless sibling requires the TS
// transform cannot resolve. So we require the COMPILED module + its real
// compiled dependency modules from dist/ (one shared require cache) and
// `vi.spyOn` the dependency exports. Run `npm run build:cli` first.
// policy-channel.ts loads several dependencies through CommonJS `require()`.
// Load the source module and its dependencies through the shared source hook
// so `vi.spyOn` observes one require cache without depending on a CLI build.
//
// isNonInteractive is destructured at module load (`const { isNonInteractive }
// = require("../../onboard")`), so it cannot be spied after load; it reads
@ -25,12 +20,12 @@ import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
const requireDist = createRequire(import.meta.url);
const D = (p: string) => requireDist(`../../../../dist/lib/${p}`);
const requireSource = createRequire(import.meta.url);
const D = (p: string) => requireSource(`../../${p}`);
type SandboxEntry = import("../../state/registry").SandboxEntry;
// Real compiled dependency modules (shared require cache with the SUT).
// Real source dependency modules (shared require cache with the SUT).
const store = D("credentials/store.js");
const registry = D("state/registry.js");
const providers = D("onboard/providers.js");

View file

@ -6,7 +6,7 @@ import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
const requireDist = createRequire(import.meta.url);
const D = (p: string) => requireDist(`../../../../dist/lib/${p}`);
const D = (p: string) => requireDist(`../../${p}`);
type PresetInfo = {
name: string;

View file

@ -12,15 +12,15 @@
* silently let the in-sandbox POLICY.md drift.
*/
import * as fs from "node:fs";
import { createRequire } from "node:module";
import * as os from "node:os";
import * as path from "node:path";
import * as fs from "node:fs";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
const requireDist = createRequire(import.meta.url);
const D = (p: string) => requireDist(`../../../../dist/lib/${p}`);
const D = (p: string) => requireDist(`../../${p}`);
type PresetInfo = { name: string };

View file

@ -6,9 +6,9 @@ import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
const requireDist = createRequire(import.meta.url);
const policyChannelModulePath = "../../../../dist/lib/actions/sandbox/policy-channel.js";
const policyChannelModulePath = "./policy-channel.js";
type PolicyChannelModule = typeof import("../../../../dist/lib/actions/sandbox/policy-channel");
type PolicyChannelModule = typeof import("./policy-channel");
describe("policy channel remove/enable flows", () => {
let exitSpy: MockInstance;
@ -52,7 +52,7 @@ describe("policy channel remove/enable flows", () => {
});
it("supports stop dry runs for configured channels", async () => {
const registry = requireDist("../../../../dist/lib/state/registry.js");
const registry = requireDist("../../state/registry.js");
vi.spyOn(registry, "getSandbox").mockReturnValue({ name: "alpha" });
vi.spyOn(registry, "getConfiguredMessagingChannelsFromEntry").mockReturnValue(["telegram"]);
vi.spyOn(registry, "getDisabledChannels").mockReturnValue([]);

View file

@ -1,13 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { afterEach, describe, it, expect } from "vitest";
import { afterEach, describe, expect, it } from "vitest";
// Import from compiled dist for parity with the other CLI tests in this project.
import {
probeSandboxInferenceGatewayHealth,
waitForRecoveredSandboxGateway,
} from "../../../../dist/lib/actions/sandbox/process-recovery";
} from "./process-recovery";
describe("probeSandboxInferenceGatewayHealth — #3265 gateway-chain subprobe", () => {
const makeExec =

View file

@ -10,11 +10,11 @@ import path from "node:path";
import { describe, expect, it } from "vitest";
type RebuildModule = typeof import("../../../../dist/lib/actions/sandbox/rebuild");
type RebuildModule = typeof import("./rebuild");
const requireDist = createRequire(import.meta.url);
const { buildRefreshMutableOpenClawConfigHashCommand } = requireDist(
"../../../../dist/lib/actions/sandbox/rebuild.js",
"./rebuild.js",
) as RebuildModule;
function sha256Hex(filePath: string): string {

View file

@ -8,7 +8,7 @@ import {
assessAmbientRecreateEnv,
isolateAmbientRecreateEnv,
sanitizeEnvValueForDisplay,
} from "../../../../dist/lib/actions/sandbox/rebuild-env-isolation.js";
} from "./rebuild-env-isolation.js";
describe("sanitizeEnvValueForDisplay (#5735 PRA-7)", () => {
it("collapses a multi-line / ANSI value into a single safe line", () => {

View file

@ -5,16 +5,14 @@ import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
type RebuildFlowHelpersModule =
typeof import("../../../../dist/lib/actions/sandbox/rebuild-flow-helpers");
type SandboxStateModule = typeof import("../../../../dist/lib/state/sandbox");
type UserManagedFilesProbeModule =
typeof import("../../../../dist/lib/state/user-managed-files-probe");
type RebuildFlowHelpersModule = typeof import("./rebuild-flow-helpers");
type SandboxStateModule = typeof import("../../state/sandbox");
type UserManagedFilesProbeModule = typeof import("../../state/user-managed-files-probe");
const requireDist = createRequire(import.meta.url);
const rebuildFlowHelpersPath = "../../../../dist/lib/actions/sandbox/rebuild-flow-helpers.js";
const sandboxStatePath = "../../../../dist/lib/state/sandbox.js";
const userManagedFilesProbePath = "../../../../dist/lib/state/user-managed-files-probe.js";
const rebuildFlowHelpersPath = "./rebuild-flow-helpers.js";
const sandboxStatePath = "../../state/sandbox.js";
const userManagedFilesProbePath = "../../state/user-managed-files-probe.js";
function loadRebuildFlowHelpers(): RebuildFlowHelpersModule {
delete require.cache[requireDist.resolve(rebuildFlowHelpersPath)];

View file

@ -5,11 +5,15 @@ import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
type RebuildSandbox =
typeof import("../../../../dist/lib/actions/sandbox/rebuild")["rebuildSandbox"];
type RebuildSandbox = typeof import("./rebuild")["rebuildSandbox"];
const requireDist = createRequire(import.meta.url);
const rebuildModulePath = "../../../../dist/lib/actions/sandbox/rebuild.js";
const rebuildModulePath = "./rebuild.js";
// Warm the CommonJS source graph outside the first test's timeout. Each harness
// still reloads the entry module after installing its dependency spies.
requireDist(rebuildModulePath);
delete require.cache[requireDist.resolve(rebuildModulePath)];
type RebuildFlowStep = {
status: string;
@ -157,28 +161,26 @@ function createRebuildFlowHarness(overrides: RebuildFlowOverrides = {}): Rebuild
const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined);
const gatewayDrift = requireDist("../../../../dist/lib/adapters/openshell/gateway-drift.js");
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
const sandboxList = requireDist("../../../../dist/lib/openshell-sandbox-list.js");
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
const agentDefs = requireDist("../../../../dist/lib/agent/defs.js");
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
const onboardMod = requireDist("../../../../dist/lib/onboard.js");
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
const registry = requireDist("../../../../dist/lib/state/registry.js");
const sandboxState = requireDist("../../../../dist/lib/state/sandbox.js");
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
const destroy = requireDist("../../../../dist/lib/actions/sandbox/destroy.js");
const rebuildShields = requireDist("../../../../dist/lib/actions/sandbox/rebuild-shields.js");
const nim = requireDist("../../../../dist/lib/inference/nim.js");
const policies = requireDist("../../../../dist/lib/policy/index.js");
const processRecovery = requireDist("../../../../dist/lib/actions/sandbox/process-recovery.js");
const messagingHostForwardLifecycle = requireDist(
"../../../../dist/lib/actions/sandbox/messaging-host-forward-lifecycle.js",
);
const messaging = requireDist("../../../../dist/lib/messaging/index.js");
const shields = requireDist("../../../../dist/lib/shields/index.js");
const gatewayDrift = requireDist("../../adapters/openshell/gateway-drift.js");
const openshellRuntime = requireDist("../../adapters/openshell/runtime.js");
const sandboxList = requireDist("../../openshell-sandbox-list.js");
const resolve = requireDist("../../adapters/openshell/resolve.js");
const agentDefs = requireDist("../../agent/defs.js");
const agentRuntime = requireDist("../../agent/runtime.js");
const onboardMod = requireDist("../../onboard.js");
const onboardSession = requireDist("../../state/onboard-session.js");
const registry = requireDist("../../state/registry.js");
const sandboxState = requireDist("../../state/sandbox.js");
const sandboxSession = requireDist("../../state/sandbox-session.js");
const sandboxVersion = requireDist("../../sandbox/version.js");
const destroy = requireDist("./destroy.js");
const rebuildShields = requireDist("./rebuild-shields.js");
const nim = requireDist("../../inference/nim.js");
const policies = requireDist("../../policy/index.js");
const processRecovery = requireDist("./process-recovery.js");
const messagingHostForwardLifecycle = requireDist("./messaging-host-forward-lifecycle.js");
const messaging = requireDist("../../messaging/index.js");
const shields = requireDist("../../shields/index.js");
const session = createRebuildFlowSession(onboardSession.MACHINE_SNAPSHOT_VERSION);
const rebuildShieldsWindow = { relocked: false, wasLocked: false };

View file

@ -7,8 +7,7 @@ import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } fr
import type { OpenShellStateRpcIssue } from "../../adapters/openshell/gateway-drift";
type RebuildSandbox =
typeof import("../../../../dist/lib/actions/sandbox/rebuild")["rebuildSandbox"];
type RebuildSandbox = typeof import("./rebuild")["rebuildSandbox"];
const requireDist = createRequire(import.meta.url);
@ -45,15 +44,15 @@ describe("rebuild gateway drift preflight", () => {
exitSpy = mockExit();
errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
const gatewayDrift = requireDist("../../../../dist/lib/adapters/openshell/gateway-drift.js");
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
const gatewayRuntime = requireDist("../../../../dist/lib/gateway-runtime-action.js");
const registry = requireDist("../../../../dist/lib/state/registry.js");
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
const gatewayDrift = requireDist("../../adapters/openshell/gateway-drift.js");
const openshellRuntime = requireDist("../../adapters/openshell/runtime.js");
const gatewayRuntime = requireDist("../../gateway-runtime-action.js");
const registry = requireDist("../../state/registry.js");
const resolve = requireDist("../../adapters/openshell/resolve.js");
const sandboxSession = requireDist("../../state/sandbox-session.js");
const onboardSession = requireDist("../../state/onboard-session.js");
const sandboxVersion = requireDist("../../sandbox/version.js");
const agentRuntime = requireDist("../../agent/runtime.js");
printIssueSpy = vi
.spyOn(gatewayDrift, "printOpenShellStateRpcIssue")
@ -100,7 +99,7 @@ describe("rebuild gateway drift preflight", () => {
checkAgentVersionSpy,
);
({ rebuildSandbox } = requireDist("../../../../dist/lib/actions/sandbox/rebuild.js"));
({ rebuildSandbox } = requireDist("./rebuild.js"));
});
afterEach(() => {
@ -156,8 +155,8 @@ describe("rebuild gateway drift preflight", () => {
// preserved registry metadata. Stub the destructive steps + recreate handoff
// so the path stays hermetic, and assert the recreate failure surfaces the
// stale-recovery message instead of "not running".
const destroy = requireDist("../../../../dist/lib/actions/sandbox/destroy.js");
const onboardMod = requireDist("../../../../dist/lib/onboard.js");
const destroy = requireDist("./destroy.js");
const onboardMod = requireDist("../../onboard.js");
spies.push(
vi.spyOn(destroy, "removeSandboxRegistryEntry").mockImplementation(() => undefined),
vi.spyOn(onboardMod, "onboard").mockRejectedValue(new Error("recreate-stub")),
@ -189,15 +188,15 @@ describe("rebuild gateway drift preflight", () => {
// recover the wrong (and possibly nonexistent) default gateway.
for (const spy of spies) spy.mockRestore();
spies.length = 0;
const gatewayDrift = requireDist("../../../../dist/lib/adapters/openshell/gateway-drift.js");
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
const gatewayRuntime = requireDist("../../../../dist/lib/gateway-runtime-action.js");
const registry = requireDist("../../../../dist/lib/state/registry.js");
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
const gatewayDrift = requireDist("../../adapters/openshell/gateway-drift.js");
const openshellRuntime = requireDist("../../adapters/openshell/runtime.js");
const gatewayRuntime = requireDist("../../gateway-runtime-action.js");
const registry = requireDist("../../state/registry.js");
const resolve = requireDist("../../adapters/openshell/resolve.js");
const sandboxSession = requireDist("../../state/sandbox-session.js");
const onboardSession = requireDist("../../state/onboard-session.js");
const sandboxVersion = requireDist("../../sandbox/version.js");
const agentRuntime = requireDist("../../agent/runtime.js");
let listCalls = 0;
detectPreflightIssueSpy = vi
@ -239,8 +238,8 @@ describe("rebuild gateway drift preflight", () => {
.spyOn(gatewayDrift, "printOpenShellStateRpcIssue")
.mockImplementation(() => undefined);
const destroy = requireDist("../../../../dist/lib/actions/sandbox/destroy.js");
const onboardMod = requireDist("../../../../dist/lib/onboard.js");
const destroy = requireDist("./destroy.js");
const onboardMod = requireDist("../../onboard.js");
spies.push(
detectPreflightIssueSpy,
vi.spyOn(gatewayDrift, "detectOpenShellStateRpcResultIssue").mockReturnValue(null),
@ -271,7 +270,7 @@ describe("rebuild gateway drift preflight", () => {
vi.spyOn(onboardMod, "onboard").mockRejectedValue(new Error("recreate-stub")),
);
({ rebuildSandbox } = requireDist("../../../../dist/lib/actions/sandbox/rebuild.js"));
({ rebuildSandbox } = requireDist("./rebuild.js"));
await expect(rebuildSandbox("alpha", ["--yes"], { throwOnError: true })).rejects.toThrow(
/stale-sandbox recovery/,

View file

@ -3,10 +3,7 @@
import { describe, expect, it } from "vitest";
import {
buildRebuildRecreateOnboardOpts,
rebuildShouldOptOutGpu,
} from "../../../../dist/lib/actions/sandbox/rebuild-gpu-opt-out";
import { buildRebuildRecreateOnboardOpts, rebuildShouldOptOutGpu } from "./rebuild-gpu-opt-out";
describe("rebuildShouldOptOutGpu", () => {
it("returns false when the registry entry is null", () => {

View file

@ -6,16 +6,15 @@
// channel manifests, so a non-messaging sandbox rebuild cannot
// carry messaging-plan state into the Dockerfile patch step.
//
// Loaded from dist/ to match the rest of the rebuild test suite (runner.ts
// loads './platform' via runtime CommonJS `require()` that vitest cannot
// resolve from a TS source file). Run `npm run build:cli` first.
// Loaded through the shared source require hook because the rebuild graph uses
// runtime CommonJS dependencies that must share one cache for test spies.
import { createRequire } from "node:module";
import { afterEach, describe, expect, it, vi } from "vitest";
const requireDist = createRequire(import.meta.url);
const D = (p: string) => requireDist(`../../../../dist/lib/${p}`);
const requireSource = createRequire(import.meta.url);
const D = (p: string) => requireSource(`../../${p}`);
const defs = D("agent/defs.js");
const messaging = D("messaging/index.js") as {

View file

@ -6,13 +6,13 @@ import { createRequire } from "node:module";
import { afterEach, describe, expect, it, vi } from "vitest";
const requireDist = createRequire(import.meta.url);
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
const onboardSession = requireDist("../../state/onboard-session.js");
const {
isLocalInferenceProvider,
getRebuildCredentialEnvFromRegistry,
getRebuildEndpointFromRegistry,
prepareRebuildResumeConfig,
} = requireDist("../../../../dist/lib/actions/sandbox/rebuild-resume-config.js");
} = requireDist("./rebuild-resume-config.js");
const noopLog = () => undefined;
const throwingBail = (msg: string): never => {

View file

@ -7,11 +7,10 @@ import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } fr
import type { Session } from "../../state/onboard-session";
type RebuildSandbox =
typeof import("../../../../dist/lib/actions/sandbox/rebuild")["rebuildSandbox"];
type RebuildSandbox = typeof import("./rebuild")["rebuildSandbox"];
const requireDist = createRequire(import.meta.url);
const rebuildModulePath = "../../../../dist/lib/actions/sandbox/rebuild.js";
const rebuildModulePath = "./rebuild.js";
function cloneSession(session: Session): Session {
return JSON.parse(JSON.stringify(session));
@ -44,22 +43,22 @@ describe("rebuild resume snapshot repair", () => {
errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
const gatewayDrift = requireDist("../../../../dist/lib/adapters/openshell/gateway-drift.js");
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
const sandboxList = requireDist("../../../../dist/lib/openshell-sandbox-list.js");
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
const agentDefs = requireDist("../../../../dist/lib/agent/defs.js");
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
const onboardMod = requireDist("../../../../dist/lib/onboard.js");
const resumeRepair = requireDist("../../../../dist/lib/onboard/resume-machine-repair.js");
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
const registry = requireDist("../../../../dist/lib/state/registry.js");
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
const sandboxState = requireDist("../../../../dist/lib/state/sandbox.js");
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
const destroy = requireDist("../../../../dist/lib/actions/sandbox/destroy.js");
const rebuildShields = requireDist("../../../../dist/lib/actions/sandbox/rebuild-shields.js");
const nim = requireDist("../../../../dist/lib/inference/nim.js");
const gatewayDrift = requireDist("../../adapters/openshell/gateway-drift.js");
const openshellRuntime = requireDist("../../adapters/openshell/runtime.js");
const sandboxList = requireDist("../../openshell-sandbox-list.js");
const resolve = requireDist("../../adapters/openshell/resolve.js");
const agentDefs = requireDist("../../agent/defs.js");
const agentRuntime = requireDist("../../agent/runtime.js");
const onboardMod = requireDist("../../onboard.js");
const resumeRepair = requireDist("../../onboard/resume-machine-repair.js");
const onboardSession = requireDist("../../state/onboard-session.js");
const registry = requireDist("../../state/registry.js");
const sandboxSession = requireDist("../../state/sandbox-session.js");
const sandboxState = requireDist("../../state/sandbox.js");
const sandboxVersion = requireDist("../../sandbox/version.js");
const destroy = requireDist("./destroy.js");
const rebuildShields = requireDist("./rebuild-shields.js");
const nim = requireDist("../../inference/nim.js");
session = onboardSession.createSession({
sandboxName: "alpha",

View file

@ -5,11 +5,10 @@ import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
type RebuildSandbox =
typeof import("../../../../dist/lib/actions/sandbox/rebuild")["rebuildSandbox"];
type RebuildSandbox = typeof import("./rebuild")["rebuildSandbox"];
const requireDist = createRequire(import.meta.url);
const rebuildModulePath = "../../../../dist/lib/actions/sandbox/rebuild.js";
const rebuildModulePath = "./rebuild.js";
describe("rebuild shields relock guard", () => {
let rebuildSandbox: RebuildSandbox;
@ -28,17 +27,17 @@ describe("rebuild shields relock guard", () => {
errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
const gatewayDrift = requireDist("../../../../dist/lib/adapters/openshell/gateway-drift.js");
const gatewayRuntime = requireDist("../../../../dist/lib/gateway-runtime-action.js");
const sandboxList = requireDist("../../../../dist/lib/openshell-sandbox-list.js");
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
const registry = requireDist("../../../../dist/lib/state/registry.js");
const sandboxState = requireDist("../../../../dist/lib/state/sandbox.js");
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
const rebuildShields = requireDist("../../../../dist/lib/actions/sandbox/rebuild-shields.js");
const gatewayDrift = requireDist("../../adapters/openshell/gateway-drift.js");
const gatewayRuntime = requireDist("../../gateway-runtime-action.js");
const sandboxList = requireDist("../../openshell-sandbox-list.js");
const resolve = requireDist("../../adapters/openshell/resolve.js");
const agentRuntime = requireDist("../../agent/runtime.js");
const onboardSession = requireDist("../../state/onboard-session.js");
const registry = requireDist("../../state/registry.js");
const sandboxState = requireDist("../../state/sandbox.js");
const sandboxSession = requireDist("../../state/sandbox-session.js");
const sandboxVersion = requireDist("../../sandbox/version.js");
const rebuildShields = requireDist("./rebuild-shields.js");
relockSpy = vi
.spyOn(rebuildShields, "relockRebuildShieldsWindow")

View file

@ -3,11 +3,11 @@
import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from "vitest";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
const requireDist = createRequire(import.meta.url);
type RoutingModule = typeof import("../../../../dist/lib/actions/sandbox/sandbox-gateway-routing");
type RoutingModule = typeof import("./sandbox-gateway-routing");
describe("sandbox gateway routing helpers", () => {
let routing: RoutingModule;
@ -17,8 +17,8 @@ describe("sandbox gateway routing helpers", () => {
let runOpenshellSpy: MockInstance;
beforeEach(() => {
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
const registry = requireDist("../../../../dist/lib/state/registry.js");
const openshellRuntime = requireDist("../../adapters/openshell/runtime.js");
const registry = requireDist("../../state/registry.js");
spies = [];
getSandboxSpy = vi.spyOn(registry, "getSandbox").mockReturnValue({
@ -44,7 +44,7 @@ describe("sandbox gateway routing helpers", () => {
} as never);
spies.push(getSandboxSpy, captureOpenshellSpy, runOpenshellSpy);
routing = requireDist("../../../../dist/lib/actions/sandbox/sandbox-gateway-routing.js");
routing = requireDist("./sandbox-gateway-routing.js");
});
afterEach(() => {

View file

@ -197,7 +197,9 @@ describe("callOpenclawGateway", () => {
{ encoding: "utf8" },
);
expect(result.status).toBe(126);
// Login-shell teardown hooks may replace the explicit 126 status, but
// the security rejection must remain non-zero and happen before Node.
expect(result.status).not.toBe(0);
expect(result.stderr).toContain("[SECURITY]");
expect(result.stderr).toContain("expected regular root-owned mode 444 file");
expect(result.stderr).not.toContain("node should not run");

View file

@ -5,11 +5,15 @@ import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
type ShowSandboxStatus =
typeof import("../../../../dist/lib/actions/sandbox/status")["showSandboxStatus"];
type ShowSandboxStatus = typeof import("./status")["showSandboxStatus"];
const requireDist = createRequire(import.meta.url);
const statusModulePath = "../../../../dist/lib/actions/sandbox/status.js";
const statusModulePath = "./status.js";
// Warm the CommonJS source graph outside the first test's timeout. Each harness
// still reloads the entry module after installing its dependency spies.
requireDist(statusModulePath);
delete require.cache[requireDist.resolve(statusModulePath)];
type StatusFlowHarness = {
checkAgentVersionSpy: MockInstance;
@ -54,17 +58,17 @@ function createStatusFlowHarness(
const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
vi.spyOn(console, "error").mockImplementation(() => undefined);
const statusPreflight = requireDist("../../../../dist/lib/actions/sandbox/status-preflight.js");
const statusSnapshot = requireDist("../../../../dist/lib/actions/sandbox/status-snapshot.js");
const dockerHealth = requireDist("../../../../dist/lib/actions/sandbox/docker-health.js");
const processRecovery = requireDist("../../../../dist/lib/actions/sandbox/process-recovery.js");
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
const nim = requireDist("../../../../dist/lib/inference/nim.js");
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
const shields = requireDist("../../../../dist/lib/shields/index.js");
const registry = requireDist("../../../../dist/lib/state/registry.js");
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
const statusPreflight = requireDist("./status-preflight.js");
const statusSnapshot = requireDist("./status-snapshot.js");
const dockerHealth = requireDist("./docker-health.js");
const processRecovery = requireDist("./process-recovery.js");
const resolve = requireDist("../../adapters/openshell/resolve.js");
const agentRuntime = requireDist("../../agent/runtime.js");
const nim = requireDist("../../inference/nim.js");
const sandboxVersion = requireDist("../../sandbox/version.js");
const shields = requireDist("../../shields/index.js");
const registry = requireDist("../../state/registry.js");
const sandboxSession = requireDist("../../state/sandbox-session.js");
const lookup =
options.lookupState === "missing"

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import { describe, expect, it } from "vitest";
import type { ProviderHealthProbeOptions } from "../../inference/health";
import {
classifySandboxContainerFailureForStatus,
classifySandboxStatusPreflightFailure,
@ -10,8 +11,7 @@ import {
maybeGetSandboxStatusInferenceHealth,
sandboxGpuProofStatusSuffix,
sandboxGpuProofUnverified,
} from "../../../../dist/lib/actions/sandbox/status";
import type { ProviderHealthProbeOptions } from "../../../../dist/lib/inference/health";
} from "./status";
describe("sandbox status inference health", () => {
it("passes the current model with the current provider", () => {

View file

@ -5,16 +5,15 @@ import { createRequire } from "node:module";
import { afterEach, describe, expect, it, type MockInstance, vi } from "vitest";
import {
const requireDist = createRequire(import.meta.url);
const {
detectOpenShellStateRpcPreflightIssue,
detectOpenShellStateRpcResultIssue,
formatOpenShellStateRpcIssue,
getGatewayClusterImageDrift,
getGatewayHostProcessDrift,
parseGatewayClusterImageVersion,
} from "../../../../dist/lib/adapters/openshell/gateway-drift";
const requireDist = createRequire(import.meta.url);
} = requireDist("./gateway-drift.ts") as typeof import("./gateway-drift");
describe("OpenShell gateway drift preflight", () => {
let spies: MockInstance[] = [];
@ -75,8 +74,8 @@ describe("OpenShell gateway drift preflight", () => {
});
it("uses the shared gateway-health classifier when checking the active cluster gateway", () => {
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
const docker = requireDist("../../../../dist/lib/adapters/docker/inspect.js");
const openshellRuntime = requireDist("./runtime.js");
const docker = requireDist("../docker/inspect.js");
spies.push(
vi.spyOn(openshellRuntime, "captureOpenshell").mockImplementation((rawArgs: unknown) => {
const args = rawArgs as string[];
@ -118,8 +117,8 @@ describe("OpenShell gateway drift preflight", () => {
});
it("ignores stale cluster containers whose published port is not the active gateway endpoint", () => {
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
const docker = requireDist("../../../../dist/lib/adapters/docker/inspect.js");
const openshellRuntime = requireDist("./runtime.js");
const docker = requireDist("../docker/inspect.js");
spies.push(
vi.spyOn(openshellRuntime, "captureOpenshell").mockImplementation((rawArgs: unknown) => {
const args = rawArgs as string[];

View file

@ -1,11 +1,11 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { describe, it, expect } from "vitest";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { resolveOpenshell } from "../../../../dist/lib/adapters/openshell/resolve";
import { describe, expect, it } from "vitest";
import { resolveOpenshell } from "./resolve";
describe("lib/resolve-openshell", () => {
it("returns command -v result when absolute path", () => {

View file

@ -4,10 +4,10 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { AgentDefinition } from "./defs";
type AgentOnboardModule = typeof import("../../../dist/lib/agent/onboard");
type DockerImageModule = typeof import("../../../dist/lib/adapters/docker/image");
type DockerInspectModule = typeof import("../../../dist/lib/adapters/docker/inspect");
type SandboxBaseImageModule = typeof import("../../../dist/lib/sandbox-base-image");
type AgentOnboardModule = typeof import("./onboard");
type DockerImageModule = typeof import("../adapters/docker/image");
type DockerInspectModule = typeof import("../adapters/docker/inspect");
type SandboxBaseImageModule = typeof import("../sandbox-base-image");
/**
* Build a minimal Hermes agent manifest for base-image provisioning tests.
@ -66,19 +66,17 @@ function withMockedDocker<T>(
}) => T,
): T {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const dockerImageModule = require("../../../dist/lib/adapters/docker/image") as DockerImageModule;
const dockerImageModule = require("../adapters/docker/image") as DockerImageModule;
// eslint-disable-next-line @typescript-eslint/no-require-imports
const dockerInspectModule =
require("../../../dist/lib/adapters/docker/inspect") as DockerInspectModule;
const dockerInspectModule = require("../adapters/docker/inspect") as DockerInspectModule;
// eslint-disable-next-line @typescript-eslint/no-require-imports
const sandboxBaseImageModule =
require("../../../dist/lib/sandbox-base-image") as SandboxBaseImageModule;
const sandboxBaseImageModule = require("../sandbox-base-image") as SandboxBaseImageModule;
// eslint-disable-next-line @typescript-eslint/no-require-imports
const runnerModule = require("../../../dist/lib/runner") as { ROOT: string };
const runnerModule = require("../runner") as { ROOT: string };
const originalDockerBuild = dockerImageModule.dockerBuild;
const originalDockerImageInspect = dockerInspectModule.dockerImageInspect;
const originalResolveSandboxBaseImage = sandboxBaseImageModule.resolveSandboxBaseImage;
const agentOnboardModulePath = require.resolve("../../../dist/lib/agent/onboard");
const agentOnboardModulePath = require.resolve("./onboard");
delete require.cache[agentOnboardModulePath];
const dockerBuildMock = vi.fn().mockReturnValue({ status: 0 });
@ -97,7 +95,7 @@ function withMockedDocker<T>(
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const agentOnboardModule = require("../../../dist/lib/agent/onboard") as AgentOnboardModule;
const agentOnboardModule = require("./onboard") as AgentOnboardModule;
return run({
ensureAgentBaseImage: agentOnboardModule.ensureAgentBaseImage,
dockerBuildMock,

View file

@ -12,7 +12,7 @@ import {
loadAgent,
resolveAgentName,
resolveAgentNameAlias,
} from "../../../dist/lib/agent/defs";
} from "./defs";
const tempAgentDirs: string[] = [];

View file

@ -2,10 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
import { describe, expect, it, vi } from "vitest";
import { loadAgent } from "../../../dist/lib/agent/defs";
// Import from compiled dist/ so coverage is attributed correctly.
import { handleAgentSetup } from "../../../dist/lib/agent/onboard";
import type { AgentDefinition } from "./defs";
import { loadAgent } from "./defs";
// Import source directly so tests cannot pass against a stale build.
import { handleAgentSetup } from "./onboard";
import {
recordFailingDeepAgentsSmokeCall,
recordSuccessfulDeepAgentsRuntimeCall,

View file

@ -2,9 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
// Import from compiled dist/ so coverage is attributed correctly.
import { printDashboardUi } from "../../../dist/lib/agent/onboard";
import type { AgentDefinition } from "./defs";
// Import source directly so tests cannot pass against a stale build.
import { printDashboardUi } from "./onboard";
const buildUrlsLoopback = (_token: string | null, port: number): string[] => [
`http://127.0.0.1:${port}/`,

View file

@ -2,14 +2,14 @@
// SPDX-License-Identifier: Apache-2.0
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest";
// Import from compiled dist/ so coverage is attributed correctly.
import type { AgentDefinition } from "./defs";
// Import source directly so tests cannot pass against a stale build.
import {
collectHermesStartupDiagnostics,
handleAgentSetup,
printDashboardUi,
verifyAgentBinaryAvailable,
} from "../../../dist/lib/agent/onboard";
import type { AgentDefinition } from "./defs";
} from "./onboard";
function makeAgent(overrides: Partial<AgentDefinition> = {}): AgentDefinition {
return {

View file

@ -13,17 +13,14 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import {
__testing,
HERMES_SECRET_BOUNDARY_VALIDATOR_PATH,
} from "../../../dist/lib/agent/hermes-recovery-boundary";
import { buildRecoveryScript } from "../../../dist/lib/agent/runtime";
import {
createRecoveryPreloadHarnessPaths,
type RecoveryPreloadHarnessPaths,
rewriteRecoveryPreloadPaths,
} from "../../../test/helpers/runtime-recovery-preload-test-helpers";
import { __testing, HERMES_SECRET_BOUNDARY_VALIDATOR_PATH } from "./hermes-recovery-boundary";
import { hermesAgent } from "./hermes-recovery-boundary-fixtures";
import { buildRecoveryScript } from "./runtime";
function writeStub(dir: string, name: string, body: string) {
const stub = path.join(dir, name);

View file

@ -8,13 +8,13 @@
// runtime-hermes-secret-boundary-behavioural.test.ts.
import { describe, expect, it } from "vitest";
import { HERMES_SECRET_BOUNDARY_VALIDATOR_PATH } from "../../../dist/lib/agent/hermes-recovery-boundary";
import { HERMES_SECRET_BOUNDARY_VALIDATOR_PATH } from "./hermes-recovery-boundary";
import { hermesAgent, minimalAgent } from "./hermes-recovery-boundary-fixtures";
import {
buildHermesDashboardProcessRecoveryScript,
buildManualRecoveryCommand,
buildRecoveryScript,
} from "../../../dist/lib/agent/runtime";
import { hermesAgent, minimalAgent } from "./hermes-recovery-boundary-fixtures";
} from "./runtime";
const VALIDATOR_PATH = HERMES_SECRET_BOUNDARY_VALIDATOR_PATH;

View file

@ -6,12 +6,9 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { buildOpenClawRecoveryScript, buildRecoveryScript } from "../../../dist/lib/agent/runtime";
import {
buildGatewayGuardRecoveryLines,
GATEWAY_PRELOAD_GUARDS,
} from "../../../dist/lib/agent/runtime-recovery-preload";
import { minimalAgent } from "./hermes-recovery-boundary-fixtures";
import { buildOpenClawRecoveryScript, buildRecoveryScript } from "./runtime";
import { buildGatewayGuardRecoveryLines, GATEWAY_PRELOAD_GUARDS } from "./runtime-recovery-preload";
const [SAFETY_NET_GUARD, CIAO_GUARD] = GATEWAY_PRELOAD_GUARDS;

View file

@ -2,15 +2,15 @@
// SPDX-License-Identifier: Apache-2.0
import { describe, expect, it } from "vitest";
import { loadAgent } from "../../../dist/lib/agent/defs";
// Import from compiled dist/ so coverage is attributed correctly.
import type { AgentDefinition } from "./defs";
import { loadAgent } from "./defs";
// Import source directly so tests cannot pass against a stale build.
import {
buildManualRecoveryCommand,
buildRecoveryScript,
getTerminalCommand,
TERMINAL_AGENT_RECOVERY_SCRIPT,
} from "../../../dist/lib/agent/runtime";
import type { AgentDefinition } from "./defs";
} from "./runtime";
const terminalAgent = {
name: "terminal-agent",

View file

@ -2,14 +2,14 @@
// SPDX-License-Identifier: Apache-2.0
import { describe, expect, it } from "vitest";
// Import from compiled dist/ so coverage is attributed correctly.
import type { AgentDefinition } from "./defs";
// Import source directly so tests cannot pass against a stale build.
import {
buildHermesDashboardProcessRecoveryScript,
buildManualRecoveryCommand,
buildOpenClawRecoveryScript,
buildRecoveryScript,
} from "../../../dist/lib/agent/runtime";
import type { AgentDefinition } from "./defs";
} from "./runtime";
function makeAgent(overrides: Partial<AgentDefinition> = {}): AgentDefinition {
return {

View file

@ -8,7 +8,7 @@ import {
printSandboxCreateRecoveryHints,
reconstructImageRefCreateCommand,
shouldIncludeBuildContextPath,
} from "../../dist/lib/build-context";
} from "./build-context";
type ConsoleErrorSpy = ReturnType<typeof vi.spyOn>;

View file

@ -8,7 +8,7 @@ import {
extractEnabledChannelsFromOpenclawConfig,
parseGatewayLogScanOutput,
probeChannelRuntimeStatus,
} from "../../dist/lib/channel-runtime-status.js";
} from "./channel-runtime-status.js";
// Build an executeSandboxCommand mock that returns the config file body
// on the `cat` call and a synthesized gateway-log-scan output on the

View file

@ -8,7 +8,7 @@ import {
computePatchedTag,
ensurePatchedClusterImage,
extractUpstreamVersion,
} from "../../dist/lib/cluster-image-patch";
} from "./cluster-image-patch";
const UPSTREAM = "ghcr.io/nvidia/openshell/cluster:0.0.36";

View file

@ -8,7 +8,7 @@ import {
getGatewayHttpEndpoint,
getGatewayHttpsEndpoint,
parseGatewayBindAddress,
} from "../../../dist/lib/core/gateway-address";
} from "./gateway-address";
const ENV_KEY = "TEST_GATEWAY_BIND_ADDRESS";

View file

@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { describe, it, expect, beforeEach, afterEach } from "vitest";
// Import from compiled dist/ so coverage is attributed correctly.
import { parseGatewayPort, parsePort } from "../../../dist/lib/core/ports";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
// Import source directly so tests cannot pass against a stale build.
import { parseGatewayPort, parsePort } from "./ports";
const GATEWAY_VALIDATION_OPTIONS = {
dashboardPort: 18789,

View file

@ -1,16 +1,16 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { describe, it, expect } from "vitest";
// Import from compiled dist/ so coverage is attributed correctly.
import { describe, expect, it } from "vitest";
// Import source directly so tests cannot pass against a stale build.
import {
compactText,
stripEndpointSuffix,
normalizeProviderBaseUrl,
isLoopbackHostname,
formatEnvAssignment,
isLoopbackHostname,
normalizeProviderBaseUrl,
parsePolicyPresetEnv,
} from "../../../dist/lib/core/url-utils";
stripEndpointSuffix,
} from "./url-utils";
describe("compactText", () => {
it("collapses whitespace", () => {

View file

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { execFileSync } from "node:child_process";
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { getVersion } from "../../../dist/lib/core/version";
import { join } from "node:path";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { getVersion } from "./version";
const repoRoot = join(import.meta.dirname, "..", "..", "..");

View file

@ -1,8 +1,8 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { describe, it, expect } from "vitest";
import { buildChain, buildControlUiUrls } from "../../../dist/lib/dashboard/contract.js";
import { describe, expect, it } from "vitest";
import { buildChain, buildControlUiUrls } from "./contract.js";
describe("buildChain", () => {
it("returns default loopback chain with no arguments", () => {

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import { describe, expect, it } from "vitest";
import { validateName } from "../runner";
import {
buildDeployEnvLines,
executeDeploy,
@ -9,8 +10,7 @@ import {
inferDeployProvider,
isBrevInstanceFailed,
isBrevInstanceReady,
} from "../../../dist/lib/deploy";
import { validateName } from "../../../dist/lib/runner";
} from "./index";
describe("inferDeployProvider", () => {
it("prefers an explicit provider override", () => {

View file

@ -3,7 +3,7 @@
import { describe, expect, it, vi } from "vitest";
import { runDebugCommandWithOptions } from "../../../dist/lib/diagnostics/debug-command";
import { runDebugCommandWithOptions } from "./debug-command";
describe("debug command", () => {
it("runs parsed debug options and falls back to the default sandbox", () => {

View file

@ -1,11 +1,11 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
// Import from compiled dist/ so coverage is attributed correctly.
// Import source directly so tests cannot pass against a stale build.
import {
buildDmesgRerunCommand,
createTarball,
@ -14,7 +14,7 @@ import {
isDmesgPermissionDeniedOutput,
isDmesgRestrictedForCurrentUser,
redact,
} from "../../../dist/lib/diagnostics/debug";
} from "./debug";
describe("redact", () => {
it("redacts NVIDIA_INFERENCE_API_KEY=value patterns", () => {

View file

@ -11,7 +11,7 @@ import {
parseVethGateway,
selectSandboxNamespace,
selectSandboxPod,
} from "../../../../dist/lib/domain/dns/setup-proxy.js";
} from "./setup-proxy.js";
describe("DNS setup proxy domain helpers", () => {
it("selects a sandbox pod using fixed-string style matching", () => {

View file

@ -5,10 +5,10 @@ import { createRequire } from "node:module";
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
type GatewayRuntimeModule = typeof import("../../dist/lib/gateway-runtime-action");
type GatewayRuntimeModule = typeof import("./gateway-runtime-action");
const requireDist = createRequire(import.meta.url);
const gatewayRuntimeModulePath = "../../dist/lib/gateway-runtime-action.js";
const gatewayRuntimeModulePath = "./gateway-runtime-action.js";
describe("gateway-runtime-action per-sandbox gateway routing", () => {
let gatewayRuntime: GatewayRuntimeModule;
@ -20,14 +20,14 @@ describe("gateway-runtime-action per-sandbox gateway routing", () => {
beforeEach(() => {
spies = [];
delete require.cache[requireDist.resolve(gatewayRuntimeModulePath)];
const openshellRuntime = requireDist("../../dist/lib/adapters/openshell/runtime.js");
const openshellRuntime = requireDist("./adapters/openshell/runtime.js");
captureSpy = vi.spyOn(openshellRuntime, "captureOpenshell");
runSpy = vi.spyOn(openshellRuntime, "runOpenshell");
spies.push(captureSpy, runSpy);
// The recovery path also pokes onboard.startGatewayForRecovery via lazy
// require(); stub it so the tests do not pull onboard's runtime in.
const onboard = requireDist("../../dist/lib/onboard.js");
const onboard = requireDist("./onboard.js");
startGatewaySpy = vi
.spyOn(onboard, "startGatewayForRecovery")
.mockResolvedValue(undefined as never);

View file

@ -7,7 +7,7 @@ import {
GatewayTokenCommandError,
parseGatewayTokenArgs,
runGatewayTokenCommand,
} from "../../dist/lib/gateway-token-command";
} from "./gateway-token-command";
function makeSinks() {
const out: string[] = [];

View file

@ -2,31 +2,17 @@
// SPDX-License-Identifier: Apache-2.0
import fs from "node:fs";
import { createRequire } from "node:module";
import os from "node:os";
import path from "node:path";
import { createRequire } from "node:module";
import { afterEach, describe, expect, it } from "vitest";
const require = createRequire(import.meta.url);
const DIST_AUTH = path.join(
import.meta.dirname,
"..",
"..",
"dist",
"lib",
"hermes-provider-auth.js",
);
const DIST_BROKER = path.join(
import.meta.dirname,
"..",
"..",
"dist",
"lib",
"hermes-tool-gateway-broker.js",
);
const SOURCE_AUTH = path.join(import.meta.dirname, "hermes-provider-auth.ts");
const SOURCE_BROKER = path.join(import.meta.dirname, "hermes-tool-gateway-broker.ts");
function clearDistModule(modulePath: string): void {
function clearSourceModule(modulePath: string): void {
try {
delete require.cache[require.resolve(modulePath)];
} catch {
@ -35,21 +21,21 @@ function clearDistModule(modulePath: string): void {
}
function loadAuth(): Record<string, any> {
clearDistModule(DIST_AUTH);
return require(DIST_AUTH);
clearSourceModule(SOURCE_AUTH);
return require(SOURCE_AUTH);
}
function loadAuthWithBrokerStub(brokerStub: Record<string, any>): Record<string, any> {
clearDistModule(DIST_AUTH);
clearDistModule(DIST_BROKER);
const broker = require(DIST_BROKER);
clearSourceModule(SOURCE_AUTH);
clearSourceModule(SOURCE_BROKER);
const broker = require(SOURCE_BROKER);
Object.assign(broker, brokerStub);
return require(DIST_AUTH);
return require(SOURCE_AUTH);
}
afterEach(() => {
clearDistModule(DIST_AUTH);
clearDistModule(DIST_BROKER);
clearSourceModule(SOURCE_AUTH);
clearSourceModule(SOURCE_BROKER);
});
describe("Hermes provider OpenShell credential handoff", () => {

View file

@ -11,7 +11,7 @@ import {
createBedrockRuntimeAdapterServer,
createOpenAiChatCompletion,
streamOpenAiChatCompletion,
} from "../../../dist/lib/inference/bedrock-runtime-adapter";
} from "./bedrock-runtime-adapter";
const servers: http.Server[] = [];

View file

@ -3,10 +3,7 @@
import { describe, expect, it } from "vitest";
import {
classifyCustomAnthropicEndpoint,
isBedrockRuntimeEndpoint,
} from "../../../dist/lib/inference/bedrock-runtime";
import { classifyCustomAnthropicEndpoint, isBedrockRuntimeEndpoint } from "./bedrock-runtime";
describe("Bedrock Runtime endpoint classification", () => {
it("detects standard Bedrock Runtime hosts", () => {

View file

@ -3,7 +3,7 @@
import { describe, expect, it } from "vitest";
// Import from compiled dist/ for correct coverage attribution.
// Import source directly so tests cannot pass against a stale build.
import {
CLOUD_MODEL_OPTIONS,
DEFAULT_HERMES_PROVIDER_MODEL,
@ -21,7 +21,7 @@ import {
planInferenceRouteReconcile,
sanitizeRouteValueForDisplay,
VLLM_LOCAL_CREDENTIAL_ENV,
} from "../../../dist/lib/inference/config";
} from "./config";
describe("inference selection config", () => {
it("exposes the curated cloud model picker options", () => {

View file

@ -3,16 +3,16 @@
import fs from "node:fs";
import { describe, it, expect } from "vitest";
import { describe, expect, it } from "vitest";
// Import from compiled dist/ for correct coverage attribution.
// Import source directly so tests cannot pass against a stale build.
import {
getRemoteProviderHealthEndpoint,
probeRemoteProviderHealth,
probeProviderHealth,
} from "../../../dist/lib/inference/health";
probeRemoteProviderHealth,
} from "./health";
import { BUILD_ENDPOINT_URL } from "../../../dist/lib/inference/provider-models";
import { BUILD_ENDPOINT_URL } from "./provider-models";
describe("inference health", () => {
describe("getRemoteProviderHealthEndpoint", () => {

View file

@ -21,7 +21,7 @@ import {
waitForLocalAdapterHealth,
writeLocalAdapterJsonFile,
writeLocalAdapterSecretFile,
} from "../../../dist/lib/inference/local-adapter-lifecycle";
} from "./local-adapter-lifecycle";
const tempDirs: string[] = [];
const servers: http.Server[] = [];

View file

@ -1,14 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { afterAll, afterEach, beforeAll, describe, it, expect } from "vitest";
import { execFileSync } from "node:child_process";
import { chmodSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
// Import from compiled dist/ for correct coverage attribution.
import { OLLAMA_MODEL_REGISTRY } from "../../../dist/lib/inference/ollama-model-registry";
// Import source directly so tests cannot pass against a stale build.
import { OLLAMA_MODEL_REGISTRY } from "./ollama-model-registry";
// Derive the "large enough to fit every registry entry" memory threshold
// from the registry itself so adding or resizing a model in the registry
@ -16,31 +15,32 @@ import { OLLAMA_MODEL_REGISTRY } from "../../../dist/lib/inference/ollama-model-
const LARGE_OLLAMA_FIT_MEMORY_MB = Math.max(
...OLLAMA_MODEL_REGISTRY.map((entry) => entry.requiredMemoryMB),
);
import {
CONTAINER_REACHABILITY_IMAGE,
DEFAULT_OLLAMA_MODEL,
LOCAL_INFERENCE_SANDBOX_HOST_URL_ENV,
QWEN3_6_OLLAMA_MODEL,
getOllamaContainerPort,
resetOllamaContainerPortCache,
getDefaultOllamaModel,
getBootstrapOllamaModelOptions,
getDefaultOllamaModel,
getLocalProviderBaseUrl,
getLocalProviderContainerReachabilityCheck,
getLocalProviderHealthCheck,
getLocalProviderHealthEndpoint,
getLocalProviderLabel,
getLocalProviderValidationBaseUrl,
getOllamaContainerPort,
getOllamaModelOptions,
getOllamaProbeCommand,
getOllamaWarmupCommand,
isOllamaRunnerCrash,
LOCAL_INFERENCE_SANDBOX_HOST_URL_ENV,
parseOllamaList,
parseOllamaTags,
probeLocalProviderHealth,
validateOllamaModel,
QWEN3_6_OLLAMA_MODEL,
resetOllamaContainerPortCache,
validateLocalProvider,
} from "../../../dist/lib/inference/local";
validateOllamaModel,
} from "./local";
describe("local inference helpers", () => {
const originalSandboxHostUrl = process.env[LOCAL_INFERENCE_SANDBOX_HOST_URL_ENV];
@ -185,37 +185,6 @@ describe("local inference helpers", () => {
expect(callCount).toBe(2);
});
it("rejects non-WSL Ollama when the backend and proxy ports collide", () => {
const output = execFileSync(
process.execPath,
[
"-e",
[
"const platform = require('./dist/lib/platform.js');",
"platform.isWsl = () => false;",
"const localInference = require('./dist/lib/inference/local.js');",
"const result = localInference.validateLocalProvider('ollama-local', () => '{\"models\":[]}');",
"process.stdout.write(JSON.stringify(result));",
].join(""),
],
{
cwd: path.resolve(__dirname, "../../.."),
encoding: "utf8",
env: {
...process.env,
NEMOCLAW_OLLAMA_PORT: "11435",
NEMOCLAW_OLLAMA_PROXY_PORT: "11435",
},
},
);
const result = JSON.parse(output);
expect(result.ok).toBe(false);
expect(result.message).toContain("NEMOCLAW_OLLAMA_PORT");
expect(result.message).toContain("NEMOCLAW_OLLAMA_PROXY_PORT");
expect(result.message).toContain("11435");
});
it("returns a clear error when ollama-local is unavailable", () => {
const result = validateLocalProvider("ollama-local", () => "");
expect(result.ok).toBe(false);
@ -679,7 +648,7 @@ describe("local inference helpers", () => {
});
it("filters installed-model selection by memory fit", async () => {
const { getDefaultOllamaModel: gdom } = await import("../../../dist/lib/inference/local");
const { getDefaultOllamaModel: gdom } = await import("./local");
// Even though nemotron-3-nano:30b is installed, it does not fit a host
// with only 12 GiB available — the selector must downgrade to a fitting
// installed model rather than blindly returning DEFAULT_OLLAMA_MODEL.
@ -690,7 +659,7 @@ describe("local inference helpers", () => {
});
it("resolveNonInteractiveOllamaModel respects unknown tags and downgrades known oversize ones", async () => {
const { resolveNonInteractiveOllamaModel } = await import("../../../dist/lib/inference/local");
const { resolveNonInteractiveOllamaModel } = await import("./local");
const messages: string[] = [];
const log = (m: string) => messages.push(m);
@ -730,7 +699,7 @@ describe("local inference helpers", () => {
});
it("resolveNonInteractiveOllamaModel surfaces the no-fit warning when even the smallest model exceeds available memory", async () => {
const { resolveNonInteractiveOllamaModel } = await import("../../../dist/lib/inference/local");
const { resolveNonInteractiveOllamaModel } = await import("./local");
const messages: string[] = [];
const log = (m: string) => messages.push(m);

View file

@ -10,8 +10,8 @@ import {
promptManualModelId,
promptRemoteModel,
promptVllmModel,
} from "../../../dist/lib/inference/model-prompts";
import { VLLM_MODELS, modelsForPlatform } from "../../../dist/lib/inference/vllm-models";
} from "./model-prompts";
import { modelsForPlatform, VLLM_MODELS } from "./vllm-models";
function promptSequence(responses: string[]) {
const queue = [...responses];

View file

@ -5,12 +5,12 @@ import { createRequire } from "module";
import type { Mock } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
// Import from compiled dist/ for coverage attribution.
import * as nim from "../../../dist/lib/inference/nim";
// Import source directly so tests cannot pass against a stale build.
import * as nim from "./nim";
const require = createRequire(import.meta.url);
const NIM_DIST_PATH = require.resolve("../../../dist/lib/inference/nim");
const RUNNER_PATH = require.resolve("../../../dist/lib/runner");
const NIM_DIST_PATH = require.resolve("./nim");
const RUNNER_PATH = require.resolve("../runner");
const fs = require("fs");
const NIM_API_KEY_ENV_KEYS = ["NGC_API_KEY", "NVIDIA_INFERENCE_API_KEY", "NVIDIA_API_KEY"];
function clearNimApiKeyEnv(): Array<[string, string | undefined]> {

View file

@ -7,7 +7,7 @@ import {
extractNousRecommendedModelOptions,
getHermesProviderModelOptions,
mergeModelOptions,
} from "../../../dist/lib/inference/nous-models";
} from "./nous-models";
describe("Nous recommended model helpers", () => {
it("prepends paid portal recommendations and keeps fallback models for the full list", () => {

View file

@ -12,7 +12,7 @@ import {
OLLAMA_DOWNLOAD_SIZE_FALLBACK_BYTES,
OLLAMA_MODEL_REGISTRY,
SMALLEST_OLLAMA_MODEL_TAG,
} from "../../../dist/lib/inference/ollama-model-registry";
} from "./ollama-model-registry";
describe("OLLAMA_MODEL_REGISTRY", () => {
it("is ordered largest-first by requiredMemoryMB", () => {

View file

@ -3,7 +3,7 @@
import { describe, expect, it } from "vitest";
import { validateOllamaModel } from "../../../dist/lib/inference/local";
import { validateOllamaModel } from "./local";
describe("Ollama probe timeout retry", () => {
it("retries with extended timeout on non-Spark hosts when first probe times out", () => {

View file

@ -10,7 +10,7 @@ import {
probeOllamaRuntimeModelStatus,
resetOllamaRuntimeContextWindowAutoState,
resolveOllamaRuntimeContextWindow,
} from "../../../dist/lib/inference/ollama-runtime-context";
} from "./ollama-runtime-context";
const getOllamaHost = () => "127.0.0.1";

View file

@ -8,7 +8,7 @@ import {
getRunningOllamaDaemonVersion,
isOllamaVersionAtLeast,
MIN_OLLAMA_VERSION,
} from "../../../dist/lib/inference/ollama-version";
} from "./ollama-version";
describe("Ollama version detection", () => {
it("parses 'ollama version is X.Y.Z' output", () => {

View file

@ -9,7 +9,7 @@ import {
formatModelSize,
getOllamaModelSize,
probeRegistrySize,
} from "../../../../dist/lib/inference/ollama/model-size";
} from "./model-size";
const MANIFEST = JSON.stringify({
layers: [{ size: 1_000_000_000 }, { size: 200_000_000 }, { size: 25_000 }],

View file

@ -5,9 +5,9 @@ import { createRequire } from "node:module";
import { afterEach, describe, expect, it } from "vitest";
const require = createRequire(import.meta.url);
const PROXY_DIST = require.resolve("../../../../dist/lib/inference/ollama/proxy");
const LOCAL_DIST = require.resolve("../../../../dist/lib/inference/local");
const CREDS_DIST = require.resolve("../../../../dist/lib/credentials/store");
const PROXY_DIST = require.resolve("./proxy");
const LOCAL_DIST = require.resolve("../local");
const CREDS_DIST = require.resolve("../../credentials/store");
interface MockSetup {
installed: string[];
@ -15,7 +15,7 @@ interface MockSetup {
}
function loadProxyWithMocks(setup: MockSetup): {
proxy: typeof import("../../../../dist/lib/inference/ollama/proxy");
proxy: typeof import("./proxy");
promptArgs: string[];
restore: () => void;
} {

View file

@ -5,8 +5,8 @@ import { createRequire } from "node:module";
import { describe, expect, it, vi } from "vitest";
const require = createRequire(import.meta.url);
const WINDOWS_DIST_PATH = require.resolve("../../../../dist/lib/inference/ollama/windows");
const RUNNER_PATH = require.resolve("../../../../dist/lib/runner");
const WINDOWS_DIST_PATH = require.resolve("./windows");
const RUNNER_PATH = require.resolve("../../runner");
const childProcess = require("node:child_process");
function commandText(command: string | string[]): string {

View file

@ -1,18 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { describe, expect, it } from "vitest";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
const {
isHijackedDockerInternalUrl,
} = require("../../../dist/lib/inference/onboard-host-docker-internal");
const {
isSandboxInternalUrl,
probeOpenAiLikeEndpoint,
} = require("../../../dist/lib/inference/onboard-probes");
const { isHijackedDockerInternalUrl } = require("./onboard-host-docker-internal");
const { isSandboxInternalUrl, probeOpenAiLikeEndpoint } = require("./onboard-probes");
describe("host.docker.internal onboarding inference policy", () => {
it("does not treat host.docker.internal as a usable sandbox URL", () => {

View file

@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { spawnSync } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
@ -19,7 +18,7 @@ const {
isSandboxInternalUrl,
probeOpenAiLikeEndpoint,
RETRIABLE_HTTP_PROBE_STATUSES,
} = require("../../../dist/lib/inference/onboard-probes");
} = require("./onboard-probes");
describe("OpenAI-compatible inference probe response parsing", () => {
it("detects tool-calling responses payloads conservatively", () => {
@ -739,80 +738,6 @@ exit 0
}
});
it("retries strict tool-call validation after the parent curl process times out", () => {
const repoRoot = path.join(import.meta.dirname, "../../..");
const onboardProbePath = JSON.stringify(
path.join(repoRoot, "dist", "lib", "inference", "onboard-probes.js"),
);
const httpProbePath = JSON.stringify(
path.join(repoRoot, "dist", "lib", "adapters", "http", "probe.js"),
);
const script = `
const httpProbe = require(${httpProbePath});
let calls = 0;
const timeoutMs = [];
httpProbe.runCurlProbe = (_args, opts = {}) => {
calls += 1;
timeoutMs.push(opts.timeoutMs ?? null);
if (calls === 1) {
return {
ok: false,
httpStatus: 0,
curlStatus: -110,
body: "",
stderr: "spawnSync curl ETIMEDOUT",
message: "curl failed (exit -110): spawnSync curl ETIMEDOUT",
};
}
return {
ok: true,
httpStatus: 200,
curlStatus: 0,
body: JSON.stringify({
choices: [
{
message: {
tool_calls: [
{
type: "function",
function: {
name: "sessions_send",
arguments: { message: "hello" },
},
},
],
},
},
],
}),
stderr: "",
message: "HTTP 200",
};
};
const probes = require(${onboardProbePath});
const result = probes.probeOpenAiLikeEndpoint(
"https://api.example.com/v1",
"test-model",
null,
{ skipResponsesProbe: true, requireChatCompletionsToolCalling: true },
);
process.stdout.write(JSON.stringify({ result, calls, timeoutMs }));
`;
const run = spawnSync(process.execPath, ["-e", script], {
cwd: repoRoot,
encoding: "utf8",
});
expect(run.status).toBe(0);
const payload = JSON.parse(run.stdout);
expect(payload.result).toMatchObject({ ok: true, api: "openai-completions" });
expect(payload.calls).toBe(2);
expect(payload.timeoutMs).toHaveLength(2);
expect(payload.timeoutMs[0]).toBeGreaterThan(0);
expect(payload.timeoutMs[1]).toBeGreaterThan(payload.timeoutMs[0]);
});
it("keeps retrying when initial timeout is followed by a transient 502", () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-timeout-502-probe-"));
const fakeBin = path.join(tmpDir, "bin");

View file

@ -11,7 +11,7 @@ import {
validateAnthropicModel,
validateNvidiaEndpointModel,
validateOpenAiLikeModel,
} from "../../../dist/lib/inference/provider-models";
} from "./provider-models";
describe("provider model helpers", () => {
it("fetches NVIDIA endpoint model ids", () => {

View file

@ -4,16 +4,16 @@
import { describe, expect, it } from "vitest";
import {
DEFAULT_VLLM_MODEL,
VLLM_EXTRA_ARGS_ENV,
VLLM_MODELS,
assertGatedModelAccess,
buildVllmServeCommand,
DEFAULT_VLLM_MODEL,
modelsForPlatform,
parseVllmExtraServeArgs,
preflightVllmModelEnv,
selectVllmModelFromEnv,
} from "../../../dist/lib/inference/vllm-models";
VLLM_EXTRA_ARGS_ENV,
VLLM_MODELS,
} from "./vllm-models";
describe("vllm model registry", () => {
it("returns null when NEMOCLAW_VLLM_MODEL is unset so the caller can fall back to the profile default", () => {

View file

@ -3,7 +3,7 @@
import { describe, expect, it } from "vitest";
import { applyVllmRuntimeContextWindow } from "../../../dist/lib/inference/vllm-runtime-context";
import { applyVllmRuntimeContextWindow } from "./vllm-runtime-context";
function applyContextWindow(
modelsResponse: unknown,

View file

@ -7,7 +7,7 @@ import {
mintAgentKeyWithAccessToken,
pollForToken,
refreshAccessTokenWithRefreshToken,
} from "../../dist/lib/oauth-device-code";
} from "./oauth-device-code";
describe("pollForToken", () => {
it("rejects successful token responses missing an access token", async () => {

View file

@ -3,10 +3,10 @@
import assert from "node:assert/strict";
import { afterEach, beforeEach, describe, it, vi } from "vitest";
import { getAgentChoices, loadAgent } from "../../../dist/lib/agent/defs";
import { resolveAgent } from "../../../dist/lib/agent/onboard";
import { createSelectOnboardAgent } from "../../../dist/lib/onboard/agent-selection";
import { selectFromNumberedMenuOrExit } from "../../../dist/lib/onboard/prompt-helpers";
import { getAgentChoices, loadAgent } from "../agent/defs";
import { resolveAgent } from "../agent/onboard";
import { createSelectOnboardAgent } from "./agent-selection";
import { selectFromNumberedMenuOrExit } from "./prompt-helpers";
// Exercises the real agent registry (agents/openclaw + agents/hermes) so the
// red->green transition reflects genuine wizard behavior rather than a mock.

View file

@ -3,8 +3,8 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { selectBedrockRuntimeCustomAnthropic } from "../../../dist/lib/onboard/bedrock-runtime";
import { BACK_TO_SELECTION } from "../../../dist/lib/onboard/credential-navigation";
import { selectBedrockRuntimeCustomAnthropic } from "./bedrock-runtime";
import { BACK_TO_SELECTION } from "./credential-navigation";
const BEDROCK_URL = "https://bedrock-runtime.us-east-1.amazonaws.com";

View file

@ -2,12 +2,11 @@
// SPDX-License-Identifier: Apache-2.0
import { afterEach, describe, expect, it, vi } from "vitest";
import { setOnboardBrandingAgent } from "./branding";
import {
printContainerDnsRemediation,
printDockerBridgeContainerStartFailure,
} from "../../../dist/lib/onboard/bridge-dns-preflight";
import { setOnboardBrandingAgent } from "../../../dist/lib/onboard/branding";
} from "./bridge-dns-preflight";
describe("printDockerBridgeContainerStartFailure", () => {
const savedInvokedAs = process.env.NEMOCLAW_INVOKED_AS;

View file

@ -6,9 +6,8 @@ import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { CUSTOM_BUILD_CONTEXT_WARN_BYTES } from "../../../dist/lib/onboard/custom-build-context";
import { stageCreateSandboxBuildContext } from "../../../dist/lib/onboard/build-context-stage";
import { stageCreateSandboxBuildContext } from "./build-context-stage";
import { CUSTOM_BUILD_CONTEXT_WARN_BYTES } from "./custom-build-context";
const tmpDirs: string[] = [];

View file

@ -3,7 +3,7 @@
import { afterEach, describe, expect, it } from "vitest";
import { hydrateCredentialEnv } from "../../../dist/lib/onboard/credential-env";
import { hydrateCredentialEnv } from "./credential-env";
const ORIGINAL_TELEGRAM_TOKEN = process.env.TELEGRAM_BOT_TOKEN;

View file

@ -7,7 +7,7 @@ import {
BACK_TO_SELECTION,
returningToProviderSelection,
shouldReturnToProviderSelection,
} from "../../../dist/lib/onboard/credential-navigation";
} from "./credential-navigation";
describe("credential prompt navigation helpers", () => {
it("treats both the shared back sentinel and credential back intents as provider-selection navigation", () => {

View file

@ -11,7 +11,7 @@ import {
getRegistryOccupiedDashboardPorts,
preflightDashboardPortRangeAvailability,
resolveCreateSandboxDashboardPort,
} from "../../../dist/lib/onboard/dashboard-port";
} from "./dashboard-port";
describe("findDashboardForwardOwner", () => {
it("parses openshell forward list column format (#2169)", () => {

Some files were not shown because too many files have changed in this diff Show more