labgate 0.5.39 → 0.5.42
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 +132 -248
- package/dist/cli.js +9 -33
- package/dist/cli.js.map +1 -1
- package/dist/lib/config.d.ts +19 -3
- package/dist/lib/config.js +154 -75
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/container.d.ts +11 -9
- package/dist/lib/container.js +749 -282
- package/dist/lib/container.js.map +1 -1
- package/dist/lib/dataset-mcp.js +2 -9
- package/dist/lib/dataset-mcp.js.map +1 -1
- package/dist/lib/display-mcp.d.ts +2 -2
- package/dist/lib/display-mcp.js +17 -38
- package/dist/lib/display-mcp.js.map +1 -1
- package/dist/lib/doctor.js +8 -0
- package/dist/lib/doctor.js.map +1 -1
- package/dist/lib/explorer-claude.js +36 -1
- package/dist/lib/explorer-claude.js.map +1 -1
- package/dist/lib/explorer-eval.js +3 -2
- package/dist/lib/explorer-eval.js.map +1 -1
- package/dist/lib/image-pull-lock.d.ts +18 -0
- package/dist/lib/image-pull-lock.js +167 -0
- package/dist/lib/image-pull-lock.js.map +1 -0
- package/dist/lib/init.js +22 -19
- package/dist/lib/init.js.map +1 -1
- package/dist/lib/slurm-cli-passthrough.d.ts +12 -2
- package/dist/lib/slurm-cli-passthrough.js +401 -143
- package/dist/lib/slurm-cli-passthrough.js.map +1 -1
- package/dist/lib/startup-stage-lock.d.ts +21 -0
- package/dist/lib/startup-stage-lock.js +196 -0
- package/dist/lib/startup-stage-lock.js.map +1 -0
- package/dist/lib/ui.d.ts +40 -0
- package/dist/lib/ui.html +4953 -3366
- package/dist/lib/ui.js +1771 -297
- package/dist/lib/ui.js.map +1 -1
- package/dist/lib/web-terminal-startup-readiness.d.ts +8 -0
- package/dist/lib/web-terminal-startup-readiness.js +29 -0
- package/dist/lib/web-terminal-startup-readiness.js.map +1 -0
- package/dist/lib/web-terminal.d.ts +51 -0
- package/dist/lib/web-terminal.js +171 -1
- package/dist/lib/web-terminal.js.map +1 -1
- package/dist/mcp-bundles/dataset-mcp.bundle.mjs +144 -93
- package/dist/mcp-bundles/display-mcp.bundle.mjs +35 -43
- package/dist/mcp-bundles/explorer-mcp.bundle.mjs +263 -146
- package/dist/mcp-bundles/results-mcp.bundle.mjs +39 -41
- package/dist/mcp-bundles/slurm-mcp.bundle.mjs +19 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,334 +1,218 @@
|
|
|
1
1
|
# LabGate
|
|
2
2
|
|
|
3
|
-
Policy-controlled sandboxes for AI coding agents
|
|
3
|
+
Policy-controlled sandboxes for AI coding agents on HPC.
|
|
4
4
|
|
|
5
|
-
## Current
|
|
5
|
+
## Current Focus
|
|
6
6
|
|
|
7
|
-
- Primary workflow:
|
|
8
|
-
- Primary runtime: Apptainer
|
|
9
|
-
-
|
|
10
|
-
- Secondary
|
|
7
|
+
- Primary workflow: `labgate claude`
|
|
8
|
+
- Primary runtime: Apptainer
|
|
9
|
+
- HPC default: SLURM tracking enabled by default
|
|
10
|
+
- Secondary path: `labgate codex` (best-effort)
|
|
11
|
+
|
|
12
|
+
Docs: [labgate.dev/docs](https://labgate.dev/docs)
|
|
11
13
|
|
|
12
14
|
## Install
|
|
13
15
|
|
|
14
16
|
```bash
|
|
15
17
|
npm i -g labgate
|
|
18
|
+
labgate init
|
|
16
19
|
```
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Note: `tmux` is a host-level dependency for `labgate ui` / web terminals. `npm i -g labgate` does not install `tmux`; install it through your OS or cluster module system.
|
|
21
|
-
|
|
22
|
-
LabGate prefers Apptainer for sandbox runtime and supports Podman as a fallback (especially on macOS).
|
|
23
|
-
|
|
24
|
-
On UI startup, LabGate ensures a bundled sample dataset (`flowers-iris`) is available at `~/.labgate/datasets/flowers-iris` for first-run testing.
|
|
25
|
-
|
|
26
|
-
## Quick start
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
labgate init # optional: pre-create ~/.labgate/config.json
|
|
30
|
-
labgate claude # launch Claude Code in current dir
|
|
31
|
-
labgate codex /projects/my-analysis # launch Codex in a specific dir
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## What it does
|
|
21
|
+
Notes:
|
|
35
22
|
|
|
36
|
-
|
|
23
|
+
- `labgate ui` requires host `tmux`.
|
|
24
|
+
- `node-pty` is optional. If it fails to build on a minimal Linux host, LabGate still works and falls back to non-sticky output.
|
|
25
|
+
- Default config path is `~/.labgate/config.json`.
|
|
26
|
+
- `labgate init` pre-registers a bundled sample dataset at `~/.labgate/datasets/flowers-iris`.
|
|
37
27
|
|
|
38
|
-
|
|
39
|
-
- **Credential blocking** — `.ssh`, `.aws`, `.env`, `.gnupg`, and other sensitive paths are hidden by default
|
|
40
|
-
- **Network policy** — configurable network modes (`host`, `filtered`, `none`)
|
|
41
|
-
- **Command blocking** — high-risk system commands (for example `mount`, `umount`, `mkfs`, `reboot`) are blocked by default
|
|
42
|
-
- **Audit logging** — session start/stop and mount configuration logged to `~/.labgate/logs/`
|
|
43
|
-
- **Dashboard instructions editor** — view and update per-session `AGENTS.md` / `CLAUDE.md` from the UI
|
|
44
|
-
- **Session context injection** — LabGate prepends a temporary sandbox-mapping instruction block during active sessions
|
|
45
|
-
- **HPC ready** — first-class Apptainer support for shared clusters
|
|
28
|
+
## Quick Start
|
|
46
29
|
|
|
47
|
-
|
|
30
|
+
Recommended two-node HPC flow:
|
|
48
31
|
|
|
49
|
-
|
|
32
|
+
1. On the login node, start the dashboard:
|
|
50
33
|
|
|
51
34
|
```bash
|
|
52
|
-
|
|
35
|
+
labgate ui
|
|
53
36
|
```
|
|
54
37
|
|
|
55
|
-
|
|
38
|
+
2. On a compute node, launch Claude inside the sandbox:
|
|
56
39
|
|
|
57
40
|
```bash
|
|
58
|
-
|
|
41
|
+
srun --pty bash
|
|
42
|
+
cd /path/to/project
|
|
43
|
+
labgate claude
|
|
59
44
|
```
|
|
60
45
|
|
|
61
|
-
|
|
46
|
+
If browser auth is not practical over SSH, pass an API key directly:
|
|
62
47
|
|
|
63
48
|
```bash
|
|
64
|
-
labgate
|
|
49
|
+
labgate claude --api-key "$ANTHROPIC_API_KEY"
|
|
65
50
|
```
|
|
66
51
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
| Setting | Default | What it does |
|
|
70
|
-
|---------|---------|-------------|
|
|
71
|
-
| `runtime` | `auto` | `auto`, `apptainer`, or `podman` |
|
|
72
|
-
| `image` | `docker.io/library/node:20-bookworm` | Container image |
|
|
73
|
-
| `session_timeout_hours` | `8` | Max session length |
|
|
74
|
-
| `filesystem.blocked_patterns` | `.ssh, .aws, .env, ...` | Hidden from sandbox |
|
|
75
|
-
| `filesystem.extra_paths` | `[]` | Additional mounts |
|
|
76
|
-
| `network.mode` | `host` | `none`, `filtered`, or `host` |
|
|
77
|
-
| `commands.blacklist` | `mount, umount, mkfs, reboot, shutdown` | Blocked commands |
|
|
78
|
-
| `slurm.enabled` | `true` | Enable SLURM CLI passthrough (`sbatch`, `squeue`, etc.) and job tracking |
|
|
79
|
-
| `headless.claude_run_with_allowed_permissions` | `true` | In Claude headless mode, auto-allow tool use via `--dangerously-skip-permissions` |
|
|
80
|
-
| `headless.continuation_in_other_terminals` | `true` | Show or hide the `Continue in terminal via labgate continue ...` footer hint |
|
|
81
|
-
| `headless.git_integration` | `false` | Show or hide Git integration UI (Git DAG sidebar widget and footer branch controls) |
|
|
52
|
+
For local non-SSH, non-SLURM shells, `labgate claude` auto-starts `labgate ui` when the UI is not already running.
|
|
82
53
|
|
|
83
|
-
##
|
|
54
|
+
## What LabGate Does
|
|
84
55
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
labgate policy init [--institution ... --admin ...] # create policy template
|
|
96
|
-
labgate policy validate [file] # validate policy JSON
|
|
97
|
-
labgate logs [-n 20] # view recent audit events
|
|
98
|
-
labgate logs --follow # stream new audit events
|
|
99
|
-
labgate init [--force] # create/reset config
|
|
100
|
-
labgate explore create --name <name> --repo <path> --eval "<command>" # create Solution Explorer experiment
|
|
101
|
-
labgate explore tick --experiment <id> # run one autopilot tick (cron-friendly)
|
|
102
|
-
labgate explore overview --experiment <id> # aggregated status/counts/best/latest
|
|
103
|
-
labgate explore run --id <run-id> # run metadata + artifact paths
|
|
104
|
-
labgate explore gc --experiment <id> # retention-based prune (dry-run by default)
|
|
105
|
-
labgate explore compare --experiment <id> --run <run-id> --to best # score+diff compare
|
|
106
|
-
```
|
|
56
|
+
- Runs Claude or Codex inside a containerized sandbox
|
|
57
|
+
- Mounts the working directory, a persistent sandbox home, configured extra paths, and named datasets
|
|
58
|
+
- Hides common credential and secret paths by default
|
|
59
|
+
- Applies network policy with `host` or `filtered` modes
|
|
60
|
+
- Blocks high-risk commands such as `mount`, `umount`, `mkfs`, `reboot`, and `shutdown`
|
|
61
|
+
- Records audit logs in `~/.labgate/logs/`
|
|
62
|
+
- Tracks SLURM jobs and exposes MCP integrations for supported LabGate subsystems
|
|
63
|
+
- Provides a browser UI and web-terminal control plane with `labgate ui`
|
|
64
|
+
- Ships a bundled `flowers-iris` sample dataset for first-run dataset workflows
|
|
65
|
+
- Lets you edit `AGENTS.md` / `CLAUDE.md` from the UI, with a temporary LabGate sandbox-context block injected for active sessions
|
|
107
66
|
|
|
108
|
-
|
|
67
|
+
`network.mode=none` is rejected for `labgate claude` and `labgate codex`.
|
|
109
68
|
|
|
110
|
-
|
|
111
|
-
variant search workflows:
|
|
69
|
+
## Key Defaults
|
|
112
70
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
71
|
+
| Setting | Default |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `runtime` | `auto` |
|
|
74
|
+
| `image` | `docker.io/library/node:20-bookworm` |
|
|
75
|
+
| `session_timeout_hours` | `8` |
|
|
76
|
+
| `network.mode` | `host` |
|
|
77
|
+
| `commands.ensure_commands` | `["git"]` |
|
|
78
|
+
| `slurm.enabled` | `true` |
|
|
79
|
+
| `slurm.mcp_server` | `true` |
|
|
80
|
+
| `audit.enabled` | `true` |
|
|
81
|
+
| `headless.continuation_in_other_terminals` | `true` |
|
|
82
|
+
| `headless.git_integration` | `false` |
|
|
118
83
|
|
|
119
|
-
|
|
84
|
+
Inspect or change config with:
|
|
120
85
|
|
|
121
86
|
```bash
|
|
122
|
-
|
|
87
|
+
labgate config path
|
|
88
|
+
labgate config show
|
|
89
|
+
labgate config get <key>
|
|
90
|
+
labgate config set <key> <value>
|
|
91
|
+
labgate config reset <key>
|
|
123
92
|
```
|
|
124
93
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
# Create experiment from a local repo
|
|
129
|
-
labgate explore create \
|
|
130
|
-
--name "TSP baseline" \
|
|
131
|
-
--repo /path/to/tsp-lab \
|
|
132
|
-
--eval "python3 eval.py" \
|
|
133
|
-
--agent-mode stub \
|
|
134
|
-
--stub-patch stub-patches/enable_two_opt.patch
|
|
135
|
-
|
|
136
|
-
# Trigger one run
|
|
137
|
-
labgate explore tick --experiment <experiment-id>
|
|
138
|
-
|
|
139
|
-
# Inspect tree and leaderboard
|
|
140
|
-
labgate explore tree --experiment <experiment-id> --mode best_path
|
|
141
|
-
labgate explore leaderboard --experiment <experiment-id> --top 5
|
|
142
|
-
```
|
|
94
|
+
## CLI Overview
|
|
143
95
|
|
|
144
|
-
|
|
96
|
+
Core session commands:
|
|
145
97
|
|
|
146
98
|
```bash
|
|
147
|
-
|
|
99
|
+
labgate init
|
|
100
|
+
labgate claude [workdir]
|
|
101
|
+
labgate codex [workdir]
|
|
102
|
+
labgate ui [--port <number> --listen-address <address> --token <string> | --socket <path>]
|
|
103
|
+
labgate status
|
|
104
|
+
labgate continue [idOrName] [--latest]
|
|
105
|
+
labgate stop <id>
|
|
106
|
+
labgate restart <id> [--dry-run]
|
|
107
|
+
labgate logs [-n|--lines <count>] [--follow]
|
|
108
|
+
labgate feedback [message...]
|
|
148
109
|
```
|
|
149
110
|
|
|
150
|
-
|
|
111
|
+
SLURM commands:
|
|
151
112
|
|
|
152
113
|
```bash
|
|
153
|
-
labgate
|
|
154
|
-
labgate
|
|
155
|
-
labgate
|
|
156
|
-
labgate
|
|
157
|
-
labgate
|
|
158
|
-
labgate ui --socket ~/.labgate/ui.sock # custom Unix socket path
|
|
159
|
-
labgate logs --lines 50 --follow # tail last 50 lines and keep following
|
|
114
|
+
labgate slurm status [--state <state>] [--limit <count>] [--search <query>]
|
|
115
|
+
labgate slurm job <id>
|
|
116
|
+
labgate slurm output <id> [--stderr] [--tail <lines>]
|
|
117
|
+
labgate slurm cancel <id>
|
|
118
|
+
labgate slurm mcp [--db <path>]
|
|
160
119
|
```
|
|
161
120
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
### SLURM inside sandboxes (`sbatch` / `squeue`)
|
|
165
|
-
|
|
166
|
-
For Apptainer sessions, LabGate now attempts SLURM CLI passthrough automatically.
|
|
167
|
-
If host `sbatch`/`squeue` are available, they are staged into the sandbox, so
|
|
168
|
-
`labgate claude` should work without extra config in the common HPC path.
|
|
169
|
-
|
|
170
|
-
SLURM tracking and MCP tools are enabled by default (`slurm.enabled=true`).
|
|
171
|
-
If native SQLite (`better-sqlite3`) is unavailable on a host, LabGate falls back
|
|
172
|
-
to a JSON tracking store automatically.
|
|
173
|
-
|
|
174
|
-
Requirements for automatic `sbatch` in sandbox:
|
|
175
|
-
|
|
176
|
-
1. Runtime is Apptainer
|
|
177
|
-
2. The host can resolve SLURM CLI tools when launching LabGate
|
|
178
|
-
|
|
179
|
-
If `sbatch` is missing inside the sandbox, run:
|
|
121
|
+
Dataset commands:
|
|
180
122
|
|
|
181
123
|
```bash
|
|
182
|
-
|
|
183
|
-
labgate
|
|
124
|
+
labgate dataset list
|
|
125
|
+
labgate dataset init <name>
|
|
184
126
|
```
|
|
185
127
|
|
|
186
|
-
|
|
128
|
+
`labgate dataset init` scans an already-registered dataset entry and stores file count and size metadata in config.
|
|
187
129
|
|
|
188
|
-
|
|
189
|
-
module load slurm # or your site-specific module name
|
|
190
|
-
labgate claude
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Optional (disable SLURM tracking DB + MCP server):
|
|
130
|
+
Solution Explorer commands:
|
|
194
131
|
|
|
195
132
|
```bash
|
|
196
|
-
labgate
|
|
133
|
+
labgate explore create --name <name> --repo <path> --eval <command> [options]
|
|
134
|
+
labgate explore list [--limit <count>] [--offset <count>]
|
|
135
|
+
labgate explore status <experimentId> [-n|--limit <count>]
|
|
136
|
+
labgate explore pause <experimentId>
|
|
137
|
+
labgate explore resume <experimentId>
|
|
138
|
+
labgate explore tick --experiment <id>
|
|
139
|
+
labgate explore tree --experiment <id> [--mode best_path|full]
|
|
140
|
+
labgate explore leaderboard --experiment <id> [-k|--top <count>]
|
|
141
|
+
labgate explore gc --experiment <id> [--yes]
|
|
142
|
+
labgate explore retention show --experiment <id>
|
|
143
|
+
labgate explore retention set --experiment <id> [retention flags]
|
|
144
|
+
labgate explore compare --experiment <id> --run <runId> [--to best|parent|<runId>] [--diff]
|
|
145
|
+
labgate explore overview --experiment <id>
|
|
146
|
+
labgate explore run --id <runId>
|
|
147
|
+
labgate explore mcp [--db <path>]
|
|
197
148
|
```
|
|
198
149
|
|
|
199
|
-
|
|
150
|
+
For Claude/Codex sessions, LabGate can also register dataset, cluster, and SLURM MCP servers inside the sandbox when the relevant integrations are enabled.
|
|
200
151
|
|
|
201
|
-
|
|
152
|
+
Enterprise commands:
|
|
202
153
|
|
|
203
154
|
```bash
|
|
204
|
-
labgate
|
|
205
|
-
|
|
206
|
-
labgate
|
|
155
|
+
labgate license
|
|
156
|
+
labgate license install <keyOrFile> [--system|--user|--path] [--overwrite]
|
|
157
|
+
labgate register <activationKey> [--server <url>] [--token <token>] [--timeout <ms>] [--system|--user|--path] [--overwrite]
|
|
158
|
+
labgate policy validate [file]
|
|
159
|
+
labgate policy init [--path <path>] [--institution <name>] [--admin <username>] [--runtime <runtime>] [--force]
|
|
207
160
|
```
|
|
208
161
|
|
|
209
|
-
|
|
210
|
-
If `LABGATE_FEEDBACK_TOKEN` is set, it will be sent as a Bearer token.
|
|
211
|
-
If no URL is configured, LabGate defaults to `https://labgate.dev/api/feedback`.
|
|
212
|
-
If the request fails (or `LABGATE_FEEDBACK_DISABLE=1`), feedback is saved locally at `~/.labgate/feedback.jsonl`.
|
|
162
|
+
For full options, use `labgate <command> --help` or the docs site.
|
|
213
163
|
|
|
214
|
-
##
|
|
164
|
+
## Apptainer and SLURM Notes
|
|
215
165
|
|
|
216
|
-
|
|
166
|
+
- The primary supported path is login-node `labgate ui` plus compute-node `labgate claude`.
|
|
167
|
+
- LabGate prefers Apptainer on HPC. If you manage runtime explicitly, prefer `apptainer`.
|
|
168
|
+
- SLURM tracking is enabled by default.
|
|
169
|
+
- For Apptainer sessions, LabGate expects host SLURM CLIs such as `sbatch` and `squeue` to be available when the session starts.
|
|
170
|
+
- If your site uses environment modules, load SLURM before launching LabGate:
|
|
217
171
|
|
|
218
172
|
```bash
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
npm run verify # build + unit + integration tests
|
|
222
|
-
npm run dev:claude # start UI in background, then launch local labgate claude
|
|
223
|
-
npm run test:unit # fast unit tests
|
|
224
|
-
npm run test:integration # dashboard integration tests
|
|
225
|
-
npm run test:e2e:real # opt-in real runtime OAuth/browser smoke test
|
|
226
|
-
npm test # unit + integration
|
|
227
|
-
npm run release:check # verify + npm pack --dry-run
|
|
173
|
+
module load slurm
|
|
174
|
+
labgate claude
|
|
228
175
|
```
|
|
229
176
|
|
|
230
|
-
|
|
177
|
+
- In SLURM job scripts, use relative paths or real host paths for `#SBATCH --output` and `#SBATCH --error`, not container-only paths such as `/work/...`.
|
|
178
|
+
|
|
179
|
+
Shared SIF cache:
|
|
231
180
|
|
|
232
181
|
```bash
|
|
233
|
-
|
|
234
|
-
export LABGATE_LICENSE_SECRET='replace-with-your-signing-secret'
|
|
235
|
-
LICENSE_KEY="$(npx tsx scripts/generate-license.ts \
|
|
236
|
-
--institution 'Example University' \
|
|
237
|
-
--tier pro \
|
|
238
|
-
--expires 2099-12-31 2>/dev/null)"
|
|
239
|
-
|
|
240
|
-
# 2) Install key on target host (admin side)
|
|
241
|
-
labgate license install "$LICENSE_KEY" --path /tmp/labgate/license.key --overwrite
|
|
242
|
-
# HPC system-wide install (root):
|
|
243
|
-
# sudo labgate license install "$LICENSE_KEY" --system --overwrite
|
|
244
|
-
|
|
245
|
-
# Optional online activation (instead of direct install):
|
|
246
|
-
# Uses default endpoint: https://labgate.dev/api/license/activate
|
|
247
|
-
# labgate register '<activation-key-from-vendor>' --path /tmp/labgate/license.key --overwrite
|
|
248
|
-
# Optional custom endpoint:
|
|
249
|
-
# export LABGATE_ACTIVATION_URL='https://your-control-plane.example.com/api/license/activate'
|
|
250
|
-
|
|
251
|
-
# 3) Bootstrap and validate policy
|
|
252
|
-
labgate policy init --path /tmp/labgate/policy.json --admin "$(whoami)" --force
|
|
253
|
-
labgate policy validate /tmp/labgate/policy.json
|
|
254
|
-
|
|
255
|
-
# 4) Verify forced settings are locked for users
|
|
256
|
-
LABGATE_LICENSE_PATH=/tmp/labgate/license.key \
|
|
257
|
-
LABGATE_POLICY_PATH=/tmp/labgate/policy.json \
|
|
258
|
-
labgate config set runtime auto
|
|
259
|
-
# expected: error about admin-locked field
|
|
260
|
-
|
|
261
|
-
# 5) Open dashboard and verify UI lock labels ("set by admin")
|
|
262
|
-
LABGATE_LICENSE_PATH=/tmp/labgate/license.key \
|
|
263
|
-
LABGATE_POLICY_PATH=/tmp/labgate/policy.json \
|
|
264
|
-
labgate ui
|
|
182
|
+
labgate config set images_dir /shared/labgate/images
|
|
265
183
|
```
|
|
266
184
|
|
|
267
|
-
|
|
185
|
+
Or with an environment override:
|
|
268
186
|
|
|
269
187
|
```bash
|
|
270
|
-
|
|
188
|
+
export LABGATE_IMAGES_DIR=/shared/labgate/images
|
|
271
189
|
```
|
|
272
190
|
|
|
273
|
-
|
|
274
|
-
`npm run test:integration` automatically rebuilds `better-sqlite3` first to avoid Node ABI mismatch errors after Node upgrades.
|
|
191
|
+
`LABGATE_IMAGES_DIR` takes precedence over `images_dir`.
|
|
275
192
|
|
|
276
|
-
|
|
193
|
+
## Feedback
|
|
277
194
|
|
|
278
195
|
```bash
|
|
279
|
-
|
|
280
|
-
|
|
196
|
+
labgate feedback
|
|
197
|
+
labgate feedback "Short feedback message"
|
|
198
|
+
echo "This was great" | labgate feedback
|
|
281
199
|
```
|
|
282
200
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
- **`npm run test:unit`** covers config/runtime/container helpers
|
|
286
|
-
- **`npm run test:integration`** covers dashboard flow that:
|
|
287
|
-
1. Builds the CLI
|
|
288
|
-
2. Starts `labgate ui` on a temporary localhost port
|
|
289
|
-
3. Launches `labgate claude` and `labgate codex` with a mocked runtime
|
|
290
|
-
4. Verifies sessions appear in `/api/sessions` (dashboard data source)
|
|
291
|
-
5. Stops a session through `POST /api/sessions/stop` and verifies it disappears
|
|
292
|
-
6. Verifies UI port fallback when requested ports are occupied
|
|
293
|
-
7. Verifies `/api/config` accepts valid payloads and rejects invalid payloads without mutating config
|
|
294
|
-
8. Verifies `labgate logs --follow` prints tail output and streams newly appended events
|
|
295
|
-
9. Verifies dashboard activity inference for accessed files and websites from agent transcripts
|
|
296
|
-
10. Verifies `GET/PUT /api/sessions/:id/instructions` with conflict detection for `AGENTS.md` and `CLAUDE.md`
|
|
297
|
-
- **`npm run test:e2e:real`** runs a real runtime smoke test for OAuth/browser opening:
|
|
298
|
-
1. Launches real `labgate claude` (no mocked container runtime)
|
|
299
|
-
2. Waits for OAuth URL flow
|
|
300
|
-
3. Verifies host browser-open hook is triggered
|
|
301
|
-
4. Optional override: `LABGATE_REAL_E2E_IMAGE`
|
|
302
|
-
|
|
303
|
-
## How it works
|
|
304
|
-
|
|
305
|
-
LabGate builds a sandboxed container from your config:
|
|
201
|
+
Feedback posts to `LABGATE_FEEDBACK_URL` when set, or to `https://labgate.dev/api/feedback` by default. If remote submission fails, LabGate saves feedback locally to `~/.labgate/feedback.jsonl`.
|
|
306
202
|
|
|
307
|
-
|
|
308
|
-
2. Mounts your working directory at `/work`
|
|
309
|
-
3. Mounts persistent sandbox HOME at `/home/sandbox` (for npm cache, agent config)
|
|
310
|
-
4. Overlays blocked paths (`.ssh`, `.aws`, etc.) with empty mounts
|
|
311
|
-
5. Applies network isolation and capability restrictions
|
|
312
|
-
6. Installs the agent (if not cached) and runs it interactively
|
|
313
|
-
|
|
314
|
-
On macOS, LabGate syncs your Claude credentials from the system keychain so the agent can authenticate automatically.
|
|
315
|
-
|
|
316
|
-
## Audit logs
|
|
317
|
-
|
|
318
|
-
Session events are logged to `~/.labgate/logs/YYYY-MM-DD.jsonl`:
|
|
203
|
+
## Development
|
|
319
204
|
|
|
320
205
|
```bash
|
|
321
|
-
|
|
206
|
+
npm run setup
|
|
207
|
+
npm run verify:quick
|
|
208
|
+
npm run verify
|
|
209
|
+
npm run test:unit
|
|
210
|
+
npm run test:integration
|
|
211
|
+
npm run test:e2e:real
|
|
212
|
+
npm run dev:claude
|
|
213
|
+
npm run release:check
|
|
322
214
|
```
|
|
323
215
|
|
|
324
|
-
## Roadmap
|
|
325
|
-
|
|
326
|
-
- **M0** CLI + sandbox engine + config + audit (this release)
|
|
327
|
-
- **M1** Mount allowlists, network filtering, project-level config
|
|
328
|
-
- **M2** SLURM proxy (submit/status/cancel from inside sandbox)
|
|
329
|
-
- **M3** Web UI for config + audit viewer
|
|
330
|
-
- **M4** Institutional mode (/etc/labgate/ policies, admin locks)
|
|
331
|
-
|
|
332
216
|
## License
|
|
333
217
|
|
|
334
|
-
|
|
218
|
+
License terms: [labgate.dev](https://labgate.dev)
|
package/dist/cli.js
CHANGED
|
@@ -150,13 +150,7 @@ configCmd
|
|
|
150
150
|
process.exit(1);
|
|
151
151
|
}
|
|
152
152
|
try {
|
|
153
|
-
const
|
|
154
|
-
// Strip comments for JSON parsing
|
|
155
|
-
const stripped = rawText
|
|
156
|
-
.split('\n')
|
|
157
|
-
.filter(line => !line.trimStart().startsWith('//'))
|
|
158
|
-
.join('\n');
|
|
159
|
-
const obj = JSON.parse(stripped);
|
|
153
|
+
const obj = (0, config_js_1.readRawConfigFile)(configPath);
|
|
160
154
|
// Parse the value (numbers, booleans, JSON arrays/objects)
|
|
161
155
|
let parsed;
|
|
162
156
|
if (value === 'true')
|
|
@@ -175,7 +169,7 @@ configCmd
|
|
|
175
169
|
}
|
|
176
170
|
setNestedValue(obj, key, parsed);
|
|
177
171
|
// Re-validate before writing
|
|
178
|
-
const testConfig =
|
|
172
|
+
const testConfig = (0, config_js_1.buildConfigFromRaw)(obj);
|
|
179
173
|
const errors = (0, config_js_1.validateConfig)(testConfig);
|
|
180
174
|
if (errors.length > 0) {
|
|
181
175
|
console.error('Invalid config value:');
|
|
@@ -183,8 +177,7 @@ configCmd
|
|
|
183
177
|
console.error(` - ${e}`);
|
|
184
178
|
process.exit(1);
|
|
185
179
|
}
|
|
186
|
-
(0,
|
|
187
|
-
(0, config_js_1.ensurePrivateFile)(configPath);
|
|
180
|
+
(0, config_js_1.writeRawConfigFile)(obj, configPath);
|
|
188
181
|
console.log(`Set ${key} = ${JSON.stringify(parsed)}`);
|
|
189
182
|
}
|
|
190
183
|
catch (err) {
|
|
@@ -208,16 +201,13 @@ configCmd
|
|
|
208
201
|
process.exit(1);
|
|
209
202
|
}
|
|
210
203
|
try {
|
|
211
|
-
const
|
|
212
|
-
const stripped = stripJsonComments(rawText).trim();
|
|
213
|
-
const obj = stripped ? JSON.parse(stripped) : {};
|
|
204
|
+
const obj = (0, config_js_1.readRawConfigFile)(configPath);
|
|
214
205
|
const deleted = deleteNestedValue(obj, key);
|
|
215
206
|
if (!deleted) {
|
|
216
207
|
console.log(`No user override for ${key} (already using defaults / policy).`);
|
|
217
208
|
return;
|
|
218
209
|
}
|
|
219
|
-
(0,
|
|
220
|
-
(0, config_js_1.ensurePrivateFile)(configPath);
|
|
210
|
+
(0, config_js_1.writeRawConfigFile)(obj, configPath);
|
|
221
211
|
console.log(`Reset ${key} (unset from user config).`);
|
|
222
212
|
}
|
|
223
213
|
catch (err) {
|
|
@@ -716,15 +706,7 @@ datasetCmd
|
|
|
716
706
|
.argument('<name>', 'Dataset name to initialize')
|
|
717
707
|
.action((name) => {
|
|
718
708
|
const configPath = (0, config_js_1.getConfigPath)();
|
|
719
|
-
const
|
|
720
|
-
const stripped = rawText.split('\n').filter(l => !l.trimStart().startsWith('//')).join('\n');
|
|
721
|
-
let obj;
|
|
722
|
-
try {
|
|
723
|
-
obj = JSON.parse(stripped);
|
|
724
|
-
}
|
|
725
|
-
catch {
|
|
726
|
-
obj = {};
|
|
727
|
-
}
|
|
709
|
+
const obj = (0, config_js_1.readRawConfigFile)(configPath);
|
|
728
710
|
const datasets = (obj.datasets || []);
|
|
729
711
|
const idx = datasets.findIndex((d) => d?.name && String(d.name).toLowerCase() === name.toLowerCase());
|
|
730
712
|
if (idx < 0) {
|
|
@@ -791,8 +773,7 @@ datasetCmd
|
|
|
791
773
|
};
|
|
792
774
|
ds.stats = stats;
|
|
793
775
|
obj.datasets = datasets;
|
|
794
|
-
(0,
|
|
795
|
-
(0, config_js_1.ensurePrivateFile)(configPath);
|
|
776
|
+
(0, config_js_1.writeRawConfigFile)(obj, configPath);
|
|
796
777
|
console.log(`\nDataset "${name}" initialized:`);
|
|
797
778
|
console.log(` Files: ${stats.file_count}`);
|
|
798
779
|
console.log(` Size: ${stats.total_size_formatted}`);
|
|
@@ -1426,7 +1407,7 @@ policyCmd
|
|
|
1426
1407
|
process.exit(1);
|
|
1427
1408
|
}
|
|
1428
1409
|
const rawText = (0, fs_1.readFileSync)(policyPath, 'utf-8');
|
|
1429
|
-
const stripped = stripJsonComments(rawText);
|
|
1410
|
+
const stripped = (0, config_js_1.stripJsonComments)(rawText);
|
|
1430
1411
|
const parsed = JSON.parse(stripped);
|
|
1431
1412
|
const errors = validatePolicy(parsed);
|
|
1432
1413
|
if (errors.length > 0) {
|
|
@@ -1565,12 +1546,6 @@ function deleteNestedValue(obj, key) {
|
|
|
1565
1546
|
}
|
|
1566
1547
|
return true;
|
|
1567
1548
|
}
|
|
1568
|
-
function stripJsonComments(rawText) {
|
|
1569
|
-
return rawText
|
|
1570
|
-
.split('\n')
|
|
1571
|
-
.filter(line => !line.trimStart().startsWith('//'))
|
|
1572
|
-
.join('\n');
|
|
1573
|
-
}
|
|
1574
1549
|
function collectStringOption(value, previous) {
|
|
1575
1550
|
previous.push(value);
|
|
1576
1551
|
return previous;
|
|
@@ -1874,6 +1849,7 @@ async function runAgent(agent, workdir, opts) {
|
|
|
1874
1849
|
['Blacklist', config.commands.blacklist.join(', ')],
|
|
1875
1850
|
['Mounts', `${config.filesystem.extra_paths.length} extra`],
|
|
1876
1851
|
['Home', `${config_js_1.LABGATE_DIR}/ai-home`],
|
|
1852
|
+
['SIF cache', (0, config_js_1.getImagesDir)()],
|
|
1877
1853
|
];
|
|
1878
1854
|
if (effective.enterprise) {
|
|
1879
1855
|
rows.push(['Enterprise', `${effective.institution ?? 'enabled'}`]);
|