palmier 0.8.11 → 0.9.2
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 +3 -1
- package/dist/linked-device.d.ts +9 -0
- package/dist/linked-device.js +45 -0
- package/dist/mcp-tools.js +19 -19
- package/dist/pwa/assets/index-BLCVzS_l.js +120 -0
- package/dist/pwa/assets/{index-DhphickB.css → index-Cjjw24Ok.css} +1 -1
- package/dist/pwa/assets/{web-4WNPL7z3.js → web-C2AU9S9n.js} +1 -1
- package/dist/pwa/assets/{web-DjwsAB0V.js → web-CfD_ah7K.js} +1 -1
- package/dist/pwa/assets/{web-Bpd2nO1M.js → web-DugGj1t8.js} +1 -1
- package/dist/pwa/index.html +2 -2
- package/dist/pwa/service-worker.js +2 -2
- package/dist/rpc-handler.js +17 -23
- package/package.json +1 -1
- package/palmier-server/README.md +2 -1
- package/palmier-server/pwa/src/App.css +37 -0
- package/palmier-server/pwa/src/App.tsx +36 -15
- package/palmier-server/pwa/src/components/CapabilityToggles.tsx +65 -225
- package/palmier-server/pwa/src/components/HostMenu.tsx +110 -21
- package/palmier-server/pwa/src/components/RunDetailView.tsx +2 -2
- package/palmier-server/pwa/src/components/SessionComposer.tsx +9 -8
- package/palmier-server/pwa/src/components/SessionsView.tsx +5 -3
- package/palmier-server/pwa/src/components/TabBar.tsx +7 -5
- package/palmier-server/pwa/src/components/TaskForm.tsx +5 -3
- package/palmier-server/pwa/src/constants.ts +1 -1
- package/palmier-server/pwa/src/contexts/HostConnectionContext.tsx +41 -41
- package/palmier-server/pwa/src/contexts/HostStoreContext.tsx +17 -60
- package/palmier-server/pwa/src/hooks/usePushSubscription.ts +6 -7
- package/palmier-server/pwa/src/native/Device.ts +23 -38
- package/palmier-server/pwa/src/pages/Dashboard.tsx +36 -41
- package/palmier-server/pwa/src/pages/PairHost.tsx +20 -1
- package/palmier-server/pwa/src/pages/PairSetup.tsx +98 -39
- package/palmier-server/pwa/src/service-worker.ts +9 -6
- package/palmier-server/pwa/src/types.ts +2 -0
- package/palmier-server/spec.md +37 -11
- package/src/linked-device.ts +52 -0
- package/src/mcp-tools.ts +19 -19
- package/src/rpc-handler.ts +14 -22
- package/dist/device-capabilities.d.ts +0 -9
- package/dist/device-capabilities.js +0 -36
- package/dist/pwa/assets/index-B7S0YoMo.js +0 -120
- package/src/device-capabilities.ts +0 -57
package/src/mcp-tools.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StringCodec, type NatsConnection } from "nats";
|
|
2
2
|
import { registerPending } from "./pending-requests.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getLinkedDevice } from "./linked-device.js";
|
|
4
4
|
import { getNotifications, onNotificationsChanged } from "./notification-store.js";
|
|
5
5
|
import { getSmsMessages, onSmsChanged } from "./sms-store.js";
|
|
6
6
|
import type { HostConfig } from "./types.js";
|
|
@@ -179,8 +179,8 @@ const deviceGeolocationTool: ToolDefinition = {
|
|
|
179
179
|
async handler(_args, ctx) {
|
|
180
180
|
if (!ctx.nc) throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
181
181
|
|
|
182
|
-
const device =
|
|
183
|
-
if (!device) throw new ToolError("No device
|
|
182
|
+
const device = getLinkedDevice();
|
|
183
|
+
if (!device) throw new ToolError("No linked device configured", 400);
|
|
184
184
|
|
|
185
185
|
const sc = StringCodec();
|
|
186
186
|
|
|
@@ -227,8 +227,8 @@ const readContactsTool: ToolDefinition = {
|
|
|
227
227
|
async handler(_args, ctx) {
|
|
228
228
|
if (!ctx.nc) throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
229
229
|
|
|
230
|
-
const device =
|
|
231
|
-
if (!device) throw new ToolError("No device
|
|
230
|
+
const device = getLinkedDevice();
|
|
231
|
+
if (!device) throw new ToolError("No linked device configured", 400);
|
|
232
232
|
|
|
233
233
|
const sc = StringCodec();
|
|
234
234
|
|
|
@@ -280,8 +280,8 @@ const createContactTool: ToolDefinition = {
|
|
|
280
280
|
async handler(args, ctx) {
|
|
281
281
|
if (!ctx.nc) throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
282
282
|
|
|
283
|
-
const device =
|
|
284
|
-
if (!device) throw new ToolError("No device
|
|
283
|
+
const device = getLinkedDevice();
|
|
284
|
+
if (!device) throw new ToolError("No linked device configured", 400);
|
|
285
285
|
|
|
286
286
|
const { name, phone, email } = args as { name: string; phone?: string; email?: string };
|
|
287
287
|
if (!name) throw new ToolError("name is required", 400);
|
|
@@ -338,8 +338,8 @@ const readCalendarTool: ToolDefinition = {
|
|
|
338
338
|
async handler(args, ctx) {
|
|
339
339
|
if (!ctx.nc) throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
340
340
|
|
|
341
|
-
const device =
|
|
342
|
-
if (!device) throw new ToolError("No device
|
|
341
|
+
const device = getLinkedDevice();
|
|
342
|
+
if (!device) throw new ToolError("No linked device configured", 400);
|
|
343
343
|
|
|
344
344
|
const { startDate, endDate } = args as { startDate?: number; endDate?: number };
|
|
345
345
|
const sc = StringCodec();
|
|
@@ -399,8 +399,8 @@ const createCalendarEventTool: ToolDefinition = {
|
|
|
399
399
|
async handler(args, ctx) {
|
|
400
400
|
if (!ctx.nc) throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
401
401
|
|
|
402
|
-
const device =
|
|
403
|
-
if (!device) throw new ToolError("No device
|
|
402
|
+
const device = getLinkedDevice();
|
|
403
|
+
if (!device) throw new ToolError("No linked device configured", 400);
|
|
404
404
|
|
|
405
405
|
const { title, startTime, endTime, location, description } = args as {
|
|
406
406
|
title: string; startTime: number; endTime: number; location?: string; description?: string;
|
|
@@ -462,7 +462,7 @@ const sendSmsTool: ToolDefinition = {
|
|
|
462
462
|
async handler(args, ctx) {
|
|
463
463
|
if (!ctx.nc) throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
464
464
|
|
|
465
|
-
const device =
|
|
465
|
+
const device = getLinkedDevice();
|
|
466
466
|
if (!device) throw new ToolError("No device has SMS Send enabled", 400);
|
|
467
467
|
|
|
468
468
|
const { to, body } = args as { to: string; body: string };
|
|
@@ -521,8 +521,8 @@ const sendAlarmTool: ToolDefinition = {
|
|
|
521
521
|
async handler(args, ctx) {
|
|
522
522
|
if (!ctx.nc) throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
523
523
|
|
|
524
|
-
const device =
|
|
525
|
-
if (!device) throw new ToolError("No device
|
|
524
|
+
const device = getLinkedDevice();
|
|
525
|
+
if (!device) throw new ToolError("No linked device configured", 400);
|
|
526
526
|
|
|
527
527
|
const { title, description } = args as { title: string; description?: string };
|
|
528
528
|
if (!title) throw new ToolError("title is required", 400);
|
|
@@ -578,8 +578,8 @@ const readBatteryTool: ToolDefinition = {
|
|
|
578
578
|
async handler(_args, ctx) {
|
|
579
579
|
if (!ctx.nc) throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
580
580
|
|
|
581
|
-
const device =
|
|
582
|
-
if (!device) throw new ToolError("No device
|
|
581
|
+
const device = getLinkedDevice();
|
|
582
|
+
if (!device) throw new ToolError("No linked device configured", 400);
|
|
583
583
|
|
|
584
584
|
const sc = StringCodec();
|
|
585
585
|
|
|
@@ -629,7 +629,7 @@ const setRingerModeTool: ToolDefinition = {
|
|
|
629
629
|
async handler(args, ctx) {
|
|
630
630
|
if (!ctx.nc) throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
631
631
|
|
|
632
|
-
const device =
|
|
632
|
+
const device = getLinkedDevice();
|
|
633
633
|
if (!device) throw new ToolError("No device has Do Not Disturb control enabled", 400);
|
|
634
634
|
|
|
635
635
|
const { mode } = args as { mode: string };
|
|
@@ -687,8 +687,8 @@ const sendEmailTool: ToolDefinition = {
|
|
|
687
687
|
async handler(args, ctx) {
|
|
688
688
|
if (!ctx.nc) throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
689
689
|
|
|
690
|
-
const device =
|
|
691
|
-
if (!device) throw new ToolError("No device
|
|
690
|
+
const device = getLinkedDevice();
|
|
691
|
+
if (!device) throw new ToolError("No linked device configured", 400);
|
|
692
692
|
|
|
693
693
|
const { to, subject, body, cc, bcc } = args as { to: string; subject?: string; body?: string; cc?: string; bcc?: string };
|
|
694
694
|
if (!to) throw new ToolError("to is required", 400);
|
package/src/rpc-handler.ts
CHANGED
|
@@ -9,9 +9,9 @@ import { getPlatform } from "./platform/index.js";
|
|
|
9
9
|
import { spawnCommand } from "./spawn-command.js";
|
|
10
10
|
import crossSpawn from "cross-spawn";
|
|
11
11
|
import { getAgent } from "./agents/agent.js";
|
|
12
|
-
import { validateClient } from "./client-store.js";
|
|
12
|
+
import { validateClient, revokeClient } from "./client-store.js";
|
|
13
13
|
import { publishHostEvent } from "./events.js";
|
|
14
|
-
import {
|
|
14
|
+
import { getLinkedDevice, setLinkedDevice, clearLinkedDevice, clearLinkedDeviceIfMatches } from "./linked-device.js";
|
|
15
15
|
import { currentVersion, performUpdate } from "./update-checker.js";
|
|
16
16
|
import { parseReportFiles, parseTaskOutcome, stripPalmierMarkers } from "./commands/run.js";
|
|
17
17
|
import { clearTaskQueue } from "./event-queues.js";
|
|
@@ -144,15 +144,11 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
|
|
|
144
144
|
|
|
145
145
|
switch (request.method) {
|
|
146
146
|
case "host.info": {
|
|
147
|
-
const capabilities: Record<string, string | null> = {};
|
|
148
|
-
for (const capability of ["location", "notifications", "sms-read", "sms-send", "contacts", "calendar", "alarm", "battery", "dnd", "send-email"] as const) {
|
|
149
|
-
capabilities[capability] = getCapabilityDevice(capability)?.clientToken ?? null;
|
|
150
|
-
}
|
|
151
147
|
return {
|
|
152
148
|
agents: config.agents ?? [],
|
|
153
149
|
version: currentVersion,
|
|
154
150
|
host_platform: process.platform,
|
|
155
|
-
|
|
151
|
+
linked_client_token: getLinkedDevice()?.clientToken ?? null,
|
|
156
152
|
pending_prompts: listPending(),
|
|
157
153
|
lan_url: buildLanUrl(config.httpPort ?? 7256, config.defaultInterface),
|
|
158
154
|
};
|
|
@@ -635,31 +631,27 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
|
|
|
635
631
|
return { ok: true };
|
|
636
632
|
}
|
|
637
633
|
|
|
638
|
-
case "device.
|
|
634
|
+
case "device.link": {
|
|
639
635
|
const params = request.params as { fcmToken: string };
|
|
640
636
|
if (!params.fcmToken) return { error: "fcmToken is required" };
|
|
641
637
|
const clientToken = request.clientToken ?? "";
|
|
642
|
-
|
|
638
|
+
if (!clientToken) return { error: "Unauthorized" };
|
|
639
|
+
setLinkedDevice(clientToken, params.fcmToken);
|
|
643
640
|
return { ok: true };
|
|
644
641
|
}
|
|
645
642
|
|
|
646
|
-
case "device.
|
|
647
|
-
clearCapabilityDevice("location");
|
|
648
|
-
return { ok: true };
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
case "device.capability.enable": {
|
|
652
|
-
const params = request.params as { capability: DeviceCapability; fcmToken: string };
|
|
653
|
-
if (!params.capability || !params.fcmToken) return { error: "capability and fcmToken are required" };
|
|
643
|
+
case "device.unlink": {
|
|
654
644
|
const clientToken = request.clientToken ?? "";
|
|
655
|
-
|
|
645
|
+
const current = getLinkedDevice();
|
|
646
|
+
if (current?.clientToken === clientToken) clearLinkedDevice();
|
|
656
647
|
return { ok: true };
|
|
657
648
|
}
|
|
658
649
|
|
|
659
|
-
case "
|
|
660
|
-
const
|
|
661
|
-
if (!
|
|
662
|
-
|
|
650
|
+
case "clients.revoke_self": {
|
|
651
|
+
const clientToken = request.clientToken ?? "";
|
|
652
|
+
if (!clientToken) return { error: "Unauthorized" };
|
|
653
|
+
clearLinkedDeviceIfMatches(clientToken);
|
|
654
|
+
revokeClient(clientToken);
|
|
663
655
|
return { ok: true };
|
|
664
656
|
}
|
|
665
657
|
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export interface RegisteredDevice {
|
|
2
|
-
clientToken: string;
|
|
3
|
-
fcmToken: string;
|
|
4
|
-
}
|
|
5
|
-
export type DeviceCapability = "location" | "notifications" | "sms-read" | "sms-send" | "contacts" | "calendar" | "alarm" | "battery" | "send-email" | "dnd";
|
|
6
|
-
export declare function getCapabilityDevice(capability: DeviceCapability): RegisteredDevice | null;
|
|
7
|
-
export declare function setCapabilityDevice(capability: DeviceCapability, clientToken: string, fcmToken: string): void;
|
|
8
|
-
export declare function clearCapabilityDevice(capability: DeviceCapability): void;
|
|
9
|
-
//# sourceMappingURL=device-capabilities.d.ts.map
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import { CONFIG_DIR } from "./config.js";
|
|
4
|
-
const CAPABILITIES_FILE = path.join(CONFIG_DIR, "device-capabilities.json");
|
|
5
|
-
function readAll() {
|
|
6
|
-
try {
|
|
7
|
-
if (!fs.existsSync(CAPABILITIES_FILE))
|
|
8
|
-
return {};
|
|
9
|
-
return JSON.parse(fs.readFileSync(CAPABILITIES_FILE, "utf-8"));
|
|
10
|
-
}
|
|
11
|
-
catch {
|
|
12
|
-
return {};
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
function writeAll(map) {
|
|
16
|
-
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
17
|
-
fs.writeFileSync(CAPABILITIES_FILE, JSON.stringify(map, null, 2), "utf-8");
|
|
18
|
-
}
|
|
19
|
-
export function getCapabilityDevice(capability) {
|
|
20
|
-
const map = readAll();
|
|
21
|
-
const device = map[capability];
|
|
22
|
-
if (!device?.clientToken || !device?.fcmToken)
|
|
23
|
-
return null;
|
|
24
|
-
return device;
|
|
25
|
-
}
|
|
26
|
-
export function setCapabilityDevice(capability, clientToken, fcmToken) {
|
|
27
|
-
const map = readAll();
|
|
28
|
-
map[capability] = { clientToken, fcmToken };
|
|
29
|
-
writeAll(map);
|
|
30
|
-
}
|
|
31
|
-
export function clearCapabilityDevice(capability) {
|
|
32
|
-
const map = readAll();
|
|
33
|
-
delete map[capability];
|
|
34
|
-
writeAll(map);
|
|
35
|
-
}
|
|
36
|
-
//# sourceMappingURL=device-capabilities.js.map
|