palmier 0.1.8 → 0.2.0
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/CLAUDE.md +5 -0
- package/README.md +51 -5
- package/dist/commands/hook.js +32 -5
- package/dist/commands/init.js +16 -29
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.js +81 -73
- package/dist/commands/serve.js +73 -28
- package/dist/commands/task-generation.md +28 -0
- package/dist/index.js +0 -7
- package/dist/systemd.d.ts +1 -5
- package/dist/systemd.js +54 -114
- package/dist/task.js +2 -0
- package/dist/types.d.ts +5 -24
- package/package.json +33 -35
- package/src/commands/init.ts +121 -141
- package/src/commands/run.ts +205 -197
- package/src/commands/serve.ts +287 -240
- package/src/commands/task-generation.md +28 -0
- package/src/index.ts +0 -8
- package/src/nats-client.ts +15 -15
- package/src/systemd.ts +164 -232
- package/src/task.ts +3 -0
- package/src/types.ts +41 -63
- package/src/commands/hook.ts +0 -240
package/src/commands/serve.ts
CHANGED
|
@@ -1,240 +1,287 @@
|
|
|
1
|
-
import { randomUUID } from "crypto";
|
|
2
|
-
import { execSync } from "child_process";
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return {
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
import { execSync, exec } from "child_process";
|
|
3
|
+
import { promisify } from "util";
|
|
4
|
+
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import * as path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import { StringCodec } from "nats";
|
|
10
|
+
import { loadConfig } from "../config.js";
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
|
|
14
|
+
function shellEscape(arg: string): string {
|
|
15
|
+
return "'" + arg.replace(/'/g, "'\\''") + "'";
|
|
16
|
+
}
|
|
17
|
+
const TASK_GENERATION_PROMPT = fs.readFileSync(
|
|
18
|
+
path.join(__dirname, "task-generation.md"),
|
|
19
|
+
"utf-8",
|
|
20
|
+
);
|
|
21
|
+
import { connectNats } from "../nats-client.js";
|
|
22
|
+
import { listTasks, parseTaskFile, writeTaskFile, getTaskDir } from "../task.js";
|
|
23
|
+
import { installTaskTimer, removeTaskTimer } from "../systemd.js";
|
|
24
|
+
import type { ParsedTask, RpcMessage } from "../types.js";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Start the persistent NATS RPC handler.
|
|
28
|
+
*/
|
|
29
|
+
export async function serveCommand(): Promise<void> {
|
|
30
|
+
const config = loadConfig();
|
|
31
|
+
const nc = await connectNats(config);
|
|
32
|
+
const sc = StringCodec();
|
|
33
|
+
|
|
34
|
+
const subject = `user.${config.userId}.agent.${config.agentId}.rpc.>`;
|
|
35
|
+
console.log(`Subscribing to: ${subject}`);
|
|
36
|
+
|
|
37
|
+
const sub = nc.subscribe(subject);
|
|
38
|
+
|
|
39
|
+
// Graceful shutdown
|
|
40
|
+
const shutdown = async () => {
|
|
41
|
+
console.log("Shutting down...");
|
|
42
|
+
sub.unsubscribe();
|
|
43
|
+
await nc.drain();
|
|
44
|
+
process.exit(0);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
process.on("SIGINT", shutdown);
|
|
48
|
+
process.on("SIGTERM", shutdown);
|
|
49
|
+
|
|
50
|
+
// On startup, clean up orphaned pending-confirmation keys for this agent
|
|
51
|
+
try {
|
|
52
|
+
const js = nc.jetstream();
|
|
53
|
+
const kv = await js.views.kv("pending-confirmation");
|
|
54
|
+
const keys = await kv.keys();
|
|
55
|
+
for await (const key of keys) {
|
|
56
|
+
if (key.startsWith(`${config.agentId}.`)) {
|
|
57
|
+
console.log(`Cleaning up orphaned hook key: ${key}`);
|
|
58
|
+
await kv.delete(key);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.error(`Warning: could not clean up pending-confirmation KV: ${err}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log("Agent serving. Waiting for RPC messages...");
|
|
66
|
+
|
|
67
|
+
for await (const msg of sub) {
|
|
68
|
+
// Derive RPC method from subject: ...rpc.<method parts>
|
|
69
|
+
const subjectTokens = msg.subject.split(".");
|
|
70
|
+
const rpcIdx = subjectTokens.indexOf("rpc");
|
|
71
|
+
const method = rpcIdx >= 0 ? subjectTokens.slice(rpcIdx + 1).join(".") : "";
|
|
72
|
+
|
|
73
|
+
// Parse params from message body (the PWA sends params directly, no wrapper)
|
|
74
|
+
let params: Record<string, unknown> = {};
|
|
75
|
+
if (msg.data && msg.data.length > 0) {
|
|
76
|
+
const raw = sc.decode(msg.data).trim();
|
|
77
|
+
if (raw.length > 0) {
|
|
78
|
+
try {
|
|
79
|
+
params = JSON.parse(raw);
|
|
80
|
+
} catch {
|
|
81
|
+
console.error(`Failed to parse RPC params for ${method}`);
|
|
82
|
+
if (msg.reply) {
|
|
83
|
+
msg.respond(sc.encode(JSON.stringify({ error: "Invalid JSON" })));
|
|
84
|
+
}
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log(`RPC: ${method}`);
|
|
91
|
+
|
|
92
|
+
let response: unknown;
|
|
93
|
+
try {
|
|
94
|
+
response = await handleRpc({ method, params });
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.error(`RPC error (${method}):`, err);
|
|
97
|
+
response = { error: String(err) };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
console.log(`RPC done: ${method}`, JSON.stringify(response).slice(0, 200));
|
|
101
|
+
if (msg.reply) {
|
|
102
|
+
msg.respond(sc.encode(JSON.stringify(response)));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function flattenTask(task: ParsedTask) {
|
|
107
|
+
return { ...task.frontmatter, body: task.body };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function handleRpc(request: RpcMessage): Promise<unknown> {
|
|
111
|
+
switch (request.method) {
|
|
112
|
+
case "task.list": {
|
|
113
|
+
const tasks = listTasks(config.projectRoot);
|
|
114
|
+
return {
|
|
115
|
+
tasks: tasks.map((task) => flattenTask(task)),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
case "task.create": {
|
|
120
|
+
const params = request.params as {
|
|
121
|
+
user_prompt: string;
|
|
122
|
+
command_line?: string;
|
|
123
|
+
triggers?: Array<{ type: "cron" | "once"; value: string }>;
|
|
124
|
+
triggers_enabled?: boolean;
|
|
125
|
+
requires_confirmation?: boolean;
|
|
126
|
+
body?: string;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const id = randomUUID();
|
|
130
|
+
const taskDir = getTaskDir(config.projectRoot, id);
|
|
131
|
+
const task = {
|
|
132
|
+
frontmatter: {
|
|
133
|
+
id,
|
|
134
|
+
user_prompt: params.user_prompt,
|
|
135
|
+
command_line: params.command_line ?? "claude -p --dangerously-skip-permissions",
|
|
136
|
+
triggers: params.triggers ?? [],
|
|
137
|
+
triggers_enabled: params.triggers_enabled ?? true,
|
|
138
|
+
requires_confirmation: params.requires_confirmation ?? true,
|
|
139
|
+
},
|
|
140
|
+
body: params.body || "",
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
writeTaskFile(taskDir, task);
|
|
144
|
+
if (task.frontmatter.triggers_enabled) {
|
|
145
|
+
installTaskTimer(config, task);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return flattenTask(task);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
case "task.update": {
|
|
152
|
+
const params = request.params as {
|
|
153
|
+
id: string;
|
|
154
|
+
user_prompt?: string;
|
|
155
|
+
command_line?: string;
|
|
156
|
+
triggers?: Array<{ type: "cron" | "once"; value: string }>;
|
|
157
|
+
triggers_enabled?: boolean;
|
|
158
|
+
requires_confirmation?: boolean;
|
|
159
|
+
body?: string;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const taskDir = getTaskDir(config.projectRoot, params.id);
|
|
163
|
+
const existing = parseTaskFile(taskDir);
|
|
164
|
+
|
|
165
|
+
// Merge updates
|
|
166
|
+
if (params.user_prompt !== undefined) existing.frontmatter.user_prompt = params.user_prompt;
|
|
167
|
+
if (params.command_line !== undefined) existing.frontmatter.command_line = params.command_line;
|
|
168
|
+
if (params.triggers !== undefined) existing.frontmatter.triggers = params.triggers;
|
|
169
|
+
if (params.triggers_enabled !== undefined) existing.frontmatter.triggers_enabled = params.triggers_enabled;
|
|
170
|
+
if (params.requires_confirmation !== undefined)
|
|
171
|
+
existing.frontmatter.requires_confirmation = params.requires_confirmation;
|
|
172
|
+
if (params.body !== undefined) existing.body = params.body;
|
|
173
|
+
|
|
174
|
+
writeTaskFile(taskDir, existing);
|
|
175
|
+
|
|
176
|
+
// Reinstall or remove timers based on triggers_enabled
|
|
177
|
+
removeTaskTimer(params.id);
|
|
178
|
+
if (existing.frontmatter.triggers_enabled) {
|
|
179
|
+
installTaskTimer(config, existing);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return flattenTask(existing);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
case "task.delete": {
|
|
186
|
+
const params = request.params as { id: string };
|
|
187
|
+
const taskDir = getTaskDir(config.projectRoot, params.id);
|
|
188
|
+
|
|
189
|
+
removeTaskTimer(params.id);
|
|
190
|
+
|
|
191
|
+
// Remove task directory
|
|
192
|
+
if (fs.existsSync(taskDir)) {
|
|
193
|
+
fs.rmSync(taskDir, { recursive: true, force: true });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return { ok: true, task_id: params.id };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
case "task.generate": {
|
|
200
|
+
const params = request.params as { prompt: string; command_line?: string };
|
|
201
|
+
const commandLine = params.command_line || "claude -p --dangerously-skip-permissions";
|
|
202
|
+
const fullPrompt = TASK_GENERATION_PROMPT + params.prompt;
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const output = execSync(`${commandLine} ${shellEscape(fullPrompt)}`, {
|
|
206
|
+
encoding: "utf-8",
|
|
207
|
+
cwd: config.projectRoot,
|
|
208
|
+
timeout: 120_000,
|
|
209
|
+
});
|
|
210
|
+
return { ok: true, body: output };
|
|
211
|
+
} catch (err: unknown) {
|
|
212
|
+
const error = err as { stdout?: string; stderr?: string };
|
|
213
|
+
return { error: "generation command failed", stdout: error.stdout, stderr: error.stderr };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
case "task.run": {
|
|
218
|
+
const params = request.params as { id: string };
|
|
219
|
+
const serviceName = `palmier-task-${params.id}.service`;
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
await execAsync(`systemctl --user start --no-block ${serviceName}`);
|
|
223
|
+
return { ok: true, task_id: params.id };
|
|
224
|
+
} catch (err: unknown) {
|
|
225
|
+
const e = err as { stderr?: string; message?: string };
|
|
226
|
+
console.error(`task.run failed for ${params.id}: ${e.stderr || e.message}`);
|
|
227
|
+
return { error: `Failed to start task: ${e.stderr || e.message}` };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
case "task.abort": {
|
|
232
|
+
const params = request.params as { id: string };
|
|
233
|
+
const serviceName = `palmier-task-${params.id}.service`;
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
await execAsync(`systemctl --user stop ${serviceName}`);
|
|
237
|
+
return { ok: true, task_id: params.id };
|
|
238
|
+
} catch (err: unknown) {
|
|
239
|
+
const e = err as { stderr?: string; message?: string };
|
|
240
|
+
console.error(`task.abort failed for ${params.id}: ${e.stderr || e.message}`);
|
|
241
|
+
return { error: `Failed to abort task: ${e.stderr || e.message}` };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
case "task.logs": {
|
|
246
|
+
const params = request.params as { id: string };
|
|
247
|
+
const serviceName = `palmier-task-${params.id}.service`;
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
const logs = execSync(
|
|
251
|
+
`journalctl --user -u ${serviceName} -n 100 --no-pager`,
|
|
252
|
+
{ encoding: "utf-8" }
|
|
253
|
+
);
|
|
254
|
+
return { task_id: params.id, logs };
|
|
255
|
+
} catch (err: unknown) {
|
|
256
|
+
const error = err as { stdout?: string; stderr?: string };
|
|
257
|
+
return { task_id: params.id, logs: error.stdout || "", error: error.stderr };
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
case "task.result": {
|
|
262
|
+
const params = request.params as { id: string };
|
|
263
|
+
const resultPath = path.join(config.projectRoot, "tasks", params.id, "RESULT.md");
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
const raw = fs.readFileSync(resultPath, "utf-8");
|
|
267
|
+
// Parse optional frontmatter
|
|
268
|
+
const fmMatch = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
269
|
+
if (fmMatch) {
|
|
270
|
+
const meta: Record<string, number> = {};
|
|
271
|
+
for (const line of fmMatch[1].split("\n")) {
|
|
272
|
+
const [key, val] = line.split(": ");
|
|
273
|
+
if (key && val) meta[key.trim()] = Number(val.trim());
|
|
274
|
+
}
|
|
275
|
+
return { task_id: params.id, content: fmMatch[2], start_time: meta.start_time, end_time: meta.end_time };
|
|
276
|
+
}
|
|
277
|
+
return { task_id: params.id, content: raw };
|
|
278
|
+
} catch {
|
|
279
|
+
return { task_id: params.id, error: "No result file found" };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
default:
|
|
284
|
+
return { error: `Unknown method: ${request.method}` };
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
You are a planning agent for a personal computer AI agent. Given a task description, produce a detailed Markdown execution plan that the agent can later follow step by step. **Do not execute any part of the task yourself.**
|
|
2
|
+
|
|
3
|
+
The plan must include the following sections:
|
|
4
|
+
|
|
5
|
+
### 1. Goal
|
|
6
|
+
What the task accomplishes and the expected end state.
|
|
7
|
+
|
|
8
|
+
### 2. Prerequisites
|
|
9
|
+
What must be true before starting:
|
|
10
|
+
- Required software and versions
|
|
11
|
+
- Files or data that must be present
|
|
12
|
+
- Permissions or access needed
|
|
13
|
+
- Environment state (e.g., running services, network access)
|
|
14
|
+
|
|
15
|
+
### 3. Plan
|
|
16
|
+
A numbered sequence of concrete, actionable steps to complete the task.
|
|
17
|
+
Use sub-steps for complex actions. Include conditional branches where behavior may vary (e.g., "If file exists, do A; otherwise, do B"). Each step should be specific enough that the agent can execute it without ambiguity.
|
|
18
|
+
|
|
19
|
+
### 4. Edge Cases & Risks
|
|
20
|
+
Anything that could go wrong and how to handle it:
|
|
21
|
+
- Common failure modes
|
|
22
|
+
- Platform-specific differences
|
|
23
|
+
- Race conditions or timing issues
|
|
24
|
+
- Data loss risks and mitigation
|
|
25
|
+
|
|
26
|
+
Format the entire document as Markdown with proper headings, code blocks for commands, and tables where appropriate.
|
|
27
|
+
|
|
28
|
+
**Task description:**
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { dirname, join } from "path";
|
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
import { initCommand } from "./commands/init.js";
|
|
9
9
|
import { runCommand } from "./commands/run.js";
|
|
10
|
-
import { hookCommand } from "./commands/hook.js";
|
|
11
10
|
import { serveCommand } from "./commands/serve.js";
|
|
12
11
|
|
|
13
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -35,13 +34,6 @@ program
|
|
|
35
34
|
await runCommand(taskId);
|
|
36
35
|
});
|
|
37
36
|
|
|
38
|
-
program
|
|
39
|
-
.command("hook")
|
|
40
|
-
.description("Handle a Claude Code hook event (reads from stdin)")
|
|
41
|
-
.action(async () => {
|
|
42
|
-
await hookCommand();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
37
|
program
|
|
46
38
|
.command("serve", { isDefault: true })
|
|
47
39
|
.description("Start the persistent NATS RPC handler")
|
package/src/nats-client.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { connect, type NatsConnection } from "nats";
|
|
2
|
-
import type { AgentConfig } from "./types.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Connect to NATS using the agent config's TCP URL and token auth.
|
|
6
|
-
*/
|
|
7
|
-
export async function connectNats(config: AgentConfig): Promise<NatsConnection> {
|
|
8
|
-
const nc = await connect({
|
|
9
|
-
servers: config.natsUrl,
|
|
10
|
-
token: config.natsToken,
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
console.log(`Connected to NATS at ${config.natsUrl}`);
|
|
14
|
-
return nc;
|
|
15
|
-
}
|
|
1
|
+
import { connect, type NatsConnection } from "nats";
|
|
2
|
+
import type { AgentConfig } from "./types.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Connect to NATS using the agent config's TCP URL and token auth.
|
|
6
|
+
*/
|
|
7
|
+
export async function connectNats(config: AgentConfig): Promise<NatsConnection> {
|
|
8
|
+
const nc = await connect({
|
|
9
|
+
servers: config.natsUrl,
|
|
10
|
+
token: config.natsToken,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
console.log(`Connected to NATS at ${config.natsUrl}`);
|
|
14
|
+
return nc;
|
|
15
|
+
}
|