axexec 1.6.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -48
- package/dist/agents/claude-code/types.d.ts +2 -2
- package/dist/agents/copilot/stream-session.js +8 -7
- package/dist/agents/copilot/watch-session.js +3 -1
- package/dist/agents/gemini/types.d.ts +4 -4
- package/dist/agents/opencode/adapter.js +8 -1
- package/dist/agents/opencode/build-permission-environment.d.ts +17 -0
- package/dist/agents/opencode/build-permission-environment.js +31 -0
- package/dist/agents/opencode/parse-sse-event.js +3 -1
- package/dist/agents/opencode/spawn-server.js +12 -1
- package/dist/build-agent-environment.js +8 -0
- package/dist/build-execution-metadata.js +1 -1
- package/dist/cli.d.ts +5 -6
- package/dist/cli.js +82 -61
- package/dist/credentials/get-credential-environment.d.ts +2 -2
- package/dist/credentials/get-credential-environment.js +5 -3
- package/dist/credentials/install-credentials.d.ts +3 -3
- package/dist/credentials/install-credentials.js +8 -8
- package/dist/credentials/write-agent-credentials.d.ts +1 -3
- package/dist/credentials/write-agent-credentials.js +1 -2
- package/dist/execute-agent.js +1 -0
- package/dist/format-zod-error.d.ts +6 -0
- package/dist/format-zod-error.js +12 -0
- package/dist/parse-credentials.js +1 -18
- package/dist/read-credentials-file.d.ts +13 -0
- package/dist/read-credentials-file.js +33 -0
- package/dist/read-stdin.d.ts +5 -0
- package/dist/read-stdin.js +11 -0
- package/dist/resolve-credentials.d.ts +20 -0
- package/dist/resolve-credentials.js +22 -0
- package/dist/resolve-output-mode.d.ts +15 -1
- package/dist/resolve-output-mode.js +16 -1
- package/dist/resolve-prompt.d.ts +22 -0
- package/dist/resolve-prompt.js +23 -0
- package/dist/run-agent.d.ts +4 -1
- package/dist/run-agent.js +32 -17
- package/dist/types/run-result.d.ts +4 -0
- package/dist/validate-cwd.d.ts +11 -0
- package/dist/validate-cwd.js +24 -0
- package/dist/validate-opencode-options.d.ts +18 -0
- package/dist/validate-opencode-options.js +50 -0
- package/dist/validate-stdin-usage.d.ts +18 -0
- package/dist/validate-stdin-usage.js +28 -0
- package/package.json +6 -6
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* axexec CLI - Unified agent execution with isolation.
|
|
3
|
+
* axexec CLI - Unified agent execution with credential/config isolation.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* - Normalized event stream output
|
|
5
|
+
* axexec is a headless runner and normalization layer. It isolates agent
|
|
6
|
+
* credentials/config from your local environment via temp directories, but it
|
|
7
|
+
* is not an OS sandbox and is intentionally permissive/non-interactive by
|
|
8
|
+
* default. Use an external sandbox (container/VM) for real enforcement.
|
|
10
9
|
*/
|
|
11
10
|
import { Command } from "@commander-js/extra-typings";
|
|
12
11
|
import packageJson from "../package.json" with { type: "json" };
|
|
@@ -17,14 +16,15 @@ import "./agents/gemini/adapter.js";
|
|
|
17
16
|
import "./agents/opencode/adapter.js";
|
|
18
17
|
import "./agents/copilot/adapter.js";
|
|
19
18
|
import { listAdapters } from "./agents/registry.js";
|
|
19
|
+
import { readStdin } from "./read-stdin.js";
|
|
20
|
+
import { resolveCredentials } from "./resolve-credentials.js";
|
|
21
|
+
import { parseOutputFormat } from "./resolve-output-mode.js";
|
|
22
|
+
import { resolvePrompt } from "./resolve-prompt.js";
|
|
20
23
|
import { runAgent } from "./run-agent.js";
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
return data.trimEnd();
|
|
27
|
-
}
|
|
24
|
+
import { validateAgent } from "./validate-agent.js";
|
|
25
|
+
import { validateCwd } from "./validate-cwd.js";
|
|
26
|
+
import { validateProviderOptions } from "./validate-opencode-options.js";
|
|
27
|
+
import { validateStdinUsage } from "./validate-stdin-usage.js";
|
|
28
28
|
const program = new Command()
|
|
29
29
|
.name(packageJson.name)
|
|
30
30
|
.description(packageJson.description)
|
|
@@ -33,12 +33,14 @@ const program = new Command()
|
|
|
33
33
|
.showSuggestionAfterError()
|
|
34
34
|
.option("--list-agents", "List available agents")
|
|
35
35
|
.option("-a, --agent <id>", "Agent to use (required)")
|
|
36
|
+
.option("--cwd <path>", "Working directory for the agent process")
|
|
37
|
+
.option("--credentials-file <path|->", "Read credentials JSON from file (or '-' for stdin)")
|
|
36
38
|
.option("-p, --prompt <text>", "Prompt text")
|
|
37
39
|
.option("-m, --model <model>", "Model to use")
|
|
38
40
|
.option("--provider <provider>", "Provider for OpenCode (required for OpenCode)")
|
|
39
|
-
.option("--allow <perms>", "
|
|
40
|
-
.option("--deny <perms>", "
|
|
41
|
-
.option("-f, --format <fmt>", "Output format: jsonl, tsv (default: tsv,
|
|
41
|
+
.option("--allow <perms>", "Allow permissions (best-effort, comma-separated)")
|
|
42
|
+
.option("--deny <perms>", "Deny permissions (best-effort, comma-separated)")
|
|
43
|
+
.option("-f, --format <fmt>", "Output format: jsonl, tsv (default when omitted: truncated tsv on TTY, full tsv otherwise)")
|
|
42
44
|
.option("--raw-log <file>", "Write raw agent output to file")
|
|
43
45
|
.option("--debug", "Enable debug mode")
|
|
44
46
|
.option("--verbose", "Show agent stderr output")
|
|
@@ -48,9 +50,11 @@ const program = new Command()
|
|
|
48
50
|
Requirements:
|
|
49
51
|
Install the agent CLIs you plan to use: claude, codex, gemini, opencode, copilot.
|
|
50
52
|
Override paths: AXEXEC_CLAUDE_PATH, AXEXEC_CODEX_PATH, AXEXEC_GEMINI_PATH, AXEXEC_OPENCODE_PATH, AXEXEC_COPILOT_PATH
|
|
53
|
+
Security: axexec is not a sandbox. Run inside an external sandbox for enforcement.
|
|
51
54
|
|
|
52
55
|
Examples:
|
|
53
56
|
axexec -a claude "Refactor auth flow"
|
|
57
|
+
axexec -a claude --credentials-file ./creds.json "Review this PR"
|
|
54
58
|
axexec -a opencode --provider anthropic -m claude-sonnet-4 "Hello"
|
|
55
59
|
axexec -a claude -f jsonl "Audit deps" | jq 'select(.type=="tool.call")'
|
|
56
60
|
axexec --list-agents | tail -n +2 | cut -f1
|
|
@@ -65,71 +69,89 @@ Examples:
|
|
|
65
69
|
}
|
|
66
70
|
return;
|
|
67
71
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// blocking in CI environments with open but unused stdin.
|
|
71
|
-
const needsStdinPrompt = !options.prompt && !positionalPrompt;
|
|
72
|
-
const stdinPrompt = needsStdinPrompt && !process.stdin.isTTY ? await readStdin() : undefined;
|
|
73
|
-
const prompt = (options.prompt ??
|
|
74
|
-
positionalPrompt ??
|
|
75
|
-
stdinPrompt)?.trimEnd();
|
|
76
|
-
if (!prompt) {
|
|
77
|
-
process.stderr.write("Error: prompt is required\n");
|
|
72
|
+
if (!options.agent) {
|
|
73
|
+
process.stderr.write("Error: --agent is required\n");
|
|
78
74
|
process.stderr.write("Usage: axexec --agent <id> <prompt>\n");
|
|
79
75
|
process.stderr.write("Try 'axexec --help' for more information.\n");
|
|
80
76
|
process.exitCode = 2;
|
|
81
77
|
return;
|
|
82
78
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
process.stderr.write(
|
|
79
|
+
// Validate agent ID early to provide clear error before processing credentials
|
|
80
|
+
const agentValidation = validateAgent(options.agent);
|
|
81
|
+
if (!agentValidation.ok) {
|
|
82
|
+
process.stderr.write(`${agentValidation.message}\n`);
|
|
83
|
+
process.exitCode = agentValidation.exitCode;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// Validate --cwd if provided
|
|
87
|
+
if (options.cwd) {
|
|
88
|
+
const cwdResult = await validateCwd(options.cwd);
|
|
89
|
+
if (!cwdResult.ok) {
|
|
90
|
+
process.stderr.write(`Error: ${cwdResult.error}\n`);
|
|
91
|
+
process.exitCode = 2;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const needsStdinPrompt = !options.prompt && !positionalPrompt;
|
|
96
|
+
const stdinResult = validateStdinUsage(options.credentialsFile, needsStdinPrompt, process.stdin.isTTY);
|
|
97
|
+
if (!stdinResult.ok) {
|
|
98
|
+
process.stderr.write(`Error: ${stdinResult.error}\n`);
|
|
87
99
|
process.exitCode = 2;
|
|
88
100
|
return;
|
|
89
101
|
}
|
|
102
|
+
// Get prompt from flag, positional argument, or stdin (in priority order)
|
|
103
|
+
// Only read stdin if no prompt was provided via CLI arguments to avoid
|
|
104
|
+
// blocking in CI environments with open but unused stdin.
|
|
105
|
+
const stdinPrompt = needsStdinPrompt &&
|
|
106
|
+
!process.stdin.isTTY &&
|
|
107
|
+
options.credentialsFile !== "-"
|
|
108
|
+
? await readStdin()
|
|
109
|
+
: undefined;
|
|
110
|
+
const promptResult = resolvePrompt({
|
|
111
|
+
promptFlag: options.prompt,
|
|
112
|
+
positionalPrompt,
|
|
113
|
+
stdinPrompt,
|
|
114
|
+
});
|
|
115
|
+
if (!promptResult.ok) {
|
|
116
|
+
process.stderr.write(`Error: ${promptResult.error}\n`);
|
|
117
|
+
process.exitCode = 2;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const { prompt } = promptResult;
|
|
90
121
|
// Validate format option
|
|
91
122
|
let format;
|
|
92
123
|
if (options.format) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
else {
|
|
97
|
-
process.stderr.write(`Error: Invalid format '${options.format}'\n`);
|
|
98
|
-
process.stderr.write("Valid formats: jsonl, tsv\n");
|
|
124
|
+
const formatResult = parseOutputFormat(options.format);
|
|
125
|
+
if (!formatResult.ok) {
|
|
126
|
+
process.stderr.write(`Error: ${formatResult.error}\n`);
|
|
99
127
|
process.exitCode = 2;
|
|
100
128
|
return;
|
|
101
129
|
}
|
|
130
|
+
format = formatResult.format;
|
|
102
131
|
}
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
132
|
+
// Resolve credentials from --credentials-file if specified.
|
|
133
|
+
const credentialsResult = await resolveCredentials(options.credentialsFile, readStdin);
|
|
134
|
+
if (!credentialsResult.ok) {
|
|
135
|
+
process.stderr.write(`Error: ${credentialsResult.error}\n`);
|
|
106
136
|
process.exitCode = 2;
|
|
107
137
|
return;
|
|
108
138
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
process.stderr.write(` axexec -a opencode --provider ${provider} -m ${model} ...\n`);
|
|
117
|
-
process.exitCode = 2;
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
// Require --provider for OpenCode
|
|
121
|
-
if (!options.provider) {
|
|
122
|
-
process.stderr.write("Error: OpenCode requires --provider\n");
|
|
123
|
-
process.stderr.write(" axexec -a opencode --provider anthropic -m claude-sonnet-4 ...\n");
|
|
124
|
-
process.exitCode = 2;
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
139
|
+
const { credentials } = credentialsResult;
|
|
140
|
+
// Validate provider options
|
|
141
|
+
const providerResult = validateProviderOptions(options.agent, options.model, options.provider);
|
|
142
|
+
if (!providerResult.ok) {
|
|
143
|
+
process.stderr.write(`Error: ${providerResult.error}\n`);
|
|
144
|
+
process.exitCode = 2;
|
|
145
|
+
return;
|
|
127
146
|
}
|
|
147
|
+
const { normalizedProvider } = providerResult;
|
|
128
148
|
// Run agent
|
|
129
149
|
const result = await runAgent(options.agent, {
|
|
130
150
|
prompt,
|
|
151
|
+
cwd: options.cwd,
|
|
152
|
+
credentials,
|
|
131
153
|
model: options.model,
|
|
132
|
-
provider:
|
|
154
|
+
provider: normalizedProvider,
|
|
133
155
|
allow: options.allow,
|
|
134
156
|
deny: options.deny,
|
|
135
157
|
format,
|
|
@@ -138,9 +160,8 @@ Examples:
|
|
|
138
160
|
verbose: options.verbose,
|
|
139
161
|
preserveGithubSha: options.preserveGithubSha,
|
|
140
162
|
});
|
|
141
|
-
// Set exit code based on success
|
|
142
|
-
if (!result.success) {
|
|
143
|
-
// eslint-disable-next-line require-atomic-updates
|
|
163
|
+
// Set exit code based on success (only if not already set to a more specific code)
|
|
164
|
+
if (!result.success && process.exitCode === undefined) {
|
|
144
165
|
process.exitCode = 1;
|
|
145
166
|
}
|
|
146
167
|
});
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* Each agent has its own env var requirements. This module extracts
|
|
5
5
|
* the appropriate values from Credentials and returns them as env vars.
|
|
6
6
|
*/
|
|
7
|
-
import type { Credentials } from "axshared";
|
|
7
|
+
import type { AgentCli, Credentials } from "axshared";
|
|
8
8
|
/**
|
|
9
9
|
* Gets additional environment variables for credential-based auth.
|
|
10
10
|
*
|
|
11
11
|
* Some agents use environment variables for API keys or OAuth tokens
|
|
12
12
|
* in addition to or instead of file-based credentials.
|
|
13
13
|
*/
|
|
14
|
-
declare function getCredentialEnvironment(credentials: Credentials): Record<string, string>;
|
|
14
|
+
declare function getCredentialEnvironment(agentId: AgentCli, credentials: Credentials, provider?: string): Record<string, string>;
|
|
15
15
|
export { getCredentialEnvironment };
|
|
@@ -11,7 +11,7 @@ import { resolveStringField } from "./resolve-string-field.js";
|
|
|
11
11
|
* Some agents use environment variables for API keys or OAuth tokens
|
|
12
12
|
* in addition to or instead of file-based credentials.
|
|
13
13
|
*/
|
|
14
|
-
function getCredentialEnvironment(credentials) {
|
|
14
|
+
function getCredentialEnvironment(agentId, credentials, provider) {
|
|
15
15
|
const environment = {};
|
|
16
16
|
// Extract API key from credentials data
|
|
17
17
|
const apiKey = resolveStringField(credentials.data, "apiKey", "key");
|
|
@@ -19,7 +19,7 @@ function getCredentialEnvironment(credentials) {
|
|
|
19
19
|
// axauth uses "accessToken", but also check legacy "oauthToken" and "token" keys
|
|
20
20
|
const oauthToken = resolveStringField(credentials.data, "accessToken", "oauthToken") ??
|
|
21
21
|
resolveStringField(credentials.data, "token", "token");
|
|
22
|
-
switch (
|
|
22
|
+
switch (agentId) {
|
|
23
23
|
case "claude": {
|
|
24
24
|
if (credentials.type === "api-key" && apiKey) {
|
|
25
25
|
environment["ANTHROPIC_API_KEY"] = apiKey;
|
|
@@ -51,7 +51,6 @@ function getCredentialEnvironment(credentials) {
|
|
|
51
51
|
}
|
|
52
52
|
case "opencode": {
|
|
53
53
|
// OpenCode supports multiple providers, env var depends on provider
|
|
54
|
-
const provider = credentials.provider;
|
|
55
54
|
if (credentials.type === "api-key" && apiKey) {
|
|
56
55
|
switch (provider) {
|
|
57
56
|
case "anthropic": {
|
|
@@ -71,6 +70,9 @@ function getCredentialEnvironment(credentials) {
|
|
|
71
70
|
environment["OPENCODE_API_KEY"] = apiKey;
|
|
72
71
|
break;
|
|
73
72
|
}
|
|
73
|
+
case undefined: {
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
74
76
|
}
|
|
75
77
|
}
|
|
76
78
|
break;
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Writes credentials to a temporary directory in agent-specific formats,
|
|
5
5
|
* then returns the environment variables needed to point the agent at that
|
|
6
|
-
* directory. This ensures
|
|
7
|
-
* locally installed credentials.
|
|
6
|
+
* directory. This ensures credential isolation: agents don't discover or use
|
|
7
|
+
* locally installed credentials/config.
|
|
8
8
|
*/
|
|
9
9
|
import { type AgentCli, type Credentials } from "axshared";
|
|
10
10
|
import type { InstallResult } from "./types.js";
|
|
@@ -19,7 +19,7 @@ type WarningWriter = (message: string) => void;
|
|
|
19
19
|
*
|
|
20
20
|
* The caller is responsible for cleaning up the temp directory after use.
|
|
21
21
|
*/
|
|
22
|
-
declare function installCredentials(agentId: AgentCli, credentials?: Credentials, warn?: WarningWriter): Promise<InstallResult>;
|
|
22
|
+
declare function installCredentials(agentId: AgentCli, credentials?: Credentials, provider?: string, warn?: WarningWriter): Promise<InstallResult>;
|
|
23
23
|
/**
|
|
24
24
|
* Cleanup for use in finally blocks.
|
|
25
25
|
*/
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Writes credentials to a temporary directory in agent-specific formats,
|
|
5
5
|
* then returns the environment variables needed to point the agent at that
|
|
6
|
-
* directory. This ensures
|
|
7
|
-
* locally installed credentials.
|
|
6
|
+
* directory. This ensures credential isolation: agents don't discover or use
|
|
7
|
+
* locally installed credentials/config.
|
|
8
8
|
*/
|
|
9
9
|
import { mkdirSync } from "node:fs";
|
|
10
10
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
@@ -42,7 +42,7 @@ async function createTemporaryDirectories(agentId) {
|
|
|
42
42
|
/**
|
|
43
43
|
* Installs agent-specific credentials to the appropriate directory.
|
|
44
44
|
*/
|
|
45
|
-
function installAgentCredentials(agentId, configDirectory, dataDirectory, credentials, warn) {
|
|
45
|
+
function installAgentCredentials(agentId, configDirectory, dataDirectory, credentials, provider, warn) {
|
|
46
46
|
switch (agentId) {
|
|
47
47
|
case "claude": {
|
|
48
48
|
installClaudeCredentials(configDirectory, credentials);
|
|
@@ -57,8 +57,8 @@ function installAgentCredentials(agentId, configDirectory, dataDirectory, creden
|
|
|
57
57
|
break;
|
|
58
58
|
}
|
|
59
59
|
case "opencode": {
|
|
60
|
-
if (
|
|
61
|
-
installOpenCodeCredentials(dataDirectory, credentials, warn);
|
|
60
|
+
if (provider) {
|
|
61
|
+
installOpenCodeCredentials(dataDirectory, credentials, provider, warn);
|
|
62
62
|
}
|
|
63
63
|
break;
|
|
64
64
|
}
|
|
@@ -78,15 +78,15 @@ function installAgentCredentials(agentId, configDirectory, dataDirectory, creden
|
|
|
78
78
|
*
|
|
79
79
|
* The caller is responsible for cleaning up the temp directory after use.
|
|
80
80
|
*/
|
|
81
|
-
async function installCredentials(agentId, credentials, warn) {
|
|
81
|
+
async function installCredentials(agentId, credentials, provider, warn) {
|
|
82
82
|
const { baseDirectory, configDirectory, dataDirectory } = await createTemporaryDirectories(agentId);
|
|
83
83
|
try {
|
|
84
84
|
if (credentials) {
|
|
85
|
-
installAgentCredentials(agentId, configDirectory, dataDirectory, credentials, warn);
|
|
85
|
+
installAgentCredentials(agentId, configDirectory, dataDirectory, credentials, provider, warn);
|
|
86
86
|
}
|
|
87
87
|
const runtimeEnvironment = buildAgentRuntimeEnvironment(agentId, configDirectory, dataDirectory);
|
|
88
88
|
const credentialEnvironment = credentials
|
|
89
|
-
? getCredentialEnvironment(credentials)
|
|
89
|
+
? getCredentialEnvironment(agentId, credentials, provider)
|
|
90
90
|
: {};
|
|
91
91
|
return {
|
|
92
92
|
env: { ...runtimeEnvironment, ...credentialEnvironment },
|
|
@@ -54,7 +54,5 @@ declare function installGeminiCredentials(configDirectory: string, credentials:
|
|
|
54
54
|
* - oauth-credentials: Written as { [provider]: { type: "oauth", ...data } }
|
|
55
55
|
* - api-key: Written as { [provider]: { type: "api", key: "..." } }
|
|
56
56
|
*/
|
|
57
|
-
declare function installOpenCodeCredentials(dataDirectory: string, credentials:
|
|
58
|
-
agent: "opencode";
|
|
59
|
-
}>, warn?: WarningWriter): void;
|
|
57
|
+
declare function installOpenCodeCredentials(dataDirectory: string, credentials: Credentials, provider: string, warn?: WarningWriter): void;
|
|
60
58
|
export { installClaudeCredentials, installCodexCredentials, installGeminiCredentials, installOpenCodeCredentials, };
|
|
@@ -100,9 +100,8 @@ function installGeminiCredentials(configDirectory, credentials) {
|
|
|
100
100
|
* - oauth-credentials: Written as { [provider]: { type: "oauth", ...data } }
|
|
101
101
|
* - api-key: Written as { [provider]: { type: "api", key: "..." } }
|
|
102
102
|
*/
|
|
103
|
-
function installOpenCodeCredentials(dataDirectory, credentials, warn = defaultWarningWriter) {
|
|
103
|
+
function installOpenCodeCredentials(dataDirectory, credentials, provider, warn = defaultWarningWriter) {
|
|
104
104
|
const authPath = path.join(dataDirectory, "auth.json");
|
|
105
|
-
const provider = credentials.provider;
|
|
106
105
|
switch (credentials.type) {
|
|
107
106
|
case "oauth-credentials": {
|
|
108
107
|
saveJsonFile(authPath, {
|
package/dist/execute-agent.js
CHANGED
|
@@ -12,6 +12,7 @@ async function executeAgent(agentId, adapter, options, configEnvironment, creden
|
|
|
12
12
|
const runOptions = {
|
|
13
13
|
prompt: options.prompt,
|
|
14
14
|
verbose: options.verbose ?? false,
|
|
15
|
+
cwd: options.cwd,
|
|
15
16
|
model: options.model,
|
|
16
17
|
provider: options.provider,
|
|
17
18
|
rawLogPath: options.rawLog,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format Zod validation errors for human-readable display.
|
|
3
|
+
*/
|
|
4
|
+
function formatZodError(error) {
|
|
5
|
+
return error.issues
|
|
6
|
+
.map((issue) => {
|
|
7
|
+
const path = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
|
|
8
|
+
return `${path}: ${issue.message}`;
|
|
9
|
+
})
|
|
10
|
+
.join("; ");
|
|
11
|
+
}
|
|
12
|
+
export { formatZodError };
|
|
@@ -6,14 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { parseCredentialsResult, } from "axshared";
|
|
8
8
|
import { getEnvironmentTrimmed } from "./credentials/get-environment-trimmed.js";
|
|
9
|
-
|
|
10
|
-
return error.issues
|
|
11
|
-
.map((issue) => {
|
|
12
|
-
const path = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
|
|
13
|
-
return `${path}: ${issue.message}`;
|
|
14
|
-
})
|
|
15
|
-
.join("; ");
|
|
16
|
-
}
|
|
9
|
+
import { formatZodError } from "./format-zod-error.js";
|
|
17
10
|
/**
|
|
18
11
|
* Parses credentials from environment variables.
|
|
19
12
|
*
|
|
@@ -32,14 +25,6 @@ function parseCredentialsFromEnvironment(agentId) {
|
|
|
32
25
|
const parsed = JSON.parse(credEnvironmentValue);
|
|
33
26
|
const parseResult = parseCredentialsResult(parsed);
|
|
34
27
|
if (parseResult.ok) {
|
|
35
|
-
if (parseResult.value.agent !== agentId) {
|
|
36
|
-
return {
|
|
37
|
-
ok: false,
|
|
38
|
-
exitCode: 2,
|
|
39
|
-
message: `Error: ${credEnvironmentVariable} agent mismatch. ` +
|
|
40
|
-
`Expected ${agentId}, got ${parseResult.value.agent}.`,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
28
|
return { ok: true, credentials: parseResult.value };
|
|
44
29
|
}
|
|
45
30
|
return {
|
|
@@ -74,7 +59,6 @@ function parseApiKeyFromEnvironment(agentId) {
|
|
|
74
59
|
return {
|
|
75
60
|
ok: true,
|
|
76
61
|
credentials: {
|
|
77
|
-
agent: agentId,
|
|
78
62
|
type: "oauth-token",
|
|
79
63
|
data: { oauthToken },
|
|
80
64
|
},
|
|
@@ -109,7 +93,6 @@ function parseApiKeyFromEnvironment(agentId) {
|
|
|
109
93
|
return {
|
|
110
94
|
ok: true,
|
|
111
95
|
credentials: {
|
|
112
|
-
agent: agentId,
|
|
113
96
|
type: "api-key",
|
|
114
97
|
data: { apiKey },
|
|
115
98
|
},
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read and parse credentials from a file or stdin.
|
|
3
|
+
*/
|
|
4
|
+
import { type Credentials } from "axshared";
|
|
5
|
+
type ReadCredentialsResult = {
|
|
6
|
+
ok: true;
|
|
7
|
+
credentials: Credentials;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
error: string;
|
|
11
|
+
};
|
|
12
|
+
declare function readCredentialsFile(path: string, readStdin: () => Promise<string>): Promise<ReadCredentialsResult>;
|
|
13
|
+
export { readCredentialsFile };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read and parse credentials from a file or stdin.
|
|
3
|
+
*/
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import { parseCredentialsResult } from "axshared";
|
|
6
|
+
import { formatZodError } from "./format-zod-error.js";
|
|
7
|
+
async function readCredentialsFile(path, readStdin) {
|
|
8
|
+
let raw;
|
|
9
|
+
try {
|
|
10
|
+
raw = path === "-" ? await readStdin() : await readFile(path, "utf8");
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14
|
+
return { ok: false, error: `Failed to read credentials file: ${message}` };
|
|
15
|
+
}
|
|
16
|
+
let parsed;
|
|
17
|
+
try {
|
|
18
|
+
parsed = JSON.parse(raw);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
22
|
+
return { ok: false, error: `Failed to parse credentials JSON: ${message}` };
|
|
23
|
+
}
|
|
24
|
+
const parseResult = parseCredentialsResult(parsed);
|
|
25
|
+
if (!parseResult.ok) {
|
|
26
|
+
return {
|
|
27
|
+
ok: false,
|
|
28
|
+
error: `Invalid credentials format: ${formatZodError(parseResult.error)}`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return { ok: true, credentials: parseResult.value };
|
|
32
|
+
}
|
|
33
|
+
export { readCredentialsFile };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read all data from stdin as a string.
|
|
3
|
+
*/
|
|
4
|
+
async function readStdin() {
|
|
5
|
+
let data = "";
|
|
6
|
+
for await (const chunk of process.stdin) {
|
|
7
|
+
data += typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
8
|
+
}
|
|
9
|
+
return data.trimEnd();
|
|
10
|
+
}
|
|
11
|
+
export { readStdin };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve credentials from --credentials-file option.
|
|
3
|
+
*/
|
|
4
|
+
import type { Credentials } from "axshared";
|
|
5
|
+
type ResolveCredentialsResult = {
|
|
6
|
+
ok: true;
|
|
7
|
+
credentials: Credentials | undefined;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
error: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Resolve credentials from --credentials-file if specified.
|
|
14
|
+
*
|
|
15
|
+
* @param credentialsFile - Path to credentials file or "-" for stdin
|
|
16
|
+
* @param readStdin - Function to read stdin
|
|
17
|
+
* @returns Credentials or undefined if no file specified
|
|
18
|
+
*/
|
|
19
|
+
declare function resolveCredentials(credentialsFile: string | undefined, readStdin: () => Promise<string>): Promise<ResolveCredentialsResult>;
|
|
20
|
+
export { resolveCredentials };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve credentials from --credentials-file option.
|
|
3
|
+
*/
|
|
4
|
+
import { readCredentialsFile } from "./read-credentials-file.js";
|
|
5
|
+
/**
|
|
6
|
+
* Resolve credentials from --credentials-file if specified.
|
|
7
|
+
*
|
|
8
|
+
* @param credentialsFile - Path to credentials file or "-" for stdin
|
|
9
|
+
* @param readStdin - Function to read stdin
|
|
10
|
+
* @returns Credentials or undefined if no file specified
|
|
11
|
+
*/
|
|
12
|
+
async function resolveCredentials(credentialsFile, readStdin) {
|
|
13
|
+
if (!credentialsFile) {
|
|
14
|
+
return { ok: true, credentials: undefined };
|
|
15
|
+
}
|
|
16
|
+
const result = await readCredentialsFile(credentialsFile, readStdin);
|
|
17
|
+
if (!result.ok) {
|
|
18
|
+
return { ok: false, error: result.error };
|
|
19
|
+
}
|
|
20
|
+
return { ok: true, credentials: result.credentials };
|
|
21
|
+
}
|
|
22
|
+
export { resolveCredentials };
|
|
@@ -35,5 +35,19 @@ type OutputMode = "jsonl" | "tsv" | "tsv-truncated";
|
|
|
35
35
|
* resolveOutputMode(undefined, false) // → "tsv"
|
|
36
36
|
*/
|
|
37
37
|
declare function resolveOutputMode(format: OutputFormat | undefined, isTTY: boolean): OutputMode;
|
|
38
|
-
|
|
38
|
+
type ParseFormatResult = {
|
|
39
|
+
ok: true;
|
|
40
|
+
format: OutputFormat;
|
|
41
|
+
} | {
|
|
42
|
+
ok: false;
|
|
43
|
+
error: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Parse and validate a format string from CLI input.
|
|
47
|
+
*
|
|
48
|
+
* @param input - Raw format string from --format flag
|
|
49
|
+
* @returns Result with validated format or error message
|
|
50
|
+
*/
|
|
51
|
+
declare function parseOutputFormat(input: string): ParseFormatResult;
|
|
52
|
+
export { resolveOutputMode, parseOutputFormat };
|
|
39
53
|
export type { OutputFormat, OutputMode };
|
|
@@ -36,4 +36,19 @@ function resolveOutputMode(format, isTTY) {
|
|
|
36
36
|
// Auto-detect based on TTY
|
|
37
37
|
return isTTY ? "tsv-truncated" : "tsv";
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Parse and validate a format string from CLI input.
|
|
41
|
+
*
|
|
42
|
+
* @param input - Raw format string from --format flag
|
|
43
|
+
* @returns Result with validated format or error message
|
|
44
|
+
*/
|
|
45
|
+
function parseOutputFormat(input) {
|
|
46
|
+
if (input === "jsonl" || input === "tsv") {
|
|
47
|
+
return { ok: true, format: input };
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
error: `Invalid format '${input}'\nValid formats: jsonl, tsv`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export { resolveOutputMode, parseOutputFormat };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve prompt from CLI options, positional argument, or stdin.
|
|
3
|
+
*/
|
|
4
|
+
type ResolvePromptResult = {
|
|
5
|
+
ok: true;
|
|
6
|
+
prompt: string;
|
|
7
|
+
} | {
|
|
8
|
+
ok: false;
|
|
9
|
+
error: string;
|
|
10
|
+
};
|
|
11
|
+
interface ResolvePromptOptions {
|
|
12
|
+
promptFlag: string | undefined;
|
|
13
|
+
positionalPrompt: string | undefined;
|
|
14
|
+
stdinPrompt: string | undefined;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve prompt from multiple sources in priority order.
|
|
18
|
+
*
|
|
19
|
+
* Priority: --prompt flag > positional argument > stdin
|
|
20
|
+
*/
|
|
21
|
+
declare function resolvePrompt(options: ResolvePromptOptions): ResolvePromptResult;
|
|
22
|
+
export { resolvePrompt };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve prompt from CLI options, positional argument, or stdin.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Resolve prompt from multiple sources in priority order.
|
|
6
|
+
*
|
|
7
|
+
* Priority: --prompt flag > positional argument > stdin
|
|
8
|
+
*/
|
|
9
|
+
function resolvePrompt(options) {
|
|
10
|
+
const prompt = (options.promptFlag ??
|
|
11
|
+
options.positionalPrompt ??
|
|
12
|
+
options.stdinPrompt)?.trimEnd();
|
|
13
|
+
if (!prompt) {
|
|
14
|
+
return {
|
|
15
|
+
ok: false,
|
|
16
|
+
error: "prompt is required\n" +
|
|
17
|
+
"Usage: axexec --agent <id> <prompt>\n" +
|
|
18
|
+
"Try 'axexec --help' for more information.",
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return { ok: true, prompt };
|
|
22
|
+
}
|
|
23
|
+
export { resolvePrompt };
|
package/dist/run-agent.d.ts
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { RunAgentOptions, RunResult } from "./types/run-result.js";
|
|
5
5
|
/**
|
|
6
|
-
* Runs an agent with
|
|
6
|
+
* Runs an agent with credential/config isolation.
|
|
7
|
+
*
|
|
8
|
+
* Note: axexec is not an OS sandbox. It runs agents in a non-interactive,
|
|
9
|
+
* permissive mode by default and should not be treated as a security boundary.
|
|
7
10
|
*
|
|
8
11
|
* 1. Validates the agent ID
|
|
9
12
|
* 2. Uses provided credentials or parses from environment
|