@meandmyagents/agent-runner 0.1.0 → 0.1.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 (3) hide show
  1. package/README.md +14 -0
  2. package/package.json +1 -1
  3. package/src/runner.js +125 -7
package/README.md CHANGED
@@ -13,6 +13,20 @@ process as `MAA_API_KEY`, so one Mac can run several agent profiles without
13
13
  sharing identity. The launched agent uses MCP to start, complete, and drain the
14
14
  remaining actionable cards.
15
15
 
16
+ Codex is launched with `--ask-for-approval never --sandbox danger-full-access`.
17
+ Claude is launched with `--permission-mode bypassPermissions`. The runner prompt
18
+ also includes the effective workflow instructions saved in MeAndMyAgents, tells
19
+ the agent not to wait for human answers, and tells it to verify, commit, and push
20
+ code changes before calling `complete_task`.
21
+
22
+ On macOS, `--launcher codex` will also look for the bundled Codex app executable
23
+ at `/Applications/Codex.app/Contents/Resources/codex` when `codex` is not on
24
+ `PATH`. If your launcher lives somewhere else, pass it explicitly:
25
+
26
+ ```bash
27
+ meandmyagents-runner watch --key maa_live_YOUR_AGENT_KEY --launcher codex --command /Applications/Codex.app/Contents/Resources/codex
28
+ ```
29
+
16
30
  ```bash
17
31
  meandmyagents-runner watch --key maa_live_YOUR_AGENT_KEY --launcher claude --once
18
32
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meandmyagents/agent-runner",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Local visible-session task runner for Me And My Agents.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/runner.js CHANGED
@@ -1,15 +1,18 @@
1
1
  import { spawn } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { delimiter, join } from "node:path";
2
4
 
3
5
  export const defaultApiUrl = "https://meandmyagents.com/api";
4
6
  const supportedLaunchers = new Set(["codex", "claude"]);
5
7
 
6
8
  export function parseRunnerArgs(argv = process.argv.slice(2)) {
7
9
  const options = {
8
- command: argv[0] ?? "help",
10
+ command: argv[0] === "--help" || argv[0] === "-h" ? "help" : argv[0] ?? "help",
9
11
  apiUrl: defaultApiUrl,
10
12
  intervalSeconds: 10,
11
13
  key: "",
12
14
  launcher: "codex",
15
+ launcherCommand: "",
13
16
  once: false
14
17
  };
15
18
 
@@ -34,6 +37,12 @@ export function parseRunnerArgs(argv = process.argv.slice(2)) {
34
37
  continue;
35
38
  }
36
39
 
40
+ if (arg === "--command" || arg === "-c") {
41
+ options.launcherCommand = argv[index + 1] ?? "";
42
+ index += 1;
43
+ continue;
44
+ }
45
+
37
46
  if (arg === "--interval") {
38
47
  options.intervalSeconds = Number(argv[index + 1] ?? "10");
39
48
  index += 1;
@@ -68,30 +77,58 @@ export function buildAgentPrompt({
68
77
  agentName,
69
78
  cardId,
70
79
  eventId,
71
- projectName
80
+ project,
81
+ projectName,
82
+ workflow
72
83
  }) {
84
+ const workflowInstructions = Array.isArray(workflow?.instructions)
85
+ ? workflow.instructions
86
+ : [];
87
+ const defaultBranch = project?.defaultBranch || "the default branch";
88
+ const codebaseLines = project?.repositoryUrl
89
+ ? [
90
+ `Repository: ${project.repositoryUrl}`,
91
+ `Default branch: ${defaultBranch}`,
92
+ project.localPath ? `Local checkout path: ${project.localPath}` : null,
93
+ `If you modify code, run verification, then commit and push to ${defaultBranch} before complete_task.`
94
+ ].filter(Boolean)
95
+ : ["This project has no repository URL, so treat it as non-code work unless list_my_tasks says otherwise."];
96
+
73
97
  return [
74
98
  `You are ${agentName}. MeAndMyAgents has assigned work in ${projectName}.`,
75
99
  `Wake event: ${eventId}. First card that woke this session: ${cardId}.`,
100
+ "This is an autonomous runner wake-up. Do not wait for a human answer.",
101
+ "If you are blocked, add a card comment explaining the blocker and stop; do not sit idle asking a question.",
76
102
  "Use the MeAndMyAgents MCP server now.",
77
103
  "Call whoami to confirm your identity and project access.",
104
+ "Follow these workflow rules from MeAndMyAgents:",
105
+ ...workflowInstructions.map((instruction) => `- ${instruction}`),
106
+ "Codebase context:",
107
+ ...codebaseLines.map((line) => `- ${line}`),
78
108
  "Call list_my_tasks, then start_task on the next actionable card before editing files.",
79
109
  "Work only in the task codebase.localPath and only when codebase.canModifyCode is true.",
80
110
  "Add comments with useful progress and completion summaries.",
81
- "Call complete_task when a task is ready for human testing.",
111
+ "Call complete_task with a useful summary when a task is ready for human testing.",
82
112
  "Call list_my_tasks again after each completion.",
83
113
  "Keep taking the next task until no actionable tasks remain, then stop."
84
114
  ].join("\n");
85
115
  }
86
116
 
87
- export function buildLaunchCommand({ launcher, prompt, cwd, apiKey, apiUrl }) {
117
+ export function buildLaunchCommand({
118
+ launcher,
119
+ launcherCommand,
120
+ prompt,
121
+ cwd,
122
+ apiKey,
123
+ apiUrl
124
+ }) {
88
125
  if (!supportedLaunchers.has(launcher)) {
89
126
  throw new Error(`Unsupported launcher: ${launcher}`);
90
127
  }
91
128
 
92
129
  return {
93
- command: launcher,
94
- args: [prompt],
130
+ command: launcherCommand || launcher,
131
+ args: buildLauncherArgs({ launcher, prompt, cwd }),
95
132
  cwd,
96
133
  env: {
97
134
  MAA_API_KEY: apiKey,
@@ -100,6 +137,26 @@ export function buildLaunchCommand({ launcher, prompt, cwd, apiKey, apiUrl }) {
100
137
  };
101
138
  }
102
139
 
140
+ function buildLauncherArgs({ launcher, prompt, cwd }) {
141
+ if (launcher === "codex") {
142
+ return [
143
+ "--ask-for-approval",
144
+ "never",
145
+ "--sandbox",
146
+ "danger-full-access",
147
+ "--cd",
148
+ cwd,
149
+ prompt
150
+ ];
151
+ }
152
+
153
+ if (launcher === "claude") {
154
+ return ["--permission-mode", "bypassPermissions", prompt];
155
+ }
156
+
157
+ return [prompt];
158
+ }
159
+
103
160
  export async function runCli(argv = process.argv.slice(2)) {
104
161
  const options = parseRunnerArgs(argv);
105
162
 
@@ -171,10 +228,16 @@ async function launchEvent(options, event) {
171
228
  agentName: event.agent?.name ?? options.launcher,
172
229
  cardId: event.card?.id ?? "unknown-card",
173
230
  eventId: event.id,
174
- projectName: event.project?.name ?? "this project"
231
+ project: event.project,
232
+ projectName: event.project?.name ?? "this project",
233
+ workflow: event.workflow
175
234
  });
176
235
  const launch = buildLaunchCommand({
177
236
  launcher: options.launcher,
237
+ launcherCommand: resolveLauncherCommand({
238
+ launcher: options.launcher,
239
+ launcherCommand: options.launcherCommand
240
+ }),
178
241
  prompt,
179
242
  cwd,
180
243
  apiKey: options.key,
@@ -202,6 +265,60 @@ async function launchEvent(options, event) {
202
265
  });
203
266
  }
204
267
 
268
+ export function resolveLauncherCommand({
269
+ launcher,
270
+ launcherCommand = "",
271
+ pathEnv = process.env.PATH ?? "",
272
+ platform = process.platform,
273
+ homeDir = process.env.HOME || process.env.USERPROFILE || "",
274
+ exists = existsSync
275
+ }) {
276
+ if (launcherCommand) return launcherCommand;
277
+ if (!supportedLaunchers.has(launcher)) {
278
+ throw new Error(`Unsupported launcher: ${launcher}`);
279
+ }
280
+
281
+ if (isCommandOnPath(launcher, { pathEnv, platform, exists })) return launcher;
282
+
283
+ const knownPath = knownLauncherPaths(launcher, platform, homeDir).find((path) =>
284
+ exists(path)
285
+ );
286
+ return knownPath ?? launcher;
287
+ }
288
+
289
+ function isCommandOnPath(command, { pathEnv, platform, exists }) {
290
+ const executableNames =
291
+ platform === "win32" ? [command, `${command}.cmd`, `${command}.exe`] : [command];
292
+
293
+ return pathEnv
294
+ .split(delimiter)
295
+ .filter(Boolean)
296
+ .some((pathDirectory) =>
297
+ executableNames.some((executableName) => exists(join(pathDirectory, executableName)))
298
+ );
299
+ }
300
+
301
+ function knownLauncherPaths(launcher, platform, homeDir) {
302
+ if (platform === "darwin") {
303
+ const commonBinPaths = [
304
+ homeDir ? join(homeDir, ".local/bin", launcher) : "",
305
+ join("/opt/homebrew/bin", launcher),
306
+ join("/usr/local/bin", launcher)
307
+ ].filter(Boolean);
308
+
309
+ if (launcher === "codex") {
310
+ return [
311
+ ...commonBinPaths,
312
+ "/Applications/Codex.app/Contents/Resources/codex"
313
+ ];
314
+ }
315
+
316
+ return commonBinPaths;
317
+ }
318
+
319
+ return homeDir ? [join(homeDir, ".local/bin", launcher)] : [];
320
+ }
321
+
205
322
  function normalizeApiUrl(value) {
206
323
  return (value || defaultApiUrl).replace(/\/$/, "").replace(/\/mcp$/, "");
207
324
  }
@@ -232,6 +349,7 @@ Options:
232
349
  --key, -k Required agent-bound API key.
233
350
  --api-url, -u API base URL. Defaults to ${defaultApiUrl}.
234
351
  --launcher, -l Visible CLI launcher: codex or claude.
352
+ --command, -c Optional explicit executable path, e.g. /Applications/Codex.app/Contents/Resources/codex.
235
353
  --interval Poll interval in seconds. Defaults to 10.
236
354
  --once Check once, launch at most one visible session, then exit.
237
355
  `;