ltcai 4.0.0 → 4.1.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/README.md +42 -33
- package/desktop/electron/main.cjs +44 -0
- package/docs/CHANGELOG.md +106 -0
- package/docs/REALTIME_COLLABORATION.md +3 -3
- package/docs/V3_FRONTEND.md +9 -8
- package/docs/V4_1_FRONTEND_ARCHITECTURE_REVIEW.md +65 -0
- package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +70 -0
- package/docs/V4_1_VALIDATION_REPORT.md +47 -0
- package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +95 -45
- package/docs/kg-schema.md +6 -2
- package/docs/spec-vs-impl.md +10 -10
- package/frontend/index.html +24 -0
- package/frontend/openapi.json +14190 -0
- package/frontend/src/App.tsx +184 -0
- package/frontend/src/api/client.ts +317 -0
- package/frontend/src/api/openapi.ts +16637 -0
- package/frontend/src/components/primitives.tsx +204 -0
- package/frontend/src/components/ui/badge.tsx +27 -0
- package/frontend/src/components/ui/button.tsx +37 -0
- package/frontend/src/components/ui/card.tsx +22 -0
- package/frontend/src/components/ui/input.tsx +16 -0
- package/frontend/src/components/ui/textarea.tsx +16 -0
- package/frontend/src/lib/utils.ts +33 -0
- package/frontend/src/main.tsx +23 -0
- package/frontend/src/pages/Act.tsx +245 -0
- package/frontend/src/pages/Ask.tsx +200 -0
- package/frontend/src/pages/Brain.tsx +267 -0
- package/frontend/src/pages/Capture.tsx +158 -0
- package/frontend/src/pages/Library.tsx +187 -0
- package/frontend/src/pages/System.tsx +344 -0
- package/frontend/src/routes.ts +85 -0
- package/frontend/src/store/appStore.ts +54 -0
- package/frontend/src/styles.css +107 -0
- package/kg_schema.py +2 -603
- package/knowledge_graph.py +37 -4958
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/admin.py +15 -16
- package/latticeai/api/agents.py +13 -6
- package/latticeai/api/auth.py +19 -11
- package/latticeai/api/invitations.py +100 -0
- package/latticeai/api/knowledge_graph.py +4 -11
- package/latticeai/api/plugins.py +3 -6
- package/latticeai/api/realtime.py +4 -7
- package/latticeai/api/setup.py +5 -4
- package/latticeai/api/static_routes.py +13 -16
- package/latticeai/api/ui_redirects.py +26 -0
- package/latticeai/api/workflow_designer.py +39 -6
- package/latticeai/api/workspace.py +24 -10
- package/latticeai/app_factory.py +88 -17
- package/latticeai/brain/_kg_common.py +1123 -0
- package/latticeai/brain/discovery.py +1455 -0
- package/latticeai/brain/documents.py +218 -0
- package/latticeai/brain/ingest.py +644 -0
- package/latticeai/brain/projection.py +561 -0
- package/latticeai/brain/provenance.py +401 -0
- package/latticeai/brain/retrieval.py +1316 -0
- package/latticeai/brain/schema.py +640 -0
- package/latticeai/brain/store.py +216 -0
- package/latticeai/brain/write_master.py +225 -0
- package/latticeai/core/invitations.py +131 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/multi_agent.py +1 -1
- package/latticeai/core/policy.py +54 -0
- package/latticeai/core/realtime.py +65 -44
- package/latticeai/core/sessions.py +31 -5
- package/latticeai/core/users.py +147 -0
- package/latticeai/core/workspace_os.py +420 -20
- package/latticeai/services/agent_runtime.py +242 -4
- package/latticeai/services/run_executor.py +328 -0
- package/latticeai/services/workspace_service.py +27 -19
- package/package.json +54 -27
- package/scripts/build_frontend_assets.mjs +38 -0
- package/scripts/bump_version.py +1 -1
- package/scripts/export_openapi.py +31 -0
- package/scripts/lint_frontend.mjs +86 -0
- package/scripts/run_python.mjs +47 -0
- package/src-tauri/Cargo.lock +4833 -0
- package/src-tauri/Cargo.toml +19 -0
- package/src-tauri/build.rs +3 -0
- package/src-tauri/capabilities/default.json +7 -0
- package/src-tauri/src/main.rs +78 -0
- package/src-tauri/tauri.conf.json +36 -0
- package/static/app/asset-manifest.json +32 -0
- package/static/app/assets/core-CwxXejkd.js +2 -0
- package/static/app/assets/core-CwxXejkd.js.map +1 -0
- package/static/app/assets/index-CJRAzNnf.js +333 -0
- package/static/app/assets/index-CJRAzNnf.js.map +1 -0
- package/static/app/assets/index-CSwBBgf4.css +2 -0
- package/static/app/index.html +25 -0
- package/static/manifest.json +2 -2
- package/static/sw.js +4 -4
- package/scripts/build_v3_assets.mjs +0 -170
- package/scripts/lint_v3.mjs +0 -97
- package/static/account.html +0 -113
- package/static/activity.html +0 -73
- package/static/admin.html +0 -486
- package/static/agents.html +0 -139
- package/static/chat.html +0 -841
- package/static/css/reference/account.css +0 -439
- package/static/css/reference/admin.css +0 -610
- package/static/css/reference/base.css +0 -1661
- package/static/css/reference/chat.css +0 -4623
- package/static/css/reference/graph.css +0 -1016
- package/static/css/responsive.css +0 -861
- package/static/graph.html +0 -122
- package/static/platform.css +0 -104
- package/static/plugins.html +0 -136
- package/static/scripts/account.js +0 -238
- package/static/scripts/admin.js +0 -1614
- package/static/scripts/chat.js +0 -5081
- package/static/scripts/graph.js +0 -1804
- package/static/scripts/platform.js +0 -64
- package/static/scripts/ux.js +0 -167
- package/static/scripts/workspace.js +0 -948
- package/static/v3/asset-manifest.json +0 -56
- package/static/v3/css/lattice.base.49deefb5.css +0 -128
- package/static/v3/css/lattice.base.css +0 -128
- package/static/v3/css/lattice.components.cde18231.css +0 -472
- package/static/v3/css/lattice.components.css +0 -472
- package/static/v3/css/lattice.shell.29d36d85.css +0 -452
- package/static/v3/css/lattice.shell.css +0 -452
- package/static/v3/css/lattice.tokens.304cbc40.css +0 -135
- package/static/v3/css/lattice.tokens.css +0 -135
- package/static/v3/css/lattice.views.0a18b6c5.css +0 -360
- package/static/v3/css/lattice.views.css +0 -360
- package/static/v3/index.html +0 -68
- package/static/v3/js/app.356e6452.js +0 -26
- package/static/v3/js/app.js +0 -26
- package/static/v3/js/core/api.7a308b89.js +0 -568
- package/static/v3/js/core/api.js +0 -568
- package/static/v3/js/core/components.f25b3b93.js +0 -230
- package/static/v3/js/core/components.js +0 -230
- package/static/v3/js/core/dom.a2773eb0.js +0 -148
- package/static/v3/js/core/dom.js +0 -148
- package/static/v3/js/core/router.584570f2.js +0 -37
- package/static/v3/js/core/router.js +0 -37
- package/static/v3/js/core/routes.7222343d.js +0 -93
- package/static/v3/js/core/routes.js +0 -93
- package/static/v3/js/core/shell.a1657f20.js +0 -391
- package/static/v3/js/core/shell.js +0 -391
- package/static/v3/js/core/store.204a08b2.js +0 -113
- package/static/v3/js/core/store.js +0 -113
- package/static/v3/js/views/admin-audit.660a1fb1.js +0 -185
- package/static/v3/js/views/admin-audit.js +0 -185
- package/static/v3/js/views/admin-permissions.a7ae5f09.js +0 -177
- package/static/v3/js/views/admin-permissions.js +0 -177
- package/static/v3/js/views/admin-policies.3658fd86.js +0 -102
- package/static/v3/js/views/admin-policies.js +0 -102
- package/static/v3/js/views/admin-private-vpc.7d342d36.js +0 -135
- package/static/v3/js/views/admin-private-vpc.js +0 -135
- package/static/v3/js/views/admin-security.07c66b72.js +0 -180
- package/static/v3/js/views/admin-security.js +0 -180
- package/static/v3/js/views/admin-users.03bac88c.js +0 -168
- package/static/v3/js/views/admin-users.js +0 -168
- package/static/v3/js/views/agents.014d0b74.js +0 -541
- package/static/v3/js/views/agents.js +0 -541
- package/static/v3/js/views/chat.e6dd7dd0.js +0 -601
- package/static/v3/js/views/chat.js +0 -601
- package/static/v3/js/views/files.adad14c1.js +0 -365
- package/static/v3/js/views/files.js +0 -365
- package/static/v3/js/views/graph-canvas.17c15d65.js +0 -509
- package/static/v3/js/views/graph-canvas.js +0 -509
- package/static/v3/js/views/home.24f8b8ae.js +0 -200
- package/static/v3/js/views/home.js +0 -200
- package/static/v3/js/views/hooks.37895880.js +0 -220
- package/static/v3/js/views/hooks.js +0 -220
- package/static/v3/js/views/hybrid-search.2fb63ed9.js +0 -194
- package/static/v3/js/views/hybrid-search.js +0 -194
- package/static/v3/js/views/knowledge-graph.5e40cbeb.js +0 -509
- package/static/v3/js/views/knowledge-graph.js +0 -509
- package/static/v3/js/views/marketplace.ab0583d4.js +0 -141
- package/static/v3/js/views/marketplace.js +0 -141
- package/static/v3/js/views/mcp.99b5c6a7.js +0 -114
- package/static/v3/js/views/mcp.js +0 -114
- package/static/v3/js/views/memory.4ebdf474.js +0 -147
- package/static/v3/js/views/memory.js +0 -147
- package/static/v3/js/views/models.a1ffa147.js +0 -256
- package/static/v3/js/views/models.js +0 -256
- package/static/v3/js/views/my-computer.d9d9ae1c.js +0 -463
- package/static/v3/js/views/my-computer.js +0 -463
- package/static/v3/js/views/pipeline.c522f1ce.js +0 -157
- package/static/v3/js/views/pipeline.js +0 -157
- package/static/v3/js/views/planning.9ac3e313.js +0 -153
- package/static/v3/js/views/planning.js +0 -153
- package/static/v3/js/views/settings.8631fa5e.js +0 -318
- package/static/v3/js/views/settings.js +0 -318
- package/static/v3/js/views/skills.c6c2f965.js +0 -109
- package/static/v3/js/views/skills.js +0 -109
- package/static/v3/js/views/tools.e4f11276.js +0 -108
- package/static/v3/js/views/tools.js +0 -108
- package/static/v3/js/views/workflows.26c57290.js +0 -128
- package/static/v3/js/views/workflows.js +0 -128
- package/static/workflows.html +0 -146
- package/static/workspace.css +0 -1121
- package/static/workspace.html +0 -357
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/* ============================================================================
|
|
2
|
-
* View: Workflows — workflow-driven agent execution.
|
|
3
|
-
* Trigger → agent chain → tools → memory → result. Reads workflow definitions
|
|
4
|
-
* and runs from the real workflow designer backend; runs a definition and shows
|
|
5
|
-
* the run ledger with replay. Unavailable state is explicit.
|
|
6
|
-
* ========================================================================== */
|
|
7
|
-
|
|
8
|
-
const STAGES = ["Trigger", "Agent chain", "Tools", "Memory", "Result"];
|
|
9
|
-
|
|
10
|
-
export async function render(ctx) {
|
|
11
|
-
const { h, c } = ctx;
|
|
12
|
-
const defsHost = h("div", c.loading({ lines: 3, block: true }));
|
|
13
|
-
const runsHost = h("div", c.loading({ lines: 3 }));
|
|
14
|
-
const defsSrc = h("span", c.sourceBadge("pending"));
|
|
15
|
-
const runsSrc = h("span", c.sourceBadge("pending"));
|
|
16
|
-
|
|
17
|
-
const root = h("div.lt3-stack-6",
|
|
18
|
-
c.viewHeader({
|
|
19
|
-
eyebrow: "Compute",
|
|
20
|
-
title: "Workflow Agents",
|
|
21
|
-
sub: "Repeatable automation: a trigger fires an agent chain that calls tools, reads and writes memory, and produces a result.",
|
|
22
|
-
}),
|
|
23
|
-
stageLegend(ctx),
|
|
24
|
-
h("section", c.sectionHead("Workflow definitions", defsSrc), defsHost),
|
|
25
|
-
c.panel({
|
|
26
|
-
head: h("div.lt3-row", { style: { "justify-content": "space-between", width: "100%" } },
|
|
27
|
-
h("div", h("div.lt3-eyebrow", "Activity"), h("h3.lt3-panel__title", "Recent runs")), runsSrc),
|
|
28
|
-
children: runsHost,
|
|
29
|
-
}),
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
loadDefs();
|
|
33
|
-
loadRuns();
|
|
34
|
-
return root;
|
|
35
|
-
|
|
36
|
-
function stageLegend(ctx2) {
|
|
37
|
-
return h("div.lt3-cluster", { style: { gap: "var(--lt3-space-2)" } }, STAGES.map((s, i) =>
|
|
38
|
-
h("div.lt3-row-2", { style: { gap: "var(--lt3-space-2)" } }, c.pill(s, i === STAGES.length - 1 ? "warn" : "info"), i < STAGES.length - 1 ? c.icon("arrow-right") : null)));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async function loadDefs() {
|
|
42
|
-
const res = await ctx.api.workflowDefinitions();
|
|
43
|
-
defsSrc.replaceChildren(c.sourceBadge(res.source));
|
|
44
|
-
const defs = normalizeDefs(res.data);
|
|
45
|
-
if (!defs.length) {
|
|
46
|
-
defsHost.replaceChildren(c.emptyState({ icon: "sitemap", title: "No workflows yet", body: res.source === "live" ? "Create a workflow definition to automate an agent chain." : "Start the backend to load workflows." }));
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
defsHost.replaceChildren(h("div.lt3-grid-auto", defs.map((w) => defCard(ctx, w))));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function defCard(ctx2, w) {
|
|
53
|
-
const nodes = w.nodes || (w.definition && w.definition.nodes) || [];
|
|
54
|
-
const triggers = nodes.filter((n) => n.type === "trigger").length || 1;
|
|
55
|
-
return c.card(h("div.lt3-stack-3",
|
|
56
|
-
h("div.lt3-row", { style: { "justify-content": "space-between", "align-items": "flex-start" } },
|
|
57
|
-
h("div", h("b", w.name || w.id), h("div.lt3-faint", { style: { "font-size": "var(--lt3-text-2xs)", "font-family": "var(--lt3-font-mono)" } }, w.id || "")),
|
|
58
|
-
c.pill(`${nodes.length || 0} nodes`),
|
|
59
|
-
),
|
|
60
|
-
w.description ? h("p.lt3-muted", { style: { margin: 0, "font-size": "var(--lt3-text-sm)" } }, w.description) : null,
|
|
61
|
-
h("div.lt3-cluster", (nodes.slice(0, 6)).map((n) => h("span.lt3-chip", c.icon(nodeIcon(n.type)), n.name || n.type))),
|
|
62
|
-
h("div.lt3-row-2",
|
|
63
|
-
h("button.lt3-btn.lt3-btn--primary.lt3-btn--sm", { on: { click: () => runDef(ctx2, w) } }, c.icon("player-play"), "Run"),
|
|
64
|
-
h("span.lt3-faint", { style: { "font-size": "var(--lt3-text-2xs)" } }, `${triggers} trigger${triggers === 1 ? "" : "s"}`),
|
|
65
|
-
),
|
|
66
|
-
), { interactive: false });
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async function runDef(ctx2, w) {
|
|
70
|
-
const res = await ctx2.api.runWorkflow(w.id, {});
|
|
71
|
-
ctx2.toast(res && res.ok ? `Ran ${w.name || w.id}` : "Run unavailable", res && res.ok ? "ok" : "err");
|
|
72
|
-
loadRuns();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async function loadRuns() {
|
|
76
|
-
const res = await ctx.api.workflowRuns();
|
|
77
|
-
runsSrc.replaceChildren(c.sourceBadge(res.source));
|
|
78
|
-
const runs = normalizeRuns(res.data);
|
|
79
|
-
if (!runs.length) {
|
|
80
|
-
runsHost.replaceChildren(c.emptyState({ icon: "history-off", title: "No runs yet", body: "Workflow runs will appear here once a workflow executes." }));
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
runsHost.replaceChildren(c.table(
|
|
84
|
-
[
|
|
85
|
-
{ key: "status", label: "Status", width: "1%", render: (r) => c.statePill(mapStatus(r.status)) },
|
|
86
|
-
{ key: "name", label: "Workflow", render: (r) => h("b", { style: { "font-size": "var(--lt3-text-sm)" } }, r.workflow_name || r.workflow_id || r.id) },
|
|
87
|
-
{ key: "when", label: "When", width: "1%", render: (r) => h("span.lt3-faint", { style: { "white-space": "nowrap", "font-size": "var(--lt3-text-2xs)" } }, fmtTime(r.created_at || r.started_at)) },
|
|
88
|
-
{ key: "act", label: "", width: "1%", render: (r) => h("button.lt3-btn.lt3-btn--ghost.lt3-btn--sm", { on: { click: () => replay(ctx, r) } }, c.icon("player-track-next"), "Replay") },
|
|
89
|
-
],
|
|
90
|
-
runs.slice(0, 20),
|
|
91
|
-
));
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async function replay(ctx2, r) {
|
|
95
|
-
const id = r.id || r.run_id;
|
|
96
|
-
const res = await ctx2.api.workflowReplay(id);
|
|
97
|
-
ctx2.toast(res && res.ok ? `Replay ready for ${id}` : "Replay unavailable", res && res.ok ? "ok" : "err");
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function normalizeDefs(data) {
|
|
102
|
-
if (!data) return [];
|
|
103
|
-
if (Array.isArray(data.workflows)) return data.workflows;
|
|
104
|
-
if (Array.isArray(data.definitions)) return data.definitions;
|
|
105
|
-
if (Array.isArray(data)) return data;
|
|
106
|
-
return [];
|
|
107
|
-
}
|
|
108
|
-
function normalizeRuns(data) {
|
|
109
|
-
if (!data) return [];
|
|
110
|
-
if (Array.isArray(data.runs)) return data.runs;
|
|
111
|
-
if (Array.isArray(data)) return data;
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
|
-
function nodeIcon(type) {
|
|
115
|
-
return { trigger: "bolt", agent: "robot", plugin: "puzzle", tool: "tool", output: "flag", memory: "brain" }[type] || "point";
|
|
116
|
-
}
|
|
117
|
-
function mapStatus(s) {
|
|
118
|
-
const v = String(s || "").toLowerCase();
|
|
119
|
-
if (v === "ok" || v === "completed" || v === "success") return "ready";
|
|
120
|
-
if (v === "failed" || v === "error") return "failed";
|
|
121
|
-
if (v === "running") return "active";
|
|
122
|
-
return v || "idle";
|
|
123
|
-
}
|
|
124
|
-
function fmtTime(ts) {
|
|
125
|
-
if (!ts) return "—";
|
|
126
|
-
try { const d = new Date(ts); return Number.isNaN(d.getTime()) ? String(ts) : d.toLocaleString(undefined, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" }); }
|
|
127
|
-
catch { return String(ts); }
|
|
128
|
-
}
|
package/static/workflows.html
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, interactive-widget=resizes-content" />
|
|
6
|
-
<title>Workflow Designer — Lattice AI</title>
|
|
7
|
-
<script src="/static/scripts/ux.js"></script>
|
|
8
|
-
<link rel="stylesheet" href="/static/css/tokens.css" />
|
|
9
|
-
<link rel="stylesheet" href="/static/platform.css" />
|
|
10
|
-
<link rel="stylesheet" href="/static/css/responsive.css" />
|
|
11
|
-
</head>
|
|
12
|
-
<body>
|
|
13
|
-
<main>
|
|
14
|
-
<h1>Workflow Designer</h1>
|
|
15
|
-
<p class="sub">Compose triggers, tools, skills, plugins, agents, conditions, and outputs into runnable workflows.</p>
|
|
16
|
-
|
|
17
|
-
<div class="row">
|
|
18
|
-
<button id="newBtn">+ New from template</button>
|
|
19
|
-
<button class="ghost" id="validateBtn">Validate</button>
|
|
20
|
-
<button class="ghost" id="saveBtn">Save</button>
|
|
21
|
-
<div class="spacer"></div>
|
|
22
|
-
</div>
|
|
23
|
-
|
|
24
|
-
<div class="section">
|
|
25
|
-
<label>Workflow name</label>
|
|
26
|
-
<input id="wfName" value="My workflow" />
|
|
27
|
-
<label>Nodes (JSON) — trigger → … → output</label>
|
|
28
|
-
<textarea id="wfNodes" spellcheck="false" style="min-height:240px"></textarea>
|
|
29
|
-
<pre id="validateOut" style="display:none"></pre>
|
|
30
|
-
</div>
|
|
31
|
-
|
|
32
|
-
<div class="section">
|
|
33
|
-
<h3>Saved workflows</h3>
|
|
34
|
-
<div id="list" class="grid"><div class="empty">Loading…</div></div>
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
<div class="section">
|
|
38
|
-
<h3>Run history</h3>
|
|
39
|
-
<div id="runs"><div class="empty">No runs yet.</div></div>
|
|
40
|
-
</div>
|
|
41
|
-
|
|
42
|
-
<div class="section">
|
|
43
|
-
<h3>Replay viewer</h3>
|
|
44
|
-
<div id="replay"><div class="empty">Select a workflow run.</div></div>
|
|
45
|
-
</div>
|
|
46
|
-
</main>
|
|
47
|
-
|
|
48
|
-
<script type="module">
|
|
49
|
-
import { mountHeader, api, escapeHtml, badge, toast } from "/static/scripts/platform.js";
|
|
50
|
-
mountHeader("/workflows");
|
|
51
|
-
|
|
52
|
-
const TEMPLATE = [
|
|
53
|
-
{ id: "trigger", type: "trigger", name: "Manual start", config: { trigger: "manual" }, next: "review" },
|
|
54
|
-
{ id: "review", type: "agent", name: "Multi-agent review", config: { goal: "Review the latest workspace changes", roles: ["planner", "executor", "reviewer"] }, next: "decide" },
|
|
55
|
-
{ id: "decide", type: "condition", name: "Passed?", config: { left: "last_output", op: "truthy" }, branches: { true: "done", false: "done" } },
|
|
56
|
-
{ id: "done", type: "output", name: "Output", config: {}, next: null },
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
function loadTemplate() {
|
|
60
|
-
document.getElementById("wfName").value = "My workflow";
|
|
61
|
-
document.getElementById("wfNodes").value = JSON.stringify(TEMPLATE, null, 2);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function readNodes() { return JSON.parse(document.getElementById("wfNodes").value); }
|
|
65
|
-
|
|
66
|
-
document.getElementById("newBtn").addEventListener("click", loadTemplate);
|
|
67
|
-
|
|
68
|
-
document.getElementById("validateBtn").addEventListener("click", async () => {
|
|
69
|
-
const out = document.getElementById("validateOut");
|
|
70
|
-
out.style.display = "block";
|
|
71
|
-
try {
|
|
72
|
-
const res = await api("/workflows/api/validate", { method: "POST", body: JSON.stringify({ name: document.getElementById("wfName").value, nodes: readNodes() }) });
|
|
73
|
-
out.textContent = res.ok ? "✓ Valid workflow" : "Errors:\n" + res.errors.join("\n");
|
|
74
|
-
} catch (err) { out.textContent = "Error: " + err.message; }
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
document.getElementById("saveBtn").addEventListener("click", async () => {
|
|
78
|
-
try {
|
|
79
|
-
await api("/workflows/api/definitions", { method: "POST", body: JSON.stringify({ name: document.getElementById("wfName").value, nodes: readNodes() }) });
|
|
80
|
-
toast("Workflow saved"); await loadList();
|
|
81
|
-
} catch (err) { toast(err.message); }
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
async function loadList() {
|
|
85
|
-
const data = await api("/workflows/api/definitions");
|
|
86
|
-
const list = document.getElementById("list");
|
|
87
|
-
const items = data.workflows || [];
|
|
88
|
-
if (!items.length) { list.innerHTML = `<div class="empty">No workflows yet.</div>`; return; }
|
|
89
|
-
list.innerHTML = items.map((w) => `
|
|
90
|
-
<div class="card">
|
|
91
|
-
<h3>${escapeHtml(w.name)}</h3>
|
|
92
|
-
<div class="meta">${(w.nodes||w.steps||[]).length} node(s) · ${escapeHtml(w.updated_at||w.created_at||"")}</div>
|
|
93
|
-
<div class="row" style="margin-top:12px">
|
|
94
|
-
<button data-run="${w.id}">Run</button>
|
|
95
|
-
<a class="btn ghost" href="/workflows/api/export/${w.id}" target="_blank">Export</a>
|
|
96
|
-
</div>
|
|
97
|
-
</div>`).join("");
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
document.getElementById("list").addEventListener("click", async (e) => {
|
|
101
|
-
const btn = e.target.closest("button[data-run]");
|
|
102
|
-
if (!btn) return;
|
|
103
|
-
btn.disabled = true;
|
|
104
|
-
try {
|
|
105
|
-
const res = await api(`/workflows/api/definitions/${btn.dataset.run}/run`, { method: "POST", body: JSON.stringify({ inputs: {} }) });
|
|
106
|
-
toast(`Run ${res.result.status} · ${res.result.step_count} steps`);
|
|
107
|
-
await loadRuns();
|
|
108
|
-
} catch (err) { toast(err.message); } finally { btn.disabled = false; }
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
async function loadRuns() {
|
|
112
|
-
const data = await api("/workflows/api/runs");
|
|
113
|
-
const runs = data.runs || [];
|
|
114
|
-
const box = document.getElementById("runs");
|
|
115
|
-
if (!runs.length) { box.innerHTML = `<div class="empty">No runs yet.</div>`; return; }
|
|
116
|
-
box.innerHTML = runs.map((r) => `
|
|
117
|
-
<div class="card" style="margin-bottom:10px">
|
|
118
|
-
<div class="row"><h3>${escapeHtml(r.name)}</h3><div class="spacer"></div>${badge(r.status)}</div>
|
|
119
|
-
<div class="meta">${escapeHtml(r.created_at)} · ${ (r.timeline||[]).length } steps</div>
|
|
120
|
-
<div class="row" style="margin-top:10px"><button class="ghost" data-replay="${r.id}">Replay</button></div>
|
|
121
|
-
<pre>${escapeHtml(JSON.stringify(r.timeline, null, 2))}</pre>
|
|
122
|
-
</div>`).join("");
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
document.getElementById("runs").addEventListener("click", async (e) => {
|
|
126
|
-
const btn = e.target.closest("button[data-replay]");
|
|
127
|
-
if (!btn) return;
|
|
128
|
-
const out = document.getElementById("replay");
|
|
129
|
-
out.innerHTML = `<div class="empty">Loading replay…</div>`;
|
|
130
|
-
try {
|
|
131
|
-
const data = await api(`/workflows/api/runs/${btn.dataset.replay}/replay`);
|
|
132
|
-
const frames = data.replay.frames || [];
|
|
133
|
-
out.innerHTML = frames.map((f) => `<div class="timeline-item">
|
|
134
|
-
<div class="row"><strong>${escapeHtml(f.event)}</strong><div class="spacer"></div>${badge(f.decision || "event")}</div>
|
|
135
|
-
<div class="t-meta">${escapeHtml(String(f.actor||""))} · ${escapeHtml(f.when||"")}</div>
|
|
136
|
-
<pre>${escapeHtml(JSON.stringify({ why: f.why, input: f.input, output: f.output }, null, 2))}</pre>
|
|
137
|
-
</div>`).join("") || `<div class="empty">No replay frames.</div>`;
|
|
138
|
-
} catch (err) { out.innerHTML = `<div class="empty">${escapeHtml(err.message)}</div>`; }
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
loadTemplate();
|
|
142
|
-
loadList().catch((e) => toast(e.message));
|
|
143
|
-
loadRuns().catch(() => {});
|
|
144
|
-
</script>
|
|
145
|
-
</body>
|
|
146
|
-
</html>
|