Skip to content

Dead plugin modules (4 TypeScript files ship in dist/ but are unreachable at runtime) #1002

Closed
deepujain wants to merge 1 commit intoNVIDIA:mainfrom
deepujain:fix/977-wire-runner-as-bin
Closed

Dead plugin modules (4 TypeScript files ship in dist/ but are unreachable at runtime) #1002
deepujain wants to merge 1 commit intoNVIDIA:mainfrom
deepujain:fix/977-wire-runner-as-bin

Conversation

@deepujain
Copy link
Copy Markdown
Contributor

@deepujain deepujain commented Mar 26, 2026

Summary

runner.ts, ssrf.ts, snapshot.ts, and migration-state.ts all compile and ship in the npm package but are never reachable at runtime (#977). This PR fixes the two modules that have a natural CLI entry point and documents the other two for a follow-up migration command.

  • runner.ts is the TypeScript port of the Python blueprint runner. It has a main() function designed for CLI use but was never exposed as a bin. This PR wires it in.
  • ssrf.ts is now reachable transitively (imported by runner.ts).
  • snapshot.ts and migration-state.ts implement the nemoclaw migrate flow. They are tested and ready, but need a future CLI subcommand to hook into; TODO(#977) comments added to mark this.

Changes

  • nemoclaw/src/blueprint/runner.ts — add #!/usr/bin/env node shebang and ESM main-module guard (process.argv[1] === fileURLToPath(import.meta.url)) so the compiled binary auto-invokes main() when run directly but is still importable as a module (tests unaffected)
  • nemoclaw/package.json — add "bin": { "nemoclaw-runner": "./dist/blueprint/runner.js" } so the binary is installed when the package is installed
  • nemoclaw/src/blueprint/snapshot.ts — add TODO(#977) comment pointing to the pending nemoclaw migrate entry point
  • nemoclaw/src/commands/migration-state.ts — same

Testing

cd nemoclaw && npm install --ignore-scripts && npm run build && cd ..
npm test

All 542 tests pass (2 pre-existing skips). Build is clean.

The shebang is preserved in compiled output (dist/blueprint/runner.js) and the main-module guard ensures main() runs when invoked as a CLI binary but not when imported by tests or other modules.

Fixes #977

Signed-off-by: Deepak Jain deepujain@gmail.com

Summary by CodeRabbit

  • New Features

    • Added nemoclaw-runner CLI command, enabling direct command-line execution of the nemoclaw runner.
  • Chores

    • Added documentation comments clarifying the purpose of migration state and snapshot functions for future CLI integration.

…to snapshot and migration-state

runner.ts, ssrf.ts, snapshot.ts, and migration-state.ts shipped in dist/
but were unreachable at runtime (issue NVIDIA#977). This PR addresses the first
two by wiring runner.ts as a CLI binary:

- Add shebang and ESM main-module guard to runner.ts so it runs correctly
  when invoked directly (no-op when imported as a module in tests)
- Register nemoclaw-runner bin in package.json pointing to dist/blueprint/runner.js
- Add TODO(NVIDIA#977) comments to snapshot.ts and migration-state.ts noting they
  are ready and await a `nemoclaw migrate` command entry point

ssrf.ts is now reachable transitively via the runner binary.

Fixes NVIDIA#977

Signed-off-by: Deepak Jain <deepujain@gmail.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

Added a CLI entry point for the nemoclaw-runner command via package.json's bin field, enabled direct execution of runner.ts as a standalone CLI tool with auto-invocation and error handling, and added TODO documentation to migration-related modules indicating pending integration into the migrate command.

Changes

Cohort / File(s) Summary
CLI Entrypoint Configuration
nemoclaw/package.json, nemoclaw/src/blueprint/runner.ts
Added bin.nemoclaw-runner field mapping to compiled runner output; enabled runner.ts as executable CLI with shebang, entry point detection via process.argv[1], auto-invocation of main(), and error handling to stderr.
Integration Documentation
nemoclaw/src/blueprint/snapshot.ts, nemoclaw/src/commands/migration-state.ts
Added module-level TODO comments indicating these functions require wiring into the nemoclaw migrate CLI command for future implementation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A runner's born, now runs on its own,
With shebang and argv, it claims the throne,
Snapshots await their moment to shine,
TODOs mark the path, a roadmap so fine! 🏃‍♂️✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The PR title describes dead plugin modules, but the changes actually implement Option 3 (keep with TODO notes) rather than addressing the core issue of unreachable modules. Clarify whether the title should reflect the actual implementation choice (adding TODO notes) or consider a more specific title like 'Mark dead plugin modules with TODOs pending integration #977'.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR implements Option 3 from #977 by marking modules with TODO comments and wiring runner.ts as a CLI binary, but does not fully resolve the core issue of making all modules runtime-reachable.
Out of Scope Changes check ✅ Passed All changes directly address #977: adding CLI binary support to runner.ts, marking snapshot.ts and migration-state.ts with TODO comments, and documenting the migration flow.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
nemoclaw/src/blueprint/runner.ts (3)

292-302: ⚠️ Potential issue | 🟠 Major

Fail apply when provider setup commands fail.

Lines 292-302 discard the exit codes from openshell provider create and openshell inference set. If either command fails, the code still writes plan.json and emits “Apply complete”, which records a partially configured run as healthy.

🔧 Proposed fix
-  await execa(providerArgs[0], providerArgs.slice(1), {
+  const providerResult = await execa(providerArgs[0], providerArgs.slice(1), {
     reject: false,
     stdout: "pipe",
     stderr: "pipe",
     env: { ...process.env, ...credEnv },
   });
+  if (providerResult.exitCode !== 0) {
+    throw new Error(`Failed to create provider: ${providerResult.stderr || providerResult.stdout}`);
+  }
 
   progress(70, "Setting inference route");
-  await runCmd(["openshell", "inference", "set", "--provider", providerName, "--model", model], {
-    reject: false,
-  });
+  const routeResult = await runCmd(
+    ["openshell", "inference", "set", "--provider", providerName, "--model", model],
+    { reject: false },
+  );
+  if (routeResult.exitCode !== 0) {
+    throw new Error(`Failed to set inference route: ${routeResult.stderr || routeResult.stdout}`);
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nemoclaw/src/blueprint/runner.ts` around lines 292 - 302, The provider setup
commands currently ignore failures; capture and check the exit status/return
value from the execa call that runs providerArgs and from
runCmd(["openshell","inference","set",...]) and if either returns a non-zero
exit code (or indicates failure) throw an error or return a rejected promise to
abort apply before writing plan.json or emitting the "Apply complete" message;
update the section around the execa(providerArgs...) call and the subsequent
runCmd invocation (referencing providerArgs, providerName, model, execa, and
runCmd) to inspect their results, log the failure via progress/processLogger,
and propagate an error so the apply fails on command failure.

429-436: ⚠️ Potential issue | 🟠 Major

Load the blueprint only for actions that actually use it.

Line 429 eagerly loads blueprint.yaml before the action switch. Now that this file is executable as a real CLI, status and rollback fail outside a blueprint directory even though neither path needs the blueprint.

🔧 Proposed fix
-  const blueprint = loadBlueprint();
-
   switch (action) {
     case "plan":
-      await actionPlan(profile, blueprint, { dryRun, endpointUrl });
+      await actionPlan(profile, loadBlueprint(), { dryRun, endpointUrl });
       break;
     case "apply":
-      await actionApply(profile, blueprint, { planPath, endpointUrl });
+      await actionApply(profile, loadBlueprint(), { planPath, endpointUrl });
       break;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nemoclaw/src/blueprint/runner.ts` around lines 429 - 436, The code currently
calls loadBlueprint() unconditionally (const blueprint = loadBlueprint()) before
the action switch, causing commands like status and rollback to fail outside a
blueprint directory; move the call into only the cases that require it (e.g.,
inside the "plan" and "apply" branches) so loadBlueprint() is invoked lazily
when needed and pass the loaded blueprint to actionPlan and actionApply; ensure
any references to the blueprint variable are updated to use the locally loaded
value in those case blocks.

17-17: ⚠️ Potential issue | 🟠 Major

Use import.meta.main for symlink-safe CLI entry detection.

npm installs bin entries as symlinks on Unix-like systems, and Node.js resolves the main module's real path by default. When nemoclaw-runner is launched through the installed wrapper, process.argv[1] points to the symlink path while fileURLToPath(import.meta.url) resolves to the real file path, causing the raw equality check on line 455 to fail and preventing main() from executing.

For Node.js v22+ (where import.meta.main was backported), use:

if (import.meta.main) {
  void main().catch((e: unknown) => {
    process.stderr.write((e instanceof Error ? e.message : String(e)) + "\n");
    process.exit(1);
  });
}

This is the official ESM equivalent to CommonJS require.main === module and works reliably regardless of symlinks. For older Node.js versions, resolve both paths before comparing:

Fallback for older Node.js
-import { mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
+import { mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync } from "node:fs";
@@
 import { fileURLToPath } from "node:url";
-if (process.argv[1] === fileURLToPath(import.meta.url)) {
-  main().catch((e: unknown) => {
+let isDirectExecution = false;
+if (process.argv[1]) {
+  try {
+    isDirectExecution =
+      realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
+  } catch {
+    isDirectExecution = false;
+  }
+}
+
+if (isDirectExecution) {
+  void main().catch((e: unknown) => {
     process.stderr.write((e instanceof Error ? e.message : String(e)) + "\n");
     process.exit(1);
   });
 }

Also applies to: 454-460

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nemoclaw/src/blueprint/runner.ts` at line 17, Replace the fragile raw path
equality check that compares process.argv[1] and fileURLToPath(import.meta.url)
to decide whether to run main(), and instead use the ESM entry detection
import.meta.main to invoke main(); update the code around the main() bootstrap
(the block currently using process.argv[1] and fileURLToPath(import.meta.url) in
runner.ts) to call main() inside an import.meta.main conditional and attach a
.catch that writes the error message to stderr and exits with non-zero status,
and for compatibility with older Node versions consider adding a fallback that
resolves both paths before comparing if import.meta.main is not available.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@nemoclaw/src/blueprint/runner.ts`:
- Around line 292-302: The provider setup commands currently ignore failures;
capture and check the exit status/return value from the execa call that runs
providerArgs and from runCmd(["openshell","inference","set",...]) and if either
returns a non-zero exit code (or indicates failure) throw an error or return a
rejected promise to abort apply before writing plan.json or emitting the "Apply
complete" message; update the section around the execa(providerArgs...) call and
the subsequent runCmd invocation (referencing providerArgs, providerName, model,
execa, and runCmd) to inspect their results, log the failure via
progress/processLogger, and propagate an error so the apply fails on command
failure.
- Around line 429-436: The code currently calls loadBlueprint() unconditionally
(const blueprint = loadBlueprint()) before the action switch, causing commands
like status and rollback to fail outside a blueprint directory; move the call
into only the cases that require it (e.g., inside the "plan" and "apply"
branches) so loadBlueprint() is invoked lazily when needed and pass the loaded
blueprint to actionPlan and actionApply; ensure any references to the blueprint
variable are updated to use the locally loaded value in those case blocks.
- Line 17: Replace the fragile raw path equality check that compares
process.argv[1] and fileURLToPath(import.meta.url) to decide whether to run
main(), and instead use the ESM entry detection import.meta.main to invoke
main(); update the code around the main() bootstrap (the block currently using
process.argv[1] and fileURLToPath(import.meta.url) in runner.ts) to call main()
inside an import.meta.main conditional and attach a .catch that writes the error
message to stderr and exits with non-zero status, and for compatibility with
older Node versions consider adding a fallback that resolves both paths before
comparing if import.meta.main is not available.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f789304b-e16f-4dc9-80fc-1f23eb663931

📥 Commits

Reviewing files that changed from the base of the PR and between 5c269c1 and bfec5b3.

📒 Files selected for processing (4)
  • nemoclaw/package.json
  • nemoclaw/src/blueprint/runner.ts
  • nemoclaw/src/blueprint/snapshot.ts
  • nemoclaw/src/commands/migration-state.ts

@cv
Copy link
Copy Markdown
Contributor

cv commented Mar 27, 2026

#982 beat you to it, @deepujain - ask yer clanker to read through the linked PRs and existing issues before firing off a new one, please! ;)

@cv cv closed this Mar 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dead plugin modules: 4 TypeScript files ship in dist/ but are unreachable at runtime

2 participants