palmier 0.2.0 → 0.2.1

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.
Files changed (79) hide show
  1. package/CLAUDE.md +5 -1
  2. package/README.md +135 -45
  3. package/dist/agents/agent.d.ts +26 -0
  4. package/dist/agents/agent.js +32 -0
  5. package/dist/agents/claude.d.ts +8 -0
  6. package/dist/agents/claude.js +35 -0
  7. package/dist/agents/codex.d.ts +8 -0
  8. package/dist/agents/codex.js +41 -0
  9. package/dist/agents/gemini.d.ts +8 -0
  10. package/dist/agents/gemini.js +39 -0
  11. package/dist/agents/openclaw.d.ts +8 -0
  12. package/dist/agents/openclaw.js +25 -0
  13. package/dist/agents/shared-prompt.d.ts +11 -0
  14. package/dist/agents/shared-prompt.js +26 -0
  15. package/dist/commands/agents.d.ts +2 -0
  16. package/dist/commands/agents.js +19 -0
  17. package/dist/commands/info.d.ts +5 -0
  18. package/dist/commands/info.js +40 -0
  19. package/dist/commands/init.d.ts +7 -2
  20. package/dist/commands/init.js +139 -49
  21. package/dist/commands/mcpserver.d.ts +2 -0
  22. package/dist/commands/mcpserver.js +75 -0
  23. package/dist/commands/pair.d.ts +6 -0
  24. package/dist/commands/pair.js +166 -0
  25. package/dist/commands/plan-generation.md +32 -0
  26. package/dist/commands/run.d.ts +0 -1
  27. package/dist/commands/run.js +258 -114
  28. package/dist/commands/serve.d.ts +1 -1
  29. package/dist/commands/serve.js +16 -228
  30. package/dist/commands/sessions.d.ts +4 -0
  31. package/dist/commands/sessions.js +30 -0
  32. package/dist/commands/task-generation.md +1 -1
  33. package/dist/config.d.ts +5 -5
  34. package/dist/config.js +24 -6
  35. package/dist/index.js +58 -5
  36. package/dist/nats-client.d.ts +3 -3
  37. package/dist/nats-client.js +2 -2
  38. package/dist/rpc-handler.d.ts +6 -0
  39. package/dist/rpc-handler.js +367 -0
  40. package/dist/session-store.d.ts +12 -0
  41. package/dist/session-store.js +57 -0
  42. package/dist/spawn-command.d.ts +26 -0
  43. package/dist/spawn-command.js +48 -0
  44. package/dist/systemd.d.ts +2 -2
  45. package/dist/task.d.ts +45 -2
  46. package/dist/task.js +155 -14
  47. package/dist/transports/http-transport.d.ts +6 -0
  48. package/dist/transports/http-transport.js +157 -0
  49. package/dist/transports/nats-transport.d.ts +6 -0
  50. package/dist/transports/nats-transport.js +69 -0
  51. package/dist/types.d.ts +30 -13
  52. package/package.json +4 -3
  53. package/src/agents/agent.ts +62 -0
  54. package/src/agents/claude.ts +39 -0
  55. package/src/agents/codex.ts +46 -0
  56. package/src/agents/gemini.ts +43 -0
  57. package/src/agents/openclaw.ts +29 -0
  58. package/src/agents/shared-prompt.ts +26 -0
  59. package/src/commands/agents.ts +20 -0
  60. package/src/commands/info.ts +44 -0
  61. package/src/commands/init.ts +229 -121
  62. package/src/commands/mcpserver.ts +92 -0
  63. package/src/commands/pair.ts +195 -0
  64. package/src/commands/plan-generation.md +32 -0
  65. package/src/commands/run.ts +323 -129
  66. package/src/commands/serve.ts +26 -287
  67. package/src/commands/sessions.ts +32 -0
  68. package/src/config.ts +30 -10
  69. package/src/index.ts +67 -6
  70. package/src/nats-client.ts +4 -4
  71. package/src/rpc-handler.ts +421 -0
  72. package/src/session-store.ts +68 -0
  73. package/src/spawn-command.ts +78 -0
  74. package/src/systemd.ts +2 -2
  75. package/src/task.ts +166 -16
  76. package/src/transports/http-transport.ts +180 -0
  77. package/src/transports/nats-transport.ts +82 -0
  78. package/src/types.ts +36 -13
  79. package/src/commands/task-generation.md +0 -28
package/CLAUDE.md CHANGED
@@ -2,4 +2,8 @@
2
2
 
3
3
  ## Getting Started
4
4
 
