palmier 0.7.8 → 0.7.9

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 (40) hide show
  1. package/README.md +1 -1
  2. package/dist/mcp-tools.d.ts +2 -0
  3. package/dist/mcp-tools.js +4 -2
  4. package/dist/platform/linux.js +11 -8
  5. package/dist/platform/windows.d.ts +5 -6
  6. package/dist/platform/windows.js +15 -12
  7. package/dist/pwa/assets/index-FP1Mipr6.js +120 -0
  8. package/dist/pwa/assets/index-bLTn8zBj.css +1 -0
  9. package/dist/pwa/assets/{web-BNr628AV.js → web-BpM3fNCn.js} +1 -1
  10. package/dist/pwa/assets/{web-DyQPewAi.js → web-CF-N8Di6.js} +1 -1
  11. package/dist/pwa/index.html +2 -2
  12. package/dist/pwa/service-worker.js +1 -1
  13. package/dist/rpc-handler.js +23 -8
  14. package/dist/task.js +1 -1
  15. package/dist/transports/http-transport.js +3 -5
  16. package/dist/types.d.ts +9 -6
  17. package/package.json +1 -1
  18. package/palmier-server/README.md +3 -3
  19. package/palmier-server/pwa/src/App.css +117 -36
  20. package/palmier-server/pwa/src/components/PullToRefreshIndicator.tsx +46 -0
  21. package/palmier-server/pwa/src/components/RunDetailView.tsx +58 -15
  22. package/palmier-server/pwa/src/components/SessionComposer.tsx +20 -10
  23. package/palmier-server/pwa/src/components/{RunsView.tsx → SessionsView.tsx} +33 -25
  24. package/palmier-server/pwa/src/components/TaskCard.tsx +33 -35
  25. package/palmier-server/pwa/src/components/TaskForm.tsx +274 -293
  26. package/palmier-server/pwa/src/components/{TaskListView.tsx → TasksView.tsx} +20 -13
  27. package/palmier-server/pwa/src/contexts/HostStoreContext.tsx +16 -8
  28. package/palmier-server/pwa/src/hooks/usePullToRefresh.ts +102 -0
  29. package/palmier-server/pwa/src/pages/Dashboard.tsx +9 -26
  30. package/palmier-server/pwa/src/types.ts +5 -9
  31. package/palmier-server/spec.md +23 -23
  32. package/src/mcp-tools.ts +6 -2
  33. package/src/platform/linux.ts +10 -8
  34. package/src/platform/windows.ts +15 -12
  35. package/src/rpc-handler.ts +26 -10
  36. package/src/task.ts +1 -1
  37. package/src/transports/http-transport.ts +3 -5
  38. package/src/types.ts +9 -7
  39. package/dist/pwa/assets/index-8cTctVnD.js +0 -120
  40. package/dist/pwa/assets/index-CSUkBBsQ.css +0 -1
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  You have AI agents on your machine. But you have to sit at your desk to use them. Palmier lets you dispatch, schedule, and monitor them from any device, anywhere.
10
10
 
