@clemrig/flow-ui 0.2.0 → 0.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/README.md +1 -1
- package/dist/static/dashboard.js +4 -0
- package/dist/static/follow.js +40 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ npm install -g @clemrig/flow-ui
|
|
|
11
11
|
flow-ui [RUNS_DIR] [--port PORT]
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
`flow-ui` serves the runs kept in `RUNS_DIR` (default: `.flow/runs`, where `@clemrig/flow` keeps them; a `RUNS_DIR` given must be an existing directory) on `127.0.0.1`, on `PORT` or a port the system chooses, prints its URL and stops on Ctrl-C. The page lists the runs with their workflow, status, current stage, duration, estimated cost and last update, and for a run its runtimes, a timeline with one block per stage (the agent call's model and settings, its tools and tokens, journal, decision), its correction counters and its result. It follows a running run as it goes. A result shows the run's validated data as JSON.
|
|
14
|
+
`flow-ui` serves the runs kept in `RUNS_DIR` (default: `.flow/runs`, where `@clemrig/flow` keeps them; a `RUNS_DIR` given must be an existing directory) on `127.0.0.1`, on `PORT` or a port the system chooses, prints its URL and stops on Ctrl-C. The page lists the runs with their workflow, status, current stage, duration, estimated cost and last update, and for a run its runtimes, a timeline with one block per stage (the agent call's model and settings, its tools and tokens, journal, decision), its correction counters and its result. It follows a running run as it goes, and follows the selected run again when it is resumed. A result shows the run's validated data as JSON.
|
|
15
15
|
|
|
16
16
|
To watch a run on another machine, forward the same port through SSH: `ssh -L 4321:127.0.0.1:4321 machine`, run `flow-ui --port 4321` there, and open `http://localhost:4321/`.
|
|
17
17
|
|
package/dist/static/dashboard.js
CHANGED
|
@@ -212,11 +212,15 @@ function updated(message) {
|
|
|
212
212
|
|
|
213
213
|
async function refreshRuns() {
|
|
214
214
|
try {
|
|
215
|
+
/** @type {{ title: string; label: string; runs: Summary[] }} */
|
|
215
216
|
const { title, label, runs } = await load("api/runs");
|
|
216
217
|
byId("title").textContent = title;
|
|
217
218
|
document.title = title;
|
|
218
219
|
byId("label").textContent = label;
|
|
219
220
|
showRuns(runs);
|
|
221
|
+
// A resumed run goes on under the same ID, running or already ended again.
|
|
222
|
+
const listed = runs.find((run) => run.id === selected);
|
|
223
|
+
if (listed?.updatedAt) follow.listed(selected, listed.updatedAt);
|
|
220
224
|
updated(`Updated ${new Date().toLocaleTimeString()}`);
|
|
221
225
|
} catch (error) {
|
|
222
226
|
unavailable(error);
|
package/dist/static/follow.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Follows the selected run: asks only for the events not received yet, one
|
|
4
4
|
* request after the answer to the previous one, drops an answer for a selection
|
|
5
|
-
* that has changed since, and stops once the run has ended or does not exist
|
|
5
|
+
* that has changed since, and stops once the run has ended or does not exist,
|
|
6
|
+
* until the run list shows it updated since, as a resumed run is.
|
|
6
7
|
* Requests and timers are given, so that this logic is tested without a browser.
|
|
7
8
|
*/
|
|
8
9
|
|
|
@@ -10,7 +11,7 @@
|
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* @typedef {object} Answer
|
|
13
|
-
* @property {{ status: string }} run
|
|
14
|
+
* @property {{ status: string; updatedAt?: string }} run
|
|
14
15
|
* @property {RunEvent[]} events Events from the index the request asked for.
|
|
15
16
|
* @property {number} next The index to ask for next.
|
|
16
17
|
*/
|
|
@@ -26,7 +27,15 @@
|
|
|
26
27
|
*/
|
|
27
28
|
export function follower({ load, schedule, cancel, show, fail }) {
|
|
28
29
|
/** One selection: a new object each time, so that an old answer never matches. */
|
|
29
|
-
let current = {
|
|
30
|
+
let current = {
|
|
31
|
+
id: "",
|
|
32
|
+
events: /** @type {RunEvent[]} */ ([]),
|
|
33
|
+
next: 0,
|
|
34
|
+
/** Whether following stopped, on the run's end or absence. */
|
|
35
|
+
stopped: false,
|
|
36
|
+
/** The run's last update, as the last answer showed it. */
|
|
37
|
+
updatedAt: /** @type {string | undefined} */ (undefined),
|
|
38
|
+
};
|
|
30
39
|
/** @type {Timer | undefined} */
|
|
31
40
|
let timer;
|
|
32
41
|
|
|
@@ -45,14 +54,17 @@ export function follower({ load, schedule, cancel, show, fail }) {
|
|
|
45
54
|
// A run that does not exist will not appear later.
|
|
46
55
|
const status = /** @type {{ status?: number }} */ (error).status;
|
|
47
56
|
if (status !== 404) timer = schedule(() => refresh(target));
|
|
57
|
+
else target.stopped = true;
|
|
48
58
|
return;
|
|
49
59
|
}
|
|
50
60
|
if (target !== current) return;
|
|
51
61
|
target.events.push(...answer.events);
|
|
52
62
|
target.next = answer.next;
|
|
63
|
+
target.updatedAt = answer.run.updatedAt;
|
|
53
64
|
show(answer, target.events);
|
|
54
65
|
if (answer.run.status === "running")
|
|
55
66
|
timer = schedule(() => refresh(target));
|
|
67
|
+
else target.stopped = true;
|
|
56
68
|
}
|
|
57
69
|
|
|
58
70
|
return {
|
|
@@ -60,7 +72,31 @@ export function follower({ load, schedule, cancel, show, fail }) {
|
|
|
60
72
|
select(id) {
|
|
61
73
|
if (id === current.id) return;
|
|
62
74
|
cancel(timer);
|
|
63
|
-
current = {
|
|
75
|
+
current = {
|
|
76
|
+
id,
|
|
77
|
+
events: [],
|
|
78
|
+
next: 0,
|
|
79
|
+
stopped: false,
|
|
80
|
+
updatedAt: undefined,
|
|
81
|
+
};
|
|
82
|
+
refresh(current);
|
|
83
|
+
},
|
|
84
|
+
/**
|
|
85
|
+
* The run list shows `id` last updated at `updatedAt`: following starts
|
|
86
|
+
* again, from the events already received, if it had stopped on an earlier
|
|
87
|
+
* state of the selected run, as when it was resumed since, even if the
|
|
88
|
+
* resumed run already ended again.
|
|
89
|
+
* @param {string} id
|
|
90
|
+
* @param {string} updatedAt
|
|
91
|
+
*/
|
|
92
|
+
listed(id, updatedAt) {
|
|
93
|
+
if (
|
|
94
|
+
id !== current.id ||
|
|
95
|
+
!current.stopped ||
|
|
96
|
+
updatedAt === current.updatedAt
|
|
97
|
+
)
|
|
98
|
+
return;
|
|
99
|
+
current.stopped = false;
|
|
64
100
|
refresh(current);
|
|
65
101
|
},
|
|
66
102
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clemrig/flow-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A local read-only dashboard of the runs of @clemrig/flow workflows",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,6 +34,6 @@
|
|
|
34
34
|
"prepack": "npm run build"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@clemrig/flow": "0.
|
|
37
|
+
"@clemrig/flow": "0.5.0"
|
|
38
38
|
}
|
|
39
39
|
}
|