palmier 0.9.16 → 0.9.18

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 (53) hide show
  1. package/README.md +1 -1
  2. package/dist/agents/agent.d.ts +38 -14
  3. package/dist/agents/agent.js +102 -38
  4. package/dist/agents/aider.d.ts +2 -9
  5. package/dist/agents/aider.js +8 -20
  6. package/dist/agents/claude.d.ts +2 -9
  7. package/dist/agents/claude.js +11 -20
  8. package/dist/agents/cline.d.ts +2 -9
  9. package/dist/agents/cline.js +9 -20
  10. package/dist/agents/codex.d.ts +2 -9
  11. package/dist/agents/codex.js +11 -20
  12. package/dist/agents/copilot.d.ts +2 -9
  13. package/dist/agents/copilot.js +11 -20
  14. package/dist/agents/cursor.d.ts +2 -9
  15. package/dist/agents/cursor.js +8 -20
  16. package/dist/agents/deepagents.d.ts +2 -9
  17. package/dist/agents/deepagents.js +8 -20
  18. package/dist/agents/droid.d.ts +2 -9
  19. package/dist/agents/droid.js +9 -20
  20. package/dist/agents/gemini.d.ts +2 -9
  21. package/dist/agents/gemini.js +11 -20
  22. package/dist/agents/goose.d.ts +2 -9
  23. package/dist/agents/goose.js +8 -20
  24. package/dist/agents/hermes.d.ts +2 -9
  25. package/dist/agents/hermes.js +8 -20
  26. package/dist/agents/kimi.d.ts +2 -9
  27. package/dist/agents/kimi.js +8 -20
  28. package/dist/agents/kiro.d.ts +2 -9
  29. package/dist/agents/kiro.js +8 -20
  30. package/dist/agents/openclaw.d.ts +2 -9
  31. package/dist/agents/openclaw.js +8 -19
  32. package/dist/agents/opencode.d.ts +2 -9
  33. package/dist/agents/opencode.js +9 -20
  34. package/dist/agents/qoder.d.ts +2 -9
  35. package/dist/agents/qoder.js +9 -20
  36. package/dist/agents/qwen.d.ts +2 -9
  37. package/dist/agents/qwen.js +9 -20
  38. package/dist/commands/init.js +86 -15
  39. package/dist/commands/run.js +3 -2
  40. package/dist/commands/serve.js +1 -1
  41. package/dist/prompts.d.ts +5 -0
  42. package/dist/prompts.js +67 -0
  43. package/dist/pwa/assets/index-DJ-f-zPM.js +120 -0
  44. package/dist/pwa/assets/{web-BUi47bZi.js → web-BM8S3YX9.js} +1 -1
  45. package/dist/pwa/assets/{web-DQOof3g6.js → web-BuyV-_jZ.js} +1 -1
  46. package/dist/pwa/assets/{web-B66LN9cT.js → web-OvMaxdX0.js} +1 -1
  47. package/dist/pwa/index.html +1 -1
  48. package/dist/rpc-handler.js +27 -4
  49. package/dist/types.d.ts +5 -2
  50. package/dist/update-checker.d.ts +2 -0
  51. package/dist/update-checker.js +20 -0
  52. package/package.json +1 -1
  53. package/dist/pwa/assets/index-rt6aPV6d.js +0 -120
@@ -1,10 +1,3 @@
1
- import type { ParsedTask, RequiredPermission } from "../types.js";
2
- import type { AgentTool, CommandLine } from "./agent.js";
1
+ import type { AgentTool } from "./agent.js";
3
2
  export declare function renderPolicyToml(allowedTools: string[]): string;
4
- export declare class GeminiAgent implements AgentTool {
5
- supportsPermissions: boolean;
6
- supportsYolo: boolean;
7
- getPromptCommandLine(prompt: string): CommandLine;
8
- getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine;
9
- init(): Promise<boolean>;
10
- }
3
+ export declare const geminiAgent: AgentTool;
@@ -1,6 +1,4 @@
1
- import { execSync } from "child_process";
2
1
  import { getAgentInstructions } from "./shared-prompt.js";