11
- It runs on your machine as a background daemon and connects to a mobile-friendly PWA, so you can create tasks, approve permissions, and check results without being at your computer.
11
+ It runs on your machine as a background daemon and connects to a mobile-friendly PWA, so you can start one-off sessions, schedule recurring tasks, approve permissions, and check results without being at your computer.
12
12
  > **Important:** By using Palmier, you agree to the [Terms of Service](https://www.palmier.me/terms) and [Privacy Policy](https://www.palmier.me/privacy). See the [Disclaimer](#disclaimer) section below.
13
13
 
14
14
  ## Quick Start
@@ -32,6 +32,8 @@ export interface ResourceDefinition {
32
32
  restPath: string;
33
33
  /** Return the current resource content. */
34
34
  read: () => unknown;
35
+ /** Register a listener for content changes. Returns an unsubscribe function. */
36
+ subscribe: (listener: () => void) => () => void;
35
37
  }
36
38
  export declare const agentResources: ResourceDefinition[];
37
39
  export declare const agentResourceMap: Map<string, ResourceDefinition>;
package/dist/mcp-tools.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { StringCodec } from "nats";
2
2
  import { registerPending } from "./pending-requests.js";
3
3
  import { getCapabilityDevice } from "./device-capabilities.js";
4
- import { getNotifications } from "./notification-store.js";
5
- import { getSmsMessages } from "./sms-store.js";
4
+ import { getNotifications, onNotificationsChanged } from "./notification-store.js";
5
+ import { getSmsMessages, onSmsChanged } from "./sms-store.js";
6
6
  export class ToolError extends Error {
7
7
  statusCode;
8
8
  constructor(message, statusCode = 500) {
@@ -651,6 +651,7 @@ const deviceNotificationsResource = {
651
651
  mimeType: "application/json",
652
652
  restPath: "/notifications",
653
653
  read: getNotifications,
654
+ subscribe: onNotificationsChanged,
654
655
  };
655
656
  const deviceSmsResource = {
656
657
  uri: "sms-messages://device",
@@ -662,6 +663,7 @@ const deviceSmsResource = {
662
663
  mimeType: "application/json",
663
664
  restPath: "/sms-messages",
664
665
  read: getSmsMessages,
666
+ subscribe: onSmsChanged,
665
667
  };
666
668
  export const agentResources = [deviceNotificationsResource, deviceSmsResource];
667
669
  export const agentResourceMap = new Map(agentResources.map((r) => [r.uri, r]));
@@ -181,17 +181,20 @@ Environment=PATH=${process.env.PATH || "/usr/local/bin:/usr/bin:/bin"}
181
181
  `;
182
182
  fs.writeFileSync(path.join(UNIT_DIR, serviceName), serviceContent, "utf-8");
183
183
  daemonReload();
184
- // Only create and enable a timer if triggers exist and are enabled
185
- if (!task.frontmatter.triggers_enabled)
184
+ // Only create and enable a timer if the schedule exists and is enabled
185
+ if (!task.frontmatter.schedule_enabled)
186
+ return;
187
+ const scheduleType = task.frontmatter.schedule_type;
188
+ const scheduleValues = task.frontmatter.schedule_values;
189
+ if (!scheduleType || !scheduleValues?.length)
186
190
  return;
187
- const triggers = task.frontmatter.triggers || [];
188
191
  const onCalendarLines = [];
189
- for (const trigger of triggers) {
190
- if (trigger.type === "cron") {
191
- onCalendarLines.push(`OnCalendar=${cronToOnCalendar(trigger.value)}`);
192
+ for (const value of scheduleValues) {
193
+ if (scheduleType === "crons") {
194
+ onCalendarLines.push(`OnCalendar=${cronToOnCalendar(value)}`);
192
195
  }
193
- else if (trigger.type === "once") {
194
- onCalendarLines.push(`OnActiveSec=${trigger.value}`);
196
+ else if (scheduleType === "specific_times") {
197
+ onCalendarLines.push(`OnActiveSec=${value}`);
195
198
  }
196
199
  }
197
200
  if (onCalendarLines.length > 0) {
@@ -1,18 +1,17 @@
1
1
  import type { PlatformService } from "./platform.js";
2
2
  import type { HostConfig, ParsedTask } from "../types.js";
3
3
  /**
4
- * Convert a cron expression or "once" trigger to Task Scheduler XML trigger elements.
4
+ * Convert a single schedule value to a Task Scheduler XML trigger element.
5
5
  *
6
- * Only these cron patterns (produced by the PWA UI) are handled:
6
+ * `specific_times` values are ISO datetime strings like "2026-03-28T09:00".
7
+ *
8
+ * `crons` values are cron expressions. Only these patterns (produced by the PWA UI) are handled:
7
9
  * hourly: "0 * * * *"
8
10
  * daily: "MM HH * * *"
9
11
  * weekly: "MM HH * * D"
10
12
  * monthly: "MM HH D * *"
11
13
  */
12
- export declare function triggerToXml(trigger: {
13
- type: string;
14
- value: string;
15
- }): string;
14
+ export declare function scheduleValueToXml(scheduleType: "crons" | "specific_times", value: string): string;
16
15
  /**
17
16
  * Build a complete Task Scheduler XML definition.
18
17
  */
@@ -7,22 +7,23 @@ const TASK_PREFIX = "\\Palmier\\PalmierTask-";
7
7
  const DAEMON_TASK_NAME = "PalmierDaemon";
8
8
  const DOW_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
9
9
  /**
10
- * Convert a cron expression or "once" trigger to Task Scheduler XML trigger elements.
10
+ * Convert a single schedule value to a Task Scheduler XML trigger element.
11
11
  *
12
- * Only these cron patterns (produced by the PWA UI) are handled:
12
+ * `specific_times` values are ISO datetime strings like "2026-03-28T09:00".
13
+ *
14
+ * `crons` values are cron expressions. Only these patterns (produced by the PWA UI) are handled:
13
15
  * hourly: "0 * * * *"
14
16
  * daily: "MM HH * * *"
15
17
  * weekly: "MM HH * * D"
16
18
  * monthly: "MM HH D * *"
17
19
  */
18
- export function triggerToXml(trigger) {
19
- if (trigger.type === "once") {
20
- // ISO datetime "2026-03-28T09:00"
21
- return `<TimeTrigger><StartBoundary>${trigger.value}:00</StartBoundary></TimeTrigger>`;
20
+ export function scheduleValueToXml(scheduleType, value) {
21
+ if (scheduleType === "specific_times") {
22
+ return `<TimeTrigger><StartBoundary>${value}:00</StartBoundary></TimeTrigger>`;
22
23
  }
23
- const parts = trigger.value.trim().split(/\s+/);
24
+ const parts = value.trim().split(/\s+/);
24
25
  if (parts.length !== 5)
25
- throw new Error(`Invalid cron expression: ${trigger.value}`);
26
+ throw new Error(`Invalid cron expression: ${value}`);
26
27
  const [minute, hour, dayOfMonth, , dayOfWeek] = parts;
27
28
  const st = `${hour.padStart(2, "0")}:${minute.padStart(2, "0")}:00`;
28
29
  // StartBoundary needs a full date; use a past date as the anchor
@@ -178,13 +179,15 @@ export class WindowsPlatform {
178
179
  const tr = `"${process.execPath}" "${script}" run ${taskId}`;
179
180
  // Build trigger XML elements
180
181
  const triggerElements = [];
181
- if (task.frontmatter.triggers_enabled) {
182
- for (const trigger of task.frontmatter.triggers ?? []) {
182
+ const scheduleType = task.frontmatter.schedule_type;
183
+ const scheduleValues = task.frontmatter.schedule_values;
184
+ if (task.frontmatter.schedule_enabled && scheduleType && scheduleValues?.length) {
185
+ for (const value of scheduleValues) {
183
186
  try {
184
- triggerElements.push(triggerToXml(trigger));
187
+ triggerElements.push(scheduleValueToXml(scheduleType, value));
185
188
  }
186
189
  catch (err) {
187
- console.error(`Invalid trigger: ${err}`);
190
+ console.error(`Invalid schedule value: ${err}`);
188
191
  }
189
192
  }
190
193
  }