5
- Always read `spec.md` and `README.md` first before starting any task. These files contain the project specification and setup instructions that should inform all work.
5
+ Always read `README.md` first before starting any task. For the full platform architecture spec (NATS protocol, data model, execution flow), see `spec.md` in the [palmier-server](../palmier-server) repo.
6
+
7
+ ## Documentation
8
+
9
+ When making architectural changes, update `README.md` and the server repo's `spec.md` to reflect the new state.
package/README.md CHANGED
@@ -1,12 +1,26 @@
1
- # Palmier Agent
1
+ # Palmier
2
2
 
3
- A Node.js CLI that runs on your machine as a persistent agent. It manages tasks, communicates with the Palmier platform via NATS, and executes Claude Code autonomously.
3
+ A Node.js CLI that runs on your machine as a persistent daemon. It manages tasks, communicates with the Palmier platform via NATS and/or direct HTTP, and executes CLI tools autonomously.
4
+
5
+ ## Connection Modes
6
+
7
+ The host supports three connection modes:
8
+
9
+ | Mode | Transport | Setup | Features |
10
+ |------|-----------|-------|----------|
11
+ | **`nats`** | NATS only | `palmier init --server <url>` → select nats | All features |
12
+ | **`auto`** | Both NATS + HTTP | `palmier init --server <url>` → select auto | All features. PWA auto-detects best route. |
13
+ | **`lan`** | HTTP only | `palmier init --lan` | No push notifications or email MCP. No server needed. |
14
+
15
+ In **auto** mode, the PWA connects directly to the host via HTTP when on the same LAN (lower latency), and falls back to NATS when the host is unreachable. Push notifications and email relay always flow through NATS/server, so no features are lost.
16
+
17
+ In **lan** mode, the host runs a standalone HTTP server. Multiple PWA clients can connect using session tokens. No Palmier web server or NATS broker is needed.
4
18
 
5
19
  ## Prerequisites
6
20
 
7
21
  - **Node.js 20+**
8
- - **Claude Code CLI** installed and authenticated
9
- - **Linux with systemd** (the agent installs as a systemd user service)
22
+ - An agent CLI tool for task execution (e.g., Claude Code, Gemini CLI, OpenAI Codex)
23
+ - **Linux with systemd** (the host installs as a systemd user service)
10
24
 
11
25
  ## Installation
12
26
 
@@ -18,87 +32,173 @@ npm install -g palmier
18
32
 
19
33
  | Command | Description |
20
34
  |---|---|
21
- | `palmier init --token <token>` | Provision the agent with a token from the dashboard |
22
- | `palmier serve` | Run the persistent NATS RPC handler (default command) |
35
+ | `palmier init --server <url>` | Provision the host via the server (prompts for nats/auto mode) |
36
+ | `palmier init --lan` | Provision standalone LAN host (no server needed) |
37
+ | `palmier pair` | Generate an OTP code to pair a new device |
38
+ | `palmier sessions list` | List active session tokens |
39
+ | `palmier sessions revoke <token>` | Revoke a specific session token |
40
+ | `palmier sessions revoke-all` | Revoke all session tokens |
41
+ | `palmier info` | Show host connection info (address, mode) |
42
+ | `palmier serve` | Run the persistent RPC handler (default command) |
23
43
  | `palmier run <task-id>` | Execute a specific task |
44
+ | `palmier mcpserver` | Start an MCP server exposing Palmier tools (stdio transport) |
45
+ | `palmier agents` | Re-detect installed agent CLIs and update config |
24
46
 
25
47
  ## Setup
26
48
 
27
- 1. Install the agent: `npm install -g palmier`
28
- 2. Register on the Palmier PWA and click **Add Agent**.
29
- 3. Copy the provisioning token.
30
- 4. Run `palmier init --token <token>` in your project directory.
49
+ ### Server-provisioned (nats or auto mode)
50
+
51
+ 1. Install the host: `npm install -g palmier`
52
+ 2. Run `palmier init --server https://your-server-url` in your project directory.
53
+ 3. Select connection mode: `auto` (recommended) or `nats`.
54
+ 4. The host registers with the server, saves config, installs a systemd service, and generates a pairing code.
55
+ 5. Enter the pairing code in the Palmier PWA to connect your device.
56
+
57
+ ### Standalone LAN mode
58
+
59
+ 1. Install the host: `npm install -g palmier`
60
+ 2. Run `palmier init --lan` in your project directory.
61
+ 3. A pairing code and address are displayed. Enter both in the PWA.
62
+
63
+ ### Pairing additional devices
64
+
65
+ Run `palmier pair` on the host to generate a new OTP code. Each paired device gets its own session token.
66
+
67
+ ### Managing sessions
68
+
69
+ ```bash
70
+ # List all paired devices
71
+ palmier sessions list
72
+
73
+ # Revoke a specific device's access
74
+ palmier sessions revoke <token>
75
+
76
+ # Revoke all sessions (unpair all devices)
77
+ palmier sessions revoke-all
78
+ ```
31
79
 