3
- import { SHELL } from "../platform/index.js";
4
2
  export function renderPolicyToml(allowedTools) {
5
3
  const list = allowedTools.map((t) => JSON.stringify(t)).join(", ");
6
4
  return [
@@ -17,12 +15,14 @@ export function renderPolicyToml(allowedTools) {
17
15
  "",
18
16
  ].join("\n");
19
17
  }
20
- export class GeminiAgent {
21
- supportsPermissions = true;
22
- supportsYolo = true;
23
- getPromptCommandLine(prompt) {
24
- return { command: "gemini", args: ["--prompt", prompt] };
25
- }
18
+ export const geminiAgent = {
19
+ command: "gemini",
20
+ promptCommandLineArgs: ["--prompt"],
21
+ versionCommandLineArg: "--version",
22
+ supportsPermissions: true,
23
+ supportsYolo: true,
24
+ npmPackage: "@google/gemini-cli",
25
+ freeUsage: "Free Tier",
26
26
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
27
27
  const yolo = extraPermissions === "yolo";
28
28
  const prompt = followupPrompt ?? getAgentInstructions(task);
@@ -43,15 +43,6 @@ export class GeminiAgent {
43
43
  }
44
44
  // Read prompt from stdin to avoid command-line length limits.
45
45
  args.push("--prompt", "-");
46
- return { command: "gemini", args, stdin: prompt, ...(files.length > 0 ? { files } : {}) };
47
- }
48
- async init() {
49
- try {
50
- execSync("gemini --version", { stdio: "ignore", shell: SHELL });
51
- }
52
- catch {
53
- return false;
54
- }
55
- return true;
56
- }
57
- }
46
+ return { args, stdin: prompt, ...(files.length > 0 ? { files } : {}) };
47
+ },
48
+ };
@@ -1,9 +1,2 @@
1
- import type { ParsedTask, RequiredPermission } from "../types.js";
2
- import type { AgentTool, CommandLine } from "./agent.js";
3
- export declare class GooseAgent implements AgentTool {
4
- supportsPermissions: boolean;
5
- supportsYolo: boolean;
6
- getPromptCommandLine(prompt: string): CommandLine;
7
- getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine;
8
- init(): Promise<boolean>;
9
- }
1
+ import type { AgentTool } from "./agent.js";
2
+ export declare const gooseAgent: AgentTool;
@@ -1,12 +1,9 @@
1
- import { execSync } from "child_process";
2
1
  import { getAgentInstructions } from "./shared-prompt.js";
3
- import { SHELL } from "../platform/index.js";
4
- export class GooseAgent {
5
- supportsPermissions = false;
6
- supportsYolo = true;
7
- getPromptCommandLine(prompt) {
8
- return { command: "goose", args: ["run", "--text", prompt] };
9
- }
2
+ export const gooseAgent = {
3
+ command: "goose",
4
+ promptCommandLineArgs: ["run", "--text"],
5
+ versionCommandLineArg: "--version",
6
+ supportsYolo: true,
10
7
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
11
8
  const yolo = extraPermissions === "yolo";
12
9
  const prompt = followupPrompt ?? getAgentInstructions(task);
@@ -15,15 +12,6 @@ export class GooseAgent {
15
12
  args.push("--resume");
16
13
  }
17
14
  args.push("--text", prompt);
18
- return { command: "goose", args, ...(yolo ? { env: { GOOSE_MODE: "auto" } } : {}) };
19
- }
20
- async init() {
21
- try {
22
- execSync("goose --version", { stdio: "ignore", shell: SHELL });
23
- }
24
- catch {
25
- return false;
26
- }
27
- return true;
28
- }
29
- }
15
+ return { args, ...(yolo ? { env: { GOOSE_MODE: "auto" } } : {}) };
16
+ },
17
+ };
@@ -1,9 +1,2 @@
1
- import type { ParsedTask, RequiredPermission } from "../types.js";
2
- import type { AgentTool, CommandLine } from "./agent.js";
3
- export declare class Hermes implements AgentTool {
4
- supportsPermissions: boolean;
5
- supportsYolo: boolean;
6
- getPromptCommandLine(prompt: string): CommandLine;
7
- getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine;
8
- init(): Promise<boolean>;
9
- }
1
+ import type { AgentTool } from "./agent.js";
2
+ export declare const hermesAgent: AgentTool;
@@ -1,12 +1,9 @@
1
- import { execSync } from "child_process";
2
1
  import { getAgentInstructions } from "./shared-prompt.js";
