@mutagent/cli 0.1.337 → 0.1.338
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 +681 -1015
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -1233,7 +1233,7 @@ var init_sdk_client = __esm(() => {
|
|
|
1233
1233
|
|
|
1234
1234
|
// src/bin/cli.ts
|
|
1235
1235
|
import { Command as Command17 } from "commander";
|
|
1236
|
-
import
|
|
1236
|
+
import chalk36 from "chalk";
|
|
1237
1237
|
import { readFileSync as readFileSync15 } from "fs";
|
|
1238
1238
|
import { join as join16, dirname as dirname4 } from "path";
|
|
1239
1239
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -2188,21 +2188,23 @@ async function loginWithPastedKey(initialEndpoint, output, wasFirstLogin) {
|
|
|
2188
2188
|
|
|
2189
2189
|
// src/commands/auth.ts
|
|
2190
2190
|
function createAuthCommand() {
|
|
2191
|
-
const auth = new Command("auth").description("
|
|
2191
|
+
const auth = new Command("auth").description("Sign in, show the sign-in state, or sign out on this machine").addHelpText("after", `
|
|
2192
2192
|
Examples:
|
|
2193
2193
|
${chalk4.dim("$")} mutagent auth login
|
|
2194
2194
|
${chalk4.dim("$")} mutagent auth login --browser
|
|
2195
2195
|
${chalk4.dim("$")} mutagent auth status
|
|
2196
2196
|
${chalk4.dim("$")} mutagent auth logout
|
|
2197
|
+
|
|
2198
|
+
${chalk4.dim("mutagent auth login is the same command as mutagent login.")}
|
|
2197
2199
|
`);
|
|
2198
|
-
auth.command("login").description("
|
|
2200
|
+
auth.command("login").description("Same as mutagent login: sign in and store the API key on this machine").option("--browser", "Sign in with the browser (skip the method prompt)").option("--non-interactive", "Skip the method prompt and use browser sign-in (with --json it also needs --browser)").option("--endpoint <url>", "API endpoint", "https://api.mutagent.io").addHelpText("after", `
|
|
2199
2201
|
Examples:
|
|
2200
|
-
${chalk4.dim("$")} mutagent auth login ${chalk4.dim("#
|
|
2201
|
-
${chalk4.dim("$")} mutagent auth login --browser ${chalk4.dim("#
|
|
2202
|
-
${chalk4.dim("$")} mutagent auth login --non-interactive ${chalk4.dim("#
|
|
2202
|
+
${chalk4.dim("$")} mutagent auth login ${chalk4.dim("# In a terminal, choose browser or API key")}
|
|
2203
|
+
${chalk4.dim("$")} mutagent auth login --browser ${chalk4.dim("# Sign in with the browser")}
|
|
2204
|
+
${chalk4.dim("$")} mutagent auth login --non-interactive ${chalk4.dim("# Browser sign-in without the method prompt (a person approves in the browser)")}
|
|
2203
2205
|
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
+
This runs the same sign-in as mutagent login, with the same options and --json output.
|
|
2207
|
+
See mutagent login --help for the full description.
|
|
2206
2208
|
`).action(async (options) => {
|
|
2207
2209
|
const isJson = getJsonFlag(auth);
|
|
2208
2210
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -2236,7 +2238,7 @@ ${chalk4.dim("See `mutagent login --help` for full documentation.")}
|
|
|
2236
2238
|
throw error;
|
|
2237
2239
|
}
|
|
2238
2240
|
});
|
|
2239
|
-
auth.command("status").description("
|
|
2241
|
+
auth.command("status").description("Show the sign-in state, endpoint and active workspace").addHelpText("after", `
|
|
2240
2242
|
Examples:
|
|
2241
2243
|
${chalk4.dim("$")} mutagent auth status
|
|
2242
2244
|
${chalk4.dim("$")} mutagent auth status --json
|
|
@@ -2322,37 +2324,29 @@ import { Command as Command2 } from "commander";
|
|
|
2322
2324
|
import chalk5 from "chalk";
|
|
2323
2325
|
init_errors();
|
|
2324
2326
|
function createLoginCommand() {
|
|
2325
|
-
const login = new Command2("login").description("
|
|
2327
|
+
const login = new Command2("login").description("Sign in to Mutagent and store the API key on this machine").option("--browser", "Sign in with the browser (skip the method prompt)").option("--non-interactive", "Skip the method prompt and use browser sign-in (with --json it also needs --browser)").option("--endpoint <url>", "API endpoint", "https://api.mutagent.io").addHelpText("after", `
|
|
2326
2328
|
Examples:
|
|
2327
|
-
${chalk5.dim("$")} mutagent login ${chalk5.dim("#
|
|
2328
|
-
${chalk5.dim("$")} mutagent login --browser ${chalk5.dim("#
|
|
2329
|
-
${chalk5.dim("$")} mutagent login --non-interactive ${chalk5.dim("#
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
MUTAGENT_NON_INTERACTIVE=true Treat environment as non-interactive
|
|
2349
|
-
CI=true Automatically enables non-interactive mode
|
|
2350
|
-
|
|
2351
|
-
Verify after login:
|
|
2352
|
-
${chalk5.dim("$")} mutagent auth status ${chalk5.dim("# Show session, workspace, org")}
|
|
2353
|
-
${chalk5.dim("$")} mutagent workspaces list --json ${chalk5.dim("# List available workspaces")}
|
|
2354
|
-
|
|
2355
|
-
${chalk5.dim("Note: `mutagent auth login` is a back-compat alias and behaves identically.")}
|
|
2329
|
+
${chalk5.dim("$")} mutagent login ${chalk5.dim("# In a terminal, choose browser or API key")}
|
|
2330
|
+
${chalk5.dim("$")} mutagent login --browser ${chalk5.dim("# Sign in with the browser")}
|
|
2331
|
+
${chalk5.dim("$")} mutagent login --non-interactive ${chalk5.dim("# Browser sign-in without the method prompt (a person approves in the browser)")}
|
|
2332
|
+
${chalk5.dim("$")} MUTAGENT_API_KEY=mt_... mutagent login --json ${chalk5.dim("# Sign in with a key (CI)")}
|
|
2333
|
+
|
|
2334
|
+
Browser sign-in opens app.mutagent.io, where you sign in or sign up, finish onboarding
|
|
2335
|
+
and approve this CLI. The CLI waits up to 5 minutes and saves the credentials.
|
|
2336
|
+
|
|
2337
|
+
When MUTAGENT_API_KEY is set, login skips the browser and validates the key.
|
|
2338
|
+
|
|
2339
|
+
Environment:
|
|
2340
|
+
MUTAGENT_API_KEY Use this key; no browser
|
|
2341
|
+
MUTAGENT_ENDPOINT API endpoint (overrides --endpoint)
|
|
2342
|
+
MUTAGENT_NON_INTERACTIVE=true Same as --non-interactive
|
|
2343
|
+
CI=true Same as --non-interactive
|
|
2344
|
+
|
|
2345
|
+
Then:
|
|
2346
|
+
${chalk5.dim("$")} mutagent auth status
|
|
2347
|
+
${chalk5.dim("$")} mutagent workspaces list --json
|
|
2348
|
+
|
|
2349
|
+
mutagent auth login runs the same sign-in.
|
|
2356
2350
|
`).action(async (options) => {
|
|
2357
2351
|
const isJson = getJsonFlag(login);
|
|
2358
2352
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -2425,6 +2419,7 @@ Examples:
|
|
|
2425
2419
|
${chalk6.dim("$")} mutagent config get apiKey --json
|
|
2426
2420
|
|
|
2427
2421
|
${chalk6.dim("Keys: apiKey, endpoint, format, timeout, defaultWorkspace, defaultOrganization")}
|
|
2422
|
+
${chalk6.dim("A key with no value set is reported as unknown.")}
|
|
2428
2423
|
`).action((key) => {
|
|
2429
2424
|
const isJson = getJsonFlag(config);
|
|
2430
2425
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -2453,7 +2448,7 @@ ${chalk6.dim("Keys: apiKey, endpoint, format, timeout, defaultWorkspace, default
|
|
|
2453
2448
|
Examples:
|
|
2454
2449
|
${chalk6.dim("$")} mutagent config set workspace <workspace-id>
|
|
2455
2450
|
|
|
2456
|
-
${chalk6.dim("
|
|
2451
|
+
${chalk6.dim("Commands use this workspace until you change it.")}
|
|
2457
2452
|
`).action((id) => {
|
|
2458
2453
|
const isJson = getJsonFlag(config);
|
|
2459
2454
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -2594,10 +2589,8 @@ Examples:
|
|
|
2594
2589
|
${chalk7.dim("$")} mutagent workspaces list
|
|
2595
2590
|
${chalk7.dim("$")} mutagent workspaces get <workspace-id>
|
|
2596
2591
|
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
Note: Workspace management (create, update, delete) is available in the Admin Panel only.
|
|
2592
|
+
Choose the active workspace with: mutagent config set workspace <workspace-id>
|
|
2593
|
+
Create or change workspaces in the dashboard: https://app.mutagent.io
|
|
2601
2594
|
`);
|
|
2602
2595
|
workspaces.command("list").description("List all workspaces").option("-l, --limit <n>", "Limit results", "50").option("-o, --offset <n>", "Offset for pagination").addHelpText("after", `
|
|
2603
2596
|
Examples:
|
|
@@ -3149,32 +3142,30 @@ function collectCredentialFields(options) {
|
|
|
3149
3142
|
return fields;
|
|
3150
3143
|
}
|
|
3151
3144
|
function registerAddCommand(parent) {
|
|
3152
|
-
parent.command("add").description("Add
|
|
3145
|
+
parent.command("add").description("Add an LLM provider to the workspace").requiredOption("-p, --provider <type>", "LLM provider type (openai, anthropic, azure, ...)").requiredOption("-n, --name <name>", "Display name").option("-k, --api-key <key>", "API key (every type except vertex)").option("--service-account-key <json>", "Google Vertex service-account credential JSON").option("--hosted-family <family>", `Model family a gateway (azure, vertex, bedrock) serves: ${HOSTED_FAMILIES.join(", ")}`).option("--base-url <url>", "Base URL of the API").option("--api-format <format>", apiFormatHelpText()).option("--resource-endpoint <url>", "Azure resource endpoint").option("--deployment-name <name>", "Azure deployment name").option("--api-version <version>", "Azure API version (e.g. 2024-08-01-preview)").option("--project-id <id>", "Google Vertex project ID").option("--region <region>", "Region (Vertex location, or AWS Bedrock region)").option("--set-default", "Make it the workspace's default LLM provider of its type").addHelpText("after", `
|
|
3153
3146
|
Examples:
|
|
3154
3147
|
${chalk8.dim("$")} mutagent providers add --provider openai --name "My OpenAI" --api-key $OPENAI_API_KEY
|
|
3155
3148
|
${chalk8.dim("$")} mutagent providers add --provider azure --name "Azure GPT" --api-key $KEY \\
|
|
3156
3149
|
--hosted-family openai --resource-endpoint https://my.openai.azure.com --deployment-name gpt-5
|
|
3157
|
-
${chalk8.dim("$")} mutagent providers add --provider bedrock --name "Bedrock Claude" --api-key $KEY
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
The API key is encrypted server-side and never returned in plain text.
|
|
3150
|
+
${chalk8.dim("$")} mutagent providers add --provider bedrock --name "Bedrock Claude" --api-key $KEY --region us-east-1
|
|
3151
|
+
${chalk8.dim("$")} mutagent providers add --provider custom --name "My gateway" --api-key $KEY \\
|
|
3152
|
+
--base-url https://llm.example.com/v1 --api-format anthropic
|
|
3153
|
+
|
|
3154
|
+
azure and vertex serve another vendor's models and need --hosted-family; bedrock serves
|
|
3155
|
+
anthropic and needs none. Other LLM provider types must not set it.
|
|
3156
|
+
--api-format applies only to the LLM provider types its option line lists. Use
|
|
3157
|
+
anthropic for an endpoint that speaks the Anthropic API; unset, the server default applies.
|
|
3158
|
+
The base URL must be a public address: hosts ending in .internal, .local, .localhost or
|
|
3159
|
+
.home.arpa are refused.
|
|
3160
|
+
|
|
3161
|
+
Adds the LLM provider to the configured workspace (mutagent config set workspace <id>).
|
|
3162
|
+
The key is encrypted and never returned.
|
|
3163
|
+
Workspace default model: when the workspace has none, adding a keyed LLM provider sets
|
|
3164
|
+
one on the server. An existing workspace default model is never changed.
|
|
3174
3165
|
|
|
3175
3166
|
AI Agent Directive:
|
|
3176
|
-
Run 'mutagent providers list --json' first to check whether the provider already exists.
|
|
3177
|
-
This command writes a credential
|
|
3167
|
+
Run 'mutagent providers list --json' first to check whether the LLM provider already exists.
|
|
3168
|
+
This command writes a credential: confirm with the user before running it.
|
|
3178
3169
|
`).action(async (options) => {
|
|
3179
3170
|
const isJson = getJsonFlag(parent);
|
|
3180
3171
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -3285,14 +3276,14 @@ init_sdk_client();
|
|
|
3285
3276
|
import chalk9 from "chalk";
|
|
3286
3277
|
init_errors();
|
|
3287
3278
|
function registerUpdateCommand(parent) {
|
|
3288
|
-
parent.command("update").description("Update an
|
|
3279
|
+
parent.command("update").description("Update an LLM provider").argument("<id>", "LLM provider ID (from: mutagent providers list)").option("-n, --name <name>", "Updated display name").option("-k, --api-key <key>", "New API key (stored encrypted)").option("--active <bool>", "Activate or deactivate (true|false)").option("--set-default", "Make it the workspace's default LLM provider of its type").option("--base-url <url>", 'Updated base URL (use "" to clear)').addHelpText("after", `
|
|
3289
3280
|
Examples:
|
|
3290
|
-
${chalk9.dim("$")} mutagent providers update <id> --name "New Name"
|
|
3291
|
-
${chalk9.dim("$")} mutagent providers update <id> --api-key $NEW_KEY --json
|
|
3292
|
-
${chalk9.dim("$")} mutagent providers update <id> --active false
|
|
3293
|
-
${chalk9.dim("$")} mutagent providers update <id> --set-default --json
|
|
3281
|
+
${chalk9.dim("$")} mutagent providers update <provider-id> --name "New Name"
|
|
3282
|
+
${chalk9.dim("$")} mutagent providers update <provider-id> --api-key $NEW_KEY --json
|
|
3283
|
+
${chalk9.dim("$")} mutagent providers update <provider-id> --active false
|
|
3284
|
+
${chalk9.dim("$")} mutagent providers update <provider-id> --set-default --json
|
|
3294
3285
|
|
|
3295
|
-
|
|
3286
|
+
Only the fields you pass change. An update never sets the workspace default model.
|
|
3296
3287
|
`).action(async (id, options) => {
|
|
3297
3288
|
const isJson = getJsonFlag(parent);
|
|
3298
3289
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -3386,14 +3377,16 @@ init_sdk_client();
|
|
|
3386
3377
|
import chalk10 from "chalk";
|
|
3387
3378
|
init_errors();
|
|
3388
3379
|
function registerDeleteCommand(parent) {
|
|
3389
|
-
parent.command("delete").description("Delete
|
|
3380
|
+
parent.command("delete").description("Delete an LLM provider").argument("<id>", "LLM provider ID (from: mutagent providers list)").option("-f, --force", "Confirm the delete (required unless --json)").addHelpText("after", `
|
|
3390
3381
|
Examples:
|
|
3391
|
-
${chalk10.dim("$")} mutagent providers delete <id>
|
|
3392
|
-
${chalk10.dim("$")} mutagent providers delete <id> --force
|
|
3393
|
-
|
|
3382
|
+
${chalk10.dim("$")} mutagent providers delete <provider-id> --force
|
|
3383
|
+
${chalk10.dim("$")} mutagent providers delete <provider-id> --force --json
|
|
3384
|
+
|
|
3385
|
+
Without --force (and without --json) the command refuses; it never prompts.
|
|
3386
|
+
The stored key is deleted and cannot be recovered. Deleting an unknown ID succeeds.
|
|
3394
3387
|
|
|
3395
|
-
|
|
3396
|
-
|
|
3388
|
+
AI Agent Directive:
|
|
3389
|
+
Confirm with the user before running it, then pass --force (or --json). The stored key cannot be recovered.
|
|
3397
3390
|
`).action(async (id, options) => {
|
|
3398
3391
|
const isJson = getJsonFlag(parent);
|
|
3399
3392
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -4115,44 +4108,42 @@ function statusGlyph(result) {
|
|
|
4115
4108
|
}
|
|
4116
4109
|
}
|
|
4117
4110
|
function registerMirrorCommand(parent) {
|
|
4118
|
-
parent.command("mirror").description("Copy the local Helix
|
|
4111
|
+
parent.command("mirror").description("Copy the API keys from your local Helix login store into the workspace as LLM providers").option("-y, --yes", "Consent without prompting (required when non-interactive)").option("--no-overwrite", "Leave LLM providers the workspace already has untouched").option("--workspace <id>", "Target workspace for this run (default: the configured one)").option("--channel <channel>", `Helix channel whose store is read: ${HELIX_CHANNELS.join(" | ")}`, "stable").option("--provider <id>", "Only this local LLM provider id (repeatable)", (value, prev = []) => [...prev, value]).addHelpText("after", `
|
|
4119
4112
|
Examples:
|
|
4120
4113
|
${chalk11.dim("$")} mutagent providers mirror
|
|
4114
|
+
${chalk11.dim("$")} mutagent providers mirror --json ${chalk11.dim("Show the plan without sending anything")}
|
|
4121
4115
|
${chalk11.dim("$")} mutagent providers mirror --yes --json
|
|
4122
4116
|
${chalk11.dim("$")} mutagent providers mirror --provider zai --provider openrouter
|
|
4123
|
-
${chalk11.dim("$")} mutagent providers mirror --no-overwrite
|
|
4117
|
+
${chalk11.dim("$")} mutagent providers mirror --no-overwrite
|
|
4124
4118
|
${chalk11.dim("$")} mutagent providers mirror --channel rc
|
|
4125
4119
|
|
|
4126
|
-
Reads the
|
|
4127
|
-
|
|
4128
|
-
providers in
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
In --json, each written result carries "endpoint" {from, to} (a URL or
|
|
4154
|
-
"default") and, for Custom providers, "apiFormat" {from, to}; the refusal
|
|
4155
|
-
without --yes carries the same per-entry "plan". Names and URLs only.
|
|
4120
|
+
Reads the API keys in your local Helix login store (~/.mutagent/agent, ~/.mutagent/agent-rc
|
|
4121
|
+
with --channel rc, or $HELIX_CODING_AGENT_DIR when set) and creates or updates the
|
|
4122
|
+
matching LLM providers in the workspace. OAuth logins and keys stored as references
|
|
4123
|
+
($VAR, \${VAR}, !command or a bare ENV_NAME) are listed and skipped; a reference is never
|
|
4124
|
+
evaluated. Exception: an openrouter login that holds a permanent key is copied as an
|
|
4125
|
+
API key. Before anything is sent, the plan shows every create and update, endpoint
|
|
4126
|
+
changes and the API format a custom LLM provider is written with.
|
|
4127
|
+
|
|
4128
|
+
anthropic, openai, google, xai and deepseek become LLM providers of the same type.
|
|
4129
|
+
zai, zai-coding-cn, moonshotai, moonshotai-cn, kimi-coding, openrouter and groq become
|
|
4130
|
+
custom LLM providers. Declared model lists are copied. An entry with headers or other
|
|
4131
|
+
fields that cannot be copied is skipped.
|
|
4132
|
+
|
|
4133
|
+
Workspace default model: when the workspace has none, the server sets one from the
|
|
4134
|
+
first LLM provider this run creates, and the output names it. An existing workspace
|
|
4135
|
+
default model is never changed. Change it with
|
|
4136
|
+
\`mutagent helix models default <provider/model>\`.
|
|
4137
|
+
|
|
4138
|
+
Mirroring does not test the keys: check \`mutagent helix models\`, then run a model.
|
|
4139
|
+
|
|
4140
|
+
Requires \`mutagent login\` and a workspace. Keys are sent only to the configured
|
|
4141
|
+
endpoint and are never printed. Consent is asked once per run; without a terminal,
|
|
4142
|
+
--yes is required. Without it the run exits 2 (with --json it returns the plan).
|
|
4143
|
+
Exit 3: not logged in or no workspace. Exit 1: at least one entry failed.
|
|
4144
|
+
|
|
4145
|
+
JSON: each written result carries "endpoint" {from, to} and, for custom LLM providers,
|
|
4146
|
+
"apiFormat" {from, to}; workspaceDefaultModelSet names a default the run set.
|
|
4156
4147
|
|
|
4157
4148
|
AI Agent Directive:
|
|
4158
4149
|
This command sends credentials off the machine. Run it with --json first
|
|
@@ -4281,24 +4272,20 @@ function validateProviderType(type) {
|
|
|
4281
4272
|
function createProvidersCommand() {
|
|
4282
4273
|
const providers = new Command5("providers").description("Manage LLM provider configurations (BYOK)").addHelpText("after", `
|
|
4283
4274
|
Examples:
|
|
4275
|
+
${chalk12.dim("$")} mutagent providers mirror
|
|
4284
4276
|
${chalk12.dim("$")} mutagent providers list
|
|
4285
4277
|
${chalk12.dim("$")} mutagent providers get <provider-id>
|
|
4286
4278
|
${chalk12.dim("$")} mutagent providers add --provider openai --name "My OpenAI" --api-key $KEY
|
|
4287
|
-
${chalk12.dim("$")} mutagent providers update <id> --name "New Name"
|
|
4288
|
-
${chalk12.dim("$")} mutagent providers delete <id> --force
|
|
4289
4279
|
${chalk12.dim("$")} mutagent providers test <provider-id>
|
|
4290
|
-
${chalk12.dim("$")} mutagent providers
|
|
4291
|
-
|
|
4292
|
-
Provider Types:
|
|
4293
|
-
${VALID_PROVIDER_TYPES.join(", ")}
|
|
4280
|
+
${chalk12.dim("$")} mutagent providers update <provider-id> --name "New Name"
|
|
4281
|
+
${chalk12.dim("$")} mutagent providers delete <provider-id> --force
|
|
4294
4282
|
|
|
4295
|
-
|
|
4296
|
-
|
|
4283
|
+
LLM providers hold the keys Helix uses to call models in the cloud. See what they
|
|
4284
|
+
unlock with \`mutagent helix models\`.
|
|
4297
4285
|
|
|
4298
|
-
|
|
4299
|
-
Use --models on list to see available models per provider.
|
|
4286
|
+
LLM provider types: ${VALID_PROVIDER_TYPES.join(", ")}
|
|
4300
4287
|
`);
|
|
4301
|
-
providers.command("list").description("List
|
|
4288
|
+
providers.command("list").description("List the workspace's LLM providers").option("-l, --limit <n>", "Limit results", "50").option("-o, --offset <n>", "Offset for pagination").option("-t, --type <type>", "Filter by LLM provider type").option("-m, --models", "Show catalogue models for each LLM provider type (for Helix cloud models: mutagent helix models)").addHelpText("after", `
|
|
4302
4289
|
Examples:
|
|
4303
4290
|
${chalk12.dim("$")} mutagent providers list
|
|
4304
4291
|
${chalk12.dim("$")} mutagent providers list --type openai
|
|
@@ -4377,7 +4364,7 @@ Examples:
|
|
|
4377
4364
|
handleError(error, isJson);
|
|
4378
4365
|
}
|
|
4379
4366
|
});
|
|
4380
|
-
providers.command("get").description("
|
|
4367
|
+
providers.command("get").description("Show one LLM provider").argument("<id>", "LLM provider ID (from: mutagent providers list)").addHelpText("after", `
|
|
4381
4368
|
Examples:
|
|
4382
4369
|
${chalk12.dim("$")} mutagent providers get <provider-id>
|
|
4383
4370
|
${chalk12.dim("$")} mutagent providers get <provider-id> --json
|
|
@@ -4407,12 +4394,12 @@ Examples:
|
|
|
4407
4394
|
handleError(error, isJson);
|
|
4408
4395
|
}
|
|
4409
4396
|
});
|
|
4410
|
-
providers.command("test").description("Test provider
|
|
4397
|
+
providers.command("test").description("Test an LLM provider's connection").argument("<id>", "LLM provider ID (from: mutagent providers list)").addHelpText("after", `
|
|
4411
4398
|
Examples:
|
|
4412
4399
|
${chalk12.dim("$")} mutagent providers test <provider-id>
|
|
4413
4400
|
${chalk12.dim("$")} mutagent providers test <provider-id> --json
|
|
4414
4401
|
|
|
4415
|
-
${chalk12.dim("Tests
|
|
4402
|
+
${chalk12.dim("Tests the connection and lists the models the LLM provider reports.")}
|
|
4416
4403
|
`).action(async (id) => {
|
|
4417
4404
|
const isJson = getJsonFlag(providers);
|
|
4418
4405
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -5281,13 +5268,14 @@ that teaches coding agents how to use the Mutagent CLI effectively.
|
|
|
5281
5268
|
// src/commands/init.ts
|
|
5282
5269
|
var LOGIN_GUIDANCE = "Run: mutagent login — sign-up happens in the browser. Then re-run mutagent init.";
|
|
5283
5270
|
function createInitCommand(deps = {}) {
|
|
5284
|
-
const init = new Command7("init").description("Initialize Mutagent in your project").option("--non-interactive", "
|
|
5271
|
+
const init = new Command7("init").description("Initialize Mutagent in your project").option("--non-interactive", "Has no effect: init never prompts").addHelpText("after", `
|
|
5285
5272
|
Examples:
|
|
5286
5273
|
${chalk14.dim("$")} mutagent init # Write .mutagentrc.json + install the CLI skill
|
|
5287
5274
|
|
|
5288
5275
|
What it does:
|
|
5289
5276
|
1. Writes .mutagentrc.json (endpoint / workspace / org) — skipped if present
|
|
5290
5277
|
2. Installs the Mutagent CLI skill into ${SKILL_DIR}/
|
|
5278
|
+
It never prompts. Without a sign-in it prints what to run and exits.
|
|
5291
5279
|
`).action(async (options) => {
|
|
5292
5280
|
const isJson = getJsonFlag(init);
|
|
5293
5281
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -5405,7 +5393,7 @@ init_errors();
|
|
|
5405
5393
|
init_sdk_client();
|
|
5406
5394
|
var PROVIDERS_URL = "https://app.mutagent.io/settings/providers";
|
|
5407
5395
|
function createUsageCommand() {
|
|
5408
|
-
const usage = new Command8("usage").description("
|
|
5396
|
+
const usage = new Command8("usage").description("Count the workspace's LLM providers and the workspaces you can see").addHelpText("after", `
|
|
5409
5397
|
Examples:
|
|
5410
5398
|
${chalk15.dim("$")} mutagent usage
|
|
5411
5399
|
${chalk15.dim("$")} mutagent usage --json
|
|
@@ -5463,6 +5451,7 @@ Examples:
|
|
|
5463
5451
|
import { readFileSync as readFileSync9 } from "node:fs";
|
|
5464
5452
|
import { basename } from "node:path";
|
|
5465
5453
|
import { Command as Command9 } from "commander";
|
|
5454
|
+
import chalk16 from "chalk";
|
|
5466
5455
|
|
|
5467
5456
|
// src/commands/hooks/import-transcript.ts
|
|
5468
5457
|
var MAX_SPAN_TEXT = 4000;
|
|
@@ -6372,32 +6361,17 @@ function installHooks(cwd) {
|
|
|
6372
6361
|
// src/commands/hooks/index.ts
|
|
6373
6362
|
function createHooksCommand() {
|
|
6374
6363
|
const hooks = new Command9("hooks").description("Hook handlers for AI coding assistants").addHelpText("after", `
|
|
6375
|
-
Claude Code
|
|
6376
|
-
Sends
|
|
6364
|
+
Claude Code session telemetry:
|
|
6365
|
+
Sends session activity (turns, tool calls, subagents, compactions) to the Mutagent
|
|
6366
|
+
traces API.
|
|
6377
6367
|
|
|
6378
|
-
|
|
6368
|
+
${chalk16.dim("$")} mutagent hooks install ${chalk16.dim("Add the Mutagent hooks to .claude/settings.local.json (11 events)")}
|
|
6379
6369
|
|
|
6380
|
-
|
|
6381
|
-
"hooks": {
|
|
6382
|
-
"SessionStart": [{"matcher": "startup", "hooks": [{"type": "command", "command": "mutagent hooks claude-code session-start"}]}],
|
|
6383
|
-
"Stop": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code session-end"}]}],
|
|
6384
|
-
"PreToolUse": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code pre-tool-use"}]}],
|
|
6385
|
-
"PostToolUse": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code post-tool-use"}]}],
|
|
6386
|
-
"UserPromptSubmit": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code user-prompt-submit"}]}],
|
|
6387
|
-
"SubagentStart": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code subagent-start"}]}],
|
|
6388
|
-
"SubagentStop": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code subagent-stop"}]}],
|
|
6389
|
-
"PreCompact": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code pre-compact"}]}],
|
|
6390
|
-
"PostCompact": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code post-compact"}]}],
|
|
6391
|
-
"PostToolUseFailure": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code post-tool-use-failure"}]}]
|
|
6392
|
-
}
|
|
6393
|
-
}
|
|
6394
|
-
|
|
6395
|
-
Or run: mutagent hooks install
|
|
6370
|
+
The claude-code subcommands are what those hooks run; Claude Code calls them.
|
|
6396
6371
|
`);
|
|
6397
|
-
hooks.command("import").argument("<files...>", "Session transcript files (.jsonl)").description("
|
|
6398
|
-
|
|
6399
|
-
live.
|
|
6400
|
-
ships them to the backend instead.
|
|
6372
|
+
hooks.command("import").argument("<files...>", "Session transcript files (.jsonl)").description("Upload Helix session transcripts as traces").option("--max-spans <n>", "Cap spans per session", "200").option("--dry-run", "Convert and report, but do not send").addHelpText("after", `
|
|
6373
|
+
Uploads Helix session transcripts that are already on disk, instead of capturing
|
|
6374
|
+
them live.
|
|
6401
6375
|
|
|
6402
6376
|
Where they live:
|
|
6403
6377
|
~/.mutagent/sessions* Helix
|
|
@@ -6494,11 +6468,16 @@ unparseable lines without sending anything.
|
|
|
6494
6468
|
`);
|
|
6495
6469
|
emit(batch.length, false);
|
|
6496
6470
|
});
|
|
6497
|
-
hooks.command("install").description("
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6471
|
+
hooks.command("install").description("Add the Mutagent hooks to .claude/settings.local.json (merges; existing hooks stay)").option("--cwd <dir>", "Target directory", process.cwd()).addHelpText("after", `
|
|
6472
|
+
Examples:
|
|
6473
|
+
${chalk16.dim("$")} mutagent hooks install
|
|
6474
|
+
${chalk16.dim("$")} mutagent hooks install --cwd ../other-project
|
|
6475
|
+
|
|
6476
|
+
Merges the Mutagent hooks into all 11 Claude Code events of
|
|
6477
|
+
.claude/settings.local.json (created if missing). Hooks already present are left
|
|
6478
|
+
alone. An old Stop → claude-code session-end entry is removed.
|
|
6479
|
+
Stop runs 'claude-code stop' and SessionEnd runs 'claude-code session-end'.
|
|
6480
|
+
Safe to run again.
|
|
6502
6481
|
`).action((opts) => {
|
|
6503
6482
|
const targetDir = opts.cwd ?? process.cwd();
|
|
6504
6483
|
const isJson = Boolean(opts.json);
|
|
@@ -6597,7 +6576,7 @@ times is safe.
|
|
|
6597
6576
|
|
|
6598
6577
|
// src/commands/feedback.ts
|
|
6599
6578
|
import { Command as Command10 } from "commander";
|
|
6600
|
-
import
|
|
6579
|
+
import chalk19 from "chalk";
|
|
6601
6580
|
import { type as osType, release as osRelease } from "os";
|
|
6602
6581
|
import { readFileSync as readFileSync11 } from "fs";
|
|
6603
6582
|
import { join as join13, dirname as dirname2 } from "path";
|
|
@@ -6618,7 +6597,7 @@ import {
|
|
|
6618
6597
|
readSync,
|
|
6619
6598
|
closeSync
|
|
6620
6599
|
} from "fs";
|
|
6621
|
-
import
|
|
6600
|
+
import chalk17 from "chalk";
|
|
6622
6601
|
var TAIL_BYTES = 200000;
|
|
6623
6602
|
function defaultReadTail(path, tailBytes) {
|
|
6624
6603
|
const { size } = fsStatSync(path);
|
|
@@ -6694,7 +6673,7 @@ Or omit the path to auto-detect the newest coding-agent session.`);
|
|
|
6694
6673
|
}
|
|
6695
6674
|
}
|
|
6696
6675
|
if (!newest) {
|
|
6697
|
-
warn(
|
|
6676
|
+
warn(chalk17.yellow("⚠ No coding-agent transcript found; sending feedback without one."));
|
|
6698
6677
|
return null;
|
|
6699
6678
|
}
|
|
6700
6679
|
const { content, truncated } = readTail(newest.path, TAIL_BYTES);
|
|
@@ -6708,7 +6687,7 @@ Or omit the path to auto-detect the newest coding-agent session.`);
|
|
|
6708
6687
|
}
|
|
6709
6688
|
|
|
6710
6689
|
// src/lib/feedback-safety.ts
|
|
6711
|
-
import
|
|
6690
|
+
import chalk18 from "chalk";
|
|
6712
6691
|
|
|
6713
6692
|
// src/lib/secret-scan.ts
|
|
6714
6693
|
var PATTERNS = [
|
|
@@ -6903,9 +6882,9 @@ function buildPreview(transcript) {
|
|
|
6903
6882
|
function buildTranscriptWarning(preview) {
|
|
6904
6883
|
const kb = (preview.bytes / 1024).toFixed(1);
|
|
6905
6884
|
return [
|
|
6906
|
-
|
|
6907
|
-
` ${
|
|
6908
|
-
` ${
|
|
6885
|
+
chalk18.yellow("⚠ About to upload your coding-agent session transcript."),
|
|
6886
|
+
` ${chalk18.bold("Source:")} ${preview.sourcePath}`,
|
|
6887
|
+
` ${chalk18.bold("Sending:")} ${String(preview.fileCount)} file, ${kb} KB${preview.truncated ? " (tail only)" : ""}`,
|
|
6909
6888
|
"",
|
|
6910
6889
|
" A session transcript typically contains:",
|
|
6911
6890
|
" • source code and file contents you or the agent opened",
|
|
@@ -6914,9 +6893,9 @@ function buildTranscriptWarning(preview) {
|
|
|
6914
6893
|
" • internal hostnames, service names, and URLs",
|
|
6915
6894
|
" • business context from your prompts and the agent replies",
|
|
6916
6895
|
"",
|
|
6917
|
-
` ${
|
|
6918
|
-
` ${
|
|
6919
|
-
` ${
|
|
6896
|
+
` ${chalk18.dim("Credentials are scanned for and redacted automatically, but detection")}`,
|
|
6897
|
+
` ${chalk18.dim("is best-effort — review the source file if the session touched secrets.")}`,
|
|
6898
|
+
` ${chalk18.dim("Omit --attach-transcript to send feedback without it.")}`
|
|
6920
6899
|
].join(`
|
|
6921
6900
|
`);
|
|
6922
6901
|
}
|
|
@@ -6928,12 +6907,12 @@ function buildRedactionReport(redactions) {
|
|
|
6928
6907
|
byKind.set(match.label, (byKind.get(match.label) ?? 0) + 1);
|
|
6929
6908
|
}
|
|
6930
6909
|
const lines = [
|
|
6931
|
-
|
|
6910
|
+
chalk18.green(`✓ Redacted ${String(redactions.length)} credential${redactions.length === 1 ? "" : "s"} from the transcript before upload:`)
|
|
6932
6911
|
];
|
|
6933
6912
|
for (const [label, count] of byKind) {
|
|
6934
6913
|
lines.push(` • ${label} ×${String(count)}`);
|
|
6935
6914
|
}
|
|
6936
|
-
lines.push(
|
|
6915
|
+
lines.push(chalk18.dim(" These were replaced with [REDACTED:…] markers and never left your machine."));
|
|
6937
6916
|
return lines.join(`
|
|
6938
6917
|
`);
|
|
6939
6918
|
}
|
|
@@ -6941,17 +6920,17 @@ function buildByDesignNotice(entries) {
|
|
|
6941
6920
|
if (entries.length === 0)
|
|
6942
6921
|
return "";
|
|
6943
6922
|
const lines = [
|
|
6944
|
-
|
|
6923
|
+
chalk18.cyan("ℹ Heads up — this may describe intended behaviour:"),
|
|
6945
6924
|
""
|
|
6946
6925
|
];
|
|
6947
6926
|
for (const entry of entries) {
|
|
6948
|
-
lines.push(` ${
|
|
6927
|
+
lines.push(` ${chalk18.bold(entry.title)}`);
|
|
6949
6928
|
for (const line of wrap(entry.reality, 76)) {
|
|
6950
6929
|
lines.push(` ${line}`);
|
|
6951
6930
|
}
|
|
6952
6931
|
lines.push("");
|
|
6953
6932
|
}
|
|
6954
|
-
lines.push(
|
|
6933
|
+
lines.push(chalk18.dim(" Sending anyway — if this is a regression rather than the design,"), chalk18.dim(" your report is exactly what we need. Nothing was withheld."));
|
|
6955
6934
|
return lines.join(`
|
|
6956
6935
|
`);
|
|
6957
6936
|
}
|
|
@@ -7062,18 +7041,18 @@ function buildPayload(feedback, category, title, session, transcript) {
|
|
|
7062
7041
|
}
|
|
7063
7042
|
function createFeedbackCommand() {
|
|
7064
7043
|
const feedback = new Command10("feedback").description("Send product feedback to Mutagent").addHelpText("after", `
|
|
7065
|
-
${
|
|
7066
|
-
${
|
|
7067
|
-
${
|
|
7068
|
-
${
|
|
7044
|
+
${chalk19.bold("Examples:")}
|
|
7045
|
+
${chalk19.cyan('mutagent feedback send "helix session attach was easy to follow"')}
|
|
7046
|
+
${chalk19.cyan('mutagent feedback send "Eval gate was confusing" --category stage:evaluate')}
|
|
7047
|
+
${chalk19.cyan('mutagent feedback send "CLI crashed on export" --category cli --json')}
|
|
7069
7048
|
|
|
7070
|
-
|
|
7049
|
+
AI Agent Directive:
|
|
7071
7050
|
ALWAYS use --json: mutagent feedback send "..." --category cli --json
|
|
7072
|
-
${
|
|
7051
|
+
${chalk19.yellow("Do NOT attach a transcript by default.")} --attach-transcript uploads the
|
|
7073
7052
|
coding-agent SESSION JSONL — source code, absolute paths, branch and repository
|
|
7074
7053
|
names, internal hostnames, and the business context of the operator's prompts.
|
|
7075
7054
|
Use it only when the operator explicitly asks for it. See:
|
|
7076
|
-
${
|
|
7055
|
+
${chalk19.dim("mutagent feedback send --help")}
|
|
7077
7056
|
`).action(() => {
|
|
7078
7057
|
feedback.help();
|
|
7079
7058
|
});
|
|
@@ -7081,42 +7060,34 @@ ${chalk18.yellow("AI Agent (MANDATORY):")}
|
|
|
7081
7060
|
return feedback;
|
|
7082
7061
|
}
|
|
7083
7062
|
function registerFeedbackSend(feedback) {
|
|
7084
|
-
feedback.command("send").description("Send product feedback about the Mutagent platform or CLI").argument("<feedback>", "The feedback body (content), max 10000 characters").option("--title <string>", "
|
|
7085
|
-
${
|
|
7086
|
-
${
|
|
7087
|
-
${
|
|
7088
|
-
${
|
|
7089
|
-
|
|
7090
|
-
${
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
${
|
|
7095
|
-
${
|
|
7096
|
-
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
${
|
|
7103
|
-
${
|
|
7104
|
-
${
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
${chalk18.yellow("username), branch and repository names, internal hostnames, and the business")}
|
|
7113
|
-
${chalk18.yellow("context of your prompts.")} Credentials are scanned for and redacted before
|
|
7114
|
-
upload, and the exact size + source path are printed before the network call.
|
|
7115
|
-
Detection is best-effort — omit the flag if the session handled secrets.
|
|
7116
|
-
|
|
7117
|
-
${chalk18.yellow("AI Agent (MANDATORY):")}
|
|
7118
|
-
ALWAYS use --json: mutagent feedback send "..." --json
|
|
7119
|
-
Auto-captured context (harness, CLI version, platform, OS, node version) is included automatically.
|
|
7063
|
+
feedback.command("send").description("Send product feedback about the Mutagent platform or CLI").argument("<feedback>", "The feedback body (content), max 10000 characters").option("--title <string>", "Short title").option("--category <value>", `Feedback category: 'cli', 'helix', or 'stage:<${VALID_STAGES.join("|")}>'`, "cli").option("--session <id>", "Link the feedback to a session").option("--attach-transcript [path]", "Attach the coding-agent session JSONL. Bare = auto-detect newest; or pass an explicit path.").addHelpText("after", `
|
|
7064
|
+
${chalk19.bold("Examples:")}
|
|
7065
|
+
${chalk19.dim("$")} mutagent feedback send "The setup flow could show progress better"
|
|
7066
|
+
${chalk19.dim("$")} mutagent feedback send "Eval gate was confusing" --category stage:evaluate --title "eval gate unclear"
|
|
7067
|
+
${chalk19.dim("$")} mutagent feedback send "Diagnose loop stalled" --category stage:diagnose --session sess_abc123 --json
|
|
7068
|
+
${chalk19.dim("$")} mutagent feedback send "CLI crashed mid-run" --category cli --attach-transcript --json
|
|
7069
|
+
${chalk19.dim("$")} mutagent feedback send "Repro attached" --category cli --attach-transcript /tmp/session.jsonl
|
|
7070
|
+
|
|
7071
|
+
${chalk19.bold("Category:")}
|
|
7072
|
+
${chalk19.bold("cli")} Feedback about the CLI itself (default)
|
|
7073
|
+
${chalk19.bold("helix")} Feedback about Helix
|
|
7074
|
+
${chalk19.bold("stage:<x>")} Feedback about a lifecycle stage: ${VALID_STAGES.join(", ")}
|
|
7075
|
+
|
|
7076
|
+
${chalk19.bold("Transcript (--attach-transcript):")}
|
|
7077
|
+
Uploads the last 200 KB of a coding-agent session transcript, separately from the
|
|
7078
|
+
feedback text. Without a path it picks the newest session from Claude Code, Codex
|
|
7079
|
+
or omp; a path picks that file.
|
|
7080
|
+
|
|
7081
|
+
${chalk19.yellow("A session transcript contains source code, absolute paths (including your")}
|
|
7082
|
+
${chalk19.yellow("username), branch and repository names, internal hostnames, and the business")}
|
|
7083
|
+
${chalk19.yellow("context of your prompts.")} Credentials are scanned for and redacted before
|
|
7084
|
+
upload, and the exact size and source path are printed before anything is sent.
|
|
7085
|
+
Detection is best-effort: omit the flag if the session handled secrets.
|
|
7086
|
+
|
|
7087
|
+
AI Agent Directive:
|
|
7088
|
+
Use --json: mutagent feedback send "..." --json
|
|
7089
|
+
Do not pass --attach-transcript unless the user explicitly asks for it.
|
|
7090
|
+
Every report includes the CLI version, platform, OS and Node version; the coding agent's name is added when a transcript is attached.
|
|
7120
7091
|
`).action(async (feedbackBody, options) => {
|
|
7121
7092
|
const isJson = getJsonFlag(feedback);
|
|
7122
7093
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -7190,34 +7161,19 @@ Or set an API key: mutagent config set apiKey <key>`);
|
|
|
7190
7161
|
|
|
7191
7162
|
// src/commands/install/index.ts
|
|
7192
7163
|
import { Command as Command11 } from "commander";
|
|
7193
|
-
import
|
|
7164
|
+
import chalk20 from "chalk";
|
|
7194
7165
|
init_errors();
|
|
7195
7166
|
function createInstallCommand(deps) {
|
|
7196
|
-
const install = new Command11("install").description("Install a Mutagent package (helix)").argument("<package>", `Package to install: ${VALID_PACKAGES.join(", ")}`).option("--harness <harness>", `
|
|
7197
|
-
${
|
|
7198
|
-
${
|
|
7199
|
-
${
|
|
7200
|
-
${
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
${chalk19.bold("Examples:")}
|
|
7208
|
-
${chalk19.dim("$")} mutagent install helix
|
|
7209
|
-
${chalk19.dim("$")} mutagent install helix --harness codex
|
|
7210
|
-
${chalk19.dim("$")} mutagent install helix --version 1.2.3
|
|
7211
|
-
${chalk19.dim("$")} mutagent install helix --json
|
|
7212
|
-
|
|
7213
|
-
${chalk19.bold("Packages:")}
|
|
7214
|
-
${chalk19.bold("helix")} ${chalk19.green("(available)")} — the ADLC (Agent Development Life Cycle Orchestrator). Downloaded from a private
|
|
7215
|
-
registry via a login-brokered signed URL, sha256-verified, then
|
|
7216
|
-
initialized into your project. No static secret ships in the CLI.
|
|
7217
|
-
${chalk19.dim("Installs every harness itself (Claude + Codex) — --harness does not apply.")}
|
|
7218
|
-
${chalk19.dim("Carries the diagnostics and evaluator skills, plus the helix-cli binary.")}
|
|
7219
|
-
|
|
7220
|
-
${chalk19.yellow("Note:")} install is login-gated. Run ${chalk19.cyan("mutagent login")} first (else exits with a login directive).
|
|
7167
|
+
const install = new Command11("install").description("Install a Mutagent package on this machine (helix)").argument("<package>", `Package to install: ${VALID_PACKAGES.join(", ")}`).option("--harness <harness>", `Harness named in the output and the install report: ${VALID_HARNESSES.join(", ")}`, "claude-code").option("--global", "Refused: helix always installs into the current directory").option("--version <version>", "Package version to install (default: latest)").addHelpText("after", `
|
|
7168
|
+
${chalk20.bold("Examples:")}
|
|
7169
|
+
${chalk20.dim("$")} mutagent install helix
|
|
7170
|
+
${chalk20.dim("$")} mutagent install helix --version <version>
|
|
7171
|
+
${chalk20.dim("$")} mutagent install helix --json
|
|
7172
|
+
|
|
7173
|
+
${chalk20.bold("helix")}: the Helix agent-development toolkit, set up in the current directory for
|
|
7174
|
+
Claude Code and Codex (includes the diagnostics and evaluator skills and helix-cli).
|
|
7175
|
+
Requires \`mutagent login\`. This installs Helix locally; \`mutagent helix\` runs it in the cloud.
|
|
7176
|
+
--harness does not change what is installed. --global exits with an error.
|
|
7221
7177
|
`).action(async (pkg, options) => {
|
|
7222
7178
|
const isJson = getJsonFlag(install);
|
|
7223
7179
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -7242,9 +7198,9 @@ ${chalk19.yellow("Note:")} install is login-gated. Run ${chalk19.cyan("mutagent
|
|
|
7242
7198
|
output.success(`Installed ${result.package}@${result.version} for ${result.harness} in ${result.projectPath ?? "this project"}`);
|
|
7243
7199
|
} else {
|
|
7244
7200
|
output.success(`Installed ${result.package}@${result.version}, but the skill is not in place yet`);
|
|
7245
|
-
console.log(`${
|
|
7201
|
+
console.log(`${chalk20.yellow("⚠")} No skill was installed yet — a setup step still has to run.`);
|
|
7246
7202
|
if (result.nextStep) {
|
|
7247
|
-
console.log(`${
|
|
7203
|
+
console.log(`${chalk20.blue("→")} Run next: ${chalk20.cyan(result.nextStep)}`);
|
|
7248
7204
|
}
|
|
7249
7205
|
}
|
|
7250
7206
|
} catch (error) {
|
|
@@ -7260,13 +7216,12 @@ init_errors();
|
|
|
7260
7216
|
var TRACE_RELOCATION_HINT = "trace lives in the `helix-cli` binary, which ships inside helix (install it with: mutagent install helix)";
|
|
7261
7217
|
function createTraceCommand() {
|
|
7262
7218
|
const trace = new Command12("trace").description("Not in this binary — trace lives in helix-cli").allowUnknownOption(true).allowExcessArguments(true).argument("[args...]", "Arguments intended for `helix-cli trace`").addHelpText("after", `
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
${TRACE_RELOCATION_HINT}
|
|
7266
|
-
|
|
7267
|
-
Install one of those, then run:
|
|
7219
|
+
mutagent does not run trace. trace is a command of helix-cli, which
|
|
7220
|
+
mutagent install helix installs. Run:
|
|
7268
7221
|
|
|
7269
7222
|
helix-cli trace <args>
|
|
7223
|
+
|
|
7224
|
+
This command only prints where trace is and exits with an error.
|
|
7270
7225
|
`).action((args) => {
|
|
7271
7226
|
const isJson = getJsonFlag(trace);
|
|
7272
7227
|
const forwarded = args.length > 0 ? ` ${args.join(" ")}` : " <args>";
|
|
@@ -9083,39 +9038,29 @@ async function runCloudLaunch(plan, mode) {
|
|
|
9083
9038
|
var MISSING_TASK_MESSAGE = 'cloud runs are non-interactive: pass the task with -p "<task>"';
|
|
9084
9039
|
function registerAgentCommand(parent, registration, deps = defaultPlanDeps()) {
|
|
9085
9040
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
9086
|
-
parent.command("agent").description("Run a
|
|
9041
|
+
parent.command("agent").description("Run a Helix agent in the cloud").argument("[argv...]", "Agent definition, then the task and Helix flags").allowUnknownOption(true).allowExcessArguments(true).option("--sandbox <id>", "(internal) Run in an existing sandbox, by sandbox id").option("--preset <name>", "Sandbox preset (not with --sandbox)").option("--sandbox-provider <name>", "Run on this configured sandbox provider").option("--env <name>", "Workspace Environment to load for this run (see mutagent env ls)").option("--rpc", "Read JSON commands on stdin; write events on stdout").option("--cwd <path>", "Working directory in the sandbox").addHelpText("after", [
|
|
9087
9042
|
"",
|
|
9088
9043
|
"Examples:",
|
|
9089
9044
|
' mutagent helix agent "You are a test reviewer." -p "review the diff"',
|
|
9090
|
-
' mutagent helix agent --file
|
|
9091
|
-
" mutagent helix agent --file
|
|
9045
|
+
' mutagent helix agent --file reviewer.md --mode json "run the suite"',
|
|
9046
|
+
" mutagent helix agent --file reviewer.md --prime --mode rpc",
|
|
9092
9047
|
' mutagent helix agent --name reviewer --env staging -p "check the deploy"',
|
|
9093
9048
|
"",
|
|
9094
|
-
"
|
|
9095
|
-
" The
|
|
9096
|
-
"
|
|
9097
|
-
" --file
|
|
9098
|
-
" --name
|
|
9099
|
-
"
|
|
9100
|
-
"
|
|
9101
|
-
" Package deployment and native brief execution complement each other on the same runtime.",
|
|
9102
|
-
" --prime preserves the agent definition and selects the native Prime scaffold.",
|
|
9049
|
+
"Definition and task:",
|
|
9050
|
+
" The first argument (or --prompt, --file, --name) defines the agent; -p gives the task.",
|
|
9051
|
+
" With --mode json, a later positional is the task (JSON-mode positional).",
|
|
9052
|
+
" --file is read on this machine and its body is sent as the definition; tools, skills and assets its frontmatter names are not installed.",
|
|
9053
|
+
" --name looks in your local agents first, then the ones built into Helix.",
|
|
9054
|
+
" Flags that point at other files on this machine are refused, with the reason.",
|
|
9055
|
+
" --prime keeps the definition and runs it on the lean Prime agent.",
|
|
9103
9056
|
"",
|
|
9104
9057
|
"Modes:",
|
|
9105
|
-
" -p
|
|
9106
|
-
" --mode json
|
|
9107
|
-
"
|
|
9108
|
-
"
|
|
9109
|
-
"
|
|
9110
|
-
"
|
|
9111
|
-
" Native session disconnect detaches; box retention follows the server idle policy.",
|
|
9112
|
-
" Ctrl-C on a native launch sends SIGINT to that execution and reports its exit status.",
|
|
9113
|
-
"",
|
|
9114
|
-
"Placement:",
|
|
9115
|
-
" The server selects the configured preset/destination; --preset overrides the preset.",
|
|
9116
|
-
" --sandbox-provider <name> places a fresh-box launch on that configured provider.",
|
|
9117
|
-
" --env selects a workspace Environment, independently of LLM --provider/--model.",
|
|
9118
|
-
" --sandbox selects an existing internal test box.",
|
|
9058
|
+
" -p runs the whole model and tool loop for this run and prints the final answer.",
|
|
9059
|
+
" --mode json streams events as JSON lines; --mode rpc reads JSON commands on stdin.",
|
|
9060
|
+
" With --mode rpc, closing stdin ends the session's input; a dropped connection only detaches.",
|
|
9061
|
+
" Output is Helix's own, even with --json.",
|
|
9062
|
+
" Ctrl-C on a native launch sends SIGINT to the session, which stops it; the exit status is reported.",
|
|
9063
|
+
" A sandbox idle for 15 minutes is checkpointed and stopped.",
|
|
9119
9064
|
"",
|
|
9120
9065
|
"AI Agent Directive:",
|
|
9121
9066
|
" Preserve the definition/task distinction and select an explicit headless mode.",
|
|
@@ -9331,7 +9276,7 @@ function planLauncherArgv(argv, deps = defaultPlanDeps()) {
|
|
|
9331
9276
|
|
|
9332
9277
|
// src/commands/helix/session-commands.ts
|
|
9333
9278
|
import { Command as Command13 } from "commander";
|
|
9334
|
-
import
|
|
9279
|
+
import chalk26 from "chalk";
|
|
9335
9280
|
init_errors();
|
|
9336
9281
|
|
|
9337
9282
|
// src/lib/sandbox-checkpoints.ts
|
|
@@ -9357,7 +9302,7 @@ async function restoreCheckpoint(id, body) {
|
|
|
9357
9302
|
}
|
|
9358
9303
|
|
|
9359
9304
|
// src/lib/sandbox-session.ts
|
|
9360
|
-
import
|
|
9305
|
+
import chalk22 from "chalk";
|
|
9361
9306
|
|
|
9362
9307
|
// src/lib/tty.ts
|
|
9363
9308
|
function isPlainOutput() {
|
|
@@ -9378,7 +9323,7 @@ init_errors();
|
|
|
9378
9323
|
init_known_secrets();
|
|
9379
9324
|
|
|
9380
9325
|
// src/lib/sandbox-frames.ts
|
|
9381
|
-
import
|
|
9326
|
+
import chalk21 from "chalk";
|
|
9382
9327
|
var SILENT2 = new Set([
|
|
9383
9328
|
"message_start",
|
|
9384
9329
|
"message_update",
|
|
@@ -9470,7 +9415,7 @@ class FrameFeed {
|
|
|
9470
9415
|
const arg = started?.arg ?? identifyingArg(frame.toolName, frame.args);
|
|
9471
9416
|
const failed = isFailure(frame.result);
|
|
9472
9417
|
const elapsed = started === undefined ? "" : formatDuration(this.#now() - started.startedAt);
|
|
9473
|
-
const name = this.#plain ? toolName : failed ?
|
|
9418
|
+
const name = this.#plain ? toolName : failed ? chalk21.red(toolName) : chalk21.cyan(toolName);
|
|
9474
9419
|
const visible = arg === "" ? toolName.padEnd(TOOL_COL) : `${toolName.padEnd(TOOL_COL)} ${arg}`;
|
|
9475
9420
|
const head = arg === "" ? pad(name, toolName, TOOL_COL) : `${pad(name, toolName, TOOL_COL)} ${arg}`;
|
|
9476
9421
|
const gap = " ".repeat(Math.max(2, DURATION_COL - visible.length));
|
|
@@ -9487,7 +9432,7 @@ class FrameFeed {
|
|
|
9487
9432
|
const method = typeof frame.method === "string" ? frame.method : "request";
|
|
9488
9433
|
const key = typeof frame.statusKey === "string" ? ` ${frame.statusKey}` : "";
|
|
9489
9434
|
const body = `hitl ${method}${key}`;
|
|
9490
|
-
return this.#line(this.#sym("◆", "?"), this.#plain ? body :
|
|
9435
|
+
return this.#line(this.#sym("◆", "?"), this.#plain ? body : chalk21.yellow(body));
|
|
9491
9436
|
}
|
|
9492
9437
|
#response(frame) {
|
|
9493
9438
|
const command = typeof frame.command === "string" ? frame.command : "command";
|
|
@@ -9496,7 +9441,7 @@ class FrameFeed {
|
|
|
9496
9441
|
const body = `${command}${id} ${ok ? "accepted" : "rejected"}`;
|
|
9497
9442
|
if (this.#plain)
|
|
9498
9443
|
return this.#line(ok ? "+" : "x", body);
|
|
9499
|
-
return this.#line(ok ? "✓" : "✗", ok ?
|
|
9444
|
+
return this.#line(ok ? "✓" : "✗", ok ? chalk21.green(body) : chalk21.red(body));
|
|
9500
9445
|
}
|
|
9501
9446
|
#line(symbol, body) {
|
|
9502
9447
|
return ` ${symbol} ${body}`;
|
|
@@ -9505,7 +9450,7 @@ class FrameFeed {
|
|
|
9505
9450
|
return this.#plain ? plain : styled;
|
|
9506
9451
|
}
|
|
9507
9452
|
#dim(text) {
|
|
9508
|
-
return this.#plain ? text :
|
|
9453
|
+
return this.#plain ? text : chalk21.dim(text);
|
|
9509
9454
|
}
|
|
9510
9455
|
#rule(label) {
|
|
9511
9456
|
const dash = this.#plain ? "-" : "─";
|
|
@@ -9595,7 +9540,7 @@ function formatDuration(ms) {
|
|
|
9595
9540
|
|
|
9596
9541
|
// src/lib/sandbox-session.ts
|
|
9597
9542
|
function dim(text) {
|
|
9598
|
-
return isPlainOutput() ? text :
|
|
9543
|
+
return isPlainOutput() ? text : chalk22.dim(text);
|
|
9599
9544
|
}
|
|
9600
9545
|
function reattachHint(id, lastSeq) {
|
|
9601
9546
|
const since = lastSeq > 0 ? ` --since ${String(lastSeq)}` : "";
|
|
@@ -9760,66 +9705,38 @@ function parseSince(raw) {
|
|
|
9760
9705
|
}
|
|
9761
9706
|
|
|
9762
9707
|
// src/commands/helix/restore-command.ts
|
|
9763
|
-
import
|
|
9708
|
+
import chalk23 from "chalk";
|
|
9764
9709
|
init_errors();
|
|
9765
9710
|
var DRIFT_MODES = ["warn", "refuse", "skip"];
|
|
9766
9711
|
function registerRestore(session, root) {
|
|
9767
|
-
session.command("restore").description("
|
|
9712
|
+
session.command("restore").description("(internal) Continue a session from a checkpoint in a rebuilt sandbox, by sandbox id").argument("<sandbox-id>", "Sandbox the checkpoint came from (it may be stopped)").option("--snapshot <id>", "Checkpoint to use (default: the newest complete one from this sandbox)").option("--on-drift <mode>", `If files the conversation names are missing: ${DRIFT_MODES.join(" | ")} (default: warn)`).option("--partial", "Also restore turns saved after the checkpoint (may end mid tool call)").addHelpText("after", `
|
|
9768
9713
|
Examples:
|
|
9769
|
-
${
|
|
9770
|
-
${
|
|
9771
|
-
${
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
9786
|
-
5. the conversation is written in, its sha256 checked, and only then switched
|
|
9787
|
-
onto — a mismatch is a 422 and the agent is never pointed at it
|
|
9788
|
-
6. the runtime settings and pending queue are replayed
|
|
9789
|
-
7. a 'restored' marker goes on the stream with a real seq
|
|
9790
|
-
|
|
9791
|
-
READ 'notRestored'. Restoring only the conversation is a legitimate outcome — a
|
|
9792
|
-
deployment with no object store, or a checkpoint taken before continuous capture
|
|
9793
|
-
existed — and it must not look identical to a full-state restore. Every leg that
|
|
9794
|
-
did not come back names itself there.
|
|
9795
|
-
|
|
9796
|
-
--on-drift, for files that are STILL missing after the workspace was extracted:
|
|
9797
|
-
${chalk22.bold("warn")} (default) restore, and report them in workspaceDrift.
|
|
9798
|
-
${chalk22.bold("refuse")} fail with 409 BEFORE switching.
|
|
9799
|
-
${chalk22.bold("skip")} do not probe at all; status comes back 'unchecked'.
|
|
9800
|
-
|
|
9801
|
-
--partial restores the incremental transcript prefix captured AFTER the last
|
|
9802
|
-
consistency point — the turns a mid-turn kill would otherwise lose — when it
|
|
9803
|
-
extends the checkpoint. ACCEPTED RISK: the prefix is cut at its last complete
|
|
9804
|
-
line and may end on a tool call whose result never landed; the harness answers
|
|
9805
|
-
it with a synthetic error result on the next turn, and any side effect the
|
|
9806
|
-
original call had is not undone. When no prefix exists, or it does not extend
|
|
9807
|
-
the checkpoint, the checkpoint is restored and 'notRestored' says why; 'partial'
|
|
9808
|
-
on the result says which happened.
|
|
9809
|
-
|
|
9810
|
-
A DEFAULTED restore (no --snapshot) may pass over newer checkpoints whose
|
|
9811
|
-
manifest never landed to reach one it can restore in full. Each one skipped is
|
|
9812
|
-
listed in 'skippedSnapshots' with why.
|
|
9714
|
+
${chalk23.dim("$")} mutagent helix session restore sbx_456
|
|
9715
|
+
${chalk23.dim("$")} mutagent helix session restore sbx_456 --snapshot snap_1
|
|
9716
|
+
${chalk23.dim("$")} mutagent helix session restore sbx_456 --on-drift refuse --json
|
|
9717
|
+
|
|
9718
|
+
Rebuilds the sandbox (the old one may be stopped or gone), restores the workspace
|
|
9719
|
+
and settings where the checkpoint has them, and starts a new session that
|
|
9720
|
+
continues the conversation. notRestored lists anything not carried over.
|
|
9721
|
+
Find the new session's reference with 'mutagent helix session ls' (STATUS restored).
|
|
9722
|
+
|
|
9723
|
+
--on-drift, for files still missing after the workspace is restored:
|
|
9724
|
+
warn restore, and list them in workspaceDrift
|
|
9725
|
+
refuse fail before switching to the restored conversation
|
|
9726
|
+
skip do not check
|
|
9727
|
+
--partial may end on a tool call whose result never arrived; Helix answers it
|
|
9728
|
+
with an error on the next turn, and that call's side effects are not undone.
|
|
9729
|
+
Without --snapshot, newer checkpoints that cannot be restored in full are passed
|
|
9730
|
+
over and listed in skippedSnapshots.
|
|
9813
9731
|
|
|
9814
9732
|
AI Agent Directive:
|
|
9815
9733
|
--json returns { success, restored, sandboxId, generation, snapshotId,
|
|
9816
9734
|
sessionId, harnessSessionId, cwd, bytes, sha256, seq, settingsReplayed,
|
|
9817
9735
|
queueReplayed, workspace, workspaceDrift, notRestored, partial, partialBytes,
|
|
9818
|
-
skippedSnapshots }.
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
defaulted restore passed over.
|
|
9736
|
+
skippedSnapshots }. The restore starts a NEW session and returns no reference:
|
|
9737
|
+
find it with 'mutagent helix session ls --json' (status restored) and use that
|
|
9738
|
+
reference for attach, send, signal and checkpoint. Read notRestored before
|
|
9739
|
+
relying on the restored state.
|
|
9823
9740
|
`).action(async (id, options) => {
|
|
9824
9741
|
const isJson = getJsonFlag(root);
|
|
9825
9742
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -9847,21 +9764,21 @@ AI Agent Directive:
|
|
|
9847
9764
|
return;
|
|
9848
9765
|
}
|
|
9849
9766
|
output.success(`Sandbox ${id} rebuilt at generation ${String(result.generation)} from snapshot ${result.snapshotId}`);
|
|
9850
|
-
console.error(
|
|
9767
|
+
console.error(chalk23.dim(` New session ${result.sessionId} · stream continues at seq ${String(result.seq)}`));
|
|
9851
9768
|
if (result.workspace !== null) {
|
|
9852
|
-
console.error(
|
|
9769
|
+
console.error(chalk23.dim(` Workspace: ${String(result.workspace.files)} files into ${result.cwd ?? "the default cwd"}`));
|
|
9853
9770
|
}
|
|
9854
9771
|
if (result.settingsReplayed.length > 0) {
|
|
9855
|
-
console.error(
|
|
9772
|
+
console.error(chalk23.dim(` Replayed: ${result.settingsReplayed.join(", ")}`));
|
|
9856
9773
|
}
|
|
9857
9774
|
if (result.partial) {
|
|
9858
|
-
console.error(
|
|
9775
|
+
console.error(chalk23.dim(` Partial: ${String(result.partialBytes)} bytes beyond the checkpoint`));
|
|
9859
9776
|
}
|
|
9860
9777
|
for (const skipped of result.skippedSnapshots) {
|
|
9861
|
-
console.error(
|
|
9778
|
+
console.error(chalk23.dim(` skipped ${skipped.id}: ${skipped.reason}`));
|
|
9862
9779
|
}
|
|
9863
9780
|
for (const gap of result.notRestored)
|
|
9864
|
-
console.error(
|
|
9781
|
+
console.error(chalk23.yellow(` ! ${gap}`));
|
|
9865
9782
|
} catch (error) {
|
|
9866
9783
|
handleError(error, isJson);
|
|
9867
9784
|
}
|
|
@@ -9890,54 +9807,45 @@ function planSessionStartArgs(argv, deps) {
|
|
|
9890
9807
|
}
|
|
9891
9808
|
|
|
9892
9809
|
// src/commands/helix/send.ts
|
|
9893
|
-
import
|
|
9810
|
+
import chalk24 from "chalk";
|
|
9894
9811
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
9895
9812
|
init_errors();
|
|
9896
9813
|
var COMMAND_TYPES = ["prompt", "steer", "follow_up", "abort"];
|
|
9897
9814
|
var NEEDS_MESSAGE = new Set(["prompt", "steer", "follow_up"]);
|
|
9898
9815
|
function registerSendCommand(parent, registration) {
|
|
9899
9816
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
9900
|
-
parent.command("send").description("Send a command
|
|
9817
|
+
parent.command("send").description("Send a prompt or command to a session").argument("<reference>", "Session reference (hs1_...); with --session, a sandbox id (internal)").argument("[message...]", "What to say to the agent").option("--session <id>", "(internal) The session id, when the first argument is a sandbox id").option("--type <type>", `${COMMAND_TYPES.join(" | ")} (default: prompt)`).option("--line <json>", "Send this exact JSON command instead of building one").option("--message-id <id>", "Use this command id instead of a generated one").addHelpText("after", `
|
|
9901
9818
|
Examples:
|
|
9902
|
-
${
|
|
9903
|
-
${
|
|
9904
|
-
${
|
|
9905
|
-
${
|
|
9906
|
-
${
|
|
9907
|
-
|
|
9908
|
-
|
|
9909
|
-
|
|
9910
|
-
|
|
9911
|
-
|
|
9912
|
-
|
|
9913
|
-
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
printed here. Attach in another terminal to see it:
|
|
9926
|
-
${chalk23.dim("$")} mutagent helix session attach <reference>
|
|
9927
|
-
|
|
9928
|
-
A session that is no longer running is refused rather than silently accepted —
|
|
9929
|
-
'mutagent sandbox ls' shows what is still up.
|
|
9930
|
-
|
|
9931
|
-
For internal box operations, use send <sandbox-id> --session <session-id>.
|
|
9932
|
-
The live reference is execution-scoped, not durable conversation identity.
|
|
9819
|
+
${chalk24.dim("$")} mutagent helix session send <reference> "also check the refresh path"
|
|
9820
|
+
${chalk24.dim("$")} mutagent helix session send <reference> --type steer "stop and re-read the spec"
|
|
9821
|
+
${chalk24.dim("$")} mutagent helix session send <reference> --type abort
|
|
9822
|
+
${chalk24.dim("$")} mutagent helix session send <reference> --line '{"type":"prompt","id":"p7","message":"go"}'
|
|
9823
|
+
${chalk24.dim("$")} mutagent helix session send <reference> "run the tests" --json
|
|
9824
|
+
|
|
9825
|
+
Types:
|
|
9826
|
+
prompt A new instruction to an agent that has finished its turn
|
|
9827
|
+
steer Redirect the turn in progress
|
|
9828
|
+
follow_up Continue from what the agent just said
|
|
9829
|
+
abort Stop the current turn (no message)
|
|
9830
|
+
--line sends one JSON object as written, for a command the four types do not
|
|
9831
|
+
cover. It cannot be combined with --type or a message.
|
|
9832
|
+
|
|
9833
|
+
Only rpc sessions (started with --mode rpc or --rpc) accept input, and a session
|
|
9834
|
+
that has ended refuses it; 'mutagent helix session ls' shows which are live.
|
|
9835
|
+
With a sandbox id and --session, if the sandbox is being stopped for idling, send
|
|
9836
|
+
waits the time the server asks and retries once. If the session's sandbox was stopped for idling,
|
|
9837
|
+
'mutagent helix session ls --json' shows its sandboxId; 'mutagent helix session
|
|
9838
|
+
restore <sandbox-id>' (internal) continues it in a new session.
|
|
9839
|
+
Send does not wait for the reply. The reply arrives on the session's stream as a
|
|
9840
|
+
'response' event carrying the command id (in --json output). Watch it with:
|
|
9841
|
+
${chalk24.dim("$")} mutagent helix session attach <reference>
|
|
9933
9842
|
|
|
9934
9843
|
AI Agent Directive:
|
|
9935
|
-
--json returns ONE object: { success, reference, type, id, line,
|
|
9936
|
-
|
|
9937
|
-
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
the four verbs cannot express.
|
|
9844
|
+
--json returns ONE object: { success, reference, type, id, line, accepted,
|
|
9845
|
+
_links }. 'accepted' means delivered, not answered: the answer is a 'response'
|
|
9846
|
+
event with the same id on 'mutagent helix session attach <reference>'. This
|
|
9847
|
+
command does not wait for it. Use --type and a message; use --line only for a
|
|
9848
|
+
command the four types cannot express.
|
|
9941
9849
|
`).action(async (id, messageParts, options) => {
|
|
9942
9850
|
const isJson = getJsonFlag(jsonSource);
|
|
9943
9851
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -9978,8 +9886,8 @@ AI Agent Directive:
|
|
|
9978
9886
|
return;
|
|
9979
9887
|
}
|
|
9980
9888
|
output.success(`Sent ${built.type} to session ${sessionId}`);
|
|
9981
|
-
console.error(
|
|
9982
|
-
console.error(
|
|
9889
|
+
console.error(chalk24.dim(` ${built.line}`));
|
|
9890
|
+
console.error(chalk24.dim(` The reply arrives on the stream, not here: mutagent sandbox attach ${id}`));
|
|
9983
9891
|
} catch (error) {
|
|
9984
9892
|
handleError(error, isJson);
|
|
9985
9893
|
}
|
|
@@ -10041,56 +9949,33 @@ function readType(raw) {
|
|
|
10041
9949
|
}
|
|
10042
9950
|
|
|
10043
9951
|
// src/commands/helix/signal.ts
|
|
10044
|
-
import
|
|
9952
|
+
import chalk25 from "chalk";
|
|
10045
9953
|
init_errors();
|
|
10046
9954
|
var SIGNAL_TYPES = ["SIGINT", "SIGTERM"];
|
|
10047
9955
|
var DEFAULT_SIGNAL = "SIGINT";
|
|
10048
9956
|
function registerSignalCommand(parent, registration) {
|
|
10049
9957
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
10050
|
-
parent.command("signal").description("Stop a
|
|
9958
|
+
parent.command("signal").description("Stop a session with SIGINT or SIGTERM; its sandbox stays up").argument("<reference>", "Session reference (hs1_...); with --session, a sandbox id (internal)").option("--session <id>", "(internal) The session id, when the argument is a sandbox id").option("--type <signal>", `${SIGNAL_TYPES.join(" | ")} (default: ${DEFAULT_SIGNAL})`).addHelpText("after", `
|
|
10051
9959
|
Examples:
|
|
10052
|
-
${
|
|
10053
|
-
${
|
|
10054
|
-
${
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
|
|
10058
|
-
|
|
10059
|
-
'mutagent
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
The session ends; the box stays.
|
|
10066
|
-
${chalk24.bold("rm")} Destroys the sandbox and everything in it.
|
|
10067
|
-
|
|
10068
|
-
THE SIGNALS:
|
|
10069
|
-
SIGINT Interrupts the current turn, the way Ctrl-C does in a local terminal.
|
|
10070
|
-
SIGTERM Asks the process to shut down.
|
|
10071
|
-
SIGKILL is not accepted: it would end the session before it could flush the
|
|
10072
|
-
output you came back to read.
|
|
10073
|
-
|
|
10074
|
-
A 200 here means a LIVE process was signalled — not just that the request was
|
|
10075
|
-
understood. A session that had already finished is reported as such rather than
|
|
10076
|
-
being counted as a success, so this never tells you that you stopped an agent
|
|
10077
|
-
that had in fact been done for a while.
|
|
10078
|
-
|
|
10079
|
-
'mutagent sandbox ls' shows which sandboxes are up; 'mutagent helix session attach <reference>'
|
|
10080
|
-
shows what the session was doing.
|
|
10081
|
-
|
|
10082
|
-
For internal box operations, use signal <sandbox-id> --session <session-id>.
|
|
10083
|
-
The live reference is execution-scoped, not durable conversation identity.
|
|
9960
|
+
${chalk25.dim("$")} mutagent helix session signal <reference>
|
|
9961
|
+
${chalk25.dim("$")} mutagent helix session signal <reference> --type SIGTERM
|
|
9962
|
+
${chalk25.dim("$")} mutagent helix session signal <reference> --json
|
|
9963
|
+
|
|
9964
|
+
SIGINT (the signal Ctrl-C sends) and SIGTERM both stop the session's Helix process.
|
|
9965
|
+
SIGKILL is not supported.
|
|
9966
|
+
To end only the current turn and keep the session, use
|
|
9967
|
+
'mutagent helix session send <reference> --type abort' instead; signal works even
|
|
9968
|
+
when the agent has stopped reading its input.
|
|
9969
|
+
Exits non-zero if the session had already ended.
|
|
9970
|
+
The sandbox is not deleted: its files and event log stay, and
|
|
9971
|
+
'mutagent helix session attach <reference>' shows what the session did. A sandbox
|
|
9972
|
+
idle for 15 minutes is checkpointed and stopped.
|
|
10084
9973
|
|
|
10085
9974
|
AI Agent Directive:
|
|
10086
|
-
--json returns ONE object: { success, reference, signal, signalled,
|
|
10087
|
-
|
|
10088
|
-
|
|
10089
|
-
|
|
10090
|
-
Treat that as information, not as a failure to retry: the agent is already
|
|
10091
|
-
stopped, which is what you wanted.
|
|
10092
|
-
This does NOT destroy the sandbox. It stays billable until 'mutagent sandbox rm
|
|
10093
|
-
<id>'. Confirm with the user before that.
|
|
9975
|
+
--json returns ONE object: { success, reference, signal, signalled, _links }.
|
|
9976
|
+
'signalled' is true only when a running process was stopped. A session that had
|
|
9977
|
+
already ended exits non-zero; the agent is already stopped, so treat it as
|
|
9978
|
+
information and do not retry.
|
|
10094
9979
|
`).action(async (id, options) => {
|
|
10095
9980
|
const isJson = getJsonFlag(jsonSource);
|
|
10096
9981
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -10129,8 +10014,8 @@ AI Agent Directive:
|
|
|
10129
10014
|
return;
|
|
10130
10015
|
}
|
|
10131
10016
|
output.success(`Sent ${signal} to session ${sessionId}`);
|
|
10132
|
-
console.error(
|
|
10133
|
-
console.error(
|
|
10017
|
+
console.error(chalk25.dim(` The sandbox is still up: mutagent sandbox attach ${id}`));
|
|
10018
|
+
console.error(chalk25.dim(` Destroy it with: mutagent sandbox rm ${id}`));
|
|
10134
10019
|
} catch (error) {
|
|
10135
10020
|
handleError(error, isJson);
|
|
10136
10021
|
}
|
|
@@ -10148,46 +10033,26 @@ function resolveSignal(raw) {
|
|
|
10148
10033
|
|
|
10149
10034
|
// src/commands/helix/session-commands.ts
|
|
10150
10035
|
function registerSessionCommand(parent) {
|
|
10151
|
-
const session = new Command13("session").description("
|
|
10036
|
+
const session = new Command13("session").description("List, watch and control Helix sessions").addHelpText("after", `
|
|
10152
10037
|
Examples:
|
|
10153
|
-
${
|
|
10154
|
-
${
|
|
10155
|
-
${
|
|
10156
|
-
${
|
|
10157
|
-
${
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
${chalk25.bold("session start")} gives you an AGENT inside that box.
|
|
10165
|
-
${chalk25.bold("session attach")} WATCHES it.
|
|
10166
|
-
${chalk25.bold("session send")} TYPES INTO it.
|
|
10167
|
-
${chalk25.bold("session signal")} STOPS it, and leaves the box standing.
|
|
10168
|
-
${chalk25.bold("sandbox rm")} destroys the box. Only that.
|
|
10169
|
-
A box with no session is a normal state — that is what 'sandbox exec' runs against.
|
|
10170
|
-
|
|
10171
|
-
EXPLICIT TRANSCRIPT TRANSFER (NOT A DURABLE CONVERSATION ID):
|
|
10172
|
-
${chalk25.bold("checkpoint")} copies the HARNESS's own transcript off the box. That is what a
|
|
10173
|
-
new box needs to CONTINUE the conversation — it is not the same as replaying
|
|
10174
|
-
the stream, which shows what you watched and restores no agent state at all.
|
|
10175
|
-
${chalk25.bold("restore")} rebuilds from the checkpoint and starts a new runtime session,
|
|
10176
|
-
verifying captured data before using it. Workspace/settings capture depends
|
|
10177
|
-
on deployment support and the checkpoint. Read notCaptured and notRestored;
|
|
10178
|
-
a stored transcript alone is not a full-state checkpoint or durable identity.
|
|
10179
|
-
|
|
10180
|
-
Moved here from 'mutagent helix' (BREAKING):
|
|
10181
|
-
'helix send' and 'helix signal' are now 'helix session send' and
|
|
10182
|
-
'helix session signal'. There is no alias; the old spellings are gone.
|
|
10038
|
+
${chalk26.dim("$")} mutagent helix session ls
|
|
10039
|
+
${chalk26.dim("$")} mutagent helix session attach <reference>
|
|
10040
|
+
${chalk26.dim("$")} mutagent helix session send <reference> "and now the tests"
|
|
10041
|
+
${chalk26.dim("$")} mutagent helix session signal <reference>
|
|
10042
|
+
${chalk26.dim("$")} mutagent helix session checkpoint <reference>
|
|
10043
|
+
|
|
10044
|
+
A session reference (hs1_...) is printed when a run starts and listed by 'session ls'.
|
|
10045
|
+
attach, send, signal and checkpoint take it.
|
|
10046
|
+
start, 'ls <sandbox-id>', checkpoints and restore are internal and take a sandbox id.
|
|
10047
|
+
checkpoint saves a session's conversation; restore continues it in a rebuilt sandbox.
|
|
10048
|
+
A sandbox idle for 15 minutes is checkpointed and stopped; restore rebuilds it.
|
|
10183
10049
|
|
|
10184
10050
|
AI Agent Directive:
|
|
10185
|
-
|
|
10186
|
-
|
|
10187
|
-
|
|
10188
|
-
send
|
|
10189
|
-
|
|
10190
|
-
Restore addresses the checkpoint's sandbox ID and starts a NEW session.
|
|
10051
|
+
Start work with 'mutagent helix --mode rpc' and keep the session reference it
|
|
10052
|
+
prints; 'mutagent helix session ls --json' lists references too. Use the
|
|
10053
|
+
reference for attach, send, signal and checkpoint; never guess one. Only an rpc
|
|
10054
|
+
session accepts send. start, checkpoints and restore are internal and take a
|
|
10055
|
+
sandbox id; restore starts a NEW session, found with 'session ls'.
|
|
10191
10056
|
`);
|
|
10192
10057
|
registerStart(session, parent);
|
|
10193
10058
|
registerSessionLs(session, parent);
|
|
@@ -10210,50 +10075,25 @@ function withSessionFlags(options, own) {
|
|
|
10210
10075
|
};
|
|
10211
10076
|
}
|
|
10212
10077
|
function registerStart(session, root) {
|
|
10213
|
-
session.command("start").description("Start a Helix session
|
|
10078
|
+
session.command("start").description("(internal) Start a Helix session in an existing sandbox, by sandbox id").argument("<sandbox-id>", "Sandbox to start the session in").argument("[args...]", "Helix flags (put -- before them)").allowUnknownOption(true).allowExcessArguments(true).option("--rpc", "Keep the session open for 'session send' (default: run once and end)").option("--cwd <path>", "Working directory (default: $HOME for an rpc session)").option("--env <name>", "Workspace Environment to load (see mutagent env ls)").addHelpText("after", `
|
|
10214
10079
|
Examples:
|
|
10215
|
-
${
|
|
10216
|
-
${
|
|
10217
|
-
${
|
|
10218
|
-
${
|
|
10219
|
-
|
|
10220
|
-
|
|
10221
|
-
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10228
|
-
${chalk25.bold("--cwd")} IS NOT COSMETIC. The in-box server runs from /app, which is root-owned
|
|
10229
|
-
while the agent runs as 'sandbox'. An rpc child started there loses its subagent
|
|
10230
|
-
scheduler to EACCES — and then answers normally, because the failure goes to
|
|
10231
|
-
stderr and the core loop survives. A half-crippled agent that looks healthy. So
|
|
10232
|
-
an rpc session with no --cwd runs in $HOME, writable by construction; name a
|
|
10233
|
-
directory only for the real case that cannot serve, a mounted work directory.
|
|
10234
|
-
|
|
10235
|
-
${chalk25.bold("--env <name>")} loads a WORKSPACE ENVIRONMENT — the variables and secrets the
|
|
10236
|
-
agent's tools need, stored with 'mutagent env set' — into this session. It is
|
|
10237
|
-
resolved on the server; no value passes through this machine. It is NOT a model
|
|
10238
|
-
key: providers come from the workspace's provider configuration, and an
|
|
10239
|
-
Environment variable named like one is refused at 'env set' for that reason.
|
|
10240
|
-
|
|
10241
|
-
TRAILING ARGUMENTS are classified exactly as on 'mutagent helix agent --sandbox':
|
|
10242
|
-
forwarded, translated or REFUSED with a reason. A flag naming a path on THIS
|
|
10243
|
-
machine (--extension, --session <file>, --skill, …) is refused, because it would
|
|
10244
|
-
point at nothing inside the box. A leading positional, --prompt, --file or
|
|
10245
|
-
--name is the agent DEFINITION, resolved here first. Put ${chalk25.bold("--")} before them so
|
|
10246
|
-
they are not read as flags for this command. A ${chalk25.bold("--")} does NOT stop the
|
|
10247
|
-
classification: the harness has no end-of-options marker, so neither does this.
|
|
10248
|
-
|
|
10249
|
-
The box must already exist — 'mutagent sandbox spawn --image <ref>' makes one,
|
|
10250
|
-
and 'mutagent sandbox ls' shows which are running. One sandbox can host several
|
|
10251
|
-
sessions, so starting one never replaces another.
|
|
10080
|
+
${chalk26.dim("$")} mutagent helix session start sbx_123 --rpc
|
|
10081
|
+
${chalk26.dim("$")} mutagent helix session start sbx_123 --rpc --env staging
|
|
10082
|
+
${chalk26.dim("$")} mutagent helix session start sbx_123 --rpc --json
|
|
10083
|
+
${chalk26.dim("$")} mutagent helix session start sbx_123 -- --model <provider/model> --thinking high
|
|
10084
|
+
|
|
10085
|
+
Prints the session id. Address the session as <sandbox-id> --session <session-id>
|
|
10086
|
+
for send, signal and checkpoint, and watch it with 'mutagent sandbox attach <sandbox-id>'.
|
|
10087
|
+
Only an --rpc session accepts 'session send'; a one-shot session runs and ends.
|
|
10088
|
+
Flags after -- go to Helix. A flag that names a file on this machine is refused,
|
|
10089
|
+
because the sandbox cannot read it. A leading positional, --prompt, --file or
|
|
10090
|
+
--name is the agent definition, resolved on this machine first.
|
|
10091
|
+
One sandbox can host several sessions; starting one never replaces another.
|
|
10252
10092
|
|
|
10253
10093
|
AI Agent Directive:
|
|
10254
10094
|
--json returns ONE object: { success, sandboxId, sessionId, status, mode,
|
|
10255
|
-
_links }.
|
|
10256
|
-
|
|
10095
|
+
_links, _directive }. Address the session as <sandbox-id> --session <sessionId>
|
|
10096
|
+
for send, signal and checkpoint.
|
|
10257
10097
|
`).action(async (id, rawArgs, options) => {
|
|
10258
10098
|
const isJson = getJsonFlag(root);
|
|
10259
10099
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -10293,9 +10133,9 @@ AI Agent Directive:
|
|
|
10293
10133
|
return;
|
|
10294
10134
|
}
|
|
10295
10135
|
output.success(`Session ${sessionId} started in ${id} (${mode})`);
|
|
10296
|
-
console.error(
|
|
10136
|
+
console.error(chalk26.dim(` Watch it: mutagent sandbox attach ${id}`));
|
|
10297
10137
|
if (mode === "rpc") {
|
|
10298
|
-
console.error(
|
|
10138
|
+
console.error(chalk26.dim(` Talk to it: mutagent helix session send ${id} --session ${sessionId} "<message>"`));
|
|
10299
10139
|
}
|
|
10300
10140
|
} catch (error) {
|
|
10301
10141
|
handleError(error, isJson);
|
|
@@ -10308,24 +10148,18 @@ function requireSessionId(result, sandboxId) {
|
|
|
10308
10148
|
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 sandbox attach ${sandboxId}`);
|
|
10309
10149
|
}
|
|
10310
10150
|
function registerSessionLs(session, root) {
|
|
10311
|
-
session.command("ls").description("List
|
|
10151
|
+
session.command("ls").description("List this workspace's Helix sessions").argument("[sandbox-id]", "(internal) List the session ids in one sandbox instead").addHelpText("after", `
|
|
10312
10152
|
Examples:
|
|
10313
|
-
${
|
|
10314
|
-
${
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10323
|
-
'restored' is running on a box rebuilt from a checkpoint. Sessions older than
|
|
10324
|
-
the persisted arm/mode fields show those columns blank rather than a guess.
|
|
10325
|
-
|
|
10326
|
-
WITH A BOX ID — the internal box listing, unchanged: bare session ids that one
|
|
10327
|
-
sandbox is hosting. An empty list is a normal result: a box with no session in
|
|
10328
|
-
it is what 'mutagent sandbox exec' runs against.
|
|
10153
|
+
${chalk26.dim("$")} mutagent helix session ls
|
|
10154
|
+
${chalk26.dim("$")} mutagent helix session ls --json
|
|
10155
|
+
|
|
10156
|
+
Every session in the workspace, live and ended, newest activity first.
|
|
10157
|
+
STATUS is live (running now), ended (finished; exitCode in --json), stopped-for-idling
|
|
10158
|
+
(its sandbox was idle for 15 minutes, then checkpointed and stopped) or restored
|
|
10159
|
+
(running in a sandbox rebuilt from a checkpoint). ARM and MODE are blank for
|
|
10160
|
+
sessions older than those fields.
|
|
10161
|
+
Pass the REFERENCE to attach, send, signal or checkpoint.
|
|
10162
|
+
With a sandbox id, lists the bare session ids in that one sandbox (internal).
|
|
10329
10163
|
|
|
10330
10164
|
AI Agent Directive:
|
|
10331
10165
|
Run 'mutagent helix session ls --json' to discover live work — { workspaceId,
|
|
@@ -10342,7 +10176,7 @@ AI Agent Directive:
|
|
|
10342
10176
|
return;
|
|
10343
10177
|
}
|
|
10344
10178
|
if (sessions2.length === 0) {
|
|
10345
|
-
console.log(
|
|
10179
|
+
console.log(chalk26.gray(`No sessions running in ${id}.`));
|
|
10346
10180
|
console.log("");
|
|
10347
10181
|
console.log(` Start one: mutagent helix session start ${id} --rpc`);
|
|
10348
10182
|
return;
|
|
@@ -10361,7 +10195,7 @@ AI Agent Directive:
|
|
|
10361
10195
|
return;
|
|
10362
10196
|
}
|
|
10363
10197
|
if (sessions.length === 0) {
|
|
10364
|
-
console.log(
|
|
10198
|
+
console.log(chalk26.gray("No Helix sessions in this workspace yet."));
|
|
10365
10199
|
console.log("");
|
|
10366
10200
|
console.log(' Launch one: mutagent helix launch --task "..." (or session start <box>)');
|
|
10367
10201
|
return;
|
|
@@ -10375,7 +10209,7 @@ AI Agent Directive:
|
|
|
10375
10209
|
"LAST ACTIVITY": s.lastActivityAt
|
|
10376
10210
|
})));
|
|
10377
10211
|
if (nextCursor !== undefined) {
|
|
10378
|
-
console.log(
|
|
10212
|
+
console.log(chalk26.gray("\nMore sessions — re-run with the cursor from --json `nextCursor`."));
|
|
10379
10213
|
}
|
|
10380
10214
|
} catch (error) {
|
|
10381
10215
|
handleError(error, isJson);
|
|
@@ -10383,17 +10217,16 @@ AI Agent Directive:
|
|
|
10383
10217
|
});
|
|
10384
10218
|
}
|
|
10385
10219
|
function registerSessionAttach(session) {
|
|
10386
|
-
session.command("attach").description("Watch
|
|
10220
|
+
session.command("attach").description("Watch a session").argument("<reference>", "Session reference (hs1_...) from 'mutagent helix' or 'session ls'").option("--since <n>", "Replay from after event number n").addHelpText("after", [
|
|
10387
10221
|
"",
|
|
10388
10222
|
"Examples:",
|
|
10389
10223
|
" mutagent helix session attach <reference>",
|
|
10390
10224
|
" mutagent helix session attach <reference> --since 412",
|
|
10391
10225
|
"",
|
|
10392
10226
|
"AI Agent Directive:",
|
|
10393
|
-
" Output is
|
|
10394
|
-
" Ctrl-C
|
|
10395
|
-
" A
|
|
10396
|
-
" Use mutagent sandbox attach for the internal box-wide event stream."
|
|
10227
|
+
" Output is Helix's own text or JSON lines on stdout; CLI messages go to stderr. --json does not change it.",
|
|
10228
|
+
" Ctrl-C stops watching without signalling the session.",
|
|
10229
|
+
" A reference names one session; after a restore, take the new reference from session ls."
|
|
10397
10230
|
].join(`
|
|
10398
10231
|
`)).action(async (reference, options) => {
|
|
10399
10232
|
try {
|
|
@@ -10411,28 +10244,20 @@ function registerSessionAttach(session) {
|
|
|
10411
10244
|
});
|
|
10412
10245
|
}
|
|
10413
10246
|
function registerCheckpoint(session, root) {
|
|
10414
|
-
session.command("checkpoint").description("
|
|
10247
|
+
session.command("checkpoint").description("Save a session's conversation as a checkpoint").argument("<reference>", "Session reference (hs1_...); with --session, a sandbox id (internal)").option("--session <id>", "(internal) The session id, when the argument is a sandbox id").addHelpText("after", `
|
|
10415
10248
|
Examples:
|
|
10416
|
-
${
|
|
10417
|
-
${
|
|
10249
|
+
${chalk26.dim("$")} mutagent helix session checkpoint <reference>
|
|
10250
|
+
${chalk26.dim("$")} mutagent helix session checkpoint <reference> --json
|
|
10418
10251
|
|
|
10419
|
-
|
|
10420
|
-
|
|
10421
|
-
|
|
10422
|
-
|
|
10423
|
-
|
|
10424
|
-
Workspace and settings may also be captured when supported. Read notCaptured
|
|
10425
|
-
before treating a result as full-state capture: an existing transcript snapshot
|
|
10426
|
-
can be reused without replacing its original manifest or capturing new state.
|
|
10427
|
-
|
|
10428
|
-
A 409 means the session has not completed a turn yet: the harness writes its
|
|
10429
|
-
file lazily, so there is genuinely nothing to capture, and storing an empty
|
|
10430
|
-
snapshot would produce a restore that succeeds and restores nothing.
|
|
10252
|
+
Saves Helix's own session file, and the workspace and settings where the
|
|
10253
|
+
deployment supports it. notCaptured lists anything left out.
|
|
10254
|
+
Fails until the session has finished its first turn: before that there is no
|
|
10255
|
+
conversation to save.
|
|
10431
10256
|
|
|
10432
10257
|
AI Agent Directive:
|
|
10433
|
-
--json returns { success, snapshotId, bytes, sha256, harnessSessionId,
|
|
10258
|
+
--json returns { success, reference, snapshotId, bytes, sha256, harnessSessionId,
|
|
10434
10259
|
messageCount, sourcePath, manifestKey, workspaceBytes, workspaceFiles, notCaptured }.
|
|
10435
|
-
|
|
10260
|
+
Keep snapshotId and read notCaptured before relying on a restore.
|
|
10436
10261
|
`).action(async (id, options) => {
|
|
10437
10262
|
const isJson = getJsonFlag(root);
|
|
10438
10263
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -10471,22 +10296,20 @@ AI Agent Directive:
|
|
|
10471
10296
|
if (Array.isArray(result.notCaptured))
|
|
10472
10297
|
for (const reason of result.notCaptured)
|
|
10473
10298
|
output.warn("Not captured: " + reason);
|
|
10474
|
-
console.error(
|
|
10299
|
+
console.error(chalk26.dim(` ${String(result.messageCount)} messages, ${String(result.bytes)} bytes, from ${result.sourcePath}`));
|
|
10475
10300
|
} catch (error) {
|
|
10476
10301
|
handleError(error, isJson);
|
|
10477
10302
|
}
|
|
10478
10303
|
});
|
|
10479
10304
|
}
|
|
10480
10305
|
function registerCheckpoints(session, root) {
|
|
10481
|
-
session.command("checkpoints").description("List
|
|
10306
|
+
session.command("checkpoints").description("(internal) List a sandbox's checkpoints, by sandbox id").argument("<sandbox-id>", "Sandbox the checkpoints were captured from").addHelpText("after", `
|
|
10482
10307
|
Examples:
|
|
10483
|
-
${
|
|
10484
|
-
${
|
|
10308
|
+
${chalk26.dim("$")} mutagent helix session checkpoints sbx_123
|
|
10309
|
+
${chalk26.dim("$")} mutagent helix session checkpoints sbx_123 --json
|
|
10485
10310
|
|
|
10486
|
-
Newest first,
|
|
10487
|
-
|
|
10488
|
-
spread conversations into logs and caches nobody chose. 'messageCount' is how
|
|
10489
|
-
much conversation each one holds.
|
|
10311
|
+
Newest first, without the stored bytes. MESSAGES is how much conversation each holds.
|
|
10312
|
+
Pass an id to 'mutagent helix session restore <sandbox-id> --snapshot <id>'.
|
|
10490
10313
|
|
|
10491
10314
|
AI Agent Directive:
|
|
10492
10315
|
--json returns { snapshots: [...], count: N, sandboxId }. Read it to find a
|
|
@@ -10501,7 +10324,7 @@ AI Agent Directive:
|
|
|
10501
10324
|
return;
|
|
10502
10325
|
}
|
|
10503
10326
|
if (snapshots.length === 0) {
|
|
10504
|
-
console.log(
|
|
10327
|
+
console.log(chalk26.gray(`No checkpoints captured from ${id}.`));
|
|
10505
10328
|
console.log("");
|
|
10506
10329
|
console.log(` Capture one: mutagent helix session checkpoint ${id} --session <session-id>`);
|
|
10507
10330
|
return;
|
|
@@ -10526,11 +10349,11 @@ function requireSession(raw, example) {
|
|
|
10526
10349
|
}
|
|
10527
10350
|
|
|
10528
10351
|
// src/commands/helix/models.ts
|
|
10529
|
-
import
|
|
10352
|
+
import chalk27 from "chalk";
|
|
10530
10353
|
init_errors();
|
|
10531
10354
|
function printView(view, output) {
|
|
10532
10355
|
if (view.models.length === 0) {
|
|
10533
|
-
console.log(
|
|
10356
|
+
console.log(chalk27.gray("No Helix Cloud models: this workspace has no active LLM provider the box can use."));
|
|
10534
10357
|
console.log("");
|
|
10535
10358
|
console.log(" Add one: mutagent providers add --provider <id> --name <name> --api-key <key>");
|
|
10536
10359
|
} else {
|
|
@@ -10540,27 +10363,26 @@ function printView(view, output) {
|
|
|
10540
10363
|
name: m.displayName
|
|
10541
10364
|
})));
|
|
10542
10365
|
console.log("");
|
|
10543
|
-
console.log(view.default === null ?
|
|
10366
|
+
console.log(view.default === null ? chalk27.yellow("No default set — a run without --model answers 428. Set one: mutagent helix models default <provider/model>") : `Default: ${view.default}`);
|
|
10544
10367
|
}
|
|
10545
10368
|
if (view.unmapped.length > 0) {
|
|
10546
|
-
console.log(
|
|
10369
|
+
console.log(chalk27.yellow(`Not usable in a box (no harness credential mapping): ${view.unmapped.join(", ")}`));
|
|
10547
10370
|
}
|
|
10548
10371
|
}
|
|
10549
10372
|
function registerModelsCommand(helix) {
|
|
10550
|
-
const models = helix.command("models").description("List
|
|
10373
|
+
const models = helix.command("models").description("List the models Helix can use in the cloud, and the workspace default model").addHelpText("after", `
|
|
10551
10374
|
Examples:
|
|
10552
|
-
${
|
|
10553
|
-
${
|
|
10554
|
-
${
|
|
10555
|
-
${
|
|
10375
|
+
${chalk27.dim("$")} mutagent helix models
|
|
10376
|
+
${chalk27.dim("$")} mutagent helix models --json
|
|
10377
|
+
${chalk27.dim("$")} mutagent helix models default zai-coding-cn/glm-5.2
|
|
10378
|
+
${chalk27.dim("$")} mutagent helix models default zai-coding-cn/glm-5.2 openai/gpt-5
|
|
10556
10379
|
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
|
|
10380
|
+
Lists the models your workspace's active LLM providers ('mutagent providers') give
|
|
10381
|
+
Helix in the cloud. Each id is provider/model: pass it as --model to 'mutagent helix'
|
|
10382
|
+
or 'mutagent helix agent'.
|
|
10560
10383
|
|
|
10561
|
-
A cloud run that names no model uses the workspace
|
|
10562
|
-
|
|
10563
|
-
keeps its own default.
|
|
10384
|
+
A cloud run that names no model uses the workspace default model (★). With none
|
|
10385
|
+
set, such runs fail until you set one. Your local Helix keeps its own default.
|
|
10564
10386
|
|
|
10565
10387
|
AI Agent Directive:
|
|
10566
10388
|
--json returns { workspaceId, models: [{ id, provider, harnessProvider, model,
|
|
@@ -10580,15 +10402,15 @@ AI Agent Directive:
|
|
|
10580
10402
|
handleError(error, isJson);
|
|
10581
10403
|
}
|
|
10582
10404
|
});
|
|
10583
|
-
models.command("default").description("Set the workspace default Helix
|
|
10405
|
+
models.command("default").description("Set the workspace default model for Helix in the cloud, and its fallbacks").argument("[models...]", "Model ids from 'mutagent helix models'; the first is the default").option("--clear", "Remove the default model and every fallback").addHelpText("after", `
|
|
10584
10406
|
Examples:
|
|
10585
|
-
${
|
|
10586
|
-
${
|
|
10587
|
-
${
|
|
10407
|
+
${chalk27.dim("$")} mutagent helix models default zai-coding-cn/glm-5.2
|
|
10408
|
+
${chalk27.dim("$")} mutagent helix models default zai-coding-cn/glm-5.2 openai/gpt-5
|
|
10409
|
+
${chalk27.dim("$")} mutagent helix models default --clear
|
|
10588
10410
|
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10411
|
+
Replaces the list. The first id is the default; later ids are used in order when
|
|
10412
|
+
an earlier model's LLM provider has been turned off. Every id must appear in
|
|
10413
|
+
'mutagent helix models'; the server refuses the rest.
|
|
10592
10414
|
|
|
10593
10415
|
AI Agent Directive:
|
|
10594
10416
|
--json returns the same shape as 'mutagent helix models --json', after the
|
|
@@ -10616,7 +10438,7 @@ AI Agent Directive:
|
|
|
10616
10438
|
}
|
|
10617
10439
|
|
|
10618
10440
|
// src/commands/helix/doctor.ts
|
|
10619
|
-
import
|
|
10441
|
+
import chalk28 from "chalk";
|
|
10620
10442
|
init_errors();
|
|
10621
10443
|
|
|
10622
10444
|
// src/lib/sandbox-catalog.ts
|
|
@@ -10651,23 +10473,14 @@ function refreshRuntimeRefusal() {
|
|
|
10651
10473
|
}
|
|
10652
10474
|
function registerDoctorCommand(parent, registration) {
|
|
10653
10475
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
10654
|
-
parent.command("doctor").description("
|
|
10476
|
+
parent.command("doctor").description("Check the Helix build in the cloud image").option("--sandbox <id>", "(internal) Run in an existing sandbox, by sandbox id").option("--preset <name>", "Sandbox preset (not with --sandbox)").option("--strict", "Also launch Helix's embedded harness with --version and fail if it fails").option("--welcome", "Print the Helix welcome screen instead of the report").option("--refresh-runtime", "Refused: the cloud image has no Helix runtime to refresh").allowUnknownOption(true).addHelpText("after", `
|
|
10655
10477
|
Examples:
|
|
10656
|
-
${
|
|
10657
|
-
${
|
|
10658
|
-
${chalk27.dim("$")} mutagent helix doctor --sandbox sbx_123
|
|
10659
|
-
|
|
10660
|
-
This runs the BOX's Helix, not yours. It answers "is the image healthy", which
|
|
10661
|
-
is a different question from 'mutagent doctor' (this CLI's own environment) and
|
|
10662
|
-
from a local 'helix doctor' (your machine's extracted runtime).
|
|
10478
|
+
${chalk28.dim("$")} mutagent helix doctor
|
|
10479
|
+
${chalk28.dim("$")} mutagent helix doctor --strict --json
|
|
10663
10480
|
|
|
10664
|
-
|
|
10665
|
-
|
|
10666
|
-
|
|
10667
|
-
|
|
10668
|
-
Without --sandbox this spawns a fresh box from a preset, runs, and tears it
|
|
10669
|
-
down. That costs a box for a diagnostic, so pass --sandbox when you already have
|
|
10670
|
-
one up.
|
|
10481
|
+
Runs Helix's own diagnostics in a temporary sandbox that is removed afterwards,
|
|
10482
|
+
and exits with Helix's exit code. It checks the cloud image, not your machine:
|
|
10483
|
+
for your local Helix run 'helix doctor'.
|
|
10671
10484
|
|
|
10672
10485
|
AI Agent Directive:
|
|
10673
10486
|
--json returns { success, command, result: { sandboxId, exitCode, stdout,
|
|
@@ -10697,15 +10510,13 @@ AI Agent Directive:
|
|
|
10697
10510
|
}
|
|
10698
10511
|
function registerSmokeCommand(parent, registration) {
|
|
10699
10512
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
10700
|
-
parent.command("smoke").description("Run
|
|
10513
|
+
parent.command("smoke").description("Run Helix's smoke test in the cloud").option("--sandbox <id>", "(internal) Run in an existing sandbox, by sandbox id").option("--preset <name>", "Sandbox preset (not with --sandbox)").allowUnknownOption(true).addHelpText("after", `
|
|
10701
10514
|
Examples:
|
|
10702
|
-
${
|
|
10703
|
-
${
|
|
10515
|
+
${chalk28.dim("$")} mutagent helix smoke
|
|
10516
|
+
${chalk28.dim("$")} mutagent helix smoke --json
|
|
10704
10517
|
|
|
10705
|
-
|
|
10706
|
-
|
|
10707
|
-
default deny policy does not, and the failure will present as an unreachable
|
|
10708
|
-
model provider.
|
|
10518
|
+
Runs one real model turn in a temporary sandbox that is removed afterwards, and
|
|
10519
|
+
exits with Helix's exit code.
|
|
10709
10520
|
|
|
10710
10521
|
AI Agent Directive:
|
|
10711
10522
|
--json returns the same shape as 'mutagent helix doctor --json'. The binary's
|
|
@@ -10729,15 +10540,9 @@ AI Agent Directive:
|
|
|
10729
10540
|
}
|
|
10730
10541
|
function registerUpdateCommand2(parent, registration) {
|
|
10731
10542
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
10732
|
-
parent.command("update").description("Not available in the cloud
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
property of the image rather than something a session can change.
|
|
10736
|
-
|
|
10737
|
-
To move the cloud version you change which image the preset points at, which is
|
|
10738
|
-
a backend/release operation, not a CLI one.
|
|
10739
|
-
${chalk27.dim("$")} mutagent sandbox presets --json ${chalk27.dim("what is available, and which is default")}
|
|
10740
|
-
${chalk27.dim("$")} mutagent helix --version ${chalk27.dim("this CLI, and the version the image pins")}
|
|
10543
|
+
parent.command("update").description("Not available in the cloud").allowUnknownOption(true).allowExcessArguments(true).argument("[argv...]", "Ignored").addHelpText("after", `
|
|
10544
|
+
The cloud Helix version is set by the release. Check it with 'mutagent helix version'.
|
|
10545
|
+
To update the Helix on this machine, run 'helix update'.
|
|
10741
10546
|
`).action(async () => {
|
|
10742
10547
|
const isJson = getJsonFlag(jsonSource);
|
|
10743
10548
|
try {
|
|
@@ -10750,21 +10555,14 @@ a backend/release operation, not a CLI one.
|
|
|
10750
10555
|
}
|
|
10751
10556
|
function registerVersionCommand(parent, registration) {
|
|
10752
10557
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
10753
|
-
parent.command("version").description("
|
|
10558
|
+
parent.command("version").description("Show this CLI's version and the Helix version the cloud uses").addHelpText("after", `
|
|
10754
10559
|
Examples:
|
|
10755
|
-
${
|
|
10756
|
-
${
|
|
10757
|
-
|
|
10758
|
-
TWO DIFFERENT VERSIONS, AND THEY ARE NOT THE SAME THING:
|
|
10759
|
-
cli the 'mutagent' binary you are running.
|
|
10760
|
-
helix the build the cloud IMAGE pins, as the backend DECLARES it.
|
|
10560
|
+
${chalk28.dim("$")} mutagent helix version
|
|
10561
|
+
${chalk28.dim("$")} mutagent helix version --json
|
|
10761
10562
|
|
|
10762
|
-
|
|
10763
|
-
|
|
10764
|
-
|
|
10765
|
-
|
|
10766
|
-
This says nothing about a Helix installed on THIS machine. For that, run
|
|
10767
|
-
'helix --version' directly.
|
|
10563
|
+
cli the mutagent CLI you are running
|
|
10564
|
+
helix the Helix version the cloud image is released with, as the server reports it
|
|
10565
|
+
For the Helix on this machine, run 'helix --version'.
|
|
10768
10566
|
|
|
10769
10567
|
AI Agent Directive:
|
|
10770
10568
|
--json returns { cli, helix, helixSource }. 'helixSource' is "presets", which
|
|
@@ -10786,7 +10584,7 @@ async function reportVersions(isJson) {
|
|
|
10786
10584
|
return;
|
|
10787
10585
|
}
|
|
10788
10586
|
console.log(`cli ${cli}`);
|
|
10789
|
-
console.log(`helix ${helix ?? "unknown"} ${
|
|
10587
|
+
console.log(`helix ${helix ?? "unknown"} ${chalk28.dim("(pinned by the image, per the backend’s presets)")}`);
|
|
10790
10588
|
}
|
|
10791
10589
|
async function pinnedHelixVersion() {
|
|
10792
10590
|
const { presets, default: fallback } = await listPresets();
|
|
@@ -10827,47 +10625,39 @@ function reportBoxRun(result, output, isJson, context) {
|
|
|
10827
10625
|
|
|
10828
10626
|
// src/commands/helix/index.ts
|
|
10829
10627
|
function createHelixCommand() {
|
|
10830
|
-
const helix = new Command14("helix").description("Run Helix in
|
|
10628
|
+
const helix = new Command14("helix").description("Run Helix in the cloud").allowUnknownOption(true).allowExcessArguments(true).argument("[argv...]", 'Helix flags and the task, e.g. -p "<task>"').helpCommand("help [command]", "Print usage for mutagent helix or one of its subcommands").addHelpText("after", [
|
|
10629
|
+
"",
|
|
10630
|
+
"Run options:",
|
|
10631
|
+
' -p "<task>" Run the task to completion and print the final answer',
|
|
10632
|
+
' --mode json "<task>" Stream events as JSON lines',
|
|
10633
|
+
" --mode rpc, --rpc Read JSON commands on stdin; write events on stdout",
|
|
10634
|
+
" --prime Run the lean Prime agent instead of the full orchestrator",
|
|
10635
|
+
" --model <provider/model> Model to use (default: the workspace default model; see mutagent helix models)",
|
|
10636
|
+
" --env <name> Workspace Environment to load (see mutagent env ls)",
|
|
10637
|
+
" --preset <name> Sandbox preset (default: the server's Helix preset)",
|
|
10638
|
+
" --sandbox-provider <name> Run on this configured sandbox provider",
|
|
10639
|
+
" --cwd <path> Working directory in the sandbox",
|
|
10640
|
+
" --sandbox <id> (internal) Run in an existing sandbox, by sandbox id",
|
|
10831
10641
|
"",
|
|
10832
10642
|
"Examples:",
|
|
10833
10643
|
' mutagent helix -p "plan the refactor"',
|
|
10834
10644
|
' mutagent helix --mode json "run the tests"',
|
|
10835
10645
|
" mutagent helix --prime --mode rpc",
|
|
10836
|
-
' mutagent helix --env staging
|
|
10837
|
-
" mutagent helix agent --file
|
|
10838
|
-
"",
|
|
10839
|
-
"Modes:",
|
|
10840
|
-
" -p runs the complete model/tool loop and prints the final text.",
|
|
10841
|
-
" --mode json streams native harness JSONL events; -p is optional.",
|
|
10842
|
-
" --mode rpc reads JSONL commands on stdin and writes native responses/events.",
|
|
10843
|
-
" Text/JSON runs preserve every message and option in relative order.",
|
|
10844
|
-
" The last valid --mode wins; RPC overrides -p and ignores positional messages.",
|
|
10845
|
-
" --mode=rpc is a Cloud convenience, normalized to native --mode rpc.",
|
|
10846
|
-
" RPC stdin EOF explicitly closes remote input; stream disconnect only detaches.",
|
|
10847
|
-
" Ctrl-C sends SIGINT to the launched execution; its native exit status is reported.",
|
|
10848
|
-
" Read-only session attach Ctrl-C detaches without signalling the execution.",
|
|
10849
|
-
" --rpc also selects the Cloud RPC transport.",
|
|
10850
|
-
" A standalone terminal chat UI is not available.",
|
|
10851
|
-
" Native launch stdout contains harness output only, including with global --json.",
|
|
10852
|
-
"",
|
|
10853
|
-
"Placement:",
|
|
10854
|
-
" The server selects the configured Helix preset and destination.",
|
|
10855
|
-
" --preset selects a compatible preset; --env selects a workspace Environment.",
|
|
10856
|
-
" --sandbox-provider <name> places the launch on that configured sandbox provider.",
|
|
10857
|
-
" LLM --provider/--model select a model, not the execution destination.",
|
|
10858
|
-
" --sandbox <id> is available for an existing internal test box.",
|
|
10859
|
-
" mutagent sandbox and mutagent env retain their independent command groups.",
|
|
10646
|
+
' mutagent helix --env staging -p "check the deploy"',
|
|
10647
|
+
" mutagent helix agent --file reviewer.md --prime --mode rpc",
|
|
10860
10648
|
"",
|
|
10861
|
-
"
|
|
10862
|
-
"
|
|
10863
|
-
"
|
|
10864
|
-
"
|
|
10649
|
+
"Each run prints its session reference (hs1_...) on stderr. Use it with",
|
|
10650
|
+
"mutagent helix session attach, send, signal and checkpoint.",
|
|
10651
|
+
"Ctrl-C sends SIGINT to the session, which stops it; the exit status is reported.",
|
|
10652
|
+
"There is no interactive chat UI: choose -p, --mode json or --mode rpc.",
|
|
10653
|
+
"With --mode rpc, closing stdin ends the session's input; a dropped connection only detaches.",
|
|
10654
|
+
"A sandbox idle for 15 minutes is checkpointed and stopped; mutagent helix session restore rebuilds it.",
|
|
10865
10655
|
"",
|
|
10866
10656
|
"AI Agent Directive:",
|
|
10867
10657
|
" Choose -p, --mode json or --mode rpc explicitly.",
|
|
10868
|
-
"
|
|
10869
|
-
"
|
|
10870
|
-
" mutagent install helix installs
|
|
10658
|
+
" Without --prime the full Helix orchestrator runs; --prime runs the lean Prime agent.",
|
|
10659
|
+
" Keep the session reference (hs1_...) printed on stderr; attach, send, signal and checkpoint take it.",
|
|
10660
|
+
" mutagent install helix installs Helix on this machine; this command runs it in the cloud."
|
|
10871
10661
|
].join(`
|
|
10872
10662
|
`)).action(async (argv, _options, command) => {
|
|
10873
10663
|
try {
|
|
@@ -10897,75 +10687,37 @@ function createHelixCommand() {
|
|
|
10897
10687
|
|
|
10898
10688
|
// src/commands/sandbox/index.ts
|
|
10899
10689
|
import { Command as Command15 } from "commander";
|
|
10900
|
-
import
|
|
10690
|
+
import chalk34 from "chalk";
|
|
10901
10691
|
|
|
10902
10692
|
// src/commands/sandbox/spawn.ts
|
|
10903
|
-
import
|
|
10693
|
+
import chalk29 from "chalk";
|
|
10904
10694
|
init_errors();
|
|
10905
10695
|
var ARCHES = ["x86_64", "arm64"];
|
|
10906
10696
|
var EGRESS_POLICIES = ["deny", "allowlist", "allow"];
|
|
10907
10697
|
var EGRESS_KINDS = ["port", "cidr", "domain"];
|
|
10908
10698
|
function registerSpawnCommand(parent, registration) {
|
|
10909
10699
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
10910
|
-
parent.command("spawn").description("Spawn a persistent sandbox from an image 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>", "
|
|
10700
|
+
parent.command("spawn").description("Spawn a persistent sandbox from an image 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>", "Sandbox provider (default: the server's; see sandbox providers)").option("--env <name>", "Workspace Environment to load (see mutagent env ls)").option("--egress <policy>", `Outbound network: ${EGRESS_POLICIES.join(" | ")} (default: deny)`).option("--egress-allow <rule>", "domain:, port: or cidr: rule for --egress allowlist (repeatable)", collectRule, []).option("--detach", "Print the sandbox ID and return").addHelpText("after", `
|
|
10911
10701
|
Examples:
|
|
10912
|
-
${
|
|
10913
|
-
${
|
|
10914
|
-
${
|
|
10915
|
-
${
|
|
10916
|
-
|
|
10917
|
-
|
|
10918
|
-
|
|
10919
|
-
|
|
10920
|
-
|
|
10921
|
-
|
|
10922
|
-
|
|
10923
|
-
|
|
10924
|
-
--arch defaults to this host's architecture, on the assumption that you are
|
|
10925
|
-
running an image you just built. A definition whose arch does not match its
|
|
10926
|
-
image is rejected at preflight rather than emulated, so set it explicitly when
|
|
10927
|
-
the image was built elsewhere.
|
|
10928
|
-
|
|
10929
|
-
--env names an Environment (a stored bundle of secrets and variables). It is a
|
|
10930
|
-
DIFFERENT axis from --provider, which selects the backend. Secrets never travel
|
|
10931
|
-
on the command line.
|
|
10932
|
-
|
|
10933
|
-
NETWORK EGRESS IS DENIED BY DEFAULT, AND THAT BREAKS HELIX SESSIONS:
|
|
10934
|
-
A sandbox runs code we did not write, so the safe default is the default:
|
|
10935
|
-
nothing reaches out. That is right for a plain 'exec' box, which needs no
|
|
10936
|
-
network at all.
|
|
10937
|
-
|
|
10938
|
-
It is WRONG for a Helix session, for two independent reasons — either alone
|
|
10939
|
-
is enough to sink it:
|
|
10940
|
-
1. Under deny the container runs with --network none. Docker accepts -P and
|
|
10941
|
-
publishes nothing, so the in-box server on :8080 is unreachable and
|
|
10942
|
-
'mutagent helix session start' fails with "Helix is not reachable".
|
|
10943
|
-
2. An agent turn has to reach a model provider regardless.
|
|
10944
|
-
|
|
10945
|
-
So: ${chalk28.bold("--egress allow")} before 'helix session start'. Without it the failure
|
|
10946
|
-
presents as an unreachable box, which reads like a broken image rather than a
|
|
10947
|
-
policy you chose.
|
|
10948
|
-
|
|
10949
|
-
${chalk28.bold("--egress allowlist")} narrows that to named allowances, each given explicitly
|
|
10950
|
-
as kind:value — 'domain:api.anthropic.com', 'port:443', 'cidr:10.0.0.0/8'. The
|
|
10951
|
-
kind is never inferred from the value. An allowlist with no rules denies
|
|
10952
|
-
everything, so it is refused rather than silently walling the box off; it also
|
|
10953
|
-
needs a provider with egress control, which not every backend has.
|
|
10954
|
-
|
|
10955
|
-
Without --detach this spawns and immediately attaches, so the common path is a
|
|
10956
|
-
single command. Ctrl-C then DETACHES — the sandbox keeps running and the
|
|
10957
|
-
re-attach command is printed. Use 'mutagent sandbox rm <id> --force' to destroy it.
|
|
10702
|
+
${chalk29.dim("$")} mutagent sandbox spawn --image alpine:3.20
|
|
10703
|
+
${chalk29.dim("$")} mutagent sandbox spawn --image alpine:3.20 --detach --json
|
|
10704
|
+
${chalk29.dim("$")} mutagent sandbox spawn --image <helix-image> --egress allow
|
|
10705
|
+
${chalk29.dim("$")} mutagent sandbox spawn --image <ref> --egress allowlist --egress-allow domain:api.anthropic.com
|
|
10706
|
+
|
|
10707
|
+
--arch defaults to this machine's architecture; set it for an image built elsewhere.
|
|
10708
|
+
Egress defaults to deny. On Docker the sandbox then runs with --network none, so a Helix
|
|
10709
|
+
session in it cannot be reached and cannot call its LLM provider: use --egress allow
|
|
10710
|
+
for a Helix sandbox. An allowlist needs at least one --egress-allow rule
|
|
10711
|
+
(domain:, port: or cidr:).
|
|
10712
|
+
Without --detach, spawn attaches; Ctrl-C detaches and prints the re-attach command.
|
|
10958
10713
|
|
|
10959
10714
|
AI Agent Directive:
|
|
10960
|
-
Pass --egress allow
|
|
10961
|
-
session
|
|
10962
|
-
|
|
10963
|
-
|
|
10964
|
-
|
|
10965
|
-
|
|
10966
|
-
Without --detach, --json output is NDJSON and the final line is the summary.
|
|
10967
|
-
A spawned sandbox costs resources until 'mutagent sandbox rm <id>' — confirm
|
|
10968
|
-
with the user before spawning, and tell them the ID afterwards.
|
|
10715
|
+
Pass --egress allow when the sandbox is for a Helix session: under the default deny
|
|
10716
|
+
policy the session cannot be reached, and the error names the sandbox, not the
|
|
10717
|
+
policy. A sandbox used only for exec needs no egress.
|
|
10718
|
+
Prefer --detach --json for automation: it returns one object, { success, sandbox,
|
|
10719
|
+
_links }. A spawned sandbox runs until it is removed or stopped by the idle policy:
|
|
10720
|
+
confirm with the user before spawning, and tell them the sandbox ID.
|
|
10969
10721
|
`).action(async (options) => {
|
|
10970
10722
|
const isJson = getJsonFlag(jsonSource);
|
|
10971
10723
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -11087,10 +10839,10 @@ async function createSandbox(definition, isJson) {
|
|
|
11087
10839
|
const spinner = ora2(`Spawning ${definition.image} (${definition.arch})...`).start();
|
|
11088
10840
|
try {
|
|
11089
10841
|
const sandbox = await spawnSandbox(definition);
|
|
11090
|
-
spinner.succeed(
|
|
10842
|
+
spinner.succeed(chalk29.green(`Sandbox ${sandbox.id} spawned`));
|
|
11091
10843
|
return sandbox;
|
|
11092
10844
|
} catch (error) {
|
|
11093
|
-
spinner.fail(
|
|
10845
|
+
spinner.fail(chalk29.red("Spawn failed"));
|
|
11094
10846
|
throw error;
|
|
11095
10847
|
}
|
|
11096
10848
|
}
|
|
@@ -11125,30 +10877,24 @@ function reportDetached(sandbox, id, output, isJson) {
|
|
|
11125
10877
|
}
|
|
11126
10878
|
|
|
11127
10879
|
// src/commands/sandbox/exec.ts
|
|
11128
|
-
import
|
|
10880
|
+
import chalk30 from "chalk";
|
|
11129
10881
|
init_errors();
|
|
11130
10882
|
function registerExecCommand(parent, registration) {
|
|
11131
10883
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
11132
|
-
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
|
|
10884
|
+
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 a fresh sandbox (not with --sandbox)").option("--keep", "Keep the fresh sandbox after the command (not with --sandbox)").option("--cwd <dir>", "Working directory inside the sandbox").option("--timeout-ms <n>", "Command timeout in milliseconds").option("--no-stream", "With --sandbox, wait for the full result instead of streaming").addHelpText("after", `
|
|
11133
10885
|
Examples:
|
|
11134
|
-
${
|
|
11135
|
-
${
|
|
11136
|
-
${
|
|
11137
|
-
${
|
|
10886
|
+
${chalk30.dim("$")} mutagent sandbox exec -- uname -m
|
|
10887
|
+
${chalk30.dim("$")} mutagent sandbox exec --json --preset <name> -- uname -m
|
|
10888
|
+
${chalk30.dim("$")} mutagent sandbox exec --sandbox sbx_123 --cwd /src -- git status
|
|
10889
|
+
${chalk30.dim("$")} mutagent sandbox exec -- sh -c 'ls | wc -l'
|
|
11138
10890
|
|
|
11139
|
-
Put
|
|
11140
|
-
|
|
10891
|
+
Put this command's flags, and --json, before the sandbox command; everything after it
|
|
10892
|
+
goes to the sandbox unchanged (${chalk30.bold("--")} is optional). The command is argv, not a shell
|
|
10893
|
+
string: name a shell for pipes.
|
|
11141
10894
|
|
|
11142
|
-
|
|
11143
|
-
|
|
11144
|
-
|
|
11145
|
-
|
|
11146
|
-
WITH --sandbox runs in that box and streams as output is produced.
|
|
11147
|
-
WITHOUT --sandbox spawns a fresh box from a preset, runs, tears it down. That
|
|
11148
|
-
path is blocking, so --no-stream is redundant there and
|
|
11149
|
-
--preset / --keep apply only there.
|
|
11150
|
-
|
|
11151
|
-
The command's exit code becomes this command's exit code.
|
|
10895
|
+
Without --sandbox, a fresh sandbox starts from a preset (see sandbox presets), runs the
|
|
10896
|
+
command and is removed unless --keep. With --sandbox, output streams as it is produced;
|
|
10897
|
+
--preset and --keep are refused there. The sandbox command's exit code is this command's.
|
|
11152
10898
|
|
|
11153
10899
|
AI Agent Directive:
|
|
11154
10900
|
Use --no-stream --json (or the one-shot form, which is always blocking) for a
|
|
@@ -11273,35 +11019,30 @@ function reportResult(result, output, isJson, context) {
|
|
|
11273
11019
|
}
|
|
11274
11020
|
|
|
11275
11021
|
// src/commands/sandbox/attach.ts
|
|
11276
|
-
import
|
|
11022
|
+
import chalk31 from "chalk";
|
|
11277
11023
|
init_errors();
|
|
11278
11024
|
function registerAttachCommand(parent, registration) {
|
|
11279
11025
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
11280
|
-
parent.command("attach").description("
|
|
11026
|
+
parent.command("attach").description("Stream a running sandbox's output").argument("<id>", "Sandbox ID").option("--since <seq>", "Resume after this sequence number instead of from the start").addHelpText("after", `
|
|
11281
11027
|
Examples:
|
|
11282
|
-
${
|
|
11283
|
-
${
|
|
11284
|
-
${
|
|
11285
|
-
${
|
|
11028
|
+
${chalk31.dim("$")} mutagent sandbox attach sbx_123
|
|
11029
|
+
${chalk31.dim("$")} mutagent sandbox attach sbx_123 --since 412
|
|
11030
|
+
${chalk31.dim("$")} mutagent sandbox attach sbx_123 --json | tail -1
|
|
11031
|
+
${chalk31.dim("$")} mutagent sandbox attach sbx_123 > session.log
|
|
11286
11032
|
|
|
11287
|
-
Ctrl-C detaches
|
|
11288
|
-
|
|
11289
|
-
|
|
11033
|
+
Ctrl-C detaches; the sandbox keeps running and the re-attach command, with the resume
|
|
11034
|
+
point filled in, is printed to stderr. The sandbox runs until it is removed or stopped
|
|
11035
|
+
by the idle policy (see \`mutagent sandbox --help\`).
|
|
11290
11036
|
|
|
11291
|
-
Sandbox stdout goes to stdout and sandbox stderr
|
|
11292
|
-
and pipes behave as they would for a local process. Everything this CLI says
|
|
11293
|
-
about the session goes to stderr.
|
|
11037
|
+
Sandbox stdout goes to stdout and sandbox stderr to stderr; CLI messages go to stderr.
|
|
11294
11038
|
|
|
11295
|
-
|
|
11296
|
-
|
|
11297
|
-
plainly rather than being papered over.
|
|
11039
|
+
A dropped connection resumes from the last sequence number; output the server no
|
|
11040
|
+
longer holds is reported as a gap.
|
|
11298
11041
|
|
|
11299
11042
|
AI Agent Directive:
|
|
11300
|
-
|
|
11301
|
-
|
|
11302
|
-
|
|
11303
|
-
you only need the result.
|
|
11304
|
-
Run 'mutagent sandbox ls --json' first to confirm the sandbox is running.
|
|
11043
|
+
Run 'mutagent sandbox ls --json' first to confirm the sandbox is running. With
|
|
11044
|
+
--json the output is one JSON object per line; the last line is the summary, so
|
|
11045
|
+
read it with 'tail -1' when you only need the result.
|
|
11305
11046
|
`).action(async (id, options) => {
|
|
11306
11047
|
const isJson = getJsonFlag(jsonSource);
|
|
11307
11048
|
try {
|
|
@@ -11332,27 +11073,24 @@ AI Agent Directive:
|
|
|
11332
11073
|
}
|
|
11333
11074
|
|
|
11334
11075
|
// src/commands/sandbox/providers.ts
|
|
11335
|
-
import
|
|
11076
|
+
import chalk32 from "chalk";
|
|
11336
11077
|
init_errors();
|
|
11337
11078
|
function registerProvidersCommand(parent, registration) {
|
|
11338
11079
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
11339
|
-
parent.command("providers").description("List
|
|
11080
|
+
parent.command("providers").description("List sandbox providers for mutagent helix --sandbox-provider").addHelpText("after", `
|
|
11340
11081
|
Examples:
|
|
11341
|
-
${
|
|
11342
|
-
${
|
|
11343
|
-
|
|
11344
|
-
A PROVIDER is where a sandbox runs (docker, agentcore, …). It is a different
|
|
11345
|
-
axis from --env, which names a stored bundle of secrets and variables, and from
|
|
11346
|
-
--preset, which names a whole definition. Omitting --provider takes the default
|
|
11347
|
-
reported here.
|
|
11082
|
+
${chalk32.dim("$")} mutagent sandbox providers
|
|
11083
|
+
${chalk32.dim("$")} mutagent sandbox providers --json
|
|
11348
11084
|
|
|
11349
|
-
|
|
11350
|
-
|
|
11085
|
+
A sandbox provider is where a sandbox runs (for example docker or agentcore). It is not an
|
|
11086
|
+
LLM provider (\`mutagent providers\`) and not an Environment (\`mutagent env\`).
|
|
11087
|
+
\`mutagent helix --sandbox-provider <name>\` accepts a name here when a Helix preset runs
|
|
11088
|
+
on it. The list is the same for every workspace on this server.
|
|
11351
11089
|
|
|
11352
11090
|
AI Agent Directive:
|
|
11353
11091
|
--json returns { providers: [...], default: <name|null>, count: N }. Read it
|
|
11354
|
-
before passing --provider to 'spawn' or 'preflight'
|
|
11355
|
-
|
|
11092
|
+
before passing --provider to 'spawn' or 'preflight', or --sandbox-provider to
|
|
11093
|
+
'mutagent helix'; the server refuses an unknown sandbox provider.
|
|
11356
11094
|
`).action(async () => {
|
|
11357
11095
|
const isJson = getJsonFlag(jsonSource);
|
|
11358
11096
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -11363,7 +11101,7 @@ AI Agent Directive:
|
|
|
11363
11101
|
return;
|
|
11364
11102
|
}
|
|
11365
11103
|
if (list.names.length === 0) {
|
|
11366
|
-
console.log(
|
|
11104
|
+
console.log(chalk32.gray("No sandbox providers configured on this backend."));
|
|
11367
11105
|
return;
|
|
11368
11106
|
}
|
|
11369
11107
|
output.output(list.names.map((name) => ({ name, default: name === list.default ? "yes" : "" })));
|
|
@@ -11374,28 +11112,19 @@ AI Agent Directive:
|
|
|
11374
11112
|
}
|
|
11375
11113
|
function registerPreflightCommand(parent, registration) {
|
|
11376
11114
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
11377
|
-
parent.command("preflight").description("Check a
|
|
11115
|
+
parent.command("preflight").description("Check whether a sandbox provider can run a definition, without creating anything").requiredOption("--image <ref>", "Image reference (repository:tag or @digest)").option("--arch <arch>", "Image architecture: x86_64 | arm64 (default: this host's)").option("--name <name>", "Definition name (default: derived from the image reference)").option("--provider <name>", "Sandbox provider to check against (default: the server's)").option("--env <name>", "Workspace Environment the definition would load").option("--egress <policy>", "Outbound network: deny | allowlist | allow (default: deny)").option("--egress-allow <rule>", "domain:, port: or cidr: rule for --egress allowlist (repeatable)", (value, previous) => [...previous, value], []).addHelpText("after", `
|
|
11378
11116
|
Examples:
|
|
11379
|
-
${
|
|
11380
|
-
${
|
|
11381
|
-
${
|
|
11117
|
+
${chalk32.dim("$")} mutagent sandbox preflight --image alpine:3.20
|
|
11118
|
+
${chalk32.dim("$")} mutagent sandbox preflight --image alpine:3.20 --provider agentcore --json
|
|
11119
|
+
${chalk32.dim("$")} mutagent sandbox preflight --image <ref> --egress allowlist --egress-allow domain:api.anthropic.com
|
|
11382
11120
|
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
so a verdict here is about the object spawn would really send — not a near-miss.
|
|
11387
|
-
|
|
11388
|
-
EVERY violation comes back, not just the first, so one round trip is enough to
|
|
11389
|
-
fix a definition rather than one per problem.
|
|
11390
|
-
|
|
11391
|
-
INCOMPATIBLE IS NOT AN ERROR. A definition the provider refuses is a successful
|
|
11392
|
-
answer to the question you asked; the command exits 0 and reports
|
|
11393
|
-
compatible: false. A non-zero exit here means the CHECK failed — a bad flag, an
|
|
11394
|
-
unknown provider, a network problem.
|
|
11121
|
+
Takes the same flags as spawn and checks the definition spawn would send. Every
|
|
11122
|
+
violation is listed. An incompatible definition exits 0 with compatible: false;
|
|
11123
|
+
a non-zero exit means the check itself failed.
|
|
11395
11124
|
|
|
11396
11125
|
AI Agent Directive:
|
|
11397
|
-
--json returns { success, provider, compatible, violations
|
|
11398
|
-
'compatible', not the exit code
|
|
11126
|
+
--json returns { success, provider, compatible, violations, definition }. Read
|
|
11127
|
+
'compatible', not the exit code: an incompatible definition still exits 0.
|
|
11399
11128
|
`).action(async (options) => {
|
|
11400
11129
|
const isJson = getJsonFlag(jsonSource);
|
|
11401
11130
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -11416,7 +11145,7 @@ AI Agent Directive:
|
|
|
11416
11145
|
output.success(`${definition.name} is compatible with ${verdict.provider}`);
|
|
11417
11146
|
return;
|
|
11418
11147
|
}
|
|
11419
|
-
console.log(
|
|
11148
|
+
console.log(chalk32.yellow(`${definition.name} is NOT compatible with ${verdict.provider}:`));
|
|
11420
11149
|
for (const violation of verdict.violations)
|
|
11421
11150
|
console.log(` - ${violation}`);
|
|
11422
11151
|
} catch (error) {
|
|
@@ -11426,20 +11155,18 @@ AI Agent Directive:
|
|
|
11426
11155
|
}
|
|
11427
11156
|
|
|
11428
11157
|
// src/commands/sandbox/inventory.ts
|
|
11429
|
-
import
|
|
11158
|
+
import chalk33 from "chalk";
|
|
11430
11159
|
init_errors();
|
|
11431
11160
|
function registerLsCommand(parent, registration) {
|
|
11432
11161
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
11433
|
-
parent.command("ls").description("List
|
|
11162
|
+
parent.command("ls").description("List the workspace's live sandboxes").addHelpText("after", `
|
|
11434
11163
|
Examples:
|
|
11435
|
-
${
|
|
11436
|
-
${
|
|
11437
|
-
|
|
11438
|
-
Listing is always workspace-scoped — these are the sandboxes in the workspace
|
|
11439
|
-
you are configured for, not every sandbox on the account. Change it with:
|
|
11440
|
-
${chalk32.dim("$")} mutagent config set workspace <workspace-id>
|
|
11164
|
+
${chalk33.dim("$")} mutagent sandbox ls
|
|
11165
|
+
${chalk33.dim("$")} mutagent sandbox ls --json
|
|
11441
11166
|
|
|
11442
|
-
|
|
11167
|
+
Lists the sandboxes in the configured workspace
|
|
11168
|
+
(mutagent config set workspace <workspace-id>).
|
|
11169
|
+
An empty list means none are running.
|
|
11443
11170
|
|
|
11444
11171
|
AI Agent Directive:
|
|
11445
11172
|
Run this before attach, exec or rm to confirm the sandbox exists and is
|
|
@@ -11457,7 +11184,7 @@ AI Agent Directive:
|
|
|
11457
11184
|
return;
|
|
11458
11185
|
}
|
|
11459
11186
|
if (sandboxes.length === 0) {
|
|
11460
|
-
console.log(
|
|
11187
|
+
console.log(chalk33.gray("No sandboxes running."));
|
|
11461
11188
|
console.log("");
|
|
11462
11189
|
console.log(" Start one: mutagent sandbox spawn --image <ref>");
|
|
11463
11190
|
return;
|
|
@@ -11480,15 +11207,14 @@ function toRow(sandbox) {
|
|
|
11480
11207
|
}
|
|
11481
11208
|
function registerPresetsCommand(parent, registration) {
|
|
11482
11209
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
11483
|
-
parent.command("presets").description("List
|
|
11210
|
+
parent.command("presets").description("List presets for mutagent helix --preset").addHelpText("after", `
|
|
11484
11211
|
Examples:
|
|
11485
|
-
${
|
|
11486
|
-
${
|
|
11212
|
+
${chalk33.dim("$")} mutagent sandbox presets
|
|
11213
|
+
${chalk33.dim("$")} mutagent sandbox presets --json
|
|
11487
11214
|
|
|
11488
|
-
A preset is a named definition on the
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
image-level detail you are looking for 'helix spawn --image'.
|
|
11215
|
+
A preset is a named sandbox definition kept on the server. \`mutagent helix --preset <name>\`
|
|
11216
|
+
and \`mutagent sandbox exec --preset <name>\` use it. Image references are not shown.
|
|
11217
|
+
An unknown preset name is refused.
|
|
11492
11218
|
|
|
11493
11219
|
AI Agent Directive:
|
|
11494
11220
|
--json returns { presets: [...], default: <name|null>, count: N }. Read this
|
|
@@ -11504,7 +11230,7 @@ AI Agent Directive:
|
|
|
11504
11230
|
return;
|
|
11505
11231
|
}
|
|
11506
11232
|
if (presets.length === 0) {
|
|
11507
|
-
console.log(
|
|
11233
|
+
console.log(chalk33.gray("No presets configured on this backend."));
|
|
11508
11234
|
console.log("");
|
|
11509
11235
|
console.log(" Spawn with an explicit image: mutagent sandbox spawn --image <ref>");
|
|
11510
11236
|
return;
|
|
@@ -11523,21 +11249,19 @@ AI Agent Directive:
|
|
|
11523
11249
|
}
|
|
11524
11250
|
function registerRmCommand(parent, registration) {
|
|
11525
11251
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
11526
|
-
parent.command("rm").description("Destroy a sandbox").argument("<id>", "Sandbox ID").option("-f, --force", "
|
|
11252
|
+
parent.command("rm").description("Destroy a sandbox now").argument("<id>", "Sandbox ID").option("-f, --force", "Confirm (required unless --json)").addHelpText("after", `
|
|
11527
11253
|
Examples:
|
|
11528
|
-
${
|
|
11529
|
-
${
|
|
11530
|
-
|
|
11531
|
-
This is the ONLY command that destroys a sandbox — detaching, Ctrl-C and a
|
|
11532
|
-
closed terminal all leave it running. Removing a sandbox that is already gone
|
|
11533
|
-
succeeds, so a retried teardown is safe.
|
|
11254
|
+
${chalk33.dim("$")} mutagent sandbox rm sbx_123 --force
|
|
11255
|
+
${chalk33.dim("$")} mutagent sandbox rm sbx_123 --json
|
|
11534
11256
|
|
|
11535
|
-
|
|
11536
|
-
|
|
11257
|
+
Ends every session in the sandbox. The server first tries to capture a checkpoint,
|
|
11258
|
+
so a Helix session can be rebuilt with \`mutagent helix session restore <id>\`.
|
|
11259
|
+
Removing a sandbox that is already gone succeeds.
|
|
11260
|
+
Without --force (and without --json) the command refuses; it never prompts.
|
|
11537
11261
|
|
|
11538
11262
|
AI Agent Directive:
|
|
11539
|
-
|
|
11540
|
-
|
|
11263
|
+
Removing a sandbox ends every session in it now. Confirm with the user before
|
|
11264
|
+
running it, then pass --force (or --json).
|
|
11541
11265
|
`).action(async (id, options) => {
|
|
11542
11266
|
const isJson = getJsonFlag(jsonSource);
|
|
11543
11267
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -11567,17 +11291,13 @@ Use --force to confirm: mutagent sandbox rm ${id} --force`);
|
|
|
11567
11291
|
}
|
|
11568
11292
|
function registerTracesCommand(parent, registration) {
|
|
11569
11293
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
11570
|
-
parent.command("traces").description("Show the spans
|
|
11294
|
+
parent.command("traces").description("Show the spans recorded for a sandbox").argument("<id>", "Sandbox ID").addHelpText("after", `
|
|
11571
11295
|
Examples:
|
|
11572
|
-
${
|
|
11573
|
-
${
|
|
11574
|
-
|
|
11575
|
-
Spans are what the session actually did — the record to read when the output
|
|
11576
|
-
alone does not explain the result. They are called spans, not traces, because
|
|
11577
|
-
that is what the server returns; the command keeps the familiar name.
|
|
11296
|
+
${chalk33.dim("$")} mutagent sandbox traces sbx_123
|
|
11297
|
+
${chalk33.dim("$")} mutagent sandbox traces sbx_123 --json
|
|
11578
11298
|
|
|
11579
|
-
An empty
|
|
11580
|
-
|
|
11299
|
+
An empty list means no spans were recorded; telemetry must be configured on the
|
|
11300
|
+
server for spans to exist.
|
|
11581
11301
|
|
|
11582
11302
|
AI Agent Directive:
|
|
11583
11303
|
--json returns { spans: [...], count: N, sandboxId }. Read spans before
|
|
@@ -11594,7 +11314,7 @@ AI Agent Directive:
|
|
|
11594
11314
|
return;
|
|
11595
11315
|
}
|
|
11596
11316
|
if (spans.length === 0) {
|
|
11597
|
-
console.log(
|
|
11317
|
+
console.log(chalk33.gray(`No spans captured for sandbox ${id}.`));
|
|
11598
11318
|
return;
|
|
11599
11319
|
}
|
|
11600
11320
|
output.output(spans.map((span) => {
|
|
@@ -11613,17 +11333,14 @@ AI Agent Directive:
|
|
|
11613
11333
|
}
|
|
11614
11334
|
function registerStatusCommand(parent, registration) {
|
|
11615
11335
|
const jsonSource = registration?.jsonSource ?? parent;
|
|
11616
|
-
parent.command("status").description("
|
|
11336
|
+
parent.command("status").description("Print one sandbox's state").argument("<id>", "Sandbox ID").addHelpText("after", `
|
|
11617
11337
|
Examples:
|
|
11618
|
-
${
|
|
11619
|
-
${
|
|
11620
|
-
|
|
11621
|
-
ONE FIELD, DELIBERATELY. 'GET /api/sandbox/:id' answers with the lifecycle
|
|
11622
|
-
state and nothing else — provider, arch, definition and creation time come from
|
|
11623
|
-
the LIST route. Use 'mutagent sandbox ls --json' when you want the record.
|
|
11338
|
+
${chalk33.dim("$")} mutagent sandbox status sbx_123
|
|
11339
|
+
${chalk33.dim("$")} mutagent sandbox status sbx_123 --json
|
|
11624
11340
|
|
|
11625
11341
|
States: pending, starting, running, paused, stopping, stopped, failed.
|
|
11626
|
-
|
|
11342
|
+
For sandbox provider, arch and creation time use \`mutagent sandbox ls --json\`.
|
|
11343
|
+
A sandbox outside this workspace is reported as not found.
|
|
11627
11344
|
|
|
11628
11345
|
AI Agent Directive:
|
|
11629
11346
|
--json returns { success, sandboxId, status, _links }. Prefer this over
|
|
@@ -11646,58 +11363,41 @@ AI Agent Directive:
|
|
|
11646
11363
|
|
|
11647
11364
|
// src/commands/sandbox/index.ts
|
|
11648
11365
|
function createSandboxCommand() {
|
|
11649
|
-
const sandbox = new Command15("sandbox").description("
|
|
11366
|
+
const sandbox = new Command15("sandbox").description("Internal: sandbox management. Everyday use goes through `mutagent helix`.").addHelpText("after", `
|
|
11650
11367
|
Examples:
|
|
11651
|
-
${
|
|
11652
|
-
${
|
|
11653
|
-
${
|
|
11654
|
-
${
|
|
11655
|
-
${
|
|
11656
|
-
${
|
|
11657
|
-
${
|
|
11658
|
-
${
|
|
11659
|
-
${
|
|
11660
|
-
${
|
|
11661
|
-
|
|
11662
|
-
BOXES ARE NOT AGENTS. This group is the machine. To put an agent in one:
|
|
11663
|
-
${chalk33.dim("$")} mutagent helix agent --prompt "<who>" -p "<task>" ${chalk33.dim("One turn, fresh box")}
|
|
11664
|
-
${chalk33.dim("$")} mutagent helix session start sbx_1 --rpc ${chalk33.dim("An agent inside a box you own")}
|
|
11368
|
+
${chalk34.dim("$")} mutagent sandbox presets ${chalk34.dim("Presets `mutagent helix --preset` can name")}
|
|
11369
|
+
${chalk34.dim("$")} mutagent sandbox providers ${chalk34.dim("Sandbox providers `mutagent helix --sandbox-provider` can name")}
|
|
11370
|
+
${chalk34.dim("$")} mutagent sandbox preflight --image alpine:3.20 ${chalk34.dim("Would this definition run?")}
|
|
11371
|
+
${chalk34.dim("$")} mutagent sandbox spawn --image alpine:3.20 --detach
|
|
11372
|
+
${chalk34.dim("$")} mutagent sandbox ls
|
|
11373
|
+
${chalk34.dim("$")} mutagent sandbox status sbx_1
|
|
11374
|
+
${chalk34.dim("$")} mutagent sandbox exec --sandbox sbx_1 -- bun test
|
|
11375
|
+
${chalk34.dim("$")} mutagent sandbox attach sbx_1 --since 412
|
|
11376
|
+
${chalk34.dim("$")} mutagent sandbox traces sbx_1
|
|
11377
|
+
${chalk34.dim("$")} mutagent sandbox rm sbx_1 --force
|
|
11665
11378
|
|
|
11666
11379
|
One-shot vs persistent:
|
|
11667
|
-
${
|
|
11668
|
-
|
|
11669
|
-
${
|
|
11670
|
-
${chalk33.bold("attach")} and ${chalk33.bold("exec --sandbox")} against until you ${chalk33.bold("rm")} it.
|
|
11380
|
+
${chalk34.bold("exec")} without --sandbox starts a fresh sandbox from a preset, runs one command
|
|
11381
|
+
and removes the sandbox. ${chalk34.bold("spawn")} starts a sandbox from an image and leaves it running
|
|
11382
|
+
for ${chalk34.bold("attach")} and ${chalk34.bold("exec --sandbox")}.
|
|
11671
11383
|
|
|
11672
11384
|
Lifecycle:
|
|
11673
|
-
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
|
|
11677
|
-
|
|
11678
|
-
Access:
|
|
11679
|
-
Sandbox access is minted per WORKSPACE, so a workspace must be configured:
|
|
11680
|
-
${chalk33.dim("$")} mutagent config set workspace <workspace-id>
|
|
11385
|
+
Ctrl-C, a dropped connection or a closed terminal detaches; the sandbox keeps running.
|
|
11386
|
+
After 15 minutes with no input, attach, exec or agent turn, the server checkpoints
|
|
11387
|
+
and stops it. A Helix session in it can be rebuilt with
|
|
11388
|
+
\`mutagent helix session restore <id>\`. \`sandbox rm\` destroys a sandbox now.
|
|
11681
11389
|
|
|
11682
11390
|
Output:
|
|
11683
|
-
Sandbox stdout goes to stdout, sandbox stderr
|
|
11684
|
-
|
|
11685
|
-
|
|
11686
|
-
suppressed when the output is not a terminal or NO_COLOR is set.
|
|
11687
|
-
|
|
11688
|
-
Moved here from 'mutagent helix' (BREAKING):
|
|
11689
|
-
spawn, ls, rm, presets, exec, traces and attach used to live under
|
|
11690
|
-
'mutagent helix'. Those spellings are GONE — there is no alias. Rewrite
|
|
11691
|
-
'mutagent helix <verb>' as 'mutagent sandbox <verb>'.
|
|
11391
|
+
Sandbox stdout goes to stdout, sandbox stderr to stderr; CLI messages go to stderr.
|
|
11392
|
+
With --json, attach, spawn without --detach and exec --sandbox without --no-stream
|
|
11393
|
+
print one JSON object per line, ending with a summary. Other commands print one object.
|
|
11692
11394
|
|
|
11693
11395
|
AI Agent Directive:
|
|
11694
|
-
Run 'mutagent sandbox ls --json' before attach, exec --sandbox, status or rm.
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
|
|
11698
|
-
|
|
11699
|
-
Spawning consumes resources until removed — confirm with the user before
|
|
11700
|
-
'spawn' and before 'rm'.
|
|
11396
|
+
Run 'mutagent sandbox ls --json' before attach, exec --sandbox, status or rm. A
|
|
11397
|
+
spawned sandbox runs until it is removed or stopped by the idle policy: confirm
|
|
11398
|
+
with the user before 'spawn' and before 'rm', and tell them the sandbox ID.
|
|
11399
|
+
|
|
11400
|
+
Requires a workspace: mutagent config set workspace <workspace-id>
|
|
11701
11401
|
`);
|
|
11702
11402
|
registerSpawnCommand(sandbox);
|
|
11703
11403
|
registerLsCommand(sandbox);
|
|
@@ -11714,7 +11414,7 @@ AI Agent Directive:
|
|
|
11714
11414
|
|
|
11715
11415
|
// src/commands/env/index.ts
|
|
11716
11416
|
import { Command as Command16 } from "commander";
|
|
11717
|
-
import
|
|
11417
|
+
import chalk35 from "chalk";
|
|
11718
11418
|
import { readFileSync as readFileSync14 } from "fs";
|
|
11719
11419
|
init_errors();
|
|
11720
11420
|
|
|
@@ -11814,34 +11514,26 @@ function describeWithoutValue(arg) {
|
|
|
11814
11514
|
|
|
11815
11515
|
// src/commands/env/index.ts
|
|
11816
11516
|
function createEnvCommand() {
|
|
11817
|
-
const env = new Command16("env").description("Workspace Environments
|
|
11517
|
+
const env = new Command16("env").description("Workspace Environments: the variables and secrets an agent’s tools need").addHelpText("after", `
|
|
11818
11518
|
Examples:
|
|
11819
|
-
${
|
|
11820
|
-
${
|
|
11821
|
-
${
|
|
11822
|
-
${
|
|
11823
|
-
${
|
|
11824
|
-
${
|
|
11825
|
-
${
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
|
|
11835
|
-
|
|
11836
|
-
|
|
11837
|
-
is enough to tell whether the value you hold is the one stored.
|
|
11838
|
-
|
|
11839
|
-
Names: Environment ^[A-Za-z0-9._-]{1,64}$ · variables ^[A-Z_][A-Z0-9_]*$ · 64 KiB
|
|
11840
|
-
per Environment.
|
|
11841
|
-
|
|
11842
|
-
Access:
|
|
11843
|
-
Environments are minted per WORKSPACE, so a workspace must be configured:
|
|
11844
|
-
${chalk34.dim("$")} mutagent config set workspace <workspace-id>
|
|
11519
|
+
${chalk35.dim("$")} mutagent env ls
|
|
11520
|
+
${chalk35.dim("$")} mutagent env set demo GREETING=hello --secret GITHUB_TOKEN=ghp_…
|
|
11521
|
+
${chalk35.dim("$")} mutagent env set staging --from-file .env.staging --secrets-from-file .env.staging.secrets
|
|
11522
|
+
${chalk35.dim("$")} mutagent env show demo
|
|
11523
|
+
${chalk35.dim("$")} mutagent env unset demo GREETING
|
|
11524
|
+
${chalk35.dim("$")} mutagent env rm demo --force
|
|
11525
|
+
${chalk35.dim("$")} mutagent helix --env demo -p "check the deploy" ${chalk35.dim("Load it into a Helix cloud run")}
|
|
11526
|
+
|
|
11527
|
+
An Environment holds the variables and secrets an agent's tools need: a GitHub token,
|
|
11528
|
+
a Langfuse key, a database URL. LLM provider keys belong in \`mutagent providers\`; a
|
|
11529
|
+
variable named like one (ANTHROPIC_API_KEY, …) is refused unless --allow-provider-key.
|
|
11530
|
+
|
|
11531
|
+
Values are write-only. \`show\` prints each entry's name and a sha256[:8] fingerprint.
|
|
11532
|
+
|
|
11533
|
+
Names: Environments use letters, digits, '.', '_' and '-' (up to 64); variables use
|
|
11534
|
+
A-Z, 0-9 and _, not starting with a digit. An Environment holds up to 64 KiB.
|
|
11535
|
+
|
|
11536
|
+
Requires a workspace: mutagent config set workspace <workspace-id>
|
|
11845
11537
|
|
|
11846
11538
|
AI Agent Directive:
|
|
11847
11539
|
Use 'set' to create or update — it creates the Environment when absent and
|
|
@@ -11860,10 +11552,10 @@ AI Agent Directive:
|
|
|
11860
11552
|
function registerLs(parent) {
|
|
11861
11553
|
parent.command("ls").description("List the workspace’s Environments").addHelpText("after", `
|
|
11862
11554
|
Examples:
|
|
11863
|
-
${
|
|
11864
|
-
${
|
|
11555
|
+
${chalk35.dim("$")} mutagent env ls
|
|
11556
|
+
${chalk35.dim("$")} mutagent env ls --json
|
|
11865
11557
|
|
|
11866
|
-
An empty list
|
|
11558
|
+
An empty list means the workspace has no Environments yet.
|
|
11867
11559
|
|
|
11868
11560
|
AI Agent Directive:
|
|
11869
11561
|
--json returns { success, environments: [{ name, vars, secrets, createdAt, updatedAt }], count }.
|
|
@@ -11878,7 +11570,7 @@ AI Agent Directive:
|
|
|
11878
11570
|
return;
|
|
11879
11571
|
}
|
|
11880
11572
|
if (environments.length === 0) {
|
|
11881
|
-
console.log(
|
|
11573
|
+
console.log(chalk35.gray("No Environments in this workspace. Create one: mutagent env set <name> KEY=VALUE"));
|
|
11882
11574
|
return;
|
|
11883
11575
|
}
|
|
11884
11576
|
output.output(environments.map((e) => ({
|
|
@@ -11895,13 +11587,15 @@ AI Agent Directive:
|
|
|
11895
11587
|
function registerShow(parent) {
|
|
11896
11588
|
parent.command("show").description("Entry names and fingerprints of one Environment — never a value").argument("<name>", "Environment name").addHelpText("after", `
|
|
11897
11589
|
Examples:
|
|
11898
|
-
${
|
|
11899
|
-
${
|
|
11590
|
+
${chalk35.dim("$")} mutagent env show demo
|
|
11591
|
+
${chalk35.dim("$")} mutagent env show demo --json
|
|
11900
11592
|
|
|
11901
11593
|
Each line is NAME KIND FINGERPRINT. The fingerprint is sha256(value)[:8]; compare
|
|
11902
11594
|
it with your own (printf %s "$VALUE" | shasum -a 256 | cut -c1-8) to check what is
|
|
11903
11595
|
stored without the server ever sending the value.
|
|
11904
11596
|
|
|
11597
|
+
An unknown name is an error.
|
|
11598
|
+
|
|
11905
11599
|
AI Agent Directive:
|
|
11906
11600
|
--json returns { success, environment: { name, vars: [{name, fingerprint}],
|
|
11907
11601
|
secrets: [{name, fingerprint}], createdAt, updatedAt } }.
|
|
@@ -11917,37 +11611,29 @@ AI Agent Directive:
|
|
|
11917
11611
|
});
|
|
11918
11612
|
}
|
|
11919
11613
|
function registerSet(parent) {
|
|
11920
|
-
parent.command("set").description("Set variables (and --secret secrets) on an Environment; creates it when absent").argument("<name>", "Environment name").argument("[assignments...]", "KEY=VALUE variables (non-secret)").option("-s, --secret <KEY=VALUE...>", "Secret entries — encrypted at rest, never returned").option("--from-file <path>", "Read variables from a .env file (KEY=VALUE lines); argv wins on the same name").option("--secrets-from-file <path>", "Read secrets from a .env file").option("--replace", "Replace the whole Environment
|
|
11614
|
+
parent.command("set").description("Set variables (and --secret secrets) on an Environment; creates it when absent").argument("<name>", "Environment name").argument("[assignments...]", "KEY=VALUE variables (non-secret)").option("-s, --secret <KEY=VALUE...>", "Secret entries — encrypted at rest, never returned").option("--from-file <path>", "Read variables from a .env file (KEY=VALUE lines); argv wins on the same name").option("--secrets-from-file <path>", "Read secrets from a .env file").option("--replace", "Replace the whole Environment instead of merging into it").option("--allow-provider-key", "Permit a variable named like an LLM provider key").addHelpText("after", `
|
|
11921
11615
|
Examples:
|
|
11922
|
-
${
|
|
11923
|
-
${
|
|
11924
|
-
${
|
|
11925
|
-
${
|
|
11926
|
-
|
|
11927
|
-
|
|
11928
|
-
|
|
11929
|
-
|
|
11930
|
-
|
|
11931
|
-
|
|
11932
|
-
|
|
11933
|
-
|
|
11934
|
-
|
|
11935
|
-
|
|
11936
|
-
|
|
11937
|
-
|
|
11938
|
-
that a secret's value can never be read back through the API. A name may not be
|
|
11939
|
-
both.
|
|
11940
|
-
|
|
11941
|
-
A VALUE MAY CONTAIN '='. The split is at the first one: TOKEN=a=b stores "a=b".
|
|
11942
|
-
Quote values with spaces for your shell; this command does not re-split them.
|
|
11943
|
-
|
|
11944
|
-
Names: variables ^[A-Z_][A-Z0-9_]*$ · Environment ^[A-Za-z0-9._-]{1,64}$ · 64 KiB
|
|
11945
|
-
per Environment. A variable named like an LLM provider key (ANTHROPIC_API_KEY, …)
|
|
11946
|
-
is a 409 unless --allow-provider-key is passed — see 'mutagent env --help'.
|
|
11616
|
+
${chalk35.dim("$")} mutagent env set demo GREETING=hello
|
|
11617
|
+
${chalk35.dim("$")} mutagent env set demo GREETING=hello --secret TOKEN=abc123
|
|
11618
|
+
${chalk35.dim("$")} mutagent env set staging --from-file .env.staging
|
|
11619
|
+
${chalk35.dim("$")} mutagent env set staging --replace --from-file .env.staging ${chalk35.dim("Drop everything not in the file")}
|
|
11620
|
+
|
|
11621
|
+
Entries you name are set and the rest stay; the Environment is created if missing.
|
|
11622
|
+
--replace removes every entry you do not name, secrets included.
|
|
11623
|
+
Positional KEY=VALUE and --from-file are variables; --secret and --secrets-from-file are
|
|
11624
|
+
secrets (encrypted at rest, never returned). Both reach the box as env vars. A name
|
|
11625
|
+
cannot be both.
|
|
11626
|
+
--secret takes every KEY=VALUE after it, so put variables first:
|
|
11627
|
+
${chalk35.dim("$")} mutagent env set demo A=1 --secret TOKEN=x
|
|
11628
|
+
A value may contain '=': TOKEN=a=b stores "a=b".
|
|
11629
|
+
Names: A-Z, 0-9 and _, not starting with a digit. An invalid name exits 2. A name that
|
|
11630
|
+
looks like an LLM provider key is refused by the server (exit 1) unless
|
|
11631
|
+
--allow-provider-key.
|
|
11947
11632
|
|
|
11948
11633
|
AI Agent Directive:
|
|
11949
11634
|
Nothing printed here contains a value. --json returns { success, environment,
|
|
11950
|
-
replaced: boolean }.
|
|
11635
|
+
replaced: boolean }. An invalid name exits 2 and names the KEY, not the value;
|
|
11636
|
+
a name that looks like an LLM provider key exits 1 unless --allow-provider-key.
|
|
11951
11637
|
`).action(async (name, assignments, options) => {
|
|
11952
11638
|
const isJson = getJsonFlag(parent);
|
|
11953
11639
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -11977,12 +11663,11 @@ AI Agent Directive:
|
|
|
11977
11663
|
function registerUnset(parent) {
|
|
11978
11664
|
parent.command("unset").description("Remove entries from an Environment").argument("<name>", "Environment name").argument("<keys...>", "Entry names to remove").option("-s, --secret", "The names are secrets (default: try both variables and secrets)").addHelpText("after", `
|
|
11979
11665
|
Examples:
|
|
11980
|
-
${
|
|
11981
|
-
${
|
|
11666
|
+
${chalk35.dim("$")} mutagent env unset demo GREETING
|
|
11667
|
+
${chalk35.dim("$")} mutagent env unset demo TOKEN OTHER_TOKEN
|
|
11982
11668
|
|
|
11983
|
-
A name is removed wherever it is
|
|
11984
|
-
|
|
11985
|
-
in the state you asked for.
|
|
11669
|
+
A name is removed wherever it is, variable or secret. Unsetting a name that is not
|
|
11670
|
+
there succeeds.
|
|
11986
11671
|
|
|
11987
11672
|
AI Agent Directive:
|
|
11988
11673
|
--json returns { success, environment, removed: [names] }.
|
|
@@ -12007,15 +11692,17 @@ AI Agent Directive:
|
|
|
12007
11692
|
});
|
|
12008
11693
|
}
|
|
12009
11694
|
function registerRm(parent) {
|
|
12010
|
-
parent.command("rm").description("Delete an Environment and its secrets").argument("<name>", "Environment name").option("-f, --force", "
|
|
11695
|
+
parent.command("rm").description("Delete an Environment and its secrets").argument("<name>", "Environment name").option("-f, --force", "Confirm (required unless --json)").addHelpText("after", `
|
|
12011
11696
|
Examples:
|
|
12012
|
-
${
|
|
12013
|
-
${
|
|
11697
|
+
${chalk35.dim("$")} mutagent env rm demo --force
|
|
11698
|
+
${chalk35.dim("$")} mutagent env rm demo --json
|
|
12014
11699
|
|
|
12015
|
-
Deletes the Environment and its encrypted secrets. Boxes already running keep
|
|
12016
|
-
|
|
11700
|
+
Deletes the Environment and its encrypted secrets. Boxes already running keep what
|
|
11701
|
+
they were handed; the next run naming this Environment fails.
|
|
11702
|
+
Without --force (and without --json) the command refuses; it never prompts.
|
|
11703
|
+
An unknown name is an error.
|
|
12017
11704
|
|
|
12018
|
-
|
|
11705
|
+
JSON: { success, deleted, name }
|
|
12019
11706
|
|
|
12020
11707
|
AI Agent Directive:
|
|
12021
11708
|
Confirm with the user before running it, then pass --force (or --json).
|
|
@@ -12061,7 +11748,7 @@ function printEnvironment(environment, output, isJson) {
|
|
|
12061
11748
|
output.output({ success: true, environment });
|
|
12062
11749
|
return;
|
|
12063
11750
|
}
|
|
12064
|
-
console.log(`${
|
|
11751
|
+
console.log(`${chalk35.bold(environment.name)} ${chalk35.dim(environment.updatedAt ?? "")}`);
|
|
12065
11752
|
printEntries(environment);
|
|
12066
11753
|
}
|
|
12067
11754
|
function printEntries(environment) {
|
|
@@ -12070,12 +11757,12 @@ function printEntries(environment) {
|
|
|
12070
11757
|
...environment.secrets.map((s) => ({ name: s.name, kind: "secret", fingerprint: s.fingerprint }))
|
|
12071
11758
|
];
|
|
12072
11759
|
if (rows.length === 0) {
|
|
12073
|
-
console.log(
|
|
11760
|
+
console.log(chalk35.gray(" (no entries)"));
|
|
12074
11761
|
return;
|
|
12075
11762
|
}
|
|
12076
11763
|
const width = Math.max(...rows.map((r) => r.name.length));
|
|
12077
11764
|
for (const row of rows) {
|
|
12078
|
-
console.log(` ${row.name.padEnd(width)} ${row.kind === "secret" ?
|
|
11765
|
+
console.log(` ${row.name.padEnd(width)} ${row.kind === "secret" ? chalk35.yellow("secret") : chalk35.dim("var ")} ${chalk35.dim(row.fingerprint)}`);
|
|
12079
11766
|
}
|
|
12080
11767
|
}
|
|
12081
11768
|
|
|
@@ -12647,77 +12334,56 @@ program.name("mutagent").description(`Mutagent CLI - command-line client for the
|
|
|
12647
12334
|
});
|
|
12648
12335
|
program.addHelpText("beforeAll", () => renderBanner());
|
|
12649
12336
|
program.addHelpText("after", `
|
|
12650
|
-
${
|
|
12651
|
-
${
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
${
|
|
12657
|
-
|
|
12658
|
-
${
|
|
12659
|
-
|
|
12660
|
-
|
|
12661
|
-
|
|
12662
|
-
${
|
|
12663
|
-
|
|
12664
|
-
|
|
12665
|
-
|
|
12666
|
-
${
|
|
12667
|
-
|
|
12668
|
-
|
|
12669
|
-
|
|
12670
|
-
|
|
12671
|
-
|
|
12672
|
-
mutagent
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
|
|
12692
|
-
|
|
12693
|
-
|
|
12694
|
-
|
|
12695
|
-
|
|
12696
|
-
Every --json response may include:
|
|
12697
|
-
${chalk35.bold("_directive.display")} Type tag — 'status_card' for card-kind directives (drives test/docs guards)
|
|
12698
|
-
${chalk35.bold("_directive.renderedCard")} Pre-formatted card ${chalk35.red("(MUST echo verbatim in chat whenever this field exists — see SKILL.md Verbatim Card Display Protocol)")}
|
|
12699
|
-
${chalk35.bold("_directive.instruction")} Next step for the agent (self-sufficient, no Skill required)
|
|
12700
|
-
${chalk35.bold("_directive.next")} Array of suggested follow-up commands
|
|
12701
|
-
${chalk35.bold("_links")} Dashboard/API URLs (format as markdown links)
|
|
12702
|
-
${chalk35.bold("_compat")} Compat metadata: cliVersion, skillVersion, skillMinCliVersion
|
|
12703
|
-
|
|
12704
|
-
${chalk35.yellow("AI Agent Rules (MANDATORY for coding agents):")}
|
|
12705
|
-
1. Login (two paths):
|
|
12706
|
-
- CI / fully automated: export MUTAGENT_API_KEY=mt_... then mutagent login --json
|
|
12707
|
-
- Helping a user onboard: mutagent login --browser --json
|
|
12708
|
-
(CLI prints an auth URL — surface it to the user verbatim, then the CLI
|
|
12709
|
-
polls for up to 5 minutes while the user authorizes in their browser.
|
|
12710
|
-
This IS agent-compatible. --non-interactive is NOT needed with --browser.)
|
|
12711
|
-
2. EVERY command MUST include --json (no exceptions)
|
|
12712
|
-
3. Run <command> --help BEFORE first use of any command
|
|
12713
|
-
4. Parse _directive.renderedCard and copy it into your CHAT RESPONSE verbatim
|
|
12714
|
-
${chalk35.red("HARD STOP")}: do NOT run further commands until the card is rendered in chat
|
|
12715
|
-
5. After mutagent init, verify workspace: mutagent workspaces list --json
|
|
12716
|
-
6. ALL user interaction via AskUserQuestion — CLI is non-interactive
|
|
12337
|
+
${chalk36.yellow("Get started:")}
|
|
12338
|
+
mutagent login ${chalk36.dim("Sign in (browser or API key)")}
|
|
12339
|
+
mutagent providers mirror ${chalk36.dim("Copy local Helix LLM provider keys into the workspace")}
|
|
12340
|
+
mutagent helix -p "<task>" ${chalk36.dim("Run Helix in the cloud")}
|
|
12341
|
+
mutagent install helix ${chalk36.dim("Install Helix on this machine")}
|
|
12342
|
+
|
|
12343
|
+
${chalk36.yellow("Workspace:")}
|
|
12344
|
+
mutagent auth status ${chalk36.dim("Sign-in state and the active workspace")}
|
|
12345
|
+
mutagent workspaces list ${chalk36.dim("Workspaces you can see")}
|
|
12346
|
+
mutagent config set workspace <id> ${chalk36.dim("Choose the active workspace")}
|
|
12347
|
+
mutagent providers list ${chalk36.dim("LLM providers in the workspace")}
|
|
12348
|
+
mutagent helix models ${chalk36.dim("Models Helix can use in the cloud, and the workspace default model")}
|
|
12349
|
+
mutagent env ls ${chalk36.dim("Environments: variables and secrets for tools")}
|
|
12350
|
+
mutagent usage ${chalk36.dim("Count the workspace's LLM providers and your workspaces")}
|
|
12351
|
+
|
|
12352
|
+
${chalk36.yellow("Automation:")}
|
|
12353
|
+
MUTAGENT_API_KEY=mt_... ${chalk36.dim("Sign in without a browser (or --api-key <key>)")}
|
|
12354
|
+
--json ${chalk36.dim("Structured output")}
|
|
12355
|
+
--non-interactive ${chalk36.dim("No sign-in method or consent prompts (also when CI=true or stdin is not a terminal); a first sign-in in a terminal ends with a menu unless --json")}
|
|
12356
|
+
-v, --version ${chalk36.dim("CLI version (only before a subcommand)")}
|
|
12357
|
+
|
|
12358
|
+
${chalk36.yellow("Report a problem:")}
|
|
12359
|
+
mutagent feedback send "<what happened>" --category cli
|
|
12360
|
+
${chalk36.dim("--category accepts: cli | helix | stage:<spec|build|evaluate|diagnose|optimize>")}
|
|
12361
|
+
|
|
12362
|
+
${chalk36.dim("For CLI usage guidance for AI agents, see the Skill at")}
|
|
12363
|
+
${chalk36.cyan(".claude/skills/mutagent-cli/SKILL.md")}
|
|
12364
|
+
|
|
12365
|
+
${chalk36.yellow("Directive System:")}
|
|
12366
|
+
A --json response may include:
|
|
12367
|
+
${chalk36.bold("_directive.renderedCard")} A status card: copy it into your chat reply verbatim before running another command
|
|
12368
|
+
${chalk36.bold("_directive.instruction")} What to do next
|
|
12369
|
+
${chalk36.bold("_directive.next")} Suggested follow-up commands
|
|
12370
|
+
${chalk36.bold("_links")} Dashboard and API URLs
|
|
12371
|
+
${chalk36.bold("_compat")} cliVersion, skillVersion, skillMinCliVersion
|
|
12372
|
+
|
|
12373
|
+
${chalk36.yellow("AI Agent Rules (MANDATORY for coding agents):")}
|
|
12374
|
+
1. Sign in: with MUTAGENT_API_KEY set, run mutagent login --json. To help a user sign in,
|
|
12375
|
+
run mutagent login --browser --json, show the printed URL to the user verbatim, and
|
|
12376
|
+
wait: the CLI polls for up to 5 minutes.
|
|
12377
|
+
2. Pass --json to every command except a mutagent helix launch (-p, --mode json or rpc), whose output is Helix's own.
|
|
12378
|
+
3. Run <command> --help before the first use of a command.
|
|
12379
|
+
4. When a response has _directive.renderedCard, copy it into your chat reply verbatim
|
|
12380
|
+
before running another command.
|
|
12381
|
+
5. After mutagent init, check the workspace: mutagent workspaces list --json
|
|
12382
|
+
6. Ask the user through your own question tool. With --json only mutagent login can prompt, in a terminal; pass --browser or set MUTAGENT_API_KEY.
|
|
12717
12383
|
${!hasCredentials() ? `
|
|
12718
|
-
` +
|
|
12384
|
+
` + chalk36.yellow(" Warning: Not authenticated. Run: mutagent login") + `
|
|
12719
12385
|
` : ""}${!hasRcConfig() ? `
|
|
12720
|
-
` +
|
|
12386
|
+
` + chalk36.green(" Get started: mutagent init") + `
|
|
12721
12387
|
` : ""}`);
|
|
12722
12388
|
var rawArgs = process.argv.slice(2);
|
|
12723
12389
|
if (isGlobalVersionFlag(rawArgs)) {
|
|
@@ -12782,5 +12448,5 @@ var argvForCommander = execCommand === undefined ? rawArgs : protectBoxArgv(rawA
|
|
|
12782
12448
|
if (!helixHelpJson)
|
|
12783
12449
|
program.parse(argvForCommander, { from: "user" });
|
|
12784
12450
|
|
|
12785
|
-
//# debugId=
|
|
12451
|
+
//# debugId=0381115E4249D9B864756E2164756E21
|
|
12786
12452
|
//# sourceMappingURL=cli.js.map
|