32
80
  The `init` command:
33
- - Saves agent configuration to `~/.config/palmier/agent.json`
34
- - Installs a systemd user service for the agent
81
+ - Detects installed agent CLIs (Claude Code, Gemini CLI, Codex CLI) and caches the result
82
+ - Saves host configuration to `~/.config/palmier/host.json`
83
+ - Installs a systemd user service for the host
84
+ - Auto-enters pair mode to connect your first device
85
+
86
+ To re-detect agents after installing or removing a CLI, run `palmier agents`.
35
87
 
36
88
  ### Verifying the Service
37
89
 
38
- After `palmier init`, verify the agent is running:
90
+ After `palmier init`, verify the host is running:
39
91
 
40
92
  ```bash
41
93
  # Check service status
42
- systemctl --user status palmier-agent.service
94
+ systemctl --user status palmier.service
43
95
 
44
96
  # View recent logs
45
- journalctl --user -u palmier-agent.service -n 50 --no-pager
97
+ journalctl --user -u palmier.service -n 50 --no-pager
46
98
 
47
99
  # Follow logs in real time
48
- journalctl --user -u palmier-agent.service -f
100
+ journalctl --user -u palmier.service -f
49
101
  ```
50
102
 
51
103
  You should see `Active: active (running)` and log output showing a NATS connection. If the service failed:
52
104
 
53
105
  ```bash
54
106
  # Restart manually
55
- systemctl --user restart palmier-agent.service
107
+ systemctl --user restart palmier.service
56
108
 
57
109
  # Check config is valid
58
- cat ~/.config/palmier/agent.json
110
+ cat ~/.config/palmier/host.json
59
111
  ```
60
112
 
61
113
  ## How It Works
62
114
 
63
- - The agent runs as a **systemd user service**, staying alive in the background.
64
- - The persistent process (`palmier serve`) is a NATS RPC handler. It derives the RPC method from the NATS subject (e.g., `...rpc.task.create` `task.create`) and treats the message body as request parameters.
65
- - **Task IDs** are generated by the agent as UUIDs.
115
+ - The host runs as a **systemd user service**, staying alive in the background.
116
+ - The persistent process (`palmier serve`) is an RPC handler. In NATS mode, it derives the method from the NATS subject; in LAN/auto mode, it exposes HTTP endpoints (`POST /rpc/<method>`). The RPC handler (`src/rpc-handler.ts`) is transport-agnostic.
117
+ - **Session tokens** every RPC request includes a `sessionToken` (in the NATS JSON payload or as an HTTP Bearer token). The host validates the token against `~/.config/palmier/sessions.json` before processing the request. If no sessions exist, validation is skipped.
118
+ - **Task IDs** are generated by the host as UUIDs.
66
119
  - All RPC responses (`task.list`, `task.create`, `task.update`) return **flat task objects** — frontmatter fields at the top level, not nested under a `frontmatter` key.
120
+ - **Plan generation is automatic** — `task.create` and `task.update` generate an execution plan and task name by invoking the configured agent CLI. For `task.update`, plans are regenerated only when `user_prompt` or `agent` changes, or when no plan body exists yet.
121
+ - **Run history** — each task run produces a time-stamped result file (`RESULT-<timestamp>.md`) and a task snapshot (`TASK-<timestamp>.md`) in the task directory. A project-level `history.jsonl` file indexes all runs with `{ task_id, result_file }` entries. The `activity.list` RPC reads this index with pagination (`offset`/`limit`, optional `task_id` filter) and enriches each entry with RESULT frontmatter. The `task.result` RPC requires a `result_file` parameter identifying which result to read.
122
+ - The `task.reports` RPC reads one or more report files (each must end with `.md`, plain filenames only) from a task's directory. If the RESULT frontmatter includes a `report_files` field, the PWA shows clickable links in the result dialog that fetch and display each report.
67
123
  - Tasks have no separate name field — the `user_prompt` is the primary identifier and display label.
68
- - Plan generation is optional — if no plan body is present, the agent uses only `user_prompt` as the prompt.
69
124
  - **Triggers can be enabled/disabled** via the `triggers_enabled` frontmatter field (default `true`). When disabled, systemd timers are removed; when re-enabled, they are reinstalled. Tasks can still be run manually regardless.
70
125
  - Incoming tasks are stored as `TASK.md` files in a local `tasks/` directory.
