labgate 0.1.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 +107 -0
- package/bin/labgate.js +3 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +97 -0
- package/dist/cli.js.map +1 -0
- package/dist/lib/audit.d.ts +8 -0
- package/dist/lib/audit.js +25 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/config.d.ts +47 -0
- package/dist/lib/config.js +128 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/container.d.ts +19 -0
- package/dist/lib/container.js +794 -0
- package/dist/lib/container.js.map +1 -0
- package/dist/lib/init.d.ts +3 -0
- package/dist/lib/init.js +81 -0
- package/dist/lib/init.js.map +1 -0
- package/dist/lib/runtime.d.ts +23 -0
- package/dist/lib/runtime.js +189 -0
- package/dist/lib/runtime.js.map +1 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# LabGate
|
|
2
|
+
|
|
3
|
+
Policy-controlled sandboxes for LLM coding agents on HPC systems.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i -g labgate
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
LabGate auto-detects your container runtime (Apptainer, Singularity, Podman, or Docker) and offers to install one if none is found.
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
labgate init # create ~/.labgate/config.json
|
|
17
|
+
labgate claude # launch Claude Code in current dir
|
|
18
|
+
labgate codex /projects/my-analysis # launch Codex in a specific dir
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## What it does
|
|
22
|
+
|
|
23
|
+
LabGate runs your AI coding agent inside a sandboxed container with:
|
|
24
|
+
|
|
25
|
+
- **Scoped filesystem** — only your working directory and configured paths are visible
|
|
26
|
+
- **Credential blocking** — `.ssh`, `.aws`, `.env`, `.gnupg`, and other sensitive paths are hidden by default
|
|
27
|
+
- **Network isolation** — no outbound network by default (configurable)
|
|
28
|
+
- **Command blocking** — `ssh`, `curl`, `wget`, and other commands are blocked by default
|
|
29
|
+
- **Audit logging** — session start/stop and mount configuration logged to `~/.labgate/logs/`
|
|
30
|
+
- **HPC ready** — first-class Apptainer/Singularity support for shared clusters
|
|
31
|
+
|
|
32
|
+
## Configuration
|
|
33
|
+
|
|
34
|
+
Edit `~/.labgate/config.json` to customize:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
$EDITOR ~/.labgate/config.json
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or start fresh:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
labgate init --force
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Key settings
|
|
47
|
+
|
|
48
|
+
| Setting | Default | What it does |
|
|
49
|
+
|---------|---------|-------------|
|
|
50
|
+
| `runtime` | `auto` | `auto`, `apptainer`, `singularity`, `podman`, or `docker` |
|
|
51
|
+
| `image` | `node:20-slim` | Container image |
|
|
52
|
+
| `session_timeout_hours` | `8` | Max session length |
|
|
53
|
+
| `filesystem.blocked_patterns` | `.ssh, .aws, .env, ...` | Hidden from sandbox |
|
|
54
|
+
| `filesystem.extra_paths` | `[]` | Additional mounts |
|
|
55
|
+
| `network.mode` | `none` | `none`, `filtered`, or `host` |
|
|
56
|
+
| `commands.blacklist` | `ssh, curl, wget, ...` | Blocked commands |
|
|
57
|
+
|
|
58
|
+
## Commands
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
labgate claude [workdir] # launch Claude Code
|
|
62
|
+
labgate codex [workdir] # launch Codex
|
|
63
|
+
labgate status # list running sessions
|
|
64
|
+
labgate stop <id> # stop a session
|
|
65
|
+
labgate init [--force] # create/reset config
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Options
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
labgate claude --dry-run # print the sandbox command without running
|
|
72
|
+
labgate claude --image my-image:tag # use a different container image
|
|
73
|
+
labgate claude --no-footer # disable the status footer line
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## How it works
|
|
77
|
+
|
|
78
|
+
LabGate builds a sandboxed container from your config:
|
|
79
|
+
|
|
80
|
+
1. Detects the best available runtime (apptainer > singularity > podman > docker)
|
|
81
|
+
2. Mounts your working directory at `/work`
|
|
82
|
+
3. Mounts persistent sandbox HOME at `/home/sandbox` (for npm cache, agent config)
|
|
83
|
+
4. Overlays blocked paths (`.ssh`, `.aws`, etc.) with empty mounts
|
|
84
|
+
5. Applies network isolation and capability restrictions
|
|
85
|
+
6. Installs the agent (if not cached) and runs it interactively
|
|
86
|
+
|
|
87
|
+
On macOS, LabGate syncs your Claude credentials from the system keychain so the agent can authenticate automatically.
|
|
88
|
+
|
|
89
|
+
## Audit logs
|
|
90
|
+
|
|
91
|
+
Session events are logged to `~/.labgate/logs/YYYY-MM-DD.jsonl`:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
cat ~/.labgate/logs/2025-02-05.jsonl | jq .
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Roadmap
|
|
98
|
+
|
|
99
|
+
- **M0** CLI + sandbox engine + config + audit (this release)
|
|
100
|
+
- **M1** Mount allowlists, network filtering, project-level config
|
|
101
|
+
- **M2** SLURM proxy (submit/status/cancel from inside sandbox)
|
|
102
|
+
- **M3** Web UI for config + audit viewer
|
|
103
|
+
- **M4** Institutional mode (/etc/labgate/ policies, admin locks)
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT
|
package/bin/labgate.js
ADDED
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const commander_1 = require("commander");
|
|
4
|
+
const path_1 = require("path");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const config_js_1 = require("./lib/config.js");
|
|
7
|
+
const init_js_1 = require("./lib/init.js");
|
|
8
|
+
const container_js_1 = require("./lib/container.js");
|
|
9
|
+
const runtime_js_1 = require("./lib/runtime.js");
|
|
10
|
+
const AGENTS = ['claude', 'codex'];
|
|
11
|
+
const program = new commander_1.Command();
|
|
12
|
+
program
|
|
13
|
+
.name('labgate')
|
|
14
|
+
.description('Policy-controlled sandboxes for LLM coding agents')
|
|
15
|
+
.version('0.1.0');
|
|
16
|
+
// ── labgate init ──────────────────────────────────────────
|
|
17
|
+
program
|
|
18
|
+
.command('init')
|
|
19
|
+
.description('Create default config at ~/.labgate/config.json')
|
|
20
|
+
.option('--force', 'Overwrite existing config')
|
|
21
|
+
.action(async (opts) => {
|
|
22
|
+
try {
|
|
23
|
+
await (0, init_js_1.initConfig)({ force: opts.force });
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
console.error(`Error: ${err.message}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
// ── labgate <agent> [workdir] ─────────────────────────────
|
|
31
|
+
// Register each agent as a subcommand so commander doesn't
|
|
32
|
+
// choke on unknown positional args. The handler is the same.
|
|
33
|
+
for (const agent of AGENTS) {
|
|
34
|
+
program
|
|
35
|
+
.command(agent)
|
|
36
|
+
.description(`Launch ${agent} in a sandboxed container`)
|
|
37
|
+
.argument('[workdir]', 'Working directory to mount', process.cwd())
|
|
38
|
+
.option('--dry-run', 'Print the container command without running it')
|
|
39
|
+
.option('--image <uri>', 'Override container image')
|
|
40
|
+
.option('--no-footer', 'Disable status footer line')
|
|
41
|
+
.option('--no-statusline', 'Disable Claude Code status line')
|
|
42
|
+
.action(async (workdir, opts) => {
|
|
43
|
+
await runAgent(agent, workdir, opts);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
// ── labgate status ────────────────────────────────────────
|
|
47
|
+
program
|
|
48
|
+
.command('status')
|
|
49
|
+
.description('List running labgate sessions')
|
|
50
|
+
.action(async () => {
|
|
51
|
+
const { listSessions } = await import('./lib/container.js');
|
|
52
|
+
await listSessions();
|
|
53
|
+
});
|
|
54
|
+
// ── labgate stop <id> ─────────────────────────────────────
|
|
55
|
+
program
|
|
56
|
+
.command('stop')
|
|
57
|
+
.description('Stop a running labgate session')
|
|
58
|
+
.argument('<id>', 'Session ID or container name')
|
|
59
|
+
.action(async (id) => {
|
|
60
|
+
const { stopSession } = await import('./lib/container.js');
|
|
61
|
+
await stopSession(id);
|
|
62
|
+
});
|
|
63
|
+
async function runAgent(agent, workdir, opts) {
|
|
64
|
+
// Resolve workdir
|
|
65
|
+
const resolved = (0, path_1.resolve)(workdir);
|
|
66
|
+
if (!(0, fs_1.existsSync)(resolved)) {
|
|
67
|
+
console.error(`Error: directory does not exist: ${resolved}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
// 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)) {
|
|
73
|
+
console.log('[labgate] No config found. Running "labgate init" first...\n');
|
|
74
|
+
await (0, init_js_1.initConfig)({ force: false });
|
|
75
|
+
}
|
|
76
|
+
const config = (0, config_js_1.loadConfig)();
|
|
77
|
+
// Check container runtime is available — offer to install if missing
|
|
78
|
+
// (skip for dry-run so users can preview without runtime)
|
|
79
|
+
if (!opts.dryRun) {
|
|
80
|
+
const runtime = await (0, runtime_js_1.ensureRuntime)(config.runtime);
|
|
81
|
+
if (!runtime.ok) {
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Start the session
|
|
86
|
+
await (0, container_js_1.startSession)({
|
|
87
|
+
agent,
|
|
88
|
+
workdir: resolved,
|
|
89
|
+
config,
|
|
90
|
+
dryRun: opts.dryRun ?? false,
|
|
91
|
+
imageOverride: opts.image,
|
|
92
|
+
footerMode: opts.footer === false ? 'off' : 'once',
|
|
93
|
+
statusline: opts.statusline ?? true,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
program.parse();
|
|
97
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.writeAuditEvent = writeAuditEvent;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const config_js_1 = require("./config.js");
|
|
7
|
+
function getLogFile(config) {
|
|
8
|
+
const dir = (0, config_js_1.getLogDir)(config);
|
|
9
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
10
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
11
|
+
return (0, path_1.join)(dir, `${date}.jsonl`);
|
|
12
|
+
}
|
|
13
|
+
function writeAuditEvent(config, event) {
|
|
14
|
+
if (!config.audit.enabled)
|
|
15
|
+
return;
|
|
16
|
+
try {
|
|
17
|
+
const file = getLogFile(config);
|
|
18
|
+
(0, fs_1.appendFileSync)(file, JSON.stringify(event) + '\n', 'utf-8');
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
// Don't crash if audit logging fails — just warn
|
|
22
|
+
console.error(`[labgate] Warning: audit log write failed: ${err.message}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/lib/audit.ts"],"names":[],"mappings":";;AAmBA,0CAUC;AA7BD,2BAA+C;AAC/C,+BAA4B;AAE5B,2CAAuD;AASvD,SAAS,UAAU,CAAC,MAAqB;IACvC,MAAM,GAAG,GAAG,IAAA,qBAAS,EAAC,MAAM,CAAC,CAAC;IAC9B,IAAA,cAAS,EAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACnD,OAAO,IAAA,WAAI,EAAC,GAAG,EAAE,GAAG,IAAI,QAAQ,CAAC,CAAC;AACpC,CAAC;AAED,SAAgB,eAAe,CAAC,MAAqB,EAAE,KAAiB;IACtE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO;QAAE,OAAO;IAElC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QAChC,IAAA,mBAAc,EAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,iDAAiD;QACjD,OAAO,CAAC,KAAK,CAAC,8CAA8C,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface MountPath {
|
|
2
|
+
path: string;
|
|
3
|
+
mode: 'rw' | 'ro';
|
|
4
|
+
}
|
|
5
|
+
export type RuntimePreference = 'auto' | 'apptainer' | 'singularity' | 'podman' | 'docker';
|
|
6
|
+
export interface LabgateConfig {
|
|
7
|
+
/** Container runtime preference: auto detects best available */
|
|
8
|
+
runtime: RuntimePreference;
|
|
9
|
+
/** Container image URI */
|
|
10
|
+
image: string;
|
|
11
|
+
/** Session timeout in hours */
|
|
12
|
+
session_timeout_hours: number;
|
|
13
|
+
/** Paths to mount into the sandbox */
|
|
14
|
+
filesystem: {
|
|
15
|
+
/** Additional paths to mount (workdir is always mounted) */
|
|
16
|
+
extra_paths: MountPath[];
|
|
17
|
+
/** Glob patterns to block via empty overlays */
|
|
18
|
+
blocked_patterns: string[];
|
|
19
|
+
};
|
|
20
|
+
/** Commands blocked inside the sandbox */
|
|
21
|
+
commands: {
|
|
22
|
+
blacklist: string[];
|
|
23
|
+
};
|
|
24
|
+
/** Network mode */
|
|
25
|
+
network: {
|
|
26
|
+
mode: 'none' | 'filtered' | 'host';
|
|
27
|
+
allowed_domains: string[];
|
|
28
|
+
};
|
|
29
|
+
/** SLURM proxy settings (M2, ignored for now) */
|
|
30
|
+
slurm: {
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
};
|
|
33
|
+
/** Audit log settings */
|
|
34
|
+
audit: {
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
log_dir: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export declare const DEFAULT_CONFIG: LabgateConfig;
|
|
40
|
+
export declare const LABGATE_DIR: string;
|
|
41
|
+
export declare const CONFIG_FILE = "config.json";
|
|
42
|
+
export declare function getConfigPath(): string;
|
|
43
|
+
export declare function getSandboxHome(): string;
|
|
44
|
+
export declare function getImagesDir(): string;
|
|
45
|
+
export declare function getEmptyDir(): string;
|
|
46
|
+
export declare function getLogDir(config: LabgateConfig): string;
|
|
47
|
+
export declare function loadConfig(): LabgateConfig;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CONFIG_FILE = exports.LABGATE_DIR = exports.DEFAULT_CONFIG = void 0;
|
|
4
|
+
exports.getConfigPath = getConfigPath;
|
|
5
|
+
exports.getSandboxHome = getSandboxHome;
|
|
6
|
+
exports.getImagesDir = getImagesDir;
|
|
7
|
+
exports.getEmptyDir = getEmptyDir;
|
|
8
|
+
exports.getLogDir = getLogDir;
|
|
9
|
+
exports.loadConfig = loadConfig;
|
|
10
|
+
const fs_1 = require("fs");
|
|
11
|
+
const path_1 = require("path");
|
|
12
|
+
const os_1 = require("os");
|
|
13
|
+
// ── Defaults ──────────────────────────────────────────────
|
|
14
|
+
exports.DEFAULT_CONFIG = {
|
|
15
|
+
runtime: 'auto',
|
|
16
|
+
image: 'docker.io/library/node:20-slim',
|
|
17
|
+
session_timeout_hours: 8,
|
|
18
|
+
filesystem: {
|
|
19
|
+
extra_paths: [],
|
|
20
|
+
blocked_patterns: [
|
|
21
|
+
'**/.ssh',
|
|
22
|
+
'**/.gnupg',
|
|
23
|
+
'**/.aws',
|
|
24
|
+
'**/.config/gcloud',
|
|
25
|
+
'**/.azure',
|
|
26
|
+
'**/.env',
|
|
27
|
+
'**/.netrc',
|
|
28
|
+
'**/.git-credentials',
|
|
29
|
+
'**/*.pem',
|
|
30
|
+
'**/*.key',
|
|
31
|
+
'**/id_rsa*',
|
|
32
|
+
'**/id_ed25519*',
|
|
33
|
+
'**/credentials*',
|
|
34
|
+
'**/secrets*',
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
commands: {
|
|
38
|
+
blacklist: [
|
|
39
|
+
'ssh', 'scp', 'rsync',
|
|
40
|
+
'curl', 'wget',
|
|
41
|
+
'mount', 'umount',
|
|
42
|
+
'dd', 'mkfs',
|
|
43
|
+
'passwd', 'chown',
|
|
44
|
+
'reboot', 'shutdown',
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
network: {
|
|
48
|
+
mode: 'none',
|
|
49
|
+
allowed_domains: [
|
|
50
|
+
'api.anthropic.com',
|
|
51
|
+
'api.openai.com',
|
|
52
|
+
'pypi.org',
|
|
53
|
+
'files.pythonhosted.org',
|
|
54
|
+
'conda.anaconda.org',
|
|
55
|
+
'registry.npmjs.org',
|
|
56
|
+
'github.com',
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
slurm: {
|
|
60
|
+
enabled: false,
|
|
61
|
+
},
|
|
62
|
+
audit: {
|
|
63
|
+
enabled: true,
|
|
64
|
+
log_dir: '~/.labgate/logs',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
// ── Paths ─────────────────────────────────────────────────
|
|
68
|
+
exports.LABGATE_DIR = (0, path_1.join)((0, os_1.homedir)(), '.labgate');
|
|
69
|
+
exports.CONFIG_FILE = 'config.json';
|
|
70
|
+
function getConfigPath() {
|
|
71
|
+
return (0, path_1.join)(exports.LABGATE_DIR, exports.CONFIG_FILE);
|
|
72
|
+
}
|
|
73
|
+
function getSandboxHome() {
|
|
74
|
+
return (0, path_1.join)(exports.LABGATE_DIR, 'ai-home');
|
|
75
|
+
}
|
|
76
|
+
function getImagesDir() {
|
|
77
|
+
return (0, path_1.join)(exports.LABGATE_DIR, 'images');
|
|
78
|
+
}
|
|
79
|
+
function getEmptyDir() {
|
|
80
|
+
return (0, path_1.join)(exports.LABGATE_DIR, 'empty');
|
|
81
|
+
}
|
|
82
|
+
function getLogDir(config) {
|
|
83
|
+
return config.audit.log_dir.replace(/^~/, (0, os_1.homedir)());
|
|
84
|
+
}
|
|
85
|
+
// ── Loader ────────────────────────────────────────────────
|
|
86
|
+
function loadConfig() {
|
|
87
|
+
const configPath = getConfigPath();
|
|
88
|
+
if (!(0, fs_1.existsSync)(configPath)) {
|
|
89
|
+
return { ...exports.DEFAULT_CONFIG };
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
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, '');
|
|
95
|
+
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 {
|
|
99
|
+
runtime: raw.runtime ?? exports.DEFAULT_CONFIG.runtime,
|
|
100
|
+
image: raw.image ?? exports.DEFAULT_CONFIG.image,
|
|
101
|
+
session_timeout_hours: raw.session_timeout_hours ?? exports.DEFAULT_CONFIG.session_timeout_hours,
|
|
102
|
+
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,
|
|
105
|
+
},
|
|
106
|
+
commands: {
|
|
107
|
+
blacklist: raw.commands?.blacklist ?? exports.DEFAULT_CONFIG.commands.blacklist,
|
|
108
|
+
},
|
|
109
|
+
network: {
|
|
110
|
+
mode: raw.network?.mode ?? exports.DEFAULT_CONFIG.network.mode,
|
|
111
|
+
allowed_domains: raw.network?.allowed_domains ?? exports.DEFAULT_CONFIG.network.allowed_domains,
|
|
112
|
+
},
|
|
113
|
+
slurm: {
|
|
114
|
+
enabled: raw.slurm?.enabled ?? exports.DEFAULT_CONFIG.slurm.enabled,
|
|
115
|
+
},
|
|
116
|
+
audit: {
|
|
117
|
+
enabled: raw.audit?.enabled ?? exports.DEFAULT_CONFIG.audit.enabled,
|
|
118
|
+
log_dir: raw.audit?.log_dir ?? exports.DEFAULT_CONFIG.audit.log_dir,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
console.error(`Warning: could not parse ${configPath}: ${err.message}`);
|
|
124
|
+
console.error('Using default config.');
|
|
125
|
+
return { ...exports.DEFAULT_CONFIG };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LabgateConfig } from './config.js';
|
|
2
|
+
export interface SessionOpts {
|
|
3
|
+
agent: string;
|
|
4
|
+
workdir: string;
|
|
5
|
+
config: LabgateConfig;
|
|
6
|
+
dryRun: boolean;
|
|
7
|
+
imageOverride?: string;
|
|
8
|
+
footerMode?: 'off' | 'once' | 'sticky';
|
|
9
|
+
statusline?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Convert a container image URI to a local SIF filename.
|
|
13
|
+
* e.g. "docker.io/library/ubuntu:22.04" → "docker.io_library_ubuntu_22.04.sif"
|
|
14
|
+
*/
|
|
15
|
+
export declare function imageToSifName(image: string): string;
|
|
16
|
+
export declare function buildEntrypoint(agent: string, statuslineEnabled?: boolean): string;
|
|
17
|
+
export declare function startSession(session: SessionOpts): Promise<void>;
|
|
18
|
+
export declare function listSessions(): Promise<void>;
|
|
19
|
+
export declare function stopSession(id: string): Promise<void>;
|