Skip to content
Draft
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
21 changes: 19 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# to all FROM directives. Can be overridden via --build-arg.
ARG BASE_IMAGE=ghcr.io/nvidia/nemoclaw/sandbox-base:latest

# Stage 1: Build TypeScript plugin from source
# Stage 1a: Build NemoClaw TypeScript plugin from source
FROM node:22-slim@sha256:4f77a690f2f8946ab16fe1e791a3ac0667ae1c3575c3e4d0d4589e9ed5bfaf3d AS builder
ENV NPM_CONFIG_AUDIT=false \
NPM_CONFIG_FUND=false \
Expand All @@ -21,6 +21,12 @@ COPY nemoclaw/src/ /opt/nemoclaw/src/
WORKDIR /opt/nemoclaw
RUN npm ci && npm run build

# Stage 1b: Build mediator-tools plugin (standalone — no NemoClaw dependency)
COPY mediator-tools/package.json mediator-tools/tsconfig.json /opt/mediator-tools/
COPY mediator-tools/src/ /opt/mediator-tools/src/
WORKDIR /opt/mediator-tools
RUN npm install && npm run build

# Stage 2: Runtime image — pull cached base from GHCR
FROM ${BASE_IMAGE}

Expand Down Expand Up @@ -160,9 +166,20 @@ path = os.path.expanduser('~/.openclaw/openclaw.json'); \
json.dump(config, open(path, 'w'), indent=2); \
os.chmod(path, 0o600)"

# Install NemoClaw plugin into OpenClaw

# Install plugins by placing raw TypeScript source in the writable data
# extensions dir and adding plugins.load.paths to the config.
# We do NOT use `openclaw plugins install` — the "installed" plugin origin
# triggers a gateway crash on load (undiagnosed OpenClaw bug). Placing raw
# Keep the original NemoClaw install line (fails silently — known upstream bug)
RUN openclaw doctor --fix > /dev/null 2>&1 || true \
&& openclaw plugins install /opt/nemoclaw > /dev/null 2>&1 || true
# Place mediator-tools files in the data extensions dir. They're dormant
# until stack.sh's post-create step patches plugins.load.paths into the
# config AFTER the gateway completes its startup migration. The gateway
# hot-reloads config changes, so the plugin activates without a restart.
COPY mediator-tools/src/index.ts /sandbox/.openclaw-data/extensions/mediator-tools/index.ts
COPY mediator-tools/openclaw.plugin.json /sandbox/.openclaw-data/extensions/mediator-tools/openclaw.plugin.json

# Lock openclaw.json via DAC: chown to root so the sandbox user cannot modify
# it at runtime. This works regardless of Landlock enforcement status.
Expand Down
4 changes: 4 additions & 0 deletions Dockerfile.mediator-plugin
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ARG BASE_IMAGE=ghcr.io/nvidia/nemoclaw/sandbox-base:latest
FROM ${BASE_IMAGE}
COPY mediator-tools/src/index.ts /usr/local/lib/node_modules/openclaw/extensions/mediator-tools/index.ts
COPY mediator-tools/openclaw.plugin.json /usr/local/lib/node_modules/openclaw/extensions/mediator-tools/openclaw.plugin.json
4 changes: 4 additions & 0 deletions bin/lib/onboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,10 @@ async function validateCustomOpenAiLikeSelection(
credentialEnv,
helpUrl = null,
) {
if (process.env.NEMOCLAW_SKIP_VALIDATE === "1") {
console.log(` [skip-validate] Skipping endpoint validation for ${label}.`);
return { ok: true, api: "openai-completions" };
}
const apiKey = getCredential(credentialEnv);
const probe = probeOpenAiLikeEndpoint(endpointUrl, model, apiKey);
if (probe.ok) {
Expand Down
18 changes: 18 additions & 0 deletions bin/lib/sandbox-build-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,24 @@ function stageOptimizedSandboxBuildContext(rootDir, tmpDir = os.tmpdir()) {
path.join(stagedScriptsDir, "nemoclaw-start.sh"),
);

// Stage mediator-tools plugin if present (separate OpenClaw plugin that
// registers mediator syscalls as native agent tools).
const sourceMediatorDir = path.join(rootDir, "mediator-tools");
if (fs.existsSync(sourceMediatorDir)) {
const stagedMediatorDir = path.join(buildCtx, "mediator-tools");
fs.mkdirSync(stagedMediatorDir, { recursive: true });
for (const file of ["package.json", "tsconfig.json", "openclaw.plugin.json"]) {
const src = path.join(sourceMediatorDir, file);
if (fs.existsSync(src)) {
fs.copyFileSync(src, path.join(stagedMediatorDir, file));
}
}
const srcDir = path.join(sourceMediatorDir, "src");
if (fs.existsSync(srcDir)) {
fs.cpSync(srcDir, path.join(stagedMediatorDir, "src"), { recursive: true });
}
}

return { buildCtx, stagedDockerfile };
}

Expand Down
11 changes: 11 additions & 0 deletions mediator-tools/openclaw.plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"id": "mediator-tools",
"name": "Mediator Syscall Tools",
"version": "0.1.0",
"description": "Exposes OpenShell mediator syscalls (policy_propose, fork_with_policy, ipc_send, etc.) as native agent tools via direct UDS connection.",
"enabledByDefault": true,
"configSchema": {
"type": "object",
"additionalProperties": false
}
}
47 changes: 47 additions & 0 deletions mediator-tools/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions mediator-tools/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "@nemoclaw/mediator-tools",
"version": "0.1.0",
"type": "module",
"description": "Mediator syscalls as native OpenClaw agent tools. Connects directly to the mediator UDS socket — no shell, no child_process.",
"openclaw": {
"extensions": [
"./dist/index.js"
]
},
"scripts": {
"build": "tsc"
},
"devDependencies": {
"@types/node": "^25.6.0",
"typescript": "^5.7.0"
}
}
Loading