palmier 0.2.9 → 0.3.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/dist/commands/serve.js +4 -0
- package/dist/rpc-handler.js +9 -2
- package/dist/task.d.ts +0 -4
- package/dist/task.js +0 -12
- package/dist/update-checker.d.ts +14 -0
- package/dist/update-checker.js +91 -0
- package/package.json +1 -1
- package/src/commands/serve.ts +5 -0
- package/src/rpc-handler.ts +9 -2
- package/src/task.ts +0 -12
- package/src/update-checker.ts +90 -0
package/dist/commands/serve.js
CHANGED
|
@@ -7,6 +7,7 @@ import { startNatsTransport } from "../transports/nats-transport.js";
|
|
|
7
7
|
import { getTaskDir, readTaskStatus, writeTaskStatus, appendHistory, parseTaskFile } from "../task.js";
|
|
8
8
|
import { publishHostEvent } from "../events.js";
|
|
9
9
|
import { getPlatform } from "../platform/index.js";
|
|
10
|
+
import { checkForUpdate } from "../update-checker.js";
|
|
10
11
|
import { CONFIG_DIR } from "../config.js";
|
|
11
12
|
const POLL_INTERVAL_MS = 30_000;
|
|
12
13
|
const DAEMON_PID_FILE = path.join(CONFIG_DIR, "daemon.pid");
|
|
@@ -80,6 +81,9 @@ export async function serveCommand() {
|
|
|
80
81
|
console.error("[monitor] Error checking stale tasks:", err);
|
|
81
82
|
});
|
|
82
83
|
}, POLL_INTERVAL_MS);
|
|
84
|
+
// Check for updates on startup and every 24 hours
|
|
85
|
+
checkForUpdate().catch(() => { });
|
|
86
|
+
setInterval(() => { checkForUpdate().catch(() => { }); }, 24 * 60 * 60 * 1000);
|
|
83
87
|
const handleRpc = createRpcHandler(config, nc);
|
|
84
88
|
await startNatsTransport(config, handleRpc, nc);
|
|
85
89
|
}
|
package/dist/rpc-handler.js
CHANGED
|
@@ -3,12 +3,13 @@ import * as fs from "fs";
|
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { parse as parseYaml } from "yaml";
|
|
6
|
-
import { listTasks, parseTaskFile, writeTaskFile, getTaskDir,
|
|
6
|
+
import { listTasks, parseTaskFile, writeTaskFile, getTaskDir, readTaskStatus, writeTaskStatus, readHistory, deleteHistoryEntry, appendTaskList, removeFromTaskList } from "./task.js";
|
|
7
7
|
import { getPlatform } from "./platform/index.js";
|
|
8
8
|
import { spawnCommand } from "./spawn-command.js";
|
|
9
9
|
import { getAgent } from "./agents/agent.js";
|
|
10
10
|
import { validateSession } from "./session-store.js";
|
|
11
11
|
import { publishHostEvent } from "./events.js";
|
|
12
|
+
import { getUpdateAvailable, performUpdate } from "./update-checker.js";
|
|
12
13
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
14
|
const PLAN_GENERATION_PROMPT = fs.readFileSync(path.join(__dirname, "commands", "plan-generation.md"), "utf-8");
|
|
14
15
|
/**
|
|
@@ -92,7 +93,6 @@ export function createRpcHandler(config, nc) {
|
|
|
92
93
|
return {
|
|
93
94
|
...task.frontmatter,
|
|
94
95
|
body: task.body,
|
|
95
|
-
created_at: getTaskCreatedAt(taskDir),
|
|
96
96
|
status: readTaskStatus(taskDir),
|
|
97
97
|
};
|
|
98
98
|
}
|
|
@@ -107,6 +107,7 @@ export function createRpcHandler(config, nc) {
|
|
|
107
107
|
return {
|
|
108
108
|
tasks: tasks.map((task) => flattenTask(task)),
|
|
109
109
|
agents: config.agents ?? [],
|
|
110
|
+
update_available: getUpdateAvailable(),
|
|
110
111
|
};
|
|
111
112
|
}
|
|
112
113
|
case "task.create": {
|
|
@@ -332,6 +333,12 @@ export function createRpcHandler(config, nc) {
|
|
|
332
333
|
}
|
|
333
334
|
return { ok: true, task_id: params.task_id, result_file: params.result_file };
|
|
334
335
|
}
|
|
336
|
+
case "host.update": {
|
|
337
|
+
const error = await performUpdate();
|
|
338
|
+
if (error)
|
|
339
|
+
return { error };
|
|
340
|
+
return { ok: true };
|
|
341
|
+
}
|
|
335
342
|
default:
|
|
336
343
|
return { error: `Unknown method: ${request.method}` };
|
|
337
344
|
}
|
package/dist/task.d.ts
CHANGED
|
@@ -25,10 +25,6 @@ export declare function listTasks(projectRoot: string): ParsedTask[];
|
|
|
25
25
|
* Get the directory path for a task by its ID.
|
|
26
26
|
*/
|
|
27
27
|
export declare function getTaskDir(projectRoot: string, taskId: string): string;
|
|
28
|
-
/**
|
|
29
|
-
* Get the creation time (birthtime) of a TASK.md file in ms since epoch.
|
|
30
|
-
*/
|
|
31
|
-
export declare function getTaskCreatedAt(taskDir: string): number;
|
|
32
28
|
/**
|
|
33
29
|
* Write task status to status.json in the task directory.
|
|
34
30
|
*/
|
package/dist/task.js
CHANGED
|
@@ -109,18 +109,6 @@ export function listTasks(projectRoot) {
|
|
|
109
109
|
export function getTaskDir(projectRoot, taskId) {
|
|
110
110
|
return path.join(projectRoot, "tasks", taskId);
|
|
111
111
|
}
|
|
112
|
-
/**
|
|
113
|
-
* Get the creation time (birthtime) of a TASK.md file in ms since epoch.
|
|
114
|
-
*/
|
|
115
|
-
export function getTaskCreatedAt(taskDir) {
|
|
116
|
-
const filePath = path.join(taskDir, "TASK.md");
|
|
117
|
-
try {
|
|
118
|
-
return fs.statSync(filePath).birthtimeMs;
|
|
119
|
-
}
|
|
120
|
-
catch {
|
|
121
|
-
return 0;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
112
|
/**
|
|
125
113
|
* Write task status to status.json in the task directory.
|
|
126
114
|
*/
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check the npm registry for a newer version of palmier.
|
|
3
|
+
*/
|
|
4
|
+
export declare function checkForUpdate(): Promise<void>;
|
|
5
|
+
/**
|
|
6
|
+
* Get the available update version, or null if up to date.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getUpdateAvailable(): string | null;
|
|
9
|
+
/**
|
|
10
|
+
* Run the update and restart the daemon.
|
|
11
|
+
* Returns an error message if the update fails.
|
|
12
|
+
*/
|
|
13
|
+
export declare function performUpdate(): Promise<string | null>;
|
|
14
|
+
//# sourceMappingURL=update-checker.d.ts.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { spawnCommand } from "./spawn-command.js";
|
|
5
|
+
import { getPlatform } from "./platform/index.js";
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8"));
|
|
8
|
+
const currentVersion = pkg.version;
|
|
9
|
+
let latestVersion = null;
|
|
10
|
+
let lastCheckTime = 0;
|
|
11
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
12
|
+
/**
|
|
13
|
+
* Compare two semver strings (major.minor.patch).
|
|
14
|
+
* Returns true if b is newer than a.
|
|
15
|
+
*/
|
|
16
|
+
function isNewer(a, b) {
|
|
17
|
+
const pa = a.split(".").map(Number);
|
|
18
|
+
const pb = b.split(".").map(Number);
|
|
19
|
+
for (let i = 0; i < 3; i++) {
|
|
20
|
+
if ((pb[i] ?? 0) > (pa[i] ?? 0))
|
|
21
|
+
return true;
|
|
22
|
+
if ((pb[i] ?? 0) < (pa[i] ?? 0))
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check the npm registry for a newer version of palmier.
|
|
29
|
+
*/
|
|
30
|
+
export async function checkForUpdate() {
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
if (now - lastCheckTime < CHECK_INTERVAL_MS)
|
|
33
|
+
return;
|
|
34
|
+
lastCheckTime = now;
|
|
35
|
+
try {
|
|
36
|
+
const res = await fetch("https://registry.npmjs.org/palmier/latest", {
|
|
37
|
+
signal: AbortSignal.timeout(10_000),
|
|
38
|
+
});
|
|
39
|
+
if (!res.ok)
|
|
40
|
+
return;
|
|
41
|
+
const data = (await res.json());
|
|
42
|
+
if (data.version && isNewer(currentVersion, data.version)) {
|
|
43
|
+
latestVersion = data.version;
|
|
44
|
+
console.log(`[update] New version available: ${data.version} (current: ${currentVersion})`);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
latestVersion = null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Network errors are expected (offline, etc.)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get the available update version, or null if up to date.
|
|
56
|
+
*/
|
|
57
|
+
export function getUpdateAvailable() {
|
|
58
|
+
return latestVersion;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Run the update and restart the daemon.
|
|
62
|
+
* Returns an error message if the update fails.
|
|
63
|
+
*/
|
|
64
|
+
export async function performUpdate() {
|
|
65
|
+
try {
|
|
66
|
+
const { output, exitCode } = await spawnCommand("npm", ["update", "-g", "palmier"], {
|
|
67
|
+
cwd: process.cwd(),
|
|
68
|
+
timeout: 120_000,
|
|
69
|
+
resolveOnFailure: true,
|
|
70
|
+
});
|
|
71
|
+
if (exitCode !== 0) {
|
|
72
|
+
console.error(`[update] npm update failed (exit ${exitCode}):`, output);
|
|
73
|
+
return `Update failed. Please run manually:\nnpm update -g palmier`;
|
|
74
|
+
}
|
|
75
|
+
console.log("[update] Update installed, restarting daemon...");
|
|
76
|
+
latestVersion = null;
|
|
77
|
+
// Small delay to allow the RPC response to be sent
|
|
78
|
+
setTimeout(() => {
|
|
79
|
+
getPlatform().restartDaemon().catch((err) => {
|
|
80
|
+
console.error("[update] Restart failed:", err);
|
|
81
|
+
});
|
|
82
|
+
}, 1000);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
87
|
+
console.error("[update] Update failed:", msg);
|
|
88
|
+
return `Update failed. Please run manually:\nnpm update -g palmier`;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=update-checker.js.map
|
package/package.json
CHANGED
package/src/commands/serve.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { startNatsTransport } from "../transports/nats-transport.js";
|
|
|
7
7
|
import { getTaskDir, readTaskStatus, writeTaskStatus, appendHistory, parseTaskFile } from "../task.js";
|
|
8
8
|
import { publishHostEvent } from "../events.js";
|
|
9
9
|
import { getPlatform } from "../platform/index.js";
|
|
10
|
+
import { checkForUpdate } from "../update-checker.js";
|
|
10
11
|
import type { HostConfig } from "../types.js";
|
|
11
12
|
import { CONFIG_DIR } from "../config.js";
|
|
12
13
|
import type { NatsConnection } from "nats";
|
|
@@ -100,6 +101,10 @@ export async function serveCommand(): Promise<void> {
|
|
|
100
101
|
});
|
|
101
102
|
}, POLL_INTERVAL_MS);
|
|
102
103
|
|
|
104
|
+
// Check for updates on startup and every 24 hours
|
|
105
|
+
checkForUpdate().catch(() => {});
|
|
106
|
+
setInterval(() => { checkForUpdate().catch(() => {}); }, 24 * 60 * 60 * 1000);
|
|
107
|
+
|
|
103
108
|
const handleRpc = createRpcHandler(config, nc);
|
|
104
109
|
await startNatsTransport(config, handleRpc, nc);
|
|
105
110
|
}
|
package/src/rpc-handler.ts
CHANGED
|
@@ -4,12 +4,13 @@ import * as path from "path";
|
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { parse as parseYaml } from "yaml";
|
|
6
6
|
import { type NatsConnection } from "nats";
|
|
7
|
-
import { listTasks, parseTaskFile, writeTaskFile, getTaskDir,
|
|
7
|
+
import { listTasks, parseTaskFile, writeTaskFile, getTaskDir, readTaskStatus, writeTaskStatus, readHistory, deleteHistoryEntry, appendTaskList, removeFromTaskList } from "./task.js";
|
|
8
8
|
import { getPlatform } from "./platform/index.js";
|
|
9
9
|
import { spawnCommand } from "./spawn-command.js";
|
|
10
10
|
import { getAgent } from "./agents/agent.js";
|
|
11
11
|
import { validateSession } from "./session-store.js";
|
|
12
12
|
import { publishHostEvent } from "./events.js";
|
|
13
|
+
import { getUpdateAvailable, performUpdate } from "./update-checker.js";
|
|
13
14
|
import type { HostConfig, ParsedTask, RpcMessage } from "./types.js";
|
|
14
15
|
|
|
15
16
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -105,7 +106,6 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
|
|
|
105
106
|
return {
|
|
106
107
|
...task.frontmatter,
|
|
107
108
|
body: task.body,
|
|
108
|
-
created_at: getTaskCreatedAt(taskDir),
|
|
109
109
|
status: readTaskStatus(taskDir),
|
|
110
110
|
};
|
|
111
111
|
}
|
|
@@ -122,6 +122,7 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
|
|
|
122
122
|
return {
|
|
123
123
|
tasks: tasks.map((task) => flattenTask(task)),
|
|
124
124
|
agents: config.agents ?? [],
|
|
125
|
+
update_available: getUpdateAvailable(),
|
|
125
126
|
};
|
|
126
127
|
}
|
|
127
128
|
|
|
@@ -379,6 +380,12 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
|
|
|
379
380
|
return { ok: true, task_id: params.task_id, result_file: params.result_file };
|
|
380
381
|
}
|
|
381
382
|
|
|
383
|
+
case "host.update": {
|
|
384
|
+
const error = await performUpdate();
|
|
385
|
+
if (error) return { error };
|
|
386
|
+
return { ok: true };
|
|
387
|
+
}
|
|
388
|
+
|
|
382
389
|
default:
|
|
383
390
|
return { error: `Unknown method: ${request.method}` };
|
|
384
391
|
}
|
package/src/task.ts
CHANGED
|
@@ -126,18 +126,6 @@ export function getTaskDir(projectRoot: string, taskId: string): string {
|
|
|
126
126
|
return path.join(projectRoot, "tasks", taskId);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
/**
|
|
130
|
-
* Get the creation time (birthtime) of a TASK.md file in ms since epoch.
|
|
131
|
-
*/
|
|
132
|
-
export function getTaskCreatedAt(taskDir: string): number {
|
|
133
|
-
const filePath = path.join(taskDir, "TASK.md");
|
|
134
|
-
try {
|
|
135
|
-
return fs.statSync(filePath).birthtimeMs;
|
|
136
|
-
} catch {
|
|
137
|
-
return 0;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
129
|
/**
|
|
142
130
|
* Write task status to status.json in the task directory.
|
|
143
131
|
*/
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { spawnCommand } from "./spawn-command.js";
|
|
5
|
+
import { getPlatform } from "./platform/index.js";
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8")) as { version: string };
|
|
9
|
+
const currentVersion = pkg.version;
|
|
10
|
+
|
|
11
|
+
let latestVersion: string | null = null;
|
|
12
|
+
let lastCheckTime = 0;
|
|
13
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Compare two semver strings (major.minor.patch).
|
|
17
|
+
* Returns true if b is newer than a.
|
|
18
|
+
*/
|
|
19
|
+
function isNewer(a: string, b: string): boolean {
|
|
20
|
+
const pa = a.split(".").map(Number);
|
|
21
|
+
const pb = b.split(".").map(Number);
|
|
22
|
+
for (let i = 0; i < 3; i++) {
|
|
23
|
+
if ((pb[i] ?? 0) > (pa[i] ?? 0)) return true;
|
|
24
|
+
if ((pb[i] ?? 0) < (pa[i] ?? 0)) return false;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Check the npm registry for a newer version of palmier.
|
|
31
|
+
*/
|
|
32
|
+
export async function checkForUpdate(): Promise<void> {
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
if (now - lastCheckTime < CHECK_INTERVAL_MS) return;
|
|
35
|
+
lastCheckTime = now;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const res = await fetch("https://registry.npmjs.org/palmier/latest", {
|
|
39
|
+
signal: AbortSignal.timeout(10_000),
|
|
40
|
+
});
|
|
41
|
+
if (!res.ok) return;
|
|
42
|
+
const data = (await res.json()) as { version?: string };
|
|
43
|
+
if (data.version && isNewer(currentVersion, data.version)) {
|
|
44
|
+
latestVersion = data.version;
|
|
45
|
+
console.log(`[update] New version available: ${data.version} (current: ${currentVersion})`);
|
|
46
|
+
} else {
|
|
47
|
+
latestVersion = null;
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
// Network errors are expected (offline, etc.)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get the available update version, or null if up to date.
|
|
56
|
+
*/
|
|
57
|
+
export function getUpdateAvailable(): string | null {
|
|
58
|
+
return latestVersion;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Run the update and restart the daemon.
|
|
63
|
+
* Returns an error message if the update fails.
|
|
64
|
+
*/
|
|
65
|
+
export async function performUpdate(): Promise<string | null> {
|
|
66
|
+
try {
|
|
67
|
+
const { output, exitCode } = await spawnCommand("npm", ["update", "-g", "palmier"], {
|
|
68
|
+
cwd: process.cwd(),
|
|
69
|
+
timeout: 120_000,
|
|
70
|
+
resolveOnFailure: true,
|
|
71
|
+
});
|
|
72
|
+
if (exitCode !== 0) {
|
|
73
|
+
console.error(`[update] npm update failed (exit ${exitCode}):`, output);
|
|
74
|
+
return `Update failed. Please run manually:\nnpm update -g palmier`;
|
|
75
|
+
}
|
|
76
|
+
console.log("[update] Update installed, restarting daemon...");
|
|
77
|
+
latestVersion = null;
|
|
78
|
+
// Small delay to allow the RPC response to be sent
|
|
79
|
+
setTimeout(() => {
|
|
80
|
+
getPlatform().restartDaemon().catch((err) => {
|
|
81
|
+
console.error("[update] Restart failed:", err);
|
|
82
|
+
});
|
|
83
|
+
}, 1000);
|
|
84
|
+
return null;
|
|
85
|
+
} catch (err) {
|
|
86
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
87
|
+
console.error("[update] Update failed:", msg);
|
|
88
|
+
return `Update failed. Please run manually:\nnpm update -g palmier`;
|
|
89
|
+
}
|
|
90
|
+
}
|