palmier 0.9.3 → 0.9.4
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/dist/commands/run.js +6 -0
- package/dist/commands/serve.js +61 -36
- package/dist/pwa/assets/{index-CknFGshO.js → index-DX5qJgHZ.js} +4 -4
- package/dist/pwa/assets/{web-DdzXb-jW.js → web-Dcldtodb.js} +1 -1
- package/dist/pwa/assets/{web-Dl9aC-Qr.js → web-DdVpqhvX.js} +1 -1
- package/dist/pwa/assets/{web-a9jK1xeo.js → web-Eg0A6HEi.js} +1 -1
- package/dist/pwa/index.html +2 -2
- package/dist/pwa/manifest.webmanifest +1 -1
- package/dist/pwa/service-worker.js +1 -1
- package/dist/rpc-handler.js +41 -19
- package/dist/task.d.ts +7 -0
- package/dist/task.js +17 -0
- package/dist/types.d.ts +4 -0
- package/package.json +1 -1
- package/palmier-server/pwa/index.html +1 -1
- package/palmier-server/pwa/src/components/RunDetailView.tsx +1 -1
- package/palmier-server/pwa/src/components/TaskForm.tsx +5 -3
- package/palmier-server/pwa/vite.config.ts +1 -1
- package/src/commands/run.ts +3 -0
- package/src/commands/serve.ts +62 -37
- package/src/rpc-handler.ts +34 -18
- package/src/task.ts +21 -0
- package/src/types.ts +4 -0
package/dist/commands/run.js
CHANGED
|
@@ -150,6 +150,12 @@ export async function runCommand(taskId) {
|
|
|
150
150
|
if (nc && !nc.isClosed()) {
|
|
151
151
|
await nc.drain();
|
|
152
152
|
}
|
|
153
|
+
if (task.frontmatter.one_off) {
|
|
154
|
+
try {
|
|
155
|
+
getPlatform().removeTaskTimer(taskId);
|
|
156
|
+
}
|
|
157
|
+
catch { /* best-effort */ }
|
|
158
|
+
}
|
|
153
159
|
};
|
|
154
160
|
try {
|
|
155
161
|
nc = await connectNats(config);
|
package/dist/commands/serve.js
CHANGED
|
@@ -5,7 +5,7 @@ import { connectNats } from "../nats-client.js";
|
|
|
5
5
|
import { createRpcHandler } from "../rpc-handler.js";
|
|
6
6
|
import { startNatsTransport } from "../transports/nats-transport.js";
|
|
7
7
|
import { startHttpTransport } from "../transports/http-transport.js";
|
|
8
|
-
import { getTaskDir, readTaskStatus, writeTaskStatus, parseTaskFile, appendRunMessage, listTasks } from "../task.js";
|
|
8
|
+
import { getTaskDir, readTaskStatus, writeTaskStatus, parseTaskFile, appendRunMessage, listTasks, readFollowupStatus, deleteFollowupStatus } from "../task.js";
|
|
9
9
|
import { publishHostEvent } from "../events.js";
|
|
10
10
|
import { getPlatform } from "../platform/index.js";
|
|
11
11
|
import { detectAgents } from "../agents/agent.js";
|
|
@@ -18,54 +18,79 @@ import { enqueueEvent } from "../event-queues.js";
|
|
|
18
18
|
const POLL_INTERVAL_MS = 30_000;
|
|
19
19
|
const DAEMON_PID_FILE = path.join(CONFIG_DIR, "daemon.pid");
|
|
20
20
|
/**
|
|
21
|
-
* Reconcile tasks stuck in "started" whose process is no longer alive
|
|
21
|
+
* Reconcile tasks stuck in "started" whose process is no longer alive, and
|
|
22
|
+
* clean up OS scheduler units for one-off tasks that have already terminated.
|
|
22
23
|
* The system scheduler (Task Scheduler / systemd) is the authoritative source.
|
|
23
24
|
*/
|
|
24
25
|
async function checkStaleTasks(config, nc) {
|
|
25
|
-
const
|
|
26
|
-
if (!fs.existsSync(
|
|
26
|
+
const tasksRoot = path.join(config.projectRoot, "tasks");
|
|
27
|
+
if (!fs.existsSync(tasksRoot))
|
|
27
28
|
return;
|
|
28
29
|
const platform = getPlatform();
|
|
29
|
-
const
|
|
30
|
-
for (const
|
|
31
|
-
|
|
30
|
+
const taskIds = fs.readdirSync(tasksRoot).filter((f) => fs.statSync(path.join(tasksRoot, f)).isDirectory());
|
|
31
|
+
for (const taskId of taskIds) {
|
|
32
|
+
const taskDir = getTaskDir(config.projectRoot, taskId);
|
|
33
|
+
const status = readTaskStatus(taskDir);
|
|
34
|
+
if (!status)
|
|
35
|
+
continue;
|
|
36
|
+
let task;
|
|
32
37
|
try {
|
|
33
|
-
|
|
38
|
+
task = parseTaskFile(taskDir);
|
|
34
39
|
}
|
|
35
40
|
catch {
|
|
36
41
|
continue;
|
|
37
42
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
43
|
+
if (status.running_state === "started" && !platform.isTaskRunning(taskId)) {
|
|
44
|
+
console.log(`[monitor] Task ${taskId} process exited unexpectedly, marking as failed.`);
|
|
45
|
+
const endTime = Date.now();
|
|
46
|
+
writeTaskStatus(taskDir, { running_state: "failed", time_stamp: endTime });
|
|
47
|
+
const runId = fs.readdirSync(taskDir)
|
|
48
|
+
.filter((f) => /^\d+$/.test(f) && fs.existsSync(path.join(taskDir, f, "TASKRUN.md")))
|
|
49
|
+
.sort()
|
|
50
|
+
.pop();
|
|
51
|
+
if (runId) {
|
|
52
|
+
appendRunMessage(taskDir, runId, {
|
|
53
|
+
role: "status",
|
|
54
|
+
time: endTime,
|
|
55
|
+
content: "",
|
|
56
|
+
type: "failed",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
await publishHostEvent(nc, config.hostId, taskId, {
|
|
60
|
+
event_type: "running-state",
|
|
61
|
+
running_state: "failed",
|
|
62
|
+
name: task.frontmatter.name || taskId,
|
|
57
63
|
});
|
|
58
64
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
if (task.frontmatter.one_off && status.running_state !== "started") {
|
|
66
|
+
try {
|
|
67
|
+
platform.removeTaskTimer(taskId);
|
|
68
|
+
}
|
|
69
|
+
catch { /* best-effort */ }
|
|
70
|
+
}
|
|
71
|
+
// Reconcile orphaned follow-ups: if a run has a persisted follow-up PID
|
|
72
|
+
// but that process is no longer alive, clear the file and mark the run
|
|
73
|
+
// as failed so the UI doesn't claim it's still running.
|
|
74
|
+
const runIds = fs.readdirSync(taskDir).filter((f) => /^\d+$/.test(f) && fs.existsSync(path.join(taskDir, f, "TASKRUN.md")));
|
|
75
|
+
for (const runId of runIds) {
|
|
76
|
+
const runDir = path.join(taskDir, runId);
|
|
77
|
+
const followup = readFollowupStatus(runDir);
|
|
78
|
+
if (!followup)
|
|
79
|
+
continue;
|
|
80
|
+
try {
|
|
81
|
+
process.kill(followup.pid, 0);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
deleteFollowupStatus(runDir);
|
|
85
|
+
appendRunMessage(taskDir, runId, {
|
|
86
|
+
role: "status",
|
|
87
|
+
time: Date.now(),
|
|
88
|
+
content: "",
|
|
89
|
+
type: "failed",
|
|
90
|
+
});
|
|
91
|
+
await publishHostEvent(nc, config.hostId, taskId, { event_type: "result-updated", run_id: runId });
|
|
92
|
+
}
|
|
62
93
|
}
|
|
63
|
-
catch { /* fallback to taskId */ }
|
|
64
|
-
await publishHostEvent(nc, config.hostId, taskId, {
|
|
65
|
-
event_type: "running-state",
|
|
66
|
-
running_state: "failed",
|
|
67
|
-
name: taskName,
|
|
68
|
-
});
|
|
69
94
|
}
|
|
70
95
|
}
|
|
71
96
|
export async function serveCommand() {
|