labgate 0.5.37 → 0.5.39
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 +2 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/lib/automation-engine.d.ts +91 -0
- package/dist/lib/automation-engine.js +401 -0
- package/dist/lib/automation-engine.js.map +1 -0
- package/dist/lib/config.d.ts +33 -0
- package/dist/lib/config.js +137 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/container.d.ts +4 -1
- package/dist/lib/container.js +26 -10
- package/dist/lib/container.js.map +1 -1
- package/dist/lib/feedback.d.ts +13 -1
- package/dist/lib/feedback.js +9 -4
- package/dist/lib/feedback.js.map +1 -1
- package/dist/lib/init.js +5 -1
- package/dist/lib/init.js.map +1 -1
- package/dist/lib/results-mcp.js +390 -4
- package/dist/lib/results-mcp.js.map +1 -1
- package/dist/lib/results-store.d.ts +66 -0
- package/dist/lib/results-store.js +324 -6
- package/dist/lib/results-store.js.map +1 -1
- package/dist/lib/ui.d.ts +2 -0
- package/dist/lib/ui.html +16870 -8381
- package/dist/lib/ui.js +1650 -120
- package/dist/lib/ui.js.map +1 -1
- package/dist/lib/web-terminal.d.ts +4 -1
- package/dist/lib/web-terminal.js +33 -3
- package/dist/lib/web-terminal.js.map +1 -1
- package/dist/mcp-bundles/dataset-mcp.bundle.mjs +120 -3
- package/dist/mcp-bundles/display-mcp.bundle.mjs +94 -0
- package/dist/mcp-bundles/explorer-mcp.bundle.mjs +123 -4
- package/dist/mcp-bundles/results-mcp.bundle.mjs +850 -53
- package/dist/mcp-bundles/slurm-mcp.bundle.mjs +94 -0
- package/package.json +1 -1
package/dist/lib/feedback.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
+
type FeedbackRecord = {
|
|
2
|
+
id: string;
|
|
3
|
+
message: string;
|
|
4
|
+
created_at: string;
|
|
5
|
+
source: 'cli' | 'ui';
|
|
6
|
+
metadata?: Record<string, unknown>;
|
|
7
|
+
};
|
|
8
|
+
type SubmitFeedbackOptions = {
|
|
9
|
+
source?: FeedbackRecord['source'];
|
|
10
|
+
metadata?: Record<string, unknown>;
|
|
11
|
+
};
|
|
1
12
|
export declare function collectFeedback(messageParts?: string[]): Promise<string>;
|
|
2
|
-
export declare function submitFeedback(message: string): Promise<{
|
|
13
|
+
export declare function submitFeedback(message: string, opts?: SubmitFeedbackOptions): Promise<{
|
|
3
14
|
mode: 'remote' | 'local';
|
|
4
15
|
detail: string;
|
|
5
16
|
}>;
|
|
17
|
+
export {};
|
package/dist/lib/feedback.js
CHANGED
|
@@ -17,12 +17,17 @@ const DEFAULT_TIMEOUT_MS = 8000;
|
|
|
17
17
|
function isTruthyEnv(value) {
|
|
18
18
|
return /^(1|true|yes|y|on)$/i.test((value || '').trim());
|
|
19
19
|
}
|
|
20
|
-
function buildRecord(message) {
|
|
20
|
+
function buildRecord(message, opts) {
|
|
21
|
+
const source = opts?.source === 'ui' ? 'ui' : 'cli';
|
|
22
|
+
const metadata = (opts && opts.metadata && typeof opts.metadata === 'object')
|
|
23
|
+
? opts.metadata
|
|
24
|
+
: undefined;
|
|
21
25
|
return {
|
|
22
26
|
id: (0, crypto_1.randomUUID)(),
|
|
23
27
|
message,
|
|
24
28
|
created_at: new Date().toISOString(),
|
|
25
|
-
source
|
|
29
|
+
source,
|
|
30
|
+
...(metadata ? { metadata } : {}),
|
|
26
31
|
};
|
|
27
32
|
}
|
|
28
33
|
async function readAllStdin() {
|
|
@@ -57,8 +62,8 @@ async function writeLocal(record) {
|
|
|
57
62
|
await fs_1.promises.appendFile(path, `${JSON.stringify(record)}\n`, 'utf8');
|
|
58
63
|
return path;
|
|
59
64
|
}
|
|
60
|
-
async function submitFeedback(message) {
|
|
61
|
-
const record = buildRecord(message);
|
|
65
|
+
async function submitFeedback(message, opts) {
|
|
66
|
+
const record = buildRecord(message, opts);
|
|
62
67
|
const disabled = isTruthyEnv(process.env[FEEDBACK_DISABLE_ENV]);
|
|
63
68
|
const url = disabled ? '' : (process.env[FEEDBACK_URL_ENV] || DEFAULT_FEEDBACK_URL);
|
|
64
69
|
if (url && !disabled) {
|
package/dist/lib/feedback.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feedback.js","sourceRoot":"","sources":["../../src/lib/feedback.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"feedback.js","sourceRoot":"","sources":["../../src/lib/feedback.ts"],"names":[],"mappings":";;AA+DA,0CAQC;AAUD,wCAqCC;AAtHD,uCAA2C;AAC3C,2BAA6B;AAC7B,+BAA4B;AAC5B,2BAAoC;AACpC,mCAAoC;AAepC,MAAM,oBAAoB,GAAG,kCAAkC,CAAC;AAChE,MAAM,gBAAgB,GAAG,sBAAsB,CAAC;AAChD,MAAM,kBAAkB,GAAG,wBAAwB,CAAC;AACpD,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AACxD,MAAM,uBAAuB,GAAG,6BAA6B,CAAC;AAC9D,MAAM,cAAc,GAAG,gBAAgB,CAAC;AACxC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC,SAAS,WAAW,CAAC,KAAyB;IAC5C,OAAO,sBAAsB,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,WAAW,CAAC,OAAe,EAAE,IAA4B;IAChE,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IACpD,MAAM,QAAQ,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC;QAC3E,CAAC,CAAC,IAAI,CAAC,QAAQ;QACf,CAAC,CAAC,SAAS,CAAC;IACd,OAAO;QACL,EAAE,EAAE,IAAA,mBAAU,GAAE;QAChB,OAAO;QACP,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,MAAM;QACN,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,YAAY;IACzB,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACxC,IAAI,IAAI,KAAK,CAAC;IAChB,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;AACrB,CAAC;AAED,KAAK,UAAU,eAAe;IAC5B,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IACnD,MAAM,EAAE,GAAG,IAAA,0BAAe,EAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7F,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1C,OAAO,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACnC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,YAAuB;IAC3D,IAAI,YAAY,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,OAAO,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACvC,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACxB,OAAO,MAAM,eAAe,EAAE,CAAC;IACjC,CAAC;IACD,OAAO,MAAM,YAAY,EAAE,CAAC;AAC9B,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,MAAsB;IAC9C,MAAM,GAAG,GAAG,IAAA,WAAI,EAAC,IAAA,YAAO,GAAE,EAAE,UAAU,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,IAAA,WAAI,EAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IACvC,MAAM,aAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,aAAE,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACjE,OAAO,IAAI,CAAC;AACd,CAAC;AAEM,KAAK,UAAU,cAAc,CAClC,OAAe,EACf,IAA4B;IAE5B,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAChE,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,oBAAoB,CAAC,CAAC;IACpF,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,MAAM,OAAO,GAA2B,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;YAC/E,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;YAC9C,IAAI,KAAK;gBAAE,OAAO,CAAC,aAAa,GAAG,UAAU,KAAK,EAAE,CAAC;YACrD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CACxB,IAAI,EACJ,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,kBAAkB,CACtF,CAAC;YACF,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;YACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;YAChE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC;iBAC/G,OAAO,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;YACxC,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;gBACX,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;YACtD,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;YACtC,OAAO;gBACL,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,wCAAwC,GAAG,CAAC,MAAM,MAAM,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,IAAI,EAAE;aACvH,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;YACtC,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,0CAA0C,GAAG,aAAa,IAAI,EAAE,EAAE,CAAC;QACrG,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;IACtC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,yBAAyB,IAAI,EAAE,EAAE,CAAC;AACpE,CAAC"}
|
package/dist/lib/init.js
CHANGED
|
@@ -148,7 +148,11 @@ const CONFIG_WITH_COMMENTS = `{
|
|
|
148
148
|
"headless": {
|
|
149
149
|
// Claude headless mode cannot handle interactive tool permission prompts.
|
|
150
150
|
// When enabled, headless runs pass --dangerously-skip-permissions.
|
|
151
|
-
"claude_run_with_allowed_permissions": ${config_js_1.DEFAULT_CONFIG.headless?.claude_run_with_allowed_permissions ?? true}
|
|
151
|
+
"claude_run_with_allowed_permissions": ${config_js_1.DEFAULT_CONFIG.headless?.claude_run_with_allowed_permissions ?? true},
|
|
152
|
+
// Show "Continue in terminal via labgate continue ..." in the terminal footer.
|
|
153
|
+
"continuation_in_other_terminals": ${config_js_1.DEFAULT_CONFIG.headless?.continuation_in_other_terminals ?? true},
|
|
154
|
+
// Enable Git UI integration (Git DAG sidebar + footer branch switcher).
|
|
155
|
+
"git_integration": ${config_js_1.DEFAULT_CONFIG.headless?.git_integration ?? false}
|
|
152
156
|
}
|
|
153
157
|
}
|
|
154
158
|
`;
|
package/dist/lib/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/lib/init.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/lib/init.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiLA,oFAiCC;AAED,gCAoCC;AAxPD,2BAA6D;AAC7D,+BAAqC;AACrC,2BAA6B;AAC7B,2CAUqB;AACrB,8CAAgC;AAEhC,MAAM,wBAAwB,GAAG,cAAc,CAAC;AAChD,MAAM,wBAAwB,GAAG,IAAI,CAAC;AACtC,MAAM,uBAAuB,GAAG,IAAA,WAAI,EAAC,uBAAW,EAAE,UAAU,EAAE,wBAAwB,CAAC,CAAC;AACxF,MAAM,eAAe,GAAG;IACtB,2DAA2D;IAC3D,wBAAwB;IACxB,wBAAwB;IACxB,wBAAwB;IACxB,wBAAwB;IACxB,wBAAwB;IACxB,4BAA4B;IAC5B,4BAA4B;IAC5B,4BAA4B;IAC5B,4BAA4B;IAC5B,4BAA4B;IAC5B,2BAA2B;IAC3B,2BAA2B;IAC3B,2BAA2B;IAC3B,2BAA2B;IAC3B,2BAA2B;CAC5B,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AACpB,MAAM,qBAAqB,GAAG,IAAI,CAAC,GAAG,CACpC,CAAC,EACD,eAAe;KACZ,KAAK,CAAC,IAAI,CAAC;KACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;KAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAChD,CAAC;AACF,MAAM,uBAAuB,GAAG,4CAA4C,qBAAqB,SAAS,CAAC;AAC3G,MAAM,kBAAkB,GACtB,2CAA2C;IAC3C,IAAI;IACJ,SAAS,qBAAqB,IAAI;IAClC,IAAI;IACJ,iFAAiF;IACjF,qEAAqE,CAAC;AACxE,MAAM,oBAAoB,GAAG,IAAI,CAAC,SAAS,CACzC;IACE;QACE,IAAI,EAAE,uBAAuB;QAC7B,IAAI,EAAE,wBAAwB;QAC9B,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EAAE,uBAAuB;KACrC;CACF,EACD,IAAI,EACJ,CAAC,CACF,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAE3B,MAAM,oBAAoB,GAAG;;;;;;;;gBAQb,0BAAc,CAAC,OAAO;;;cAGxB,0BAAc,CAAC,KAAK;;;6BAGL,0BAAc,CAAC,qBAAqB;;;;;;;;;0BASvC,IAAI,CAAC,SAAS,CAAC,0BAAc,CAAC,UAAU,CAAC,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC;;;;;;;gBAOtG,oBAAoB;;;;mBAIjB,IAAI,CAAC,SAAS,CAAC,0BAAc,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC;;;;;;;eAOvF,0BAAc,CAAC,OAAO,CAAC,IAAI;;;yBAGjB,IAAI,CAAC,SAAS,CAAC,0BAAc,CAAC,OAAO,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC;;;;;;iBAMhG,0BAAc,CAAC,KAAK,CAAC,OAAO;;+BAEd,0BAAc,CAAC,KAAK,CAAC,qBAAqB;;8BAE3C,0BAAc,CAAC,KAAK,CAAC,oBAAoB;;oBAEnD,0BAAc,CAAC,KAAK,CAAC,UAAU;;;;;;;;;;;;6CAYN,0BAAc,CAAC,QAAQ,EAAE,mCAAmC,IAAI,IAAI;;yCAExE,0BAAc,CAAC,QAAQ,EAAE,+BAA+B,IAAI,IAAI;;yBAEhF,0BAAc,CAAC,QAAQ,EAAE,eAAe,IAAI,KAAK;;;CAGzE,CAAC;AAEF,SAAS,6BAA6B,CAAC,OAAgC,EAAE;IACvE,IAAA,4BAAgB,EAAC,IAAA,WAAI,EAAC,uBAAW,EAAE,UAAU,CAAC,CAAC,CAAC;IAChD,IAAA,4BAAgB,EAAC,uBAAuB,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAA,WAAI,EAAC,uBAAuB,EAAE,UAAU,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,IAAA,WAAI,EAAC,uBAAuB,EAAE,WAAW,CAAC,CAAC;IAC9D,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,IAAA,eAAU,EAAC,OAAO,CAAC,EAAE,CAAC;QAC3C,IAAA,kBAAa,EAAC,OAAO,EAAE,eAAe,EAAE;YACtC,QAAQ,EAAE,OAAO;YACjB,IAAI,EAAE,6BAAiB;SACxB,CAAC,CAAC;IACL,CAAC;IACD,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,IAAA,eAAU,EAAC,UAAU,CAAC,EAAE,CAAC;QAC9C,IAAA,kBAAa,EAAC,UAAU,EAAE,kBAAkB,EAAE;YAC5C,QAAQ,EAAE,OAAO;YACjB,IAAI,EAAE,6BAAiB;SACxB,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY;IACrC,OAAO,IAAI;SACR,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;SACpD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,SAAS,uBAAuB;IAC9B,OAAO;QACL,IAAI,EAAE,uBAAuB;QAC7B,IAAI,EAAE,wBAAwB;QAC9B,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EAAE,uBAAuB;KACrC,CAAC;AACJ,CAAC;AAED,SAAgB,oCAAoC;IAClD,MAAM,UAAU,GAAG,IAAA,yBAAa,GAAE,CAAC;IACnC,IAAI,CAAC,IAAA,eAAU,EAAC,UAAU,CAAC;QAAE,OAAO;IAEpC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAA,iBAAY,EAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/C,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAA4B,CAAC;QAC9E,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC1C,CAAC,CAAE,GAAG,CAAC,QAA2C;YAClD,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,qBAAqB,GAAG,IAAA,cAAO,EAAC,uBAAuB,CAAC,CAAC;QAC/D,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE;YACtC,MAAM,IAAI,GAAG,OAAO,EAAE,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9E,IAAI,IAAI,KAAK,wBAAwB;gBAAE,OAAO,IAAI,CAAC;YACnD,IAAI,OAAO,EAAE,EAAE,IAAI,KAAK,QAAQ;gBAAE,OAAO,KAAK,CAAC;YAC/C,MAAM,UAAU,GAAG,IAAA,cAAO,EAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAA,YAAO,GAAE,CAAC,CAAC,CAAC;YAC7D,OAAO,UAAU,KAAK,qBAAqB,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,6BAA6B,EAAE,CAAC;QAChC,IAAI,UAAU;YAAE,OAAO;QAEvB,QAAQ,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC,CAAC;QACzC,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACxB,IAAA,kBAAa,EAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE;YAC7D,QAAQ,EAAE,OAAO;YACjB,IAAI,EAAE,6BAAiB;SACxB,CAAC,CAAC;QACH,IAAA,6BAAiB,EAAC,UAAU,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,GAAG,CAAC,IAAI,CAAC,uDAAuD,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACjG,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,UAAU,CAAC,IAA0C;IACzE,MAAM,UAAU,GAAG,IAAA,yBAAa,GAAE,CAAC;IAEnC,qBAAqB;IACrB,IAAA,4BAAgB,EAAC,uBAAW,CAAC,CAAC;IAC9B,IAAA,4BAAgB,EAAC,IAAA,0BAAc,GAAE,CAAC,CAAC;IACnC,IAAA,4BAAgB,EAAC,IAAA,wBAAY,GAAE,CAAC,CAAC;IACjC,IAAA,4BAAgB,EAAC,IAAA,uBAAW,GAAE,CAAC,CAAC;IAChC,IAAA,4BAAgB,EAAC,IAAA,WAAI,EAAC,uBAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IAE5C,IAAI,IAAA,eAAU,EAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAC1C,GAAG,CAAC,IAAI,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAAC;QACjD,GAAG,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IAED,6BAA6B,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,IAAA,kBAAa,EAAC,UAAU,EAAE,oBAAoB,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,6BAAiB,EAAE,CAAC,CAAC;IAChG,IAAA,6BAAiB,EAAC,UAAU,CAAC,CAAC;IAE9B,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,OAAO,CAAC,qBAAqB,UAAU,EAAE,CAAC,CAAC;QAC/C,OAAO;IACT,CAAC;IAED,GAAG,CAAC,OAAO,CAAC,qBAAqB,UAAU,EAAE,CAAC,CAAC;IAC/C,GAAG,CAAC,KAAK,CAAC;QACR,CAAC,cAAc,EAAE,IAAA,0BAAc,GAAE,CAAC;QAClC,CAAC,WAAW,EAAE,IAAA,wBAAY,GAAE,CAAC;QAC7B,CAAC,YAAY,EAAE,IAAA,WAAI,EAAC,uBAAW,EAAE,MAAM,CAAC,CAAC;QACzC,CAAC,gBAAgB,EAAE,uBAAuB,CAAC;KAC5C,CAAC,CAAC;IACH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,GAAG,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACrC,GAAG,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACrC,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;AACtC,CAAC"}
|
package/dist/lib/results-mcp.js
CHANGED
|
@@ -12,7 +12,15 @@ exports.main = main;
|
|
|
12
12
|
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
13
13
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
14
14
|
const zod_1 = require("zod");
|
|
15
|
+
const fs_1 = require("fs");
|
|
16
|
+
const path_1 = require("path");
|
|
17
|
+
const crypto_1 = require("crypto");
|
|
18
|
+
const child_process_1 = require("child_process");
|
|
19
|
+
const util_1 = require("util");
|
|
15
20
|
const results_store_js_1 = require("./results-store.js");
|
|
21
|
+
const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
|
|
22
|
+
const DEFAULT_REPRO_TIMEOUT_SECONDS = 120;
|
|
23
|
+
const MAX_REPRO_OUTPUT_CHARS = 20_000;
|
|
16
24
|
function clampInt(value, fallback, min, max) {
|
|
17
25
|
if (!Number.isFinite(value))
|
|
18
26
|
return fallback;
|
|
@@ -55,6 +63,206 @@ function parseDbPathFromArgs(args) {
|
|
|
55
63
|
}
|
|
56
64
|
return dbPath;
|
|
57
65
|
}
|
|
66
|
+
function trimTail(value, maxLen = MAX_REPRO_OUTPUT_CHARS) {
|
|
67
|
+
let text = null;
|
|
68
|
+
if (typeof value === 'string') {
|
|
69
|
+
text = value;
|
|
70
|
+
}
|
|
71
|
+
else if (Buffer.isBuffer(value)) {
|
|
72
|
+
text = value.toString('utf-8');
|
|
73
|
+
}
|
|
74
|
+
else if (value !== undefined && value !== null) {
|
|
75
|
+
text = String(value);
|
|
76
|
+
}
|
|
77
|
+
if (text === null)
|
|
78
|
+
return null;
|
|
79
|
+
if (text.length <= maxLen)
|
|
80
|
+
return text;
|
|
81
|
+
return text.slice(text.length - maxLen);
|
|
82
|
+
}
|
|
83
|
+
function isPathInside(rootPath, targetPath) {
|
|
84
|
+
const rel = (0, path_1.relative)(rootPath, targetPath);
|
|
85
|
+
return rel === '' || (!rel.startsWith('..') && !(0, path_1.isAbsolute)(rel));
|
|
86
|
+
}
|
|
87
|
+
function buildAllowedRoots(input) {
|
|
88
|
+
const roots = [
|
|
89
|
+
input.executionWorkdir ? (0, path_1.resolve)(input.executionWorkdir) : '',
|
|
90
|
+
input.workdir ? (0, path_1.resolve)(input.workdir) : '',
|
|
91
|
+
(0, path_1.resolve)(process.cwd()),
|
|
92
|
+
].filter(Boolean);
|
|
93
|
+
return [...new Set(roots)];
|
|
94
|
+
}
|
|
95
|
+
function ensurePathAllowed(path, allowedRoots) {
|
|
96
|
+
const allowed = allowedRoots.some((root) => isPathInside(root, path));
|
|
97
|
+
if (allowed)
|
|
98
|
+
return;
|
|
99
|
+
throw new Error(`Script path "${path}" is outside allowed roots: ${allowedRoots.join(', ')}. ` +
|
|
100
|
+
'Set workdir/working_directory or use a path inside your workspace.');
|
|
101
|
+
}
|
|
102
|
+
function ensureScriptOnDisk(scriptPath, allowedRoots, scriptContent) {
|
|
103
|
+
const resolvedPath = (0, path_1.resolve)(String(scriptPath || '').trim());
|
|
104
|
+
if (!resolvedPath) {
|
|
105
|
+
throw new Error('reproducibility.script_path is required.');
|
|
106
|
+
}
|
|
107
|
+
ensurePathAllowed(resolvedPath, allowedRoots);
|
|
108
|
+
if (typeof scriptContent === 'string') {
|
|
109
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(resolvedPath), { recursive: true });
|
|
110
|
+
const normalized = scriptContent.endsWith('\n') ? scriptContent : `${scriptContent}\n`;
|
|
111
|
+
(0, fs_1.writeFileSync)(resolvedPath, normalized, 'utf-8');
|
|
112
|
+
try {
|
|
113
|
+
(0, fs_1.chmodSync)(resolvedPath, 0o755);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Best effort on platforms that do not support chmod.
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (!(0, fs_1.existsSync)(resolvedPath)) {
|
|
120
|
+
throw new Error(`Reproducibility script not found: ${resolvedPath}`);
|
|
121
|
+
}
|
|
122
|
+
const raw = (0, fs_1.readFileSync)(resolvedPath);
|
|
123
|
+
const scriptSha256 = (0, crypto_1.createHash)('sha256').update(raw).digest('hex');
|
|
124
|
+
return { scriptPath: resolvedPath, scriptSha256 };
|
|
125
|
+
}
|
|
126
|
+
function parseSlurmJobId(text) {
|
|
127
|
+
const match = text.match(/Submitted batch job\s+([0-9]+)/i);
|
|
128
|
+
return match?.[1] || null;
|
|
129
|
+
}
|
|
130
|
+
function makeDefaultExecution(strategy, runNow) {
|
|
131
|
+
if (String(strategy || '').toLowerCase() === 'precomputed') {
|
|
132
|
+
return { status: 'precomputed' };
|
|
133
|
+
}
|
|
134
|
+
if (runNow === false)
|
|
135
|
+
return { status: 'not_run' };
|
|
136
|
+
return { status: 'not_run' };
|
|
137
|
+
}
|
|
138
|
+
async function executeLocalReproScript(input) {
|
|
139
|
+
const cwd = input.workdir ? (0, path_1.resolve)(input.workdir) : (0, path_1.dirname)(input.scriptPath);
|
|
140
|
+
const timeoutMs = clampInt(input.timeoutSeconds, DEFAULT_REPRO_TIMEOUT_SECONDS, 10, 86_400) * 1000;
|
|
141
|
+
const start = Date.now();
|
|
142
|
+
const scriptKind = String(input.scriptKind || '').toLowerCase();
|
|
143
|
+
const useCustomCommand = typeof input.commandOverride === 'string' && input.commandOverride.trim().length > 0;
|
|
144
|
+
const displayCommand = useCustomCommand
|
|
145
|
+
? input.commandOverride.trim()
|
|
146
|
+
: (scriptKind === 'python'
|
|
147
|
+
? `python3 ${input.scriptPath}`
|
|
148
|
+
: `bash ${input.scriptPath}`);
|
|
149
|
+
try {
|
|
150
|
+
if (useCustomCommand) {
|
|
151
|
+
const ok = await execFileAsync('bash', ['-lc', input.commandOverride.trim()], {
|
|
152
|
+
cwd,
|
|
153
|
+
timeout: timeoutMs,
|
|
154
|
+
maxBuffer: 5 * 1024 * 1024,
|
|
155
|
+
});
|
|
156
|
+
return {
|
|
157
|
+
status: 'succeeded',
|
|
158
|
+
command: displayCommand,
|
|
159
|
+
ran_at: new Date(start).toISOString(),
|
|
160
|
+
duration_ms: Date.now() - start,
|
|
161
|
+
exit_code: 0,
|
|
162
|
+
signal: null,
|
|
163
|
+
stdout_tail: trimTail(ok.stdout),
|
|
164
|
+
stderr_tail: trimTail(ok.stderr),
|
|
165
|
+
slurm_job_id: null,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (scriptKind === 'python') {
|
|
169
|
+
const ok = await execFileAsync('python3', [input.scriptPath], {
|
|
170
|
+
cwd,
|
|
171
|
+
timeout: timeoutMs,
|
|
172
|
+
maxBuffer: 5 * 1024 * 1024,
|
|
173
|
+
});
|
|
174
|
+
return {
|
|
175
|
+
status: 'succeeded',
|
|
176
|
+
command: displayCommand,
|
|
177
|
+
ran_at: new Date(start).toISOString(),
|
|
178
|
+
duration_ms: Date.now() - start,
|
|
179
|
+
exit_code: 0,
|
|
180
|
+
signal: null,
|
|
181
|
+
stdout_tail: trimTail(ok.stdout),
|
|
182
|
+
stderr_tail: trimTail(ok.stderr),
|
|
183
|
+
slurm_job_id: null,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const ok = await execFileAsync('bash', [input.scriptPath], {
|
|
187
|
+
cwd,
|
|
188
|
+
timeout: timeoutMs,
|
|
189
|
+
maxBuffer: 5 * 1024 * 1024,
|
|
190
|
+
});
|
|
191
|
+
return {
|
|
192
|
+
status: 'succeeded',
|
|
193
|
+
command: displayCommand,
|
|
194
|
+
ran_at: new Date(start).toISOString(),
|
|
195
|
+
duration_ms: Date.now() - start,
|
|
196
|
+
exit_code: 0,
|
|
197
|
+
signal: null,
|
|
198
|
+
stdout_tail: trimTail(ok.stdout),
|
|
199
|
+
stderr_tail: trimTail(ok.stderr),
|
|
200
|
+
slurm_job_id: null,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
const code = typeof err?.code === 'number' ? err.code : null;
|
|
205
|
+
const signal = typeof err?.signal === 'string' ? err.signal : null;
|
|
206
|
+
return {
|
|
207
|
+
status: 'failed',
|
|
208
|
+
command: displayCommand,
|
|
209
|
+
ran_at: new Date(start).toISOString(),
|
|
210
|
+
duration_ms: Date.now() - start,
|
|
211
|
+
exit_code: code,
|
|
212
|
+
signal,
|
|
213
|
+
stdout_tail: trimTail(err?.stdout),
|
|
214
|
+
stderr_tail: trimTail(err?.stderr || err?.message),
|
|
215
|
+
slurm_job_id: null,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async function executeSlurmReproScript(input) {
|
|
220
|
+
const cwd = input.workdir ? (0, path_1.resolve)(input.workdir) : (0, path_1.dirname)(input.scriptPath);
|
|
221
|
+
const timeoutMs = clampInt(input.timeoutSeconds, DEFAULT_REPRO_TIMEOUT_SECONDS, 10, 86_400) * 1000;
|
|
222
|
+
const start = Date.now();
|
|
223
|
+
const useCustomCommand = typeof input.commandOverride === 'string' && input.commandOverride.trim().length > 0;
|
|
224
|
+
const displayCommand = useCustomCommand ? input.commandOverride.trim() : `sbatch ${input.scriptPath}`;
|
|
225
|
+
try {
|
|
226
|
+
const ok = useCustomCommand
|
|
227
|
+
? await execFileAsync('bash', ['-lc', input.commandOverride.trim()], {
|
|
228
|
+
cwd,
|
|
229
|
+
timeout: timeoutMs,
|
|
230
|
+
maxBuffer: 5 * 1024 * 1024,
|
|
231
|
+
})
|
|
232
|
+
: await execFileAsync('sbatch', [input.scriptPath], {
|
|
233
|
+
cwd,
|
|
234
|
+
timeout: timeoutMs,
|
|
235
|
+
maxBuffer: 5 * 1024 * 1024,
|
|
236
|
+
});
|
|
237
|
+
const combined = `${ok.stdout || ''}\n${ok.stderr || ''}`;
|
|
238
|
+
return {
|
|
239
|
+
status: 'submitted',
|
|
240
|
+
command: displayCommand,
|
|
241
|
+
ran_at: new Date(start).toISOString(),
|
|
242
|
+
duration_ms: Date.now() - start,
|
|
243
|
+
exit_code: 0,
|
|
244
|
+
signal: null,
|
|
245
|
+
stdout_tail: trimTail(ok.stdout),
|
|
246
|
+
stderr_tail: trimTail(ok.stderr),
|
|
247
|
+
slurm_job_id: parseSlurmJobId(combined),
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
const code = typeof err?.code === 'number' ? err.code : null;
|
|
252
|
+
const signal = typeof err?.signal === 'string' ? err.signal : null;
|
|
253
|
+
return {
|
|
254
|
+
status: 'failed',
|
|
255
|
+
command: displayCommand,
|
|
256
|
+
ran_at: new Date(start).toISOString(),
|
|
257
|
+
duration_ms: Date.now() - start,
|
|
258
|
+
exit_code: code,
|
|
259
|
+
signal,
|
|
260
|
+
stdout_tail: trimTail(err?.stdout),
|
|
261
|
+
stderr_tail: trimTail(err?.stderr || err?.message),
|
|
262
|
+
slurm_job_id: null,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
58
266
|
async function main(args = process.argv.slice(2)) {
|
|
59
267
|
const dbPath = parseDbPathFromArgs(args);
|
|
60
268
|
const store = dbPath ? new results_store_js_1.ResultsStore(dbPath) : new results_store_js_1.ResultsStore();
|
|
@@ -68,6 +276,36 @@ async function main(args = process.argv.slice(2)) {
|
|
|
68
276
|
instructions: 'LabGate results registry. Use these tools to record important findings, ' +
|
|
69
277
|
'retrieve previously recorded outcomes, and keep results structured for later review.',
|
|
70
278
|
});
|
|
279
|
+
const reproducibilityExecutionSchema = zod_1.z.object({
|
|
280
|
+
status: zod_1.z.enum(['not_run', 'succeeded', 'failed', 'submitted', 'precomputed']).optional(),
|
|
281
|
+
command: zod_1.z.string().nullable().optional(),
|
|
282
|
+
ran_at: zod_1.z.string().nullable().optional(),
|
|
283
|
+
duration_ms: zod_1.z.number().int().min(0).nullable().optional(),
|
|
284
|
+
exit_code: zod_1.z.number().int().nullable().optional(),
|
|
285
|
+
signal: zod_1.z.string().nullable().optional(),
|
|
286
|
+
stdout_tail: zod_1.z.string().nullable().optional(),
|
|
287
|
+
stderr_tail: zod_1.z.string().nullable().optional(),
|
|
288
|
+
slurm_job_id: zod_1.z.string().nullable().optional(),
|
|
289
|
+
});
|
|
290
|
+
const reproducibilitySchema = zod_1.z.object({
|
|
291
|
+
script_path: zod_1.z.string().nullable().optional(),
|
|
292
|
+
script_kind: zod_1.z.enum(['bash', 'python', 'slurm', 'other']).optional(),
|
|
293
|
+
script_sha256: zod_1.z.string().nullable().optional(),
|
|
294
|
+
input_files: zod_1.z.array(zod_1.z.string()).optional(),
|
|
295
|
+
runtime: zod_1.z.string().nullable().optional(),
|
|
296
|
+
requirements: zod_1.z.array(zod_1.z.string()).optional(),
|
|
297
|
+
strategy: zod_1.z.enum(['run', 'precomputed']).optional(),
|
|
298
|
+
precomputed_reason: zod_1.z.string().nullable().optional(),
|
|
299
|
+
execution: reproducibilityExecutionSchema.nullable().optional(),
|
|
300
|
+
});
|
|
301
|
+
const reproducibilityRegistrationSchema = reproducibilitySchema.extend({
|
|
302
|
+
script_path: zod_1.z.string().describe('Path to the script that reproduces this result'),
|
|
303
|
+
script_content: zod_1.z.string().optional().describe('Optional script body to write at script_path'),
|
|
304
|
+
run_now: zod_1.z.boolean().optional().default(true).describe('Execute or submit the script immediately'),
|
|
305
|
+
timeout_seconds: zod_1.z.number().int().min(10).max(86_400).optional().default(DEFAULT_REPRO_TIMEOUT_SECONDS),
|
|
306
|
+
command_override: zod_1.z.string().optional().describe('Optional shell command used instead of the default runner'),
|
|
307
|
+
working_directory: zod_1.z.string().nullable().optional().describe('Working directory used for execution/submission'),
|
|
308
|
+
});
|
|
71
309
|
// ── Tool: list_results ────────────────────────────────
|
|
72
310
|
server.registerTool('list_results', {
|
|
73
311
|
title: 'List Results',
|
|
@@ -76,16 +314,20 @@ async function main(args = process.argv.slice(2)) {
|
|
|
76
314
|
search: zod_1.z.string().optional().describe('Search text across title, summary, details, tags, and metadata'),
|
|
77
315
|
tag: zod_1.z.string().optional().describe('Filter by exact tag'),
|
|
78
316
|
source: zod_1.z.string().optional().describe('Filter by source (e.g. claude, codex)'),
|
|
317
|
+
lineage: zod_1.z.string().optional().describe('Filter by lineage id (all versions of a result thread)'),
|
|
318
|
+
starred: zod_1.z.boolean().optional().describe('Filter by starred status'),
|
|
79
319
|
limit: zod_1.z.number().int().min(1).max(500).optional().default(50).describe('Maximum results to return'),
|
|
80
320
|
offset: zod_1.z.number().int().min(0).optional().default(0).describe('Offset for pagination'),
|
|
81
321
|
},
|
|
82
|
-
}, async ({ search, tag, source, limit, offset }) => {
|
|
322
|
+
}, async ({ search, tag, source, lineage, starred, limit, offset }) => {
|
|
83
323
|
const safeLimit = clampInt(limit, 50, 1, 500);
|
|
84
324
|
const safeOffset = clampInt(offset, 0, 0, 100_000);
|
|
85
325
|
const listed = store.listResults({
|
|
86
326
|
search: search || undefined,
|
|
87
327
|
tag: tag || undefined,
|
|
88
328
|
source: source || undefined,
|
|
329
|
+
lineage: lineage || undefined,
|
|
330
|
+
starred: typeof starred === 'boolean' ? starred : undefined,
|
|
89
331
|
limit: safeLimit,
|
|
90
332
|
offset: safeOffset,
|
|
91
333
|
});
|
|
@@ -102,28 +344,42 @@ async function main(args = process.argv.slice(2)) {
|
|
|
102
344
|
title: 'Register Result',
|
|
103
345
|
description: 'Register a new structured result (finding/outcome) so it can be reviewed later in the LabGate UI.',
|
|
104
346
|
inputSchema: {
|
|
347
|
+
id: zod_1.z.string().optional().describe('Optional explicit id (normally auto-generated)'),
|
|
105
348
|
title: zod_1.z.string().describe('Short title of the result'),
|
|
106
349
|
summary: zod_1.z.string().optional().describe('One-line summary'),
|
|
107
350
|
details: zod_1.z.string().nullable().optional().describe('Long-form details'),
|
|
108
351
|
source: zod_1.z.string().optional().default('claude').describe('Result source label, e.g. claude or codex'),
|
|
352
|
+
starred: zod_1.z.boolean().optional().default(false).describe('Whether the result is starred for quick access'),
|
|
109
353
|
session_id: zod_1.z.string().nullable().optional().describe('Optional LabGate session id'),
|
|
110
354
|
workdir: zod_1.z.string().nullable().optional().describe('Optional work directory path'),
|
|
355
|
+
lineage_id: zod_1.z.string().optional().describe('Optional lineage id used to group versioned results'),
|
|
356
|
+
version: zod_1.z.number().int().min(1).optional().describe('Explicit version number within the lineage'),
|
|
357
|
+
previous_result_id: zod_1.z.string().nullable().optional().describe('Optional previous version id in this lineage'),
|
|
358
|
+
version_of: zod_1.z.string().optional().describe('Create a next version based on an existing result id'),
|
|
111
359
|
tags: zod_1.z.array(zod_1.z.string()).optional().describe('Tags for filtering'),
|
|
112
360
|
artifacts: zod_1.z.array(zod_1.z.string()).optional().describe('Related file paths or artifact references'),
|
|
113
361
|
metadata: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).nullable().optional().describe('Optional flat metadata map'),
|
|
362
|
+
reproducibility: reproducibilitySchema.nullable().optional().describe('Reproducibility specification for this result'),
|
|
114
363
|
},
|
|
115
|
-
}, async ({ title, summary, details, source, session_id, workdir, tags, artifacts, metadata }) => {
|
|
364
|
+
}, async ({ id, title, summary, details, source, starred, session_id, workdir, lineage_id, version, previous_result_id, version_of, tags, artifacts, metadata, reproducibility, }) => {
|
|
116
365
|
try {
|
|
117
366
|
const created = store.createResult({
|
|
367
|
+
id,
|
|
118
368
|
title,
|
|
119
369
|
summary,
|
|
120
370
|
details: details === undefined ? null : details,
|
|
121
371
|
source,
|
|
372
|
+
starred,
|
|
122
373
|
session_id,
|
|
123
374
|
workdir,
|
|
375
|
+
lineage_id,
|
|
376
|
+
version,
|
|
377
|
+
previous_result_id,
|
|
378
|
+
version_of,
|
|
124
379
|
tags,
|
|
125
380
|
artifacts,
|
|
126
381
|
metadata,
|
|
382
|
+
reproducibility,
|
|
127
383
|
});
|
|
128
384
|
return asText(`Result "${created.title}" recorded with id ${created.id}.`);
|
|
129
385
|
}
|
|
@@ -131,6 +387,103 @@ async function main(args = process.argv.slice(2)) {
|
|
|
131
387
|
return asError(`Failed to register result: ${err.message}`);
|
|
132
388
|
}
|
|
133
389
|
});
|
|
390
|
+
// ── Tool: register_reproducible_result ───────────────
|
|
391
|
+
server.registerTool('register_reproducible_result', {
|
|
392
|
+
title: 'Register Reproducible Result',
|
|
393
|
+
description: 'Register a result with reproducibility guarantees: script path/hash, explicit inputs/requirements, and optional immediate execution (or SLURM submission).',
|
|
394
|
+
inputSchema: {
|
|
395
|
+
id: zod_1.z.string().optional().describe('Optional explicit id (normally auto-generated)'),
|
|
396
|
+
title: zod_1.z.string().describe('Short title of the result'),
|
|
397
|
+
summary: zod_1.z.string().optional().describe('One-line summary'),
|
|
398
|
+
details: zod_1.z.string().nullable().optional().describe('Long-form details'),
|
|
399
|
+
source: zod_1.z.string().optional().default('claude').describe('Result source label, e.g. claude or codex'),
|
|
400
|
+
starred: zod_1.z.boolean().optional().default(false).describe('Whether the result is starred for quick access'),
|
|
401
|
+
session_id: zod_1.z.string().nullable().optional().describe('Optional LabGate session id'),
|
|
402
|
+
workdir: zod_1.z.string().nullable().optional().describe('Optional result workdir (also default run cwd)'),
|
|
403
|
+
lineage_id: zod_1.z.string().optional().describe('Optional lineage id used to group versioned results'),
|
|
404
|
+
version: zod_1.z.number().int().min(1).optional().describe('Explicit version number within the lineage'),
|
|
405
|
+
previous_result_id: zod_1.z.string().nullable().optional().describe('Optional previous version id in this lineage'),
|
|
406
|
+
version_of: zod_1.z.string().optional().describe('Create a next version based on an existing result id'),
|
|
407
|
+
tags: zod_1.z.array(zod_1.z.string()).optional().describe('Tags for filtering'),
|
|
408
|
+
artifacts: zod_1.z.array(zod_1.z.string()).optional().describe('Related file paths or artifact references'),
|
|
409
|
+
metadata: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).nullable().optional().describe('Optional flat metadata map'),
|
|
410
|
+
reproducibility: reproducibilityRegistrationSchema,
|
|
411
|
+
},
|
|
412
|
+
}, async ({ id, title, summary, details, source, starred, session_id, workdir, lineage_id, version, previous_result_id, version_of, tags, artifacts, metadata, reproducibility, }) => {
|
|
413
|
+
try {
|
|
414
|
+
const repro = (reproducibility || {});
|
|
415
|
+
const executionWorkdir = (typeof repro.working_directory === 'string' && repro.working_directory.trim())
|
|
416
|
+
? String(repro.working_directory)
|
|
417
|
+
: null;
|
|
418
|
+
const allowedRoots = buildAllowedRoots({
|
|
419
|
+
workdir: typeof workdir === 'string' ? workdir : null,
|
|
420
|
+
executionWorkdir,
|
|
421
|
+
});
|
|
422
|
+
const scriptSpec = ensureScriptOnDisk(String(repro.script_path || ''), allowedRoots, typeof repro.script_content === 'string' ? repro.script_content : undefined);
|
|
423
|
+
const strategy = String(repro.strategy || 'run').toLowerCase() === 'precomputed' ? 'precomputed' : 'run';
|
|
424
|
+
const runNow = repro.run_now === undefined ? true : !!repro.run_now;
|
|
425
|
+
const scriptKind = String(repro.script_kind || 'bash').toLowerCase();
|
|
426
|
+
const timeoutSeconds = clampInt(typeof repro.timeout_seconds === 'number' ? repro.timeout_seconds : undefined, DEFAULT_REPRO_TIMEOUT_SECONDS, 10, 86_400);
|
|
427
|
+
const commandOverride = typeof repro.command_override === 'string' ? repro.command_override : undefined;
|
|
428
|
+
const runCwd = (executionWorkdir && executionWorkdir.trim())
|
|
429
|
+
|| (typeof workdir === 'string' && workdir.trim())
|
|
430
|
+
|| null;
|
|
431
|
+
let execution = makeDefaultExecution(strategy, runNow);
|
|
432
|
+
if (strategy !== 'precomputed' && runNow) {
|
|
433
|
+
execution = scriptKind === 'slurm'
|
|
434
|
+
? await executeSlurmReproScript({
|
|
435
|
+
scriptPath: scriptSpec.scriptPath,
|
|
436
|
+
timeoutSeconds,
|
|
437
|
+
workdir: runCwd,
|
|
438
|
+
commandOverride,
|
|
439
|
+
})
|
|
440
|
+
: await executeLocalReproScript({
|
|
441
|
+
scriptPath: scriptSpec.scriptPath,
|
|
442
|
+
scriptKind,
|
|
443
|
+
timeoutSeconds,
|
|
444
|
+
workdir: runCwd,
|
|
445
|
+
commandOverride,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
const reproducibilityPayload = {
|
|
449
|
+
script_path: scriptSpec.scriptPath,
|
|
450
|
+
script_kind: scriptKind,
|
|
451
|
+
script_sha256: scriptSpec.scriptSha256,
|
|
452
|
+
input_files: Array.isArray(repro.input_files) ? repro.input_files : undefined,
|
|
453
|
+
runtime: typeof repro.runtime === 'string' ? repro.runtime : null,
|
|
454
|
+
requirements: Array.isArray(repro.requirements) ? repro.requirements : undefined,
|
|
455
|
+
strategy,
|
|
456
|
+
precomputed_reason: typeof repro.precomputed_reason === 'string' ? repro.precomputed_reason : null,
|
|
457
|
+
execution,
|
|
458
|
+
};
|
|
459
|
+
const created = store.createResult({
|
|
460
|
+
id,
|
|
461
|
+
title,
|
|
462
|
+
summary,
|
|
463
|
+
details: details === undefined ? null : details,
|
|
464
|
+
source,
|
|
465
|
+
starred,
|
|
466
|
+
session_id,
|
|
467
|
+
workdir,
|
|
468
|
+
lineage_id,
|
|
469
|
+
version,
|
|
470
|
+
previous_result_id,
|
|
471
|
+
version_of,
|
|
472
|
+
tags,
|
|
473
|
+
artifacts,
|
|
474
|
+
metadata,
|
|
475
|
+
reproducibility: reproducibilityPayload,
|
|
476
|
+
});
|
|
477
|
+
return asJson({
|
|
478
|
+
ok: true,
|
|
479
|
+
result: created,
|
|
480
|
+
reproducibility_execution: created.reproducibility?.execution || null,
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
catch (err) {
|
|
484
|
+
return asError(`Failed to register reproducible result: ${err.message}`);
|
|
485
|
+
}
|
|
486
|
+
});
|
|
134
487
|
// ── Tool: get_result ──────────────────────────────────
|
|
135
488
|
server.registerTool('get_result', {
|
|
136
489
|
title: 'Get Result',
|
|
@@ -145,6 +498,24 @@ async function main(args = process.argv.slice(2)) {
|
|
|
145
498
|
}
|
|
146
499
|
return asJson(result);
|
|
147
500
|
});
|
|
501
|
+
// ── Tool: list_result_versions ────────────────────────
|
|
502
|
+
server.registerTool('list_result_versions', {
|
|
503
|
+
title: 'List Result Versions',
|
|
504
|
+
description: 'List all versions in a result lineage. Accepts either a result id or a lineage id.',
|
|
505
|
+
inputSchema: {
|
|
506
|
+
id_or_lineage: zod_1.z.string().describe('Result id or lineage id'),
|
|
507
|
+
},
|
|
508
|
+
}, async ({ id_or_lineage }) => {
|
|
509
|
+
const versions = store.listResultVersions(id_or_lineage);
|
|
510
|
+
if (versions.length === 0) {
|
|
511
|
+
return asError(`No versions found for "${id_or_lineage}".`);
|
|
512
|
+
}
|
|
513
|
+
return asJson({
|
|
514
|
+
lineage_id: versions[0].lineage_id,
|
|
515
|
+
count: versions.length,
|
|
516
|
+
versions,
|
|
517
|
+
});
|
|
518
|
+
});
|
|
148
519
|
// ── Tool: update_result ───────────────────────────────
|
|
149
520
|
server.registerTool('update_result', {
|
|
150
521
|
title: 'Update Result',
|
|
@@ -155,22 +526,32 @@ async function main(args = process.argv.slice(2)) {
|
|
|
155
526
|
summary: zod_1.z.string().optional().describe('New summary'),
|
|
156
527
|
details: zod_1.z.string().nullable().optional().describe('New details, null clears'),
|
|
157
528
|
source: zod_1.z.string().optional().describe('New source'),
|
|
529
|
+
starred: zod_1.z.boolean().optional().describe('Set whether this result is starred'),
|
|
158
530
|
session_id: zod_1.z.string().nullable().optional().describe('New session id, null clears'),
|
|
159
531
|
workdir: zod_1.z.string().nullable().optional().describe('New workdir, null clears'),
|
|
532
|
+
lineage_id: zod_1.z.string().optional().describe('Set lineage id'),
|
|
533
|
+
version: zod_1.z.number().int().min(1).optional().describe('Set version number'),
|
|
534
|
+
previous_result_id: zod_1.z.string().nullable().optional().describe('Set previous version id'),
|
|
160
535
|
tags: zod_1.z.array(zod_1.z.string()).optional().describe('Replace tags'),
|
|
161
536
|
artifacts: zod_1.z.array(zod_1.z.string()).optional().describe('Replace artifacts'),
|
|
162
537
|
metadata: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).nullable().optional().describe('Replace metadata, null clears'),
|
|
538
|
+
reproducibility: reproducibilitySchema.nullable().optional().describe('Replace reproducibility metadata, null clears'),
|
|
163
539
|
},
|
|
164
|
-
}, async ({ id, title, summary, details, source, session_id, workdir, tags, artifacts, metadata }) => {
|
|
540
|
+
}, async ({ id, title, summary, details, source, starred, session_id, workdir, lineage_id, version, previous_result_id, tags, artifacts, metadata, reproducibility, }) => {
|
|
165
541
|
const hasPatch = title !== undefined ||
|
|
166
542
|
summary !== undefined ||
|
|
167
543
|
details !== undefined ||
|
|
168
544
|
source !== undefined ||
|
|
545
|
+
starred !== undefined ||
|
|
169
546
|
session_id !== undefined ||
|
|
170
547
|
workdir !== undefined ||
|
|
548
|
+
lineage_id !== undefined ||
|
|
549
|
+
version !== undefined ||
|
|
550
|
+
previous_result_id !== undefined ||
|
|
171
551
|
tags !== undefined ||
|
|
172
552
|
artifacts !== undefined ||
|
|
173
|
-
metadata !== undefined
|
|
553
|
+
metadata !== undefined ||
|
|
554
|
+
reproducibility !== undefined;
|
|
174
555
|
if (!hasPatch) {
|
|
175
556
|
return asError('No update fields provided.');
|
|
176
557
|
}
|
|
@@ -180,11 +561,16 @@ async function main(args = process.argv.slice(2)) {
|
|
|
180
561
|
summary,
|
|
181
562
|
details,
|
|
182
563
|
source,
|
|
564
|
+
starred,
|
|
183
565
|
session_id,
|
|
184
566
|
workdir,
|
|
567
|
+
lineage_id,
|
|
568
|
+
version,
|
|
569
|
+
previous_result_id,
|
|
185
570
|
tags,
|
|
186
571
|
artifacts,
|
|
187
572
|
metadata,
|
|
573
|
+
reproducibility,
|
|
188
574
|
});
|
|
189
575
|
if (!updated) {
|
|
190
576
|
return asError(`Result "${id}" not found.`);
|