axexec 1.0.0 → 1.1.0
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 +21 -0
- package/dist/agents/claude-code/types.d.ts +2 -2
- package/dist/agents/codex/adapter.js +8 -13
- package/dist/agents/gemini/types.d.ts +4 -4
- package/dist/build-agent-environment.d.ts +2 -0
- package/dist/build-agent-environment.js +19 -0
- package/dist/build-execution-metadata.d.ts +10 -0
- package/dist/build-execution-metadata.js +27 -0
- package/dist/build-permissions-config.d.ts +24 -0
- package/dist/build-permissions-config.js +48 -0
- package/dist/credentials/credentials.d.ts +39 -0
- package/dist/credentials/credentials.js +73 -0
- package/dist/credentials/get-credential-environment.d.ts +7 -5
- package/dist/credentials/get-credential-environment.js +44 -14
- package/dist/credentials/get-environment-trimmed.d.ts +5 -0
- package/dist/credentials/get-environment-trimmed.js +8 -0
- package/dist/credentials/install-credentials.d.ts +7 -6
- package/dist/credentials/install-credentials.js +26 -16
- package/dist/credentials/resolve-string-field.d.ts +7 -0
- package/dist/credentials/resolve-string-field.js +21 -0
- package/dist/credentials/types.d.ts +1 -10
- package/dist/credentials/write-agent-credentials.d.ts +28 -6
- package/dist/credentials/write-agent-credentials.js +88 -43
- package/dist/execute-agent.d.ts +12 -0
- package/dist/execute-agent.js +68 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +10 -0
- package/dist/parse-credentials.d.ts +17 -3
- package/dist/parse-credentials.js +146 -27
- package/dist/resolve-run-diagnostics.d.ts +9 -0
- package/dist/resolve-run-diagnostics.js +35 -0
- package/dist/run-agent.d.ts +4 -20
- package/dist/run-agent.js +67 -109
- package/dist/stream-agent.js +5 -15
- package/dist/types/run-result.d.ts +81 -0
- package/dist/types/run-result.js +4 -0
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -162,6 +162,23 @@ GITHUB_TOKEN=ghp_... axexec -a copilot "Write tests"
|
|
|
162
162
|
```
|
|
163
163
|
|
|
164
164
|
For Claude, `CLAUDE_CODE_OAUTH_TOKEN` (generated via `claude setup-token`) also works.
|
|
165
|
+
For Copilot, token precedence is `COPILOT_GITHUB_TOKEN`, then `GH_TOKEN`, then
|
|
166
|
+
`GITHUB_TOKEN`.
|
|
167
|
+
|
|
168
|
+
For OpenCode, if multiple provider API keys are set, axexec uses the model
|
|
169
|
+
prefix (`anthropic/...`, `openai/...`, `google/...`) to disambiguate. When
|
|
170
|
+
multiple keys are present and no provider prefix is available, set
|
|
171
|
+
`AX_OPENCODE_CREDENTIALS` explicitly. This value must be a JSON-encoded
|
|
172
|
+
Credentials object (not a raw API key), for example:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"agent": "opencode",
|
|
177
|
+
"type": "api-key",
|
|
178
|
+
"provider": "openai",
|
|
179
|
+
"data": { "apiKey": "sk-..." }
|
|
180
|
+
}
|
|
181
|
+
```
|
|
165
182
|
|
|
166
183
|
## Isolation
|
|
167
184
|
|
|
@@ -173,6 +190,10 @@ axexec provides complete environment isolation:
|
|
|
173
190
|
4. Sets environment variables to point agents at the temp directory
|
|
174
191
|
5. Cleans up the temp directory after execution
|
|
175
192
|
|
|
193
|
+
Codex runs with `--dangerously-bypass-approvals-and-sandbox`, so `--allow` /
|
|
194
|
+
`--deny` do not constrain Codex. Run axexec inside an external sandbox if you
|
|
195
|
+
need enforcement.
|
|
196
|
+
|
|
176
197
|
Agents **never** access:
|
|
177
198
|
|
|
178
199
|
- User's home directory config (`~/.claude`, `~/.codex`, `~/.gemini`)
|
|
@@ -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
|
-
success: "success";
|
|
81
80
|
error: "error";
|
|
81
|
+
success: "success";
|
|
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
|
-
success: "success";
|
|
157
156
|
error: "error";
|
|
157
|
+
success: "success";
|
|
158
158
|
cancelled: "cancelled";
|
|
159
159
|
}>;
|
|
160
160
|
timestamp: z.ZodOptional<z.ZodString>;
|
|
@@ -16,23 +16,18 @@ const CODEX_INFO = {
|
|
|
16
16
|
* Prepares the command to spawn Codex CLI.
|
|
17
17
|
*/
|
|
18
18
|
function prepareCommand(options) {
|
|
19
|
-
// Build CLI arguments.
|
|
20
|
-
// Enable network access in sandbox: Codex blocks network by default in
|
|
21
|
-
// workspace-write mode, but other agents (Gemini, OpenCode) allow it by
|
|
22
|
-
// default. For unified behavior, enable it. This keeps file-level sandbox
|
|
23
|
-
// protections while allowing gh, curl, etc.
|
|
24
19
|
const cliArguments = [
|
|
25
20
|
"exec",
|
|
26
21
|
"--json",
|
|
27
|
-
|
|
28
|
-
"-
|
|
29
|
-
"sandbox_workspace_write.network_access=true",
|
|
30
|
-
// Add model flag if specified (o4-mini, gpt-5.1-codex, gpt-5.2, etc.)
|
|
31
|
-
...(options.model ? ["--model", options.model] : []),
|
|
32
|
-
// Use "--" to prevent prompt from being misinterpreted as a flag
|
|
33
|
-
"--",
|
|
34
|
-
options.prompt,
|
|
22
|
+
// Run Codex without internal approvals/sandbox; no Codex-enforced restrictions apply.
|
|
23
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
35
24
|
];
|
|
25
|
+
// Add model flag if specified (o4-mini, gpt-5.1-codex, gpt-5.2, etc.)
|
|
26
|
+
if (options.model) {
|
|
27
|
+
cliArguments.push("--model", options.model);
|
|
28
|
+
}
|
|
29
|
+
// Use "--" to prevent prompt from being misinterpreted as a flag
|
|
30
|
+
cliArguments.push("--", options.prompt);
|
|
36
31
|
const environment = {};
|
|
37
32
|
// Pass through authentication environment variable.
|
|
38
33
|
// If not set, Codex CLI will handle auth errors on its own.
|
|
@@ -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
|
-
success: "success";
|
|
45
44
|
error: "error";
|
|
45
|
+
success: "success";
|
|
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
|
-
success: "success";
|
|
71
70
|
error: "error";
|
|
71
|
+
success: "success";
|
|
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
|
-
success: "success";
|
|
113
112
|
error: "error";
|
|
113
|
+
success: "success";
|
|
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
|
-
success: "success";
|
|
133
132
|
error: "error";
|
|
133
|
+
success: "success";
|
|
134
134
|
}>;
|
|
135
135
|
error: z.ZodOptional<z.ZodObject<{
|
|
136
136
|
type: z.ZodString;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const VAULT_ENV_EXCLUSIONS = ["AXVAULT", "AXVAULT_URL", "AXVAULT_API_KEY"];
|
|
2
|
+
function isAxCredentialEnvironment(key) {
|
|
3
|
+
return key.startsWith("AX_") && key.endsWith("_CREDENTIALS");
|
|
4
|
+
}
|
|
5
|
+
function buildBaseEnvironment(additionalExclusions = []) {
|
|
6
|
+
const allExclusions = new Set([
|
|
7
|
+
...VAULT_ENV_EXCLUSIONS,
|
|
8
|
+
...additionalExclusions,
|
|
9
|
+
]);
|
|
10
|
+
const entries = Object.entries(process.env).flatMap(([key, value]) => {
|
|
11
|
+
if (value === undefined)
|
|
12
|
+
return [];
|
|
13
|
+
if (allExclusions.has(key) || isAxCredentialEnvironment(key))
|
|
14
|
+
return [];
|
|
15
|
+
return [[key, value]];
|
|
16
|
+
});
|
|
17
|
+
return Object.fromEntries(entries);
|
|
18
|
+
}
|
|
19
|
+
export { buildBaseEnvironment };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AgentCli } from "./types/events.js";
|
|
2
|
+
import type { RunAgentOptions, ExecutionMetadata } from "./types/run-result.js";
|
|
3
|
+
import type { Credentials } from "./credentials/credentials.js";
|
|
4
|
+
import type { installCredentials } from "./credentials/install-credentials.js";
|
|
5
|
+
type CredentialInstallResult = Awaited<ReturnType<typeof installCredentials>>;
|
|
6
|
+
/**
|
|
7
|
+
* Builds the execution metadata for the result.
|
|
8
|
+
*/
|
|
9
|
+
declare function buildExecutionMetadata(agentId: AgentCli, options: RunAgentOptions, credentialResult: CredentialInstallResult | undefined, credentials: Credentials | undefined, preserved: boolean): ExecutionMetadata;
|
|
10
|
+
export { buildExecutionMetadata };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the execution metadata for the result.
|
|
3
|
+
*/
|
|
4
|
+
function buildExecutionMetadata(agentId, options, credentialResult, credentials, preserved) {
|
|
5
|
+
const execution = {
|
|
6
|
+
agent: agentId,
|
|
7
|
+
model: options.model,
|
|
8
|
+
};
|
|
9
|
+
// Add directory info if an isolated directory was created
|
|
10
|
+
if (credentialResult) {
|
|
11
|
+
execution.directories = {
|
|
12
|
+
base: credentialResult.baseDirectory,
|
|
13
|
+
config: credentialResult.configDirectory,
|
|
14
|
+
data: credentialResult.dataDirectory,
|
|
15
|
+
preserved,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
// Add credential info if credentials were used
|
|
19
|
+
if (credentials) {
|
|
20
|
+
execution.credentials = {
|
|
21
|
+
type: credentials.type,
|
|
22
|
+
provider: credentials.provider,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return execution;
|
|
26
|
+
}
|
|
27
|
+
export { buildExecutionMetadata };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission config building for agent execution.
|
|
3
|
+
*/
|
|
4
|
+
import type { AgentCli } from "./types/events.js";
|
|
5
|
+
import type { RunAgentOptions } from "./types/run-result.js";
|
|
6
|
+
import type { installCredentials } from "./credentials/install-credentials.js";
|
|
7
|
+
type CredentialInstallResult = Awaited<ReturnType<typeof installCredentials>>;
|
|
8
|
+
/** Result of building permissions config */
|
|
9
|
+
type BuildConfigResult = {
|
|
10
|
+
ok: true;
|
|
11
|
+
env: Record<string, string>;
|
|
12
|
+
warnings: string[];
|
|
13
|
+
} | {
|
|
14
|
+
ok: false;
|
|
15
|
+
exitCode: number;
|
|
16
|
+
errors: string[];
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Builds agent config with permissions.
|
|
20
|
+
*
|
|
21
|
+
* Requires credentialResult for the config directory.
|
|
22
|
+
*/
|
|
23
|
+
declare function buildPermissionsConfig(agentId: AgentCli, options: Pick<RunAgentOptions, "allow" | "deny">, credentialResult: CredentialInstallResult): BuildConfigResult;
|
|
24
|
+
export { buildPermissionsConfig };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission config building for agent execution.
|
|
3
|
+
*/
|
|
4
|
+
import { buildAgentConfig } from "axconfig";
|
|
5
|
+
/**
|
|
6
|
+
* Builds agent config with permissions.
|
|
7
|
+
*
|
|
8
|
+
* Requires credentialResult for the config directory.
|
|
9
|
+
*/
|
|
10
|
+
function buildPermissionsConfig(agentId, options, credentialResult) {
|
|
11
|
+
try {
|
|
12
|
+
const configResult = buildAgentConfig({
|
|
13
|
+
agentId,
|
|
14
|
+
allow: options.allow,
|
|
15
|
+
deny: options.deny,
|
|
16
|
+
output: credentialResult.configDirectory,
|
|
17
|
+
});
|
|
18
|
+
if (!configResult.ok) {
|
|
19
|
+
const errors = [];
|
|
20
|
+
if ("message" in configResult) {
|
|
21
|
+
errors.push(`Error: ${configResult.message}`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
errors.push("Error building config: permission errors");
|
|
25
|
+
for (const error of configResult.errors) {
|
|
26
|
+
errors.push(` - ${error.reason}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return { ok: false, exitCode: configResult.exitCode, errors };
|
|
30
|
+
}
|
|
31
|
+
const warnings = configResult.warnings.map((warning) => `Warning: ${warning.reason}`);
|
|
32
|
+
// Merge config env with credential env (config env takes precedence)
|
|
33
|
+
return {
|
|
34
|
+
ok: true,
|
|
35
|
+
env: { ...credentialResult.env, ...configResult.env },
|
|
36
|
+
warnings,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
41
|
+
return {
|
|
42
|
+
ok: false,
|
|
43
|
+
exitCode: 1,
|
|
44
|
+
errors: [`Error parsing permissions: ${message}`],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export { buildPermissionsConfig };
|
|
@@ -0,0 +1,39 @@
|
|
|
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 };
|
|
@@ -0,0 +1,73 @@
|
|
|
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 };
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Environment variable helpers for credential-based auth.
|
|
3
|
+
*
|
|
4
|
+
* Each agent has its own env var requirements. This module extracts
|
|
5
|
+
* the appropriate values from Credentials and returns them as env vars.
|
|
3
6
|
*/
|
|
4
|
-
import type {
|
|
5
|
-
import type { RawCredentials } from "./types.js";
|
|
7
|
+
import type { Credentials } from "./credentials.js";
|
|
6
8
|
/**
|
|
7
9
|
* Gets additional environment variables for credential-based auth.
|
|
8
10
|
*
|
|
9
|
-
* Some agents use environment variables for API keys
|
|
10
|
-
* or instead of file-based credentials.
|
|
11
|
+
* Some agents use environment variables for API keys or OAuth tokens
|
|
12
|
+
* in addition to or instead of file-based credentials.
|
|
11
13
|
*/
|
|
12
|
-
declare function getCredentialEnvironment(
|
|
14
|
+
declare function getCredentialEnvironment(credentials: Credentials): Record<string, string>;
|
|
13
15
|
export { getCredentialEnvironment };
|
|
@@ -1,42 +1,72 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Environment variable helpers for credential-based auth.
|
|
3
|
+
*
|
|
4
|
+
* Each agent has its own env var requirements. This module extracts
|
|
5
|
+
* the appropriate values from Credentials and returns them as env vars.
|
|
3
6
|
*/
|
|
7
|
+
import { resolveStringField } from "./resolve-string-field.js";
|
|
4
8
|
/**
|
|
5
9
|
* Gets additional environment variables for credential-based auth.
|
|
6
10
|
*
|
|
7
|
-
* Some agents use environment variables for API keys
|
|
8
|
-
* or instead of file-based credentials.
|
|
11
|
+
* Some agents use environment variables for API keys or OAuth tokens
|
|
12
|
+
* in addition to or instead of file-based credentials.
|
|
9
13
|
*/
|
|
10
|
-
function getCredentialEnvironment(
|
|
14
|
+
function getCredentialEnvironment(credentials) {
|
|
11
15
|
const environment = {};
|
|
12
|
-
|
|
16
|
+
// Extract API key from credentials data
|
|
17
|
+
const apiKey = resolveStringField(credentials.data, "apiKey", "key");
|
|
18
|
+
// Extract OAuth token from credentials data
|
|
19
|
+
const oauthToken = resolveStringField(credentials.data, "oauthToken", "token");
|
|
20
|
+
switch (credentials.agent) {
|
|
13
21
|
case "claude": {
|
|
14
|
-
if (credentials.apiKey) {
|
|
15
|
-
environment["ANTHROPIC_API_KEY"] =
|
|
22
|
+
if (credentials.type === "api-key" && apiKey) {
|
|
23
|
+
environment["ANTHROPIC_API_KEY"] = apiKey;
|
|
24
|
+
}
|
|
25
|
+
else if (credentials.type === "oauth-token" && oauthToken) {
|
|
26
|
+
environment["CLAUDE_CODE_OAUTH_TOKEN"] = oauthToken;
|
|
16
27
|
}
|
|
17
28
|
break;
|
|
18
29
|
}
|
|
19
30
|
case "codex": {
|
|
20
|
-
if (credentials.apiKey) {
|
|
21
|
-
environment["OPENAI_API_KEY"] =
|
|
31
|
+
if (credentials.type === "api-key" && apiKey) {
|
|
32
|
+
environment["OPENAI_API_KEY"] = apiKey;
|
|
22
33
|
}
|
|
23
34
|
break;
|
|
24
35
|
}
|
|
25
36
|
case "gemini": {
|
|
26
|
-
if (credentials.apiKey) {
|
|
27
|
-
environment["GEMINI_API_KEY"] =
|
|
37
|
+
if (credentials.type === "api-key" && apiKey) {
|
|
38
|
+
environment["GEMINI_API_KEY"] = apiKey;
|
|
28
39
|
}
|
|
29
40
|
break;
|
|
30
41
|
}
|
|
31
42
|
case "opencode": {
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
// OpenCode supports multiple providers, env var depends on provider
|
|
44
|
+
const provider = credentials.provider ?? "anthropic";
|
|
45
|
+
if (credentials.type === "api-key" && apiKey) {
|
|
46
|
+
switch (provider) {
|
|
47
|
+
case "anthropic": {
|
|
48
|
+
environment["ANTHROPIC_API_KEY"] = apiKey;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case "openai": {
|
|
52
|
+
environment["OPENAI_API_KEY"] = apiKey;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
case "gemini":
|
|
56
|
+
case "google": {
|
|
57
|
+
environment["GEMINI_API_KEY"] = apiKey;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
34
61
|
}
|
|
35
62
|
break;
|
|
36
63
|
}
|
|
37
64
|
case "copilot": {
|
|
38
|
-
|
|
39
|
-
|
|
65
|
+
// Copilot uses GitHub token as its credential
|
|
66
|
+
if (credentials.type === "api-key" && apiKey) {
|
|
67
|
+
environment["COPILOT_GITHUB_TOKEN"] = apiKey;
|
|
68
|
+
environment["GH_TOKEN"] = apiKey;
|
|
69
|
+
environment["GITHUB_TOKEN"] = apiKey;
|
|
40
70
|
}
|
|
41
71
|
break;
|
|
42
72
|
}
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Credential installation for isolated agent execution.
|
|
3
3
|
*
|
|
4
|
-
* Writes
|
|
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
6
|
* directory. This ensures complete isolation - agents never discover or use
|
|
7
7
|
* locally installed credentials.
|
|
8
8
|
*/
|
|
9
9
|
import { type AgentCli } from "axshared";
|
|
10
|
-
import type {
|
|
11
|
-
|
|
10
|
+
import type { Credentials } from "./credentials.js";
|
|
11
|
+
import type { InstallResult } from "./types.js";
|
|
12
|
+
type WarningWriter = (message: string) => void;
|
|
12
13
|
/**
|
|
13
|
-
* Installs credentials to a temporary directory and returns isolation env vars.
|
|
14
|
+
* Installs credentials (if provided) to a temporary directory and returns isolation env vars.
|
|
14
15
|
*
|
|
15
16
|
* This function:
|
|
16
17
|
* 1. Creates a temp directory structure appropriate for the agent
|
|
17
|
-
* 2. Writes credential files in the agent-specific format
|
|
18
|
+
* 2. Writes credential files in the agent-specific format (if provided)
|
|
18
19
|
* 3. Returns environment variables that point the agent at the temp directory
|
|
19
20
|
*
|
|
20
21
|
* The caller is responsible for cleaning up the temp directory after use.
|
|
21
22
|
*/
|
|
22
|
-
declare function installCredentials(agentId: AgentCli, credentials
|
|
23
|
+
declare function installCredentials(agentId: AgentCli, credentials?: Credentials, warn?: WarningWriter): Promise<InstallResult>;
|
|
23
24
|
/**
|
|
24
25
|
* Cleanup for use in finally blocks.
|
|
25
26
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Credential installation for isolated agent execution.
|
|
3
3
|
*
|
|
4
|
-
* Writes
|
|
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
6
|
* directory. This ensures complete isolation - agents never discover or use
|
|
7
7
|
* locally installed credentials.
|
|
@@ -42,14 +42,14 @@ 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) {
|
|
45
|
+
function installAgentCredentials(agentId, configDirectory, dataDirectory, credentials, warn) {
|
|
46
46
|
switch (agentId) {
|
|
47
47
|
case "claude": {
|
|
48
48
|
installClaudeCredentials(configDirectory, credentials);
|
|
49
49
|
break;
|
|
50
50
|
}
|
|
51
51
|
case "codex": {
|
|
52
|
-
installCodexCredentials(configDirectory, credentials);
|
|
52
|
+
installCodexCredentials(configDirectory, credentials, warn);
|
|
53
53
|
break;
|
|
54
54
|
}
|
|
55
55
|
case "gemini": {
|
|
@@ -57,7 +57,7 @@ function installAgentCredentials(agentId, configDirectory, dataDirectory, creden
|
|
|
57
57
|
break;
|
|
58
58
|
}
|
|
59
59
|
case "opencode": {
|
|
60
|
-
installOpenCodeCredentials(dataDirectory, credentials);
|
|
60
|
+
installOpenCodeCredentials(dataDirectory, credentials, warn);
|
|
61
61
|
break;
|
|
62
62
|
}
|
|
63
63
|
case "copilot": {
|
|
@@ -67,26 +67,36 @@ function installAgentCredentials(agentId, configDirectory, dataDirectory, creden
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
|
-
* Installs credentials to a temporary directory and returns isolation env vars.
|
|
70
|
+
* Installs credentials (if provided) to a temporary directory and returns isolation env vars.
|
|
71
71
|
*
|
|
72
72
|
* This function:
|
|
73
73
|
* 1. Creates a temp directory structure appropriate for the agent
|
|
74
|
-
* 2. Writes credential files in the agent-specific format
|
|
74
|
+
* 2. Writes credential files in the agent-specific format (if provided)
|
|
75
75
|
* 3. Returns environment variables that point the agent at the temp directory
|
|
76
76
|
*
|
|
77
77
|
* The caller is responsible for cleaning up the temp directory after use.
|
|
78
78
|
*/
|
|
79
|
-
async function installCredentials(agentId, credentials) {
|
|
79
|
+
async function installCredentials(agentId, credentials, warn) {
|
|
80
80
|
const { baseDirectory, configDirectory, dataDirectory } = await createTemporaryDirectories(agentId);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
81
|
+
try {
|
|
82
|
+
if (credentials) {
|
|
83
|
+
installAgentCredentials(agentId, configDirectory, dataDirectory, credentials, warn);
|
|
84
|
+
}
|
|
85
|
+
const runtimeEnvironment = buildAgentRuntimeEnvironment(agentId, configDirectory, dataDirectory);
|
|
86
|
+
const credentialEnvironment = credentials
|
|
87
|
+
? getCredentialEnvironment(credentials)
|
|
88
|
+
: {};
|
|
89
|
+
return {
|
|
90
|
+
env: { ...runtimeEnvironment, ...credentialEnvironment },
|
|
91
|
+
configDirectory,
|
|
92
|
+
dataDirectory,
|
|
93
|
+
baseDirectory,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
await cleanupCredentials(baseDirectory);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
90
100
|
}
|
|
91
101
|
/**
|
|
92
102
|
* Cleanup for use in finally blocks.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves a string field from a data object, checking primary and fallback keys.
|
|
3
|
+
* Returns trimmed value if non-empty, undefined otherwise.
|
|
4
|
+
* Empty strings and whitespace-only values are treated as missing.
|
|
5
|
+
*/
|
|
6
|
+
declare function resolveStringField(data: Record<string, unknown>, primaryKey: string, fallbackKey: string): string | undefined;
|
|
7
|
+
export { resolveStringField };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves a string field from a data object, checking primary and fallback keys.
|
|
3
|
+
* Returns trimmed value if non-empty, undefined otherwise.
|
|
4
|
+
* Empty strings and whitespace-only values are treated as missing.
|
|
5
|
+
*/
|
|
6
|
+
function resolveStringField(data, primaryKey, fallbackKey) {
|
|
7
|
+
const primary = data[primaryKey];
|
|
8
|
+
if (typeof primary === "string") {
|
|
9
|
+
const trimmed = primary.trim();
|
|
10
|
+
if (trimmed)
|
|
11
|
+
return trimmed;
|
|
12
|
+
}
|
|
13
|
+
const fallback = data[fallbackKey];
|
|
14
|
+
if (typeof fallback === "string") {
|
|
15
|
+
const trimmed = fallback.trim();
|
|
16
|
+
if (trimmed)
|
|
17
|
+
return trimmed;
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
export { resolveStringField };
|