@versatly/workgraph 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-OTSECVE5.js → chunk-BJVBE7F4.js} +544 -9
- package/dist/{chunk-TOFSHG5S.js → chunk-NGRQ6T4O.js} +367 -60
- package/dist/cli.js +82 -2
- package/dist/index.d.ts +140 -2
- package/dist/index.js +4 -2
- package/dist/mcp-server.js +1 -1
- package/package.json +1 -1
|
@@ -3,13 +3,16 @@ import {
|
|
|
3
3
|
allClaims,
|
|
4
4
|
append,
|
|
5
5
|
checkpoint,
|
|
6
|
+
claim,
|
|
6
7
|
create,
|
|
7
8
|
createRun,
|
|
8
9
|
createThread,
|
|
10
|
+
done,
|
|
9
11
|
historyOf,
|
|
10
12
|
keywordSearch,
|
|
11
13
|
list,
|
|
12
14
|
listReadyThreads,
|
|
15
|
+
listReadyThreadsInSpace,
|
|
13
16
|
listRuns,
|
|
14
17
|
listTypes,
|
|
15
18
|
loadPolicyRegistry,
|
|
@@ -23,7 +26,7 @@ import {
|
|
|
23
26
|
saveRegistry,
|
|
24
27
|
stop,
|
|
25
28
|
update
|
|
26
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-BJVBE7F4.js";
|
|
27
30
|
|
|
28
31
|
// src/workspace.ts
|
|
29
32
|
var workspace_exports = {};
|
|
@@ -300,7 +303,7 @@ function renderCommandCenter(input) {
|
|
|
300
303
|
const claimsSection = [
|
|
301
304
|
"## Active Claims",
|
|
302
305
|
"",
|
|
303
|
-
...input.claims.length > 0 ? input.claims.map((
|
|
306
|
+
...input.claims.length > 0 ? input.claims.map((claim2) => `- ${claim2.owner} -> \`${claim2.target}\``) : ["- None"],
|
|
304
307
|
""
|
|
305
308
|
];
|
|
306
309
|
const blockedSection = [
|
|
@@ -360,8 +363,7 @@ function writeSkill(workspacePath, title, body, actor, options = {}) {
|
|
|
360
363
|
owner: options.owner ?? actor,
|
|
361
364
|
version: options.version ?? "0.1.0",
|
|
362
365
|
status,
|
|
363
|
-
distribution: options.distribution ?? "
|
|
364
|
-
tailscale_path: options.tailscalePath,
|
|
366
|
+
distribution: options.distribution ?? "shared-vault",
|
|
365
367
|
reviewers: options.reviewers ?? [],
|
|
366
368
|
depends_on: options.dependsOn ?? [],
|
|
367
369
|
tags: options.tags ?? []
|
|
@@ -376,8 +378,7 @@ function writeSkill(workspacePath, title, body, actor, options = {}) {
|
|
|
376
378
|
owner: options.owner ?? existing.fields.owner ?? actor,
|
|
377
379
|
version: options.version ?? existing.fields.version ?? "0.1.0",
|
|
378
380
|
status,
|
|
379
|
-
distribution: options.distribution ?? existing.fields.distribution ?? "
|
|
380
|
-
tailscale_path: options.tailscalePath ?? existing.fields.tailscale_path,
|
|
381
|
+
distribution: options.distribution ?? existing.fields.distribution ?? "shared-vault",
|
|
381
382
|
reviewers: options.reviewers ?? existing.fields.reviewers ?? [],
|
|
382
383
|
depends_on: options.dependsOn ?? existing.fields.depends_on ?? [],
|
|
383
384
|
tags: options.tags ?? existing.fields.tags ?? []
|
|
@@ -1610,6 +1611,311 @@ function supportedIntegrationList() {
|
|
|
1610
1611
|
return Object.keys(INTEGRATIONS).sort().join(", ");
|
|
1611
1612
|
}
|
|
1612
1613
|
|
|
1614
|
+
// src/swarm.ts
|
|
1615
|
+
var swarm_exports = {};
|
|
1616
|
+
__export(swarm_exports, {
|
|
1617
|
+
createPlanTemplate: () => createPlanTemplate,
|
|
1618
|
+
deployPlan: () => deployPlan,
|
|
1619
|
+
getSwarmStatus: () => getSwarmStatus,
|
|
1620
|
+
synthesize: () => synthesize,
|
|
1621
|
+
validatePlan: () => validatePlan,
|
|
1622
|
+
workerClaim: () => workerClaim,
|
|
1623
|
+
workerComplete: () => workerComplete,
|
|
1624
|
+
workerLoop: () => workerLoop
|
|
1625
|
+
});
|
|
1626
|
+
import * as fs7 from "fs";
|
|
1627
|
+
import * as path7 from "path";
|
|
1628
|
+
function createPlanTemplate(goal) {
|
|
1629
|
+
return {
|
|
1630
|
+
goal,
|
|
1631
|
+
tasks: [],
|
|
1632
|
+
phases: [],
|
|
1633
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1634
|
+
estimatedTotalMinutes: 0
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
function validatePlan(plan) {
|
|
1638
|
+
const errors = [];
|
|
1639
|
+
if (!plan.goal.title) errors.push("Goal title is required");
|
|
1640
|
+
if (plan.tasks.length === 0) errors.push("Plan has no tasks");
|
|
1641
|
+
if (plan.tasks.length > (plan.goal.maxTasks ?? 1e3)) {
|
|
1642
|
+
errors.push(`Plan has ${plan.tasks.length} tasks, exceeds max ${plan.goal.maxTasks ?? 1e3}`);
|
|
1643
|
+
}
|
|
1644
|
+
const taskTitles = new Set(plan.tasks.map((t) => t.title));
|
|
1645
|
+
for (const task of plan.tasks) {
|
|
1646
|
+
for (const dep of task.dependsOn ?? []) {
|
|
1647
|
+
if (!taskTitles.has(dep)) {
|
|
1648
|
+
errors.push(`Task "${task.title}" depends on unknown task "${dep}"`);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1653
|
+
const stack = /* @__PURE__ */ new Set();
|
|
1654
|
+
const depMap = /* @__PURE__ */ new Map();
|
|
1655
|
+
for (const task of plan.tasks) {
|
|
1656
|
+
depMap.set(task.title, task.dependsOn ?? []);
|
|
1657
|
+
}
|
|
1658
|
+
function hasCycle(node) {
|
|
1659
|
+
if (stack.has(node)) return true;
|
|
1660
|
+
if (visited.has(node)) return false;
|
|
1661
|
+
visited.add(node);
|
|
1662
|
+
stack.add(node);
|
|
1663
|
+
for (const dep of depMap.get(node) ?? []) {
|
|
1664
|
+
if (hasCycle(dep)) return true;
|
|
1665
|
+
}
|
|
1666
|
+
stack.delete(node);
|
|
1667
|
+
return false;
|
|
1668
|
+
}
|
|
1669
|
+
for (const task of plan.tasks) {
|
|
1670
|
+
visited.clear();
|
|
1671
|
+
stack.clear();
|
|
1672
|
+
if (hasCycle(task.title)) {
|
|
1673
|
+
errors.push(`Circular dependency detected involving "${task.title}"`);
|
|
1674
|
+
break;
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
for (const phase of plan.phases) {
|
|
1678
|
+
for (const idx of phase.taskIndices) {
|
|
1679
|
+
if (idx < 0 || idx >= plan.tasks.length) {
|
|
1680
|
+
errors.push(`Phase "${phase.name}" references invalid task index ${idx}`);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
return { valid: errors.length === 0, errors };
|
|
1685
|
+
}
|
|
1686
|
+
function deployPlan(workspacePath, plan, actor) {
|
|
1687
|
+
const validation = validatePlan(plan);
|
|
1688
|
+
if (!validation.valid) {
|
|
1689
|
+
throw new Error(`Invalid plan: ${validation.errors.join("; ")}`);
|
|
1690
|
+
}
|
|
1691
|
+
const spaceSlug = slugify(`swarm-${plan.goal.title}`);
|
|
1692
|
+
const spacePath = path7.join("spaces", `${spaceSlug}.md`);
|
|
1693
|
+
const spaceFullPath = path7.join(workspacePath, spacePath);
|
|
1694
|
+
if (!fs7.existsSync(spaceFullPath)) {
|
|
1695
|
+
const spaceDir = path7.join(workspacePath, "spaces");
|
|
1696
|
+
fs7.mkdirSync(spaceDir, { recursive: true });
|
|
1697
|
+
const spaceFrontmatter = [
|
|
1698
|
+
"---",
|
|
1699
|
+
`title: "Swarm: ${plan.goal.title}"`,
|
|
1700
|
+
`status: active`,
|
|
1701
|
+
`created: '${(/* @__PURE__ */ new Date()).toISOString()}'`,
|
|
1702
|
+
`updated: '${(/* @__PURE__ */ new Date()).toISOString()}'`,
|
|
1703
|
+
"---",
|
|
1704
|
+
"",
|
|
1705
|
+
`# Swarm Space: ${plan.goal.title}`,
|
|
1706
|
+
"",
|
|
1707
|
+
plan.goal.description,
|
|
1708
|
+
"",
|
|
1709
|
+
`Total tasks: ${plan.tasks.length}`
|
|
1710
|
+
].join("\n");
|
|
1711
|
+
fs7.writeFileSync(spaceFullPath, spaceFrontmatter);
|
|
1712
|
+
}
|
|
1713
|
+
const threadPaths = [];
|
|
1714
|
+
const slugMap = /* @__PURE__ */ new Map();
|
|
1715
|
+
for (const task of plan.tasks) {
|
|
1716
|
+
const taskSlug = slugify(task.title);
|
|
1717
|
+
slugMap.set(task.title, taskSlug);
|
|
1718
|
+
}
|
|
1719
|
+
for (const task of plan.tasks) {
|
|
1720
|
+
const taskSlug = slugMap.get(task.title);
|
|
1721
|
+
let body = `# ${task.title}
|
|
1722
|
+
|
|
1723
|
+
${task.description}
|
|
1724
|
+
`;
|
|
1725
|
+
if (task.dependsOn && task.dependsOn.length > 0) {
|
|
1726
|
+
body += `
|
|
1727
|
+
## Dependencies
|
|
1728
|
+
`;
|
|
1729
|
+
for (const dep of task.dependsOn) {
|
|
1730
|
+
const depSlug = slugMap.get(dep);
|
|
1731
|
+
if (depSlug) {
|
|
1732
|
+
body += `- [[${depSlug}]]
|
|
1733
|
+
`;
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
body += `
|
|
1738
|
+
## Output
|
|
1739
|
+
|
|
1740
|
+
_Agent writes result here._
|
|
1741
|
+
`;
|
|
1742
|
+
if (task.tags && task.tags.length > 0) {
|
|
1743
|
+
body += `
|
|
1744
|
+
Tags: ${task.tags.join(", ")}
|
|
1745
|
+
`;
|
|
1746
|
+
}
|
|
1747
|
+
const created = createThread(workspacePath, task.title, body, actor, {
|
|
1748
|
+
priority: task.priority,
|
|
1749
|
+
space: `spaces/${spaceSlug}`
|
|
1750
|
+
});
|
|
1751
|
+
threadPaths.push(created.path);
|
|
1752
|
+
}
|
|
1753
|
+
const deployment = {
|
|
1754
|
+
planPath: path7.join(".workgraph", `swarm-${spaceSlug}.json`),
|
|
1755
|
+
workspacePath,
|
|
1756
|
+
threadPaths,
|
|
1757
|
+
spaceSlug,
|
|
1758
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1759
|
+
status: "deployed"
|
|
1760
|
+
};
|
|
1761
|
+
const manifestPath = path7.join(workspacePath, deployment.planPath);
|
|
1762
|
+
fs7.mkdirSync(path7.dirname(manifestPath), { recursive: true });
|
|
1763
|
+
fs7.writeFileSync(manifestPath, JSON.stringify({ plan, deployment }, null, 2));
|
|
1764
|
+
append(workspacePath, actor, "create", deployment.planPath, "swarm");
|
|
1765
|
+
return deployment;
|
|
1766
|
+
}
|
|
1767
|
+
function getSwarmStatus(workspacePath, spaceSlug) {
|
|
1768
|
+
const manifestPath = path7.join(workspacePath, ".workgraph", `swarm-${spaceSlug}.json`);
|
|
1769
|
+
if (!fs7.existsSync(manifestPath)) {
|
|
1770
|
+
throw new Error(`No swarm deployment found for space "${spaceSlug}"`);
|
|
1771
|
+
}
|
|
1772
|
+
const manifest = JSON.parse(fs7.readFileSync(manifestPath, "utf-8"));
|
|
1773
|
+
const deployment = manifest.deployment;
|
|
1774
|
+
const threads = [];
|
|
1775
|
+
let claimed = 0;
|
|
1776
|
+
let done2 = 0;
|
|
1777
|
+
let blocked = 0;
|
|
1778
|
+
let open = 0;
|
|
1779
|
+
for (const threadPath of deployment.threadPaths) {
|
|
1780
|
+
const t = read(workspacePath, threadPath);
|
|
1781
|
+
if (!t) continue;
|
|
1782
|
+
const status = String(t.fields.status ?? "open");
|
|
1783
|
+
const threadInfo = {
|
|
1784
|
+
path: threadPath,
|
|
1785
|
+
title: String(t.fields.title ?? ""),
|
|
1786
|
+
status,
|
|
1787
|
+
owner: t.fields.owner ? String(t.fields.owner) : void 0,
|
|
1788
|
+
priority: String(t.fields.priority ?? "medium")
|
|
1789
|
+
};
|
|
1790
|
+
threads.push(threadInfo);
|
|
1791
|
+
if (status === "done") done2++;
|
|
1792
|
+
else if (status === "active") claimed++;
|
|
1793
|
+
else if (status === "blocked") blocked++;
|
|
1794
|
+
else open++;
|
|
1795
|
+
}
|
|
1796
|
+
const total = deployment.threadPaths.length;
|
|
1797
|
+
const readyToClaim = open;
|
|
1798
|
+
const percentComplete = total > 0 ? Math.round(done2 / total * 100) : 0;
|
|
1799
|
+
if (done2 === total) deployment.status = "done";
|
|
1800
|
+
else if (claimed > 0 || done2 > 0) deployment.status = "running";
|
|
1801
|
+
return {
|
|
1802
|
+
deployment,
|
|
1803
|
+
total,
|
|
1804
|
+
claimed,
|
|
1805
|
+
done: done2,
|
|
1806
|
+
blocked,
|
|
1807
|
+
open,
|
|
1808
|
+
readyToClaim,
|
|
1809
|
+
percentComplete,
|
|
1810
|
+
threads
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1813
|
+
function workerClaim(workspacePath, spaceSlug, agent) {
|
|
1814
|
+
const ready = listReadyThreadsInSpace(workspacePath, `spaces/${spaceSlug}`);
|
|
1815
|
+
if (ready.length === 0) return null;
|
|
1816
|
+
const priorityOrder = {
|
|
1817
|
+
critical: 0,
|
|
1818
|
+
high: 1,
|
|
1819
|
+
medium: 2,
|
|
1820
|
+
low: 3
|
|
1821
|
+
};
|
|
1822
|
+
ready.sort((a, b) => {
|
|
1823
|
+
const aPri = priorityOrder[String(a.fields.priority)] ?? 2;
|
|
1824
|
+
const bPri = priorityOrder[String(b.fields.priority)] ?? 2;
|
|
1825
|
+
return aPri - bPri;
|
|
1826
|
+
});
|
|
1827
|
+
const target = ready[0];
|
|
1828
|
+
return claim(workspacePath, target.path, agent);
|
|
1829
|
+
}
|
|
1830
|
+
function workerComplete(workspacePath, threadPath, agent, result) {
|
|
1831
|
+
const t = read(workspacePath, threadPath);
|
|
1832
|
+
if (!t) throw new Error(`Thread not found: ${threadPath}`);
|
|
1833
|
+
const currentBody = t.body ?? "";
|
|
1834
|
+
const updatedBody = currentBody.replace(
|
|
1835
|
+
"_Agent writes result here._",
|
|
1836
|
+
result
|
|
1837
|
+
);
|
|
1838
|
+
return done(workspacePath, threadPath, agent, updatedBody);
|
|
1839
|
+
}
|
|
1840
|
+
async function workerLoop(workspacePath, spaceSlug, agent, workFn, options) {
|
|
1841
|
+
let completed = 0;
|
|
1842
|
+
let errors = 0;
|
|
1843
|
+
const maxTasks = options?.maxTasks ?? Infinity;
|
|
1844
|
+
const delayMs = options?.delayMs ?? 1e3;
|
|
1845
|
+
while (completed + errors < maxTasks) {
|
|
1846
|
+
const claimed = workerClaim(workspacePath, spaceSlug, agent);
|
|
1847
|
+
if (!claimed) break;
|
|
1848
|
+
try {
|
|
1849
|
+
const result = await workFn(claimed);
|
|
1850
|
+
workerComplete(workspacePath, claimed.path, agent, result);
|
|
1851
|
+
completed++;
|
|
1852
|
+
} catch (err) {
|
|
1853
|
+
errors++;
|
|
1854
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1855
|
+
try {
|
|
1856
|
+
update(workspacePath, claimed.path, {
|
|
1857
|
+
status: "blocked"
|
|
1858
|
+
}, `Error: ${errorMsg}`, agent);
|
|
1859
|
+
} catch {
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
if (delayMs > 0) {
|
|
1863
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
return { completed, errors };
|
|
1867
|
+
}
|
|
1868
|
+
function synthesize(workspacePath, spaceSlug) {
|
|
1869
|
+
const status = getSwarmStatus(workspacePath, spaceSlug);
|
|
1870
|
+
const sections = [];
|
|
1871
|
+
const manifestPath = path7.join(workspacePath, ".workgraph", `swarm-${spaceSlug}.json`);
|
|
1872
|
+
const manifest = JSON.parse(fs7.readFileSync(manifestPath, "utf-8"));
|
|
1873
|
+
const plan = manifest.plan;
|
|
1874
|
+
sections.push(`# ${plan.goal.title}
|
|
1875
|
+
`);
|
|
1876
|
+
sections.push(`${plan.goal.description}
|
|
1877
|
+
`);
|
|
1878
|
+
sections.push(`---
|
|
1879
|
+
`);
|
|
1880
|
+
for (const threadInfo of status.threads) {
|
|
1881
|
+
const t = read(workspacePath, threadInfo.path);
|
|
1882
|
+
if (!t) continue;
|
|
1883
|
+
if (threadInfo.status !== "done") {
|
|
1884
|
+
sections.push(`## [PENDING] ${threadInfo.title}
|
|
1885
|
+
|
|
1886
|
+
_Not yet completed._
|
|
1887
|
+
`);
|
|
1888
|
+
continue;
|
|
1889
|
+
}
|
|
1890
|
+
const body = t.body ?? "";
|
|
1891
|
+
const result = body.replace(/^#\s+.*\n/, "").trim();
|
|
1892
|
+
if (result && result !== "_Agent writes result here._") {
|
|
1893
|
+
sections.push(`## ${threadInfo.title}
|
|
1894
|
+
|
|
1895
|
+
${result}
|
|
1896
|
+
`);
|
|
1897
|
+
} else {
|
|
1898
|
+
sections.push(`## ${threadInfo.title}
|
|
1899
|
+
|
|
1900
|
+
_Completed but no output found._
|
|
1901
|
+
`);
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
sections.push(`
|
|
1905
|
+
---
|
|
1906
|
+
`);
|
|
1907
|
+
sections.push(`*Generated from swarm "${plan.goal.title}" \u2014 ${status.done}/${status.total} tasks completed.*
|
|
1908
|
+
`);
|
|
1909
|
+
return {
|
|
1910
|
+
markdown: sections.join("\n"),
|
|
1911
|
+
completedCount: status.done,
|
|
1912
|
+
totalCount: status.total
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
function slugify(text) {
|
|
1916
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").substring(0, 80);
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1613
1919
|
// src/diagnostics/index.ts
|
|
1614
1920
|
var diagnostics_exports = {};
|
|
1615
1921
|
__export(diagnostics_exports, {
|
|
@@ -1625,8 +1931,8 @@ __export(diagnostics_exports, {
|
|
|
1625
1931
|
});
|
|
1626
1932
|
|
|
1627
1933
|
// src/diagnostics/doctor.ts
|
|
1628
|
-
import
|
|
1629
|
-
import
|
|
1934
|
+
import fs8 from "fs";
|
|
1935
|
+
import path9 from "path";
|
|
1630
1936
|
import YAML2 from "yaml";
|
|
1631
1937
|
|
|
1632
1938
|
// src/diagnostics/format.ts
|
|
@@ -1690,7 +1996,7 @@ function inferPrimitiveTypeFromPath(targetPath) {
|
|
|
1690
1996
|
}
|
|
1691
1997
|
|
|
1692
1998
|
// src/diagnostics/primitives.ts
|
|
1693
|
-
import
|
|
1999
|
+
import path8 from "path";
|
|
1694
2000
|
function loadPrimitiveInventory(workspacePath) {
|
|
1695
2001
|
const registry = loadRegistry(workspacePath);
|
|
1696
2002
|
const allPrimitives = queryPrimitives(workspacePath);
|
|
@@ -1708,7 +2014,7 @@ function loadPrimitiveInventory(workspacePath) {
|
|
|
1708
2014
|
const requiredFields = Object.entries(typeDef?.fields ?? {}).filter(([, fieldDef]) => fieldDef.required === true).map(([fieldName]) => fieldName);
|
|
1709
2015
|
const presentCount = requiredFields.filter((fieldName) => hasRequiredValue(instance.fields[fieldName])).length;
|
|
1710
2016
|
const frontmatterCompleteness = requiredFields.length === 0 ? 1 : presentCount / requiredFields.length;
|
|
1711
|
-
const slug =
|
|
2017
|
+
const slug = path8.basename(instance.path, ".md");
|
|
1712
2018
|
return {
|
|
1713
2019
|
...instance,
|
|
1714
2020
|
slug,
|
|
@@ -2050,8 +2356,8 @@ function collectStaleRuns(workspacePath, staleAfterMs, now) {
|
|
|
2050
2356
|
}
|
|
2051
2357
|
function collectPrimitiveRegistryReferenceIssues(workspacePath, inventory) {
|
|
2052
2358
|
const issues = [];
|
|
2053
|
-
const manifestPath =
|
|
2054
|
-
if (!
|
|
2359
|
+
const manifestPath = path9.join(workspacePath, ".workgraph", "primitive-registry.yaml");
|
|
2360
|
+
if (!fs8.existsSync(manifestPath)) {
|
|
2055
2361
|
issues.push({
|
|
2056
2362
|
code: "broken-primitive-registry-reference",
|
|
2057
2363
|
severity: "error",
|
|
@@ -2062,7 +2368,7 @@ function collectPrimitiveRegistryReferenceIssues(workspacePath, inventory) {
|
|
|
2062
2368
|
}
|
|
2063
2369
|
let parsed;
|
|
2064
2370
|
try {
|
|
2065
|
-
parsed = YAML2.parse(
|
|
2371
|
+
parsed = YAML2.parse(fs8.readFileSync(manifestPath, "utf-8"));
|
|
2066
2372
|
} catch (error) {
|
|
2067
2373
|
issues.push({
|
|
2068
2374
|
code: "broken-primitive-registry-reference",
|
|
@@ -2114,7 +2420,7 @@ function collectPrimitiveRegistryReferenceIssues(workspacePath, inventory) {
|
|
|
2114
2420
|
path: ".workgraph/primitive-registry.yaml"
|
|
2115
2421
|
});
|
|
2116
2422
|
}
|
|
2117
|
-
if (!
|
|
2423
|
+
if (!fs8.existsSync(path9.join(workspacePath, directory))) {
|
|
2118
2424
|
issues.push({
|
|
2119
2425
|
code: "broken-primitive-registry-reference",
|
|
2120
2426
|
severity: "error",
|
|
@@ -2149,8 +2455,8 @@ function collectPrimitiveRegistryReferenceIssues(workspacePath, inventory) {
|
|
|
2149
2455
|
function collectEmptyPrimitiveDirectoryIssues(workspacePath, inventory) {
|
|
2150
2456
|
const issues = [];
|
|
2151
2457
|
for (const typeDef of inventory.typeDefs.values()) {
|
|
2152
|
-
const directoryPath =
|
|
2153
|
-
if (!
|
|
2458
|
+
const directoryPath = path9.join(workspacePath, typeDef.directory);
|
|
2459
|
+
if (!fs8.existsSync(directoryPath)) continue;
|
|
2154
2460
|
const markdownCount = listMarkdownFilesRecursive(directoryPath).length;
|
|
2155
2461
|
if (markdownCount > 0) continue;
|
|
2156
2462
|
issues.push({
|
|
@@ -2179,10 +2485,10 @@ function removeOrphanLinks(workspacePath, orphanLinks) {
|
|
|
2179
2485
|
}
|
|
2180
2486
|
let removedLinks = 0;
|
|
2181
2487
|
for (const [sourcePath, tokenSet] of tokensBySource.entries()) {
|
|
2182
|
-
const absPath =
|
|
2183
|
-
if (!
|
|
2488
|
+
const absPath = path9.join(workspacePath, sourcePath);
|
|
2489
|
+
if (!fs8.existsSync(absPath)) continue;
|
|
2184
2490
|
try {
|
|
2185
|
-
const raw =
|
|
2491
|
+
const raw = fs8.readFileSync(absPath, "utf-8");
|
|
2186
2492
|
let fileRemoved = 0;
|
|
2187
2493
|
const updated = raw.replace(/\[\[([^[\]]+)\]\]/g, (token) => {
|
|
2188
2494
|
if (!tokenSet.has(token)) return token;
|
|
@@ -2190,7 +2496,7 @@ function removeOrphanLinks(workspacePath, orphanLinks) {
|
|
|
2190
2496
|
return "";
|
|
2191
2497
|
});
|
|
2192
2498
|
if (fileRemoved === 0) continue;
|
|
2193
|
-
|
|
2499
|
+
fs8.writeFileSync(absPath, updated, "utf-8");
|
|
2194
2500
|
removedLinks += fileRemoved;
|
|
2195
2501
|
filesUpdated.push(sourcePath);
|
|
2196
2502
|
} catch (error) {
|
|
@@ -2255,10 +2561,10 @@ function cancelStaleRuns(workspacePath, staleRuns, actor) {
|
|
|
2255
2561
|
return { cancelled, errors };
|
|
2256
2562
|
}
|
|
2257
2563
|
function readDispatchRunsSnapshot(workspacePath) {
|
|
2258
|
-
const runsPath =
|
|
2259
|
-
if (!
|
|
2564
|
+
const runsPath = path9.join(workspacePath, ".workgraph", "dispatch-runs.json");
|
|
2565
|
+
if (!fs8.existsSync(runsPath)) return [];
|
|
2260
2566
|
try {
|
|
2261
|
-
const parsed = JSON.parse(
|
|
2567
|
+
const parsed = JSON.parse(fs8.readFileSync(runsPath, "utf-8"));
|
|
2262
2568
|
return Array.isArray(parsed.runs) ? parsed.runs : [];
|
|
2263
2569
|
} catch {
|
|
2264
2570
|
return [];
|
|
@@ -2269,9 +2575,9 @@ function listMarkdownFilesRecursive(rootDirectory) {
|
|
|
2269
2575
|
const stack = [rootDirectory];
|
|
2270
2576
|
while (stack.length > 0) {
|
|
2271
2577
|
const current = stack.pop();
|
|
2272
|
-
const entries =
|
|
2578
|
+
const entries = fs8.readdirSync(current, { withFileTypes: true });
|
|
2273
2579
|
for (const entry of entries) {
|
|
2274
|
-
const absPath =
|
|
2580
|
+
const absPath = path9.join(current, entry.name);
|
|
2275
2581
|
if (entry.isDirectory()) {
|
|
2276
2582
|
stack.push(absPath);
|
|
2277
2583
|
continue;
|
|
@@ -2415,7 +2721,7 @@ function toNullableString(value) {
|
|
|
2415
2721
|
}
|
|
2416
2722
|
|
|
2417
2723
|
// src/diagnostics/viz.ts
|
|
2418
|
-
import
|
|
2724
|
+
import path10 from "path";
|
|
2419
2725
|
var TYPE_COLORS = ["cyan", "magenta", "yellow", "green", "blue", "red"];
|
|
2420
2726
|
function visualizeVaultGraph(workspacePath, options = {}) {
|
|
2421
2727
|
const inventory = loadPrimitiveInventory(workspacePath);
|
|
@@ -2555,7 +2861,7 @@ function resolveFocusPath(focusInput, inventory) {
|
|
|
2555
2861
|
const directCandidate = normalized.endsWith(".md") ? normalized : `${normalized}.md`;
|
|
2556
2862
|
if (inventory.byPath.has(normalized)) return normalized;
|
|
2557
2863
|
if (inventory.byPath.has(directCandidate)) return directCandidate;
|
|
2558
|
-
const slug =
|
|
2864
|
+
const slug = path10.basename(normalized, ".md");
|
|
2559
2865
|
const candidates = inventory.slugToPaths.get(slug) ?? [];
|
|
2560
2866
|
if (candidates.length === 1) return candidates[0];
|
|
2561
2867
|
if (candidates.length > 1) {
|
|
@@ -2863,8 +3169,8 @@ __export(autonomy_daemon_exports, {
|
|
|
2863
3169
|
startAutonomyDaemon: () => startAutonomyDaemon,
|
|
2864
3170
|
stopAutonomyDaemon: () => stopAutonomyDaemon
|
|
2865
3171
|
});
|
|
2866
|
-
import
|
|
2867
|
-
import
|
|
3172
|
+
import fs9 from "fs";
|
|
3173
|
+
import path11 from "path";
|
|
2868
3174
|
import { spawn } from "child_process";
|
|
2869
3175
|
var DAEMON_DIR = ".workgraph/daemon";
|
|
2870
3176
|
var AUTONOMY_PID_FILE = "autonomy.pid";
|
|
@@ -2873,29 +3179,29 @@ var AUTONOMY_LOG_FILE = "autonomy.log";
|
|
|
2873
3179
|
var AUTONOMY_META_FILE = "autonomy-process.json";
|
|
2874
3180
|
function startAutonomyDaemon(workspacePath, input) {
|
|
2875
3181
|
const daemonDir = ensureDaemonDir(workspacePath);
|
|
2876
|
-
const pidPath =
|
|
2877
|
-
const heartbeatPath = input.heartbeatPath ? resolvePathWithinWorkspace4(workspacePath, input.heartbeatPath) :
|
|
2878
|
-
const logPath = input.logPath ? resolvePathWithinWorkspace4(workspacePath, input.logPath) :
|
|
2879
|
-
const metaPath =
|
|
3182
|
+
const pidPath = path11.join(daemonDir, AUTONOMY_PID_FILE);
|
|
3183
|
+
const heartbeatPath = input.heartbeatPath ? resolvePathWithinWorkspace4(workspacePath, input.heartbeatPath) : path11.join(daemonDir, AUTONOMY_HEARTBEAT_FILE);
|
|
3184
|
+
const logPath = input.logPath ? resolvePathWithinWorkspace4(workspacePath, input.logPath) : path11.join(daemonDir, AUTONOMY_LOG_FILE);
|
|
3185
|
+
const metaPath = path11.join(daemonDir, AUTONOMY_META_FILE);
|
|
2880
3186
|
const existing = readAutonomyDaemonStatus(workspacePath, { cleanupStalePidFile: true });
|
|
2881
3187
|
if (existing.running) {
|
|
2882
3188
|
throw new Error(`Autonomy daemon already running (pid=${existing.pid}). Stop it before starting a new one.`);
|
|
2883
3189
|
}
|
|
2884
|
-
const logFd =
|
|
3190
|
+
const logFd = fs9.openSync(logPath, "a");
|
|
2885
3191
|
const args = buildAutonomyDaemonArgs(workspacePath, input, heartbeatPath);
|
|
2886
3192
|
const child = spawn(process.execPath, args, {
|
|
2887
3193
|
detached: true,
|
|
2888
3194
|
stdio: ["ignore", logFd, logFd],
|
|
2889
3195
|
env: process.env
|
|
2890
3196
|
});
|
|
2891
|
-
|
|
3197
|
+
fs9.closeSync(logFd);
|
|
2892
3198
|
child.unref();
|
|
2893
3199
|
if (!child.pid) {
|
|
2894
3200
|
throw new Error("Failed to start autonomy daemon: missing child process pid.");
|
|
2895
3201
|
}
|
|
2896
|
-
|
|
3202
|
+
fs9.writeFileSync(pidPath, `${child.pid}
|
|
2897
3203
|
`, "utf-8");
|
|
2898
|
-
|
|
3204
|
+
fs9.writeFileSync(metaPath, JSON.stringify({
|
|
2899
3205
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2900
3206
|
pid: child.pid,
|
|
2901
3207
|
args,
|
|
@@ -2929,9 +3235,9 @@ async function stopAutonomyDaemon(workspacePath, input = {}) {
|
|
|
2929
3235
|
await waitForProcessExit(pid, 1500);
|
|
2930
3236
|
stopped = !isProcessAlive(pid);
|
|
2931
3237
|
}
|
|
2932
|
-
const pidPath =
|
|
2933
|
-
if (stopped &&
|
|
2934
|
-
|
|
3238
|
+
const pidPath = path11.join(ensureDaemonDir(workspacePath), AUTONOMY_PID_FILE);
|
|
3239
|
+
if (stopped && fs9.existsSync(pidPath)) {
|
|
3240
|
+
fs9.rmSync(pidPath, { force: true });
|
|
2935
3241
|
}
|
|
2936
3242
|
return {
|
|
2937
3243
|
stopped,
|
|
@@ -2942,14 +3248,14 @@ async function stopAutonomyDaemon(workspacePath, input = {}) {
|
|
|
2942
3248
|
}
|
|
2943
3249
|
function readAutonomyDaemonStatus(workspacePath, options = {}) {
|
|
2944
3250
|
const daemonDir = ensureDaemonDir(workspacePath);
|
|
2945
|
-
const pidPath =
|
|
2946
|
-
const meta = readDaemonMeta(
|
|
2947
|
-
const logPath = meta?.logPath ? String(meta.logPath) :
|
|
2948
|
-
const heartbeatPath = meta?.heartbeatPath ? String(meta.heartbeatPath) :
|
|
3251
|
+
const pidPath = path11.join(daemonDir, AUTONOMY_PID_FILE);
|
|
3252
|
+
const meta = readDaemonMeta(path11.join(daemonDir, AUTONOMY_META_FILE));
|
|
3253
|
+
const logPath = meta?.logPath ? String(meta.logPath) : path11.join(daemonDir, AUTONOMY_LOG_FILE);
|
|
3254
|
+
const heartbeatPath = meta?.heartbeatPath ? String(meta.heartbeatPath) : path11.join(daemonDir, AUTONOMY_HEARTBEAT_FILE);
|
|
2949
3255
|
const pid = readPid(pidPath);
|
|
2950
3256
|
const running = pid ? isProcessAlive(pid) : false;
|
|
2951
|
-
if (!running && pid && options.cleanupStalePidFile !== false &&
|
|
2952
|
-
|
|
3257
|
+
if (!running && pid && options.cleanupStalePidFile !== false && fs9.existsSync(pidPath)) {
|
|
3258
|
+
fs9.rmSync(pidPath, { force: true });
|
|
2953
3259
|
}
|
|
2954
3260
|
return {
|
|
2955
3261
|
running,
|
|
@@ -2962,7 +3268,7 @@ function readAutonomyDaemonStatus(workspacePath, options = {}) {
|
|
|
2962
3268
|
}
|
|
2963
3269
|
function buildAutonomyDaemonArgs(workspacePath, input, heartbeatPath) {
|
|
2964
3270
|
const args = [
|
|
2965
|
-
|
|
3271
|
+
path11.resolve(input.cliEntrypointPath),
|
|
2966
3272
|
"autonomy",
|
|
2967
3273
|
"run",
|
|
2968
3274
|
"-w",
|
|
@@ -3020,22 +3326,22 @@ function waitForProcessExit(pid, timeoutMs) {
|
|
|
3020
3326
|
});
|
|
3021
3327
|
}
|
|
3022
3328
|
function ensureDaemonDir(workspacePath) {
|
|
3023
|
-
const daemonDir =
|
|
3024
|
-
if (!
|
|
3329
|
+
const daemonDir = path11.join(workspacePath, DAEMON_DIR);
|
|
3330
|
+
if (!fs9.existsSync(daemonDir)) fs9.mkdirSync(daemonDir, { recursive: true });
|
|
3025
3331
|
return daemonDir;
|
|
3026
3332
|
}
|
|
3027
3333
|
function readPid(pidPath) {
|
|
3028
|
-
if (!
|
|
3029
|
-
const raw =
|
|
3334
|
+
if (!fs9.existsSync(pidPath)) return void 0;
|
|
3335
|
+
const raw = fs9.readFileSync(pidPath, "utf-8").trim();
|
|
3030
3336
|
if (!raw) return void 0;
|
|
3031
3337
|
const parsed = Number(raw);
|
|
3032
3338
|
if (!Number.isInteger(parsed) || parsed <= 0) return void 0;
|
|
3033
3339
|
return parsed;
|
|
3034
3340
|
}
|
|
3035
3341
|
function readHeartbeat(heartbeatPath) {
|
|
3036
|
-
if (!
|
|
3342
|
+
if (!fs9.existsSync(heartbeatPath)) return void 0;
|
|
3037
3343
|
try {
|
|
3038
|
-
const parsed = JSON.parse(
|
|
3344
|
+
const parsed = JSON.parse(fs9.readFileSync(heartbeatPath, "utf-8"));
|
|
3039
3345
|
if (!parsed || typeof parsed !== "object") return void 0;
|
|
3040
3346
|
return parsed;
|
|
3041
3347
|
} catch {
|
|
@@ -3043,9 +3349,9 @@ function readHeartbeat(heartbeatPath) {
|
|
|
3043
3349
|
}
|
|
3044
3350
|
}
|
|
3045
3351
|
function readDaemonMeta(metaPath) {
|
|
3046
|
-
if (!
|
|
3352
|
+
if (!fs9.existsSync(metaPath)) return void 0;
|
|
3047
3353
|
try {
|
|
3048
|
-
const parsed = JSON.parse(
|
|
3354
|
+
const parsed = JSON.parse(fs9.readFileSync(metaPath, "utf-8"));
|
|
3049
3355
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
3050
3356
|
return parsed;
|
|
3051
3357
|
} catch {
|
|
@@ -3063,9 +3369,9 @@ function isProcessAlive(pid) {
|
|
|
3063
3369
|
}
|
|
3064
3370
|
function isZombieProcess(pid) {
|
|
3065
3371
|
const statPath = `/proc/${pid}/stat`;
|
|
3066
|
-
if (!
|
|
3372
|
+
if (!fs9.existsSync(statPath)) return false;
|
|
3067
3373
|
try {
|
|
3068
|
-
const stat =
|
|
3374
|
+
const stat = fs9.readFileSync(statPath, "utf-8");
|
|
3069
3375
|
const closingIdx = stat.indexOf(")");
|
|
3070
3376
|
if (closingIdx === -1 || closingIdx + 2 >= stat.length) return false;
|
|
3071
3377
|
const state = stat.slice(closingIdx + 2, closingIdx + 3);
|
|
@@ -3075,9 +3381,9 @@ function isZombieProcess(pid) {
|
|
|
3075
3381
|
}
|
|
3076
3382
|
}
|
|
3077
3383
|
function resolvePathWithinWorkspace4(workspacePath, filePath) {
|
|
3078
|
-
const base =
|
|
3079
|
-
const resolved =
|
|
3080
|
-
if (!resolved.startsWith(base +
|
|
3384
|
+
const base = path11.resolve(workspacePath);
|
|
3385
|
+
const resolved = path11.resolve(base, filePath);
|
|
3386
|
+
if (!resolved.startsWith(base + path11.sep) && resolved !== base) {
|
|
3081
3387
|
throw new Error(`Invalid path outside workspace: ${filePath}`);
|
|
3082
3388
|
}
|
|
3083
3389
|
return resolved;
|
|
@@ -3102,6 +3408,7 @@ export {
|
|
|
3102
3408
|
fetchSkillMarkdownFromUrl,
|
|
3103
3409
|
clawdapus_exports,
|
|
3104
3410
|
integration_exports,
|
|
3411
|
+
swarm_exports,
|
|
3105
3412
|
diagnostics_exports,
|
|
3106
3413
|
autonomy_daemon_exports
|
|
3107
3414
|
};
|