palmier 0.2.0 → 0.2.2

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.
Files changed (79) hide show
  1. package/CLAUDE.md +5 -1
  2. package/README.md +135 -45
  3. package/dist/agents/agent.d.ts +26 -0
  4. package/dist/agents/agent.js +32 -0
  5. package/dist/agents/claude.d.ts +8 -0
  6. package/dist/agents/claude.js +35 -0
  7. package/dist/agents/codex.d.ts +8 -0
  8. package/dist/agents/codex.js +41 -0
  9. package/dist/agents/gemini.d.ts +8 -0
  10. package/dist/agents/gemini.js +39 -0
  11. package/dist/agents/openclaw.d.ts +8 -0
  12. package/dist/agents/openclaw.js +25 -0
  13. package/dist/agents/shared-prompt.d.ts +11 -0
  14. package/dist/agents/shared-prompt.js +26 -0
  15. package/dist/commands/agents.d.ts +2 -0
  16. package/dist/commands/agents.js +19 -0
  17. package/dist/commands/info.d.ts +5 -0
  18. package/dist/commands/info.js +40 -0
  19. package/dist/commands/init.d.ts +7 -2
  20. package/dist/commands/init.js +139 -49
  21. package/dist/commands/mcpserver.d.ts +2 -0
  22. package/dist/commands/mcpserver.js +75 -0
  23. package/dist/commands/pair.d.ts +6 -0
  24. package/dist/commands/pair.js +140 -0
  25. package/dist/commands/plan-generation.md +32 -0
  26. package/dist/commands/run.d.ts +0 -1
  27. package/dist/commands/run.js +258 -114
  28. package/dist/commands/serve.d.ts +1 -1
  29. package/dist/commands/serve.js +16 -228
  30. package/dist/commands/sessions.d.ts +4 -0
  31. package/dist/commands/sessions.js +30 -0
  32. package/dist/commands/task-generation.md +1 -1
  33. package/dist/config.d.ts +5 -5
  34. package/dist/config.js +24 -6
  35. package/dist/index.js +58 -5
  36. package/dist/nats-client.d.ts +3 -3
  37. package/dist/nats-client.js +2 -2
  38. package/dist/rpc-handler.d.ts +6 -0
  39. package/dist/rpc-handler.js +367 -0
  40. package/dist/session-store.d.ts +12 -0
  41. package/dist/session-store.js +57 -0
  42. package/dist/spawn-command.d.ts +26 -0
  43. package/dist/spawn-command.js +48 -0
  44. package/dist/systemd.d.ts +2 -2
  45. package/dist/task.d.ts +45 -2
  46. package/dist/task.js +155 -14
  47. package/dist/transports/http-transport.d.ts +6 -0
  48. package/dist/transports/http-transport.js +243 -0
  49. package/dist/transports/nats-transport.d.ts +6 -0
  50. package/dist/transports/nats-transport.js +69 -0
  51. package/dist/types.d.ts +30 -13
  52. package/package.json +4 -3
  53. package/src/agents/agent.ts +62 -0
  54. package/src/agents/claude.ts +39 -0
  55. package/src/agents/codex.ts +46 -0
  56. package/src/agents/gemini.ts +43 -0
  57. package/src/agents/openclaw.ts +29 -0
  58. package/src/agents/shared-prompt.ts +26 -0
  59. package/src/commands/agents.ts +20 -0
  60. package/src/commands/info.ts +44 -0
  61. package/src/commands/init.ts +229 -121
  62. package/src/commands/mcpserver.ts +92 -0
  63. package/src/commands/pair.ts +163 -0
  64. package/src/commands/plan-generation.md +32 -0
  65. package/src/commands/run.ts +323 -129
  66. package/src/commands/serve.ts +26 -287
  67. package/src/commands/sessions.ts +32 -0
  68. package/src/config.ts +30 -10
  69. package/src/index.ts +67 -6
  70. package/src/nats-client.ts +4 -4
  71. package/src/rpc-handler.ts +421 -0
  72. package/src/session-store.ts +68 -0
  73. package/src/spawn-command.ts +78 -0
  74. package/src/systemd.ts +2 -2
  75. package/src/task.ts +166 -16
  76. package/src/transports/http-transport.ts +290 -0
  77. package/src/transports/nats-transport.ts +82 -0
  78. package/src/types.ts +36 -13
  79. package/src/commands/task-generation.md +0 -28
