@meandmyagents/agent-runner 0.1.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/README.md +18 -0
- package/bin/meandmyagents-runner.js +8 -0
- package/package.json +21 -0
- package/src/runner.js +238 -0
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# MeAndMyAgents Agent Runner
|
|
2
|
+
|
|
3
|
+
Local visible-session runner for MeAndMyAgents.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm exec --yes --registry=https://registry.npmjs.org/ --package=@meandmyagents/agent-runner@latest -- meandmyagents-runner watch --key maa_live_YOUR_AGENT_KEY --launcher codex
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The runner polls the cheap `/api/agent/events` endpoint with an agent-bound key.
|
|
10
|
+
When work exists, it claims the wake event and launches a visible Codex or Claude
|
|
11
|
+
CLI session in the task project path. It injects that same key into the child
|
|
12
|
+
process as `MAA_API_KEY`, so one Mac can run several agent profiles without
|
|
13
|
+
sharing identity. The launched agent uses MCP to start, complete, and drain the
|
|
14
|
+
remaining actionable cards.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
meandmyagents-runner watch --key maa_live_YOUR_AGENT_KEY --launcher claude --once
|
|
18
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meandmyagents/agent-runner",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local visible-session task runner for Me And My Agents.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"meandmyagents-runner": "bin/meandmyagents-runner.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"src",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"license": "UNLICENSED"
|
|
21
|
+
}
|
package/src/runner.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
export const defaultApiUrl = "https://meandmyagents.com/api";
|
|
4
|
+
const supportedLaunchers = new Set(["codex", "claude"]);
|
|
5
|
+
|
|
6
|
+
export function parseRunnerArgs(argv = process.argv.slice(2)) {
|
|
7
|
+
const options = {
|
|
8
|
+
command: argv[0] ?? "help",
|
|
9
|
+
apiUrl: defaultApiUrl,
|
|
10
|
+
intervalSeconds: 10,
|
|
11
|
+
key: "",
|
|
12
|
+
launcher: "codex",
|
|
13
|
+
once: false
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
17
|
+
const arg = argv[index];
|
|
18
|
+
|
|
19
|
+
if (arg === "--key" || arg === "-k") {
|
|
20
|
+
options.key = argv[index + 1] ?? "";
|
|
21
|
+
index += 1;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (arg === "--api-url" || arg === "-u") {
|
|
26
|
+
options.apiUrl = normalizeApiUrl(argv[index + 1] ?? "");
|
|
27
|
+
index += 1;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (arg === "--launcher" || arg === "-l") {
|
|
32
|
+
options.launcher = argv[index + 1] ?? "";
|
|
33
|
+
index += 1;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (arg === "--interval") {
|
|
38
|
+
options.intervalSeconds = Number(argv[index + 1] ?? "10");
|
|
39
|
+
index += 1;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (arg === "--once") {
|
|
44
|
+
options.once = true;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (arg === "--help" || arg === "-h") {
|
|
49
|
+
options.command = "help";
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
throw new Error(`Unknown runner option: ${arg}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!Number.isFinite(options.intervalSeconds) || options.intervalSeconds < 2) {
|
|
57
|
+
options.intervalSeconds = 10;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!supportedLaunchers.has(options.launcher)) {
|
|
61
|
+
throw new Error(`Unsupported launcher: ${options.launcher}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return options;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function buildAgentPrompt({
|
|
68
|
+
agentName,
|
|
69
|
+
cardId,
|
|
70
|
+
eventId,
|
|
71
|
+
projectName
|
|
72
|
+
}) {
|
|
73
|
+
return [
|
|
74
|
+
`You are ${agentName}. MeAndMyAgents has assigned work in ${projectName}.`,
|
|
75
|
+
`Wake event: ${eventId}. First card that woke this session: ${cardId}.`,
|
|
76
|
+
"Use the MeAndMyAgents MCP server now.",
|
|
77
|
+
"Call whoami to confirm your identity and project access.",
|
|
78
|
+
"Call list_my_tasks, then start_task on the next actionable card before editing files.",
|
|
79
|
+
"Work only in the task codebase.localPath and only when codebase.canModifyCode is true.",
|
|
80
|
+
"Add comments with useful progress and completion summaries.",
|
|
81
|
+
"Call complete_task when a task is ready for human testing.",
|
|
82
|
+
"Call list_my_tasks again after each completion.",
|
|
83
|
+
"Keep taking the next task until no actionable tasks remain, then stop."
|
|
84
|
+
].join("\n");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function buildLaunchCommand({ launcher, prompt, cwd, apiKey, apiUrl }) {
|
|
88
|
+
if (!supportedLaunchers.has(launcher)) {
|
|
89
|
+
throw new Error(`Unsupported launcher: ${launcher}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
command: launcher,
|
|
94
|
+
args: [prompt],
|
|
95
|
+
cwd,
|
|
96
|
+
env: {
|
|
97
|
+
MAA_API_KEY: apiKey,
|
|
98
|
+
MAA_API_URL: mcpUrlFromApiUrl(apiUrl)
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function runCli(argv = process.argv.slice(2)) {
|
|
104
|
+
const options = parseRunnerArgs(argv);
|
|
105
|
+
|
|
106
|
+
if (options.command === "help") {
|
|
107
|
+
process.stdout.write(helpText());
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (options.command !== "watch") {
|
|
112
|
+
throw new Error(`Unknown runner command: ${options.command}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!options.key) {
|
|
116
|
+
throw new Error("Missing required --key maa_live_... argument");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await watchForEvents(options);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function watchForEvents(options) {
|
|
123
|
+
process.stdout.write(
|
|
124
|
+
`Watching ${options.apiUrl}/agent/events for ${options.launcher} work...\n`
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
do {
|
|
128
|
+
const events = await fetchPendingEvents(options);
|
|
129
|
+
|
|
130
|
+
if (events.length > 0) {
|
|
131
|
+
const event = events[0];
|
|
132
|
+
await claimEvent(options, event.id);
|
|
133
|
+
await launchEvent(options, event);
|
|
134
|
+
} else if (options.once) {
|
|
135
|
+
process.stdout.write("No pending task events.\n");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!options.once) {
|
|
139
|
+
await sleep(options.intervalSeconds * 1000);
|
|
140
|
+
}
|
|
141
|
+
} while (!options.once);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function fetchPendingEvents(options) {
|
|
145
|
+
const response = await fetch(`${options.apiUrl}/agent/events?limit=10`, {
|
|
146
|
+
headers: { authorization: `Bearer ${options.key}` }
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
throw new Error(`Event fetch failed: HTTP ${response.status} ${await response.text()}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const body = await response.json();
|
|
154
|
+
return Array.isArray(body.events) ? body.events : [];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function claimEvent(options, eventId) {
|
|
158
|
+
const response = await fetch(`${options.apiUrl}/agent/events/${eventId}/claim`, {
|
|
159
|
+
method: "POST",
|
|
160
|
+
headers: { authorization: `Bearer ${options.key}` }
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
throw new Error(`Event claim failed: HTTP ${response.status} ${await response.text()}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function launchEvent(options, event) {
|
|
169
|
+
const cwd = resolveLaunchCwd(event);
|
|
170
|
+
const prompt = buildAgentPrompt({
|
|
171
|
+
agentName: event.agent?.name ?? options.launcher,
|
|
172
|
+
cardId: event.card?.id ?? "unknown-card",
|
|
173
|
+
eventId: event.id,
|
|
174
|
+
projectName: event.project?.name ?? "this project"
|
|
175
|
+
});
|
|
176
|
+
const launch = buildLaunchCommand({
|
|
177
|
+
launcher: options.launcher,
|
|
178
|
+
prompt,
|
|
179
|
+
cwd,
|
|
180
|
+
apiKey: options.key,
|
|
181
|
+
apiUrl: options.apiUrl
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
process.stdout.write(
|
|
185
|
+
`Launching ${launch.command} for ${event.card?.title ?? event.id} in ${launch.cwd}\n`
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
await new Promise((resolve, reject) => {
|
|
189
|
+
const child = spawn(launch.command, launch.args, {
|
|
190
|
+
cwd: launch.cwd,
|
|
191
|
+
env: { ...process.env, ...launch.env },
|
|
192
|
+
stdio: "inherit"
|
|
193
|
+
});
|
|
194
|
+
child.once("error", reject);
|
|
195
|
+
child.once("exit", (code) => {
|
|
196
|
+
if (code && code !== 0) {
|
|
197
|
+
reject(new Error(`${launch.command} exited with code ${code}`));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
resolve();
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function normalizeApiUrl(value) {
|
|
206
|
+
return (value || defaultApiUrl).replace(/\/$/, "").replace(/\/mcp$/, "");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function mcpUrlFromApiUrl(value) {
|
|
210
|
+
return `${normalizeApiUrl(value)}/mcp`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function resolveLaunchCwd(
|
|
214
|
+
event,
|
|
215
|
+
env = { homeDir: process.env.HOME || process.env.USERPROFILE, cwd: process.cwd() }
|
|
216
|
+
) {
|
|
217
|
+
return event.project?.localPath || env.homeDir || env.cwd;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function sleep(ms) {
|
|
221
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function helpText() {
|
|
225
|
+
return `MeAndMyAgents agent runner
|
|
226
|
+
|
|
227
|
+
Usage:
|
|
228
|
+
meandmyagents-runner watch --key maa_live_YOUR_AGENT_KEY --launcher codex
|
|
229
|
+
meandmyagents-runner watch --key maa_live_YOUR_AGENT_KEY --launcher claude --once
|
|
230
|
+
|
|
231
|
+
Options:
|
|
232
|
+
--key, -k Required agent-bound API key.
|
|
233
|
+
--api-url, -u API base URL. Defaults to ${defaultApiUrl}.
|
|
234
|
+
--launcher, -l Visible CLI launcher: codex or claude.
|
|
235
|
+
--interval Poll interval in seconds. Defaults to 10.
|
|
236
|
+
--once Check once, launch at most one visible session, then exit.
|
|
237
|
+
`;
|
|
238
|
+
}
|