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/run-agent.js
CHANGED
|
@@ -4,12 +4,16 @@
|
|
|
4
4
|
import { installCredentials, cleanupCredentials, } from "./credentials/install-credentials.js";
|
|
5
5
|
import { buildPermissionsConfig } from "./build-permissions-config.js";
|
|
6
6
|
import { validateAgent } from "./validate-agent.js";
|
|
7
|
+
import { validateCwd } from "./validate-cwd.js";
|
|
7
8
|
import { parseCredentialsFromEnvironment } from "./parse-credentials.js";
|
|
8
9
|
import { buildExecutionMetadata } from "./build-execution-metadata.js";
|
|
9
10
|
import { executeAgent } from "./execute-agent.js";
|
|
10
11
|
import { resolveDiagnostics, resolveExitCodeSetter, } from "./resolve-run-diagnostics.js";
|
|
11
12
|
/**
|
|
12
|
-
* Runs an agent with
|
|
13
|
+
* Runs an agent with credential/config isolation.
|
|
14
|
+
*
|
|
15
|
+
* Note: axexec is not an OS sandbox. It runs agents in a non-interactive,
|
|
16
|
+
* permissive mode by default and should not be treated as a security boundary.
|
|
13
17
|
*
|
|
14
18
|
* 1. Validates the agent ID
|
|
15
19
|
* 2. Uses provided credentials or parses from environment
|
|
@@ -35,12 +39,28 @@ async function runAgent(agentId, options) {
|
|
|
35
39
|
};
|
|
36
40
|
}
|
|
37
41
|
const { adapter } = validation;
|
|
38
|
-
// Validate
|
|
39
|
-
if (
|
|
40
|
-
const
|
|
42
|
+
// Validate cwd if provided (for programmatic API consumers)
|
|
43
|
+
if (options.cwd) {
|
|
44
|
+
const cwdValidation = await validateCwd(options.cwd);
|
|
45
|
+
if (!cwdValidation.ok) {
|
|
46
|
+
diagnostics.error(`Error: ${cwdValidation.error}`);
|
|
47
|
+
setExitCode(2);
|
|
48
|
+
return {
|
|
49
|
+
events: [],
|
|
50
|
+
success: false,
|
|
51
|
+
execution: { agent: validation.agentId, model: options.model },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Validate --model format for OpenCode (reject "provider/model" format only when --provider is missing).
|
|
56
|
+
// Many legitimate model IDs contain slashes (e.g., nvidia/nemotron, google/gemma).
|
|
57
|
+
if (validation.agentId === "opencode" &&
|
|
58
|
+
!options.provider &&
|
|
59
|
+
options.model?.includes("/")) {
|
|
60
|
+
const [providerPart, modelPart] = options.model.split("/", 2);
|
|
41
61
|
diagnostics.error(`Error: Model format 'provider/model' is no longer supported.\n` +
|
|
42
62
|
`Use separate --provider and --model flags instead:\n` +
|
|
43
|
-
` --provider ${
|
|
63
|
+
` --provider ${providerPart} --model ${modelPart}`);
|
|
44
64
|
setExitCode(2);
|
|
45
65
|
return {
|
|
46
66
|
events: [],
|
|
@@ -63,20 +83,15 @@ async function runAgent(agentId, options) {
|
|
|
63
83
|
}
|
|
64
84
|
// Credentials are optional; isolation still applies even when none are found.
|
|
65
85
|
const credentials = credentialsResult.credentials;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
events: [],
|
|
72
|
-
success: false,
|
|
73
|
-
execution: { agent: validation.agentId, model: options.model },
|
|
74
|
-
};
|
|
75
|
-
}
|
|
86
|
+
// Normalize provider to lowercase for OpenCode (all OpenCode provider IDs are lowercase).
|
|
87
|
+
// This ensures consistent behavior for both CLI and programmatic API users.
|
|
88
|
+
const normalizedOptions = validation.agentId === "opencode" && options.provider
|
|
89
|
+
? { ...options, provider: options.provider.toLowerCase() }
|
|
90
|
+
: options;
|
|
76
91
|
// Always create isolated runtime directories; install credentials if provided.
|
|
77
92
|
let credentialResult;
|
|
78
93
|
try {
|
|
79
|
-
credentialResult = await installCredentials(validation.agentId, credentials, diagnostics.warn);
|
|
94
|
+
credentialResult = await installCredentials(validation.agentId, credentials, normalizedOptions.provider, diagnostics.warn);
|
|
80
95
|
}
|
|
81
96
|
catch (error) {
|
|
82
97
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -112,6 +127,6 @@ async function runAgent(agentId, options) {
|
|
|
112
127
|
configEnvironment = configResult.env;
|
|
113
128
|
}
|
|
114
129
|
// Execute agent
|
|
115
|
-
return executeAgent(validation.agentId, adapter,
|
|
130
|
+
return executeAgent(validation.agentId, adapter, normalizedOptions, configEnvironment, credentialResult, credentials, preserveConfigDirectory, diagnostics.error);
|
|
116
131
|
}
|
|
117
132
|
export { runAgent };
|
|
@@ -10,6 +10,10 @@ interface RunAgentDiagnostics {
|
|
|
10
10
|
}
|
|
11
11
|
interface RunAgentOptions {
|
|
12
12
|
prompt: string;
|
|
13
|
+
/**
|
|
14
|
+
* Working directory for the agent process.
|
|
15
|
+
*/
|
|
16
|
+
cwd?: string;
|
|
13
17
|
/**
|
|
14
18
|
* Credentials to use. If not provided, parses from environment variables.
|
|
15
19
|
* Accepts the canonical Credentials shape shared across ax* packages.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate --cwd option points to an existing directory.
|
|
3
|
+
*/
|
|
4
|
+
import { stat } from "node:fs/promises";
|
|
5
|
+
async function validateCwd(cwd) {
|
|
6
|
+
try {
|
|
7
|
+
const stats = await stat(cwd);
|
|
8
|
+
if (!stats.isDirectory()) {
|
|
9
|
+
return {
|
|
10
|
+
ok: false,
|
|
11
|
+
error: `--cwd path is not a directory: ${cwd}`,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return { ok: true };
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
error: `--cwd directory error: ${message}`,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export { validateCwd };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate OpenCode-specific CLI options.
|
|
3
|
+
*/
|
|
4
|
+
type ProviderValidationResult = {
|
|
5
|
+
ok: true;
|
|
6
|
+
normalizedProvider: string | undefined;
|
|
7
|
+
} | {
|
|
8
|
+
ok: false;
|
|
9
|
+
error: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Validate provider usage and OpenCode-specific options.
|
|
13
|
+
*
|
|
14
|
+
* - For non-OpenCode agents: provider must not be specified
|
|
15
|
+
* - For OpenCode: validates model format, requires provider
|
|
16
|
+
*/
|
|
17
|
+
declare function validateProviderOptions(agentId: string, model: string | undefined, provider: string | undefined): ProviderValidationResult;
|
|
18
|
+
export { validateProviderOptions };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate OpenCode-specific CLI options.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Validate provider usage and OpenCode-specific options.
|
|
6
|
+
*
|
|
7
|
+
* - For non-OpenCode agents: provider must not be specified
|
|
8
|
+
* - For OpenCode: validates model format, requires provider
|
|
9
|
+
*/
|
|
10
|
+
function validateProviderOptions(agentId, model, provider) {
|
|
11
|
+
// --provider is only valid for OpenCode
|
|
12
|
+
if (provider && agentId !== "opencode") {
|
|
13
|
+
return {
|
|
14
|
+
ok: false,
|
|
15
|
+
error: "--provider is only supported for OpenCode agent",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
// Non-OpenCode agents don't need further validation
|
|
19
|
+
if (agentId !== "opencode") {
|
|
20
|
+
return { ok: true, normalizedProvider: undefined };
|
|
21
|
+
}
|
|
22
|
+
// OpenCode-specific validation
|
|
23
|
+
return validateOpenCodeOptions(model, provider);
|
|
24
|
+
}
|
|
25
|
+
function validateOpenCodeOptions(model, provider) {
|
|
26
|
+
// Require --provider for OpenCode
|
|
27
|
+
if (!provider) {
|
|
28
|
+
// If model contains "/" and no provider given, suggest they might be using
|
|
29
|
+
// the old provider/model format, but primarily tell them --provider is required.
|
|
30
|
+
if (model?.includes("/")) {
|
|
31
|
+
const [providerPart, modelPart] = model.split("/", 2);
|
|
32
|
+
return {
|
|
33
|
+
ok: false,
|
|
34
|
+
error: `OpenCode requires --provider\n` +
|
|
35
|
+
`If using the old 'provider/model' format, use separate flags:\n` +
|
|
36
|
+
` axexec -a opencode --provider ${providerPart} -m ${modelPart} ...`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
error: `OpenCode requires --provider\n` +
|
|
42
|
+
` axexec -a opencode --provider anthropic -m claude-sonnet-4 ...`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Normalize to lowercase to match OpenCode's provider ID format.
|
|
46
|
+
// OpenCode stores all provider IDs in lowercase (anthropic, openai, etc).
|
|
47
|
+
const normalizedProvider = provider.toLowerCase();
|
|
48
|
+
return { ok: true, normalizedProvider };
|
|
49
|
+
}
|
|
50
|
+
export { validateProviderOptions };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate stdin usage when credentials and prompt may both need stdin.
|
|
3
|
+
*/
|
|
4
|
+
type ValidateStdinResult = {
|
|
5
|
+
ok: true;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
error: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Validates that stdin usage is not conflicting.
|
|
12
|
+
*
|
|
13
|
+
* Returns an error if:
|
|
14
|
+
* - --credentials-file - is used with no prompt (both would read stdin)
|
|
15
|
+
* - --credentials-file - is used in interactive TTY mode (requires piping)
|
|
16
|
+
*/
|
|
17
|
+
declare function validateStdinUsage(credentialsFile: string | undefined, needsStdinPrompt: boolean, isTTY: boolean | undefined): ValidateStdinResult;
|
|
18
|
+
export { validateStdinUsage };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate stdin usage when credentials and prompt may both need stdin.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Validates that stdin usage is not conflicting.
|
|
6
|
+
*
|
|
7
|
+
* Returns an error if:
|
|
8
|
+
* - --credentials-file - is used with no prompt (both would read stdin)
|
|
9
|
+
* - --credentials-file - is used in interactive TTY mode (requires piping)
|
|
10
|
+
*/
|
|
11
|
+
function validateStdinUsage(credentialsFile, needsStdinPrompt, isTTY) {
|
|
12
|
+
if (credentialsFile === "-" && needsStdinPrompt) {
|
|
13
|
+
return {
|
|
14
|
+
ok: false,
|
|
15
|
+
error: "Cannot read both prompt and credentials from stdin\n" +
|
|
16
|
+
"Provide the prompt via --prompt or positional argument when using --credentials-file -",
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (credentialsFile === "-" && isTTY) {
|
|
20
|
+
return {
|
|
21
|
+
ok: false,
|
|
22
|
+
error: "Cannot read credentials from stdin in interactive mode\n" +
|
|
23
|
+
"Pipe credentials JSON to stdin or use a file path instead of '-'",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return { ok: true };
|
|
27
|
+
}
|
|
28
|
+
export { validateStdinUsage };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "axexec",
|
|
3
3
|
"author": "Łukasz Jerciński",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "2.0.1",
|
|
6
6
|
"description": "Unified CLI runner for AI coding agents with normalized event streaming",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -59,27 +59,27 @@
|
|
|
59
59
|
"automation",
|
|
60
60
|
"coding-assistant"
|
|
61
61
|
],
|
|
62
|
-
"packageManager": "pnpm@10.28.
|
|
62
|
+
"packageManager": "pnpm@10.28.1",
|
|
63
63
|
"engines": {
|
|
64
64
|
"node": ">=22.14.0"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@commander-js/extra-typings": "^14.0.0",
|
|
68
68
|
"axconfig": "^3.6.3",
|
|
69
|
-
"axshared": "^
|
|
69
|
+
"axshared": "^5.0.0",
|
|
70
70
|
"commander": "^14.0.2",
|
|
71
71
|
"zod": "^4.3.5"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@total-typescript/ts-reset": "^0.6.1",
|
|
75
|
-
"@types/node": "^25.0.
|
|
75
|
+
"@types/node": "^25.0.10",
|
|
76
76
|
"@vitest/coverage-v8": "^4.0.17",
|
|
77
77
|
"eslint": "^9.39.2",
|
|
78
78
|
"eslint-config-axkit": "^1.1.0",
|
|
79
79
|
"fta-check": "^1.5.1",
|
|
80
80
|
"fta-cli": "^3.0.0",
|
|
81
|
-
"knip": "^5.82.
|
|
82
|
-
"prettier": "3.8.
|
|
81
|
+
"knip": "^5.82.1",
|
|
82
|
+
"prettier": "3.8.1",
|
|
83
83
|
"semantic-release": "^25.0.2",
|
|
84
84
|
"typescript": "^5.9.3",
|
|
85
85
|
"vitest": "^4.0.17"
|