3
- import { SHELL } from "../platform/index.js";
4
- export class Hermes {
5
- supportsPermissions = false;
6
- supportsYolo = true;
7
- getPromptCommandLine(prompt) {
8
- return { command: "hermes", args: ["chat", "-q", prompt] };
9
- }
2
+ export const hermesAgent = {
3
+ command: "hermes",
4
+ promptCommandLineArgs: ["chat", "-q"],
5
+ versionCommandLineArg: "--version",
6
+ supportsYolo: true,
10
7
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
11
8
  const yolo = extraPermissions === "yolo";
12
9
  const prompt = followupPrompt ?? getAgentInstructions(task);
@@ -18,15 +15,6 @@ export class Hermes {
18
15
  args.push("--continue");
19
16
  }
20
17
  args.push("-q", prompt);
21
- return { command: "hermes", args };
22
- }
23
- async init() {
24
- try {
25
- execSync("hermes --version", { stdio: "ignore", shell: SHELL });
26
- }
27
- catch {
28
- return false;
29
- }
30
- return true;
31
- }
32
- }
18
+ return { args };
19
+ },
20
+ };
@@ -1,9 +1,2 @@
1
- import type { ParsedTask, RequiredPermission } from "../types.js";
2
- import type { AgentTool, CommandLine } from "./agent.js";
3
- export declare class KimiAgent implements AgentTool {
4
- supportsPermissions: boolean;
5
- supportsYolo: boolean;
6
- getPromptCommandLine(prompt: string): CommandLine;
7
- getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine;
8
- init(): Promise<boolean>;
9
- }
1
+ import type { AgentTool } from "./agent.js";
2
+ export declare const kimiAgent: AgentTool;
@@ -1,12 +1,9 @@
1
- import { execSync } from "child_process";
2
1
  import { getAgentInstructions } from "./shared-prompt.js";
3
- import { SHELL } from "../platform/index.js";
4
- export class KimiAgent {
5
- supportsPermissions = false;
6
- supportsYolo = true;
7
- getPromptCommandLine(prompt) {
8
- return { command: "kimi", args: ["-p", prompt] };
9
- }
2
+ export const kimiAgent = {
3
+ command: "kimi",
4
+ promptCommandLineArgs: ["-p"],
5
+ versionCommandLineArg: "--version",
6
+ supportsYolo: true,
10
7
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
11
8
  const yolo = extraPermissions === "yolo";
12
9
  const prompt = followupPrompt ?? getAgentInstructions(task);
@@ -18,15 +15,6 @@ export class KimiAgent {
18
15
  args.push("--continue");
19
16
  }
20
17
  args.push("-p", prompt);
21
- return { command: "kimi", args };
22
- }
23
- async init() {
24
- try {
25
- execSync("kimi --version", { stdio: "ignore", shell: SHELL });
26
- }
27
- catch {
28
- return false;
29
- }
30
- return true;
31
- }
32
- }
18
+ return { args };
19
+ },
20
+ };
@@ -1,9 +1,2 @@
1
- import type { ParsedTask, RequiredPermission } from "../types.js";
2
- import type { AgentTool, CommandLine } from "./agent.js";
3
- export declare class Kiro implements AgentTool {
4
- supportsPermissions: boolean;
5
- supportsYolo: boolean;
6
- getPromptCommandLine(prompt: string): CommandLine;
7
- getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine;
8
- init(): Promise<boolean>;
9
- }
1
+ import type { AgentTool } from "./agent.js";
2
+ export declare const kiroAgent: AgentTool;
@@ -1,12 +1,9 @@
1
- import { execSync } from "child_process";
2
1
  import { getAgentInstructions } from "./shared-prompt.js";
