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.
- package/CLAUDE.md +5 -1
- package/README.md +135 -45
- package/dist/agents/agent.d.ts +26 -0
- package/dist/agents/agent.js +32 -0
- package/dist/agents/claude.d.ts +8 -0
- package/dist/agents/claude.js +35 -0
- package/dist/agents/codex.d.ts +8 -0
- package/dist/agents/codex.js +41 -0
- package/dist/agents/gemini.d.ts +8 -0
- package/dist/agents/gemini.js +39 -0
- package/dist/agents/openclaw.d.ts +8 -0
- package/dist/agents/openclaw.js +25 -0
- package/dist/agents/shared-prompt.d.ts +11 -0
- package/dist/agents/shared-prompt.js +26 -0
- package/dist/commands/agents.d.ts +2 -0
- package/dist/commands/agents.js +19 -0
- package/dist/commands/info.d.ts +5 -0
- package/dist/commands/info.js +40 -0
- package/dist/commands/init.d.ts +7 -2
- package/dist/commands/init.js +139 -49
- package/dist/commands/mcpserver.d.ts +2 -0
- package/dist/commands/mcpserver.js +75 -0
- package/dist/commands/pair.d.ts +6 -0
- package/dist/commands/pair.js +140 -0
- package/dist/commands/plan-generation.md +32 -0
- package/dist/commands/run.d.ts +0 -1
- package/dist/commands/run.js +258 -114
- package/dist/commands/serve.d.ts +1 -1
- package/dist/commands/serve.js +16 -228
- package/dist/commands/sessions.d.ts +4 -0
- package/dist/commands/sessions.js +30 -0
- package/dist/commands/task-generation.md +1 -1
- package/dist/config.d.ts +5 -5
- package/dist/config.js +24 -6
- package/dist/index.js +58 -5
- package/dist/nats-client.d.ts +3 -3
- package/dist/nats-client.js +2 -2
- package/dist/rpc-handler.d.ts +6 -0
- package/dist/rpc-handler.js +367 -0
- package/dist/session-store.d.ts +12 -0
- package/dist/session-store.js +57 -0
- package/dist/spawn-command.d.ts +26 -0
- package/dist/spawn-command.js +48 -0
- package/dist/systemd.d.ts +2 -2
- package/dist/task.d.ts +45 -2
- package/dist/task.js +155 -14
- package/dist/transports/http-transport.d.ts +6 -0
- package/dist/transports/http-transport.js +243 -0
- package/dist/transports/nats-transport.d.ts +6 -0
- package/dist/transports/nats-transport.js +69 -0
- package/dist/types.d.ts +30 -13
- package/package.json +4 -3
- package/src/agents/agent.ts +62 -0
- package/src/agents/claude.ts +39 -0
- package/src/agents/codex.ts +46 -0
- package/src/agents/gemini.ts +43 -0
- package/src/agents/openclaw.ts +29 -0
- package/src/agents/shared-prompt.ts +26 -0
- package/src/commands/agents.ts +20 -0
- package/src/commands/info.ts +44 -0
- package/src/commands/init.ts +229 -121
- package/src/commands/mcpserver.ts +92 -0
- package/src/commands/pair.ts +163 -0
- package/src/commands/plan-generation.md +32 -0
- package/src/commands/run.ts +323 -129
- package/src/commands/serve.ts +26 -287
- package/src/commands/sessions.ts +32 -0
- package/src/config.ts +30 -10
- package/src/index.ts +67 -6
- package/src/nats-client.ts +4 -4
- package/src/rpc-handler.ts +421 -0
- package/src/session-store.ts +68 -0
- package/src/spawn-command.ts +78 -0
- package/src/systemd.ts +2 -2
- package/src/task.ts +166 -16
- package/src/transports/http-transport.ts +290 -0
- package/src/transports/nats-transport.ts +82 -0
- package/src/types.ts +36 -13
- package/src/commands/task-generation.md +0 -28
package/dist/commands/init.js
CHANGED
|
@@ -1,92 +1,182 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
import * as readline from "readline";
|
|
5
|
+
import { randomUUID, randomBytes } from "crypto";
|
|
3
6
|
import { execSync } from "child_process";
|
|
4
7
|
import { homedir } from "os";
|
|
5
8
|
import { saveConfig } from "../config.js";
|
|
9
|
+
import { detectAgents } from "../agents/agent.js";
|
|
10
|
+
import { pairCommand } from "./pair.js";
|
|
6
11
|
/**
|
|
7
|
-
* Provision this
|
|
12
|
+
* Provision this host. Two flows:
|
|
13
|
+
* - palmier init --lan → standalone LAN mode, no server needed
|
|
14
|
+
* - palmier init --server <url> → register with server, prompts for nats/auto mode
|
|
8
15
|
*/
|
|
9
16
|
export async function initCommand(options) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
const jsonStr = Buffer.from(options.token, "base64").toString("utf-8");
|
|
14
|
-
decoded = JSON.parse(jsonStr);
|
|
17
|
+
if (options.lan) {
|
|
18
|
+
await initLan(options);
|
|
15
19
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
process.exit(1);
|
|
20
|
+
else if (options.server) {
|
|
21
|
+
await initWithServer(options);
|
|
19
22
|
}
|
|
20
|
-
|
|
21
|
-
console.error("
|
|
23
|
+
else {
|
|
24
|
+
console.error("Either --lan or --server <url> is required.");
|
|
22
25
|
process.exit(1);
|
|
23
26
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Flow A: Standalone LAN mode. No web server needed.
|
|
30
|
+
*/
|
|
31
|
+
async function initLan(options) {
|
|
32
|
+
const hostId = randomUUID();
|
|
33
|
+
const directToken = randomBytes(32).toString("hex");
|
|
34
|
+
const directPort = options.port ?? 7400;
|
|
35
|
+
const lanIp = options.host ?? detectLanIp();
|
|
36
|
+
const config = {
|
|
37
|
+
hostId,
|
|
38
|
+
mode: "lan",
|
|
39
|
+
directPort,
|
|
40
|
+
directToken,
|
|
41
|
+
projectRoot: process.cwd(),
|
|
42
|
+
};
|
|
43
|
+
console.log("Detecting installed agents...");
|
|
44
|
+
config.agents = await detectAgents();
|
|
45
|
+
saveConfig(config);
|
|
46
|
+
console.log(`Host provisioned (LAN mode). ID: ${hostId}`);
|
|
47
|
+
console.log("Config saved to ~/.config/palmier/host.json");
|
|
48
|
+
installSystemdService(config);
|
|
49
|
+
// Auto-enter pair mode so user can connect their PWA immediately
|
|
50
|
+
console.log("");
|
|
51
|
+
console.log("Starting pairing...");
|
|
52
|
+
await pairCommand();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Flow B: Register directly with server. No provisioning token needed.
|
|
56
|
+
*/
|
|
57
|
+
async function initWithServer(options) {
|
|
58
|
+
const serverUrl = options.server;
|
|
59
|
+
console.log(`Registering host at ${serverUrl}...`);
|
|
60
|
+
let registerResponse;
|
|
27
61
|
try {
|
|
28
|
-
const res = await fetch(`${
|
|
62
|
+
const res = await fetch(`${serverUrl}/api/hosts/register`, {
|
|
29
63
|
method: "POST",
|
|
30
64
|
headers: { "Content-Type": "application/json" },
|
|
31
|
-
body: JSON.stringify({
|
|
65
|
+
body: JSON.stringify({}),
|
|
32
66
|
});
|
|
33
67
|
if (!res.ok) {
|
|
34
68
|
const body = await res.text();
|
|
35
|
-
console.error(`Failed to
|
|
69
|
+
console.error(`Failed to register host: ${res.status} ${res.statusText}\n${body}`);
|
|
36
70
|
process.exit(1);
|
|
37
71
|
}
|
|
38
|
-
|
|
72
|
+
registerResponse = (await res.json());
|
|
39
73
|
}
|
|
40
74
|
catch (err) {
|
|
41
75
|
console.error(`Failed to reach server: ${err}`);
|
|
42
76
|
process.exit(1);
|
|
43
77
|
}
|
|
44
|
-
//
|
|
78
|
+
// Prompt for connection mode
|
|
79
|
+
const mode = await promptMode();
|
|
80
|
+
// Build config
|
|
45
81
|
const config = {
|
|
46
|
-
|
|
47
|
-
userId: claimResponse.userId,
|
|
48
|
-
natsUrl: claimResponse.natsUrl,
|
|
49
|
-
natsWsUrl: claimResponse.natsWsUrl,
|
|
50
|
-
natsToken: claimResponse.natsToken,
|
|
82
|
+
hostId: registerResponse.hostId,
|
|
51
83
|
projectRoot: process.cwd(),
|
|
84
|
+
mode,
|
|
85
|
+
natsUrl: registerResponse.natsUrl,
|
|
86
|
+
natsWsUrl: registerResponse.natsWsUrl,
|
|
87
|
+
natsToken: registerResponse.natsToken,
|
|
52
88
|
};
|
|
89
|
+
if (mode === "auto") {
|
|
90
|
+
const directToken = randomBytes(32).toString("hex");
|
|
91
|
+
const directPort = options.port ?? 7400;
|
|
92
|
+
const lanIp = options.host ?? detectLanIp();
|
|
93
|
+
config.directPort = directPort;
|
|
94
|
+
config.directToken = directToken;
|
|
95
|
+
console.log("");
|
|
96
|
+
console.log("Direct connection info (for LAN clients):");
|
|
97
|
+
console.log(` Address: ${lanIp}:${directPort}`);
|
|
98
|
+
console.log(` Token: ${directToken}`);
|
|
99
|
+
}
|
|
100
|
+
console.log("Detecting installed agents...");
|
|
101
|
+
config.agents = await detectAgents();
|
|
53
102
|
saveConfig(config);
|
|
54
|
-
console.log(`
|
|
55
|
-
console.log("Config saved to ~/.config/palmier/
|
|
56
|
-
|
|
103
|
+
console.log(`Host provisioned (${mode} mode). ID: ${config.hostId}`);
|
|
104
|
+
console.log("Config saved to ~/.config/palmier/host.json");
|
|
105
|
+
installSystemdService(config);
|
|
106
|
+
// Auto-enter pair mode so user can connect their PWA immediately
|
|
107
|
+
console.log("");
|
|
108
|
+
console.log("Starting pairing...");
|
|
109
|
+
await pairCommand();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Prompt user to select connection mode.
|
|
113
|
+
*/
|
|
114
|
+
async function promptMode() {
|
|
115
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
116
|
+
const question = (q) => new Promise((resolve) => rl.question(q, resolve));
|
|
117
|
+
console.log("");
|
|
118
|
+
console.log("Connection mode:");
|
|
119
|
+
console.log(" 1) auto — Both LAN and NATS (recommended)");
|
|
120
|
+
console.log(" 2) nats — NATS only");
|
|
121
|
+
console.log("");
|
|
122
|
+
const answer = await question("Select [1]: ");
|
|
123
|
+
rl.close();
|
|
124
|
+
if (answer.trim() === "2" || answer.trim().toLowerCase() === "nats") {
|
|
125
|
+
return "nats";
|
|
126
|
+
}
|
|
127
|
+
return "auto";
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Detect the first non-internal IPv4 address.
|
|
131
|
+
*/
|
|
132
|
+
function detectLanIp() {
|
|
133
|
+
const interfaces = os.networkInterfaces();
|
|
134
|
+
for (const name of Object.keys(interfaces)) {
|
|
135
|
+
for (const iface of interfaces[name] ?? []) {
|
|
136
|
+
if (iface.family === "IPv4" && !iface.internal) {
|
|
137
|
+
return iface.address;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return "127.0.0.1";
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Install systemd user service for palmier serve.
|
|
145
|
+
*/
|
|
146
|
+
function installSystemdService(config) {
|
|
57
147
|
const unitDir = path.join(homedir(), ".config", "systemd", "user");
|
|
58
148
|
fs.mkdirSync(unitDir, { recursive: true });
|
|
59
149
|
const palmierBin = process.argv[1] || "palmier";
|
|
60
|
-
const serviceContent = `[Unit]
|
|
61
|
-
Description=Palmier
|
|
62
|
-
After=network-online.target
|
|
63
|
-
Wants=network-online.target
|
|
64
|
-
|
|
65
|
-
[Service]
|
|
66
|
-
Type=simple
|
|
67
|
-
ExecStart=${palmierBin} serve
|
|
68
|
-
WorkingDirectory=${config.projectRoot}
|
|
69
|
-
Restart=on-failure
|
|
70
|
-
RestartSec=5
|
|
71
|
-
Environment=PATH=${process.env.PATH || "/usr/local/bin:/usr/bin:/bin"}
|
|
72
|
-
|
|
73
|
-
[Install]
|
|
74
|
-
WantedBy=default.target
|
|
150
|
+
const serviceContent = `[Unit]
|
|
151
|
+
Description=Palmier Host
|
|
152
|
+
After=network-online.target
|
|
153
|
+
Wants=network-online.target
|
|
154
|
+
|
|
155
|
+
[Service]
|
|
156
|
+
Type=simple
|
|
157
|
+
ExecStart=${palmierBin} serve
|
|
158
|
+
WorkingDirectory=${config.projectRoot}
|
|
159
|
+
Restart=on-failure
|
|
160
|
+
RestartSec=5
|
|
161
|
+
Environment=PATH=${process.env.PATH || "/usr/local/bin:/usr/bin:/bin"}
|
|
162
|
+
|
|
163
|
+
[Install]
|
|
164
|
+
WantedBy=default.target
|
|
75
165
|
`;
|
|
76
|
-
const servicePath = path.join(unitDir, "palmier
|
|
166
|
+
const servicePath = path.join(unitDir, "palmier.service");
|
|
77
167
|
fs.writeFileSync(servicePath, serviceContent, "utf-8");
|
|
78
168
|
console.log("Systemd service installed at:", servicePath);
|
|
79
|
-
//
|
|
169
|
+
// Enable and start the service
|
|
80
170
|
try {
|
|
81
171
|
execSync("systemctl --user daemon-reload", { stdio: "inherit" });
|
|
82
|
-
execSync("systemctl --user enable --now palmier
|
|
83
|
-
console.log("Palmier
|
|
172
|
+
execSync("systemctl --user enable --now palmier.service", { stdio: "inherit" });
|
|
173
|
+
console.log("Palmier host service enabled and started.");
|
|
84
174
|
}
|
|
85
175
|
catch (err) {
|
|
86
176
|
console.error(`Warning: failed to enable systemd service: ${err}`);
|
|
87
|
-
console.error("You may need to start it manually: systemctl --user enable --now palmier
|
|
177
|
+
console.error("You may need to start it manually: systemctl --user enable --now palmier.service");
|
|
88
178
|
}
|
|
89
|
-
//
|
|
179
|
+
// Enable lingering so service runs without active login session
|
|
90
180
|
try {
|
|
91
181
|
execSync(`loginctl enable-linger ${process.env.USER || ""}`, { stdio: "inherit" });
|
|
92
182
|
console.log("Login lingering enabled.");
|
|
@@ -94,6 +184,6 @@ WantedBy=default.target
|
|
|
94
184
|
catch (err) {
|
|
95
185
|
console.error(`Warning: failed to enable linger: ${err}`);
|
|
96
186
|
}
|
|
97
|
-
console.log("\
|
|
187
|
+
console.log("\nHost initialization complete!");
|
|
98
188
|
}
|
|
99
189
|
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { StringCodec } from "nats";
|
|
5
|
+
import { loadConfig } from "../config.js";
|
|
6
|
+
import { connectNats } from "../nats-client.js";
|
|
7
|
+
export async function mcpserverCommand() {
|
|
8
|
+
const config = loadConfig();
|
|
9
|
+
const mode = config.mode ?? "nats";
|
|
10
|
+
const useNats = mode === "nats" || mode === "auto";
|
|
11
|
+
let nc;
|
|
12
|
+
if (useNats) {
|
|
13
|
+
nc = await connectNats(config);
|
|
14
|
+
}
|
|
15
|
+
const sc = StringCodec();
|
|
16
|
+
const server = new McpServer({ name: "palmier", version: "1.0.0" }, { capabilities: { tools: {} } });
|
|
17
|
+
// send-email requires NATS — only register in nats/auto mode
|
|
18
|
+
if (nc) {
|
|
19
|
+
server.registerTool("send-email", {
|
|
20
|
+
description: "Send an email via the Palmier platform's SMTP relay",
|
|
21
|
+
inputSchema: {
|
|
22
|
+
to: z.string().describe("Recipient email address(es), comma-separated"),
|
|
23
|
+
subject: z.string().describe("Email subject line"),
|
|
24
|
+
text: z.string().describe("Plain text body"),
|
|
25
|
+
html: z.string().optional().describe("HTML body"),
|
|
26
|
+
reply_to: z.string().optional().describe("Reply-To address"),
|
|
27
|
+
cc: z.string().optional().describe("CC recipients, comma-separated"),
|
|
28
|
+
bcc: z.string().optional().describe("BCC recipients, comma-separated"),
|
|
29
|
+
},
|
|
30
|
+
}, async (args) => {
|
|
31
|
+
const payload = {
|
|
32
|
+
hostId: config.hostId,
|
|
33
|
+
to: args.to,
|
|
34
|
+
subject: args.subject,
|
|
35
|
+
text: args.text,
|
|
36
|
+
html: args.html,
|
|
37
|
+
replyTo: args.reply_to,
|
|
38
|
+
cc: args.cc,
|
|
39
|
+
bcc: args.bcc,
|
|
40
|
+
};
|
|
41
|
+
try {
|
|
42
|
+
const subject = `host.${config.hostId}.email.send`;
|
|
43
|
+
const reply = await nc.request(subject, sc.encode(JSON.stringify(payload)), {
|
|
44
|
+
timeout: 15_000,
|
|
45
|
+
});
|
|
46
|
+
const result = JSON.parse(sc.decode(reply.data));
|
|
47
|
+
if (result.ok) {
|
|
48
|
+
return {
|
|
49
|
+
content: [{ type: "text", text: `Email sent successfully (messageId: ${result.messageId})` }],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
return {
|
|
54
|
+
content: [{ type: "text", text: `Failed to send email: ${result.error}` }],
|
|
55
|
+
isError: true,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
return {
|
|
61
|
+
content: [{ type: "text", text: `Error sending email: ${err}` }],
|
|
62
|
+
isError: true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const transport = new StdioServerTransport();
|
|
68
|
+
await server.connect(transport);
|
|
69
|
+
// Graceful shutdown
|
|
70
|
+
transport.onclose = async () => {
|
|
71
|
+
if (nc)
|
|
72
|
+
await nc.drain();
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=mcpserver.js.map
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import * as http from "node:http";
|
|
2
|
+
import { StringCodec } from "nats";
|
|
3
|
+
import { loadConfig } from "../config.js";
|
|
4
|
+
import { connectNats } from "../nats-client.js";
|
|
5
|
+
import { addSession } from "../session-store.js";
|
|
6
|
+
const CODE_CHARS = "ABCDEFGHJKMNPQRSTUVWXYZ23456789"; // no O/0/I/1/L
|
|
7
|
+
const CODE_LENGTH = 6;
|
|
8
|
+
const EXPIRY_MS = 5 * 60 * 1000; // 5 minutes
|
|
9
|
+
function generateCode() {
|
|
10
|
+
const bytes = new Uint8Array(CODE_LENGTH);
|
|
11
|
+
crypto.getRandomValues(bytes);
|
|
12
|
+
return Array.from(bytes, (b) => CODE_CHARS[b % CODE_CHARS.length]).join("");
|
|
13
|
+
}
|
|
14
|
+
function buildPairResponse(config, label) {
|
|
15
|
+
const session = addSession(label);
|
|
16
|
+
const response = {
|
|
17
|
+
hostId: config.hostId,
|
|
18
|
+
sessionToken: session.token,
|
|
19
|
+
};
|
|
20
|
+
return response;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* POST to the running serve process and long-poll until paired or expired.
|
|
24
|
+
* Returns true if paired, false if expired/failed.
|
|
25
|
+
*/
|
|
26
|
+
function lanPairRegister(port, code) {
|
|
27
|
+
const body = JSON.stringify({ code, expiryMs: EXPIRY_MS });
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
const req = http.request({
|
|
30
|
+
hostname: "127.0.0.1",
|
|
31
|
+
port,
|
|
32
|
+
path: "/internal/pair-register",
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: { "Content-Type": "application/json" },
|
|
35
|
+
timeout: EXPIRY_MS + 5000, // slightly longer than expiry
|
|
36
|
+
}, (res) => {
|
|
37
|
+
const chunks = [];
|
|
38
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
39
|
+
res.on("end", () => {
|
|
40
|
+
try {
|
|
41
|
+
const result = JSON.parse(Buffer.concat(chunks).toString("utf-8"));
|
|
42
|
+
resolve(result.paired);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
resolve(false);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
req.on("error", (err) => {
|
|
50
|
+
console.error(`Failed to reach palmier serve on port ${port}: ${err.message}`);
|
|
51
|
+
console.error("Make sure `palmier serve` is running first.");
|
|
52
|
+
resolve(false);
|
|
53
|
+
});
|
|
54
|
+
req.on("timeout", () => {
|
|
55
|
+
req.destroy();
|
|
56
|
+
resolve(false);
|
|
57
|
+
});
|
|
58
|
+
req.end(body);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Generate an OTP code and wait for a PWA client to pair.
|
|
63
|
+
* Listens on NATS (nats/auto modes) and/or HTTP (lan/auto modes).
|
|
64
|
+
*/
|
|
65
|
+
export async function pairCommand() {
|
|
66
|
+
const config = loadConfig();
|
|
67
|
+
const code = generateCode();
|
|
68
|
+
const mode = config.mode ?? "nats";
|
|
69
|
+
let paired = false;
|
|
70
|
+
function onPaired() {
|
|
71
|
+
paired = true;
|
|
72
|
+
console.log("Paired successfully!");
|
|
73
|
+
}
|
|
74
|
+
const cleanups = [];
|
|
75
|
+
// Display pairing info
|
|
76
|
+
console.log("");
|
|
77
|
+
console.log("Enter this code in your Palmier app:");
|
|
78
|
+
console.log("");
|
|
79
|
+
console.log(` ${code}`);
|
|
80
|
+
console.log("");
|
|
81
|
+
console.log("Code expires in 5 minutes.");
|
|
82
|
+
// NATS pairing (nats or auto mode)
|
|
83
|
+
if (mode === "nats" || mode === "auto") {
|
|
84
|
+
const nc = await connectNats(config);
|
|
85
|
+
const sc = StringCodec();
|
|
86
|
+
const subject = `pair.${code}`;
|
|
87
|
+
const sub = nc.subscribe(subject, { max: 1 });
|
|
88
|
+
cleanups.push(async () => {
|
|
89
|
+
sub.unsubscribe();
|
|
90
|
+
await nc.drain();
|
|
91
|
+
});
|
|
92
|
+
(async () => {
|
|
93
|
+
for await (const msg of sub) {
|
|
94
|
+
if (paired)
|
|
95
|
+
break;
|
|
96
|
+
let label;
|
|
97
|
+
try {
|
|
98
|
+
if (msg.data && msg.data.length > 0) {
|
|
99
|
+
const body = JSON.parse(sc.decode(msg.data));
|
|
100
|
+
label = body.label;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch { /* empty body is fine */ }
|
|
104
|
+
const response = buildPairResponse(config, label);
|
|
105
|
+
if (msg.reply) {
|
|
106
|
+
msg.respond(sc.encode(JSON.stringify(response)));
|
|
107
|
+
}
|
|
108
|
+
onPaired();
|
|
109
|
+
}
|
|
110
|
+
})();
|
|
111
|
+
}
|
|
112
|
+
// LAN pairing — long-poll the running serve process
|
|
113
|
+
if (mode === "lan" || mode === "auto") {
|
|
114
|
+
const port = config.directPort ?? 7400;
|
|
115
|
+
(async () => {
|
|
116
|
+
const result = await lanPairRegister(port, code);
|
|
117
|
+
if (result)
|
|
118
|
+
onPaired();
|
|
119
|
+
})();
|
|
120
|
+
}
|
|
121
|
+
// Wait for pairing or timeout
|
|
122
|
+
const start = Date.now();
|
|
123
|
+
await new Promise((resolve) => {
|
|
124
|
+
const interval = setInterval(() => {
|
|
125
|
+
if (paired || Date.now() - start >= EXPIRY_MS) {
|
|
126
|
+
clearInterval(interval);
|
|
127
|
+
resolve();
|
|
128
|
+
}
|
|
129
|
+
}, 500);
|
|
130
|
+
});
|
|
131
|
+
// Cleanup
|
|
132
|
+
for (const cleanup of cleanups) {
|
|
133
|
+
await cleanup();
|
|
134
|
+
}
|
|
135
|
+
if (!paired) {
|
|
136
|
+
console.log("Code expired. Run `palmier pair` to try again.");
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=pair.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
You are a task planning assistant. Given a task description, produce a detailed Markdown execution plan that an agent can follow step by step. **Do not execute any part of the plan yourself.**
|
|
2
|
+
|
|
3
|
+
Your output must begin with a raw YAML frontmatter block (delimited by `---`), followed by the plan body in Markdown. Do NOT wrap the frontmatter in code fences. The very first line of your output must be `---`.
|
|
4
|
+
|
|
5
|
+
**Output format:**
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
task_name: <short descriptive name, 3-6 words>
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<plan body in Markdown>
|
|
12
|
+
|
|
13
|
+
**Frontmatter fields:**
|
|
14
|
+
|
|
15
|
+
- `task_name`: A concise label for the task (e.g., "Clean up temp files", "Update system packages", "Backup database daily").
|
|
16
|
+
|
|
17
|
+
**Plan body sections:**
|
|
18
|
+
|
|
19
|
+
### 1. Goal
|
|
20
|
+
State what the task accomplishes and the expected end state.
|
|
21
|
+
|
|
22
|
+
### 2. Plan
|
|
23
|
+
A numbered sequence of concrete, actionable steps. Use sub-steps for complex actions and include conditional branches where behavior may vary (e.g., "If file exists, do A; otherwise, do B"). Each step should be specific enough for the agent to execute without ambiguity.
|
|
24
|
+
|
|
25
|
+
### 3. Output Format (if applicable)
|
|
26
|
+
If the task produces a report, email, or other formatted output, specify the exact structure, sections, tone, and any templates the agent should follow.
|
|
27
|
+
|
|
28
|
+
Use Markdown formatting throughout — headings, code blocks, and tables where appropriate.
|
|
29
|
+
|
|
30
|
+
**Important:** Any relative times or dates in the task description (e.g., "yesterday", "last week") are relative to when the task is executed, not when this plan is generated. The agent must resolve them at execution time.
|
|
31
|
+
|
|
32
|
+
**Task description:**
|
package/dist/commands/run.d.ts
CHANGED