palmier 0.9.26 → 0.9.27

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.
@@ -1 +1 @@
1
- import{W as p}from"./index-CsXEu9PI.js";class f extends p{constructor(){super(...arguments),this.group="CapacitorStorage"}async configure({group:e}){typeof e=="string"&&(this.group=e)}async get(e){return{value:this.impl.getItem(this.applyPrefix(e.key))}}async set(e){this.impl.setItem(this.applyPrefix(e.key),e.value)}async remove(e){this.impl.removeItem(this.applyPrefix(e.key))}async keys(){return{keys:this.rawKeys().map(t=>t.substring(this.prefix.length))}}async clear(){for(const e of this.rawKeys())this.impl.removeItem(e)}async migrate(){var e;const t=[],s=[],n="_cap_",o=Object.keys(this.impl).filter(i=>i.indexOf(n)===0);for(const i of o){const r=i.substring(n.length),a=(e=this.impl.getItem(i))!==null&&e!==void 0?e:"",{value:l}=await this.get({key:r});typeof l=="string"?s.push(r):(await this.set({key:r,value:a}),t.push(r))}return{migrated:t,existing:s}}async removeOld(){const e="_cap_",t=Object.keys(this.impl).filter(s=>s.indexOf(e)===0);for(const s of t)this.impl.removeItem(s)}get impl(){return window.localStorage}get prefix(){return this.group==="NativeStorage"?"":`${this.group}.`}rawKeys(){return Object.keys(this.impl).filter(e=>e.indexOf(this.prefix)===0)}applyPrefix(e){return this.prefix+e}}export{f as PreferencesWeb};
1
+ import{W as p}from"./index-D6UTaQKv.js";class f extends p{constructor(){super(...arguments),this.group="CapacitorStorage"}async configure({group:e}){typeof e=="string"&&(this.group=e)}async get(e){return{value:this.impl.getItem(this.applyPrefix(e.key))}}async set(e){this.impl.setItem(this.applyPrefix(e.key),e.value)}async remove(e){this.impl.removeItem(this.applyPrefix(e.key))}async keys(){return{keys:this.rawKeys().map(t=>t.substring(this.prefix.length))}}async clear(){for(const e of this.rawKeys())this.impl.removeItem(e)}async migrate(){var e;const t=[],s=[],n="_cap_",o=Object.keys(this.impl).filter(i=>i.indexOf(n)===0);for(const i of o){const r=i.substring(n.length),a=(e=this.impl.getItem(i))!==null&&e!==void 0?e:"",{value:l}=await this.get({key:r});typeof l=="string"?s.push(r):(await this.set({key:r,value:a}),t.push(r))}return{migrated:t,existing:s}}async removeOld(){const e="_cap_",t=Object.keys(this.impl).filter(s=>s.indexOf(e)===0);for(const s of t)this.impl.removeItem(s)}get impl(){return window.localStorage}get prefix(){return this.group==="NativeStorage"?"":`${this.group}.`}rawKeys(){return Object.keys(this.impl).filter(e=>e.indexOf(this.prefix)===0)}applyPrefix(e){return this.prefix+e}}export{f as PreferencesWeb};
@@ -1 +1 @@
1
- import{W as t}from"./index-CsXEu9PI.js";class s extends t{constructor(){super(),this.handleVisibilityChange=()=>{const e={isActive:document.hidden!==!0};this.notifyListeners("appStateChange",e),document.hidden?this.notifyListeners("pause",null):this.notifyListeners("resume",null)},document.addEventListener("visibilitychange",this.handleVisibilityChange,!1)}exitApp(){throw this.unimplemented("Not implemented on web.")}async getInfo(){throw this.unimplemented("Not implemented on web.")}async getLaunchUrl(){return{url:""}}async getState(){return{isActive:document.hidden!==!0}}async minimizeApp(){throw this.unimplemented("Not implemented on web.")}async toggleBackButtonHandler(){throw this.unimplemented("Not implemented on web.")}async getAppLanguage(){return{value:navigator.language.split("-")[0].toLowerCase()}}}export{s as AppWeb};
1
+ import{W as t}from"./index-D6UTaQKv.js";class s extends t{constructor(){super(),this.handleVisibilityChange=()=>{const e={isActive:document.hidden!==!0};this.notifyListeners("appStateChange",e),document.hidden?this.notifyListeners("pause",null):this.notifyListeners("resume",null)},document.addEventListener("visibilitychange",this.handleVisibilityChange,!1)}exitApp(){throw this.unimplemented("Not implemented on web.")}async getInfo(){throw this.unimplemented("Not implemented on web.")}async getLaunchUrl(){return{url:""}}async getState(){return{isActive:document.hidden!==!0}}async minimizeApp(){throw this.unimplemented("Not implemented on web.")}async toggleBackButtonHandler(){throw this.unimplemented("Not implemented on web.")}async getAppLanguage(){return{value:navigator.language.split("-")[0].toLowerCase()}}}export{s as AppWeb};
@@ -1 +1 @@
1
- import{W as i}from"./index-CsXEu9PI.js";function o(){const t=window.navigator.connection||window.navigator.mozConnection||window.navigator.webkitConnection;let n="unknown";const e=t?t.type||t.effectiveType:null;if(e&&typeof e=="string")switch(e){case"bluetooth":case"cellular":n="cellular";break;case"none":n="none";break;case"ethernet":case"wifi":case"wimax":n="wifi";break;case"other":case"unknown":n="unknown";break;case"slow-2g":case"2g":case"3g":n="cellular";break;case"4g":n="wifi";break}return n}class s extends i{constructor(){super(),this.handleOnline=()=>{const e={connected:!0,connectionType:o()};this.notifyListeners("networkStatusChange",e)},this.handleOffline=()=>{const n={connected:!1,connectionType:"none"};this.notifyListeners("networkStatusChange",n)},typeof window<"u"&&(window.addEventListener("online",this.handleOnline),window.addEventListener("offline",this.handleOffline))}async getStatus(){if(!window.navigator)throw this.unavailable("Browser does not support the Network Information API");const n=window.navigator.onLine,e=o();return{connected:n,connectionType:n?e:"none"}}}const r=new s;export{r as Network,s as NetworkWeb};
1
+ import{W as i}from"./index-D6UTaQKv.js";function o(){const t=window.navigator.connection||window.navigator.mozConnection||window.navigator.webkitConnection;let n="unknown";const e=t?t.type||t.effectiveType:null;if(e&&typeof e=="string")switch(e){case"bluetooth":case"cellular":n="cellular";break;case"none":n="none";break;case"ethernet":case"wifi":case"wimax":n="wifi";break;case"other":case"unknown":n="unknown";break;case"slow-2g":case"2g":case"3g":n="cellular";break;case"4g":n="wifi";break}return n}class s extends i{constructor(){super(),this.handleOnline=()=>{const e={connected:!0,connectionType:o()};this.notifyListeners("networkStatusChange",e)},this.handleOffline=()=>{const n={connected:!1,connectionType:"none"};this.notifyListeners("networkStatusChange",n)},typeof window<"u"&&(window.addEventListener("online",this.handleOnline),window.addEventListener("offline",this.handleOffline))}async getStatus(){if(!window.navigator)throw this.unavailable("Browser does not support the Network Information API");const n=window.navigator.onLine,e=o();return{connected:n,connectionType:n?e:"none"}}}const r=new s;export{r as Network,s as NetworkWeb};
@@ -8,8 +8,8 @@
8
8
  <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
