Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ci/source-architecture-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"src/lib/actions/uninstall/run-plan.ts": 26,
"src/lib/inference/onboard-probes.ts": 20,
"src/lib/inference/vllm.ts": 21,
"src/lib/onboard.ts": 219,
"src/lib/onboard.ts": 212,
"src/lib/onboard/machine/handlers/sandbox.ts": 21,
"src/lib/sandbox/config.ts": 22,
"src/lib/shields/index.ts": 23
Expand Down
142 changes: 38 additions & 104 deletions src/lib/onboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,6 @@ const { isLinuxDockerDriverGatewayEnabled } = dockerDriverPlatform;
const {
reconcileGatewayGpuReuseForGpuIntent,
}: typeof import("./onboard/gateway-gpu-passthrough") = require("./onboard/gateway-gpu-passthrough");
const {
syncPresetSelection,
}: typeof import("./onboard/policy-preset-sync") = require("./onboard/policy-preset-sync");
const {
maybeForceE2eStepFailure,
}: typeof import("./onboard/e2e-failure-injection") = require("./onboard/e2e-failure-injection");
Expand Down Expand Up @@ -495,10 +492,7 @@ const {
}: typeof import("./onboard/machine/initial-flow-composition") = require("./onboard/machine/initial-flow-composition");
const { skippedStepMessage }: typeof import("./onboard/skipped-step-message") =
require("./onboard/skipped-step-message");
const policies: typeof import("./policy") = require("./policy");
const policyPresetCarry: typeof import("./onboard/policy-preset-persistence") = require("./onboard/policy-preset-persistence");
const tiers: typeof import("./policy/tiers") = require("./policy/tiers");
const policyTierEnv: typeof import("./onboard/policy-tier-env") = require("./onboard/policy-tier-env");
const { ensureUsageNoticeConsent } = require("./onboard/usage-notice");
const {
findAvailableDashboardPort,
Expand Down Expand Up @@ -589,7 +583,6 @@ import {
setupHermesToolGateways,
stringSetsEqual,
} from "./onboard/hermes-managed-tools";
import { mergePolicyMessagingChannels } from "./onboard/messaging-policy-presets";
import { filterEnabledChannelsByAgent } from "./onboard/messaging-state";
import { getValidatedMessagingTokenByEnvKey } from "./onboard/messaging-token";
import * as ollamaFlow from "./onboard/ollama-probe-failure";
Expand All @@ -600,15 +593,7 @@ import type {
OpenShellInstallDeps,
OpenShellInstallResult,
} from "./onboard/openshell-install";
import { getSuggestedPolicyPresets } from "./onboard/policy-presets";
import {
computeSetupPresetSuggestions as computeSetupPresetSuggestionsImpl,
preparePolicyPresetResumeSelection,
type SetupPolicySelectionOptions,
type SetupPresetSuggestionOptions,
setupPoliciesWithSelection as setupPoliciesWithSelectionImpl,
} from "./onboard/policy-selection";
import { createPolicySelectionPromptHelpers } from "./onboard/policy-selection-prompts";
import { createOnboardPolicyApplication } from "./onboard/policy-selection";
import {
printLowMemoryWarning,
printMessagingProviderMissing,
Expand Down Expand Up @@ -3682,89 +3667,6 @@ const setupOpenclaw = createOpenclawSetup({
cleanupTempDir,
});

// ── Step 7: Policy presets ───────────────────────────────────────

function arePolicyPresetsApplied(sandboxName: string, selectedPresets: string[] = []): boolean {
if (!Array.isArray(selectedPresets) || selectedPresets.length === 0) return false;
const applied = new Set(policies.getAppliedPresets(sandboxName));
return selectedPresets.every((preset) => applied.has(preset));
}

function getPolicySelectionPromptHelpers(): ReturnType<typeof createPolicySelectionPromptHelpers> {
return createPolicySelectionPromptHelpers({
tiers,
policyTierEnv,
isNonInteractive,
note,
prompt,
selectFromNumberedMenuOrExit,
makeOnboardCancelExit,
sandboxCancelRollback,
useColor: USE_COLOR,
});
}

async function selectPolicyTier(): Promise<string> {
return getPolicySelectionPromptHelpers().selectPolicyTier();
}

async function selectTierPresetsAndAccess(
tierName: string,
allPresets: Array<{ name: string; description?: string }>,
initialSelected?: string[],
): Promise<Array<{ name: string; access: string }>> {
return getPolicySelectionPromptHelpers().selectTierPresetsAndAccess(
tierName,
allPresets,
initialSelected,
);
}

async function presetsCheckboxSelector(
allPresets: Array<{ name: string; description: string }>,
initialSelected: string[],
): Promise<string[]> {
return getPolicySelectionPromptHelpers().presetsCheckboxSelector(allPresets, initialSelected);
}

const computeSetupPresetSuggestions = (
tierName: string,
options: SetupPresetSuggestionOptions = {},
): string[] =>
computeSetupPresetSuggestionsImpl(
{ policies, tiers, localInferenceProviders: [...LOCAL_INFERENCE_PROVIDERS, "llama-cpp-local"] },
tierName,
options,
);
async function setupPoliciesWithSelection(
sandboxName: string,
options: SetupPolicySelectionOptions = {},
) {
return sandboxMutationLock.withSandboxMutationLock(sandboxName, () =>
setupPoliciesWithSelectionImpl(
{
policies,
tiers,
localInferenceProviders: [...LOCAL_INFERENCE_PROVIDERS, "llama-cpp-local"],
step,
note,
isNonInteractive,
waitForSandboxReady,
waitForSandboxControlPlaneReady: finalizationHandlerDeps.waitForSandboxControlPlaneReady,
syncPresetSelection,
selectPolicyTier,
setPolicyTier: (s, t) => registry.updateSandbox(s, { policyTier: t }),
getRecordedPolicyTier: (s) => registry.getSandbox(s)?.policyTier ?? null,
selectTierPresetsAndAccess,
parsePolicyPresetEnv,
env: process.env,
},
sandboxName,
options,
),
);
}

const {
buildChain,
buildControlUiUrls,
Expand Down Expand Up @@ -3804,6 +3706,39 @@ const sandboxCancelRollback = installSandboxCancelRollback({
clearOnboardSession: onboardSession.clearSession,
}); // #4614

const {
arePolicyPresetsApplied,
computeSetupPresetSuggestions,
filterSetupPolicyPresets,
getSuggestedPolicyPresets,
mergePolicyMessagingChannels,
preparePolicyPresetResumeSelection,
presetsCheckboxSelector,
resolveSandboxBaselinePolicy,
selectPolicyTier,
selectTierPresetsAndAccess,
setupPoliciesWithSelection,
validatePolicyTierEnvEarly,
} = createOnboardPolicyApplication({
localInferenceProviders: [...LOCAL_INFERENCE_PROVIDERS, "llama-cpp-local"],
step,
note,
isNonInteractive,
prompt,
selectFromNumberedMenuOrExit,
makeOnboardCancelExit,
sandboxCancelRollback,
useColor: USE_COLOR,
withSandboxMutationLock: sandboxMutationLock.withSandboxMutationLock,
waitForSandboxReady,
waitForSandboxControlPlaneReady: finalizationHandlerDeps.waitForSandboxControlPlaneReady,
setPolicyTier: (sandboxName, tierName) =>
registry.updateSandbox(sandboxName, { policyTier: tierName }),
getRecordedPolicyTier: (sandboxName) => registry.getSandbox(sandboxName)?.policyTier ?? null,
parsePolicyPresetEnv,
env: process.env,
});

const startRecordedStep = onboardRuntimeBoundary.startRecordedStep.bind(onboardRuntimeBoundary);
const recordStepComplete = onboardRuntimeBoundary.recordStepComplete.bind(onboardRuntimeBoundary);
const recordStepSkipped = onboardRuntimeBoundary.recordStepSkipped.bind(onboardRuntimeBoundary);
Expand Down Expand Up @@ -3834,7 +3769,7 @@ async function preflightAuthoritativeRebuildTarget(
await authoritativeRebuildTarget.preflightAuthoritativeRebuildTarget(
{ ...opts, controlUiPort: opts.controlUiPort ?? null },
{
resolveBaselinePolicy: (sandboxName) => policies.resolveSandboxBaselinePolicy(sandboxName),
resolveBaselinePolicy: resolveSandboxBaselinePolicy,
runFatalRuntimePreflight: () =>
fatalRuntimePreflight.runFatalOnboardRuntimePreflight(
{
Expand Down Expand Up @@ -3914,7 +3849,7 @@ async function runOnboard(opts: OnboardOptions = {}): Promise<void> {
initialPreResolvedMetadata: opts.preResolvedBaseImageMetadata,
});
const onboardingComputePlan = dockerDriverPlatform.resolveCurrentOpenShellComputePlan();
if (isNonInteractive()) policyTierEnv.validatePolicyTierEnvEarly();
if (isNonInteractive()) validatePolicyTierEnvEarly();
const noticeAccepted = await ensureUsageNoticeConsent({
nonInteractive: isNonInteractive(),
acceptedByFlag: opts.acceptThirdPartySoftware === true,
Expand Down Expand Up @@ -4445,8 +4380,7 @@ async function runOnboard(opts: OnboardOptions = {}): Promise<void> {
mergePolicyMessagingChannels,
// biome-ignore format: keep src/lib/onboard.ts net-neutral for growth guardrail.
verifyCompatibleEndpointSandboxSmoke: (options) => verifyCompatibleEndpointSandboxSmoke({ ...options, runOpenshell: runCoreGatewayOpenshell, redact }),
preparePolicyPresetResumeSelection: (name, options) =>
preparePolicyPresetResumeSelection({ policies }, name, options),
preparePolicyPresetResumeSelection,
arePolicyPresetsApplied,
skippedStepMessage,
recordStateSkipped,
Expand Down Expand Up @@ -4655,7 +4589,7 @@ module.exports = {
getSuggestedPolicyPresets,
computeSetupPresetSuggestions,
mergeRequiredHermesToolGatewayPolicyPresets,
filterSetupPolicyPresets: policies.filterSetupPolicyPresets,
filterSetupPolicyPresets,
LOCAL_INFERENCE_PROVIDERS,
presetsCheckboxSelector,
selectPolicyTier,
Expand Down
79 changes: 79 additions & 0 deletions src/lib/onboard/policy-selection-application.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { describe, expect, it, vi } from "vitest";
import {
createOnboardPolicyApplication,
type OnboardPolicyApplicationDeps,
} from "./policy-selection";
import { selectFromNumberedMenuOrExit } from "./prompt-helpers";

const { seedInitialPolicyContext, syncPresetSelection } = vi.hoisted(() => ({
seedInitialPolicyContext: vi.fn(),
syncPresetSelection: vi.fn(),
}));

vi.mock("../policy", () => ({
clampSetupPolicyPresetNames: vi.fn((names: string[]) => names),
customPresetOwnsNetworkPolicyKey: vi.fn(() => false),
filterSetupPolicyPresets: vi.fn(),
getAppliedPresets: vi.fn(() => []),
listCustomPresets: vi.fn(() => []),
listSetupPolicyPresets: vi.fn(() => [{ name: "npm" }]),
resolveSandboxBaselinePolicy: vi.fn(),
setupPolicyPresetSupported: vi.fn(() => true),
}));
vi.mock("./policy-context-seed", () => ({ seedInitialPolicyContext }));
vi.mock("./policy-preset-sync", () => ({ syncPresetSelection }));

describe("onboarding policy application", () => {
it("runs policy application while holding the sandbox mutation lock", async () => {
const events: string[] = [];
const withSandboxMutationLock: OnboardPolicyApplicationDeps["withSandboxMutationLock"] = vi.fn(
async (_sandboxName, action) => {
events.push("lock entered");
try {
return await action();
} finally {
events.push("lock released");
}
},
);
syncPresetSelection.mockImplementation(() => events.push("policies synchronized"));
seedInitialPolicyContext.mockImplementation(() => events.push("policy context seeded"));
const application = createOnboardPolicyApplication({
localInferenceProviders: [],
step: vi.fn(),
note: vi.fn(),
isNonInteractive: vi.fn(() => true),
prompt: vi.fn(async () => ""),
selectFromNumberedMenuOrExit,
makeOnboardCancelExit: (rollback, cleanup) => () => {
cleanup();
rollback.markCancelled();
},
sandboxCancelRollback: { markCancelled: vi.fn() },
useColor: false,
withSandboxMutationLock,
waitForSandboxReady: vi.fn(() => true),
waitForSandboxControlPlaneReady: vi.fn(() => true),
setPolicyTier: vi.fn(),
getRecordedPolicyTier: vi.fn(() => null),
parsePolicyPresetEnv: vi.fn(() => []),
env: {},
});

await expect(
application.setupPoliciesWithSelection("alpha", { selectedPresets: ["npm"] }),
).resolves.toEqual(["npm"]);
expect(withSandboxMutationLock).toHaveBeenCalledOnce();
expect(withSandboxMutationLock).toHaveBeenCalledWith("alpha", expect.any(Function));
expect(syncPresetSelection).toHaveBeenCalledWith("alpha", [], ["npm"]);
expect(events).toEqual([
"lock entered",
"policies synchronized",
"policy context seeded",
"lock released",
]);
});
});
Loading
Loading