@barivia/barmesh-mcp 0.8.7 → 0.8.9
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/dist/index.js +1 -1
- package/dist/shared.js +1 -1
- package/dist/tools/cfd.js +34 -36
- package/dist/tools/datasets.js +10 -6
- package/dist/tools/guide.js +5 -20
- package/dist/tools/jobs.js +11 -4
- package/dist/tools/results.js +2 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{McpServer as e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as r}from"@modelcontextprotocol/sdk/server/stdio.js";import{getUiCapability as
|
|
2
|
+
import{McpServer as e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as r}from"@modelcontextprotocol/sdk/server/stdio.js";import{getUiCapability as o,registerAppResource as s,RESOURCE_MIME_TYPE as t}from"@modelcontextprotocol/ext-apps/server";import{startVizServer as n}from"./viz-server.js";import{API_KEY as i,CLIENT_VERSION as a,apiCall as m,apiRawCall as l,loadViewHtml as c,setVizPort as p,setClientSupportsMcpApps as h}from"./shared.js";import{registerGuideTool as d}from"./tools/guide.js";import{registerDatasetsTool as _}from"./tools/datasets.js";import{registerCfdTools as b}from"./tools/cfd.js";import{registerJobsTool as u}from"./tools/jobs.js";import{registerResultsTool as f}from"./tools/results.js";import{registerResultsExplorerTool as v,RESULTS_EXPLORER_URI as y}from"./tools/barmesh_results_explorer.js";import{registerTrainingMonitorTool as I,TRAINING_MONITOR_URI as w}from"./tools/training_monitor.js";import{registerFeedbackTool as g}from"./tools/feedback.js";i||(console.error("Error: BARIVIA_API_KEY not set. Set it in your MCP client config."),process.exit(1));(async function(){const i=new e({name:"barmesh",version:a},{instructions:"# Barivia barmesh — CFD mesh-convergence analytics\n\n## Bootstrap (do this first)\n\nCall `barmesh_guide_workflow` (plan-scoped) and `barmesh_prepare_mesh_data` before upload/submit. Method details and mesh-prep recipe come from the API when your key has the CFD entitlement.\n\n## Tracks (names only)\n\n- `barmesh_mesh_convergence` — field-level mesh comparison (SOM fingerprints)\n- `barmesh_richardson` — classical Richardson/GCI on scalar QoIs\n\n## Workflow (short)\n\n1. `barmesh_guide_workflow`\n2. `barmesh_prepare_mesh_data`\n3. `barmesh_datasets(upload)` → preview\n4. Submit mesh_convergence and/or richardson → job_id\n5. `barmesh_training_monitor` or `barmesh_jobs(monitor/status)`\n6. `barmesh_results(get)` → `barmesh_results_explorer`\n\nInventory: `barmesh_jobs(inventory)` or list datasets+jobs. Set description/tags on upload/submit.\n\n## UI / outages\n\nTool results lead with a localhost viz URL and `ui_delivery` — follow `hint_for_agent`. Env: `BARIVIA_UI_DELIVERY`, `BARIVIA_VIZ_PORT`.\nOn `api_unreachable`: one calm line; honor `retry_after_sec`; use `barmesh_api_health`; keep reviewing last good figures. `barmesh_send_feedback` queues deferred drafts when the API is down."});s(i,y,y,{mimeType:t},async()=>{const e=await c("barmesh-results-explorer");return{contents:[{uri:y,mimeType:t,text:e??"<html><body>Results Explorer view not built yet. Run: npm run build:views</body></html>"}]}}),s(i,w,w,{mimeType:t},async()=>{const e=await c("barmesh-training-monitor");return{contents:[{uri:w,mimeType:t,text:e??"<html><body>Training Monitor view not built yet. Run: npm run build:views</body></html>"}]}}),d(i),v(i),_(i),b(i),u(i),I(i),f(i),g(i);try{const e=await n(m,l,c);p(e)}catch(e){process.env.BARIVIA_VIZ_PORT&&console.error("barmesh viz server failed to start:",e)}const j=i.server;j.oninitialized=()=>{const e=j.getClientCapabilities(),r=o(e);h(!!r?.mimeTypes?.includes(t))};const A=new r;await i.connect(A),console.error(`barmesh-mcp ${a} ready (API: ${process.env.BARIVIA_API_URL??"https://api.barivia.se"})`)})().catch(e=>{console.error("Fatal error starting barmesh-mcp:",e),process.exit(1)});
|
package/dist/shared.js
CHANGED
|
@@ -35,7 +35,7 @@ function newRequestId() {
|
|
|
35
35
|
return randomUUID();
|
|
36
36
|
}
|
|
37
37
|
/** Single source of truth for the proxy version. Keep in sync with package.json on bump. */
|
|
38
|
-
export const CLIENT_VERSION = "0.8.
|
|
38
|
+
export const CLIENT_VERSION = "0.8.9";
|
|
39
39
|
export const PUBLIC_SITE_ORIGIN = "https://barivia.se";
|
|
40
40
|
/** Large per-cell CSV uploads may exceed the default fetch timeout. */
|
|
41
41
|
export const UPLOAD_DATASET_TIMEOUT_MS = 180_000;
|
package/dist/tools/cfd.js
CHANGED
|
@@ -5,34 +5,33 @@ import { pollCfdPrepareIfPresent } from "../cfd_prepare.js";
|
|
|
5
5
|
export function registerCfdTools(server) {
|
|
6
6
|
registerAuditedTool(server, "barmesh_mesh_convergence", `Run SOM-based mesh-convergence analysis on an uploaded combined per-cell CSV.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
COMMON MISTAKES: omitting feature_columns (required); choosing a reference_mesh label that is not present; forgetting the cell-volume column at upload time; multi-topology studies (uniform U + graded G meshes) without explicit mesh_order — equal cell counts sort U-then-G and invent bogus stepwise pairs (U160|G20). Pass mesh_order coarse→fine interleaved by refinement (U20,G20,U40,G40,…) or use preset=cavity which defaults interleaved when U/G labels are detected.`, {
|
|
8
|
+
BEST FOR: Field-level mesh-refinement comparison (not a single scalar QoI).
|
|
9
|
+
NOT FOR: Classical GCI on scalars — use barmesh_richardson.
|
|
10
|
+
ASYNC: Returns job_id; poll barmesh_jobs(status) every 10–20s; then barmesh_results(get).
|
|
11
|
+
Call **barmesh_guide_workflow** / **barmesh_prepare_mesh_data** for method details, mesh_order rules, and prep recipe (API, CFD entitlement).
|
|
12
|
+
COMMON MISTAKES: omitting feature_columns; bad reference_mesh label; missing volume column — see prepare_mesh_data.`, {
|
|
14
13
|
dataset_id: z.string().describe("Dataset ID from barmesh_datasets(upload)"),
|
|
15
|
-
feature_columns: z.array(z.string()).describe("Per-cell numeric feature columns
|
|
16
|
-
preset: z.enum(["generic", "cavity", "pitz", "datacenter"]).optional().describe("Hyperparameter preset
|
|
17
|
-
mesh_column: z.string().optional().describe("
|
|
18
|
-
volume_column: z.string().optional().describe("Cell-volume column
|
|
19
|
-
reference_mesh: z.string().optional().describe("
|
|
20
|
-
mesh_order: z.array(z.string()).optional().describe("Explicit coarse
|
|
21
|
-
grid: z.array(z.number().int()).optional().describe("SOM grid [rows, cols]
|
|
22
|
-
epochs: z.array(z.number().int()).optional().describe("[ordering, convergence] epochs
|
|
23
|
-
batch_size: z.number().int().optional().describe("SOM batch size
|
|
24
|
-
sigma_f: z.number().optional().describe("Final ordering neighborhood width
|
|
25
|
-
backend: z.enum(["auto", "cpu", "gpu", "gpu_graphs"]).optional().describe("Compute backend
|
|
26
|
-
stratify_scale: z.number().optional().describe("[0,1] per-mesh training-row cap
|
|
27
|
-
emd_method: z.enum(["exact", "sinkhorn"]).optional().describe("EMD solver
|
|
28
|
-
te_panel_size: z.number().int().optional().describe("
|
|
14
|
+
feature_columns: z.array(z.string()).describe("Per-cell numeric feature columns for the SOM"),
|
|
15
|
+
preset: z.enum(["generic", "cavity", "pitz", "datacenter"]).optional().describe("Hyperparameter preset (default generic) — see barmesh_guide_workflow"),
|
|
16
|
+
mesh_column: z.string().optional().describe("Mesh label column (default mesh_id)"),
|
|
17
|
+
volume_column: z.string().optional().describe("Cell-volume column (default V)"),
|
|
18
|
+
reference_mesh: z.string().optional().describe("Reference mesh label (default: finest / most cells)"),
|
|
19
|
+
mesh_order: z.array(z.string()).optional().describe("Explicit coarse→fine mesh order — see barmesh_prepare_mesh_data"),
|
|
20
|
+
grid: z.array(z.number().int()).optional().describe("SOM grid [rows, cols] override"),
|
|
21
|
+
epochs: z.array(z.number().int()).optional().describe("[ordering, convergence] epochs override"),
|
|
22
|
+
batch_size: z.number().int().optional().describe("SOM batch size override"),
|
|
23
|
+
sigma_f: z.number().optional().describe("Final ordering neighborhood width override"),
|
|
24
|
+
backend: z.enum(["auto", "cpu", "gpu", "gpu_graphs"]).optional().describe("Compute backend"),
|
|
25
|
+
stratify_scale: z.number().optional().describe("[0,1] per-mesh training-row cap"),
|
|
26
|
+
emd_method: z.enum(["exact", "sinkhorn"]).optional().describe("EMD solver — see barmesh_guide_workflow"),
|
|
27
|
+
te_panel_size: z.number().int().optional().describe("Live TE evaluation panel size"),
|
|
29
28
|
te_inner_samples: z.number().int().optional().describe("Deprecated alias for te_panel_size"),
|
|
30
|
-
component_planes_physical: z.boolean().optional().describe("Physical-scale component-plane colorbars
|
|
29
|
+
component_planes_physical: z.boolean().optional().describe("Physical-scale component-plane colorbars"),
|
|
31
30
|
figures: z.boolean().optional().describe("Generate publication figures (default true)"),
|
|
32
|
-
transforms: z.record(z.enum(["log", "log1p", "log10", "sqrt", "square", "abs", "invert", "none"])).optional().describe("Per-feature transform
|
|
33
|
-
normalize: z.union([z.enum(["all", "auto", "mad", "sigmoidal", "sepd"]), z.array(z.string())]).optional().describe("
|
|
34
|
-
normalization_methods: z.record(z.enum(["zscore", "mad", "sigmoidal", "sepd", "none"])).optional().describe("Per-feature normalization override
|
|
35
|
-
row_range: z.tuple([z.number().int().min(1), z.number().int().min(1)]).optional().describe("1-based inclusive [start, end] row slice
|
|
31
|
+
transforms: z.record(z.enum(["log", "log1p", "log10", "sqrt", "square", "abs", "invert", "none"])).optional().describe("Per-feature transform before normalization"),
|
|
32
|
+
normalize: z.union([z.enum(["all", "auto", "mad", "sigmoidal", "sepd"]), z.array(z.string())]).optional().describe("SOM feature normalization mode"),
|
|
33
|
+
normalization_methods: z.record(z.enum(["zscore", "mad", "sigmoidal", "sepd", "none"])).optional().describe("Per-feature normalization override"),
|
|
34
|
+
row_range: z.tuple([z.number().int().min(1), z.number().int().min(1)]).optional().describe("1-based inclusive [start, end] row slice"),
|
|
36
35
|
label: z.string().optional().describe("Optional job label"),
|
|
37
36
|
description: z.string().optional().describe("Optional free-text description for inventory"),
|
|
38
37
|
tags: z.array(z.string()).optional().describe("Optional tags for inventory/navigation"),
|
|
@@ -63,21 +62,20 @@ COMMON MISTAKES: omitting feature_columns (required); choosing a reference_mesh
|
|
|
63
62
|
}
|
|
64
63
|
return textResult(data);
|
|
65
64
|
});
|
|
66
|
-
registerAuditedTool(server, "barmesh_richardson", `Run classical Richardson extrapolation / Grid Convergence Index (GCI) on scalar
|
|
67
|
-
|
|
68
|
-
What it does: from a small CSV with one row per mesh (a mesh label, a representative cell size h or a cell count, and one or more QoI columns), slides the three-level Celik (2008) GCI over consecutive mesh triplets (finest first) and reports the observed order p, the extrapolated value, and the fine-grid GCI per triplet per QoI.
|
|
65
|
+
registerAuditedTool(server, "barmesh_richardson", `Run classical Richardson extrapolation / Grid Convergence Index (GCI) on scalar QoIs.
|
|
69
66
|
|
|
70
|
-
BEST FOR: Scalar benchmarks
|
|
71
|
-
NOT FOR: High-dimensional field comparison — use barmesh_mesh_convergence
|
|
72
|
-
ASYNC: queued job; poll barmesh_jobs(
|
|
73
|
-
|
|
67
|
+
BEST FOR: Scalar benchmarks where classical asymptotic-convergence checks are expected.
|
|
68
|
+
NOT FOR: High-dimensional field comparison — use barmesh_mesh_convergence.
|
|
69
|
+
ASYNC: queued job; poll barmesh_jobs(status), then barmesh_results(get).
|
|
70
|
+
Method details (Fs, one-family-per-CSV, triplets): **barmesh_guide_workflow**.
|
|
71
|
+
COMMON MISTAKES: missing h_column or n_cells_column; mixing mesh families — see guide.`, {
|
|
74
72
|
dataset_id: z.string().describe("Dataset ID (one row per mesh)"),
|
|
75
73
|
qoi_columns: z.array(z.string()).describe("Scalar QoI column names to extrapolate"),
|
|
76
74
|
mesh_column: z.string().optional().describe("Mesh label column (default mesh_id)"),
|
|
77
|
-
h_column: z.string().optional().describe("Representative cell-size column (
|
|
78
|
-
n_cells_column: z.string().optional().describe("Cell-count column
|
|
79
|
-
dimension: z.number().int().optional().describe("Spatial dimension
|
|
80
|
-
safety_factor: z.number().optional().describe("GCI safety factor
|
|
75
|
+
h_column: z.string().optional().describe("Representative cell-size column (or use n_cells_column)"),
|
|
76
|
+
n_cells_column: z.string().optional().describe("Cell-count column (alternative to h_column)"),
|
|
77
|
+
dimension: z.number().int().optional().describe("Spatial dimension for h from cell count"),
|
|
78
|
+
safety_factor: z.number().optional().describe("GCI safety factor — see barmesh_guide_workflow"),
|
|
81
79
|
label: z.string().optional().describe("Optional job label"),
|
|
82
80
|
description: z.string().optional().describe("Optional free-text description for inventory"),
|
|
83
81
|
tags: z.array(z.string()).optional().describe("Optional tags for inventory/navigation"),
|
package/dist/tools/datasets.js
CHANGED
|
@@ -7,6 +7,10 @@ import { registerAuditedTool } from "../audit.js";
|
|
|
7
7
|
import { apiCall, getWorkspaceRootAsync, resolveFilePathForUpload, textResult, pollUntilComplete, UPLOAD_DATASET_TIMEOUT_MS, LARGE_UPLOAD_BYTES, PRESIGNED_PUT_TIMEOUT_MS, POLL_STAGE_MAX_MS, awaitDatasetStageIfNeeded, streamFileSha256, putPresignedStream, resolveUploadContentType, suggestMeshDatasetPreview, } from "../shared.js";
|
|
8
8
|
import { GZIP_UPLOAD_HINT } from "../upload_hints.js";
|
|
9
9
|
import { formatDatasetInventoryLine, formatTags } from "../inventory_format.js";
|
|
10
|
+
/** Percent-encode header values so Node fetch accepts Unicode (ByteString limit). */
|
|
11
|
+
function encodeHeaderValue(s) {
|
|
12
|
+
return encodeURIComponent(s);
|
|
13
|
+
}
|
|
10
14
|
/**
|
|
11
15
|
* Normalize a nullable string field from the API. Returns "" for absent values,
|
|
12
16
|
* empty strings, and the literal SQL/serialization sentinels "missing"/"null".
|
|
@@ -169,15 +173,15 @@ ESCALATION: If preview shows a feature column as non-numeric, fix the extraction
|
|
|
169
173
|
if (isGzipInput) {
|
|
170
174
|
const gzBytes = await fs.readFile(resolved);
|
|
171
175
|
const gzHeaders = {
|
|
172
|
-
"X-Dataset-Name": name,
|
|
176
|
+
"X-Dataset-Name": encodeHeaderValue(name),
|
|
173
177
|
"Content-Type": uploadContentType,
|
|
174
178
|
"Content-Encoding": "gzip",
|
|
175
179
|
"Idempotency-Key": createHash("sha256").update(`${name}\n`).update(gzBytes).digest("hex"),
|
|
176
180
|
};
|
|
177
181
|
if (description !== undefined)
|
|
178
|
-
gzHeaders["X-Dataset-Description"] = description;
|
|
182
|
+
gzHeaders["X-Dataset-Description"] = encodeHeaderValue(description);
|
|
179
183
|
if (tags !== undefined)
|
|
180
|
-
gzHeaders["X-Dataset-Tags"] = JSON.stringify(tags);
|
|
184
|
+
gzHeaders["X-Dataset-Tags"] = encodeHeaderValue(JSON.stringify(tags));
|
|
181
185
|
const data = await awaitDatasetStageIfNeeded((await apiCall("POST", "/v1/datasets", gzBytes, gzHeaders, UPLOAD_DATASET_TIMEOUT_MS)));
|
|
182
186
|
const gid = data.id ?? data.dataset_id;
|
|
183
187
|
if (gid != null) {
|
|
@@ -195,14 +199,14 @@ ESCALATION: If preview shows a feature column as non-numeric, fix the extraction
|
|
|
195
199
|
}
|
|
196
200
|
const GZIP_THRESHOLD = 1024 * 1024;
|
|
197
201
|
const uploadHeaders = {
|
|
198
|
-
"X-Dataset-Name": name,
|
|
202
|
+
"X-Dataset-Name": encodeHeaderValue(name),
|
|
199
203
|
"Content-Type": uploadContentType,
|
|
200
204
|
"Idempotency-Key": createHash("sha256").update(`${name}\n`).update(body).digest("hex"),
|
|
201
205
|
};
|
|
202
206
|
if (description !== undefined)
|
|
203
|
-
uploadHeaders["X-Dataset-Description"] = description;
|
|
207
|
+
uploadHeaders["X-Dataset-Description"] = encodeHeaderValue(description);
|
|
204
208
|
if (tags !== undefined)
|
|
205
|
-
uploadHeaders["X-Dataset-Tags"] = JSON.stringify(tags);
|
|
209
|
+
uploadHeaders["X-Dataset-Tags"] = encodeHeaderValue(JSON.stringify(tags));
|
|
206
210
|
let uploadBody = body;
|
|
207
211
|
if (Buffer.byteLength(body, "utf-8") > GZIP_THRESHOLD) {
|
|
208
212
|
uploadBody = gzipSync(Buffer.from(body, "utf-8"));
|
package/dist/tools/guide.js
CHANGED
|
@@ -3,27 +3,12 @@ import { apiCall, probeApiHealth, structuredTextResult, textResult } from "../sh
|
|
|
3
3
|
import { listDeferredFeedback } from "../deferred_feedback.js";
|
|
4
4
|
const OFFLINE_GUIDE = `Mesh analysis API temporarily unavailable — figures already on disk stay available; new jobs/feedback will wait.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Say the API is unavailable plainly; the proxy already burst-retried (~3×2s) — wait ~30s before another round. Keep reviewing the last good distances/figures; do not treat the session as failed.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- barmesh_richardson — classical Richardson / GCI on scalar QoIs (one mesh family per CSV: uniform OR graded, not mixed)
|
|
8
|
+
When reconnecting (BARIVIA_API_KEY / BARIVIA_API_URL), call barmesh_guide_workflow again for the plan-scoped workflow.`;
|
|
9
|
+
const OFFLINE_PREP = `API unreachable — cannot load the CFD mesh-prep recipe.
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
1. barmesh_prepare_mesh_data — recipe for the combined per-cell CSV (mesh_id + features + cell volume V)
|
|
14
|
-
2. barmesh_datasets(upload) → barmesh_datasets(preview)
|
|
15
|
-
3. barmesh_mesh_convergence and/or barmesh_richardson — returns job_id
|
|
16
|
-
- Multi-topology cavity studies: pass mesh_order interleaved (U20,G20,U40,G40,…) or preset=cavity
|
|
17
|
-
4. barmesh_training_monitor (visual MCP App) or barmesh_jobs(action=monitor) headless — poll until complete
|
|
18
|
-
5. barmesh_results(action=get) → barmesh_results_explorer for figures (mesh_convergence) or richardson_gci.csv summary (Richardson)
|
|
19
|
-
|
|
20
|
-
(Set BARIVIA_API_KEY / BARIVIA_API_URL when reconnecting.)`;
|
|
21
|
-
const OFFLINE_PREP = `barmesh mesh-data prep (offline summary; API unreachable):
|
|
22
|
-
Build ONE combined per-cell CSV across all meshes of the refinement study:
|
|
23
|
-
- one row per cell; a mesh label column (mesh_id); the physical channels you want compared (e.g. p, U_mag, k, log_epsilon — log-compress turbulence quantities); and a cell-volume column (V) for fingerprint weighting.
|
|
24
|
-
- keep the SAME feature columns across every mesh; pick the finest mesh as the reference.
|
|
25
|
-
- multi-topology (U + G): pass mesh_order coarse→fine interleaved by refinement (U20,G20,U40,G40,U80,G80,U160,G160) or use preset=cavity.
|
|
26
|
-
Then: barmesh_datasets(upload) -> barmesh_mesh_convergence. (Set BARIVIA_API_KEY / BARIVIA_API_URL.)`;
|
|
11
|
+
Retry when BARIVIA_API_KEY / BARIVIA_API_URL reconnect, then call barmesh_prepare_mesh_data again (entitlement-scoped).`;
|
|
27
12
|
export function registerGuideTool(server) {
|
|
28
13
|
registerAuditedTool(server, "barmesh_guide_workflow", `Get the barmesh CFD mesh-convergence workflow and tool map from the API (tier-scoped).
|
|
29
14
|
|
|
@@ -49,7 +34,7 @@ UNAVAILABLE: If the API is down, this tool returns a calm offline summary — sa
|
|
|
49
34
|
|
|
50
35
|
BEST FOR: Before barmesh_datasets(upload) — tells you which physical channels to extract, how to label meshes (mesh_id), the cell-volume column (V), and how to pick the reference mesh.
|
|
51
36
|
NOT FOR: Submitting jobs — after preparing the CSV, use barmesh_datasets(upload) then barmesh_mesh_convergence.
|
|
52
|
-
COMMON MISTAKES:
|
|
37
|
+
COMMON MISTAKES: missing volume column; mismatched channels across meshes; multi-topology order issues — full recipe is in the API response when entitled.`, {}, async () => {
|
|
53
38
|
try {
|
|
54
39
|
const data = (await apiCall("GET", "/v1/cfd/prep"));
|
|
55
40
|
return textResult({ recipe: data.recipe, entitled: data.entitled });
|
package/dist/tools/jobs.js
CHANGED
|
@@ -15,18 +15,19 @@ export function registerJobsTool(server) {
|
|
|
15
15
|
| status | One-shot progress check |
|
|
16
16
|
| list | Slim recent jobs (filter/page with limit/cursor/status/job_type/has_results) |
|
|
17
17
|
| update | Edit label / description / tags |
|
|
18
|
+
| delete | Permanently remove a job and its result files |
|
|
18
19
|
| inventory | Markdown overview of datasets + recent jobs |
|
|
19
20
|
|
|
20
|
-
BEST FOR: action=monitor after submit (one call, throttled snapshots — preferred for agents). action=status for a single one-shot check. action=inventory for a durable org catalog.
|
|
21
|
+
BEST FOR: action=monitor after submit (one call, throttled snapshots — preferred for agents). action=status for a single one-shot check. action=inventory for a durable org catalog. action=delete for cleanup by known job_id (do not deep-page list to find IDs).
|
|
21
22
|
MONITOR MODES: barmesh_training_monitor — default visual MCP App (live curves, post-hoc review on completed jobs). barmesh_jobs(action=monitor) — headless blocking snapshots for agents (live or post-hoc review; attaches learning_curve.png when already completed).
|
|
22
23
|
ASYNC PROTOCOL: monitor blocks server-side until completed/failed or block_until timeout (default ${DEFAULT_BLOCK_UNTIL_SEC}s, poll every ${DEFAULT_POLL_INTERVAL_SEC}s). status is one-shot; poll every 10-20s manually if not using monitor. When status=completed, call barmesh_results(action=get, job_id=...) then barmesh_results_explorer(job_id=...).
|
|
23
24
|
LIST: slim by default (label,id,status,job_type,dataset_id,created_at,result_ref,description,tags — no fat params). has_results=true is the results catalog.
|
|
24
25
|
ESCALATION: status=failed returns an error message and (when available) a failure_stage; read it before retrying.
|
|
25
26
|
UNAVAILABLE: If the mesh analysis API is temporarily unavailable, say so plainly, pause (proxy already burst-retried; wait ~retry_after_sec), and keep reviewing the last good distances/figures — do not treat the demo as failed. Suggest retry later.`, {
|
|
26
27
|
action: z
|
|
27
|
-
.enum(["status", "monitor", "list", "update", "inventory"])
|
|
28
|
-
.describe("status: one-shot check; monitor: block until terminal; list: slim paginated jobs; update: metadata; inventory: datasets+jobs overview"),
|
|
29
|
-
job_id: z.string().optional().describe("Job ID (required for status, monitor, update)"),
|
|
28
|
+
.enum(["status", "monitor", "list", "update", "delete", "inventory"])
|
|
29
|
+
.describe("status: one-shot check; monitor: block until terminal; list: slim paginated jobs; update: metadata; delete: remove job+results; inventory: datasets+jobs overview"),
|
|
30
|
+
job_id: z.string().optional().describe("Job ID (required for status, monitor, update, delete)"),
|
|
30
31
|
block_until_sec: z
|
|
31
32
|
.number()
|
|
32
33
|
.int()
|
|
@@ -123,6 +124,12 @@ UNAVAILABLE: If the mesh analysis API is temporarily unavailable, say so plainly
|
|
|
123
124
|
const data = await apiCall("PATCH", `/v1/jobs/${job_id}`, body);
|
|
124
125
|
return textResult(data);
|
|
125
126
|
}
|
|
127
|
+
if (action === "delete") {
|
|
128
|
+
if (!job_id)
|
|
129
|
+
throw new Error("barmesh_jobs(delete) requires job_id.");
|
|
130
|
+
const data = await apiCall("DELETE", `/v1/jobs/${job_id}`);
|
|
131
|
+
return textResult(data);
|
|
132
|
+
}
|
|
126
133
|
if (action === "inventory") {
|
|
127
134
|
const jobLimit = limit && limit > 0 ? Math.min(Math.floor(limit), 100) : 50;
|
|
128
135
|
const [datasetsRaw, jobsRaw] = await Promise.all([
|
package/dist/tools/results.js
CHANGED
|
@@ -235,7 +235,8 @@ NOT FOR: Submitting jobs.`, {
|
|
|
235
235
|
}
|
|
236
236
|
toDownload = [...new Set(toDownload)];
|
|
237
237
|
let resolvedDir = sandboxPath(folder, await getWorkspaceRootAsync(server));
|
|
238
|
-
const
|
|
238
|
+
const prefix = isRichardsonJob(summary) ? "cfd_richardson" : "cfd_mesh_convergence";
|
|
239
|
+
const jobSubfolder = `${prefix}_${jobLabel ?? job_id}`.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
239
240
|
resolvedDir = path.join(resolvedDir, jobSubfolder);
|
|
240
241
|
await fs.mkdir(resolvedDir, { recursive: true });
|
|
241
242
|
const saved = [];
|