71
- - Task execution spawns **Claude Code as a background process** with `-p --dangerously-skip-permissions`, running non-interactively. Task lifecycle events (`start`, `finish`, `abort`, `fail`) are tracked via a NATS JetStream KV bucket (`task-event`), keyed by `<agent_id>.<task_id>`.
72
- - **Task confirmation** — tasks with `requires_confirmation: true` write a pending entry to the `pending-confirmation` KV bucket before execution. The Web Server and PWA watch this bucket to show confirmation prompts. The task waits until the user confirms or aborts via the PWA or push notification.
126
+ - Task execution is abstracted through an **`AgentTool` interface** (`src/agents/agent.ts`). Each task stores an `agent` field (e.g., `"claude"`) that selects which agent implementation constructs the full command line and arguments. The agent's `getTaskRunCommandLine(task)` method builds the appropriate flags (e.g., `--allowedTools` for Claude based on task permissions). The process is spawned without a shell, and stdin is closed to prevent tools from hanging on an open pipe. The spawned process inherits the default physical GUI session environment (`DISPLAY=:0`) so commands that launch graphical applications (e.g., headed browsers) run within the user's desktop session. Task lifecycle status (`start`, `finish`, `abort`, `fail`) is persisted to a `status.json` file in the task directory. Status changes are broadcast on a unified `host-event.<host_id>.<task_id>` subject via NATS pub/sub (when available) and/or pushed through SSE (in LAN/auto mode) by `run.ts` POSTing to the serve process's `/internal/event` endpoint. All events carry an `event_type` field (`"running-state"`, `"confirm-request"`, or `"confirm-resolved"`) with the same payload shape on both transports. Consumers (PWA, Web Server) subscribe to these notifications and fetch full status from the host via the `task.status` RPC.
127
+ - **Task confirmation** — tasks with `requires_confirmation: true` set `pending_confirmation: true` in `status.json` before execution. A `confirm-request` event is published on `host-event` so the Web Server can send push notifications and connected PWA clients show a confirmation dialog. The user confirms or aborts via the PWA, which calls the `task.user_input` RPC on the host. The `serve` process sets `user_input` to the user's response (e.g., `"confirmed"` or `"aborted"`), and `run` watches the file for `user_input` to become defined, then updates `running_state` accordingly.
128
+ - **MCP server** (`palmier mcpserver`) — starts an MCP server over stdio that exposes platform tools to AI agents (e.g., Claude Code). In NATS/auto mode, connects to NATS on startup. Currently exposes a `send-email` tool that relays email requests to the Web Server via NATS (`host.<host_id>.email.send`).
73
129
 
74
130
  ## Project Structure
75
131
 
76
132
  ```
77
133
  src/
78
134
  index.ts # CLI entrypoint (commander setup)
79
- config.ts # Agent configuration (read/write ~/.config/palmier)
80
- nats-client.ts # NATS connection and messaging
135
+ config.ts # Host configuration (read/write ~/.config/palmier)
136
+ rpc-handler.ts # Transport-agnostic RPC handler (with session validation)
137
+ session-store.ts # Session token management (~/.config/palmier/sessions.json)
138
+ nats-client.ts # NATS connection helper
139
+ spawn-command.ts # Shared helper for spawning CLI tools without a shell
81
140
  systemd.ts # systemd service installation
82
141
  task.ts # Task file management
83
142
  types.ts # Shared type definitions
143
+ agents/
144
+ agent.ts # AgentTool interface, registry, and agent detection
145
+ claude.ts # Claude Code agent implementation
146
+ gemini.ts # Gemini CLI agent implementation
147
+ codex.ts # Codex CLI agent implementation
148
+ openclaw.ts # OpenClaw agent implementation
84
149
  commands/
85
- init.ts # Provisioning logic
86
- serve.ts # Persistent NATS RPC handler
150
+ init.ts # Provisioning logic (--server and --lan flows, auto-pair)
151
+ pair.ts # OTP code generation and pairing handler
152
+ sessions.ts # Session token management CLI (list, revoke, revoke-all)
153
+ info.ts # Print host connection info
154
+ agents.ts # Re-detect installed agent CLIs
155
+ serve.ts # Transport selection and startup
87
156
  run.ts # Single task execution
157
+ mcpserver.ts # MCP server with platform tools (send-email)
158
+ transports/
159
+ nats-transport.ts # NATS subscription loop (host.<hostId>.rpc.>)
160
+ http-transport.ts # HTTP server with RPC, SSE, and internal event endpoints
161
+ ```
162
+
163
+ ## MCP Server
164
+
165
+ The host includes an MCP server that exposes Palmier platform tools to AI agents like Claude Code.
166
+
167
+ ### Setup
168
+
169
+ Add to your Claude Code MCP settings:
170
+
171
+ ```json
172
+ {
173
+ "mcpServers": {
174
+ "palmier": {
175
+ "command": "palmier",
176
+ "args": ["mcpserver"]
177
+ }
178
+ }
179
+ }
88
180
  ```
89
181
 
