axexec 1.5.1 → 1.6.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 +16 -13
- package/dist/agents/claude-code/types.d.ts +2 -2
- package/dist/agents/gemini/types.d.ts +4 -4
- package/dist/agents/opencode/adapter.js +3 -1
- package/dist/agents/opencode/create-session-start-event.d.ts +1 -0
- package/dist/agents/opencode/create-session-start-event.js +3 -5
- package/dist/agents/opencode/process-sse-events.d.ts +1 -0
- package/dist/agents/opencode/process-sse-events.js +2 -0
- package/dist/agents/opencode/session-api.d.ts +1 -1
- package/dist/agents/opencode/session-api.js +4 -17
- package/dist/build-execution-metadata.d.ts +1 -2
- package/dist/build-execution-metadata.js +1 -1
- package/dist/cli.js +28 -0
- package/dist/credentials/get-credential-environment.d.ts +1 -1
- package/dist/credentials/get-credential-environment.js +1 -1
- package/dist/credentials/install-credentials.d.ts +1 -2
- package/dist/credentials/install-credentials.js +3 -1
- package/dist/credentials/write-agent-credentials.d.ts +4 -2
- package/dist/credentials/write-agent-credentials.js +1 -6
- package/dist/execute-agent.d.ts +1 -2
- package/dist/execute-agent.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/parse-credentials.d.ts +2 -3
- package/dist/parse-credentials.js +19 -83
- package/dist/run-agent.js +14 -1
- package/dist/types/options.d.ts +6 -1
- package/dist/types/run-result.d.ts +6 -2
- package/package.json +1 -1
- package/dist/credentials/credentials.d.ts +0 -39
- package/dist/credentials/credentials.js +0 -73
package/README.md
CHANGED
|
@@ -88,6 +88,7 @@ axexec --list-agents | tail -n +2 | awk -F'\t' '$3 ~ /openai/ {print $1}'
|
|
|
88
88
|
-a, --agent <id> Agent to use (claude, codex, gemini, opencode, copilot)
|
|
89
89
|
-p, --prompt <text> Prompt text (alternative to positional argument)
|
|
90
90
|
-m, --model <model> Model to use (agent-specific)
|
|
91
|
+
--provider <provider> Provider for OpenCode (anthropic, openai, google)
|
|
91
92
|
--allow <perms> Permission rules to allow (comma-separated)
|
|
92
93
|
--deny <perms> Permission rules to deny (comma-separated)
|
|
93
94
|
-f, --format <fmt> Output format: jsonl, tsv (default: tsv, truncated on TTY)
|
|
@@ -165,19 +166,21 @@ For Claude, `CLAUDE_CODE_OAUTH_TOKEN` (generated via `claude setup-token`) also
|
|
|
165
166
|
For Copilot, token precedence is `COPILOT_GITHUB_TOKEN`, then `GH_TOKEN`, then
|
|
166
167
|
`GITHUB_TOKEN`.
|
|
167
168
|
|
|
168
|
-
For OpenCode,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
169
|
+
For OpenCode, set `AX_OPENCODE_CREDENTIALS` with your credentials. The `provider`
|
|
170
|
+
field must match your `--provider` flag:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# Anthropic (provider defaults to "anthropic" if omitted)
|
|
174
|
+
AX_OPENCODE_CREDENTIALS='{"agent":"opencode","type":"api-key","data":{"apiKey":"sk-ant-..."}}' \
|
|
175
|
+
axexec -a opencode --provider anthropic -m claude-sonnet-4 "Hello"
|
|
176
|
+
|
|
177
|
+
# OpenAI (provider must be specified in credentials)
|
|
178
|
+
AX_OPENCODE_CREDENTIALS='{"agent":"opencode","type":"api-key","provider":"openai","data":{"apiKey":"sk-..."}}' \
|
|
179
|
+
axexec -a opencode --provider openai -m gpt-4.1 "Hello"
|
|
180
|
+
|
|
181
|
+
# OpenCode hosted models (provider must be "opencode")
|
|
182
|
+
AX_OPENCODE_CREDENTIALS='{"agent":"opencode","type":"api-key","provider":"opencode","data":{"apiKey":"..."}}' \
|
|
183
|
+
axexec -a opencode --provider opencode -m glm-4.7-free "Hello"
|
|
181
184
|
```
|
|
182
185
|
|
|
183
186
|
## Isolation
|
|
@@ -77,8 +77,8 @@ type ClaudeUserEvent = z.infer<typeof ClaudeUserEvent>;
|
|
|
77
77
|
declare const ClaudeResultEvent: z.ZodObject<{
|
|
78
78
|
type: z.ZodLiteral<"result">;
|
|
79
79
|
subtype: z.ZodEnum<{
|
|
80
|
-
error: "error";
|
|
81
80
|
success: "success";
|
|
81
|
+
error: "error";
|
|
82
82
|
cancelled: "cancelled";
|
|
83
83
|
}>;
|
|
84
84
|
timestamp: z.ZodOptional<z.ZodString>;
|
|
@@ -153,8 +153,8 @@ declare const ClaudeEvent: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
153
153
|
}, z.core.$strip>, z.ZodObject<{
|
|
154
154
|
type: z.ZodLiteral<"result">;
|
|
155
155
|
subtype: z.ZodEnum<{
|
|
156
|
-
error: "error";
|
|
157
156
|
success: "success";
|
|
157
|
+
error: "error";
|
|
158
158
|
cancelled: "cancelled";
|
|
159
159
|
}>;
|
|
160
160
|
timestamp: z.ZodOptional<z.ZodString>;
|
|
@@ -41,8 +41,8 @@ declare const GeminiToolResultEvent: z.ZodObject<{
|
|
|
41
41
|
type: z.ZodLiteral<"tool_result">;
|
|
42
42
|
tool_id: z.ZodString;
|
|
43
43
|
status: z.ZodEnum<{
|
|
44
|
-
error: "error";
|
|
45
44
|
success: "success";
|
|
45
|
+
error: "error";
|
|
46
46
|
}>;
|
|
47
47
|
output: z.ZodOptional<z.ZodString>;
|
|
48
48
|
error: z.ZodOptional<z.ZodObject<{
|
|
@@ -67,8 +67,8 @@ type GeminiErrorEvent = z.infer<typeof GeminiErrorEvent>;
|
|
|
67
67
|
declare const GeminiResultEvent: z.ZodObject<{
|
|
68
68
|
type: z.ZodLiteral<"result">;
|
|
69
69
|
status: z.ZodEnum<{
|
|
70
|
-
error: "error";
|
|
71
70
|
success: "success";
|
|
71
|
+
error: "error";
|
|
72
72
|
}>;
|
|
73
73
|
error: z.ZodOptional<z.ZodObject<{
|
|
74
74
|
type: z.ZodString;
|
|
@@ -109,8 +109,8 @@ declare const GeminiEvent: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
109
109
|
type: z.ZodLiteral<"tool_result">;
|
|
110
110
|
tool_id: z.ZodString;
|
|
111
111
|
status: z.ZodEnum<{
|
|
112
|
-
error: "error";
|
|
113
112
|
success: "success";
|
|
113
|
+
error: "error";
|
|
114
114
|
}>;
|
|
115
115
|
output: z.ZodOptional<z.ZodString>;
|
|
116
116
|
error: z.ZodOptional<z.ZodObject<{
|
|
@@ -129,8 +129,8 @@ declare const GeminiEvent: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
129
129
|
}, z.core.$strip>, z.ZodObject<{
|
|
130
130
|
type: z.ZodLiteral<"result">;
|
|
131
131
|
status: z.ZodEnum<{
|
|
132
|
-
error: "error";
|
|
133
132
|
success: "success";
|
|
133
|
+
error: "error";
|
|
134
134
|
}>;
|
|
135
135
|
error: z.ZodOptional<z.ZodObject<{
|
|
136
136
|
type: z.ZodString;
|
|
@@ -73,12 +73,13 @@ async function* streamSession(options) {
|
|
|
73
73
|
const session = await createSession(serverUrl, cwd);
|
|
74
74
|
sessionId = session.id;
|
|
75
75
|
sessionStartTime = Date.now();
|
|
76
|
-
await sendPrompt(serverUrl, sessionId, options.prompt, cwd, options.model);
|
|
76
|
+
await sendPrompt(serverUrl, sessionId, options.prompt, cwd, options.provider, options.model);
|
|
77
77
|
// 5. Process SSE events
|
|
78
78
|
const eventProcessor = processSSEEvents(sseGenerator, {
|
|
79
79
|
serverUrl,
|
|
80
80
|
sessionId,
|
|
81
81
|
sessionStartTime,
|
|
82
|
+
requestedProvider: options.provider,
|
|
82
83
|
requestedModel: options.model,
|
|
83
84
|
cwd,
|
|
84
85
|
signal,
|
|
@@ -103,6 +104,7 @@ async function* streamSession(options) {
|
|
|
103
104
|
sessionId,
|
|
104
105
|
timestamp: sessionStartTime ?? Date.now(),
|
|
105
106
|
modelInfo: undefined,
|
|
107
|
+
requestedProvider: options.provider,
|
|
106
108
|
requestedModel: options.model,
|
|
107
109
|
});
|
|
108
110
|
}
|
|
@@ -7,11 +7,9 @@
|
|
|
7
7
|
* Creates a session.start event with model and provider info.
|
|
8
8
|
*/
|
|
9
9
|
function createSessionStartEvent(options) {
|
|
10
|
-
const { sessionId, timestamp, modelInfo, requestedModel } = options;
|
|
11
|
-
const model = modelInfo
|
|
12
|
-
|
|
13
|
-
: (requestedModel ?? "unknown");
|
|
14
|
-
const provider = modelInfo?.providerID;
|
|
10
|
+
const { sessionId, timestamp, modelInfo, requestedProvider, requestedModel } = options;
|
|
11
|
+
const model = modelInfo?.modelID ?? requestedModel ?? "unknown";
|
|
12
|
+
const provider = modelInfo?.providerID ?? requestedProvider;
|
|
15
13
|
return {
|
|
16
14
|
type: "session.start",
|
|
17
15
|
sessionId,
|
|
@@ -27,6 +27,7 @@ async function* processSSEEvents(sseGenerator, context) {
|
|
|
27
27
|
sessionId: context.sessionId,
|
|
28
28
|
timestamp: context.sessionStartTime,
|
|
29
29
|
modelInfo,
|
|
30
|
+
requestedProvider: context.requestedProvider,
|
|
30
31
|
requestedModel: context.requestedModel,
|
|
31
32
|
});
|
|
32
33
|
sessionStartEmitted = true;
|
|
@@ -38,6 +39,7 @@ async function* processSSEEvents(sseGenerator, context) {
|
|
|
38
39
|
sessionId: context.sessionId,
|
|
39
40
|
timestamp: context.sessionStartTime,
|
|
40
41
|
modelInfo: undefined,
|
|
42
|
+
requestedProvider: context.requestedProvider,
|
|
41
43
|
requestedModel: context.requestedModel,
|
|
42
44
|
});
|
|
43
45
|
sessionStartEmitted = true;
|
|
@@ -37,7 +37,7 @@ declare function createSession(baseUrl: string, directory: string): Promise<Sess
|
|
|
37
37
|
* Uses prompt_async endpoint which returns immediately and processes
|
|
38
38
|
* the prompt asynchronously. Events are streamed via SSE.
|
|
39
39
|
*/
|
|
40
|
-
declare function sendPrompt(baseUrl: string, sessionId: string, prompt: string, directory: string, model?: string): Promise<void>;
|
|
40
|
+
declare function sendPrompt(baseUrl: string, sessionId: string, prompt: string, directory: string, provider?: string, model?: string): Promise<void>;
|
|
41
41
|
/**
|
|
42
42
|
* Aborts a running session with timeout.
|
|
43
43
|
*/
|
|
@@ -21,16 +21,6 @@ class PromptSubmitError extends Error {
|
|
|
21
21
|
this.statusCode = statusCode;
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
-
/** Parses "provider/model" string into components */
|
|
25
|
-
function parseModel(model) {
|
|
26
|
-
const slashIndex = model.indexOf("/");
|
|
27
|
-
if (slashIndex === -1)
|
|
28
|
-
return undefined;
|
|
29
|
-
return {
|
|
30
|
-
providerID: model.slice(0, slashIndex),
|
|
31
|
-
modelID: model.slice(slashIndex + 1),
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
24
|
/**
|
|
35
25
|
* Creates a new session on the OpenCode server.
|
|
36
26
|
*/
|
|
@@ -54,17 +44,14 @@ async function createSession(baseUrl, directory) {
|
|
|
54
44
|
* Uses prompt_async endpoint which returns immediately and processes
|
|
55
45
|
* the prompt asynchronously. Events are streamed via SSE.
|
|
56
46
|
*/
|
|
57
|
-
async function sendPrompt(baseUrl, sessionId, prompt, directory, model) {
|
|
47
|
+
async function sendPrompt(baseUrl, sessionId, prompt, directory, provider, model) {
|
|
58
48
|
// Build request body
|
|
59
49
|
const body = {
|
|
60
50
|
parts: [{ type: "text", text: prompt }],
|
|
61
51
|
};
|
|
62
|
-
// Add model if
|
|
63
|
-
if (model) {
|
|
64
|
-
|
|
65
|
-
if (parsed) {
|
|
66
|
-
body.model = parsed;
|
|
67
|
-
}
|
|
52
|
+
// Add model if provider and model are both provided
|
|
53
|
+
if (provider && model) {
|
|
54
|
+
body.model = { providerID: provider, modelID: model };
|
|
68
55
|
}
|
|
69
56
|
const response = await fetch(`${baseUrl}/session/${sessionId}/prompt_async`, {
|
|
70
57
|
method: "POST",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { AgentCli } from "axshared";
|
|
1
|
+
import type { AgentCli, Credentials } from "axshared";
|
|
2
2
|
import type { RunAgentOptions, ExecutionMetadata } from "./types/run-result.js";
|
|
3
|
-
import type { Credentials } from "./credentials/credentials.js";
|
|
4
3
|
import type { installCredentials } from "./credentials/install-credentials.js";
|
|
5
4
|
type CredentialInstallResult = Awaited<ReturnType<typeof installCredentials>>;
|
|
6
5
|
/**
|
|
@@ -19,7 +19,7 @@ function buildExecutionMetadata(agentId, options, credentialResult, credentials,
|
|
|
19
19
|
if (credentials) {
|
|
20
20
|
execution.credentials = {
|
|
21
21
|
type: credentials.type,
|
|
22
|
-
provider: credentials.provider,
|
|
22
|
+
provider: credentials.agent === "opencode" ? credentials.provider : undefined,
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
return execution;
|
package/dist/cli.js
CHANGED
|
@@ -35,6 +35,7 @@ const program = new Command()
|
|
|
35
35
|
.option("-a, --agent <id>", "Agent to use (required)")
|
|
36
36
|
.option("-p, --prompt <text>", "Prompt text")
|
|
37
37
|
.option("-m, --model <model>", "Model to use")
|
|
38
|
+
.option("--provider <provider>", "Provider for OpenCode (required for OpenCode)")
|
|
38
39
|
.option("--allow <perms>", "Permission rules to allow (comma-separated)")
|
|
39
40
|
.option("--deny <perms>", "Permission rules to deny (comma-separated)")
|
|
40
41
|
.option("-f, --format <fmt>", "Output format: jsonl, tsv (default: tsv, truncated on TTY)")
|
|
@@ -50,6 +51,7 @@ Requirements:
|
|
|
50
51
|
|
|
51
52
|
Examples:
|
|
52
53
|
axexec -a claude "Refactor auth flow"
|
|
54
|
+
axexec -a opencode --provider anthropic -m claude-sonnet-4 "Hello"
|
|
53
55
|
axexec -a claude -f jsonl "Audit deps" | jq 'select(.type=="tool.call")'
|
|
54
56
|
axexec --list-agents | tail -n +2 | cut -f1
|
|
55
57
|
`)
|
|
@@ -98,10 +100,36 @@ Examples:
|
|
|
98
100
|
return;
|
|
99
101
|
}
|
|
100
102
|
}
|
|
103
|
+
// Validate --provider is only used with OpenCode
|
|
104
|
+
if (options.provider && options.agent !== "opencode") {
|
|
105
|
+
process.stderr.write("Error: --provider is only supported for OpenCode agent\n");
|
|
106
|
+
process.exitCode = 2;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// Validate OpenCode requires --provider
|
|
110
|
+
if (options.agent === "opencode") {
|
|
111
|
+
// Check for deprecated provider/model format
|
|
112
|
+
if (options.model?.includes("/")) {
|
|
113
|
+
const [provider, model] = options.model.split("/", 2);
|
|
114
|
+
process.stderr.write("Error: Model format 'provider/model' is no longer supported\n");
|
|
115
|
+
process.stderr.write("Use separate --provider and --model flags:\n");
|
|
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
|
+
}
|
|
127
|
+
}
|
|
101
128
|
// Run agent
|
|
102
129
|
const result = await runAgent(options.agent, {
|
|
103
130
|
prompt,
|
|
104
131
|
model: options.model,
|
|
132
|
+
provider: options.provider,
|
|
105
133
|
allow: options.allow,
|
|
106
134
|
deny: options.deny,
|
|
107
135
|
format,
|
|
@@ -4,7 +4,7 @@
|
|
|
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 "
|
|
7
|
+
import type { Credentials } from "axshared";
|
|
8
8
|
/**
|
|
9
9
|
* Gets additional environment variables for credential-based auth.
|
|
10
10
|
*
|
|
@@ -51,7 +51,7 @@ 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
|
|
54
|
+
const provider = credentials.provider;
|
|
55
55
|
if (credentials.type === "api-key" && apiKey) {
|
|
56
56
|
switch (provider) {
|
|
57
57
|
case "anthropic": {
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
* directory. This ensures complete isolation - agents never discover or use
|
|
7
7
|
* locally installed credentials.
|
|
8
8
|
*/
|
|
9
|
-
import { type AgentCli } from "axshared";
|
|
10
|
-
import type { Credentials } from "./credentials.js";
|
|
9
|
+
import { type AgentCli, type Credentials } from "axshared";
|
|
11
10
|
import type { InstallResult } from "./types.js";
|
|
12
11
|
type WarningWriter = (message: string) => void;
|
|
13
12
|
/**
|
|
@@ -57,7 +57,9 @@ function installAgentCredentials(agentId, configDirectory, dataDirectory, creden
|
|
|
57
57
|
break;
|
|
58
58
|
}
|
|
59
59
|
case "opencode": {
|
|
60
|
-
|
|
60
|
+
if (credentials.agent === "opencode") {
|
|
61
|
+
installOpenCodeCredentials(dataDirectory, credentials, warn);
|
|
62
|
+
}
|
|
61
63
|
break;
|
|
62
64
|
}
|
|
63
65
|
case "copilot": {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* write credentials in the agent-specific format, extracting what
|
|
6
6
|
* they need from the canonical Credentials type.
|
|
7
7
|
*/
|
|
8
|
-
import type { Credentials } from "
|
|
8
|
+
import type { Credentials } from "axshared";
|
|
9
9
|
type WarningWriter = (message: string) => void;
|
|
10
10
|
/**
|
|
11
11
|
* Installs Claude credentials to a config directory.
|
|
@@ -54,5 +54,7 @@ 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: Credentials,
|
|
57
|
+
declare function installOpenCodeCredentials(dataDirectory: string, credentials: Extract<Credentials, {
|
|
58
|
+
agent: "opencode";
|
|
59
|
+
}>, warn?: WarningWriter): void;
|
|
58
60
|
export { installClaudeCredentials, installCodexCredentials, installGeminiCredentials, installOpenCodeCredentials, };
|
|
@@ -8,11 +8,6 @@
|
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { resolveStringField } from "./resolve-string-field.js";
|
|
10
10
|
import { saveJsonFile } from "./save-json-file.js";
|
|
11
|
-
function normalizeOpenCodeProvider(provider) {
|
|
12
|
-
if (!provider)
|
|
13
|
-
return "anthropic";
|
|
14
|
-
return provider === "gemini" ? "google" : provider;
|
|
15
|
-
}
|
|
16
11
|
function defaultWarningWriter(message) {
|
|
17
12
|
const formatted = message.endsWith("\n") ? message : `${message}\n`;
|
|
18
13
|
process.stderr.write(formatted);
|
|
@@ -107,7 +102,7 @@ function installGeminiCredentials(configDirectory, credentials) {
|
|
|
107
102
|
*/
|
|
108
103
|
function installOpenCodeCredentials(dataDirectory, credentials, warn = defaultWarningWriter) {
|
|
109
104
|
const authPath = path.join(dataDirectory, "auth.json");
|
|
110
|
-
const provider =
|
|
105
|
+
const provider = credentials.provider;
|
|
111
106
|
switch (credentials.type) {
|
|
112
107
|
case "oauth-credentials": {
|
|
113
108
|
saveJsonFile(authPath, {
|
package/dist/execute-agent.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { AgentAdapter } from "./types/adapter.js";
|
|
2
|
-
import type { AgentCli } from "axshared";
|
|
2
|
+
import type { AgentCli, Credentials } from "axshared";
|
|
3
3
|
import type { RunAgentOptions, RunResult } from "./types/run-result.js";
|
|
4
|
-
import type { Credentials } from "./credentials/credentials.js";
|
|
5
4
|
import type { installCredentials } from "./credentials/install-credentials.js";
|
|
6
5
|
import type { DiagnosticWriter } from "./resolve-run-diagnostics.js";
|
|
7
6
|
type CredentialInstallResult = Awaited<ReturnType<typeof installCredentials>>;
|
package/dist/execute-agent.js
CHANGED
|
@@ -13,6 +13,7 @@ async function executeAgent(agentId, adapter, options, configEnvironment, creden
|
|
|
13
13
|
prompt: options.prompt,
|
|
14
14
|
verbose: options.verbose ?? false,
|
|
15
15
|
model: options.model,
|
|
16
|
+
provider: options.provider,
|
|
16
17
|
rawLogPath: options.rawLog,
|
|
17
18
|
debug: options.debug,
|
|
18
19
|
configEnv: configEnvironment,
|
package/dist/index.d.ts
CHANGED
|
@@ -13,4 +13,4 @@ export { runAgent } from "./run-agent.js";
|
|
|
13
13
|
export { cleanupCredentials } from "./credentials/install-credentials.js";
|
|
14
14
|
export type { ExecutionCredentials, ExecutionDirectories, ExecutionMetadata, RunAgentDiagnostics, RunAgentOptions, RunResult, } from "./types/run-result.js";
|
|
15
15
|
export type { AxexecEvent } from "./types/events.js";
|
|
16
|
-
export type { Credentials } from "
|
|
16
|
+
export type { Credentials } from "axshared";
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* Used by axexec CLI to read credentials from environment when
|
|
5
5
|
* not provided via the programmatic API.
|
|
6
6
|
*/
|
|
7
|
-
import type
|
|
8
|
-
import { type Credentials } from "./credentials/credentials.js";
|
|
7
|
+
import { type AgentCli, type Credentials } from "axshared";
|
|
9
8
|
type ParseCredentialsResult = {
|
|
10
9
|
ok: true;
|
|
11
10
|
credentials: Credentials | undefined;
|
|
@@ -23,5 +22,5 @@ type ParseCredentialsResult = {
|
|
|
23
22
|
*
|
|
24
23
|
* Returns ok with undefined credentials if none are found.
|
|
25
24
|
*/
|
|
26
|
-
declare function parseCredentialsFromEnvironment(agentId: AgentCli
|
|
25
|
+
declare function parseCredentialsFromEnvironment(agentId: AgentCli): ParseCredentialsResult;
|
|
27
26
|
export { parseCredentialsFromEnvironment };
|
|
@@ -4,8 +4,16 @@
|
|
|
4
4
|
* Used by axexec CLI to read credentials from environment when
|
|
5
5
|
* not provided via the programmatic API.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { parseCredentialsResult, } from "axshared";
|
|
8
8
|
import { getEnvironmentTrimmed } from "./credentials/get-environment-trimmed.js";
|
|
9
|
+
function formatZodError(error) {
|
|
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
17
|
/**
|
|
10
18
|
* Parses credentials from environment variables.
|
|
11
19
|
*
|
|
@@ -15,30 +23,30 @@ import { getEnvironmentTrimmed } from "./credentials/get-environment-trimmed.js"
|
|
|
15
23
|
*
|
|
16
24
|
* Returns ok with undefined credentials if none are found.
|
|
17
25
|
*/
|
|
18
|
-
function parseCredentialsFromEnvironment(agentId
|
|
26
|
+
function parseCredentialsFromEnvironment(agentId) {
|
|
19
27
|
// Check for axexec credentials env var (full Credentials JSON)
|
|
20
28
|
const credEnvironmentVariable = `AX_${agentId.toUpperCase()}_CREDENTIALS`;
|
|
21
29
|
const credEnvironmentValue = process.env[credEnvironmentVariable];
|
|
22
30
|
if (credEnvironmentValue) {
|
|
23
31
|
try {
|
|
24
32
|
const parsed = JSON.parse(credEnvironmentValue);
|
|
25
|
-
const parseResult =
|
|
33
|
+
const parseResult = parseCredentialsResult(parsed);
|
|
26
34
|
if (parseResult.ok) {
|
|
27
|
-
if (parseResult.
|
|
35
|
+
if (parseResult.value.agent !== agentId) {
|
|
28
36
|
return {
|
|
29
37
|
ok: false,
|
|
30
38
|
exitCode: 2,
|
|
31
39
|
message: `Error: ${credEnvironmentVariable} agent mismatch. ` +
|
|
32
|
-
`Expected ${agentId}, got ${parseResult.
|
|
40
|
+
`Expected ${agentId}, got ${parseResult.value.agent}.`,
|
|
33
41
|
};
|
|
34
42
|
}
|
|
35
|
-
return { ok: true, credentials: parseResult.
|
|
43
|
+
return { ok: true, credentials: parseResult.value };
|
|
36
44
|
}
|
|
37
45
|
return {
|
|
38
46
|
ok: false,
|
|
39
47
|
exitCode: 2,
|
|
40
48
|
message: `Error: Invalid credentials format in ${credEnvironmentVariable}: ` +
|
|
41
|
-
parseResult.error,
|
|
49
|
+
formatZodError(parseResult.error),
|
|
42
50
|
};
|
|
43
51
|
}
|
|
44
52
|
catch (error) {
|
|
@@ -51,12 +59,12 @@ function parseCredentialsFromEnvironment(agentId, model) {
|
|
|
51
59
|
}
|
|
52
60
|
}
|
|
53
61
|
// Fall back to standard API key env vars
|
|
54
|
-
return parseApiKeyFromEnvironment(agentId
|
|
62
|
+
return parseApiKeyFromEnvironment(agentId);
|
|
55
63
|
}
|
|
56
64
|
/**
|
|
57
65
|
* Parses API key from standard environment variables.
|
|
58
66
|
*/
|
|
59
|
-
function parseApiKeyFromEnvironment(agentId
|
|
67
|
+
function parseApiKeyFromEnvironment(agentId) {
|
|
60
68
|
let apiKey;
|
|
61
69
|
switch (agentId) {
|
|
62
70
|
case "claude": {
|
|
@@ -84,8 +92,8 @@ function parseApiKeyFromEnvironment(agentId, model) {
|
|
|
84
92
|
break;
|
|
85
93
|
}
|
|
86
94
|
case "opencode": {
|
|
87
|
-
// OpenCode
|
|
88
|
-
return
|
|
95
|
+
// OpenCode requires AX_OPENCODE_CREDENTIALS - no env var fallback
|
|
96
|
+
return { ok: true, credentials: undefined };
|
|
89
97
|
}
|
|
90
98
|
case "copilot": {
|
|
91
99
|
apiKey =
|
|
@@ -107,76 +115,4 @@ function parseApiKeyFromEnvironment(agentId, model) {
|
|
|
107
115
|
},
|
|
108
116
|
};
|
|
109
117
|
}
|
|
110
|
-
function normalizeOpenCodeProvider(provider) {
|
|
111
|
-
if (!provider)
|
|
112
|
-
return undefined;
|
|
113
|
-
return provider === "gemini" ? "google" : provider;
|
|
114
|
-
}
|
|
115
|
-
function parseOpenCodeApiKeyFromEnvironment(model) {
|
|
116
|
-
const providers = [
|
|
117
|
-
{
|
|
118
|
-
provider: "anthropic",
|
|
119
|
-
envVar: "ANTHROPIC_API_KEY",
|
|
120
|
-
value: getEnvironmentTrimmed("ANTHROPIC_API_KEY"),
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
provider: "openai",
|
|
124
|
-
envVar: "OPENAI_API_KEY",
|
|
125
|
-
value: getEnvironmentTrimmed("OPENAI_API_KEY"),
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
provider: "google",
|
|
129
|
-
envVar: "GEMINI_API_KEY",
|
|
130
|
-
value: getEnvironmentTrimmed("GEMINI_API_KEY"),
|
|
131
|
-
},
|
|
132
|
-
];
|
|
133
|
-
const available = providers.filter((entry) => entry.value !== undefined);
|
|
134
|
-
const modelProvider = normalizeOpenCodeProvider(model?.split("/")[0]);
|
|
135
|
-
if (modelProvider &&
|
|
136
|
-
["anthropic", "openai", "google"].includes(modelProvider)) {
|
|
137
|
-
const match = providers.find((entry) => entry.provider === modelProvider);
|
|
138
|
-
if (match?.value) {
|
|
139
|
-
return {
|
|
140
|
-
ok: true,
|
|
141
|
-
credentials: {
|
|
142
|
-
agent: "opencode",
|
|
143
|
-
type: "api-key",
|
|
144
|
-
provider: modelProvider,
|
|
145
|
-
data: { apiKey: match.value },
|
|
146
|
-
},
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
const requiredEnvironmentVariable = match?.envVar ?? "AX_OPENCODE_CREDENTIALS";
|
|
150
|
-
return {
|
|
151
|
-
ok: false,
|
|
152
|
-
exitCode: 2,
|
|
153
|
-
message: `Error: ${requiredEnvironmentVariable} is required for OpenCode ` +
|
|
154
|
-
`model provider "${modelProvider}".`,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
if (available.length === 0) {
|
|
158
|
-
return { ok: true, credentials: undefined };
|
|
159
|
-
}
|
|
160
|
-
if (available.length === 1) {
|
|
161
|
-
const match = available[0];
|
|
162
|
-
if (!match)
|
|
163
|
-
return { ok: true, credentials: undefined };
|
|
164
|
-
return {
|
|
165
|
-
ok: true,
|
|
166
|
-
credentials: {
|
|
167
|
-
agent: "opencode",
|
|
168
|
-
type: "api-key",
|
|
169
|
-
provider: match.provider,
|
|
170
|
-
data: { apiKey: match.value },
|
|
171
|
-
},
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
const environmentList = available.map((entry) => entry.envVar).join(", ");
|
|
175
|
-
return {
|
|
176
|
-
ok: false,
|
|
177
|
-
exitCode: 2,
|
|
178
|
-
message: "Error: Multiple OpenCode API keys detected. " +
|
|
179
|
-
`Set AX_OPENCODE_CREDENTIALS to disambiguate (${environmentList}).`,
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
118
|
export { parseCredentialsFromEnvironment };
|
package/dist/run-agent.js
CHANGED
|
@@ -35,10 +35,23 @@ async function runAgent(agentId, options) {
|
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
const { adapter } = validation;
|
|
38
|
+
// Validate --model format for OpenCode (reject "provider/model" format)
|
|
39
|
+
if (validation.agentId === "opencode" && options.model?.includes("/")) {
|
|
40
|
+
const [provider, model] = options.model.split("/", 2);
|
|
41
|
+
diagnostics.error(`Error: Model format 'provider/model' is no longer supported.\n` +
|
|
42
|
+
`Use separate --provider and --model flags instead:\n` +
|
|
43
|
+
` --provider ${provider} --model ${model}`);
|
|
44
|
+
setExitCode(2);
|
|
45
|
+
return {
|
|
46
|
+
events: [],
|
|
47
|
+
success: false,
|
|
48
|
+
execution: { agent: validation.agentId },
|
|
49
|
+
};
|
|
50
|
+
}
|
|
38
51
|
// Use provided credentials or parse from environment
|
|
39
52
|
const credentialsResult = options.credentials
|
|
40
53
|
? { ok: true, credentials: options.credentials }
|
|
41
|
-
: parseCredentialsFromEnvironment(validation.agentId
|
|
54
|
+
: parseCredentialsFromEnvironment(validation.agentId);
|
|
42
55
|
if (!credentialsResult.ok) {
|
|
43
56
|
diagnostics.error(credentialsResult.message);
|
|
44
57
|
setExitCode(credentialsResult.exitCode);
|
package/dist/types/options.d.ts
CHANGED
|
@@ -24,9 +24,14 @@ interface RunOptions {
|
|
|
24
24
|
* - Codex: model names ('o4-mini', 'gpt-5.1-codex')
|
|
25
25
|
* - Gemini: aliases ('pro', 'flash') or full names ('gemini-2.5-pro')
|
|
26
26
|
* - Copilot: model names ('gpt-5', 'claude-sonnet-4.5')
|
|
27
|
-
* - OpenCode:
|
|
27
|
+
* - OpenCode: model name only ('claude-sonnet-4'), provider specified separately
|
|
28
28
|
*/
|
|
29
29
|
model?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Provider for multi-provider agents (e.g., OpenCode).
|
|
32
|
+
* OpenCode providers: 'anthropic', 'openai', 'google'
|
|
33
|
+
*/
|
|
34
|
+
provider?: string;
|
|
30
35
|
/**
|
|
31
36
|
* Preserve GITHUB_SHA environment variable.
|
|
32
37
|
*
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Types for run-agent results and options.
|
|
3
3
|
*/
|
|
4
|
-
import type { AgentCli } from "axshared";
|
|
4
|
+
import type { AgentCli, Credentials } from "axshared";
|
|
5
5
|
import type { AxexecEvent } from "./events.js";
|
|
6
6
|
import type { OutputFormat } from "../resolve-output-mode.js";
|
|
7
|
-
import type { Credentials } from "../credentials/credentials.js";
|
|
8
7
|
interface RunAgentDiagnostics {
|
|
9
8
|
error: (message: string) => void;
|
|
10
9
|
warn: (message: string) => void;
|
|
@@ -17,6 +16,11 @@ interface RunAgentOptions {
|
|
|
17
16
|
*/
|
|
18
17
|
credentials?: Credentials;
|
|
19
18
|
model?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Provider for multi-provider agents (e.g., OpenCode).
|
|
21
|
+
* Used to select which API key to use when multiple are present.
|
|
22
|
+
*/
|
|
23
|
+
provider?: string;
|
|
20
24
|
allow?: string;
|
|
21
25
|
deny?: string;
|
|
22
26
|
format?: OutputFormat;
|
package/package.json
CHANGED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Credential schema and parser.
|
|
3
|
-
*
|
|
4
|
-
* Temporary local copy of the axshared credentials schema.
|
|
5
|
-
* Remove once axshared exports an equivalent parser with matching validation.
|
|
6
|
-
*/
|
|
7
|
-
import { z } from "zod";
|
|
8
|
-
/** Zod schema for Credentials */
|
|
9
|
-
declare const Credentials: z.ZodObject<{
|
|
10
|
-
agent: z.ZodEnum<{
|
|
11
|
-
claude: "claude";
|
|
12
|
-
codex: "codex";
|
|
13
|
-
gemini: "gemini";
|
|
14
|
-
opencode: "opencode";
|
|
15
|
-
copilot: "copilot";
|
|
16
|
-
}>;
|
|
17
|
-
type: z.ZodEnum<{
|
|
18
|
-
"oauth-credentials": "oauth-credentials";
|
|
19
|
-
"oauth-token": "oauth-token";
|
|
20
|
-
"api-key": "api-key";
|
|
21
|
-
}>;
|
|
22
|
-
provider: z.ZodOptional<z.ZodString>;
|
|
23
|
-
data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
24
|
-
}, z.core.$strip>;
|
|
25
|
-
type Credentials = z.infer<typeof Credentials>;
|
|
26
|
-
type ParseCredentialsResult = {
|
|
27
|
-
ok: true;
|
|
28
|
-
credentials: Credentials;
|
|
29
|
-
} | {
|
|
30
|
-
ok: false;
|
|
31
|
-
error: string;
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* Parse and validate credentials from unknown input.
|
|
35
|
-
*
|
|
36
|
-
* @returns Validated Credentials or error details
|
|
37
|
-
*/
|
|
38
|
-
declare function parseCredentials(input: unknown): ParseCredentialsResult;
|
|
39
|
-
export { Credentials, parseCredentials };
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Credential schema and parser.
|
|
3
|
-
*
|
|
4
|
-
* Temporary local copy of the axshared credentials schema.
|
|
5
|
-
* Remove once axshared exports an equivalent parser with matching validation.
|
|
6
|
-
*/
|
|
7
|
-
import { z } from "zod";
|
|
8
|
-
import { AGENT_CLIS } from "axshared";
|
|
9
|
-
import { resolveStringField } from "./resolve-string-field.js";
|
|
10
|
-
/** Credential storage types */
|
|
11
|
-
const CREDENTIAL_TYPES = [
|
|
12
|
-
"oauth-credentials",
|
|
13
|
-
"oauth-token",
|
|
14
|
-
"api-key",
|
|
15
|
-
];
|
|
16
|
-
/** Zod schema for credential type validation and parsing */
|
|
17
|
-
const CredentialType = z.enum(CREDENTIAL_TYPES);
|
|
18
|
-
/** Zod schema for Credentials */
|
|
19
|
-
const Credentials = z
|
|
20
|
-
.object({
|
|
21
|
-
/** Agent this credential belongs to */
|
|
22
|
-
agent: z.enum(AGENT_CLIS),
|
|
23
|
-
/** Credential storage type */
|
|
24
|
-
type: CredentialType,
|
|
25
|
-
/** Provider identifier for multi-provider agents like OpenCode */
|
|
26
|
-
provider: z.string().trim().min(1).optional(),
|
|
27
|
-
/** The actual credential data (format depends on type and agent) */
|
|
28
|
-
data: z.record(z.string(), z.unknown()),
|
|
29
|
-
})
|
|
30
|
-
.superRefine((value, context) => {
|
|
31
|
-
if (value.type === "api-key") {
|
|
32
|
-
const apiKey = resolveStringField(value.data, "apiKey", "key");
|
|
33
|
-
if (!apiKey) {
|
|
34
|
-
context.addIssue({
|
|
35
|
-
code: "custom",
|
|
36
|
-
path: ["data"],
|
|
37
|
-
message: 'api-key credentials require "apiKey" or "key"',
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
if (value.type === "oauth-token") {
|
|
42
|
-
const oauthToken = resolveStringField(value.data, "oauthToken", "token");
|
|
43
|
-
if (!oauthToken) {
|
|
44
|
-
context.addIssue({
|
|
45
|
-
code: "custom",
|
|
46
|
-
path: ["data"],
|
|
47
|
-
message: 'oauth-token credentials require "oauthToken" or "token"',
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
// oauth-credentials payloads are provider-specific and intentionally
|
|
52
|
-
// not validated beyond basic schema checks here.
|
|
53
|
-
});
|
|
54
|
-
function formatParseError(error) {
|
|
55
|
-
return error.issues
|
|
56
|
-
.map((issue) => {
|
|
57
|
-
const path = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
|
|
58
|
-
return `${path}: ${issue.message}`;
|
|
59
|
-
})
|
|
60
|
-
.join("; ");
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Parse and validate credentials from unknown input.
|
|
64
|
-
*
|
|
65
|
-
* @returns Validated Credentials or error details
|
|
66
|
-
*/
|
|
67
|
-
function parseCredentials(input) {
|
|
68
|
-
const result = Credentials.safeParse(input);
|
|
69
|
-
if (result.success)
|
|
70
|
-
return { ok: true, credentials: result.data };
|
|
71
|
-
return { ok: false, error: formatParseError(result.error) };
|
|
72
|
-
}
|
|
73
|
-
export { Credentials, parseCredentials };
|