9
9
  <title>Palmier</title>
10
10
  <meta name="description" content="Run tasks from anywhere, approve actions on the go, and let your agents use your phone’s built-in capabilities when needed." />
11
- <script type="module" crossorigin src="/assets/index-CsXEu9PI.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-D8gfaj_Y.css">
11
+ <script type="module" crossorigin src="/assets/index-D6UTaQKv.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-BpsVmTp9.css">
13
13
  <link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
14
14
  <body>
15
15
  <div id="root"></div>
@@ -90,7 +90,7 @@ function parseAttr(attrs, name) {
90
90
  return match ? match[1] : undefined;
91
91
  }
92
92
  async function generateName(projectRoot, userPrompt, agentName) {
93
- const prompt = `Generate a concise 3-6 word name for this task. Reply with ONLY the name, nothing else.\n\nTask: ${userPrompt}`;
93
+ const prompt = `Generate a concise 3-6 word name from the task description. Do not execute the task and reply with ONLY the name, nothing else.\n\nTask description: ${userPrompt}`;
94
94
  const agent = getAgent(agentName);
95
95
  const { args, stdin, env: agentEnv } = getPromptCommandLine(agent, prompt);
96
96
  const command = agent.command;
@@ -110,6 +110,48 @@ async function generateName(projectRoot, userPrompt, agentName) {
110
110
  }
111
111
  /** Active follow-up child processes, keyed by "taskId:runId". */
112
112
  const activeFollowups = new Map();
113
+ /**
114
+ * Kill the follow-up child for the given run and clear its in-memory and
115
+ * on-disk tracking entries. Returns false when no follow-up is registered
116
+ * (neither in-memory nor persisted). Callers handle status messages and event
117
+ * publishing themselves — this helper only owns the kill + cleanup.
118
+ */
119
+ async function killActiveFollowup(projectRoot, taskId, runId) {
120
+ const key = `${taskId}:${runId}`;
121
+ const taskDir = getTaskDir(projectRoot, taskId);
122
+ const runDir = getRunDir(taskDir, runId);
123
+ const child = activeFollowups.get(key);
124
+ let pidToKill = child?.pid;
125
+ if (!child) {
126
+ // Daemon restarted since spawn — the in-memory handle is gone but the
127
+ // child may still be running. Fall back to the persisted PID.
128
+ const persisted = readFollowupStatus(runDir);
129
+ if (!persisted)
130
+ return false;
131
+ pidToKill = persisted.pid;
132
+ }
133
+ if (pidToKill !== undefined) {
134
+ if (process.platform === "win32") {
135
+ try {
136
+ const { execFileSync } = await import("child_process");
137
+ execFileSync("taskkill", ["/pid", String(pidToKill), "/f", "/t"], { windowsHide: true, stdio: "pipe" });
138
+ }
139
+ catch { /* may have already exited */ }
140
+ }
141
+ else if (child) {
142
+ child.kill();
143
+ }
144
+ else {
145
+ try {
146
+ process.kill(pidToKill, "SIGTERM");
147
+ }
148
+ catch { /* already dead */ }
149
+ }
150
+ }
151
+ activeFollowups.delete(key);
152
+ deleteFollowupStatus(runDir);
153
+ return true;
154
+ }
113
155
  /**
114
156
  * The run process never started, so write a `started` + `failed` status pair
115
157
  * directly to TASKRUN.md (run.ts normally does this) and notify clients.
@@ -344,7 +386,12 @@ export function createRpcHandler(config, nc) {
344
386
  content: "",
345
387
  type: "started",
346
388
  });
389
+ writeTaskStatus(followupTaskDir, { running_state: "started", time_stamp: Date.now() });
347
390
  await publishHostEvent(nc, config.hostId, params.id, { event_type: "result-updated", run_id: params.run_id });
391
+ await publishHostEvent(nc, config.hostId, params.id, {
392
+ event_type: "running-state", running_state: "started",
393
+ name: followupTask.frontmatter.name, run_id: params.run_id,
394
+ });
348
395
  const followupAgent = getAgent(followupTask.frontmatter.agent);
349
396
  const { args: cmdArgs, stdin, env: followupAgentEnv, files: followupFiles } = followupAgent.getTaskRunCommandLine(followupTask, params.message, followupTask.frontmatter.yolo_mode ? "yolo" : followupTask.frontmatter.permissions);
350
397
  const cmd = followupAgent.command;
@@ -387,7 +434,12 @@ export function createRpcHandler(config, nc) {
387
434
  content: "",
388
435
  type: outcome,
389
436
  });
437
+ writeTaskStatus(followupTaskDir, { running_state: outcome, time_stamp: Date.now() });
390
438
  await publishHostEvent(nc, config.hostId, params.id, { event_type: "result-updated", run_id: params.run_id });
439
+ await publishHostEvent(nc, config.hostId, params.id, {
440
+ event_type: "running-state", running_state: outcome,
441
+ name: followupTask.frontmatter.name, run_id: params.run_id,
442
+ });
391
443
  });
392
444
  child.on("error", async (err) => {
393
445
  activeFollowups.delete(followupKey);
@@ -399,61 +451,31 @@ export function createRpcHandler(config, nc) {
399
451
  content: "",
400
452
  type: "failed",
401
453
  });
454
+ writeTaskStatus(followupTaskDir, { running_state: "failed", time_stamp: Date.now() });
402
455
  await publishHostEvent(nc, config.hostId, params.id, { event_type: "result-updated", run_id: params.run_id });
456
+ await publishHostEvent(nc, config.hostId, params.id, {
457
+ event_type: "running-state", running_state: "failed",
458
+ name: followupTask.frontmatter.name, run_id: params.run_id,
459
+ });
403
460
  });
404
461
  return { ok: true, task_id: params.id, run_id: params.run_id };
405
462
  }
406
- case "task.stop_followup": {
407
- const params = request.params;
408
- if (!params.run_id) {
409
- return { error: "run_id is required" };
410
- }
411
- const stopKey = `${params.id}:${params.run_id}`;
412
- const stopTaskDir = getTaskDir(config.projectRoot, params.id);
413
- const stopRunDir = getRunDir(stopTaskDir, params.run_id);
414
- const child = activeFollowups.get(stopKey);
415
- let pidToKill = child?.pid;
416
- if (!child) {
417
- // Daemon restarted since spawn — the in-memory handle is gone but
418
- // the child may still be running. Fall back to the persisted PID.
419
- const persisted = readFollowupStatus(stopRunDir);
420
- if (!persisted)
421
- return { error: "No active follow-up for this run" };
422
- pidToKill = persisted.pid;
423
- }
424
- if (pidToKill !== undefined) {
425
- if (process.platform === "win32") {
426
- try {
427
- const { execFileSync } = await import("child_process");
428
- execFileSync("taskkill", ["/pid", String(pidToKill), "/f", "/t"], { windowsHide: true, stdio: "pipe" });
429
- }
430
- catch { /* may have already exited */ }
431
- }
432
- else if (child) {
433
- child.kill();
434
- }
435
- else {
436
- try {
437
- process.kill(pidToKill, "SIGTERM");
438
- }
439
- catch { /* already dead */ }
440
- }
441
- }
442
- // child.killed stops the close handler from double-writing the status.
443
- appendRunMessage(stopTaskDir, params.run_id, {
444
- role: "status",
445
- time: Date.now(),
446
- content: "",
447
- type: "stopped",
448
- });
449
- activeFollowups.delete(stopKey);
450
- deleteFollowupStatus(stopRunDir);
451
- await publishHostEvent(nc, config.hostId, params.id, { event_type: "result-updated", run_id: params.run_id });
452
- return { ok: true, task_id: params.id, run_id: params.run_id };
453
- }
454
463
  case "task.abort": {
455
464
  const params = request.params;
456
465
  const abortTaskDir = getTaskDir(config.projectRoot, params.id);
466
+ // Any in-flight follow-up children for this task aren't reachable via
467
+ // getPlatform().stopTask (they're daemon-spawned, not scheduler-spawned),
468
+ // so kill them up front before they can race with the abort status write
469
+ // and overwrite task-status with their own close-handler outcome.
470
+ const followupRunIds = [];
471
+ const prefix = `${params.id}:`;
472
+ for (const key of activeFollowups.keys()) {
473
+ if (key.startsWith(prefix))
474
+ followupRunIds.push(key.slice(prefix.length));
475
+ }
476
+ for (const runId of followupRunIds) {
477
+ await killActiveFollowup(config.projectRoot, params.id, runId);
478
+ }
457
479
  // Read PID before overwriting — stopTask needs it to kill the
458
480
  // process tree on Windows.
459
481
  const abortPrevStatus = readTaskStatus(abortTaskDir);
@@ -479,13 +501,19 @@ export function createRpcHandler(config, nc) {
479
501
  }
480
502
  }
481
503
  catch { /* best-effort */ }
482
- try {
483
- await getPlatform().stopTask(params.id);
484
- }
485
- catch (err) {
486
- const e = err;
487
- console.error(`task.abort failed for ${params.id}: ${e.stderr || e.message}`);
488
- return { error: `Failed to abort task: ${e.stderr || e.message}` };
504
+ // Only ask the scheduler/launcher to stop the task when an original
505
+ // run was actually in progress. Without a pid, the live work was
506
+ // follow-up children only — already killed above — and platform
507
+ // stopTask would error out on a non-running service unit.
508
+ if (abortPrevStatus?.pid) {
509
+ try {
510
+ await getPlatform().stopTask(params.id);
511
+ }
512
+ catch (err) {
513
+ const e = err;
514
+ console.error(`task.abort failed for ${params.id}: ${e.stderr || e.message}`);
515
+ return { error: `Failed to abort task: ${e.stderr || e.message}` };
516
+ }
489
517
  }
490
518
  try {
491
519
  const aborted = parseTaskFile(abortTaskDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "palmier",
3
- "version": "0.9.26",
3
+ "version": "0.9.27",
4
4
  "description": "Palmier host CLI - provisions, executes tasks, and serves NATS RPC",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Hongxu Cai",