3
- import { SHELL } from "../platform/index.js";
4
- export class Kiro {
5
- supportsPermissions = false;
6
- supportsYolo = true;
7
- getPromptCommandLine(prompt) {
8
- return { command: "kiro-cli", args: ["--no-interactive", prompt] };
9
- }
2
+ export const kiroAgent = {
3
+ command: "kiro-cli",
4
+ promptCommandLineArgs: ["--no-interactive"],
5
+ versionCommandLineArg: "--version",
6
+ supportsYolo: true,
10
7
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
11
8
  const yolo = extraPermissions === "yolo";
12
9
  const prompt = followupPrompt ?? getAgentInstructions(task);
@@ -18,15 +15,6 @@ export class Kiro {
18
15
  args.push("--resume");
19
16
  }
20
17
  args.push("--no-interactive", prompt);
21
- return { command: "kiro-cli", args };
22
- }
23
- async init() {
24
- try {
25
- execSync("kiro-cli --version", { stdio: "ignore", shell: SHELL });
26
- }
27
- catch {
28
- return false;
29
- }
30
- return true;
31
- }
32
- }
18
+ return { args };
19
+ },
20
+ };
@@ -1,9 +1,2 @@
1
- import type { ParsedTask, RequiredPermission } from "../types.js";
2
- import type { AgentTool, CommandLine } from "./agent.js";
3
- export declare class OpenClawAgent implements AgentTool {
4
- supportsPermissions: boolean;
5
- supportsYolo: boolean;
6
- getPromptCommandLine(prompt: string): CommandLine;
7
- getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine;
8
- init(): Promise<boolean>;
9
- }
1
+ import type { AgentTool } from "./agent.js";
2
+ export declare const openClawAgent: AgentTool;
@@ -1,24 +1,13 @@
1
- import { execSync } from "child_process";
2
1
  import { getAgentInstructions } from "./shared-prompt.js";
3
- export class OpenClawAgent {
4
- supportsPermissions = false;
5
- supportsYolo = false;
6
- getPromptCommandLine(prompt) {
7
- return { command: "openclaw", args: ["agent", "--local", "--agent", "main", "--message", prompt] };
8
- }
2
+ export const openClawAgent = {
3
+ command: "openclaw",
4
+ promptCommandLineArgs: ["agent", "--local", "--agent", "main", "--message"],
5
+ versionCommandLineArg: "--version",
6
+ npmPackage: "openclaw",
9
7
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
10
8
  const prompt = followupPrompt ?? getAgentInstructions(task);
11
9
  // OpenClaw does not support stdin as prompt.
12
10
  const args = ["agent", "--local", "--session-id", task.frontmatter.id, "--message", prompt];
13
- return { command: "openclaw", args };
14
- }
15
- async init() {
16
- try {
17
- execSync("openclaw --version", { stdio: "ignore" });
18
- }
19
- catch {
20
- return false;
21
- }
22
- return true;
23
- }
24
- }
11
+ return { args };
12
+ },
13
+ };
@@ -1,9 +1,2 @@
1
- import type { ParsedTask, RequiredPermission } from "../types.js";
2
- import type { AgentTool, CommandLine } from "./agent.js";
3
- export declare class OpenCodeAgent implements AgentTool {
4
- supportsPermissions: boolean;
5
- supportsYolo: boolean;
6
- getPromptCommandLine(prompt: string): CommandLine;
7
- getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine;
8
- init(): Promise<boolean>;
9
- }
1
+ import type { AgentTool } from "./agent.js";
2
+ export declare const openCodeAgent: AgentTool;
@@ -1,12 +1,10 @@
1
- import { execSync } from "child_process";
2
1
  import { getAgentInstructions } from "./shared-prompt.js";
