palmier 0.7.7 → 0.7.8
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/dist/agents/agent.d.ts +3 -0
- package/dist/agents/agent.js +1 -1
- package/dist/agents/aider.d.ts +1 -0
- package/dist/agents/aider.js +1 -0
- package/dist/agents/claude.d.ts +1 -0
- package/dist/agents/claude.js +1 -0
- package/dist/agents/cline.d.ts +1 -0
- package/dist/agents/cline.js +1 -0
- package/dist/agents/codex.d.ts +1 -0
- package/dist/agents/codex.js +1 -0
- package/dist/agents/copilot.d.ts +1 -0
- package/dist/agents/copilot.js +1 -0
- package/dist/agents/cursor.d.ts +1 -0
- package/dist/agents/cursor.js +1 -0
- package/dist/agents/deepagents.d.ts +1 -0
- package/dist/agents/deepagents.js +1 -0
- package/dist/agents/droid.d.ts +1 -0
- package/dist/agents/droid.js +1 -0
- package/dist/agents/gemini.d.ts +1 -0
- package/dist/agents/gemini.js +1 -0
- package/dist/agents/goose.d.ts +1 -0
- package/dist/agents/goose.js +1 -0
- package/dist/agents/hermes.d.ts +1 -0
- package/dist/agents/hermes.js +1 -0
- package/dist/agents/kimi.d.ts +1 -0
- package/dist/agents/kimi.js +1 -0
- package/dist/agents/kiro.d.ts +1 -0
- package/dist/agents/kiro.js +1 -0
- package/dist/agents/openclaw.d.ts +1 -0
- package/dist/agents/openclaw.js +2 -2
- package/dist/agents/opencode.d.ts +1 -0
- package/dist/agents/opencode.js +1 -0
- package/dist/agents/qoder.d.ts +1 -0
- package/dist/agents/qoder.js +1 -0
- package/dist/agents/qwen.d.ts +1 -0
- package/dist/agents/qwen.js +1 -0
- package/dist/commands/pair.js +2 -2
- package/dist/mcp-tools.js +16 -7
- package/dist/pending-requests.d.ts +30 -8
- package/dist/pending-requests.js +28 -15
- package/dist/pwa/assets/index-8cTctVnD.js +120 -0
- package/dist/pwa/assets/index-CSUkBBsQ.css +1 -0
- package/dist/pwa/assets/{web-CkWrlNwc.js → web-BNr628AV.js} +1 -1
- package/dist/pwa/assets/{web-lx34oBi7.js → web-DyQPewAi.js} +1 -1
- package/dist/pwa/index.html +2 -2
- package/dist/pwa/service-worker.js +1 -1
- package/dist/rpc-handler.js +12 -16
- package/dist/transports/http-transport.js +6 -3
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
- package/palmier-server/pwa/src/App.css +66 -0
- package/palmier-server/pwa/src/App.tsx +1 -0
- package/palmier-server/pwa/src/components/HostMenu.tsx +7 -2
- package/palmier-server/pwa/src/components/RunsView.tsx +48 -22
- package/palmier-server/pwa/src/components/SessionComposer.tsx +137 -0
- package/palmier-server/pwa/src/components/TabBar.tsx +17 -10
- package/palmier-server/pwa/src/components/TaskForm.tsx +11 -66
- package/palmier-server/pwa/src/components/TaskListView.tsx +17 -283
- package/palmier-server/pwa/src/constants.ts +1 -1
- package/palmier-server/pwa/src/draftGuard.ts +24 -0
- package/palmier-server/pwa/src/pages/Dashboard.tsx +335 -12
- package/palmier-server/pwa/src/types.ts +1 -6
- package/palmier-server/spec.md +22 -9
- package/src/agents/agent.ts +5 -1
- package/src/agents/aider.ts +1 -0
- package/src/agents/claude.ts +1 -0
- package/src/agents/cline.ts +1 -0
- package/src/agents/codex.ts +1 -0
- package/src/agents/copilot.ts +1 -0
- package/src/agents/cursor.ts +1 -0
- package/src/agents/deepagents.ts +1 -0
- package/src/agents/droid.ts +1 -0
- package/src/agents/gemini.ts +1 -0
- package/src/agents/goose.ts +1 -0
- package/src/agents/hermes.ts +1 -0
- package/src/agents/kimi.ts +1 -0
- package/src/agents/kiro.ts +1 -0
- package/src/agents/openclaw.ts +2 -2
- package/src/agents/opencode.ts +1 -0
- package/src/agents/qoder.ts +1 -0
- package/src/agents/qwen.ts +1 -0
- package/src/commands/pair.ts +2 -2
- package/src/mcp-tools.ts +16 -7
- package/src/pending-requests.ts +47 -15
- package/src/rpc-handler.ts +13 -16
- package/src/transports/http-transport.ts +6 -3
- package/src/types.ts +1 -1
- package/test/pairing.test.ts +2 -2
- package/dist/pwa/assets/index-B-ByUHPS.css +0 -1
- package/dist/pwa/assets/index-Bt8Hhaw3.js +0 -118
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { useHostConnection } from "../contexts/HostConnectionContext";
|
|
3
|
+
import { setDraftMessage } from "../draftGuard";
|
|
4
|
+
import type { AgentInfo } from "../types";
|
|
5
|
+
|
|
6
|
+
interface SessionComposerProps {
|
|
7
|
+
agents: AgentInfo[];
|
|
8
|
+
onStarted(taskId: string, runId?: string): void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function pickDefaultAgent(agents: AgentInfo[]): string {
|
|
12
|
+
const stored = localStorage.getItem("palmier:lastAgent");
|
|
13
|
+
const keys = agents.map((a) => a.key);
|
|
14
|
+
if (stored && keys.includes(stored)) return stored;
|
|
15
|
+
return agents[0]?.key ?? "";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default function SessionComposer({ agents, onStarted }: SessionComposerProps) {
|
|
19
|
+
const { request } = useHostConnection();
|
|
20
|
+
const [prompt, setPrompt] = useState("");
|
|
21
|
+
const [agent, setAgent] = useState(() => pickDefaultAgent(agents));
|
|
22
|
+
const [yoloMode, setYoloMode] = useState(false);
|
|
23
|
+
const [running, setRunning] = useState(false);
|
|
24
|
+
const [error, setError] = useState<string | null>(null);
|
|
25
|
+
|
|
26
|
+
// Keep agent selection valid as the agent list arrives/changes.
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!agents.length) return;
|
|
29
|
+
if (!agents.find((a) => a.key === agent)) {
|
|
30
|
+
setAgent(pickDefaultAgent(agents));
|
|
31
|
+
}
|
|
32
|
+
}, [agents, agent]);
|
|
33
|
+
|
|
34
|
+
// Draft guard: warns on navigation / reload when the input has content.
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const hasDraft = prompt.trim().length > 0;
|
|
37
|
+
setDraftMessage(hasDraft ? "Your session draft will be lost. Continue?" : null);
|
|
38
|
+
return () => setDraftMessage(null);
|
|
39
|
+
}, [prompt]);
|
|
40
|
+
|
|
41
|
+
const canRun = !!prompt.trim() && !!agent && !running;
|
|
42
|
+
|
|
43
|
+
function confirmYolo(): boolean {
|
|
44
|
+
if (!yoloMode) return true;
|
|
45
|
+
return confirm(
|
|
46
|
+
"Yolo mode is enabled. The agent will auto-approve all tool calls \u2014 it can read, write, delete files, run arbitrary commands, and access the network without asking for permission.\n\nAre you sure you want to continue?"
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function handleRun() {
|
|
51
|
+
if (!canRun || !confirmYolo()) return;
|
|
52
|
+
setRunning(true);
|
|
53
|
+
setError(null);
|
|
54
|
+
try {
|
|
55
|
+
const result = await request<{ task_id?: string; run_id?: string; error?: string }>(
|
|
56
|
+
"task.run_oneoff",
|
|
57
|
+
{ user_prompt: prompt, agent, yolo_mode: yoloMode },
|
|
58
|
+
);
|
|
59
|
+
if (result.error) {
|
|
60
|
+
setError(result.error);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
localStorage.setItem("palmier:lastAgent", agent);
|
|
64
|
+
setPrompt("");
|
|
65
|
+
setDraftMessage(null);
|
|
66
|
+
if (result.task_id) onStarted(result.task_id, result.run_id);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
69
|
+
} finally {
|
|
70
|
+
setRunning(false);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function handleKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>) {
|
|
75
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
handleRun();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div className="session-composer">
|
|
83
|
+
{error && <div className="form-error">{error}</div>}
|
|
84
|
+
<textarea
|
|
85
|
+
className="session-composer-textarea"
|
|
86
|
+
value={prompt}
|
|
87
|
+
onChange={(e) => setPrompt(e.target.value)}
|
|
88
|
+
onKeyDown={handleKeyDown}
|
|
89
|
+
placeholder="What can I do for you?"
|
|
90
|
+
rows={3}
|
|
91
|
+
disabled={running}
|
|
92
|
+
/>
|
|
93
|
+
<div className="session-composer-controls">
|
|
94
|
+
<div className="agent-picker-section-inline">
|
|
95
|
+
<span className="agent-picker-label">Run with</span>
|
|
96
|
+
<select
|
|
97
|
+
className="form-select form-select-sm"
|
|
98
|
+
value={agent}
|
|
99
|
+
onChange={(e) => setAgent(e.target.value)}
|
|
100
|
+
disabled={running || !agents.length}
|
|
101
|
+
>
|
|
102
|
+
{agents.map((a) => (
|
|
103
|
+
<option key={a.key} value={a.key}>{a.label}</option>
|
|
104
|
+
))}
|
|
105
|
+
</select>
|
|
106
|
+
</div>
|
|
107
|
+
<label className="session-composer-yolo">
|
|
108
|
+
<input
|
|
109
|
+
type="checkbox"
|
|
110
|
+
checked={yoloMode}
|
|
111
|
+
onChange={(e) => setYoloMode(e.target.checked)}
|
|
112
|
+
disabled={running}
|
|
113
|
+
/>
|
|
114
|
+
Yolo
|
|
115
|
+
</label>
|
|
116
|
+
<button
|
|
117
|
+
className="btn btn-primary chat-send-btn"
|
|
118
|
+
onClick={handleRun}
|
|
119
|
+
disabled={!canRun}
|
|
120
|
+
aria-label="Run session"
|
|
121
|
+
title="Run session"
|
|
122
|
+
>
|
|
123
|
+
{running ? (
|
|
124
|
+
<span className="btn-spinner" />
|
|
125
|
+
) : (
|
|
126
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="22" y1="2" x2="11" y2="13" /><polygon points="22 2 15 22 11 13 2 9 22 2" /></svg>
|
|
127
|
+
)}
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
{yoloMode && (
|
|
131
|
+
<p className="command-help-text">
|
|
132
|
+
The agent will auto-approve all tool calls without asking for permission.
|
|
133
|
+
</p>
|
|
134
|
+
)}
|
|
135
|
+
</div>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
@@ -1,30 +1,37 @@
|
|
|
1
1
|
import { useNavigate, useLocation } from "react-router-dom";
|
|
2
|
+
import { confirmLeaveDraft } from "../draftGuard";
|
|
2
3
|
|
|
3
4
|
export default function TabBar() {
|
|
4
5
|
const navigate = useNavigate();
|
|
5
6
|
const location = useLocation();
|
|
6
|
-
const
|
|
7
|
+
const isTasks = location.pathname.startsWith("/tasks");
|
|
8
|
+
const isSessions = !isTasks;
|
|
9
|
+
|
|
10
|
+
function go(path: string) {
|
|
11
|
+
if (!confirmLeaveDraft()) return;
|
|
12
|
+
navigate(path);
|
|
13
|
+
}
|
|
7
14
|
|
|
8
15
|
return (
|
|
9
16
|
<>
|
|
10
17
|
<button
|
|
11
|
-
className={`tab-btn ${
|
|
12
|
-
onClick={() =>
|
|
18
|
+
className={`tab-btn ${isSessions ? "tab-btn-active" : ""}`}
|
|
19
|
+
onClick={() => go("/")}
|
|
13
20
|
>
|
|
14
21
|
<svg className="tab-icon" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
|
15
|
-
<
|
|
16
|
-
<path d="M5.5 8L7 9.5L10.5 6" />
|
|
22
|
+
<path d="M2 8H4.5L6 4L8 12L10 6L11.5 8H14" />
|
|
17
23
|
</svg>
|
|
18
|
-
|
|
24
|
+
Sessions
|
|
19
25
|
</button>
|
|
20
26
|
<button
|
|
21
|
-
className={`tab-btn ${
|
|
22
|
-
onClick={() =>
|
|
27
|
+
className={`tab-btn ${isTasks ? "tab-btn-active" : ""}`}
|
|
28
|
+
onClick={() => go("/tasks")}
|
|
23
29
|
>
|
|
24
30
|
<svg className="tab-icon" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
|
25
|
-
<
|
|
31
|
+
<rect x="2" y="2" width="12" height="12" rx="2" />
|
|
32
|
+
<path d="M5.5 8L7 9.5L10.5 6" />
|
|
26
33
|
</svg>
|
|
27
|
-
|
|
34
|
+
Tasks
|
|
28
35
|
</button>
|
|
29
36
|
</>
|
|
30
37
|
);
|
|
@@ -64,11 +64,10 @@ interface TaskFormProps {
|
|
|
64
64
|
agents: AgentInfo[];
|
|
65
65
|
hostPlatform?: string;
|
|
66
66
|
onSaved(task: Task): void;
|
|
67
|
-
onRun(taskId: string, runId?: string): void;
|
|
68
67
|
onCancel(): void;
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
export default function TaskForm({ initial, agents, hostPlatform, onSaved,
|
|
70
|
+
export default function TaskForm({ initial, agents, hostPlatform, onSaved, onCancel }: TaskFormProps) {
|
|
72
71
|
const { request } = useHostConnection();
|
|
73
72
|
|
|
74
73
|
// Default agent: last used from localStorage, or first available
|
|
@@ -107,8 +106,7 @@ export default function TaskForm({ initial, agents, hostPlatform, onSaved, onRun
|
|
|
107
106
|
);
|
|
108
107
|
const [yoloMode, setYoloMode] = useState(initial?.yolo_mode ?? false);
|
|
109
108
|
const [foregroundMode, setForegroundMode] = useState(initial?.foreground_mode ?? false);
|
|
110
|
-
const [
|
|
111
|
-
const saving = savingAction !== null;
|
|
109
|
+
const [saving, setSaving] = useState(false);
|
|
112
110
|
|
|
113
111
|
// Command-triggered mode
|
|
114
112
|
const [commandEnabled, setCommandEnabled] = useState(!!initial?.command);
|
|
@@ -170,7 +168,7 @@ export default function TaskForm({ initial, agents, hostPlatform, onSaved, onRun
|
|
|
170
168
|
}
|
|
171
169
|
|
|
172
170
|
async function handleSave() {
|
|
173
|
-
|
|
171
|
+
setSaving(true);
|
|
174
172
|
setError(null);
|
|
175
173
|
try {
|
|
176
174
|
const method = isEdit ? "task.update" : "task.create";
|
|
@@ -202,34 +200,7 @@ export default function TaskForm({ initial, agents, hostPlatform, onSaved, onRun
|
|
|
202
200
|
setError(err instanceof Error ? err.message : String(err));
|
|
203
201
|
return null;
|
|
204
202
|
} finally {
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
async function handleRunOneoff() {
|
|
210
|
-
setSavingAction("run");
|
|
211
|
-
setError(null);
|
|
212
|
-
try {
|
|
213
|
-
const payload: Record<string, unknown> = {
|
|
214
|
-
user_prompt: userPrompt,
|
|
215
|
-
agent,
|
|
216
|
-
requires_confirmation: requiresConfirmation,
|
|
217
|
-
yolo_mode: yoloMode,
|
|
218
|
-
foreground_mode: foregroundMode,
|
|
219
|
-
command: commandEnabled ? command : "",
|
|
220
|
-
};
|
|
221
|
-
const result = await request<{ ok?: boolean; task_id?: string; run_id?: string; error?: string }>("task.run_oneoff", payload);
|
|
222
|
-
if (result.error) {
|
|
223
|
-
setError(result.error);
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
localStorage.setItem("palmier:lastAgent", agent);
|
|
227
|
-
onRun(result.task_id!, result.run_id);
|
|
228
|
-
onCancel();
|
|
229
|
-
} catch (err) {
|
|
230
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
231
|
-
} finally {
|
|
232
|
-
setSavingAction(null);
|
|
203
|
+
setSaving(false);
|
|
233
204
|
}
|
|
234
205
|
}
|
|
235
206
|
|
|
@@ -505,59 +476,33 @@ export default function TaskForm({ initial, agents, hostPlatform, onSaved, onRun
|
|
|
505
476
|
<div className="form-actions">
|
|
506
477
|
{(() => {
|
|
507
478
|
const hasSchedule = triggerRows.length > 0;
|
|
508
|
-
const
|
|
479
|
+
const label = hasSchedule ? "Schedule" : "Save";
|
|
509
480
|
if (!isEdit) {
|
|
510
|
-
|
|
511
|
-
// New task with schedule: "Schedule" only
|
|
512
|
-
return (
|
|
513
|
-
<button
|
|
514
|
-
className="btn btn-primary"
|
|
515
|
-
onClick={() => confirmYolo() && handleSave()}
|
|
516
|
-
disabled={!canSave || saving}
|
|
517
|
-
>
|
|
518
|
-
{savingAction === "save" && <span className="btn-spinner" />}
|
|
519
|
-
Schedule
|
|
520
|
-
</button>
|
|
521
|
-
);
|
|
522
|
-
}
|
|
523
|
-
// New task, no schedule: "Run" (primary) + "Save"
|
|
524
|
-
return (<>
|
|
481
|
+
return (
|
|
525
482
|
<button
|
|
526
483
|
className="btn btn-primary"
|
|
527
|
-
onClick={() => confirmYolo() && handleRunOneoff()}
|
|
528
|
-
disabled={!canRun || saving}
|
|
529
|
-
>
|
|
530
|
-
{savingAction === "run" && <span className="btn-spinner" />}
|
|
531
|
-
Run
|
|
532
|
-
</button>
|
|
533
|
-
<button
|
|
534
|
-
className="btn btn-secondary"
|
|
535
484
|
onClick={() => confirmYolo() && handleSave()}
|
|
536
485
|
disabled={!canSave || saving}
|
|
537
486
|
>
|
|
538
|
-
|
|
487
|
+
{saving && <span className="btn-spinner" />}
|
|
488
|
+
{label}
|
|
539
489
|
</button>
|
|
540
|
-
|
|
490
|
+
);
|
|
541
491
|
}
|
|
542
492
|
if (isDirty) {
|
|
543
|
-
// Edit, changed: Save only
|
|
544
493
|
return (
|
|
545
494
|
<button
|
|
546
495
|
className="btn btn-primary"
|
|
547
496
|
onClick={() => confirmYolo() && handleSave()}
|
|
548
497
|
disabled={!canSave || saving}
|
|
549
498
|
>
|
|
550
|
-
{
|
|
499
|
+
{saving && <span className="btn-spinner" />}
|
|
551
500
|
Save
|
|
552
501
|
</button>
|
|
553
502
|
);
|
|
554
503
|
}
|
|
555
|
-
// Edit, unchanged: disabled Save
|
|
556
504
|
return (
|
|
557
|
-
<button
|
|
558
|
-
className="btn btn-primary"
|
|
559
|
-
disabled
|
|
560
|
-
>
|
|
505
|
+
<button className="btn btn-primary" disabled>
|
|
561
506
|
Save
|
|
562
507
|
</button>
|
|
563
508
|
);
|