90
- ## Removing an Agent
182
+ Requires a provisioned host (`palmier init`). In NATS/auto mode, uses NATS relay. In LAN mode with `serverUrl` configured, uses HTTP relay. Not available in standalone LAN mode without a server.
183
+
184
+ ### Available Tools
91
185
 
92
- To fully remove an agent from a machine:
186
+ | Tool | Inputs | Description |
187
+ |---|---|---|
188
+ | `send-email` | `to`, `subject`, `text` (required); `html`, `reply_to`, `cc`, `bcc` (optional) | Send an email via the platform's SMTP relay |
93
189
 
94
- 1. **Delete the agent from the PWA dashboard** (this removes it from the server database).
190
+ ## Removing a Host
191
+
192
+ To fully remove a host from a machine:
193
+
194
+ 1. **Unpair the host from the PWA** (via the host menu).
95
195
 
96
196
  2. **Stop and remove the systemd service:**
97
197
 
98
198
  ```bash
99
- systemctl --user stop palmier-agent.service
100
- systemctl --user disable palmier-agent.service
101
- rm ~/.config/systemd/user/palmier-agent.service
199
+ systemctl --user stop palmier.service
200
+ systemctl --user disable palmier.service
201
+ rm ~/.config/systemd/user/palmier.service
102
202
  ```
103
203
 
104
204
  3. **Remove any task timers and services:**
@@ -115,7 +215,7 @@ To fully remove an agent from a machine:
115
215
  systemctl --user daemon-reload
116
216
  ```
117
217
 
118
- 5. **Remove the agent configuration:**
218
+ 5. **Remove the host configuration:**
119
219
 
120
220
  ```bash
121
221
  rm -rf ~/.config/palmier
@@ -126,13 +226,3 @@ To fully remove an agent from a machine:
126
226
  ```bash
127
227
  rm -rf tasks/
128
228
  ```
