palmier 1.0.3 → 1.0.5

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
@@ -179,6 +179,7 @@ palmier passwords clear
179
179
  The wizard:
180
180
  - Detects installed agent CLIs and caches the result; agents previously installed by Palmier have their installed version re-probed so the recorded version stays in sync with manual upgrades
181
181
  - Offers to install missing supported agents from npm (one at a time, arrow-key menu). After each install the agent's version is stamped (becoming "Palmier-managed"), the wizard kicks off authentication, and waits for you to press Enter before offering the next install. On re-init, the agents list is saved after every install so an interrupted wizard is resumable.
182
+ - Offers to install the **Playwright CLI** for browser automation (see [Browser Automation](#browser-automation)). Skipped on re-init if it's already managed.
182
183
  - Asks for the HTTP port
183
184
  - Detects the default network interface (used for auto-LAN)
184
185
  - Shows a summary (including any existing scheduled tasks to recover) and asks for confirmation
@@ -200,10 +201,19 @@ Agents installed by the user outside the wizard (e.g., `npm install -g <pkg>` di
200
201
 
201
202
  Run `palmier agents` to manage agent CLIs after setup: it lists installed agents and offers an interactive picker to install or uninstall one. `palmier init` only prompts for an agent install when none are detected; once any agent is installed, init just lists them and continues with host registration.
202
203
 
204
+ ### Browser Automation
205
+
206
+ Some capabilities (like the [saved-password autofill](#saved-passwords)) drive a real browser through the [`playwright-cli`](https://www.npmjs.com/package/@playwright/cli) skill. `palmier init` offers to install and manage the Playwright CLI (`@playwright/cli`) for this, and on install also runs `playwright-cli install --skills` in your Palmier task directory (`~/palmier`) so agents get the Playwright skills that teach them how to drive the browser. When you opt in, Palmier manages it **just like an agent CLI** — the version is stamped, re-probed live on each daemon start, and the PWA shows a "Browser Automation Update Available" dialog (same flow as agents) when a newer version is published. Its version is shown in the app's host drawer. The browser binaries Playwright needs are downloaded lazily on first use, not during init.
207
+
208
+ Unlike agents, the Playwright CLI has **no explicit uninstall** — it's not offered in any uninstall picker. It is removed only by a full `palmier uninstall`.
209
+
203
210
  ### Updates
204
211
 
205
- - **Palmier itself** — when a newer version of `palmier` is published to npm, the PWA shows a dismissible "Update Available" dialog. Clicking "Update Now" runs `npm update -g palmier` on the host and restarts the daemon. Clicking "Dismiss" suppresses the dialog for that exact version (per host, per device); a future release re-arms it.
206
- - **Palmier-managed agents** — same flow per agent: when npm publishes a newer version, the PWA shows an "Agent Update Available" dialog. Clicking "Update Now" runs `npm update -g <pkg>` on the host (no daemon restart needed). Dismissals are per host, per agent, per version.
212
+ - **Palmier itself** — when a newer version of `palmier` is published to npm, the PWA shows an "Update Available" dialog. Clicking "Update Now" runs `npm update -g palmier` on the host and restarts the daemon. Clicking "Dismiss" only hides it for the current session reopening or reloading the app shows it again until you update.
213
+ - **Palmier-managed agents** — same flow per agent: when npm publishes a newer version, the PWA shows an "Agent Update Available" dialog. Clicking "Update Now" runs `npm update -g <pkg>` on the host (no daemon restart needed).
214
+ - **Managed Playwright CLI** — same flow: a "Browser Automation Update Available" dialog runs `npm update -g @playwright/cli`.
215
+
216
+ Dismissals are session-only: they are never persisted, so a reload re-arms every update dialog until the update is applied.
207
217
 
208
218
  ### Re-detecting the LAN Network
209
219
 
@@ -226,7 +236,7 @@ The default network interface is detected once during `palmier init` and saved t
226
236
  | `palmier serve` | Run the persistent RPC handler (default command) |
227
237
  | `palmier restart` | Restart the palmier serve daemon |
228
238
  | `palmier run <task-id>` | Execute a specific task |
229
- | `palmier uninstall` | Stop daemon, remove all scheduled tasks, and uninstall Palmier-managed agent CLIs |
239
+ | `palmier uninstall` | Stop daemon, remove all scheduled tasks, and uninstall Palmier-managed agent CLIs and the managed Playwright CLI |
230
240
 
231
241
  ## Uninstalling
232
242
 
@@ -234,7 +244,7 @@ To fully remove Palmier from a machine:
234
244
 
235
245
  1. **Unpair your device** in the PWA (via the host menu).
236
246
 
237
- 2. **Stop the daemon, remove all scheduled tasks, and uninstall Palmier-managed agent CLIs:**
247
+ 2. **Stop the daemon, remove all scheduled tasks, and uninstall Palmier-managed agent CLIs and the managed Playwright CLI:**
238
248
 
239
249
  ```bash
240
250
  palmier uninstall
@@ -16,6 +16,7 @@ import { kiroAgent } from "./kiro.js";
16
16
  import { clineAgent } from "./cline.js";
17
17
  import { qoderAgent } from "./qoder.js";
18
18
  import { hermesAgent } from "./hermes.js";
19
+ import { grokAgent } from "./grok.js";
19
20
  export function getPromptCommandLine(agent, prompt) {
20
21
  return { args: [...agent.promptArgs, prompt] };
21
22
  }
@@ -70,6 +71,7 @@ const agentRegistry = {
70
71
  cline: clineAgent,
71
72
  qoder: qoderAgent,
72
73
  hermes: hermesAgent,
74
+ grok: grokAgent,
73
75
  };
74
76
  const TIER_ONE_ORDER = ["claude", "codex", "copilot"];
75
77
  export function listInstallableAgents() {
@@ -14,7 +14,7 @@ export const claudeAgent = {
14
14
  const prompt = followupPrompt ?? getAgentInstructions(task);
15
15
  const args = ["--permission-mode", yolo ? "bypassPermissions" : "acceptEdits", "-p"];
16
16
  if (!yolo) {
17
- args.push("--allowedTools", "Bash(curl)", "WebFetch");
17
+ args.push("--allowedTools", "WebFetch", "Skill(playwright-cli:*)", "Bash(curl:*)", "Bash(wget:*)", "Bash(playwright:*)", "Bash(playwright-cli:*)");
18
18
  const allPerms = [...(task.frontmatter.permissions ?? []), ...(extraPermissions ?? [])];
19
19
  for (const p of allPerms) {
20
20
  args.push(p.name);
@@ -23,6 +23,15 @@ export const claudeAgent = {
23
23
  if (followupPrompt) {
24
24
  args.push("-c");
25
25
  }
26
- return { args, stdin: prompt };
26
+ return {
27
+ args,
28
+ stdin: prompt,
29
+ // Don't let the Bash tool kill the curl that blocks on user input — the
30
+ // user may take arbitrarily long to answer. Max non-overflowing setTimeout.
31
+ env: {
32
+ BASH_DEFAULT_TIMEOUT_MS: "2147483647",
33
+ BASH_MAX_TIMEOUT_MS: "2147483647",
34
+ },
35
+ };
27
36
  },
28
37
  };
@@ -0,0 +1,2 @@
1
+ import type { AgentTool } from "./agent.js";
2
+ export declare const grokAgent: AgentTool;
@@ -0,0 +1,21 @@
1
+ import { getAgentInstructions } from "./shared-prompt.js";
2
+ export const grokAgent = {
3
+ label: "Grok Build",
4
+ command: "grok",
5
+ promptArgs: ["-p"],
6
+ probeArg: "--version",
7
+ supportsYolo: true,
8
+ getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
9
+ const yolo = extraPermissions === "yolo";
10
+ const prompt = followupPrompt ?? getAgentInstructions(task);
11
+ const args = [];
12
+ if (yolo) {
13
+ args.push("--always-approve");
14
+ }
15
+ if (followupPrompt) {
16
+ args.push("-c");
17
+ }
18
+ args.push("-p", prompt);
19
+ return { args };
20
+ },
21
+ };
@@ -25,4 +25,10 @@ export declare function pickAndUninstallAgent(current: DetectedAgent[]): Promise
25
25
  * Shared between the `palmier agents` interactive picker and the bulk
26
26
  * managed-agent removal in `palmier uninstall`. */
27
27
  export declare function uninstallAgent(agent: DetectedAgent): boolean;
28
+ /** `npm install -g <pkg>`. Shared by the agent installer and the Playwright CLI
29
+ * tool manager. Logs progress and a permissions hint on failure. */
30
+ export declare function npmInstallGlobal(npmPackage: string): boolean;
28
31
  export declare function uninstallManagedAgents(agents: DetectedAgent[]): void;
32
+ /** `npm uninstall -g <pkg>`. Shared by the agent uninstaller and the Playwright
33
+ * CLI tool manager (the latter only via `palmier uninstall`, never a picker). */
34
+ export declare function npmUninstallGlobal(npmPackage: string): boolean;
@@ -105,16 +105,18 @@ export async function pickAndUninstallAgent(current) {
105
105
  export function uninstallAgent(agent) {
106
106
  if (!agent.npmPackage)
107
107
  return false;
108
- if (!uninstallAgentPackage(agent.npmPackage)) {
108
+ if (!npmUninstallGlobal(agent.npmPackage)) {
109
109
  console.log(red(` Skipped ${agent.label}; uninstall it manually with npm uninstall -g ${agent.npmPackage}.`));
110
110
  return false;
111
111
  }
112
112
  console.log(green(` ${agent.label} uninstalled.`));
113
113
  return true;
114
114
  }
115
- function installAgentPackage(agent) {
116
- console.log(`\nInstalling ${cyan(agent.npmPackage)}...\n`);
117
- const cmd = `npm install -g ${agent.npmPackage}`;
115
+ /** `npm install -g <pkg>`. Shared by the agent installer and the Playwright CLI
116
+ * tool manager. Logs progress and a permissions hint on failure. */
117
+ export function npmInstallGlobal(npmPackage) {
118
+ console.log(`\nInstalling ${cyan(npmPackage)}...\n`);
119
+ const cmd = `npm install -g ${npmPackage}`;
118
120
  const result = spawnSync(cmd, { shell: true, stdio: "inherit" });
119
121
  if (result.error) {
120
122
  console.log(`\n${red(`Failed to run npm: ${result.error.message}`)}`);
@@ -134,6 +136,9 @@ function installAgentPackage(agent) {
134
136
  }
135
137
  return true;
136
138
  }
139
+ function installAgentPackage(agent) {
140
+ return npmInstallGlobal(agent.npmPackage);
141
+ }
137
142
  export function uninstallManagedAgents(agents) {
138
143
  const managed = agents.filter((a) => a.version && a.npmPackage);
139
144
  if (managed.length === 0) {
@@ -156,7 +161,9 @@ export function uninstallManagedAgents(agents) {
156
161
  console.log(`\n${yellow(`Uninstalled ${succeeded} of ${managed.length} agent CLIs (${failed} skipped).`)}`);
157
162
  }
158
163
  }
159
- function uninstallAgentPackage(npmPackage) {
164
+ /** `npm uninstall -g <pkg>`. Shared by the agent uninstaller and the Playwright
165
+ * CLI tool manager (the latter only via `palmier uninstall`, never a picker). */
166
+ export function npmUninstallGlobal(npmPackage) {
160
167
  console.log(`\nUninstalling ${cyan(npmPackage)}...\n`);
161
168
  const cmd = `npm uninstall -g ${npmPackage}`;
162
169
  const result = spawnSync(cmd, { shell: true, stdio: "inherit" });
@@ -1,6 +1,7 @@
1
1
  import { loadConfig } from "../config.js";
2
2
  import { loadClients } from "../client-store.js";
3
3
  import { buildLanUrl } from "../network.js";
4
+ import { PLAYWRIGHT_CLI_LABEL } from "../playwright-cli.js";
4
5
  export async function infoCommand() {
5
6
  const config = loadConfig();
6
7
  const clients = loadClients();
@@ -16,6 +17,9 @@ export async function infoCommand() {
16
17
  else {
17
18
  console.log(`Agents: (none detected — run \`palmier agents\`)`);
18
19
  }
20
+ if (config.playwrightCliVersion) {
21
+ console.log(`Browser: ${PLAYWRIGHT_CLI_LABEL} (v${config.playwrightCliVersion})`);
22
+ }
19
23
  console.log(`Clients: ${clients.length} active`);
20
24
  if (clients.length === 0) {
21
25
  console.log("");
@@ -5,6 +5,7 @@ import { homedir } from "os";
5
5
  import { loadConfig, saveConfig } from "../config.js";
6
6
  import { detectAgents } from "../agents/agent.js";
7
7
  import { colors, pickAndInstallAgent, printInstalledAgents } from "../agents/wizard.js";
8
+ import { installPlaywrightCli, installPlaywrightSkills, getPlaywrightCliVersion, PLAYWRIGHT_CLI_LABEL } from "../playwright-cli.js";
8
9
  import { getPlatform } from "../platform/index.js";
9
10
  import { pairCommand } from "./pair.js";
10
11
  import { detectDefaultInterface, getInterfaceIpv4 } from "../network.js";
@@ -47,6 +48,24 @@ export async function initCommand() {
47
48
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
48
49
  const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
49
50
  try {
51
+ // Browser automation is optional. Palmier manages the Playwright CLI like an
52
+ // agent CLI (install, version-stamp, update) once the user opts in. Trust the
53
+ // actual global install, not just the saved marker: `palmier uninstall`
54
+ // removes the package but leaves host.json, so a stale playwrightCliVersion
55
+ // must not suppress the prompt.
56
+ let playwrightCliVersion = previousConfig?.playwrightCliVersion
57
+ ? getPlaywrightCliVersion() ?? undefined
58
+ : undefined;
59
+ if (!playwrightCliVersion) {
60
+ const answer = (await ask(`\nInstall ${PLAYWRIGHT_CLI_LABEL} for browser automation? Lets agents control a browser — e.g. autofill saved passwords. (Y/n): `)).trim().toLowerCase();
61
+ if (answer !== "n" && answer !== "no") {
62
+ if (installPlaywrightCli()) {
63
+ playwrightCliVersion = getPlaywrightCliVersion() ?? undefined;
64
+ console.log(green(` ${PLAYWRIGHT_CLI_LABEL} installed.`));
65
+ installPlaywrightSkills(projectRoot);
66
+ }
67
+ }
68
+ }
50
69
  let httpPort = 7256;
51
70
  const portAnswer = await ask(`HTTP port (default ${httpPort}): `);
52
71
  const parsed = parseInt(portAnswer.trim(), 10);
@@ -72,6 +91,9 @@ export async function initCommand() {
72
91
  console.log(` ${dim("Remote (web):")} ${cyan("https://app.palmier.me")}`);
73
92
  console.log(` Pair a browser on any device. Traffic always goes through the relay.\n`);
74
93
  console.log(` ${dim("Agents:")} ${agents.map((a) => a.version ? `${a.label} v${a.version}` : a.label).join(", ")}\n`);
94
+ if (playwrightCliVersion) {
95
+ console.log(` ${dim("Browser:")} ${PLAYWRIGHT_CLI_LABEL} v${playwrightCliVersion}\n`);
96
+ }
75
97
  const existingTasks = listTasks(projectRoot);
76
98
  if (existingTasks.length > 0) {
77
99
  console.log(` ${dim("Recover tasks:")} ${existingTasks.length} existing task(s) found:`);
@@ -114,6 +136,7 @@ export async function initCommand() {
114
136
  natsJwt: registerResponse.natsJwt,
115
137
  natsNkeySeed: registerResponse.natsNkeySeed,
116
138
  agents,
139
+ ...(playwrightCliVersion ? { playwrightCliVersion } : {}),
117
140
  httpPort,
118
141
  defaultInterface,
119
142
  };
@@ -9,6 +9,7 @@ import { getTaskDir, readTaskStatus, writeTaskStatus, parseTaskFile, appendRunMe
9
9
  import { publishHostEvent } from "../events.js";
10
10
  import { getPlatform } from "../platform/index.js";
11
11
  import { detectAgents } from "../agents/agent.js";
12
+ import { getPlaywrightCliVersion } from "../playwright-cli.js";
12
13
  import { saveConfig } from "../config.js";
13
14
  import { CONFIG_DIR } from "../config.js";
14
15
  import { StringCodec } from "nats";
@@ -102,6 +103,12 @@ export async function serveCommand() {
102
103
  console.log(`Starting Palmier daemon v${currentVersion}...`);
103
104
  const agents = await detectAgents(config.agents);
104
105
  config.agents = agents;
106
+ // Keep the managed Playwright CLI version in sync with manual upgrades, and
107
+ // drop the marker if the package was removed out-of-band. Only touched when
108
+ // Palmier already manages it.
109
+ if (config.playwrightCliVersion) {
110
+ config.playwrightCliVersion = getPlaywrightCliVersion() ?? undefined;
111
+ }
105
112
  saveConfig(config);
106
113
  console.log(`Detected agents: ${agents.map((a) => a.key).join(", ") || "none"}`);
107
114
  let nc;
@@ -1,6 +1,7 @@
1
1
  import { getPlatform } from "../platform/index.js";
2
2
  import { loadConfig } from "../config.js";
3
3
  import { uninstallManagedAgents } from "../agents/wizard.js";
4
+ import { uninstallPlaywrightCli, PLAYWRIGHT_CLI_LABEL } from "../playwright-cli.js";
4
5
  export async function uninstallCommand() {
5
6
  console.log("Stopping daemon and removing scheduled tasks...");
6
7
  const platform = getPlatform();
@@ -14,6 +15,10 @@ export async function uninstallCommand() {
14
15
  if (config?.agents) {
15
16
  uninstallManagedAgents(config.agents);
16
17
  }
18
+ if (config?.playwrightCliVersion) {
19
+ console.log(`\nUninstalling ${PLAYWRIGHT_CLI_LABEL}...`);
20
+ uninstallPlaywrightCli();
21
+ }
17
22
  console.log("\nUninstall finished.");
18
23
  console.log("To remove the palmier package itself: npm uninstall -g palmier");
19
24
  console.log("To also remove configuration and task data, see https://github.com/caihongxu/palmier#uninstalling");
@@ -0,0 +1,15 @@
1
+ /** Palmier-managed browser-automation tool. Managed like an agent CLI (install,
2
+ * version-stamp, re-probe, PWA update prompt) but never offered for explicit
3
+ * uninstall — it's only removed by `palmier uninstall`. */
4
+ export declare const PLAYWRIGHT_CLI_PACKAGE = "@playwright/cli";
5
+ export declare const PLAYWRIGHT_CLI_COMMAND = "playwright-cli";
6
+ export declare const PLAYWRIGHT_CLI_LABEL = "Playwright CLI";
7
+ /** Installed global version, or null when not installed. Doubles as the
8
+ * "is it installed" check (mirrors how agents resolve their version). */
9
+ export declare function getPlaywrightCliVersion(): string | null;
10
+ export declare function installPlaywrightCli(): boolean;
11
+ /** Install the Playwright agent skills so agent CLIs know how to drive the
12
+ * browser. Runs in `cwd` (the Palmier task directory) so the skills land where
13
+ * agents execute. Best-effort: a failure leaves the CLI installed and managed. */
14
+ export declare function installPlaywrightSkills(cwd: string): boolean;
15
+ export declare function uninstallPlaywrightCli(): boolean;
@@ -0,0 +1,32 @@
1
+ import { spawnSync } from "child_process";
2
+ import { getNpmInstalledVersion } from "./agents/agent.js";
3
+ import { npmInstallGlobal, npmUninstallGlobal } from "./agents/wizard.js";
4
+ /** Palmier-managed browser-automation tool. Managed like an agent CLI (install,
5
+ * version-stamp, re-probe, PWA update prompt) but never offered for explicit
6
+ * uninstall — it's only removed by `palmier uninstall`. */
7
+ export const PLAYWRIGHT_CLI_PACKAGE = "@playwright/cli";
8
+ export const PLAYWRIGHT_CLI_COMMAND = "playwright-cli";
9
+ export const PLAYWRIGHT_CLI_LABEL = "Playwright CLI";
10
+ /** Installed global version, or null when not installed. Doubles as the
11
+ * "is it installed" check (mirrors how agents resolve their version). */
12
+ export function getPlaywrightCliVersion() {
13
+ return getNpmInstalledVersion(PLAYWRIGHT_CLI_PACKAGE);
14
+ }
15
+ export function installPlaywrightCli() {
16
+ return npmInstallGlobal(PLAYWRIGHT_CLI_PACKAGE);
17
+ }
18
+ /** Install the Playwright agent skills so agent CLIs know how to drive the
19
+ * browser. Runs in `cwd` (the Palmier task directory) so the skills land where
20
+ * agents execute. Best-effort: a failure leaves the CLI installed and managed. */
21
+ export function installPlaywrightSkills(cwd) {
22
+ console.log(`\nInstalling Playwright skills...\n`);
23
+ const result = spawnSync(`${PLAYWRIGHT_CLI_COMMAND} install --skills`, { cwd, shell: true, stdio: "inherit" });
24
+ if (result.error || result.status !== 0) {
25
+ console.log(`\nCould not install Playwright skills. Run \`${PLAYWRIGHT_CLI_COMMAND} install --skills\` manually.`);
26
+ return false;
27
+ }
28
+ return true;
29
+ }
30
+ export function uninstallPlaywrightCli() {
31
+ return npmUninstallGlobal(PLAYWRIGHT_CLI_PACKAGE);
32
+ }