pi-forge 1.2.3 → 1.2.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 +1 -1
- package/dist/client/assets/{CodeMirrorEditor-1gu-DS9k.js → CodeMirrorEditor-DXmxwE2Z.js} +2 -2
- package/dist/client/assets/{CodeMirrorEditor-1gu-DS9k.js.map → CodeMirrorEditor-DXmxwE2Z.js.map} +1 -1
- package/dist/client/assets/index-CMSjnWtF.js +365 -0
- package/dist/client/assets/index-CMSjnWtF.js.map +1 -0
- package/dist/client/assets/index-Cp8qEy7Q.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/client/sw.js +1 -1
- package/dist/client/sw.js.map +1 -1
- package/dist/server/agent-extensions/compaction-continuation.js +65 -0
- package/dist/server/agent-extensions/compaction-continuation.js.map +1 -0
- package/dist/server/agent-resource-loader.js +10 -0
- package/dist/server/agent-resource-loader.js.map +1 -1
- package/dist/server/ask-user-question/envelope.js +56 -0
- package/dist/server/ask-user-question/envelope.js.map +1 -0
- package/dist/server/ask-user-question/prompt-strings.js +44 -0
- package/dist/server/ask-user-question/prompt-strings.js.map +1 -0
- package/dist/server/ask-user-question/registry.js +157 -0
- package/dist/server/ask-user-question/registry.js.map +1 -0
- package/dist/server/ask-user-question/tool.js +115 -0
- package/dist/server/ask-user-question/tool.js.map +1 -0
- package/dist/server/ask-user-question/types.js +27 -0
- package/dist/server/ask-user-question/types.js.map +1 -0
- package/dist/server/ask-user-question/validate.js +135 -0
- package/dist/server/ask-user-question/validate.js.map +1 -0
- package/dist/server/config.js +10 -0
- package/dist/server/config.js.map +1 -1
- package/dist/server/index.js +16 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/tool-bridge.js +14 -8
- package/dist/server/mcp/tool-bridge.js.map +1 -1
- package/dist/server/processes/envelope.js +60 -0
- package/dist/server/processes/envelope.js.map +1 -0
- package/dist/server/processes/log-store.js +132 -0
- package/dist/server/processes/log-store.js.map +1 -0
- package/dist/server/processes/manager.js +348 -0
- package/dist/server/processes/manager.js.map +1 -0
- package/dist/server/processes/prompt-strings.js +43 -0
- package/dist/server/processes/prompt-strings.js.map +1 -0
- package/dist/server/processes/tool.js +273 -0
- package/dist/server/processes/tool.js.map +1 -0
- package/dist/server/processes/types.js +21 -0
- package/dist/server/processes/types.js.map +1 -0
- package/dist/server/processes/watches.js +59 -0
- package/dist/server/processes/watches.js.map +1 -0
- package/dist/server/quick-actions.js +141 -0
- package/dist/server/quick-actions.js.map +1 -0
- package/dist/server/routes/ask-user-question.js +129 -0
- package/dist/server/routes/ask-user-question.js.map +1 -0
- package/dist/server/routes/config.js +12 -0
- package/dist/server/routes/config.js.map +1 -1
- package/dist/server/routes/processes.js +228 -0
- package/dist/server/routes/processes.js.map +1 -0
- package/dist/server/routes/quick-actions.js +384 -0
- package/dist/server/routes/quick-actions.js.map +1 -0
- package/dist/server/routes/todos.js +67 -0
- package/dist/server/routes/todos.js.map +1 -0
- package/dist/server/session-registry.js +72 -4
- package/dist/server/session-registry.js.map +1 -1
- package/dist/server/sse-bridge.js +225 -4
- package/dist/server/sse-bridge.js.map +1 -1
- package/dist/server/todo/envelope.js +87 -0
- package/dist/server/todo/envelope.js.map +1 -0
- package/dist/server/todo/invariants.js +21 -0
- package/dist/server/todo/invariants.js.map +1 -0
- package/dist/server/todo/prompt-strings.js +29 -0
- package/dist/server/todo/prompt-strings.js.map +1 -0
- package/dist/server/todo/reducer.js +189 -0
- package/dist/server/todo/reducer.js.map +1 -0
- package/dist/server/todo/replay.js +45 -0
- package/dist/server/todo/replay.js.map +1 -0
- package/dist/server/todo/store.js +92 -0
- package/dist/server/todo/store.js.map +1 -0
- package/dist/server/todo/task-graph.js +60 -0
- package/dist/server/todo/task-graph.js.map +1 -0
- package/dist/server/todo/tool.js +95 -0
- package/dist/server/todo/tool.js.map +1 -0
- package/dist/server/todo/types.js +23 -0
- package/dist/server/todo/types.js.map +1 -0
- package/package.json +1 -1
- package/dist/client/assets/index-BxZV6ddv.js +0 -359
- package/dist/client/assets/index-BxZV6ddv.js.map +0 -1
- package/dist/client/assets/index-KUhxvBxw.css +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-bridge.js","sourceRoot":"","sources":["../../src/mcp/tool-bridge.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"tool-bridge.js","sourceRoot":"","sources":["../../src/mcp/tool-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { LIVE_STATUSES } from "./types.js";
|
|
2
|
+
/** Truncate a command for one-line display in `list`. */
|
|
3
|
+
function truncateCmd(cmd, max = 60) {
|
|
4
|
+
if (cmd.length <= max)
|
|
5
|
+
return cmd;
|
|
6
|
+
return `${cmd.slice(0, max - 1)}…`;
|
|
7
|
+
}
|
|
8
|
+
function formatRuntime(start, end) {
|
|
9
|
+
const ms = (end ?? Date.now()) - start;
|
|
10
|
+
if (ms < 1_000)
|
|
11
|
+
return `${ms}ms`;
|
|
12
|
+
const s = ms / 1_000;
|
|
13
|
+
if (s < 60)
|
|
14
|
+
return `${s.toFixed(1)}s`;
|
|
15
|
+
const m = s / 60;
|
|
16
|
+
if (m < 60)
|
|
17
|
+
return `${m.toFixed(1)}m`;
|
|
18
|
+
return `${(m / 60).toFixed(1)}h`;
|
|
19
|
+
}
|
|
20
|
+
function formatStatus(p) {
|
|
21
|
+
if (LIVE_STATUSES.has(p.status))
|
|
22
|
+
return p.status;
|
|
23
|
+
if (p.success === true)
|
|
24
|
+
return "exited(0)";
|
|
25
|
+
if (p.exitCode !== null)
|
|
26
|
+
return `${p.status}(${p.exitCode})`;
|
|
27
|
+
return p.status;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build the agent-facing result envelope. Per-action `details`
|
|
31
|
+
* fields piggyback on the same flat `ProcessesDetails` shape the
|
|
32
|
+
* plugin uses; agents that switch between implementations see the
|
|
33
|
+
* same field names.
|
|
34
|
+
*/
|
|
35
|
+
export function ok(details) {
|
|
36
|
+
return { content: [{ type: "text", text: details.message }], details };
|
|
37
|
+
}
|
|
38
|
+
export function err(action, message) {
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: "text", text: message }],
|
|
41
|
+
details: { action, success: false, message },
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** `start` summary the model reads. Mirrors the plugin's text shape. */
|
|
45
|
+
export function buildStartMessage(p) {
|
|
46
|
+
return [
|
|
47
|
+
`Started "${p.name}" (${p.id}, PID: ${p.pid})`,
|
|
48
|
+
"Log files:",
|
|
49
|
+
` stdout: ${p.stdoutFile}`,
|
|
50
|
+
` stderr: ${p.stderrFile}`,
|
|
51
|
+
].join("\n");
|
|
52
|
+
}
|
|
53
|
+
/** `list` summary. One line per process, oldest sort handled by caller. */
|
|
54
|
+
export function buildListMessage(processes) {
|
|
55
|
+
if (processes.length === 0)
|
|
56
|
+
return "No background processes running";
|
|
57
|
+
const lines = processes.map((p) => `${p.id} "${p.name}": ${truncateCmd(p.command)} [${formatStatus(p)}] ${formatRuntime(p.startTime, p.endTime)}`);
|
|
58
|
+
return `${processes.length} process(es):\n${lines.join("\n")}`;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../src/processes/envelope.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { createWriteStream, mkdirSync } from "node:fs";
|
|
2
|
+
import { rename, stat } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
export const DEFAULT_OPTIONS = {
|
|
5
|
+
ringMaxLines: 1_000,
|
|
6
|
+
ringMaxBytes: 64 * 1024,
|
|
7
|
+
diskMaxBytes: 10 * 1024 * 1024,
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* One per process+stream. Owns its WriteStream (closes on dispose)
|
|
11
|
+
* and a ring buffer of recent lines split on `\n`. The append()
|
|
12
|
+
* call is synchronous-looking from the caller's perspective — the
|
|
13
|
+
* write to disk is fire-and-forget through the stream's internal
|
|
14
|
+
* buffer.
|
|
15
|
+
*/
|
|
16
|
+
export class LogChannel {
|
|
17
|
+
opts;
|
|
18
|
+
filePath;
|
|
19
|
+
stream;
|
|
20
|
+
byteCount = 0;
|
|
21
|
+
rotating = false;
|
|
22
|
+
/** Pending tail of an incoming chunk that didn't end on a newline. */
|
|
23
|
+
partial = "";
|
|
24
|
+
/** Most-recent N lines; oldest evicted first. */
|
|
25
|
+
lines = [];
|
|
26
|
+
linesBytes = 0;
|
|
27
|
+
disposed = false;
|
|
28
|
+
constructor(filePath, opts = {}) {
|
|
29
|
+
this.opts = { ...DEFAULT_OPTIONS, ...opts };
|
|
30
|
+
this.filePath = filePath;
|
|
31
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
32
|
+
this.stream = createWriteStream(filePath, { flags: "a" });
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Push a chunk of process output. Splits on `\n` to update the
|
|
36
|
+
* ring buffer (and to feed log watches via the caller's
|
|
37
|
+
* `onLine` callback). Disk write is the raw chunk — preserves
|
|
38
|
+
* the original bytes including any embedded carriage returns
|
|
39
|
+
* the process emitted.
|
|
40
|
+
*/
|
|
41
|
+
append(chunk, onLine) {
|
|
42
|
+
if (this.disposed)
|
|
43
|
+
return;
|
|
44
|
+
const bytes = chunk.length;
|
|
45
|
+
this.byteCount += bytes;
|
|
46
|
+
this.stream.write(chunk);
|
|
47
|
+
// Maybe rotate AFTER writing — the file is what we want to cap,
|
|
48
|
+
// not the chunk size.
|
|
49
|
+
if (this.byteCount >= this.opts.diskMaxBytes && !this.rotating) {
|
|
50
|
+
void this.rotate();
|
|
51
|
+
}
|
|
52
|
+
const text = chunk.toString("utf8");
|
|
53
|
+
let buf = this.partial + text;
|
|
54
|
+
let nl = buf.indexOf("\n");
|
|
55
|
+
while (nl !== -1) {
|
|
56
|
+
const line = buf.slice(0, nl);
|
|
57
|
+
buf = buf.slice(nl + 1);
|
|
58
|
+
this.pushLine(line);
|
|
59
|
+
if (onLine !== undefined)
|
|
60
|
+
onLine(line);
|
|
61
|
+
nl = buf.indexOf("\n");
|
|
62
|
+
}
|
|
63
|
+
this.partial = buf;
|
|
64
|
+
}
|
|
65
|
+
pushLine(line) {
|
|
66
|
+
const bytes = Buffer.byteLength(line, "utf8") + 1;
|
|
67
|
+
this.lines.push(line);
|
|
68
|
+
this.linesBytes += bytes;
|
|
69
|
+
while (this.lines.length > this.opts.ringMaxLines || this.linesBytes > this.opts.ringMaxBytes) {
|
|
70
|
+
const dropped = this.lines.shift();
|
|
71
|
+
if (dropped === undefined)
|
|
72
|
+
break;
|
|
73
|
+
this.linesBytes -= Buffer.byteLength(dropped, "utf8") + 1;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async rotate() {
|
|
77
|
+
this.rotating = true;
|
|
78
|
+
try {
|
|
79
|
+
const backup = `${this.filePath}.1`;
|
|
80
|
+
// Close the current stream so the rename succeeds on Windows
|
|
81
|
+
// (best-effort — Unix doesn't need it but it's idempotent).
|
|
82
|
+
await new Promise((resolve) => this.stream.end(() => resolve()));
|
|
83
|
+
await rename(this.filePath, backup).catch(() => undefined);
|
|
84
|
+
this.stream = createWriteStream(this.filePath, { flags: "a" });
|
|
85
|
+
// Re-stat to refresh byteCount (the rename happens to a
|
|
86
|
+
// possibly-stale `byteCount` value if writes raced — re-read
|
|
87
|
+
// the truth).
|
|
88
|
+
try {
|
|
89
|
+
const st = await stat(this.filePath);
|
|
90
|
+
this.byteCount = st.size;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
this.byteCount = 0;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
this.rotating = false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Latest N lines (default: all retained). */
|
|
101
|
+
tail(n) {
|
|
102
|
+
if (n === undefined || n >= this.lines.length)
|
|
103
|
+
return [...this.lines];
|
|
104
|
+
return this.lines.slice(this.lines.length - n);
|
|
105
|
+
}
|
|
106
|
+
filePathOnDisk() {
|
|
107
|
+
return this.filePath;
|
|
108
|
+
}
|
|
109
|
+
async dispose() {
|
|
110
|
+
if (this.disposed)
|
|
111
|
+
return;
|
|
112
|
+
this.disposed = true;
|
|
113
|
+
// Flush any partial line into the ring buffer (no trailing
|
|
114
|
+
// newline, but the user-visible "last line" makes sense).
|
|
115
|
+
if (this.partial.length > 0) {
|
|
116
|
+
this.pushLine(this.partial);
|
|
117
|
+
this.partial = "";
|
|
118
|
+
}
|
|
119
|
+
await new Promise((resolve) => {
|
|
120
|
+
this.stream.end(() => resolve());
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Resolve a process's log directory under the session's tree.
|
|
126
|
+
* Lives in FORGE_DATA_DIR so it cascades with the rest of the
|
|
127
|
+
* forge's per-session state when a session is cold-deleted.
|
|
128
|
+
*/
|
|
129
|
+
export function processLogDir(forgeDataDir, sessionId, processId) {
|
|
130
|
+
return join(forgeDataDir, "processes", sessionId, processId);
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=log-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-store.js","sourceRoot":"","sources":["../../src/processes/log-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { rm } from "node:fs/promises";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { config } from "../config.js";
|
|
6
|
+
import { scrubbedEnv } from "../pty-manager.js";
|
|
7
|
+
import { LogChannel, processLogDir } from "./log-store.js";
|
|
8
|
+
import { buildMatchEvent, compileWatches, evaluateWatches } from "./watches.js";
|
|
9
|
+
import { LIVE_STATUSES, } from "./types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Per-session background process manager. One instance per
|
|
12
|
+
* sessionId; `createManagerForSession` lazily constructs and
|
|
13
|
+
* registers. On session dispose the registry calls
|
|
14
|
+
* `disposeManagerForSession` which SIGTERMs every live process,
|
|
15
|
+
* waits briefly for graceful exit, escalates to SIGKILL, then
|
|
16
|
+
* removes the session's log directory.
|
|
17
|
+
*
|
|
18
|
+
* State is in-memory only by deliberate choice (matches
|
|
19
|
+
* `@aliou/pi-processes`): a server restart drops everything;
|
|
20
|
+
* the OS may leak the actual children if pi-forge crashes mid-
|
|
21
|
+
* lifecycle, same as the plugin.
|
|
22
|
+
*/
|
|
23
|
+
const GRACE_MS = 5_000;
|
|
24
|
+
const SIGKILL_TIMEOUT_MS = 2_000;
|
|
25
|
+
class ProcessManagerRegistry {
|
|
26
|
+
bySession = new Map();
|
|
27
|
+
listeners = new Set();
|
|
28
|
+
subscribe(fn) {
|
|
29
|
+
this.listeners.add(fn);
|
|
30
|
+
return () => {
|
|
31
|
+
this.listeners.delete(fn);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
notify(event) {
|
|
35
|
+
for (const fn of this.listeners) {
|
|
36
|
+
try {
|
|
37
|
+
fn(event);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// best-effort fanout — listener errors must not break the manager
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
getOrCreateSession(sessionId) {
|
|
45
|
+
let s = this.bySession.get(sessionId);
|
|
46
|
+
if (s === undefined) {
|
|
47
|
+
s = { sessionId, processes: new Map() };
|
|
48
|
+
this.bySession.set(sessionId, s);
|
|
49
|
+
}
|
|
50
|
+
return s;
|
|
51
|
+
}
|
|
52
|
+
/** Spawn a new process bound to this session. */
|
|
53
|
+
start(sessionId, name, command, cwd, opts) {
|
|
54
|
+
const session = this.getOrCreateSession(sessionId);
|
|
55
|
+
const id = randomBytes(4).toString("hex");
|
|
56
|
+
const logDir = processLogDir(config.forgeDataDir, sessionId, id);
|
|
57
|
+
const stdoutFile = join(logDir, "stdout.log");
|
|
58
|
+
const stderrFile = join(logDir, "stderr.log");
|
|
59
|
+
// Spawn under `/bin/sh -c` so the command can use shell
|
|
60
|
+
// features (pipes, &&, env expansion). Scrubbed env matches
|
|
61
|
+
// the terminal + quick-actions posture: no pi-forge / provider
|
|
62
|
+
// secrets leak to the child.
|
|
63
|
+
const child = spawn("/bin/sh", ["-c", command], {
|
|
64
|
+
cwd,
|
|
65
|
+
env: scrubbedEnv(),
|
|
66
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
67
|
+
// detached=false on purpose — when pi-forge exits, the OS
|
|
68
|
+
// sends SIGTERM to the process group via the parent's death,
|
|
69
|
+
// matching the plugin's behavior. detached=true would orphan
|
|
70
|
+
// processes; we want them tied to our lifetime.
|
|
71
|
+
detached: false,
|
|
72
|
+
});
|
|
73
|
+
const info = {
|
|
74
|
+
id,
|
|
75
|
+
name,
|
|
76
|
+
pid: child.pid ?? -1,
|
|
77
|
+
command,
|
|
78
|
+
cwd,
|
|
79
|
+
startTime: Date.now(),
|
|
80
|
+
endTime: null,
|
|
81
|
+
status: "running",
|
|
82
|
+
exitCode: null,
|
|
83
|
+
success: null,
|
|
84
|
+
stdoutFile,
|
|
85
|
+
stderrFile,
|
|
86
|
+
alertOnSuccess: opts.alertOnSuccess === true,
|
|
87
|
+
alertOnFailure: opts.alertOnFailure !== false, // default true
|
|
88
|
+
alertOnKill: opts.alertOnKill === true,
|
|
89
|
+
};
|
|
90
|
+
const managed = {
|
|
91
|
+
info,
|
|
92
|
+
child,
|
|
93
|
+
stdout: new LogChannel(stdoutFile),
|
|
94
|
+
stderr: new LogChannel(stderrFile),
|
|
95
|
+
watches: compileWatches(opts.logWatches),
|
|
96
|
+
killSent: null,
|
|
97
|
+
killTimer: null,
|
|
98
|
+
};
|
|
99
|
+
session.processes.set(id, managed);
|
|
100
|
+
const wireUp = (source) => {
|
|
101
|
+
const stream = source === "stdout" ? child.stdout : child.stderr;
|
|
102
|
+
const channel = source === "stdout" ? managed.stdout : managed.stderr;
|
|
103
|
+
stream.on("data", (chunk) => {
|
|
104
|
+
channel.append(chunk, (line) => {
|
|
105
|
+
const hits = evaluateWatches(managed.watches, source, line);
|
|
106
|
+
for (const w of hits) {
|
|
107
|
+
this.notify({
|
|
108
|
+
type: "process_watch_matched",
|
|
109
|
+
sessionId,
|
|
110
|
+
match: buildMatchEvent(w, id, name, command, source, line),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
this.notify({ type: "process_output_changed", sessionId, id });
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
wireUp("stdout");
|
|
118
|
+
wireUp("stderr");
|
|
119
|
+
child.on("error", (err) => {
|
|
120
|
+
// Spawn failure path — child never came up. Surface as a
|
|
121
|
+
// failed exit so the agent gets a clean result.
|
|
122
|
+
const text = `[spawn error] ${err.message}\n`;
|
|
123
|
+
managed.stderr.append(Buffer.from(text, "utf8"));
|
|
124
|
+
info.endTime = Date.now();
|
|
125
|
+
info.exitCode = -1;
|
|
126
|
+
info.success = false;
|
|
127
|
+
info.status = "exited";
|
|
128
|
+
// Notify BEFORE async log cleanup so the UI updates
|
|
129
|
+
// immediately. Log dispose runs in the background.
|
|
130
|
+
this.notify({ type: "process_ended", sessionId, info });
|
|
131
|
+
this.notify({ type: "processes_changed", sessionId });
|
|
132
|
+
void this.finalize(managed);
|
|
133
|
+
});
|
|
134
|
+
child.on("close", (code, signal) => {
|
|
135
|
+
info.endTime = Date.now();
|
|
136
|
+
info.exitCode = typeof code === "number" ? code : null;
|
|
137
|
+
const wasKilled = managed.killSent !== null || signal !== null;
|
|
138
|
+
info.status = wasKilled ? "killed" : "exited";
|
|
139
|
+
info.success = info.exitCode === 0 && !wasKilled;
|
|
140
|
+
if (managed.killTimer !== null) {
|
|
141
|
+
clearTimeout(managed.killTimer);
|
|
142
|
+
managed.killTimer = null;
|
|
143
|
+
}
|
|
144
|
+
// Notify BEFORE flushing logs. The old order awaited
|
|
145
|
+
// stdout.dispose() + stderr.dispose() FIRST and only
|
|
146
|
+
// fanned out the status change after — for any process
|
|
147
|
+
// with non-trivial output, that gated the UI update on
|
|
148
|
+
// a multi-tick filesystem flush and made Kill feel
|
|
149
|
+
// broken (status visibly stuck on "terminating" until
|
|
150
|
+
// the flush finished). Status flip is what the user
|
|
151
|
+
// needs to see; log cleanup is bookkeeping.
|
|
152
|
+
this.notify({ type: "process_ended", sessionId, info });
|
|
153
|
+
this.notify({ type: "processes_changed", sessionId });
|
|
154
|
+
void this.finalize(managed);
|
|
155
|
+
});
|
|
156
|
+
this.notify({ type: "process_started", sessionId, info });
|
|
157
|
+
this.notify({ type: "processes_changed", sessionId });
|
|
158
|
+
return cloneInfo(info);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Async log-cleanup tail. Lifecycle notification fires
|
|
162
|
+
* synchronously from the close/error handler that called this;
|
|
163
|
+
* this method just flushes and closes the on-disk log streams
|
|
164
|
+
* so a slow filesystem can't delay the UI update.
|
|
165
|
+
*/
|
|
166
|
+
async finalize(managed) {
|
|
167
|
+
await managed.stdout.dispose();
|
|
168
|
+
await managed.stderr.dispose();
|
|
169
|
+
}
|
|
170
|
+
/** List every process for this session, newest-first. */
|
|
171
|
+
list(sessionId) {
|
|
172
|
+
const s = this.bySession.get(sessionId);
|
|
173
|
+
if (s === undefined)
|
|
174
|
+
return [];
|
|
175
|
+
return [...s.processes.values()]
|
|
176
|
+
.map((m) => cloneInfo(m.info))
|
|
177
|
+
.sort((a, b) => b.startTime - a.startTime);
|
|
178
|
+
}
|
|
179
|
+
/** Recent tail of stdout/stderr for a single process. */
|
|
180
|
+
output(sessionId, id, tail = 200) {
|
|
181
|
+
const m = this.getManaged(sessionId, id);
|
|
182
|
+
if (m === undefined)
|
|
183
|
+
return undefined;
|
|
184
|
+
return {
|
|
185
|
+
stdout: m.stdout.tail(tail),
|
|
186
|
+
stderr: m.stderr.tail(tail),
|
|
187
|
+
status: m.info.status,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
logFiles(sessionId, id) {
|
|
191
|
+
const m = this.getManaged(sessionId, id);
|
|
192
|
+
if (m === undefined)
|
|
193
|
+
return undefined;
|
|
194
|
+
return { stdoutFile: m.info.stdoutFile, stderrFile: m.info.stderrFile };
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Kill a process. SIGTERM, 5 s grace, then SIGKILL. Resolves
|
|
198
|
+
* once we've initiated the signal — the `process_ended` event
|
|
199
|
+
* fans out async when the OS reports close. The promise's
|
|
200
|
+
* resolved value tells the caller what happened immediately
|
|
201
|
+
* (process_already_dead, signal_sent, etc.).
|
|
202
|
+
*/
|
|
203
|
+
async kill(sessionId, id) {
|
|
204
|
+
const m = this.getManaged(sessionId, id);
|
|
205
|
+
if (m === undefined) {
|
|
206
|
+
return { ok: false, info: undefined, reason: "not_found" };
|
|
207
|
+
}
|
|
208
|
+
if (!LIVE_STATUSES.has(m.info.status)) {
|
|
209
|
+
// Already exited — return success with the final info so the
|
|
210
|
+
// caller can render a clean result instead of an error.
|
|
211
|
+
return { ok: true, info: cloneInfo(m.info) };
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
m.killSent = "SIGTERM";
|
|
215
|
+
m.info.status = "terminating";
|
|
216
|
+
this.notify({ type: "processes_changed", sessionId });
|
|
217
|
+
m.child.kill("SIGTERM");
|
|
218
|
+
m.killTimer = setTimeout(() => {
|
|
219
|
+
if (!LIVE_STATUSES.has(m.info.status))
|
|
220
|
+
return;
|
|
221
|
+
m.info.status = "terminate_timeout";
|
|
222
|
+
m.killSent = "SIGKILL";
|
|
223
|
+
this.notify({ type: "processes_changed", sessionId });
|
|
224
|
+
try {
|
|
225
|
+
m.child.kill("SIGKILL");
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// ignore
|
|
229
|
+
}
|
|
230
|
+
// Last-resort timeout — if SIGKILL still doesn't move the
|
|
231
|
+
// process (zombie / kernel state), wait one more beat then
|
|
232
|
+
// report timeout to the caller. The OS WILL eventually
|
|
233
|
+
// close the streams; we just can't wait forever.
|
|
234
|
+
setTimeout(() => {
|
|
235
|
+
// no-op — the close handler will mark the final state
|
|
236
|
+
}, SIGKILL_TIMEOUT_MS).unref();
|
|
237
|
+
}, GRACE_MS);
|
|
238
|
+
return { ok: true, info: cloneInfo(m.info) };
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
void err;
|
|
242
|
+
return { ok: false, info: cloneInfo(m.info), reason: "error" };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/** Pipe input to a live process's stdin. `end:true` closes the stream. */
|
|
246
|
+
async write(sessionId, id, input, end) {
|
|
247
|
+
const m = this.getManaged(sessionId, id);
|
|
248
|
+
if (m === undefined)
|
|
249
|
+
return { ok: false, reason: "not_found" };
|
|
250
|
+
if (!LIVE_STATUSES.has(m.info.status)) {
|
|
251
|
+
return { ok: false, reason: "process_exited" };
|
|
252
|
+
}
|
|
253
|
+
const stdin = m.child.stdin;
|
|
254
|
+
if (stdin === null || stdin.writableEnded) {
|
|
255
|
+
return { ok: false, reason: "stdin_closed" };
|
|
256
|
+
}
|
|
257
|
+
return await new Promise((resolve) => {
|
|
258
|
+
stdin.write(input, (err) => {
|
|
259
|
+
if (err !== null && err !== undefined) {
|
|
260
|
+
resolve({ ok: false, reason: "write_error" });
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (end) {
|
|
264
|
+
stdin.end(() => resolve({ ok: true }));
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
resolve({ ok: true });
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/** Drop all FINISHED processes for the session. Live ones stay. */
|
|
273
|
+
clear(sessionId) {
|
|
274
|
+
const s = this.bySession.get(sessionId);
|
|
275
|
+
if (s === undefined)
|
|
276
|
+
return 0;
|
|
277
|
+
let count = 0;
|
|
278
|
+
for (const [id, m] of s.processes) {
|
|
279
|
+
if (!LIVE_STATUSES.has(m.info.status)) {
|
|
280
|
+
s.processes.delete(id);
|
|
281
|
+
count += 1;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (count > 0)
|
|
285
|
+
this.notify({ type: "processes_changed", sessionId });
|
|
286
|
+
return count;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Dispose every process for this session: SIGTERM, brief wait,
|
|
290
|
+
* SIGKILL. Then unlink the session's entire log directory.
|
|
291
|
+
* Called from session-registry.disposeSession.
|
|
292
|
+
*/
|
|
293
|
+
async disposeSession(sessionId) {
|
|
294
|
+
const s = this.bySession.get(sessionId);
|
|
295
|
+
if (s === undefined)
|
|
296
|
+
return;
|
|
297
|
+
const live = [...s.processes.values()].filter((m) => LIVE_STATUSES.has(m.info.status));
|
|
298
|
+
for (const m of live) {
|
|
299
|
+
try {
|
|
300
|
+
m.killSent = "SIGTERM";
|
|
301
|
+
m.child.kill("SIGTERM");
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
// ignore
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (live.length > 0) {
|
|
308
|
+
await new Promise((resolve) => setTimeout(resolve, GRACE_MS).unref());
|
|
309
|
+
for (const m of live) {
|
|
310
|
+
if (LIVE_STATUSES.has(m.info.status)) {
|
|
311
|
+
try {
|
|
312
|
+
m.child.kill("SIGKILL");
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// ignore
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// Best-effort log cleanup. A failure here (e.g. EBUSY on
|
|
321
|
+
// Windows) just leaves the files on disk — harmless, the
|
|
322
|
+
// session dir will be cleaned up by deleteColdSession's
|
|
323
|
+
// cascade.
|
|
324
|
+
const logRoot = join(config.forgeDataDir, "processes", sessionId);
|
|
325
|
+
await rm(logRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
326
|
+
this.bySession.delete(sessionId);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Test-only: nuke everything WITHOUT signalling children. Use
|
|
330
|
+
* between integration test cases that spawn real processes;
|
|
331
|
+
* combine with explicit dispose for the cleanup of children
|
|
332
|
+
* the test actually started.
|
|
333
|
+
*/
|
|
334
|
+
_resetForTests() {
|
|
335
|
+
this.bySession.clear();
|
|
336
|
+
// listeners intentionally NOT cleared — the SSE bridge's
|
|
337
|
+
// subscription is process-lifetime.
|
|
338
|
+
}
|
|
339
|
+
getManaged(sessionId, id) {
|
|
340
|
+
return this.bySession.get(sessionId)?.processes.get(id);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function cloneInfo(info) {
|
|
344
|
+
return { ...info };
|
|
345
|
+
}
|
|
346
|
+
/** Singleton — one registry per process. */
|
|
347
|
+
export const processManager = new ProcessManagerRegistry();
|
|
348
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/processes/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt snippet + guidelines + tool description for the `process`
|
|
3
|
+
* tool.
|
|
4
|
+
*
|
|
5
|
+
* ─────────────────────────────────────────────────────────────────
|
|
6
|
+
* Adapted from @aliou/pi-processes (MIT).
|
|
7
|
+
* Copyright (c) aliou.
|
|
8
|
+
* https://github.com/aliou/pi-processes
|
|
9
|
+
* ─────────────────────────────────────────────────────────────────
|
|
10
|
+
*
|
|
11
|
+
* The wording is preserved because it's been tuned against real
|
|
12
|
+
* model behavior — rules like "avoid &, nohup, disown, or setsid
|
|
13
|
+
* when the process tool fits" steer the model toward the right
|
|
14
|
+
* primitive. The reducer, lifecycle, log handling, UI, and SSE
|
|
15
|
+
* wiring are pi-forge's own.
|
|
16
|
+
*/
|
|
17
|
+
export const PROMPT_SNIPPET = "Manage background processes without blocking the conversation";
|
|
18
|
+
export const PROMPT_GUIDELINES = [
|
|
19
|
+
"Use the process tool for long-running commands such as dev servers, test watchers, build watchers, and log tails instead of bash.",
|
|
20
|
+
"Avoid shell background patterns such as &, nohup, disown, or setsid when the process tool fits.",
|
|
21
|
+
"After starting a process, continue other work instead of waiting for it.",
|
|
22
|
+
"Use the pi-forge process tool's notify flags (alertOnSuccess / alertOnFailure / alertOnKill) and logWatches when you need to react to events without polling.",
|
|
23
|
+
];
|
|
24
|
+
export const TOOL_DESCRIPTION = `Manage background processes. Actions:
|
|
25
|
+
- start: Run command in background (requires 'name' and 'command')
|
|
26
|
+
- alertOnSuccess (default: false): Get a turn to react when process completes successfully
|
|
27
|
+
- alertOnFailure (default: true): Get a turn to react when process crashes/fails
|
|
28
|
+
- alertOnKill (default: false): Get a turn to react if killed by external signal (killing via tool never triggers a turn)
|
|
29
|
+
- logWatches (optional): Runtime output watches that trigger immediate alerts while running
|
|
30
|
+
- pattern: regex string to match per output line
|
|
31
|
+
- stream: stdout | stderr | both (default both)
|
|
32
|
+
- repeat: false by default (single-fire). Set true for repeat alerts
|
|
33
|
+
- list: Show all managed processes with their IDs and names
|
|
34
|
+
- output: Get recent stdout/stderr (requires 'id')
|
|
35
|
+
- logs: Get log file paths to inspect with read tool (requires 'id')
|
|
36
|
+
- kill: Terminate a process (requires 'id')
|
|
37
|
+
- clear: Remove all finished processes from the list
|
|
38
|
+
- write: Write to process stdin (requires 'id' and 'input', optional 'end' to close stdin)
|
|
39
|
+
|
|
40
|
+
Important: You DON'T need to poll or wait for processes. Notifications arrive automatically based on your preferences. Start processes and continue with other work — you'll be informed if something requires attention.
|
|
41
|
+
|
|
42
|
+
Note: User always sees process updates in the UI. The notify flags control whether YOU (the agent) get a turn to react (e.g. check results, fix code, restart).`;
|
|
43
|
+
//# sourceMappingURL=prompt-strings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-strings.js","sourceRoot":"","sources":["../../src/processes/prompt-strings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,+DAA+D,CAAC;AAE9F,MAAM,CAAC,MAAM,iBAAiB,GAAa;IACzC,mIAAmI;IACnI,iGAAiG;IACjG,0EAA0E;IAC1E,+JAA+J;CAChK,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;gKAkBgI,CAAC"}
|