palmier 0.4.5 → 0.4.7
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 +29 -31
- package/dist/agents/agent-instructions.md +9 -9
- package/dist/agents/claude.js +3 -3
- package/dist/agents/codex.js +3 -3
- package/dist/agents/copilot.js +3 -6
- package/dist/agents/gemini.js +4 -5
- package/dist/agents/openclaw.js +2 -2
- package/dist/agents/shared-prompt.d.ts +2 -4
- package/dist/agents/shared-prompt.js +9 -4
- package/dist/commands/init.js +31 -2
- package/dist/commands/pair.d.ts +1 -1
- package/dist/commands/pair.js +12 -15
- package/dist/commands/plan-generation.md +12 -15
- package/dist/commands/run.js +23 -44
- package/dist/commands/serve.d.ts +1 -1
- package/dist/commands/serve.js +9 -2
- package/dist/events.d.ts +2 -2
- package/dist/events.js +15 -16
- package/dist/index.js +0 -25
- package/dist/pending-requests.d.ts +27 -0
- package/dist/pending-requests.js +39 -0
- package/dist/rpc-handler.js +18 -10
- package/dist/task.d.ts +1 -1
- package/dist/task.js +3 -2
- package/dist/transports/http-transport.d.ts +4 -2
- package/dist/transports/http-transport.js +218 -77
- package/dist/types.d.ts +7 -16
- package/package.json +1 -1
- package/src/agents/agent-instructions.md +9 -9
- package/src/agents/claude.ts +3 -3
- package/src/agents/codex.ts +3 -3
- package/src/agents/copilot.ts +3 -6
- package/src/agents/gemini.ts +5 -5
- package/src/agents/openclaw.ts +2 -2
- package/src/agents/shared-prompt.ts +12 -6
- package/src/commands/init.ts +34 -3
- package/src/commands/pair.ts +11 -14
- package/src/commands/plan-generation.md +12 -15
- package/src/commands/run.ts +21 -58
- package/src/commands/serve.ts +11 -2
- package/src/events.ts +14 -15
- package/src/index.ts +0 -26
- package/src/pending-requests.ts +55 -0
- package/src/rpc-handler.ts +18 -11
- package/src/task.ts +3 -1
- package/src/transports/http-transport.ts +232 -133
- package/src/types.ts +10 -16
- package/dist/commands/lan.d.ts +0 -8
- package/dist/commands/lan.js +0 -44
- package/dist/commands/notify.d.ts +0 -9
- package/dist/commands/notify.js +0 -43
- package/dist/commands/request-input.d.ts +0 -10
- package/dist/commands/request-input.js +0 -49
- package/dist/lan-lock.d.ts +0 -7
- package/dist/lan-lock.js +0 -18
- package/dist/user-input.d.ts +0 -15
- package/dist/user-input.js +0 -50
- package/src/commands/lan.ts +0 -48
- package/src/commands/notify.ts +0 -44
- package/src/commands/request-input.ts +0 -51
- package/src/lan-lock.ts +0 -16
- package/src/user-input.ts +0 -67
package/dist/commands/run.js
CHANGED
|
@@ -9,7 +9,6 @@ import { getAgent } from "../agents/agent.js";
|
|
|
9
9
|
import { getPlatform } from "../platform/index.js";
|
|
10
10
|
import { TASK_SUCCESS_MARKER, TASK_FAILURE_MARKER, TASK_REPORT_PREFIX, TASK_PERMISSION_PREFIX } from "../agents/shared-prompt.js";
|
|
11
11
|
import { publishHostEvent } from "../events.js";
|
|
12
|
-
import { waitForUserInput } from "../user-input.js";
|
|
13
12
|
/**
|
|
14
13
|
* Invoke the agent CLI with a continuation loop for permissions and user input.
|
|
15
14
|
*
|
|
@@ -24,7 +23,7 @@ async function invokeAgentWithContinuation(ctx, invokeTask) {
|
|
|
24
23
|
const { command, args, stdin } = ctx.agent.getTaskRunCommandLine(invokeTask, followupPrompt, ctx.transientPermissions);
|
|
25
24
|
const result = await spawnCommand(command, args, {
|
|
26
25
|
cwd: getRunDir(ctx.taskDir, ctx.runId),
|
|
27
|
-
env: { ...ctx.guiEnv, PALMIER_TASK_ID: ctx.task.frontmatter.id, PALMIER_RUN_DIR: getRunDir(ctx.taskDir, ctx.runId) },
|
|
26
|
+
env: { ...ctx.guiEnv, PALMIER_TASK_ID: ctx.task.frontmatter.id, PALMIER_RUN_DIR: getRunDir(ctx.taskDir, ctx.runId), PALMIER_HTTP_PORT: String(ctx.config.httpPort ?? 7400) },
|
|
28
27
|
echoStdout: true,
|
|
29
28
|
resolveOnFailure: true,
|
|
30
29
|
stdin,
|
|
@@ -41,8 +40,7 @@ async function invokeAgentWithContinuation(ctx, invokeTask) {
|
|
|
41
40
|
});
|
|
42
41
|
// Permission handling — agent requested permissions
|
|
43
42
|
if (requiredPermissions.length > 0) {
|
|
44
|
-
const response = await requestPermission(ctx.
|
|
45
|
-
await publishPermissionResolved(ctx.nc, ctx.config, ctx.taskId, response);
|
|
43
|
+
const response = await requestPermission(ctx.config, ctx.task, ctx.taskDir, requiredPermissions);
|
|
46
44
|
if (response === "aborted") {
|
|
47
45
|
await appendAndNotify(ctx, {
|
|
48
46
|
role: "user",
|
|
@@ -128,7 +126,7 @@ export async function runCommand(taskId) {
|
|
|
128
126
|
const taskName = task.frontmatter.name;
|
|
129
127
|
// Use existing run dir if just created by RPC, otherwise create a new one
|
|
130
128
|
const existingRunId = findLatestPendingRunId(taskDir);
|
|
131
|
-
const runId = existingRunId ?? createRunDir(taskDir, taskName, Date.now());
|
|
129
|
+
const runId = existingRunId ?? createRunDir(taskDir, taskName, Date.now(), task.frontmatter.agent);
|
|
132
130
|
if (!existingRunId) {
|
|
133
131
|
appendHistory(config.projectRoot, { task_id: taskId, run_id: runId });
|
|
134
132
|
}
|
|
@@ -144,9 +142,7 @@ export async function runCommand(taskId) {
|
|
|
144
142
|
await publishHostEvent(nc, config.hostId, taskId, { event_type: "result-updated", run_id: runId });
|
|
145
143
|
// If requires_confirmation, notify clients and wait
|
|
146
144
|
if (task.frontmatter.requires_confirmation) {
|
|
147
|
-
const confirmed = await requestConfirmation(
|
|
148
|
-
const resolvedStatus = confirmed ? "confirmed" : "aborted";
|
|
149
|
-
await publishConfirmResolved(nc, config, taskId, resolvedStatus);
|
|
145
|
+
const confirmed = await requestConfirmation(config, task, taskDir);
|
|
150
146
|
if (!confirmed) {
|
|
151
147
|
console.log("Task aborted by user.");
|
|
152
148
|
appendRunMessage(taskDir, runId, { role: "status", time: Date.now(), content: "", type: "aborted" });
|
|
@@ -220,7 +216,7 @@ async function runCommandTriggeredMode(ctx) {
|
|
|
220
216
|
console.log(`[command-triggered] Spawning: ${commandStr}`);
|
|
221
217
|
const child = spawnStreamingCommand(commandStr, {
|
|
222
218
|
cwd: getRunDir(ctx.taskDir, ctx.runId),
|
|
223
|
-
env: { ...ctx.guiEnv, PALMIER_TASK_ID: ctx.task.frontmatter.id, PALMIER_RUN_DIR: getRunDir(ctx.taskDir, ctx.runId) },
|
|
219
|
+
env: { ...ctx.guiEnv, PALMIER_TASK_ID: ctx.task.frontmatter.id, PALMIER_RUN_DIR: getRunDir(ctx.taskDir, ctx.runId), PALMIER_HTTP_PORT: String(ctx.config.httpPort ?? 7400) },
|
|
224
220
|
});
|
|
225
221
|
let linesProcessed = 0;
|
|
226
222
|
let invocationsSucceeded = 0;
|
|
@@ -336,49 +332,32 @@ async function publishTaskEvent(nc, config, taskDir, taskId, eventType, taskName
|
|
|
336
332
|
payload.run_id = runId;
|
|
337
333
|
await publishHostEvent(nc, config.hostId, taskId, payload);
|
|
338
334
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
async function requestPermission(nc, config, task, taskDir, requiredPermissions) {
|
|
350
|
-
const currentStatus = readTaskStatus(taskDir);
|
|
351
|
-
writeTaskStatus(taskDir, { ...currentStatus, pending_permission: requiredPermissions });
|
|
352
|
-
await publishHostEvent(nc, config.hostId, task.frontmatter.id, {
|
|
353
|
-
event_type: "permission-request",
|
|
354
|
-
host_id: config.hostId,
|
|
355
|
-
required_permissions: requiredPermissions,
|
|
356
|
-
name: task.frontmatter.name,
|
|
335
|
+
async function requestPermission(config, task, taskDir, requiredPermissions) {
|
|
336
|
+
const port = config.httpPort ?? 7400;
|
|
337
|
+
const res = await fetch(`http://localhost:${port}/request-permission`, {
|
|
338
|
+
method: "POST",
|
|
339
|
+
headers: { "Content-Type": "application/json" },
|
|
340
|
+
body: JSON.stringify({
|
|
341
|
+
taskId: task.frontmatter.id,
|
|
342
|
+
taskName: task.frontmatter.name,
|
|
343
|
+
permissions: requiredPermissions,
|
|
344
|
+
}),
|
|
357
345
|
});
|
|
358
|
-
const
|
|
359
|
-
const response = userInput[0];
|
|
346
|
+
const { response } = await res.json();
|
|
360
347
|
writeTaskStatus(taskDir, {
|
|
361
348
|
running_state: response === "aborted" ? "aborted" : "started",
|
|
362
349
|
time_stamp: Date.now(),
|
|
363
350
|
});
|
|
364
351
|
return response;
|
|
365
352
|
}
|
|
366
|
-
async function
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
async function requestConfirmation(nc, config, task, taskDir) {
|
|
374
|
-
const currentStatus = readTaskStatus(taskDir);
|
|
375
|
-
writeTaskStatus(taskDir, { ...currentStatus, pending_confirmation: true });
|
|
376
|
-
await publishHostEvent(nc, config.hostId, task.frontmatter.id, {
|
|
377
|
-
event_type: "confirm-request",
|
|
378
|
-
host_id: config.hostId,
|
|
353
|
+
async function requestConfirmation(config, task, taskDir) {
|
|
354
|
+
const port = config.httpPort ?? 7400;
|
|
355
|
+
const res = await fetch(`http://localhost:${port}/request-confirmation`, {
|
|
356
|
+
method: "POST",
|
|
357
|
+
headers: { "Content-Type": "application/json" },
|
|
358
|
+
body: JSON.stringify({ taskId: task.frontmatter.id, taskName: task.frontmatter.name }),
|
|
379
359
|
});
|
|
380
|
-
const
|
|
381
|
-
const confirmed = userInput[0] === "confirmed";
|
|
360
|
+
const { confirmed } = await res.json();
|
|
382
361
|
writeTaskStatus(taskDir, {
|
|
383
362
|
running_state: confirmed ? "started" : "aborted",
|
|
384
363
|
time_stamp: Date.now(),
|
package/dist/commands/serve.d.ts
CHANGED
package/dist/commands/serve.js
CHANGED
|
@@ -4,6 +4,7 @@ import { loadConfig } from "../config.js";
|
|
|
4
4
|
import { connectNats } from "../nats-client.js";
|
|
5
5
|
import { createRpcHandler } from "../rpc-handler.js";
|
|
6
6
|
import { startNatsTransport } from "../transports/nats-transport.js";
|
|
7
|
+
import { startHttpTransport } from "../transports/http-transport.js";
|
|
7
8
|
import { getTaskDir, readTaskStatus, writeTaskStatus, parseTaskFile, appendRunMessage } from "../task.js";
|
|
8
9
|
import { publishHostEvent } from "../events.js";
|
|
9
10
|
import { getPlatform } from "../platform/index.js";
|
|
@@ -69,7 +70,7 @@ async function checkStaleTasks(config, nc) {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
/**
|
|
72
|
-
* Start the persistent RPC handler (NATS
|
|
73
|
+
* Start the persistent RPC handler (NATS + HTTP).
|
|
73
74
|
*/
|
|
74
75
|
export async function serveCommand() {
|
|
75
76
|
const config = loadConfig();
|
|
@@ -91,6 +92,12 @@ export async function serveCommand() {
|
|
|
91
92
|
});
|
|
92
93
|
}, POLL_INTERVAL_MS);
|
|
93
94
|
const handleRpc = createRpcHandler(config, nc);
|
|
94
|
-
|
|
95
|
+
const httpPort = config.httpPort ?? 7400;
|
|
96
|
+
// Start NATS transport (loops forever, fire-and-forget)
|
|
97
|
+
if (nc) {
|
|
98
|
+
startNatsTransport(config, handleRpc, nc);
|
|
99
|
+
}
|
|
100
|
+
// Start HTTP transport (loops forever)
|
|
101
|
+
await startHttpTransport(config, handleRpc, httpPort, nc);
|
|
95
102
|
}
|
|
96
103
|
//# sourceMappingURL=serve.js.map
|
package/dist/events.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type NatsConnection } from "nats";
|
|
2
2
|
/**
|
|
3
|
-
* Broadcast an event to connected clients via NATS and HTTP SSE
|
|
3
|
+
* Broadcast an event to connected clients via NATS and HTTP SSE.
|
|
4
4
|
*
|
|
5
5
|
* - NATS: publishes to `host-event.{hostId}.{taskId}`
|
|
6
|
-
* - HTTP: POSTs to the
|
|
6
|
+
* - HTTP: POSTs to the serve daemon's `/event` endpoint
|
|
7
7
|
*/
|
|
8
8
|
export declare function publishHostEvent(nc: NatsConnection | undefined, hostId: string, taskId: string, payload: Record<string, unknown>): Promise<void>;
|
|
9
9
|
//# sourceMappingURL=events.d.ts.map
|
package/dist/events.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { StringCodec } from "nats";
|
|
2
|
-
import {
|
|
2
|
+
import { loadConfig } from "./config.js";
|
|
3
3
|
const sc = StringCodec();
|
|
4
4
|
/**
|
|
5
|
-
* Broadcast an event to connected clients via NATS and HTTP SSE
|
|
5
|
+
* Broadcast an event to connected clients via NATS and HTTP SSE.
|
|
6
6
|
*
|
|
7
7
|
* - NATS: publishes to `host-event.{hostId}.{taskId}`
|
|
8
|
-
* - HTTP: POSTs to the
|
|
8
|
+
* - HTTP: POSTs to the serve daemon's `/event` endpoint
|
|
9
9
|
*/
|
|
10
10
|
export async function publishHostEvent(nc, hostId, taskId, payload) {
|
|
11
11
|
const subject = `host-event.${hostId}.${taskId}`;
|
|
@@ -13,19 +13,18 @@ export async function publishHostEvent(nc, hostId, taskId, payload) {
|
|
|
13
13
|
nc.publish(subject, sc.encode(JSON.stringify(payload)));
|
|
14
14
|
console.log(`[nats] ${subject} →`, payload);
|
|
15
15
|
}
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
16
|
+
const config = loadConfig();
|
|
17
|
+
const port = config.httpPort ?? 7400;
|
|
18
|
+
try {
|
|
19
|
+
await fetch(`http://localhost:${port}/event`, {
|
|
20
|
+
method: "POST",
|
|
21
|
+
headers: { "Content-Type": "application/json" },
|
|
22
|
+
body: JSON.stringify({ task_id: taskId, ...payload }),
|
|
23
|
+
});
|
|
24
|
+
console.log(`[http] host-event: ${taskId} →`, payload);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Serve HTTP may not be ready yet — ignore
|
|
29
28
|
}
|
|
30
29
|
}
|
|
31
30
|
//# sourceMappingURL=events.js.map
|
package/dist/index.js
CHANGED
|
@@ -8,10 +8,7 @@ import { initCommand } from "./commands/init.js";
|
|
|
8
8
|
import { infoCommand } from "./commands/info.js";
|
|
9
9
|
import { runCommand } from "./commands/run.js";
|
|
10
10
|
import { serveCommand } from "./commands/serve.js";
|
|
11
|
-
import { notifyCommand } from "./commands/notify.js";
|
|
12
|
-
import { requestInputCommand } from "./commands/request-input.js";
|
|
13
11
|
import { pairCommand } from "./commands/pair.js";
|
|
14
|
-
import { lanCommand } from "./commands/lan.js";
|
|
15
12
|
import { restartCommand } from "./commands/restart.js";
|
|
16
13
|
import { sessionsListCommand, sessionsRevokeCommand, sessionsRevokeAllCommand } from "./commands/sessions.js";
|
|
17
14
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -51,34 +48,12 @@ program
|
|
|
51
48
|
.action(async () => {
|
|
52
49
|
await restartCommand();
|
|
53
50
|
});
|
|
54
|
-
program
|
|
55
|
-
.command("notify")
|
|
56
|
-
.description("Send a push notification to the user")
|
|
57
|
-
.requiredOption("--title <title>", "Notification title")
|
|
58
|
-
.requiredOption("--body <body>", "Notification body text")
|
|
59
|
-
.action(async (opts) => {
|
|
60
|
-
await notifyCommand(opts);
|
|
61
|
-
});
|
|
62
|
-
program
|
|
63
|
-
.command("request-input")
|
|
64
|
-
.description("Request input from the user (requires PALMIER_TASK_ID env var)")
|
|
65
|
-
.requiredOption("--description <desc...>", "Input descriptions to show the user")
|
|
66
|
-
.action(async (opts) => {
|
|
67
|
-
await requestInputCommand(opts);
|
|
68
|
-
});
|
|
69
51
|
program
|
|
70
52
|
.command("pair")
|
|
71
53
|
.description("Generate a pairing code for connecting a PWA client")
|
|
72
54
|
.action(async () => {
|
|
73
55
|
await pairCommand();
|
|
74
56
|
});
|
|
75
|
-
program
|
|
76
|
-
.command("lan")
|
|
77
|
-
.description("Start an on-demand LAN server for direct HTTP connections")
|
|
78
|
-
.option("-p, --port <port>", "Port to listen on", "7400")
|
|
79
|
-
.action(async (opts) => {
|
|
80
|
-
await lanCommand({ port: parseInt(opts.port, 10) });
|
|
81
|
-
});
|
|
82
57
|
const sessionsCmd = program
|
|
83
58
|
.command("sessions")
|
|
84
59
|
.description("Manage paired client sessions");
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { RequiredPermission } from "./types.js";
|
|
2
|
+
export interface PendingRequest {
|
|
3
|
+
type: "confirmation" | "permission" | "input";
|
|
4
|
+
resolve: (value: string[]) => void;
|
|
5
|
+
/** Permission list (for 'permission') or input descriptions (for 'input'). */
|
|
6
|
+
params?: RequiredPermission[] | string[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Register a pending request for a task. Returns a Promise that resolves
|
|
10
|
+
* when `resolvePending` is called with the user's response.
|
|
11
|
+
* Only one pending request per task at a time.
|
|
12
|
+
*/
|
|
13
|
+
export declare function registerPending(taskId: string, type: PendingRequest["type"], params?: PendingRequest["params"]): Promise<string[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a pending request with the user's response.
|
|
16
|
+
* Returns true if a pending request was found and resolved.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolvePending(taskId: string, value: string[]): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Get the current pending request for a task (if any).
|
|
21
|
+
*/
|
|
22
|
+
export declare function getPending(taskId: string): PendingRequest | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Remove a pending request without resolving it.
|
|
25
|
+
*/
|
|
26
|
+
export declare function removePending(taskId: string): void;
|
|
27
|
+
//# sourceMappingURL=pending-requests.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const pending = new Map();
|
|
2
|
+
/**
|
|
3
|
+
* Register a pending request for a task. Returns a Promise that resolves
|
|
4
|
+
* when `resolvePending` is called with the user's response.
|
|
5
|
+
* Only one pending request per task at a time.
|
|
6
|
+
*/
|
|
7
|
+
export function registerPending(taskId, type, params) {
|
|
8
|
+
if (pending.has(taskId)) {
|
|
9
|
+
return Promise.reject(new Error(`Task ${taskId} already has a pending request`));
|
|
10
|
+
}
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
pending.set(taskId, { type, resolve, params });
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Resolve a pending request with the user's response.
|
|
17
|
+
* Returns true if a pending request was found and resolved.
|
|
18
|
+
*/
|
|
19
|
+
export function resolvePending(taskId, value) {
|
|
20
|
+
const entry = pending.get(taskId);
|
|
21
|
+
if (!entry)
|
|
22
|
+
return false;
|
|
23
|
+
pending.delete(taskId);
|
|
24
|
+
entry.resolve(value);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get the current pending request for a task (if any).
|
|
29
|
+
*/
|
|
30
|
+
export function getPending(taskId) {
|
|
31
|
+
return pending.get(taskId);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Remove a pending request without resolving it.
|
|
35
|
+
*/
|
|
36
|
+
export function removePending(taskId) {
|
|
37
|
+
pending.delete(taskId);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=pending-requests.js.map
|
package/dist/rpc-handler.js
CHANGED
|
@@ -5,6 +5,7 @@ import { fileURLToPath } from "url";
|
|
|
5
5
|
import { spawn } from "child_process";
|
|
6
6
|
import { parse as parseYaml } from "yaml";
|
|
7
7
|
import { listTasks, parseTaskFile, writeTaskFile, getTaskDir, readTaskStatus, writeTaskStatus, readHistory, deleteHistoryEntry, appendTaskList, removeFromTaskList, appendHistory, createRunDir, appendRunMessage, getRunDir } from "./task.js";
|
|
8
|
+
import { resolvePending, getPending } from "./pending-requests.js";
|
|
8
9
|
import { getPlatform } from "./platform/index.js";
|
|
9
10
|
import { spawnCommand } from "./spawn-command.js";
|
|
10
11
|
import crossSpawn from "cross-spawn";
|
|
@@ -47,6 +48,7 @@ function parseResultFrontmatter(raw) {
|
|
|
47
48
|
return {
|
|
48
49
|
messages,
|
|
49
50
|
task_name: meta.task_name,
|
|
51
|
+
agent: meta.agent,
|
|
50
52
|
running_state: runningState,
|
|
51
53
|
start_time: startedMsg?.time || undefined,
|
|
52
54
|
end_time: terminalMsg?.time || undefined,
|
|
@@ -131,8 +133,8 @@ export function createRpcHandler(config, nc) {
|
|
|
131
133
|
};
|
|
132
134
|
}
|
|
133
135
|
async function handleRpc(request) {
|
|
134
|
-
// Session token validation:
|
|
135
|
-
if (!request.sessionToken || !validateSession(request.sessionToken)) {
|
|
136
|
+
// Session token validation: skip for trusted localhost requests
|
|
137
|
+
if (!request.localhost && (!request.sessionToken || !validateSession(request.sessionToken))) {
|
|
136
138
|
return { error: "Unauthorized" };
|
|
137
139
|
}
|
|
138
140
|
switch (request.method) {
|
|
@@ -259,7 +261,7 @@ export function createRpcHandler(config, nc) {
|
|
|
259
261
|
writeTaskFile(taskDir, task);
|
|
260
262
|
// Do NOT append to tasks.jsonl — this is a one-off run
|
|
261
263
|
// Create initial result file so it appears in runs list immediately
|
|
262
|
-
const runId = createRunDir(taskDir, name, Date.now());
|
|
264
|
+
const runId = createRunDir(taskDir, name, Date.now(), params.agent);
|
|
263
265
|
appendHistory(config.projectRoot, { task_id: id, run_id: runId });
|
|
264
266
|
// Spawn `palmier run <id>` directly as a detached process
|
|
265
267
|
const script = process.argv[1] || "palmier";
|
|
@@ -277,7 +279,7 @@ export function createRpcHandler(config, nc) {
|
|
|
277
279
|
// Create initial result file so it appears in runs list immediately
|
|
278
280
|
const runTaskDir = getTaskDir(config.projectRoot, params.id);
|
|
279
281
|
const runTask = parseTaskFile(runTaskDir);
|
|
280
|
-
const taskRunId = createRunDir(runTaskDir, runTask.frontmatter.name, Date.now());
|
|
282
|
+
const taskRunId = createRunDir(runTaskDir, runTask.frontmatter.name, Date.now(), runTask.frontmatter.agent);
|
|
281
283
|
appendHistory(config.projectRoot, { task_id: params.id, run_id: taskRunId });
|
|
282
284
|
await getPlatform().startTask(params.id);
|
|
283
285
|
return { ok: true, task_id: params.id, run_id: taskRunId };
|
|
@@ -447,7 +449,14 @@ export function createRpcHandler(config, nc) {
|
|
|
447
449
|
if (!status) {
|
|
448
450
|
return { task_id: params.id, error: "No status found" };
|
|
449
451
|
}
|
|
450
|
-
|
|
452
|
+
const pending = getPending(params.id);
|
|
453
|
+
return {
|
|
454
|
+
task_id: params.id,
|
|
455
|
+
...status,
|
|
456
|
+
...(pending?.type === "confirmation" ? { pending_confirmation: true } : {}),
|
|
457
|
+
...(pending?.type === "permission" ? { pending_permission: pending.params } : {}),
|
|
458
|
+
...(pending?.type === "input" ? { pending_input: pending.params } : {}),
|
|
459
|
+
};
|
|
451
460
|
}
|
|
452
461
|
case "task.result": {
|
|
453
462
|
const params = request.params;
|
|
@@ -494,14 +503,13 @@ export function createRpcHandler(config, nc) {
|
|
|
494
503
|
}
|
|
495
504
|
case "task.user_input": {
|
|
496
505
|
const params = request.params;
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
if (!currentStatus?.pending_confirmation && !currentStatus?.pending_permission?.length && !currentStatus?.pending_input?.length) {
|
|
506
|
+
const pending = getPending(params.id);
|
|
507
|
+
if (!pending) {
|
|
500
508
|
return { ok: false, error: "not pending" };
|
|
501
509
|
}
|
|
502
|
-
|
|
510
|
+
const resolved = resolvePending(params.id, params.value);
|
|
503
511
|
console.log(`[task.user_input] ${params.id} → ${params.value}`);
|
|
504
|
-
return { ok:
|
|
512
|
+
return { ok: resolved };
|
|
505
513
|
}
|
|
506
514
|
case "taskrun.list": {
|
|
507
515
|
const params = request.params;
|
package/dist/task.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export declare function readTaskStatus(taskDir: string): TaskStatus | undefined;
|
|
|
42
42
|
* Create a run directory with an initial TASKRUN.md file.
|
|
43
43
|
* Returns the run ID (timestamp string used as directory name).
|
|
44
44
|
*/
|
|
45
|
-
export declare function createRunDir(taskDir: string, taskName: string, startTime: number): string;
|
|
45
|
+
export declare function createRunDir(taskDir: string, taskName: string, startTime: number, agent?: string): string;
|
|
46
46
|
/**
|
|
47
47
|
* Get the path to a run directory.
|
|
48
48
|
*/
|
package/dist/task.js
CHANGED
|
@@ -133,11 +133,12 @@ export function readTaskStatus(taskDir) {
|
|
|
133
133
|
* Create a run directory with an initial TASKRUN.md file.
|
|
134
134
|
* Returns the run ID (timestamp string used as directory name).
|
|
135
135
|
*/
|
|
136
|
-
export function createRunDir(taskDir, taskName, startTime) {
|
|
136
|
+
export function createRunDir(taskDir, taskName, startTime, agent) {
|
|
137
137
|
const runId = String(startTime);
|
|
138
138
|
const runDir = path.join(taskDir, runId);
|
|
139
139
|
fs.mkdirSync(runDir, { recursive: true });
|
|
140
|
-
const
|
|
140
|
+
const agentLine = agent ? `\nagent: ${agent}` : "";
|
|
141
|
+
const content = `---\ntask_name: ${taskName}${agentLine}\n---\n\n`;
|
|
141
142
|
fs.writeFileSync(path.join(runDir, "TASKRUN.md"), content, "utf-8");
|
|
142
143
|
return runId;
|
|
143
144
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { type NatsConnection } from "nats";
|
|
1
2
|
import type { HostConfig, RpcMessage } from "../types.js";
|
|
2
3
|
export declare function detectLanIp(): string;
|
|
3
4
|
/**
|
|
4
|
-
* Start the HTTP transport:
|
|
5
|
+
* Start the HTTP transport: server with RPC, SSE, PWA proxy, pairing, and
|
|
6
|
+
* localhost-only agent endpoints (notify, request-input, confirmation, permission).
|
|
5
7
|
*/
|
|
6
|
-
export declare function startHttpTransport(config: HostConfig, handleRpc: (req: RpcMessage) => Promise<unknown>, port: number, pairingCode?: string, onReady?: () => void): Promise<void>;
|
|
8
|
+
export declare function startHttpTransport(config: HostConfig, handleRpc: (req: RpcMessage) => Promise<unknown>, port: number, nc: NatsConnection | undefined, pairingCode?: string, onReady?: () => void): Promise<void>;
|
|
7
9
|
//# sourceMappingURL=http-transport.d.ts.map
|