palmier 0.1.4 → 0.1.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/dist/commands/serve.js +38 -23
- package/package.json +1 -1
- package/src/commands/serve.ts +48 -29
package/dist/commands/serve.js
CHANGED
|
@@ -41,49 +41,64 @@ export async function serveCommand() {
|
|
|
41
41
|
}
|
|
42
42
|
console.log("Agent serving. Waiting for RPC messages...");
|
|
43
43
|
for await (const msg of sub) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
// Derive RPC method from subject: ...rpc.<method parts>
|
|
45
|
+
const subjectTokens = msg.subject.split(".");
|
|
46
|
+
const rpcIdx = subjectTokens.indexOf("rpc");
|
|
47
|
+
const method = rpcIdx >= 0 ? subjectTokens.slice(rpcIdx + 1).join(".") : "";
|
|
48
|
+
// Parse params from message body (the PWA sends params directly, no wrapper)
|
|
49
|
+
let params = {};
|
|
50
|
+
if (msg.data && msg.data.length > 0) {
|
|
51
|
+
const raw = sc.decode(msg.data).trim();
|
|
52
|
+
if (raw.length > 0) {
|
|
53
|
+
try {
|
|
54
|
+
params = JSON.parse(raw);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
console.error(`Failed to parse RPC params for ${method}`);
|
|
58
|
+
if (msg.reply) {
|
|
59
|
+
msg.respond(sc.encode(JSON.stringify({ error: "Invalid JSON" })));
|
|
60
|
+
}
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
52
63
|
}
|
|
53
|
-
continue;
|
|
54
64
|
}
|
|
55
|
-
console.log(`RPC: ${
|
|
65
|
+
console.log(`RPC: ${method}`);
|
|
56
66
|
let response;
|
|
57
67
|
try {
|
|
58
|
-
response = await handleRpc(
|
|
68
|
+
response = await handleRpc({ method, params });
|
|
59
69
|
}
|
|
60
70
|
catch (err) {
|
|
61
|
-
console.error(`RPC error (${
|
|
71
|
+
console.error(`RPC error (${method}):`, err);
|
|
62
72
|
response = { error: String(err) };
|
|
63
73
|
}
|
|
64
74
|
if (msg.reply) {
|
|
65
75
|
msg.respond(sc.encode(JSON.stringify(response)));
|
|
66
76
|
}
|
|
67
77
|
}
|
|
78
|
+
function toKebab(str) {
|
|
79
|
+
return str.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
80
|
+
}
|
|
81
|
+
function flattenTask(task, status) {
|
|
82
|
+
return { ...task.frontmatter, body: task.body, ...(status != null ? { status } : {}) };
|
|
83
|
+
}
|
|
68
84
|
async function handleRpc(request) {
|
|
69
85
|
switch (request.method) {
|
|
70
86
|
case "task.list": {
|
|
71
87
|
const tasks = listTasks(config.projectRoot);
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}));
|
|
76
|
-
return { tasks: tasksWithStatus };
|
|
88
|
+
return {
|
|
89
|
+
tasks: tasks.map((task) => flattenTask(task, getTaskStatus(task.frontmatter.id))),
|
|
90
|
+
};
|
|
77
91
|
}
|
|
78
92
|
case "task.create": {
|
|
79
93
|
const params = request.params;
|
|
80
|
-
const
|
|
94
|
+
const id = toKebab(params.name);
|
|
95
|
+
const taskDir = getTaskDir(config.projectRoot, id);
|
|
81
96
|
const task = {
|
|
82
97
|
frontmatter: {
|
|
83
|
-
id
|
|
98
|
+
id,
|
|
84
99
|
name: params.name,
|
|
85
100
|
user_prompt: params.user_prompt,
|
|
86
|
-
triggers: params.triggers
|
|
101
|
+
triggers: params.triggers ?? [],
|
|
87
102
|
requires_confirmation: params.requires_confirmation ?? true,
|
|
88
103
|
suppress_permissions: params.suppress_permissions ?? false,
|
|
89
104
|
enabled: params.enabled ?? true,
|
|
@@ -92,7 +107,7 @@ export async function serveCommand() {
|
|
|
92
107
|
};
|
|
93
108
|
writeTaskFile(taskDir, task);
|
|
94
109
|
installTaskTimer(config, task);
|
|
95
|
-
return
|
|
110
|
+
return flattenTask(task);
|
|
96
111
|
}
|
|
97
112
|
case "task.update": {
|
|
98
113
|
const params = request.params;
|
|
@@ -117,7 +132,7 @@ export async function serveCommand() {
|
|
|
117
132
|
// Reinstall timer with updated config
|
|
118
133
|
removeTaskTimer(params.id);
|
|
119
134
|
installTaskTimer(config, existing);
|
|
120
|
-
return
|
|
135
|
+
return flattenTask(existing, getTaskStatus(params.id));
|
|
121
136
|
}
|
|
122
137
|
case "task.delete": {
|
|
123
138
|
const params = request.params;
|
|
@@ -137,7 +152,7 @@ export async function serveCommand() {
|
|
|
137
152
|
cwd: config.projectRoot,
|
|
138
153
|
timeout: 120_000,
|
|
139
154
|
});
|
|
140
|
-
return { ok: true, output };
|
|
155
|
+
return { ok: true, body: output };
|
|
141
156
|
}
|
|
142
157
|
catch (err) {
|
|
143
158
|
const error = err;
|
package/package.json
CHANGED
package/src/commands/serve.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { loadConfig } from "../config.js";
|
|
|
5
5
|
import { connectNats } from "../nats-client.js";
|
|
6
6
|
import { listTasks, parseTaskFile, writeTaskFile, getTaskDir } from "../task.js";
|
|
7
7
|
import { installTaskTimer, removeTaskTimer, getTaskStatus } from "../systemd.js";
|
|
8
|
-
import type {
|
|
8
|
+
import type { ParsedTask, RpcMessage } from "../types.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Start the persistent NATS RPC handler.
|
|
@@ -49,24 +49,35 @@ export async function serveCommand(): Promise<void> {
|
|
|
49
49
|
console.log("Agent serving. Waiting for RPC messages...");
|
|
50
50
|
|
|
51
51
|
for await (const msg of sub) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
// Derive RPC method from subject: ...rpc.<method parts>
|
|
53
|
+
const subjectTokens = msg.subject.split(".");
|
|
54
|
+
const rpcIdx = subjectTokens.indexOf("rpc");
|
|
55
|
+
const method = rpcIdx >= 0 ? subjectTokens.slice(rpcIdx + 1).join(".") : "";
|
|
56
|
+
|
|
57
|
+
// Parse params from message body (the PWA sends params directly, no wrapper)
|
|
58
|
+
let params: Record<string, unknown> = {};
|
|
59
|
+
if (msg.data && msg.data.length > 0) {
|
|
60
|
+
const raw = sc.decode(msg.data).trim();
|
|
61
|
+
if (raw.length > 0) {
|
|
62
|
+
try {
|
|
63
|
+
params = JSON.parse(raw);
|
|
64
|
+
} catch {
|
|
65
|
+
console.error(`Failed to parse RPC params for ${method}`);
|
|
66
|
+
if (msg.reply) {
|
|
67
|
+
msg.respond(sc.encode(JSON.stringify({ error: "Invalid JSON" })));
|
|
68
|
+
}
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
59
71
|
}
|
|
60
|
-
continue;
|
|
61
72
|
}
|
|
62
73
|
|
|
63
|
-
console.log(`RPC: ${
|
|
74
|
+
console.log(`RPC: ${method}`);
|
|
64
75
|
|
|
65
76
|
let response: unknown;
|
|
66
77
|
try {
|
|
67
|
-
response = await handleRpc(
|
|
78
|
+
response = await handleRpc({ method, params });
|
|
68
79
|
} catch (err) {
|
|
69
|
-
console.error(`RPC error (${
|
|
80
|
+
console.error(`RPC error (${method}):`, err);
|
|
70
81
|
response = { error: String(err) };
|
|
71
82
|
}
|
|
72
83
|
|
|
@@ -75,36 +86,44 @@ export async function serveCommand(): Promise<void> {
|
|
|
75
86
|
}
|
|
76
87
|
}
|
|
77
88
|
|
|
89
|
+
function toKebab(str: string): string {
|
|
90
|
+
return str.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function flattenTask(task: ParsedTask, status?: unknown) {
|
|
94
|
+
return { ...task.frontmatter, body: task.body, ...(status != null ? { status } : {}) };
|
|
95
|
+
}
|
|
96
|
+
|
|
78
97
|
async function handleRpc(request: RpcMessage): Promise<unknown> {
|
|
79
98
|
switch (request.method) {
|
|
80
99
|
case "task.list": {
|
|
81
100
|
const tasks = listTasks(config.projectRoot);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
101
|
+
return {
|
|
102
|
+
tasks: tasks.map((task) =>
|
|
103
|
+
flattenTask(task, getTaskStatus(task.frontmatter.id))
|
|
104
|
+
),
|
|
105
|
+
};
|
|
87
106
|
}
|
|
88
107
|
|
|
89
108
|
case "task.create": {
|
|
90
109
|
const params = request.params as {
|
|
91
|
-
id: string;
|
|
92
110
|
name: string;
|
|
93
111
|
user_prompt: string;
|
|
94
|
-
triggers
|
|
95
|
-
requires_confirmation
|
|
96
|
-
suppress_permissions
|
|
97
|
-
enabled
|
|
98
|
-
body
|
|
112
|
+
triggers?: Array<{ type: "cron" | "once"; value: string }>;
|
|
113
|
+
requires_confirmation?: boolean;
|
|
114
|
+
suppress_permissions?: boolean;
|
|
115
|
+
enabled?: boolean;
|
|
116
|
+
body?: string;
|
|
99
117
|
};
|
|
100
118
|
|
|
101
|
-
const
|
|
119
|
+
const id = toKebab(params.name);
|
|
120
|
+
const taskDir = getTaskDir(config.projectRoot, id);
|
|
102
121
|
const task = {
|
|
103
122
|
frontmatter: {
|
|
104
|
-
id
|
|
123
|
+
id,
|
|
105
124
|
name: params.name,
|
|
106
125
|
user_prompt: params.user_prompt,
|
|
107
|
-
triggers: params.triggers
|
|
126
|
+
triggers: params.triggers ?? [],
|
|
108
127
|
requires_confirmation: params.requires_confirmation ?? true,
|
|
109
128
|
suppress_permissions: params.suppress_permissions ?? false,
|
|
110
129
|
enabled: params.enabled ?? true,
|
|
@@ -115,7 +134,7 @@ export async function serveCommand(): Promise<void> {
|
|
|
115
134
|
writeTaskFile(taskDir, task);
|
|
116
135
|
installTaskTimer(config, task);
|
|
117
136
|
|
|
118
|
-
return
|
|
137
|
+
return flattenTask(task);
|
|
119
138
|
}
|
|
120
139
|
|
|
121
140
|
case "task.update": {
|
|
@@ -150,7 +169,7 @@ export async function serveCommand(): Promise<void> {
|
|
|
150
169
|
removeTaskTimer(params.id);
|
|
151
170
|
installTaskTimer(config, existing);
|
|
152
171
|
|
|
153
|
-
return
|
|
172
|
+
return flattenTask(existing, getTaskStatus(params.id));
|
|
154
173
|
}
|
|
155
174
|
|
|
156
175
|
case "task.delete": {
|
|
@@ -176,7 +195,7 @@ export async function serveCommand(): Promise<void> {
|
|
|
176
195
|
cwd: config.projectRoot,
|
|
177
196
|
timeout: 120_000,
|
|
178
197
|
});
|
|
179
|
-
return { ok: true, output };
|
|
198
|
+
return { ok: true, body: output };
|
|
180
199
|
} catch (err: unknown) {
|
|
181
200
|
const error = err as { stdout?: string; stderr?: string };
|
|
182
201
|
return { error: "claude command failed", stdout: error.stdout, stderr: error.stderr };
|