labgate 0.1.0 → 0.3.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/README.md CHANGED
@@ -8,6 +8,8 @@ Policy-controlled sandboxes for LLM coding agents on HPC systems.
8
8
  npm i -g labgate
9
9
  ```
10
10
 
11
+ Note: LabGate uses `node-pty` only for the optional sticky footer. On minimal Linux installs, that dependency may fail to build without a compiler toolchain. If it fails, the install still works and LabGate falls back to non-sticky output.
12
+
11
13
  LabGate auto-detects your container runtime (Apptainer, Singularity, Podman, or Docker) and offers to install one if none is found.
12
14
 
13
15
  ## Quick start
@@ -60,6 +62,7 @@ labgate init --force
60
62
  ```bash
61
63
  labgate claude [workdir] # launch Claude Code
62
64
  labgate codex [workdir] # launch Codex
65
+ labgate feedback # submit feedback (interactive or piped)
63
66
  labgate status # list running sessions
64
67
  labgate stop <id> # stop a session
65
68
  labgate init [--force] # create/reset config
@@ -73,6 +76,20 @@ labgate claude --image my-image:tag # use a different container image
73
76
  labgate claude --no-footer # disable the status footer line
74
77
  ```
75
78
 
79
+ ## Feedback
80
+
81
+ Submit feedback from the CLI:
82
+
83
+ ```bash
84
+ labgate feedback
85
+ echo "This was great" | labgate feedback
86
+ labgate feedback "Short feedback message"
87
+ ```
88
+
89
+ If `LABGATE_FEEDBACK_URL` is set, LabGate will `POST` feedback JSON to that URL.
90
+ If `LABGATE_FEEDBACK_TOKEN` is set, it will be sent as a Bearer token.
91
+ If no URL is configured or the request fails, feedback is saved locally at `~/.labgate/feedback.jsonl`.
92
+
76
93
  ## How it works
77
94
 
78
95
  LabGate builds a sandboxed container from your config:
package/dist/cli.js CHANGED
@@ -7,12 +7,16 @@ const config_js_1 = require("./lib/config.js");
7
7
  const init_js_1 = require("./lib/init.js");
8
8
  const container_js_1 = require("./lib/container.js");
9
9
  const runtime_js_1 = require("./lib/runtime.js");
10
+ const feedback_js_1 = require("./lib/feedback.js");
10
11
  const AGENTS = ['claude', 'codex'];
12
+ // Read version from package.json so it stays in sync
13
+ const pkgPath = (0, path_1.resolve)(__dirname, '..', 'package.json');
14
+ const PKG_VERSION = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf-8')).version;
11
15
  const program = new commander_1.Command();
12
16
  program
13
17
  .name('labgate')
14
18
  .description('Policy-controlled sandboxes for LLM coding agents')
15
- .version('0.1.0');
19
+ .version(PKG_VERSION);
16
20
  // ── labgate init ──────────────────────────────────────────
17
21
  program
18
22
  .command('init')
@@ -38,11 +42,172 @@ for (const agent of AGENTS) {
38
42
  .option('--dry-run', 'Print the container command without running it')
39
43
  .option('--image <uri>', 'Override container image')
40
44
  .option('--no-footer', 'Disable status footer line')
41
- .option('--no-statusline', 'Disable Claude Code status line')
45
+ .option('--verbose', 'Show detailed debug output')
42
46
  .action(async (workdir, opts) => {
43
47
  await runAgent(agent, workdir, opts);
44
48
  });
45
49
  }
50
+ // ── labgate feedback ──────────────────────────────────────
51
+ program
52
+ .command('feedback')
53
+ .description('Submit feedback to LabGate')
54
+ .argument('[message...]', 'Feedback message (omit to enter interactively)')
55
+ .action(async (messageParts) => {
56
+ try {
57
+ const message = await (0, feedback_js_1.collectFeedback)(messageParts ?? []);
58
+ if (!message) {
59
+ console.error('Error: feedback cannot be empty');
60
+ process.exit(1);
61
+ }
62
+ const result = await (0, feedback_js_1.submitFeedback)(message);
63
+ console.log(result.detail);
64
+ }
65
+ catch (err) {
66
+ console.error(`Error: ${err?.message ?? String(err)}`);
67
+ process.exit(1);
68
+ }
69
+ });
70
+ // ── labgate config [key] [value] ──────────────────────────
71
+ const configCmd = program
72
+ .command('config')
73
+ .description('View or modify config');
74
+ configCmd
75
+ .command('get')
76
+ .description('Get a config value (dot-notation key)')
77
+ .argument('<key>', 'Config key (e.g. network.mode, session_timeout_hours)')
78
+ .action((key) => {
79
+ const config = (0, config_js_1.loadConfig)();
80
+ const value = getNestedValue(config, key);
81
+ if (value === undefined) {
82
+ console.error(`Unknown config key: ${key}`);
83
+ process.exit(1);
84
+ }
85
+ console.log(typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value));
86
+ });
87
+ configCmd
88
+ .command('set')
89
+ .description('Set a config value (dot-notation key)')
90
+ .argument('<key>', 'Config key (e.g. network.mode, session_timeout_hours)')
91
+ .argument('<value>', 'Value to set')
92
+ .action((key, value) => {
93
+ const configPath = (0, config_js_1.getConfigPath)();
94
+ if (!(0, fs_1.existsSync)(configPath)) {
95
+ console.error('No config file found. Run "labgate init" first.');
96
+ process.exit(1);
97
+ }
98
+ try {
99
+ const rawText = (0, fs_1.readFileSync)(configPath, 'utf-8');
100
+ // Strip comments for JSON parsing
101
+ const stripped = rawText
102
+ .split('\n')
103
+ .filter(line => !line.trimStart().startsWith('//'))
104
+ .join('\n');
105
+ const obj = JSON.parse(stripped);
106
+ // Parse the value (numbers, booleans, JSON arrays/objects)
107
+ let parsed;
108
+ if (value === 'true')
109
+ parsed = true;
110
+ else if (value === 'false')
111
+ parsed = false;
112
+ else if (/^\d+(\.\d+)?$/.test(value))
113
+ parsed = Number(value);
114
+ else {
115
+ try {
116
+ parsed = JSON.parse(value);
117
+ }
118
+ catch {
119
+ parsed = value;
120
+ }
121
+ }
122
+ setNestedValue(obj, key, parsed);
123
+ // Re-validate before writing
124
+ const testConfig = { ...(0, config_js_1.loadConfig)(), ...obj };
125
+ const errors = (0, config_js_1.validateConfig)(testConfig);
126
+ if (errors.length > 0) {
127
+ console.error('Invalid config value:');
128
+ for (const e of errors)
129
+ console.error(` - ${e}`);
130
+ process.exit(1);
131
+ }
132
+ (0, fs_1.writeFileSync)(configPath, JSON.stringify(obj, null, 2) + '\n', 'utf-8');
133
+ console.log(`Set ${key} = ${JSON.stringify(parsed)}`);
134
+ }
135
+ catch (err) {
136
+ console.error(`Error: ${err.message}`);
137
+ process.exit(1);
138
+ }
139
+ });
140
+ configCmd
141
+ .command('show')
142
+ .description('Show the full resolved config')
143
+ .action(() => {
144
+ const config = (0, config_js_1.loadConfig)();
145
+ console.log(JSON.stringify(config, null, 2));
146
+ });
147
+ configCmd
148
+ .command('path')
149
+ .description('Print the config file path')
150
+ .action(() => {
151
+ console.log((0, config_js_1.getConfigPath)());
152
+ });
153
+ // ── labgate logs ──────────────────────────────────────────
154
+ program
155
+ .command('logs')
156
+ .description('View audit logs')
157
+ .option('-n, --lines <count>', 'Number of recent lines to show', '20')
158
+ .option('--follow', 'Follow the log file for new entries')
159
+ .action(async (opts) => {
160
+ const config = (0, config_js_1.loadConfig)();
161
+ if (!config.audit.enabled) {
162
+ console.log('Audit logging is disabled. Enable it in config: audit.enabled = true');
163
+ return;
164
+ }
165
+ const logDir = (0, config_js_1.getLogDir)(config);
166
+ if (!(0, fs_1.existsSync)(logDir)) {
167
+ console.log('No audit logs found yet.');
168
+ return;
169
+ }
170
+ // Find the most recent log file
171
+ const files = (0, fs_1.readdirSync)(logDir)
172
+ .filter((f) => f.endsWith('.jsonl'))
173
+ .sort()
174
+ .reverse();
175
+ if (files.length === 0) {
176
+ console.log('No audit logs found yet.');
177
+ return;
178
+ }
179
+ const logFile = (0, path_1.resolve)(logDir, files[0]);
180
+ console.log(`Log file: ${logFile}\n`);
181
+ const content = (0, fs_1.readFileSync)(logFile, 'utf-8').trim();
182
+ if (!content) {
183
+ console.log('Log file is empty.');
184
+ return;
185
+ }
186
+ const allLines = content.split('\n');
187
+ const count = parseInt(opts.lines, 10) || 20;
188
+ const lines = allLines.slice(-count);
189
+ for (const line of lines) {
190
+ try {
191
+ const event = JSON.parse(line);
192
+ const time = event.timestamp?.slice(11, 19) ?? '??:??:??';
193
+ const sess = event.session?.slice(0, 8) ?? '????????';
194
+ const ev = event.event ?? 'unknown';
195
+ const extra = [];
196
+ if (event.agent)
197
+ extra.push(`agent=${event.agent}`);
198
+ if (event.exit_code !== undefined)
199
+ extra.push(`exit=${event.exit_code}`);
200
+ if (event.timeout_hours)
201
+ extra.push(`timeout=${event.timeout_hours}h`);
202
+ if (event.network_mode)
203
+ extra.push(`net=${event.network_mode}`);
204
+ console.log(`${time} [${sess}] ${ev}${extra.length ? ' ' + extra.join(' ') : ''}`);
205
+ }
206
+ catch {
207
+ console.log(line);
208
+ }
209
+ }
210
+ });
46
211
  // ── labgate status ────────────────────────────────────────
47
212
  program
48
213
  .command('status')
@@ -60,6 +225,28 @@ program
60
225
  const { stopSession } = await import('./lib/container.js');
61
226
  await stopSession(id);
62
227
  });
228
+ // ── Dot-notation helpers ──────────────────────────────────
229
+ function getNestedValue(obj, key) {
230
+ const parts = key.split('.');
231
+ let current = obj;
232
+ for (const part of parts) {
233
+ if (current == null || typeof current !== 'object')
234
+ return undefined;
235
+ current = current[part];
236
+ }
237
+ return current;
238
+ }
239
+ function setNestedValue(obj, key, value) {
240
+ const parts = key.split('.');
241
+ let current = obj;
242
+ for (let i = 0; i < parts.length - 1; i++) {
243
+ if (current[parts[i]] == null || typeof current[parts[i]] !== 'object') {
244
+ current[parts[i]] = {};
245
+ }
246
+ current = current[parts[i]];
247
+ }
248
+ current[parts[parts.length - 1]] = value;
249
+ }
63
250
  async function runAgent(agent, workdir, opts) {
64
251
  // Resolve workdir
65
252
  const resolved = (0, path_1.resolve)(workdir);
@@ -67,13 +254,25 @@ async function runAgent(agent, workdir, opts) {
67
254
  console.error(`Error: directory does not exist: ${resolved}`);
68
255
  process.exit(1);
69
256
  }
257
+ const verbose = opts.verbose ?? false;
70
258
  // Load config (creates default if missing) — need config.runtime for runtime check
71
- const configPath = (0, config_js_1.getConfigPath)();
72
- if (!(0, fs_1.existsSync)(configPath)) {
259
+ const cfgPath = (0, config_js_1.getConfigPath)();
260
+ if (!(0, fs_1.existsSync)(cfgPath)) {
73
261
  console.log('[labgate] No config found. Running "labgate init" first...\n');
74
262
  await (0, init_js_1.initConfig)({ force: false });
75
263
  }
76
264
  const config = (0, config_js_1.loadConfig)();
265
+ if (verbose) {
266
+ console.log(`[labgate] Config: ${cfgPath}`);
267
+ console.log(`[labgate] Runtime preference: ${config.runtime}`);
268
+ console.log(`[labgate] Image: ${opts.image ?? config.image}`);
269
+ console.log(`[labgate] Network mode: ${config.network.mode}`);
270
+ console.log(`[labgate] Blocked patterns: ${config.filesystem.blocked_patterns.length}`);
271
+ console.log(`[labgate] Command blacklist: ${config.commands.blacklist.join(', ')}`);
272
+ console.log(`[labgate] Extra mounts: ${config.filesystem.extra_paths.length}`);
273
+ console.log(`[labgate] Sandbox home: ${config_js_1.LABGATE_DIR}/ai-home`);
274
+ console.log('');
275
+ }
77
276
  // Check container runtime is available — offer to install if missing
78
277
  // (skip for dry-run so users can preview without runtime)
79
278
  if (!opts.dryRun) {
@@ -81,6 +280,9 @@ async function runAgent(agent, workdir, opts) {
81
280
  if (!runtime.ok) {
82
281
  process.exit(1);
83
282
  }
283
+ if (verbose) {
284
+ console.log(`[labgate] Detected runtime: ${runtime.runtime} (${runtime.version})`);
285
+ }
84
286
  }
85
287
  // Start the session
86
288
  await (0, container_js_1.startSession)({
@@ -90,7 +292,6 @@ async function runAgent(agent, workdir, opts) {
90
292
  dryRun: opts.dryRun ?? false,
91
293
  imageOverride: opts.image,
92
294
  footerMode: opts.footer === false ? 'off' : 'once',
93
- statusline: opts.statusline ?? true,
94
295
  });
95
296
  }
96
297
  program.parse();
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -44,4 +44,5 @@ export declare function getSandboxHome(): string;
44
44
  export declare function getImagesDir(): string;
45
45
  export declare function getEmptyDir(): string;
46
46
  export declare function getLogDir(config: LabgateConfig): string;
47
+ export declare function validateConfig(config: LabgateConfig): string[];
47
48
  export declare function loadConfig(): LabgateConfig;
@@ -6,6 +6,7 @@ exports.getSandboxHome = getSandboxHome;
6
6
  exports.getImagesDir = getImagesDir;
7
7
  exports.getEmptyDir = getEmptyDir;
8
8
  exports.getLogDir = getLogDir;
9
+ exports.validateConfig = validateConfig;
9
10
  exports.loadConfig = loadConfig;
10
11
  const fs_1 = require("fs");
11
12
  const path_1 = require("path");
@@ -82,33 +83,73 @@ function getEmptyDir() {
82
83
  function getLogDir(config) {
83
84
  return config.audit.log_dir.replace(/^~/, (0, os_1.homedir)());
84
85
  }
86
+ // ── Deep clone helper ──────────────────────────────────────
87
+ function cloneConfig(config) {
88
+ return JSON.parse(JSON.stringify(config));
89
+ }
90
+ // ── Validation ────────────────────────────────────────────
91
+ const VALID_RUNTIMES = ['auto', 'apptainer', 'singularity', 'podman', 'docker'];
92
+ const VALID_NETWORK_MODES = ['none', 'filtered', 'host'];
93
+ const VALID_MOUNT_MODES = ['rw', 'ro'];
94
+ function validateConfig(config) {
95
+ const errors = [];
96
+ if (!VALID_RUNTIMES.includes(config.runtime)) {
97
+ errors.push(`Invalid runtime: "${config.runtime}". Must be one of: ${VALID_RUNTIMES.join(', ')}`);
98
+ }
99
+ if (!config.image || typeof config.image !== 'string') {
100
+ errors.push('image must be a non-empty string');
101
+ }
102
+ if (typeof config.session_timeout_hours !== 'number' || config.session_timeout_hours < 0) {
103
+ errors.push('session_timeout_hours must be a non-negative number');
104
+ }
105
+ if (!VALID_NETWORK_MODES.includes(config.network.mode)) {
106
+ errors.push(`Invalid network.mode: "${config.network.mode}". Must be one of: ${VALID_NETWORK_MODES.join(', ')}`);
107
+ }
108
+ if (!Array.isArray(config.filesystem.blocked_patterns)) {
109
+ errors.push('filesystem.blocked_patterns must be an array');
110
+ }
111
+ if (!Array.isArray(config.commands.blacklist)) {
112
+ errors.push('commands.blacklist must be an array');
113
+ }
114
+ for (const mount of config.filesystem.extra_paths) {
115
+ if (!mount.path || typeof mount.path !== 'string') {
116
+ errors.push('Each extra_paths entry must have a non-empty "path" string');
117
+ }
118
+ if (!VALID_MOUNT_MODES.includes(mount.mode)) {
119
+ errors.push(`Invalid mount mode "${mount.mode}" for path "${mount.path}". Must be "rw" or "ro"`);
120
+ }
121
+ }
122
+ return errors;
123
+ }
85
124
  // ── Loader ────────────────────────────────────────────────
86
125
  function loadConfig() {
87
126
  const configPath = getConfigPath();
88
127
  if (!(0, fs_1.existsSync)(configPath)) {
89
- return { ...exports.DEFAULT_CONFIG };
128
+ return cloneConfig(exports.DEFAULT_CONFIG);
90
129
  }
91
130
  try {
92
131
  const rawText = (0, fs_1.readFileSync)(configPath, 'utf-8');
93
- // Strip // comments (our config uses them for documentation)
94
- const stripped = rawText.replace(/^\s*\/\/.*$/gm, '');
132
+ // Strip full-line // comments (our config uses them for documentation)
133
+ const stripped = rawText
134
+ .split('\n')
135
+ .filter(line => !line.trimStart().startsWith('//'))
136
+ .join('\n');
95
137
  const raw = JSON.parse(stripped);
96
- // Merge with defaults so missing fields get filled in.
97
- // Shallow merge per section — good enough for M0.
98
- return {
138
+ // Merge with defaults so missing fields get filled in
139
+ const config = {
99
140
  runtime: raw.runtime ?? exports.DEFAULT_CONFIG.runtime,
100
141
  image: raw.image ?? exports.DEFAULT_CONFIG.image,
101
142
  session_timeout_hours: raw.session_timeout_hours ?? exports.DEFAULT_CONFIG.session_timeout_hours,
102
143
  filesystem: {
103
- extra_paths: raw.filesystem?.extra_paths ?? exports.DEFAULT_CONFIG.filesystem.extra_paths,
104
- blocked_patterns: raw.filesystem?.blocked_patterns ?? exports.DEFAULT_CONFIG.filesystem.blocked_patterns,
144
+ extra_paths: raw.filesystem?.extra_paths ?? [...exports.DEFAULT_CONFIG.filesystem.extra_paths],
145
+ blocked_patterns: raw.filesystem?.blocked_patterns ?? [...exports.DEFAULT_CONFIG.filesystem.blocked_patterns],
105
146
  },
106
147
  commands: {
107
- blacklist: raw.commands?.blacklist ?? exports.DEFAULT_CONFIG.commands.blacklist,
148
+ blacklist: raw.commands?.blacklist ?? [...exports.DEFAULT_CONFIG.commands.blacklist],
108
149
  },
109
150
  network: {
110
151
  mode: raw.network?.mode ?? exports.DEFAULT_CONFIG.network.mode,
111
- allowed_domains: raw.network?.allowed_domains ?? exports.DEFAULT_CONFIG.network.allowed_domains,
152
+ allowed_domains: raw.network?.allowed_domains ?? [...exports.DEFAULT_CONFIG.network.allowed_domains],
112
153
  },
113
154
  slurm: {
114
155
  enabled: raw.slurm?.enabled ?? exports.DEFAULT_CONFIG.slurm.enabled,
@@ -118,11 +159,20 @@ function loadConfig() {
118
159
  log_dir: raw.audit?.log_dir ?? exports.DEFAULT_CONFIG.audit.log_dir,
119
160
  },
120
161
  };
162
+ const errors = validateConfig(config);
163
+ if (errors.length > 0) {
164
+ console.error(`Warning: invalid config in ${configPath}:`);
165
+ for (const e of errors)
166
+ console.error(` - ${e}`);
167
+ console.error('Using default config.');
168
+ return cloneConfig(exports.DEFAULT_CONFIG);
169
+ }
170
+ return config;
121
171
  }
122
172
  catch (err) {
123
173
  console.error(`Warning: could not parse ${configPath}: ${err.message}`);
124
174
  console.error('Using default config.');
125
- return { ...exports.DEFAULT_CONFIG };
175
+ return cloneConfig(exports.DEFAULT_CONFIG);
126
176
  }
127
177
  }
128
178
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
@@ -6,14 +6,13 @@ export interface SessionOpts {
6
6
  dryRun: boolean;
7
7
  imageOverride?: string;
8
8
  footerMode?: 'off' | 'once' | 'sticky';
9
- statusline?: boolean;
10
9
  }
11
10
  /**
12
11
  * Convert a container image URI to a local SIF filename.
13
12
  * e.g. "docker.io/library/ubuntu:22.04" → "docker.io_library_ubuntu_22.04.sif"
14
13
  */
15
14
  export declare function imageToSifName(image: string): string;
16
- export declare function buildEntrypoint(agent: string, statuslineEnabled?: boolean): string;
15
+ export declare function buildEntrypoint(agent: string): string;
17
16
  export declare function startSession(session: SessionOpts): Promise<void>;
18
17
  export declare function listSessions(): Promise<void>;
19
18
  export declare function stopSession(id: string): Promise<void>;