diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 837d494429..634f46aeed 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -243,7 +243,7 @@ reviews: - path: "src/lib/onboard/preflight.ts" instructions: *e2e-overlayfs - - path: "src/lib/deploy.ts" + - path: "src/lib/deploy/**" instructions: | This file contains deployment lifecycle logic (start/stop, cloudflared tunnel, uninstall). diff --git a/bin/lib/tiers.js b/bin/lib/tiers.js index 1f7a58e485..e0ccac7dc0 100644 --- a/bin/lib/tiers.js +++ b/bin/lib/tiers.js @@ -1,10 +1,10 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 // -// Thin re-export shim — the implementation lives in src/lib/tiers.ts, -// compiled to dist/lib/tiers.js. +// Thin re-export shim — the implementation lives in src/lib/policy/tiers.ts, +// compiled to dist/lib/policy/tiers.js. -const mod = require("../../dist/lib/tiers"); +const mod = require("../../dist/lib/policy/tiers"); module.exports = { TIERS_FILE: mod.TIERS_FILE, listTiers: mod.listTiers, diff --git a/nemoclaw-blueprint/private-networks.yaml b/nemoclaw-blueprint/private-networks.yaml index 35179e07d2..3081d0eab7 100644 --- a/nemoclaw-blueprint/private-networks.yaml +++ b/nemoclaw-blueprint/private-networks.yaml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # # IPv4 and IPv6 networks that SSRF validation must reject. Consumed by: -# - src/lib/sandbox-config.ts (CLI `config set` literal-IP gate) +# - src/lib/sandbox/config.ts (CLI `config set` literal-IP gate) # - nemoclaw/src/blueprint/ssrf.ts (plugin endpoint URL validator) # Both consumers build a node:net BlockList from this data at module load. # diff --git a/scripts/benchmark-sandbox-image-build.js b/scripts/benchmark-sandbox-image-build.js index 332af919bf..20f5cfff8a 100755 --- a/scripts/benchmark-sandbox-image-build.js +++ b/scripts/benchmark-sandbox-image-build.js @@ -10,7 +10,7 @@ const { collectBuildContextStats, stageLegacySandboxBuildContext, stageOptimizedSandboxBuildContext, -} = require("../dist/lib/sandbox-build-context"); +} = require("../dist/lib/sandbox/build-context"); function parseArgs(argv) { const args = { diff --git a/scripts/check-legacy-migrated-paths.ts b/scripts/check-legacy-migrated-paths.ts index 8a12edcfd9..64eac01bf7 100644 --- a/scripts/check-legacy-migrated-paths.ts +++ b/scripts/check-legacy-migrated-paths.ts @@ -36,11 +36,11 @@ const REMOVED_SHIM_MOVES: Record = { "bin/lib/registry.js": "src/lib/state/registry.ts", "bin/lib/resolve-openshell.js": "src/lib/adapters/openshell/resolve.ts", "bin/lib/runtime-recovery.js": "src/lib/runtime-recovery.ts", - "bin/lib/sandbox-build-context.js": "src/lib/sandbox-build-context.ts", - "bin/lib/services.js": "src/lib/services.ts", + "bin/lib/sandbox-build-context.js": "src/lib/sandbox/build-context.ts", + "bin/lib/services.js": "src/lib/tunnel/services.ts", "bin/lib/version.js": "src/lib/core/version.ts", "bin/lib/onboard.js": "src/lib/onboard.ts", - "bin/lib/policies.js": "src/lib/policies.ts", + "bin/lib/policies.js": "src/lib/policy/index.ts", "bin/lib/runner.js": "src/lib/runner.ts", }; diff --git a/scripts/dev-tier-selector.js b/scripts/dev-tier-selector.js index 49aa688429..543d821749 100644 --- a/scripts/dev-tier-selector.js +++ b/scripts/dev-tier-selector.js @@ -55,7 +55,7 @@ const onboard = /** @type {{ * selectTierPresetsAndAccess: (tierName: string, allPresets: unknown[]) => Promise; * }} */ (require("../dist/lib/onboard.js")); const { selectPolicyTier, selectTierPresetsAndAccess } = onboard; -const policies = require("../dist/lib/policies.js"); +const policies = require("../dist/lib/policy/index.js"); (async () => { const tier = await selectPolicyTier(); diff --git a/scripts/ts-migration-assist.ts b/scripts/ts-migration-assist.ts index 847913ad4c..ab223c6453 100644 --- a/scripts/ts-migration-assist.ts +++ b/scripts/ts-migration-assist.ts @@ -41,20 +41,20 @@ const SPECIAL_REWRITES: Record> = { ['require("./lib/credentials")', 'require("../bin/lib/credentials")'], ['require("./lib/registry")', 'require("../bin/lib/registry")'], ['require("./lib/nim")', 'require("../bin/lib/nim")'], - ['require("./lib/policies")', 'require("../bin/lib/policies")'], + ['require("./lib/policy")', 'require("../bin/lib/policies")'], ['require("./lib/inference/config")', 'require("../bin/lib/inference-config")'], ['require("./lib/version")', 'require("../bin/lib/version")'], ['require("./lib/state/onboard-session")', 'require("../bin/lib/onboard-session")'], ['require("./lib/runtime-recovery")', 'require("../bin/lib/runtime-recovery")'], ['require("./lib/onboard/usage-notice")', 'require("../bin/lib/usage-notice")'], - ['require("./lib/services")', 'require("../bin/lib/services")'], + ['require("./lib/tunnel/services")', 'require("../bin/lib/services")'], ['require("./lib/debug")', 'require("./lib/diagnostics/debug")'], ['require("./lib/debug-command")', 'require("./lib/diagnostics/debug-command")'], ['require("../dist/lib/debug-command")', 'require("./lib/diagnostics/debug-command")'], ['require("../dist/lib/openshell")', 'require("./lib/openshell")'], - ['require("../dist/lib/inventory-commands")', 'require("./lib/inventory-commands")'], + ['require("./lib/inventory")', 'require("./lib/inventory-commands")'], ['require("../dist/lib/deploy")', 'require("./lib/deploy")'], - ['require("../dist/lib/services-command")', 'require("./lib/services-command")'], + ['require("./lib/tunnel/service-command")', 'require("./lib/services-command")'], ['require("../dist/lib/uninstall-command")', 'require("./lib/uninstall-command")'], ], }; diff --git a/scripts/ts-migration/move-map.json b/scripts/ts-migration/move-map.json index 15f7191619..fd33cff84a 100644 --- a/scripts/ts-migration/move-map.json +++ b/scripts/ts-migration/move-map.json @@ -1,9 +1,9 @@ { "runtimeMoves": { "bin/lib/platform.js": "src/lib/platform.ts", - "bin/lib/sandbox-build-context.js": "src/lib/sandbox-build-context.ts", + "bin/lib/sandbox-build-context.js": "src/lib/sandbox/build-context.ts", "bin/lib/runner.js": "src/lib/runner.ts", - "bin/lib/policies.js": "src/lib/policies.ts", + "bin/lib/policies.js": "src/lib/policy/index.ts", "bin/lib/onboard.js": "src/lib/onboard.ts", "bin/nemoclaw.js": "src/nemoclaw.ts" } diff --git a/src/commands/README.md b/src/commands/README.md new file mode 100644 index 0000000000..8cfa828961 --- /dev/null +++ b/src/commands/README.md @@ -0,0 +1,21 @@ + + + +# `src/commands` + +This tree is the oclif discovery surface for the packaged `nemoclaw` CLI. +Each file is intentionally thin: it exports a command class from `src/lib/commands/**` +and attaches NemoClaw's public display metadata. + +```text +src/commands/.ts + -> import command implementation from src/lib/commands/** + -> wrap with src/lib/cli/command-display.ts metadata +``` + +Keep behavior out of this tree. Product behavior belongs in `src/lib/actions/**`, pure +planning and classification belongs in `src/lib/domain/**`, and host/runtime boundaries +belong in `src/lib/adapters/**`. + +Hidden `nemoclaw internal ...` entrypoints live under `src/commands/internal/**`; see +`src/commands/internal/README.md` for their narrower compatibility contract. diff --git a/src/commands/sandbox/config/set.ts b/src/commands/sandbox/config/set.ts index c53fe4b151..01ec6d0330 100644 --- a/src/commands/sandbox/config/set.ts +++ b/src/commands/sandbox/config/set.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import Command from "../../../lib/sandbox-config-set-cli-command"; +import Command from "../../../lib/commands/sandbox/config/set"; import { withCommandDisplay } from "../../../lib/cli/command-display"; export default withCommandDisplay(Command, [ diff --git a/src/lib/README.md b/src/lib/README.md index b7e9a4ece1..d08e8ce4b1 100644 --- a/src/lib/README.md +++ b/src/lib/README.md @@ -36,17 +36,18 @@ src/lib/dashboard/** dashboard contract, health, and recovery helpers src/lib/deploy/** deploy/build-image support that is not yet action-shaped src/lib/diagnostics/** debug collection and diagnostic report helpers src/lib/inference/** inference config, health probes, local runtime helpers +src/lib/inventory/** list/status inventory shaping and presentation models src/lib/messaging/** channel/messaging policy and message filtering helpers src/lib/onboard/** onboarding support modules around the large legacy flow -src/lib/policy/** policy preset loading and application support +src/lib/policy/** policy preset loading, tier selection, and application support src/lib/runtime/** sandbox/runtime recovery helpers -src/lib/sandbox/** sandbox config, build, stream, and version support +src/lib/sandbox/** sandbox config, build, stream, channel, and version support src/lib/security/** redaction, secret patterns, and credential filtering src/lib/shields/** shields orchestration, timers, and audit helpers src/lib/tunnel/** local service/tunnel command support ``` -Prefer small mechanical PRs that move one cluster at a time. High-import legacy files such as `onboard.ts`, `runner.ts`, `policies.ts`, `nim.ts`, and `services.ts` should either move late or keep temporary compatibility re-export files at their old paths. +Prefer small mechanical PRs that move one cluster at a time. High-import legacy files such as `onboard.ts`, `runner.ts`, and any remaining large top-level modules should either move late or keep temporary compatibility re-export files at their old paths. ## Suggested migration sequence diff --git a/src/lib/actions/inference-set.test.ts b/src/lib/actions/inference-set.test.ts index 1cc39e5ff4..8cd4e16d5a 100644 --- a/src/lib/actions/inference-set.test.ts +++ b/src/lib/actions/inference-set.test.ts @@ -4,7 +4,7 @@ import { describe, expect, it, vi } from "vitest"; import type { ConfigObject } from "../security/credential-filter"; -import type { AgentConfigTarget } from "../sandbox-config"; +import type { AgentConfigTarget } from "../sandbox/config"; import type { Session } from "../state/onboard-session"; import type { SandboxEntry } from "../state/registry"; @@ -16,7 +16,7 @@ vi.mock("../inference/local", () => ({ DEFAULT_OLLAMA_MODEL: "llama3.1", })); -vi.mock("../sandbox-config", () => ({ +vi.mock("../sandbox/config", () => ({ readSandboxConfig: vi.fn(), recomputeSandboxConfigHash: vi.fn(), resolveAgentConfig: vi.fn(), diff --git a/src/lib/actions/inference-set.ts b/src/lib/actions/inference-set.ts index 3340848791..f84a9ff187 100644 --- a/src/lib/actions/inference-set.ts +++ b/src/lib/actions/inference-set.ts @@ -17,7 +17,7 @@ import { resolveAgentConfig, type AgentConfigTarget, writeSandboxConfig, -} from "../sandbox-config"; +} from "../sandbox/config"; import { appendAuditEntry } from "../shields/audit"; import * as onboardSession from "../state/onboard-session"; import * as registry from "../state/registry"; diff --git a/src/lib/actions/sandbox/connect.ts b/src/lib/actions/sandbox/connect.ts index 193047d511..04ce645a7e 100644 --- a/src/lib/actions/sandbox/connect.ts +++ b/src/lib/actions/sandbox/connect.ts @@ -27,7 +27,7 @@ import { getActiveSandboxSessions, } from "../../state/sandbox-session"; import { checkAndRecoverSandboxProcesses } from "./process-recovery"; -import * as sandboxVersion from "../../sandbox-version"; +import * as sandboxVersion from "../../sandbox/version"; import { D, G, R, YW } from "../../cli/terminal-style"; import { resolveOpenshell } from "../../adapters/openshell/resolve"; diff --git a/src/lib/actions/sandbox/destroy.ts b/src/lib/actions/sandbox/destroy.ts index 82fa747ed8..d359b87df2 100644 --- a/src/lib/actions/sandbox/destroy.ts +++ b/src/lib/actions/sandbox/destroy.ts @@ -196,7 +196,7 @@ export function cleanupSandboxServices( const stopAll = deps.stopAll ?? ((opts: { sandboxName: string }) => { - const services = require("../../services") as { + const services = require("../../tunnel/services") as { stopAll: (opts: { sandboxName: string }) => void; }; services.stopAll(opts); @@ -221,7 +221,7 @@ export function cleanupSandboxServices( if (stopHostServices) { // `stopAll()` already runs `unloadOllamaModels()` unconditionally — - // see src/lib/services.ts. Don't double-call here. + // see src/lib/tunnel/services.ts. Don't double-call here. stopAll({ sandboxName }); } else { // No global stop, so `stopAll()` did not run; explicitly free Ollama diff --git a/src/lib/actions/sandbox/doctor.ts b/src/lib/actions/sandbox/doctor.ts index 1d15f583b1..6e0c90b830 100644 --- a/src/lib/actions/sandbox/doctor.ts +++ b/src/lib/actions/sandbox/doctor.ts @@ -20,7 +20,7 @@ import type { SandboxEntry } from "../../state/registry"; import { resolveOpenshell } from "../../adapters/openshell/resolve"; import { ROOT } from "../../runner"; import { parseLiveSandboxNames } from "../../runtime-recovery"; -import * as sandboxVersion from "../../sandbox-version"; +import * as sandboxVersion from "../../sandbox/version"; import * as shields from "../../shields"; import { buildStatusCommandDeps } from "../../status-command-deps"; import { B, D, G, R, RD, YW } from "../../cli/terminal-style"; diff --git a/src/lib/actions/sandbox/policy-channel.ts b/src/lib/actions/sandbox/policy-channel.ts index d19b2b0933..c85bd64549 100644 --- a/src/lib/actions/sandbox/policy-channel.ts +++ b/src/lib/actions/sandbox/policy-channel.ts @@ -11,11 +11,11 @@ import { getCredential, prompt as askPrompt } from "../../credentials/store"; import { recoverNamedGatewayRuntime } from "../../gateway-runtime-action"; const { isNonInteractive } = require("../../onboard") as { isNonInteractive: () => boolean }; const onboardProviders = require("../../onboard/providers"); -import * as policies from "../../policies"; +import * as policies from "../../policy"; import { parsePolicyAddArgs } from "../../domain/policy-channel"; import * as registry from "../../state/registry"; import { runOpenshell } from "../../adapters/openshell/runtime"; -import { rebuildSandbox } from "./runtime"; +import { rebuildSandbox } from "./rebuild"; import { KNOWN_CHANNELS, clearChannelTokens, @@ -23,7 +23,7 @@ import { getChannelTokenKeys, knownChannelNames, persistChannelTokens, -} from "../../sandbox-channels"; +} from "../../sandbox/channels"; const useColor = !process.env.NO_COLOR && !!process.stdout.isTTY; const trueColor = diff --git a/src/lib/actions/sandbox/rebuild.ts b/src/lib/actions/sandbox/rebuild.ts index 79ac00acfb..3f49069afd 100644 --- a/src/lib/actions/sandbox/rebuild.ts +++ b/src/lib/actions/sandbox/rebuild.ts @@ -35,7 +35,7 @@ import * as nim from "../../inference/nim"; import type { Session } from "../../state/onboard-session"; import * as onboardSession from "../../state/onboard-session"; import { captureOpenshell, runOpenshell } from "../../adapters/openshell/runtime"; -import * as policies from "../../policies"; +import * as policies from "../../policy"; import * as registry from "../../state/registry"; import { resolveOpenshell } from "../../adapters/openshell/resolve"; import { parseLiveSandboxNames } from "../../runtime-recovery"; @@ -46,7 +46,7 @@ import { getActiveSandboxSessions, } from "../../state/sandbox-session"; import * as sandboxState from "../../state/sandbox"; -import * as sandboxVersion from "../../sandbox-version"; +import * as sandboxVersion from "../../sandbox/version"; import { B, D, G, R, RD as _RD, YW } from "../../cli/terminal-style"; const agentRuntime = require("../../../../bin/lib/agent-runtime"); diff --git a/src/lib/actions/sandbox/runtime.ts b/src/lib/actions/sandbox/runtime.ts deleted file mode 100644 index 18502d718f..0000000000 --- a/src/lib/actions/sandbox/runtime.ts +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - - -import type { DestroySandboxOptions, RebuildSandboxOptions } from "../../domain/lifecycle/options"; -import type { SandboxConnectOptions } from "./connect"; -import type { SandboxLogsOptions } from "../../domain/sandbox/log-options"; - -export async function connectSandbox( - sandboxName: string, - options?: SandboxConnectOptions, -): Promise { - const { connectSandbox: connectExtractedSandbox } = require("./connect") as { - connectSandbox: (sandboxName: string, options?: SandboxConnectOptions) => Promise; - }; - await connectExtractedSandbox(sandboxName, options); -} - -export async function showSandboxStatus(sandboxName: string): Promise { - const { showSandboxStatus: showExtractedSandboxStatus } = require("./status") as { - showSandboxStatus: (sandboxName: string) => Promise; - }; - await showExtractedSandboxStatus(sandboxName); -} - -export function showSandboxLogs(sandboxName: string, options: SandboxLogsOptions): void { - const { showSandboxLogs: showSandboxLogsAction } = require("./logs") as { - showSandboxLogs: (sandboxName: string, options: SandboxLogsOptions) => void; - }; - showSandboxLogsAction(sandboxName, options); -} - -export async function destroySandbox( - sandboxName: string, - options: string[] | DestroySandboxOptions = {}, -): Promise { - const { destroySandbox: destroyExtractedSandbox } = require("./destroy") as { - destroySandbox: ( - sandboxName: string, - options?: string[] | DestroySandboxOptions, - ) => Promise; - }; - await destroyExtractedSandbox(sandboxName, options); -} - -export async function rebuildSandbox( - sandboxName: string, - options: string[] | RebuildSandboxOptions = {}, -): Promise { - const { rebuildSandbox: rebuildExtractedSandbox } = require("./rebuild") as { - rebuildSandbox: ( - sandboxName: string, - options?: string[] | RebuildSandboxOptions, - ) => Promise; - }; - await rebuildExtractedSandbox(sandboxName, options); -} - -export async function installSandboxSkill( - sandboxName: string, - args: string[] = [], -): Promise { - const { installSandboxSkill: installExtractedSandboxSkill } = require("./skill-install") as { - installSandboxSkill: (sandboxName: string, args?: string[]) => Promise; - }; - await installExtractedSandboxSkill(sandboxName, args); -} - -export async function runSandboxSnapshot(sandboxName: string, args: string[]): Promise { - const { runSandboxSnapshot: runExtractedSandboxSnapshot } = require("./snapshot") as { - runSandboxSnapshot: (sandboxName: string, args: string[]) => Promise; - }; - await runExtractedSandboxSnapshot(sandboxName, args); -} diff --git a/src/lib/actions/sandbox/snapshot.ts b/src/lib/actions/sandbox/snapshot.ts index eca054be4d..5000f05bfe 100644 --- a/src/lib/actions/sandbox/snapshot.ts +++ b/src/lib/actions/sandbox/snapshot.ts @@ -11,7 +11,7 @@ import { stripAnsi } from "../../adapters/openshell/client"; import { parseLiveSandboxNames } from "../../runtime-recovery"; import { ROOT, run, shellQuote, validateName } from "../../runner"; import { captureOpenshell, getOpenshellBinary } from "../../adapters/openshell/runtime"; -import * as policies from "../../policies"; +import * as policies from "../../policy"; import * as registry from "../../state/registry"; import type { SandboxEntry } from "../../state/registry"; import * as sandboxState from "../../state/sandbox"; @@ -125,7 +125,7 @@ async function autoCreateSandboxFromSource( dstName: string, srcEntry: SandboxEntry | { name: string }, ): Promise { - const sandboxCreateStream = require("../../sandbox-create-stream"); + const sandboxCreateStream = require("../../sandbox/create-stream"); const { isSandboxReady } = require("../../state/gateway"); const basePolicy = path.join(ROOT, "nemoclaw-blueprint", "policies", "openclaw-sandbox.yaml"); const openshellBin = getOpenshellBinary(); diff --git a/src/lib/actions/sandbox/status.ts b/src/lib/actions/sandbox/status.ts index 50c4050906..f7d56ed6ea 100644 --- a/src/lib/actions/sandbox/status.ts +++ b/src/lib/actions/sandbox/status.ts @@ -32,7 +32,7 @@ import { createSystemDeps as createSessionDeps, getActiveSandboxSessions, } from "../../state/sandbox-session"; -import * as sandboxVersion from "../../sandbox-version"; +import * as sandboxVersion from "../../sandbox/version"; import * as shields from "../../shields"; import { D, G, R, RD, YW } from "../../cli/terminal-style"; diff --git a/src/lib/actions/upgrade-sandboxes.ts b/src/lib/actions/upgrade-sandboxes.ts index 9cbbfe2f0b..183803aaeb 100644 --- a/src/lib/actions/upgrade-sandboxes.ts +++ b/src/lib/actions/upgrade-sandboxes.ts @@ -12,7 +12,7 @@ import { captureOpenshell } from "../adapters/openshell/runtime"; import * as registry from "../state/registry"; import { parseLiveSandboxNames } from "../runtime-recovery"; import { rebuildSandbox } from "./sandbox/rebuild"; -import * as sandboxVersion from "../sandbox-version"; +import * as sandboxVersion from "../sandbox/version"; import { B, D, G, R, YW } from "../cli/terminal-style"; import { classifyUpgradeableSandboxes, diff --git a/src/lib/adapters/http/README.md b/src/lib/adapters/http/README.md new file mode 100644 index 0000000000..551d6643bf --- /dev/null +++ b/src/lib/adapters/http/README.md @@ -0,0 +1,8 @@ + + + +# HTTP adapters + +HTTP adapter modules isolate host-side network probes and subprocess-backed HTTP +checks from action/domain logic. Keep pure response classification in domain or +feature modules; keep `curl`, temporary files, and network-boundary behavior here. diff --git a/src/lib/http-probe.test.ts b/src/lib/adapters/http/probe.test.ts similarity index 99% rename from src/lib/http-probe.test.ts rename to src/lib/adapters/http/probe.test.ts index 91f0a12580..4852fc364c 100644 --- a/src/lib/http-probe.test.ts +++ b/src/lib/adapters/http/probe.test.ts @@ -13,7 +13,7 @@ import { summarizeCurlFailure, summarizeProbeError, summarizeProbeFailure, -} from "./http-probe"; +} from "./probe"; describe("http-probe helpers", () => { it("returns explicit curl timeouts", () => { diff --git a/src/lib/http-probe.ts b/src/lib/adapters/http/probe.ts similarity index 98% rename from src/lib/http-probe.ts rename to src/lib/adapters/http/probe.ts index 156ea37e4a..5c6046bc66 100644 --- a/src/lib/http-probe.ts +++ b/src/lib/adapters/http/probe.ts @@ -10,11 +10,11 @@ import { type SpawnSyncReturns, } from "node:child_process"; -import type { ProbeResult } from "./onboard/types"; -import { ROOT } from "./state/paths"; -import { compactText } from "./core/url-utils"; +import type { ProbeResult } from "../../onboard/types"; +import { ROOT } from "../../state/paths"; +import { compactText } from "../../core/url-utils"; -import { isErrnoException } from "./core/errno"; +import { isErrnoException } from "../../core/errno"; export type CurlProbeResult = ProbeResult; diff --git a/src/lib/commands/deprecated/start.ts b/src/lib/commands/deprecated/start.ts index b680d4d880..f3bafd262a 100644 --- a/src/lib/commands/deprecated/start.ts +++ b/src/lib/commands/deprecated/start.ts @@ -4,8 +4,8 @@ import { Command, Flags } from "@oclif/core"; import { CLI_NAME } from "../../cli/branding"; -import { startAll } from "../../services"; -import { runStartCommand } from "../../services-command"; +import { startAll } from "../../tunnel/services"; +import { runStartCommand } from "../../tunnel/service-command"; import { serviceDeps } from "../tunnel/common"; export default class DeprecatedStartCommand extends Command { diff --git a/src/lib/commands/deprecated/stop.ts b/src/lib/commands/deprecated/stop.ts index 95ac2ecf98..46fbf833dc 100644 --- a/src/lib/commands/deprecated/stop.ts +++ b/src/lib/commands/deprecated/stop.ts @@ -4,8 +4,8 @@ import { Command, Flags } from "@oclif/core"; import { CLI_NAME } from "../../cli/branding"; -import { stopAll } from "../../services"; -import { runStopCommand } from "../../services-command"; +import { stopAll } from "../../tunnel/services"; +import { runStopCommand } from "../../tunnel/service-command"; import { serviceDeps } from "../tunnel/common"; export default class DeprecatedStopCommand extends Command { diff --git a/src/lib/commands/global-oclif-command-adapters.test.ts b/src/lib/commands/global-oclif-command-adapters.test.ts index 1cc1c4f18d..911336ca77 100644 --- a/src/lib/commands/global-oclif-command-adapters.test.ts +++ b/src/lib/commands/global-oclif-command-adapters.test.ts @@ -19,7 +19,7 @@ const mocks = vi.hoisted(() => ({ showStatusCommand: vi.fn(), })); -vi.mock("../inventory-commands", () => ({ +vi.mock("../inventory", () => ({ getSandboxInventory: mocks.getSandboxInventory, getStatusReport: mocks.getStatusReport, renderSandboxInventoryText: mocks.renderSandboxInventoryText, diff --git a/src/lib/commands/list.ts b/src/lib/commands/list.ts index bd136beb1f..52a6805593 100644 --- a/src/lib/commands/list.ts +++ b/src/lib/commands/list.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { getSandboxInventory, renderSandboxInventoryText } from "../inventory-commands"; +import { getSandboxInventory, renderSandboxInventoryText } from "../inventory"; import { NemoClawCommand } from "../cli/nemoclaw-oclif-command"; import { buildListCommandDeps } from "../list-command-deps"; diff --git a/src/lib/commands/sandbox/config/get.ts b/src/lib/commands/sandbox/config/get.ts index 2509d44bf3..400127dd16 100644 --- a/src/lib/commands/sandbox/config/get.ts +++ b/src/lib/commands/sandbox/config/get.ts @@ -4,7 +4,7 @@ import { Command, Flags } from "@oclif/core"; import { CLI_NAME } from "../../../cli/branding"; -import * as sandboxConfig from "../../../sandbox-config"; +import * as sandboxConfig from "../../../sandbox/config"; import { sandboxNameArg } from "../common"; export default class SandboxConfigGetCommand extends Command { diff --git a/src/lib/sandbox-config-set-cli-command.ts b/src/lib/commands/sandbox/config/set.ts similarity index 96% rename from src/lib/sandbox-config-set-cli-command.ts rename to src/lib/commands/sandbox/config/set.ts index 40ae493bdc..90eaa12ee9 100644 --- a/src/lib/sandbox-config-set-cli-command.ts +++ b/src/lib/commands/sandbox/config/set.ts @@ -4,7 +4,7 @@ import { Args, Command, Flags } from "@oclif/core"; -import * as sandboxConfig from "./sandbox-config"; +import * as sandboxConfig from "../../../sandbox/config"; const sandboxNameArg = Args.string({ name: "sandbox", diff --git a/src/lib/commands/sandbox/connect.ts b/src/lib/commands/sandbox/connect.ts index 13825f85f8..36a61c5ea6 100644 --- a/src/lib/commands/sandbox/connect.ts +++ b/src/lib/commands/sandbox/connect.ts @@ -4,7 +4,7 @@ import { Args, Command, Flags } from "@oclif/core"; import { CLI_NAME } from "../../cli/branding"; -import { connectSandbox } from "../../actions/sandbox/runtime"; +import { connectSandbox } from "../../actions/sandbox/connect"; export default class ConnectCliCommand extends Command { static id = "sandbox:connect"; diff --git a/src/lib/commands/sandbox/destroy.ts b/src/lib/commands/sandbox/destroy.ts index a8f6d35ae8..b590b3a089 100644 --- a/src/lib/commands/sandbox/destroy.ts +++ b/src/lib/commands/sandbox/destroy.ts @@ -4,7 +4,7 @@ import { Args, Flags } from "@oclif/core"; import { NemoClawCommand } from "../../cli/nemoclaw-oclif-command"; -import { destroySandbox } from "../../actions/sandbox/runtime"; +import { destroySandbox } from "../../actions/sandbox/destroy"; export default class DestroyCliCommand extends NemoClawCommand { static id = "sandbox:destroy"; diff --git a/src/lib/commands/sandbox/logs.ts b/src/lib/commands/sandbox/logs.ts index 97b45a5a95..09a8f818e8 100644 --- a/src/lib/commands/sandbox/logs.ts +++ b/src/lib/commands/sandbox/logs.ts @@ -6,15 +6,20 @@ import { Args, Command, Flags } from "@oclif/core"; import { logsSinceDurationFlag } from "../../cli/duration-flags"; import type { SandboxLogsOptions } from "../../domain/sandbox/log-options"; import { DEFAULT_SANDBOX_LOG_LINES } from "../../domain/sandbox/log-options"; -import { showSandboxLogs } from "../../actions/sandbox/runtime"; - type SandboxLogsRuntimeBridge = { sandboxLogs: (sandboxName: string, options: SandboxLogsOptions) => void; }; const DEFAULT_SANDBOX_LOG_LINE_COUNT = Number(DEFAULT_SANDBOX_LOG_LINES); -let runtimeBridgeFactory = (): SandboxLogsRuntimeBridge => ({ sandboxLogs: showSandboxLogs }); +let runtimeBridgeFactory = (): SandboxLogsRuntimeBridge => ({ + sandboxLogs: (sandboxName, options) => { + const { showSandboxLogs } = require("../../actions/sandbox/logs") as { + showSandboxLogs: (sandboxName: string, options: SandboxLogsOptions) => void; + }; + showSandboxLogs(sandboxName, options); + }, +}); export function setSandboxLogsRuntimeBridgeFactoryForTest( factory: () => SandboxLogsRuntimeBridge, diff --git a/src/lib/commands/sandbox/oclif-command-adapters.test.ts b/src/lib/commands/sandbox/oclif-command-adapters.test.ts index fee3c10871..4187810ca8 100644 --- a/src/lib/commands/sandbox/oclif-command-adapters.test.ts +++ b/src/lib/commands/sandbox/oclif-command-adapters.test.ts @@ -17,10 +17,19 @@ const mocks = vi.hoisted(() => ({ showSandboxStatus: vi.fn().mockResolvedValue(undefined), })); -vi.mock("../../actions/sandbox/runtime", () => ({ +vi.mock("../../actions/sandbox/connect", () => ({ connectSandbox: mocks.connectSandbox, +})); + +vi.mock("../../actions/sandbox/destroy", () => ({ destroySandbox: mocks.destroySandbox, +})); + +vi.mock("../../actions/sandbox/rebuild", () => ({ rebuildSandbox: mocks.rebuildSandbox, +})); + +vi.mock("../../actions/sandbox/status", () => ({ showSandboxStatus: mocks.showSandboxStatus, })); @@ -29,7 +38,7 @@ vi.mock("../../actions/sandbox/policy-channel", () => ({ listSandboxPolicies: mocks.listSandboxPolicies, })); -vi.mock("../../sandbox-config", () => ({ +vi.mock("../../sandbox/config", () => ({ configGet: mocks.configGet, })); diff --git a/src/lib/commands/sandbox/rebuild.ts b/src/lib/commands/sandbox/rebuild.ts index 74ba5cad2a..9a65f38811 100644 --- a/src/lib/commands/sandbox/rebuild.ts +++ b/src/lib/commands/sandbox/rebuild.ts @@ -4,7 +4,7 @@ import { Args, Flags } from "@oclif/core"; import { NemoClawCommand } from "../../cli/nemoclaw-oclif-command"; -import { rebuildSandbox } from "../../actions/sandbox/runtime"; +import { rebuildSandbox } from "../../actions/sandbox/rebuild"; export default class RebuildCliCommand extends NemoClawCommand { static id = "sandbox:rebuild"; diff --git a/src/lib/commands/sandbox/skill/common.ts b/src/lib/commands/sandbox/skill/common.ts index d4a2e70a94..9c76c4e9a3 100644 --- a/src/lib/commands/sandbox/skill/common.ts +++ b/src/lib/commands/sandbox/skill/common.ts @@ -1,9 +1,14 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { installSandboxSkill } from "../../../actions/sandbox/runtime"; - -let runtimeBridgeFactory = () => ({ sandboxSkillInstall: installSandboxSkill }); +let runtimeBridgeFactory = () => ({ + sandboxSkillInstall: async (sandboxName: string, args?: string[]) => { + const { installSandboxSkill } = require("../../../actions/sandbox/skill-install") as { + installSandboxSkill: (sandboxName: string, args?: string[]) => Promise; + }; + await installSandboxSkill(sandboxName, args); + }, +}); export function setSkillInstallRuntimeBridgeFactoryForTest( factory: () => { sandboxSkillInstall: (sandboxName: string, args?: string[]) => Promise }, diff --git a/src/lib/commands/sandbox/snapshot/common.ts b/src/lib/commands/sandbox/snapshot/common.ts index b46c42e608..8de5869bd9 100644 --- a/src/lib/commands/sandbox/snapshot/common.ts +++ b/src/lib/commands/sandbox/snapshot/common.ts @@ -3,9 +3,14 @@ import { Args } from "@oclif/core"; -import { runSandboxSnapshot } from "../../../actions/sandbox/runtime"; - -let runtimeBridgeFactory = () => ({ sandboxSnapshot: runSandboxSnapshot }); +let runtimeBridgeFactory = () => ({ + sandboxSnapshot: async (sandboxName: string, args: string[]) => { + const { runSandboxSnapshot } = require("../../../actions/sandbox/snapshot") as { + runSandboxSnapshot: (sandboxName: string, args: string[]) => Promise; + }; + await runSandboxSnapshot(sandboxName, args); + }, +}); export function setSnapshotRuntimeBridgeFactoryForTest( factory: () => { sandboxSnapshot: (sandboxName: string, args: string[]) => Promise }, diff --git a/src/lib/commands/sandbox/status.ts b/src/lib/commands/sandbox/status.ts index 18f3d174cd..78a9284058 100644 --- a/src/lib/commands/sandbox/status.ts +++ b/src/lib/commands/sandbox/status.ts @@ -3,7 +3,7 @@ import { Command, Flags } from "@oclif/core"; -import { showSandboxStatus } from "../../actions/sandbox/runtime"; +import { showSandboxStatus } from "../../actions/sandbox/status"; import { sandboxNameArg } from "./common"; export default class SandboxStatusCommand extends Command { diff --git a/src/lib/commands/simple-global-oclif-adapters.test.ts b/src/lib/commands/simple-global-oclif-adapters.test.ts index 578e03e785..8d2c2f7836 100644 --- a/src/lib/commands/simple-global-oclif-adapters.test.ts +++ b/src/lib/commands/simple-global-oclif-adapters.test.ts @@ -39,8 +39,8 @@ vi.mock("../actions/global", () => ({ vi.mock("../adapters/openshell/client", () => ({ captureOpenshellCommand: mocks.captureOpenshellCommand })); vi.mock("../state/registry", () => ({ listSandboxes: mocks.listSandboxes })); vi.mock("../adapters/openshell/resolve", () => ({ resolveOpenshell: mocks.resolveOpenshell })); -vi.mock("../services", () => ({ startAll: mocks.startAll, stopAll: mocks.stopAll })); -vi.mock("../services-command", () => ({ +vi.mock("../tunnel/services", () => ({ startAll: mocks.startAll, stopAll: mocks.stopAll })); +vi.mock("../tunnel/service-command", () => ({ runStartCommand: mocks.runStartCommand, runStopCommand: mocks.runStopCommand, })); diff --git a/src/lib/commands/status.ts b/src/lib/commands/status.ts index 5ce043f27f..4c962da07e 100644 --- a/src/lib/commands/status.ts +++ b/src/lib/commands/status.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { getStatusReport, showStatusCommand } from "../inventory-commands"; +import { getStatusReport, showStatusCommand } from "../inventory"; import { NemoClawCommand } from "../cli/nemoclaw-oclif-command"; import { buildStatusCommandDeps } from "../status-command-deps"; diff --git a/src/lib/commands/tunnel/start.ts b/src/lib/commands/tunnel/start.ts index 81ffc6c13c..e3288dc25c 100644 --- a/src/lib/commands/tunnel/start.ts +++ b/src/lib/commands/tunnel/start.ts @@ -3,8 +3,8 @@ import { Command, Flags } from "@oclif/core"; -import { startAll } from "../../services"; -import { runStartCommand } from "../../services-command"; +import { startAll } from "../../tunnel/services"; +import { runStartCommand } from "../../tunnel/service-command"; import { serviceDeps } from "./common"; export default class TunnelStartCommand extends Command { diff --git a/src/lib/commands/tunnel/stop.ts b/src/lib/commands/tunnel/stop.ts index 6b30dafb26..0d25b7f152 100644 --- a/src/lib/commands/tunnel/stop.ts +++ b/src/lib/commands/tunnel/stop.ts @@ -3,8 +3,8 @@ import { Command, Flags } from "@oclif/core"; -import { stopAll } from "../../services"; -import { runStopCommand } from "../../services-command"; +import { stopAll } from "../../tunnel/services"; +import { runStopCommand } from "../../tunnel/service-command"; import { serviceDeps } from "./common"; export default class TunnelStopCommand extends Command { diff --git a/src/lib/coverage-hotspots.test.ts b/src/lib/coverage-hotspots.test.ts index f6241bf003..9264c82055 100644 --- a/src/lib/coverage-hotspots.test.ts +++ b/src/lib/coverage-hotspots.test.ts @@ -8,7 +8,7 @@ import { describe, expect, it } from "vitest"; import { parseDuration } from "./domain/duration"; import { parseGatewayTokenArgs, runGatewayTokenCommand } from "./gateway-token-command"; -import { resolveDefaultSandboxName, runStartCommand, runStopCommand } from "./services-command"; +import { resolveDefaultSandboxName, runStartCommand, runStopCommand } from "./tunnel/service-command"; import { getVersion } from "./core/version"; // Narrow coverage guard for small helper modules that are otherwise only diff --git a/src/lib/deploy/README.md b/src/lib/deploy/README.md new file mode 100644 index 0000000000..147658fd9f --- /dev/null +++ b/src/lib/deploy/README.md @@ -0,0 +1,8 @@ + + + +# Deploy + +Deploy modules support remote/Brev compatibility flows and build-image setup that +has not yet been split into action/domain/adapter layers. Prefer new orchestration +in `src/lib/actions/**` and pure deploy planning helpers in `src/lib/domain/**`. diff --git a/src/lib/deploy.test.ts b/src/lib/deploy/index.test.ts similarity index 99% rename from src/lib/deploy.test.ts rename to src/lib/deploy/index.test.ts index c21d97ce75..c34045f000 100644 --- a/src/lib/deploy.test.ts +++ b/src/lib/deploy/index.test.ts @@ -9,8 +9,8 @@ import { inferDeployProvider, isBrevInstanceFailed, isBrevInstanceReady, -} from "../../dist/lib/deploy"; -import { validateName } from "../../dist/lib/runner"; +} from "../../../dist/lib/deploy"; +import { validateName } from "../../../dist/lib/runner"; describe("inferDeployProvider", () => { it("prefers an explicit provider override", () => { diff --git a/src/lib/deploy.ts b/src/lib/deploy/index.ts similarity index 99% rename from src/lib/deploy.ts rename to src/lib/deploy/index.ts index aa3c645928..48ba9bb3d1 100644 --- a/src/lib/deploy.ts +++ b/src/lib/deploy/index.ts @@ -5,8 +5,8 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import { NAME_ALLOWED_FORMAT, getNameValidationGuidance } from "./name-validation"; -import { sleepSeconds } from "./core/wait"; +import { NAME_ALLOWED_FORMAT, getNameValidationGuidance } from "../name-validation"; +import { sleepSeconds } from "../core/wait"; type ExecLikeValue = | string diff --git a/src/lib/inference/health.ts b/src/lib/inference/health.ts index 252aac0500..2204da8ad8 100644 --- a/src/lib/inference/health.ts +++ b/src/lib/inference/health.ts @@ -11,8 +11,8 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import type { CurlProbeResult } from "../http-probe"; -import { runCurlProbe } from "../http-probe"; +import type { CurlProbeResult } from "../adapters/http/probe"; +import { runCurlProbe } from "../adapters/http/probe"; import { normalizeCredentialValue, resolveProviderCredential } from "../credentials/store"; import { getProviderSelectionConfig } from "./config"; import type { LocalProviderHealthProbeOptions } from "./local"; diff --git a/src/lib/inference/local.ts b/src/lib/inference/local.ts index cadfa8b98a..015df92dc8 100644 --- a/src/lib/inference/local.ts +++ b/src/lib/inference/local.ts @@ -6,8 +6,8 @@ * health checks, and command generators for vLLM and Ollama. */ -import type { CurlProbeResult } from "../http-probe"; -import { runCurlProbe } from "../http-probe"; +import type { CurlProbeResult } from "../adapters/http/probe"; +import { runCurlProbe } from "../adapters/http/probe"; const { shellQuote, runCapture } = require("../runner"); diff --git a/src/lib/inference/onboard-probes.ts b/src/lib/inference/onboard-probes.ts index 024fb00b1d..38e7fa9dde 100644 --- a/src/lib/inference/onboard-probes.ts +++ b/src/lib/inference/onboard-probes.ts @@ -7,7 +7,7 @@ const { normalizeCredentialValue } = require("../credentials/store"); const { isWsl } = require("../platform"); -const httpProbe = require("../http-probe"); +const httpProbe = require("../adapters/http/probe"); const { isNvcfFunctionNotFoundForAccount, nvcfFunctionNotFoundMessage, diff --git a/src/lib/inference/provider-models.ts b/src/lib/inference/provider-models.ts index 2cf6206568..2d219599fa 100644 --- a/src/lib/inference/provider-models.ts +++ b/src/lib/inference/provider-models.ts @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import type { CurlProbeResult } from "../http-probe"; -import { getCurlTimingArgs, runCurlProbe } from "../http-probe"; +import type { CurlProbeResult } from "../adapters/http/probe"; +import { getCurlTimingArgs, runCurlProbe } from "../adapters/http/probe"; import type { ModelCatalogFetchResult, ModelValidationResult } from "../onboard/types"; // credentials.ts still uses CommonJS-style exports. diff --git a/src/lib/inventory/README.md b/src/lib/inventory/README.md new file mode 100644 index 0000000000..fda5d3a657 --- /dev/null +++ b/src/lib/inventory/README.md @@ -0,0 +1,9 @@ + + + +# Inventory + +Inventory modules shape sandbox registry, live inference, service, and messaging +health data into the rows printed by `nemoclaw list` and `nemoclaw status`. +Command parser glue should stay in `src/lib/commands/**`; registry I/O should stay +in `src/lib/state/**`. diff --git a/src/lib/inventory-commands.test.ts b/src/lib/inventory/index.test.ts similarity index 99% rename from src/lib/inventory-commands.test.ts rename to src/lib/inventory/index.test.ts index 7f070c455b..f55a0d0a32 100644 --- a/src/lib/inventory-commands.test.ts +++ b/src/lib/inventory/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it, vi } from "vitest"; -import { getSandboxInventory, listSandboxesCommand, showStatusCommand } from "./inventory-commands"; +import { getSandboxInventory, listSandboxesCommand, showStatusCommand } from "./index"; describe("inventory commands", () => { it("returns structured empty inventory for JSON consumers", async () => { diff --git a/src/lib/inventory-commands.ts b/src/lib/inventory/index.ts similarity index 99% rename from src/lib/inventory-commands.ts rename to src/lib/inventory/index.ts index 60367bded7..2c32d1b832 100644 --- a/src/lib/inventory-commands.ts +++ b/src/lib/inventory/index.ts @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { CLI_NAME } from "./cli/branding"; -import type { GatewayInference } from "./inference/config"; -import { redactFull } from "./security/redact"; +import { CLI_NAME } from "../cli/branding"; +import type { GatewayInference } from "../inference/config"; +import { redactFull } from "../security/redact"; export interface SandboxEntry { name: string; diff --git a/src/lib/list-command-deps.ts b/src/lib/list-command-deps.ts index a6d50733fa..2bd9b99a42 100644 --- a/src/lib/list-command-deps.ts +++ b/src/lib/list-command-deps.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import * as onboardSession from "./state/onboard-session"; -import type { ListSandboxesCommandDeps, SandboxEntry } from "./inventory-commands"; +import type { ListSandboxesCommandDeps, SandboxEntry } from "./inventory"; import { parseGatewayInference } from "./inference/config"; import { OPENSHELL_PROBE_TIMEOUT_MS } from "./adapters/openshell/timeouts"; import { parseSshProcesses, createSystemDeps } from "./state/sandbox-session"; diff --git a/src/lib/messaging-channel-config.ts b/src/lib/messaging-channel-config.ts index a39811e431..814810a42f 100644 --- a/src/lib/messaging-channel-config.ts +++ b/src/lib/messaging-channel-config.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { listChannels } from "./sandbox-channels"; +import { listChannels } from "./sandbox/channels"; export type MessagingChannelConfig = Record; diff --git a/src/lib/messaging-conflict.ts b/src/lib/messaging-conflict.ts index 8486a8e369..70ea33d2fe 100644 --- a/src/lib/messaging-conflict.ts +++ b/src/lib/messaging-conflict.ts @@ -14,7 +14,7 @@ // the live OpenShell gateway for known provider names. import type { SandboxEntry } from "./state/registry"; -import { getChannelDef, getChannelTokenKeys } from "./sandbox-channels"; +import { getChannelDef, getChannelTokenKeys } from "./sandbox/channels"; type ProbeResult = "present" | "absent" | "error"; type ConflictReason = "matching-token" | "unknown-token"; diff --git a/src/lib/onboard.ts b/src/lib/onboard.ts index ebb12ad64b..3aedb345a0 100644 --- a/src/lib/onboard.ts +++ b/src/lib/onboard.ts @@ -5,7 +5,17 @@ // Supports non-interactive mode via --non-interactive flag or // NEMOCLAW_NON_INTERACTIVE=1 env var for CI/CD pipelines. -const { getAgentBranding } = require("./cli/branding"); +const { + envInt, + LOCAL_INFERENCE_TIMEOUT_SECS, + SANDBOX_READY_TIMEOUT_SECS, +}: typeof import("./onboard/env") = require("./onboard/env"); +const { + agentProductName, + cliDisplayName, + cliName, + setOnboardBrandingAgent, +}: typeof import("./onboard/branding") = require("./onboard/branding"); const crypto = require("node:crypto"); const fs = require("fs"); const os = require("os"); @@ -13,40 +23,6 @@ const path = require("path"); const { spawn, spawnSync } = require("child_process"); const pRetry = require("p-retry"); -/** Parse a numeric env var, returning `fallback` when unset or non-finite. */ -function envInt(name: string, fallback: number): number { - const raw = process.env[name]; - if (raw === undefined || raw === "") return fallback; - const n = Number(raw); - return Number.isFinite(n) ? Math.max(0, Math.round(n)) : fallback; -} -/** Inference timeout (seconds) for local providers (Ollama, vLLM, NIM). */ -const LOCAL_INFERENCE_TIMEOUT_SECS = envInt("NEMOCLAW_LOCAL_INFERENCE_TIMEOUT", 180); -/** Sandbox Ready wait after OpenShell create returns but k3s is still converging. */ -const SANDBOX_READY_TIMEOUT_SECS = envInt("NEMOCLAW_SANDBOX_READY_TIMEOUT", 180); - -let onboardBrandingAgent: string | null = null; - -function setOnboardBrandingAgent(agentName: string | null | undefined): void { - onboardBrandingAgent = agentName || null; -} - -function onboardBranding(): import("./cli/branding").AgentBranding { - return getAgentBranding(onboardBrandingAgent || process.env.NEMOCLAW_AGENT || null); -} - -function cliName(): string { - return onboardBranding().cli; -} - -function cliDisplayName(): string { - return onboardBranding().display; -} - -function agentProductName(): string { - return onboardBranding().product; -} - /** Strip ANSI escape sequences before printing process output to the terminal. * Covers CSI (color, erase, cursor), OSC, and C1 two-byte escapes per ECMA-48. */ const ANSI_RE = /\x1B(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\)|[@-_])/g; @@ -101,7 +77,7 @@ function requireValue(value: T | null | undefined, message: string): T { const { collectBuildContextStats, stageOptimizedSandboxBuildContext, -} = require("./sandbox-build-context"); +} = require("./sandbox/build-context"); const { buildSubprocessEnv } = require("./subprocess-env"); const { DASHBOARD_PORT, @@ -286,9 +262,9 @@ const { const registry: typeof import("./state/registry") = require("./state/registry"); const nim: typeof import("./inference/nim") = require("./inference/nim"); const onboardSession: typeof import("./state/onboard-session") = require("./state/onboard-session"); -const policies: typeof import("./policies") = require("./policies"); +const policies: typeof import("./policy") = require("./policy"); const shields = require("./shields"); -const tiers: typeof import("./tiers") = require("./tiers"); +const tiers: typeof import("./policy/tiers") = require("./policy/tiers"); const { ensureUsageNoticeConsent } = require("./onboard/usage-notice"); const preflightUtils: typeof import("./onboard/preflight") = require("./onboard/preflight"); const clusterImagePatch: typeof import("./cluster-image-patch") = require("./cluster-image-patch"); @@ -310,16 +286,16 @@ const validation: typeof import("./validation") = require("./validation"); const urlUtils: typeof import("./core/url-utils") = require("./core/url-utils"); const buildContext = require("./build-context"); const dashboardContract: typeof import("./dashboard/contract") = require("./dashboard/contract"); -const httpProbe: typeof import("./http-probe") = require("./http-probe"); +const httpProbe: typeof import("./adapters/http/probe") = require("./adapters/http/probe"); const modelPrompts: typeof import("./inference/model-prompts") = require("./inference/model-prompts"); const providerModels: typeof import("./inference/provider-models") = require("./inference/provider-models"); -const sandboxCreateStream: typeof import("./sandbox-create-stream") = require("./sandbox-create-stream"); +const sandboxCreateStream: typeof import("./sandbox/create-stream") = require("./sandbox/create-stream"); const validationRecovery: typeof import("./validation-recovery") = require("./validation-recovery"); const webSearch: typeof import("./inference/web-search") = require("./inference/web-search"); import type { AgentDefinition } from "./agent/defs"; +import type { CurlProbeResult } from "./adapters/http/probe"; import type { GatewayReuseState } from "./state/gateway"; -import type { CurlProbeResult } from "./http-probe"; import type { GatewayInference, ProviderSelectionConfig } from "./inference/config"; import type { GpuInfo, ValidationResult } from "./inference/local"; import { @@ -338,11 +314,11 @@ import type { ProbeResult, ValidationFailureLike, } from "./onboard/types"; -import { listChannels } from "./sandbox-channels"; -import type { StreamSandboxCreateResult } from "./sandbox-create-stream"; +import { listChannels } from "./sandbox/channels"; +import type { StreamSandboxCreateResult } from "./sandbox/create-stream"; import type { SandboxEntry } from "./state/registry"; import type { BackupResult } from "./state/sandbox"; -import type { TierDefinition, TierPreset } from "./tiers"; +import type { TierDefinition, TierPreset } from "./policy/tiers"; import type { SandboxCreateFailure, ValidationClassification } from "./validation"; import type { ProbeRecovery } from "./validation-recovery"; import type { WebSearchConfig } from "./inference/web-search"; diff --git a/src/lib/onboard/branding.ts b/src/lib/onboard/branding.ts new file mode 100644 index 0000000000..1f191bf69e --- /dev/null +++ b/src/lib/onboard/branding.ts @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { getAgentBranding, type AgentBranding } from "../cli/branding"; + +let onboardBrandingAgent: string | null = null; + +export function setOnboardBrandingAgent(agentName: string | null | undefined): void { + onboardBrandingAgent = agentName || null; +} + +export function onboardBranding(): AgentBranding { + return getAgentBranding(onboardBrandingAgent || process.env.NEMOCLAW_AGENT || null); +} + +export function cliName(): string { + return onboardBranding().cli; +} + +export function cliDisplayName(): string { + return onboardBranding().display; +} + +export function agentProductName(): string { + return onboardBranding().product; +} diff --git a/src/lib/onboard/env.ts b/src/lib/onboard/env.ts new file mode 100644 index 0000000000..d59bbb3fda --- /dev/null +++ b/src/lib/onboard/env.ts @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +/** Parse a numeric env var, returning `fallback` when unset or non-finite. */ +export function envInt( + name: string, + fallback: number, + env: Record = process.env, +): number { + const raw = env[name]; + if (raw === undefined || raw === "") return fallback; + const n = Number(raw); + return Number.isFinite(n) ? Math.max(0, Math.round(n)) : fallback; +} + +/** Inference timeout (seconds) for local providers (Ollama, vLLM, NIM). */ +export const LOCAL_INFERENCE_TIMEOUT_SECS = envInt("NEMOCLAW_LOCAL_INFERENCE_TIMEOUT", 180); + +/** Sandbox Ready wait after OpenShell create returns but k3s is still converging. */ +export const SANDBOX_READY_TIMEOUT_SECS = envInt("NEMOCLAW_SANDBOX_READY_TIMEOUT", 180); diff --git a/src/lib/policy/README.md b/src/lib/policy/README.md new file mode 100644 index 0000000000..ab44d0ef08 --- /dev/null +++ b/src/lib/policy/README.md @@ -0,0 +1,9 @@ + + + +# Policy + +Policy modules own sandbox network-policy preset loading, tier resolution, and +policy application helpers. They may orchestrate OpenShell policy commands while +legacy flows are being migrated, but pure selection/planning helpers should move +under `src/lib/domain/**` when they can be isolated. diff --git a/src/lib/policies.ts b/src/lib/policy/index.ts similarity index 99% rename from src/lib/policies.ts rename to src/lib/policy/index.ts index 2158cb6f0a..2c7bf1eec5 100644 --- a/src/lib/policies.ts +++ b/src/lib/policy/index.ts @@ -3,16 +3,16 @@ // // Policy preset management — list, load, merge, and apply presets. -import type { JsonValue, JsonObject } from "./core/json-types"; +import type { JsonValue, JsonObject } from "../core/json-types"; const fs = require("fs"); const path = require("path"); const os = require("os"); const readline = require("readline"); const YAML = require("yaml"); -const { ROOT, run, runCapture } = require("./runner"); -const registry = require("./state/registry"); -const { loadAgent } = require("./agent/defs"); +const { ROOT, run, runCapture } = require("../runner"); +const registry = require("../state/registry"); +const { loadAgent } = require("../agent/defs"); const PRESETS_DIR = path.join(ROOT, "nemoclaw-blueprint", "policies", "presets"); diff --git a/src/lib/tiers.ts b/src/lib/policy/tiers.ts similarity index 99% rename from src/lib/tiers.ts rename to src/lib/policy/tiers.ts index 5e0fefc0cd..be4b77bb52 100644 --- a/src/lib/tiers.ts +++ b/src/lib/policy/tiers.ts @@ -14,7 +14,7 @@ import fs from "node:fs"; import path from "node:path"; import YAML from "yaml"; -import { ROOT } from "./runner"; +import { ROOT } from "../runner"; const TIERS_FILE = path.join(ROOT, "nemoclaw-blueprint", "policies", "tiers.yaml"); const ALLOWED_ACCESS: ReadonlySet = new Set(["read", "read-write"]); diff --git a/src/lib/recover-cli-command.ts b/src/lib/recover-cli-command.ts index fd5eb77473..576b5a89c8 100644 --- a/src/lib/recover-cli-command.ts +++ b/src/lib/recover-cli-command.ts @@ -4,7 +4,7 @@ import { Args, Command, Flags } from "@oclif/core"; -import { connectSandbox } from "./actions/sandbox/runtime"; +import { connectSandbox } from "./actions/sandbox/connect"; export default class RecoverCliCommand extends Command { static id = "sandbox:recover"; diff --git a/src/lib/registry-recovery-action.ts b/src/lib/registry-recovery-action.ts index 3df6c244b6..6783ded4bc 100644 --- a/src/lib/registry-recovery-action.ts +++ b/src/lib/registry-recovery-action.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { recoverNamedGatewayRuntime } from "./gateway-runtime-action"; -import type { RecoveryResult } from "./inventory-commands"; +import type { RecoveryResult } from "./inventory"; import * as onboardSession from "./state/onboard-session"; import { OPENSHELL_PROBE_TIMEOUT_MS } from "./adapters/openshell/timeouts"; import { captureOpenshell } from "./adapters/openshell/runtime"; diff --git a/src/lib/sandbox-build-context.ts b/src/lib/sandbox/build-context.ts similarity index 100% rename from src/lib/sandbox-build-context.ts rename to src/lib/sandbox/build-context.ts diff --git a/src/lib/sandbox-channels.test.ts b/src/lib/sandbox/channels.test.ts similarity index 98% rename from src/lib/sandbox-channels.test.ts rename to src/lib/sandbox/channels.test.ts index 2d9536ab89..f69655b660 100644 --- a/src/lib/sandbox-channels.test.ts +++ b/src/lib/sandbox/channels.test.ts @@ -9,7 +9,7 @@ import { getChannelTokenKeys, knownChannelNames, listChannels, -} from "../../dist/lib/sandbox-channels"; +} from "./channels"; describe("sandbox-channels KNOWN_CHANNELS", () => { it("covers telegram, discord, and slack", () => { diff --git a/src/lib/sandbox-channels.ts b/src/lib/sandbox/channels.ts similarity index 98% rename from src/lib/sandbox-channels.ts rename to src/lib/sandbox/channels.ts index 1be69ec277..8e52addd9d 100644 --- a/src/lib/sandbox-channels.ts +++ b/src/lib/sandbox/channels.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { deleteCredential, saveCredential } from "./credentials/store"; +import { deleteCredential, saveCredential } from "../credentials/store"; export interface ChannelDef { envKey: string; diff --git a/src/lib/sandbox-config.ts b/src/lib/sandbox/config.ts similarity index 97% rename from src/lib/sandbox-config.ts rename to src/lib/sandbox/config.ts index 4b69a82523..1f3814c793 100644 --- a/src/lib/sandbox-config.ts +++ b/src/lib/sandbox/config.ts @@ -18,21 +18,21 @@ const os = require("os"); const path = require("path"); const { promises: dnsPromises } = require("node:dns"); const { isIP } = require("node:net"); -const { validateName } = require("./runner"); -const { shellQuote } = require("./core/shell-quote"); -const { dockerExecFileSync } = require("./adapters/docker/exec"); -const { dockerCapture } = require("./adapters/docker/run"); -const credentialFilter: typeof import("./security/credential-filter") = require("./security/credential-filter"); +const { validateName } = require("../runner"); +const { shellQuote } = require("../core/shell-quote"); +const { dockerExecFileSync } = require("../adapters/docker/exec"); +const { dockerCapture } = require("../adapters/docker/run"); +const credentialFilter: typeof import("../security/credential-filter") = require("../security/credential-filter"); const { stripCredentials, isConfigObject, isConfigValue, isCredentialField } = credentialFilter; -const { appendAuditEntry } = require("./shields/audit"); -const { isPrivateHostname, isPrivateIp } = require("./private-networks"); -const registry = require("./state/registry") as { +const { appendAuditEntry } = require("../shields/audit"); +const { isPrivateHostname, isPrivateIp } = require("../private-networks"); +const registry = require("../state/registry") as { getSandbox?: (name: string) => { openshellDriver?: string | null } | null; }; -type ConfigObject = import("./security/credential-filter").ConfigObject; -type ConfigValue = import("./security/credential-filter").ConfigValue; -const { runOpenshellCommand, captureOpenshellCommand } = require("./adapters/openshell/client"); +type ConfigObject = import("../security/credential-filter").ConfigObject; +type ConfigValue = import("../security/credential-filter").ConfigValue; +const { runOpenshellCommand, captureOpenshellCommand } = require("../adapters/openshell/client"); function parseJson(text: string): T { return JSON.parse(text); @@ -172,11 +172,11 @@ function privilegedSandboxExec( function resolveAgentConfig(sandboxName: string): AgentConfigTarget { try { - const registry = require("./state/registry"); + const registry = require("../state/registry"); const entry = registry.getSandbox(sandboxName); if (!entry || !entry.agent) return DEFAULT_AGENT_CONFIG; - const agentDefs = require("./agent/defs"); + const agentDefs = require("../agent/defs"); const agent = agentDefs.loadAgent(entry.agent); const cfg = agent.configPaths; @@ -921,7 +921,7 @@ async function configRotateToken(sandboxName: string, opts: RotateTokenOpts = {} // 1. Determine which provider and credentialEnv the sandbox uses. // Load the onboard session and verify it matches this sandbox. - const { loadSession } = require("./state/onboard-session"); + const { loadSession } = require("../state/onboard-session"); const session = loadSession(); if (!session || !session.credentialEnv) { @@ -958,7 +958,7 @@ async function configRotateToken(sandboxName: string, opts: RotateTokenOpts = {} } else if (opts.fromStdin) { newToken = await readStdin(); } else { - const { promptSecret } = require("./credentials/store"); + const { promptSecret } = require("../credentials/store"); newToken = await promptSecret(` New ${credentialEnv} value: `); } @@ -978,7 +978,7 @@ async function configRotateToken(sandboxName: string, opts: RotateTokenOpts = {} // 4. Stage the new value in the current process so the openshell update // that follows can read it via --credential . The OpenShell // gateway becomes the system of record once the update succeeds. - const { saveCredential } = require("./credentials/store"); + const { saveCredential } = require("../credentials/store"); saveCredential(credentialEnv, newToken); // 5. Update the openshell provider diff --git a/src/lib/sandbox-create-stream.test.ts b/src/lib/sandbox/create-stream.test.ts similarity index 99% rename from src/lib/sandbox-create-stream.test.ts rename to src/lib/sandbox/create-stream.test.ts index 9547610d19..9902f949c0 100644 --- a/src/lib/sandbox-create-stream.test.ts +++ b/src/lib/sandbox/create-stream.test.ts @@ -9,7 +9,7 @@ import { type StreamableChildProcess, type StreamableReadable, streamSandboxCreate, -} from "./sandbox-create-stream"; +} from "./create-stream"; class FakeReadable extends EventEmitter implements StreamableReadable { destroy(): void {} diff --git a/src/lib/sandbox-create-stream.ts b/src/lib/sandbox/create-stream.ts similarity index 99% rename from src/lib/sandbox-create-stream.ts rename to src/lib/sandbox/create-stream.ts index 02c684f3df..d357430847 100644 --- a/src/lib/sandbox-create-stream.ts +++ b/src/lib/sandbox/create-stream.ts @@ -3,7 +3,7 @@ import { spawn, type ChildProcess, type SpawnOptions } from "node:child_process"; -import { ROOT } from "./state/paths"; +import { ROOT } from "../state/paths"; export interface StreamSandboxCreateResult { status: number; diff --git a/src/lib/sandbox-version.test.ts b/src/lib/sandbox/version.test.ts similarity index 94% rename from src/lib/sandbox-version.test.ts rename to src/lib/sandbox/version.test.ts index b181c7fdcc..8597ae35d8 100644 --- a/src/lib/sandbox-version.test.ts +++ b/src/lib/sandbox/version.test.ts @@ -7,11 +7,11 @@ import { join } from "node:path"; import { tmpdir } from "node:os"; // Mock heavy dependencies that pull in the full module graph -vi.mock("./adapters/openshell/resolve.js", () => ({ +vi.mock("../adapters/openshell/resolve.js", () => ({ resolveOpenshell: vi.fn(() => "/usr/local/bin/openshell"), })); -vi.mock("./adapters/openshell/client.js", () => ({ +vi.mock("../adapters/openshell/client.js", () => ({ parseVersionFromText: (value = "") => { const match = String(value).match(/([0-9]+\.[0-9]+\.[0-9]+)/); return match ? match[1] : null; @@ -31,7 +31,7 @@ vi.mock("./adapters/openshell/client.js", () => ({ captureOpenshellCommand: vi.fn(), })); -vi.mock("./agent/defs.js", () => ({ +vi.mock("../agent/defs.js", () => ({ loadAgent: vi.fn((name: string) => ({ name, displayName: name === "openclaw" ? "OpenClaw" : "Hermes Agent", @@ -47,10 +47,10 @@ vi.mock("child_process", async (importOriginal) => { return { ...actual, spawnSync: vi.fn() }; }); -import { checkAgentVersion, formatStalenessWarning } from "./sandbox-version.js"; -import * as registry from "./state/registry.js"; -import { captureOpenshellCommand } from "./adapters/openshell/client.js"; -import { OPENSHELL_PROBE_TIMEOUT_MS } from "./adapters/openshell/timeouts.js"; +import { checkAgentVersion, formatStalenessWarning } from "./version.js"; +import * as registry from "../state/registry.js"; +import { captureOpenshellCommand } from "../adapters/openshell/client.js"; +import { OPENSHELL_PROBE_TIMEOUT_MS } from "../adapters/openshell/timeouts.js"; import { spawnSync } from "child_process"; describe("checkAgentVersion", () => { diff --git a/src/lib/sandbox-version.ts b/src/lib/sandbox/version.ts similarity index 91% rename from src/lib/sandbox-version.ts rename to src/lib/sandbox/version.ts index 958adc1005..b9b1959690 100644 --- a/src/lib/sandbox-version.ts +++ b/src/lib/sandbox/version.ts @@ -13,12 +13,12 @@ import fs from "fs"; import os from "os"; import path from "path"; -import { parseVersionFromText, versionGte } from "./adapters/openshell/client.js"; -import * as registry from "./state/registry.js"; -import { loadAgent } from "./agent/defs.js"; -import { resolveOpenshell } from "./adapters/openshell/resolve.js"; -import { captureOpenshellCommand } from "./adapters/openshell/client.js"; -import { OPENSHELL_PROBE_TIMEOUT_MS } from "./adapters/openshell/timeouts.js"; +import { parseVersionFromText, versionGte } from "../adapters/openshell/client.js"; +import * as registry from "../state/registry.js"; +import { loadAgent } from "../agent/defs.js"; +import { resolveOpenshell } from "../adapters/openshell/resolve.js"; +import { captureOpenshellCommand } from "../adapters/openshell/client.js"; +import { OPENSHELL_PROBE_TIMEOUT_MS } from "../adapters/openshell/timeouts.js"; export interface VersionCheckResult { sandboxVersion: string | null; diff --git a/src/lib/shields/index.test.ts b/src/lib/shields/index.test.ts index 83c509ca31..a7f72990a4 100644 --- a/src/lib/shields/index.test.ts +++ b/src/lib/shields/index.test.ts @@ -18,7 +18,7 @@ vi.mock("../runner", () => ({ ROOT: "/mock/root", })); -vi.mock("../policies", () => ({ +vi.mock("../policy", () => ({ buildPolicyGetCommand: vi.fn((name) => ["openshell", "policy", "get", "--full", name]), buildPolicySetCommand: vi.fn((file, name) => [ "openshell", @@ -33,7 +33,7 @@ vi.mock("../policies", () => ({ PERMISSIVE_POLICY_PATH: "/mock/permissive.yaml", })); -vi.mock("../sandbox-config", () => ({ +vi.mock("../sandbox/config", () => ({ resolveAgentConfig: vi.fn(() => ({ agentName: "openclaw", configPath: "/sandbox/.openclaw/openclaw.json", diff --git a/src/lib/shields/index.ts b/src/lib/shields/index.ts index 1fd11e7b9f..cb0454c619 100644 --- a/src/lib/shields/index.ts +++ b/src/lib/shields/index.ts @@ -24,10 +24,10 @@ const { buildPolicySetCommand, parseCurrentPolicy, PERMISSIVE_POLICY_PATH, -} = require("../policies"); +} = require("../policy"); const { parseDuration, MAX_SECONDS, DEFAULT_SECONDS } = require("../domain/duration"); const { appendAuditEntry } = require("./audit"); -const { resolveAgentConfig } = require("../sandbox-config"); +const { resolveAgentConfig } = require("../sandbox/config"); const STATE_DIR = path.join(process.env.HOME ?? "/tmp", ".nemoclaw", "state"); diff --git a/src/lib/shields/timer.ts b/src/lib/shields/timer.ts index a0b8917114..c7c79f095e 100644 --- a/src/lib/shields/timer.ts +++ b/src/lib/shields/timer.ts @@ -11,9 +11,9 @@ import fs from "node:fs"; import path from "node:path"; -import { buildPolicySetCommand } from "../policies"; +import { buildPolicySetCommand } from "../policy"; import { run } from "../runner"; -import { DEFAULT_AGENT_CONFIG, resolveAgentConfig } from "../sandbox-config"; +import { DEFAULT_AGENT_CONFIG, resolveAgentConfig } from "../sandbox/config"; import { lockAgentConfig } from "./index"; type UnknownRecord = { [key: string]: unknown }; diff --git a/src/lib/status-command-deps.ts b/src/lib/status-command-deps.ts index 854509e823..cb90578c5e 100644 --- a/src/lib/status-command-deps.ts +++ b/src/lib/status-command-deps.ts @@ -5,14 +5,14 @@ import { spawnSync } from "node:child_process"; import { parseGatewayInference } from "./inference/config"; -import type { MessagingBridgeHealth, ShowStatusCommandDeps } from "./inventory-commands"; +import type { MessagingBridgeHealth, ShowStatusCommandDeps } from "./inventory"; import { backfillMessagingChannels, findAllOverlaps } from "./messaging-conflict"; import type { CaptureOpenshellResult } from "./adapters/openshell/client"; import { captureOpenshellCommand, stripAnsi } from "./adapters/openshell/client"; import { OPENSHELL_PROBE_TIMEOUT_MS } from "./adapters/openshell/timeouts"; import * as registry from "./state/registry"; import { resolveOpenshell } from "./adapters/openshell/resolve"; -import { getServiceStatuses, showStatus as showServiceStatus } from "./services"; +import { getServiceStatuses, showStatus as showServiceStatus } from "./tunnel/services"; function captureOpenshell( rootDir: string, diff --git a/src/lib/tunnel/README.md b/src/lib/tunnel/README.md new file mode 100644 index 0000000000..e75a878511 --- /dev/null +++ b/src/lib/tunnel/README.md @@ -0,0 +1,9 @@ + + + +# Tunnel and host services + +This folder contains host-side service lifecycle helpers used by `nemoclaw start`, +`nemoclaw stop`, and tunnel/port-forward related commands. Keep oclif parser glue +in `src/lib/commands/**`; service orchestration that starts or stops host +processes belongs here or in `src/lib/actions/**` when it is command-specific. diff --git a/src/lib/services-command.test.ts b/src/lib/tunnel/service-command.test.ts similarity index 98% rename from src/lib/services-command.test.ts rename to src/lib/tunnel/service-command.test.ts index 2e7fba9921..9cdb600448 100644 --- a/src/lib/services-command.test.ts +++ b/src/lib/tunnel/service-command.test.ts @@ -7,7 +7,7 @@ import { resolveDefaultSandboxName, runStartCommand, runStopCommand, -} from "../../dist/lib/services-command"; +} from "../../../dist/lib/tunnel/service-command"; describe("services command", () => { let savedEnv: Record; diff --git a/src/lib/services-command.ts b/src/lib/tunnel/service-command.ts similarity index 100% rename from src/lib/services-command.ts rename to src/lib/tunnel/service-command.ts diff --git a/src/lib/services-sandbox.test.ts b/src/lib/tunnel/services-sandbox.test.ts similarity index 99% rename from src/lib/services-sandbox.test.ts rename to src/lib/tunnel/services-sandbox.test.ts index 202025012c..f4530acbd4 100644 --- a/src/lib/services-sandbox.test.ts +++ b/src/lib/tunnel/services-sandbox.test.ts @@ -14,12 +14,12 @@ import { spawnSync as realSpawnSync } from "node:child_process"; // --------------------------------------------------------------------------- // eslint-disable-next-line @typescript-eslint/no-require-imports -const resolveOpenshellModule = require("../../dist/lib/adapters/openshell/resolve"); +const resolveOpenshellModule = require("../../../dist/lib/adapters/openshell/resolve"); import { stopSandboxChannels, stopAll, -} from "../../dist/lib/services"; +} from "../../../dist/lib/tunnel/services"; // --------------------------------------------------------------------------- // stopSandboxChannels diff --git a/src/lib/services.test.ts b/src/lib/tunnel/services.test.ts similarity index 98% rename from src/lib/services.test.ts rename to src/lib/tunnel/services.test.ts index 99b84a70eb..bb0ec2d5ae 100644 --- a/src/lib/services.test.ts +++ b/src/lib/tunnel/services.test.ts @@ -8,12 +8,13 @@ import { join, resolve } from "node:path"; import { tmpdir } from "node:os"; // Import from compiled dist/ so coverage is attributed correctly. -import { getServiceStatuses, showStatus, stopAll } from "../../dist/lib/services"; +import { getServiceStatuses, showStatus, stopAll } from "../../../dist/lib/tunnel/services"; const ollamaProxyDistPath = resolve( import.meta.dirname, "..", "..", + "..", "dist", "lib", "inference", diff --git a/src/lib/services.ts b/src/lib/tunnel/services.ts similarity index 97% rename from src/lib/services.ts rename to src/lib/tunnel/services.ts index cc2bfe4167..c4639ebd07 100644 --- a/src/lib/services.ts +++ b/src/lib/tunnel/services.ts @@ -15,11 +15,11 @@ import { } from "node:fs"; import { join } from "node:path"; -import { AGENT_PRODUCT_NAME, CLI_DISPLAY_NAME } from "./cli/branding"; -import { dockerSpawnSync } from "./adapters/docker"; -import { DASHBOARD_PORT } from "./core/ports"; -import { resolveOpenshell } from "./adapters/openshell/resolve"; -import { buildSubprocessEnv } from "./subprocess-env"; +import { AGENT_PRODUCT_NAME, CLI_DISPLAY_NAME } from "../cli/branding"; +import { dockerSpawnSync } from "../adapters/docker"; +import { DASHBOARD_PORT } from "../core/ports"; +import { resolveOpenshell } from "../adapters/openshell/resolve"; +import { buildSubprocessEnv } from "../subprocess-env"; // --------------------------------------------------------------------------- // Types @@ -429,7 +429,7 @@ export function stopAll(opts: ServiceOptions = {}): void { } try { - const { unloadOllamaModels } = require("./inference/ollama/proxy"); + const { unloadOllamaModels } = require("../inference/ollama/proxy"); unloadOllamaModels(); } catch { /* best-effort */ diff --git a/src/nemoclaw.ts b/src/nemoclaw.ts index 03035ec25a..b21c208b81 100644 --- a/src/nemoclaw.ts +++ b/src/nemoclaw.ts @@ -41,7 +41,6 @@ import type { SandboxEntry } from "./lib/state/registry"; const nim = require("./lib/inference/nim"); const shields = require("./lib/shields"); const { parseGatewayInference } = require("./lib/inference/config"); -const policies = require("./lib/policies"); const { probeProviderHealth } = require("./lib/inference/health"); const { buildStatusCommandDeps } = require("./lib/status-command-deps"); const { help, version } = require("./lib/actions/root-help"); diff --git a/test/cli.test.ts b/test/cli.test.ts index 631d1f3b56..02857a8c6b 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -307,7 +307,7 @@ function createDebugCommandTestEnv(prefix: string): Record { describe("CLI dispatch", () => { it("config get validates flags and values before dispatch", async () => { - const sandboxConfigModule = await import("../dist/lib/sandbox-config.js"); + const sandboxConfigModule = await import("../dist/lib/sandbox/config.js"); const { parseConfigGetArgs } = (sandboxConfigModule.default ?? sandboxConfigModule) as { parseConfigGetArgs: ( args: string[], diff --git a/test/config-set-cli-dispatch.test.ts b/test/config-set-cli-dispatch.test.ts index 4baba1f0b0..4e3f056af2 100644 --- a/test/config-set-cli-dispatch.test.ts +++ b/test/config-set-cli-dispatch.test.ts @@ -19,7 +19,7 @@ describe("config set CLI dispatch", () => { it("awaits configSet before completing the dispatcher", async () => { const cliPath = require.resolve("../dist/nemoclaw.js"); const registryPath = require.resolve("../dist/lib/state/registry.js"); - const sandboxConfigPath = require.resolve("../dist/lib/sandbox-config.js"); + const sandboxConfigPath = require.resolve("../dist/lib/sandbox/config.js"); const runnerPath = require.resolve("../dist/lib/runner.js"); const priorCli = require.cache[cliPath]; diff --git a/test/config-set-nested-ssrf.test.ts b/test/config-set-nested-ssrf.test.ts index b935a75409..014d11f487 100644 --- a/test/config-set-nested-ssrf.test.ts +++ b/test/config-set-nested-ssrf.test.ts @@ -9,7 +9,7 @@ const requireCache: Record = require.cache as any; describe("config set nested URL SSRF enforcement", () => { it("rejects nested object/array URL values that target private hosts", async () => { - const sandboxConfigPath = require.resolve("../dist/lib/sandbox-config"); + const sandboxConfigPath = require.resolve("../dist/lib/sandbox/config"); const openshellPath = require.resolve("../dist/lib/adapters/openshell/client"); const shieldsAuditPath = require.resolve("../dist/lib/shields/audit"); @@ -51,7 +51,7 @@ describe("config set nested URL SSRF enforcement", () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); try { - const { configSet } = require("../dist/lib/sandbox-config"); + const { configSet } = require("../dist/lib/sandbox/config"); const nestedValue = JSON.stringify({ primary: "https://api.nvidia.com/v1", fallback: ["https://example.com/v1", { internal: "http://localhost:8080/internal" }], @@ -84,7 +84,7 @@ describe("config set nested URL SSRF enforcement", () => { }); it("validates the key before doing URL or DNS validation", async () => { - const sandboxConfigPath = require.resolve("../dist/lib/sandbox-config"); + const sandboxConfigPath = require.resolve("../dist/lib/sandbox/config"); const openshellPath = require.resolve("../dist/lib/adapters/openshell/client"); const shieldsAuditPath = require.resolve("../dist/lib/shields/audit"); @@ -130,7 +130,7 @@ describe("config set nested URL SSRF enforcement", () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); try { - const { configSet } = require("../dist/lib/sandbox-config"); + const { configSet } = require("../dist/lib/sandbox/config"); await expect( configSet("sandbox-ssrf-test", { @@ -163,7 +163,7 @@ describe("config set nested URL SSRF enforcement", () => { }); it("accepts nested object/array URL values when all are public", async () => { - const sandboxConfigPath = require.resolve("../dist/lib/sandbox-config"); + const sandboxConfigPath = require.resolve("../dist/lib/sandbox/config"); const openshellPath = require.resolve("../dist/lib/adapters/openshell/client"); const shieldsAuditPath = require.resolve("../dist/lib/shields/audit"); @@ -205,7 +205,7 @@ describe("config set nested URL SSRF enforcement", () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); try { - const { configSet } = require("../dist/lib/sandbox-config"); + const { configSet } = require("../dist/lib/sandbox/config"); const nestedValue = JSON.stringify({ primary: "https://93.184.216.34/v1", fallback: ["http://93.184.216.35/v1", { backup: "https://93.184.216.36/v2" }], @@ -238,7 +238,7 @@ describe("config set nested URL SSRF enforcement", () => { }); it("ignores nested non-http URL-like strings and does not crash", async () => { - const sandboxConfigPath = require.resolve("../dist/lib/sandbox-config"); + const sandboxConfigPath = require.resolve("../dist/lib/sandbox/config"); const openshellPath = require.resolve("../dist/lib/adapters/openshell/client"); const shieldsAuditPath = require.resolve("../dist/lib/shields/audit"); @@ -280,7 +280,7 @@ describe("config set nested URL SSRF enforcement", () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); try { - const { configSet } = require("../dist/lib/sandbox-config"); + const { configSet } = require("../dist/lib/sandbox/config"); const nestedValue = JSON.stringify({ ftpUrl: "ftp://files.example.com", plainText: "not-a-url", @@ -314,7 +314,7 @@ describe("config set nested URL SSRF enforcement", () => { }); it("recognizes mixed-case http and https schemes in nested values", async () => { - const sandboxConfigPath = require.resolve("../dist/lib/sandbox-config"); + const sandboxConfigPath = require.resolve("../dist/lib/sandbox/config"); const openshellPath = require.resolve("../dist/lib/adapters/openshell/client"); const shieldsAuditPath = require.resolve("../dist/lib/shields/audit"); @@ -356,7 +356,7 @@ describe("config set nested URL SSRF enforcement", () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); try { - const { configSet } = require("../dist/lib/sandbox-config"); + const { configSet } = require("../dist/lib/sandbox/config"); const nestedValue = JSON.stringify({ primary: "HTTP://93.184.216.34/v1", fallback: ["HtTpS://93.184.216.35/v2", { backup: "hTtP://93.184.216.36/v3" }], @@ -389,7 +389,7 @@ describe("config set nested URL SSRF enforcement", () => { }); it("redacts credentials, query strings, and fragments in validation errors", async () => { - const sandboxConfigPath = require.resolve("../dist/lib/sandbox-config"); + const sandboxConfigPath = require.resolve("../dist/lib/sandbox/config"); const openshellPath = require.resolve("../dist/lib/adapters/openshell/client"); const shieldsAuditPath = require.resolve("../dist/lib/shields/audit"); @@ -431,7 +431,7 @@ describe("config set nested URL SSRF enforcement", () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); try { - const { configSet } = require("../dist/lib/sandbox-config"); + const { configSet } = require("../dist/lib/sandbox/config"); const nestedValue = JSON.stringify({ primary: "http://user:pass@127.0.0.1:8080/private/path?token=secret#frag", }); diff --git a/test/config-set.test.ts b/test/config-set.test.ts index 8229560909..5aa24f9e0c 100644 --- a/test/config-set.test.ts +++ b/test/config-set.test.ts @@ -19,7 +19,7 @@ const { resolveAgentConfig, buildRecomputeSandboxConfigHashScript, selectDockerDriverSandboxContainer, -} = require("../dist/lib/sandbox-config"); +} = require("../dist/lib/sandbox/config"); type MutableScalar = string | number | boolean | null | undefined; type MutableValue = MutableScalar | MutableMap | MutableValue[]; diff --git a/test/credential-exposure.test.ts b/test/credential-exposure.test.ts index 211acd7f6f..fc975b2956 100644 --- a/test/credential-exposure.test.ts +++ b/test/credential-exposure.test.ts @@ -13,7 +13,7 @@ import { createRequire } from "node:module"; import { describe, it, expect } from "vitest"; import { buildSubprocessEnv as buildCliSubprocessEnv } from "../src/lib/subprocess-env"; import { buildSubprocessEnv as buildPluginSubprocessEnv } from "../nemoclaw/src/lib/subprocess-env"; -import { getCurlTimingArgs } from "../src/lib/http-probe"; +import { getCurlTimingArgs } from "../src/lib/adapters/http/probe"; const require = createRequire(import.meta.url); const { buildProviderArgs } = require("../dist/lib/onboard/providers.js") as { diff --git a/test/e2e/test-rebuild-openclaw.sh b/test/e2e/test-rebuild-openclaw.sh index 66c564aadc..7ce79484d1 100755 --- a/test/e2e/test-rebuild-openclaw.sh +++ b/test/e2e/test-rebuild-openclaw.sh @@ -246,14 +246,14 @@ NEMOCLAW_MODULE_DIR="$(node -e " if (m) { const nodeDir = path.dirname(path.dirname(m[1])); const candidate = path.join(nodeDir, 'lib/node_modules/nemoclaw'); - if (fs.existsSync(path.join(candidate, 'dist/lib/policies.js'))) { + if (fs.existsSync(path.join(candidate, 'dist/lib/policy/index.js'))) { console.log(candidate); process.exit(0); } } // Last resort: relative to the repo root const repoCandidate = '${REPO_ROOT}'; - if (fs.existsSync(path.join(repoCandidate, 'dist/lib/policies.js'))) { + if (fs.existsSync(path.join(repoCandidate, 'dist/lib/policy/index.js'))) { console.log(repoCandidate); process.exit(0); } @@ -266,7 +266,7 @@ diag "NemoClaw module dir: ${NEMOCLAW_MODULE_DIR}" for preset in npm pypi; do info " Applying preset: ${preset}" node -e " - const policies = require('${NEMOCLAW_MODULE_DIR}/dist/lib/policies.js'); + const policies = require('${NEMOCLAW_MODULE_DIR}/dist/lib/policy/index.js'); const ok = policies.applyPreset('${SANDBOX_NAME}', '${preset}'); if (!ok) { console.error('applyPreset returned false for ${preset}'); process.exit(1); } " || fail "Failed to apply preset: ${preset}" diff --git a/test/onboard-preset-diff.test.ts b/test/onboard-preset-diff.test.ts index f2d701c8f2..7662b142e7 100644 --- a/test/onboard-preset-diff.test.ts +++ b/test/onboard-preset-diff.test.ts @@ -50,7 +50,7 @@ function buildPreamble({ const credPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "credentials", "store.js")); const runnerPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "runner.js")); const registryPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "state", "registry.js")); - const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policies.js")); + const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "index.js")); const resolveOpenshellPath = JSON.stringify( path.join(repoRoot, "dist", "lib", "adapters", "openshell", "resolve.js"), ); diff --git a/test/onboard-readiness.test.ts b/test/onboard-readiness.test.ts index 85635f316c..de5ad3d811 100644 --- a/test/onboard-readiness.test.ts +++ b/test/onboard-readiness.test.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { describe, it, expect } from "vitest"; -import { applyPreset, buildPolicySetCommand, buildPolicyGetCommand } from "../dist/lib/policies"; +import { applyPreset, buildPolicySetCommand, buildPolicyGetCommand } from "../dist/lib/policy"; type OnboardReadinessInternals = { hasStaleGateway: (output: string | null | undefined) => boolean; diff --git a/test/onboard.test.ts b/test/onboard.test.ts index 6dc43b3325..30ffccdaeb 100644 --- a/test/onboard.test.ts +++ b/test/onboard.test.ts @@ -14,7 +14,7 @@ import type { AgentDefinition } from "../dist/lib/agent/defs.js"; import { loadAgent } from "../dist/lib/agent/defs.js"; import { buildChain, buildControlUiUrls } from "../dist/lib/dashboard/contract.js"; import { NAME_ALLOWED_FORMAT } from "../dist/lib/name-validation.js"; -import { stageOptimizedSandboxBuildContext } from "../dist/lib/sandbox-build-context.js"; +import { stageOptimizedSandboxBuildContext } from "../dist/lib/sandbox/build-context.js"; type ShimScalar = string | number | boolean | null | undefined; type ShimCallable = (...args: readonly string[]) => ShimValue; @@ -2368,12 +2368,16 @@ const { loadAgent } = require(${agentDefsPath}); }); it("allows slow sandbox create recovery to wait beyond 60 seconds", () => { + const envSource = fs.readFileSync( + path.join(import.meta.dirname, "..", "src", "lib", "onboard", "env.ts"), + "utf-8", + ); const source = fs.readFileSync( path.join(import.meta.dirname, "..", "src", "lib", "onboard.ts"), "utf-8", ); - assert.match(source, /NEMOCLAW_SANDBOX_READY_TIMEOUT", 180/); + assert.match(envSource, /NEMOCLAW_SANDBOX_READY_TIMEOUT", 180/); assert.match(source, /Math\.ceil\(SANDBOX_READY_TIMEOUT_SECS \/ 2\)/); assert.match(source, /within \$\{SANDBOX_READY_TIMEOUT_SECS\}s/); }); @@ -4631,7 +4635,7 @@ const { setupInference } = require(${onboardPath}); "utf-8", ); const probeSource = fs.readFileSync( - path.join(import.meta.dirname, "..", "src", "lib", "http-probe.ts"), + path.join(import.meta.dirname, "..", "src", "lib", "adapters", "http", "probe.ts"), "utf-8", ); const recoverySource = fs.readFileSync( @@ -4639,7 +4643,7 @@ const { setupInference } = require(${onboardPath}); "utf-8", ); - assert.match(onboardSource, /http-probe/); + assert.match(onboardSource, /adapters\/http\/probe/); assert.match(probeSource, /return \["--connect-timeout", "10", "--max-time", "60"\];/); assert.match(recoverySource, /failure\.curlStatus === 2/); assert.match(recoverySource, /local curl invocation error/); @@ -4851,9 +4855,9 @@ const { setupInference } = require(${onboardPath}); path.join(import.meta.dirname, "..", "src", "lib", "onboard.ts"), "utf-8", ); - const { streamSandboxCreate } = require("../dist/lib/sandbox-create-stream"); + const { streamSandboxCreate } = require("../dist/lib/sandbox/create-stream"); - assert.match(onboardSource, /sandbox-create-stream/); + assert.match(onboardSource, /sandbox\/create-stream/); assert.equal(typeof streamSandboxCreate, "function"); }); @@ -6809,7 +6813,7 @@ childProcess.spawn = fakeSpawn; // childProcess object above does not reach it. Patch the cached module // directly so streamSandboxCreate (called by createSandbox) doesn't spawn // a real bash process that tries to hit a live gateway. -const sandboxCreateStreamMod = require(${JSON.stringify(path.join(repoRoot, "dist", "lib", "sandbox-create-stream.js"))}); +const sandboxCreateStreamMod = require(${JSON.stringify(path.join(repoRoot, "dist", "lib", "sandbox", "create-stream.js"))}); const _origStreamCreate = sandboxCreateStreamMod.streamSandboxCreate; sandboxCreateStreamMod.streamSandboxCreate = (command, env, options = {}) => { return _origStreamCreate(command, env, { ...options, spawnImpl: fakeSpawn }); @@ -7888,7 +7892,7 @@ const { createSandbox } = require(${onboardPath}); const scriptPath = path.join(tmpDir, "messaging-noninteractive.js"); const onboardPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "onboard.js")); const runnerPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "runner.js")); - const httpProbePath = JSON.stringify(path.join(repoRoot, "dist", "lib", "http-probe.js")); + const httpProbePath = JSON.stringify(path.join(repoRoot, "dist", "lib", "adapters", "http", "probe.js")); fs.mkdirSync(fakeBin, { recursive: true }); fs.writeFileSync(path.join(fakeBin, "openshell"), "#!/usr/bin/env bash\nexit 0\n", { diff --git a/test/policies.test.ts b/test/policies.test.ts index f3b6dec7c1..fa54965050 100644 --- a/test/policies.test.ts +++ b/test/policies.test.ts @@ -9,7 +9,7 @@ import { createRequire } from "node:module"; import type { Interface as ReadlineInterface } from "node:readline"; import { afterEach, describe, it, expect, vi } from "vitest"; import { spawnSync } from "node:child_process"; -import policies from "../dist/lib/policies"; +import policies from "../dist/lib/policy"; import { execTimeout } from "./helpers/timeouts"; const requireForTest = createRequire(import.meta.url); @@ -18,7 +18,7 @@ const YAML = requireForTest("yaml"); const REPO_ROOT = path.join(import.meta.dirname, ".."); const CLI_PATH = JSON.stringify(path.join(REPO_ROOT, "dist", "nemoclaw.js")); const CREDENTIALS_PATH = JSON.stringify(path.join(REPO_ROOT, "dist", "lib", "credentials", "store.js")); -const POLICIES_PATH = JSON.stringify(path.join(REPO_ROOT, "dist", "lib", "policies.js")); +const POLICIES_PATH = JSON.stringify(path.join(REPO_ROOT, "dist", "lib", "policy", "index.js")); const REGISTRY_PATH = JSON.stringify(path.join(REPO_ROOT, "dist", "lib", "state", "registry.js")); const SELECT_FROM_LIST_ITEMS = [ { name: "npm", description: "npm and Yarn registry access" }, diff --git a/test/policy-tiers-onboard.test.ts b/test/policy-tiers-onboard.test.ts index ab361cfc36..d04ff1c9af 100644 --- a/test/policy-tiers-onboard.test.ts +++ b/test/policy-tiers-onboard.test.ts @@ -112,7 +112,7 @@ console.log = () => {}; }); it("restricted tier produces an empty preset list", () => { - const tiersPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "tiers.js")); + const tiersPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "tiers.js")); const script = buildPreamble({ tierEnv: "restricted" }) + String.raw` @@ -132,7 +132,7 @@ console.log = () => {}; }); it("balanced tier resolves presets all with read-write access", () => { - const tiersPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "tiers.js")); + const tiersPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "tiers.js")); const script = buildPreamble({ tierEnv: "balanced" }) + String.raw` @@ -155,7 +155,7 @@ console.log = () => {}; }); it("open tier resolves presets including at least one social/messaging preset", () => { - const tiersPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "tiers.js")); + const tiersPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "tiers.js")); const script = buildPreamble({ tierEnv: "open" }) + String.raw` @@ -181,7 +181,7 @@ console.log = () => {}; }); it("a preset can be deselected via selected option in resolveTierPresets", () => { - const tiersPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "tiers.js")); + const tiersPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "tiers.js")); const script = buildPreamble({ tierEnv: "balanced" }) + String.raw` @@ -201,7 +201,7 @@ console.log = () => {}; }); it("access level can be restricted from read-write to read via override", () => { - const tiersPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "tiers.js")); + const tiersPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "tiers.js")); const script = buildPreamble({ tierEnv: "balanced" }) + String.raw` @@ -252,7 +252,7 @@ console.log = (...args) => lines.push(args.join(" ")); }); it("selected tier is persisted to the registry via updateSandbox({ policyTier })", () => { - const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policies.js")); + const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "index.js")); const script = buildPreamble({ tierEnv: "open", policyMode: "skip" }) + String.raw` @@ -291,7 +291,7 @@ console.log = (...args) => lines.push(args.join(" ")); }); it("omits Brave from policy preset selection when web search is unsupported", () => { - const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policies.js")); + const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "index.js")); const script = buildPreamble({ tierEnv: "balanced", @@ -332,7 +332,7 @@ console.log = () => {}; }); it("removes a previously-applied Brave preset when web search is unsupported", () => { - const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policies.js")); + const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "index.js")); const script = buildPreamble({ tierEnv: "balanced", @@ -378,7 +378,7 @@ console.log = () => {}; }); it("clamps resumed policy presets to web-search-supported presets", () => { - const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policies.js")); + const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "index.js")); const script = buildPreamble({ tierEnv: "balanced", @@ -418,7 +418,7 @@ console.log = () => {}; }); it("clamps an unsupported-only resumed policy preset list to empty", () => { - const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policies.js")); + const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "index.js")); const script = buildPreamble({ tierEnv: "balanced", @@ -458,7 +458,7 @@ console.log = () => {}; }); it("preserves a resumed custom preset whose name matches an unsupported built-in", () => { - const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policies.js")); + const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "index.js")); const script = buildPreamble({ tierEnv: "balanced", @@ -499,7 +499,7 @@ console.log = () => {}; }); it("preserves a non-interactive custom preset whose name matches an unsupported built-in", () => { - const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policies.js")); + const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "index.js")); const script = buildPreamble({ tierEnv: "balanced", @@ -548,7 +548,7 @@ console.log = () => {}; // that the user may have meant NEMOCLAW_POLICY_TIER when the value looks like // a tier name. it("falls back to tier suggestions when NEMOCLAW_POLICY_MODE is unknown (#2429)", () => { - const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policies.js")); + const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "index.js")); const script = buildPreamble({ tierEnv: "balanced", @@ -591,7 +591,7 @@ console.log = () => {}; }); it("omits the tier-name hint for a non-tier invalid value (#2429)", () => { - const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policies.js")); + const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "index.js")); const script = buildPreamble({ tierEnv: "balanced", @@ -628,8 +628,8 @@ console.log = () => {}; }); describe("selectTierPresetsAndAccess", () => { - const tiersPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "tiers.js")); - const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policies.js")); + const tiersPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "tiers.js")); + const policiesPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "policy", "index.js")); function buildPresetsScript(body: string): string { const credPath = JSON.stringify(path.join(repoRoot, "dist", "lib", "credentials", "store.js")); diff --git a/test/policy-tiers.test.ts b/test/policy-tiers.test.ts index 7c9f968e91..dd9ce82eb6 100644 --- a/test/policy-tiers.test.ts +++ b/test/policy-tiers.test.ts @@ -11,8 +11,8 @@ // - Integration with the existing policies module import { describe, expect, it } from "vitest"; -import policies from "../dist/lib/policies"; -import tiers from "../dist/lib/tiers"; +import policies from "../dist/lib/policy"; +import tiers from "../dist/lib/policy/tiers"; interface TierPreset { name: string; diff --git a/test/repro-2010.test.ts b/test/repro-2010.test.ts index 4f4d976f94..8524a6361f 100644 --- a/test/repro-2010.test.ts +++ b/test/repro-2010.test.ts @@ -17,7 +17,7 @@ import os from "node:os"; import { spawnSync } from "node:child_process"; const REPO_ROOT = path.join(import.meta.dirname, ".."); -const POLICIES_PATH = path.join(REPO_ROOT, "dist", "lib", "policies.js"); +const POLICIES_PATH = path.join(REPO_ROOT, "dist", "lib", "policy", "index.js"); const RUNNER_PATH = path.join(REPO_ROOT, "dist", "lib", "runner.js"); const CLI_PATH = path.join(REPO_ROOT, "bin", "nemoclaw.js"); const REGISTRY_PATH = path.join(REPO_ROOT, "dist", "lib", "state", "registry.js"); diff --git a/test/repro-2666-silent-list-status.test.ts b/test/repro-2666-silent-list-status.test.ts index de2e22f68e..f5a8c55eb2 100644 --- a/test/repro-2666-silent-list-status.test.ts +++ b/test/repro-2666-silent-list-status.test.ts @@ -29,7 +29,7 @@ import { type ListSandboxesCommandDeps, getSandboxInventory, renderSandboxInventoryText, -} from "../dist/lib/inventory-commands.js"; +} from "../dist/lib/inventory/index.js"; import { recoverRegistryEntriesWithFallback } from "../dist/lib/list-command-deps.js"; const CLI = path.join(import.meta.dirname, "..", "bin", "nemoclaw.js"); diff --git a/test/sandbox-build-context.test.ts b/test/sandbox-build-context.test.ts index 1ed65d4090..03bf0b6132 100644 --- a/test/sandbox-build-context.test.ts +++ b/test/sandbox-build-context.test.ts @@ -10,7 +10,7 @@ import { collectBuildContextStats, stageLegacySandboxBuildContext, stageOptimizedSandboxBuildContext, -} from "../dist/lib/sandbox-build-context"; +} from "../dist/lib/sandbox/build-context"; describe("sandbox build context staging", () => { it("optimized staging excludes blueprint .venv and extra scripts while preserving required files", () => { diff --git a/test/shellquote-sandbox.test.ts b/test/shellquote-sandbox.test.ts index 2be5c2038e..f74dea52ff 100644 --- a/test/shellquote-sandbox.test.ts +++ b/test/shellquote-sandbox.test.ts @@ -48,7 +48,7 @@ describe("sandboxName command hardening in onboard.js", () => { pathToFileURL(path.join(repoRoot, "dist", "lib", "credentials", "store.js")).href, ); const streamUrl = JSON.stringify( - pathToFileURL(path.join(repoRoot, "dist", "lib", "sandbox-create-stream.js")).href, + pathToFileURL(path.join(repoRoot, "dist", "lib", "sandbox", "create-stream.js")).href, ); fs.mkdirSync(fakeBin, { recursive: true }); diff --git a/test/wsl2-probe-timeout.test.ts b/test/wsl2-probe-timeout.test.ts index 7967de66e6..b28c3fa786 100644 --- a/test/wsl2-probe-timeout.test.ts +++ b/test/wsl2-probe-timeout.test.ts @@ -64,7 +64,7 @@ describe("WSL2 inference verification timeouts (issue #987)", () => { describe("retry logic in probeOpenAiLikeEndpoint", () => { function runProbeWithCurlStatuses(statuses: number[]) { - const httpProbePath = require.resolve("../dist/lib/http-probe.js"); + const httpProbePath = require.resolve("../dist/lib/adapters/http/probe.js"); const platformPath = require.resolve("../dist/lib/platform.js"); const probesPath = require.resolve("../dist/lib/inference/onboard-probes.js"); const httpProbe = require(httpProbePath); @@ -151,7 +151,7 @@ describe("WSL2 inference verification timeouts (issue #987)", () => { }; function runProbeWithResults(results: ProbeResultFixture[], opts: { isWsl?: boolean } = {}) { - const httpProbePath = require.resolve("../dist/lib/http-probe.js"); + const httpProbePath = require.resolve("../dist/lib/adapters/http/probe.js"); const platformPath = require.resolve("../dist/lib/platform.js"); const probesPath = require.resolve("../dist/lib/inference/onboard-probes.js"); const httpProbe = require(httpProbePath);