diff --git a/nemoclaw-blueprint/policies/presets/local-inference.yaml b/nemoclaw-blueprint/policies/presets/local-inference.yaml new file mode 100644 index 00000000000..c692cdce252 --- /dev/null +++ b/nemoclaw-blueprint/policies/presets/local-inference.yaml @@ -0,0 +1,28 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +preset: + name: local-inference + description: "Local inference access (Ollama, vLLM) via host gateway" + +network_policies: + local_inference: + name: local_inference + endpoints: + - host: host.openshell.internal + port: 11434 + protocol: rest + enforcement: enforce + rules: + - allow: { method: GET, path: "/**" } + - allow: { method: POST, path: "/**" } + - host: host.openshell.internal + port: 8000 + protocol: rest + enforcement: enforce + rules: + - allow: { method: GET, path: "/**" } + - allow: { method: POST, path: "/**" } + binaries: + - { path: /usr/local/bin/openclaw } + - { path: /usr/local/bin/claude } diff --git a/src/lib/onboard.ts b/src/lib/onboard.ts index 4ae3f674699..052dfdc8715 100644 --- a/src/lib/onboard.ts +++ b/src/lib/onboard.ts @@ -4502,8 +4502,14 @@ async function setupMessagingChannels() { return selected; } -function getSuggestedPolicyPresets({ enabledChannels = null, webSearchConfig = null } = {}) { +function getSuggestedPolicyPresets({ enabledChannels = null, webSearchConfig = null, provider = null } = {}) { const suggestions = ["pypi", "npm"]; + + // Auto-suggest local-inference preset when a local provider is selected + const localProviders = ["ollama-local", "vllm-local", "nim-local"]; + if (provider && localProviders.includes(provider)) { + suggestions.push("local-inference"); + } const usesExplicitMessagingSelection = Array.isArray(enabledChannels); const maybeSuggestMessagingPreset = (channel, envKey) => { @@ -5118,6 +5124,7 @@ async function setupPoliciesWithSelection(sandboxName, options = {}) { const onSelection = typeof options.onSelection === "function" ? options.onSelection : null; const webSearchConfig = options.webSearchConfig || null; const enabledChannels = Array.isArray(options.enabledChannels) ? options.enabledChannels : null; + const provider = options.provider || null; step(8, 8, "Policy presets"); @@ -5147,6 +5154,11 @@ async function setupPoliciesWithSelection(sandboxName, options = {}) { const suggestions = tiers.resolveTierPresets(tierName).map((p) => p.name); // Allow credential-based overrides on top of the tier (additive only). if (webSearchConfig && !suggestions.includes("brave")) suggestions.push("brave"); + // Auto-suggest local-inference preset when a local provider is selected + const localProviders = ["ollama-local", "vllm-local", "nim-local"]; + if (provider && localProviders.includes(provider) && !suggestions.includes("local-inference")) { + suggestions.push("local-inference"); + } if (isNonInteractive()) { const policyMode = (process.env.NEMOCLAW_POLICY_MODE || "suggested").trim().toLowerCase(); @@ -5966,6 +5978,7 @@ async function onboard(opts = {}) { : null, enabledChannels: selectedMessagingChannels, webSearchConfig, + provider, onSelection: (policyPresets) => { onboardSession.updateSession((current) => { current.policyPresets = policyPresets; diff --git a/test/policies.test.ts b/test/policies.test.ts index 137f94cbfe5..426f32f85f1 100644 --- a/test/policies.test.ts +++ b/test/policies.test.ts @@ -96,9 +96,9 @@ selectFromList(items, options) describe("policies", () => { describe("listPresets", () => { - it("returns all 11 presets", () => { + it("returns all 12 presets", () => { const presets = policies.listPresets(); - expect(presets.length).toBe(11); + expect(presets.length).toBe(12); }); it("each preset has name and description", () => { @@ -120,6 +120,7 @@ describe("policies", () => { "github", "huggingface", "jira", + "local-inference", "npm", "outlook", "pypi",