129
-
130
- 7. *(Optional)* **Disable login lingering** if no other user services need it:
131
-
132
- ```bash
133
- loginctl disable-linger
134
- ```
135
-
136
- ## Related
137
-
138
- See the [palmier](../palmier) repo for the server, API, and PWA.
@@ -0,0 +1,26 @@
1
+ import type { ParsedTask, RequiredPermission } from "../types.js";
2
+ export interface CommandLine {
3
+ command: string;
4
+ args: string[];
5
+ }
6
+ /**
7
+ * Interface that each agent tool must implement.
8
+ * Abstracts how plans are generated and tasks are executed across different AI agents.
9
+ */
10
+ export interface AgentTool {
11
+ /** Return the command and args used to generate a plan from a prompt. */
12
+ getPlanGenerationCommandLine(prompt: string): CommandLine;
13
+ /** Return the command and args used to run a task. If prompt is provided, use it instead of the task's prompt.
14
+ * extraPermissions are transient permissions granted for this run only (not persisted in frontmatter). */
15
+ getTaskRunCommandLine(task: ParsedTask, prompt?: string, extraPermissions?: RequiredPermission[]): CommandLine;
16
+ /** Detect whether the agent CLI is available and perform any agent-specific
17
+ * initialization. Returns true if the agent was detected and initialized successfully. */
18
+ init(): Promise<boolean>;
19
+ }
20
+ export interface DetectedAgent {
21
+ key: string;
22
+ label: string;
23
+ }
24
+ export declare function detectAgents(): Promise<DetectedAgent[]>;
25
+ export declare function getAgent(name: string): AgentTool;
26
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1,32 @@
1
+ import { ClaudeAgent } from "./claude.js";
2
+ import { GeminiAgent } from "./gemini.js";
3
+ import { CodexAgent } from "./codex.js";
4
+ const agentRegistry = {
5
+ claude: new ClaudeAgent(),
6
+ gemini: new GeminiAgent(),
7
+ codex: new CodexAgent(),
8
+ };
9
+ const agentLabels = {
10
+ claude: "Claude Code",
11
+ gemini: "Gemini CLI",
12
+ codex: "Codex CLI",
13
+ openclaw: "OpenClaw"
14
+ };
15
+ export async function detectAgents() {
16
+ const detected = [];
17
+ for (const [key, agent] of Object.entries(agentRegistry)) {
18
+ const label = agentLabels[key] ?? key;
19
+ const ok = await agent.init();
20
+ if (ok)
21
+ detected.push({ key, label });
22
+ }
23
+ return detected;
24
+ }
25
+ export function getAgent(name) {
26
+ const agent = agentRegistry[name];
27
+ if (!agent) {
28
+ throw new Error(`Unknown agent: "${name}". Available agents: ${Object.keys(agentRegistry).join(", ")}`);
29
+ }
30
+ return agent;
31
+ }
32
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1,8 @@
1
+ import type { ParsedTask, RequiredPermission } from "../types.js";
2
+ import type { AgentTool, CommandLine } from "./agent.js";
3
+ export declare class ClaudeAgent implements AgentTool {
4
+ getPlanGenerationCommandLine(prompt: string): CommandLine;
5
+ getTaskRunCommandLine(task: ParsedTask, prompt?: string, extraPermissions?: RequiredPermission[]): CommandLine;
6
+ init(): Promise<boolean>;
7
+ }
8
+ //# sourceMappingURL=claude.d.ts.map
@@ -0,0 +1,35 @@
1
+ import { execSync } from "child_process";
2
+ import { TASK_OUTCOME_SUFFIX } from "./shared-prompt.js";
3
+ export class ClaudeAgent {
4
+ getPlanGenerationCommandLine(prompt) {
5
+ return {
6
+ command: "claude",
7
+ args: ["-p", prompt],
8
+ };
9
+ }
10
+ getTaskRunCommandLine(task, prompt, extraPermissions) {
11
+ prompt = (prompt ?? (task.body || task.frontmatter.user_prompt)) + TASK_OUTCOME_SUFFIX;
12
+ const args = ["-c", "--permission-mode", "acceptEdits", "-p", prompt];
13
+ const allPerms = [...(task.frontmatter.permissions ?? []), ...(extraPermissions ?? [])];
14
+ for (const p of allPerms) {
15
+ args.push("--allowedTools", p.name);
16
+ }
17
+ return { command: "claude", args };
18
+ }
19
+ async init() {
20
+ try {
21
+ execSync("claude --version");
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ try {
27
+ execSync("claude mcp add --transport stdio palmier --scope user -- palmier mcpserver");
28
+ }
29
+ catch (err) {
30
+ console.warn("Warning: failed to install MCP for Claude:", err instanceof Error ? err.message : err);
31
+ }
32
+ return true;
33
+ }
34
+ }
35
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1,8 @@
1
+ import type { ParsedTask, RequiredPermission } from "../types.js";
2
+ import type { AgentTool, CommandLine } from "./agent.js";
3
+ export declare class CodexAgent implements AgentTool {
4
+ getPlanGenerationCommandLine(prompt: string): CommandLine;
5
+ getTaskRunCommandLine(task: ParsedTask, prompt?: string, extraPermissions?: RequiredPermission[]): CommandLine;
6
+ init(): Promise<boolean>;
7
+ }
8
+ //# sourceMappingURL=codex.d.ts.map
@@ -0,0 +1,41 @@
1
+ import { execSync } from "child_process";
2
+ import { TASK_OUTCOME_SUFFIX } from "./shared-prompt.js";
3
+ export class CodexAgent {
4
+ getPlanGenerationCommandLine(prompt) {
5
+ // TODO: fill in
6
+ return {
7
+ command: "codex",
8
+ args: ["exec", "--skip-git-repo-check", prompt],
9
+ };
10
+ }
11
+ getTaskRunCommandLine(task, prompt, extraPermissions) {
12
+ prompt = (prompt ?? (task.body || task.frontmatter.user_prompt)) + TASK_OUTCOME_SUFFIX;
13
+ // TODO: Update sandbox to workspace-write once https://github.com/openai/codex/issues/12572
14
+ // is fixed.
15
+ const args = ["exec", "--full-auto", "--skip-git-repo-check", "--sandbox", "danger-full-access"];
16
+ const allPerms = [...(task.frontmatter.permissions ?? []), ...(extraPermissions ?? [])];
17
+ for (const p of allPerms) {
18
+ args.push("--config");
19
+ args.push(`apps.${p.name}.default_tools_approval_mode="approve"`);
20
+ }
21
+ args.push(prompt);
22
+ args.push("resume", "--last");
23
+ return { command: "codex", args };
24
+ }
25
+ async init() {
26
+ try {
27
+ execSync("codex --version");
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ try {
33
+ execSync("codex mcp add palmier palmier mcpserver");
34
+ }
35
+ catch (err) {
36
+ console.warn("Warning: failed to install MCP for Codex:", err instanceof Error ? err.message : err);
37
+ }
38
+ return true;
39
+ }
40
+ }
41
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1,8 @@
1
+ import type { ParsedTask, RequiredPermission } from "../types.js";
2
+ import type { AgentTool, CommandLine } from "./agent.js";
3
+ export declare class GeminiAgent implements AgentTool {
4
+ getPlanGenerationCommandLine(prompt: string): CommandLine;
5
+ getTaskRunCommandLine(task: ParsedTask, prompt?: string, extraPermissions?: RequiredPermission[]): CommandLine;
6
+ init(): Promise<boolean>;
7
+ }
8
+ //# sourceMappingURL=gemini.d.ts.map
@@ -0,0 +1,39 @@
1
+ import { execSync } from "child_process";
2
+ import { TASK_OUTCOME_SUFFIX } from "./shared-prompt.js";
3
+ export class GeminiAgent {
4
+ getPlanGenerationCommandLine(prompt) {
5
+ // TODO: fill in
6
+ return {
7
+ command: "gemini",
8
+ args: ["--approval-mode", "auto_edit", "--prompt", prompt],
9
+ };
10
+ }
11
+ getTaskRunCommandLine(task, prompt, extraPermissions) {
12
+ prompt = prompt ?? (task.body || task.frontmatter.user_prompt);
13
+ const args = ["--resume", "--prompt", prompt + TASK_OUTCOME_SUFFIX];
14
+ const allPerms = [...(task.frontmatter.permissions ?? []), ...(extraPermissions ?? [])];
15
+ if (allPerms.length > 0) {
16
+ args.push("--allowed-tools");
17
+ for (const p of allPerms) {
18
+ args.push(p.name);
19
+ }
20
+ }
21
+ return { command: "gemini", args };
22
+ }
23
+ async init() {
24
+ try {
25
+ execSync("gemini --version");
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ try {
31
+ execSync("gemini mcp add --scope user palmier palmier mcpserver");
32
+ }
33
+ catch (err) {
34
+ console.warn("Warning: failed to install MCP for Gemini:", err instanceof Error ? err.message : err);
35
+ }
36
+ return true;
37
+ }
38
+ }
39
+ //# sourceMappingURL=gemini.js.map
@@ -0,0 +1,8 @@
1
+ import type { ParsedTask, RequiredPermission } from "../types.js";
2
+ import type { AgentTool, CommandLine } from "./agent.js";
3
+ export declare class ClaudeAgent implements AgentTool {
4
+ getPlanGenerationCommandLine(prompt: string): CommandLine;
5
+ getTaskRunCommandLine(task: ParsedTask, prompt?: string, extraPermissions?: RequiredPermission[]): CommandLine;
6
+ init(): Promise<boolean>;
7
+ }
8
+ //# sourceMappingURL=openclaw.d.ts.map
@@ -0,0 +1,25 @@
1
+ import { execSync } from "child_process";
2
+ import { TASK_OUTCOME_SUFFIX } from "./shared-prompt.js";
3
+ export class ClaudeAgent {
4
+ getPlanGenerationCommandLine(prompt) {
5
+ return {
6
+ command: "openclaw",
7
+ args: ["agent", "--message", prompt],
8
+ };
9
+ }
10
+ getTaskRunCommandLine(task, prompt, extraPermissions) {
11
+ prompt = (prompt ?? (task.body || task.frontmatter.user_prompt)) + TASK_OUTCOME_SUFFIX;
12
+ const args = ["agent", "--message", prompt];
13
+ return { command: "openclaw", args };
14
+ }
15
+ async init() {
16
+ try {
17
+ execSync("openclaw --version");
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ return true;
23
+ }
24
+ }
25
+ //# sourceMappingURL=openclaw.js.map
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Appended to the end of every task prompt.
3
+ * Instructs the agent to output a clear success/failure marker
4
+ * so that palmier can determine the task outcome.
5
+ */
6
+ export declare const TASK_OUTCOME_SUFFIX = "\n\n---\nIf you generate report or output files, print each file name on its own line prefixed with [PALMIER_REPORT]: e.g.\n[PALMIER_REPORT] report.md\n[PALMIER_REPORT] summary.md\n\nWhen you are done, output exactly one of these markers as the very last line:\n- Success: [PALMIER_TASK_SUCCESS]\n- Failure: [PALMIER_TASK_FAILURE]\nDo not wrap them in code blocks or add text on the same line.\n\nIf the task fails because a tool was denied or you lack the required permissions, print each required permission on its own line prefixed with [PALMIER_PERMISSION]: e.g.\n[PALMIER_PERMISSION] Read | Read file contents from the repository\n[PALMIER_PERMISSION] Bash(npm test) | Run the test suite via npm\n[PALMIER_PERMISSION] Write | Write generated output files";
7
+ export declare const TASK_SUCCESS_MARKER = "[PALMIER_TASK_SUCCESS]";
8
+ export declare const TASK_FAILURE_MARKER = "[PALMIER_TASK_FAILURE]";
9
+ export declare const TASK_REPORT_PREFIX = "[PALMIER_REPORT]";
10
+ export declare const TASK_PERMISSION_PREFIX = "[PALMIER_PERMISSION]";
11
+ //# sourceMappingURL=shared-prompt.d.ts.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Appended to the end of every task prompt.
3
+ * Instructs the agent to output a clear success/failure marker
4
+ * so that palmier can determine the task outcome.
5
+ */
6
+ export const TASK_OUTCOME_SUFFIX = `
7
+
8
+ ---
9
+ If you generate report or output files, print each file name on its own line prefixed with [PALMIER_REPORT]: e.g.
10
+ [PALMIER_REPORT] report.md
11
+ [PALMIER_REPORT] summary.md
12
+
13
+ When you are done, output exactly one of these markers as the very last line:
14
+ - Success: [PALMIER_TASK_SUCCESS]
15
+ - Failure: [PALMIER_TASK_FAILURE]
16
+ Do not wrap them in code blocks or add text on the same line.
17
+
18
+ If the task fails because a tool was denied or you lack the required permissions, print each required permission on its own line prefixed with [PALMIER_PERMISSION]: e.g.
19
+ [PALMIER_PERMISSION] Read | Read file contents from the repository
20
+ [PALMIER_PERMISSION] Bash(npm test) | Run the test suite via npm
21
+ [PALMIER_PERMISSION] Write | Write generated output files`;
22
+ export const TASK_SUCCESS_MARKER = "[PALMIER_TASK_SUCCESS]";
23
+ export const TASK_FAILURE_MARKER = "[PALMIER_TASK_FAILURE]";
24
+ export const TASK_REPORT_PREFIX = "[PALMIER_REPORT]";
25
+ export const TASK_PERMISSION_PREFIX = "[PALMIER_PERMISSION]";
26
+ //# sourceMappingURL=shared-prompt.js.map
@@ -0,0 +1,2 @@
1
+ export declare function agentsCommand(): Promise<void>;
2
+ //# sourceMappingURL=agents.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { loadConfig, saveConfig } from "../config.js";
2
+ import { detectAgents } from "../agents/agent.js";
3
+ export async function agentsCommand() {
4
+ const config = loadConfig();
5
+ console.log("Detecting installed agents...");
6
+ const agents = await detectAgents();
7
+ config.agents = agents;
8
+ saveConfig(config);
9
+ if (agents.length === 0) {
10
+ console.log("No agent CLIs detected.");
11
+ }
12
+ else {
13
+ console.log("Detected agents:");
14
+ for (const a of agents) {
15
+ console.log(` ${a.key} — ${a.label}`);
16
+ }
17
+ }
18
+ }
19
+ //# sourceMappingURL=agents.js.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Print host connection info for setting up clients.
3
+ */
4
+ export declare function infoCommand(): Promise<void>;
5
+ //# sourceMappingURL=info.d.ts.map
@@ -0,0 +1,40 @@
1
+ import * as os from "os";
2
+ import { loadConfig } from "../config.js";
3
+ /**
4
+ * Detect the first non-internal IPv4 address.
5
+ */
6
+ function detectLanIp() {
7
+ const interfaces = os.networkInterfaces();
8
+ for (const name of Object.keys(interfaces)) {
9
+ for (const iface of interfaces[name] ?? []) {
10
+ if (iface.family === "IPv4" && !iface.internal) {
11
+ return iface.address;
12
+ }
13
+ }
14
+ }
15
+ return "127.0.0.1";
16
+ }
17
+ /**
18
+ * Print host connection info for setting up clients.
19
+ */
20
+ export async function infoCommand() {
21
+ const config = loadConfig();
22
+ const mode = config.mode ?? "nats";
23
+ console.log(`Mode: ${mode}`);
24
+ console.log(`Host ID: ${config.hostId}`);
25
+ if (mode === "lan" || mode === "auto") {
26
+ const lanIp = detectLanIp();
27
+ const port = config.directPort ?? 7400;
28
+ console.log("");
29
+ console.log("Direct connection info:");
30
+ console.log(` Address: ${lanIp}:${port}`);
31
+ console.log(` Token: ${config.directToken}`);
32
+ }
33
+ if (mode === "nats" || mode === "auto") {
34
+ console.log("");
35
+ console.log("NATS connection info:");
36
+ console.log(` URL: ${config.natsUrl}`);
37
+ console.log(` WS: ${config.natsWsUrl}`);
38
+ }
39
+ }
40
+ //# sourceMappingURL=info.js.map
@@ -1,8 +1,13 @@
1
1
  export interface InitOptions {
2
- token: string;
2
+ server?: string;
3
+ lan?: boolean;
4
+ host?: string;
5
+ port?: number;
3
6
  }
4
7
  /**
5
- * Provision this agent by exchanging a base64 provisioning token for permanent credentials.
8
+ * Provision this host. Two flows:
9
+ * - palmier init --lan → standalone LAN mode, no server needed
10
+ * - palmier init --server <url> → register with server, prompts for nats/auto mode
6
11
  */
7
12
  export declare function initCommand(options: InitOptions): Promise<void>;
8
13
  //# sourceMappingURL=init.d.ts.map