palmier 0.3.2 → 0.3.3

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 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. It manages tasks, communicates with the Palmier app via NATS and/or direct HTTP, and executes tasks on schedule or demand using CLI tools.
9
+ A Node.js CLI that runs on your machine as a persistent daemon. It lets you create, schedule, and run AI agent tasks from your phone or browser, communicating 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
 
@@ -16,10 +16,10 @@ The host supports two independent connection modes, enabled during `palmier init
16
16
 
17
17
  | Mode | Transport | PWA URL | Features |
18
18
  |------|-----------|---------|----------|
19
- | **Server** | NATS (cloud relay) | `https://app.palmier.me` | Push notifications, remote access |
19
+ | **Server** | Cloud relay (NATS) | `https://app.palmier.me` | Push notifications, remote access |
20
20
  | **LAN** | HTTP (direct, on-demand) | `http://<host-ip>:7400` | Low-latency, no external server needed |
21
21
 
22
- **Server mode** relays communication through the Palmier server via NATS. All features including push notifications are available. The PWA is served over HTTPS.
22
+ **Server mode** relays communication through the Palmier cloud server (via [NATS](https://nats.io), a lightweight messaging system). All features including push notifications are available. The PWA is served over HTTPS.
23
23
 
24
24
  **LAN mode** is started on-demand via `palmier lan`. It runs a local HTTP server that reverse-proxies PWA assets from `app.palmier.me` and serves API endpoints locally. The browser accesses everything at `http://<host-ip>:<port>` (same-origin). Push notifications are not available in LAN mode.
25
25
 
@@ -35,6 +35,8 @@ The host supports two independent connection modes, enabled during `palmier init
35
35
  npm install -g palmier
36
36
  ```
37
37
 
38
+ All `palmier` commands should be run from a dedicated Palmier root directory (e.g., `~/palmier`). This is where tasks, configuration, and execution data are stored.
39
+
38
40
  ## CLI Commands
39
41
 
40
42
  | Command | Description |
@@ -57,7 +59,7 @@ npm install -g palmier
57
59
  ### Quick Start
58
60
 
59
61
  1. Install the host: `npm install -g palmier`
60
- 2. Run `palmier init` in your project directory.
62
+ 2. Run `palmier init` in your Palmier root directory (e.g., `~/palmier`).
61
63
  3. The wizard detects installed agents, registers with the Palmier server, installs a background daemon, and generates a pairing code.
62
64
  4. Enter the pairing code in the Palmier PWA to connect your device.
63
65
 
@@ -153,7 +155,6 @@ src/
153
155
  spawn-command.ts # Shared helper for spawning CLI tools
154
156
  task.ts # Task file management
155
157
  types.ts # Shared type definitions
156
- pairing.ts # OTP code generation and expiry constant
157
158
  lan-lock.ts # LAN lockfile path and port reader
158
159
  events.ts # Event broadcasting (NATS pub/sub or HTTP SSE)
159
160
  agents/
@@ -210,11 +211,11 @@ Requires a provisioned host (`palmier init`) with server mode enabled.
210
211
  |---|---|---|
211
212
  | `send-push-notification` | `title`, `body` (required) | Send a push notification to all paired devices |
212
213
 
213
- ## Removing a Host
214
+ ## Uninstalling
214
215
 
215
- To fully remove a host from a machine:
216
+ To fully remove Palmier from a machine:
216
217
 
217
- 1. **Unpair the host from the PWA** (via the host menu).
218
+ 1. **Unpair your device** in the PWA (via the host menu).
218
219
 
219
220
  2. **Stop and remove the daemon:**
220
221
 
@@ -248,16 +249,11 @@ To fully remove a host from a machine:
248
249
  schtasks /delete /tn "PalmierTask-*" /f 2>$null
249
250
  ```
250
251
 
251
- 4. **Remove the host configuration:**
252
+ 4. **Remove configuration and task data:**
252
253
 
253
254
  ```bash
254
255
  rm -rf ~/.config/palmier
255
- ```
256
-
257
- 5. **Remove the tasks directory** from your project root:
258
-
259
- ```bash
260
- rm -rf tasks/
256
+ rm -rf tasks/ # from your Palmier root directory
261
257
  ```
262
258
 
263
259
  ## Disclaimer
@@ -2,7 +2,7 @@ import * as fs from "fs";
2
2
  import { loadConfig, CONFIG_DIR } from "../config.js";
3
3
  import { createRpcHandler } from "../rpc-handler.js";
4
4
  import { startHttpTransport, detectLanIp } from "../transports/http-transport.js";
5
- import { generatePairingCode } from "../pairing.js";
5
+ import { generatePairingCode } from "./pair.js";
6
6
  import { LAN_LOCKFILE } from "../lan-lock.js";
7
7
  const bold = (s) => `\x1b[1m${s}\x1b[0m`;
8
8
  const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
@@ -1,3 +1,5 @@
1
+ export declare const PAIRING_EXPIRY_MS: number;
2
+ export declare function generatePairingCode(): string;
1
3
  /**
2
4
  * Generate an OTP code and wait for a PWA client to pair.
3
5
  * Listens on NATS always, and also on the LAN server if `palmier lan` is running.
@@ -3,8 +3,15 @@ import { StringCodec } from "nats";
3
3
  import { loadConfig } from "../config.js";
4
4
  import { connectNats } from "../nats-client.js";
5
5
  import { addSession } from "../session-store.js";
6
- import { generatePairingCode, PAIRING_EXPIRY_MS } from "../pairing.js";
7
6
  import { getLanPort } from "../lan-lock.js";
7
+ const CODE_CHARS = "ABCDEFGHJKMNPQRSTUVWXYZ23456789"; // no O/0/I/1/L
8
+ const CODE_LENGTH = 6;
9
+ export const PAIRING_EXPIRY_MS = 5 * 60 * 1000; // 5 minutes
10
+ export function generatePairingCode() {
11
+ const bytes = new Uint8Array(CODE_LENGTH);
12
+ crypto.getRandomValues(bytes);
13
+ return Array.from(bytes, (b) => CODE_CHARS[b % CODE_CHARS.length]).join("");
14
+ }
8
15
  function buildPairResponse(config, label) {
9
16
  const session = addSession(label);
10
17
  return {
@@ -63,8 +63,9 @@ function schtasksTaskName(taskId) {
63
63
  export class WindowsPlatform {
64
64
  installDaemon(config) {
65
65
  const script = process.argv[1] || "palmier";
66
- // Write a VBS launcher that starts the daemon with no visible console window
67
- const vbs = `CreateObject("WScript.Shell").Run """${process.execPath.replace(/\\/g, "\\\\")}"" ""${script.replace(/\\/g, "\\\\")}"" serve", 0, False`;
66
+ // Write a VBS launcher that starts the daemon with no visible console window.
67
+ // VBS doesn't use backslash escaping — only quotes need doubling ("").
68
+ const vbs = `CreateObject("WScript.Shell").Run """${process.execPath}"" ""${script}"" serve", 0, False`;
68
69
  fs.writeFileSync(DAEMON_VBS_FILE, vbs, "utf-8");
69
70
  const regValue = `"${process.env.SYSTEMROOT || "C:\\Windows"}\\System32\\wscript.exe" "${DAEMON_VBS_FILE}"`;
70
71
  try {
@@ -1,3 +1,5 @@
1
+ /** True when running from a source checkout (has .git) rather than a global npm install. */
2
+ export declare const isDevBuild: boolean;
1
3
  export declare const currentVersion: string;
2
4
  /**
3
5
  * Check the npm registry for the latest version of palmier.
@@ -4,8 +4,11 @@ import { fileURLToPath } from "url";
4
4
  import { spawnCommand } from "./spawn-command.js";
5
5
  import { getPlatform } from "./platform/index.js";
6
6
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
- const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8"));
8
- export const currentVersion = pkg.version;
7
+ const packageRoot = path.join(__dirname, "..");
8
+ const pkg = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf-8"));
9
+ /** True when running from a source checkout (has .git) rather than a global npm install. */
10
+ export const isDevBuild = fs.existsSync(path.join(packageRoot, ".git"));
11
+ export const currentVersion = isDevBuild ? `${pkg.version}-dev` : pkg.version;
9
12
  let latestVersion = null;
10
13
  let lastCheckTime = 0;
11
14
  const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
@@ -13,6 +16,8 @@ const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
13
16
  * Check the npm registry for the latest version of palmier.
14
17
  */
15
18
  export async function checkForUpdate() {
19
+ if (isDevBuild)
20
+ return;
16
21
  const now = Date.now();
17
22
  if (now - lastCheckTime < CHECK_INTERVAL_MS)
18
23
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "palmier",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "Palmier host CLI - provisions, executes tasks, and serves NATS RPC",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Hongxu Cai",
@@ -2,7 +2,7 @@ import * as fs from "fs";
2
2
  import { loadConfig, CONFIG_DIR } from "../config.js";
3
3
  import { createRpcHandler } from "../rpc-handler.js";
4
4
  import { startHttpTransport, detectLanIp } from "../transports/http-transport.js";
5
- import { generatePairingCode } from "../pairing.js";
5
+ import { generatePairingCode } from "./pair.js";
6
6
  import { LAN_LOCKFILE } from "../lan-lock.js";
7
7
 
8
8
  const bold = (s: string) => `\x1b[1m${s}\x1b[0m`;
@@ -3,10 +3,20 @@ import { StringCodec } from "nats";
3
3
  import { loadConfig } from "../config.js";
4
4
  import { connectNats } from "../nats-client.js";
5
5
  import { addSession } from "../session-store.js";
6
- import { generatePairingCode, PAIRING_EXPIRY_MS } from "../pairing.js";
7
6
  import { getLanPort } from "../lan-lock.js";
8
7
  import type { HostConfig } from "../types.js";
9
8
 
9
+ const CODE_CHARS = "ABCDEFGHJKMNPQRSTUVWXYZ23456789"; // no O/0/I/1/L
10
+ const CODE_LENGTH = 6;
11
+
12
+ export const PAIRING_EXPIRY_MS = 5 * 60 * 1000; // 5 minutes
13
+
14
+ export function generatePairingCode(): string {
15
+ const bytes = new Uint8Array(CODE_LENGTH);
16
+ crypto.getRandomValues(bytes);
17
+ return Array.from(bytes, (b) => CODE_CHARS[b % CODE_CHARS.length]).join("");
18
+ }
19
+
10
20
  function buildPairResponse(config: HostConfig, label?: string) {
11
21
  const session = addSession(label);
12
22
  return {
@@ -78,8 +78,9 @@ export class WindowsPlatform implements PlatformService {
78
78
  installDaemon(config: HostConfig): void {
79
79
  const script = process.argv[1] || "palmier";
80
80
 
81
- // Write a VBS launcher that starts the daemon with no visible console window
82
- const vbs = `CreateObject("WScript.Shell").Run """${process.execPath.replace(/\\/g, "\\\\")}"" ""${script.replace(/\\/g, "\\\\")}"" serve", 0, False`;
81
+ // Write a VBS launcher that starts the daemon with no visible console window.
82
+ // VBS doesn't use backslash escaping — only quotes need doubling ("").
83
+ const vbs = `CreateObject("WScript.Shell").Run """${process.execPath}"" ""${script}"" serve", 0, False`;
83
84
  fs.writeFileSync(DAEMON_VBS_FILE, vbs, "utf-8");
84
85
 
85
86
  const regValue = `"${process.env.SYSTEMROOT || "C:\\Windows"}\\System32\\wscript.exe" "${DAEMON_VBS_FILE}"`;
@@ -5,8 +5,12 @@ import { spawnCommand } from "./spawn-command.js";
5
5
  import { getPlatform } from "./platform/index.js";
6
6
 
7
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
- export const currentVersion = pkg.version;
8
+ const packageRoot = path.join(__dirname, "..");
9
+ const pkg = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf-8")) as { version: string };
10
+
11
+ /** True when running from a source checkout (has .git) rather than a global npm install. */
12
+ export const isDevBuild = fs.existsSync(path.join(packageRoot, ".git"));
13
+ export const currentVersion = isDevBuild ? `${pkg.version}-dev` : pkg.version;
10
14
 
11
15
  let latestVersion: string | null = null;
12
16
  let lastCheckTime = 0;
@@ -16,6 +20,7 @@ const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
16
20
  * Check the npm registry for the latest version of palmier.
17
21
  */
18
22
  export async function checkForUpdate(): Promise<void> {
23
+ if (isDevBuild) return;
19
24
  const now = Date.now();
20
25
  if (now - lastCheckTime < CHECK_INTERVAL_MS) return;
21
26
  lastCheckTime = now;
package/dist/pairing.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export declare const PAIRING_EXPIRY_MS: number;
2
- export declare function generatePairingCode(): string;
3
- //# sourceMappingURL=pairing.d.ts.map
package/dist/pairing.js DELETED
@@ -1,9 +0,0 @@
1
- const CODE_CHARS = "ABCDEFGHJKMNPQRSTUVWXYZ23456789"; // no O/0/I/1/L
2
- const CODE_LENGTH = 6;
3
- export const PAIRING_EXPIRY_MS = 5 * 60 * 1000; // 5 minutes
4
- export function generatePairingCode() {
5
- const bytes = new Uint8Array(CODE_LENGTH);
6
- crypto.getRandomValues(bytes);
7
- return Array.from(bytes, (b) => CODE_CHARS[b % CODE_CHARS.length]).join("");
8
- }
9
- //# sourceMappingURL=pairing.js.map
package/src/pairing.ts DELETED
@@ -1,10 +0,0 @@
1
- const CODE_CHARS = "ABCDEFGHJKMNPQRSTUVWXYZ23456789"; // no O/0/I/1/L
2
- const CODE_LENGTH = 6;
3
-
4
- export const PAIRING_EXPIRY_MS = 5 * 60 * 1000; // 5 minutes
5
-
6
- export function generatePairingCode(): string {
7
- const bytes = new Uint8Array(CODE_LENGTH);
8
- crypto.getRandomValues(bytes);
9
- return Array.from(bytes, (b) => CODE_CHARS[b % CODE_CHARS.length]).join("");
10
- }