3
- import { SHELL } from "../platform/index.js";
4
- export class OpenCodeAgent {
5
- supportsPermissions = false;
6
- supportsYolo = true;
7
- getPromptCommandLine(prompt) {
8
- return { command: "opencode", args: ["run", prompt] };
9
- }
2
+ export const openCodeAgent = {
3
+ command: "opencode",
4
+ promptCommandLineArgs: ["run"],
5
+ versionCommandLineArg: "--version",
6
+ supportsYolo: true,
7
+ npmPackage: "opencode-ai",
10
8
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
11
9
  const yolo = extraPermissions === "yolo";
12
10
  const prompt = followupPrompt ?? getAgentInstructions(task);
@@ -18,15 +16,6 @@ export class OpenCodeAgent {
18
16
  args.push("--continue");
19
17
  }
20
18
  args.push(prompt);
21
- return { command: "opencode", args };
22
- }
23
- async init() {
24
- try {
25
- execSync("opencode --version", { stdio: "ignore", shell: SHELL });
26
- }
27
- catch {
28
- return false;
29
- }
30
- return true;
31
- }
32
- }
19
+ return { args };
20
+ },
21
+ };
@@ -1,9 +1,2 @@
1
- import type { ParsedTask, RequiredPermission } from "../types.js";
2
- import type { AgentTool, CommandLine } from "./agent.js";
3
- export declare class Qoder implements AgentTool {
4
- supportsPermissions: boolean;
5
- supportsYolo: boolean;
6
- getPromptCommandLine(prompt: string): CommandLine;
7
- getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine;
8
- init(): Promise<boolean>;
9
- }
1
+ import type { AgentTool } from "./agent.js";
2
+ export declare const qoderAgent: AgentTool;
@@ -1,12 +1,10 @@
1
- import { execSync } from "child_process";
2
1
  import { getAgentInstructions } from "./shared-prompt.js";
3
- import { SHELL } from "../platform/index.js";
4
- export class Qoder {
5
- supportsPermissions = false;
6
- supportsYolo = true;
7
- getPromptCommandLine(prompt) {
8
- return { command: "qodercli", args: ["-p", prompt] };
9
- }
2
+ export const qoderAgent = {
3
+ command: "qodercli",
4
+ promptCommandLineArgs: ["-p"],
5
+ versionCommandLineArg: "--version",
6
+ supportsYolo: true,
7
+ npmPackage: "@qoder-ai/qodercli",
10
8
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
11
9
  const yolo = extraPermissions === "yolo";
12
10
  const prompt = followupPrompt ?? getAgentInstructions(task);
@@ -18,15 +16,6 @@ export class Qoder {
18
16
  args.push("-c");
19
17
  }
20
18
  args.push("-p", prompt);
21
- return { command: "qodercli", args };
22
- }
23
- async init() {
24
- try {
25
- execSync("qodercli --version", { stdio: "ignore", shell: SHELL });
26
- }
27
- catch {
28
- return false;
29
- }
30
- return true;
31
- }
32
- }
19
+ return { args };
20
+ },
21
+ };
@@ -1,9 +1,2 @@
1
- import type { ParsedTask, RequiredPermission } from "../types.js";
2
- import type { AgentTool, CommandLine } from "./agent.js";
3
- export declare class QwenAgent implements AgentTool {
4
- supportsPermissions: boolean;
5
- supportsYolo: boolean;
6
- getPromptCommandLine(prompt: string): CommandLine;
7
- getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine;
8
- init(): Promise<boolean>;
9
- }
1
+ import type { AgentTool } from "./agent.js";
2
+ export declare const qwenAgent: AgentTool;
@@ -1,12 +1,10 @@
1
- import { execSync } from "child_process";
2
1
  import { getAgentInstructions } from "./shared-prompt.js";
