palmier 0.5.5 → 0.5.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 +1 -1
- package/dist/agents/codex.js +1 -2
- package/dist/commands/run.d.ts +0 -4
- package/dist/commands/run.js +15 -2
- package/dist/rpc-handler.js +25 -4
- package/package.json +1 -1
- package/src/agents/codex.ts +1 -2
- package/src/commands/run.ts +15 -1
- package/src/rpc-handler.ts +25 -5
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ The serve daemon always runs a local HTTP server. Three access modes are availab
|
|
|
22
22
|
|
|
23
23
|
**Local mode** is always available. The PWA is served at `http://localhost:<port>` and works without pairing or internet. The daemon binds to `127.0.0.1` by default.
|
|
24
24
|
|
|
25
|
-
**LAN mode**
|
|
25
|
+
**LAN mode** can be enabled during `palmier init`. The daemon binds to `0.0.0.0` instead, making the PWA and API endpoints accessible from the local network at `http://<host-ip>:<port>`. Devices must pair via OTP to access. Push notifications are not available.
|
|
26
26
|
|
|
27
27
|
**Server mode** relays communication through the Palmier cloud server (via [NATS](https://nats.io), a lightweight messaging system). All features including push notifications are available. The PWA is served over HTTPS. Server mode and LAN mode can be active at the same time.
|
|
28
28
|
|
package/dist/agents/codex.js
CHANGED
|
@@ -12,8 +12,7 @@ export class CodexAgent {
|
|
|
12
12
|
getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
|
|
13
13
|
const yolo = extraPermissions === "yolo";
|
|
14
14
|
const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
|
|
15
|
-
|
|
16
|
-
const args = ["exec", "--skip-git-repo-check", "--sandbox", "danger-full-access"];
|
|
15
|
+
const args = ["exec", "--skip-git-repo-check", "--sandbox", yolo ? "danger-full-access" : "workspace-write"];
|
|
17
16
|
if (!yolo) {
|
|
18
17
|
const allPerms = [...(task.frontmatter.permissions ?? []), ...(extraPermissions ?? [])];
|
|
19
18
|
for (const p of allPerms) {
|
package/dist/commands/run.d.ts
CHANGED
|
@@ -7,10 +7,6 @@ export declare function stripPalmierMarkers(output: string): string;
|
|
|
7
7
|
* Execute a task by ID.
|
|
8
8
|
*/
|
|
9
9
|
export declare function runCommand(taskId: string): Promise<void>;
|
|
10
|
-
/**
|
|
11
|
-
* Extract report file names from agent output.
|
|
12
|
-
* Looks for lines matching: [PALMIER_REPORT] <filename>
|
|
13
|
-
*/
|
|
14
10
|
export declare function parseReportFiles(output: string): string[];
|
|
15
11
|
/**
|
|
16
12
|
* Extract required permissions from agent output.
|
package/dist/commands/run.js
CHANGED
|
@@ -70,6 +70,14 @@ async function invokeAgentWithRetries(ctx, invokeTask) {
|
|
|
70
70
|
}
|
|
71
71
|
writer.end(reportFiles.length > 0 ? reportFiles : undefined);
|
|
72
72
|
await publishHostEvent(ctx.nc, ctx.config.hostId, ctx.taskId, { event_type: "result-updated", run_id: ctx.runId });
|
|
73
|
+
if (reportFiles.length > 0) {
|
|
74
|
+
await publishHostEvent(ctx.nc, ctx.config.hostId, ctx.taskId, {
|
|
75
|
+
event_type: "report-generated",
|
|
76
|
+
run_id: ctx.runId,
|
|
77
|
+
name: ctx.task.frontmatter.name,
|
|
78
|
+
report_files: reportFiles,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
73
81
|
// Permission handling — agent requested permissions
|
|
74
82
|
if (requiredPermissions.length > 0) {
|
|
75
83
|
const response = await requestPermission(ctx.config, ctx.task, ctx.taskDir, requiredPermissions);
|
|
@@ -408,6 +416,7 @@ async function requestConfirmation(config, task, taskDir) {
|
|
|
408
416
|
* Extract report file names from agent output.
|
|
409
417
|
* Looks for lines matching: [PALMIER_REPORT] <filename>
|
|
410
418
|
*/
|
|
419
|
+
const ALLOWED_REPORT_EXT = [".md", ".txt", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"];
|
|
411
420
|
export function parseReportFiles(output) {
|
|
412
421
|
const regex = new RegExp(`^\\${TASK_REPORT_PREFIX}\\s+(.+)$`, "gm");
|
|
413
422
|
const files = [];
|
|
@@ -415,8 +424,12 @@ export function parseReportFiles(output) {
|
|
|
415
424
|
while ((match = regex.exec(output)) !== null) {
|
|
416
425
|
const name = match[1].trim();
|
|
417
426
|
// Skip placeholder examples echoed from the prompt (e.g. "<filename>")
|
|
418
|
-
if (name
|
|
419
|
-
|
|
427
|
+
if (!name || name.startsWith("<"))
|
|
428
|
+
continue;
|
|
429
|
+
const ext = name.lastIndexOf(".") >= 0 ? name.slice(name.lastIndexOf(".")).toLowerCase() : "";
|
|
430
|
+
if (!ALLOWED_REPORT_EXT.includes(ext))
|
|
431
|
+
continue;
|
|
432
|
+
files.push(name);
|
|
420
433
|
}
|
|
421
434
|
return files;
|
|
422
435
|
}
|
package/dist/rpc-handler.js
CHANGED
|
@@ -154,6 +154,17 @@ export function createRpcHandler(config, nc) {
|
|
|
154
154
|
host_platform: process.platform,
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
|
+
case "task.get": {
|
|
158
|
+
const params = request.params;
|
|
159
|
+
const taskDir = getTaskDir(config.projectRoot, params.id);
|
|
160
|
+
try {
|
|
161
|
+
const task = parseTaskFile(taskDir);
|
|
162
|
+
return flattenTask(task);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return { error: "Task not found" };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
157
168
|
case "task.create": {
|
|
158
169
|
const params = request.params;
|
|
159
170
|
// Only generate a plan for longer prompts that benefit from it
|
|
@@ -497,11 +508,14 @@ export function createRpcHandler(config, nc) {
|
|
|
497
508
|
if (!params.run_id || !Array.isArray(params.report_files) || params.report_files.length === 0) {
|
|
498
509
|
return { error: "run_id and report_files are required" };
|
|
499
510
|
}
|
|
511
|
+
const ALLOWED_EXT = [".md", ".txt", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"];
|
|
512
|
+
const IMAGE_EXT = [".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"];
|
|
500
513
|
const reports = [];
|
|
501
514
|
const runDir = path.join(config.projectRoot, "tasks", params.id, params.run_id);
|
|
502
515
|
for (const file of params.report_files) {
|
|
503
|
-
|
|
504
|
-
|
|
516
|
+
const ext = path.extname(file).toLowerCase();
|
|
517
|
+
if (!ALLOWED_EXT.includes(ext)) {
|
|
518
|
+
reports.push({ file, error: `unsupported file type: ${ext}` });
|
|
505
519
|
continue;
|
|
506
520
|
}
|
|
507
521
|
const basename = path.basename(file);
|
|
@@ -511,8 +525,15 @@ export function createRpcHandler(config, nc) {
|
|
|
511
525
|
}
|
|
512
526
|
const reportPath = path.join(runDir, basename);
|
|
513
527
|
try {
|
|
514
|
-
|
|
515
|
-
|
|
528
|
+
if (IMAGE_EXT.includes(ext)) {
|
|
529
|
+
const buf = fs.readFileSync(reportPath);
|
|
530
|
+
const mime = ext === ".svg" ? "image/svg+xml" : `image/${ext.slice(1).replace("jpg", "jpeg")}`;
|
|
531
|
+
reports.push({ file, data_url: `data:${mime};base64,${buf.toString("base64")}` });
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
const content = fs.readFileSync(reportPath, "utf-8");
|
|
535
|
+
reports.push({ file, content });
|
|
536
|
+
}
|
|
516
537
|
}
|
|
517
538
|
catch {
|
|
518
539
|
reports.push({ file, error: "Report file not found" });
|
package/package.json
CHANGED
package/src/agents/codex.ts
CHANGED
|
@@ -16,8 +16,7 @@ export class CodexAgent implements AgentTool {
|
|
|
16
16
|
getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
|
|
17
17
|
const yolo = extraPermissions === "yolo";
|
|
18
18
|
const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
|
|
19
|
-
|
|
20
|
-
const args = ["exec", "--skip-git-repo-check", "--sandbox", "danger-full-access"];
|
|
19
|
+
const args = ["exec", "--skip-git-repo-check", "--sandbox", yolo ? "danger-full-access" : "workspace-write"];
|
|
21
20
|
|
|
22
21
|
if (!yolo) {
|
|
23
22
|
const allPerms = [...(task.frontmatter.permissions ?? []), ...(extraPermissions ?? [])];
|
package/src/commands/run.ts
CHANGED
|
@@ -106,6 +106,15 @@ async function invokeAgentWithRetries(
|
|
|
106
106
|
writer.end(reportFiles.length > 0 ? reportFiles : undefined);
|
|
107
107
|
await publishHostEvent(ctx.nc, ctx.config.hostId, ctx.taskId, { event_type: "result-updated", run_id: ctx.runId });
|
|
108
108
|
|
|
109
|
+
if (reportFiles.length > 0) {
|
|
110
|
+
await publishHostEvent(ctx.nc, ctx.config.hostId, ctx.taskId, {
|
|
111
|
+
event_type: "report-generated",
|
|
112
|
+
run_id: ctx.runId,
|
|
113
|
+
name: ctx.task.frontmatter.name,
|
|
114
|
+
report_files: reportFiles,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
109
118
|
// Permission handling — agent requested permissions
|
|
110
119
|
if (requiredPermissions.length > 0) {
|
|
111
120
|
const response = await requestPermission(ctx.config, ctx.task, ctx.taskDir, requiredPermissions);
|
|
@@ -499,6 +508,8 @@ async function requestConfirmation(
|
|
|
499
508
|
* Extract report file names from agent output.
|
|
500
509
|
* Looks for lines matching: [PALMIER_REPORT] <filename>
|
|
501
510
|
*/
|
|
511
|
+
const ALLOWED_REPORT_EXT = [".md", ".txt", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"];
|
|
512
|
+
|
|
502
513
|
export function parseReportFiles(output: string): string[] {
|
|
503
514
|
const regex = new RegExp(`^\\${TASK_REPORT_PREFIX}\\s+(.+)$`, "gm");
|
|
504
515
|
const files: string[] = [];
|
|
@@ -506,7 +517,10 @@ export function parseReportFiles(output: string): string[] {
|
|
|
506
517
|
while ((match = regex.exec(output)) !== null) {
|
|
507
518
|
const name = match[1].trim();
|
|
508
519
|
// Skip placeholder examples echoed from the prompt (e.g. "<filename>")
|
|
509
|
-
if (name
|
|
520
|
+
if (!name || name.startsWith("<")) continue;
|
|
521
|
+
const ext = name.lastIndexOf(".") >= 0 ? name.slice(name.lastIndexOf(".")).toLowerCase() : "";
|
|
522
|
+
if (!ALLOWED_REPORT_EXT.includes(ext)) continue;
|
|
523
|
+
files.push(name);
|
|
510
524
|
}
|
|
511
525
|
return files;
|
|
512
526
|
}
|
package/src/rpc-handler.ts
CHANGED
|
@@ -182,6 +182,17 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
|
|
|
182
182
|
};
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
case "task.get": {
|
|
186
|
+
const params = request.params as { id: string };
|
|
187
|
+
const taskDir = getTaskDir(config.projectRoot, params.id);
|
|
188
|
+
try {
|
|
189
|
+
const task = parseTaskFile(taskDir);
|
|
190
|
+
return flattenTask(task);
|
|
191
|
+
} catch {
|
|
192
|
+
return { error: "Task not found" };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
185
196
|
case "task.create": {
|
|
186
197
|
const params = request.params as {
|
|
187
198
|
user_prompt: string;
|
|
@@ -577,11 +588,14 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
|
|
|
577
588
|
if (!params.run_id || !Array.isArray(params.report_files) || params.report_files.length === 0) {
|
|
578
589
|
return { error: "run_id and report_files are required" };
|
|
579
590
|
}
|
|
580
|
-
const
|
|
591
|
+
const ALLOWED_EXT = [".md", ".txt", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"];
|
|
592
|
+
const IMAGE_EXT = [".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"];
|
|
593
|
+
const reports: Array<{ file: string; content?: string; data_url?: string; error?: string }> = [];
|
|
581
594
|
const runDir = path.join(config.projectRoot, "tasks", params.id, params.run_id);
|
|
582
595
|
for (const file of params.report_files) {
|
|
583
|
-
|
|
584
|
-
|
|
596
|
+
const ext = path.extname(file).toLowerCase();
|
|
597
|
+
if (!ALLOWED_EXT.includes(ext)) {
|
|
598
|
+
reports.push({ file, error: `unsupported file type: ${ext}` });
|
|
585
599
|
continue;
|
|
586
600
|
}
|
|
587
601
|
const basename = path.basename(file);
|
|
@@ -591,8 +605,14 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
|
|
|
591
605
|
}
|
|
592
606
|
const reportPath = path.join(runDir, basename);
|
|
593
607
|
try {
|
|
594
|
-
|
|
595
|
-
|
|
608
|
+
if (IMAGE_EXT.includes(ext)) {
|
|
609
|
+
const buf = fs.readFileSync(reportPath);
|
|
610
|
+
const mime = ext === ".svg" ? "image/svg+xml" : `image/${ext.slice(1).replace("jpg", "jpeg")}`;
|
|
611
|
+
reports.push({ file, data_url: `data:${mime};base64,${buf.toString("base64")}` });
|
|
612
|
+
} else {
|
|
613
|
+
const content = fs.readFileSync(reportPath, "utf-8");
|
|
614
|
+
reports.push({ file, content });
|
|
615
|
+
}
|
|
596
616
|
} catch {
|
|
597
617
|
reports.push({ file, error: "Report file not found" });
|
|
598
618
|
}
|