@aiaiai-pt/martha-cli 0.24.0 → 0.25.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/dist/index.js +124 -76
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11074,10 +11074,11 @@ ${items.length} ${config.typeNamePlural}`));
|
|
|
11074
11074
|
|
|
11075
11075
|
// src/commands/chat.ts
|
|
11076
11076
|
import { createInterface as createInterface2 } from "node:readline";
|
|
11077
|
+
init_config();
|
|
11077
11078
|
init_errors();
|
|
11078
11079
|
|
|
11079
11080
|
// src/version.ts
|
|
11080
|
-
var CLI_VERSION = "0.
|
|
11081
|
+
var CLI_VERSION = "0.25.0";
|
|
11081
11082
|
|
|
11082
11083
|
// src/commands/sessions.ts
|
|
11083
11084
|
function relativeTime(iso) {
|
|
@@ -11373,15 +11374,26 @@ async function runHostCommand(command, toolCallId, opts) {
|
|
|
11373
11374
|
return "";
|
|
11374
11375
|
}
|
|
11375
11376
|
};
|
|
11377
|
+
const stdin = opts.stdinData != null ? "pipe" : "ignore";
|
|
11376
11378
|
return await new Promise((resolve, reject) => {
|
|
11377
11379
|
let settled = false;
|
|
11378
|
-
const child = spawn(
|
|
11380
|
+
const child = opts.argv ? spawn(opts.argv[0], opts.argv.slice(1), {
|
|
11381
|
+
shell: false,
|
|
11382
|
+
cwd: opts.cwd,
|
|
11383
|
+
detached: true,
|
|
11384
|
+
stdio: [stdin, fd, fd]
|
|
11385
|
+
}) : spawn(command, {
|
|
11379
11386
|
shell: true,
|
|
11380
11387
|
cwd: opts.cwd,
|
|
11381
11388
|
detached: true,
|
|
11382
|
-
stdio: [
|
|
11389
|
+
stdio: [stdin, fd, fd]
|
|
11383
11390
|
});
|
|
11384
11391
|
closeSync(fd);
|
|
11392
|
+
if (opts.stdinData != null && child.stdin) {
|
|
11393
|
+
child.stdin.on("error", () => {});
|
|
11394
|
+
child.stdin.write(opts.stdinData);
|
|
11395
|
+
child.stdin.end();
|
|
11396
|
+
}
|
|
11385
11397
|
const timer = setTimeout(async () => {
|
|
11386
11398
|
if (settled)
|
|
11387
11399
|
return;
|
|
@@ -11392,7 +11404,8 @@ async function runHostCommand(command, toolCallId, opts) {
|
|
|
11392
11404
|
stderr: "",
|
|
11393
11405
|
exit_code: 0,
|
|
11394
11406
|
backgrounded: true,
|
|
11395
|
-
pid: child.pid
|
|
11407
|
+
pid: child.pid,
|
|
11408
|
+
log_path: logPath
|
|
11396
11409
|
});
|
|
11397
11410
|
}, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
11398
11411
|
child.on("error", (e) => {
|
|
@@ -11407,7 +11420,12 @@ async function runHostCommand(command, toolCallId, opts) {
|
|
|
11407
11420
|
return;
|
|
11408
11421
|
settled = true;
|
|
11409
11422
|
clearTimeout(timer);
|
|
11410
|
-
resolve({
|
|
11423
|
+
resolve({
|
|
11424
|
+
stdout: await readLog(),
|
|
11425
|
+
stderr: "",
|
|
11426
|
+
exit_code: code ?? -1,
|
|
11427
|
+
log_path: logPath
|
|
11428
|
+
});
|
|
11411
11429
|
});
|
|
11412
11430
|
});
|
|
11413
11431
|
}
|
|
@@ -11816,66 +11834,51 @@ function parseHostFileRequests(data) {
|
|
|
11816
11834
|
}
|
|
11817
11835
|
|
|
11818
11836
|
// src/lib/local-agent.ts
|
|
11819
|
-
|
|
11820
|
-
|
|
11837
|
+
var AGENT_PRESETS = {
|
|
11838
|
+
claude: {
|
|
11839
|
+
name: "claude",
|
|
11840
|
+
command: ["claude", "-p"],
|
|
11841
|
+
promptVia: "argv",
|
|
11842
|
+
description: "Claude Code headless (prompt passed as an argument)."
|
|
11843
|
+
},
|
|
11844
|
+
codex: {
|
|
11845
|
+
name: "codex",
|
|
11846
|
+
command: ["codex", "exec", "--skip-git-repo-check"],
|
|
11847
|
+
promptVia: "stdin",
|
|
11848
|
+
description: "OpenAI Codex CLI (prompt on stdin; runs outside a git repo)."
|
|
11849
|
+
},
|
|
11850
|
+
"cursor-agent": {
|
|
11851
|
+
name: "cursor-agent",
|
|
11852
|
+
command: ["cursor-agent", "--print", "--trust"],
|
|
11853
|
+
promptVia: "argv",
|
|
11854
|
+
description: "Cursor Agent headless (prompt passed as an argument)."
|
|
11855
|
+
}
|
|
11856
|
+
};
|
|
11857
|
+
var DEFAULT_LOCAL_AGENT = "codex";
|
|
11821
11858
|
var DEFAULT_LOCAL_AGENT_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
const timeoutMs = opts.timeoutMs ?? DEFAULT_LOCAL_AGENT_TIMEOUT_MS;
|
|
11833
|
-
return await new Promise((resolve2, reject) => {
|
|
11834
|
-
let out = "";
|
|
11835
|
-
let settled = false;
|
|
11836
|
-
let child;
|
|
11837
|
-
try {
|
|
11838
|
-
child = spawn2(argv[0], argv.slice(1), {
|
|
11839
|
-
cwd: opts.cwd,
|
|
11840
|
-
shell: false,
|
|
11841
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
11842
|
-
});
|
|
11843
|
-
} catch (e) {
|
|
11844
|
-
reject(e);
|
|
11845
|
-
return;
|
|
11846
|
-
}
|
|
11847
|
-
const append = (chunk) => {
|
|
11848
|
-
if (out.length < cap)
|
|
11849
|
-
out += chunk.toString("utf8");
|
|
11859
|
+
function resolveAgentInvocation(value) {
|
|
11860
|
+
const trimmed = (value || "").trim();
|
|
11861
|
+
if (!trimmed)
|
|
11862
|
+
throw new Error("local agent command is empty");
|
|
11863
|
+
const preset = AGENT_PRESETS[trimmed];
|
|
11864
|
+
if (preset) {
|
|
11865
|
+
return {
|
|
11866
|
+
command: preset.command,
|
|
11867
|
+
promptVia: preset.promptVia,
|
|
11868
|
+
source: preset.name
|
|
11850
11869
|
};
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
}, timeoutMs);
|
|
11864
|
-
child.on("error", (e) => {
|
|
11865
|
-
if (settled)
|
|
11866
|
-
return;
|
|
11867
|
-
settled = true;
|
|
11868
|
-
clearTimeout(timer);
|
|
11869
|
-
reject(e);
|
|
11870
|
-
});
|
|
11871
|
-
child.on("close", (code) => {
|
|
11872
|
-
if (settled)
|
|
11873
|
-
return;
|
|
11874
|
-
settled = true;
|
|
11875
|
-
clearTimeout(timer);
|
|
11876
|
-
resolve2({ text: out.slice(0, cap), exit_code: code ?? -1 });
|
|
11877
|
-
});
|
|
11878
|
-
});
|
|
11870
|
+
}
|
|
11871
|
+
const parts = trimmed.split(/\s+/).filter(Boolean);
|
|
11872
|
+
return { command: parts, promptVia: "argv", source: trimmed };
|
|
11873
|
+
}
|
|
11874
|
+
function buildSpawn(invocation, task) {
|
|
11875
|
+
if (invocation.command.length === 0)
|
|
11876
|
+
throw new Error("local agent command is empty");
|
|
11877
|
+
const [bin, ...rest] = invocation.command;
|
|
11878
|
+
if (invocation.promptVia === "stdin") {
|
|
11879
|
+
return { bin, args: rest, stdin: task };
|
|
11880
|
+
}
|
|
11881
|
+
return { bin, args: [...rest, task], stdin: null };
|
|
11879
11882
|
}
|
|
11880
11883
|
function parseLocalAgentRequests(data) {
|
|
11881
11884
|
let tools;
|
|
@@ -12032,7 +12035,7 @@ async function resolveHostFile(req, state) {
|
|
|
12032
12035
|
return { error: true, message: result.message };
|
|
12033
12036
|
return result;
|
|
12034
12037
|
}
|
|
12035
|
-
async function resolveLocalAgent(req, state) {
|
|
12038
|
+
async function resolveLocalAgent(sessionId, toolCallId, req, state) {
|
|
12036
12039
|
if (!state.autoYes) {
|
|
12037
12040
|
process.stderr.write(source_default.yellow(`
|
|
12038
12041
|
[local_agent] refused (re-run with --yes to allow): ${req.task.slice(0, 80)}
|
|
@@ -12042,24 +12045,64 @@ async function resolveLocalAgent(req, state) {
|
|
|
12042
12045
|
message: "Local agent delegation declined; re-run with --yes to allow."
|
|
12043
12046
|
};
|
|
12044
12047
|
}
|
|
12045
|
-
|
|
12048
|
+
let invocation;
|
|
12049
|
+
try {
|
|
12050
|
+
invocation = resolveAgentInvocation(state.localAgent);
|
|
12051
|
+
} catch (e) {
|
|
12052
|
+
return { error: true, message: `invalid local agent config: ${String(e)}` };
|
|
12053
|
+
}
|
|
12054
|
+
const decision = decideFromJournal(await state.journal.lookup(sessionId, toolCallId));
|
|
12055
|
+
if (decision.kind === "replay")
|
|
12056
|
+
return decision.result;
|
|
12057
|
+
if (decision.kind === "crashed") {
|
|
12058
|
+
return {
|
|
12059
|
+
error: true,
|
|
12060
|
+
exit_code: -1,
|
|
12061
|
+
message: `A previous local_agent run (${decision.command}) did not complete; ` + `not re-running. Its log survives under .martha-runner/ — read it with host_read.`
|
|
12062
|
+
};
|
|
12063
|
+
}
|
|
12046
12064
|
const cwd = req.cwd ? `${state.cwd}/${req.cwd}` : state.cwd;
|
|
12065
|
+
const { bin, args, stdin } = buildSpawn(invocation, req.task);
|
|
12047
12066
|
process.stderr.write(source_default.dim(`
|
|
12048
|
-
[local_agent] (${
|
|
12067
|
+
[local_agent] (${invocation.source}) ${req.task.slice(0, 100)}
|
|
12049
12068
|
`));
|
|
12069
|
+
await state.journal.recordRunning(sessionId, toolCallId, `local_agent:${invocation.source}`, new Date().toISOString());
|
|
12070
|
+
let result;
|
|
12050
12071
|
try {
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
|
|
12054
|
-
|
|
12055
|
-
|
|
12056
|
-
};
|
|
12072
|
+
result = await runHostCommand(`local_agent:${invocation.source}`, toolCallId, {
|
|
12073
|
+
cwd,
|
|
12074
|
+
argv: [bin, ...args],
|
|
12075
|
+
stdinData: stdin ?? undefined,
|
|
12076
|
+
timeoutMs: DEFAULT_LOCAL_AGENT_TIMEOUT_MS
|
|
12077
|
+
});
|
|
12057
12078
|
} catch (e) {
|
|
12079
|
+
const failed = {
|
|
12080
|
+
stdout: "",
|
|
12081
|
+
stderr: String(e),
|
|
12082
|
+
exit_code: -1
|
|
12083
|
+
};
|
|
12084
|
+
await state.journal.recordDone(sessionId, toolCallId, failed);
|
|
12058
12085
|
return {
|
|
12059
12086
|
error: true,
|
|
12060
|
-
message: `local agent failed to start (${
|
|
12087
|
+
message: `local agent failed to start (${invocation.source}): ${String(e)}. ` + `Pick an installed agent with --local-agent <claude|codex|cursor-agent> ` + `or set one via 'martha config set local_agent <name>'.`
|
|
12088
|
+
};
|
|
12089
|
+
}
|
|
12090
|
+
await state.journal.recordDone(sessionId, toolCallId, result);
|
|
12091
|
+
if (result.backgrounded) {
|
|
12092
|
+
const mins = Math.round(DEFAULT_LOCAL_AGENT_TIMEOUT_MS / 60000);
|
|
12093
|
+
return {
|
|
12094
|
+
text: result.stdout || "(no output captured yet)",
|
|
12095
|
+
backgrounded: true,
|
|
12096
|
+
pid: result.pid,
|
|
12097
|
+
log_path: result.log_path,
|
|
12098
|
+
message: `The local agent is still running after ${mins} min; it was left running ` + `in the background (it survives this session). Read its live log and final ` + `result with host_read on ${result.log_path}.`
|
|
12061
12099
|
};
|
|
12062
12100
|
}
|
|
12101
|
+
return {
|
|
12102
|
+
text: result.stdout || "(local agent produced no output)",
|
|
12103
|
+
exit_code: result.exit_code,
|
|
12104
|
+
log_path: result.log_path
|
|
12105
|
+
};
|
|
12063
12106
|
}
|
|
12064
12107
|
async function _postToolResult(ctx, sessionId, toolCallId, result, label) {
|
|
12065
12108
|
try {
|
|
@@ -12107,7 +12150,7 @@ async function handleHostExecFrame(ctx, sessionId, data, state) {
|
|
|
12107
12150
|
if (state.handled.has(id))
|
|
12108
12151
|
continue;
|
|
12109
12152
|
state.handled.add(id);
|
|
12110
|
-
const result = await resolveLocalAgent(req, state);
|
|
12153
|
+
const result = await resolveLocalAgent(sessionId, id, req, state);
|
|
12111
12154
|
await _postToolResult(ctx, sessionId, id, result, "local_agent");
|
|
12112
12155
|
}
|
|
12113
12156
|
}
|
|
@@ -12164,6 +12207,7 @@ async function sendMessage(ctx, sessionId, message, opts = {}) {
|
|
|
12164
12207
|
const localExecState = opts.localExec ? {
|
|
12165
12208
|
cwd: opts.localExec.cwd,
|
|
12166
12209
|
autoYes: opts.localExec.autoYes,
|
|
12210
|
+
localAgent: opts.localExec.agent,
|
|
12167
12211
|
handled: new Set,
|
|
12168
12212
|
journal: new Journal(opts.localExec.cwd)
|
|
12169
12213
|
} : null;
|
|
@@ -12685,7 +12729,7 @@ async function runOneShot(ctx, sessionId, message, isJson, clientId, agentName,
|
|
|
12685
12729
|
}
|
|
12686
12730
|
}
|
|
12687
12731
|
function registerChatCommand(program2) {
|
|
12688
|
-
program2.command("chat").description("Chat with a Martha agent").option("--session <id>", "Resume an existing session").option("--client <nameOrId>", "Use a specific client (name or ID)").option("--agent <name>", "Drive the chat with a specific copilot agent (defaults to profile.default_agent)").option("--message <text>", "Send a single message (non-interactive)").option("--show-tools", "Show tool calls and results during chat").option("--local-exec", "Allow the agent to run host tools on THIS machine (#568). Requires the server to have LOCAL_EXEC_ENABLED=1.").option("--yes", "Auto-approve host tool execution (required with --local-exec for non-interactive runs).").option("--timeout <seconds>", "Per-message HTTP timeout in seconds (default: 600)").action(async (opts) => {
|
|
12732
|
+
program2.command("chat").description("Chat with a Martha agent").option("--session <id>", "Resume an existing session").option("--client <nameOrId>", "Use a specific client (name or ID)").option("--agent <name>", "Drive the chat with a specific copilot agent (defaults to profile.default_agent)").option("--message <text>", "Send a single message (non-interactive)").option("--show-tools", "Show tool calls and results during chat").option("--local-exec", "Allow the agent to run host tools on THIS machine (#568). Requires the server to have LOCAL_EXEC_ENABLED=1.").option("--yes", "Auto-approve host tool execution (required with --local-exec for non-interactive runs).").option("--local-agent <name>", "Which local coding agent `local_agent` delegates to: a preset (claude | codex | cursor-agent) or a raw command. Overrides MARTHA_LOCAL_AGENT_CMD and `config local_agent` for this run.").option("--timeout <seconds>", "Per-message HTTP timeout in seconds (default: 600)").action(async (opts) => {
|
|
12689
12733
|
const ctx = createContext({
|
|
12690
12734
|
profileOverride: program2.opts().profile,
|
|
12691
12735
|
verbose: program2.opts().verbose
|
|
@@ -12720,7 +12764,8 @@ function registerChatCommand(program2) {
|
|
|
12720
12764
|
}
|
|
12721
12765
|
const showTools = !!opts.showTools;
|
|
12722
12766
|
const timeoutMs = opts.timeout ? parseTimeoutSeconds(opts.timeout) * 1000 : undefined;
|
|
12723
|
-
const
|
|
12767
|
+
const localAgentChoice = opts.localAgent || process.env.MARTHA_LOCAL_AGENT_CMD || loadConfig().settings.local_agent || DEFAULT_LOCAL_AGENT;
|
|
12768
|
+
const localExec = opts.localExec ? { cwd: process.cwd(), autoYes: !!opts.yes, agent: localAgentChoice } : undefined;
|
|
12724
12769
|
if (opts.message) {
|
|
12725
12770
|
await runOneShot(ctx, sessionId, opts.message, isJson, clientId, agentName, showTools, timeoutMs, localExec);
|
|
12726
12771
|
} else {
|
|
@@ -12841,6 +12886,8 @@ Global Settings
|
|
|
12841
12886
|
console.log(` Editor: ${settings.editor}`);
|
|
12842
12887
|
if (settings.pager)
|
|
12843
12888
|
console.log(` Pager: ${settings.pager}`);
|
|
12889
|
+
if (settings.local_agent)
|
|
12890
|
+
console.log(` Local agent: ${settings.local_agent}`);
|
|
12844
12891
|
if (settings.chat) {
|
|
12845
12892
|
if (settings.chat.show_tool_calls)
|
|
12846
12893
|
console.log(` Tool calls: ${settings.chat.show_tool_calls}`);
|
|
@@ -12879,6 +12926,7 @@ var SETTINGS_KEYS = {
|
|
|
12879
12926
|
color: (s) => s.color,
|
|
12880
12927
|
editor: (s) => s.editor,
|
|
12881
12928
|
pager: (s) => s.pager,
|
|
12929
|
+
local_agent: (s) => s.local_agent,
|
|
12882
12930
|
"chat.show_tool_calls": (s) => s.chat?.show_tool_calls,
|
|
12883
12931
|
"chat.markdown": (s) => s.chat?.markdown,
|
|
12884
12932
|
"chat.history_file": (s) => s.chat?.history_file
|