3
- import { SHELL } from "../platform/index.js";
4
- export class QwenAgent {
5
- supportsPermissions = false;
6
- supportsYolo = true;
7
- getPromptCommandLine(prompt) {
8
- return { command: "qwen", args: ["-p", prompt] };
9
- }
2
+ export const qwenAgent = {
3
+ command: "qwen",
4
+ promptCommandLineArgs: ["-p"],
5
+ versionCommandLineArg: "--version",
6
+ supportsYolo: true,
7
+ npmPackage: "@qwen-code/qwen-code",
10
8
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
11
9
  const yolo = extraPermissions === "yolo";
12
10
  const prompt = followupPrompt ?? getAgentInstructions(task);
@@ -15,15 +13,6 @@ export class QwenAgent {
15
13
  args.push("-c");
16
14
  }
17
15
  args.push("-p", prompt);
18
- return { command: "qwen", args };
19
- }
20
- async init() {
21
- try {
22
- execSync("qwen --version", { stdio: "ignore", shell: SHELL });
23
- }
24
- catch {
25
- return false;
26
- }
27
- return true;
28
- }
29
- }
16
+ return { args };
17
+ },
18
+ };
@@ -1,32 +1,37 @@
1
1
  import * as readline from "readline";
2
+ import { spawnSync } from "child_process";
2
3
  import { loadConfig, saveConfig } from "../config.js";
3
- import { detectAgents } from "../agents/agent.js";
4
+ import { detectAgents, getNpmInstalledVersion, listInstallableAgents } from "../agents/agent.js";
4
5
  import { getPlatform } from "../platform/index.js";
5
6
  import { pairCommand } from "./pair.js";
6
7
  import { detectDefaultInterface, getInterfaceIpv4 } from "../network.js";
7
8
  import { listTasks } from "../task.js";
9
+ import { selectFromList } from "../prompts.js";
8
10
  const bold = (s) => `\x1b[1m${s}\x1b[0m`;
9
11
  const dim = (s) => `\x1b[2m${s}\x1b[0m`;
10
12
  const green = (s) => `\x1b[32m${s}\x1b[0m`;
11
13
  const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
12
14
  const red = (s) => `\x1b[31m${s}\x1b[0m`;
