palmier 0.9.24 → 0.9.26
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 +5 -10
- package/dist/agents/agent.js +12 -1
- package/dist/agents/wizard.js +2 -4
- package/dist/mcp-tools.js +39 -1
- package/dist/pwa/assets/index-CsXEu9PI.js +120 -0
- package/dist/pwa/assets/{web-BcJSY5-D.js → web-BNVl5cy2.js} +1 -1
- package/dist/pwa/assets/{web-BQCHF9J7.js → web-CfKyecWX.js} +1 -1
- package/dist/pwa/assets/{web-CrbgQM5-.js → web-D9PV07-0.js} +1 -1
- package/dist/pwa/index.html +1 -1
- package/package.json +1 -1
- package/dist/pwa/assets/index-Dhvun09d.js +0 -120
package/README.md
CHANGED
|
@@ -11,14 +11,9 @@ Palmier installs, manages, and runs AI agent CLIs (Claude Code, Gemini CLI, Code
|
|
|
11
11
|
The control surface is bidirectional:
|
|
12
12
|
|
|
13
13
|
* **Phone → agents:** start ad-hoc sessions, register schedule- or event-triggered tasks, inspect session output, and respond to agent input/confirmation requests.
|
|
14
|
-
* **Agents → phone:** agents can
|
|
14
|
+
* **Agents → phone:** agents can read device state (location, calendar, contacts, notifications, SMS, battery) and trigger actions (push notifications, full-screen alarms, SMS, email, contact/calendar writes, ringer mode).
|
|
15
15
|
|
|
16
|
-
Capability access is opt-in per device: each
|
|
17
|
-
|
|
18
|
-
It is not:
|
|
19
|
-
|
|
20
|
-
* an agent runtime itself — Palmier shells out to the agent CLI and streams its stdio
|
|
21
|
-
* a system for driving phone UI like a human tapping through apps — phone access is via OS-level APIs (FCM data messages, content providers, calendar/contacts APIs), not UI automation
|
|
16
|
+
Capability access is opt-in per device: each capability is gated behind an Android permission and a per-host toggle. An optional yolo mode auto-approves agent input/confirmation requests.
|
|
22
17
|
|
|
23
18
|
## Quick Start
|
|
24
19
|
|
|
@@ -29,12 +24,12 @@ It is not:
|
|
|
29
24
|
curl -fsSL https://palmier.me/install.sh | bash
|
|
30
25
|
```
|
|
31
26
|
|
|
32
|
-
**Windows
|
|
27
|
+
**Windows:**
|
|
33
28
|
```powershell
|
|
34
|
-
irm https://palmier.me/install.ps1 | iex
|
|
29
|
+
powershell -c "irm https://palmier.me/install.ps1 | iex"
|
|
35
30
|
```
|
|
36
31
|
|
|
37
|
-
The one-liner installs Node.js 24+ if needed
|
|
32
|
+
The one-liner installs Node.js 24+ if needed, installs `palmier` globally, and runs the setup wizard. If you already have Node.js 24+ and npm:
|
|
38
33
|
```bash
|
|
39
34
|
npm install -g palmier && palmier init
|
|
40
35
|
```
|
package/dist/agents/agent.js
CHANGED
|
@@ -73,6 +73,7 @@ const agentRegistry = {
|
|
|
73
73
|
qoder: qoderAgent,
|
|
74
74
|
hermes: hermesAgent,
|
|
75
75
|
};
|
|
76
|
+
const TIER_ONE_ORDER = ["claude", "gemini", "codex", "copilot"];
|
|
76
77
|
export function listInstallableAgents() {
|
|
77
78
|
const out = [];
|
|
78
79
|
for (const [key, agent] of Object.entries(agentRegistry)) {
|
|
@@ -86,7 +87,17 @@ export function listInstallableAgents() {
|
|
|
86
87
|
...(agent.freeUsage ? { freeUsage: agent.freeUsage } : {}),
|
|
87
88
|
});
|
|
88
89
|
}
|
|
89
|
-
return out
|
|
90
|
+
return out.sort((a, b) => {
|
|
91
|
+
const ai = TIER_ONE_ORDER.indexOf(a.key);
|
|
92
|
+
const bi = TIER_ONE_ORDER.indexOf(b.key);
|
|
93
|
+
if (ai !== -1 && bi !== -1)
|
|
94
|
+
return ai - bi;
|
|
95
|
+
if (ai !== -1)
|
|
96
|
+
return -1;
|
|
97
|
+
if (bi !== -1)
|
|
98
|
+
return 1;
|
|
99
|
+
return 0;
|
|
100
|
+
});
|
|
90
101
|
}
|
|
91
102
|
/** Detect agents present on PATH and resolve their version when they are
|
|
92
103
|
* Palmier-managed. An agent is treated as managed if either:
|
package/dist/agents/wizard.js
CHANGED
|
@@ -27,9 +27,7 @@ export function printInstalledAgents(agents) {
|
|
|
27
27
|
* cancelled, no installables remain, or the install failed. */
|
|
28
28
|
export async function pickAndInstallAgent(current, options = {}) {
|
|
29
29
|
const detectedKeys = new Set(current.map((a) => a.key));
|
|
30
|
-
const missing = listInstallableAgents()
|
|
31
|
-
.filter((a) => !detectedKeys.has(a.key))
|
|
32
|
-
.sort((a, b) => a.label.localeCompare(b.label));
|
|
30
|
+
const missing = listInstallableAgents().filter((a) => !detectedKeys.has(a.key));
|
|
33
31
|
if (missing.length === 0) {
|
|
34
32
|
console.log(`\n${dim("All supported agents are already installed.")}`);
|
|
35
33
|
return null;
|
|
@@ -189,7 +187,7 @@ function runAgentAuthFlow(label, command, args) {
|
|
|
189
187
|
console.log(`Re-run ${cyan(cmd)} manually after this.\n`);
|
|
190
188
|
return;
|
|
191
189
|
}
|
|
192
|
-
console.log(green(`
|
|
190
|
+
console.log(green(`Finished authenticating ${label}.\n`));
|
|
193
191
|
}
|
|
194
192
|
async function waitForEnter(message) {
|
|
195
193
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
package/dist/mcp-tools.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
1
2
|
import { StringCodec } from "nats";
|
|
2
3
|
import { registerPending } from "./pending-requests.js";
|
|
3
4
|
import { getLinkedDevice } from "./linked-device.js";
|
|
4
5
|
import { getNotifications, onNotificationsChanged } from "./notification-store.js";
|
|
5
6
|
import { getSmsMessages, onSmsChanged } from "./sms-store.js";
|
|
7
|
+
import { getTaskDir, readHistory, spliceUserMessage } from "./task.js";
|
|
6
8
|
export class ToolError extends Error {
|
|
7
9
|
statusCode;
|
|
8
10
|
constructor(message, statusCode = 500) {
|
|
@@ -84,7 +86,9 @@ const requestInputTool = {
|
|
|
84
86
|
input_questions: questions,
|
|
85
87
|
});
|
|
86
88
|
const response = await pendingPromise;
|
|
87
|
-
|
|
89
|
+
const aborted = response.length === 1 && response[0] === "aborted";
|
|
90
|
+
recordUserInputToRun(ctx, questions, response, aborted);
|
|
91
|
+
if (aborted) {
|
|
88
92
|
await ctx.publishEvent("_input", {
|
|
89
93
|
event_type: "input-resolved", host_id: ctx.config.hostId,
|
|
90
94
|
session_id: ctx.sessionId, status: "aborted",
|
|
@@ -98,6 +102,40 @@ const requestInputTool = {
|
|
|
98
102
|
return { values: response };
|
|
99
103
|
},
|
|
100
104
|
};
|
|
105
|
+
/**
|
|
106
|
+
* Splice the user's answer into the task run file so the conversation history
|
|
107
|
+
* shows what the user typed, independent of whether the agent echoes it.
|
|
108
|
+
* Only writes when sessionId resolves to a task with an active run (i.e. the
|
|
109
|
+
* agent called via the REST endpoint with taskId); silently skips for pure
|
|
110
|
+
* MCP-protocol sessions where sessionId is a random UUID.
|
|
111
|
+
*/
|
|
112
|
+
function recordUserInputToRun(ctx, questions, response, aborted) {
|
|
113
|
+
const taskId = ctx.sessionId;
|
|
114
|
+
if (!taskId)
|
|
115
|
+
return;
|
|
116
|
+
const taskDir = getTaskDir(ctx.config.projectRoot, taskId);
|
|
117
|
+
if (!fs.existsSync(taskDir))
|
|
118
|
+
return;
|
|
119
|
+
const { entries } = readHistory(ctx.config.projectRoot, { task_id: taskId, limit: 1 });
|
|
120
|
+
const runId = entries[0]?.run_id;
|
|
121
|
+
if (!runId)
|
|
122
|
+
return;
|
|
123
|
+
const content = aborted
|
|
124
|
+
? "Cancel"
|
|
125
|
+
: questions.map((q, i) => `**${q}**\n\n${response[i] ?? ""}`).join("\n\n");
|
|
126
|
+
try {
|
|
127
|
+
spliceUserMessage(taskDir, runId, {
|
|
128
|
+
role: "user",
|
|
129
|
+
time: Date.now(),
|
|
130
|
+
content,
|
|
131
|
+
type: "input",
|
|
132
|
+
});
|
|
133
|
+
void ctx.publishEvent(taskId, { event_type: "result-updated", run_id: runId });
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Run file missing or unwritable — best-effort, do not fail the tool call.
|
|
137
|
+
}
|
|
138
|
+
}
|
|
101
139
|
const requestConfirmationTool = {
|
|
102
140
|
name: "request-confirmation",
|
|
103
141
|
description: [
|