axexec 1.0.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.
Files changed (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +196 -0
  3. package/bin/axexec +17 -0
  4. package/dist/agents/claude-code/adapter.d.ts +6 -0
  5. package/dist/agents/claude-code/adapter.js +71 -0
  6. package/dist/agents/claude-code/parse-event.d.ts +16 -0
  7. package/dist/agents/claude-code/parse-event.js +185 -0
  8. package/dist/agents/claude-code/types.d.ts +180 -0
  9. package/dist/agents/claude-code/types.js +112 -0
  10. package/dist/agents/codex/adapter.d.ts +6 -0
  11. package/dist/agents/codex/adapter.js +62 -0
  12. package/dist/agents/codex/item-parsers.d.ts +17 -0
  13. package/dist/agents/codex/item-parsers.js +126 -0
  14. package/dist/agents/codex/parse-event.d.ts +38 -0
  15. package/dist/agents/codex/parse-event.js +141 -0
  16. package/dist/agents/codex/types.d.ts +407 -0
  17. package/dist/agents/codex/types.js +188 -0
  18. package/dist/agents/copilot/adapter.d.ts +11 -0
  19. package/dist/agents/copilot/adapter.js +81 -0
  20. package/dist/agents/copilot/parse-event.d.ts +20 -0
  21. package/dist/agents/copilot/parse-event.js +137 -0
  22. package/dist/agents/copilot/stream-session.d.ts +10 -0
  23. package/dist/agents/copilot/stream-session.js +120 -0
  24. package/dist/agents/copilot/tail-file.d.ts +11 -0
  25. package/dist/agents/copilot/tail-file.js +52 -0
  26. package/dist/agents/copilot/transform-event.d.ts +19 -0
  27. package/dist/agents/copilot/transform-event.js +100 -0
  28. package/dist/agents/copilot/types.d.ts +320 -0
  29. package/dist/agents/copilot/types.js +220 -0
  30. package/dist/agents/copilot/watch-session.d.ts +26 -0
  31. package/dist/agents/copilot/watch-session.js +186 -0
  32. package/dist/agents/gemini/adapter.d.ts +6 -0
  33. package/dist/agents/gemini/adapter.js +78 -0
  34. package/dist/agents/gemini/parse-event.d.ts +18 -0
  35. package/dist/agents/gemini/parse-event.js +144 -0
  36. package/dist/agents/gemini/types.d.ts +162 -0
  37. package/dist/agents/gemini/types.js +103 -0
  38. package/dist/agents/opencode/adapter.d.ts +16 -0
  39. package/dist/agents/opencode/adapter.js +142 -0
  40. package/dist/agents/opencode/cleanup-session.d.ts +17 -0
  41. package/dist/agents/opencode/cleanup-session.js +41 -0
  42. package/dist/agents/opencode/create-session-start-event.d.ts +18 -0
  43. package/dist/agents/opencode/create-session-start-event.js +24 -0
  44. package/dist/agents/opencode/detect-empty-session.d.ts +25 -0
  45. package/dist/agents/opencode/detect-empty-session.js +42 -0
  46. package/dist/agents/opencode/map-error-to-event.d.ts +10 -0
  47. package/dist/agents/opencode/map-error-to-event.js +55 -0
  48. package/dist/agents/opencode/parse-message-part.d.ts +24 -0
  49. package/dist/agents/opencode/parse-message-part.js +112 -0
  50. package/dist/agents/opencode/parse-sse-event.d.ts +23 -0
  51. package/dist/agents/opencode/parse-sse-event.js +125 -0
  52. package/dist/agents/opencode/process-sse-events.d.ts +24 -0
  53. package/dist/agents/opencode/process-sse-events.js +66 -0
  54. package/dist/agents/opencode/server-types.d.ts +509 -0
  55. package/dist/agents/opencode/server-types.js +293 -0
  56. package/dist/agents/opencode/session-api.d.ts +56 -0
  57. package/dist/agents/opencode/session-api.js +151 -0
  58. package/dist/agents/opencode/spawn-server.d.ts +29 -0
  59. package/dist/agents/opencode/spawn-server.js +95 -0
  60. package/dist/agents/opencode/sse-client.d.ts +33 -0
  61. package/dist/agents/opencode/sse-client.js +145 -0
  62. package/dist/agents/registry.d.ts +15 -0
  63. package/dist/agents/registry.js +24 -0
  64. package/dist/cli.d.ts +15 -0
  65. package/dist/cli.js +119 -0
  66. package/dist/credentials/get-credential-environment.d.ts +13 -0
  67. package/dist/credentials/get-credential-environment.js +46 -0
  68. package/dist/credentials/install-credentials.d.ts +27 -0
  69. package/dist/credentials/install-credentials.js +102 -0
  70. package/dist/credentials/save-json-file.d.ts +11 -0
  71. package/dist/credentials/save-json-file.js +21 -0
  72. package/dist/credentials/types.d.ts +24 -0
  73. package/dist/credentials/types.js +4 -0
  74. package/dist/credentials/write-agent-credentials.d.ts +36 -0
  75. package/dist/credentials/write-agent-credentials.js +91 -0
  76. package/dist/determine-session-success.d.ts +15 -0
  77. package/dist/determine-session-success.js +25 -0
  78. package/dist/format-event-tsv.d.ts +23 -0
  79. package/dist/format-event-tsv.js +136 -0
  80. package/dist/parse-credentials.d.ts +13 -0
  81. package/dist/parse-credentials.js +63 -0
  82. package/dist/parse-iso-timestamp.d.ts +21 -0
  83. package/dist/parse-iso-timestamp.js +37 -0
  84. package/dist/resolve-binary.d.ts +29 -0
  85. package/dist/resolve-binary.js +46 -0
  86. package/dist/resolve-output-mode.d.ts +39 -0
  87. package/dist/resolve-output-mode.js +39 -0
  88. package/dist/run-agent.d.ts +32 -0
  89. package/dist/run-agent.js +146 -0
  90. package/dist/stream-agent.d.ts +20 -0
  91. package/dist/stream-agent.js +207 -0
  92. package/dist/types/adapter.d.ts +101 -0
  93. package/dist/types/adapter.js +14 -0
  94. package/dist/types/events.d.ts +82 -0
  95. package/dist/types/events.js +13 -0
  96. package/dist/types/options.d.ts +41 -0
  97. package/dist/types/options.js +4 -0
  98. package/dist/validate-agent.d.ts +20 -0
  99. package/dist/validate-agent.js +50 -0
  100. package/dist/write-event.d.ts +23 -0
  101. package/dist/write-event.js +43 -0
  102. package/package.json +79 -0
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Per-agent credential file writers.
3
+ *
4
+ * Each agent has its own credential file format. These functions
5
+ * write credentials in the agent-specific format.
6
+ */
7
+ import path from "node:path";
8
+ import { saveJsonFile } from "./save-json-file.js";
9
+ /**
10
+ * Installs Claude credentials to a config directory.
11
+ *
12
+ * File: .credentials.json
13
+ * Format: { claudeAiOauth: { ... } }
14
+ */
15
+ function installClaudeCredentials(configDirectory, credentials) {
16
+ if (!credentials.oauth && !credentials.apiKey) {
17
+ return;
18
+ }
19
+ const credentialsPath = path.join(configDirectory, ".credentials.json");
20
+ if (credentials.oauth) {
21
+ saveJsonFile(credentialsPath, { claudeAiOauth: credentials.oauth });
22
+ }
23
+ // API key is passed via environment variable, not file
24
+ }
25
+ /**
26
+ * Installs Codex credentials to a config directory.
27
+ *
28
+ * File: auth.json
29
+ * Format: { OPENAI_API_KEY: "..." } or { tokens: { ... }, last_refresh: "..." }
30
+ */
31
+ function installCodexCredentials(configDirectory, credentials) {
32
+ if (!credentials.oauth && !credentials.apiKey) {
33
+ return;
34
+ }
35
+ const authPath = path.join(configDirectory, "auth.json");
36
+ if (credentials.apiKey) {
37
+ saveJsonFile(authPath, { OPENAI_API_KEY: credentials.apiKey });
38
+ }
39
+ else if (credentials.oauth) {
40
+ saveJsonFile(authPath, {
41
+ tokens: credentials.oauth,
42
+ last_refresh: new Date().toISOString(),
43
+ });
44
+ }
45
+ }
46
+ /**
47
+ * Installs Gemini credentials to a config directory.
48
+ *
49
+ * File: oauth_creds.json
50
+ * Format: { access_token: "...", refresh_token: "...", ... }
51
+ */
52
+ function installGeminiCredentials(configDirectory, credentials) {
53
+ if (!credentials.oauth && !credentials.apiKey) {
54
+ return;
55
+ }
56
+ if (credentials.oauth) {
57
+ const credentialsPath = path.join(configDirectory, "oauth_creds.json");
58
+ saveJsonFile(credentialsPath, credentials.oauth);
59
+ }
60
+ // API key is passed via environment variable, not file
61
+ }
62
+ /**
63
+ * Installs OpenCode credentials to a data directory.
64
+ *
65
+ * File: auth.json
66
+ * Format: { anthropic: { type: "oauth", ... }, openai: { type: "api", ... } }
67
+ */
68
+ function installOpenCodeCredentials(dataDirectory, credentials) {
69
+ if (!credentials.oauth && !credentials.apiKey) {
70
+ return;
71
+ }
72
+ const authPath = path.join(dataDirectory, "auth.json");
73
+ const authData = {};
74
+ if (credentials.oauth) {
75
+ authData.anthropic = {
76
+ type: "oauth",
77
+ ...credentials.oauth,
78
+ };
79
+ }
80
+ if (credentials.apiKey) {
81
+ authData.anthropic = {
82
+ type: "api",
83
+ key: credentials.apiKey,
84
+ };
85
+ }
86
+ if (Object.keys(authData).length > 0) {
87
+ saveJsonFile(authPath, authData);
88
+ }
89
+ }
90
+ // Note: Copilot uses environment variables (GITHUB_TOKEN), not file-based credentials.
91
+ export { installClaudeCredentials, installCodexCredentials, installGeminiCredentials, installOpenCodeCredentials, };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Determines session success from an event stream.
3
+ *
4
+ * Shared implementation used by all adapters to check if a session
5
+ * completed successfully based on the normalized event stream.
6
+ */
7
+ import type { AxexecEvent } from "./types/events.js";
8
+ /**
9
+ * Determines if the session completed successfully based on the event stream.
10
+ *
11
+ * Scans backwards for terminal events: returns true if session.complete
12
+ * is found before any session.error event.
13
+ */
14
+ declare function determineSessionSuccess(events: AxexecEvent[]): boolean;
15
+ export { determineSessionSuccess };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Determines session success from an event stream.
3
+ *
4
+ * Shared implementation used by all adapters to check if a session
5
+ * completed successfully based on the normalized event stream.
6
+ */
7
+ /**
8
+ * Determines if the session completed successfully based on the event stream.
9
+ *
10
+ * Scans backwards for terminal events: returns true if session.complete
11
+ * is found before any session.error event.
12
+ */
13
+ function determineSessionSuccess(events) {
14
+ for (let index = events.length - 1; index >= 0; index--) {
15
+ const event = events[index];
16
+ if (event?.type === "session.complete") {
17
+ return true;
18
+ }
19
+ if (event?.type === "session.error") {
20
+ return false;
21
+ }
22
+ }
23
+ return false;
24
+ }
25
+ export { determineSessionSuccess };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * TSV (Tab-Separated Values) formatter for axexec events.
3
+ *
4
+ * Formats events as human-readable TSV lines with proper escaping
5
+ * for newlines and tabs. Supports optional truncation for interactive
6
+ * display.
7
+ */
8
+ import type { AxexecEvent } from "./types/events.js";
9
+ interface FormatOptions {
10
+ /** Whether to truncate long content (default: false) */
11
+ truncate?: boolean;
12
+ /** Maximum length before truncation (default: 80) */
13
+ maxLength?: number;
14
+ }
15
+ /**
16
+ * Formats an AxexecEvent as a TSV line.
17
+ *
18
+ * @param event - The event to format
19
+ * @param options - Formatting options
20
+ * @returns A single TSV line (without trailing newline)
21
+ */
22
+ declare function formatEventTsv(event: AxexecEvent, options?: FormatOptions): string;
23
+ export { formatEventTsv };
@@ -0,0 +1,136 @@
1
+ /**
2
+ * TSV (Tab-Separated Values) formatter for axexec events.
3
+ *
4
+ * Formats events as human-readable TSV lines with proper escaping
5
+ * for newlines and tabs. Supports optional truncation for interactive
6
+ * display.
7
+ */
8
+ /** Default maximum length for content before truncation */
9
+ const DEFAULT_MAX_LENGTH = 80;
10
+ /**
11
+ * Escapes special characters in a string for TSV output.
12
+ * - Newlines → \n (literal backslash-n)
13
+ * - Tabs → \t (literal backslash-t)
14
+ * - Backslashes → \\ (escaped)
15
+ */
16
+ function escapeTsv(value) {
17
+ return value
18
+ .replaceAll("\\", String.raw `\\`)
19
+ .replaceAll("\n", String.raw `\n`)
20
+ .replaceAll("\t", String.raw `\t`);
21
+ }
22
+ /**
23
+ * Truncates a string with a [+N] marker indicating remaining characters.
24
+ * Returns the original string if within maxLength.
25
+ */
26
+ function truncateWithMarker(value, maxLength) {
27
+ if (value.length <= maxLength) {
28
+ return value;
29
+ }
30
+ const remaining = value.length - maxLength;
31
+ const marker = `[+${remaining}]`;
32
+ // Ensure we have room for the marker
33
+ const truncateAt = maxLength - marker.length;
34
+ if (truncateAt <= 0) {
35
+ return marker;
36
+ }
37
+ return value.slice(0, truncateAt) + marker;
38
+ }
39
+ /**
40
+ * Formats a timestamp (milliseconds since epoch) as HH:MM:SS local time.
41
+ */
42
+ function formatTime(timestamp) {
43
+ const date = new Date(timestamp);
44
+ return date.toLocaleTimeString("en-US", {
45
+ hour12: false,
46
+ hour: "2-digit",
47
+ minute: "2-digit",
48
+ second: "2-digit",
49
+ });
50
+ }
51
+ /**
52
+ * Formats a duration in milliseconds as a human-readable string.
53
+ */
54
+ function formatDuration(ms) {
55
+ if (ms < 1000) {
56
+ return `${ms}ms`;
57
+ }
58
+ const seconds = ms / 1000;
59
+ if (seconds < 60) {
60
+ return `${seconds.toFixed(1)}s`;
61
+ }
62
+ const minutes = Math.floor(seconds / 60);
63
+ const remainingSeconds = seconds % 60;
64
+ return `${minutes}m${remainingSeconds.toFixed(0)}s`;
65
+ }
66
+ /**
67
+ * Summarizes an unknown value for TSV display.
68
+ * Objects are JSON-stringified, primitives are converted to strings.
69
+ */
70
+ function summarizeValue(value) {
71
+ if (value === null || value === undefined) {
72
+ return "";
73
+ }
74
+ if (typeof value === "string") {
75
+ return value;
76
+ }
77
+ if (typeof value === "number" || typeof value === "boolean") {
78
+ return String(value);
79
+ }
80
+ return JSON.stringify(value);
81
+ }
82
+ /**
83
+ * Formats an AxexecEvent as a TSV line.
84
+ *
85
+ * @param event - The event to format
86
+ * @param options - Formatting options
87
+ * @returns A single TSV line (without trailing newline)
88
+ */
89
+ function formatEventTsv(event, options = {}) {
90
+ const { truncate = false, maxLength = DEFAULT_MAX_LENGTH } = options;
91
+ const time = formatTime(event.timestamp);
92
+ const processContent = (content) => {
93
+ const escaped = escapeTsv(content);
94
+ return truncate ? truncateWithMarker(escaped, maxLength) : escaped;
95
+ };
96
+ switch (event.type) {
97
+ case "session.start": {
98
+ return `${time}\t${event.type}\tagent=${event.agent}\tmodel=${event.model}`;
99
+ }
100
+ case "session.complete": {
101
+ const parts = [
102
+ time,
103
+ event.type,
104
+ `duration=${formatDuration(event.stats.durationMs)}`,
105
+ ];
106
+ if (event.stats.inputTokens !== undefined ||
107
+ event.stats.outputTokens !== undefined) {
108
+ const input = event.stats.inputTokens ?? 0;
109
+ const output = event.stats.outputTokens ?? 0;
110
+ parts.push(`tokens=${input}+${output}`);
111
+ }
112
+ if (event.stats.costUsd !== undefined) {
113
+ parts.push(`cost=$${event.stats.costUsd.toFixed(4)}`);
114
+ }
115
+ return parts.join("\t");
116
+ }
117
+ case "session.error": {
118
+ const message = processContent(event.message);
119
+ return `${time}\t${event.type}\tcode=${event.code}\t${message}`;
120
+ }
121
+ case "agent.reasoning":
122
+ case "agent.message": {
123
+ return `${time}\t${event.type}\t${processContent(event.content)}`;
124
+ }
125
+ case "tool.call": {
126
+ const inputSummary = processContent(summarizeValue(event.input));
127
+ return `${time}\t${event.type}\ttool=${event.tool}\t${inputSummary}`;
128
+ }
129
+ case "tool.result": {
130
+ const outputSummary = processContent(summarizeValue(event.output));
131
+ const status = event.success ? "success" : "failed";
132
+ return `${time}\t${event.type}\ttool=${event.tool}\t${status}\t${outputSummary}`;
133
+ }
134
+ }
135
+ }
136
+ export { formatEventTsv };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Credential parsing from environment variables.
3
+ */
4
+ import type { RawCredentials } from "./credentials/install-credentials.js";
5
+ /**
6
+ * Parses credentials from environment variables.
7
+ *
8
+ * Environment variables checked (in order):
9
+ * - AX_<AGENT>_CREDENTIALS: JSON with credentials
10
+ * - Standard env vars: ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.
11
+ */
12
+ declare function parseCredentialsFromEnvironment(agentId: string): RawCredentials;
13
+ export { parseCredentialsFromEnvironment };
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Credential parsing from environment variables.
3
+ */
4
+ /**
5
+ * Parses credentials from environment variables.
6
+ *
7
+ * Environment variables checked (in order):
8
+ * - AX_<AGENT>_CREDENTIALS: JSON with credentials
9
+ * - Standard env vars: ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.
10
+ */
11
+ function parseCredentialsFromEnvironment(agentId) {
12
+ const credentials = {};
13
+ // Check for axexec credentials env var
14
+ const credEnvironmentVariable = `AX_${agentId.toUpperCase()}_CREDENTIALS`;
15
+ const credEnvironmentValue = process.env[credEnvironmentVariable];
16
+ if (credEnvironmentValue) {
17
+ try {
18
+ const parsed = JSON.parse(credEnvironmentValue);
19
+ return parsed;
20
+ }
21
+ catch {
22
+ process.stderr.write(`Warning: Failed to parse ${credEnvironmentVariable}\n`);
23
+ }
24
+ }
25
+ // Fall back to standard env vars
26
+ switch (agentId) {
27
+ case "claude": {
28
+ if (process.env["ANTHROPIC_API_KEY"]) {
29
+ credentials.apiKey = process.env["ANTHROPIC_API_KEY"];
30
+ }
31
+ break;
32
+ }
33
+ case "codex": {
34
+ if (process.env["OPENAI_API_KEY"]) {
35
+ credentials.apiKey = process.env["OPENAI_API_KEY"];
36
+ }
37
+ break;
38
+ }
39
+ case "gemini": {
40
+ if (process.env["GEMINI_API_KEY"]) {
41
+ credentials.apiKey = process.env["GEMINI_API_KEY"];
42
+ }
43
+ break;
44
+ }
45
+ case "opencode": {
46
+ if (process.env["ANTHROPIC_API_KEY"]) {
47
+ credentials.apiKey = process.env["ANTHROPIC_API_KEY"];
48
+ }
49
+ break;
50
+ }
51
+ case "copilot": {
52
+ const token = process.env["GITHUB_TOKEN"] ??
53
+ process.env["GH_TOKEN"] ??
54
+ process.env["COPILOT_GITHUB_TOKEN"];
55
+ if (token) {
56
+ credentials.githubToken = token;
57
+ }
58
+ break;
59
+ }
60
+ }
61
+ return credentials;
62
+ }
63
+ export { parseCredentialsFromEnvironment };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * ISO timestamp parsing utility.
3
+ *
4
+ * Converts ISO 8601 timestamp strings to epoch milliseconds,
5
+ * with stateful fallback to preserve event ordering.
6
+ */
7
+ /**
8
+ * Creates a stateful timestamp parser that tracks the last valid timestamp.
9
+ *
10
+ * When a timestamp is missing or invalid, falls back to the previous event's
11
+ * timestamp to preserve ordering. Only uses `Date.now()` if no previous
12
+ * timestamp exists (i.e., the very first event has no timestamp).
13
+ *
14
+ * @example
15
+ * const parseTimestamp = createTimestampParser();
16
+ * parseTimestamp("2025-01-15T14:30:45.123Z") // Returns 1736951445123
17
+ * parseTimestamp(undefined) // Returns 1736951445123 (previous value)
18
+ * parseTimestamp("2025-01-15T14:30:46.000Z") // Returns 1736951446000
19
+ */
20
+ declare function createTimestampParser(): (isoString: string | undefined) => number;
21
+ export { createTimestampParser };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * ISO timestamp parsing utility.
3
+ *
4
+ * Converts ISO 8601 timestamp strings to epoch milliseconds,
5
+ * with stateful fallback to preserve event ordering.
6
+ */
7
+ /**
8
+ * Creates a stateful timestamp parser that tracks the last valid timestamp.
9
+ *
10
+ * When a timestamp is missing or invalid, falls back to the previous event's
11
+ * timestamp to preserve ordering. Only uses `Date.now()` if no previous
12
+ * timestamp exists (i.e., the very first event has no timestamp).
13
+ *
14
+ * @example
15
+ * const parseTimestamp = createTimestampParser();
16
+ * parseTimestamp("2025-01-15T14:30:45.123Z") // Returns 1736951445123
17
+ * parseTimestamp(undefined) // Returns 1736951445123 (previous value)
18
+ * parseTimestamp("2025-01-15T14:30:46.000Z") // Returns 1736951446000
19
+ */
20
+ function createTimestampParser() {
21
+ let lastTimestamp;
22
+ return (isoString) => {
23
+ if (isoString) {
24
+ const parsed = Date.parse(isoString);
25
+ if (!Number.isNaN(parsed)) {
26
+ lastTimestamp = parsed;
27
+ return parsed;
28
+ }
29
+ }
30
+ // Fall back to previous timestamp, or Date.now() for very first event
31
+ if (lastTimestamp === undefined) {
32
+ lastTimestamp = Date.now();
33
+ }
34
+ return lastTimestamp;
35
+ };
36
+ }
37
+ export { createTimestampParser };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Resolve and validate external CLI binaries used by axexec.
3
+ */
4
+ interface ResolveBinaryOptions {
5
+ /** Friendly dependency name for error messages */
6
+ name: string;
7
+ /** Default command name on PATH */
8
+ command: string;
9
+ /** Environment variable override for the binary path */
10
+ environmentVariable: string;
11
+ /** Install hint shown in error messages */
12
+ installHint: string;
13
+ }
14
+ declare class DependencyError extends Error {
15
+ name: string;
16
+ readonly dependency: string;
17
+ readonly environmentVariable: string;
18
+ readonly command: string;
19
+ constructor(message: string, dependency: string, environmentVariable: string, command: string);
20
+ }
21
+ /**
22
+ * Resolves a binary path and verifies it can be spawned.
23
+ *
24
+ * Checks only that the binary exists and can be spawned - does not validate
25
+ * exit code since we're checking availability, not correctness. Uses a short
26
+ * timeout to prevent hangs in CI environments.
27
+ */
28
+ declare function resolveBinary(options: ResolveBinaryOptions): string;
29
+ export { DependencyError, resolveBinary };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Resolve and validate external CLI binaries used by axexec.
3
+ */
4
+ import { spawnSync } from "node:child_process";
5
+ class DependencyError extends Error {
6
+ name = "DependencyError";
7
+ dependency;
8
+ environmentVariable;
9
+ command;
10
+ constructor(message, dependency, environmentVariable, command) {
11
+ super(message);
12
+ this.dependency = dependency;
13
+ this.environmentVariable = environmentVariable;
14
+ this.command = command;
15
+ }
16
+ }
17
+ /**
18
+ * Resolves a binary path and verifies it can be spawned.
19
+ *
20
+ * Checks only that the binary exists and can be spawned - does not validate
21
+ * exit code since we're checking availability, not correctness. Uses a short
22
+ * timeout to prevent hangs in CI environments.
23
+ */
24
+ function resolveBinary(options) {
25
+ const override = process.env[options.environmentVariable];
26
+ const candidate = override && override.trim() !== "" ? override : options.command;
27
+ const result = spawnSync(candidate, ["--version"], {
28
+ stdio: "ignore",
29
+ timeout: 5000,
30
+ });
31
+ if (result.error) {
32
+ const lines = [
33
+ `Error: Required dependency '${options.name}' not found.`,
34
+ `Looked for: ${candidate}`,
35
+ `Reason: ${result.error.message}`,
36
+ "",
37
+ "To fix:",
38
+ ` 1. Install it: ${options.installHint}`,
39
+ ` 2. Set ${options.environmentVariable}=/path/to/${options.command}`,
40
+ "See: axexec --help for requirements.",
41
+ ];
42
+ throw new DependencyError(lines.join("\n"), options.name, options.environmentVariable, options.command);
43
+ }
44
+ return candidate;
45
+ }
46
+ export { DependencyError, resolveBinary };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Resolves the output mode based on CLI flags and TTY status.
3
+ *
4
+ * The output mode determines how events are formatted:
5
+ * - jsonl: JSON Lines format (one JSON object per line)
6
+ * - tsv: Tab-separated values (no truncation)
7
+ * - tsv-truncated: Tab-separated values with truncation for readability
8
+ */
9
+ /** User-specified output format from CLI flag */
10
+ type OutputFormat = "jsonl" | "tsv";
11
+ /**
12
+ * Resolved output mode including auto-detected variants.
13
+ * - jsonl: Full JSON Lines output
14
+ * - tsv: Full TSV output (no truncation)
15
+ * - tsv-truncated: TSV with truncation for interactive display
16
+ */
17
+ type OutputMode = "jsonl" | "tsv" | "tsv-truncated";
18
+ /**
19
+ * Resolves the output mode based on format flag and TTY status.
20
+ *
21
+ * Pure function for testability - isTTY is passed as argument.
22
+ *
23
+ * @param format - User-specified format from --format flag, or undefined for auto-detect
24
+ * @param isTTY - Whether stdout is a TTY (interactive terminal)
25
+ * @returns The resolved output mode
26
+ *
27
+ * @example
28
+ * // Explicit format overrides TTY detection
29
+ * resolveOutputMode("jsonl", true) // → "jsonl"
30
+ * resolveOutputMode("tsv", false) // → "tsv"
31
+ *
32
+ * @example
33
+ * // Auto-detect based on TTY
34
+ * resolveOutputMode(undefined, true) // → "tsv-truncated"
35
+ * resolveOutputMode(undefined, false) // → "tsv"
36
+ */
37
+ declare function resolveOutputMode(format: OutputFormat | undefined, isTTY: boolean): OutputMode;
38
+ export { resolveOutputMode };
39
+ export type { OutputFormat, OutputMode };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Resolves the output mode based on CLI flags and TTY status.
3
+ *
4
+ * The output mode determines how events are formatted:
5
+ * - jsonl: JSON Lines format (one JSON object per line)
6
+ * - tsv: Tab-separated values (no truncation)
7
+ * - tsv-truncated: Tab-separated values with truncation for readability
8
+ */
9
+ /**
10
+ * Resolves the output mode based on format flag and TTY status.
11
+ *
12
+ * Pure function for testability - isTTY is passed as argument.
13
+ *
14
+ * @param format - User-specified format from --format flag, or undefined for auto-detect
15
+ * @param isTTY - Whether stdout is a TTY (interactive terminal)
16
+ * @returns The resolved output mode
17
+ *
18
+ * @example
19
+ * // Explicit format overrides TTY detection
20
+ * resolveOutputMode("jsonl", true) // → "jsonl"
21
+ * resolveOutputMode("tsv", false) // → "tsv"
22
+ *
23
+ * @example
24
+ * // Auto-detect based on TTY
25
+ * resolveOutputMode(undefined, true) // → "tsv-truncated"
26
+ * resolveOutputMode(undefined, false) // → "tsv"
27
+ */
28
+ function resolveOutputMode(format, isTTY) {
29
+ // Explicit format from flag takes precedence
30
+ if (format === "jsonl") {
31
+ return "jsonl";
32
+ }
33
+ if (format === "tsv") {
34
+ return "tsv";
35
+ }
36
+ // Auto-detect based on TTY
37
+ return isTTY ? "tsv-truncated" : "tsv";
38
+ }
39
+ export { resolveOutputMode };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Agent execution with credential isolation and config generation.
3
+ */
4
+ import type { AxexecEvent } from "./types/events.js";
5
+ import { type OutputFormat } from "./resolve-output-mode.js";
6
+ interface RunAgentOptions {
7
+ prompt: string;
8
+ model?: string;
9
+ allow?: string;
10
+ deny?: string;
11
+ format?: OutputFormat;
12
+ rawLog?: string;
13
+ debug?: boolean;
14
+ verbose?: boolean;
15
+ preserveGithubSha?: boolean;
16
+ }
17
+ interface RunResult {
18
+ events: AxexecEvent[];
19
+ success: boolean;
20
+ }
21
+ /**
22
+ * Runs an agent with full isolation.
23
+ *
24
+ * 1. Validates the agent ID
25
+ * 2. Parses credentials from environment
26
+ * 3. Installs credentials to temp directory
27
+ * 4. Builds config if permissions specified
28
+ * 5. Streams events from agent
29
+ * 6. Cleans up temp directory
30
+ */
31
+ declare function runAgent(agentId: string, options: RunAgentOptions): Promise<RunResult>;
32
+ export { runAgent };