nomarmy 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +25 -0
- package/README.md +484 -0
- package/bin/nomarmy.mjs +2248 -0
- package/config/agents.yml.example +63 -0
- package/config/common.env +31 -0
- package/config/profiles/bedrock-cheap.env +26 -0
- package/config/profiles/bedrock.env +28 -0
- package/config/profiles/cpu-linux.env +8 -0
- package/config/profiles/dgx-spark.env +12 -0
- package/config/profiles/macbook-pro.env +9 -0
- package/config/profiles/nvidia-linux.env +9 -0
- package/docker/Dockerfile +15 -0
- package/docker/Dockerfile.go +29 -0
- package/docker/Dockerfile.rust +19 -0
- package/e2e.sh +153 -0
- package/install.sh +125 -0
- package/lib/agents.mjs +285 -0
- package/lib/army.mjs +400 -0
- package/lib/budget.mjs +368 -0
- package/lib/claude-transcript.mjs +150 -0
- package/lib/config.mjs +193 -0
- package/lib/connect.mjs +409 -0
- package/lib/coordinator-instructions.mjs +23 -0
- package/lib/decompose.mjs +389 -0
- package/lib/dispatch-config.mjs +164 -0
- package/lib/dispatch-schema.mjs +280 -0
- package/lib/doctor.mjs +443 -0
- package/lib/evidence.mjs +679 -0
- package/lib/gguf.mjs +589 -0
- package/lib/hardware.mjs +476 -0
- package/lib/health.mjs +278 -0
- package/lib/model-catalog.mjs +71 -0
- package/lib/notifier-app.mjs +95 -0
- package/lib/notify.mjs +66 -0
- package/lib/openclaw-config.mjs +65 -0
- package/lib/openclaw-errors.mjs +40 -0
- package/lib/propose.mjs +110 -0
- package/lib/prune.mjs +77 -0
- package/lib/repo-query.mjs +267 -0
- package/lib/runs.mjs +150 -0
- package/lib/sabotage.mjs +128 -0
- package/lib/sandbox-images.mjs +434 -0
- package/lib/scan.mjs +1538 -0
- package/lib/schema.mjs +288 -0
- package/lib/scout.mjs +544 -0
- package/lib/sizing.mjs +1322 -0
- package/lib/slots.mjs +112 -0
- package/lib/statusline.mjs +126 -0
- package/lib/subscription-config.mjs +68 -0
- package/lib/subscription-setup.mjs +217 -0
- package/lib/transcript.mjs +195 -0
- package/lib/verify.mjs +700 -0
- package/mcp/server.mjs +4206 -0
- package/notifier/icon.swift +34 -0
- package/notifier/main.swift +52 -0
- package/notifier/nomarmy-icon.png +0 -0
- package/package.json +67 -0
- package/playbooks/feature.md +43 -0
- package/policies/coder.md +49 -0
- package/policies/orchestrator.md +35 -0
- package/policies/reviewer.md +35 -0
- package/policies/scout.md +65 -0
- package/scripts/configure-openclaw.sh +96 -0
- package/scripts/configure-orchestrator.sh +84 -0
- package/scripts/install-llama-cpp.sh +16 -0
- package/scripts/lib.sh +198 -0
- package/scripts/select-model.mjs +96 -0
- package/scripts/select-model.sh +4 -0
- package/scripts/setup-sandbox.sh +38 -0
- package/scripts/start-inference.sh +46 -0
- package/scripts/stop-inference.sh +5 -0
- package/scripts/uninstall.sh +6 -0
- package/scripts/verify-install.sh +68 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# ~/.config/nomarmy/agents.yml (or $NOMARMY_CONFIG_DIR/agents.yml)
|
|
2
|
+
#
|
|
3
|
+
# Every model a nomArmy job can run on, by name. You rarely edit this by
|
|
4
|
+
# hand: `nomarmy agents add` walks through each kind (key registration,
|
|
5
|
+
# vendor logins, a real test call) and writes it for you.
|
|
6
|
+
#
|
|
7
|
+
# An api or subscription agent is an ACCOUNT, not a model. The model is
|
|
8
|
+
# picked per role (`nomarmy army assign sr-dev codex gpt-6-astra`, or `auto`
|
|
9
|
+
# to let the General choose per job), or by the General on a job; `model`
|
|
10
|
+
# here is only an optional default. Nothing ever picks between agents at
|
|
11
|
+
# random.
|
|
12
|
+
#
|
|
13
|
+
# `local` (the coder slot) is built in; you only list it to change it.
|
|
14
|
+
#
|
|
15
|
+
# Security: this file never holds a credential. An api agent names the
|
|
16
|
+
# environment variable its key was registered from (the key itself lives in
|
|
17
|
+
# OpenClaw's own store); a subscription agent's login lives in the vendor
|
|
18
|
+
# CLI's own session or your OS keychain. nomArmy refuses to load this file
|
|
19
|
+
# if another OS account owns it or can write to it.
|
|
20
|
+
|
|
21
|
+
agents:
|
|
22
|
+
# The local model's gpt slot (NOMARMY_WORKER_MODEL_FALLBACK).
|
|
23
|
+
local-gpt:
|
|
24
|
+
kind: local
|
|
25
|
+
slot: gpt
|
|
26
|
+
|
|
27
|
+
# A metered API key. provider is one of: xai, openai, anthropic,
|
|
28
|
+
# deepinfra, bedrock / azure-openai / openai-compatible (need base_url),
|
|
29
|
+
# or openclaw for any other OpenClaw provider by id:
|
|
30
|
+
# provider: openclaw
|
|
31
|
+
# openclaw_provider: deepseek
|
|
32
|
+
# plugin: clawhub:@openclaw/deepseek-provider # if it isn't built in
|
|
33
|
+
grok:
|
|
34
|
+
kind: api
|
|
35
|
+
provider: xai
|
|
36
|
+
model: grok-4.7 # optional default
|
|
37
|
+
auth_env: NOMARMY_XAI_API_KEY # the variable's NAME, never the key
|
|
38
|
+
thinking: high # true = follow the job, false = off, or a fixed level
|
|
39
|
+
# max_concurrent: 2
|
|
40
|
+
# context_window: 500000 # only for a model newer than OpenClaw's catalog
|
|
41
|
+
|
|
42
|
+
# ONE person's own subscription, never pooled: every job on it must name
|
|
43
|
+
# this owner in on_behalf_of. Set up with `nomarmy agents add subscription
|
|
44
|
+
# claude|codex|meta`.
|
|
45
|
+
claude:
|
|
46
|
+
kind: subscription
|
|
47
|
+
provider: claude-cli # Claude Pro/Max/Team seat
|
|
48
|
+
owner: "you@example.com"
|
|
49
|
+
codex:
|
|
50
|
+
kind: subscription
|
|
51
|
+
provider: openai # ChatGPT plan, via the Codex login
|
|
52
|
+
owner: "you@example.com"
|
|
53
|
+
muse:
|
|
54
|
+
kind: subscription
|
|
55
|
+
provider: meta # Muse Code subscription
|
|
56
|
+
owner: "you@example.com"
|
|
57
|
+
# max_concurrent: 1 # the default for a subscription
|
|
58
|
+
|
|
59
|
+
# One OpenClaw provider id holds one credential. OpenAI, Meta and xAI use the
|
|
60
|
+
# same id for the subscription and the API key, so an api agent and a
|
|
61
|
+
# subscription agent on the same provider are refused. Here, `grok` (xai)
|
|
62
|
+
# means you can't also add an xAI subscription agent. Claude never collides:
|
|
63
|
+
# its subscription is claude-cli, its API key is anthropic.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
NOMARMY_MODEL_REPO=ggml-org/gpt-oss-20b-GGUF
|
|
2
|
+
NOMARMY_MODEL_QUANT=MXFP4
|
|
3
|
+
NOMARMY_MODEL_ALIAS=gpt-oss-20b
|
|
4
|
+
# Whether the configured model has a thinking/reasoning mode at all --
|
|
5
|
+
# Coder-Next does not. `nomarmy connect` reads this (and NOMARMY_WORKER_MODEL
|
|
6
|
+
# below) to keep the MCP registration's worker-routing env vars in sync with
|
|
7
|
+
# whatever model is actually configured here, instead of the two silently
|
|
8
|
+
# drifting apart (the exact bug this fixes: NOMARMY_WORKER_MODEL sat unused
|
|
9
|
+
# in this file for every model choice until now).
|
|
10
|
+
NOMARMY_MODEL_THINKING=true
|
|
11
|
+
NOMARMY_LLAMA_HOST=127.0.0.1
|
|
12
|
+
NOMARMY_LLAMA_PORT=8080
|
|
13
|
+
NOMARMY_LLAMA_CONTEXT=65536
|
|
14
|
+
NOMARMY_LLAMA_PARALLEL=1
|
|
15
|
+
NOMARMY_MAX_WORKERS=1
|
|
16
|
+
NOMARMY_AGENT_IMAGE=openclaw-nomarmy-coder:bookworm
|
|
17
|
+
NOMARMY_INSTALL_ROOT=$HOME/.local/share/nomarmy-local-agents
|
|
18
|
+
|
|
19
|
+
# Execution layer: 'local' runs llama-server on this machine, 'bedrock' calls a
|
|
20
|
+
# hosted OpenAI-compatible endpoint. Profiles override this.
|
|
21
|
+
NOMARMY_EXECUTION=local
|
|
22
|
+
|
|
23
|
+
# Worker model routing. The MCP server composes "<provider>/<model>" for
|
|
24
|
+
# OpenClaw, so these are the only place model identity is declared.
|
|
25
|
+
NOMARMY_WORKER_PROVIDER=llama-cpp
|
|
26
|
+
NOMARMY_WORKER_MODEL=gpt-oss-20b
|
|
27
|
+
NOMARMY_WORKER_MODEL_FALLBACK=gpt-oss-20b
|
|
28
|
+
|
|
29
|
+
# 'frontier' means the coordinator outranks the workers it reviews, which is
|
|
30
|
+
# what policies/reviewer.md assumes. 'degraded' is the explicit opt-out.
|
|
31
|
+
NOMARMY_ORCHESTRATOR_TRUST=frontier
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# DEGRADED-ACCEPTANCE PROFILE: read policies/reviewer.md before using.
|
|
2
|
+
#
|
|
3
|
+
# Orchestrator and workers are the same capability class, so the coordinator is
|
|
4
|
+
# grading output from a peer rather than from below. Acceptance stops being an
|
|
5
|
+
# independent check. Use only where a wrong accept is cheap and reversible, and
|
|
6
|
+
# never for security decisions, architecture, or anything heading for a release.
|
|
7
|
+
#
|
|
8
|
+
# Note this path cannot be Claude Code: CLAUDE_CODE_USE_BEDROCK only routes
|
|
9
|
+
# Anthropic models. The coordinator here is OpenClaw driving the same MCP server.
|
|
10
|
+
NOMARMY_PROFILE=bedrock-cheap
|
|
11
|
+
NOMARMY_EXECUTION=bedrock
|
|
12
|
+
|
|
13
|
+
NOMARMY_BEDROCK_REGION=eu-west-2
|
|
14
|
+
|
|
15
|
+
# --- Orchestrator -----------------------------------------------------------
|
|
16
|
+
NOMARMY_ORCHESTRATOR_RUNTIME=openclaw
|
|
17
|
+
NOMARMY_ORCHESTRATOR_MODEL=qwen.qwen3-coder-next
|
|
18
|
+
NOMARMY_ORCHESTRATOR_TRUST=degraded
|
|
19
|
+
|
|
20
|
+
# --- Workers ----------------------------------------------------------------
|
|
21
|
+
NOMARMY_WORKER_PROVIDER=bedrock
|
|
22
|
+
NOMARMY_WORKER_AUTH_CHOICE=openai-compatible-existing-server
|
|
23
|
+
NOMARMY_WORKER_MODEL=qwen.qwen3-coder-next
|
|
24
|
+
NOMARMY_WORKER_MODEL_FALLBACK=nvidia.nemotron-nano-3-30b
|
|
25
|
+
|
|
26
|
+
NOMARMY_MAX_WORKERS=4
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Bedrock-hosted orchestrator and workers. No local inference, so this profile
|
|
2
|
+
# carries no GPU or OS requirement and runs anywhere AWS credentials resolve.
|
|
3
|
+
NOMARMY_PROFILE=bedrock
|
|
4
|
+
NOMARMY_EXECUTION=bedrock
|
|
5
|
+
|
|
6
|
+
# Region, models and worker count honour an environment override; the value here
|
|
7
|
+
# is the default. Base URL is derived from the region unless you set it.
|
|
8
|
+
NOMARMY_BEDROCK_REGION=eu-west-2
|
|
9
|
+
# NOMARMY_BEDROCK_BASE_URL=https://bedrock-runtime.eu-west-2.amazonaws.com/openai/v1
|
|
10
|
+
|
|
11
|
+
# --- Orchestrator -----------------------------------------------------------
|
|
12
|
+
# Claude Opus 5 served from Bedrock. The coordinator still outranks the workers
|
|
13
|
+
# it reviews, so policies/orchestrator.md and policies/reviewer.md hold as written.
|
|
14
|
+
NOMARMY_ORCHESTRATOR_RUNTIME=claude-code
|
|
15
|
+
NOMARMY_ORCHESTRATOR_MODEL=eu.anthropic.claude-opus-5
|
|
16
|
+
NOMARMY_ORCHESTRATOR_TRUST=frontier
|
|
17
|
+
|
|
18
|
+
# --- Workers ----------------------------------------------------------------
|
|
19
|
+
# Bedrock model IDs. Confirm against `aws bedrock list-foundation-models`;
|
|
20
|
+
# scripts/verify-install.sh checks these are invokable in your account.
|
|
21
|
+
NOMARMY_WORKER_PROVIDER=bedrock
|
|
22
|
+
NOMARMY_WORKER_AUTH_CHOICE=openai-compatible-existing-server
|
|
23
|
+
NOMARMY_WORKER_MODEL=qwen.qwen3-coder-next
|
|
24
|
+
NOMARMY_WORKER_MODEL_FALLBACK=nvidia.nemotron-3-super-120b
|
|
25
|
+
|
|
26
|
+
# Hosted inference removes the VRAM ceiling; the practical limit is now your
|
|
27
|
+
# Bedrock TPM quota and your budget. Still measure before raising further.
|
|
28
|
+
NOMARMY_MAX_WORKERS=4
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Generic Linux CPU-only worker. Use where CUDA is unavailable.
|
|
2
|
+
# Retained as the constrained profile: 16K context, one worker.
|
|
3
|
+
NOMARMY_PROFILE=cpu-linux
|
|
4
|
+
NOMARMY_LLAMA_GPU_LAYERS=0
|
|
5
|
+
NOMARMY_LLAMA_THREADS=8
|
|
6
|
+
NOMARMY_LLAMA_CONTEXT=16384
|
|
7
|
+
NOMARMY_LLAMA_PARALLEL=1
|
|
8
|
+
NOMARMY_MAX_WORKERS=1
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# NVIDIA DGX Spark / GB10 Grace Blackwell, Linux ARM64, CUDA.
|
|
2
|
+
# Start conservative; raise parallelism only after E2E passes and memory is measured.
|
|
3
|
+
#
|
|
4
|
+
# NOTE: -c is divided across -np, so 65536 with 2 slots gives each nom 32K,
|
|
5
|
+
# below the v1.3 64K-per-nom target. Run `nomarmy sizing` on the machine and
|
|
6
|
+
# apply what it recommends rather than trusting these defaults.
|
|
7
|
+
NOMARMY_PROFILE=dgx-spark
|
|
8
|
+
NOMARMY_LLAMA_GPU_LAYERS=999
|
|
9
|
+
NOMARMY_LLAMA_THREADS=16
|
|
10
|
+
NOMARMY_LLAMA_CONTEXT=65536
|
|
11
|
+
NOMARMY_LLAMA_PARALLEL=2
|
|
12
|
+
NOMARMY_MAX_WORKERS=2
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Apple Silicon / Metal. Conservative defaults for 64 GB unified memory.
|
|
2
|
+
NOMARMY_PROFILE=macbook-pro
|
|
3
|
+
NOMARMY_LLAMA_GPU_LAYERS=999
|
|
4
|
+
NOMARMY_LLAMA_THREADS=12
|
|
5
|
+
# v1.3 raises this from 32K: the autonomous explore/implement/test/repair loop
|
|
6
|
+
# needs more room than a one-shot edit. 64 GB unified memory carries 64K.
|
|
7
|
+
NOMARMY_LLAMA_CONTEXT=65536
|
|
8
|
+
NOMARMY_LLAMA_PARALLEL=1
|
|
9
|
+
NOMARMY_MAX_WORKERS=1
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Generic NVIDIA Linux/CUDA worker. Tune for available VRAM/system memory.
|
|
2
|
+
NOMARMY_PROFILE=nvidia-linux
|
|
3
|
+
NOMARMY_LLAMA_GPU_LAYERS=999
|
|
4
|
+
NOMARMY_LLAMA_THREADS=12
|
|
5
|
+
# Conservative for unknown VRAM. Raise to 65536 for the v1.3 autonomous loop
|
|
6
|
+
# where the card allows: NOMARMY_LLAMA_CONTEXT=65536 ./install.sh --profile nvidia-linux
|
|
7
|
+
NOMARMY_LLAMA_CONTEXT=32768
|
|
8
|
+
NOMARMY_LLAMA_PARALLEL=1
|
|
9
|
+
NOMARMY_MAX_WORKERS=1
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
FROM node:24-bookworm-slim
|
|
2
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
|
3
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
4
|
+
bash ca-certificates curl git jq python3 python3-pip python3-venv ripgrep \
|
|
5
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
6
|
+
# node:24-bookworm-slim already ships a non-root "node" user at uid/gid 1000,
|
|
7
|
+
# the same uid this Dockerfile used to try to create a "sandbox" user at.
|
|
8
|
+
# useradd silently failed on every build (masked by `|| true`), so `USER
|
|
9
|
+
# sandbox` below referred to a user that was never actually created --
|
|
10
|
+
# `docker run` for any command failed with "unable to find user sandbox: no
|
|
11
|
+
# matching entries in passwd file." Use the user the base image already
|
|
12
|
+
# provides instead of colliding with it.
|
|
13
|
+
USER node
|
|
14
|
+
WORKDIR /workspace
|
|
15
|
+
CMD ["sleep", "infinity"]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
FROM node:24-bookworm-slim
|
|
2
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
|
3
|
+
# Same base as docker/Dockerfile plus a Go toolchain -- the worker's own
|
|
4
|
+
# tool-calling harness needs Node regardless of the target repo's language.
|
|
5
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
6
|
+
bash ca-certificates curl git jq python3 python3-pip python3-venv ripgrep \
|
|
7
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
8
|
+
# Debian bookworm's packaged golang-go lags stable Go by multiple years
|
|
9
|
+
# (verified live: apt installed 1.19 here while go.dev's stable was 1.27) --
|
|
10
|
+
# same reasoning as using rustup instead of apt for Rust in Dockerfile.rust.
|
|
11
|
+
# Pinned, not "latest", for a reproducible build; bump GO_VERSION by hand
|
|
12
|
+
# periodically against https://go.dev/dl/.
|
|
13
|
+
ENV GO_VERSION=1.27.1
|
|
14
|
+
RUN ARCH="$(dpkg --print-architecture)" \
|
|
15
|
+
&& case "$ARCH" in \
|
|
16
|
+
amd64) GOARCH=amd64 ;; \
|
|
17
|
+
arm64) GOARCH=arm64 ;; \
|
|
18
|
+
*) echo "unsupported architecture for Go install: $ARCH" >&2; exit 1 ;; \
|
|
19
|
+
esac \
|
|
20
|
+
&& curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${GOARCH}.tar.gz" -o /tmp/go.tgz \
|
|
21
|
+
&& tar -C /usr/local -xzf /tmp/go.tgz \
|
|
22
|
+
&& rm /tmp/go.tgz
|
|
23
|
+
ENV PATH="/usr/local/go/bin:${PATH}"
|
|
24
|
+
ENV GOPATH=/home/node/go
|
|
25
|
+
ENV PATH="${GOPATH}/bin:${PATH}"
|
|
26
|
+
USER node
|
|
27
|
+
RUN mkdir -p "${GOPATH}"
|
|
28
|
+
WORKDIR /workspace
|
|
29
|
+
CMD ["sleep", "infinity"]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
FROM node:24-bookworm-slim
|
|
2
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
|
3
|
+
# Same base as docker/Dockerfile plus a Rust toolchain -- the worker's own
|
|
4
|
+
# tool-calling harness needs Node regardless of the target repo's language.
|
|
5
|
+
# Debian bookworm's packaged rustc/cargo lags stable Rust noticeably, and
|
|
6
|
+
# real Cargo.toml files routinely need a recent edition/toolchain, so this
|
|
7
|
+
# uses rustup (network access is fine here -- this is the BUILD step,
|
|
8
|
+
# offline, on the host; the running container it produces still gets
|
|
9
|
+
# --network none like every other job sandbox).
|
|
10
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
11
|
+
bash ca-certificates curl git jq python3 python3-pip python3-venv ripgrep build-essential \
|
|
12
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
13
|
+
USER node
|
|
14
|
+
ENV RUSTUP_HOME=/home/node/.rustup
|
|
15
|
+
ENV CARGO_HOME=/home/node/.cargo
|
|
16
|
+
ENV PATH="${CARGO_HOME}/bin:${PATH}"
|
|
17
|
+
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable
|
|
18
|
+
WORKDIR /workspace
|
|
19
|
+
CMD ["sleep", "infinity"]
|
package/e2e.sh
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
# shellcheck disable=SC1091
|
|
6
|
+
source "$ROOT/scripts/lib.sh"
|
|
7
|
+
|
|
8
|
+
PROFILE=""
|
|
9
|
+
|
|
10
|
+
while [[ $# -gt 0 ]]; do
|
|
11
|
+
case "$1" in
|
|
12
|
+
--profile)
|
|
13
|
+
PROFILE="$2"
|
|
14
|
+
shift 2
|
|
15
|
+
;;
|
|
16
|
+
*)
|
|
17
|
+
echo "Unknown arg $1"
|
|
18
|
+
exit 2
|
|
19
|
+
;;
|
|
20
|
+
esac
|
|
21
|
+
done
|
|
22
|
+
|
|
23
|
+
load_profile "$PROFILE"
|
|
24
|
+
|
|
25
|
+
echo "=== nomArmy E2E: $NOMARMY_PROFILE ==="
|
|
26
|
+
|
|
27
|
+
"$ROOT/scripts/start-inference.sh" "$NOMARMY_PROFILE"
|
|
28
|
+
|
|
29
|
+
if nomarmy_is_cloud; then
|
|
30
|
+
echo "INFO hosted inference at $NOMARMY_BEDROCK_BASE_URL"
|
|
31
|
+
|
|
32
|
+
# Reachability and model entitlement are an AWS concern on this path, so the
|
|
33
|
+
# provider listing stands in for the local health and discovery checks.
|
|
34
|
+
openclaw models list --provider "$NOMARMY_WORKER_PROVIDER" \
|
|
35
|
+
| grep -q "$NOMARMY_WORKER_MODEL"
|
|
36
|
+
|
|
37
|
+
echo "PASS model discovery"
|
|
38
|
+
else
|
|
39
|
+
curl -fsS \
|
|
40
|
+
"http://$NOMARMY_LLAMA_HOST:$NOMARMY_LLAMA_PORT/health" \
|
|
41
|
+
>/dev/null
|
|
42
|
+
|
|
43
|
+
echo "PASS inference health"
|
|
44
|
+
|
|
45
|
+
MODELS="$(
|
|
46
|
+
curl -fsS \
|
|
47
|
+
"http://$NOMARMY_LLAMA_HOST:$NOMARMY_LLAMA_PORT/v1/models"
|
|
48
|
+
)"
|
|
49
|
+
|
|
50
|
+
echo "$MODELS" | grep -q "$NOMARMY_MODEL_ALIAS"
|
|
51
|
+
|
|
52
|
+
echo "PASS model discovery"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Under $HOME, not the system tmpdir: Podman Machine on macOS only allows
|
|
56
|
+
# bind-mounting paths under the host home directory, and the cleanup step
|
|
57
|
+
# below bind-mounts this directory into a container.
|
|
58
|
+
TMP="$(mktemp -d "$HOME/.nomarmy-e2e.XXXXXX")"
|
|
59
|
+
|
|
60
|
+
cleanup() {
|
|
61
|
+
local exit_code=$?
|
|
62
|
+
|
|
63
|
+
if [[ -n "${TMP:-}" && -d "$TMP" ]]; then
|
|
64
|
+
|
|
65
|
+
# OpenClaw's Podman sandbox may create files that the host user
|
|
66
|
+
# cannot delete directly. Use a disposable container to clean
|
|
67
|
+
# the temporary workspace first.
|
|
68
|
+
if command -v podman >/dev/null 2>&1 && podman info >/dev/null 2>&1; then
|
|
69
|
+
podman run --rm \
|
|
70
|
+
-v "$TMP:/cleanup" \
|
|
71
|
+
alpine:3.20 \
|
|
72
|
+
sh -c '
|
|
73
|
+
find /cleanup -mindepth 1 -maxdepth 1 -exec rm -rf -- {} + \
|
|
74
|
+
2>/dev/null || true
|
|
75
|
+
' \
|
|
76
|
+
>/dev/null 2>&1 || true
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
rm -rf "$TMP" 2>/dev/null || true
|
|
80
|
+
|
|
81
|
+
if [[ -d "$TMP" ]]; then
|
|
82
|
+
echo "WARN: E2E temporary directory could not be completely removed:"
|
|
83
|
+
echo " $TMP"
|
|
84
|
+
fi
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
exit "$exit_code"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
trap cleanup EXIT INT TERM
|
|
91
|
+
|
|
92
|
+
cd "$TMP"
|
|
93
|
+
|
|
94
|
+
git init -q
|
|
95
|
+
git config user.email e2e@nomarmy.local
|
|
96
|
+
git config user.name "nomArmy E2E"
|
|
97
|
+
|
|
98
|
+
cat > calc.js <<'JS'
|
|
99
|
+
export function add(a,b){ return a-b; }
|
|
100
|
+
JS
|
|
101
|
+
|
|
102
|
+
cat > test.mjs <<'JS'
|
|
103
|
+
import { add } from './calc.js';
|
|
104
|
+
|
|
105
|
+
if (add(2,3)!==5) {
|
|
106
|
+
console.error('FAIL');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
console.log('PASS');
|
|
111
|
+
JS
|
|
112
|
+
|
|
113
|
+
cat > package.json <<'JSON'
|
|
114
|
+
{
|
|
115
|
+
"type": "module",
|
|
116
|
+
"scripts": {
|
|
117
|
+
"test": "node test.mjs"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
JSON
|
|
121
|
+
|
|
122
|
+
git add .
|
|
123
|
+
git commit -qm baseline
|
|
124
|
+
|
|
125
|
+
PROMPT='Fix the bug so npm test passes. Work only in the workspace. Run npm test. Do not run git. Final response MUST begin with exactly four lines: STATUS: done | partial | blocked; CHANGES: <brief>; VERIFICATION: pass | partial | failed - <brief>; NOT DONE: <list or none>.'
|
|
126
|
+
|
|
127
|
+
OUT="$TMP/openclaw.json"
|
|
128
|
+
|
|
129
|
+
openclaw agent exec "$PROMPT" \
|
|
130
|
+
--model "$NOMARMY_WORKER_PROVIDER/$NOMARMY_WORKER_MODEL" \
|
|
131
|
+
--cwd "$TMP" \
|
|
132
|
+
--code-mode direct \
|
|
133
|
+
--local-model-lean \
|
|
134
|
+
--thinking off \
|
|
135
|
+
--timeout 600 \
|
|
136
|
+
--json \
|
|
137
|
+
> "$OUT"
|
|
138
|
+
|
|
139
|
+
# Independent verification. We do not trust the worker's claim
|
|
140
|
+
# that its implementation is correct.
|
|
141
|
+
npm test
|
|
142
|
+
|
|
143
|
+
grep -Eq 'STATUS: (done|partial|blocked)' "$OUT"
|
|
144
|
+
echo "PASS worker report contract"
|
|
145
|
+
|
|
146
|
+
# The worker must have actually modified the implementation.
|
|
147
|
+
if git diff --exit-code -- calc.js >/dev/null; then
|
|
148
|
+
echo "ERROR: worker made no code change"
|
|
149
|
+
exit 1
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
echo "PASS autonomous edit + verification"
|
|
153
|
+
echo "=== E2E PASS ==="
|
package/install.sh
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
4
|
+
# shellcheck disable=SC1091
|
|
5
|
+
source "$ROOT/scripts/lib.sh"
|
|
6
|
+
PROFILE=""
|
|
7
|
+
WITH_CLAUDE=1
|
|
8
|
+
while [[ $# -gt 0 ]]; do case "$1" in --profile) PROFILE="$2"; shift 2;; --no-claude) WITH_CLAUDE=0; shift;; *) echo "Unknown arg: $1"; exit 2;; esac; done
|
|
9
|
+
load_profile "$PROFILE"
|
|
10
|
+
echo "==> nomArmy install ($NOMARMY_PROFILE)"
|
|
11
|
+
OS_NAME="$(uname -s)"
|
|
12
|
+
case "$OS_NAME" in
|
|
13
|
+
Darwin)
|
|
14
|
+
need brew || { echo 'ERROR: Homebrew is required on macOS. Install it from https://brew.sh, then rerun.'; exit 1; }
|
|
15
|
+
command -v curl >/dev/null 2>&1 || brew install curl
|
|
16
|
+
command -v git >/dev/null 2>&1 || brew install git
|
|
17
|
+
command -v node >/dev/null 2>&1 || brew install node
|
|
18
|
+
if ! nomarmy_is_cloud; then
|
|
19
|
+
command -v xcode-select >/dev/null 2>&1 && xcode-select -p >/dev/null 2>&1 || { echo 'ERROR: Xcode Command Line Tools are required. Run: xcode-select --install'; exit 1; }
|
|
20
|
+
command -v cmake >/dev/null 2>&1 || brew install cmake
|
|
21
|
+
fi
|
|
22
|
+
# A Homebrew formula, not a cask -- installs headlessly like curl/git/node,
|
|
23
|
+
# no manual first-launch the way Docker Desktop needs.
|
|
24
|
+
command -v podman >/dev/null 2>&1 || brew install podman
|
|
25
|
+
;;
|
|
26
|
+
Linux)
|
|
27
|
+
if nomarmy_is_cloud; then
|
|
28
|
+
# No local model is built or served, so the C++ toolchain is not needed.
|
|
29
|
+
need curl; need git; need node; need npm
|
|
30
|
+
else
|
|
31
|
+
if ! command -v cmake >/dev/null 2>&1 || ! command -v c++ >/dev/null 2>&1 || ! command -v curl >/dev/null 2>&1 || ! command -v git >/dev/null 2>&1 || ! command -v node >/dev/null 2>&1 || ! command -v npm >/dev/null 2>&1; then install_build_dependencies; fi
|
|
32
|
+
need curl; need git; need cmake; need c++; need node; need npm
|
|
33
|
+
if [[ "$NOMARMY_PROFILE" == dgx-spark || "$NOMARMY_PROFILE" == nvidia-linux ]]; then need nvidia-smi; need nvcc; fi
|
|
34
|
+
fi
|
|
35
|
+
# Podman runs rootless, directly on the host kernel here -- no daemon to
|
|
36
|
+
# enable or start, unlike Docker Engine.
|
|
37
|
+
command -v podman >/dev/null 2>&1 || install_podman
|
|
38
|
+
;;
|
|
39
|
+
MINGW*|MSYS*|CYGWIN*|Windows_NT)
|
|
40
|
+
cat >&2 <<'MSG'
|
|
41
|
+
ERROR: install.sh does not run natively on Windows. Three options, and the usual
|
|
42
|
+
advice is not always the better one:
|
|
43
|
+
|
|
44
|
+
1. WSL2, then run the Linux install inside your distro (supported path)
|
|
45
|
+
Podman runs natively inside the WSL2 distro itself -- nothing to install
|
|
46
|
+
on the Windows host, and no Docker Desktop licensing exposure there.
|
|
47
|
+
Note: WSL2 defaults to ~50% of host RAM, shared across every distro. On
|
|
48
|
+
a 32 GB machine that caps the model at ~16 GB. Raise it in
|
|
49
|
+
%UserProfile%\.wslconfig, which needs `wsl --shutdown` and will restart
|
|
50
|
+
every running container.
|
|
51
|
+
|
|
52
|
+
2. Native Windows llama.cpp built from source, then point nomArmy at it.
|
|
53
|
+
Gets the full host RAM. Needs a C++ toolchain:
|
|
54
|
+
winget install Microsoft.VisualStudio.2022.BuildTools --override \
|
|
55
|
+
"--wait --passive --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"
|
|
56
|
+
Prebuilt llama.cpp Windows binaries are NOT a reliable shortcut: on some
|
|
57
|
+
CPUs every compute backend crashes at startup (access violation) while
|
|
58
|
+
non-compute binaries run fine. The cause is dynamic backend loading, so
|
|
59
|
+
build it statically instead -- these flags are verified working:
|
|
60
|
+
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
|
61
|
+
-DGGML_BACKEND_DL=OFF -DBUILD_SHARED_LIBS=OFF \
|
|
62
|
+
-DGGML_NATIVE=ON -DLLAMA_CURL=OFF
|
|
63
|
+
GGML_BACKEND_DL=OFF is the one that matters: it links the CPU backend in
|
|
64
|
+
rather than probing for it at runtime.
|
|
65
|
+
|
|
66
|
+
3. Run workers on a Bedrock profile and skip local inference entirely:
|
|
67
|
+
./install.sh --profile bedrock
|
|
68
|
+
|
|
69
|
+
Run `nomarmy sizing` on the host first -- it reports which of these your
|
|
70
|
+
hardware can actually support before you commit to one.
|
|
71
|
+
MSG
|
|
72
|
+
exit 1
|
|
73
|
+
;;
|
|
74
|
+
*)
|
|
75
|
+
echo "ERROR: $OS_NAME is not a supported host. See the README for supported platforms." >&2
|
|
76
|
+
exit 1
|
|
77
|
+
;;
|
|
78
|
+
esac
|
|
79
|
+
need node; need npm
|
|
80
|
+
|
|
81
|
+
# The nomarmy CLI and the MCP server both import from lib/, which has real
|
|
82
|
+
# dependencies. Without this a fresh clone cannot run `nomarmy` at all -- the
|
|
83
|
+
# first import of `yaml` fails. This is separate from the copy the worker setup
|
|
84
|
+
# scripts install into $NOMARMY_AGENT_INSTALL_DIR.
|
|
85
|
+
echo '==> Installing nomArmy dependencies'
|
|
86
|
+
(cd "$ROOT" && npm install --omit=dev --no-audit --no-fund)
|
|
87
|
+
|
|
88
|
+
# Put `nomarmy` on PATH. Non-fatal by design: linking needs a writable npm
|
|
89
|
+
# global prefix, and the CLI is equally usable as `node bin/nomarmy.mjs`.
|
|
90
|
+
if (cd "$ROOT" && npm link >/dev/null 2>&1); then
|
|
91
|
+
echo '==> Linked the nomarmy CLI onto PATH'
|
|
92
|
+
else
|
|
93
|
+
echo 'NOTE: could not link the nomarmy CLI (npm global prefix not writable).'
|
|
94
|
+
echo " Run it directly instead: node $ROOT/bin/nomarmy.mjs <command>"
|
|
95
|
+
fi
|
|
96
|
+
# The sandbox is required on every profile, cloud included: it is what keeps
|
|
97
|
+
# repository content away from host credentials. Podman readiness (starting
|
|
98
|
+
# the macOS VM if needed) is verified later, in scripts/setup-sandbox.sh --
|
|
99
|
+
# on a fresh macOS install nothing has initialized that VM yet at this point.
|
|
100
|
+
if nomarmy_is_cloud; then need aws || { echo 'ERROR: the AWS CLI is required for cloud profiles.'; exit 1; }; fi
|
|
101
|
+
"$ROOT/scripts/install-llama-cpp.sh" "$NOMARMY_PROFILE"
|
|
102
|
+
if ! command -v openclaw >/dev/null 2>&1; then
|
|
103
|
+
echo '==> Installing OpenClaw (non-interactive)'
|
|
104
|
+
curl -fsSL https://openclaw.ai/install.sh | bash -s -- --no-onboard
|
|
105
|
+
export PATH="$HOME/.local/bin:$HOME/.npm-global/bin:$PATH"
|
|
106
|
+
fi
|
|
107
|
+
need openclaw
|
|
108
|
+
if ! nomarmy_is_cloud; then openclaw plugins install @openclaw/llama-cpp-provider || true; fi
|
|
109
|
+
"$ROOT/scripts/start-inference.sh" "$NOMARMY_PROFILE"
|
|
110
|
+
# Sandbox before provider config: configure-openclaw.sh refuses to store a real
|
|
111
|
+
# Bedrock credential unless the coder sandbox is already network-isolated.
|
|
112
|
+
"$ROOT/scripts/setup-sandbox.sh"
|
|
113
|
+
"$ROOT/scripts/configure-openclaw.sh" "$NOMARMY_PROFILE"
|
|
114
|
+
if [[ "$WITH_CLAUDE" == 1 ]]; then
|
|
115
|
+
if command -v claude >/dev/null 2>&1; then node "$ROOT/bin/nomarmy.mjs" connect claude; else echo 'NOTE: Claude Code not found; worker stack installed. Install Claude Code then run: nomarmy connect claude'; fi
|
|
116
|
+
fi
|
|
117
|
+
if command -v codex >/dev/null 2>&1; then node "$ROOT/bin/nomarmy.mjs" connect codex; else echo 'NOTE: Codex not found; run: nomarmy connect codex (after installing Codex)'; fi
|
|
118
|
+
"$ROOT/scripts/verify-install.sh" "$NOMARMY_PROFILE"
|
|
119
|
+
if nomarmy_is_cloud && [[ "${NOMARMY_ORCHESTRATOR_RUNTIME:-}" == "claude-code" ]]; then
|
|
120
|
+
echo
|
|
121
|
+
echo "==> Next: point the orchestrator at Bedrock"
|
|
122
|
+
echo " ./scripts/configure-orchestrator.sh $NOMARMY_PROFILE # print the settings"
|
|
123
|
+
echo " ./scripts/configure-orchestrator.sh $NOMARMY_PROFILE --apply # write them to Claude Code"
|
|
124
|
+
fi
|
|
125
|
+
echo "==> Install complete. Run: ./e2e.sh --profile $NOMARMY_PROFILE"
|