@aliou/pi-processes 0.6.4 → 0.7.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 +49 -0
- package/package.json +2 -1
- package/src/commands/kill/command.ts +6 -6
- package/src/commands/logs/command.ts +1 -1
- package/src/commands/pin/command.ts +2 -1
- package/src/components/log-dock-component.ts +0 -11
- package/src/components/log-overlay-component.ts +0 -9
- package/src/components/processes-component.ts +32 -16
- package/src/constants/index.ts +4 -0
- package/src/constants/types.ts +36 -1
- package/src/hooks/index.ts +2 -0
- package/src/hooks/message-renderer.ts +35 -3
- package/src/hooks/process-end.ts +2 -0
- package/src/hooks/process-watch.ts +83 -0
- package/src/manager.test.ts +331 -0
- package/src/manager.ts +214 -30
- package/src/tools/actions/debug.ts +148 -0
- package/src/tools/actions/index.ts +105 -10
- package/src/tools/actions/kill.ts +14 -1
- package/src/tools/actions/list.ts +153 -2
- package/src/tools/actions/logs.ts +61 -3
- package/src/tools/actions/output.ts +129 -4
- package/src/tools/actions/start.ts +197 -8
- package/src/tools/actions/write.ts +28 -2
- package/src/tools/index.ts +93 -246
- package/src/utils/command-executor.ts +3 -0
- package/src/utils/format.ts +32 -0
- package/src/utils/index.ts +7 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { ToolCallHeader } from "@aliou/pi-utils-ui";
|
|
2
|
+
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
3
|
+
import type { ExecuteResult, ProcessInfo } from "../../constants";
|
|
4
|
+
|
|
5
|
+
interface DebugParams {
|
|
6
|
+
preview?: "start" | "list" | "output" | "logs" | "error";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function renderDebugCall(
|
|
10
|
+
args: DebugParams,
|
|
11
|
+
theme: Theme,
|
|
12
|
+
): ToolCallHeader {
|
|
13
|
+
return new ToolCallHeader(
|
|
14
|
+
{
|
|
15
|
+
toolName: "Process",
|
|
16
|
+
action: "debug_preview",
|
|
17
|
+
mainArg: args.preview,
|
|
18
|
+
},
|
|
19
|
+
theme,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function mockProcess(overrides?: Partial<ProcessInfo>): ProcessInfo {
|
|
24
|
+
const now = Date.now();
|
|
25
|
+
return {
|
|
26
|
+
id: "proc_42",
|
|
27
|
+
name: "demo-server",
|
|
28
|
+
pid: 4242,
|
|
29
|
+
command: "pnpm dev --port 3000",
|
|
30
|
+
cwd: "/tmp/demo",
|
|
31
|
+
startTime: now - 12_000,
|
|
32
|
+
endTime: null,
|
|
33
|
+
status: "running",
|
|
34
|
+
exitCode: null,
|
|
35
|
+
success: null,
|
|
36
|
+
stdoutFile: "/tmp/pi-processes-demo/proc_42-stdout.log",
|
|
37
|
+
stderrFile: "/tmp/pi-processes-demo/proc_42-stderr.log",
|
|
38
|
+
alertOnSuccess: false,
|
|
39
|
+
alertOnFailure: true,
|
|
40
|
+
alertOnKill: false,
|
|
41
|
+
...overrides,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Temporary no-side-effect previews for process tool renderers.
|
|
47
|
+
* Remove before release.
|
|
48
|
+
*/
|
|
49
|
+
export function executeDebugPreview(params: DebugParams): ExecuteResult {
|
|
50
|
+
const preview = params.preview ?? "start";
|
|
51
|
+
|
|
52
|
+
if (preview === "start") {
|
|
53
|
+
const process = mockProcess();
|
|
54
|
+
const message = [
|
|
55
|
+
`Started "${process.name}" (${process.id}, PID: ${process.pid})`,
|
|
56
|
+
"Log files:",
|
|
57
|
+
` stdout: ${process.stdoutFile}`,
|
|
58
|
+
` stderr: ${process.stderrFile}`,
|
|
59
|
+
].join("\n");
|
|
60
|
+
return {
|
|
61
|
+
content: [{ type: "text", text: message }],
|
|
62
|
+
details: {
|
|
63
|
+
action: "start",
|
|
64
|
+
success: true,
|
|
65
|
+
message,
|
|
66
|
+
process,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (preview === "list") {
|
|
72
|
+
const processes = [
|
|
73
|
+
mockProcess(),
|
|
74
|
+
mockProcess({
|
|
75
|
+
id: "proc_11",
|
|
76
|
+
name: "builder",
|
|
77
|
+
pid: 1011,
|
|
78
|
+
command: "pnpm build --watch",
|
|
79
|
+
}),
|
|
80
|
+
mockProcess({
|
|
81
|
+
id: "proc_10",
|
|
82
|
+
name: "tests",
|
|
83
|
+
pid: 1010,
|
|
84
|
+
command: "pnpm test",
|
|
85
|
+
status: "exited",
|
|
86
|
+
success: false,
|
|
87
|
+
endTime: Date.now() - 3_000,
|
|
88
|
+
exitCode: 1,
|
|
89
|
+
}),
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
content: [{ type: "text", text: "Debug preview: list" }],
|
|
94
|
+
details: {
|
|
95
|
+
action: "list",
|
|
96
|
+
success: true,
|
|
97
|
+
message: "Debug preview: list",
|
|
98
|
+
processes,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (preview === "output") {
|
|
104
|
+
return {
|
|
105
|
+
content: [{ type: "text", text: "Debug preview: output" }],
|
|
106
|
+
details: {
|
|
107
|
+
action: "output",
|
|
108
|
+
success: true,
|
|
109
|
+
message:
|
|
110
|
+
'"demo-server" (proc_42) [running]: 4 stdout lines, 2 stderr lines',
|
|
111
|
+
output: {
|
|
112
|
+
status: "running",
|
|
113
|
+
stdout: [
|
|
114
|
+
"starting...",
|
|
115
|
+
"loading config",
|
|
116
|
+
"ready on http://localhost:3000",
|
|
117
|
+
"watching for changes",
|
|
118
|
+
],
|
|
119
|
+
stderr: [
|
|
120
|
+
"warn: deprecated option in config",
|
|
121
|
+
"error: simulated stack trace line",
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
logFiles: {
|
|
125
|
+
stdoutFile: "/tmp/pi-processes-demo/proc_42-stdout.log",
|
|
126
|
+
stderrFile: "/tmp/pi-processes-demo/proc_42-stderr.log",
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (preview === "logs") {
|
|
133
|
+
return {
|
|
134
|
+
content: [{ type: "text", text: "Debug preview: logs" }],
|
|
135
|
+
details: {
|
|
136
|
+
action: "logs",
|
|
137
|
+
success: true,
|
|
138
|
+
message: "Debug preview: logs",
|
|
139
|
+
logFiles: {
|
|
140
|
+
stdoutFile: "/tmp/pi-processes-demo/proc_42-stdout.log",
|
|
141
|
+
stderrFile: "/tmp/pi-processes-demo/proc_42-stderr.log",
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
throw new Error("Invalid logWatches[0].pattern: Unterminated group");
|
|
148
|
+
}
|
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
1
|
+
import { ToolBody, ToolCallHeader } from "@aliou/pi-utils-ui";
|
|
2
|
+
import type {
|
|
3
|
+
AgentToolResult,
|
|
4
|
+
ExtensionContext,
|
|
5
|
+
Theme,
|
|
6
|
+
ToolRenderResultOptions,
|
|
7
|
+
} from "@mariozechner/pi-coding-agent";
|
|
8
|
+
import type { Component } from "@mariozechner/pi-tui";
|
|
9
|
+
import type {
|
|
10
|
+
ExecuteResult,
|
|
11
|
+
ProcessAction,
|
|
12
|
+
ProcessesDetails,
|
|
13
|
+
} from "../../constants";
|
|
3
14
|
import type { ProcessManager } from "../../manager";
|
|
4
15
|
import { executeClear } from "./clear";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
16
|
+
import { executeDebugPreview, renderDebugCall } from "./debug";
|
|
17
|
+
import { executeKill, renderKillCall } from "./kill";
|
|
18
|
+
import { executeList, renderListResult } from "./list";
|
|
19
|
+
import { executeLogs, renderLogsCall, renderLogsResult } from "./logs";
|
|
20
|
+
import { executeOutput, renderOutputCall, renderOutputResult } from "./output";
|
|
21
|
+
import { executeStart, renderStartCall, renderStartResult } from "./start";
|
|
22
|
+
import { executeWrite, renderWriteCall } from "./write";
|
|
23
|
+
|
|
24
|
+
const DEBUG_PREVIEW_ENABLED = process.env.PI_PROCESSES_DEBUG_PREVIEW === "1";
|
|
11
25
|
|
|
12
26
|
interface ActionParams {
|
|
13
|
-
action: string;
|
|
27
|
+
action: ProcessAction | string;
|
|
14
28
|
command?: string;
|
|
15
29
|
name?: string;
|
|
16
30
|
id?: string;
|
|
@@ -19,6 +33,12 @@ interface ActionParams {
|
|
|
19
33
|
alertOnSuccess?: boolean;
|
|
20
34
|
alertOnFailure?: boolean;
|
|
21
35
|
alertOnKill?: boolean;
|
|
36
|
+
logWatches?: Array<{
|
|
37
|
+
pattern: string;
|
|
38
|
+
stream?: "stdout" | "stderr" | "both";
|
|
39
|
+
repeat?: boolean;
|
|
40
|
+
}>;
|
|
41
|
+
preview?: "start" | "list" | "output" | "logs" | "error";
|
|
22
42
|
}
|
|
23
43
|
|
|
24
44
|
export async function executeAction(
|
|
@@ -41,14 +61,89 @@ export async function executeAction(
|
|
|
41
61
|
return executeClear(manager);
|
|
42
62
|
case "write":
|
|
43
63
|
return executeWrite(params, manager);
|
|
64
|
+
case "debug_preview":
|
|
65
|
+
if (!DEBUG_PREVIEW_ENABLED) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
"Action 'debug_preview' is disabled. Set PI_PROCESSES_DEBUG_PREVIEW=1 to enable.",
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
return executeDebugPreview(params);
|
|
44
71
|
default:
|
|
45
72
|
return {
|
|
46
73
|
content: [{ type: "text", text: `Unknown action: ${params.action}` }],
|
|
47
74
|
details: {
|
|
48
|
-
action: params.action,
|
|
75
|
+
action: params.action as ProcessAction,
|
|
49
76
|
success: false,
|
|
50
77
|
message: `Unknown action: ${params.action}`,
|
|
51
78
|
},
|
|
52
79
|
};
|
|
53
80
|
}
|
|
54
81
|
}
|
|
82
|
+
|
|
83
|
+
export function renderActionCall(
|
|
84
|
+
args: ActionParams,
|
|
85
|
+
theme: Theme,
|
|
86
|
+
): Component | undefined {
|
|
87
|
+
switch (args.action) {
|
|
88
|
+
case "start":
|
|
89
|
+
return renderStartCall(args, theme);
|
|
90
|
+
case "output":
|
|
91
|
+
return renderOutputCall(args, theme);
|
|
92
|
+
case "logs":
|
|
93
|
+
return renderLogsCall(args, theme);
|
|
94
|
+
case "kill":
|
|
95
|
+
return renderKillCall(args, theme);
|
|
96
|
+
case "write":
|
|
97
|
+
return renderWriteCall(args, theme);
|
|
98
|
+
case "debug_preview":
|
|
99
|
+
return renderDebugCall(args, theme);
|
|
100
|
+
default:
|
|
101
|
+
return new ToolCallHeader(
|
|
102
|
+
{ toolName: "Process", action: args.action },
|
|
103
|
+
theme,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function renderActionResult(
|
|
109
|
+
result: AgentToolResult<ProcessesDetails>,
|
|
110
|
+
options: ToolRenderResultOptions,
|
|
111
|
+
theme: Theme,
|
|
112
|
+
): Component | undefined {
|
|
113
|
+
const { details } = result;
|
|
114
|
+
|
|
115
|
+
if (!details) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
switch (details.action) {
|
|
120
|
+
case "start":
|
|
121
|
+
return renderStartResult(result, options, theme);
|
|
122
|
+
case "list":
|
|
123
|
+
return renderListResult(result, options, theme);
|
|
124
|
+
case "output":
|
|
125
|
+
return renderOutputResult(result, options, theme);
|
|
126
|
+
case "logs":
|
|
127
|
+
return renderLogsResult(result, options, theme);
|
|
128
|
+
case "kill":
|
|
129
|
+
case "write":
|
|
130
|
+
case "clear":
|
|
131
|
+
case "debug_preview":
|
|
132
|
+
// Default rendering for these actions
|
|
133
|
+
return new ToolBody(
|
|
134
|
+
{
|
|
135
|
+
fields: [
|
|
136
|
+
{
|
|
137
|
+
label: "Result",
|
|
138
|
+
value: details.message,
|
|
139
|
+
showCollapsed: true,
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
options,
|
|
144
|
+
theme,
|
|
145
|
+
);
|
|
146
|
+
default:
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ToolCallHeader } from "@aliou/pi-utils-ui";
|
|
2
|
+
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
1
3
|
import type { ExecuteResult } from "../../constants";
|
|
2
4
|
import type { ProcessManager } from "../../manager";
|
|
3
5
|
|
|
@@ -5,6 +7,17 @@ interface KillParams {
|
|
|
5
7
|
id?: string;
|
|
6
8
|
}
|
|
7
9
|
|
|
10
|
+
export function renderKillCall(args: KillParams, theme: Theme): ToolCallHeader {
|
|
11
|
+
return new ToolCallHeader(
|
|
12
|
+
{
|
|
13
|
+
toolName: "Process",
|
|
14
|
+
action: "kill",
|
|
15
|
+
mainArg: args.id,
|
|
16
|
+
},
|
|
17
|
+
theme,
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
8
21
|
export async function executeKill(
|
|
9
22
|
params: KillParams,
|
|
10
23
|
manager: ProcessManager,
|
|
@@ -20,7 +33,7 @@ export async function executeKill(
|
|
|
20
33
|
};
|
|
21
34
|
}
|
|
22
35
|
|
|
23
|
-
const proc = manager.
|
|
36
|
+
const proc = manager.get(params.id);
|
|
24
37
|
if (!proc) {
|
|
25
38
|
const message = `Process not found: ${params.id}`;
|
|
26
39
|
return {
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ToolBody, ToolFooter } from "@aliou/pi-utils-ui";
|
|
2
|
+
import type {
|
|
3
|
+
AgentToolResult,
|
|
4
|
+
Theme,
|
|
5
|
+
ToolRenderResultOptions,
|
|
6
|
+
} from "@mariozechner/pi-coding-agent";
|
|
7
|
+
import { Text } from "@mariozechner/pi-tui";
|
|
8
|
+
import type { ExecuteResult, ProcessesDetails } from "../../constants";
|
|
2
9
|
import type { ProcessManager } from "../../manager";
|
|
3
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
formatRuntime,
|
|
12
|
+
formatStatus,
|
|
13
|
+
formatStatusTag,
|
|
14
|
+
formatTimestamp,
|
|
15
|
+
truncateCmd,
|
|
16
|
+
} from "../../utils";
|
|
4
17
|
|
|
5
18
|
export function executeList(manager: ProcessManager): ExecuteResult {
|
|
6
19
|
const processes = manager.list();
|
|
@@ -35,3 +48,141 @@ export function executeList(manager: ProcessManager): ExecuteResult {
|
|
|
35
48
|
},
|
|
36
49
|
};
|
|
37
50
|
}
|
|
51
|
+
|
|
52
|
+
export function renderListResult(
|
|
53
|
+
result: AgentToolResult<ProcessesDetails>,
|
|
54
|
+
options: ToolRenderResultOptions,
|
|
55
|
+
theme: Theme,
|
|
56
|
+
): ToolBody {
|
|
57
|
+
const { details } = result;
|
|
58
|
+
|
|
59
|
+
if (!details.processes || details.processes.length === 0) {
|
|
60
|
+
return new ToolBody(
|
|
61
|
+
{
|
|
62
|
+
fields: [
|
|
63
|
+
{
|
|
64
|
+
label: "Processes",
|
|
65
|
+
value: "No background processes running",
|
|
66
|
+
showCollapsed: true,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
options,
|
|
71
|
+
theme,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const processes = [...details.processes];
|
|
76
|
+
|
|
77
|
+
const statusRank = (status: string): number => {
|
|
78
|
+
switch (status) {
|
|
79
|
+
case "running":
|
|
80
|
+
return 0;
|
|
81
|
+
case "terminating":
|
|
82
|
+
return 1;
|
|
83
|
+
case "terminate_timeout":
|
|
84
|
+
return 2;
|
|
85
|
+
case "killed":
|
|
86
|
+
return 3;
|
|
87
|
+
case "exited":
|
|
88
|
+
return 4;
|
|
89
|
+
default:
|
|
90
|
+
return 5;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
processes.sort((a, b) => {
|
|
95
|
+
const rankDiff = statusRank(a.status) - statusRank(b.status);
|
|
96
|
+
if (rankDiff !== 0) return rankDiff;
|
|
97
|
+
return b.startTime - a.startTime;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const runningCount = processes.filter(
|
|
101
|
+
(p) => p.status === "running" || p.status === "terminating",
|
|
102
|
+
).length;
|
|
103
|
+
|
|
104
|
+
const lines: string[] = [
|
|
105
|
+
theme.fg(
|
|
106
|
+
"success",
|
|
107
|
+
`${processes.length} process(es), ${runningCount} running/terminating`,
|
|
108
|
+
),
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
for (const process of processes) {
|
|
112
|
+
const status = formatStatusTag(process, theme);
|
|
113
|
+
lines.push(
|
|
114
|
+
[
|
|
115
|
+
`- ${theme.fg("accent", process.name)} ${theme.fg("muted", `(${process.id})`)}`,
|
|
116
|
+
` pid: ${process.pid} status: ${status}`,
|
|
117
|
+
` started: ${theme.fg("muted", formatTimestamp(process.startTime))}`,
|
|
118
|
+
` ended: ${theme.fg("muted", formatTimestamp(process.endTime))}`,
|
|
119
|
+
` runtime: ${theme.fg("muted", formatRuntime(process.startTime, process.endTime))}`,
|
|
120
|
+
].join("\n"),
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const fields: Array<
|
|
125
|
+
{ label: string; value: string; showCollapsed?: boolean } | Text
|
|
126
|
+
> = [new Text(lines.join("\n"), 0, 0)];
|
|
127
|
+
|
|
128
|
+
const running = details.processes.filter(
|
|
129
|
+
(p) => p.status === "running" || p.status === "terminating",
|
|
130
|
+
);
|
|
131
|
+
const finishedOk = details.processes.filter(
|
|
132
|
+
(p) => p.status === "exited" && p.success,
|
|
133
|
+
).length;
|
|
134
|
+
const failed = details.processes.filter(
|
|
135
|
+
(p) => p.status === "exited" && !p.success,
|
|
136
|
+
).length;
|
|
137
|
+
const killed = details.processes.filter((p) => p.status === "killed").length;
|
|
138
|
+
|
|
139
|
+
const runningSummary =
|
|
140
|
+
running.length > 0
|
|
141
|
+
? running
|
|
142
|
+
.slice(0, 3)
|
|
143
|
+
.map(
|
|
144
|
+
(p) =>
|
|
145
|
+
`${theme.fg("accent", `"${p.name}"`)} [${formatStatusTag(p, theme)}]`,
|
|
146
|
+
)
|
|
147
|
+
.join(", ")
|
|
148
|
+
: theme.fg("muted", "no running process");
|
|
149
|
+
|
|
150
|
+
const restParts: string[] = [];
|
|
151
|
+
if (finishedOk > 0) restParts.push(`${finishedOk} finished`);
|
|
152
|
+
if (failed > 0) restParts.push(`${failed} failed`);
|
|
153
|
+
if (killed > 0) restParts.push(`${killed} killed`);
|
|
154
|
+
const restSummary =
|
|
155
|
+
restParts.length > 0 ? theme.fg("muted", ` + ${restParts.join(", ")}`) : "";
|
|
156
|
+
|
|
157
|
+
fields.push({
|
|
158
|
+
label: "Processes",
|
|
159
|
+
value: runningSummary + restSummary,
|
|
160
|
+
showCollapsed: true,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const footerItems: Array<{
|
|
164
|
+
label: string;
|
|
165
|
+
value: string;
|
|
166
|
+
}> = [];
|
|
167
|
+
if (runningCount > 0) {
|
|
168
|
+
footerItems.push({ label: "running", value: String(runningCount) });
|
|
169
|
+
}
|
|
170
|
+
if (failed > 0) {
|
|
171
|
+
footerItems.push({ label: "failed", value: String(failed) });
|
|
172
|
+
}
|
|
173
|
+
if (killed > 0) {
|
|
174
|
+
footerItems.push({ label: "killed", value: String(killed) });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return new ToolBody(
|
|
178
|
+
{
|
|
179
|
+
fields,
|
|
180
|
+
footer:
|
|
181
|
+
footerItems.length > 0
|
|
182
|
+
? new ToolFooter(theme, { items: footerItems, separator: " | " })
|
|
183
|
+
: undefined,
|
|
184
|
+
},
|
|
185
|
+
options,
|
|
186
|
+
theme,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
@@ -1,10 +1,68 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ToolBody, ToolCallHeader } from "@aliou/pi-utils-ui";
|
|
2
|
+
import type {
|
|
3
|
+
AgentToolResult,
|
|
4
|
+
Theme,
|
|
5
|
+
ToolRenderResultOptions,
|
|
6
|
+
} from "@mariozechner/pi-coding-agent";
|
|
7
|
+
import { Text } from "@mariozechner/pi-tui";
|
|
8
|
+
import type { ExecuteResult, ProcessesDetails } from "../../constants";
|
|
2
9
|
import type { ProcessManager } from "../../manager";
|
|
3
10
|
|
|
4
11
|
interface LogsParams {
|
|
5
12
|
id?: string;
|
|
6
13
|
}
|
|
7
14
|
|
|
15
|
+
export function renderLogsCall(args: LogsParams, theme: Theme): ToolCallHeader {
|
|
16
|
+
return new ToolCallHeader(
|
|
17
|
+
{
|
|
18
|
+
toolName: "Process",
|
|
19
|
+
action: "logs",
|
|
20
|
+
mainArg: args.id,
|
|
21
|
+
},
|
|
22
|
+
theme,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function renderLogsResult(
|
|
27
|
+
result: AgentToolResult<ProcessesDetails>,
|
|
28
|
+
options: ToolRenderResultOptions,
|
|
29
|
+
theme: Theme,
|
|
30
|
+
): ToolBody {
|
|
31
|
+
const { details } = result;
|
|
32
|
+
|
|
33
|
+
if (!details.logFiles) {
|
|
34
|
+
return new ToolBody(
|
|
35
|
+
{
|
|
36
|
+
fields: [
|
|
37
|
+
{
|
|
38
|
+
label: "Error",
|
|
39
|
+
value: "Missing log file details",
|
|
40
|
+
showCollapsed: true,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
options,
|
|
45
|
+
theme,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const fields: Array<
|
|
50
|
+
{ label: string; value: string; showCollapsed?: boolean } | Text
|
|
51
|
+
> = [
|
|
52
|
+
new Text(
|
|
53
|
+
[
|
|
54
|
+
theme.fg("success", "Log files:"),
|
|
55
|
+
` stdout: ${theme.fg("accent", details.logFiles.stdoutFile)}`,
|
|
56
|
+
` stderr: ${theme.fg("accent", details.logFiles.stderrFile)}`,
|
|
57
|
+
].join("\n"),
|
|
58
|
+
0,
|
|
59
|
+
0,
|
|
60
|
+
),
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
return new ToolBody({ fields }, options, theme);
|
|
64
|
+
}
|
|
65
|
+
|
|
8
66
|
export function executeLogs(
|
|
9
67
|
params: LogsParams,
|
|
10
68
|
manager: ProcessManager,
|
|
@@ -20,7 +78,7 @@ export function executeLogs(
|
|
|
20
78
|
};
|
|
21
79
|
}
|
|
22
80
|
|
|
23
|
-
const proc = manager.
|
|
81
|
+
const proc = manager.get(params.id);
|
|
24
82
|
if (!proc) {
|
|
25
83
|
const message = `Process not found: ${params.id}`;
|
|
26
84
|
return {
|
|
@@ -35,7 +93,7 @@ export function executeLogs(
|
|
|
35
93
|
|
|
36
94
|
const logFiles = manager.getLogFiles(proc.id);
|
|
37
95
|
if (!logFiles) {
|
|
38
|
-
const message = `Could not get log files for
|
|
96
|
+
const message = `Could not get log files for "${proc.name}" (${proc.id})`;
|
|
39
97
|
return {
|
|
40
98
|
content: [{ type: "text", text: message }],
|
|
41
99
|
details: {
|