labgate 0.5.2 → 0.5.4

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.
Files changed (40) hide show
  1. package/README.md +48 -3
  2. package/dist/cli.js +322 -19
  3. package/dist/cli.js.map +1 -1
  4. package/dist/lib/audit.d.ts +5 -1
  5. package/dist/lib/audit.js +19 -3
  6. package/dist/lib/audit.js.map +1 -1
  7. package/dist/lib/config.d.ts +71 -2
  8. package/dist/lib/config.js +192 -8
  9. package/dist/lib/config.js.map +1 -1
  10. package/dist/lib/container.d.ts +54 -0
  11. package/dist/lib/container.js +650 -178
  12. package/dist/lib/container.js.map +1 -1
  13. package/dist/lib/init.js +22 -9
  14. package/dist/lib/init.js.map +1 -1
  15. package/dist/lib/license.d.ts +44 -0
  16. package/dist/lib/license.js +164 -0
  17. package/dist/lib/license.js.map +1 -0
  18. package/dist/lib/policy.d.ts +85 -0
  19. package/dist/lib/policy.js +321 -0
  20. package/dist/lib/policy.js.map +1 -0
  21. package/dist/lib/runtime.d.ts +2 -2
  22. package/dist/lib/runtime.js +19 -36
  23. package/dist/lib/runtime.js.map +1 -1
  24. package/dist/lib/slurm-db.d.ts +51 -0
  25. package/dist/lib/slurm-db.js +179 -0
  26. package/dist/lib/slurm-db.js.map +1 -0
  27. package/dist/lib/slurm-mcp.d.ts +12 -0
  28. package/dist/lib/slurm-mcp.js +347 -0
  29. package/dist/lib/slurm-mcp.js.map +1 -0
  30. package/dist/lib/slurm-poller.d.ts +36 -0
  31. package/dist/lib/slurm-poller.js +423 -0
  32. package/dist/lib/slurm-poller.js.map +1 -0
  33. package/dist/lib/test/integration-harness.d.ts +44 -0
  34. package/dist/lib/test/integration-harness.js +260 -0
  35. package/dist/lib/test/integration-harness.js.map +1 -0
  36. package/dist/lib/ui.d.ts +34 -1
  37. package/dist/lib/ui.html +3081 -356
  38. package/dist/lib/ui.js +2123 -108
  39. package/dist/lib/ui.js.map +1 -1
  40. package/package.json +11 -3
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SlurmJobDB = exports.TERMINAL_STATES = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ exports.TERMINAL_STATES = new Set([
7
+ 'COMPLETED',
8
+ 'FAILED',
9
+ 'CANCELLED',
10
+ 'TIMEOUT',
11
+ 'NODE_FAIL',
12
+ 'PREEMPTED',
13
+ 'OUT_OF_MEMORY',
14
+ ]);
15
+ // ── Database abstraction ──────────────────────────────────
16
+ let Database;
17
+ try {
18
+ Database = require('better-sqlite3');
19
+ }
20
+ catch {
21
+ Database = null;
22
+ }
23
+ const SCHEMA = `
24
+ CREATE TABLE IF NOT EXISTS slurm_jobs (
25
+ job_id TEXT PRIMARY KEY,
26
+ name TEXT,
27
+ state TEXT NOT NULL,
28
+ submit_time TEXT NOT NULL,
29
+ start_time TEXT,
30
+ end_time TEXT,
31
+ partition TEXT,
32
+ nodes TEXT,
33
+ num_nodes INTEGER,
34
+ num_tasks INTEGER,
35
+ cpus_per_task INTEGER,
36
+ mem TEXT,
37
+ stdout_path TEXT,
38
+ stderr_path TEXT,
39
+ stdout_size INTEGER,
40
+ stderr_size INTEGER,
41
+ command TEXT,
42
+ script_path TEXT,
43
+ session_id TEXT,
44
+ workdir TEXT,
45
+ exit_code INTEGER,
46
+ account TEXT,
47
+ qos TEXT,
48
+ time_limit TEXT,
49
+ discovered_at TEXT NOT NULL,
50
+ updated_at TEXT NOT NULL,
51
+ notes TEXT
52
+ );
53
+ CREATE INDEX IF NOT EXISTS idx_slurm_jobs_state ON slurm_jobs(state);
54
+ CREATE INDEX IF NOT EXISTS idx_slurm_jobs_session ON slurm_jobs(session_id);
55
+ CREATE INDEX IF NOT EXISTS idx_slurm_jobs_submit_time ON slurm_jobs(submit_time);
56
+ `;
57
+ class SlurmJobDB {
58
+ db;
59
+ constructor(dbPath) {
60
+ if (!Database) {
61
+ throw new Error('SLURM tracking requires the "better-sqlite3" package. ' +
62
+ 'Install it with: npm install better-sqlite3');
63
+ }
64
+ const dir = (0, path_1.dirname)(dbPath);
65
+ if (!(0, fs_1.existsSync)(dir)) {
66
+ (0, fs_1.mkdirSync)(dir, { recursive: true });
67
+ }
68
+ this.db = new Database(dbPath);
69
+ try {
70
+ (0, fs_1.chmodSync)(dbPath, 0o600);
71
+ }
72
+ catch {
73
+ // Best effort for filesystems without POSIX chmod semantics.
74
+ }
75
+ this.db.pragma('journal_mode = WAL');
76
+ this.db.pragma('busy_timeout = 5000');
77
+ this.db.exec(SCHEMA);
78
+ // Migration: add notes column for databases created before this feature
79
+ try {
80
+ this.db.exec('ALTER TABLE slurm_jobs ADD COLUMN notes TEXT');
81
+ }
82
+ catch { /* column already exists */ }
83
+ }
84
+ upsertJob(job) {
85
+ const now = new Date().toISOString();
86
+ const existing = this.getJob(job.job_id);
87
+ if (existing) {
88
+ // Don't overwrite with less information — only update fields that have values
89
+ const updates = [];
90
+ const values = [];
91
+ const fields = [
92
+ 'name', 'state', 'submit_time', 'start_time', 'end_time',
93
+ 'partition', 'nodes', 'num_nodes', 'num_tasks', 'cpus_per_task',
94
+ 'mem', 'stdout_path', 'stderr_path', 'stdout_size', 'stderr_size',
95
+ 'command', 'script_path', 'session_id', 'workdir', 'exit_code',
96
+ 'account', 'qos', 'time_limit', 'notes',
97
+ ];
98
+ for (const field of fields) {
99
+ if (job[field] !== undefined) {
100
+ updates.push(`${field} = ?`);
101
+ values.push(job[field]);
102
+ }
103
+ }
104
+ updates.push('updated_at = ?');
105
+ values.push(now);
106
+ values.push(job.job_id);
107
+ this.db.prepare(`UPDATE slurm_jobs SET ${updates.join(', ')} WHERE job_id = ?`).run(...values);
108
+ }
109
+ else {
110
+ this.db
111
+ .prepare(`INSERT INTO slurm_jobs (
112
+ job_id, name, state, submit_time, start_time, end_time,
113
+ partition, nodes, num_nodes, num_tasks, cpus_per_task, mem,
114
+ stdout_path, stderr_path, stdout_size, stderr_size,
115
+ command, script_path, session_id, workdir, exit_code,
116
+ account, qos, time_limit, notes, discovered_at, updated_at
117
+ ) VALUES (
118
+ ?, ?, ?, ?, ?, ?,
119
+ ?, ?, ?, ?, ?, ?,
120
+ ?, ?, ?, ?,
121
+ ?, ?, ?, ?, ?,
122
+ ?, ?, ?, ?, ?, ?
123
+ )`)
124
+ .run(job.job_id, job.name ?? null, job.state ?? 'UNKNOWN', job.submit_time ?? now, job.start_time ?? null, job.end_time ?? null, job.partition ?? null, job.nodes ?? null, job.num_nodes ?? null, job.num_tasks ?? null, job.cpus_per_task ?? null, job.mem ?? null, job.stdout_path ?? null, job.stderr_path ?? null, job.stdout_size ?? null, job.stderr_size ?? null, job.command ?? null, job.script_path ?? null, job.session_id ?? null, job.workdir ?? null, job.exit_code ?? null, job.account ?? null, job.qos ?? null, job.time_limit ?? null, job.notes ?? null, now, now);
125
+ }
126
+ }
127
+ updateJobNotes(jobId, notes) {
128
+ const now = new Date().toISOString();
129
+ const result = this.db
130
+ .prepare('UPDATE slurm_jobs SET notes = ?, updated_at = ? WHERE job_id = ?')
131
+ .run(notes, now, jobId);
132
+ return result.changes > 0;
133
+ }
134
+ getJob(jobId) {
135
+ return this.db.prepare('SELECT * FROM slurm_jobs WHERE job_id = ?').get(jobId) ?? null;
136
+ }
137
+ listJobs(opts) {
138
+ const conditions = [];
139
+ const values = [];
140
+ if (opts?.state) {
141
+ conditions.push('state = ?');
142
+ values.push(opts.state);
143
+ }
144
+ if (opts?.session_id) {
145
+ conditions.push('session_id = ?');
146
+ values.push(opts.session_id);
147
+ }
148
+ if (opts?.search) {
149
+ conditions.push('(job_id LIKE ? OR name LIKE ? OR command LIKE ? OR notes LIKE ?)');
150
+ const pattern = `%${opts.search}%`;
151
+ values.push(pattern, pattern, pattern, pattern);
152
+ }
153
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
154
+ const limitRaw = opts?.limit ?? 100;
155
+ const offsetRaw = opts?.offset ?? 0;
156
+ const limit = Number.isFinite(limitRaw) ? Math.min(1000, Math.max(1, Math.floor(limitRaw))) : 100;
157
+ const offset = Number.isFinite(offsetRaw) ? Math.max(0, Math.floor(offsetRaw)) : 0;
158
+ return this.db
159
+ .prepare(`SELECT * FROM slurm_jobs ${where} ORDER BY submit_time DESC LIMIT ? OFFSET ?`)
160
+ .all(...values, limit, offset);
161
+ }
162
+ getActiveJobIds() {
163
+ const rows = this.db
164
+ .prepare("SELECT job_id FROM slurm_jobs WHERE state IN ('PENDING', 'RUNNING', 'COMPLETING', 'SUSPENDED')")
165
+ .all();
166
+ return rows.map((r) => r.job_id);
167
+ }
168
+ getJobCount(state) {
169
+ if (state) {
170
+ return this.db.prepare('SELECT COUNT(*) as count FROM slurm_jobs WHERE state = ?').get(state).count;
171
+ }
172
+ return this.db.prepare('SELECT COUNT(*) as count FROM slurm_jobs').get().count;
173
+ }
174
+ close() {
175
+ this.db.close();
176
+ }
177
+ }
178
+ exports.SlurmJobDB = SlurmJobDB;
179
+ //# sourceMappingURL=slurm-db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slurm-db.js","sourceRoot":"","sources":["../../src/lib/slurm-db.ts"],"names":[],"mappings":";;;AAAA,2BAAsD;AACtD,+BAA+B;AAkBlB,QAAA,eAAe,GAAwB,IAAI,GAAG,CAAC;IAC1D,WAAW;IACX,QAAQ;IACR,WAAW;IACX,SAAS;IACT,WAAW;IACX,WAAW;IACX,eAAe;CAChB,CAAC,CAAC;AAwCH,6DAA6D;AAE7D,IAAI,QAAa,CAAC;AAClB,IAAI,CAAC;IACH,QAAQ,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;AACvC,CAAC;AAAC,MAAM,CAAC;IACP,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC;AAED,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCd,CAAC;AAEF,MAAa,UAAU;IACb,EAAE,CAAM;IAEhB,YAAY,MAAc;QACxB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CACb,wDAAwD;gBACtD,6CAA6C,CAChD,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,GAAG,IAAA,cAAO,EAAC,MAAM,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAA,eAAU,EAAC,GAAG,CAAC,EAAE,CAAC;YACrB,IAAA,cAAS,EAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,CAAC,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC;YACH,IAAA,cAAS,EAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;QAC/D,CAAC;QACD,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACrC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;QACtC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,wEAAwE;QACxE,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAC/D,CAAC;QAAC,MAAM,CAAC,CAAC,2BAA2B,CAAC,CAAC;IACzC,CAAC;IAED,SAAS,CAAC,GAA2C;QACnD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAEzC,IAAI,QAAQ,EAAE,CAAC;YACb,8EAA8E;YAC9E,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAU,EAAE,CAAC;YAEzB,MAAM,MAAM,GAAuB;gBACjC,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU;gBACxD,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe;gBAC/D,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa;gBACjE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW;gBAC9D,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO;aACxC,CAAC;YAEF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,SAAS,EAAE,CAAC;oBAC7B,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC;oBAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;YAED,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAExB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,yBAAyB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;QACjG,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,EAAE;iBACJ,OAAO,CACN;;;;;;;;;;;;YAYE,CACH;iBACA,GAAG,CACF,GAAG,CAAC,MAAM,EACV,GAAG,CAAC,IAAI,IAAI,IAAI,EAChB,GAAG,CAAC,KAAK,IAAI,SAAS,EACtB,GAAG,CAAC,WAAW,IAAI,GAAG,EACtB,GAAG,CAAC,UAAU,IAAI,IAAI,EACtB,GAAG,CAAC,QAAQ,IAAI,IAAI,EACpB,GAAG,CAAC,SAAS,IAAI,IAAI,EACrB,GAAG,CAAC,KAAK,IAAI,IAAI,EACjB,GAAG,CAAC,SAAS,IAAI,IAAI,EACrB,GAAG,CAAC,SAAS,IAAI,IAAI,EACrB,GAAG,CAAC,aAAa,IAAI,IAAI,EACzB,GAAG,CAAC,GAAG,IAAI,IAAI,EACf,GAAG,CAAC,WAAW,IAAI,IAAI,EACvB,GAAG,CAAC,WAAW,IAAI,IAAI,EACvB,GAAG,CAAC,WAAW,IAAI,IAAI,EACvB,GAAG,CAAC,WAAW,IAAI,IAAI,EACvB,GAAG,CAAC,OAAO,IAAI,IAAI,EACnB,GAAG,CAAC,WAAW,IAAI,IAAI,EACvB,GAAG,CAAC,UAAU,IAAI,IAAI,EACtB,GAAG,CAAC,OAAO,IAAI,IAAI,EACnB,GAAG,CAAC,SAAS,IAAI,IAAI,EACrB,GAAG,CAAC,OAAO,IAAI,IAAI,EACnB,GAAG,CAAC,GAAG,IAAI,IAAI,EACf,GAAG,CAAC,UAAU,IAAI,IAAI,EACtB,GAAG,CAAC,KAAK,IAAI,IAAI,EACjB,GAAG,EACH,GAAG,CACJ,CAAC;QACN,CAAC;IACH,CAAC;IAED,cAAc,CAAC,KAAa,EAAE,KAAoB;QAChD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE;aACnB,OAAO,CAAC,kEAAkE,CAAC;aAC3E,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1B,OAAO,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,CAAC,KAAa;QAClB,OAAO,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,2CAA2C,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;IACzF,CAAC;IAED,QAAQ,CAAC,IAAsB;QAC7B,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,MAAM,MAAM,GAAU,EAAE,CAAC;QAEzB,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;YAChB,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;QACD,IAAI,IAAI,EAAE,UAAU,EAAE,CAAC;YACrB,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/B,CAAC;QACD,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;YACjB,UAAU,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAC;YACpF,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAClD,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/E,MAAM,QAAQ,GAAG,IAAI,EAAE,KAAK,IAAI,GAAG,CAAC;QACpC,MAAM,SAAS,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAClG,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEnF,OAAO,IAAI,CAAC,EAAE;aACX,OAAO,CAAC,4BAA4B,KAAK,6CAA6C,CAAC;aACvF,GAAG,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,eAAe;QACb,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;aACjB,OAAO,CAAC,gGAAgG,CAAC;aACzG,GAAG,EAAE,CAAC;QACT,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAED,WAAW,CAAC,KAAc;QACxB,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,0DAA0D,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC;QACtG,CAAC;QACD,OAAO,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;IACjF,CAAC;IAED,KAAK;QACH,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;CACF;AArKD,gCAqKC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * LabGate SLURM MCP Server
3
+ *
4
+ * A Model Context Protocol server that exposes SLURM job tracking tools
5
+ * to Claude Code. Runs as a standalone process using stdio transport.
6
+ *
7
+ * Usage: node dist/lib/slurm-mcp.js [--db <path>]
8
+ *
9
+ * Environment variables:
10
+ * LABGATE_SLURM_DB – Path to the SLURM SQLite database (default: ~/.labgate/slurm.db)
11
+ */
12
+ export declare function main(): Promise<void>;
@@ -0,0 +1,347 @@
1
+ "use strict";
2
+ /**
3
+ * LabGate SLURM MCP Server
4
+ *
5
+ * A Model Context Protocol server that exposes SLURM job tracking tools
6
+ * to Claude Code. Runs as a standalone process using stdio transport.
7
+ *
8
+ * Usage: node dist/lib/slurm-mcp.js [--db <path>]
9
+ *
10
+ * Environment variables:
11
+ * LABGATE_SLURM_DB – Path to the SLURM SQLite database (default: ~/.labgate/slurm.db)
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.main = main;
15
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
16
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
17
+ const slurm_db_js_1 = require("./slurm-db.js");
18
+ const config_js_1 = require("./config.js");
19
+ const fs_1 = require("fs");
20
+ const zod_1 = require("zod");
21
+ // ── Resolve DB path ──────────────────────────────────────
22
+ function resolveDbPath() {
23
+ // CLI arg
24
+ const dbArgIdx = process.argv.indexOf('--db');
25
+ if (dbArgIdx >= 0 && process.argv[dbArgIdx + 1]) {
26
+ return process.argv[dbArgIdx + 1];
27
+ }
28
+ // Environment variable
29
+ if (process.env.LABGATE_SLURM_DB) {
30
+ return process.env.LABGATE_SLURM_DB;
31
+ }
32
+ // Default
33
+ return (0, config_js_1.getSlurmDbPath)();
34
+ }
35
+ // ── Helpers ──────────────────────────────────────────────
36
+ function tailReadFile(filePath, maxLines) {
37
+ if (!(0, fs_1.existsSync)(filePath))
38
+ return '';
39
+ const st = (0, fs_1.statSync)(filePath);
40
+ const maxBytes = 1_048_576; // 1MB
41
+ const readSize = Math.min(st.size, maxBytes);
42
+ if (readSize === 0)
43
+ return '';
44
+ const buf = Buffer.alloc(readSize);
45
+ let fd = null;
46
+ try {
47
+ fd = (0, fs_1.openSync)(filePath, 'r');
48
+ (0, fs_1.readSync)(fd, buf, 0, readSize, Math.max(0, st.size - readSize));
49
+ }
50
+ finally {
51
+ if (fd !== null) {
52
+ try {
53
+ (0, fs_1.closeSync)(fd);
54
+ }
55
+ catch { /* best effort */ }
56
+ }
57
+ }
58
+ let text = buf.toString('utf-8');
59
+ if (st.size > readSize) {
60
+ const firstNewline = text.indexOf('\n');
61
+ if (firstNewline >= 0)
62
+ text = text.slice(firstNewline + 1);
63
+ }
64
+ const lines = text.split('\n');
65
+ return lines.slice(-maxLines).join('\n');
66
+ }
67
+ function clampInt(value, fallback, min, max) {
68
+ if (!Number.isFinite(value))
69
+ return fallback;
70
+ return Math.min(max, Math.max(min, Math.floor(value)));
71
+ }
72
+ function normalizeJobId(value) {
73
+ return String(value || '').trim();
74
+ }
75
+ // ── MCP Server ───────────────────────────────────────────
76
+ async function main() {
77
+ const dbPath = resolveDbPath();
78
+ let db;
79
+ try {
80
+ db = new slurm_db_js_1.SlurmJobDB(dbPath);
81
+ }
82
+ catch (err) {
83
+ console.error(`Failed to open SLURM database at ${dbPath}: ${err.message}`);
84
+ process.exit(1);
85
+ }
86
+ const server = new mcp_js_1.McpServer({
87
+ name: 'labgate-slurm',
88
+ version: '1.0.0',
89
+ }, {
90
+ capabilities: {
91
+ tools: {},
92
+ },
93
+ instructions: 'LabGate SLURM job tracker. Use these tools to check the status of SLURM jobs, ' +
94
+ 'read their output files, and cancel jobs. Jobs are automatically discovered by ' +
95
+ 'polling squeue/sacct.',
96
+ });
97
+ // ── Tool: list_slurm_jobs ──────────────────────────────
98
+ server.registerTool('list_slurm_jobs', {
99
+ title: 'List SLURM Jobs',
100
+ description: 'List SLURM jobs tracked by LabGate. Returns job ID, name, state, runtime, ' +
101
+ 'partition, nodes, and output file paths. Filter by state or search by name/ID.',
102
+ inputSchema: {
103
+ state: zod_1.z.string().optional().describe('Filter by state: PENDING, RUNNING, COMPLETED, FAILED, CANCELLED, TIMEOUT'),
104
+ search: zod_1.z.string().optional().describe('Search by job ID, name, or command'),
105
+ limit: zod_1.z.number().int().min(1).max(500).optional().default(20).describe('Max number of jobs to return'),
106
+ },
107
+ }, async ({ state, search, limit }) => {
108
+ const safeLimit = clampInt(limit, 20, 1, 500);
109
+ const jobs = db.listJobs({
110
+ state: state || undefined,
111
+ search: search || undefined,
112
+ limit: safeLimit,
113
+ });
114
+ const summary = jobs.map((j) => ({
115
+ job_id: j.job_id,
116
+ name: j.name,
117
+ state: j.state,
118
+ partition: j.partition,
119
+ nodes: j.nodes,
120
+ submit_time: j.submit_time,
121
+ start_time: j.start_time,
122
+ end_time: j.end_time,
123
+ exit_code: j.exit_code,
124
+ stdout_path: j.stdout_path,
125
+ stderr_path: j.stderr_path,
126
+ stdout_size: j.stdout_size,
127
+ stderr_size: j.stderr_size,
128
+ command: j.command,
129
+ notes: j.notes,
130
+ }));
131
+ return {
132
+ content: [
133
+ {
134
+ type: 'text',
135
+ text: jobs.length === 0
136
+ ? 'No SLURM jobs found.'
137
+ : JSON.stringify(summary, null, 2),
138
+ },
139
+ ],
140
+ };
141
+ });
142
+ // ── Tool: get_slurm_job ────────────────────────────────
143
+ server.registerTool('get_slurm_job', {
144
+ title: 'Get SLURM Job Details',
145
+ description: 'Get detailed information about a specific SLURM job by its ID.',
146
+ inputSchema: {
147
+ job_id: zod_1.z.string().describe('The SLURM job ID'),
148
+ },
149
+ }, async ({ job_id }) => {
150
+ const normalizedJobId = normalizeJobId(job_id);
151
+ if (!normalizedJobId) {
152
+ return {
153
+ content: [{ type: 'text', text: 'job_id must be a non-empty string.' }],
154
+ isError: true,
155
+ };
156
+ }
157
+ const job = db.getJob(normalizedJobId);
158
+ if (!job) {
159
+ return {
160
+ content: [{ type: 'text', text: `Job ${normalizedJobId} not found.` }],
161
+ isError: true,
162
+ };
163
+ }
164
+ return {
165
+ content: [{ type: 'text', text: JSON.stringify(job, null, 2) }],
166
+ };
167
+ });
168
+ // ── Tool: get_slurm_output ─────────────────────────────
169
+ server.registerTool('get_slurm_output', {
170
+ title: 'Read SLURM Job Output',
171
+ description: 'Read the stdout or stderr output file of a SLURM job. Returns the last N lines.',
172
+ inputSchema: {
173
+ job_id: zod_1.z.string().describe('The SLURM job ID'),
174
+ stream: zod_1.z.enum(['stdout', 'stderr']).optional().default('stdout').describe('Which output stream to read'),
175
+ tail_lines: zod_1.z.number().optional().default(100).describe('Number of lines to read from the end'),
176
+ },
177
+ }, async ({ job_id, stream, tail_lines }) => {
178
+ const normalizedJobId = normalizeJobId(job_id);
179
+ if (!normalizedJobId) {
180
+ return {
181
+ content: [{ type: 'text', text: 'job_id must be a non-empty string.' }],
182
+ isError: true,
183
+ };
184
+ }
185
+ const safeTailLines = clampInt(tail_lines, 100, 1, 5000);
186
+ const job = db.getJob(normalizedJobId);
187
+ if (!job) {
188
+ return {
189
+ content: [{ type: 'text', text: `Job ${normalizedJobId} not found.` }],
190
+ isError: true,
191
+ };
192
+ }
193
+ const filePath = stream === 'stderr' ? job.stderr_path : job.stdout_path;
194
+ if (!filePath) {
195
+ return {
196
+ content: [
197
+ {
198
+ type: 'text',
199
+ text: `No ${stream} path recorded for job ${normalizedJobId}.`,
200
+ },
201
+ ],
202
+ };
203
+ }
204
+ if (!(0, fs_1.existsSync)(filePath)) {
205
+ return {
206
+ content: [
207
+ {
208
+ type: 'text',
209
+ text: `Output file ${filePath} does not exist (yet).`,
210
+ },
211
+ ],
212
+ };
213
+ }
214
+ const st = (0, fs_1.statSync)(filePath);
215
+ if (st.size === 0) {
216
+ return {
217
+ content: [
218
+ {
219
+ type: 'text',
220
+ text: `Output file ${filePath} exists but is empty (0 bytes).`,
221
+ },
222
+ ],
223
+ };
224
+ }
225
+ const content = tailReadFile(filePath, safeTailLines);
226
+ return {
227
+ content: [
228
+ {
229
+ type: 'text',
230
+ text: `=== ${stream} for job ${normalizedJobId} (last ${safeTailLines} lines, file size: ${st.size} bytes) ===\n${content}`,
231
+ },
232
+ ],
233
+ };
234
+ });
235
+ // ── Tool: cancel_slurm_job ─────────────────────────────
236
+ server.registerTool('cancel_slurm_job', {
237
+ title: 'Cancel SLURM Job',
238
+ description: 'Cancel a pending or running SLURM job using scancel.',
239
+ inputSchema: {
240
+ job_id: zod_1.z.string().describe('The SLURM job ID to cancel'),
241
+ },
242
+ }, async ({ job_id }) => {
243
+ const normalizedJobId = normalizeJobId(job_id);
244
+ if (!normalizedJobId) {
245
+ return {
246
+ content: [{ type: 'text', text: 'job_id must be a non-empty string.' }],
247
+ isError: true,
248
+ };
249
+ }
250
+ const job = db.getJob(normalizedJobId);
251
+ if (!job) {
252
+ return {
253
+ content: [{ type: 'text', text: `Job ${normalizedJobId} not found.` }],
254
+ isError: true,
255
+ };
256
+ }
257
+ if (slurm_db_js_1.TERMINAL_STATES.has(job.state)) {
258
+ return {
259
+ content: [
260
+ {
261
+ type: 'text',
262
+ text: `Job ${normalizedJobId} is already in terminal state: ${job.state}`,
263
+ },
264
+ ],
265
+ };
266
+ }
267
+ const { execFile } = await import('child_process');
268
+ const { promisify } = await import('util');
269
+ const execFileAsync = promisify(execFile);
270
+ try {
271
+ await execFileAsync('scancel', [normalizedJobId], { timeout: 10_000 });
272
+ db.upsertJob({ job_id: normalizedJobId, state: 'CANCELLED' });
273
+ return {
274
+ content: [
275
+ { type: 'text', text: `Job ${normalizedJobId} has been cancelled.` },
276
+ ],
277
+ };
278
+ }
279
+ catch (err) {
280
+ return {
281
+ content: [
282
+ {
283
+ type: 'text',
284
+ text: `Failed to cancel job ${normalizedJobId}: ${err.message}`,
285
+ },
286
+ ],
287
+ isError: true,
288
+ };
289
+ }
290
+ });
291
+ // ── Tool: set_slurm_job_notes ─────────────────────────
292
+ server.registerTool('set_slurm_job_notes', {
293
+ title: 'Set SLURM Job Notes',
294
+ description: 'Add or update a text note on a SLURM job. Use this to annotate jobs with context or observations.',
295
+ inputSchema: {
296
+ job_id: zod_1.z.string().describe('The SLURM job ID'),
297
+ notes: zod_1.z.string().nullable().describe('Note text, or null to clear'),
298
+ },
299
+ }, async ({ job_id, notes }) => {
300
+ const id = normalizeJobId(job_id);
301
+ if (!id) {
302
+ return {
303
+ content: [{ type: 'text', text: 'job_id must be a non-empty string.' }],
304
+ isError: true,
305
+ };
306
+ }
307
+ const job = db.getJob(id);
308
+ if (!job) {
309
+ return {
310
+ content: [{ type: 'text', text: `Job ${id} not found.` }],
311
+ isError: true,
312
+ };
313
+ }
314
+ db.updateJobNotes(id, notes);
315
+ return {
316
+ content: [
317
+ {
318
+ type: 'text',
319
+ text: notes ? `Notes set for job ${id}.` : `Notes cleared for job ${id}.`,
320
+ },
321
+ ],
322
+ };
323
+ });
324
+ // ── Connect and start ──────────────────────────────────
325
+ const transport = new stdio_js_1.StdioServerTransport();
326
+ await server.connect(transport);
327
+ // Clean up on exit
328
+ process.on('SIGINT', () => {
329
+ db.close();
330
+ process.exit(0);
331
+ });
332
+ process.on('SIGTERM', () => {
333
+ db.close();
334
+ process.exit(0);
335
+ });
336
+ }
337
+ function isDirectRun() {
338
+ const argv1 = process.argv[1] || '';
339
+ return /(?:^|\/|\\)slurm-mcp(?:\.js)?$/.test(argv1);
340
+ }
341
+ if (isDirectRun()) {
342
+ main().catch((err) => {
343
+ console.error('MCP server error:', err);
344
+ process.exit(1);
345
+ });
346
+ }
347
+ //# sourceMappingURL=slurm-mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slurm-mcp.js","sourceRoot":"","sources":["../../src/lib/slurm-mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { SlurmJobDB, SlurmJob } from './slurm-db.js';
2
+ export interface PollResult {
3
+ squeueJobs: number;
4
+ sacctJobs: number;
5
+ errors: string[];
6
+ }
7
+ export interface SlurmPollerOptions {
8
+ db: SlurmJobDB;
9
+ pollIntervalMs?: number;
10
+ sacctLookbackHours?: number;
11
+ username?: string;
12
+ }
13
+ export declare class SlurmPoller {
14
+ private db;
15
+ private pollIntervalMs;
16
+ private sacctLookbackHours;
17
+ private username;
18
+ private timer;
19
+ private sacctTimer;
20
+ private backoffMs;
21
+ private squeueSupportsJson;
22
+ private readonly localHost;
23
+ constructor(opts: SlurmPollerOptions);
24
+ start(): void;
25
+ stop(): void;
26
+ pollOnce(): Promise<PollResult>;
27
+ private pollSqueue;
28
+ parseSqueueJson(output: string): Partial<SlurmJob>[];
29
+ parseSqueueDelimited(output: string, delimiter?: string): Partial<SlurmJob>[];
30
+ private pollSacct;
31
+ parseSacctOutput(output: string): Partial<SlurmJob>[];
32
+ private upsertWithSessionCorrelation;
33
+ private findMatchingSession;
34
+ private updateOutputFileStats;
35
+ private exec;
36
+ }