@vendian/cli 0.0.42 → 0.0.44
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 +1 -1
- package/cli-wrapper.mjs +225 -201
- package/dev-ui/css/app.css +0 -12
- package/dev-ui/index.html +2 -11
- package/dev-ui/js/app.js +1 -7
- package/dev-ui/js/serve.js +1 -77
- package/dev-ui/js/state.js +0 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ Agent authoring commands are also forwarded to the managed Python SDK CLI.
|
|
|
46
46
|
`vendian init` writes the current SDK-owned authoring docs into `.vendian-docs/`,
|
|
47
47
|
and `vendian create` scaffolds agents from the SDK templates. Updating the
|
|
48
48
|
managed runtime refreshes these docs and templates with the Python SDK. The CLI
|
|
49
|
-
remembers
|
|
49
|
+
remembers folders initialized with `vendian init` and refreshes their
|
|
50
50
|
`.vendian-docs/` automatically after `vendian update` or a managed auto-update.
|
|
51
51
|
|
|
52
52
|
## Local Data
|
package/cli-wrapper.mjs
CHANGED
|
@@ -863,26 +863,27 @@ function initOutputDir(args = []) {
|
|
|
863
863
|
}
|
|
864
864
|
return ".";
|
|
865
865
|
}
|
|
866
|
-
function
|
|
866
|
+
function resolveDocsPath(outputDir, { cwd = process.cwd(), platform = process.platform } = {}) {
|
|
867
867
|
const api = pathApi2(platform);
|
|
868
868
|
const raw = outputDir || ".";
|
|
869
869
|
return api.isAbsolute(raw) ? api.normalize(raw) : api.resolve(cwd, raw);
|
|
870
870
|
}
|
|
871
|
-
function
|
|
871
|
+
function recordAgentDocsDir(outputDir, {
|
|
872
872
|
env = process.env,
|
|
873
873
|
platform = process.platform,
|
|
874
874
|
cwd = process.cwd(),
|
|
875
875
|
now = /* @__PURE__ */ new Date()
|
|
876
876
|
} = {}) {
|
|
877
|
-
const
|
|
877
|
+
const docsPath = resolveDocsPath(outputDir, { cwd, platform });
|
|
878
878
|
const config = loadConfig(env, platform);
|
|
879
|
-
const existing =
|
|
880
|
-
const
|
|
879
|
+
const existing = agentDocsDirs(config);
|
|
880
|
+
const dirs = [docsPath, ...existing.filter((entry) => entry !== docsPath)];
|
|
881
881
|
const next = {
|
|
882
882
|
...config,
|
|
883
|
-
[
|
|
883
|
+
[DOC_DIRS_KEY]: dirs,
|
|
884
884
|
lastAgentDocsInitAt: now.toISOString()
|
|
885
885
|
};
|
|
886
|
+
delete next[LEGACY_DOC_DIRS_KEY];
|
|
886
887
|
saveConfig(next, env, platform);
|
|
887
888
|
return next;
|
|
888
889
|
}
|
|
@@ -890,10 +891,10 @@ function recordForwardedDocsCommand(args, code, options = {}) {
|
|
|
890
891
|
if (code !== 0 || !commandWritesAgentDocs(args)) {
|
|
891
892
|
return false;
|
|
892
893
|
}
|
|
893
|
-
|
|
894
|
+
recordAgentDocsDir(initOutputDir(args), options);
|
|
894
895
|
return true;
|
|
895
896
|
}
|
|
896
|
-
function
|
|
897
|
+
function refreshAgentDocsDirs({
|
|
897
898
|
config,
|
|
898
899
|
venvPath,
|
|
899
900
|
env = process.env,
|
|
@@ -902,41 +903,52 @@ function refreshAgentDocsWorkspaces({
|
|
|
902
903
|
now = /* @__PURE__ */ new Date()
|
|
903
904
|
} = {}) {
|
|
904
905
|
const currentConfig = config || loadConfig(env, platform);
|
|
905
|
-
const
|
|
906
|
-
if (
|
|
906
|
+
const dirs = agentDocsDirs(currentConfig);
|
|
907
|
+
if (dirs.length === 0) {
|
|
907
908
|
saveConfig(currentConfig, env, platform);
|
|
908
909
|
return { config: currentConfig, refreshed: 0, failed: 0 };
|
|
909
910
|
}
|
|
910
911
|
const vendianPath = venvVendian(venvPath, platform);
|
|
911
912
|
let refreshed = 0;
|
|
912
913
|
let failed = 0;
|
|
913
|
-
for (const
|
|
914
|
-
if (!fs7.existsSync(
|
|
914
|
+
for (const dir of dirs) {
|
|
915
|
+
if (!fs7.existsSync(dir)) {
|
|
915
916
|
continue;
|
|
916
917
|
}
|
|
917
|
-
const status = run2(vendianPath, ["init", "--output-dir",
|
|
918
|
+
const status = run2(vendianPath, ["init", "--output-dir", dir, "--yes"]);
|
|
918
919
|
if (status === 0) {
|
|
919
920
|
refreshed += 1;
|
|
920
921
|
} else {
|
|
921
922
|
failed += 1;
|
|
922
|
-
console.error(`[vendian] Could not refresh SDK docs in ${
|
|
923
|
+
console.error(`[vendian] Could not refresh SDK docs in ${dir}`);
|
|
923
924
|
}
|
|
924
925
|
}
|
|
925
926
|
const next = {
|
|
926
927
|
...currentConfig,
|
|
927
|
-
[
|
|
928
|
+
[DOC_DIRS_KEY]: dirs,
|
|
928
929
|
lastAgentDocsRefreshAt: now.toISOString()
|
|
929
930
|
};
|
|
931
|
+
delete next[LEGACY_DOC_DIRS_KEY];
|
|
930
932
|
saveConfig(next, env, platform);
|
|
931
933
|
return { config: next, refreshed, failed };
|
|
932
934
|
}
|
|
933
|
-
|
|
935
|
+
function agentDocsDirs(config = {}) {
|
|
936
|
+
return [.../* @__PURE__ */ new Set([
|
|
937
|
+
...stringList(config[DOC_DIRS_KEY]),
|
|
938
|
+
...stringList(config[LEGACY_DOC_DIRS_KEY])
|
|
939
|
+
])];
|
|
940
|
+
}
|
|
941
|
+
function stringList(value) {
|
|
942
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim()) : [];
|
|
943
|
+
}
|
|
944
|
+
var DOC_DIRS_KEY, LEGACY_DOC_DIRS_KEY;
|
|
934
945
|
var init_docs = __esm({
|
|
935
946
|
"src/docs.js"() {
|
|
936
947
|
init_config();
|
|
937
948
|
init_paths();
|
|
938
949
|
init_process();
|
|
939
|
-
|
|
950
|
+
DOC_DIRS_KEY = "agentDocDirs";
|
|
951
|
+
LEGACY_DOC_DIRS_KEY = ["agentDoc", "Work", "spaces"].join("");
|
|
940
952
|
}
|
|
941
953
|
});
|
|
942
954
|
|
|
@@ -1011,7 +1023,7 @@ async function setup({
|
|
|
1011
1023
|
saveConfig(next, env, platform);
|
|
1012
1024
|
installVendianPackages({ pythonPath, venvPath, config: next, env, platform });
|
|
1013
1025
|
const updated = { ...next, lastManagedUpdateAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1014
|
-
|
|
1026
|
+
refreshAgentDocsDirs({ config: updated, venvPath, env, platform });
|
|
1015
1027
|
if (!verifyVendianImports(pythonPath)) {
|
|
1016
1028
|
throw new Error("Vendian packages installed, but import verification failed.");
|
|
1017
1029
|
}
|
|
@@ -1196,8 +1208,8 @@ function findAgentDirectoryCandidates({
|
|
|
1196
1208
|
]);
|
|
1197
1209
|
for (const root of roots) {
|
|
1198
1210
|
for (const manifest of findManifestFiles(root, { maxDepth, maxDirs, limit: 250 })) {
|
|
1199
|
-
const
|
|
1200
|
-
addCandidate(
|
|
1211
|
+
const agentsGroup = nearestAgentsAncestor(manifest, root);
|
|
1212
|
+
addCandidate(agentsGroup || path4.dirname(manifest));
|
|
1201
1213
|
if (candidates.length >= limit) {
|
|
1202
1214
|
return candidates.slice(0, limit);
|
|
1203
1215
|
}
|
|
@@ -1459,7 +1471,7 @@ function maybeAutoUpdateManagedEnv({
|
|
|
1459
1471
|
load = loadConfig,
|
|
1460
1472
|
save = saveConfig,
|
|
1461
1473
|
installPackages = installVendianPackages,
|
|
1462
|
-
refreshDocs =
|
|
1474
|
+
refreshDocs = refreshAgentDocsDirs,
|
|
1463
1475
|
now = Date.now(),
|
|
1464
1476
|
onProgress = null
|
|
1465
1477
|
} = {}) {
|
|
@@ -1509,60 +1521,8 @@ function reportProgress(onProgress, message) {
|
|
|
1509
1521
|
init_process();
|
|
1510
1522
|
init_paths();
|
|
1511
1523
|
|
|
1512
|
-
// src/workspaces.js
|
|
1513
|
-
init_process();
|
|
1514
|
-
async function listCloudWorkspaces({
|
|
1515
|
-
env = process.env,
|
|
1516
|
-
platform = process.platform,
|
|
1517
|
-
prepareInvocation = preparePythonVendianInvocation,
|
|
1518
|
-
run: run2 = runCapture,
|
|
1519
|
-
onProgress = null,
|
|
1520
|
-
timeoutMs = 2e4
|
|
1521
|
-
} = {}) {
|
|
1522
|
-
const progress = typeof onProgress === "function" ? onProgress : () => {
|
|
1523
|
-
};
|
|
1524
|
-
progress("Checking managed Python runtime");
|
|
1525
|
-
const invocation = await prepareInvocation(["cloud", "collections", "list", "--json"], {
|
|
1526
|
-
env,
|
|
1527
|
-
platform,
|
|
1528
|
-
onProgress: progress,
|
|
1529
|
-
skipAutoUpdate: true
|
|
1530
|
-
});
|
|
1531
|
-
progress("Loading workspaces from Vendian");
|
|
1532
|
-
const result = run2(invocation.command, invocation.args, { env: invocation.env, timeout: timeoutMs });
|
|
1533
|
-
if (!result.ok) {
|
|
1534
|
-
return {
|
|
1535
|
-
ok: false,
|
|
1536
|
-
workspaces: [],
|
|
1537
|
-
error: (result.stderr || result.stdout || `workspace list exited with code ${result.status}`).trim()
|
|
1538
|
-
};
|
|
1539
|
-
}
|
|
1540
|
-
try {
|
|
1541
|
-
return { ok: true, workspaces: normalizeWorkspaceList(JSON.parse(result.stdout)), error: "" };
|
|
1542
|
-
} catch (error) {
|
|
1543
|
-
const message = error && typeof error.message === "string" ? error.message : String(error);
|
|
1544
|
-
return { ok: false, workspaces: [], error: `Could not parse workspace list: ${message}` };
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
function normalizeWorkspaceList(payload) {
|
|
1548
|
-
const data = payload && typeof payload === "object" && "data" in payload ? payload.data : payload;
|
|
1549
|
-
const raw = Array.isArray(data) ? data : data && typeof data === "object" && Array.isArray(data.data) ? data.data : data && typeof data === "object" && Array.isArray(data.collections) ? data.collections : [];
|
|
1550
|
-
return raw.filter((item) => item && typeof item === "object").map((item) => ({
|
|
1551
|
-
id: stringValue(item.id),
|
|
1552
|
-
name: stringValue(item.name || item.slug || item.id || "Unnamed workspace"),
|
|
1553
|
-
slug: stringValue(item.slug)
|
|
1554
|
-
})).filter((item) => item.id);
|
|
1555
|
-
}
|
|
1556
|
-
function workspaceLabel(workspace) {
|
|
1557
|
-
const name = workspace?.name || workspace?.slug || workspace?.id || "Unnamed workspace";
|
|
1558
|
-
const slug = workspace?.slug && workspace.slug !== name ? ` (${workspace.slug})` : "";
|
|
1559
|
-
return `${name}${slug}`;
|
|
1560
|
-
}
|
|
1561
|
-
function stringValue(value) {
|
|
1562
|
-
return value == null ? "" : String(value).trim();
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
1524
|
// src/serve-events.js
|
|
1525
|
+
var LEGACY_COLLECTION_RESOLVED_EVENT = ["work", "space_resolved"].join("");
|
|
1566
1526
|
function initialServeState() {
|
|
1567
1527
|
return {
|
|
1568
1528
|
connected: false,
|
|
@@ -1616,15 +1576,15 @@ function applyServeEvent(state, event) {
|
|
|
1616
1576
|
...state,
|
|
1617
1577
|
logs: appendLog(state.logs, event)
|
|
1618
1578
|
};
|
|
1619
|
-
if (event.type === "
|
|
1620
|
-
return { ...next, collectionId:
|
|
1579
|
+
if (event.type === "collection_resolved" || event.type === LEGACY_COLLECTION_RESOLVED_EVENT) {
|
|
1580
|
+
return { ...next, collectionId: stringValue(event.collectionId), activity: "Collection resolved" };
|
|
1621
1581
|
}
|
|
1622
1582
|
if (event.type === "daemon_registered") {
|
|
1623
1583
|
return {
|
|
1624
1584
|
...next,
|
|
1625
1585
|
connected: true,
|
|
1626
|
-
daemonId:
|
|
1627
|
-
cliSessionId:
|
|
1586
|
+
daemonId: stringValue(event.daemonId),
|
|
1587
|
+
cliSessionId: stringValue(event.cliSessionId),
|
|
1628
1588
|
activity: "Daemon registered"
|
|
1629
1589
|
};
|
|
1630
1590
|
}
|
|
@@ -1644,7 +1604,7 @@ function applyServeEvent(state, event) {
|
|
|
1644
1604
|
};
|
|
1645
1605
|
}
|
|
1646
1606
|
if (event.type === "daemon_wake_received") {
|
|
1647
|
-
const sourceType =
|
|
1607
|
+
const sourceType = stringValue(event.sourceEventType || "backend wake");
|
|
1648
1608
|
return {
|
|
1649
1609
|
...next,
|
|
1650
1610
|
activity: `Wake received: ${sourceType}`
|
|
@@ -1659,17 +1619,17 @@ function applyServeEvent(state, event) {
|
|
|
1659
1619
|
if (event.type === "dispatch_job_received") {
|
|
1660
1620
|
return {
|
|
1661
1621
|
...next,
|
|
1662
|
-
activity: `Dispatch received for run ${
|
|
1622
|
+
activity: `Dispatch received for run ${stringValue(event.runId || event.sessionId || "unknown")}`
|
|
1663
1623
|
};
|
|
1664
1624
|
}
|
|
1665
1625
|
if (event.type === "dispatch_job_acked") {
|
|
1666
1626
|
return {
|
|
1667
1627
|
...next,
|
|
1668
|
-
activity: `Dispatch acknowledged for run ${
|
|
1628
|
+
activity: `Dispatch acknowledged for run ${stringValue(event.runId || event.sessionId || "unknown")}`
|
|
1669
1629
|
};
|
|
1670
1630
|
}
|
|
1671
1631
|
if (event.type === "daemon_stderr") {
|
|
1672
|
-
const message =
|
|
1632
|
+
const message = stringValue(event.message || event.stderr || "Daemon wrote to stderr");
|
|
1673
1633
|
return {
|
|
1674
1634
|
...next,
|
|
1675
1635
|
activity: `Daemon stderr: ${message}`,
|
|
@@ -1714,14 +1674,14 @@ function applyServeEvent(state, event) {
|
|
|
1714
1674
|
if (event.type === "agent_prepare_progress") {
|
|
1715
1675
|
const relativePath = agentLabel(event);
|
|
1716
1676
|
const timestamp = event.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1717
|
-
const message =
|
|
1677
|
+
const message = stringValue(event.message || event.stage || "Preparing agent");
|
|
1718
1678
|
return {
|
|
1719
1679
|
...next,
|
|
1720
1680
|
agentLogs: appendAgentLog(state.agentLogs, event),
|
|
1721
1681
|
agentRunState: setAgentRunState(state.agentRunState, relativePath, {
|
|
1722
1682
|
status: "preparing",
|
|
1723
1683
|
lastEventAt: timestamp,
|
|
1724
|
-
progressStage:
|
|
1684
|
+
progressStage: stringValue(event.stage),
|
|
1725
1685
|
progressMessage: message
|
|
1726
1686
|
}),
|
|
1727
1687
|
activity: `${relativePath}: ${message}`
|
|
@@ -1733,11 +1693,11 @@ function applyServeEvent(state, event) {
|
|
|
1733
1693
|
const runState = isError ? setAgentRunState(state.agentRunState, agentLabel(event), {
|
|
1734
1694
|
status: "error",
|
|
1735
1695
|
lastEventAt: event.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1736
|
-
errorMessage:
|
|
1696
|
+
errorMessage: stringValue(event.error || event.errorMessage || "Agent setup failed")
|
|
1737
1697
|
}) : isDisabled ? setAgentRunState(state.agentRunState, agentLabel(event), {
|
|
1738
1698
|
status: "disabled",
|
|
1739
1699
|
lastEventAt: event.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1740
|
-
disabledReason:
|
|
1700
|
+
disabledReason: stringValue(event.disabledReason || event.warning || "System dependency not met"),
|
|
1741
1701
|
resolutionHints: Array.isArray(event.resolutionHints) ? event.resolutionHints : []
|
|
1742
1702
|
}) : setAgentRunState(state.agentRunState, agentLabel(event), {
|
|
1743
1703
|
status: "ready",
|
|
@@ -1773,8 +1733,8 @@ function applyServeEvent(state, event) {
|
|
|
1773
1733
|
agentLogs: appendAgentLog(state.agentLogs, event),
|
|
1774
1734
|
agentRunState: setAgentRunState(state.agentRunState, relativePath, {
|
|
1775
1735
|
status: "running",
|
|
1776
|
-
runId:
|
|
1777
|
-
jobType:
|
|
1736
|
+
runId: stringValue(event.runId || event.deployRequestId),
|
|
1737
|
+
jobType: stringValue(event.jobType || "run"),
|
|
1778
1738
|
startedAt: timestamp,
|
|
1779
1739
|
completedAt: null,
|
|
1780
1740
|
lastEventAt: timestamp,
|
|
@@ -1788,7 +1748,7 @@ function applyServeEvent(state, event) {
|
|
|
1788
1748
|
const relativePath = agentLabel(event);
|
|
1789
1749
|
const timestamp = event.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1790
1750
|
const success = event.success !== false;
|
|
1791
|
-
const runId =
|
|
1751
|
+
const runId = stringValue(event.runId || event.deployRequestId);
|
|
1792
1752
|
const previous = (state.agentRunState || {})[relativePath];
|
|
1793
1753
|
const effectiveSuccess = success && !(previous?.status === "error" && previous?.runId === runId);
|
|
1794
1754
|
return {
|
|
@@ -1797,10 +1757,10 @@ function applyServeEvent(state, event) {
|
|
|
1797
1757
|
agentRunState: setAgentRunState(state.agentRunState, relativePath, {
|
|
1798
1758
|
status: effectiveSuccess ? "completed" : "error",
|
|
1799
1759
|
runId,
|
|
1800
|
-
jobType:
|
|
1760
|
+
jobType: stringValue(event.jobType || "run"),
|
|
1801
1761
|
completedAt: timestamp,
|
|
1802
1762
|
lastEventAt: timestamp,
|
|
1803
|
-
errorMessage: effectiveSuccess ? null : previous?.errorMessage ||
|
|
1763
|
+
errorMessage: effectiveSuccess ? null : previous?.errorMessage || stringValue(event.error || "Job failed")
|
|
1804
1764
|
}),
|
|
1805
1765
|
jobsRun: state.jobsRun + 1,
|
|
1806
1766
|
currentJob: null,
|
|
@@ -1808,13 +1768,13 @@ function applyServeEvent(state, event) {
|
|
|
1808
1768
|
};
|
|
1809
1769
|
}
|
|
1810
1770
|
if (event.type === "backend_connection_lost") {
|
|
1811
|
-
return { ...next, connected: false, activity: `Connection lost during ${
|
|
1771
|
+
return { ...next, connected: false, activity: `Connection lost during ${stringValue(event.activity)}`, retry: event };
|
|
1812
1772
|
}
|
|
1813
1773
|
if (event.type === "backend_connection_restored") {
|
|
1814
1774
|
return { ...next, connected: true, activity: "Backend connection restored", retry: null };
|
|
1815
1775
|
}
|
|
1816
1776
|
if (event.type === "retry_scheduled") {
|
|
1817
|
-
return { ...next, retry: event, activity: `Retrying ${
|
|
1777
|
+
return { ...next, retry: event, activity: `Retrying ${stringValue(event.activity)} in ${formatSeconds(event.delaySeconds)}` };
|
|
1818
1778
|
}
|
|
1819
1779
|
if (event.type === "error") {
|
|
1820
1780
|
const relativePath = event.relativePath || event.path ? agentLabel(event) : "";
|
|
@@ -1825,11 +1785,11 @@ function applyServeEvent(state, event) {
|
|
|
1825
1785
|
agentLogs: hasAgentScope ? appendAgentLog(state.agentLogs, event) : state.agentLogs,
|
|
1826
1786
|
agentRunState: hasAgentScope ? setAgentRunState(state.agentRunState, relativePath, {
|
|
1827
1787
|
status: "error",
|
|
1828
|
-
runId:
|
|
1829
|
-
jobType:
|
|
1788
|
+
runId: stringValue(event.runId || event.deployRequestId),
|
|
1789
|
+
jobType: stringValue(event.jobType || "run"),
|
|
1830
1790
|
completedAt: timestamp,
|
|
1831
1791
|
lastEventAt: timestamp,
|
|
1832
|
-
errorMessage:
|
|
1792
|
+
errorMessage: stringValue(event.error || event.code || "Unknown error")
|
|
1833
1793
|
}) : state.agentRunState,
|
|
1834
1794
|
activity: "Error",
|
|
1835
1795
|
errors: appendError(state.errors, jobLabel(event), event.error || event.code || "Unknown error")
|
|
@@ -1847,8 +1807,8 @@ function applyServeEvent(state, event) {
|
|
|
1847
1807
|
}
|
|
1848
1808
|
if (event.type === "process_exit" || event.type === "daemon_exit") {
|
|
1849
1809
|
const timestamp = event.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1850
|
-
const message =
|
|
1851
|
-
const relativePath =
|
|
1810
|
+
const message = stringValue(event.message || processExitMessage(event));
|
|
1811
|
+
const relativePath = stringValue(event.relativePath || "");
|
|
1852
1812
|
const runState = failRunningAgents(state.agentRunState, message, timestamp, relativePath);
|
|
1853
1813
|
return {
|
|
1854
1814
|
...next,
|
|
@@ -1903,7 +1863,7 @@ function mergeAgentLogRecords(agentLogs, records = []) {
|
|
|
1903
1863
|
let next = agentLogs || {};
|
|
1904
1864
|
for (const record of records) {
|
|
1905
1865
|
if (!record || typeof record !== "object") continue;
|
|
1906
|
-
const relativePath =
|
|
1866
|
+
const relativePath = stringValue(record.relativePath || ".");
|
|
1907
1867
|
const entry = record.entry && typeof record.entry === "object" ? record.entry : record;
|
|
1908
1868
|
const existing = next[relativePath] || [];
|
|
1909
1869
|
next = {
|
|
@@ -1918,13 +1878,13 @@ function agentRunStateFromLogs(agentLogs, { includeRunning = true } = {}) {
|
|
|
1918
1878
|
for (const [relativePath, entries] of Object.entries(agentLogs || {})) {
|
|
1919
1879
|
for (const entry of entries || []) {
|
|
1920
1880
|
const timestamp = entry.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1921
|
-
const runId =
|
|
1881
|
+
const runId = stringValue(entry.runId);
|
|
1922
1882
|
const current = next[relativePath] || {};
|
|
1923
1883
|
if (entry.eventType === "job_started") {
|
|
1924
1884
|
next = setAgentRunState(next, relativePath, {
|
|
1925
1885
|
status: includeRunning ? "running" : current.status,
|
|
1926
1886
|
runId,
|
|
1927
|
-
jobType: entry.jobType ?
|
|
1887
|
+
jobType: entry.jobType ? stringValue(entry.jobType) : current.jobType,
|
|
1928
1888
|
startedAt: timestamp,
|
|
1929
1889
|
lastEventAt: timestamp
|
|
1930
1890
|
});
|
|
@@ -1935,10 +1895,10 @@ function agentRunStateFromLogs(agentLogs, { includeRunning = true } = {}) {
|
|
|
1935
1895
|
next = setAgentRunState(next, relativePath, {
|
|
1936
1896
|
status: failed ? "error" : "completed",
|
|
1937
1897
|
runId,
|
|
1938
|
-
jobType: entry.jobType ?
|
|
1898
|
+
jobType: entry.jobType ? stringValue(entry.jobType) : current.jobType,
|
|
1939
1899
|
completedAt: timestamp,
|
|
1940
1900
|
lastEventAt: timestamp,
|
|
1941
|
-
errorMessage: failed ? formatErrorMessage(entry.error) || current.errorMessage ||
|
|
1901
|
+
errorMessage: failed ? formatErrorMessage(entry.error) || current.errorMessage || stringValue(entry.message || "Job failed") : null
|
|
1942
1902
|
});
|
|
1943
1903
|
continue;
|
|
1944
1904
|
}
|
|
@@ -1946,10 +1906,10 @@ function agentRunStateFromLogs(agentLogs, { includeRunning = true } = {}) {
|
|
|
1946
1906
|
next = setAgentRunState(next, relativePath, {
|
|
1947
1907
|
status: "error",
|
|
1948
1908
|
runId,
|
|
1949
|
-
jobType: entry.jobType ?
|
|
1909
|
+
jobType: entry.jobType ? stringValue(entry.jobType) : current.jobType,
|
|
1950
1910
|
completedAt: timestamp,
|
|
1951
1911
|
lastEventAt: timestamp,
|
|
1952
|
-
errorMessage: formatErrorMessage(entry.error) ||
|
|
1912
|
+
errorMessage: formatErrorMessage(entry.error) || stringValue(entry.message || "Job failed")
|
|
1953
1913
|
});
|
|
1954
1914
|
continue;
|
|
1955
1915
|
}
|
|
@@ -1967,34 +1927,34 @@ function serveEventAgentLogEntry(event) {
|
|
|
1967
1927
|
if (!event || typeof event !== "object") {
|
|
1968
1928
|
return null;
|
|
1969
1929
|
}
|
|
1970
|
-
const type =
|
|
1971
|
-
const relativePath =
|
|
1930
|
+
const type = stringValue(event.type);
|
|
1931
|
+
const relativePath = stringValue(event.relativePath || event.path || ".");
|
|
1972
1932
|
const timestamp = event.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1973
1933
|
if (type === "run_log") {
|
|
1974
1934
|
return {
|
|
1975
1935
|
relativePath,
|
|
1976
1936
|
entry: {
|
|
1977
1937
|
timestamp,
|
|
1978
|
-
runId:
|
|
1979
|
-
eventType:
|
|
1980
|
-
level:
|
|
1981
|
-
message:
|
|
1982
|
-
stepId: event.stepId ?
|
|
1938
|
+
runId: stringValue(event.runId),
|
|
1939
|
+
eventType: stringValue(event.eventType || "log"),
|
|
1940
|
+
level: stringValue(event.level || "info"),
|
|
1941
|
+
message: stringValue(event.message),
|
|
1942
|
+
stepId: event.stepId ? stringValue(event.stepId) : null,
|
|
1983
1943
|
current: event.current ?? null,
|
|
1984
1944
|
total: event.total ?? null,
|
|
1985
1945
|
success: event.success ?? null,
|
|
1986
1946
|
error: event.error ?? null,
|
|
1987
|
-
jobType: event.jobType ?
|
|
1947
|
+
jobType: event.jobType ? stringValue(event.jobType) : null
|
|
1988
1948
|
}
|
|
1989
1949
|
};
|
|
1990
1950
|
}
|
|
1991
1951
|
if (type === "job_started") {
|
|
1992
|
-
const jobType =
|
|
1952
|
+
const jobType = stringValue(event.jobType || "run");
|
|
1993
1953
|
return {
|
|
1994
1954
|
relativePath,
|
|
1995
1955
|
entry: {
|
|
1996
1956
|
timestamp,
|
|
1997
|
-
runId:
|
|
1957
|
+
runId: stringValue(event.runId || event.deployRequestId),
|
|
1998
1958
|
eventType: "job_started",
|
|
1999
1959
|
level: "info",
|
|
2000
1960
|
message: `Started ${jobType}`,
|
|
@@ -2009,15 +1969,15 @@ function serveEventAgentLogEntry(event) {
|
|
|
2009
1969
|
}
|
|
2010
1970
|
if (type === "job_completed") {
|
|
2011
1971
|
const success = event.success !== false;
|
|
2012
|
-
const jobType =
|
|
1972
|
+
const jobType = stringValue(event.jobType || "run");
|
|
2013
1973
|
return {
|
|
2014
1974
|
relativePath,
|
|
2015
1975
|
entry: {
|
|
2016
1976
|
timestamp,
|
|
2017
|
-
runId:
|
|
1977
|
+
runId: stringValue(event.runId || event.deployRequestId),
|
|
2018
1978
|
eventType: "job_completed",
|
|
2019
1979
|
level: success ? "info" : "error",
|
|
2020
|
-
message: success ? "Completed successfully" :
|
|
1980
|
+
message: success ? "Completed successfully" : stringValue(event.error || "Failed"),
|
|
2021
1981
|
stepId: null,
|
|
2022
1982
|
current: null,
|
|
2023
1983
|
total: null,
|
|
@@ -2032,16 +1992,16 @@ function serveEventAgentLogEntry(event) {
|
|
|
2032
1992
|
relativePath,
|
|
2033
1993
|
entry: {
|
|
2034
1994
|
timestamp,
|
|
2035
|
-
runId:
|
|
1995
|
+
runId: stringValue(event.runId || event.deployRequestId),
|
|
2036
1996
|
eventType: "error",
|
|
2037
1997
|
level: "error",
|
|
2038
|
-
message:
|
|
1998
|
+
message: stringValue(event.error || event.code || "Unknown error"),
|
|
2039
1999
|
stepId: null,
|
|
2040
2000
|
current: null,
|
|
2041
2001
|
total: null,
|
|
2042
2002
|
success: false,
|
|
2043
2003
|
error: event.error ?? event.code ?? null,
|
|
2044
|
-
jobType: event.jobType ?
|
|
2004
|
+
jobType: event.jobType ? stringValue(event.jobType) : null
|
|
2045
2005
|
}
|
|
2046
2006
|
};
|
|
2047
2007
|
}
|
|
@@ -2055,7 +2015,7 @@ function serveEventAgentLogEntry(event) {
|
|
|
2055
2015
|
eventType: type,
|
|
2056
2016
|
level: event.status === "error" ? "error" : "info",
|
|
2057
2017
|
message: agentPrepareLogMessage(event),
|
|
2058
|
-
stepId:
|
|
2018
|
+
stepId: stringValue(event.stage || type),
|
|
2059
2019
|
current: null,
|
|
2060
2020
|
total: null,
|
|
2061
2021
|
success,
|
|
@@ -2065,12 +2025,12 @@ function serveEventAgentLogEntry(event) {
|
|
|
2065
2025
|
};
|
|
2066
2026
|
}
|
|
2067
2027
|
if (type === "process_exit" || type === "daemon_exit") {
|
|
2068
|
-
const message =
|
|
2028
|
+
const message = stringValue(event.message || processExitMessage(event));
|
|
2069
2029
|
return {
|
|
2070
2030
|
relativePath,
|
|
2071
2031
|
entry: {
|
|
2072
2032
|
timestamp,
|
|
2073
|
-
runId:
|
|
2033
|
+
runId: stringValue(event.runId || event.sessionId || "daemon"),
|
|
2074
2034
|
eventType: type,
|
|
2075
2035
|
level: "error",
|
|
2076
2036
|
message,
|
|
@@ -2084,16 +2044,16 @@ function serveEventAgentLogEntry(event) {
|
|
|
2084
2044
|
signal: event.signal ?? null,
|
|
2085
2045
|
stderrTail: event.stderrTail ?? null
|
|
2086
2046
|
},
|
|
2087
|
-
jobType: event.jobType ?
|
|
2047
|
+
jobType: event.jobType ? stringValue(event.jobType) : null
|
|
2088
2048
|
}
|
|
2089
2049
|
};
|
|
2090
2050
|
}
|
|
2091
2051
|
return null;
|
|
2092
2052
|
}
|
|
2093
2053
|
function agentRuntimeStatus(agent, agentRunState = {}) {
|
|
2094
|
-
const path10 =
|
|
2054
|
+
const path10 = stringValue(agent?.relativePath || ".");
|
|
2095
2055
|
const run2 = (agentRunState || {})[path10];
|
|
2096
|
-
const inventoryStatus =
|
|
2056
|
+
const inventoryStatus = stringValue(agent?.status);
|
|
2097
2057
|
if (run2?.status === "running") {
|
|
2098
2058
|
return { status: "running", label: "running", run: run2 };
|
|
2099
2059
|
}
|
|
@@ -2121,28 +2081,29 @@ function agentRuntimeStatus(agent, agentRunState = {}) {
|
|
|
2121
2081
|
return { status: inventoryStatus || "unknown", label: inventoryStatus || "unknown", run: run2 };
|
|
2122
2082
|
}
|
|
2123
2083
|
function appendError(errors, scope, message) {
|
|
2124
|
-
return [...errors, { scope, message:
|
|
2084
|
+
return [...errors, { scope, message: stringValue(message) }].slice(-20);
|
|
2125
2085
|
}
|
|
2126
2086
|
function serveDebugEntry(event) {
|
|
2127
2087
|
if (!event || typeof event !== "object") return null;
|
|
2128
|
-
const type =
|
|
2088
|
+
const type = stringValue(event.type);
|
|
2129
2089
|
const timestamp = event.timestamp || event.occurredAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
2130
2090
|
const base = { timestamp, eventType: type, level: debugLevel(event) };
|
|
2131
2091
|
switch (type) {
|
|
2132
|
-
case "
|
|
2133
|
-
|
|
2092
|
+
case "collection_resolved":
|
|
2093
|
+
case LEGACY_COLLECTION_RESOLVED_EVENT:
|
|
2094
|
+
return { ...base, message: `collection resolved id=${stringValue(event.collectionId || "unknown")}` };
|
|
2134
2095
|
case "daemon_registered":
|
|
2135
2096
|
return { ...base, message: `daemon registered daemon=${shortId(event.daemonId)} cli=${shortId(event.cliSessionId)}` };
|
|
2136
2097
|
case "daemon_event_stream_connected":
|
|
2137
2098
|
return { ...base, message: `daemon event stream connected daemon=${shortId(event.daemonId)}` };
|
|
2138
2099
|
case "daemon_event_stream_lost":
|
|
2139
|
-
return { ...base, level: "warn", message: `daemon event stream lost retry=${formatSeconds(event.retryInSeconds)} error=${
|
|
2100
|
+
return { ...base, level: "warn", message: `daemon event stream lost retry=${formatSeconds(event.retryInSeconds)} error=${stringValue(event.error || "unknown")}` };
|
|
2140
2101
|
case "daemon_wake_received":
|
|
2141
2102
|
return {
|
|
2142
2103
|
...base,
|
|
2143
2104
|
message: [
|
|
2144
|
-
`wake received source=${
|
|
2145
|
-
event.eventType ? `event=${
|
|
2105
|
+
`wake received source=${stringValue(event.sourceEventType || "backend")}`,
|
|
2106
|
+
event.eventType ? `event=${stringValue(event.eventType)}` : "",
|
|
2146
2107
|
event.sessionId ? `session=${shortId(event.sessionId)}` : "",
|
|
2147
2108
|
event.runId ? `run=${shortId(event.runId)}` : "",
|
|
2148
2109
|
event.localAgentRowId ? `agentRow=${shortId(event.localAgentRowId)}` : ""
|
|
@@ -2165,7 +2126,7 @@ function serveDebugEntry(event) {
|
|
|
2165
2126
|
message: `dispatch acked delivery=${shortId(event.deliveryId)} run=${shortId(event.runId || event.sessionId)} attempt=${shortId(event.attemptId)}`
|
|
2166
2127
|
};
|
|
2167
2128
|
case "job_started":
|
|
2168
|
-
return { ...base, message: `job started type=${
|
|
2129
|
+
return { ...base, message: `job started type=${stringValue(event.jobType || "run")} run=${shortId(event.runId || event.sessionId || event.deployRequestId || event.testRunId)}` };
|
|
2169
2130
|
case "job_completed":
|
|
2170
2131
|
return {
|
|
2171
2132
|
...base,
|
|
@@ -2173,18 +2134,18 @@ function serveDebugEntry(event) {
|
|
|
2173
2134
|
message: `job completed success=${event.success !== false} run=${shortId(event.runId || event.sessionId || event.deployRequestId || event.testRunId)}${event.error ? ` error=${formatErrorMessage(event.error)}` : ""}`
|
|
2174
2135
|
};
|
|
2175
2136
|
case "backend_connection_lost":
|
|
2176
|
-
return { ...base, level: "warn", message: `backend connection lost activity=${
|
|
2137
|
+
return { ...base, level: "warn", message: `backend connection lost activity=${stringValue(event.activity)} error=${stringValue(event.error)}` };
|
|
2177
2138
|
case "backend_connection_restored":
|
|
2178
|
-
return { ...base, message: `backend connection restored activity=${
|
|
2139
|
+
return { ...base, message: `backend connection restored activity=${stringValue(event.activity)}` };
|
|
2179
2140
|
case "retry_scheduled":
|
|
2180
|
-
return { ...base, level: "warn", message: `retry scheduled activity=${
|
|
2141
|
+
return { ...base, level: "warn", message: `retry scheduled activity=${stringValue(event.activity)} delay=${formatSeconds(event.delaySeconds)}` };
|
|
2181
2142
|
case "daemon_stderr":
|
|
2182
|
-
return { ...base, level: looksLikeError(event.message || event.stderr) ? "error" : "warn", message: `stderr ${
|
|
2143
|
+
return { ...base, level: looksLikeError(event.message || event.stderr) ? "error" : "warn", message: `stderr ${stringValue(event.message || event.stderr)}` };
|
|
2183
2144
|
case "error":
|
|
2184
|
-
return { ...base, level: "error", message: `error scope=${
|
|
2145
|
+
return { ...base, level: "error", message: `error scope=${stringValue(event.relativePath || event.path || "daemon")} ${stringValue(event.error || event.code || "unknown")}` };
|
|
2185
2146
|
case "process_exit":
|
|
2186
2147
|
case "daemon_exit":
|
|
2187
|
-
return { ...base, level: "error", message:
|
|
2148
|
+
return { ...base, level: "error", message: stringValue(event.message || processExitMessage(event)) };
|
|
2188
2149
|
case "stopped":
|
|
2189
2150
|
return { ...base, message: `stopped jobsRun=${Number(event.jobsRun ?? 0)}` };
|
|
2190
2151
|
default:
|
|
@@ -2192,7 +2153,7 @@ function serveDebugEntry(event) {
|
|
|
2192
2153
|
}
|
|
2193
2154
|
}
|
|
2194
2155
|
function debugLevel(event) {
|
|
2195
|
-
if (event?.level) return
|
|
2156
|
+
if (event?.level) return stringValue(event.level);
|
|
2196
2157
|
if (event?.type === "error" || event?.type === "process_exit" || event?.type === "daemon_exit") return "error";
|
|
2197
2158
|
return "info";
|
|
2198
2159
|
}
|
|
@@ -2200,14 +2161,14 @@ function boolText(value) {
|
|
|
2200
2161
|
return value ? "yes" : "no";
|
|
2201
2162
|
}
|
|
2202
2163
|
function shortId(value) {
|
|
2203
|
-
const text =
|
|
2164
|
+
const text = stringValue(value);
|
|
2204
2165
|
return text ? text.slice(0, 8) : "unknown";
|
|
2205
2166
|
}
|
|
2206
2167
|
function looksLikeError(value) {
|
|
2207
|
-
return /(error|exception|traceback|failed|fatal|denied|unauthorized|timeout)/i.test(
|
|
2168
|
+
return /(error|exception|traceback|failed|fatal|denied|unauthorized|timeout)/i.test(stringValue(value));
|
|
2208
2169
|
}
|
|
2209
2170
|
function setAgentRunState(agentRunState, relativePath, patch) {
|
|
2210
|
-
const key =
|
|
2171
|
+
const key = stringValue(relativePath || ".");
|
|
2211
2172
|
return {
|
|
2212
2173
|
...agentRunState || {},
|
|
2213
2174
|
[key]: {
|
|
@@ -2259,19 +2220,19 @@ function appendProcessExitAgentLogs(agentLogs, event, agentRunState, relativePat
|
|
|
2259
2220
|
function reconcileInventoryRunState(agentRunState, agents, timestamp) {
|
|
2260
2221
|
let next = agentRunState || {};
|
|
2261
2222
|
for (const agent of agents || []) {
|
|
2262
|
-
const path10 =
|
|
2223
|
+
const path10 = stringValue(agent?.relativePath || ".");
|
|
2263
2224
|
const current = next[path10];
|
|
2264
2225
|
if (agent?.status === "error") {
|
|
2265
2226
|
next = setAgentRunState(next, path10, {
|
|
2266
2227
|
status: "error",
|
|
2267
2228
|
lastEventAt: timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
2268
|
-
errorMessage:
|
|
2229
|
+
errorMessage: stringValue(agent.errorMessage || agent.error || "Agent setup failed")
|
|
2269
2230
|
});
|
|
2270
2231
|
} else if (agent?.status === "disabled") {
|
|
2271
2232
|
next = setAgentRunState(next, path10, {
|
|
2272
2233
|
status: "disabled",
|
|
2273
2234
|
lastEventAt: timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
2274
|
-
disabledReason:
|
|
2235
|
+
disabledReason: stringValue(agent.disabledReason || "System dependency not met locally"),
|
|
2275
2236
|
resolutionHints: Array.isArray(agent.resolutionHints) ? agent.resolutionHints : []
|
|
2276
2237
|
});
|
|
2277
2238
|
} else if (agent?.status === "online" && ["preparing", "error", "disabled"].includes(current?.status) && !current?.runId) {
|
|
@@ -2291,22 +2252,22 @@ function reconcileInventoryRunState(agentRunState, agents, timestamp) {
|
|
|
2291
2252
|
function normalizeStoredAgentLogEntry(entry) {
|
|
2292
2253
|
return {
|
|
2293
2254
|
timestamp: entry.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
2294
|
-
runId:
|
|
2295
|
-
eventType:
|
|
2296
|
-
level:
|
|
2297
|
-
message:
|
|
2298
|
-
stepId: entry.stepId ?
|
|
2255
|
+
runId: stringValue(entry.runId),
|
|
2256
|
+
eventType: stringValue(entry.eventType || "log"),
|
|
2257
|
+
level: stringValue(entry.level || "info"),
|
|
2258
|
+
message: stringValue(entry.message),
|
|
2259
|
+
stepId: entry.stepId ? stringValue(entry.stepId) : null,
|
|
2299
2260
|
current: entry.current ?? null,
|
|
2300
2261
|
total: entry.total ?? null,
|
|
2301
2262
|
success: entry.success ?? null,
|
|
2302
2263
|
error: entry.error ?? null,
|
|
2303
|
-
jobType: entry.jobType ?
|
|
2264
|
+
jobType: entry.jobType ? stringValue(entry.jobType) : null
|
|
2304
2265
|
};
|
|
2305
2266
|
}
|
|
2306
2267
|
function formatErrorMessage(error) {
|
|
2307
2268
|
if (!error) return null;
|
|
2308
|
-
if (typeof error === "object") return
|
|
2309
|
-
return
|
|
2269
|
+
if (typeof error === "object") return stringValue(error.message || "error");
|
|
2270
|
+
return stringValue(error);
|
|
2310
2271
|
}
|
|
2311
2272
|
function findNewAgents(previous, next) {
|
|
2312
2273
|
const seen = new Set(previous.map(agentKey).filter(Boolean));
|
|
@@ -2319,15 +2280,15 @@ function agentKey(agent) {
|
|
|
2319
2280
|
if (!agent || typeof agent !== "object") {
|
|
2320
2281
|
return "";
|
|
2321
2282
|
}
|
|
2322
|
-
return
|
|
2283
|
+
return stringValue(agent.localAgentId || agent.relativePath || agent.path || agent.manifestName || agent.id);
|
|
2323
2284
|
}
|
|
2324
2285
|
function agentLabel(event) {
|
|
2325
|
-
return
|
|
2286
|
+
return stringValue(event.relativePath || event.path || ".");
|
|
2326
2287
|
}
|
|
2327
2288
|
function jobLabel(event) {
|
|
2328
|
-
return
|
|
2289
|
+
return stringValue(event.runId || event.deployRequestId || event.relativePath || event.jobType || "job");
|
|
2329
2290
|
}
|
|
2330
|
-
function
|
|
2291
|
+
function stringValue(value) {
|
|
2331
2292
|
return value == null ? "" : String(value);
|
|
2332
2293
|
}
|
|
2333
2294
|
function processExitMessage(event) {
|
|
@@ -2344,11 +2305,11 @@ function agentPrepareLogMessage(event) {
|
|
|
2344
2305
|
return "Preparing agent";
|
|
2345
2306
|
}
|
|
2346
2307
|
if (event.type === "agent_prepare_completed") {
|
|
2347
|
-
if (event.status === "error") return
|
|
2348
|
-
if (event.status === "disabled") return
|
|
2308
|
+
if (event.status === "error") return stringValue(event.error || event.errorMessage || "Agent setup failed");
|
|
2309
|
+
if (event.status === "disabled") return stringValue(event.warning || event.disabledReason || "Agent disabled locally");
|
|
2349
2310
|
return "Agent ready";
|
|
2350
2311
|
}
|
|
2351
|
-
return
|
|
2312
|
+
return stringValue(event.message || event.stage || "Preparing agent");
|
|
2352
2313
|
}
|
|
2353
2314
|
function textTail(value, limit = 800) {
|
|
2354
2315
|
const text = String(value || "").trim().replace(/\s+/g, " ");
|
|
@@ -2500,7 +2461,7 @@ function buildLocalServeEventStreamArgs({ agentsDir: agentsDir2 = "./agents", co
|
|
|
2500
2461
|
}
|
|
2501
2462
|
|
|
2502
2463
|
// src/version.js
|
|
2503
|
-
var CLI_VERSION = true ? "0.0.
|
|
2464
|
+
var CLI_VERSION = true ? "0.0.44" : process.env.npm_package_version || "0.0.0-dev";
|
|
2504
2465
|
|
|
2505
2466
|
// src/dev-server.js
|
|
2506
2467
|
var __dirname = path8.dirname(fileURLToPath(import.meta.url));
|
|
@@ -2513,6 +2474,7 @@ var logStore = null;
|
|
|
2513
2474
|
var agentsDir = "";
|
|
2514
2475
|
var activeServeAgentsDir = "";
|
|
2515
2476
|
var collectionId = "";
|
|
2477
|
+
var activeAuthLogin = null;
|
|
2516
2478
|
async function startDevServer({
|
|
2517
2479
|
port = 3859,
|
|
2518
2480
|
agents = "",
|
|
@@ -2620,8 +2582,6 @@ async function handleApi(req, res, pathname, parsed) {
|
|
|
2620
2582
|
return apiServeState(req, res);
|
|
2621
2583
|
case "/api/auth":
|
|
2622
2584
|
return apiAuth(req, res);
|
|
2623
|
-
case "/api/workspaces":
|
|
2624
|
-
return await apiWorkspaces(req, res);
|
|
2625
2585
|
default:
|
|
2626
2586
|
return jsonResponse(res, { error: "not found" }, 404);
|
|
2627
2587
|
}
|
|
@@ -2784,10 +2744,6 @@ function apiAuth(req, res) {
|
|
|
2784
2744
|
backends
|
|
2785
2745
|
});
|
|
2786
2746
|
}
|
|
2787
|
-
async function apiWorkspaces(req, res) {
|
|
2788
|
-
const result = await listCloudWorkspaces({ timeoutMs: 15e3 });
|
|
2789
|
-
jsonResponse(res, result);
|
|
2790
|
-
}
|
|
2791
2747
|
async function apiAuthSwitch(req, res, body) {
|
|
2792
2748
|
const backend = body.backend;
|
|
2793
2749
|
if (!backend || !BACKEND_TARGETS[backend]) {
|
|
@@ -2828,13 +2784,32 @@ async function loginOrActivateBackend({ backend, env, setupFn, statusFn, activat
|
|
|
2828
2784
|
}
|
|
2829
2785
|
async function apiAuthLogin(req, res, body) {
|
|
2830
2786
|
try {
|
|
2831
|
-
const result = await
|
|
2787
|
+
const result = await runSingleAuthLogin({ backend: body.backend });
|
|
2832
2788
|
const status = result.ok ? 200 : 400;
|
|
2833
2789
|
jsonResponse(res, result, status);
|
|
2834
2790
|
} catch (err) {
|
|
2835
2791
|
jsonResponse(res, { ok: false, error: err.message || "Login failed" }, 500);
|
|
2836
2792
|
}
|
|
2837
2793
|
}
|
|
2794
|
+
async function runSingleAuthLogin({ backend, loginFn } = {}) {
|
|
2795
|
+
if (activeAuthLogin?.promise) {
|
|
2796
|
+
if (activeAuthLogin.backend !== backend) {
|
|
2797
|
+
return {
|
|
2798
|
+
ok: false,
|
|
2799
|
+
error: `A Vendian sign-in is already in progress for ${activeAuthLogin.backend}. Finish that browser flow or wait for it to close, then try ${backend} again.`
|
|
2800
|
+
};
|
|
2801
|
+
}
|
|
2802
|
+
return activeAuthLogin.promise;
|
|
2803
|
+
}
|
|
2804
|
+
const executeLogin = loginFn || loginOrActivateBackend;
|
|
2805
|
+
const promise = Promise.resolve().then(() => executeLogin({ backend })).finally(() => {
|
|
2806
|
+
if (activeAuthLogin?.promise === promise) {
|
|
2807
|
+
activeAuthLogin = null;
|
|
2808
|
+
}
|
|
2809
|
+
});
|
|
2810
|
+
activeAuthLogin = { backend, promise };
|
|
2811
|
+
return promise;
|
|
2812
|
+
}
|
|
2838
2813
|
async function apiValidate(req, res, body) {
|
|
2839
2814
|
const targetPath = body.path || agentsDir;
|
|
2840
2815
|
const invocation = await preparePythonVendianInvocation(
|
|
@@ -2856,9 +2831,6 @@ async function apiValidate(req, res, body) {
|
|
|
2856
2831
|
function resolveServeStartTarget(body = {}, current = {}) {
|
|
2857
2832
|
const targetDir = body.agentsDir || current.agentsDir || "";
|
|
2858
2833
|
const targetCollection = body.collectionId || current.collectionId || "";
|
|
2859
|
-
if (!targetCollection) {
|
|
2860
|
-
return { ok: false, error: "Choose a workspace before starting local serve." };
|
|
2861
|
-
}
|
|
2862
2834
|
return { ok: true, agentsDir: targetDir, collectionId: targetCollection };
|
|
2863
2835
|
}
|
|
2864
2836
|
function devServerStateAfterServeStart(current = {}, target = {}) {
|
|
@@ -3144,6 +3116,59 @@ init_paths();
|
|
|
3144
3116
|
init_setup();
|
|
3145
3117
|
init_process();
|
|
3146
3118
|
|
|
3119
|
+
// src/collections.js
|
|
3120
|
+
init_process();
|
|
3121
|
+
async function listCloudCollections({
|
|
3122
|
+
env = process.env,
|
|
3123
|
+
platform = process.platform,
|
|
3124
|
+
prepareInvocation = preparePythonVendianInvocation,
|
|
3125
|
+
run: run2 = runCapture,
|
|
3126
|
+
onProgress = null,
|
|
3127
|
+
timeoutMs = 2e4
|
|
3128
|
+
} = {}) {
|
|
3129
|
+
const progress = typeof onProgress === "function" ? onProgress : () => {
|
|
3130
|
+
};
|
|
3131
|
+
progress("Checking managed Python runtime");
|
|
3132
|
+
const invocation = await prepareInvocation(["cloud", "collections", "list", "--json"], {
|
|
3133
|
+
env,
|
|
3134
|
+
platform,
|
|
3135
|
+
onProgress: progress,
|
|
3136
|
+
skipAutoUpdate: true
|
|
3137
|
+
});
|
|
3138
|
+
progress("Loading collections from Vendian");
|
|
3139
|
+
const result = run2(invocation.command, invocation.args, { env: invocation.env, timeout: timeoutMs });
|
|
3140
|
+
if (!result.ok) {
|
|
3141
|
+
return {
|
|
3142
|
+
ok: false,
|
|
3143
|
+
collections: [],
|
|
3144
|
+
error: (result.stderr || result.stdout || `collection list exited with code ${result.status}`).trim()
|
|
3145
|
+
};
|
|
3146
|
+
}
|
|
3147
|
+
try {
|
|
3148
|
+
return { ok: true, collections: normalizeCollectionList(JSON.parse(result.stdout)), error: "" };
|
|
3149
|
+
} catch (error) {
|
|
3150
|
+
const message = error && typeof error.message === "string" ? error.message : String(error);
|
|
3151
|
+
return { ok: false, collections: [], error: `Could not parse collection list: ${message}` };
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
function normalizeCollectionList(payload) {
|
|
3155
|
+
const data = payload && typeof payload === "object" && "data" in payload ? payload.data : payload;
|
|
3156
|
+
const raw = Array.isArray(data) ? data : data && typeof data === "object" && Array.isArray(data.data) ? data.data : data && typeof data === "object" && Array.isArray(data.collections) ? data.collections : [];
|
|
3157
|
+
return raw.filter((item) => item && typeof item === "object").map((item) => ({
|
|
3158
|
+
id: stringValue2(item.id),
|
|
3159
|
+
name: stringValue2(item.name || item.slug || item.id || "Unnamed collection"),
|
|
3160
|
+
slug: stringValue2(item.slug)
|
|
3161
|
+
})).filter((item) => item.id);
|
|
3162
|
+
}
|
|
3163
|
+
function collectionLabel(collection) {
|
|
3164
|
+
const name = collection?.name || collection?.slug || collection?.id || "Unnamed collection";
|
|
3165
|
+
const slug = collection?.slug && collection.slug !== name ? ` (${collection.slug})` : "";
|
|
3166
|
+
return `${name}${slug}`;
|
|
3167
|
+
}
|
|
3168
|
+
function stringValue2(value) {
|
|
3169
|
+
return value == null ? "" : String(value).trim();
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3147
3172
|
// src/ui/figures.js
|
|
3148
3173
|
var supportsUnicode = process.platform !== "win32" || Boolean(
|
|
3149
3174
|
process.env.WT_SESSION || // Windows Terminal
|
|
@@ -3443,7 +3468,7 @@ async function connectCustomUrl({ env, platform }) {
|
|
|
3443
3468
|
var COMMAND_GROUPS = [
|
|
3444
3469
|
{ section: "Getting started", commands: [
|
|
3445
3470
|
{ cmd: "vendian login", desc: "Sign in and set up your computer" },
|
|
3446
|
-
{ cmd: "vendian init --output-dir ./agents", desc: "Set up
|
|
3471
|
+
{ cmd: "vendian init --output-dir ./agents", desc: "Set up SDK docs" }
|
|
3447
3472
|
] },
|
|
3448
3473
|
{ section: "Building agents", commands: [
|
|
3449
3474
|
{ cmd: 'vendian create "My Agent" --output-dir .', desc: "Create a new agent" },
|
|
@@ -3567,13 +3592,13 @@ async function pickSingleAgentToRun({ env, platform, candidates }) {
|
|
|
3567
3592
|
if (idx === null || idx === -1) return null;
|
|
3568
3593
|
return agents[idx];
|
|
3569
3594
|
}
|
|
3570
|
-
async function
|
|
3595
|
+
async function chooseCloudCollection({ env, platform, loadingTitle, pickerTitle } = {}) {
|
|
3571
3596
|
print("");
|
|
3572
|
-
process.stdout.write(` ${col.gray(loadingTitle || "Loading
|
|
3597
|
+
process.stdout.write(` ${col.gray(loadingTitle || "Loading collections...")}
|
|
3573
3598
|
`);
|
|
3574
|
-
let
|
|
3599
|
+
let result;
|
|
3575
3600
|
try {
|
|
3576
|
-
|
|
3601
|
+
result = await listCloudCollections({
|
|
3577
3602
|
env,
|
|
3578
3603
|
platform,
|
|
3579
3604
|
onProgress: (msg) => {
|
|
@@ -3589,28 +3614,28 @@ async function chooseCloudWorkspace({ env, platform, loadingTitle, pickerTitle }
|
|
|
3589
3614
|
await pressEnterToContinue({ grabActive: false });
|
|
3590
3615
|
return null;
|
|
3591
3616
|
}
|
|
3592
|
-
if (!
|
|
3617
|
+
if (!result.ok) {
|
|
3593
3618
|
print(col.red(`
|
|
3594
|
-
${fig.cross} ${
|
|
3619
|
+
${fig.cross} ${result.error}`));
|
|
3595
3620
|
await pressEnterToContinue({ grabActive: false });
|
|
3596
3621
|
return null;
|
|
3597
3622
|
}
|
|
3598
|
-
if (
|
|
3599
|
-
if (
|
|
3600
|
-
const
|
|
3601
|
-
return { id:
|
|
3623
|
+
if (result.collections.length === 0) return null;
|
|
3624
|
+
if (result.collections.length === 1) {
|
|
3625
|
+
const collection2 = result.collections[0];
|
|
3626
|
+
return { id: collection2.id || "", label: collectionLabel(collection2) };
|
|
3602
3627
|
}
|
|
3603
3628
|
clearScreen();
|
|
3604
3629
|
print("");
|
|
3605
3630
|
printHeader({ env, platform });
|
|
3606
3631
|
print("");
|
|
3607
|
-
print(col.bold(` ${pickerTitle || "Choose a
|
|
3632
|
+
print(col.bold(` ${pickerTitle || "Choose a collection:"}`));
|
|
3608
3633
|
print("");
|
|
3609
|
-
const items =
|
|
3634
|
+
const items = result.collections.map((collection2) => collectionLabel(collection2));
|
|
3610
3635
|
const idx = await pickMenu(items, { allowBack: true });
|
|
3611
3636
|
if (idx === null || idx === -1) return null;
|
|
3612
|
-
const
|
|
3613
|
-
return { id:
|
|
3637
|
+
const collection = result.collections[idx];
|
|
3638
|
+
return { id: collection.id || "", label: collectionLabel(collection) };
|
|
3614
3639
|
}
|
|
3615
3640
|
async function showCreate({ env, platform }) {
|
|
3616
3641
|
clearScreen();
|
|
@@ -3631,19 +3656,19 @@ async function showRun({ env, platform }) {
|
|
|
3631
3656
|
const candidates = findAgentDirectoryCandidates();
|
|
3632
3657
|
const picked = await pickSingleAgentToRun({ env, platform, candidates });
|
|
3633
3658
|
if (!picked) return "home";
|
|
3634
|
-
const
|
|
3659
|
+
const collection = await chooseCloudCollection({
|
|
3635
3660
|
env,
|
|
3636
3661
|
platform,
|
|
3637
3662
|
loadingTitle: "Getting ready to run this agent...",
|
|
3638
|
-
pickerTitle: "Which
|
|
3663
|
+
pickerTitle: "Which collection should own this run?"
|
|
3639
3664
|
});
|
|
3640
|
-
if (!
|
|
3665
|
+
if (!collection) return "home";
|
|
3641
3666
|
const inputJson = await ask("Input JSON", "{}");
|
|
3642
3667
|
await withOutputMode(
|
|
3643
3668
|
`Running ${picked.displayName}`,
|
|
3644
3669
|
() => forwardToPythonVendian(buildLocalRunArgs({
|
|
3645
3670
|
agentPath: picked.path,
|
|
3646
|
-
collectionId:
|
|
3671
|
+
collectionId: collection.id,
|
|
3647
3672
|
inputJson: inputJson || "{}"
|
|
3648
3673
|
}), { env, platform })
|
|
3649
3674
|
);
|
|
@@ -3654,16 +3679,15 @@ async function showServe({ env, platform }) {
|
|
|
3654
3679
|
const picked = await pickAgentsToRun({ env, platform, candidates });
|
|
3655
3680
|
if (!picked) return "home";
|
|
3656
3681
|
const { agentsDir: agentsDir2 } = picked;
|
|
3657
|
-
|
|
3682
|
+
return await runServeDashboard({
|
|
3658
3683
|
env,
|
|
3659
3684
|
platform,
|
|
3660
|
-
|
|
3661
|
-
|
|
3685
|
+
agentsDir: agentsDir2,
|
|
3686
|
+
collectionId: "",
|
|
3687
|
+
collectionLabel: "Admin Local Lab"
|
|
3662
3688
|
});
|
|
3663
|
-
if (!workspace) return "home";
|
|
3664
|
-
return await runServeDashboard({ env, platform, agentsDir: agentsDir2, collectionId: workspace.id, wsLabel: workspace.label });
|
|
3665
3689
|
}
|
|
3666
|
-
async function runServeDashboard({ env, platform, agentsDir: agentsDir2, collectionId: collectionId2,
|
|
3690
|
+
async function runServeDashboard({ env, platform, agentsDir: agentsDir2, collectionId: collectionId2, collectionLabel: collectionLabel2 = "" }) {
|
|
3667
3691
|
let state = initialServeState();
|
|
3668
3692
|
let child = null;
|
|
3669
3693
|
let ctrlCArmed = false;
|
|
@@ -3683,7 +3707,7 @@ async function runServeDashboard({ env, platform, agentsDir: agentsDir2, collect
|
|
|
3683
3707
|
print(col.gray("\u2550".repeat(W)));
|
|
3684
3708
|
print(` ${col.cyan(col.bold("\u2191 VENDIAN"))} ${col.bold("Serving agents")}`);
|
|
3685
3709
|
print(` ${col.gray("Dir:")} ${agentsDir2}`);
|
|
3686
|
-
print(` ${col.gray("
|
|
3710
|
+
print(` ${col.gray("Scope:")} ${collectionLabel2 || collectionId2 || "Admin Local Lab"}`);
|
|
3687
3711
|
print(col.gray(hr(W)));
|
|
3688
3712
|
print(col.gray(" S stop gracefully | ^c ^c exit"));
|
|
3689
3713
|
print(col.gray(hr(W)));
|
|
@@ -4142,7 +4166,7 @@ function helpText() {
|
|
|
4142
4166
|
" vendian login Sign in and prepare the local runtime",
|
|
4143
4167
|
" vendian doctor Check local bootstrap health",
|
|
4144
4168
|
" vendian update Update the managed runtime",
|
|
4145
|
-
" vendian init Write SDK agent docs into a
|
|
4169
|
+
" vendian init Write SDK agent docs into a folder",
|
|
4146
4170
|
' vendian create "My Agent" Scaffold a new agent from templates',
|
|
4147
4171
|
" vendian dev Open the dev UI (alias for bare vendian)",
|
|
4148
4172
|
" vendian <command> Run a managed Python SDK/cloud command",
|
package/dev-ui/css/app.css
CHANGED
|
@@ -152,22 +152,10 @@ body { font-family: var(--font); background: var(--bg-page); color: var(--text);
|
|
|
152
152
|
.serve-section-title { font-size: 11px; font-weight: 700; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
153
153
|
.serve-section-desc { font-size: 12.5px; color: var(--text-secondary); margin-top: 3px; }
|
|
154
154
|
.serve-section-meta { font-size: 11.5px; color: var(--text-dim); white-space: nowrap; padding-top: 1px; }
|
|
155
|
-
#workspace-picker { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px; margin-bottom: 6px; }
|
|
156
155
|
.serve-select-bar { display: flex; align-items: center; gap: 9px; padding: 10px 14px; border-radius: 8px; background: var(--bg-card); border: 1px solid var(--border); box-shadow: var(--shadow); margin-bottom: 14px; font-size: 13px; font-weight: 600; cursor: pointer; user-select: none; }
|
|
157
156
|
.serve-select-bar:hover { background: var(--bg-hover); }
|
|
158
157
|
.serve-select-bar input { width: 16px; height: 16px; accent-color: var(--accent-solid); cursor: pointer; }
|
|
159
158
|
.serve-select-bar .count { margin-left: auto; font-weight: 400; color: var(--text-dim); font-size: 12px; }
|
|
160
|
-
.workspace-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 14px 16px; text-align: left; cursor: pointer; transition: all var(--transition); box-shadow: var(--shadow); font: inherit; color: inherit; }
|
|
161
|
-
.workspace-card:hover { border-color: var(--border-hover); transform: translateY(-1px); box-shadow: var(--shadow-lg); }
|
|
162
|
-
.workspace-card.selected { border-color: var(--accent); background: var(--accent-bg); }
|
|
163
|
-
.workspace-card-title-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
|
|
164
|
-
.workspace-card-title { font-size: 13.5px; font-weight: 650; color: var(--text); }
|
|
165
|
-
.workspace-card-slug { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
|
|
166
|
-
.workspace-card-id { font-size: 11px; font-family: var(--mono); color: var(--text-dim); margin-top: 10px; overflow: hidden; text-overflow: ellipsis; }
|
|
167
|
-
.workspace-card-badge { font-size: 10px; font-weight: 700; padding: 3px 8px; border-radius: 4px; border: 1px solid var(--border); color: var(--text-dim); background: var(--bg-elevated); white-space: nowrap; }
|
|
168
|
-
.workspace-card.selected .workspace-card-badge { color: var(--accent); border-color: var(--accent-border); background: white; }
|
|
169
|
-
.workspace-empty { padding: 14px 16px; border-radius: var(--radius); background: var(--bg-card); border: 1px dashed var(--border); color: var(--text-secondary); font-size: 12.5px; }
|
|
170
|
-
.workspace-empty.workspace-error { color: var(--red); border-color: var(--red-border); background: var(--red-bg); }
|
|
171
159
|
.serve-folder { margin-bottom: 20px; }
|
|
172
160
|
.serve-folder-title { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; font-size: 10.5px; font-weight: 700; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
173
161
|
.serve-folder-title svg { width: 12px; height: 12px; opacity: 0.5; }
|
package/dev-ui/index.html
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
<div class="layout">
|
|
25
25
|
<aside class="sidebar">
|
|
26
|
-
<div class="nav-label">
|
|
26
|
+
<div class="nav-label">Agents</div>
|
|
27
27
|
<div class="nav-item active" data-page="agents">
|
|
28
28
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="2"/><rect x="14" y="3" width="7" height="7" rx="2"/><rect x="3" y="14" width="7" height="7" rx="2"/><rect x="14" y="14" width="7" height="7" rx="2"/></svg>
|
|
29
29
|
<span>Agents</span>
|
|
@@ -95,18 +95,9 @@
|
|
|
95
95
|
|
|
96
96
|
<div id="serve-picker-section">
|
|
97
97
|
<div class="serve-section-head">
|
|
98
|
-
<div>
|
|
99
|
-
<div class="serve-section-title">Workspace</div>
|
|
100
|
-
<div class="serve-section-desc">Choose the Vendian workspace that should own this daemon and its local runs.</div>
|
|
101
|
-
</div>
|
|
102
|
-
<div class="serve-section-meta" id="workspace-summary">Loading workspaces…</div>
|
|
103
|
-
</div>
|
|
104
|
-
<div id="workspace-picker"></div>
|
|
105
|
-
|
|
106
|
-
<div class="serve-section-head" style="margin-top:20px">
|
|
107
98
|
<div>
|
|
108
99
|
<div class="serve-section-title">Agents</div>
|
|
109
|
-
<div class="serve-section-desc">Pick which local agents this
|
|
100
|
+
<div class="serve-section-desc">Pick which local agents this Local Lab session should expose.</div>
|
|
110
101
|
</div>
|
|
111
102
|
</div>
|
|
112
103
|
<div class="serve-select-bar" id="serve-select-bar">
|
package/dev-ui/js/app.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { state, api } from './state.js';
|
|
2
2
|
import { renderAgents } from './agents.js';
|
|
3
3
|
import { populateValidateGrid, runValidateAll, triggerValidateAgent } from './validate.js';
|
|
4
|
-
import { populateServePicker, toggleAll, startServe, stopServe, setServing, clearLogs
|
|
4
|
+
import { populateServePicker, toggleAll, startServe, stopServe, setServing, clearLogs } from './serve.js';
|
|
5
5
|
import { loadAuth } from './settings.js';
|
|
6
6
|
|
|
7
7
|
// ─── Navigation ───
|
|
@@ -20,10 +20,6 @@ window.addEventListener('navigate', (e) => {
|
|
|
20
20
|
if (page === 'validate' && path) triggerValidateAgent(path);
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
window.addEventListener('auth-changed', () => {
|
|
24
|
-
loadWorkspaces();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
23
|
// ─── Load agents ───
|
|
28
24
|
async function loadAgents() {
|
|
29
25
|
try {
|
|
@@ -96,8 +92,6 @@ document.getElementById('serve-select-bar').addEventListener('click', (e) => {
|
|
|
96
92
|
// ─── Init ───
|
|
97
93
|
loadAgents();
|
|
98
94
|
loadAuth();
|
|
99
|
-
loadWorkspaces();
|
|
100
95
|
checkStatus();
|
|
101
96
|
setInterval(checkStatus, 4000);
|
|
102
97
|
setInterval(loadAgents, 8000);
|
|
103
|
-
setInterval(loadWorkspaces, 15000);
|
package/dev-ui/js/serve.js
CHANGED
|
@@ -1,80 +1,5 @@
|
|
|
1
1
|
import { state, api, esc, groupByFolder, formatTime } from './state.js';
|
|
2
2
|
|
|
3
|
-
export async function loadWorkspaces() {
|
|
4
|
-
state.workspacesLoading = true;
|
|
5
|
-
state.workspacesError = '';
|
|
6
|
-
renderWorkspacePicker();
|
|
7
|
-
try {
|
|
8
|
-
const result = await api('/workspaces');
|
|
9
|
-
state.workspaces = Array.isArray(result.workspaces) ? result.workspaces : [];
|
|
10
|
-
const hasSelected = state.workspaces.some((workspace) => workspace.id === state.selectedWorkspaceId);
|
|
11
|
-
if (!hasSelected) {
|
|
12
|
-
state.selectedWorkspaceId = state.workspaces[0]?.id || '';
|
|
13
|
-
}
|
|
14
|
-
if (!result.ok) {
|
|
15
|
-
state.workspacesError = result.error || 'Could not load workspaces.';
|
|
16
|
-
}
|
|
17
|
-
} catch (error) {
|
|
18
|
-
state.workspaces = [];
|
|
19
|
-
state.selectedWorkspaceId = '';
|
|
20
|
-
state.workspacesError = error?.message || 'Could not load workspaces.';
|
|
21
|
-
} finally {
|
|
22
|
-
state.workspacesLoading = false;
|
|
23
|
-
renderWorkspacePicker();
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function renderWorkspacePicker() {
|
|
28
|
-
const container = document.getElementById('workspace-picker');
|
|
29
|
-
const summary = document.getElementById('workspace-summary');
|
|
30
|
-
if (!container || !summary) return;
|
|
31
|
-
|
|
32
|
-
if (state.workspacesLoading) {
|
|
33
|
-
summary.textContent = 'Loading workspaces…';
|
|
34
|
-
container.innerHTML = '<div class="workspace-empty">Loading workspaces from Vendian…</div>';
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (state.workspacesError) {
|
|
39
|
-
summary.textContent = 'Workspace required';
|
|
40
|
-
container.innerHTML = `<div class="workspace-empty workspace-error">${esc(state.workspacesError)}</div>`;
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!state.workspaces.length) {
|
|
45
|
-
summary.textContent = 'No workspace available';
|
|
46
|
-
container.innerHTML = '<div class="workspace-empty">Sign in to a Vendian environment to load workspaces.</div>';
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const selected = state.workspaces.find((workspace) => workspace.id === state.selectedWorkspaceId) || state.workspaces[0];
|
|
51
|
-
if (selected && state.selectedWorkspaceId !== selected.id) {
|
|
52
|
-
state.selectedWorkspaceId = selected.id || '';
|
|
53
|
-
}
|
|
54
|
-
summary.textContent = `${state.workspaces.length} workspace${state.workspaces.length === 1 ? '' : 's'}`;
|
|
55
|
-
container.innerHTML = state.workspaces.map((workspace) => {
|
|
56
|
-
const selectedClass = workspace.id === state.selectedWorkspaceId ? ' selected' : '';
|
|
57
|
-
const slug = workspace.slug && workspace.slug !== workspace.name ? `<div class="workspace-card-slug">${esc(workspace.slug)}</div>` : '';
|
|
58
|
-
return `<button type="button" class="workspace-card${selectedClass}" data-workspace-id="${esc(workspace.id)}">
|
|
59
|
-
<div class="workspace-card-title-row">
|
|
60
|
-
<div>
|
|
61
|
-
<div class="workspace-card-title">${esc(workspace.name || workspace.id)}</div>
|
|
62
|
-
${slug}
|
|
63
|
-
</div>
|
|
64
|
-
<div class="workspace-card-badge">${workspace.id === state.selectedWorkspaceId ? 'Selected' : 'Choose'}</div>
|
|
65
|
-
</div>
|
|
66
|
-
<div class="workspace-card-id">${esc(workspace.id)}</div>
|
|
67
|
-
</button>`;
|
|
68
|
-
}).join('');
|
|
69
|
-
|
|
70
|
-
container.querySelectorAll('[data-workspace-id]').forEach((button) => {
|
|
71
|
-
button.addEventListener('click', () => {
|
|
72
|
-
state.selectedWorkspaceId = button.dataset.workspaceId || '';
|
|
73
|
-
renderWorkspacePicker();
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
3
|
// ─── Picker ───
|
|
79
4
|
let userSelections = null; // null = all selected (default), Set = explicit selections
|
|
80
5
|
|
|
@@ -160,9 +85,8 @@ export function getSelectedPaths() {
|
|
|
160
85
|
// ─── Controls ───
|
|
161
86
|
export async function startServe() {
|
|
162
87
|
const selected = getSelectedPaths();
|
|
163
|
-
if (!state.selectedWorkspaceId) { alert('Choose a workspace before starting local serve.'); return; }
|
|
164
88
|
if (!selected.length) { alert('Select at least one agent.'); return; }
|
|
165
|
-
const body = {
|
|
89
|
+
const body = {};
|
|
166
90
|
if (selected.length < state.agents.length) {
|
|
167
91
|
body.agentsDir = commonParent(selected);
|
|
168
92
|
}
|
package/dev-ui/js/state.js
CHANGED