@meandmyagents/agent-runner 0.1.2 → 0.1.3
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 +19 -11
- package/package.json +1 -1
- package/src/runner.js +342 -13
package/README.md
CHANGED
|
@@ -3,30 +3,38 @@
|
|
|
3
3
|
Local visible-session runner for MeAndMyAgents.
|
|
4
4
|
|
|
5
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
|
|
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-app
|
|
7
7
|
```
|
|
8
8
|
|
|
9
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
|
|
11
|
-
CLI session in the task project path.
|
|
12
|
-
|
|
13
|
-
sharing identity. The launched agent uses MCP to start,
|
|
14
|
-
remaining actionable cards.
|
|
10
|
+
When work exists, it claims the wake event and launches a visible Codex Desktop
|
|
11
|
+
thread or Claude CLI session in the task project path. Codex app threads get the
|
|
12
|
+
agent key in their per-thread MCP config, so one Mac can run several agent
|
|
13
|
+
profiles without sharing identity. The launched agent uses MCP to start,
|
|
14
|
+
complete, and drain the remaining actionable cards.
|
|
15
15
|
|
|
16
|
-
Codex
|
|
16
|
+
Use `--launcher codex-app` for Codex Desktop. It opens a Codex app-server
|
|
17
|
+
controlled thread with approval policy `never` and sandbox
|
|
18
|
+
`danger-full-access`, then waits for that turn to finish before polling again.
|
|
17
19
|
Claude is launched with `--permission-mode bypassPermissions`. The runner prompt
|
|
18
20
|
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
|
|
20
|
-
code changes before calling `complete_task`.
|
|
21
|
+
the agent not to wait for human answers, and tells it to verify, commit, and
|
|
22
|
+
push code changes before calling `complete_task`.
|
|
21
23
|
|
|
22
|
-
On macOS, `--launcher codex` will
|
|
24
|
+
On macOS, `--launcher codex-app` will look for the bundled Codex app executable
|
|
23
25
|
at `/Applications/Codex.app/Contents/Resources/codex` when `codex` is not on
|
|
24
26
|
`PATH`. If your launcher lives somewhere else, pass it explicitly:
|
|
25
27
|
|
|
26
28
|
```bash
|
|
27
|
-
meandmyagents-runner watch --key maa_live_YOUR_AGENT_KEY --launcher codex --command /Applications/Codex.app/Contents/Resources/codex
|
|
29
|
+
meandmyagents-runner watch --key maa_live_YOUR_AGENT_KEY --launcher codex-app --command /Applications/Codex.app/Contents/Resources/codex
|
|
28
30
|
```
|
|
29
31
|
|
|
30
32
|
```bash
|
|
31
33
|
meandmyagents-runner watch --key maa_live_YOUR_AGENT_KEY --launcher claude --once
|
|
32
34
|
```
|
|
35
|
+
|
|
36
|
+
For legacy terminal Codex behavior, `--launcher codex` is still available.
|
|
37
|
+
|
|
38
|
+
If the runner reports that Codex Desktop remote control is not ready, open Codex
|
|
39
|
+
Desktop once and make sure the standalone Codex install is available. The runner
|
|
40
|
+
calls `codex remote-control` for you before opening the app-server thread.
|
package/package.json
CHANGED
package/src/runner.js
CHANGED
|
@@ -3,7 +3,10 @@ import { existsSync } from "node:fs";
|
|
|
3
3
|
import { delimiter, join } from "node:path";
|
|
4
4
|
|
|
5
5
|
export const defaultApiUrl = "https://meandmyagents.com/api";
|
|
6
|
-
const
|
|
6
|
+
const codexAppLauncher = "codex-app";
|
|
7
|
+
const supportedLaunchers = new Set(["codex", codexAppLauncher, "claude"]);
|
|
8
|
+
const codexAppClientVersion = "0.1.3";
|
|
9
|
+
const codexAppRequestTimeoutMs = 30_000;
|
|
7
10
|
|
|
8
11
|
export function parseRunnerArgs(argv = process.argv.slice(2)) {
|
|
9
12
|
const options = {
|
|
@@ -126,8 +129,22 @@ export function buildLaunchCommand({
|
|
|
126
129
|
throw new Error(`Unsupported launcher: ${launcher}`);
|
|
127
130
|
}
|
|
128
131
|
|
|
132
|
+
if (launcher === codexAppLauncher) {
|
|
133
|
+
return {
|
|
134
|
+
type: codexAppLauncher,
|
|
135
|
+
command: launcherCommand || launcherExecutableName(launcher),
|
|
136
|
+
args: [],
|
|
137
|
+
cwd,
|
|
138
|
+
prompt,
|
|
139
|
+
env: {
|
|
140
|
+
MAA_API_KEY: apiKey,
|
|
141
|
+
MAA_API_URL: mcpUrlFromApiUrl(apiUrl)
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
129
146
|
return {
|
|
130
|
-
command: launcherCommand || launcher,
|
|
147
|
+
command: launcherCommand || launcherExecutableName(launcher),
|
|
131
148
|
args: buildLauncherArgs({ launcher, prompt, cwd }),
|
|
132
149
|
cwd,
|
|
133
150
|
env: {
|
|
@@ -137,6 +154,40 @@ export function buildLaunchCommand({
|
|
|
137
154
|
};
|
|
138
155
|
}
|
|
139
156
|
|
|
157
|
+
export function buildCodexAppThreadStartParams({ cwd, apiKey, apiUrl }) {
|
|
158
|
+
return {
|
|
159
|
+
approvalPolicy: "never",
|
|
160
|
+
config: apiKey
|
|
161
|
+
? {
|
|
162
|
+
mcp_servers: {
|
|
163
|
+
me_and_my_agents: {
|
|
164
|
+
enabled: true,
|
|
165
|
+
http_headers: { Authorization: `Bearer ${apiKey}` },
|
|
166
|
+
url: mcpUrlFromApiUrl(apiUrl)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
: null,
|
|
171
|
+
cwd,
|
|
172
|
+
developerInstructions:
|
|
173
|
+
"This thread was opened by the MeAndMyAgents local runner. Treat the user prompt as an autonomous task wake-up and use the MeAndMyAgents MCP server for task state.",
|
|
174
|
+
sandbox: "danger-full-access",
|
|
175
|
+
serviceName: "MeAndMyAgents",
|
|
176
|
+
sessionStartSource: "startup",
|
|
177
|
+
threadSource: "user"
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function buildCodexAppTurnStartParams({ threadId, cwd, prompt }) {
|
|
182
|
+
return {
|
|
183
|
+
approvalPolicy: "never",
|
|
184
|
+
cwd,
|
|
185
|
+
input: [{ type: "text", text: prompt }],
|
|
186
|
+
sandboxPolicy: { type: "dangerFullAccess" },
|
|
187
|
+
threadId
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
140
191
|
function buildLauncherArgs({ launcher, prompt, cwd }) {
|
|
141
192
|
if (launcher === "codex") {
|
|
142
193
|
return [
|
|
@@ -248,6 +299,14 @@ async function launchEvent(options, event) {
|
|
|
248
299
|
`Launching ${launch.command} for ${event.card?.title ?? event.id} in ${launch.cwd}\n`
|
|
249
300
|
);
|
|
250
301
|
|
|
302
|
+
if (options.launcher === codexAppLauncher) {
|
|
303
|
+
await launchCodexAppSession(launch, {
|
|
304
|
+
apiKey: options.key,
|
|
305
|
+
apiUrl: options.apiUrl
|
|
306
|
+
});
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
251
310
|
await new Promise((resolve, reject) => {
|
|
252
311
|
const child = spawn(launch.command, launch.args, {
|
|
253
312
|
cwd: launch.cwd,
|
|
@@ -265,6 +324,269 @@ async function launchEvent(options, event) {
|
|
|
265
324
|
});
|
|
266
325
|
}
|
|
267
326
|
|
|
327
|
+
async function launchCodexAppSession(launch, { apiKey, apiUrl }) {
|
|
328
|
+
await ensureCodexAppRemoteControl(launch);
|
|
329
|
+
|
|
330
|
+
const proxy = spawn(launch.command, ["app-server", "proxy"], {
|
|
331
|
+
cwd: launch.cwd,
|
|
332
|
+
env: { ...process.env, ...launch.env },
|
|
333
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
334
|
+
});
|
|
335
|
+
const connection = createCodexAppConnection(proxy);
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
await connection.request("initialize", {
|
|
339
|
+
clientInfo: {
|
|
340
|
+
name: "meandmyagents-runner",
|
|
341
|
+
title: "MeAndMyAgents runner",
|
|
342
|
+
version: codexAppClientVersion
|
|
343
|
+
},
|
|
344
|
+
capabilities: { experimentalApi: true }
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const threadResponse = await connection.request(
|
|
348
|
+
"thread/start",
|
|
349
|
+
buildCodexAppThreadStartParams({ cwd: launch.cwd, apiKey, apiUrl })
|
|
350
|
+
);
|
|
351
|
+
const threadId = threadResponse?.thread?.id;
|
|
352
|
+
if (!threadId) {
|
|
353
|
+
throw new Error("Codex app-server did not return a thread id.");
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const turnResponse = await connection.request(
|
|
357
|
+
"turn/start",
|
|
358
|
+
buildCodexAppTurnStartParams({
|
|
359
|
+
threadId,
|
|
360
|
+
cwd: launch.cwd,
|
|
361
|
+
prompt: launch.prompt
|
|
362
|
+
})
|
|
363
|
+
);
|
|
364
|
+
const turnId = turnResponse?.turn?.id;
|
|
365
|
+
process.stdout.write(
|
|
366
|
+
`Codex app thread ${threadId}${turnId ? ` turn ${turnId}` : ""} started.\n`
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
const completion = await connection.waitForNotification((message) => {
|
|
370
|
+
if (message.method !== "turn/completed") return false;
|
|
371
|
+
if (message.params?.threadId !== threadId) return false;
|
|
372
|
+
return !turnId || message.params?.turn?.id === turnId;
|
|
373
|
+
});
|
|
374
|
+
const completedTurn = completion.params?.turn;
|
|
375
|
+
if (completedTurn?.status === "failed") {
|
|
376
|
+
const message = completedTurn.error?.message ?? "Codex app turn failed.";
|
|
377
|
+
throw new Error(message);
|
|
378
|
+
}
|
|
379
|
+
} finally {
|
|
380
|
+
connection.close();
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async function ensureCodexAppRemoteControl(launch) {
|
|
385
|
+
try {
|
|
386
|
+
await runProcess({
|
|
387
|
+
command: launch.command,
|
|
388
|
+
args: ["remote-control"],
|
|
389
|
+
cwd: launch.cwd,
|
|
390
|
+
env: launch.env,
|
|
391
|
+
timeoutMs: codexAppRequestTimeoutMs
|
|
392
|
+
});
|
|
393
|
+
} catch (error) {
|
|
394
|
+
throw new Error(
|
|
395
|
+
[
|
|
396
|
+
"Codex Desktop remote control is not ready.",
|
|
397
|
+
String(error?.message ?? error),
|
|
398
|
+
"Open Codex Desktop, make sure the standalone Codex install is available, then run the runner again."
|
|
399
|
+
].join("\n")
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function createCodexAppConnection(child) {
|
|
405
|
+
const pending = new Map();
|
|
406
|
+
const notificationWaiters = new Set();
|
|
407
|
+
const notificationBacklog = [];
|
|
408
|
+
let stdoutBuffer = "";
|
|
409
|
+
let stderr = "";
|
|
410
|
+
let closed = false;
|
|
411
|
+
|
|
412
|
+
child.stdout.on("data", (chunk) => {
|
|
413
|
+
stdoutBuffer += chunk.toString("utf8");
|
|
414
|
+
stdoutBuffer = consumeCodexAppMessages(stdoutBuffer, (message) => {
|
|
415
|
+
if (Object.prototype.hasOwnProperty.call(message, "id")) {
|
|
416
|
+
const waiter = pending.get(message.id);
|
|
417
|
+
if (!waiter) return;
|
|
418
|
+
pending.delete(message.id);
|
|
419
|
+
clearTimeout(waiter.timer);
|
|
420
|
+
if (message.error) {
|
|
421
|
+
waiter.reject(new Error(message.error.message ?? "Codex app-server request failed."));
|
|
422
|
+
} else {
|
|
423
|
+
waiter.resolve(message.result);
|
|
424
|
+
}
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
let handled = false;
|
|
429
|
+
for (const waiter of notificationWaiters) {
|
|
430
|
+
if (waiter.predicate(message)) {
|
|
431
|
+
notificationWaiters.delete(waiter);
|
|
432
|
+
clearTimeout(waiter.timer);
|
|
433
|
+
waiter.resolve(message);
|
|
434
|
+
handled = true;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (!handled) {
|
|
438
|
+
notificationBacklog.push(message);
|
|
439
|
+
if (notificationBacklog.length > 100) notificationBacklog.shift();
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
child.stderr.on("data", (chunk) => {
|
|
445
|
+
stderr += chunk.toString("utf8");
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
child.once("error", (error) => {
|
|
449
|
+
rejectAll(error);
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
child.once("exit", (code) => {
|
|
453
|
+
closed = true;
|
|
454
|
+
if (code && code !== 0) {
|
|
455
|
+
rejectAll(new Error(`Codex app-server proxy exited with code ${code}: ${stderr}`));
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
let nextId = 1;
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
request(method, params) {
|
|
463
|
+
if (closed) {
|
|
464
|
+
return Promise.reject(new Error("Codex app-server proxy is closed."));
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const id = nextId;
|
|
468
|
+
nextId += 1;
|
|
469
|
+
const message = { id, method, params };
|
|
470
|
+
|
|
471
|
+
return new Promise((resolve, reject) => {
|
|
472
|
+
const timer = setTimeout(() => {
|
|
473
|
+
pending.delete(id);
|
|
474
|
+
reject(new Error(`Codex app-server request timed out: ${method}`));
|
|
475
|
+
}, codexAppRequestTimeoutMs);
|
|
476
|
+
pending.set(id, { resolve, reject, timer });
|
|
477
|
+
child.stdin.write(`${JSON.stringify(message)}\n`);
|
|
478
|
+
});
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
waitForNotification(predicate) {
|
|
482
|
+
if (closed) {
|
|
483
|
+
return Promise.reject(new Error("Codex app-server proxy is closed."));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const existingIndex = notificationBacklog.findIndex(predicate);
|
|
487
|
+
if (existingIndex >= 0) {
|
|
488
|
+
const [message] = notificationBacklog.splice(existingIndex, 1);
|
|
489
|
+
return Promise.resolve(message);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return new Promise((resolve, reject) => {
|
|
493
|
+
const timer = setTimeout(() => {
|
|
494
|
+
notificationWaiters.delete(waiter);
|
|
495
|
+
reject(new Error("Timed out waiting for Codex app turn completion."));
|
|
496
|
+
}, Number(process.env.MAA_CODEX_APP_TURN_TIMEOUT_MS ?? 21_600_000));
|
|
497
|
+
const waiter = { predicate, resolve, reject, timer };
|
|
498
|
+
notificationWaiters.add(waiter);
|
|
499
|
+
});
|
|
500
|
+
},
|
|
501
|
+
|
|
502
|
+
close() {
|
|
503
|
+
closed = true;
|
|
504
|
+
child.kill("SIGTERM");
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
function rejectAll(error) {
|
|
509
|
+
for (const waiter of pending.values()) {
|
|
510
|
+
clearTimeout(waiter.timer);
|
|
511
|
+
waiter.reject(error);
|
|
512
|
+
}
|
|
513
|
+
pending.clear();
|
|
514
|
+
|
|
515
|
+
for (const waiter of notificationWaiters) {
|
|
516
|
+
clearTimeout(waiter.timer);
|
|
517
|
+
waiter.reject(error);
|
|
518
|
+
}
|
|
519
|
+
notificationWaiters.clear();
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function consumeCodexAppMessages(buffer, onMessage) {
|
|
524
|
+
let nextBuffer = buffer;
|
|
525
|
+
|
|
526
|
+
while (nextBuffer.length > 0) {
|
|
527
|
+
const contentLengthMatch = nextBuffer.match(/^Content-Length: (\d+)\r?\n\r?\n/i);
|
|
528
|
+
if (contentLengthMatch) {
|
|
529
|
+
const headerLength = contentLengthMatch[0].length;
|
|
530
|
+
const contentLength = Number(contentLengthMatch[1]);
|
|
531
|
+
if (nextBuffer.length < headerLength + contentLength) break;
|
|
532
|
+
emitJson(nextBuffer.slice(headerLength, headerLength + contentLength), onMessage);
|
|
533
|
+
nextBuffer = nextBuffer.slice(headerLength + contentLength);
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const newlineIndex = nextBuffer.indexOf("\n");
|
|
538
|
+
if (newlineIndex === -1) break;
|
|
539
|
+
const line = nextBuffer.slice(0, newlineIndex).trim();
|
|
540
|
+
nextBuffer = nextBuffer.slice(newlineIndex + 1);
|
|
541
|
+
if (line) emitJson(line, onMessage);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return nextBuffer;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function emitJson(value, onMessage) {
|
|
548
|
+
try {
|
|
549
|
+
onMessage(JSON.parse(value));
|
|
550
|
+
} catch {
|
|
551
|
+
// Ignore non-JSON process output; request timeouts surface protocol failures.
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
async function runProcess({ command, args, cwd, env, timeoutMs }) {
|
|
556
|
+
return await new Promise((resolve, reject) => {
|
|
557
|
+
const child = spawn(command, args, {
|
|
558
|
+
cwd,
|
|
559
|
+
env: { ...process.env, ...env },
|
|
560
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
561
|
+
});
|
|
562
|
+
let stdout = "";
|
|
563
|
+
let stderr = "";
|
|
564
|
+
const timer = setTimeout(() => {
|
|
565
|
+
child.kill("SIGTERM");
|
|
566
|
+
reject(new Error(`${command} ${args.join(" ")} timed out.`));
|
|
567
|
+
}, timeoutMs);
|
|
568
|
+
|
|
569
|
+
child.stdout.on("data", (chunk) => {
|
|
570
|
+
stdout += chunk.toString("utf8");
|
|
571
|
+
});
|
|
572
|
+
child.stderr.on("data", (chunk) => {
|
|
573
|
+
stderr += chunk.toString("utf8");
|
|
574
|
+
});
|
|
575
|
+
child.once("error", (error) => {
|
|
576
|
+
clearTimeout(timer);
|
|
577
|
+
reject(error);
|
|
578
|
+
});
|
|
579
|
+
child.once("exit", (code) => {
|
|
580
|
+
clearTimeout(timer);
|
|
581
|
+
if (code && code !== 0) {
|
|
582
|
+
reject(new Error(stderr || stdout || `${command} exited with code ${code}`));
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
resolve({ stdout, stderr });
|
|
586
|
+
});
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
|
|
268
590
|
export function resolveLauncherCommand({
|
|
269
591
|
launcher,
|
|
270
592
|
launcherCommand = "",
|
|
@@ -278,12 +600,18 @@ export function resolveLauncherCommand({
|
|
|
278
600
|
throw new Error(`Unsupported launcher: ${launcher}`);
|
|
279
601
|
}
|
|
280
602
|
|
|
281
|
-
|
|
603
|
+
const executableName = launcherExecutableName(launcher);
|
|
282
604
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
605
|
+
if (isCommandOnPath(executableName, { pathEnv, platform, exists })) {
|
|
606
|
+
return executableName;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const knownPath = knownLauncherPaths(launcher, platform, homeDir).find((path) => exists(path));
|
|
610
|
+
return knownPath ?? executableName;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function launcherExecutableName(launcher) {
|
|
614
|
+
return launcher === codexAppLauncher ? "codex" : launcher;
|
|
287
615
|
}
|
|
288
616
|
|
|
289
617
|
function isCommandOnPath(command, { pathEnv, platform, exists }) {
|
|
@@ -301,12 +629,12 @@ function isCommandOnPath(command, { pathEnv, platform, exists }) {
|
|
|
301
629
|
function knownLauncherPaths(launcher, platform, homeDir) {
|
|
302
630
|
if (platform === "darwin") {
|
|
303
631
|
const commonBinPaths = [
|
|
304
|
-
homeDir ? join(homeDir, ".local/bin", launcher) : "",
|
|
305
|
-
join("/opt/homebrew/bin", launcher),
|
|
306
|
-
join("/usr/local/bin", launcher)
|
|
632
|
+
homeDir ? join(homeDir, ".local/bin", launcherExecutableName(launcher)) : "",
|
|
633
|
+
join("/opt/homebrew/bin", launcherExecutableName(launcher)),
|
|
634
|
+
join("/usr/local/bin", launcherExecutableName(launcher))
|
|
307
635
|
].filter(Boolean);
|
|
308
636
|
|
|
309
|
-
if (launcher === "codex") {
|
|
637
|
+
if (launcher === "codex" || launcher === codexAppLauncher) {
|
|
310
638
|
return [
|
|
311
639
|
...commonBinPaths,
|
|
312
640
|
"/Applications/Codex.app/Contents/Resources/codex"
|
|
@@ -316,7 +644,7 @@ function knownLauncherPaths(launcher, platform, homeDir) {
|
|
|
316
644
|
return commonBinPaths;
|
|
317
645
|
}
|
|
318
646
|
|
|
319
|
-
return homeDir ? [join(homeDir, ".local/bin", launcher)] : [];
|
|
647
|
+
return homeDir ? [join(homeDir, ".local/bin", launcherExecutableName(launcher))] : [];
|
|
320
648
|
}
|
|
321
649
|
|
|
322
650
|
function normalizeApiUrl(value) {
|
|
@@ -342,13 +670,14 @@ function helpText() {
|
|
|
342
670
|
return `MeAndMyAgents agent runner
|
|
343
671
|
|
|
344
672
|
Usage:
|
|
673
|
+
meandmyagents-runner watch --key maa_live_YOUR_AGENT_KEY --launcher codex-app
|
|
345
674
|
meandmyagents-runner watch --key maa_live_YOUR_AGENT_KEY --launcher codex
|
|
346
675
|
meandmyagents-runner watch --key maa_live_YOUR_AGENT_KEY --launcher claude --once
|
|
347
676
|
|
|
348
677
|
Options:
|
|
349
678
|
--key, -k Required agent-bound API key.
|
|
350
679
|
--api-url, -u API base URL. Defaults to ${defaultApiUrl}.
|
|
351
|
-
--launcher, -l Visible
|
|
680
|
+
--launcher, -l Visible launcher: codex-app, codex, or claude.
|
|
352
681
|
--command, -c Optional explicit executable path, e.g. /Applications/Codex.app/Contents/Resources/codex.
|
|
353
682
|
--interval Poll interval in seconds. Defaults to 10.
|
|
354
683
|
--once Check once, launch at most one visible session, then exit.
|