13
15
  export async function initCommand() {
16
+ console.log(`\n${bold("=== Palmier Host Setup ===")}\n`);
17
+ console.log(`By continuing, you agree to the ${cyan("Terms of Service")} (https://www.palmier.me/terms)`);
18
+ console.log(`and ${cyan("Privacy Policy")} (https://www.palmier.me/privacy).\n`);
19
+ console.log("Detecting installed agents...");
20
+ let agents = await detectAgents();
21
+ if (agents.length > 0) {
22
+ console.log(` Found: ${green(agents.map((a) => a.label).join(", "))}`);
23
+ }
24
+ agents = await offerAgentInstall(agents);
25
+ if (agents.length === 0) {
26
+ console.log(`\n${red("No agent CLIs detected.")} Palmier requires at least one supported agent CLI.\n`);
27
+ console.log(`See supported agents: https://www.palmier.me/agents\n`);
28
+ console.log(`Install at least one agent CLI, then run ${cyan("palmier init")} again.`);
29
+ process.exit(1);
30
+ }
31
+ console.log(`\n Agents: ${green(agents.map((a) => a.label).join(", "))}\n`);
14
32
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
15
33
  const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
16
34
  try {
17
- console.log(`\n${bold("=== Palmier Host Setup ===")}\n`);
18
- console.log(`By continuing, you agree to the ${cyan("Terms of Service")} (https://www.palmier.me/terms)`);
19
- console.log(`and ${cyan("Privacy Policy")} (https://www.palmier.me/privacy).\n`);
20
- console.log("Detecting installed agents...");
21
- const agents = await detectAgents();
22
- if (agents.length === 0) {
23
- console.log(`\n${red("No agent CLIs detected.")} Palmier requires at least one supported agent CLI.\n`);
24
- console.log(`See supported agents: https://www.palmier.me/agents\n`);
25
- console.log(`Install at least one agent CLI, then run ${cyan("palmier init")} again.`);
26
- rl.close();
27
- process.exit(1);
28
- }
29
- console.log(` Found: ${green(agents.map((a) => a.label).join(", "))}\n`);
30
35
  let httpPort = 7256;
31
36
  const portAnswer = await ask(`HTTP port (default ${httpPort}): `);
32
37
  const parsed = parseInt(portAnswer.trim(), 10);
@@ -39,7 +44,7 @@ export async function initCommand() {
39
44
  console.log(` All tasks and execution data will be stored here.\n`);
40
45
  console.log(` ${dim("Local:")} ${cyan(`http://localhost:${httpPort}`)}`);
41
46
  console.log(` Open in a browser on this machine — no internet required.\n`);
42
- console.log(` ${dim("Remote (app):")} ${cyan("https://github.com/caihongxu/palmier-android/releases/latest")}`);
47
+ console.log(` ${dim("Remote (app):")} ${cyan("https://github.com/caihongxu/palmier-android/releases/latest/download/palmier.apk")}`);
43
48
  if (lanIp) {
44
49
  console.log(` Download the Android APK. The app uses LAN for direct RPC`);
45
50
  console.log(` on the same network (detected ${cyan(`http://${lanIp}:${httpPort}`)}),`);
@@ -115,6 +120,72 @@ export async function initCommand() {
115
120
  throw err;
116
121
  }
117
122
  }
123
+ async function offerAgentInstall(currentAgents) {
124
+ let agents = currentAgents;
125
+ while (true) {
126
+ const detectedKeys = new Set(agents.map((a) => a.key));
127
+ const missing = listInstallableAgents()
128
+ .filter((a) => !detectedKeys.has(a.key))
129
+ .sort((a, b) => a.label.localeCompare(b.label));
130
+ if (missing.length === 0)
131
+ return agents;
132
+ const canFinish = agents.length > 0;
133
+ const message = canFinish
134
+ ? `\n${bold("Install additional agents?")} The following supported agents can be installed:`
135
+ : `\n${red("No agent CLIs detected.")} Palmier can install one for you via npm:`;
136
+ const installChoices = missing.map((a) => ({
137
+ label: a.freeUsage ? `${a.label} ${green(`[${a.freeUsage}]`)}` : a.label,
138
+ hint: `npm install -g ${a.npmPackage}`,
139
+ }));
140
+ const choices = canFinish
141
+ ? [{ label: "No — continue setup", hint: "skip installation" }, ...installChoices]
142
+ : installChoices;
143
+ const idx = await selectFromList(message, choices);
144
+ if (idx === null)
145
+ return agents;
146
+ if (canFinish && idx === 0)
147
+ return agents;
148
+ const choice = missing[canFinish ? idx - 1 : idx];
149
+ if (!installAgentPackage(choice))
150
+ return agents;
151
+ console.log(`\nRedetecting agents...`);
152
+ agents = await detectAgents(agents);
153
+ const installedAgent = agents.find((a) => a.key === choice.key);
154
+ if (!installedAgent) {
155
+ console.log(`${red(`${choice.label} still not detected after install.`)} It may not be on PATH yet — open a new terminal and run ${cyan("palmier init")} again.`);
156
+ return agents;
157
+ }
158
+ const version = getNpmInstalledVersion(choice.npmPackage);
159
+ if (version)
160
+ installedAgent.version = version;
161
+ console.log(green(` ${choice.label} installed.`));
162
+ console.log(`\n${bold("Next: authenticate the CLI.")}`);
163
+ console.log(` Run ${cyan(choice.command)} in another terminal and follow the sign-in prompts.`);
164
+ console.log(` Palmier will use the CLI on your behalf once it's signed in.`);
165
+ }
166
+ }
167
+ function installAgentPackage(agent) {
168
+ console.log(`\nInstalling ${cyan(agent.npmPackage)}...\n`);
169
+ const cmd = `npm install -g ${agent.npmPackage}`;
170
+ const result = spawnSync(cmd, { shell: true, stdio: "inherit" });
171
+ if (result.error) {
172
+ console.log(`\n${red(`Failed to run npm: ${result.error.message}`)}`);
173
+ console.log(`Make sure ${cyan("npm")} is on your PATH, then retry.`);
174
+ return false;
175
+ }
176
+ if (result.status !== 0) {
177
+ const exitInfo = result.signal ? `signal ${result.signal}` : `exit ${result.status}`;
178
+ console.log(`\n${red(`${cmd} failed (${exitInfo}).`)}`);
179
+ if (process.platform === "win32") {
180
+ console.log(`If this is a permissions error, try opening a terminal as Administrator and re-running ${cyan("palmier init")}.`);
181
+ }
182
+ else {
183
+ console.log(`If this is a permissions error, try running with ${cyan("sudo")} or fix your global npm prefix.`);
184
+ }
185
+ return false;
186
+ }
187
+ return true;
188
+ }
118
189
  async function registerHost(serverUrl, existingHostId) {
119
190
  try {
120
191
  const res = await fetch(`${serverUrl}/api/hosts/register`, {
@@ -63,7 +63,8 @@ async function invokeAgentWithRetries(ctx, invokeTask) {
63
63
  ensureWriter(stream).write(filtered.join("\n") + "\n");
64
64
  throttledNotify();
65
65
  }
66
- const { command, args, stdin, env: agentEnv, files } = ctx.agent.getTaskRunCommandLine(invokeTask, undefined, ctx.task.frontmatter.yolo_mode ? "yolo" : ctx.transientPermissions);
66
+ const { args, stdin, env: agentEnv, files } = ctx.agent.getTaskRunCommandLine(invokeTask, undefined, ctx.task.frontmatter.yolo_mode ? "yolo" : ctx.transientPermissions);
67
+ const command = ctx.agent.command;
67
68
  const runDir = getRunDir(ctx.taskDir, ctx.runId);
68
69
  if (files) {
69
70
  for (const f of files)
@@ -76,7 +77,7 @@ async function invokeAgentWithRetries(ctx, invokeTask) {
76
77
  resolveOnFailure: true,
77
78
  stdin,
78
79
  onStdout: (chunk) => emit("stdout", chunk),
79
- onStderr: (chunk) => emit("stderr", chunk),
80
+ onStderr: ctx.agent.suppressStdErr ? undefined : (chunk) => emit("stderr", chunk),
80
81
  });
81
82
  if (notifyTimer)
82
83
  clearTimeout(notifyTimer);
@@ -98,7 +98,7 @@ export async function serveCommand() {
98
98
  // PID file lets `palmier restart` find us regardless of how we were started
99
99
  fs.writeFileSync(DAEMON_PID_FILE, String(process.pid), "utf-8");
100
100
  console.log("Starting...");
101
- const agents = await detectAgents();
101
+ const agents = await detectAgents(config.agents);
102
102
  config.agents = agents;
103
103
  saveConfig(config);
104
104
  console.log(`Detected agents: ${agents.map((a) => a.key).join(", ") || "none"}`);
@@ -0,0 +1,5 @@
1
+ export interface SelectChoice {
2
+ label: string;
3
+ hint?: string;
4
+ }
5
+ export declare function selectFromList(message: string, choices: SelectChoice[], footer?: string): Promise<number | null>;