@@ -1,10 +1,24 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
- import { spawn } from "child_process";
3
+ import { spawnCommand } from "../spawn-command.js";
4
4
  import { loadConfig } from "../config.js";
5
5
  import { connectNats } from "../nats-client.js";
6
- import { parseTaskFile, getTaskDir } from "../task.js";
6
+ import { parseTaskFile, getTaskDir, writeTaskFile, writeTaskStatus, readTaskStatus, appendHistory } from "../task.js";
7
+ import { getAgent } from "../agents/agent.js";
8
+ import { TASK_SUCCESS_MARKER, TASK_FAILURE_MARKER, TASK_REPORT_PREFIX, TASK_PERMISSION_PREFIX } from "../agents/shared-prompt.js";
7
9
  import { StringCodec } from "nats";
10
+ /**
11
+ * Write a time-stamped RESULT file with frontmatter.
12
+ * Always generated, even for abort/fail.
13
+ */
14
+ function writeResult(taskDir, taskName, taskSnapshotName, runningState, startTime, endTime, output, reportFiles, requiredPermissions) {
15
+ const resultFileName = `RESULT-${endTime}.md`;
16
+ const reportLine = reportFiles.length > 0 ? `\nreport_files: ${reportFiles.join(", ")}` : "";
17
+ const permLines = requiredPermissions.map((p) => `\nrequired_permission: ${p.name} | ${p.description}`).join("");
18
+ const content = `---\ntask_name: ${taskName}\nrunning_state: ${runningState}\nstart_time: ${startTime}\nend_time: ${endTime}\ntask_file: ${taskSnapshotName}${reportLine}${permLines}\n---\n${output}`;
19
+ fs.writeFileSync(path.join(taskDir, resultFileName), content, "utf-8");
20
+ return resultFileName;
21
+ }
8
22
  /**
9
23
  * Execute a task by ID.
10
24
  */
