palmier 0.7.3 → 0.7.6
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 +26 -16
- package/dist/device-capabilities.d.ts +9 -0
- package/dist/device-capabilities.js +36 -0
- package/dist/mcp-tools.js +55 -38
- package/dist/pwa/assets/index-uSwkmHBs.js +118 -0
- package/dist/pwa/assets/{web-SlBB3mP3.js → web-7raT3zOZ.js} +1 -1
- package/dist/pwa/assets/{web-Dwi8DLNK.js → web-DnuoxUd4.js} +1 -1
- package/dist/pwa/index.html +1 -1
- package/dist/pwa/service-worker.js +1 -1
- package/dist/rpc-handler.js +24 -5
- package/dist/transports/http-transport.js +1 -1
- package/package.json +1 -1
- package/palmier-server/pwa/src/components/HostMenu.tsx +137 -141
- package/palmier-server/pwa/src/components/TaskListView.tsx +4 -4
- package/palmier-server/pwa/src/constants.ts +1 -1
- package/palmier-server/pwa/src/pages/Dashboard.tsx +4 -4
- package/palmier-server/server/src/index.ts +17 -12
- package/palmier-server/server/src/routes/device.ts +4 -4
- package/palmier-server/spec.md +2 -2
- package/src/device-capabilities.ts +55 -0
- package/src/mcp-tools.ts +54 -36
- package/src/rpc-handler.ts +24 -5
- package/src/transports/http-transport.ts +1 -1
- package/dist/location-device.d.ts +0 -8
- package/dist/location-device.js +0 -32
- package/dist/pwa/assets/index-CPIqbV9-.js +0 -118
- package/src/location-device.ts +0 -35
package/README.md
CHANGED
|
@@ -53,37 +53,47 @@ Palmier exposes an [MCP](https://modelcontextprotocol.io) server at `http://loca
|
|
|
53
53
|
| `create-contact` | Create a new contact on the user's device | Contacts Access |
|
|
54
54
|
| `read-calendar` | Read calendar events (with time range filter) | Calendar Access |
|
|
55
55
|
| `create-calendar-event` | Create a calendar event on the user's device | Calendar Access |
|
|
56
|
-
| `send-sms` | Send an SMS message from the user's device | SMS Access |
|
|
56
|
+
| `send-sms-message` | Send an SMS message from the user's device | SMS Access |
|
|
57
57
|
| `set-alarm` | Set an alarm on the user's device | None |
|
|
58
58
|
| `read-battery` | Get battery level and charging status | None |
|
|
59
59
|
| `set-ringer-mode` | Set ringer mode (normal/vibrate/silent) | Do Not Disturb Control |
|
|
60
60
|
|
|
61
61
|
**Available resources:**
|
|
62
|
-
| Resource | URI |
|
|
63
|
-
|
|
64
|
-
| Device Notifications | `notifications://device` |
|
|
65
|
-
| Device SMS | `sms://device` |
|
|
62
|
+
| Resource | URI | Permission | Description |
|
|
63
|
+
|----------|-----|------------|-------------|
|
|
64
|
+
| Device Notifications | `notifications://device` | Notification Access | Recent notifications from the user's Android device |
|
|
65
|
+
| Device SMS | `sms-messages://device` | SMS Access | Recent SMS messages from the user's Android device |
|
|
66
66
|
|
|
67
67
|
Resources support MCP subscriptions — clients can subscribe via `resources/subscribe` and receive real-time `notifications/resources/updated` events via the streamable HTTP transport when the resource changes.
|
|
68
68
|
|
|
69
69
|
All device tools work while the Palmier Android app is in the background — they communicate via FCM data messages which wake the app's service even when it's not in the foreground. Permissions listed above must be granted via toggles in the Android app's settings menu.
|
|
70
70
|
|
|
71
|
+
### Architecture
|
|
72
|
+
|
|
71
73
|
```
|
|
72
74
|
┌──────────────┐ HTTP ┌──────────────────┐
|
|
73
75
|
│ │◄──────────────────────│ │
|
|
74
76
|
│ Host Daemon │ │ PWA (Browser) │
|
|
75
|
-
│
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
│
|
|
81
|
-
│
|
|
82
|
-
│
|
|
83
|
-
|
|
84
|
-
|
|
77
|
+
│ (MCP Server)│◄──────┐ │ │
|
|
78
|
+
└──┬────────┬──┘ │ └──────────────────┘
|
|
79
|
+
│ │ │ │
|
|
80
|
+
▼ ▼ │ NATS (TLS) │ NATS (TLS)
|
|
81
|
+
┌──────┐ ┌──────┐ │ ┌────────┴─────────┐
|
|
82
|
+
│Agent │ │Agent │ └───────────────│ Relay Server │
|
|
83
|
+
│ CLIs │ │Tools/│ │ (passthrough, │
|
|
84
|
+
│ │ │Rsrcs │◄──── FCM ───────────│ push, FCM) │
|
|
85
|
+
└──────┘ └──────┘ └──────────────────┘
|
|
86
|
+
│
|
|
87
|
+
FCM │
|
|
88
|
+
▼
|
|
89
|
+
┌──────────────────┐
|
|
90
|
+
│ Android Device │
|
|
91
|
+
│ (notifications, │
|
|
92
|
+
│ SMS, contacts, │
|
|
93
|
+
│ calendar, GPS) │
|
|
94
|
+
└──────────────────┘
|
|
85
95
|
Local / LAN: direct HTTP
|
|
86
|
-
Server mode: via relay server
|
|
96
|
+
Server mode: via relay server + FCM
|
|
87
97
|
```
|
|
88
98
|
|
|
89
99
|
## Access Modes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface RegisteredDevice {
|
|
2
|
+
clientToken: string;
|
|
3
|
+
fcmToken: string;
|
|
4
|
+
}
|
|
5
|
+
export type DeviceCapability = "location" | "notifications" | "sms" | "contacts" | "calendar" | "alert" | "battery" | "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
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
package/dist/mcp-tools.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StringCodec } from "nats";
|
|
2
2
|
import { registerPending } from "./pending-requests.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getCapabilityDevice } from "./device-capabilities.js";
|
|
4
4
|
import { getNotifications } from "./notification-store.js";
|
|
5
5
|
import { getSmsMessages } from "./sms-store.js";
|
|
6
6
|
export class ToolError extends Error {
|
|
@@ -145,11 +145,11 @@ const deviceGeolocationTool = {
|
|
|
145
145
|
async handler(_args, ctx) {
|
|
146
146
|
if (!ctx.nc)
|
|
147
147
|
throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
148
|
-
const
|
|
149
|
-
if (!
|
|
148
|
+
const device = getCapabilityDevice("location");
|
|
149
|
+
if (!device)
|
|
150
150
|
throw new ToolError("No device has location access enabled", 400);
|
|
151
151
|
const sc = StringCodec();
|
|
152
|
-
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.geolocation`, sc.encode(JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.sessionId, fcmToken:
|
|
152
|
+
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.geolocation`, sc.encode(JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.sessionId, fcmToken: device.fcmToken })), { timeout: 5_000 });
|
|
153
153
|
const ack = JSON.parse(sc.decode(ackReply.data));
|
|
154
154
|
if (ack.error)
|
|
155
155
|
throw new ToolError(ack.error, 502);
|
|
@@ -186,8 +186,11 @@ const readContactsTool = {
|
|
|
186
186
|
async handler(_args, ctx) {
|
|
187
187
|
if (!ctx.nc)
|
|
188
188
|
throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
189
|
+
const device = getCapabilityDevice("contacts");
|
|
190
|
+
if (!device)
|
|
191
|
+
throw new ToolError("No device has contacts access enabled", 400);
|
|
189
192
|
const sc = StringCodec();
|
|
190
|
-
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.contacts`, sc.encode(JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.sessionId, action: "read" })), { timeout: 5_000 });
|
|
193
|
+
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.contacts`, sc.encode(JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.sessionId, fcmToken: device.fcmToken, action: "read" })), { timeout: 5_000 });
|
|
191
194
|
const ack = JSON.parse(sc.decode(ackReply.data));
|
|
192
195
|
if (ack.error)
|
|
193
196
|
throw new ToolError(ack.error, 502);
|
|
@@ -229,12 +232,15 @@ const createContactTool = {
|
|
|
229
232
|
async handler(args, ctx) {
|
|
230
233
|
if (!ctx.nc)
|
|
231
234
|
throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
235
|
+
const device = getCapabilityDevice("contacts");
|
|
236
|
+
if (!device)
|
|
237
|
+
throw new ToolError("No device has contacts access enabled", 400);
|
|
232
238
|
const { name, phone, email } = args;
|
|
233
239
|
if (!name)
|
|
234
240
|
throw new ToolError("name is required", 400);
|
|
235
241
|
const sc = StringCodec();
|
|
236
242
|
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.contacts`, sc.encode(JSON.stringify({
|
|
237
|
-
hostId: ctx.config.hostId, requestId: ctx.sessionId,
|
|
243
|
+
hostId: ctx.config.hostId, requestId: ctx.sessionId, fcmToken: device.fcmToken,
|
|
238
244
|
action: "create", name, phone, email,
|
|
239
245
|
})), { timeout: 5_000 });
|
|
240
246
|
const ack = JSON.parse(sc.decode(ackReply.data));
|
|
@@ -277,10 +283,13 @@ const readCalendarTool = {
|
|
|
277
283
|
async handler(args, ctx) {
|
|
278
284
|
if (!ctx.nc)
|
|
279
285
|
throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
286
|
+
const device = getCapabilityDevice("calendar");
|
|
287
|
+
if (!device)
|
|
288
|
+
throw new ToolError("No device has calendar access enabled", 400);
|
|
280
289
|
const { startDate, endDate } = args;
|
|
281
290
|
const sc = StringCodec();
|
|
282
291
|
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.calendar`, sc.encode(JSON.stringify({
|
|
283
|
-
hostId: ctx.config.hostId, requestId: ctx.sessionId,
|
|
292
|
+
hostId: ctx.config.hostId, requestId: ctx.sessionId, fcmToken: device.fcmToken,
|
|
284
293
|
action: "read",
|
|
285
294
|
...(startDate ? { startDate: String(startDate) } : {}),
|
|
286
295
|
...(endDate ? { endDate: String(endDate) } : {}),
|
|
@@ -328,12 +337,15 @@ const createCalendarEventTool = {
|
|
|
328
337
|
async handler(args, ctx) {
|
|
329
338
|
if (!ctx.nc)
|
|
330
339
|
throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
340
|
+
const device = getCapabilityDevice("calendar");
|
|
341
|
+
if (!device)
|
|
342
|
+
throw new ToolError("No device has calendar access enabled", 400);
|
|
331
343
|
const { title, startTime, endTime, location, description } = args;
|
|
332
344
|
if (!title || !startTime || !endTime)
|
|
333
345
|
throw new ToolError("title, startTime, and endTime are required", 400);
|
|
334
346
|
const sc = StringCodec();
|
|
335
347
|
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.calendar`, sc.encode(JSON.stringify({
|
|
336
|
-
hostId: ctx.config.hostId, requestId: ctx.sessionId,
|
|
348
|
+
hostId: ctx.config.hostId, requestId: ctx.sessionId, fcmToken: device.fcmToken,
|
|
337
349
|
action: "create",
|
|
338
350
|
title, startTime: String(startTime), endTime: String(endTime),
|
|
339
351
|
...(location ? { location } : {}),
|
|
@@ -362,7 +374,7 @@ const createCalendarEventTool = {
|
|
|
362
374
|
},
|
|
363
375
|
};
|
|
364
376
|
const sendSmsTool = {
|
|
365
|
-
name: "send-sms",
|
|
377
|
+
name: "send-sms-message",
|
|
366
378
|
description: [
|
|
367
379
|
"Send an SMS message from the user's mobile device.",
|
|
368
380
|
"Blocks until the device responds (up to 30 seconds).",
|
|
@@ -379,12 +391,15 @@ const sendSmsTool = {
|
|
|
379
391
|
async handler(args, ctx) {
|
|
380
392
|
if (!ctx.nc)
|
|
381
393
|
throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
394
|
+
const device = getCapabilityDevice("sms");
|
|
395
|
+
if (!device)
|
|
396
|
+
throw new ToolError("No device has SMS access enabled", 400);
|
|
382
397
|
const { to, body } = args;
|
|
383
398
|
if (!to || !body)
|
|
384
399
|
throw new ToolError("to and body are required", 400);
|
|
385
400
|
const sc = StringCodec();
|
|
386
401
|
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.sms`, sc.encode(JSON.stringify({
|
|
387
|
-
hostId: ctx.config.hostId, requestId: ctx.sessionId,
|
|
402
|
+
hostId: ctx.config.hostId, requestId: ctx.sessionId, fcmToken: device.fcmToken,
|
|
388
403
|
action: "send", to, body,
|
|
389
404
|
})), { timeout: 5_000 });
|
|
390
405
|
const ack = JSON.parse(sc.decode(ackReply.data));
|
|
@@ -409,48 +424,44 @@ const sendSmsTool = {
|
|
|
409
424
|
return result;
|
|
410
425
|
},
|
|
411
426
|
};
|
|
412
|
-
const
|
|
413
|
-
name: "
|
|
427
|
+
const sendAlertTool = {
|
|
428
|
+
name: "send-alert",
|
|
414
429
|
description: [
|
|
415
|
-
"
|
|
430
|
+
"Send an alert to the user's mobile device with an alarm sound and full-screen popup.",
|
|
431
|
+
"Use this to urgently get the user's attention. The device will play an alarm sound and show a full-screen dialog even on the lock screen.",
|
|
416
432
|
"Blocks until the device responds (up to 30 seconds).",
|
|
417
433
|
'Response: `{"ok": true}` on success, or `{"error": "..."}` on failure.',
|
|
418
434
|
],
|
|
419
435
|
inputSchema: {
|
|
420
436
|
type: "object",
|
|
421
437
|
properties: {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
label: { type: "string", description: "Alarm label" },
|
|
425
|
-
days: {
|
|
426
|
-
type: "array",
|
|
427
|
-
items: { type: "number" },
|
|
428
|
-
description: "Recurring days (1=Sun, 2=Mon, ..., 7=Sat). Omit for one-time.",
|
|
429
|
-
},
|
|
438
|
+
title: { type: "string", description: "Alert title" },
|
|
439
|
+
description: { type: "string", description: "Alert description/details" },
|
|
430
440
|
},
|
|
431
|
-
required: ["
|
|
441
|
+
required: ["title"],
|
|
432
442
|
},
|
|
433
443
|
async handler(args, ctx) {
|
|
434
444
|
if (!ctx.nc)
|
|
435
445
|
throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
436
|
-
const
|
|
437
|
-
if (
|
|
438
|
-
throw new ToolError("
|
|
446
|
+
const device = getCapabilityDevice("alert");
|
|
447
|
+
if (!device)
|
|
448
|
+
throw new ToolError("No device has alert access enabled", 400);
|
|
449
|
+
const { title, description } = args;
|
|
450
|
+
if (!title)
|
|
451
|
+
throw new ToolError("title is required", 400);
|
|
439
452
|
const sc = StringCodec();
|
|
440
453
|
const payload = {
|
|
441
|
-
hostId: ctx.config.hostId, requestId: ctx.sessionId,
|
|
442
|
-
|
|
454
|
+
hostId: ctx.config.hostId, requestId: ctx.sessionId, fcmToken: device.fcmToken,
|
|
455
|
+
title,
|
|
443
456
|
};
|
|
444
|
-
if (
|
|
445
|
-
payload.
|
|
446
|
-
|
|
447
|
-
payload.days = days.join(",");
|
|
448
|
-
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.alarm`, sc.encode(JSON.stringify(payload)), { timeout: 5_000 });
|
|
457
|
+
if (description)
|
|
458
|
+
payload.description = description;
|
|
459
|
+
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.alert`, sc.encode(JSON.stringify(payload)), { timeout: 5_000 });
|
|
449
460
|
const ack = JSON.parse(sc.decode(ackReply.data));
|
|
450
461
|
if (ack.error)
|
|
451
462
|
throw new ToolError(ack.error, 502);
|
|
452
463
|
const responsePromise = new Promise((resolve, reject) => {
|
|
453
|
-
const sub = ctx.nc.subscribe(`host.${ctx.config.hostId}.
|
|
464
|
+
const sub = ctx.nc.subscribe(`host.${ctx.config.hostId}.alert.${ctx.sessionId}`, { max: 1 });
|
|
454
465
|
const timer = setTimeout(() => {
|
|
455
466
|
sub.unsubscribe();
|
|
456
467
|
reject(new ToolError("Device did not respond within 30 seconds", 504));
|
|
@@ -482,8 +493,11 @@ const readBatteryTool = {
|
|
|
482
493
|
async handler(_args, ctx) {
|
|
483
494
|
if (!ctx.nc)
|
|
484
495
|
throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
496
|
+
const device = getCapabilityDevice("battery");
|
|
497
|
+
if (!device)
|
|
498
|
+
throw new ToolError("No device has battery access enabled", 400);
|
|
485
499
|
const sc = StringCodec();
|
|
486
|
-
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.battery`, sc.encode(JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.sessionId })), { timeout: 5_000 });
|
|
500
|
+
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.battery`, sc.encode(JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.sessionId, fcmToken: device.fcmToken })), { timeout: 5_000 });
|
|
487
501
|
const ack = JSON.parse(sc.decode(ackReply.data));
|
|
488
502
|
if (ack.error)
|
|
489
503
|
throw new ToolError(ack.error, 502);
|
|
@@ -523,11 +537,14 @@ const setRingerModeTool = {
|
|
|
523
537
|
async handler(args, ctx) {
|
|
524
538
|
if (!ctx.nc)
|
|
525
539
|
throw new ToolError("Not connected to server (NATS unavailable)", 503);
|
|
540
|
+
const device = getCapabilityDevice("dnd");
|
|
541
|
+
if (!device)
|
|
542
|
+
throw new ToolError("No device has Do Not Disturb control enabled", 400);
|
|
526
543
|
const { mode } = args;
|
|
527
544
|
if (!["normal", "vibrate", "silent"].includes(mode))
|
|
528
545
|
throw new ToolError("mode must be 'normal', 'vibrate', or 'silent'", 400);
|
|
529
546
|
const sc = StringCodec();
|
|
530
|
-
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.ringer`, sc.encode(JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.sessionId, mode })), { timeout: 5_000 });
|
|
547
|
+
const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.ringer`, sc.encode(JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.sessionId, fcmToken: device.fcmToken, mode })), { timeout: 5_000 });
|
|
531
548
|
const ack = JSON.parse(sc.decode(ackReply.data));
|
|
532
549
|
if (ack.error)
|
|
533
550
|
throw new ToolError(ack.error, 502);
|
|
@@ -550,7 +567,7 @@ const setRingerModeTool = {
|
|
|
550
567
|
return result;
|
|
551
568
|
},
|
|
552
569
|
};
|
|
553
|
-
export const agentTools = [notifyTool, requestInputTool, requestConfirmationTool, deviceGeolocationTool, readContactsTool, createContactTool, readCalendarTool, createCalendarEventTool, sendSmsTool,
|
|
570
|
+
export const agentTools = [notifyTool, requestInputTool, requestConfirmationTool, deviceGeolocationTool, readContactsTool, createContactTool, readCalendarTool, createCalendarEventTool, sendSmsTool, sendAlertTool, readBatteryTool, setRingerModeTool];
|
|
554
571
|
export const agentToolMap = new Map(agentTools.map((t) => [t.name, t]));
|
|
555
572
|
const deviceNotificationsResource = {
|
|
556
573
|
uri: "notifications://device",
|
|
@@ -564,14 +581,14 @@ const deviceNotificationsResource = {
|
|
|
564
581
|
read: getNotifications,
|
|
565
582
|
};
|
|
566
583
|
const deviceSmsResource = {
|
|
567
|
-
uri: "sms://device",
|
|
584
|
+
uri: "sms-messages://device",
|
|
568
585
|
name: "Device SMS",
|
|
569
586
|
description: [
|
|
570
587
|
"Get recent SMS messages from the user's Android device.",
|
|
571
588
|
"Response: JSON array of message objects with `id`, `sender`, `body`, `timestamp`.",
|
|
572
589
|
],
|
|
573
590
|
mimeType: "application/json",
|
|
574
|
-
restPath: "/sms",
|
|
591
|
+
restPath: "/sms-messages",
|
|
575
592
|
read: getSmsMessages,
|
|
576
593
|
};
|
|
577
594
|
export const agentResources = [deviceNotificationsResource, deviceSmsResource];
|