@agimon-ai/doompi-workflow 0.0.1-alpha.113 → 0.0.1-alpha.115
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/apiContracts.cjs +27 -1
- package/dist/apiContracts.cjs.map +1 -1
- package/dist/apiContracts.mjs +27 -1
- package/dist/apiContracts.mjs.map +1 -1
- package/dist/extensions/server.cjs +144 -31
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.mjs +144 -31
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/extensions/web.mjs +180 -55
- package/dist/extensions/web.mjs.map +1 -1
- package/package.json +13 -13
- package/src/types/webWorkflowTerminal.ts +5 -0
- package/src/types/webWorkflows.ts +2 -1
package/dist/extensions/web.mjs
CHANGED
|
@@ -1136,9 +1136,15 @@ var apiRoutes_default = defineApiRoutes({
|
|
|
1136
1136
|
path: "/launch",
|
|
1137
1137
|
response: apiResponse()
|
|
1138
1138
|
},
|
|
1139
|
+
openRunDirectory: {
|
|
1140
|
+
method: "POST",
|
|
1141
|
+
path: `${RUN}/open-directory`,
|
|
1142
|
+
response: apiResponse()
|
|
1143
|
+
},
|
|
1139
1144
|
artifacts: {
|
|
1140
1145
|
method: "GET",
|
|
1141
1146
|
path: `${RUN}/artifacts`,
|
|
1147
|
+
query: ["directory"],
|
|
1142
1148
|
response: apiResponse()
|
|
1143
1149
|
},
|
|
1144
1150
|
/** One artifact: its metadata and text, or its raw bytes under `raw=1`. */
|
|
@@ -1331,8 +1337,17 @@ async function steerStep(workspace, runKey, message, sessionId, step) {
|
|
|
1331
1337
|
if (result.data.delivered === true) return { result: { delivered: true } };
|
|
1332
1338
|
return { error: "The workflow hub returned an invalid steer response." };
|
|
1333
1339
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1340
|
+
/** Opens the current registry folder, never a browser-supplied path. */
|
|
1341
|
+
async function openRunDirectory(workspace, runKey, sessionId) {
|
|
1342
|
+
const result = await scoped(sessionId).openRunDirectory({ params: runParams(workspace, runKey) });
|
|
1343
|
+
if (!result.ok) return { error: messageOf(result, "The run folder could not be opened in Finder.") };
|
|
1344
|
+
return result.data.opened === true ? {} : { error: "The workflow hub returned an invalid folder response." };
|
|
1345
|
+
}
|
|
1346
|
+
async function fetchArtifacts(workspace, runKey, sessionId, directory) {
|
|
1347
|
+
const result = await scoped(sessionId).artifacts({
|
|
1348
|
+
params: runParams(workspace, runKey),
|
|
1349
|
+
query: { directory }
|
|
1350
|
+
});
|
|
1336
1351
|
if (!result.ok) return { error: messageOf(result, "This run has no directory to read.") };
|
|
1337
1352
|
return { artifacts: result.data };
|
|
1338
1353
|
}
|
|
@@ -1700,13 +1715,7 @@ function ArtifactViewerPanel({ workspace, runKey, path, sessionId }) {
|
|
|
1700
1715
|
]
|
|
1701
1716
|
});
|
|
1702
1717
|
}
|
|
1703
|
-
/**
|
|
1704
|
-
* The run directory, as the run pane's second tab.
|
|
1705
|
-
*
|
|
1706
|
-
* The workflow's own declaration leads, in the order it was written and
|
|
1707
|
-
* including entries no job has produced yet, because the declaration is what
|
|
1708
|
-
* the run is for. What the folder holds besides that follows.
|
|
1709
|
-
*/
|
|
1718
|
+
/** The run's file tree, including declared outputs that have not been written yet. */
|
|
1710
1719
|
function ArtifactsPane({ run, sessionId, onOpen }) {
|
|
1711
1720
|
const [listing, setListing] = useState();
|
|
1712
1721
|
const [error, setError] = useState();
|
|
@@ -1743,56 +1752,163 @@ function ArtifactsPane({ run, sessionId, onOpen }) {
|
|
|
1743
1752
|
return /* @__PURE__ */ jsx(ArtifactList, {
|
|
1744
1753
|
listing,
|
|
1745
1754
|
error,
|
|
1746
|
-
|
|
1755
|
+
onLoadDirectory: (directory) => fetchArtifacts(run.workspace, run.runKey, sessionId, directory),
|
|
1756
|
+
onOpen,
|
|
1757
|
+
onOpenDirectory: () => openRunDirectory(run.workspace, run.runKey, sessionId)
|
|
1747
1758
|
});
|
|
1748
1759
|
}
|
|
1749
|
-
/**
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1760
|
+
/** Directory-first file tree with declared output metadata and lazy folder reads. */
|
|
1761
|
+
function ArtifactList({ listing, error, onOpen, onOpenDirectory, onLoadDirectory }) {
|
|
1762
|
+
const [opening, setOpening] = useState(false);
|
|
1763
|
+
const [openError, setOpenError] = useState();
|
|
1764
|
+
const openDirectory = async () => {
|
|
1765
|
+
if (onOpenDirectory === void 0 || opening) return;
|
|
1766
|
+
setOpening(true);
|
|
1767
|
+
setOpenError(void 0);
|
|
1768
|
+
try {
|
|
1769
|
+
const result = await onOpenDirectory();
|
|
1770
|
+
setOpenError(result.error);
|
|
1771
|
+
} catch {
|
|
1772
|
+
setOpenError("The run folder could not be opened in Finder.");
|
|
1773
|
+
} finally {
|
|
1774
|
+
setOpening(false);
|
|
1775
|
+
}
|
|
1776
|
+
};
|
|
1777
|
+
const [expanded, setExpanded] = useState(/* @__PURE__ */ new Set());
|
|
1778
|
+
const [loaded, setLoaded] = useState({});
|
|
1779
|
+
const [folderErrors, setFolderErrors] = useState({});
|
|
1780
|
+
const [loading, setLoading] = useState(/* @__PURE__ */ new Set());
|
|
1781
|
+
const entries = /* @__PURE__ */ new Map();
|
|
1782
|
+
for (const entry of [...Object.values(loaded).flat(), ...listing?.artifacts ?? []]) entries.set(entry.path, entry);
|
|
1783
|
+
for (const entry of entries.values()) {
|
|
1784
|
+
const parts = entry.path.split("/");
|
|
1785
|
+
parts.pop();
|
|
1786
|
+
while (parts.length > 0) {
|
|
1787
|
+
const parent = parts.join("/");
|
|
1788
|
+
if (!entries.has(parent)) entries.set(parent, {
|
|
1789
|
+
path: parent,
|
|
1790
|
+
kind: "directory",
|
|
1791
|
+
description: "",
|
|
1792
|
+
producedBy: [],
|
|
1793
|
+
declared: false,
|
|
1794
|
+
state: entry.state === "pending" ? "pending" : "written"
|
|
1795
|
+
});
|
|
1796
|
+
parts.pop();
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
const toggle = async (entry) => {
|
|
1800
|
+
const closing = expanded.has(entry.path);
|
|
1801
|
+
setExpanded((current) => {
|
|
1802
|
+
const next = new Set(current);
|
|
1803
|
+
if (closing) next.delete(entry.path);
|
|
1804
|
+
else next.add(entry.path);
|
|
1805
|
+
return next;
|
|
1806
|
+
});
|
|
1807
|
+
if (closing || loading.has(entry.path) || onLoadDirectory === void 0 || entry.state === "pending") return;
|
|
1808
|
+
setLoading((current) => new Set(current).add(entry.path));
|
|
1809
|
+
try {
|
|
1810
|
+
const result = await onLoadDirectory(entry.path);
|
|
1811
|
+
if ("error" in result) setFolderErrors((current) => ({
|
|
1812
|
+
...current,
|
|
1813
|
+
[entry.path]: result.error
|
|
1814
|
+
}));
|
|
1815
|
+
else {
|
|
1816
|
+
setLoaded((current) => ({
|
|
1817
|
+
...current,
|
|
1818
|
+
[entry.path]: result.artifacts.artifacts
|
|
1819
|
+
}));
|
|
1820
|
+
setFolderErrors((current) => ({
|
|
1821
|
+
...current,
|
|
1822
|
+
[entry.path]: ""
|
|
1823
|
+
}));
|
|
1824
|
+
}
|
|
1825
|
+
} catch {
|
|
1826
|
+
setFolderErrors((current) => ({
|
|
1827
|
+
...current,
|
|
1828
|
+
[entry.path]: "The folder cannot be read."
|
|
1829
|
+
}));
|
|
1830
|
+
} finally {
|
|
1831
|
+
setLoading((current) => {
|
|
1832
|
+
const next = new Set(current);
|
|
1833
|
+
next.delete(entry.path);
|
|
1834
|
+
return next;
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
};
|
|
1838
|
+
const renderRow = (entry) => {
|
|
1757
1839
|
const glyph = STATE_GLYPH[entry.state];
|
|
1758
|
-
const openable = entry.kind === "
|
|
1840
|
+
const openable = entry.kind === "directory" || entry.state !== "pending";
|
|
1759
1841
|
return /* @__PURE__ */ jsxs("button", {
|
|
1760
1842
|
type: "button",
|
|
1761
1843
|
"data-testid": `artifact-row-${entry.path}`,
|
|
1762
1844
|
"data-artifact-state": entry.state,
|
|
1763
1845
|
disabled: !openable,
|
|
1764
|
-
|
|
1846
|
+
"aria-expanded": entry.kind === "directory" ? expanded.has(entry.path) : void 0,
|
|
1847
|
+
onClick: () => entry.kind === "directory" ? void toggle(entry) : onOpen(entry.path),
|
|
1765
1848
|
className: `flex flex-col gap-0.5 px-3 py-1.5 text-left ${openable ? "cursor-pointer hover:bg-doom-panel" : "cursor-default"}`,
|
|
1766
1849
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
1767
1850
|
className: "flex min-w-0 items-center gap-2",
|
|
1768
1851
|
children: [
|
|
1769
1852
|
/* @__PURE__ */ jsx("span", {
|
|
1770
1853
|
className: `w-3 shrink-0 text-xs ${glyph.className}`,
|
|
1771
|
-
children: glyph.glyph
|
|
1854
|
+
children: entry.kind === "directory" ? expanded.has(entry.path) ? "▾" : "▸" : glyph.glyph
|
|
1772
1855
|
}),
|
|
1773
|
-
/* @__PURE__ */
|
|
1856
|
+
/* @__PURE__ */ jsxs("span", {
|
|
1774
1857
|
title: entry.path,
|
|
1775
|
-
className: `min-w-0 truncate text-xs ${entry.state === "pending" ? "text-doom-dim" : "text-doom-hi"}`,
|
|
1776
|
-
children: entry.path
|
|
1858
|
+
className: `min-w-0 flex-1 truncate text-xs ${entry.state === "pending" ? "text-doom-dim" : "text-doom-hi"}`,
|
|
1859
|
+
children: [entry.path.split("/").pop(), entry.kind === "directory" ? "/" : ""]
|
|
1777
1860
|
}),
|
|
1778
|
-
|
|
1861
|
+
/* @__PURE__ */ jsx("span", {
|
|
1862
|
+
className: "shrink-0 text-2xs text-doom-faint",
|
|
1863
|
+
children: entry.state === "pending" ? "pending" : entry.kind === "directory" ? "" : [formatSize(entry.size), formatWhen(entry.modifiedAt)].filter(Boolean).join(" · ")
|
|
1864
|
+
})
|
|
1865
|
+
]
|
|
1866
|
+
}), entry.declared || entry.producedBy.length > 0 || entry.description !== "" ? /* @__PURE__ */ jsxs("span", {
|
|
1867
|
+
className: "flex min-w-0 items-center gap-1 pl-5 text-2xs text-doom-faint",
|
|
1868
|
+
children: [
|
|
1869
|
+
entry.declared ? /* @__PURE__ */ jsx(Badge, {
|
|
1779
1870
|
size: "xs",
|
|
1780
1871
|
tone: "violet",
|
|
1781
1872
|
className: "shrink-0",
|
|
1873
|
+
children: "declared"
|
|
1874
|
+
}) : null,
|
|
1875
|
+
entry.producedBy.length === 0 ? null : /* @__PURE__ */ jsx("span", {
|
|
1876
|
+
title: entry.producedBy.join(", "),
|
|
1877
|
+
className: "truncate",
|
|
1782
1878
|
children: entry.producedBy.join(", ")
|
|
1783
1879
|
}),
|
|
1784
|
-
/* @__PURE__ */ jsx("span", {
|
|
1785
|
-
|
|
1786
|
-
className: "
|
|
1787
|
-
children: entry.
|
|
1880
|
+
entry.description === "" ? null : /* @__PURE__ */ jsx("span", {
|
|
1881
|
+
title: entry.description,
|
|
1882
|
+
className: "truncate",
|
|
1883
|
+
children: entry.description
|
|
1788
1884
|
})
|
|
1789
1885
|
]
|
|
1790
|
-
})
|
|
1791
|
-
className: "truncate pl-5 text-2xs text-doom-faint",
|
|
1792
|
-
children: entry.description
|
|
1793
|
-
})]
|
|
1886
|
+
}) : null]
|
|
1794
1887
|
});
|
|
1795
1888
|
};
|
|
1889
|
+
const renderChildren = (parent) => {
|
|
1890
|
+
return [...entries.values()].filter((entry) => {
|
|
1891
|
+
const cut = entry.path.lastIndexOf("/");
|
|
1892
|
+
return (cut === -1 ? "" : entry.path.slice(0, cut)) === parent;
|
|
1893
|
+
}).sort((left, right) => Number(right.kind === "directory") - Number(left.kind === "directory") || left.path.localeCompare(right.path)).map((entry) => /* @__PURE__ */ jsxs("div", {
|
|
1894
|
+
className: "flex flex-col",
|
|
1895
|
+
children: [renderRow(entry), entry.kind === "directory" && expanded.has(entry.path) ? /* @__PURE__ */ jsxs("div", {
|
|
1896
|
+
className: "ml-3 border-l border-doom-border pl-1",
|
|
1897
|
+
children: [
|
|
1898
|
+
loading.has(entry.path) ? /* @__PURE__ */ jsx("span", {
|
|
1899
|
+
className: "px-3 text-xs text-doom-faint",
|
|
1900
|
+
children: "loading…"
|
|
1901
|
+
}) : null,
|
|
1902
|
+
folderErrors[entry.path] ? /* @__PURE__ */ jsx("span", {
|
|
1903
|
+
role: "alert",
|
|
1904
|
+
className: "px-3 text-xs text-doom-yellow",
|
|
1905
|
+
children: folderErrors[entry.path]
|
|
1906
|
+
}) : null,
|
|
1907
|
+
renderChildren(entry.path)
|
|
1908
|
+
]
|
|
1909
|
+
}) : null]
|
|
1910
|
+
}, entry.path));
|
|
1911
|
+
};
|
|
1796
1912
|
return /* @__PURE__ */ jsxs("div", {
|
|
1797
1913
|
"data-testid": "artifacts-pane",
|
|
1798
1914
|
className: "flex min-h-0 flex-1 flex-col",
|
|
@@ -1802,20 +1918,28 @@ function ArtifactList({ listing, error, onOpen }) {
|
|
|
1802
1918
|
title: "the run directory cannot be read",
|
|
1803
1919
|
description: error
|
|
1804
1920
|
}) : null,
|
|
1805
|
-
|
|
1921
|
+
entries.size === 0 && error === void 0 ? /* @__PURE__ */ jsx(EmptyState, {
|
|
1806
1922
|
className: "py-4",
|
|
1807
1923
|
title: "nothing in the run directory yet"
|
|
1808
1924
|
}) : null,
|
|
1809
|
-
|
|
1810
|
-
className: "px-3
|
|
1811
|
-
children:
|
|
1812
|
-
})
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1925
|
+
listing?.description ? /* @__PURE__ */ jsx("span", {
|
|
1926
|
+
className: "px-3 py-2 text-2xs text-doom-faint",
|
|
1927
|
+
children: listing.description
|
|
1928
|
+
}) : null,
|
|
1929
|
+
renderChildren(""),
|
|
1930
|
+
openError === void 0 ? null : /* @__PURE__ */ jsx("span", {
|
|
1931
|
+
role: "alert",
|
|
1932
|
+
className: "px-3 text-xs text-doom-yellow",
|
|
1933
|
+
children: openError
|
|
1934
|
+
}),
|
|
1935
|
+
listing === void 0 ? null : /* @__PURE__ */ jsx("button", {
|
|
1936
|
+
type: "button",
|
|
1937
|
+
"data-testid": "workflow-open-directory",
|
|
1938
|
+
"aria-label": "Open workflow folder in Finder",
|
|
1939
|
+
title: `${listing.runDir} (open in Finder)`,
|
|
1940
|
+
disabled: opening || onOpenDirectory === void 0,
|
|
1941
|
+
onClick: () => void openDirectory(),
|
|
1942
|
+
className: "truncate px-3 py-2 text-left text-2xs text-doom-faint hover:text-doom-hi disabled:cursor-default",
|
|
1819
1943
|
children: listing.runDir
|
|
1820
1944
|
})
|
|
1821
1945
|
]
|
|
@@ -2250,6 +2374,10 @@ function StepTerminal({ sessionId, target, run }) {
|
|
|
2250
2374
|
//#endregion
|
|
2251
2375
|
//#region src/extensions/workspaces/sessions/(frontend)/_components/WorkflowRunParts.tsx
|
|
2252
2376
|
const STATE_ICON = {
|
|
2377
|
+
pending: {
|
|
2378
|
+
glyph: "○",
|
|
2379
|
+
className: "text-doom-faint"
|
|
2380
|
+
},
|
|
2253
2381
|
running: {
|
|
2254
2382
|
glyph: "●",
|
|
2255
2383
|
className: "text-doom-blue"
|
|
@@ -2478,11 +2606,13 @@ function StepRow({ step, now, selected, onSelect }) {
|
|
|
2478
2606
|
const icon = STATE_ICON[step.status];
|
|
2479
2607
|
return /* @__PURE__ */ jsxs("button", {
|
|
2480
2608
|
type: "button",
|
|
2609
|
+
disabled: step.status === "pending",
|
|
2610
|
+
title: `${step.name}: ${step.status}`,
|
|
2481
2611
|
"data-testid": `step-row-${step.name}`,
|
|
2482
2612
|
"data-step-status": step.status,
|
|
2483
2613
|
"data-active": selected,
|
|
2484
2614
|
onClick: onSelect,
|
|
2485
|
-
className: cn("flex w-full cursor-pointer flex-col gap-0.5 rounded px-2 py-1.5 text-left hover:bg-doom-deep", selected && "bg-doom-tint-blue ring-1 ring-inset ring-doom-blue/40"),
|
|
2615
|
+
className: cn("flex w-full cursor-pointer flex-col gap-0.5 rounded px-2 py-1.5 text-left enabled:hover:bg-doom-deep disabled:cursor-default", selected && "bg-doom-tint-blue ring-1 ring-inset ring-doom-blue/40"),
|
|
2486
2616
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
2487
2617
|
className: "flex items-center gap-2",
|
|
2488
2618
|
children: [
|
|
@@ -2501,7 +2631,7 @@ function StepRow({ step, now, selected, onSelect }) {
|
|
|
2501
2631
|
}),
|
|
2502
2632
|
/* @__PURE__ */ jsx("span", {
|
|
2503
2633
|
className: "shrink-0 text-2xs text-doom-faint",
|
|
2504
|
-
children: spanDuration(step.startedAt, step.endedAt, now) ?? ""
|
|
2634
|
+
children: step.status === "pending" ? "pending" : spanDuration(step.startedAt, step.endedAt, now) ?? ""
|
|
2505
2635
|
})
|
|
2506
2636
|
]
|
|
2507
2637
|
}), step.reason === void 0 ? null : /* @__PURE__ */ jsx("span", {
|
|
@@ -2624,12 +2754,6 @@ function activeSteps(run) {
|
|
|
2624
2754
|
step
|
|
2625
2755
|
})));
|
|
2626
2756
|
}
|
|
2627
|
-
/** A job opens by default while it is moving or failed, so the step that needs a look is already showing. */
|
|
2628
|
-
function openByDefault(run, job) {
|
|
2629
|
-
if (run.position?.job === job.name) return true;
|
|
2630
|
-
if (ACTIVE_STATES.has(job.status) || job.status === "failed") return true;
|
|
2631
|
-
return job.steps.some((step) => ACTIVE_STATES.has(step.status) || step.status === "failed");
|
|
2632
|
-
}
|
|
2633
2757
|
function SectionHeading({ label, meta }) {
|
|
2634
2758
|
return /* @__PURE__ */ jsxs("div", {
|
|
2635
2759
|
className: "flex items-center px-4 pb-1 pt-3",
|
|
@@ -2785,7 +2909,7 @@ function JobTree({ run, now, onOpenStep }) {
|
|
|
2785
2909
|
}) : /* @__PURE__ */ jsx("div", {
|
|
2786
2910
|
className: "flex flex-col gap-0.5 px-2",
|
|
2787
2911
|
children: run.jobs.map((job) => {
|
|
2788
|
-
const open = toggled[job.name] ??
|
|
2912
|
+
const open = toggled[job.name] ?? true;
|
|
2789
2913
|
const icon = STATE_ICON[job.status];
|
|
2790
2914
|
const finished = job.steps.filter((step) => step.status === "completed" || step.status === "skipped").length;
|
|
2791
2915
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -2795,6 +2919,7 @@ function JobTree({ run, now, onOpenStep }) {
|
|
|
2795
2919
|
"data-testid": `job-row-${job.name}`,
|
|
2796
2920
|
"data-job-status": job.status,
|
|
2797
2921
|
"aria-expanded": open,
|
|
2922
|
+
title: `${job.name}: ${job.status}`,
|
|
2798
2923
|
onClick: () => setToggled((current) => ({
|
|
2799
2924
|
...current,
|
|
2800
2925
|
[job.name]: !open
|
|
@@ -2812,7 +2937,7 @@ function JobTree({ run, now, onOpenStep }) {
|
|
|
2812
2937
|
}),
|
|
2813
2938
|
/* @__PURE__ */ jsx("span", {
|
|
2814
2939
|
className: "shrink-0 text-2xs text-doom-faint",
|
|
2815
|
-
children: open || job.steps.length === 0 ? spanDuration(job.startedAt, job.endedAt, now) ?? "" : `${finished}/${job.steps.length} steps`
|
|
2940
|
+
children: job.status === "pending" ? "pending" : open || job.steps.length === 0 ? spanDuration(job.startedAt, job.endedAt, now) ?? "" : `${finished}/${job.steps.length} steps`
|
|
2816
2941
|
})
|
|
2817
2942
|
]
|
|
2818
2943
|
}), open && job.steps.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
@@ -2942,7 +3067,7 @@ function WorkflowDockPanel({ sessionId, openTransientTab }) {
|
|
|
2942
3067
|
run,
|
|
2943
3068
|
sessionId,
|
|
2944
3069
|
onOpen: (path) => openTransientTab(artifactTab(run, path))
|
|
2945
|
-
})
|
|
3070
|
+
}, `${workflowRunIdentity(run)}/${sessionId}`)
|
|
2946
3071
|
});
|
|
2947
3072
|
}
|
|
2948
3073
|
//#endregion
|