@aliou/pi-processes 0.4.4 → 0.4.5
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 +2 -17
- package/package.json +28 -13
- package/src/index.ts +27 -0
- package/commands/index.ts +0 -334
- package/commands/settings-command.ts +0 -157
- package/components/log-stream-component.ts +0 -149
- package/components/process-picker-component.ts +0 -155
- package/components/processes-component.ts +0 -450
- package/components/status-format.ts +0 -38
- package/config.ts +0 -70
- package/constants/index.ts +0 -11
- package/constants/types.ts +0 -65
- package/hooks/cleanup.ts +0 -10
- package/hooks/index.ts +0 -18
- package/hooks/message-renderer.ts +0 -83
- package/hooks/process-end.ts +0 -92
- package/hooks/widget.ts +0 -146
- package/index.ts +0 -29
- package/manager.ts +0 -522
- package/tools/actions/clear.ts +0 -20
- package/tools/actions/index.ts +0 -49
- package/tools/actions/kill.ts +0 -76
- package/tools/actions/list.ts +0 -37
- package/tools/actions/logs.ts +0 -59
- package/tools/actions/output.ts +0 -144
- package/tools/actions/start.ts +0 -55
- package/tools/index.ts +0 -280
- package/utils/ansi.ts +0 -36
- package/utils/command-executor.test.ts +0 -47
- package/utils/command-executor.ts +0 -56
- package/utils/format.ts +0 -42
- package/utils/index.ts +0 -3
- package/utils/process-group.ts +0 -22
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ExtensionAPI,
|
|
3
|
-
MessageRenderOptions,
|
|
4
|
-
Theme,
|
|
5
|
-
} from "@mariozechner/pi-coding-agent";
|
|
6
|
-
import { Text } from "@mariozechner/pi-tui";
|
|
7
|
-
import { MESSAGE_TYPE_PROCESS_UPDATE } from "../constants";
|
|
8
|
-
|
|
9
|
-
interface ProcessUpdateDetails {
|
|
10
|
-
processId: string;
|
|
11
|
-
processName: string;
|
|
12
|
-
command: string;
|
|
13
|
-
status: "exited" | "killed";
|
|
14
|
-
exitCode: number | null;
|
|
15
|
-
success: boolean;
|
|
16
|
-
runtime: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface ProcessUpdateMessage {
|
|
20
|
-
customType: string;
|
|
21
|
-
content: string | Array<{ type: string; text?: string }>;
|
|
22
|
-
details?: ProcessUpdateDetails;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function getContentText(
|
|
26
|
-
content: string | Array<{ type: string; text?: string }>,
|
|
27
|
-
): string {
|
|
28
|
-
if (typeof content === "string") {
|
|
29
|
-
return content;
|
|
30
|
-
}
|
|
31
|
-
return content
|
|
32
|
-
.filter((c) => c.type === "text" && c.text)
|
|
33
|
-
.map((c) => c.text as string)
|
|
34
|
-
.join("");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function setupMessageRenderer(pi: ExtensionAPI) {
|
|
38
|
-
pi.registerMessageRenderer<ProcessUpdateDetails>(
|
|
39
|
-
MESSAGE_TYPE_PROCESS_UPDATE,
|
|
40
|
-
(
|
|
41
|
-
message: ProcessUpdateMessage,
|
|
42
|
-
_options: MessageRenderOptions,
|
|
43
|
-
theme: Theme,
|
|
44
|
-
) => {
|
|
45
|
-
const details = message.details;
|
|
46
|
-
|
|
47
|
-
if (!details) {
|
|
48
|
-
return new Text(getContentText(message.content), 0, 0);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
let icon: string;
|
|
52
|
-
let color: "success" | "error" | "warning";
|
|
53
|
-
|
|
54
|
-
if (details.status === "killed") {
|
|
55
|
-
icon = "\u2717"; // x mark
|
|
56
|
-
color = "warning";
|
|
57
|
-
} else if (details.success) {
|
|
58
|
-
icon = "\u2713"; // check mark
|
|
59
|
-
color = "success";
|
|
60
|
-
} else {
|
|
61
|
-
icon = "\u2717"; // x mark
|
|
62
|
-
color = "error";
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const statusText =
|
|
66
|
-
details.status === "killed"
|
|
67
|
-
? "terminated"
|
|
68
|
-
: details.success
|
|
69
|
-
? "completed"
|
|
70
|
-
: `exited(${details.exitCode ?? "?"})`;
|
|
71
|
-
|
|
72
|
-
const text =
|
|
73
|
-
theme.fg(color, `${icon} `) +
|
|
74
|
-
theme.fg("accent", `"${details.processName}"`) +
|
|
75
|
-
theme.fg("muted", ` (${details.processId})`) +
|
|
76
|
-
" " +
|
|
77
|
-
theme.fg(color, statusText) +
|
|
78
|
-
theme.fg("muted", ` ${details.runtime}`);
|
|
79
|
-
|
|
80
|
-
return new Text(text, 0, 0);
|
|
81
|
-
},
|
|
82
|
-
);
|
|
83
|
-
}
|
package/hooks/process-end.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ExtensionAPI,
|
|
3
|
-
ExtensionContext,
|
|
4
|
-
} from "@mariozechner/pi-coding-agent";
|
|
5
|
-
import { MESSAGE_TYPE_PROCESS_UPDATE, type ProcessInfo } from "../constants";
|
|
6
|
-
import type { ProcessManager } from "../manager";
|
|
7
|
-
import { formatRuntime } from "../utils";
|
|
8
|
-
|
|
9
|
-
interface ProcessUpdateDetails {
|
|
10
|
-
processId: string;
|
|
11
|
-
processName: string;
|
|
12
|
-
command: string;
|
|
13
|
-
status: "exited" | "killed";
|
|
14
|
-
exitCode: number | null;
|
|
15
|
-
success: boolean;
|
|
16
|
-
runtime: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function setupProcessEndHook(pi: ExtensionAPI, manager: ProcessManager) {
|
|
20
|
-
let latestContext: ExtensionContext | null = null;
|
|
21
|
-
|
|
22
|
-
// Capture context from session events
|
|
23
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
24
|
-
latestContext = ctx;
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
pi.on("turn_start", async (_event, ctx) => {
|
|
28
|
-
latestContext = ctx;
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
pi.on("turn_end", async (_event, ctx) => {
|
|
32
|
-
latestContext = ctx;
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
manager.onEvent((event) => {
|
|
36
|
-
if (event.type !== "process_ended") return;
|
|
37
|
-
|
|
38
|
-
const info: ProcessInfo = event.info;
|
|
39
|
-
|
|
40
|
-
// Determine if the agent should get a turn to react to this process ending.
|
|
41
|
-
// When true, the agent receives the message in its context and can respond
|
|
42
|
-
// (e.g. check results, fix code, restart the process).
|
|
43
|
-
const triggerAgentTurn =
|
|
44
|
-
(info.status === "killed" && info.alertOnKill) ||
|
|
45
|
-
(info.status === "exited" && info.success && info.alertOnSuccess) ||
|
|
46
|
-
(info.status === "exited" && !info.success && info.alertOnFailure);
|
|
47
|
-
|
|
48
|
-
const runtime = formatRuntime(info.startTime, info.endTime);
|
|
49
|
-
|
|
50
|
-
// Build notification message
|
|
51
|
-
let message: string;
|
|
52
|
-
let level: "info" | "error" | "warning";
|
|
53
|
-
|
|
54
|
-
if (info.status === "killed") {
|
|
55
|
-
message = `Process '${info.name}' was terminated (${runtime})`;
|
|
56
|
-
level = "warning";
|
|
57
|
-
} else if (info.success) {
|
|
58
|
-
message = `Process '${info.name}' completed successfully (${runtime})`;
|
|
59
|
-
level = "info";
|
|
60
|
-
} else {
|
|
61
|
-
message = `Process '${info.name}' crashed with exit code ${info.exitCode ?? "?"} (${runtime})`;
|
|
62
|
-
level = "error";
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Always notify user via UI
|
|
66
|
-
if (latestContext?.hasUI) {
|
|
67
|
-
latestContext.ui.notify(message, level);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Always send the message so it appears in the conversation history.
|
|
71
|
-
// Only trigger an agent turn when the notification preferences say so.
|
|
72
|
-
const details: ProcessUpdateDetails = {
|
|
73
|
-
processId: info.id,
|
|
74
|
-
processName: info.name,
|
|
75
|
-
command: info.command,
|
|
76
|
-
status: info.status as "exited" | "killed",
|
|
77
|
-
exitCode: info.exitCode,
|
|
78
|
-
success: info.success ?? false,
|
|
79
|
-
runtime,
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
pi.sendMessage(
|
|
83
|
-
{
|
|
84
|
-
customType: MESSAGE_TYPE_PROCESS_UPDATE,
|
|
85
|
-
content: message,
|
|
86
|
-
display: true,
|
|
87
|
-
details,
|
|
88
|
-
},
|
|
89
|
-
{ triggerTurn: triggerAgentTurn },
|
|
90
|
-
);
|
|
91
|
-
});
|
|
92
|
-
}
|
package/hooks/widget.ts
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ExtensionAPI,
|
|
3
|
-
ExtensionContext,
|
|
4
|
-
} from "@mariozechner/pi-coding-agent";
|
|
5
|
-
import { visibleWidth } from "@mariozechner/pi-tui";
|
|
6
|
-
import { configLoader } from "../config";
|
|
7
|
-
import type { ProcessInfo } from "../constants";
|
|
8
|
-
import type { ProcessManager } from "../manager";
|
|
9
|
-
|
|
10
|
-
const WIDGET_ID = "processes-status";
|
|
11
|
-
|
|
12
|
-
function formatProcessStatus(
|
|
13
|
-
proc: ProcessInfo,
|
|
14
|
-
theme: ExtensionContext["ui"]["theme"],
|
|
15
|
-
): string {
|
|
16
|
-
const name =
|
|
17
|
-
proc.name.length > 20 ? `${proc.name.slice(0, 17)}...` : proc.name;
|
|
18
|
-
|
|
19
|
-
switch (proc.status) {
|
|
20
|
-
case "running":
|
|
21
|
-
return `${theme.fg("accent", name)} ${theme.fg("dim", "running")}`;
|
|
22
|
-
case "terminating":
|
|
23
|
-
return `${theme.fg("warning", name)} ${theme.fg("dim", "terminating")}`;
|
|
24
|
-
case "terminate_timeout":
|
|
25
|
-
return `${theme.fg("error", name)} ${theme.fg("error", "terminate_timeout")}`;
|
|
26
|
-
case "killed":
|
|
27
|
-
return `${theme.fg("warning", name)} ${theme.fg("dim", "killed")}`;
|
|
28
|
-
case "exited":
|
|
29
|
-
if (proc.success) {
|
|
30
|
-
return `${theme.fg("dim", name)} ${theme.fg("success", "done")}`;
|
|
31
|
-
}
|
|
32
|
-
return `${theme.fg("error", name)} ${theme.fg("error", `exit(${proc.exitCode ?? "?"})`)}`;
|
|
33
|
-
default:
|
|
34
|
-
return `${theme.fg("dim", name)} ${theme.fg("dim", proc.status)}`;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function renderWidget(
|
|
39
|
-
processes: ProcessInfo[],
|
|
40
|
-
theme: ExtensionContext["ui"]["theme"],
|
|
41
|
-
maxWidth?: number,
|
|
42
|
-
): string[] {
|
|
43
|
-
if (processes.length === 0) {
|
|
44
|
-
return [];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const aliveish = processes.filter(
|
|
48
|
-
(p) =>
|
|
49
|
-
p.status === "running" ||
|
|
50
|
-
p.status === "terminating" ||
|
|
51
|
-
p.status === "terminate_timeout",
|
|
52
|
-
);
|
|
53
|
-
const finished = processes.filter(
|
|
54
|
-
(p) =>
|
|
55
|
-
p.status !== "running" &&
|
|
56
|
-
p.status !== "terminating" &&
|
|
57
|
-
p.status !== "terminate_timeout",
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
const allProcs: ProcessInfo[] = [
|
|
61
|
-
...aliveish,
|
|
62
|
-
...finished.sort((a, b) => (b.endTime ?? 0) - (a.endTime ?? 0)),
|
|
63
|
-
];
|
|
64
|
-
|
|
65
|
-
const prefix = theme.fg("dim", "processes: ");
|
|
66
|
-
const prefixLen = visibleWidth(prefix);
|
|
67
|
-
const separator = theme.fg("dim", " | ");
|
|
68
|
-
const separatorLen = visibleWidth(separator);
|
|
69
|
-
const effectiveMax = maxWidth ?? 200;
|
|
70
|
-
|
|
71
|
-
const parts: string[] = [];
|
|
72
|
-
let currentLen = prefixLen;
|
|
73
|
-
let includedCount = 0;
|
|
74
|
-
|
|
75
|
-
for (const proc of allProcs) {
|
|
76
|
-
const formatted = formatProcessStatus(proc, theme);
|
|
77
|
-
const formattedLen = visibleWidth(formatted);
|
|
78
|
-
|
|
79
|
-
// Check if adding this part would exceed the width
|
|
80
|
-
const needed =
|
|
81
|
-
includedCount > 0 ? separatorLen + formattedLen : formattedLen;
|
|
82
|
-
|
|
83
|
-
if (currentLen + needed > effectiveMax && includedCount > 0) {
|
|
84
|
-
// Show how many are hidden
|
|
85
|
-
const remaining = allProcs.length - includedCount;
|
|
86
|
-
if (remaining > 0) {
|
|
87
|
-
parts.push(theme.fg("dim", `+${remaining} more`));
|
|
88
|
-
}
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
parts.push(formatted);
|
|
93
|
-
currentLen += needed;
|
|
94
|
-
includedCount++;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (parts.length === 0) {
|
|
98
|
-
return [];
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return [prefix + parts.join(separator)];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function setupProcessWidget(pi: ExtensionAPI, manager: ProcessManager) {
|
|
105
|
-
let latestContext: ExtensionContext | null = null;
|
|
106
|
-
|
|
107
|
-
function updateWidget() {
|
|
108
|
-
if (!latestContext?.hasUI) return;
|
|
109
|
-
|
|
110
|
-
if (!configLoader.getConfig().widget.showStatusWidget) {
|
|
111
|
-
latestContext.ui.setWidget(WIDGET_ID, undefined);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const processes = manager.list();
|
|
116
|
-
const maxWidth = process.stdout.columns || 120;
|
|
117
|
-
const lines = renderWidget(processes, latestContext.ui.theme, maxWidth);
|
|
118
|
-
|
|
119
|
-
if (lines.length === 0) {
|
|
120
|
-
latestContext.ui.setWidget(WIDGET_ID, undefined);
|
|
121
|
-
} else {
|
|
122
|
-
latestContext.ui.setWidget(WIDGET_ID, lines, {
|
|
123
|
-
placement: "belowEditor",
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
129
|
-
// Startup defer: capture context only. First render happens on process
|
|
130
|
-
// manager events or explicit settings updates.
|
|
131
|
-
latestContext = ctx;
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
pi.on("session_switch", async (_event, ctx) => {
|
|
135
|
-
latestContext = ctx;
|
|
136
|
-
updateWidget();
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
manager.onEvent(() => {
|
|
140
|
-
updateWidget();
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
update: updateWidget,
|
|
145
|
-
};
|
|
146
|
-
}
|
package/index.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import { setupProcessesCommands } from "./commands";
|
|
3
|
-
import { registerProcessesSettings } from "./commands/settings-command";
|
|
4
|
-
import { configLoader } from "./config";
|
|
5
|
-
import { setupProcessesHooks } from "./hooks";
|
|
6
|
-
import { ProcessManager } from "./manager";
|
|
7
|
-
import { setupProcessesTools } from "./tools";
|
|
8
|
-
|
|
9
|
-
export default async function (pi: ExtensionAPI) {
|
|
10
|
-
if (process.platform === "win32") {
|
|
11
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
12
|
-
if (!ctx.hasUI) return;
|
|
13
|
-
ctx.ui.notify("processes extension not available on Windows", "warning");
|
|
14
|
-
});
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
await configLoader.load();
|
|
19
|
-
const manager = new ProcessManager({
|
|
20
|
-
getConfiguredShellPath: () => configLoader.getConfig().execution.shellPath,
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const { update: updateWidget } = setupProcessesHooks(pi, manager);
|
|
24
|
-
setupProcessesCommands(pi, manager);
|
|
25
|
-
setupProcessesTools(pi, manager);
|
|
26
|
-
registerProcessesSettings(pi, () => {
|
|
27
|
-
updateWidget();
|
|
28
|
-
});
|
|
29
|
-
}
|