palmier 0.3.8 → 0.4.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 +1 -1
- package/dist/agents/agent.js +1 -1
- package/dist/platform/linux.js +22 -15
- package/dist/platform/windows.js +11 -5
- package/package.json +1 -1
- package/src/agents/agent.ts +1 -1
- package/src/platform/linux.ts +27 -17
- package/src/platform/windows.ts +12 -6
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**Website:** [palmier.me](https://www.palmier.me) | **App:** [app.palmier.me](https://app.palmier.me)
|
|
8
8
|
|
|
9
|
-
A Node.js CLI that runs on your machine as a persistent daemon
|
|
9
|
+
A Node.js CLI that lets you run your own AI agents from your phone. It runs on your machine as a persistent daemon, letting you create, schedule, and monitor agent tasks from any device via a cloud relay (NATS) and/or direct HTTP.
|
|
10
10
|
|
|
11
11
|
> **Important:** By using Palmier, you agree to the [Terms of Service](https://www.palmier.me/terms) and [Privacy Policy](https://www.palmier.me/privacy). See the [Disclaimer](#disclaimer) section below.
|
|
12
12
|
|
package/dist/agents/agent.js
CHANGED
package/dist/platform/linux.js
CHANGED
|
@@ -5,6 +5,7 @@ import { execSync, exec } from "child_process";
|
|
|
5
5
|
import { promisify } from "util";
|
|
6
6
|
const execAsync = promisify(exec);
|
|
7
7
|
const UNIT_DIR = path.join(homedir(), ".config", "systemd", "user");
|
|
8
|
+
const PATH_FILE = path.join(homedir(), ".config", "palmier", "user-path");
|
|
8
9
|
function getTimerName(taskId) {
|
|
9
10
|
return `palmier-task-${taskId}.timer`;
|
|
10
11
|
}
|
|
@@ -56,6 +57,11 @@ export class LinuxPlatform {
|
|
|
56
57
|
installDaemon(config) {
|
|
57
58
|
fs.mkdirSync(UNIT_DIR, { recursive: true });
|
|
58
59
|
const palmierBin = process.argv[1] || "palmier";
|
|
60
|
+
// Save the user's shell PATH so restartDaemon can use it later
|
|
61
|
+
// (the daemon itself runs under systemd with a limited PATH).
|
|
62
|
+
const userPath = process.env.PATH || "/usr/local/bin:/usr/bin:/bin";
|
|
63
|
+
fs.mkdirSync(path.dirname(PATH_FILE), { recursive: true });
|
|
64
|
+
fs.writeFileSync(PATH_FILE, userPath, "utf-8");
|
|
59
65
|
const serviceContent = `[Unit]
|
|
60
66
|
Description=Palmier Host
|
|
61
67
|
After=network-online.target
|
|
@@ -67,7 +73,7 @@ ExecStart=${palmierBin} serve
|
|
|
67
73
|
WorkingDirectory=${config.projectRoot}
|
|
68
74
|
Restart=on-failure
|
|
69
75
|
RestartSec=5
|
|
70
|
-
Environment=PATH=${
|
|
76
|
+
Environment=PATH=${userPath}
|
|
71
77
|
|
|
72
78
|
[Install]
|
|
73
79
|
WantedBy=default.target
|
|
@@ -96,21 +102,22 @@ WantedBy=default.target
|
|
|
96
102
|
console.log("\nHost initialization complete!");
|
|
97
103
|
}
|
|
98
104
|
async restartDaemon() {
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
105
|
+
// If called from a user's terminal, save the current PATH for future use.
|
|
106
|
+
// If called from the daemon (auto-update), read the saved PATH instead.
|
|
107
|
+
if (process.stdin.isTTY) {
|
|
108
|
+
fs.mkdirSync(path.dirname(PATH_FILE), { recursive: true });
|
|
109
|
+
fs.writeFileSync(PATH_FILE, process.env.PATH || "", "utf-8");
|
|
110
|
+
}
|
|
102
111
|
const servicePath = path.join(UNIT_DIR, "palmier.service");
|
|
103
|
-
if (fs.existsSync(servicePath)) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
fs.writeFileSync(servicePath, updated, "utf-8");
|
|
113
|
-
execSync("systemctl --user daemon-reload", { encoding: "utf-8" });
|
|
112
|
+
if (fs.existsSync(servicePath) && fs.existsSync(PATH_FILE)) {
|
|
113
|
+
const userPath = fs.readFileSync(PATH_FILE, "utf-8").trim();
|
|
114
|
+
if (userPath) {
|
|
115
|
+
const content = fs.readFileSync(servicePath, "utf-8");
|
|
116
|
+
const updated = content.replace(/^Environment=PATH=.*/m, `Environment=PATH=${userPath}`);
|
|
117
|
+
if (updated !== content) {
|
|
118
|
+
fs.writeFileSync(servicePath, updated, "utf-8");
|
|
119
|
+
execSync("systemctl --user daemon-reload", { encoding: "utf-8" });
|
|
120
|
+
}
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
123
|
execSync("systemctl --user restart palmier.service", { stdio: "inherit" });
|
package/dist/platform/windows.js
CHANGED
|
@@ -106,9 +106,17 @@ export class WindowsPlatform {
|
|
|
106
106
|
console.log("\nHost initialization complete!");
|
|
107
107
|
}
|
|
108
108
|
async restartDaemon() {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
const script = process.argv[1] || "palmier";
|
|
110
|
+
const oldPid = fs.existsSync(DAEMON_PID_FILE)
|
|
111
|
+
? fs.readFileSync(DAEMON_PID_FILE, "utf-8").trim()
|
|
112
|
+
: null;
|
|
113
|
+
// Spawn the new daemon before killing the old one.
|
|
114
|
+
this.spawnDaemon(script);
|
|
115
|
+
if (oldPid && oldPid === String(process.pid)) {
|
|
116
|
+
// We ARE the old daemon (auto-update) — exit so only the new one runs.
|
|
117
|
+
process.exit(0);
|
|
118
|
+
}
|
|
119
|
+
else if (oldPid) {
|
|
112
120
|
try {
|
|
113
121
|
execFileSync("taskkill", ["/pid", oldPid, "/t", "/f"], { windowsHide: true });
|
|
114
122
|
}
|
|
@@ -116,8 +124,6 @@ export class WindowsPlatform {
|
|
|
116
124
|
// Process may have already exited
|
|
117
125
|
}
|
|
118
126
|
}
|
|
119
|
-
const script = process.argv[1] || "palmier";
|
|
120
|
-
this.spawnDaemon(script);
|
|
121
127
|
}
|
|
122
128
|
spawnDaemon(script) {
|
|
123
129
|
const child = nodeSpawn(process.execPath, [script, "serve"], {
|
package/package.json
CHANGED
package/src/agents/agent.ts
CHANGED
package/src/platform/linux.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type { HostConfig, ParsedTask } from "../types.js";
|
|
|
9
9
|
const execAsync = promisify(exec);
|
|
10
10
|
|
|
11
11
|
const UNIT_DIR = path.join(homedir(), ".config", "systemd", "user");
|
|
12
|
+
const PATH_FILE = path.join(homedir(), ".config", "palmier", "user-path");
|
|
12
13
|
|
|
13
14
|
function getTimerName(taskId: string): string {
|
|
14
15
|
return `palmier-task-${taskId}.timer`;
|
|
@@ -70,6 +71,12 @@ export class LinuxPlatform implements PlatformService {
|
|
|
70
71
|
fs.mkdirSync(UNIT_DIR, { recursive: true });
|
|
71
72
|
|
|
72
73
|
const palmierBin = process.argv[1] || "palmier";
|
|
74
|
+
// Save the user's shell PATH so restartDaemon can use it later
|
|
75
|
+
// (the daemon itself runs under systemd with a limited PATH).
|
|
76
|
+
const userPath = process.env.PATH || "/usr/local/bin:/usr/bin:/bin";
|
|
77
|
+
fs.mkdirSync(path.dirname(PATH_FILE), { recursive: true });
|
|
78
|
+
fs.writeFileSync(PATH_FILE, userPath, "utf-8");
|
|
79
|
+
|
|
73
80
|
const serviceContent = `[Unit]
|
|
74
81
|
Description=Palmier Host
|
|
75
82
|
After=network-online.target
|
|
@@ -81,7 +88,7 @@ ExecStart=${palmierBin} serve
|
|
|
81
88
|
WorkingDirectory=${config.projectRoot}
|
|
82
89
|
Restart=on-failure
|
|
83
90
|
RestartSec=5
|
|
84
|
-
Environment=PATH=${
|
|
91
|
+
Environment=PATH=${userPath}
|
|
85
92
|
|
|
86
93
|
[Install]
|
|
87
94
|
WantedBy=default.target
|
|
@@ -113,23 +120,26 @@ WantedBy=default.target
|
|
|
113
120
|
}
|
|
114
121
|
|
|
115
122
|
async restartDaemon(): Promise<void> {
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
|
|
123
|
+
// If called from a user's terminal, save the current PATH for future use.
|
|
124
|
+
// If called from the daemon (auto-update), read the saved PATH instead.
|
|
125
|
+
if (process.stdin.isTTY) {
|
|
126
|
+
fs.mkdirSync(path.dirname(PATH_FILE), { recursive: true });
|
|
127
|
+
fs.writeFileSync(PATH_FILE, process.env.PATH || "", "utf-8");
|
|
128
|
+
}
|
|
129
|
+
|
|
119
130
|
const servicePath = path.join(UNIT_DIR, "palmier.service");
|
|
120
|
-
if (fs.existsSync(servicePath)) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
execSync("systemctl --user daemon-reload", { encoding: "utf-8" });
|
|
131
|
+
if (fs.existsSync(servicePath) && fs.existsSync(PATH_FILE)) {
|
|
132
|
+
const userPath = fs.readFileSync(PATH_FILE, "utf-8").trim();
|
|
133
|
+
if (userPath) {
|
|
134
|
+
const content = fs.readFileSync(servicePath, "utf-8");
|
|
135
|
+
const updated = content.replace(
|
|
136
|
+
/^Environment=PATH=.*/m,
|
|
137
|
+
`Environment=PATH=${userPath}`,
|
|
138
|
+
);
|
|
139
|
+
if (updated !== content) {
|
|
140
|
+
fs.writeFileSync(servicePath, updated, "utf-8");
|
|
141
|
+
execSync("systemctl --user daemon-reload", { encoding: "utf-8" });
|
|
142
|
+
}
|
|
133
143
|
}
|
|
134
144
|
}
|
|
135
145
|
execSync("systemctl --user restart palmier.service", { stdio: "inherit" });
|
package/src/platform/windows.ts
CHANGED
|
@@ -126,18 +126,24 @@ export class WindowsPlatform implements PlatformService {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
async restartDaemon(): Promise<void> {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
const script = process.argv[1] || "palmier";
|
|
130
|
+
const oldPid = fs.existsSync(DAEMON_PID_FILE)
|
|
131
|
+
? fs.readFileSync(DAEMON_PID_FILE, "utf-8").trim()
|
|
132
|
+
: null;
|
|
133
|
+
|
|
134
|
+
// Spawn the new daemon before killing the old one.
|
|
135
|
+
this.spawnDaemon(script);
|
|
136
|
+
|
|
137
|
+
if (oldPid && oldPid === String(process.pid)) {
|
|
138
|
+
// We ARE the old daemon (auto-update) — exit so only the new one runs.
|
|
139
|
+
process.exit(0);
|
|
140
|
+
} else if (oldPid) {
|
|
132
141
|
try {
|
|
133
142
|
execFileSync("taskkill", ["/pid", oldPid, "/t", "/f"], { windowsHide: true });
|
|
134
143
|
} catch {
|
|
135
144
|
// Process may have already exited
|
|
136
145
|
}
|
|
137
146
|
}
|
|
138
|
-
|
|
139
|
-
const script = process.argv[1] || "palmier";
|
|
140
|
-
this.spawnDaemon(script);
|
|
141
147
|
}
|
|
142
148
|
|
|
143
149
|
private spawnDaemon(script: string): void {
|