@mutagent/cli 0.1.279 → 0.1.281
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/bin/cli.js +735 -130
- package/dist/bin/cli.js.map +12 -9
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -883,8 +883,8 @@ var init_sdk_client = __esm(() => {
|
|
|
883
883
|
});
|
|
884
884
|
|
|
885
885
|
// src/bin/cli.ts
|
|
886
|
-
import { Command as
|
|
887
|
-
import
|
|
886
|
+
import { Command as Command15 } from "commander";
|
|
887
|
+
import chalk28 from "chalk";
|
|
888
888
|
import { readFileSync as readFileSync12 } from "fs";
|
|
889
889
|
import { join as join14, dirname as dirname4 } from "path";
|
|
890
890
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -1139,7 +1139,7 @@ init_errors();
|
|
|
1139
1139
|
import { spawn } from "child_process";
|
|
1140
1140
|
import { createHash } from "crypto";
|
|
1141
1141
|
import { homedir as homedir2 } from "os";
|
|
1142
|
-
import { join as join5 } from "path";
|
|
1142
|
+
import { join as join5, posix as posixPath, win32 as win32Path, resolve as resolvePath } from "path";
|
|
1143
1143
|
import {
|
|
1144
1144
|
existsSync as existsSync4,
|
|
1145
1145
|
mkdirSync as mkdirSync2,
|
|
@@ -1266,9 +1266,18 @@ async function defaultDownload(url, destPath) {
|
|
|
1266
1266
|
async function defaultSha256(filePath) {
|
|
1267
1267
|
return Promise.resolve(createHash("sha256").update(readFileSync4(filePath)).digest("hex"));
|
|
1268
1268
|
}
|
|
1269
|
+
function buildTarExtractInvocation(tgzPath, destDir, platform = process.platform) {
|
|
1270
|
+
const p = platform === "win32" ? win32Path : posixPath;
|
|
1271
|
+
const cwd = p.dirname(tgzPath) || ".";
|
|
1272
|
+
const archive = p.basename(tgzPath);
|
|
1273
|
+
const dest = p.isAbsolute(destDir) ? destDir : resolvePath(destDir);
|
|
1274
|
+
return { args: ["-xzf", archive, "-C", dest], cwd };
|
|
1275
|
+
}
|
|
1269
1276
|
function defaultExtract(tgzPath, destDir) {
|
|
1277
|
+
const invocation = buildTarExtractInvocation(tgzPath, destDir);
|
|
1270
1278
|
return new Promise((resolve, reject) => {
|
|
1271
|
-
const child = spawn("tar",
|
|
1279
|
+
const child = spawn("tar", invocation.args, {
|
|
1280
|
+
cwd: invocation.cwd,
|
|
1272
1281
|
stdio: ["ignore", "ignore", "pipe"]
|
|
1273
1282
|
});
|
|
1274
1283
|
let stderr = "";
|
|
@@ -6112,11 +6121,11 @@ If mutagent-cli is not installed: mutagent install helix`), isJson);
|
|
|
6112
6121
|
}
|
|
6113
6122
|
|
|
6114
6123
|
// src/commands/helix/index.ts
|
|
6115
|
-
import { Command as
|
|
6116
|
-
import
|
|
6124
|
+
import { Command as Command14 } from "commander";
|
|
6125
|
+
import chalk27 from "chalk";
|
|
6117
6126
|
|
|
6118
6127
|
// src/commands/helix/spawn.ts
|
|
6119
|
-
import
|
|
6128
|
+
import chalk21 from "chalk";
|
|
6120
6129
|
init_errors();
|
|
6121
6130
|
|
|
6122
6131
|
// src/lib/sandbox-api.ts
|
|
@@ -6399,6 +6408,20 @@ async function execSandbox(id, body) {
|
|
|
6399
6408
|
sandboxId: id
|
|
6400
6409
|
});
|
|
6401
6410
|
}
|
|
6411
|
+
async function postInput(id, body) {
|
|
6412
|
+
return requestJson(`/api/sandbox/${encodeURIComponent(id)}/input`, {
|
|
6413
|
+
method: "POST",
|
|
6414
|
+
body: JSON.stringify(body),
|
|
6415
|
+
sandboxId: id
|
|
6416
|
+
});
|
|
6417
|
+
}
|
|
6418
|
+
async function startSession(id, body) {
|
|
6419
|
+
return requestJson(`/api/sandbox/${encodeURIComponent(id)}/session`, {
|
|
6420
|
+
method: "POST",
|
|
6421
|
+
body: JSON.stringify(body),
|
|
6422
|
+
sandboxId: id
|
|
6423
|
+
});
|
|
6424
|
+
}
|
|
6402
6425
|
async function oneShotRun(body) {
|
|
6403
6426
|
return requestJson("/api/sandbox/run", {
|
|
6404
6427
|
method: "POST",
|
|
@@ -6534,7 +6557,7 @@ function canAnimate(isJson) {
|
|
|
6534
6557
|
}
|
|
6535
6558
|
|
|
6536
6559
|
// src/commands/helix/session.ts
|
|
6537
|
-
import
|
|
6560
|
+
import chalk20 from "chalk";
|
|
6538
6561
|
|
|
6539
6562
|
// src/lib/sse.ts
|
|
6540
6563
|
var DEFAULT_EVENT_NAME = "message";
|
|
@@ -6633,6 +6656,13 @@ async function runStreamSession(options) {
|
|
|
6633
6656
|
});
|
|
6634
6657
|
const detached = () => outcome("detached");
|
|
6635
6658
|
const isAborted = () => signal.aborted;
|
|
6659
|
+
const noteSeq = (seq) => {
|
|
6660
|
+
if (seq > lastSeq + 1 && lastSeq > 0) {
|
|
6661
|
+
gaps += 1;
|
|
6662
|
+
sink.notice(gapNotice(lastSeq, seq));
|
|
6663
|
+
}
|
|
6664
|
+
lastSeq = Math.max(lastSeq, seq);
|
|
6665
|
+
};
|
|
6636
6666
|
for (;; ) {
|
|
6637
6667
|
if (isAborted())
|
|
6638
6668
|
return detached();
|
|
@@ -6657,38 +6687,44 @@ async function runStreamSession(options) {
|
|
|
6657
6687
|
for await (const event of readSseEvents(stream)) {
|
|
6658
6688
|
attempt = 0;
|
|
6659
6689
|
if (event.event === "stdout" || event.event === "stderr") {
|
|
6660
|
-
const
|
|
6661
|
-
if (!
|
|
6690
|
+
const payload2 = parseOutputPayload(event.data);
|
|
6691
|
+
if (!payload2)
|
|
6662
6692
|
continue;
|
|
6663
|
-
|
|
6664
|
-
gaps += 1;
|
|
6665
|
-
sink.notice(gapNotice(lastSeq, payload.seq));
|
|
6666
|
-
}
|
|
6667
|
-
lastSeq = Math.max(lastSeq, payload.seq);
|
|
6693
|
+
noteSeq(payload2.seq);
|
|
6668
6694
|
if (event.event === "stdout")
|
|
6669
|
-
sink.stdout(
|
|
6695
|
+
sink.stdout(payload2.text, payload2.seq);
|
|
6670
6696
|
else
|
|
6671
|
-
sink.stderr(
|
|
6697
|
+
sink.stderr(payload2.text, payload2.seq);
|
|
6672
6698
|
continue;
|
|
6673
6699
|
}
|
|
6674
6700
|
if (event.event === "sandbox") {
|
|
6675
|
-
const
|
|
6676
|
-
if (
|
|
6677
|
-
sandbox =
|
|
6678
|
-
options.onSandbox?.(
|
|
6701
|
+
const payload2 = parseSandboxRunPayload(event.data);
|
|
6702
|
+
if (payload2) {
|
|
6703
|
+
sandbox = payload2;
|
|
6704
|
+
options.onSandbox?.(payload2);
|
|
6679
6705
|
}
|
|
6680
6706
|
continue;
|
|
6681
6707
|
}
|
|
6682
6708
|
if (event.event === "result") {
|
|
6683
|
-
const
|
|
6684
|
-
if (
|
|
6685
|
-
result =
|
|
6709
|
+
const payload2 = parseSandboxResultPayload(event.data);
|
|
6710
|
+
if (payload2)
|
|
6711
|
+
result = payload2;
|
|
6712
|
+
continue;
|
|
6713
|
+
}
|
|
6714
|
+
if (event.event === "rpc") {
|
|
6715
|
+
const payload2 = parseOutputPayload(event.data);
|
|
6716
|
+
if (!payload2) {
|
|
6717
|
+
sink.unrecognised(event.data, 0);
|
|
6718
|
+
continue;
|
|
6719
|
+
}
|
|
6720
|
+
noteSeq(payload2.seq);
|
|
6721
|
+
sink.rpc(payload2.text, payload2.seq);
|
|
6686
6722
|
continue;
|
|
6687
6723
|
}
|
|
6688
6724
|
if (event.event === "status") {
|
|
6689
|
-
const
|
|
6690
|
-
if (
|
|
6691
|
-
exitCode =
|
|
6725
|
+
const payload2 = parseStatusPayload(event.data);
|
|
6726
|
+
if (payload2?.state === "exited") {
|
|
6727
|
+
exitCode = payload2.exitCode;
|
|
6692
6728
|
exited = true;
|
|
6693
6729
|
break;
|
|
6694
6730
|
}
|
|
@@ -6704,6 +6740,15 @@ async function runStreamSession(options) {
|
|
|
6704
6740
|
}
|
|
6705
6741
|
continue;
|
|
6706
6742
|
}
|
|
6743
|
+
if (event.event === "ping")
|
|
6744
|
+
continue;
|
|
6745
|
+
const payload = parseOutputPayload(event.data);
|
|
6746
|
+
if (payload) {
|
|
6747
|
+
noteSeq(payload.seq);
|
|
6748
|
+
sink.unrecognised(payload.text, payload.seq);
|
|
6749
|
+
} else {
|
|
6750
|
+
sink.unrecognised(event.data, 0);
|
|
6751
|
+
}
|
|
6707
6752
|
}
|
|
6708
6753
|
} catch (error) {
|
|
6709
6754
|
if (isAborted())
|
|
@@ -6766,8 +6811,226 @@ function delay(ms, signal) {
|
|
|
6766
6811
|
|
|
6767
6812
|
// src/commands/helix/session.ts
|
|
6768
6813
|
init_errors();
|
|
6814
|
+
|
|
6815
|
+
// src/commands/helix/frames.ts
|
|
6816
|
+
import chalk19 from "chalk";
|
|
6817
|
+
var SILENT = new Set([
|
|
6818
|
+
"message_start",
|
|
6819
|
+
"message_update",
|
|
6820
|
+
"message_delta",
|
|
6821
|
+
"turn_end",
|
|
6822
|
+
"agent_start",
|
|
6823
|
+
"agent_end"
|
|
6824
|
+
]);
|
|
6825
|
+
var IDENTIFYING_ARG = {
|
|
6826
|
+
read: ["path", "file_path", "filePath"],
|
|
6827
|
+
write: ["path", "file_path", "filePath"],
|
|
6828
|
+
edit: ["path", "file_path", "filePath"],
|
|
6829
|
+
ls: ["path", "dir"],
|
|
6830
|
+
glob: ["pattern", "path"],
|
|
6831
|
+
grep: ["pattern", "query"],
|
|
6832
|
+
search: ["pattern", "query"],
|
|
6833
|
+
bash: ["command", "cmd", "script"],
|
|
6834
|
+
shell: ["command", "cmd"],
|
|
6835
|
+
exec: ["command", "cmd"],
|
|
6836
|
+
fetch: ["url"],
|
|
6837
|
+
webfetch: ["url"],
|
|
6838
|
+
task: ["description", "prompt"],
|
|
6839
|
+
agent: ["description", "prompt"]
|
|
6840
|
+
};
|
|
6841
|
+
var GENERIC_ARG = ["path", "file_path", "command", "pattern", "url", "query", "name", "id"];
|
|
6842
|
+
var TOOL_COL = 9;
|
|
6843
|
+
var DURATION_COL = 44;
|
|
6844
|
+
var MAX_ARG = 56;
|
|
6845
|
+
var MAX_TEXT = 400;
|
|
6846
|
+
|
|
6847
|
+
class FrameFeed {
|
|
6848
|
+
#plain;
|
|
6849
|
+
#pending = new Map;
|
|
6850
|
+
#turn = 0;
|
|
6851
|
+
#now;
|
|
6852
|
+
constructor(options = {}) {
|
|
6853
|
+
this.#plain = options.plain ?? isPlainOutput();
|
|
6854
|
+
this.#now = options.now ?? (() => Date.now());
|
|
6855
|
+
}
|
|
6856
|
+
render(frameText, seq) {
|
|
6857
|
+
const frame = asRecord(frameText);
|
|
6858
|
+
if (!frame)
|
|
6859
|
+
return this.renderUnrecognised(frameText, seq);
|
|
6860
|
+
const type = typeof frame.type === "string" ? frame.type : "";
|
|
6861
|
+
switch (type) {
|
|
6862
|
+
case "tool_execution_start":
|
|
6863
|
+
this.#startCall(frame);
|
|
6864
|
+
return null;
|
|
6865
|
+
case "tool_execution_end":
|
|
6866
|
+
return this.#endCall(frame);
|
|
6867
|
+
case "turn_start":
|
|
6868
|
+
this.#turn += 1;
|
|
6869
|
+
return this.#rule(`turn ${String(this.#turn)}`);
|
|
6870
|
+
case "message_end":
|
|
6871
|
+
return this.#assistantText(frame);
|
|
6872
|
+
case "agent_settled":
|
|
6873
|
+
return this.#line(this.#sym("●", "*"), this.#dim("agent settled — waiting for input"));
|
|
6874
|
+
case "extension_ui_request":
|
|
6875
|
+
return this.#hitl(frame);
|
|
6876
|
+
case "response":
|
|
6877
|
+
return this.#response(frame);
|
|
6878
|
+
default:
|
|
6879
|
+
if (type === "" || SILENT.has(type))
|
|
6880
|
+
return null;
|
|
6881
|
+
return this.#line(this.#sym("·", "."), this.#dim(type));
|
|
6882
|
+
}
|
|
6883
|
+
}
|
|
6884
|
+
renderUnrecognised(raw, seq) {
|
|
6885
|
+
const where = seq > 0 ? ` (seq ${String(seq)})` : "";
|
|
6886
|
+
const body = `unrecognised${where}: ${oneLine(raw, MAX_ARG * 2)}`;
|
|
6887
|
+
return this.#line(this.#sym("·", "."), this.#dim(body));
|
|
6888
|
+
}
|
|
6889
|
+
#startCall(frame) {
|
|
6890
|
+
const id = typeof frame.toolCallId === "string" ? frame.toolCallId : "";
|
|
6891
|
+
if (id === "")
|
|
6892
|
+
return;
|
|
6893
|
+
this.#pending.set(id, {
|
|
6894
|
+
toolName: typeof frame.toolName === "string" ? frame.toolName : "tool",
|
|
6895
|
+
arg: identifyingArg(frame.toolName, frame.args),
|
|
6896
|
+
startedAt: this.#now()
|
|
6897
|
+
});
|
|
6898
|
+
}
|
|
6899
|
+
#endCall(frame) {
|
|
6900
|
+
const id = typeof frame.toolCallId === "string" ? frame.toolCallId : "";
|
|
6901
|
+
const started = id === "" ? undefined : this.#pending.get(id);
|
|
6902
|
+
if (id !== "")
|
|
6903
|
+
this.#pending.delete(id);
|
|
6904
|
+
const toolName = started?.toolName ?? (typeof frame.toolName === "string" ? frame.toolName : "tool");
|
|
6905
|
+
const arg = started?.arg ?? identifyingArg(frame.toolName, frame.args);
|
|
6906
|
+
const failed = isFailure(frame.result);
|
|
6907
|
+
const elapsed = started === undefined ? "" : formatDuration(this.#now() - started.startedAt);
|
|
6908
|
+
const name = this.#plain ? toolName : failed ? chalk19.red(toolName) : chalk19.cyan(toolName);
|
|
6909
|
+
const visible = arg === "" ? toolName.padEnd(TOOL_COL) : `${toolName.padEnd(TOOL_COL)} ${arg}`;
|
|
6910
|
+
const head = arg === "" ? pad(name, toolName, TOOL_COL) : `${pad(name, toolName, TOOL_COL)} ${arg}`;
|
|
6911
|
+
const gap = " ".repeat(Math.max(2, DURATION_COL - visible.length));
|
|
6912
|
+
const tail = elapsed === "" ? "" : gap + this.#dim(elapsed);
|
|
6913
|
+
return this.#line(failed ? this.#sym("✗", "x") : this.#sym("▸", ">"), head + tail);
|
|
6914
|
+
}
|
|
6915
|
+
#assistantText(frame) {
|
|
6916
|
+
const text = extractText(frame);
|
|
6917
|
+
if (text === "")
|
|
6918
|
+
return null;
|
|
6919
|
+
return this.#line(this.#sym("❯", "|"), oneLine(text, MAX_TEXT));
|
|
6920
|
+
}
|
|
6921
|
+
#hitl(frame) {
|
|
6922
|
+
const method = typeof frame.method === "string" ? frame.method : "request";
|
|
6923
|
+
const key = typeof frame.statusKey === "string" ? ` ${frame.statusKey}` : "";
|
|
6924
|
+
const body = `hitl ${method}${key}`;
|
|
6925
|
+
return this.#line(this.#sym("◆", "?"), this.#plain ? body : chalk19.yellow(body));
|
|
6926
|
+
}
|
|
6927
|
+
#response(frame) {
|
|
6928
|
+
const command = typeof frame.command === "string" ? frame.command : "command";
|
|
6929
|
+
const id = typeof frame.id === "string" ? ` ${frame.id}` : "";
|
|
6930
|
+
const ok = frame.success !== false;
|
|
6931
|
+
const body = `${command}${id} ${ok ? "accepted" : "rejected"}`;
|
|
6932
|
+
if (this.#plain)
|
|
6933
|
+
return this.#line(ok ? "+" : "x", body);
|
|
6934
|
+
return this.#line(ok ? "✓" : "✗", ok ? chalk19.green(body) : chalk19.red(body));
|
|
6935
|
+
}
|
|
6936
|
+
#line(symbol, body) {
|
|
6937
|
+
return ` ${symbol} ${body}`;
|
|
6938
|
+
}
|
|
6939
|
+
#sym(styled, plain) {
|
|
6940
|
+
return this.#plain ? plain : styled;
|
|
6941
|
+
}
|
|
6942
|
+
#dim(text) {
|
|
6943
|
+
return this.#plain ? text : chalk19.dim(text);
|
|
6944
|
+
}
|
|
6945
|
+
#rule(label) {
|
|
6946
|
+
const dash = this.#plain ? "-" : "─";
|
|
6947
|
+
const body = `${dash.repeat(2)} ${label} ${dash.repeat(Math.max(2, 40 - label.length))}`;
|
|
6948
|
+
return ` ${this.#dim(body)}`;
|
|
6949
|
+
}
|
|
6950
|
+
}
|
|
6951
|
+
function asRecord(text) {
|
|
6952
|
+
try {
|
|
6953
|
+
const parsed = JSON.parse(text);
|
|
6954
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
6955
|
+
return parsed;
|
|
6956
|
+
}
|
|
6957
|
+
} catch {}
|
|
6958
|
+
return null;
|
|
6959
|
+
}
|
|
6960
|
+
function pad(styled, visible, width) {
|
|
6961
|
+
const missing = Math.max(0, width - visible.length);
|
|
6962
|
+
return styled + " ".repeat(missing);
|
|
6963
|
+
}
|
|
6964
|
+
function identifyingArg(toolName, args) {
|
|
6965
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
6966
|
+
return "";
|
|
6967
|
+
const record = args;
|
|
6968
|
+
const name = typeof toolName === "string" ? toolName.toLowerCase() : "";
|
|
6969
|
+
for (const key of IDENTIFYING_ARG[name] ?? GENERIC_ARG) {
|
|
6970
|
+
const value = record[key];
|
|
6971
|
+
if (typeof value === "string" && value.trim() !== "")
|
|
6972
|
+
return oneLine(value, MAX_ARG);
|
|
6973
|
+
if (typeof value === "number")
|
|
6974
|
+
return String(value);
|
|
6975
|
+
}
|
|
6976
|
+
for (const value of Object.values(record)) {
|
|
6977
|
+
if (typeof value === "string" && value.trim() !== "")
|
|
6978
|
+
return oneLine(value, MAX_ARG);
|
|
6979
|
+
}
|
|
6980
|
+
return "";
|
|
6981
|
+
}
|
|
6982
|
+
function isFailure(result) {
|
|
6983
|
+
if (!result || typeof result !== "object" || Array.isArray(result))
|
|
6984
|
+
return false;
|
|
6985
|
+
const record = result;
|
|
6986
|
+
if (record.isError === true || record.error != null)
|
|
6987
|
+
return true;
|
|
6988
|
+
return record.success === false;
|
|
6989
|
+
}
|
|
6990
|
+
function extractText(frame) {
|
|
6991
|
+
const candidates = [
|
|
6992
|
+
frame.text,
|
|
6993
|
+
frame.content,
|
|
6994
|
+
frame.message?.content,
|
|
6995
|
+
frame.message?.text,
|
|
6996
|
+
frame.message
|
|
6997
|
+
];
|
|
6998
|
+
for (const candidate of candidates) {
|
|
6999
|
+
if (typeof candidate === "string" && candidate.trim() !== "")
|
|
7000
|
+
return candidate.trim();
|
|
7001
|
+
if (Array.isArray(candidate)) {
|
|
7002
|
+
const joined = candidate.map((part) => {
|
|
7003
|
+
if (typeof part === "string")
|
|
7004
|
+
return part;
|
|
7005
|
+
if (part && typeof part === "object" && typeof part.text === "string") {
|
|
7006
|
+
return part.text;
|
|
7007
|
+
}
|
|
7008
|
+
return "";
|
|
7009
|
+
}).join("").trim();
|
|
7010
|
+
if (joined !== "")
|
|
7011
|
+
return joined;
|
|
7012
|
+
}
|
|
7013
|
+
}
|
|
7014
|
+
return "";
|
|
7015
|
+
}
|
|
7016
|
+
function oneLine(text, max) {
|
|
7017
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
7018
|
+
return flat.length <= max ? flat : `${flat.slice(0, max - 1)}…`;
|
|
7019
|
+
}
|
|
7020
|
+
function formatDuration(ms) {
|
|
7021
|
+
const safe = Math.max(0, Math.round(ms));
|
|
7022
|
+
if (safe < 1000)
|
|
7023
|
+
return `${String(safe)}ms`;
|
|
7024
|
+
if (safe < 60000)
|
|
7025
|
+
return `${(safe / 1000).toFixed(1)}s`;
|
|
7026
|
+
const minutes = Math.floor(safe / 60000);
|
|
7027
|
+
const seconds = Math.round(safe % 60000 / 1000);
|
|
7028
|
+
return `${String(minutes)}m ${String(seconds).padStart(2, "0")}s`;
|
|
7029
|
+
}
|
|
7030
|
+
|
|
7031
|
+
// src/commands/helix/session.ts
|
|
6769
7032
|
function dim(text) {
|
|
6770
|
-
return isPlainOutput() ? text :
|
|
7033
|
+
return isPlainOutput() ? text : chalk20.dim(text);
|
|
6771
7034
|
}
|
|
6772
7035
|
function reattachHint(id, lastSeq) {
|
|
6773
7036
|
const since = lastSeq > 0 ? ` --since ${String(lastSeq)}` : "";
|
|
@@ -6782,12 +7045,23 @@ function createSessionSink(isJson) {
|
|
|
6782
7045
|
stderr: (text, seq) => {
|
|
6783
7046
|
writeEvent("stderr", { seq, text });
|
|
6784
7047
|
},
|
|
7048
|
+
rpc: (frame, seq) => {
|
|
7049
|
+
const parsed = parseFrame(frame);
|
|
7050
|
+
if (parsed === null)
|
|
7051
|
+
writeEvent("unrecognised", { seq, raw: frame });
|
|
7052
|
+
else
|
|
7053
|
+
writeEvent("rpc", { seq, frame: parsed });
|
|
7054
|
+
},
|
|
7055
|
+
unrecognised: (raw, seq) => {
|
|
7056
|
+
writeEvent("unrecognised", { seq, raw });
|
|
7057
|
+
},
|
|
6785
7058
|
notice: (message) => {
|
|
6786
7059
|
process.stderr.write(`${JSON.stringify({ type: "notice", message })}
|
|
6787
7060
|
`);
|
|
6788
7061
|
}
|
|
6789
7062
|
};
|
|
6790
7063
|
}
|
|
7064
|
+
const feed = new FrameFeed;
|
|
6791
7065
|
return {
|
|
6792
7066
|
stdout: (text) => {
|
|
6793
7067
|
process.stdout.write(text);
|
|
@@ -6795,12 +7069,33 @@ function createSessionSink(isJson) {
|
|
|
6795
7069
|
stderr: (text) => {
|
|
6796
7070
|
process.stderr.write(text);
|
|
6797
7071
|
},
|
|
7072
|
+
rpc: (frame, seq) => {
|
|
7073
|
+
const line = feed.render(frame, seq);
|
|
7074
|
+
if (line !== null)
|
|
7075
|
+
process.stderr.write(`${line}
|
|
7076
|
+
`);
|
|
7077
|
+
},
|
|
7078
|
+
unrecognised: (raw, seq) => {
|
|
7079
|
+
const line = feed.renderUnrecognised(raw, seq);
|
|
7080
|
+
if (line !== null)
|
|
7081
|
+
process.stderr.write(`${line}
|
|
7082
|
+
`);
|
|
7083
|
+
},
|
|
6798
7084
|
notice: (message) => {
|
|
6799
7085
|
process.stderr.write(`${dim(`[mutagent] ${message}`)}
|
|
6800
7086
|
`);
|
|
6801
7087
|
}
|
|
6802
7088
|
};
|
|
6803
7089
|
}
|
|
7090
|
+
function parseFrame(text) {
|
|
7091
|
+
try {
|
|
7092
|
+
const parsed = JSON.parse(text);
|
|
7093
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
7094
|
+
return parsed;
|
|
7095
|
+
}
|
|
7096
|
+
} catch {}
|
|
7097
|
+
return null;
|
|
7098
|
+
}
|
|
6804
7099
|
function writeEvent(type, fields) {
|
|
6805
7100
|
process.stdout.write(`${JSON.stringify({ type, ...fields })}
|
|
6806
7101
|
`);
|
|
@@ -6874,13 +7169,17 @@ function parseSince(raw) {
|
|
|
6874
7169
|
|
|
6875
7170
|
// src/commands/helix/spawn.ts
|
|
6876
7171
|
var ARCHES = ["x86_64", "arm64"];
|
|
7172
|
+
var EGRESS_POLICIES = ["deny", "allowlist", "allow"];
|
|
7173
|
+
var EGRESS_KINDS = ["port", "cidr", "domain"];
|
|
6877
7174
|
function registerSpawnCommand(parent) {
|
|
6878
|
-
parent.command("spawn").description("Spawn a Helix session in a cloud sandbox and attach to it").requiredOption("--image <ref>", "Image reference (repository:tag or @digest)").option("--arch <arch>", `Image architecture: ${ARCHES.join(" | ")} (default: this host's)`).option("--name <name>", "Definition name (default: derived from the image reference)").option("--provider <name>", "Provider registry key (default: the backend default)").option("--env <name>", "Environment (secrets + variables) to load at spawn").option("--detach", "Spawn without attaching — print the ID and return").addHelpText("after", `
|
|
7175
|
+
parent.command("spawn").description("Spawn a Helix session in a cloud sandbox and attach to it").requiredOption("--image <ref>", "Image reference (repository:tag or @digest)").option("--arch <arch>", `Image architecture: ${ARCHES.join(" | ")} (default: this host's)`).option("--name <name>", "Definition name (default: derived from the image reference)").option("--provider <name>", "Provider registry key (default: the backend default)").option("--env <name>", "Environment (secrets + variables) to load at spawn").option("--egress <policy>", `Outbound network policy: ${EGRESS_POLICIES.join(" | ")} (default: deny)`).option("--egress-allow <rule>", "Allowance for --egress allowlist, as kind:value (repeatable)", collectRule, []).option("--detach", "Spawn without attaching — print the ID and return").addHelpText("after", `
|
|
6879
7176
|
Examples:
|
|
6880
|
-
${
|
|
6881
|
-
${
|
|
6882
|
-
${
|
|
6883
|
-
${
|
|
7177
|
+
${chalk21.dim("$")} mutagent helix spawn --image alpine:3.20
|
|
7178
|
+
${chalk21.dim("$")} mutagent helix spawn --image ghcr.io/example/helix:latest --arch x86_64
|
|
7179
|
+
${chalk21.dim("$")} mutagent helix spawn --image alpine:3.20 --detach --json
|
|
7180
|
+
${chalk21.dim("$")} mutagent helix spawn --image alpine:3.20 --provider docker --env staging
|
|
7181
|
+
${chalk21.dim("$")} mutagent helix spawn --image <helix-image> --egress allow ${chalk21.dim("Required for a Helix session")}
|
|
7182
|
+
${chalk21.dim("$")} mutagent helix spawn --image <ref> --egress allowlist --egress-allow domain:api.anthropic.com
|
|
6884
7183
|
|
|
6885
7184
|
--image is required. A sandbox is spawned from a DEFINITION, and the definition
|
|
6886
7185
|
names its own image — there is no default the CLI could invent for you. To run
|
|
@@ -6896,11 +7195,37 @@ the image was built elsewhere.
|
|
|
6896
7195
|
DIFFERENT axis from --provider, which selects the backend. Secrets never travel
|
|
6897
7196
|
on the command line.
|
|
6898
7197
|
|
|
7198
|
+
NETWORK EGRESS IS DENIED BY DEFAULT, AND THAT BREAKS HELIX SESSIONS:
|
|
7199
|
+
A sandbox runs code we did not write, so the safe default is the default:
|
|
7200
|
+
nothing reaches out. That is right for a plain 'exec' box, which needs no
|
|
7201
|
+
network at all.
|
|
7202
|
+
|
|
7203
|
+
It is WRONG for a Helix session, for two independent reasons — either alone
|
|
7204
|
+
is enough to sink it:
|
|
7205
|
+
1. Under deny the container runs with --network none. Docker accepts -P and
|
|
7206
|
+
publishes nothing, so the in-box server on :8080 is unreachable and
|
|
7207
|
+
'mutagent helix session start' fails with "Helix is not reachable".
|
|
7208
|
+
2. An agent turn has to reach a model provider regardless.
|
|
7209
|
+
|
|
7210
|
+
So: ${chalk21.bold("--egress allow")} before 'helix session start'. Without it the failure
|
|
7211
|
+
presents as an unreachable box, which reads like a broken image rather than a
|
|
7212
|
+
policy you chose.
|
|
7213
|
+
|
|
7214
|
+
${chalk21.bold("--egress allowlist")} narrows that to named allowances, each given explicitly
|
|
7215
|
+
as kind:value — 'domain:api.anthropic.com', 'port:443', 'cidr:10.0.0.0/8'. The
|
|
7216
|
+
kind is never inferred from the value. An allowlist with no rules denies
|
|
7217
|
+
everything, so it is refused rather than silently walling the box off; it also
|
|
7218
|
+
needs a provider with egress control, which not every backend has.
|
|
7219
|
+
|
|
6899
7220
|
Without --detach this spawns and immediately attaches, so the common path is a
|
|
6900
7221
|
single command. Ctrl-C then DETACHES — the sandbox keeps running and the
|
|
6901
7222
|
re-attach command is printed. Use 'mutagent helix rm <id> --force' to destroy it.
|
|
6902
7223
|
|
|
6903
7224
|
AI Agent Directive:
|
|
7225
|
+
Pass --egress allow whenever the box is for a Helix session — 'mutagent helix
|
|
7226
|
+
session start' CANNOT reach the in-box server under the default deny policy,
|
|
7227
|
+
and the error it raises names the box, not the policy. A box you only 'exec'
|
|
7228
|
+
in needs no egress.
|
|
6904
7229
|
Prefer --detach --json for automation: it returns one JSON object with the
|
|
6905
7230
|
sandbox id, so you can decide what to run before committing to a live stream.
|
|
6906
7231
|
Without --detach, --json output is NDJSON and the final line is the summary.
|
|
@@ -6959,8 +7284,49 @@ function buildDefinition(options) {
|
|
|
6959
7284
|
definition.provider = options.provider;
|
|
6960
7285
|
if (options.env)
|
|
6961
7286
|
definition.environment = options.env;
|
|
7287
|
+
const network = resolveNetwork(options.egress, options.egressAllow ?? []);
|
|
7288
|
+
if (network)
|
|
7289
|
+
definition.network = network;
|
|
6962
7290
|
return definition;
|
|
6963
7291
|
}
|
|
7292
|
+
function resolveNetwork(requested, rules) {
|
|
7293
|
+
if (requested === undefined) {
|
|
7294
|
+
if (rules.length > 0) {
|
|
7295
|
+
throw new MutagentError("INVALID_ARGUMENTS", "--egress-allow only means something with --egress allowlist.", "Add --egress allowlist, or drop the allowances and use --egress allow.");
|
|
7296
|
+
}
|
|
7297
|
+
return;
|
|
7298
|
+
}
|
|
7299
|
+
const policy = requested.trim();
|
|
7300
|
+
if (!EGRESS_POLICIES.includes(policy)) {
|
|
7301
|
+
throw new MutagentError("INVALID_ARGUMENTS", `--egress must be one of ${EGRESS_POLICIES.join(", ")}, got "${requested}".`, "Use --egress allow to drive a Helix session; the default, deny, leaves the box with no network at all.");
|
|
7302
|
+
}
|
|
7303
|
+
if (policy !== "allowlist") {
|
|
7304
|
+
if (rules.length > 0) {
|
|
7305
|
+
throw new MutagentError("INVALID_ARGUMENTS", `--egress-allow is only read under "allowlist", so under "${policy}" the allowances would be silently discarded.`, `Use --egress allowlist to apply them, or drop them to keep --egress ${policy}.`);
|
|
7306
|
+
}
|
|
7307
|
+
return { egress: policy };
|
|
7308
|
+
}
|
|
7309
|
+
if (rules.length === 0) {
|
|
7310
|
+
throw new MutagentError("INVALID_ARGUMENTS", "An allowlist with no allowances denies everything — the same wall as --egress deny, under a name that says otherwise.", `Name what may be reached: --egress-allow domain:api.anthropic.com
|
|
7311
|
+
Or open it fully: --egress allow`);
|
|
7312
|
+
}
|
|
7313
|
+
return { egress: "allowlist", allow: rules.map(parseRule) };
|
|
7314
|
+
}
|
|
7315
|
+
function collectRule(value, previous) {
|
|
7316
|
+
return [...previous, value];
|
|
7317
|
+
}
|
|
7318
|
+
function parseRule(raw) {
|
|
7319
|
+
const separator = raw.indexOf(":");
|
|
7320
|
+
const kind = separator === -1 ? "" : raw.slice(0, separator).trim();
|
|
7321
|
+
const value = separator === -1 ? "" : raw.slice(separator + 1).trim();
|
|
7322
|
+
if (!EGRESS_KINDS.includes(kind) || value === "") {
|
|
7323
|
+
throw new MutagentError("INVALID_ARGUMENTS", `--egress-allow must be <kind>:<value>, got "${raw}".`, `kind is one of ${EGRESS_KINDS.join(", ")}. Examples:
|
|
7324
|
+
--egress-allow domain:api.anthropic.com
|
|
7325
|
+
--egress-allow port:443
|
|
7326
|
+
--egress-allow cidr:10.0.0.0/8`);
|
|
7327
|
+
}
|
|
7328
|
+
return { kind, value };
|
|
7329
|
+
}
|
|
6964
7330
|
function resolveArch(requested) {
|
|
6965
7331
|
if (requested !== undefined) {
|
|
6966
7332
|
const value = requested.trim();
|
|
@@ -6986,10 +7352,10 @@ async function createSandbox(definition, isJson) {
|
|
|
6986
7352
|
const spinner = ora2(`Spawning ${definition.image} (${definition.arch})...`).start();
|
|
6987
7353
|
try {
|
|
6988
7354
|
const sandbox = await spawnSandbox(definition);
|
|
6989
|
-
spinner.succeed(
|
|
7355
|
+
spinner.succeed(chalk21.green(`Sandbox ${sandbox.id} spawned`));
|
|
6990
7356
|
return sandbox;
|
|
6991
7357
|
} catch (error) {
|
|
6992
|
-
spinner.fail(
|
|
7358
|
+
spinner.fail(chalk21.red("Spawn failed"));
|
|
6993
7359
|
throw error;
|
|
6994
7360
|
}
|
|
6995
7361
|
}
|
|
@@ -7024,15 +7390,15 @@ function reportDetached(sandbox, id, output, isJson) {
|
|
|
7024
7390
|
}
|
|
7025
7391
|
|
|
7026
7392
|
// src/commands/helix/attach.ts
|
|
7027
|
-
import
|
|
7393
|
+
import chalk22 from "chalk";
|
|
7028
7394
|
init_errors();
|
|
7029
7395
|
function registerAttachCommand(parent) {
|
|
7030
7396
|
parent.command("attach").description("Attach to a running sandbox and stream its output").argument("<id>", "Sandbox ID").option("--since <seq>", "Resume after this sequence number instead of from the start").addHelpText("after", `
|
|
7031
7397
|
Examples:
|
|
7032
|
-
${
|
|
7033
|
-
${
|
|
7034
|
-
${
|
|
7035
|
-
${
|
|
7398
|
+
${chalk22.dim("$")} mutagent helix attach sbx_123
|
|
7399
|
+
${chalk22.dim("$")} mutagent helix attach sbx_123 --since 412
|
|
7400
|
+
${chalk22.dim("$")} mutagent helix attach sbx_123 --json | tail -1
|
|
7401
|
+
${chalk22.dim("$")} mutagent helix attach sbx_123 > session.log
|
|
7036
7402
|
|
|
7037
7403
|
Ctrl-C detaches. The sandbox keeps running and the exact re-attach command,
|
|
7038
7404
|
with the resume point filled in, is printed to stderr. Only 'mutagent helix rm <id> --force'
|
|
@@ -7081,16 +7447,244 @@ AI Agent Directive:
|
|
|
7081
7447
|
});
|
|
7082
7448
|
}
|
|
7083
7449
|
|
|
7450
|
+
// src/commands/helix/send.ts
|
|
7451
|
+
import chalk23 from "chalk";
|
|
7452
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
7453
|
+
init_errors();
|
|
7454
|
+
var COMMAND_TYPES = ["prompt", "steer", "follow_up", "abort"];
|
|
7455
|
+
var NEEDS_MESSAGE = new Set(["prompt", "steer", "follow_up"]);
|
|
7456
|
+
function registerSendCommand(parent) {
|
|
7457
|
+
parent.command("send").description("Send a command line into a running session").argument("<id>", "Sandbox ID").argument("[message...]", "What to say to the agent").requiredOption("--session <id>", "Session ID inside the sandbox").option("--type <type>", `Command verb: ${COMMAND_TYPES.join(" | ")} (default: prompt)`).option("--line <json>", "Send this exact JSON line instead of building one").option("--message-id <id>", "Correlation id to use instead of a generated one").addHelpText("after", `
|
|
7458
|
+
Examples:
|
|
7459
|
+
${chalk23.dim("$")} mutagent helix send sbx_123 --session s1 "also check the refresh path"
|
|
7460
|
+
${chalk23.dim("$")} mutagent helix send sbx_123 --session s1 --type steer "stop and re-read the spec"
|
|
7461
|
+
${chalk23.dim("$")} mutagent helix send sbx_123 --session s1 --type abort
|
|
7462
|
+
${chalk23.dim("$")} mutagent helix send sbx_123 --session s1 --line '{"type":"prompt","id":"p7","message":"go"}'
|
|
7463
|
+
${chalk23.dim("$")} mutagent helix send sbx_123 --session s1 "run the tests" --json
|
|
7464
|
+
|
|
7465
|
+
TWO FORMS:
|
|
7466
|
+
${chalk23.bold("The friendly form")} — a --type and a message. This command builds the JSON:
|
|
7467
|
+
{"type":"prompt","id":"<generated>","message":"<your message>"}. Use it for
|
|
7468
|
+
everything the four verbs cover.
|
|
7469
|
+
${chalk23.bold("--line")} — one raw JSON object, passed through byte for byte. It exists for
|
|
7470
|
+
a command shape this CLI does not know about yet. It is validated as a single
|
|
7471
|
+
JSON object before anything is sent, but its CONTENTS are the harness's
|
|
7472
|
+
business, not this CLI's. Do not combine it with --type or a message.
|
|
7473
|
+
|
|
7474
|
+
THE VERBS:
|
|
7475
|
+
prompt A new instruction to a settled agent.
|
|
7476
|
+
steer Redirect the turn already running.
|
|
7477
|
+
follow_up Continue from what the agent just said.
|
|
7478
|
+
abort Stop the current turn. Takes no message.
|
|
7479
|
+
|
|
7480
|
+
Sending is not watching. A 200 means the line was written to the session; the
|
|
7481
|
+
agent's reply arrives on the event stream as a 'response' frame carrying the id
|
|
7482
|
+
printed here. Attach in another terminal to see it:
|
|
7483
|
+
${chalk23.dim("$")} mutagent helix attach sbx_123
|
|
7484
|
+
|
|
7485
|
+
A session that is no longer running is refused rather than silently accepted —
|
|
7486
|
+
'mutagent helix ls' shows what is still up.
|
|
7487
|
+
|
|
7488
|
+
AI Agent Directive:
|
|
7489
|
+
--json returns ONE object: { success, sandboxId, sessionId, type, id, line,
|
|
7490
|
+
accepted, _links }. 'accepted' means DELIVERED, not answered — the answer is a
|
|
7491
|
+
'response' frame with the same id on 'mutagent helix attach <id> --json'.
|
|
7492
|
+
Attach BEFORE sending if you need the reply; this call does not wait for one.
|
|
7493
|
+
Use the friendly form (--type + message). Reserve --line for a command shape
|
|
7494
|
+
the four verbs cannot express.
|
|
7495
|
+
`).action(async (id, messageParts, options) => {
|
|
7496
|
+
const isJson = getJsonFlag(parent);
|
|
7497
|
+
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
7498
|
+
try {
|
|
7499
|
+
const sessionId = options.session?.trim() ?? "";
|
|
7500
|
+
if (sessionId === "") {
|
|
7501
|
+
throw new MutagentError("MISSING_ARGUMENTS", "--session is required — a sandbox can hold more than one session.", `Run: mutagent helix send ${id} --session <session-id> "<message>"`);
|
|
7502
|
+
}
|
|
7503
|
+
const built = buildLine(messageParts.join(" ").trim(), options);
|
|
7504
|
+
const result = await postInput(id, { sessionId, line: built.line });
|
|
7505
|
+
if (isJson) {
|
|
7506
|
+
output.output({
|
|
7507
|
+
success: true,
|
|
7508
|
+
sandboxId: id,
|
|
7509
|
+
sessionId,
|
|
7510
|
+
type: built.type,
|
|
7511
|
+
id: built.id,
|
|
7512
|
+
line: built.line,
|
|
7513
|
+
accepted: result.accepted,
|
|
7514
|
+
_links: sandboxLinks(id),
|
|
7515
|
+
_directive: {
|
|
7516
|
+
instruction: "The line was delivered, not answered. The reply is a `response` frame with this id on the event stream.",
|
|
7517
|
+
next: [`mutagent helix attach ${id} --json`]
|
|
7518
|
+
}
|
|
7519
|
+
});
|
|
7520
|
+
return;
|
|
7521
|
+
}
|
|
7522
|
+
output.success(`Sent ${built.type} to session ${sessionId}`);
|
|
7523
|
+
console.error(chalk23.dim(` ${built.line}`));
|
|
7524
|
+
console.error(chalk23.dim(` The reply arrives on the stream, not here: mutagent helix attach ${id}`));
|
|
7525
|
+
} catch (error) {
|
|
7526
|
+
handleError(error, isJson);
|
|
7527
|
+
}
|
|
7528
|
+
});
|
|
7529
|
+
}
|
|
7530
|
+
function buildLine(message, options) {
|
|
7531
|
+
const raw = options.line;
|
|
7532
|
+
if (raw !== undefined) {
|
|
7533
|
+
if (message !== "" || options.type !== undefined) {
|
|
7534
|
+
throw new MutagentError("INVALID_ARGUMENTS", "--line is the whole command already — it cannot be combined with --type or a message.", `Send the raw line on its own, or drop --line and use the friendly form:
|
|
7535
|
+
--type prompt "<message>"`);
|
|
7536
|
+
}
|
|
7537
|
+
return { line: requireOneJsonObject(raw), type: readType(raw), id: null };
|
|
7538
|
+
}
|
|
7539
|
+
const type = options.type ?? "prompt";
|
|
7540
|
+
if (!COMMAND_TYPES.includes(type)) {
|
|
7541
|
+
throw new MutagentError("INVALID_ARGUMENTS", `Unknown command type "${options.type ?? ""}".`, `Use one of: ${COMMAND_TYPES.join(", ")}. For anything else, pass the raw JSON with --line.`);
|
|
7542
|
+
}
|
|
7543
|
+
if (message === "" && NEEDS_MESSAGE.has(type)) {
|
|
7544
|
+
throw new MutagentError("MISSING_ARGUMENTS", `A "${type}" carries a message — there is nothing to send without one.`, `Run: mutagent helix send <id> --session <session-id> --type ${type} "<message>"`);
|
|
7545
|
+
}
|
|
7546
|
+
if (message !== "" && !NEEDS_MESSAGE.has(type)) {
|
|
7547
|
+
throw new MutagentError("INVALID_ARGUMENTS", `A "${type}" takes no message, so the text would be silently discarded.`, `Drop the message, or send it separately as a prompt.`);
|
|
7548
|
+
}
|
|
7549
|
+
const id = options.messageId?.trim() ?? "";
|
|
7550
|
+
const correlationId = id === "" ? `cli_${randomUUID3().slice(0, 8)}` : id;
|
|
7551
|
+
const body = { type, id: correlationId };
|
|
7552
|
+
if (message !== "")
|
|
7553
|
+
body.message = message;
|
|
7554
|
+
return { line: JSON.stringify(body), type, id: correlationId };
|
|
7555
|
+
}
|
|
7556
|
+
function requireOneJsonObject(raw) {
|
|
7557
|
+
const trimmed = raw.trim();
|
|
7558
|
+
if (trimmed.includes(`
|
|
7559
|
+
`)) {
|
|
7560
|
+
throw new MutagentError("INVALID_ARGUMENTS", "--line must be ONE line — a newline inside it would be read as two commands.", "Send one command per call.");
|
|
7561
|
+
}
|
|
7562
|
+
let parsed;
|
|
7563
|
+
try {
|
|
7564
|
+
parsed = JSON.parse(trimmed);
|
|
7565
|
+
} catch {
|
|
7566
|
+
throw new MutagentError("INVALID_ARGUMENTS", "--line is not valid JSON.", `The harness reads one JSON object per line. Example:
|
|
7567
|
+
--line '{"type":"prompt","id":"p1","message":"go"}'`);
|
|
7568
|
+
}
|
|
7569
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
7570
|
+
throw new MutagentError("INVALID_ARGUMENTS", "--line must be a JSON object, not an array or a bare value.", `Example:
|
|
7571
|
+
--line '{"type":"prompt","id":"p1","message":"go"}'`);
|
|
7572
|
+
}
|
|
7573
|
+
return trimmed;
|
|
7574
|
+
}
|
|
7575
|
+
function readType(raw) {
|
|
7576
|
+
try {
|
|
7577
|
+
const parsed = JSON.parse(raw);
|
|
7578
|
+
if (parsed && typeof parsed === "object" && typeof parsed.type === "string") {
|
|
7579
|
+
return parsed.type;
|
|
7580
|
+
}
|
|
7581
|
+
} catch {}
|
|
7582
|
+
return "raw";
|
|
7583
|
+
}
|
|
7584
|
+
|
|
7585
|
+
// src/commands/helix/session-start.ts
|
|
7586
|
+
import { Command as Command13 } from "commander";
|
|
7587
|
+
import chalk24 from "chalk";
|
|
7588
|
+
init_errors();
|
|
7589
|
+
function registerSessionCommand(parent) {
|
|
7590
|
+
const session = new Command13("session").description("Start and inspect Helix sessions inside a sandbox").addHelpText("after", `
|
|
7591
|
+
Examples:
|
|
7592
|
+
${chalk24.dim("$")} mutagent helix session start sbx_123 --rpc
|
|
7593
|
+
${chalk24.dim("$")} mutagent helix session start sbx_123
|
|
7594
|
+
${chalk24.dim("$")} mutagent helix session start sbx_123 --rpc --json
|
|
7595
|
+
|
|
7596
|
+
A sandbox and a session are not the same thing:
|
|
7597
|
+
${chalk24.bold("spawn")} gives you a BOX — a machine with nothing running in it.
|
|
7598
|
+
${chalk24.bold("session start")} gives you an AGENT inside that box.
|
|
7599
|
+
${chalk24.bold("attach")} WATCHES that agent.
|
|
7600
|
+
${chalk24.bold("send")} TYPES INTO it.
|
|
7601
|
+
A box with no session is a normal state — that is what 'exec' runs against.
|
|
7602
|
+
|
|
7603
|
+
AI Agent Directive:
|
|
7604
|
+
Run 'mutagent helix session start <sandbox-id> --rpc --json' before 'send':
|
|
7605
|
+
a session id is required and there is no default one to guess.
|
|
7606
|
+
`);
|
|
7607
|
+
registerStart(session, parent);
|
|
7608
|
+
parent.addCommand(session);
|
|
7609
|
+
}
|
|
7610
|
+
function registerStart(session, root) {
|
|
7611
|
+
session.command("start").description("Start a Helix session inside an existing sandbox").argument("<id>", "Sandbox ID").option("--rpc", "Long-lived session you can send into (default: one-shot)").addHelpText("after", `
|
|
7612
|
+
Examples:
|
|
7613
|
+
${chalk24.dim("$")} mutagent helix session start sbx_123 --rpc
|
|
7614
|
+
${chalk24.dim("$")} mutagent helix session start sbx_123
|
|
7615
|
+
${chalk24.dim("$")} mutagent helix session start sbx_123 --rpc --json
|
|
7616
|
+
|
|
7617
|
+
TWO MODES, AND ONLY ONE OF THEM LISTENS:
|
|
7618
|
+
${chalk24.bold("--rpc")} A long-lived agent reading commands on its stdin. This is
|
|
7619
|
+
the mode 'mutagent helix send' writes into; without it there
|
|
7620
|
+
is nothing to write to and every send is refused.
|
|
7621
|
+
${chalk24.dim("(default)")} One-shot. The session takes its turn and ends. Watchable
|
|
7622
|
+
with 'attach', not steerable.
|
|
7623
|
+
|
|
7624
|
+
The box must already exist — 'mutagent helix spawn --image <ref>' makes one, and
|
|
7625
|
+
'mutagent helix ls' shows which are running. One sandbox can host several
|
|
7626
|
+
sessions, so starting one never replaces another.
|
|
7627
|
+
|
|
7628
|
+
The session id printed here is the handle for everything after it. Nothing else
|
|
7629
|
+
can produce it, so a session started and then forgotten can only be found again
|
|
7630
|
+
by attaching to the box and reading the stream.
|
|
7631
|
+
|
|
7632
|
+
AI Agent Directive:
|
|
7633
|
+
--json returns ONE object: { success, sandboxId, sessionId, status, mode,
|
|
7634
|
+
_links }. Capture 'sessionId' — 'mutagent helix send' requires it and there is
|
|
7635
|
+
no default. Use --rpc whenever you intend to send into the session; a one-shot
|
|
7636
|
+
session refuses input with a 409.
|
|
7637
|
+
`).action(async (id, options) => {
|
|
7638
|
+
const isJson = getJsonFlag(root);
|
|
7639
|
+
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
7640
|
+
try {
|
|
7641
|
+
const mode = options.rpc === true ? "rpc" : "oneshot";
|
|
7642
|
+
const result = await startSession(id, { mode });
|
|
7643
|
+
const sessionId = requireSessionId(result, id);
|
|
7644
|
+
if (isJson) {
|
|
7645
|
+
output.output({
|
|
7646
|
+
success: true,
|
|
7647
|
+
sandboxId: id,
|
|
7648
|
+
sessionId,
|
|
7649
|
+
status: result.status,
|
|
7650
|
+
mode,
|
|
7651
|
+
_links: sandboxLinks(id),
|
|
7652
|
+
_directive: {
|
|
7653
|
+
instruction: mode === "rpc" ? "The session is live and reading its stdin. Attach to watch it; send to steer it." : "The session takes one turn and ends. Attach to watch it; it does not accept input.",
|
|
7654
|
+
next: mode === "rpc" ? [
|
|
7655
|
+
`mutagent helix attach ${id} --json`,
|
|
7656
|
+
`mutagent helix send ${id} --session ${sessionId} "<message>" --json`
|
|
7657
|
+
] : [`mutagent helix attach ${id} --json`]
|
|
7658
|
+
}
|
|
7659
|
+
});
|
|
7660
|
+
return;
|
|
7661
|
+
}
|
|
7662
|
+
output.success(`Session ${sessionId} started in ${id} (${mode})`);
|
|
7663
|
+
console.error(chalk24.dim(` Watch it: mutagent helix attach ${id}`));
|
|
7664
|
+
if (mode === "rpc") {
|
|
7665
|
+
console.error(chalk24.dim(` Talk to it: mutagent helix send ${id} --session ${sessionId} "<message>"`));
|
|
7666
|
+
}
|
|
7667
|
+
} catch (error) {
|
|
7668
|
+
handleError(error, isJson);
|
|
7669
|
+
}
|
|
7670
|
+
});
|
|
7671
|
+
}
|
|
7672
|
+
function requireSessionId(result, sandboxId) {
|
|
7673
|
+
if (typeof result.sessionId === "string" && result.sessionId !== "")
|
|
7674
|
+
return result.sessionId;
|
|
7675
|
+
throw new MutagentError("INVALID_RESPONSE", "The server started a session but did not return a session id.", `The session may be running — watch it with: mutagent helix attach ${sandboxId}`);
|
|
7676
|
+
}
|
|
7677
|
+
|
|
7084
7678
|
// src/commands/helix/run.ts
|
|
7085
|
-
import
|
|
7679
|
+
import chalk25 from "chalk";
|
|
7086
7680
|
init_errors();
|
|
7087
7681
|
function registerRunCommand(parent) {
|
|
7088
7682
|
parent.command("run").description("Run one Helix agent turn in a fresh sandbox").argument("<task...>", "WHAT to do this turn").requiredOption("--prompt <definition>", "WHO the agent is — the agent definition").option("--preset <name>", "Preset to run on (default: the backend default)").option("--keep", "Keep the sandbox afterwards instead of tearing it down").option("--timeout-ms <n>", "Per-command timeout in milliseconds").option("--no-stream", "Wait for the whole turn instead of watching it happen").addHelpText("after", `
|
|
7089
7683
|
Examples:
|
|
7090
|
-
${
|
|
7091
|
-
${
|
|
7092
|
-
${
|
|
7093
|
-
${
|
|
7684
|
+
${chalk25.dim("$")} mutagent helix run "summarise the failing tests" --prompt "You are a test triage agent."
|
|
7685
|
+
${chalk25.dim("$")} mutagent helix run fix the flaky login spec --prompt "$(cat agent.md)"
|
|
7686
|
+
${chalk25.dim("$")} mutagent helix run "list open PRs" --prompt "You are a release assistant." --json
|
|
7687
|
+
${chalk25.dim("$")} mutagent helix run "explore the repo" --prompt "$(cat agent.md)" --keep
|
|
7094
7688
|
|
|
7095
7689
|
TWO PROMPTS, AND THEY ARE NOT THE SAME THING:
|
|
7096
7690
|
--prompt WHO the agent is — its definition, its standing instructions.
|
|
@@ -7197,17 +7791,17 @@ async function streamAgentTurn(body, isJson, keep) {
|
|
|
7197
7791
|
function registerExecCommand(parent) {
|
|
7198
7792
|
parent.command("exec").description("Run a command in a sandbox — an existing one, or a fresh one").argument("<argv...>", "Command and arguments (argv, not a shell string)").option("--sandbox <id>", "Run in this existing sandbox instead of a fresh one").option("--preset <name>", "Preset for the fresh sandbox (ignored with --sandbox)").option("--keep", "Keep the fresh sandbox afterwards (ignored with --sandbox)").option("--cwd <dir>", "Working directory inside the sandbox").option("--timeout-ms <n>", "Command timeout in milliseconds").option("--no-stream", "Wait for the full result instead of streaming it").addHelpText("after", `
|
|
7199
7793
|
Examples:
|
|
7200
|
-
${
|
|
7201
|
-
${
|
|
7202
|
-
${
|
|
7203
|
-
${
|
|
7794
|
+
${chalk25.dim("$")} mutagent helix exec -- ls -la /workspace
|
|
7795
|
+
${chalk25.dim("$")} mutagent helix exec --sandbox sbx_123 -- bun test
|
|
7796
|
+
${chalk25.dim("$")} mutagent helix exec --sandbox sbx_123 --cwd /src -- git status
|
|
7797
|
+
${chalk25.dim("$")} mutagent helix exec --preset default -- uname -m --json
|
|
7204
7798
|
|
|
7205
|
-
Put ${
|
|
7799
|
+
Put ${chalk25.bold("--")} before the command. Everything after it is argv and is passed through
|
|
7206
7800
|
untouched, so flags meant for your command are not read as flags for this one.
|
|
7207
7801
|
|
|
7208
7802
|
ARGV, NOT A SHELL STRING. There is no shell in the sandbox unless you name one:
|
|
7209
|
-
${
|
|
7210
|
-
${
|
|
7803
|
+
${chalk25.dim("$")} mutagent helix exec -- sh -c 'ls | wc -l' ${chalk25.dim("a shell, explicitly")}
|
|
7804
|
+
${chalk25.dim("$")} mutagent helix exec -- 'ls | wc -l' ${chalk25.dim("looks for a file called 'ls | wc -l'")}
|
|
7211
7805
|
|
|
7212
7806
|
WITH --sandbox runs in that box and streams as output is produced.
|
|
7213
7807
|
WITHOUT --sandbox spawns a fresh box from a preset, runs, tears it down. That
|
|
@@ -7339,17 +7933,17 @@ function reportResult(result, output, isJson, context) {
|
|
|
7339
7933
|
}
|
|
7340
7934
|
|
|
7341
7935
|
// src/commands/helix/inventory.ts
|
|
7342
|
-
import
|
|
7936
|
+
import chalk26 from "chalk";
|
|
7343
7937
|
init_errors();
|
|
7344
7938
|
function registerLsCommand(parent) {
|
|
7345
7939
|
parent.command("ls").description("List your sandboxes").addHelpText("after", `
|
|
7346
7940
|
Examples:
|
|
7347
|
-
${
|
|
7348
|
-
${
|
|
7941
|
+
${chalk26.dim("$")} mutagent helix ls
|
|
7942
|
+
${chalk26.dim("$")} mutagent helix ls --json
|
|
7349
7943
|
|
|
7350
7944
|
Listing is always workspace-scoped — these are the sandboxes in the workspace
|
|
7351
7945
|
you are configured for, not every sandbox on the account. Change it with:
|
|
7352
|
-
${
|
|
7946
|
+
${chalk26.dim("$")} mutagent config set workspace <workspace-id>
|
|
7353
7947
|
|
|
7354
7948
|
An empty list is a normal result, not an error: it means no sandbox is running.
|
|
7355
7949
|
|
|
@@ -7369,7 +7963,7 @@ AI Agent Directive:
|
|
|
7369
7963
|
return;
|
|
7370
7964
|
}
|
|
7371
7965
|
if (sandboxes.length === 0) {
|
|
7372
|
-
console.log(
|
|
7966
|
+
console.log(chalk26.gray("No sandboxes running."));
|
|
7373
7967
|
console.log("");
|
|
7374
7968
|
console.log(" Start one: mutagent helix spawn --image <ref>");
|
|
7375
7969
|
return;
|
|
@@ -7393,8 +7987,8 @@ function toRow(sandbox) {
|
|
|
7393
7987
|
function registerPresetsCommand(parent) {
|
|
7394
7988
|
parent.command("presets").description("List the presets a one-shot run can name").addHelpText("after", `
|
|
7395
7989
|
Examples:
|
|
7396
|
-
${
|
|
7397
|
-
${
|
|
7990
|
+
${chalk26.dim("$")} mutagent helix presets
|
|
7991
|
+
${chalk26.dim("$")} mutagent helix presets --json
|
|
7398
7992
|
|
|
7399
7993
|
A preset is a named definition on the backend. It is what lets 'helix run' and
|
|
7400
7994
|
'helix exec' start something without you knowing an image reference, a registry
|
|
@@ -7415,7 +8009,7 @@ AI Agent Directive:
|
|
|
7415
8009
|
return;
|
|
7416
8010
|
}
|
|
7417
8011
|
if (presets.length === 0) {
|
|
7418
|
-
console.log(
|
|
8012
|
+
console.log(chalk26.gray("No presets configured on this backend."));
|
|
7419
8013
|
console.log("");
|
|
7420
8014
|
console.log(" Spawn with an explicit image: mutagent helix spawn --image <ref>");
|
|
7421
8015
|
return;
|
|
@@ -7435,15 +8029,15 @@ AI Agent Directive:
|
|
|
7435
8029
|
function registerRmCommand(parent) {
|
|
7436
8030
|
parent.command("rm").description("Destroy a sandbox").argument("<id>", "Sandbox ID").option("-f, --force", "Skip confirmation").addHelpText("after", `
|
|
7437
8031
|
Examples:
|
|
7438
|
-
${
|
|
7439
|
-
${
|
|
8032
|
+
${chalk26.dim("$")} mutagent helix rm sbx_123 --force
|
|
8033
|
+
${chalk26.dim("$")} mutagent helix rm sbx_123 --json
|
|
7440
8034
|
|
|
7441
8035
|
This is the ONLY command that destroys a sandbox — detaching, Ctrl-C and a
|
|
7442
8036
|
closed terminal all leave it running. Removing a sandbox that is already gone
|
|
7443
8037
|
succeeds, so a retried teardown is safe.
|
|
7444
8038
|
|
|
7445
|
-
${
|
|
7446
|
-
${
|
|
8039
|
+
${chalk26.dim("Note: --force is required. The CLI is non-interactive — confirm with the user via your native flow, then pass --force. --json auto-confirms.")}
|
|
8040
|
+
${chalk26.dim("Warning: any session running in the sandbox ends immediately and cannot be recovered.")}
|
|
7447
8041
|
|
|
7448
8042
|
AI Agent Directive:
|
|
7449
8043
|
Destroying a sandbox ends any session running in it and cannot be undone.
|
|
@@ -7478,8 +8072,8 @@ Use --force to confirm: mutagent helix rm ${id} --force`);
|
|
|
7478
8072
|
function registerTracesCommand(parent) {
|
|
7479
8073
|
parent.command("traces").description("Show the spans captured for a sandbox").argument("<id>", "Sandbox ID").addHelpText("after", `
|
|
7480
8074
|
Examples:
|
|
7481
|
-
${
|
|
7482
|
-
${
|
|
8075
|
+
${chalk26.dim("$")} mutagent helix traces sbx_123
|
|
8076
|
+
${chalk26.dim("$")} mutagent helix traces sbx_123 --json
|
|
7483
8077
|
|
|
7484
8078
|
Spans are what the session actually did — the record to read when the output
|
|
7485
8079
|
alone does not explain the result. They are called spans, not traces, because
|
|
@@ -7503,7 +8097,7 @@ AI Agent Directive:
|
|
|
7503
8097
|
return;
|
|
7504
8098
|
}
|
|
7505
8099
|
if (spans.length === 0) {
|
|
7506
|
-
console.log(
|
|
8100
|
+
console.log(chalk26.gray(`No spans captured for sandbox ${id}.`));
|
|
7507
8101
|
return;
|
|
7508
8102
|
}
|
|
7509
8103
|
output.output(spans.map((span) => {
|
|
@@ -7523,26 +8117,35 @@ AI Agent Directive:
|
|
|
7523
8117
|
|
|
7524
8118
|
// src/commands/helix/index.ts
|
|
7525
8119
|
function createHelixCommand() {
|
|
7526
|
-
const helix = new
|
|
8120
|
+
const helix = new Command14("helix").description("Run Helix sessions in a cloud sandbox").addHelpText("after", `
|
|
7527
8121
|
Examples:
|
|
7528
|
-
${
|
|
7529
|
-
${
|
|
7530
|
-
${
|
|
7531
|
-
${
|
|
7532
|
-
${
|
|
7533
|
-
${
|
|
7534
|
-
${
|
|
7535
|
-
${
|
|
7536
|
-
${
|
|
8122
|
+
${chalk27.dim("$")} mutagent helix run "fix the failing test" --prompt "$(cat agent.md)"
|
|
8123
|
+
${chalk27.dim("$")} mutagent helix exec -- uname -m ${chalk27.dim("One command, fresh box")}
|
|
8124
|
+
${chalk27.dim("$")} mutagent helix presets ${chalk27.dim("What a one-shot can run on")}
|
|
8125
|
+
${chalk27.dim("$")} mutagent helix spawn --image alpine:3.20 ${chalk27.dim("Persistent box, then attach")}
|
|
8126
|
+
${chalk27.dim("$")} mutagent helix session start sbx_1 --rpc ${chalk27.dim("Start an agent inside that box")}
|
|
8127
|
+
${chalk27.dim("$")} mutagent helix ls ${chalk27.dim("What is running")}
|
|
8128
|
+
${chalk27.dim("$")} mutagent helix exec --sandbox sbx_1 -- bun test ${chalk27.dim("Command in that box")}
|
|
8129
|
+
${chalk27.dim("$")} mutagent helix attach sbx_1 --since 412 ${chalk27.dim("Resume after a sequence number")}
|
|
8130
|
+
${chalk27.dim("$")} mutagent helix send sbx_1 --session s1 "and now" ${chalk27.dim("Talk to a running session")}
|
|
8131
|
+
${chalk27.dim("$")} mutagent helix traces sbx_1 --json ${chalk27.dim("What the session actually did")}
|
|
8132
|
+
${chalk27.dim("$")} mutagent helix rm sbx_1 --force ${chalk27.dim("Destroy it")}
|
|
7537
8133
|
|
|
7538
8134
|
Subcommands:
|
|
7539
|
-
run, exec, spawn, ls, presets, attach, traces, rm
|
|
8135
|
+
run, exec, spawn, session, ls, presets, attach, send, traces, rm
|
|
7540
8136
|
|
|
7541
8137
|
One-shot vs persistent:
|
|
7542
|
-
${
|
|
8138
|
+
${chalk27.bold("run")} and ${chalk27.bold("exec")} (without --sandbox) start from a named PRESET, do one
|
|
7543
8139
|
thing and tear the sandbox down. Nothing to clean up, no image to know.
|
|
7544
|
-
${
|
|
7545
|
-
${
|
|
8140
|
+
${chalk27.bold("spawn")} takes an explicit --image and leaves a box running for you to
|
|
8141
|
+
${chalk27.bold("attach")} and ${chalk27.bold("exec --sandbox")} against until you ${chalk27.bold("rm")} it.
|
|
8142
|
+
|
|
8143
|
+
Boxes vs sessions vs watching vs steering:
|
|
8144
|
+
${chalk27.bold("spawn")} gives you a BOX. ${chalk27.bold("session start")} gives you an AGENT inside that box.
|
|
8145
|
+
${chalk27.bold("attach")} WATCHES that agent; it does not type into one. ${chalk27.bold("send")} TYPES INTO it,
|
|
8146
|
+
from any terminal holding the id — so a session can be redirected without the
|
|
8147
|
+
terminal that started it. A box with no session is normal: that is what
|
|
8148
|
+
${chalk27.bold("exec")} runs against.
|
|
7546
8149
|
|
|
7547
8150
|
Lifecycle:
|
|
7548
8151
|
A spawned sandbox outlives your terminal. Ctrl-C, a dropped connection and a
|
|
@@ -7552,7 +8155,7 @@ Lifecycle:
|
|
|
7552
8155
|
|
|
7553
8156
|
Access:
|
|
7554
8157
|
Sandbox access is minted per WORKSPACE, so a workspace must be configured:
|
|
7555
|
-
${
|
|
8158
|
+
${chalk27.dim("$")} mutagent config set workspace <workspace-id>
|
|
7556
8159
|
|
|
7557
8160
|
Output:
|
|
7558
8161
|
Sandbox stdout goes to stdout, sandbox stderr goes to stderr, and anything
|
|
@@ -7581,6 +8184,8 @@ AI Agent Directive:
|
|
|
7581
8184
|
registerLsCommand(helix);
|
|
7582
8185
|
registerPresetsCommand(helix);
|
|
7583
8186
|
registerAttachCommand(helix);
|
|
8187
|
+
registerSendCommand(helix);
|
|
8188
|
+
registerSessionCommand(helix);
|
|
7584
8189
|
registerTracesCommand(helix);
|
|
7585
8190
|
registerRmCommand(helix);
|
|
7586
8191
|
return helix;
|
|
@@ -7694,7 +8299,7 @@ if (process.env.CLI_VERSION) {
|
|
|
7694
8299
|
} catch {}
|
|
7695
8300
|
}
|
|
7696
8301
|
setCliVersion(cliVersion);
|
|
7697
|
-
var program = new
|
|
8302
|
+
var program = new Command15;
|
|
7698
8303
|
program.name("mutagent").description(`Mutagent CLI - command-line client for the Mutagent platform
|
|
7699
8304
|
|
|
7700
8305
|
Documentation: https://docs.mutagent.io/cli
|
|
@@ -7704,61 +8309,61 @@ program.name("mutagent").description(`Mutagent CLI - command-line client for the
|
|
|
7704
8309
|
});
|
|
7705
8310
|
program.addHelpText("beforeAll", () => renderBanner());
|
|
7706
8311
|
program.addHelpText("after", `
|
|
7707
|
-
${
|
|
7708
|
-
${
|
|
7709
|
-
${
|
|
7710
|
-
${
|
|
8312
|
+
${chalk28.bold.cyan("WORKFLOWS:")}
|
|
8313
|
+
${chalk28.bold("Setup")} mutagent login → mutagent init
|
|
8314
|
+
${chalk28.bold("Lifecycle Tools")} mutagent install helix ${chalk28.dim("(login-gated)")}
|
|
8315
|
+
${chalk28.bold("Feedback")} mutagent feedback send "<what happened>" --category <cli|helix|stage:<x>> ${chalk28.dim("[--session <id>] [--attach-transcript]")}
|
|
7711
8316
|
|
|
7712
|
-
${
|
|
7713
|
-
${
|
|
8317
|
+
${chalk28.dim("For CLI usage guidance for AI agents, see the Skill at")}
|
|
8318
|
+
${chalk28.cyan(".claude/skills/mutagent-cli/SKILL.md")}
|
|
7714
8319
|
|
|
7715
|
-
${
|
|
7716
|
-
-v, --version ${
|
|
8320
|
+
${chalk28.yellow("Global flags:")}
|
|
8321
|
+
-v, --version ${chalk28.dim("Print the CLI version")} ${chalk28.dim("(before a subcommand; after one it is the subcommand's)")}
|
|
7717
8322
|
--json --api-key <k> --endpoint <url> --non-interactive
|
|
7718
8323
|
|
|
7719
|
-
${
|
|
7720
|
-
export MUTAGENT_API_KEY=mt_... ${
|
|
7721
|
-
--json ${
|
|
8324
|
+
${chalk28.yellow("Non-Interactive Mode (CI/CD & Coding Agents):")}
|
|
8325
|
+
export MUTAGENT_API_KEY=mt_... ${chalk28.dim("or")} --api-key mt_...
|
|
8326
|
+
--json ${chalk28.dim("for structured output")} --non-interactive ${chalk28.dim("to disable prompts")}
|
|
7722
8327
|
|
|
7723
|
-
${
|
|
7724
|
-
mutagent login ${
|
|
7725
|
-
mutagent auth status ${
|
|
7726
|
-
mutagent init ${
|
|
7727
|
-
mutagent workspaces list --json ${
|
|
7728
|
-
mutagent config set workspace <id> ${
|
|
7729
|
-
mutagent usage --json ${
|
|
8328
|
+
${chalk28.yellow("Command Navigation:")}
|
|
8329
|
+
mutagent login ${chalk28.dim("Login (browser OAuth — recommended)")}
|
|
8330
|
+
mutagent auth status ${chalk28.dim("Check auth + workspace")}
|
|
8331
|
+
mutagent init ${chalk28.dim("Initialize project (.mutagentrc.json)")}
|
|
8332
|
+
mutagent workspaces list --json ${chalk28.dim("List workspaces (verify ID)")}
|
|
8333
|
+
mutagent config set workspace <id> ${chalk28.dim("Set active workspace")}
|
|
8334
|
+
mutagent usage --json ${chalk28.dim("Show account usage + provider status")}
|
|
7730
8335
|
|
|
7731
|
-
mutagent providers list --json ${
|
|
7732
|
-
mutagent providers list --models ${
|
|
8336
|
+
mutagent providers list --json ${chalk28.dim("List configured BYOK providers")}
|
|
8337
|
+
mutagent providers list --models ${chalk28.dim("See available models per provider")}
|
|
7733
8338
|
|
|
7734
|
-
mutagent helix run "<task>" --prompt "<who>" ${
|
|
7735
|
-
mutagent helix ls --json ${
|
|
7736
|
-
mutagent helix spawn --image <ref> ${
|
|
7737
|
-
mutagent helix rm <id> --force ${
|
|
8339
|
+
mutagent helix run "<task>" --prompt "<who>" ${chalk28.dim("One Helix agent turn in a cloud sandbox")}
|
|
8340
|
+
mutagent helix ls --json ${chalk28.dim("List running sandboxes")}
|
|
8341
|
+
mutagent helix spawn --image <ref> ${chalk28.dim("Persistent sandbox you attach to")}
|
|
8342
|
+
mutagent helix rm <id> --force ${chalk28.dim("Destroy a sandbox (the only thing that does)")}
|
|
7738
8343
|
|
|
7739
|
-
mutagent install helix ${
|
|
7740
|
-
mutagent install helix --version 1.2.3 ${
|
|
7741
|
-
mutagent install --help ${
|
|
8344
|
+
mutagent install helix ${chalk28.dim("Install the ADL lifecycle conductor locally (login-gated)")}
|
|
8345
|
+
mutagent install helix --version 1.2.3 ${chalk28.dim("Pin a version")}
|
|
8346
|
+
mutagent install --help ${chalk28.dim("helix")}
|
|
7742
8347
|
|
|
7743
|
-
mutagent hooks --help ${
|
|
8348
|
+
mutagent hooks --help ${chalk28.dim("Hook setup for Claude Code session telemetry upload")}
|
|
7744
8349
|
|
|
7745
|
-
${
|
|
7746
|
-
Hit a bug? Run: ${
|
|
7747
|
-
Lifecycle-stage feedback: ${
|
|
7748
|
-
Link to a session: ${
|
|
7749
|
-
Attach your coding-agent session transcript: ${
|
|
7750
|
-
${
|
|
8350
|
+
${chalk28.bold.red("Report Issues:")}
|
|
8351
|
+
Hit a bug? Run: ${chalk28.cyan('mutagent feedback send "describe what went wrong" --category cli')}
|
|
8352
|
+
Lifecycle-stage feedback: ${chalk28.cyan('mutagent feedback send "eval gate unclear" --category stage:evaluate')}
|
|
8353
|
+
Link to a session: ${chalk28.cyan('mutagent feedback send "..." --session <session-id>')}
|
|
8354
|
+
Attach your coding-agent session transcript: ${chalk28.cyan('mutagent feedback send "..." --attach-transcript')}
|
|
8355
|
+
${chalk28.dim("--category accepts: cli | helix | stage:<spec|build|evaluate|diagnose|optimize>")}
|
|
7751
8356
|
|
|
7752
|
-
${
|
|
8357
|
+
${chalk28.yellow("Directive System:")}
|
|
7753
8358
|
Every --json response may include:
|
|
7754
|
-
${
|
|
7755
|
-
${
|
|
7756
|
-
${
|
|
7757
|
-
${
|
|
7758
|
-
${
|
|
7759
|
-
${
|
|
7760
|
-
|
|
7761
|
-
${
|
|
8359
|
+
${chalk28.bold("_directive.display")} Type tag — 'status_card' for card-kind directives (drives test/docs guards)
|
|
8360
|
+
${chalk28.bold("_directive.renderedCard")} Pre-formatted card ${chalk28.red("(MUST echo verbatim in chat whenever this field exists — see SKILL.md Verbatim Card Display Protocol)")}
|
|
8361
|
+
${chalk28.bold("_directive.instruction")} Next step for the agent (self-sufficient, no Skill required)
|
|
8362
|
+
${chalk28.bold("_directive.next")} Array of suggested follow-up commands
|
|
8363
|
+
${chalk28.bold("_links")} Dashboard/API URLs (format as markdown links)
|
|
8364
|
+
${chalk28.bold("_compat")} Compat metadata: cliVersion, skillVersion, skillMinCliVersion
|
|
8365
|
+
|
|
8366
|
+
${chalk28.yellow("AI Agent Rules (MANDATORY for coding agents):")}
|
|
7762
8367
|
1. Login (two paths):
|
|
7763
8368
|
- CI / fully automated: export MUTAGENT_API_KEY=mt_... then mutagent login --json
|
|
7764
8369
|
- Helping a user onboard: mutagent login --browser --json
|
|
@@ -7768,13 +8373,13 @@ ${chalk25.yellow("AI Agent Rules (MANDATORY for coding agents):")}
|
|
|
7768
8373
|
2. EVERY command MUST include --json (no exceptions)
|
|
7769
8374
|
3. Run <command> --help BEFORE first use of any command
|
|
7770
8375
|
4. Parse _directive.renderedCard and copy it into your CHAT RESPONSE verbatim
|
|
7771
|
-
${
|
|
8376
|
+
${chalk28.red("HARD STOP")}: do NOT run further commands until the card is rendered in chat
|
|
7772
8377
|
5. After mutagent init, verify workspace: mutagent workspaces list --json
|
|
7773
8378
|
6. ALL user interaction via AskUserQuestion — CLI is non-interactive
|
|
7774
8379
|
${!hasCredentials() ? `
|
|
7775
|
-
` +
|
|
8380
|
+
` + chalk28.yellow(" Warning: Not authenticated. Run: mutagent login") + `
|
|
7776
8381
|
` : ""}${!hasRcConfig() ? `
|
|
7777
|
-
` +
|
|
8382
|
+
` + chalk28.green(" Get started: mutagent init") + `
|
|
7778
8383
|
` : ""}`);
|
|
7779
8384
|
var rawArgs = process.argv.slice(2);
|
|
7780
8385
|
if (isGlobalVersionFlag(rawArgs)) {
|
|
@@ -7815,5 +8420,5 @@ program.addCommand(createFeedbackCommand());
|
|
|
7815
8420
|
program.addCommand(createTraceCommand());
|
|
7816
8421
|
program.parse();
|
|
7817
8422
|
|
|
7818
|
-
//# debugId=
|
|
8423
|
+
//# debugId=9CA63CD3787CB08B64756E2164756E21
|
|
7819
8424
|
//# sourceMappingURL=cli.js.map
|