@osolmaz/pi-workflows 0.8.2 → 0.9.1
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 +19 -0
- package/dist/extension/herdr-viewer.d.ts +50 -0
- package/dist/extension/herdr-viewer.js +310 -0
- package/dist/extension/herdr-viewer.js.map +1 -0
- package/dist/extension/index.js +104 -1
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/widget.d.ts +1 -1
- package/dist/extension/widget.js +15 -6
- package/dist/extension/widget.js.map +1 -1
- package/dist/herdr/constants.d.ts +2 -0
- package/dist/herdr/constants.js +3 -0
- package/dist/herdr/constants.js.map +1 -0
- package/dist/herdr/setup.d.ts +8 -0
- package/dist/herdr/setup.js +78 -0
- package/dist/herdr/setup.js.map +1 -0
- package/dist/viewer/cli.d.ts +1 -0
- package/dist/viewer/cli.js +18 -1
- package/dist/viewer/cli.js.map +1 -1
- package/docs/2026-08-18-herdr-piw-plan.md +73 -0
- package/docs/tui-viewer.md +14 -0
- package/herdr-plugin.toml +12 -0
- package/package.json +3 -1
- package/plugins/herdr/viewer.mjs +56 -0
- package/src/extension/herdr-viewer.ts +394 -0
- package/src/extension/index.ts +130 -0
- package/src/extension/widget.ts +23 -5
- package/src/herdr/constants.ts +2 -0
- package/src/herdr/setup.ts +93 -0
- package/src/viewer/cli.ts +20 -1
package/README.md
CHANGED
|
@@ -71,6 +71,25 @@ The npm package also includes the simpler `pi-workflows` snapshot viewer. To
|
|
|
71
71
|
link that command from a clone, run `npm install && npm run build && npm link`,
|
|
72
72
|
or run it in place with `npx tsx src/viewer/cli.ts`.
|
|
73
73
|
|
|
74
|
+
## Herdr integration
|
|
75
|
+
|
|
76
|
+
Pi Workflows also ships as a [Herdr](https://herdr.dev) plugin. After installing
|
|
77
|
+
`piw` and Pi Workflows, link the bundled plugin once:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pi-workflows herdr setup
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
When Pi runs inside Herdr, a workflow widget shows `Ctrl+Shift+R piw`. When the widget has hidden rows, this call to action shares the existing scroll-controls line instead of taking another line.
|
|
84
|
+
The shortcut opens the exact run bundle and lets you choose a split, tab, or new
|
|
85
|
+
workspace. `/piw` opens the same menu, and `/piw right`, `/piw below`, `/piw
|
|
86
|
+
left`, `/piw above`, `/piw tab`, or `/piw workspace` selects a placement
|
|
87
|
+
directly. If a viewer for that run already exists, Pi Workflows focuses it
|
|
88
|
+
instead of opening a duplicate.
|
|
89
|
+
|
|
90
|
+
The plugin uses Herdr's public pane APIs and runs no service or polling loop. It
|
|
91
|
+
is also available through the [Herdr plugin marketplace](https://herdr.dev/plugins/).
|
|
92
|
+
|
|
74
93
|
## Quick start
|
|
75
94
|
|
|
76
95
|
Put a workflow file in `.pi/workflows/` (project) or `~/.pi/agent/workflows/`
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
export declare const PIW_SHORTCUT = "ctrl+shift+r";
|
|
3
|
+
export declare const PIW_SHORTCUT_HINT = "Ctrl+Shift+R piw";
|
|
4
|
+
export declare const VIEWER_PLACEMENTS: readonly ["right", "below", "left", "above", "tab", "workspace"];
|
|
5
|
+
export type ViewerPlacement = (typeof VIEWER_PLACEMENTS)[number];
|
|
6
|
+
export type WorkflowViewTarget = {
|
|
7
|
+
runId: string;
|
|
8
|
+
workflowName: string;
|
|
9
|
+
runDir: string;
|
|
10
|
+
};
|
|
11
|
+
export type HerdrCapability = {
|
|
12
|
+
available: true;
|
|
13
|
+
} | {
|
|
14
|
+
available: false;
|
|
15
|
+
reason: string;
|
|
16
|
+
};
|
|
17
|
+
export type ViewerOpenResult = {
|
|
18
|
+
paneId: string;
|
|
19
|
+
reused: boolean;
|
|
20
|
+
warning?: string;
|
|
21
|
+
};
|
|
22
|
+
type Exec = ExtensionAPI["exec"];
|
|
23
|
+
type HerdrPane = {
|
|
24
|
+
paneId: string;
|
|
25
|
+
tabId: string;
|
|
26
|
+
workspaceId: string;
|
|
27
|
+
label?: string;
|
|
28
|
+
};
|
|
29
|
+
export declare class HerdrWorkflowViewer {
|
|
30
|
+
private readonly exec;
|
|
31
|
+
private readonly env;
|
|
32
|
+
private readonly knownPanes;
|
|
33
|
+
private readonly opening;
|
|
34
|
+
constructor(exec: Exec, env?: NodeJS.ProcessEnv);
|
|
35
|
+
probe(): Promise<HerdrCapability>;
|
|
36
|
+
focusExisting(target: WorkflowViewTarget): Promise<boolean>;
|
|
37
|
+
open(target: WorkflowViewTarget, placement: ViewerPlacement, cwd: string): Promise<ViewerOpenResult>;
|
|
38
|
+
private openOnce;
|
|
39
|
+
private findAndFocus;
|
|
40
|
+
find(target: WorkflowViewTarget): Promise<HerdrPane | undefined>;
|
|
41
|
+
private currentPane;
|
|
42
|
+
private openPluginPane;
|
|
43
|
+
private openWorkspace;
|
|
44
|
+
private closePane;
|
|
45
|
+
private run;
|
|
46
|
+
private runJson;
|
|
47
|
+
}
|
|
48
|
+
export declare function parseViewerPlacement(value: string): ViewerPlacement | undefined;
|
|
49
|
+
export declare function viewerPaneLabel(runId: string): string;
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { HERDR_PLUGIN_ENTRYPOINT, HERDR_PLUGIN_ID } from "../herdr/constants.js";
|
|
2
|
+
export const PIW_SHORTCUT = "ctrl+shift+r";
|
|
3
|
+
export const PIW_SHORTCUT_HINT = "Ctrl+Shift+R piw";
|
|
4
|
+
const COMMAND_TIMEOUT_MS = 5_000;
|
|
5
|
+
const MAX_JSON_CHARS = 1_000_000;
|
|
6
|
+
const VIEWER_LABEL_PREFIX = "piw · ";
|
|
7
|
+
export const VIEWER_PLACEMENTS = ["right", "below", "left", "above", "tab", "workspace"];
|
|
8
|
+
export class HerdrWorkflowViewer {
|
|
9
|
+
exec;
|
|
10
|
+
env;
|
|
11
|
+
knownPanes = new Map();
|
|
12
|
+
opening = new Map();
|
|
13
|
+
constructor(exec, env = process.env) {
|
|
14
|
+
this.exec = exec;
|
|
15
|
+
this.env = env;
|
|
16
|
+
}
|
|
17
|
+
async probe() {
|
|
18
|
+
if (this.env.HERDR_ENV !== "1") {
|
|
19
|
+
return { available: false, reason: "Pi is not running in Herdr." };
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
await this.currentPane();
|
|
23
|
+
const plugin = await this.runJson("herdr", [
|
|
24
|
+
"plugin",
|
|
25
|
+
"list",
|
|
26
|
+
"--plugin",
|
|
27
|
+
HERDR_PLUGIN_ID,
|
|
28
|
+
"--json",
|
|
29
|
+
]);
|
|
30
|
+
if (!pluginIsEnabled(plugin)) {
|
|
31
|
+
return {
|
|
32
|
+
available: false,
|
|
33
|
+
reason: `Herdr plugin ${HERDR_PLUGIN_ID} is not linked and enabled.`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
await this.run("piw", ["--version"]);
|
|
37
|
+
return { available: true };
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return { available: false, reason: errorMessage(error) };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async focusExisting(target) {
|
|
44
|
+
return (await this.findAndFocus(target)) !== undefined;
|
|
45
|
+
}
|
|
46
|
+
async open(target, placement, cwd) {
|
|
47
|
+
const pending = this.opening.get(target.runId);
|
|
48
|
+
if (pending !== undefined)
|
|
49
|
+
return await pending;
|
|
50
|
+
const opening = this.openOnce(target, placement, cwd).finally(() => {
|
|
51
|
+
if (this.opening.get(target.runId) === opening)
|
|
52
|
+
this.opening.delete(target.runId);
|
|
53
|
+
});
|
|
54
|
+
this.opening.set(target.runId, opening);
|
|
55
|
+
return await opening;
|
|
56
|
+
}
|
|
57
|
+
async openOnce(target, placement, cwd) {
|
|
58
|
+
const existingPaneId = await this.findAndFocus(target);
|
|
59
|
+
if (existingPaneId !== undefined) {
|
|
60
|
+
return { paneId: existingPaneId, reused: true };
|
|
61
|
+
}
|
|
62
|
+
const caller = await this.currentPane();
|
|
63
|
+
if (placement === "workspace") {
|
|
64
|
+
const opened = await this.openWorkspace(target, cwd);
|
|
65
|
+
this.knownPanes.set(target.runId, opened.paneId);
|
|
66
|
+
return { ...opened, reused: false };
|
|
67
|
+
}
|
|
68
|
+
const opened = await this.openPluginPane(target, placement, caller);
|
|
69
|
+
if (placement === "left" || placement === "above") {
|
|
70
|
+
try {
|
|
71
|
+
await this.run("herdr", [
|
|
72
|
+
"pane",
|
|
73
|
+
"swap",
|
|
74
|
+
"--source-pane",
|
|
75
|
+
opened.paneId,
|
|
76
|
+
"--target-pane",
|
|
77
|
+
caller.paneId,
|
|
78
|
+
]);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
await this.closePane(opened.paneId);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
this.knownPanes.set(target.runId, opened.paneId);
|
|
86
|
+
return { paneId: opened.paneId, reused: false };
|
|
87
|
+
}
|
|
88
|
+
async findAndFocus(target) {
|
|
89
|
+
const knownPaneId = this.knownPanes.get(target.runId);
|
|
90
|
+
if (knownPaneId !== undefined) {
|
|
91
|
+
try {
|
|
92
|
+
await this.run("herdr", ["plugin", "pane", "focus", knownPaneId]);
|
|
93
|
+
return knownPaneId;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
this.knownPanes.delete(target.runId);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const existing = await this.find(target);
|
|
100
|
+
if (existing === undefined)
|
|
101
|
+
return undefined;
|
|
102
|
+
try {
|
|
103
|
+
await this.run("herdr", ["plugin", "pane", "focus", existing.paneId]);
|
|
104
|
+
this.knownPanes.set(target.runId, existing.paneId);
|
|
105
|
+
return existing.paneId;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// The pane can close between the snapshot and focus request.
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async find(target) {
|
|
113
|
+
const snapshot = await this.runJson("herdr", ["api", "snapshot"]);
|
|
114
|
+
return snapshotPanes(snapshot).find((pane) => pane.label === viewerPaneLabel(target.runId));
|
|
115
|
+
}
|
|
116
|
+
async currentPane() {
|
|
117
|
+
return parseCurrentPane(await this.runJson("herdr", ["pane", "current", "--current"]));
|
|
118
|
+
}
|
|
119
|
+
async openPluginPane(target, placement, caller) {
|
|
120
|
+
const args = [
|
|
121
|
+
"plugin",
|
|
122
|
+
"pane",
|
|
123
|
+
"open",
|
|
124
|
+
"--plugin",
|
|
125
|
+
HERDR_PLUGIN_ID,
|
|
126
|
+
"--entrypoint",
|
|
127
|
+
HERDR_PLUGIN_ENTRYPOINT,
|
|
128
|
+
"--env",
|
|
129
|
+
`PI_WORKFLOWS_RUN_ID=${target.runId}`,
|
|
130
|
+
"--env",
|
|
131
|
+
`PI_WORKFLOWS_RUN_DIR=${target.runDir}`,
|
|
132
|
+
"--focus",
|
|
133
|
+
];
|
|
134
|
+
if (placement === "tab") {
|
|
135
|
+
args.push("--placement", "tab", "--workspace", caller.workspaceId);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
args.push("--placement", "split", "--target-pane", caller.paneId, "--direction", placement === "below" || placement === "above" ? "down" : "right");
|
|
139
|
+
}
|
|
140
|
+
return parseOpenedPane(await this.runJson("herdr", args));
|
|
141
|
+
}
|
|
142
|
+
async openWorkspace(target, cwd) {
|
|
143
|
+
const created = parseCreatedWorkspace(await this.runJson("herdr", [
|
|
144
|
+
"workspace",
|
|
145
|
+
"create",
|
|
146
|
+
"--cwd",
|
|
147
|
+
cwd,
|
|
148
|
+
"--label",
|
|
149
|
+
workspaceLabel(target.workflowName),
|
|
150
|
+
"--no-focus",
|
|
151
|
+
]));
|
|
152
|
+
let opened;
|
|
153
|
+
try {
|
|
154
|
+
opened = parseOpenedPane(await this.runJson("herdr", [
|
|
155
|
+
"plugin",
|
|
156
|
+
"pane",
|
|
157
|
+
"open",
|
|
158
|
+
"--plugin",
|
|
159
|
+
HERDR_PLUGIN_ID,
|
|
160
|
+
"--entrypoint",
|
|
161
|
+
HERDR_PLUGIN_ENTRYPOINT,
|
|
162
|
+
"--placement",
|
|
163
|
+
"tab",
|
|
164
|
+
"--workspace",
|
|
165
|
+
created.workspaceId,
|
|
166
|
+
"--env",
|
|
167
|
+
`PI_WORKFLOWS_RUN_ID=${target.runId}`,
|
|
168
|
+
"--env",
|
|
169
|
+
`PI_WORKFLOWS_RUN_DIR=${target.runDir}`,
|
|
170
|
+
"--focus",
|
|
171
|
+
]));
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
await this.run("herdr", ["workspace", "close", created.workspaceId]).catch(() => undefined);
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
await this.run("herdr", ["tab", "close", created.bootstrapTabId]);
|
|
179
|
+
return { paneId: opened.paneId };
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
return {
|
|
183
|
+
paneId: opened.paneId,
|
|
184
|
+
warning: `The piw viewer opened, but its empty bootstrap tab could not be closed: ${errorMessage(error)}`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async closePane(paneId) {
|
|
189
|
+
await this.run("herdr", ["plugin", "pane", "close", paneId]).catch(() => undefined);
|
|
190
|
+
}
|
|
191
|
+
async run(command, args) {
|
|
192
|
+
const result = await this.exec(command, args, { timeout: COMMAND_TIMEOUT_MS });
|
|
193
|
+
if (result.killed) {
|
|
194
|
+
throw new Error(`${command} timed out.`);
|
|
195
|
+
}
|
|
196
|
+
if (result.code !== 0) {
|
|
197
|
+
const message = result.stderr.trim() || result.stdout.trim() || `exit ${result.code}`;
|
|
198
|
+
throw new Error(`${command} failed: ${boundedText(message)}`);
|
|
199
|
+
}
|
|
200
|
+
return result.stdout;
|
|
201
|
+
}
|
|
202
|
+
async runJson(command, args) {
|
|
203
|
+
const stdout = await this.run(command, args);
|
|
204
|
+
if (stdout.length > MAX_JSON_CHARS) {
|
|
205
|
+
throw new Error(`${command} returned too much data.`);
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
return JSON.parse(stdout);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
throw new Error(`${command} returned invalid JSON.`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
export function parseViewerPlacement(value) {
|
|
216
|
+
return VIEWER_PLACEMENTS.find((placement) => placement === value);
|
|
217
|
+
}
|
|
218
|
+
export function viewerPaneLabel(runId) {
|
|
219
|
+
return `${VIEWER_LABEL_PREFIX}${runId}`;
|
|
220
|
+
}
|
|
221
|
+
function pluginIsEnabled(value) {
|
|
222
|
+
const result = recordValue(value, "result");
|
|
223
|
+
const plugins = result === undefined ? undefined : arrayValue(result, "plugins");
|
|
224
|
+
return (plugins?.some((plugin) => recordValue(plugin, "plugin_id") === HERDR_PLUGIN_ID &&
|
|
225
|
+
recordValue(plugin, "enabled") === true) === true);
|
|
226
|
+
}
|
|
227
|
+
function parseCurrentPane(value) {
|
|
228
|
+
const result = requiredRecord(value, "result", "Herdr pane response");
|
|
229
|
+
return paneFromValue(requiredRecord(result, "pane", "Herdr pane response"));
|
|
230
|
+
}
|
|
231
|
+
function parseOpenedPane(value) {
|
|
232
|
+
const result = requiredRecord(value, "result", "Herdr plugin pane response");
|
|
233
|
+
const pluginPane = requiredRecord(result, "plugin_pane", "Herdr plugin pane response");
|
|
234
|
+
return paneFromValue(requiredRecord(pluginPane, "pane", "Herdr plugin pane response"));
|
|
235
|
+
}
|
|
236
|
+
function parseCreatedWorkspace(value) {
|
|
237
|
+
const result = requiredRecord(value, "result", "Herdr workspace response");
|
|
238
|
+
const workspace = requiredRecord(result, "workspace", "Herdr workspace response");
|
|
239
|
+
const rootPane = requiredRecord(result, "root_pane", "Herdr workspace response");
|
|
240
|
+
return {
|
|
241
|
+
workspaceId: requiredString(workspace, "workspace_id", "Herdr workspace response"),
|
|
242
|
+
bootstrapTabId: requiredString(rootPane, "tab_id", "Herdr workspace response"),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function snapshotPanes(value) {
|
|
246
|
+
const result = requiredRecord(value, "result", "Herdr snapshot");
|
|
247
|
+
const snapshot = requiredRecord(result, "snapshot", "Herdr snapshot");
|
|
248
|
+
const panes = arrayValue(snapshot, "panes");
|
|
249
|
+
if (panes === undefined)
|
|
250
|
+
throw new Error("Herdr snapshot has no panes.");
|
|
251
|
+
return panes.flatMap((pane) => {
|
|
252
|
+
try {
|
|
253
|
+
return [paneFromValue(pane)];
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
function paneFromValue(value) {
|
|
261
|
+
if (!isRecord(value))
|
|
262
|
+
throw new Error("Herdr returned an invalid pane.");
|
|
263
|
+
const label = recordValue(value, "label");
|
|
264
|
+
return {
|
|
265
|
+
paneId: requiredString(value, "pane_id", "Herdr pane"),
|
|
266
|
+
tabId: requiredString(value, "tab_id", "Herdr pane"),
|
|
267
|
+
workspaceId: requiredString(value, "workspace_id", "Herdr pane"),
|
|
268
|
+
...(typeof label === "string" ? { label } : {}),
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function workspaceLabel(workflowName) {
|
|
272
|
+
const compact = workflowName.replace(/[\r\n\t]+/gu, " ").trim();
|
|
273
|
+
return `piw · ${(compact || "workflow").slice(0, 60)}`;
|
|
274
|
+
}
|
|
275
|
+
function requiredRecord(value, key, label) {
|
|
276
|
+
if (!isRecord(value))
|
|
277
|
+
throw new Error(`${label} is invalid.`);
|
|
278
|
+
const nested = value[key];
|
|
279
|
+
if (!isRecord(nested))
|
|
280
|
+
throw new Error(`${label} has no ${key}.`);
|
|
281
|
+
return nested;
|
|
282
|
+
}
|
|
283
|
+
function requiredString(value, key, label) {
|
|
284
|
+
const nested = value[key];
|
|
285
|
+
if (typeof nested !== "string" || nested.length === 0) {
|
|
286
|
+
throw new Error(`${label} has no ${key}.`);
|
|
287
|
+
}
|
|
288
|
+
return nested;
|
|
289
|
+
}
|
|
290
|
+
function recordValue(value, key) {
|
|
291
|
+
return isRecord(value) ? value[key] : undefined;
|
|
292
|
+
}
|
|
293
|
+
function arrayValue(value, key) {
|
|
294
|
+
const nested = recordValue(value, key);
|
|
295
|
+
return Array.isArray(nested) ? nested : undefined;
|
|
296
|
+
}
|
|
297
|
+
function isRecord(value) {
|
|
298
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
299
|
+
}
|
|
300
|
+
function boundedText(value) {
|
|
301
|
+
const compact = value
|
|
302
|
+
.replace(/[\r\n\t]+/gu, " ")
|
|
303
|
+
.replace(/ +/gu, " ")
|
|
304
|
+
.trim();
|
|
305
|
+
return compact.length <= 300 ? compact : `${compact.slice(0, 299)}…`;
|
|
306
|
+
}
|
|
307
|
+
function errorMessage(error) {
|
|
308
|
+
return error instanceof Error ? error.message : String(error);
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=herdr-viewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"herdr-viewer.js","sourceRoot":"","sources":["../../src/extension/herdr-viewer.ts"],"names":[],"mappings":"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"}
|
package/dist/extension/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
2
3
|
import { isDeepStrictEqual } from "node:util";
|
|
3
4
|
import { builtinWorkflowCatalog } from "../builtins/catalog.js";
|
|
4
5
|
import { projectControllerStorePath, SqliteControllerStore } from "../controllers/index.js";
|
|
@@ -10,6 +11,7 @@ import { appendProgressHistory, progressRecordsFromTrace } from "../workflows/pr
|
|
|
10
11
|
import { createRunId, listRunBundles, readLastTraceEvent, readRunBundle, WorkflowRunStore, createDefinitionSnapshot, } from "../workflows/store.js";
|
|
11
12
|
import { PiControllerHost, parseControllerArgs, } from "./controller-host.js";
|
|
12
13
|
import { ConversationStepExecutor } from "./executor.js";
|
|
14
|
+
import { HerdrWorkflowViewer, parseViewerPlacement, PIW_SHORTCUT, PIW_SHORTCUT_HINT, VIEWER_PLACEMENTS, } from "./herdr-viewer.js";
|
|
13
15
|
import { SessionRecorder } from "./recorder.js";
|
|
14
16
|
import { registerWorkflowAgentStepMessageRenderer, WORKFLOW_AGENT_STEP_MESSAGE_SCHEMA, WORKFLOW_AGENT_STEP_MESSAGE_TYPE, } from "./step-message.js";
|
|
15
17
|
import { buildWidgetView } from "./widget.js";
|
|
@@ -27,6 +29,15 @@ const MAX_STATUS_ERROR_CHARS = 4_000;
|
|
|
27
29
|
const MAX_WORKFLOW_LIST_ITEMS = 50;
|
|
28
30
|
const MAX_WORKFLOW_LIST_NAME_CHARS = 3_500;
|
|
29
31
|
const PRESENTATION_TIMEOUT_MS = 30_000;
|
|
32
|
+
const PIW_PLACEMENT_LABELS = {
|
|
33
|
+
right: "Split right",
|
|
34
|
+
below: "Split below",
|
|
35
|
+
left: "Split left",
|
|
36
|
+
above: "Split above",
|
|
37
|
+
tab: "New tab",
|
|
38
|
+
workspace: "New workspace",
|
|
39
|
+
};
|
|
40
|
+
const PIW_PLACEMENT_BY_LABEL = new Map(VIEWER_PLACEMENTS.map((placement) => [PIW_PLACEMENT_LABELS[placement], placement]));
|
|
30
41
|
class PresentationSupersededError extends Error {
|
|
31
42
|
}
|
|
32
43
|
class PresentationTimeoutError extends Error {
|
|
@@ -123,6 +134,14 @@ function workflowStateSummary(state) {
|
|
|
123
134
|
}
|
|
124
135
|
export default function piWorkflows(pi) {
|
|
125
136
|
registerWorkflowAgentStepMessageRenderer(pi);
|
|
137
|
+
const herdrEnabled = process.env.HERDR_ENV === "1";
|
|
138
|
+
const herdrViewer = new HerdrWorkflowViewer((command, args, options) => pi.exec(command, args, options));
|
|
139
|
+
let herdrCapability = {
|
|
140
|
+
available: false,
|
|
141
|
+
reason: "Herdr integration has not been checked.",
|
|
142
|
+
};
|
|
143
|
+
let workflowViewTarget = null;
|
|
144
|
+
let herdrProbeGeneration = 0;
|
|
126
145
|
// One runner identity per session; it names this session in run claims.
|
|
127
146
|
const runnerId = randomUUID();
|
|
128
147
|
let runQueueStore = null;
|
|
@@ -277,7 +296,12 @@ export default function piWorkflows(pi) {
|
|
|
277
296
|
const render = (width = Number.POSITIVE_INFINITY, theme) => {
|
|
278
297
|
if (!widgetSource)
|
|
279
298
|
return [];
|
|
280
|
-
const view = buildWidgetView(widgetSource.state, widgetSource.snapshot, new Date(), widgetScroll, runHeld(), width, theme, widgetSource.updateHistory
|
|
299
|
+
const view = buildWidgetView(widgetSource.state, widgetSource.snapshot, new Date(), widgetScroll, runHeld(), width, theme, widgetSource.updateHistory, ctx.mode === "tui" &&
|
|
300
|
+
herdrEnabled &&
|
|
301
|
+
herdrCapability.available &&
|
|
302
|
+
workflowViewTarget?.runId === widgetSource.state.runId
|
|
303
|
+
? PIW_SHORTCUT_HINT
|
|
304
|
+
: undefined);
|
|
281
305
|
widgetShownScroll = view.scroll;
|
|
282
306
|
widgetMaxScroll = view.maxScroll;
|
|
283
307
|
if (widgetScroll !== null) {
|
|
@@ -308,14 +332,66 @@ export default function piWorkflows(pi) {
|
|
|
308
332
|
snapshot,
|
|
309
333
|
...(updateHistory !== undefined ? { updateHistory: [...updateHistory] } : {}),
|
|
310
334
|
};
|
|
335
|
+
workflowViewTarget = {
|
|
336
|
+
runId: state.runId,
|
|
337
|
+
workflowName: state.workflowName,
|
|
338
|
+
runDir: path.resolve(new WorkflowRunStore().runDirFor(state.runId)),
|
|
339
|
+
};
|
|
311
340
|
renderWidget(ctx);
|
|
312
341
|
};
|
|
313
342
|
const clearWidget = (ctx) => {
|
|
314
343
|
widgetSource = null;
|
|
344
|
+
workflowViewTarget = null;
|
|
315
345
|
widgetScroll = null;
|
|
316
346
|
setWidget(ctx, undefined);
|
|
317
347
|
setStatus(ctx, undefined);
|
|
318
348
|
};
|
|
349
|
+
const refreshHerdrCapability = async (ctx) => {
|
|
350
|
+
const generation = ++herdrProbeGeneration;
|
|
351
|
+
const capability = await herdrViewer.probe();
|
|
352
|
+
if (!sessionClosed && generation === herdrProbeGeneration) {
|
|
353
|
+
herdrCapability = capability;
|
|
354
|
+
renderWidget(ctx);
|
|
355
|
+
}
|
|
356
|
+
return capability;
|
|
357
|
+
};
|
|
358
|
+
const selectPiwPlacement = async (ctx) => {
|
|
359
|
+
if (!ctx.hasUI || ctx.mode !== "tui")
|
|
360
|
+
return undefined;
|
|
361
|
+
const label = await ctx.ui.select("Open workflow in piw", VIEWER_PLACEMENTS.map((placement) => PIW_PLACEMENT_LABELS[placement]));
|
|
362
|
+
return label === undefined ? undefined : PIW_PLACEMENT_BY_LABEL.get(label);
|
|
363
|
+
};
|
|
364
|
+
const openPiw = async (ctx, requestedPlacement) => {
|
|
365
|
+
const target = workflowViewTarget;
|
|
366
|
+
if (target === null) {
|
|
367
|
+
notify(ctx, "No workflow run is available to open in piw.", "warning");
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const capability = await refreshHerdrCapability(ctx);
|
|
371
|
+
if (!capability.available) {
|
|
372
|
+
notify(ctx, capability.reason, "warning");
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
if (await herdrViewer.focusExisting(target)) {
|
|
377
|
+
notify(ctx, `Focused the piw viewer for ${target.workflowName}.`);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const placement = requestedPlacement ?? (await selectPiwPlacement(ctx));
|
|
381
|
+
if (placement === undefined) {
|
|
382
|
+
if (!ctx.hasUI || ctx.mode !== "tui") {
|
|
383
|
+
notify(ctx, "Specify a piw placement: right, below, left, above, tab, or workspace.");
|
|
384
|
+
}
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
const opened = await herdrViewer.open(target, placement, ctx.cwd);
|
|
388
|
+
if (opened.warning !== undefined)
|
|
389
|
+
notify(ctx, opened.warning, "warning");
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
notify(ctx, `Could not open piw: ${errorMessage(error)}`, "error");
|
|
393
|
+
}
|
|
394
|
+
};
|
|
319
395
|
const scrollWidget = (ctx, delta) => {
|
|
320
396
|
if (!widgetSource || widgetMaxScroll === 0) {
|
|
321
397
|
return;
|
|
@@ -696,6 +772,7 @@ export default function piWorkflows(pi) {
|
|
|
696
772
|
run.renewTimer.unref?.();
|
|
697
773
|
}
|
|
698
774
|
clearWidgetTimer();
|
|
775
|
+
workflowViewTarget = null;
|
|
699
776
|
startWidgetTicker(ctx, run);
|
|
700
777
|
if (!options.quiet) {
|
|
701
778
|
notify(ctx, `Workflow ${workflow.name} started. Follow it live with: pi-workflows view`);
|
|
@@ -1191,6 +1268,22 @@ export default function piWorkflows(pi) {
|
|
|
1191
1268
|
throw error;
|
|
1192
1269
|
}
|
|
1193
1270
|
};
|
|
1271
|
+
pi.registerCommand("piw", {
|
|
1272
|
+
description: "Open the current workflow run in piw through Herdr",
|
|
1273
|
+
getArgumentCompletions: async (prefix) => {
|
|
1274
|
+
const items = VIEWER_PLACEMENTS.filter((placement) => placement.startsWith(prefix)).map((placement) => ({ value: placement, label: placement }));
|
|
1275
|
+
return items.length > 0 ? items : null;
|
|
1276
|
+
},
|
|
1277
|
+
handler: async (args, ctx) => {
|
|
1278
|
+
const value = args.trim();
|
|
1279
|
+
const placement = value.length === 0 ? undefined : parseViewerPlacement(value);
|
|
1280
|
+
if (value.length > 0 && placement === undefined) {
|
|
1281
|
+
notify(ctx, "piw placement must be right, below, left, above, tab, or workspace.", "error");
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
await openPiw(ctx, placement);
|
|
1285
|
+
},
|
|
1286
|
+
});
|
|
1194
1287
|
pi.registerCommand("workflow", {
|
|
1195
1288
|
description: "Run or manage a workflow: /workflow <name-or-path> [task | --input-json {…}]; also: status, pause, resume, cancel, answer",
|
|
1196
1289
|
getArgumentCompletions: async (prefix) => {
|
|
@@ -1406,6 +1499,12 @@ export default function piWorkflows(pi) {
|
|
|
1406
1499
|
};
|
|
1407
1500
|
},
|
|
1408
1501
|
});
|
|
1502
|
+
if (herdrEnabled) {
|
|
1503
|
+
pi.registerShortcut(PIW_SHORTCUT, {
|
|
1504
|
+
description: "Open the current workflow run in piw",
|
|
1505
|
+
handler: async (ctx) => await openPiw(ctx),
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1409
1508
|
pi.registerShortcut("shift+up", {
|
|
1410
1509
|
description: "Scroll the workflow widget up",
|
|
1411
1510
|
handler: (ctx) => scrollWidget(ctx, -WIDGET_SCROLL_STEP),
|
|
@@ -1417,6 +1516,8 @@ export default function piWorkflows(pi) {
|
|
|
1417
1516
|
pi.on("session_start", async (_event, ctx) => {
|
|
1418
1517
|
sessionClosed = false;
|
|
1419
1518
|
controllerContext = ctx;
|
|
1519
|
+
if (herdrEnabled)
|
|
1520
|
+
void refreshHerdrCapability(ctx);
|
|
1420
1521
|
try {
|
|
1421
1522
|
const queue = ensureRunQueueStore(ctx.cwd);
|
|
1422
1523
|
const migration = await migrateLegacyWorkflowSources({
|
|
@@ -1550,6 +1651,7 @@ export default function piWorkflows(pi) {
|
|
|
1550
1651
|
});
|
|
1551
1652
|
pi.on("session_shutdown", async () => {
|
|
1552
1653
|
sessionClosed = true;
|
|
1654
|
+
herdrProbeGeneration += 1;
|
|
1553
1655
|
systemTurnAbort = null;
|
|
1554
1656
|
suppressWorkflowAssistantTail = false;
|
|
1555
1657
|
supersedePresentation();
|
|
@@ -1581,6 +1683,7 @@ export default function piWorkflows(pi) {
|
|
|
1581
1683
|
clearWidgetTimer();
|
|
1582
1684
|
stopWidgetTicker();
|
|
1583
1685
|
widgetSource = null;
|
|
1686
|
+
workflowViewTarget = null;
|
|
1584
1687
|
widgetScroll = null;
|
|
1585
1688
|
});
|
|
1586
1689
|
}
|