@sagentlab/navarch-runtime 0.1.0 → 0.1.1
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/README.md +57 -56
- package/dist/adapter.cjs +1 -1
- package/dist/adapters/claude.cjs +2 -2
- package/dist/adapters/codex.cjs +5 -5
- package/dist/adapters/index.cjs +1 -1
- package/dist/api.cjs +2 -2
- package/dist/capacity.cjs +1 -1
- package/dist/claim-loop.cjs +12 -1
- package/dist/cli.cjs +33 -15
- package/dist/config.cjs +19 -19
- package/dist/machine-store.cjs +5 -5
- package/dist/mcp-config.cjs +7 -7
- package/dist/prompt.cjs +5 -0
- package/dist/sandbox.cjs +1 -1
- package/dist/session.cjs +4 -4
- package/dist/types.cjs +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,13 +7,13 @@ it. Plain Node/TypeScript, zero production dependencies, no Next.js coupling
|
|
|
7
7
|
machine.
|
|
8
8
|
|
|
9
9
|
The agent CLI a session runs is either **Claude Code** or **OpenAI Codex**,
|
|
10
|
-
selected per machine via `
|
|
10
|
+
selected per machine via `NAVARCH_AGENT` (default `claude-code`) — see
|
|
11
11
|
"Choosing an agent (Claude Code vs. Codex)" below.
|
|
12
12
|
|
|
13
13
|
See [`docs/agent-platform-project-plan.md`](../docs/agent-platform-project-plan.md)
|
|
14
|
-
§3.8/§3.9/§3.11 and [`docs/
|
|
14
|
+
§3.8/§3.9/§3.11 and [`docs/navarch/implementation-plan.md`](../docs/navarch/implementation-plan.md)
|
|
15
15
|
WP-07 for the design this implements, and
|
|
16
|
-
[`docs/
|
|
16
|
+
[`docs/navarch/schema-design.md`](../docs/navarch/schema-design.md) §7 for
|
|
17
17
|
the API contract.
|
|
18
18
|
|
|
19
19
|
## Quick start on a fresh machine
|
|
@@ -21,7 +21,7 @@ the API contract.
|
|
|
21
21
|
```sh
|
|
22
22
|
git clone <this repo> && cd sagentlab/runtime
|
|
23
23
|
./install.sh # checks node/docker, npm install, npm run build
|
|
24
|
-
export
|
|
24
|
+
export NAVARCH_API_BASE=https://navarch.example.com
|
|
25
25
|
node bin/navarch.cjs register --token <enrollment-token> --name my-machine-1
|
|
26
26
|
node bin/navarch.cjs start
|
|
27
27
|
```
|
|
@@ -30,15 +30,15 @@ Or, once dependencies are installed:
|
|
|
30
30
|
|
|
31
31
|
```sh
|
|
32
32
|
npm run build
|
|
33
|
-
|
|
33
|
+
NAVARCH_API_BASE=https://navarch.example.com npm run register -- --token <enrollment-token> --name my-machine-1
|
|
34
34
|
npm start
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
`register` is the one command that prints the machine's auth token — exactly
|
|
38
|
-
once, to stdout. It is stored at `$
|
|
39
|
-
`~/.
|
|
38
|
+
once, to stdout. It is stored at `$NAVARCH_CONFIG_DIR/machine.json` (default
|
|
39
|
+
`~/.navarch/machine.json`, mode `0600`) and never echoed again. For
|
|
40
40
|
twelve-factor deployments (systemd `EnvironmentFile`, container secrets),
|
|
41
|
-
skip `register` and set `
|
|
41
|
+
skip `register` and set `NAVARCH_MACHINE_TOKEN` + `NAVARCH_MACHINE_ID`
|
|
42
42
|
directly.
|
|
43
43
|
|
|
44
44
|
Run `node bin/navarch.cjs doctor` any time to print resolved config, Docker
|
|
@@ -52,21 +52,21 @@ runner token): a project **owner** mints a short-lived (60 minute),
|
|
|
52
52
|
single-use token from the platform UI (the project settings page's "Connect
|
|
53
53
|
an agent" panel, or the onboarding wizard's Agent step),
|
|
54
54
|
`POST /api/projects/:id/enrollment-tokens`, and pastes you the ready-to-run
|
|
55
|
-
command. Unlike `register`, no `
|
|
55
|
+
command. Unlike `register`, no `NAVARCH_ENROLLMENT_SECRET` or
|
|
56
56
|
`--owner-zone` is needed — the token is already scoped to exactly one
|
|
57
57
|
project, and the resulting machine's `project_bindings` is set to that
|
|
58
58
|
project only (it will never be dispatched work from any other project).
|
|
59
59
|
|
|
60
60
|
```sh
|
|
61
61
|
npx @sagentlab/navarch-runtime connect --token flmt_<...> --project <project-id> \
|
|
62
|
-
--name my-agent-1 --api-base https://
|
|
62
|
+
--name my-agent-1 --api-base https://navarch.example.com
|
|
63
63
|
node bin/navarch.cjs start
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
(`@sagentlab/navarch-runtime` is
|
|
67
|
-
|
|
68
|
-
`git clone` + `./install.sh` + `node bin/navarch.cjs
|
|
69
|
-
|
|
66
|
+
(`@sagentlab/navarch-runtime` is published to npm, so `npx
|
|
67
|
+
@sagentlab/navarch-runtime <cmd>` works on a fresh machine with no clone.
|
|
68
|
+
The from-source flow — `git clone` + `./install.sh` + `node bin/navarch.cjs
|
|
69
|
+
<cmd>` — remains available for local development.)
|
|
70
70
|
|
|
71
71
|
## Commands
|
|
72
72
|
|
|
@@ -74,50 +74,50 @@ above.)
|
|
|
74
74
|
|---|---|
|
|
75
75
|
| `register --token <t> --name <n> [--capabilities a,b] [--max-sessions N] [--owner-zone z] [--api-base url]` | Registers this machine (global enrollment secret), prints the token once. |
|
|
76
76
|
| `connect --token <t> --name <n> [--project <id>] [--capabilities a,b] [--max-sessions N] [--api-base url]` | Connects this machine to exactly one project (project-scoped enrollment token), prints the token once. |
|
|
77
|
-
| `start` | Runs the daemon: machine heartbeat loop + claim loop, `
|
|
77
|
+
| `start` | Runs the daemon: machine heartbeat loop + claim loop, `NAVARCH_MAX_SESSIONS` concurrent task sessions. |
|
|
78
78
|
| `doctor` | Prints resolved config + Docker/registration status; no side effects. |
|
|
79
79
|
|
|
80
|
-
## Configuration (`
|
|
80
|
+
## Configuration (`NAVARCH_*` env vars)
|
|
81
81
|
|
|
82
82
|
| Var | Default | Meaning |
|
|
83
83
|
|---|---|---|
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
84
|
+
| `NAVARCH_API_BASE` | `http://localhost:3000` | Control-plane base URL. |
|
|
85
|
+
| `NAVARCH_MACHINE_TOKEN` / `NAVARCH_MACHINE_ID` | — | Skip `register`/the config file; twelve-factor auth. |
|
|
86
|
+
| `NAVARCH_MACHINE_NAME` | — | Used by `register`. |
|
|
87
|
+
| `NAVARCH_ENROLLMENT_TOKEN` | — | Alternative to `register --token` / `connect --token`. |
|
|
88
|
+
| `NAVARCH_PROJECT_ID` | — | Alternative to `connect --project`. |
|
|
89
|
+
| `NAVARCH_CONFIG_DIR` | `~/.navarch` | Where `machine.json` lives. |
|
|
90
|
+
| `NAVARCH_WORKSPACE_ROOT` | `<config dir>/sandboxes` | Per-session working directories (host side; mounted into each container). |
|
|
91
|
+
| `NAVARCH_MAX_SESSIONS` | `3` | Capacity cap — see `src/capacity.cts`. |
|
|
92
|
+
| `NAVARCH_CAPABILITIES` | `docker-sandbox,shell` | Comma list reported at heartbeat/claim time. |
|
|
93
|
+
| `NAVARCH_OWNER_ZONE` | `sagentlab` | `sagentlab` or `customer-<slug>-premises` (project-plan.md §3.11). |
|
|
94
|
+
| `NAVARCH_POLL_INTERVAL_MS` | `5000` | Claim-loop poll interval. |
|
|
95
|
+
| `NAVARCH_HEARTBEAT_INTERVAL_MS` | `60000` | Machine-level heartbeat interval. |
|
|
96
|
+
| `NAVARCH_LEASE_HEARTBEAT_INTERVAL_MS` | `300000` | Per-lease heartbeat interval; must stay well under the 15-minute lease TTL (schema-design.md §4). |
|
|
97
|
+
| `NAVARCH_SESSION_TIMEOUT_MS` | `2700000` (45 min) | Hard kill timeout for a single session. |
|
|
98
|
+
| `NAVARCH_SANDBOX_MODE` | `docker` | `docker` or `host` (host mode skips the container — dev/debug only, no isolation). |
|
|
99
|
+
| `NAVARCH_DOCKER_IMAGE` | `node:20-slim` | Image used for the per-session container. |
|
|
100
|
+
| `NAVARCH_AGENT` | `claude-code` | Which agent CLI runs sessions: `claude-code` or `codex`. Any other value falls back to `claude-code`. Recorded as `agent_type` on the `complete()` call so the sessions row shows which one ran. |
|
|
101
|
+
| `NAVARCH_CLAUDE_BIN` | `claude` | Path/name of the Claude Code CLI binary. |
|
|
102
|
+
| `NAVARCH_CLAUDE_EXTRA_ARGS` | — | Comma list of extra CLI args appended after `--mcp-config` (Claude Code). |
|
|
103
|
+
| `NAVARCH_CODEX_BIN` | `codex` | Path/name of the Codex CLI binary. |
|
|
104
|
+
| `NAVARCH_CODEX_EXTRA_ARGS` | — | Comma list of extra CLI args appended after `--mcp-config`/`--json` (Codex). |
|
|
105
|
+
| `NAVARCH_MCP_CONFIG_PATH` | — | Path to the platform MCP config passed as `--mcp-config`. |
|
|
106
106
|
|
|
107
107
|
## Choosing an agent (Claude Code vs. Codex)
|
|
108
108
|
|
|
109
109
|
Each machine runs sessions with exactly one agent CLI, set once via
|
|
110
|
-
`
|
|
110
|
+
`NAVARCH_AGENT`:
|
|
111
111
|
|
|
112
112
|
```sh
|
|
113
113
|
# Claude Code (default) — requires the `claude` CLI installed and
|
|
114
|
-
# authenticated on this machine (or
|
|
115
|
-
export
|
|
114
|
+
# authenticated on this machine (or NAVARCH_CLAUDE_BIN pointing at it).
|
|
115
|
+
export NAVARCH_AGENT=claude-code
|
|
116
116
|
|
|
117
117
|
# OpenAI Codex — requires the `codex` CLI installed and authenticated on
|
|
118
|
-
# this machine (or
|
|
118
|
+
# this machine (or NAVARCH_CODEX_BIN pointing at it), analogous to the
|
|
119
119
|
# Claude Code prerequisite above.
|
|
120
|
-
export
|
|
120
|
+
export NAVARCH_AGENT=codex
|
|
121
121
|
```
|
|
122
122
|
|
|
123
123
|
Both adapters implement the same `AgentAdapter` interface
|
|
@@ -144,17 +144,17 @@ cli.cts
|
|
|
144
144
|
├─ register → api.registerMachine() → machine-store.cts (writes machine.json once)
|
|
145
145
|
├─ connect → api.connectMachine() → machine-store.cts (writes machine.json once)
|
|
146
146
|
└─ start
|
|
147
|
-
├─ MachineHeartbeatLoop (heartbeat-loop.cts) → api.machineHeartbeat() [every
|
|
148
|
-
└─ ClaimLoop (claim-loop.cts) → api.claim() [every
|
|
149
|
-
└─ runSession (session.cts), one per claimed lease, run concurrently up to
|
|
147
|
+
├─ MachineHeartbeatLoop (heartbeat-loop.cts) → api.machineHeartbeat() [every NAVARCH_HEARTBEAT_INTERVAL_MS]
|
|
148
|
+
└─ ClaimLoop (claim-loop.cts) → api.claim() [every NAVARCH_POLL_INTERVAL_MS, gated by CapacityTracker]
|
|
149
|
+
└─ runSession (session.cts), one per claimed lease, run concurrently up to NAVARCH_MAX_SESSIONS:
|
|
150
150
|
1. write prompt.md (prompt.cts renders the 4-layer context bundle)
|
|
151
151
|
2. api.issueSecrets() → held in memory only
|
|
152
152
|
3. DockerSandbox.create/injectEnv/cloneRepo (sandbox.cts)
|
|
153
153
|
4. selectAdapter(config.agentType) (adapters/index.cts) picks one AgentAdapter
|
|
154
|
-
(adapters/types.cts) by
|
|
154
|
+
(adapters/types.cts) by NAVARCH_AGENT, then .run(...):
|
|
155
155
|
- claudeCodeAdapter (adapters/claude.cts) — `claude -p <prompt> --mcp-config <path>`
|
|
156
156
|
- codexAdapter (adapters/codex.cts) — `codex exec <prompt> --json --mcp-config <path>` (ASSUMED)
|
|
157
|
-
heartbeating the lease every
|
|
157
|
+
heartbeating the lease every NAVARCH_LEASE_HEARTBEAT_INTERVAL_MS throughout either;
|
|
158
158
|
a failed heartbeat aborts the run (kills the process) and marks the outcome as lease-lost
|
|
159
159
|
5. mapExitCondition (exit-conditions.cts) → redact.cts scrubs the transcript → upload.cts PUTs it
|
|
160
160
|
6. api.completeLease(), reporting agent_type: config.agentType
|
|
@@ -229,13 +229,13 @@ everything that can be verified without those:
|
|
|
229
229
|
plus Claude's and Codex's usage/report parsing (`tests/exit-conditions.test.cts`).
|
|
230
230
|
- `redact.cts` — exact-value and pattern-based redaction (`tests/redact.test.cts`).
|
|
231
231
|
- `capacity.cts` — capacity math and acquire/release bookkeeping (`tests/capacity.test.cts`).
|
|
232
|
-
- `config.cts` — env var parsing and defaults, including `
|
|
233
|
-
`
|
|
232
|
+
- `config.cts` — env var parsing and defaults, including `NAVARCH_AGENT`/
|
|
233
|
+
`NAVARCH_CODEX_BIN`/`NAVARCH_CODEX_EXTRA_ARGS` (`tests/config.test.cts`).
|
|
234
234
|
- `sandbox.cts` — command construction (flags, env-via-stdin, credential-helper
|
|
235
235
|
argv hygiene) against an injected fake `CommandRunner`, plus `isDockerAvailable()`
|
|
236
236
|
degrading to `false` instead of throwing when Docker is absent (`tests/sandbox.test.cts`).
|
|
237
237
|
- `adapters/index.cts#selectAdapter` — picks the right `AgentAdapter` for every
|
|
238
|
-
`
|
|
238
|
+
`NAVARCH_AGENT` value, including the claude-code fallback for an unrecognized
|
|
239
239
|
one (`tests/adapters/index.test.cts`).
|
|
240
240
|
- `adapters/codex.cts` — arg construction on both the host path (mocked `spawn`)
|
|
241
241
|
and the docker-exec path (fake `CommandRunner`), and usage/report-text
|
|
@@ -287,7 +287,7 @@ secrets absent from disk after exit"):
|
|
|
287
287
|
`--dangerously-bypass-approvals-and-sandbox` per published Codex CLI
|
|
288
288
|
docs) to avoid blocking on an approval prompt inside the already-isolated
|
|
289
289
|
Docker sandbox — deliberately **not** hardcoded, left to
|
|
290
|
-
`
|
|
290
|
+
`NAVARCH_CODEX_EXTRA_ARGS` until confirmed, since guessing the wrong
|
|
291
291
|
flag here could silently disable sandboxing rather than just fail loudly.
|
|
292
292
|
- Whether the Codex CLI even authenticates/runs non-interactively the same
|
|
293
293
|
way `claude` does (API key vs. ChatGPT-account OAuth device flow) — this
|
|
@@ -315,13 +315,14 @@ secrets absent from disk after exit"):
|
|
|
315
315
|
that one project. Unit-tested here: `connectMachine()`'s request shape
|
|
316
316
|
(`tests/api.test.cts`) and the `connect` command's flag/env parsing
|
|
317
317
|
(`tests/cli.test.cts`); not tested here: the real Postgres round trip
|
|
318
|
-
(`lib/
|
|
318
|
+
(`lib/navarch/__tests__/enrollment.test.ts` and the
|
|
319
319
|
`app/api/machines/connect` / `app/api/projects/[id]/enrollment-tokens`
|
|
320
320
|
route tests cover that with a mocked admin client, not a live database).
|
|
321
|
-
- `@sagentlab/navarch-runtime` is
|
|
321
|
+
- `@sagentlab/navarch-runtime` is published to npm — every `npx
|
|
322
322
|
@sagentlab/navarch-runtime ...` command shown above (and in
|
|
323
|
-
`ConnectAgentPanel`)
|
|
324
|
-
(`git clone` + `./install.sh` + `node bin/navarch.cjs
|
|
323
|
+
`ConnectAgentPanel`) resolves the published package directly; the
|
|
324
|
+
from-source flow (`git clone` + `./install.sh` + `node bin/navarch.cjs
|
|
325
|
+
connect ...`) is an equivalent local-development alternative.
|
|
325
326
|
|
|
326
327
|
## A note on `.cts` instead of `.ts`
|
|
327
328
|
|
package/dist/adapter.cjs
CHANGED
|
@@ -7,7 +7,7 @@ exports.claudeCodeAdapter = exports.runClaudeCodeAdapter = void 0;
|
|
|
7
7
|
* The Claude Code adapter that used to live entirely in this file now lives
|
|
8
8
|
* in adapters/claude.cts, implementing the generalized `AgentAdapter`
|
|
9
9
|
* interface (adapters/types.cts) alongside adapters/codex.cts's Codex CLI
|
|
10
|
-
* sibling — session.cts picks between the two via
|
|
10
|
+
* sibling — session.cts picks between the two via NAVARCH_AGENT
|
|
11
11
|
* (config.cts's `agentType`) through adapters/index.cts#selectAdapter.
|
|
12
12
|
*
|
|
13
13
|
* This file is kept, unchanged in its exported names, so any existing
|
package/dist/adapters/claude.cjs
CHANGED
|
@@ -14,7 +14,7 @@ const exit_conditions_cjs_1 = require("../exit-conditions.cjs");
|
|
|
14
14
|
*
|
|
15
15
|
* This is one of two implementations of the AgentAdapter interface
|
|
16
16
|
* (adapters/types.cts) — see adapters/codex.cts for the Codex CLI sibling
|
|
17
|
-
* session.cts picks between via
|
|
17
|
+
* session.cts picks between via NAVARCH_AGENT (config.cts's `agentType`).
|
|
18
18
|
*
|
|
19
19
|
* NEEDS LIVE VERIFICATION (WP-13): `--output-format json` is assumed to make
|
|
20
20
|
* `claude -p` print a single JSON result object with a `usage` block, per
|
|
@@ -128,7 +128,7 @@ async function runViaDocker(options, args) {
|
|
|
128
128
|
function shellQuote(value) {
|
|
129
129
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
130
130
|
}
|
|
131
|
-
/** The AgentAdapter (adapters/types.cts) wrapper session.cts selects via
|
|
131
|
+
/** The AgentAdapter (adapters/types.cts) wrapper session.cts selects via NAVARCH_AGENT=claude-code (the default). */
|
|
132
132
|
exports.claudeCodeAdapter = {
|
|
133
133
|
agentType: "claude-code",
|
|
134
134
|
run: runClaudeCodeAdapter,
|
package/dist/adapters/codex.cjs
CHANGED
|
@@ -8,7 +8,7 @@ const exit_conditions_cjs_1 = require("../exit-conditions.cjs");
|
|
|
8
8
|
* Headless OpenAI Codex CLI adapter — the Codex sibling of claude.cts's
|
|
9
9
|
* `runClaudeCodeAdapter`, implementing the same AgentAdapter interface
|
|
10
10
|
* (adapters/types.cts) so session.cts can pick either one at runtime via
|
|
11
|
-
*
|
|
11
|
+
* NAVARCH_AGENT (config.cts's `agentType`). Structurally this mirrors
|
|
12
12
|
* claude.cts exactly: same host-vs-docker-exec split, same
|
|
13
13
|
* timeout/AbortSignal handling, same "attach best-effort usage onto the raw
|
|
14
14
|
* result" shape — only the CLI invocation and output parsing differ.
|
|
@@ -41,14 +41,14 @@ const exit_conditions_cjs_1 = require("../exit-conditions.cjs");
|
|
|
41
41
|
* - Sandboxing/approvals: a real `codex exec` may prompt for
|
|
42
42
|
* approval/sandbox-escalation on some actions by default; because this
|
|
43
43
|
* runtime already isolates the session in its own Docker container (or,
|
|
44
|
-
* in `
|
|
44
|
+
* in `NAVARCH_SANDBOX_MODE=host`, trusts the host), the intent is to pass
|
|
45
45
|
* whatever flag disables Codex's own approval gate for a fully
|
|
46
46
|
* non-interactive run (something like `--full-auto` or
|
|
47
47
|
* `--dangerously-bypass-approvals-and-sandbox` in published Codex CLI
|
|
48
48
|
* documentation) — deliberately NOT hardcoded here since getting an
|
|
49
49
|
* unverified flag wrong could silently change sandboxing behavior; left to
|
|
50
|
-
* be supplied via
|
|
51
|
-
* - `
|
|
50
|
+
* be supplied via NAVARCH_CODEX_EXTRA_ARGS until confirmed.
|
|
51
|
+
* - `NAVARCH_CODEX_EXTRA_ARGS` (`extraArgs`) wins over the default `--json`
|
|
52
52
|
* exactly like the Claude adapter's `--output-format` opt-out, so an
|
|
53
53
|
* operator can fall back to plain-text output (or add the real
|
|
54
54
|
* approval-bypass flag) without an adapter code change.
|
|
@@ -160,7 +160,7 @@ async function runViaDocker(options, args) {
|
|
|
160
160
|
function shellQuote(value) {
|
|
161
161
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
162
162
|
}
|
|
163
|
-
/** The AgentAdapter (adapters/types.cts) wrapper session.cts selects via
|
|
163
|
+
/** The AgentAdapter (adapters/types.cts) wrapper session.cts selects via NAVARCH_AGENT=codex. */
|
|
164
164
|
exports.codexAdapter = {
|
|
165
165
|
agentType: "codex",
|
|
166
166
|
run: runCodexAdapter,
|
package/dist/adapters/index.cjs
CHANGED
|
@@ -10,7 +10,7 @@ Object.defineProperty(exports, "codexAdapter", { enumerable: true, get: function
|
|
|
10
10
|
Object.defineProperty(exports, "runCodexAdapter", { enumerable: true, get: function () { return codex_cjs_1.runCodexAdapter; } });
|
|
11
11
|
/**
|
|
12
12
|
* Picks the AgentAdapter (adapters/types.cts) session.cts should run a
|
|
13
|
-
* session with, keyed off config.cts's `agentType` (
|
|
13
|
+
* session with, keyed off config.cts's `agentType` (NAVARCH_AGENT). This is
|
|
14
14
|
* the one place agent-type branching happens outside config loading itself —
|
|
15
15
|
* session.cts calls the returned adapter's `run()` uniformly regardless of
|
|
16
16
|
* which one it got.
|
package/dist/api.cjs
CHANGED
|
@@ -74,11 +74,11 @@ class NavarchApiClient {
|
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
76
76
|
* `POST /api/machines/connect` — "Connect an agent to a project"
|
|
77
|
-
* (docs/
|
|
77
|
+
* (docs/navarch/schema-design.md §7 "Agent connect"). CONFIRMED endpoint,
|
|
78
78
|
* the project-scoped sibling of {@link registerMachine}: redeems a
|
|
79
79
|
* single-use, project-scoped enrollment token a project owner minted
|
|
80
80
|
* (POST /api/projects/:id/enrollment-tokens) instead of the global
|
|
81
|
-
*
|
|
81
|
+
* NAVARCH_ENROLLMENT_SECRET. Unauthenticated like registerMachine — the
|
|
82
82
|
* enrollment token in the body is the auth.
|
|
83
83
|
*/
|
|
84
84
|
async connectMachine(req) {
|
package/dist/capacity.cjs
CHANGED
|
@@ -9,7 +9,7 @@ function computeAvailableCapacity(maxSessions, activeSessions) {
|
|
|
9
9
|
/**
|
|
10
10
|
* Tracks which session (lease) ids currently occupy a slot on this machine so
|
|
11
11
|
* the heartbeat loop reports accurate available_capacity and the claim loop
|
|
12
|
-
* never over-claims beyond
|
|
12
|
+
* never over-claims beyond NAVARCH_MAX_SESSIONS.
|
|
13
13
|
*/
|
|
14
14
|
class CapacityTracker {
|
|
15
15
|
maxSessions;
|
package/dist/claim-loop.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ClaimLoop = void 0;
|
|
4
4
|
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const api_cjs_1 = require("./api.cjs");
|
|
5
6
|
const logger_cjs_1 = require("./logger.cjs");
|
|
6
7
|
const log = (0, logger_cjs_1.createLogger)("claim");
|
|
7
8
|
/**
|
|
@@ -57,7 +58,17 @@ class ClaimLoop {
|
|
|
57
58
|
.finally(() => this.capacity.release(claimed.lease_id));
|
|
58
59
|
}
|
|
59
60
|
catch (err) {
|
|
60
|
-
|
|
61
|
+
// NavarchApiError's message is only the status line ("… failed with
|
|
62
|
+
// 500"); the control plane's actual error text lives in `.body`. Log it
|
|
63
|
+
// so a server-side claim failure is diagnosable from the runtime alone
|
|
64
|
+
// instead of an opaque bare status.
|
|
65
|
+
if (err instanceof api_cjs_1.NavarchApiError) {
|
|
66
|
+
const detail = typeof err.body === "string" ? err.body : JSON.stringify(err.body);
|
|
67
|
+
log.warn(`claim failed: ${err.message}${detail ? ` — ${detail}` : ""}`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
log.warn(`claim failed: ${String(err)}`);
|
|
71
|
+
}
|
|
61
72
|
}
|
|
62
73
|
}
|
|
63
74
|
}
|
package/dist/cli.cjs
CHANGED
|
@@ -11,6 +11,24 @@ const session_cjs_1 = require("./session.cjs");
|
|
|
11
11
|
const sandbox_cjs_1 = require("./sandbox.cjs");
|
|
12
12
|
const logger_cjs_1 = require("./logger.cjs");
|
|
13
13
|
const log = (0, logger_cjs_1.createLogger)("cli");
|
|
14
|
+
const PACKAGE_NAME = "@sagentlab/navarch-runtime";
|
|
15
|
+
/**
|
|
16
|
+
* How to tell the user to re-invoke this CLI, matching however THEY launched
|
|
17
|
+
* it. The bare `navarch-runtime` bin only exists on PATH after a global install
|
|
18
|
+
* (`npm i -g`) or `npm link`; the documented onboarding path is `npx
|
|
19
|
+
* @sagentlab/navarch-runtime …`, where the bare name is NOT on PATH. Printing
|
|
20
|
+
* `navarch-runtime start` to someone who ran us via npx sends them straight
|
|
21
|
+
* into `command not found`, so detect that case and echo the form that works.
|
|
22
|
+
*/
|
|
23
|
+
function invocation(subcommand) {
|
|
24
|
+
const scriptPath = process.argv[1] ?? "";
|
|
25
|
+
const viaNpx = scriptPath.includes("/_npx/") ||
|
|
26
|
+
scriptPath.includes("\\_npx\\") ||
|
|
27
|
+
process.env.npm_command === "exec" ||
|
|
28
|
+
process.env.npm_lifecycle_event === "npx";
|
|
29
|
+
const prefix = viaNpx ? `npx ${PACKAGE_NAME}` : "navarch-runtime";
|
|
30
|
+
return `${prefix} ${subcommand}`;
|
|
31
|
+
}
|
|
14
32
|
function parseArgs(argv) {
|
|
15
33
|
const [command, ...rest] = argv;
|
|
16
34
|
const flags = {};
|
|
@@ -38,13 +56,13 @@ function parseArgs(argv) {
|
|
|
38
56
|
async function registerCommand(flags) {
|
|
39
57
|
const config = (0, config_cjs_1.loadRuntimeConfig)();
|
|
40
58
|
const apiBase = flags["api-base"] ?? config.apiBase;
|
|
41
|
-
const enrollmentToken = flags.token ?? process.env.
|
|
42
|
-
const name = flags.name ?? process.env.
|
|
59
|
+
const enrollmentToken = flags.token ?? process.env.NAVARCH_ENROLLMENT_TOKEN;
|
|
60
|
+
const name = flags.name ?? process.env.NAVARCH_MACHINE_NAME;
|
|
43
61
|
if (!enrollmentToken) {
|
|
44
|
-
throw new Error("Missing --token (or
|
|
62
|
+
throw new Error("Missing --token (or NAVARCH_ENROLLMENT_TOKEN) — get one from the admin console.");
|
|
45
63
|
}
|
|
46
64
|
if (!name) {
|
|
47
|
-
throw new Error("Missing --name (or
|
|
65
|
+
throw new Error("Missing --name (or NAVARCH_MACHINE_NAME) for this machine.");
|
|
48
66
|
}
|
|
49
67
|
const maxSessions = Number(flags["max-sessions"] ?? config.maxSessions);
|
|
50
68
|
const capabilities = (flags.capabilities ?? config.capabilities.join(","))
|
|
@@ -70,28 +88,28 @@ async function registerCommand(flags) {
|
|
|
70
88
|
console.log("Machine registered.");
|
|
71
89
|
console.log(` machine_id: ${result.machine_id}`);
|
|
72
90
|
console.log(` token: ${result.token}`);
|
|
73
|
-
console.log(`\nStored in ${config.configDir}/machine.json (mode 0600). Run
|
|
91
|
+
console.log(`\nStored in ${config.configDir}/machine.json (mode 0600). Run \`${invocation("start")}\` to begin serving tasks.`);
|
|
74
92
|
}
|
|
75
93
|
/**
|
|
76
94
|
* `navarch-runtime connect` — "Connect an agent to a project"
|
|
77
|
-
* (docs/
|
|
95
|
+
* (docs/navarch/schema-design.md §7 "Agent connect"; self-hosted-runner
|
|
78
96
|
* style). The project-scoped sibling of `register`: redeems a short-lived,
|
|
79
97
|
* single-use enrollment token a project owner minted from the platform UI
|
|
80
98
|
* (ConnectAgentPanel → POST /api/projects/:id/enrollment-tokens) instead of
|
|
81
|
-
* the global
|
|
99
|
+
* the global NAVARCH_ENROLLMENT_SECRET `register` needs. Prints the
|
|
82
100
|
* machine token exactly once, same discipline as `register`.
|
|
83
101
|
*/
|
|
84
102
|
async function connectCommand(flags) {
|
|
85
103
|
const config = (0, config_cjs_1.loadRuntimeConfig)();
|
|
86
104
|
const apiBase = flags["api-base"] ?? config.apiBase;
|
|
87
|
-
const enrollmentToken = flags.token ?? process.env.
|
|
88
|
-
const name = flags.name ?? process.env.
|
|
89
|
-
const projectId = flags.project ?? process.env.
|
|
105
|
+
const enrollmentToken = flags.token ?? process.env.NAVARCH_ENROLLMENT_TOKEN;
|
|
106
|
+
const name = flags.name ?? process.env.NAVARCH_MACHINE_NAME;
|
|
107
|
+
const projectId = flags.project ?? process.env.NAVARCH_PROJECT_ID;
|
|
90
108
|
if (!enrollmentToken) {
|
|
91
|
-
throw new Error("Missing --token (or
|
|
109
|
+
throw new Error("Missing --token (or NAVARCH_ENROLLMENT_TOKEN) — get one from a project owner's \"Connect an agent\" panel.");
|
|
92
110
|
}
|
|
93
111
|
if (!name) {
|
|
94
|
-
throw new Error("Missing --name (or
|
|
112
|
+
throw new Error("Missing --name (or NAVARCH_MACHINE_NAME) for this machine.");
|
|
95
113
|
}
|
|
96
114
|
const maxSessions = Number(flags["max-sessions"] ?? config.maxSessions);
|
|
97
115
|
const capabilities = (flags.capabilities ?? config.capabilities.join(","))
|
|
@@ -116,7 +134,7 @@ async function connectCommand(flags) {
|
|
|
116
134
|
console.log("Machine connected.");
|
|
117
135
|
console.log(` machine_id: ${result.machine_id}`);
|
|
118
136
|
console.log(` token: ${result.token}`);
|
|
119
|
-
console.log(`\nStored in ${config.configDir}/machine.json (mode 0600). Run
|
|
137
|
+
console.log(`\nStored in ${config.configDir}/machine.json (mode 0600). Run \`${invocation("start")}\` to begin serving tasks.`);
|
|
120
138
|
}
|
|
121
139
|
async function startCommand() {
|
|
122
140
|
const config = (0, config_cjs_1.loadRuntimeConfig)();
|
|
@@ -152,7 +170,7 @@ async function doctorCommand() {
|
|
|
152
170
|
console.log(`machine: ${identity.name} (${identity.machine_id})`);
|
|
153
171
|
}
|
|
154
172
|
catch {
|
|
155
|
-
console.log(
|
|
173
|
+
console.log(`machine: not registered — run \`${invocation("register")}\``);
|
|
156
174
|
}
|
|
157
175
|
}
|
|
158
176
|
function helpText() {
|
|
@@ -166,7 +184,7 @@ Usage:
|
|
|
166
184
|
navarch-runtime start
|
|
167
185
|
navarch-runtime doctor
|
|
168
186
|
|
|
169
|
-
Configuration is via
|
|
187
|
+
Configuration is via NAVARCH_* environment variables; see runtime/README.md.
|
|
170
188
|
`;
|
|
171
189
|
}
|
|
172
190
|
async function main(argv = process.argv.slice(2)) {
|
package/dist/config.cjs
CHANGED
|
@@ -23,33 +23,33 @@ function envList(env, name, fallback) {
|
|
|
23
23
|
.filter(Boolean);
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
-
* Loads runtime config from
|
|
26
|
+
* Loads runtime config from NAVARCH_* env vars, with sane defaults for a
|
|
27
27
|
* fresh machine. Accepts an explicit env map (defaulting to process.env) so
|
|
28
28
|
* it is trivially unit-testable without mutating global state.
|
|
29
29
|
*/
|
|
30
30
|
function loadRuntimeConfig(env = process.env) {
|
|
31
|
-
const configDir = env.
|
|
32
|
-
const leaseHeartbeatIntervalMs = envInt(env, "
|
|
31
|
+
const configDir = env.NAVARCH_CONFIG_DIR ?? node_path_1.default.join(node_os_1.default.homedir(), ".navarch");
|
|
32
|
+
const leaseHeartbeatIntervalMs = envInt(env, "NAVARCH_LEASE_HEARTBEAT_INTERVAL_MS", 5 * 60 * 1000);
|
|
33
33
|
return {
|
|
34
|
-
apiBase: env.
|
|
35
|
-
workspaceRoot: env.
|
|
34
|
+
apiBase: env.NAVARCH_API_BASE ?? "http://localhost:3000",
|
|
35
|
+
workspaceRoot: env.NAVARCH_WORKSPACE_ROOT ?? node_path_1.default.join(configDir, "sandboxes"),
|
|
36
36
|
configDir,
|
|
37
|
-
maxSessions: envInt(env, "
|
|
38
|
-
capabilities: envList(env, "
|
|
39
|
-
ownerZone: env.
|
|
40
|
-
pollIntervalMs: envInt(env, "
|
|
41
|
-
machineHeartbeatIntervalMs: envInt(env, "
|
|
37
|
+
maxSessions: envInt(env, "NAVARCH_MAX_SESSIONS", 3),
|
|
38
|
+
capabilities: envList(env, "NAVARCH_CAPABILITIES", ["docker-sandbox", "shell"]),
|
|
39
|
+
ownerZone: env.NAVARCH_OWNER_ZONE ?? "sagentlab",
|
|
40
|
+
pollIntervalMs: envInt(env, "NAVARCH_POLL_INTERVAL_MS", 5000),
|
|
41
|
+
machineHeartbeatIntervalMs: envInt(env, "NAVARCH_HEARTBEAT_INTERVAL_MS", 60_000),
|
|
42
42
|
// leases.expires_at = claimed_at + 15 min (schema-design.md §4) — default renewal
|
|
43
43
|
// interval must stay comfortably under that TTL.
|
|
44
44
|
leaseHeartbeatIntervalMs,
|
|
45
|
-
sessionTimeoutMs: envInt(env, "
|
|
46
|
-
agentType: env.
|
|
47
|
-
claudeBin: env.
|
|
48
|
-
claudeExtraArgs: envList(env, "
|
|
49
|
-
codexBin: env.
|
|
50
|
-
codexExtraArgs: envList(env, "
|
|
51
|
-
mcpConfigPath: env.
|
|
52
|
-
sandboxMode: env.
|
|
53
|
-
dockerImage: env.
|
|
45
|
+
sessionTimeoutMs: envInt(env, "NAVARCH_SESSION_TIMEOUT_MS", 45 * 60 * 1000),
|
|
46
|
+
agentType: env.NAVARCH_AGENT === "codex" ? "codex" : "claude-code",
|
|
47
|
+
claudeBin: env.NAVARCH_CLAUDE_BIN ?? "claude",
|
|
48
|
+
claudeExtraArgs: envList(env, "NAVARCH_CLAUDE_EXTRA_ARGS", []),
|
|
49
|
+
codexBin: env.NAVARCH_CODEX_BIN ?? "codex",
|
|
50
|
+
codexExtraArgs: envList(env, "NAVARCH_CODEX_EXTRA_ARGS", []),
|
|
51
|
+
mcpConfigPath: env.NAVARCH_MCP_CONFIG_PATH ?? null,
|
|
52
|
+
sandboxMode: env.NAVARCH_SANDBOX_MODE === "host" ? "host" : "docker",
|
|
53
|
+
dockerImage: env.NAVARCH_DOCKER_IMAGE ?? "node:20-slim",
|
|
54
54
|
};
|
|
55
55
|
}
|
package/dist/machine-store.cjs
CHANGED
|
@@ -40,19 +40,19 @@ async function loadMachineIdentity(configDir) {
|
|
|
40
40
|
* per-task broker-issued secrets, handled entirely in session.cts/sandbox.cts.
|
|
41
41
|
*/
|
|
42
42
|
async function resolveMachineIdentity(configDir, apiBaseFallback) {
|
|
43
|
-
const envToken = process.env.
|
|
44
|
-
const envId = process.env.
|
|
43
|
+
const envToken = process.env.NAVARCH_MACHINE_TOKEN;
|
|
44
|
+
const envId = process.env.NAVARCH_MACHINE_ID;
|
|
45
45
|
if (envToken && envId) {
|
|
46
46
|
return {
|
|
47
47
|
machine_id: envId,
|
|
48
48
|
token: envToken,
|
|
49
|
-
name: process.env.
|
|
50
|
-
api_base: process.env.
|
|
49
|
+
name: process.env.NAVARCH_MACHINE_NAME ?? envId,
|
|
50
|
+
api_base: process.env.NAVARCH_API_BASE ?? apiBaseFallback,
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
const stored = await loadMachineIdentity(configDir);
|
|
54
54
|
if (stored)
|
|
55
55
|
return stored;
|
|
56
56
|
throw new Error("No machine identity found. Run `navarch-runtime register` first, or set " +
|
|
57
|
-
"
|
|
57
|
+
"NAVARCH_MACHINE_TOKEN + NAVARCH_MACHINE_ID for a twelve-factor deployment.");
|
|
58
58
|
}
|
package/dist/mcp-config.cjs
CHANGED
|
@@ -5,27 +5,27 @@
|
|
|
5
5
|
// platform MCP server".
|
|
6
6
|
//
|
|
7
7
|
// Previously `mcpConfigPath` was just a static, operator-supplied path
|
|
8
|
-
// (config.cts's `
|
|
8
|
+
// (config.cts's `NAVARCH_MCP_CONFIG_PATH`) with nothing that ever wrote a
|
|
9
9
|
// real config file -- there was no way for a session to actually reach the
|
|
10
10
|
// platform MCP server with the auth it needs. This module is the fix: one
|
|
11
11
|
// config, generated fresh per session, carrying the machine's bearer token
|
|
12
12
|
// and this session's lease id (the same two-layer auth app/api/mcp/route.ts
|
|
13
|
-
// requires -- see lib/
|
|
13
|
+
// requires -- see lib/navarch/mcp/context.ts).
|
|
14
14
|
//
|
|
15
15
|
// Pure and side-effect free (no filesystem access) so it's trivially unit
|
|
16
16
|
// testable; session.cts is the only caller that writes the result to disk.
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
exports.
|
|
18
|
+
exports.NAVARCH_LEASE_HEADER = void 0;
|
|
19
19
|
exports.buildNavarchMcpConfig = buildNavarchMcpConfig;
|
|
20
|
-
/** The header app/api/mcp/route.ts's lib/
|
|
21
|
-
exports.
|
|
20
|
+
/** The header app/api/mcp/route.ts's lib/navarch/mcp/context.ts reads to identify which lease is calling. */
|
|
21
|
+
exports.NAVARCH_LEASE_HEADER = "X-Navarch-Lease-Id";
|
|
22
22
|
/**
|
|
23
23
|
* Builds the `.mcp.json`-shaped config object Claude Code's `--mcp-config`
|
|
24
24
|
* flag expects: a remote "http" (streamable HTTP) server entry with the
|
|
25
25
|
* bearer token and lease id as request headers.
|
|
26
26
|
*/
|
|
27
27
|
function buildNavarchMcpConfig(opts) {
|
|
28
|
-
const serverName = opts.serverName ?? "
|
|
28
|
+
const serverName = opts.serverName ?? "navarch";
|
|
29
29
|
return {
|
|
30
30
|
mcpServers: {
|
|
31
31
|
[serverName]: {
|
|
@@ -33,7 +33,7 @@ function buildNavarchMcpConfig(opts) {
|
|
|
33
33
|
url: `${opts.apiBase.replace(/\/+$/, "")}/api/mcp`,
|
|
34
34
|
headers: {
|
|
35
35
|
Authorization: `Bearer ${opts.machineToken}`,
|
|
36
|
-
[exports.
|
|
36
|
+
[exports.NAVARCH_LEASE_HEADER]: opts.leaseId,
|
|
37
37
|
},
|
|
38
38
|
},
|
|
39
39
|
},
|
package/dist/prompt.cjs
CHANGED
|
@@ -9,6 +9,11 @@ exports.renderPrompt = renderPrompt;
|
|
|
9
9
|
* WP-07 "write prompt file".
|
|
10
10
|
*/
|
|
11
11
|
function renderPrompt(task, bundle) {
|
|
12
|
+
if (bundle.task_context) {
|
|
13
|
+
return [bundle.task_context.trim(), bundle.retry_context?.trim()]
|
|
14
|
+
.filter((section) => Boolean(section))
|
|
15
|
+
.join("\n\n") + "\n";
|
|
16
|
+
}
|
|
12
17
|
const sections = [];
|
|
13
18
|
sections.push(`# Task: ${task.summary}`);
|
|
14
19
|
sections.push(`Type: ${task.task_type} | Repo: ${task.repo} | Environment: ${task.environment_scope}`);
|
package/dist/sandbox.cjs
CHANGED
|
@@ -69,7 +69,7 @@ async function isDockerAvailable(runner = exports.nodeCommandRunner) {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
function containerName(sessionId) {
|
|
72
|
-
return `
|
|
72
|
+
return `navarch-${sessionId}`;
|
|
73
73
|
}
|
|
74
74
|
/**
|
|
75
75
|
* One Docker container per session (implementation-plan.md WP-07 "Sandbox:
|
package/dist/session.cjs
CHANGED
|
@@ -23,7 +23,7 @@ const log = (0, logger_cjs_1.createLogger)("session");
|
|
|
23
23
|
* 2. fetch secrets from the broker once, at session start
|
|
24
24
|
* 3. stand up a Docker sandbox, clone the repo, inject env vars
|
|
25
25
|
* 4. run the configured agent adapter (Claude Code or Codex, per
|
|
26
|
-
*
|
|
26
|
+
* NAVARCH_AGENT — adapters/index.cts#selectAdapter), heartbeating the
|
|
27
27
|
* lease throughout
|
|
28
28
|
* 5. redact + upload the transcript, map the exit condition, complete the
|
|
29
29
|
* lease (recording which agent_type ran it)
|
|
@@ -87,8 +87,8 @@ async function runSession(deps, claimed, sessionId) {
|
|
|
87
87
|
// Platform MCP config (implementation-plan.md WP-07: "--mcp-config
|
|
88
88
|
// pointing at the platform MCP server"): generated fresh per session,
|
|
89
89
|
// carrying this machine's bearer token and this session's lease id (the
|
|
90
|
-
// auth app/api/mcp/route.ts requires -- see lib/
|
|
91
|
-
// unless the operator pinned a static override via
|
|
90
|
+
// auth app/api/mcp/route.ts requires -- see lib/navarch/mcp/context.ts),
|
|
91
|
+
// unless the operator pinned a static override via NAVARCH_MCP_CONFIG_PATH
|
|
92
92
|
// (e.g. pointing at a fake MCP server in local testing). Written to
|
|
93
93
|
// workDir (host path) so it also lands at /workspace/mcp-config.json
|
|
94
94
|
// inside the Docker sandbox (sandbox.cts binds workDir at /workspace) --
|
|
@@ -110,7 +110,7 @@ async function runSession(deps, claimed, sessionId) {
|
|
|
110
110
|
await sandbox.injectEnv(toEnvMap(secrets));
|
|
111
111
|
await sandbox.cloneRepo(task.repo, Boolean(secrets["github-pat"]));
|
|
112
112
|
}
|
|
113
|
-
// Picks the Claude Code or Codex adapter per
|
|
113
|
+
// Picks the Claude Code or Codex adapter per NAVARCH_AGENT
|
|
114
114
|
// (config.cts's `agentType`) — see adapters/index.cts#selectAdapter.
|
|
115
115
|
// Both adapters implement the same AgentAdapter.run() shape
|
|
116
116
|
// (adapters/types.cts), so nothing else in this function branches on
|
package/dist/types.cjs
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Navarch runtime — shared wire types.
|
|
3
3
|
//
|
|
4
4
|
// These interfaces mirror the JSON shapes described in:
|
|
5
|
-
// - docs/
|
|
6
|
-
// - docs/
|
|
5
|
+
// - docs/navarch/schema-design.md (table columns => API field names, esp. §4, §5, §7)
|
|
6
|
+
// - docs/navarch/implementation-plan.md (WP-07 behavioral contract)
|
|
7
7
|
// - docs/agent-platform-project-plan.md (§3.8 dispatch/§3.9 adapter contract)
|
|
8
8
|
//
|
|
9
9
|
// The control-plane API is being built in parallel (WP-01/WP-04/WP-05) in
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sagentlab/navarch-runtime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Navarch machine-side session manager: registers a machine, claims tasks from the control-plane dispatcher, runs them in a Docker sandbox via the Claude Code or Codex adapter, and reports results back.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,6 +45,6 @@
|
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/node": "^20.14.0",
|
|
47
47
|
"typescript": "^5.7.2",
|
|
48
|
-
"vitest": "^
|
|
48
|
+
"vitest": "^4.1.10"
|
|
49
49
|
}
|
|
50
50
|
}
|