palmier 0.9.19 → 0.9.20
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 +16 -4
- package/dist/agents/agent.d.ts +17 -3
- package/dist/agents/agent.js +17 -27
- package/dist/agents/aider.js +3 -2
- package/dist/agents/claude.js +4 -2
- package/dist/agents/cline.js +3 -2
- package/dist/agents/codex.js +4 -2
- package/dist/agents/copilot.js +4 -2
- package/dist/agents/cursor.js +3 -2
- package/dist/agents/deepagents.js +3 -2
- package/dist/agents/droid.js +3 -2
- package/dist/agents/gemini.js +3 -2
- package/dist/agents/goose.js +3 -2
- package/dist/agents/hermes.js +3 -2
- package/dist/agents/kimi.js +3 -2
- package/dist/agents/kiro.js +3 -2
- package/dist/agents/openclaw.js +3 -2
- package/dist/agents/opencode.js +3 -2
- package/dist/agents/qoder.js +3 -2
- package/dist/agents/qwen.js +4 -2
- package/dist/commands/init.js +85 -39
- package/dist/pwa/assets/{index-C0RDMMZW.css → index-BcEa2BTo.css} +1 -1
- package/dist/pwa/assets/{index-CcZCM6E8.js → index-xF0O5I2z.js} +26 -26
- package/dist/pwa/assets/{web-5pnMoH5p.js → web-DVnCcdDQ.js} +1 -1
- package/dist/pwa/assets/{web-DOqABMBn.js → web-Dnq4poOz.js} +1 -1
- package/dist/pwa/assets/{web-DnbZXe0M.js → web-okZXRyxh.js} +1 -1
- package/dist/pwa/index.html +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ Palmier runs as a background daemon (systemd on Linux, launchd on macOS, Task Sc
|
|
|
67
67
|
|
|
68
68
|
### MCP Server
|
|
69
69
|
|
|
70
|
-
Palmier exposes an [MCP](https://modelcontextprotocol.io) server at `http://localhost:7256/mcp` (streamable HTTP transport). MCP-capable agents can register it to get tool and resource definitions automatically. The same tools and resources are also available
|
|
70
|
+
Palmier exposes an [MCP](https://modelcontextprotocol.io) server at `http://localhost:7256/mcp` (streamable HTTP transport). MCP-capable agents can register it to get tool and resource definitions automatically. The same tools and resources are also readily available if running agent tasks directly from Palmier app, without installing the MCP server.
|
|
71
71
|
|
|
72
72
|
**MCP server URL:** `http://localhost:7256/mcp`
|
|
73
73
|
|
|
@@ -174,19 +174,31 @@ Revoking the linked device also clears the host's linked-device record; device c
|
|
|
174
174
|
### The `init` Command
|
|
175
175
|
|
|
176
176
|
The wizard:
|
|
177
|
-
- Detects installed agent CLIs and caches the result
|
|
177
|
+
- 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
|
|
178
|
+
- 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.
|
|
178
179
|
- Asks for the HTTP port
|
|
179
180
|
- Detects the default network interface (used for auto-LAN)
|
|
180
181
|
- Shows a summary (including any existing scheduled tasks to recover) and asks for confirmation
|
|
181
182
|
- Registers with the Palmier server, saves configuration to `~/.config/palmier/host.json`
|
|
182
|
-
- Installs a background daemon (systemd user service on Linux, LaunchAgent on macOS, Task Scheduler on Windows)
|
|
183
|
+
- Installs a background daemon (systemd user service on Linux, LaunchAgent on macOS, Task Scheduler on Windows). On re-init, also restarts the daemon so the running process picks up the new agents/versions for `host.info`.
|
|
183
184
|
- Auto-enters pair mode to connect your first device
|
|
184
185
|
|
|
185
186
|
The daemon automatically recovers existing tasks by reinstalling their system timers on startup.
|
|
186
187
|
|
|
187
188
|
> **macOS note:** Palmier installs as a user-level LaunchAgent, so it runs without `sudo`. LaunchAgents only run while the user is logged into the GUI session — after a reboot, scheduled tasks stay dormant until you log in at least once. Enable auto-login in System Settings → Users & Groups if you need unattended operation across reboots.
|
|
188
189
|
|
|
189
|
-
Agents are re-detected on every daemon start
|
|
190
|
+
Agents are re-detected on every daemon start; managed-agent versions are re-probed live so upgrades performed outside the wizard show up automatically.
|
|
191
|
+
|
|
192
|
+
### Palmier-managed Agents
|
|
193
|
+
|
|
194
|
+
An agent is considered **Palmier-managed** if it was installed via `palmier init` (or its update is initiated via the PWA). Palmier-managed agents have a known installed version stamped at install/update time; that version is what the PWA uses to drive the agent soft-update dialog and what's recorded into each session's run metadata so a session always shows the agent version it actually ran with — even after the live agent is upgraded.
|
|
195
|
+
|
|
196
|
+
Agents installed by the user outside the wizard (e.g., `npm install -g <pkg>` directly) are detected and usable but are **not** considered Palmier-managed. The PWA shows them under a separate "Version not managed by Palmier" section and does not offer auto-update for them.
|
|
197
|
+
|
|
198
|
+
### Updates
|
|
199
|
+
|
|
200
|
+
- **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.
|
|
201
|
+
- **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.
|
|
190
202
|
|
|
191
203
|
### Re-detecting the LAN Network
|
|
192
204
|
|
package/dist/agents/agent.d.ts
CHANGED
|
@@ -12,12 +12,19 @@ export interface CommandLine {
|
|
|
12
12
|
}>;
|
|
13
13
|
}
|
|
14
14
|
export interface AgentTool {
|
|
15
|
+
/** Human-readable name shown in the PWA and CLI (e.g. "Claude Code"). */
|
|
16
|
+
label: string;
|
|
15
17
|
/** The agent's CLI binary name (e.g. "claude", "kiro-cli"). */
|
|
16
18
|
command: string;
|
|
17
19
|
/** Static args for a short, non-interactive prompt. The prompt is appended to the end of this list. */
|
|
18
|
-
|
|
20
|
+
promptArgs: string[];
|
|
19
21
|
/** Single arg passed to `command` to probe whether the CLI is installed. Usually `"--version"`. */
|
|
20
|
-
|
|
22
|
+
probeArg: string;
|
|
23
|
+
/** Optional args to launch the agent's auth flow after a successful install. When set,
|
|
24
|
+
* `palmier init` runs `<command> <args...>` interactively (stdio: "inherit") so the user
|
|
25
|
+
* can sign in before configuration continues. Leave undefined for agents that auth on
|
|
26
|
+
* first run with no separate command. */
|
|
27
|
+
authArgs?: string[];
|
|
21
28
|
/** Whether this agent supports permission overrides (e.g. --allowedTools).
|
|
22
29
|
* When falsy, the permissions section is omitted from agent instructions. */
|
|
23
30
|
supportsPermissions?: boolean;
|
|
@@ -59,5 +66,12 @@ export interface InstallableAgent {
|
|
|
59
66
|
freeUsage?: string;
|
|
60
67
|
}
|
|
61
68
|
export declare function listInstallableAgents(): InstallableAgent[];
|
|
62
|
-
|
|
69
|
+
/** Detect agents present on PATH and resolve their version when they are
|
|
70
|
+
* Palmier-managed. An agent is treated as managed if either:
|
|
71
|
+
* - it had a `version` in the `previous` list (preserved across daemon restarts), or
|
|
72
|
+
* - its key is in `newlyInstalled` (e.g. just installed by the wizard this session).
|
|
73
|
+
*
|
|
74
|
+
* Every managed agent has its version probed live via `npm ls -g`, so manual
|
|
75
|
+
* upgrades outside Palmier are picked up on the next detection. */
|
|
76
|
+
export declare function detectAgents(previous?: DetectedAgent[], newlyInstalled?: Set<string>): Promise<DetectedAgent[]>;
|
|
63
77
|
export declare function getAgent(name: string): AgentTool;
|
package/dist/agents/agent.js
CHANGED
|
@@ -18,10 +18,10 @@ import { clineAgent } from "./cline.js";
|
|
|
18
18
|
import { qoderAgent } from "./qoder.js";
|
|
19
19
|
import { hermesAgent } from "./hermes.js";
|
|
20
20
|
export function getPromptCommandLine(agent, prompt) {
|
|
21
|
-
return { args: [...agent.
|
|
21
|
+
return { args: [...agent.promptArgs, prompt] };
|
|
22
22
|
}
|
|
23
23
|
export async function probeAgent(agent) {
|
|
24
|
-
const probe = `${agent.command} ${agent.
|
|
24
|
+
const probe = `${agent.command} ${agent.probeArg}`;
|
|
25
25
|
try {
|
|
26
26
|
execSync(probe, { stdio: "ignore", shell: SHELL });
|
|
27
27
|
}
|
|
@@ -73,25 +73,6 @@ const agentRegistry = {
|
|
|
73
73
|
qoder: qoderAgent,
|
|
74
74
|
hermes: hermesAgent,
|
|
75
75
|
};
|
|
76
|
-
const agentLabels = {
|
|
77
|
-
claude: "Claude Code",
|
|
78
|
-
gemini: "Gemini CLI",
|
|
79
|
-
codex: "Codex CLI",
|
|
80
|
-
droid: "Droid CLI",
|
|
81
|
-
openclaw: "OpenClaw",
|
|
82
|
-
copilot: "Copilot CLI",
|
|
83
|
-
qwen: "Qwen Code",
|
|
84
|
-
kimi: "Kimi Code",
|
|
85
|
-
goose: "Goose CLI",
|
|
86
|
-
opencode: "OpenCode",
|
|
87
|
-
deepagents: "Deep Agents CLI",
|
|
88
|
-
aider: "Aider",
|
|
89
|
-
cursor: "Cursor CLI",
|
|
90
|
-
kiro: "Kiro CLI",
|
|
91
|
-
cline: "Cline CLI",
|
|
92
|
-
qoder: "Qoder CLI",
|
|
93
|
-
hermes: "Hermes Agent",
|
|
94
|
-
};
|
|
95
76
|
export function listInstallableAgents() {
|
|
96
77
|
const out = [];
|
|
97
78
|
for (const [key, agent] of Object.entries(agentRegistry)) {
|
|
@@ -99,7 +80,7 @@ export function listInstallableAgents() {
|
|
|
99
80
|
continue;
|
|
100
81
|
out.push({
|
|
101
82
|
key,
|
|
102
|
-
label:
|
|
83
|
+
label: agent.label,
|
|
103
84
|
npmPackage: agent.npmPackage,
|
|
104
85
|
command: agent.command,
|
|
105
86
|
...(agent.freeUsage ? { freeUsage: agent.freeUsage } : {}),
|
|
@@ -107,22 +88,31 @@ export function listInstallableAgents() {
|
|
|
107
88
|
}
|
|
108
89
|
return out;
|
|
109
90
|
}
|
|
110
|
-
|
|
91
|
+
/** Detect agents present on PATH and resolve their version when they are
|
|
92
|
+
* Palmier-managed. An agent is treated as managed if either:
|
|
93
|
+
* - it had a `version` in the `previous` list (preserved across daemon restarts), or
|
|
94
|
+
* - its key is in `newlyInstalled` (e.g. just installed by the wizard this session).
|
|
95
|
+
*
|
|
96
|
+
* Every managed agent has its version probed live via `npm ls -g`, so manual
|
|
97
|
+
* upgrades outside Palmier are picked up on the next detection. */
|
|
98
|
+
export async function detectAgents(previous, newlyInstalled) {
|
|
111
99
|
const previousByKey = new Map((previous ?? []).map((a) => [a.key, a]));
|
|
112
100
|
const detected = [];
|
|
113
101
|
for (const [key, agent] of Object.entries(agentRegistry)) {
|
|
114
|
-
const label = agentLabels[key] ?? key;
|
|
115
102
|
const ok = await probeAgent(agent);
|
|
116
103
|
if (!ok)
|
|
117
104
|
continue;
|
|
118
|
-
const
|
|
105
|
+
const wasManaged = !!previousByKey.get(key)?.version || (newlyInstalled?.has(key) ?? false);
|
|
106
|
+
const version = wasManaged && agent.npmPackage
|
|
107
|
+
? getNpmInstalledVersion(agent.npmPackage) ?? undefined
|
|
108
|
+
: undefined;
|
|
119
109
|
detected.push({
|
|
120
110
|
key,
|
|
121
|
-
label,
|
|
111
|
+
label: agent.label,
|
|
122
112
|
supportsPermissions: agent.supportsPermissions,
|
|
123
113
|
supportsYolo: agent.supportsYolo,
|
|
124
114
|
...(agent.npmPackage ? { npmPackage: agent.npmPackage } : {}),
|
|
125
|
-
...(
|
|
115
|
+
...(version ? { version } : {}),
|
|
126
116
|
});
|
|
127
117
|
}
|
|
128
118
|
return detected;
|
package/dist/agents/aider.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const aiderAgent = {
|
|
3
|
+
label: "Aider",
|
|
3
4
|
command: "aider",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["--message"],
|
|
6
|
+
probeArg: "--version",
|
|
6
7
|
supportsYolo: true,
|
|
7
8
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
|
8
9
|
const yolo = extraPermissions === "yolo";
|
package/dist/agents/claude.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const claudeAgent = {
|
|
3
|
+
label: "Claude Code",
|
|
3
4
|
command: "claude",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["-p"],
|
|
6
|
+
probeArg: "--version",
|
|
7
|
+
authArgs: ["auth", "login"],
|
|
6
8
|
supportsPermissions: true,
|
|
7
9
|
supportsYolo: true,
|
|
8
10
|
npmPackage: "@anthropic-ai/claude-code",
|
package/dist/agents/cline.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const clineAgent = {
|
|
3
|
+
label: "Cline CLI",
|
|
3
4
|
command: "cline",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["--yolo", "-p"],
|
|
6
|
+
probeArg: "--version",
|
|
6
7
|
supportsYolo: true,
|
|
7
8
|
npmPackage: "cline",
|
|
8
9
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
package/dist/agents/codex.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const codexAgent = {
|
|
3
|
+
label: "Codex CLI",
|
|
3
4
|
command: "codex",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["exec", "--skip-git-repo-check"],
|
|
6
|
+
probeArg: "--version",
|
|
7
|
+
authArgs: ["login"],
|
|
6
8
|
supportsYolo: true,
|
|
7
9
|
suppressStdErr: true,
|
|
8
10
|
npmPackage: "@openai/codex",
|
package/dist/agents/copilot.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const copilotAgent = {
|
|
3
|
+
label: "Copilot CLI",
|
|
3
4
|
command: "copilot",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["-p"],
|
|
6
|
+
probeArg: "-v",
|
|
7
|
+
authArgs: ["login"],
|
|
6
8
|
supportsYolo: true,
|
|
7
9
|
suppressStdErr: true,
|
|
8
10
|
npmPackage: "@github/copilot",
|
package/dist/agents/cursor.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const cursorAgent = {
|
|
3
|
+
label: "Cursor CLI",
|
|
3
4
|
command: "cursor",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["-p"],
|
|
6
|
+
probeArg: "--version",
|
|
6
7
|
supportsYolo: true,
|
|
7
8
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
|
8
9
|
const yolo = extraPermissions === "yolo";
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const deepAgentsAgent = {
|
|
3
|
+
label: "Deep Agents CLI",
|
|
3
4
|
command: "deepagents",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["--non-interactive"],
|
|
6
|
+
probeArg: "--version",
|
|
6
7
|
supportsYolo: true,
|
|
7
8
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
|
8
9
|
const yolo = extraPermissions === "yolo";
|
package/dist/agents/droid.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const droidAgent = {
|
|
3
|
+
label: "Droid CLI",
|
|
3
4
|
command: "droid",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["exec"],
|
|
6
|
+
probeArg: "--version",
|
|
6
7
|
supportsYolo: true,
|
|
7
8
|
npmPackage: "@factory/cli",
|
|
8
9
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
package/dist/agents/gemini.js
CHANGED
|
@@ -16,9 +16,10 @@ export function renderPolicyToml(allowedTools) {
|
|
|
16
16
|
].join("\n");
|
|
17
17
|
}
|
|
18
18
|
export const geminiAgent = {
|
|
19
|
+
label: "Gemini CLI",
|
|
19
20
|
command: "gemini",
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
promptArgs: ["--prompt"],
|
|
22
|
+
probeArg: "--version",
|
|
22
23
|
supportsPermissions: true,
|
|
23
24
|
supportsYolo: true,
|
|
24
25
|
npmPackage: "@google/gemini-cli",
|
package/dist/agents/goose.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const gooseAgent = {
|
|
3
|
+
label: "Goose CLI",
|
|
3
4
|
command: "goose",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["run", "--text"],
|
|
6
|
+
probeArg: "--version",
|
|
6
7
|
supportsYolo: true,
|
|
7
8
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
|
8
9
|
const yolo = extraPermissions === "yolo";
|
package/dist/agents/hermes.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const hermesAgent = {
|
|
3
|
+
label: "Hermes Agent",
|
|
3
4
|
command: "hermes",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["chat", "-q"],
|
|
6
|
+
probeArg: "--version",
|
|
6
7
|
supportsYolo: true,
|
|
7
8
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
|
8
9
|
const yolo = extraPermissions === "yolo";
|
package/dist/agents/kimi.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const kimiAgent = {
|
|
3
|
+
label: "Kimi Code",
|
|
3
4
|
command: "kimi",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["-p"],
|
|
6
|
+
probeArg: "--version",
|
|
6
7
|
supportsYolo: true,
|
|
7
8
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
|
8
9
|
const yolo = extraPermissions === "yolo";
|
package/dist/agents/kiro.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const kiroAgent = {
|
|
3
|
+
label: "Kiro CLI",
|
|
3
4
|
command: "kiro-cli",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["--no-interactive"],
|
|
6
|
+
probeArg: "--version",
|
|
6
7
|
supportsYolo: true,
|
|
7
8
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
|
8
9
|
const yolo = extraPermissions === "yolo";
|
package/dist/agents/openclaw.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const openClawAgent = {
|
|
3
|
+
label: "OpenClaw",
|
|
3
4
|
command: "openclaw",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["agent", "--local", "--agent", "main", "--message"],
|
|
6
|
+
probeArg: "--version",
|
|
6
7
|
npmPackage: "openclaw",
|
|
7
8
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
|
8
9
|
const prompt = followupPrompt ?? getAgentInstructions(task);
|
package/dist/agents/opencode.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const openCodeAgent = {
|
|
3
|
+
label: "OpenCode",
|
|
3
4
|
command: "opencode",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["run"],
|
|
6
|
+
probeArg: "--version",
|
|
6
7
|
supportsYolo: true,
|
|
7
8
|
npmPackage: "opencode-ai",
|
|
8
9
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
package/dist/agents/qoder.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const qoderAgent = {
|
|
3
|
+
label: "Qoder CLI",
|
|
3
4
|
command: "qodercli",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["-p"],
|
|
6
|
+
probeArg: "--version",
|
|
6
7
|
supportsYolo: true,
|
|
7
8
|
npmPackage: "@qoder-ai/qodercli",
|
|
8
9
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
package/dist/agents/qwen.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { getAgentInstructions } from "./shared-prompt.js";
|
|
2
2
|
export const qwenAgent = {
|
|
3
|
+
label: "Qwen Code",
|
|
3
4
|
command: "qwen",
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
promptArgs: ["-p"],
|
|
6
|
+
probeArg: "--version",
|
|
7
|
+
authArgs: ["auth", "qwen-oauth"],
|
|
6
8
|
supportsYolo: true,
|
|
7
9
|
npmPackage: "@qwen-code/qwen-code",
|
|
8
10
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
package/dist/commands/init.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as readline from "readline";
|
|
2
2
|
import { spawnSync } from "child_process";
|
|
3
3
|
import { loadConfig, saveConfig } from "../config.js";
|
|
4
|
-
import { detectAgents, getNpmInstalledVersion, listInstallableAgents } from "../agents/agent.js";
|
|
4
|
+
import { detectAgents, getAgent, getNpmInstalledVersion, listInstallableAgents } from "../agents/agent.js";
|
|
5
5
|
import { getPlatform } from "../platform/index.js";
|
|
6
6
|
import { pairCommand } from "./pair.js";
|
|
7
7
|
import { detectDefaultInterface, getInterfaceIpv4 } from "../network.js";
|
|
@@ -17,18 +17,25 @@ export async function initCommand() {
|
|
|
17
17
|
console.log(`By continuing, you agree to the ${cyan("Terms of Service")} (https://www.palmier.me/terms)`);
|
|
18
18
|
console.log(`and ${cyan("Privacy Policy")} (https://www.palmier.me/privacy).\n`);
|
|
19
19
|
console.log("Detecting installed agents...");
|
|
20
|
-
let
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
let previousConfig = null;
|
|
21
|
+
try {
|
|
22
|
+
previousConfig = loadConfig();
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
catch { /* first init */ }
|
|
25
|
+
let agents = await detectAgents(previousConfig?.agents);
|
|
26
|
+
logDetectedAgents(agents);
|
|
27
|
+
await offerAgentInstall(agents, () => {
|
|
28
|
+
if (previousConfig) {
|
|
29
|
+
previousConfig.agents = agents;
|
|
30
|
+
saveConfig(previousConfig);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
25
33
|
if (agents.length === 0) {
|
|
26
34
|
console.log(`\n${red("No agent CLIs detected.")} Palmier requires at least one supported agent CLI.\n`);
|
|
27
35
|
console.log(`See supported agents: https://www.palmier.me/agents\n`);
|
|
28
36
|
console.log(`Install at least one agent CLI, then run ${cyan("palmier init")} again.`);
|
|
29
37
|
process.exit(1);
|
|
30
38
|
}
|
|
31
|
-
console.log(`\n Agents: ${green(agents.map((a) => a.label).join(", "))}\n`);
|
|
32
39
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
33
40
|
const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
|
|
34
41
|
try {
|
|
@@ -56,7 +63,7 @@ export async function initCommand() {
|
|
|
56
63
|
}
|
|
57
64
|
console.log(` ${dim("Remote (web):")} ${cyan("https://app.palmier.me")}`);
|
|
58
65
|
console.log(` Pair a browser on any device. Traffic always goes through the relay.\n`);
|
|
59
|
-
console.log(` ${dim("Agents:")} ${agents.map((a) => a.label).join(", ")}\n`);
|
|
66
|
+
console.log(` ${dim("Agents:")} ${agents.map((a) => a.version ? `${a.label} v${a.version}` : a.label).join(", ")}\n`);
|
|
60
67
|
const existingTasks = listTasks(process.cwd());
|
|
61
68
|
if (existingTasks.length > 0) {
|
|
62
69
|
console.log(` ${dim("Recover tasks:")} ${existingTasks.length} existing task(s) found:`);
|
|
@@ -71,11 +78,7 @@ export async function initCommand() {
|
|
|
71
78
|
rl.close();
|
|
72
79
|
return;
|
|
73
80
|
}
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
existingHostId = loadConfig().hostId;
|
|
77
|
-
}
|
|
78
|
-
catch { /* first init */ }
|
|
81
|
+
const existingHostId = previousConfig?.hostId;
|
|
79
82
|
const serverUrl = "https://app.palmier.me";
|
|
80
83
|
let registerResponse;
|
|
81
84
|
while (true) {
|
|
@@ -108,7 +111,13 @@ export async function initCommand() {
|
|
|
108
111
|
};
|
|
109
112
|
saveConfig(config);
|
|
110
113
|
console.log(`Config saved to ${dim("~/.config/palmier/host.json")}`);
|
|
111
|
-
getPlatform()
|
|
114
|
+
const platform = getPlatform();
|
|
115
|
+
platform.installDaemon(config);
|
|
116
|
+
if (previousConfig) {
|
|
117
|
+
// Re-init: a daemon is already running with stale in-memory config.
|
|
118
|
+
// Restart so it picks up the new agents/versions for host.info.
|
|
119
|
+
await platform.restartDaemon();
|
|
120
|
+
}
|
|
112
121
|
// Task recovery runs in the daemon (palmier serve) because that process
|
|
113
122
|
// is elevated and can create S4U scheduled tasks.
|
|
114
123
|
console.log("\nStarting pairing...");
|
|
@@ -120,49 +129,86 @@ export async function initCommand() {
|
|
|
120
129
|
throw err;
|
|
121
130
|
}
|
|
122
131
|
}
|
|
123
|
-
async function offerAgentInstall(
|
|
124
|
-
let agents = currentAgents;
|
|
132
|
+
async function offerAgentInstall(agents, onAgentInstalled) {
|
|
125
133
|
while (true) {
|
|
126
134
|
const detectedKeys = new Set(agents.map((a) => a.key));
|
|
127
135
|
const missing = listInstallableAgents()
|
|
128
136
|
.filter((a) => !detectedKeys.has(a.key))
|
|
129
137
|
.sort((a, b) => a.label.localeCompare(b.label));
|
|
130
138
|
if (missing.length === 0)
|
|
131
|
-
return
|
|
132
|
-
const
|
|
133
|
-
const message =
|
|
139
|
+
return;
|
|
140
|
+
const hasAgents = agents.length > 0;
|
|
141
|
+
const message = hasAgents
|
|
134
142
|
? `\n${bold("Install additional agents?")} The following supported agents can be installed:`
|
|
135
143
|
: `\n${red("No agent CLIs detected.")} Palmier can install one for you via npm:`;
|
|
136
144
|
const installChoices = missing.map((a) => ({
|
|
137
145
|
label: a.freeUsage ? `${a.label} ${green(`[${a.freeUsage}]`)}` : a.label,
|
|
138
|
-
hint:
|
|
146
|
+
hint: `${a.npmPackage}`,
|
|
139
147
|
}));
|
|
140
|
-
const choices =
|
|
141
|
-
? [{ label: "No — continue
|
|
148
|
+
const choices = hasAgents
|
|
149
|
+
? [{ label: "No — continue to the next step ", hint: "skip installation" }, ...installChoices]
|
|
142
150
|
: installChoices;
|
|
143
151
|
const idx = await selectFromList(message, choices);
|
|
144
152
|
if (idx === null)
|
|
145
|
-
return
|
|
146
|
-
if (
|
|
147
|
-
return
|
|
148
|
-
const choice = missing[
|
|
153
|
+
return;
|
|
154
|
+
if (hasAgents && idx === 0)
|
|
155
|
+
return;
|
|
156
|
+
const choice = missing[hasAgents ? idx - 1 : idx];
|
|
149
157
|
if (!installAgentPackage(choice))
|
|
150
|
-
return
|
|
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;
|
|
158
|
+
return;
|
|
161
159
|
console.log(green(` ${choice.label} installed.`));
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
160
|
+
// Stamp the agent record with version *before* auth so that an interrupted
|
|
161
|
+
// wizard (Ctrl+C during sign-in, etc.) still leaves the agent recorded as
|
|
162
|
+
// Palmier-managed in the persisted config on next run.
|
|
163
|
+
const tool = getAgent(choice.key);
|
|
164
|
+
const version = getNpmInstalledVersion(choice.npmPackage) ?? undefined;
|
|
165
|
+
agents.push({
|
|
166
|
+
key: choice.key,
|
|
167
|
+
label: choice.label,
|
|
168
|
+
...(tool.supportsPermissions ? { supportsPermissions: true } : {}),
|
|
169
|
+
...(tool.supportsYolo ? { supportsYolo: true } : {}),
|
|
170
|
+
npmPackage: choice.npmPackage,
|
|
171
|
+
...(version ? { version } : {}),
|
|
172
|
+
});
|
|
173
|
+
onAgentInstalled?.();
|
|
174
|
+
if (tool.authArgs && tool.authArgs.length > 0) {
|
|
175
|
+
runAgentAuthFlow(choice.label, tool.command, tool.authArgs);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
console.log(`\n${bold("Next: authenticate the CLI.")}`);
|
|
179
|
+
console.log(` Run ${cyan(choice.command)} in another terminal and follow the sign-in prompts.`);
|
|
180
|
+
console.log(` Palmier will use the CLI on your behalf once it's signed in.`);
|
|
181
|
+
}
|
|
182
|
+
await waitForEnter("Press Enter once authentication is complete...");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async function waitForEnter(message) {
|
|
186
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
187
|
+
await new Promise((resolve) => rl.question(`\n${dim(message)} `, resolve));
|
|
188
|
+
rl.close();
|
|
189
|
+
}
|
|
190
|
+
function logDetectedAgents(agents) {
|
|
191
|
+
if (agents.length === 0)
|
|
192
|
+
return;
|
|
193
|
+
console.log(` Found: ${green(agents.map((a) => a.version ? `${a.label} v${a.version}` : a.label).join(", "))}`);
|
|
194
|
+
}
|
|
195
|
+
function runAgentAuthFlow(label, command, args) {
|
|
196
|
+
const cmd = `${command} ${args.join(" ")}`;
|
|
197
|
+
console.log(`\n${bold(`Authenticating ${label}...`)} ${dim(`(${cmd})`)}\n`);
|
|
198
|
+
const result = spawnSync(cmd, { shell: true, stdio: "inherit" });
|
|
199
|
+
console.log("");
|
|
200
|
+
if (result.error) {
|
|
201
|
+
console.log(red(`Auth failed: could not run ${cmd} — ${result.error.message}`));
|
|
202
|
+
console.log(`Re-run ${cyan(cmd)} manually after init finishes.\n`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (result.status !== 0) {
|
|
206
|
+
const exitInfo = result.signal ? `signal ${result.signal}` : `exit ${result.status}`;
|
|
207
|
+
console.log(red(`Auth failed (${exitInfo}).`));
|
|
208
|
+
console.log(`Re-run ${cyan(cmd)} manually after init finishes.\n`);
|
|
209
|
+
return;
|
|
165
210
|
}
|
|
211
|
+
console.log(green(`Successfully authenticated ${label}.\n`));
|
|
166
212
|
}
|
|
167
213
|
function installAgentPackage(agent) {
|
|
168
214
|
console.log(`\nInstalling ${cyan(agent.npmPackage)}...\n`);
|