@@ -12,17 +26,17 @@ export async function runCommand(taskId) {
12
26
  const config = loadConfig();
13
27
  const taskDir = getTaskDir(config.projectRoot, taskId);
14
28
  const task = parseTaskFile(taskDir);
15
- console.log(`Running task: ${taskId}`);
29
+ const mode = config.mode ?? "nats";
30
+ const useNats = mode === "nats" || mode === "auto";
31
+ const useHttp = mode === "lan" || mode === "auto";
32
+ console.log(`Running task: ${taskId} (mode: ${mode})`);
16
33
  let nc;
17
- let confirmKv;
18
- const confirmKey = `${config.agentId}.${taskId}`;
34
+ const startTime = Date.now();
35
+ const taskName = task.frontmatter.name;
36
+ // Snapshot the task file at run time
37
+ const taskSnapshotName = `TASK-${startTime}.md`;
38
+ fs.copyFileSync(path.join(taskDir, "TASK.md"), path.join(taskDir, taskSnapshotName));
19
39
  const cleanup = async () => {
20
- if (confirmKv) {
21
- try {
22
- await confirmKv.delete(confirmKey);
23
- }
24
- catch { /* may not exist */ }
25
- }
26
40
  if (nc && !nc.isClosed()) {
27
41
  await nc.drain();
28
42
  }
@@ -30,51 +44,104 @@ export async function runCommand(taskId) {
30
44
  // Handle signals
31
45
  const onSignal = async () => {
32
46
  console.log("Received signal, cleaning up...");
33
- if (eventKv) {
34
- await writeTaskEvent(eventKv, `${config.agentId}.${taskId}`, "abort").catch(() => { });
35
- }
47
+ const endTime = Date.now();
48
+ const resultFileName = writeResult(taskDir, taskName, taskSnapshotName, "abort", startTime, endTime, "", [], []);
49
+ appendHistory(config.projectRoot, { task_id: taskId, result_file: resultFileName });
50
+ await publishTaskEvent(nc, config, taskDir, taskId, "abort", useHttp, taskName);
36
51
  await cleanup();
37
52
  process.exit(1);
38
53
  };
39
54
  process.on("SIGINT", onSignal);
40
55
  process.on("SIGTERM", onSignal);
41
- let eventKv;
42
56
  try {
43
- nc = await connectNats(config);
44
- const js = nc.jetstream();
45
- // Set up task-event KV and mark as started immediately
46
- eventKv = await js.views.kv("task-event", { history: 1 });
47
- const eventKey = `${config.agentId}.${taskId}`;
48
- await writeTaskEvent(eventKv, eventKey, "start");
49
- // If requires_confirmation, ask user via NATS KV
57
+ if (useNats) {
58
+ nc = await connectNats(config);
59
+ }
60
+ // Mark as started immediately
61
+ await publishTaskEvent(nc, config, taskDir, taskId, "start", useHttp, taskName);
62
+ // If requires_confirmation, notify clients and wait
50
63
  if (task.frontmatter.requires_confirmation) {
51
- confirmKv = await js.views.kv("pending-confirmation");
52
- const confirmed = await requestConfirmation(config, task, confirmKv);
64
+ const confirmed = await requestConfirmation(nc, config, task, taskDir, useHttp);
65
+ const resolvedStatus = confirmed ? "confirmed" : "aborted";
66
+ await publishConfirmResolved(nc, config, taskId, resolvedStatus, useHttp);
53
67
  if (!confirmed) {
54
68
  console.log("Task aborted by user.");
55
- await writeTaskEvent(eventKv, eventKey, "abort");
69
+ const endTime = Date.now();
70
+ const resultFileName = writeResult(taskDir, taskName, taskSnapshotName, "abort", startTime, endTime, "", [], []);
71
+ appendHistory(config.projectRoot, { task_id: taskId, result_file: resultFileName });
72
+ await publishTaskEvent(nc, config, taskDir, taskId, "abort", useHttp, taskName);
56
73
  await cleanup();
57
74
  return;
58
75
  }
59
76
  console.log("Task confirmed by user.");
60
77
  }
61
- // Spawn task process
62
- const startTime = Date.now();
63
- const output = await spawnTask(config, task);
64
- const endTime = Date.now();
65
- // Save result with frontmatter to task directory
66
- const resultPath = path.join(taskDir, "RESULT.md");
67
- const resultContent = `---\nstart_time: ${startTime}\nend_time: ${endTime}\n---\n${output}`;
68
- fs.writeFileSync(resultPath, resultContent, "utf-8");
69
- // Set event to finish on completion
70
- await writeTaskEvent(eventKv, eventKey, "finish");
78
+ // Execution loop: retry on permission failure if user grants
79
+ const guiEnv = getGuiEnv();
80
+ const agent = getAgent(task.frontmatter.agent);
81
+ let lastOutput = "";
82
+ let lastOutcome = "fail";
83
+ let lastReportFiles = [];
84
+ let lastRequiredPermissions = [];
85
+ let lastEndTime = Date.now();
86
+ let retryPrompt;
87
+ let transientPermissions = [];
88
+ // eslint-disable-next-line no-constant-condition
89
+ while (true) {
90
+ const { command, args } = agent.getTaskRunCommandLine(task, retryPrompt, transientPermissions);
91
+ const result = await spawnCommand(command, args, {
92
+ cwd: taskDir,
93
+ env: {
94
+ ...guiEnv,
95
+ PALMIER_TASK_ID: task.frontmatter.id,
96
+ },
97
+ echoStdout: true,
98
+ forwardSignals: true,
99
+ resolveOnFailure: true,
100
+ });
101
+ lastOutput = result.output;
102
+ lastEndTime = Date.now();
103
+ lastOutcome = result.exitCode !== 0 ? "fail" : parseTaskOutcome(lastOutput);
104
+ lastReportFiles = parseReportFiles(lastOutput);
105
+ lastRequiredPermissions = parsePermissions(lastOutput);
106
+ // If failed with permission requirements, ask user to grant
107
+ if (lastOutcome === "fail" && lastRequiredPermissions.length > 0) {
108
+ const response = await requestPermission(nc, config, task, taskDir, lastRequiredPermissions, useHttp);
109
+ await publishPermissionResolved(nc, config, taskId, response, useHttp);
110
+ if (response === "aborted") {
111
+ console.log("Permission request aborted by user.");
112
+ break;
113
+ }
114
+ const newPerms = lastRequiredPermissions.filter((rp) => !task.frontmatter.permissions?.some((ep) => ep.name === rp.name)
115
+ && !transientPermissions.some((ep) => ep.name === rp.name));
116
+ if (response === "granted_all") {
117
+ // Persist permissions to task frontmatter for all future runs
118
+ task.frontmatter.permissions = [...(task.frontmatter.permissions ?? []), ...newPerms];
119
+ writeTaskFile(taskDir, task);
120
+ }
121
+ else {
122
+ // "granted" — allow for this run only
123
+ transientPermissions = [...transientPermissions, ...newPerms];
124
+ }
125
+ console.log(`Permissions granted, retrying task ${taskId}...`);
126
+ retryPrompt = "Permissions granted, please continue.";
127
+ continue;
128
+ }
129
+ // Normal completion (success or non-permission failure)
130
+ break;
131
+ }
132
+ // Write result and history once after the loop
133
+ const resultFileName = writeResult(taskDir, taskName, taskSnapshotName, lastOutcome, startTime, lastEndTime, lastOutput, lastReportFiles, lastRequiredPermissions);
134
+ appendHistory(config.projectRoot, { task_id: taskId, result_file: resultFileName });
135
+ await publishTaskEvent(nc, config, taskDir, taskId, lastOutcome, useHttp, taskName);
71
136
  console.log(`Task ${taskId} completed.`);
72
137
  }
73
138
  catch (err) {
74
139
  console.error(`Task ${taskId} failed:`, err);
75
- if (eventKv) {
76
- await writeTaskEvent(eventKv, `${config.agentId}.${taskId}`, "fail").catch(() => { });
77
- }
140
+ const endTime = Date.now();
141
+ const errorMsg = err instanceof Error ? err.message : String(err);
142
+ const resultFileName = writeResult(taskDir, taskName, taskSnapshotName, "fail", startTime, endTime, errorMsg, [], []);
143
+ appendHistory(config.projectRoot, { task_id: taskId, result_file: resultFileName });
144
+ await publishTaskEvent(nc, config, taskDir, taskId, "fail", useHttp, taskName);
78
145
  process.exitCode = 1;
79
146
  }
80
147
  finally {
@@ -82,90 +149,167 @@ export async function runCommand(taskId) {
82
149
  }
83
150
  }
84
151
  const sc = StringCodec();
85
- async function writeTaskEvent(kv, key, eventType) {
86
- const event = { event_type: eventType, time_stamp: Date.now() };
87
- console.log(`[task-event] ${key} → ${eventType}`);
88
- await kv.put(key, sc.encode(JSON.stringify(event)));
89
- }
90
- async function requestConfirmation(config, task, kv) {
91
- const kvKey = `${config.agentId}.${task.frontmatter.id}`;
92
- // Write confirmation payload to KV — the server watches this bucket and sends push notifications
93
- const payload = {
94
- type: "confirm",
95
- task_id: task.frontmatter.id,
96
- agent_id: config.agentId,
97
- user_id: config.userId,
98
- details: {
99
- prompt: task.frontmatter.user_prompt,
100
- },
101
- status: "pending",
102
- };
103
- await kv.put(kvKey, sc.encode(JSON.stringify(payload)));
104
- // Watch AFTER writing — the initial history replay delivers the "pending" entry (skipped),
105
- // then the iterator stays open for live updates (confirmed/aborted).
106
- const watch = await kv.watch({ key: kvKey });
107
- for await (const entry of watch) {
108
- if (entry.operation === "DEL" || entry.operation === "PURGE") {
109
- return false;
110
- }
152
+ /**
153
+ * Write status.json and notify connected clients via NATS and/or HTTP SSE.
154
+ */
155
+ async function publishHostEvent(nc, config, taskId, payload, useHttp) {
156
+ const subject = `host-event.${config.hostId}.${taskId}`;
157
+ if (nc) {
158
+ nc.publish(subject, sc.encode(JSON.stringify(payload)));
159
+ console.log(`[nats] ${subject} →`, payload);
160
+ }
161
+ if (useHttp && config.directPort) {
111
162
  try {
112
- const updated = JSON.parse(sc.decode(entry.value));
113
- if (updated.status === "confirmed")
114
- return true;
115
- if (updated.status === "aborted")
116
- return false;
117
- // Still pending, keep watching
163
+ await fetch(`http://localhost:${config.directPort}/internal/event`, {
164
+ method: "POST",
165
+ headers: { "Content-Type": "application/json" },
166
+ body: JSON.stringify({ task_id: taskId, ...payload }),
167
+ });
168
+ console.log(`[http] host-event: ${taskId} →`, payload);
118
169
  }
119
- catch {
120
- // Couldn't parse, keep watching
170
+ catch (err) {
171
+ console.error(`[http] Failed to push event:`, err);
121
172
  }
122
173
  }
123
- return false;
124
174
  }
125
- function shellEscape(arg) {
126
- return "'" + arg.replace(/'/g, "'\\''") + "'";
175
+ async function publishTaskEvent(nc, config, taskDir, taskId, eventType, useHttp, taskName) {
176
+ writeTaskStatus(taskDir, {
177
+ running_state: eventType,
178
+ time_stamp: Date.now(),
179
+ });
180
+ const payload = { event_type: "running-state", running_state: eventType };
181
+ if (taskName)
182
+ payload.name = taskName;
183
+ await publishHostEvent(nc, config, taskId, payload, useHttp);
127
184
  }
128
- async function spawnTask(config, task) {
129
- return new Promise((resolve, reject) => {
130
- const prompt = task.body
131
- ? `${task.body}\n\n${task.frontmatter.user_prompt}`
132
- : task.frontmatter.user_prompt;
133
- const command = `${task.frontmatter.command_line} ${shellEscape(prompt)}`;
134
- const child = spawn(command, {
135
- cwd: config.projectRoot,
136
- shell: true,
137
- stdio: ["ignore", "pipe", "pipe"],
138
- env: {
139
- ...process.env,
140
- PALMIER_TASK_ID: task.frontmatter.id,
141
- },
142
- });
143
- const stdoutChunks = [];
144
- child.stdout?.on("data", (data) => {
145
- stdoutChunks.push(data);
146
- process.stdout.write(data);
147
- });
148
- child.stderr?.on("data", (data) => {
149
- process.stderr.write(data);
150
- });
151
- let stopping = false;
152
- const killChild = () => {
153
- stopping = true;
154
- child.kill("SIGTERM");
155
- };
156
- process.on("SIGINT", killChild);
157
- process.on("SIGTERM", killChild);
158
- child.on("close", (exitCode) => {
159
- if (exitCode === 0 || stopping) {
160
- resolve(Buffer.concat(stdoutChunks).toString("utf-8"));
161
- }
162
- else {
163
- reject(new Error(`Task process exited with code ${exitCode}`));
164
- }
185
+ /**
186
+ * Notify clients that a confirmation request has been resolved.
187
+ */
188
+ async function publishConfirmResolved(nc, config, taskId, status, useHttp) {
189
+ await publishHostEvent(nc, config, taskId, {
190
+ event_type: "confirm-resolved",
191
+ host_id: config.hostId,
192
+ status,
193
+ }, useHttp);
194
+ }
195
+ async function requestPermission(nc, config, task, taskDir, requiredPermissions, useHttp) {
196
+ const taskId = task.frontmatter.id;
197
+ const statusPath = path.join(taskDir, "status.json");
198
+ const currentStatus = readTaskStatus(taskDir);
199
+ writeTaskStatus(taskDir, { ...currentStatus, pending_permission: requiredPermissions });
200
+ await publishHostEvent(nc, config, taskId, {
201
+ event_type: "permission-request",
202
+ host_id: config.hostId,
203
+ required_permissions: requiredPermissions,
204
+ name: task.frontmatter.name,
205
+ }, useHttp);
206
+ return new Promise((resolve) => {
207
+ const watcher = fs.watch(statusPath, () => {
208
+ const status = readTaskStatus(taskDir);
209
+ if (!status || status.user_input === undefined)
210
+ return;
211
+ watcher.close();
212
+ const response = status.user_input;
213
+ writeTaskStatus(taskDir, {
214
+ running_state: response === "aborted" ? "abort" : "start",
215
+ time_stamp: Date.now(),
216
+ });
217
+ resolve(response);
165
218
  });
166
- child.on("error", (err) => {
167
- reject(err);
219
+ });
220
+ }
221
+ async function publishPermissionResolved(nc, config, taskId, status, useHttp) {
222
+ await publishHostEvent(nc, config, taskId, {
223
+ event_type: "permission-resolved",
224
+ host_id: config.hostId,
225
+ status,
226
+ }, useHttp);
227
+ }
228
+ async function requestConfirmation(nc, config, task, taskDir, useHttp) {
229
+ const taskId = task.frontmatter.id;
230
+ const statusPath = path.join(taskDir, "status.json");
231
+ // Flag that we're awaiting user confirmation
232
+ const currentStatus = readTaskStatus(taskDir);
233
+ writeTaskStatus(taskDir, { ...currentStatus, pending_confirmation: true });
234
+ // Publish confirmation request via NATS and/or HTTP SSE
235
+ await publishHostEvent(nc, config, taskId, {
236
+ event_type: "confirm-request",
237
+ host_id: config.hostId,
238
+ }, useHttp);
239
+ // Wait for task.user_input RPC to set user_input in status.json
240
+ return new Promise((resolve) => {
241
+ const watcher = fs.watch(statusPath, () => {
242
+ const status = readTaskStatus(taskDir);
243
+ if (!status || status.user_input === undefined)
244
+ return; // still pending
245
+ watcher.close();
246
+ const confirmed = status.user_input === "confirmed";
247
+ // Clear pending_confirmation/user_input and update running_state
248
+ writeTaskStatus(taskDir, {
249
+ running_state: confirmed ? "start" : "abort",
250
+ time_stamp: Date.now(),
251
+ });
252
+ resolve(confirmed);
168
253
  });
169
254
  });
170
255
  }
256
+ /**
257
+ * Extract report file names from agent output.
258
+ * Looks for lines matching: [PALMIER_REPORT] <filename>
259
+ */
260
+ function parseReportFiles(output) {
261
+ const regex = new RegExp(`^\\${TASK_REPORT_PREFIX}\\s+(.+)$`, "gm");
262
+ const files = [];
263
+ let match;
264
+ while ((match = regex.exec(output)) !== null) {
265
+ const name = match[1].trim();
266
+ if (name)
267
+ files.push(name);
268
+ }
269
+ return files;
270
+ }
271
+ /**
272
+ * Extract required permissions from agent output.
273
+ * Looks for lines matching: [PALMIER_PERMISSION] <tool> | <description>
274
+ */
275
+ function parsePermissions(output) {
276
+ const regex = new RegExp(`^\\${TASK_PERMISSION_PREFIX}\\s+(.+)$`, "gm");
277
+ const perms = [];
278
+ let match;
279
+ while ((match = regex.exec(output)) !== null) {
280
+ const raw = match[1].trim();
281
+ const sep = raw.indexOf("|");
282
+ if (sep !== -1) {
283
+ perms.push({ name: raw.slice(0, sep).trim(), description: raw.slice(sep + 1).trim() });
284
+ }
285
+ else {
286
+ perms.push({ name: raw, description: "" });
287
+ }
288
+ }
289
+ return perms;
290
+ }
291
+ /**
292
+ * Parse the agent's output for success/failure markers.
293
+ * Falls back to "finish" if no marker is found.
294
+ */
295
+ function parseTaskOutcome(output) {
296
+ const lastChunk = output.slice(-500);
297
+ if (lastChunk.includes(TASK_FAILURE_MARKER))
298
+ return "fail";
299
+ if (lastChunk.includes(TASK_SUCCESS_MARKER))
300
+ return "finish";
301
+ return "finish";
302
+ }
303
+ /**
304
+ * Return env vars for the default physical GUI session (:0).
305
+ */
306
+ function getGuiEnv() {
307
+ const uid = process.getuid?.();
308
+ const runtimeDir = process.env.XDG_RUNTIME_DIR ||
309
+ (uid !== undefined ? `/run/user/${uid}` : "");
310
+ return {
311
+ DISPLAY: ":0",
312
+ ...(runtimeDir ? { XDG_RUNTIME_DIR: runtimeDir } : {}),
313
+ };
314
+ }
171
315
  //# sourceMappingURL=run.js.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Start the persistent NATS RPC handler.
2
+ * Start the persistent RPC handler using the configured transport(s).
3
3
  */
4
4
  export declare function serveCommand(): Promise<void>;
5
5
  //# sourceMappingURL=serve.d.ts.map