@herdctl/core 1.3.0 → 2.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.
- package/dist/config/__tests__/agent.test.js +12 -12
- package/dist/config/__tests__/agent.test.js.map +1 -1
- package/dist/config/__tests__/loader.test.js +201 -4
- package/dist/config/__tests__/loader.test.js.map +1 -1
- package/dist/config/__tests__/merge.test.js +29 -4
- package/dist/config/__tests__/merge.test.js.map +1 -1
- package/dist/config/__tests__/parser.test.js +13 -13
- package/dist/config/__tests__/parser.test.js.map +1 -1
- package/dist/config/__tests__/schema.test.js +10 -10
- package/dist/config/__tests__/schema.test.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +2 -2
- package/dist/config/index.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +71 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/merge.d.ts +4 -1
- package/dist/config/merge.d.ts.map +1 -1
- package/dist/config/merge.js +16 -0
- package/dist/config/merge.js.map +1 -1
- package/dist/config/schema.d.ts +906 -89
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +109 -7
- package/dist/config/schema.js.map +1 -1
- package/dist/fleet-manager/__tests__/coverage.test.js +25 -24
- package/dist/fleet-manager/__tests__/coverage.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/discord-manager.test.js +9 -2
- package/dist/fleet-manager/__tests__/discord-manager.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/integration.test.js +27 -0
- package/dist/fleet-manager/__tests__/integration.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/job-control.test.js +66 -0
- package/dist/fleet-manager/__tests__/job-control.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/status-queries.test.js +12 -11
- package/dist/fleet-manager/__tests__/status-queries.test.js.map +1 -1
- package/dist/fleet-manager/config-reload.js +9 -9
- package/dist/fleet-manager/config-reload.js.map +1 -1
- package/dist/fleet-manager/discord-manager.d.ts.map +1 -1
- package/dist/fleet-manager/discord-manager.js +27 -4
- package/dist/fleet-manager/discord-manager.js.map +1 -1
- package/dist/fleet-manager/fleet-manager.d.ts +11 -0
- package/dist/fleet-manager/fleet-manager.d.ts.map +1 -1
- package/dist/fleet-manager/fleet-manager.js +27 -0
- package/dist/fleet-manager/fleet-manager.js.map +1 -1
- package/dist/fleet-manager/job-control.d.ts +1 -1
- package/dist/fleet-manager/job-control.d.ts.map +1 -1
- package/dist/fleet-manager/job-control.js +36 -14
- package/dist/fleet-manager/job-control.js.map +1 -1
- package/dist/fleet-manager/schedule-executor.d.ts +1 -1
- package/dist/fleet-manager/schedule-executor.d.ts.map +1 -1
- package/dist/fleet-manager/schedule-executor.js +11 -14
- package/dist/fleet-manager/schedule-executor.js.map +1 -1
- package/dist/fleet-manager/status-queries.js +7 -7
- package/dist/fleet-manager/status-queries.js.map +1 -1
- package/dist/fleet-manager/types.d.ts +10 -2
- package/dist/fleet-manager/types.d.ts.map +1 -1
- package/dist/fleet-manager/working-directory-helper.d.ts +29 -0
- package/dist/fleet-manager/working-directory-helper.d.ts.map +1 -0
- package/dist/fleet-manager/working-directory-helper.js +36 -0
- package/dist/fleet-manager/working-directory-helper.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/runner/__tests__/job-executor.test.js +449 -118
- package/dist/runner/__tests__/job-executor.test.js.map +1 -1
- package/dist/runner/__tests__/sdk-adapter.test.js +147 -23
- package/dist/runner/__tests__/sdk-adapter.test.js.map +1 -1
- package/dist/runner/index.d.ts +2 -0
- package/dist/runner/index.d.ts.map +1 -1
- package/dist/runner/index.js +1 -0
- package/dist/runner/index.js.map +1 -1
- package/dist/runner/job-executor.d.ts +12 -8
- package/dist/runner/job-executor.d.ts.map +1 -1
- package/dist/runner/job-executor.js +257 -126
- package/dist/runner/job-executor.js.map +1 -1
- package/dist/runner/runtime/__tests__/cli-session-path.test.d.ts +2 -0
- package/dist/runner/runtime/__tests__/cli-session-path.test.d.ts.map +1 -0
- package/dist/runner/runtime/__tests__/cli-session-path.test.js +150 -0
- package/dist/runner/runtime/__tests__/cli-session-path.test.js.map +1 -0
- package/dist/runner/runtime/__tests__/docker-config.test.d.ts +2 -0
- package/dist/runner/runtime/__tests__/docker-config.test.d.ts.map +1 -0
- package/dist/runner/runtime/__tests__/docker-config.test.js +352 -0
- package/dist/runner/runtime/__tests__/docker-config.test.js.map +1 -0
- package/dist/runner/runtime/__tests__/docker-security.test.d.ts +2 -0
- package/dist/runner/runtime/__tests__/docker-security.test.d.ts.map +1 -0
- package/dist/runner/runtime/__tests__/docker-security.test.js +384 -0
- package/dist/runner/runtime/__tests__/docker-security.test.js.map +1 -0
- package/dist/runner/runtime/__tests__/factory.test.d.ts +2 -0
- package/dist/runner/runtime/__tests__/factory.test.d.ts.map +1 -0
- package/dist/runner/runtime/__tests__/factory.test.js +149 -0
- package/dist/runner/runtime/__tests__/factory.test.js.map +1 -0
- package/dist/runner/runtime/__tests__/integration.test.d.ts +2 -0
- package/dist/runner/runtime/__tests__/integration.test.d.ts.map +1 -0
- package/dist/runner/runtime/__tests__/integration.test.js +274 -0
- package/dist/runner/runtime/__tests__/integration.test.js.map +1 -0
- package/dist/runner/runtime/cli-runtime.d.ts +107 -0
- package/dist/runner/runtime/cli-runtime.d.ts.map +1 -0
- package/dist/runner/runtime/cli-runtime.js +335 -0
- package/dist/runner/runtime/cli-runtime.js.map +1 -0
- package/dist/runner/runtime/cli-session-path.d.ts +108 -0
- package/dist/runner/runtime/cli-session-path.d.ts.map +1 -0
- package/dist/runner/runtime/cli-session-path.js +173 -0
- package/dist/runner/runtime/cli-session-path.js.map +1 -0
- package/dist/runner/runtime/cli-session-watcher.d.ts +55 -0
- package/dist/runner/runtime/cli-session-watcher.d.ts.map +1 -0
- package/dist/runner/runtime/cli-session-watcher.js +187 -0
- package/dist/runner/runtime/cli-session-watcher.js.map +1 -0
- package/dist/runner/runtime/container-manager.d.ts +76 -0
- package/dist/runner/runtime/container-manager.d.ts.map +1 -0
- package/dist/runner/runtime/container-manager.js +229 -0
- package/dist/runner/runtime/container-manager.js.map +1 -0
- package/dist/runner/runtime/container-runner.d.ts +62 -0
- package/dist/runner/runtime/container-runner.d.ts.map +1 -0
- package/dist/runner/runtime/container-runner.js +235 -0
- package/dist/runner/runtime/container-runner.js.map +1 -0
- package/dist/runner/runtime/docker-config.d.ts +100 -0
- package/dist/runner/runtime/docker-config.d.ts.map +1 -0
- package/dist/runner/runtime/docker-config.js +98 -0
- package/dist/runner/runtime/docker-config.js.map +1 -0
- package/dist/runner/runtime/factory.d.ts +63 -0
- package/dist/runner/runtime/factory.d.ts.map +1 -0
- package/dist/runner/runtime/factory.js +68 -0
- package/dist/runner/runtime/factory.js.map +1 -0
- package/dist/runner/runtime/index.d.ts +20 -0
- package/dist/runner/runtime/index.d.ts.map +1 -0
- package/dist/runner/runtime/index.js +21 -0
- package/dist/runner/runtime/index.js.map +1 -0
- package/dist/runner/runtime/interface.d.ts +59 -0
- package/dist/runner/runtime/interface.d.ts.map +1 -0
- package/dist/runner/runtime/interface.js +12 -0
- package/dist/runner/runtime/interface.js.map +1 -0
- package/dist/runner/runtime/sdk-runtime.d.ts +46 -0
- package/dist/runner/runtime/sdk-runtime.d.ts.map +1 -0
- package/dist/runner/runtime/sdk-runtime.js +63 -0
- package/dist/runner/runtime/sdk-runtime.js.map +1 -0
- package/dist/runner/sdk-adapter.d.ts +4 -0
- package/dist/runner/sdk-adapter.d.ts.map +1 -1
- package/dist/runner/sdk-adapter.js +35 -16
- package/dist/runner/sdk-adapter.js.map +1 -1
- package/dist/runner/types.d.ts +12 -6
- package/dist/runner/types.d.ts.map +1 -1
- package/dist/scheduler/__tests__/schedule-runner.test.js +61 -50
- package/dist/scheduler/__tests__/schedule-runner.test.js.map +1 -1
- package/dist/scheduler/schedule-runner.d.ts +1 -4
- package/dist/scheduler/schedule-runner.d.ts.map +1 -1
- package/dist/scheduler/schedule-runner.js +40 -8
- package/dist/scheduler/schedule-runner.js.map +1 -1
- package/dist/state/__tests__/session-schema.test.js +4 -0
- package/dist/state/__tests__/session-schema.test.js.map +1 -1
- package/dist/state/__tests__/session-validation.test.d.ts +2 -0
- package/dist/state/__tests__/session-validation.test.d.ts.map +1 -0
- package/dist/state/__tests__/session-validation.test.js +446 -0
- package/dist/state/__tests__/session-validation.test.js.map +1 -0
- package/dist/state/__tests__/session.test.js +68 -0
- package/dist/state/__tests__/session.test.js.map +1 -1
- package/dist/state/__tests__/working-directory-validation.test.d.ts +5 -0
- package/dist/state/__tests__/working-directory-validation.test.d.ts.map +1 -0
- package/dist/state/__tests__/working-directory-validation.test.js +101 -0
- package/dist/state/__tests__/working-directory-validation.test.js.map +1 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +4 -0
- package/dist/state/index.js.map +1 -1
- package/dist/state/schemas/session-info.d.ts +32 -0
- package/dist/state/schemas/session-info.d.ts.map +1 -1
- package/dist/state/schemas/session-info.js +22 -0
- package/dist/state/schemas/session-info.js.map +1 -1
- package/dist/state/session-validation.d.ts +202 -0
- package/dist/state/session-validation.d.ts.map +1 -0
- package/dist/state/session-validation.js +407 -0
- package/dist/state/session-validation.js.map +1 -0
- package/dist/state/session.d.ts +23 -3
- package/dist/state/session.d.ts.map +1 -1
- package/dist/state/session.js +41 -6
- package/dist/state/session.js.map +1 -1
- package/dist/state/working-directory-validation.d.ts +52 -0
- package/dist/state/working-directory-validation.d.ts.map +1 -0
- package/dist/state/working-directory-validation.js +81 -0
- package/dist/state/working-directory-validation.js.map +1 -0
- package/package.json +7 -2
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Runtime implementation
|
|
3
|
+
*
|
|
4
|
+
* Executes Claude agents via the Claude CLI instead of the SDK, enabling Max plan
|
|
5
|
+
* pricing for agent execution. This runtime spawns the `claude` CLI command and
|
|
6
|
+
* watches the session file for messages (since claude only outputs to TTY).
|
|
7
|
+
*
|
|
8
|
+
* Requirements:
|
|
9
|
+
* - Claude CLI must be installed (`brew install claude-ai/tap/claude`)
|
|
10
|
+
* - CLI must be authenticated (`claude login`)
|
|
11
|
+
* - Uses Max plan pricing when available
|
|
12
|
+
*
|
|
13
|
+
* The CLIRuntime provides identical streaming interface to SDKRuntime, allowing
|
|
14
|
+
* seamless runtime switching via agent configuration.
|
|
15
|
+
*/
|
|
16
|
+
import { execa } from "execa";
|
|
17
|
+
import { getCliSessionDir, getCliSessionFile, waitForNewSessionFile, } from "./cli-session-path.js";
|
|
18
|
+
import { CLISessionWatcher } from "./cli-session-watcher.js";
|
|
19
|
+
import { transformMcpServers } from "../sdk-adapter.js";
|
|
20
|
+
/**
|
|
21
|
+
* CLI runtime implementation
|
|
22
|
+
*
|
|
23
|
+
* This runtime uses the Claude CLI to execute agents, providing an alternative
|
|
24
|
+
* backend to the SDK runtime. It spawns `claude` CLI and watches the session file
|
|
25
|
+
* for new messages (since claude only outputs stream-json to TTY).
|
|
26
|
+
*
|
|
27
|
+
* The CLI runtime enables:
|
|
28
|
+
* - Max plan pricing (cost savings vs SDK/API pricing)
|
|
29
|
+
* - Full Claude Code capabilities (identical to manual CLI usage)
|
|
30
|
+
* - AbortController support for process cancellation
|
|
31
|
+
*
|
|
32
|
+
* Supports both local and Docker execution via configurable process spawning.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* // Local execution
|
|
37
|
+
* const runtime = new CLIRuntime();
|
|
38
|
+
*
|
|
39
|
+
* // Docker execution
|
|
40
|
+
* const runtime = new CLIRuntime({
|
|
41
|
+
* processSpawner: async (args, cwd, signal) => {
|
|
42
|
+
* return execa("docker", ["exec", containerId, "sh", "-c",
|
|
43
|
+
* `cd /workspace && claude ${args.join(" ")}`],
|
|
44
|
+
* { cancelSignal: signal });
|
|
45
|
+
* },
|
|
46
|
+
* sessionDirOverride: "/path/to/.herdctl/docker-sessions"
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export class CLIRuntime {
|
|
51
|
+
processSpawner;
|
|
52
|
+
sessionDirOverride;
|
|
53
|
+
constructor(options) {
|
|
54
|
+
// Default to local execa spawning with prompt via stdin
|
|
55
|
+
this.processSpawner = options?.processSpawner ?? ((args, cwd, prompt, signal) => execa("claude", args, {
|
|
56
|
+
cwd,
|
|
57
|
+
input: prompt, // Provide prompt via stdin (required for -p mode)
|
|
58
|
+
cancelSignal: signal
|
|
59
|
+
}));
|
|
60
|
+
this.sessionDirOverride = options?.sessionDirOverride;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Execute an agent using the Claude CLI
|
|
64
|
+
*
|
|
65
|
+
* Spawns `claude` CLI and watches the session file for messages. The session
|
|
66
|
+
* file approach is used because claude only outputs stream-json to TTY, not
|
|
67
|
+
* to pipes.
|
|
68
|
+
*
|
|
69
|
+
* Process flow:
|
|
70
|
+
* 1. Build CLI arguments from execution options
|
|
71
|
+
* 2. Spawn claude subprocess (output is ignored)
|
|
72
|
+
* 3. Find the CLI session directory for the workspace
|
|
73
|
+
* 4. Wait briefly for session file to be created
|
|
74
|
+
* 5. Find the newest .jsonl file (the one just created)
|
|
75
|
+
* 6. Watch that file and stream messages as they're appended
|
|
76
|
+
* 7. Handle process completion and exit codes
|
|
77
|
+
*
|
|
78
|
+
* @param options - Execution options including prompt, agent, and session info
|
|
79
|
+
* @returns AsyncIterable of SDK messages
|
|
80
|
+
*/
|
|
81
|
+
async *execute(options) {
|
|
82
|
+
// Build CLI arguments
|
|
83
|
+
// Note: -p is --print mode (print response and exit)
|
|
84
|
+
// Prompt is provided via stdin, not as a CLI argument
|
|
85
|
+
const args = ["-p"];
|
|
86
|
+
// Add permission mode from agent config (defaults to acceptEdits)
|
|
87
|
+
const permissionMode = options.agent.permission_mode ?? "acceptEdits";
|
|
88
|
+
args.push("--permission-mode", permissionMode);
|
|
89
|
+
// Add model if specified
|
|
90
|
+
if (options.agent.model) {
|
|
91
|
+
args.push("--model", options.agent.model);
|
|
92
|
+
}
|
|
93
|
+
// Add system prompt if specified
|
|
94
|
+
if (options.agent.system_prompt) {
|
|
95
|
+
args.push("--system-prompt", options.agent.system_prompt);
|
|
96
|
+
}
|
|
97
|
+
// Collect all allowed tools into a single array to avoid multiple --allowedTools flags
|
|
98
|
+
const allAllowedTools = [];
|
|
99
|
+
// Add allowed tools if specified
|
|
100
|
+
if (options.agent.permissions?.allowed_tools?.length) {
|
|
101
|
+
allAllowedTools.push(...options.agent.permissions.allowed_tools);
|
|
102
|
+
}
|
|
103
|
+
// Add bash allowed commands as Bash(command *) patterns
|
|
104
|
+
if (options.agent.permissions?.bash?.allowed_commands?.length) {
|
|
105
|
+
const bashPatterns = options.agent.permissions.bash.allowed_commands.map((cmd) => `Bash(${cmd} *)`);
|
|
106
|
+
allAllowedTools.push(...bashPatterns);
|
|
107
|
+
}
|
|
108
|
+
// Add all allowed tools as comma-separated string to prevent consuming subsequent args
|
|
109
|
+
// Note: --allowedTools accepts "comma or space-separated" but space-separated consumes
|
|
110
|
+
// all following args, so we must use comma-separated
|
|
111
|
+
if (allAllowedTools.length > 0) {
|
|
112
|
+
args.push("--allowedTools", allAllowedTools.join(","));
|
|
113
|
+
}
|
|
114
|
+
// Collect all denied tools into a single array
|
|
115
|
+
const allDeniedTools = [];
|
|
116
|
+
// Add denied tools if specified
|
|
117
|
+
if (options.agent.permissions?.denied_tools?.length) {
|
|
118
|
+
allDeniedTools.push(...options.agent.permissions.denied_tools);
|
|
119
|
+
}
|
|
120
|
+
// Add bash denied patterns as Bash(pattern) patterns
|
|
121
|
+
if (options.agent.permissions?.bash?.denied_patterns?.length) {
|
|
122
|
+
const bashDeniedPatterns = options.agent.permissions.bash.denied_patterns.map((pattern) => `Bash(${pattern})`);
|
|
123
|
+
allDeniedTools.push(...bashDeniedPatterns);
|
|
124
|
+
}
|
|
125
|
+
// Add all denied tools as comma-separated string
|
|
126
|
+
if (allDeniedTools.length > 0) {
|
|
127
|
+
args.push("--disallowedTools", allDeniedTools.join(","));
|
|
128
|
+
}
|
|
129
|
+
// Add setting sources if specified (comma-separated)
|
|
130
|
+
if (options.agent.setting_sources?.length) {
|
|
131
|
+
args.push("--setting-sources", options.agent.setting_sources.join(","));
|
|
132
|
+
}
|
|
133
|
+
// Add MCP servers if specified
|
|
134
|
+
// Transform agent config format to SDK format and serialize to JSON
|
|
135
|
+
if (options.agent.mcp_servers && Object.keys(options.agent.mcp_servers).length > 0) {
|
|
136
|
+
const mcpServers = transformMcpServers(options.agent.mcp_servers);
|
|
137
|
+
const mcpConfig = JSON.stringify(mcpServers);
|
|
138
|
+
args.push("--mcp-config", mcpConfig);
|
|
139
|
+
}
|
|
140
|
+
// Add session options
|
|
141
|
+
if (options.resume) {
|
|
142
|
+
args.push("--resume", options.resume);
|
|
143
|
+
}
|
|
144
|
+
if (options.fork) {
|
|
145
|
+
args.push("--fork-session");
|
|
146
|
+
}
|
|
147
|
+
// Note: Prompt is NOT added to args - it's provided via stdin (see processSpawner call below)
|
|
148
|
+
// DEBUG: Log the command being executed
|
|
149
|
+
console.log("[CLIRuntime] Executing command:", "claude", args);
|
|
150
|
+
console.log("[CLIRuntime] Prompt:", options.prompt);
|
|
151
|
+
// Track process and watcher for cleanup
|
|
152
|
+
let subprocess;
|
|
153
|
+
let watcher;
|
|
154
|
+
let hasError = false;
|
|
155
|
+
try {
|
|
156
|
+
// Determine working directory root for cwd
|
|
157
|
+
const working_directory = options.agent.working_directory;
|
|
158
|
+
const cwd = working_directory
|
|
159
|
+
? typeof working_directory === "string"
|
|
160
|
+
? working_directory
|
|
161
|
+
: working_directory.root
|
|
162
|
+
: process.cwd();
|
|
163
|
+
console.log("[CLIRuntime] Working directory:", cwd);
|
|
164
|
+
console.log("[CLIRuntime] Agent working_directory config:", working_directory);
|
|
165
|
+
// Get the CLI session directory where files will be written
|
|
166
|
+
// Use override if provided (for Docker execution with mounted sessions)
|
|
167
|
+
const sessionDir = this.sessionDirOverride ?? getCliSessionDir(cwd);
|
|
168
|
+
console.log("[CLIRuntime] Session directory:", sessionDir);
|
|
169
|
+
// Record start time before spawning process
|
|
170
|
+
const processStartTime = Date.now();
|
|
171
|
+
// Spawn claude subprocess with prompt via stdin
|
|
172
|
+
// Uses custom spawner if provided (e.g., for Docker execution)
|
|
173
|
+
// Note: processSpawner returns Subprocess directly (which is promise-like)
|
|
174
|
+
subprocess = this.processSpawner(args, cwd, options.prompt, options.abortController?.signal);
|
|
175
|
+
console.log("[CLIRuntime] Subprocess spawned, PID:", subprocess.pid);
|
|
176
|
+
// Log subprocess output for debugging
|
|
177
|
+
subprocess.stdout?.on("data", (data) => {
|
|
178
|
+
console.log("[CLIRuntime] stdout:", data.toString());
|
|
179
|
+
});
|
|
180
|
+
subprocess.stderr?.on("data", (data) => {
|
|
181
|
+
console.error("[CLIRuntime] stderr:", data.toString());
|
|
182
|
+
});
|
|
183
|
+
// Track subprocess completion for later
|
|
184
|
+
const processExitPromise = (async () => {
|
|
185
|
+
try {
|
|
186
|
+
return await subprocess;
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
console.error("[CLIRuntime] Process failed:", error);
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
})();
|
|
193
|
+
// Monitor subprocess completion in background (for logging only)
|
|
194
|
+
processExitPromise.then((result) => {
|
|
195
|
+
console.log("[CLIRuntime] Process completed with exit code:", result.exitCode);
|
|
196
|
+
}, () => {
|
|
197
|
+
// Error already logged above
|
|
198
|
+
});
|
|
199
|
+
// Determine which session file to watch
|
|
200
|
+
let sessionFilePath;
|
|
201
|
+
if (options.resume) {
|
|
202
|
+
// When resuming, use the known session ID
|
|
203
|
+
sessionFilePath = getCliSessionFile(cwd, options.resume);
|
|
204
|
+
console.log("[CLIRuntime] Resuming session, watching file:", sessionFilePath);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
// When starting new session, wait for a NEW file created after process start
|
|
208
|
+
console.log("[CLIRuntime] Waiting for new session file (timeout: 15s)...");
|
|
209
|
+
sessionFilePath = await waitForNewSessionFile(sessionDir, processStartTime, {
|
|
210
|
+
timeoutMs: 15000, // Increase timeout to 15 seconds for debugging
|
|
211
|
+
pollIntervalMs: 200,
|
|
212
|
+
});
|
|
213
|
+
console.log("[CLIRuntime] New session, watching newly created file:", sessionFilePath);
|
|
214
|
+
}
|
|
215
|
+
// Extract session ID from filename (basename without .jsonl extension)
|
|
216
|
+
// For CLI runtime, the session ID is the filename - this matches SDK runtime behavior
|
|
217
|
+
const sessionFileName = sessionFilePath.split("/").pop() || "";
|
|
218
|
+
const extractedSessionId = sessionFileName.replace(/\.jsonl$/, "");
|
|
219
|
+
console.log("[CLIRuntime] Extracted session ID:", extractedSessionId);
|
|
220
|
+
// Watch the session file for messages
|
|
221
|
+
watcher = new CLISessionWatcher(sessionFilePath);
|
|
222
|
+
// When resuming, initialize watcher to skip existing content
|
|
223
|
+
// This prevents replaying the entire conversation history on each message
|
|
224
|
+
if (options.resume) {
|
|
225
|
+
await watcher.initialize();
|
|
226
|
+
console.log("[CLIRuntime] Watcher initialized for resume, will skip existing content");
|
|
227
|
+
}
|
|
228
|
+
// Set up abort handling
|
|
229
|
+
if (options.abortController) {
|
|
230
|
+
options.abortController.signal.addEventListener("abort", () => {
|
|
231
|
+
subprocess?.kill();
|
|
232
|
+
watcher?.stop();
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
// Set up process completion handler - stop watcher when process exits
|
|
236
|
+
// This allows the for-await loop to exit naturally
|
|
237
|
+
processExitPromise.then(() => {
|
|
238
|
+
console.log("[CLIRuntime] Process completed, stopping watcher to exit loop");
|
|
239
|
+
watcher?.stop();
|
|
240
|
+
}, (error) => {
|
|
241
|
+
console.log("[CLIRuntime] Process failed, stopping watcher");
|
|
242
|
+
watcher?.stop();
|
|
243
|
+
});
|
|
244
|
+
// Stream messages from the session file
|
|
245
|
+
// Just iterate naturally - the watcher handles all the waiting
|
|
246
|
+
console.log("[CLIRuntime] Starting to stream messages from watcher");
|
|
247
|
+
// Yield synthetic system message with session ID (matches SDK runtime behavior)
|
|
248
|
+
// This allows the message processor to extract the session ID for persistence
|
|
249
|
+
yield {
|
|
250
|
+
type: "system",
|
|
251
|
+
subtype: "init",
|
|
252
|
+
session_id: extractedSessionId,
|
|
253
|
+
content: "CLI session initialized",
|
|
254
|
+
};
|
|
255
|
+
console.log("[CLIRuntime] Yielded synthetic system message with session ID");
|
|
256
|
+
// Stream messages from the watcher as they arrive
|
|
257
|
+
for await (const message of watcher.watch()) {
|
|
258
|
+
console.log(`[CLIRuntime] Received message type: ${message.type}`);
|
|
259
|
+
yield message;
|
|
260
|
+
// Track errors
|
|
261
|
+
if (message.type === "error") {
|
|
262
|
+
hasError = true;
|
|
263
|
+
}
|
|
264
|
+
// If this is a result message, we're done
|
|
265
|
+
if (message.type === "result") {
|
|
266
|
+
console.log("[CLIRuntime] Got result message, stopping");
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
console.log("[CLIRuntime] Watcher iteration complete");
|
|
271
|
+
// Wait for process to complete
|
|
272
|
+
const { exitCode } = await processExitPromise;
|
|
273
|
+
console.log("[CLIRuntime] Process completed, flushing any remaining messages");
|
|
274
|
+
// After process exits, explicitly flush the file one more time
|
|
275
|
+
// This catches any final messages that hadn't triggered chokidar events yet
|
|
276
|
+
const remainingMessages = await watcher.flushRemainingMessages();
|
|
277
|
+
console.log(`[CLIRuntime] Found ${remainingMessages.length} remaining message(s) after process exit`);
|
|
278
|
+
// Stop the watcher now - we've flushed everything we need
|
|
279
|
+
console.log("[CLIRuntime] Stopping watcher after flush");
|
|
280
|
+
watcher.stop();
|
|
281
|
+
// Yield any remaining messages
|
|
282
|
+
for (const message of remainingMessages) {
|
|
283
|
+
console.log(`[CLIRuntime] Yielding remaining message type: ${message.type}`);
|
|
284
|
+
yield message;
|
|
285
|
+
// Track errors
|
|
286
|
+
if (message.type === "error") {
|
|
287
|
+
hasError = true;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// If process failed and we didn't yield an error message, create one
|
|
291
|
+
if (exitCode !== 0 && !hasError) {
|
|
292
|
+
yield {
|
|
293
|
+
type: "error",
|
|
294
|
+
message: `Claude CLI exited with code ${exitCode}`,
|
|
295
|
+
code: `EXIT_${exitCode}`,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
// Handle process errors
|
|
301
|
+
if (error && typeof error === "object" && "code" in error) {
|
|
302
|
+
const execaError = error;
|
|
303
|
+
// CLI not found
|
|
304
|
+
if (execaError.code === "ENOENT") {
|
|
305
|
+
yield {
|
|
306
|
+
type: "error",
|
|
307
|
+
message: "Claude CLI not found. Install with: brew install claude-ai/tap/claude",
|
|
308
|
+
code: "CLI_NOT_FOUND",
|
|
309
|
+
};
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
// Process was killed (likely by AbortController)
|
|
313
|
+
if (execaError.code === "ABORT_ERR") {
|
|
314
|
+
yield {
|
|
315
|
+
type: "error",
|
|
316
|
+
message: "Claude CLI execution was cancelled",
|
|
317
|
+
code: "CANCELLED",
|
|
318
|
+
};
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// Generic error
|
|
323
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
324
|
+
yield {
|
|
325
|
+
type: "error",
|
|
326
|
+
message: `CLI execution failed: ${errorMessage}`,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
finally {
|
|
330
|
+
// Cleanup
|
|
331
|
+
watcher?.stop();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
//# sourceMappingURL=cli-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-runtime.js","sourceRoot":"","sources":["../../../src/runner/runtime/cli-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI session path utilities - locate Claude CLI session files
|
|
3
|
+
*
|
|
4
|
+
* The Claude CLI stores session files in ~/.claude/projects/ with workspace paths
|
|
5
|
+
* encoded by replacing slashes with hyphens. These utilities help locate CLI session
|
|
6
|
+
* directories and specific session files.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Encode a workspace path for CLI session storage
|
|
10
|
+
*
|
|
11
|
+
* The CLI encodes workspace paths by replacing all path separators with hyphens.
|
|
12
|
+
* Works on both Unix (/) and Windows (\) paths.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* encodePathForCli('/Users/ed/Code/myproject')
|
|
17
|
+
* // => '-Users-ed-Code-myproject'
|
|
18
|
+
*
|
|
19
|
+
* encodePathForCli('C:\\Users\\ed\\Code\\myproject')
|
|
20
|
+
* // => 'C:-Users-ed-Code-myproject'
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @param absolutePath - Absolute path to workspace directory
|
|
24
|
+
* @returns Encoded path with slashes replaced by hyphens
|
|
25
|
+
*/
|
|
26
|
+
export declare function encodePathForCli(absolutePath: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Get the CLI session directory for a workspace
|
|
29
|
+
*
|
|
30
|
+
* Returns the directory where Claude CLI stores sessions for the given workspace.
|
|
31
|
+
* Format: ~/.claude/projects/{encoded-workspace-path}/
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* getCliSessionDir('/Users/ed/Code/myproject')
|
|
36
|
+
* // => '/Users/ed/.claude/projects/-Users-ed-Code-myproject'
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @param workspacePath - Absolute path to workspace directory
|
|
40
|
+
* @returns Absolute path to CLI session storage directory
|
|
41
|
+
*/
|
|
42
|
+
export declare function getCliSessionDir(workspacePath: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Get the path to a specific CLI session file
|
|
45
|
+
*
|
|
46
|
+
* Returns the full path to a session's JSONL file in the CLI session directory.
|
|
47
|
+
* Format: ~/.claude/projects/{encoded-workspace-path}/{session-id}.jsonl
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* getCliSessionFile(
|
|
52
|
+
* '/Users/ed/Code/myproject',
|
|
53
|
+
* 'dda6da5b-8788-4990-a582-d5a2c63fbfba'
|
|
54
|
+
* )
|
|
55
|
+
* // => '/Users/ed/.claude/projects/-Users-ed-Code-myproject/dda6da5b-8788-4990-a582-d5a2c63fbfba.jsonl'
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @param workspacePath - Absolute path to workspace directory
|
|
59
|
+
* @param sessionId - CLI session ID (UUID format)
|
|
60
|
+
* @returns Absolute path to session JSONL file
|
|
61
|
+
*/
|
|
62
|
+
export declare function getCliSessionFile(workspacePath: string, sessionId: string): string;
|
|
63
|
+
/**
|
|
64
|
+
* Find the newest session file in a CLI session directory
|
|
65
|
+
*
|
|
66
|
+
* Scans the session directory for .jsonl files and returns the path to the
|
|
67
|
+
* most recently modified one. This is useful when spawning a new CLI session
|
|
68
|
+
* without knowing the session ID upfront - the newest file is typically the
|
|
69
|
+
* one just created.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const sessionDir = getCliSessionDir('/Users/ed/Code/myproject');
|
|
74
|
+
* const newestFile = await findNewestSessionFile(sessionDir);
|
|
75
|
+
* // => '/Users/ed/.claude/projects/-Users-ed-Code-myproject/abc123.jsonl'
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @param sessionDir - Absolute path to CLI session directory
|
|
79
|
+
* @returns Promise resolving to path of newest .jsonl file
|
|
80
|
+
* @throws {Error} If directory doesn't exist or contains no .jsonl files
|
|
81
|
+
*/
|
|
82
|
+
export declare function findNewestSessionFile(sessionDir: string): Promise<string>;
|
|
83
|
+
/**
|
|
84
|
+
* Wait for a new session file to be created after a given timestamp
|
|
85
|
+
*
|
|
86
|
+
* Polls the session directory until a new .jsonl file appears that was
|
|
87
|
+
* created after the specified start time. This prevents picking up old
|
|
88
|
+
* session files when spawning a new CLI session.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* const startTime = Date.now();
|
|
93
|
+
* // ... spawn claude CLI ...
|
|
94
|
+
* const sessionFile = await waitForNewSessionFile(sessionDir, startTime);
|
|
95
|
+
* // => '/Users/ed/.claude/projects/-Users-ed-Code-myproject/new-session.jsonl'
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @param sessionDir - Absolute path to CLI session directory
|
|
99
|
+
* @param startTime - Timestamp (ms) before which files should be ignored
|
|
100
|
+
* @param options - Optional configuration
|
|
101
|
+
* @returns Promise resolving to path of newly created session file
|
|
102
|
+
* @throws {Error} If timeout exceeded or directory doesn't exist
|
|
103
|
+
*/
|
|
104
|
+
export declare function waitForNewSessionFile(sessionDir: string, startTime: number, options?: {
|
|
105
|
+
timeoutMs?: number;
|
|
106
|
+
pollIntervalMs?: number;
|
|
107
|
+
}): Promise<string>;
|
|
108
|
+
//# sourceMappingURL=cli-session-path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-session-path.d.ts","sourceRoot":"","sources":["../../../src/runner/runtime/cli-session-path.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,GAChB,MAAM,CAGR;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5D,OAAO,CAAC,MAAM,CAAC,CAwCjB"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI session path utilities - locate Claude CLI session files
|
|
3
|
+
*
|
|
4
|
+
* The Claude CLI stores session files in ~/.claude/projects/ with workspace paths
|
|
5
|
+
* encoded by replacing slashes with hyphens. These utilities help locate CLI session
|
|
6
|
+
* directories and specific session files.
|
|
7
|
+
*/
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import * as os from "node:os";
|
|
10
|
+
import { readdir, stat } from "node:fs/promises";
|
|
11
|
+
/**
|
|
12
|
+
* Encode a workspace path for CLI session storage
|
|
13
|
+
*
|
|
14
|
+
* The CLI encodes workspace paths by replacing all path separators with hyphens.
|
|
15
|
+
* Works on both Unix (/) and Windows (\) paths.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* encodePathForCli('/Users/ed/Code/myproject')
|
|
20
|
+
* // => '-Users-ed-Code-myproject'
|
|
21
|
+
*
|
|
22
|
+
* encodePathForCli('C:\\Users\\ed\\Code\\myproject')
|
|
23
|
+
* // => 'C:-Users-ed-Code-myproject'
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @param absolutePath - Absolute path to workspace directory
|
|
27
|
+
* @returns Encoded path with slashes replaced by hyphens
|
|
28
|
+
*/
|
|
29
|
+
export function encodePathForCli(absolutePath) {
|
|
30
|
+
// Replace both forward slashes (Unix) and backslashes (Windows)
|
|
31
|
+
return absolutePath.replace(/[/\\]/g, "-");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get the CLI session directory for a workspace
|
|
35
|
+
*
|
|
36
|
+
* Returns the directory where Claude CLI stores sessions for the given workspace.
|
|
37
|
+
* Format: ~/.claude/projects/{encoded-workspace-path}/
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* getCliSessionDir('/Users/ed/Code/myproject')
|
|
42
|
+
* // => '/Users/ed/.claude/projects/-Users-ed-Code-myproject'
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @param workspacePath - Absolute path to workspace directory
|
|
46
|
+
* @returns Absolute path to CLI session storage directory
|
|
47
|
+
*/
|
|
48
|
+
export function getCliSessionDir(workspacePath) {
|
|
49
|
+
const encoded = encodePathForCli(workspacePath);
|
|
50
|
+
return path.join(os.homedir(), ".claude", "projects", encoded);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get the path to a specific CLI session file
|
|
54
|
+
*
|
|
55
|
+
* Returns the full path to a session's JSONL file in the CLI session directory.
|
|
56
|
+
* Format: ~/.claude/projects/{encoded-workspace-path}/{session-id}.jsonl
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* getCliSessionFile(
|
|
61
|
+
* '/Users/ed/Code/myproject',
|
|
62
|
+
* 'dda6da5b-8788-4990-a582-d5a2c63fbfba'
|
|
63
|
+
* )
|
|
64
|
+
* // => '/Users/ed/.claude/projects/-Users-ed-Code-myproject/dda6da5b-8788-4990-a582-d5a2c63fbfba.jsonl'
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* @param workspacePath - Absolute path to workspace directory
|
|
68
|
+
* @param sessionId - CLI session ID (UUID format)
|
|
69
|
+
* @returns Absolute path to session JSONL file
|
|
70
|
+
*/
|
|
71
|
+
export function getCliSessionFile(workspacePath, sessionId) {
|
|
72
|
+
const sessionDir = getCliSessionDir(workspacePath);
|
|
73
|
+
return path.join(sessionDir, `${sessionId}.jsonl`);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Find the newest session file in a CLI session directory
|
|
77
|
+
*
|
|
78
|
+
* Scans the session directory for .jsonl files and returns the path to the
|
|
79
|
+
* most recently modified one. This is useful when spawning a new CLI session
|
|
80
|
+
* without knowing the session ID upfront - the newest file is typically the
|
|
81
|
+
* one just created.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const sessionDir = getCliSessionDir('/Users/ed/Code/myproject');
|
|
86
|
+
* const newestFile = await findNewestSessionFile(sessionDir);
|
|
87
|
+
* // => '/Users/ed/.claude/projects/-Users-ed-Code-myproject/abc123.jsonl'
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @param sessionDir - Absolute path to CLI session directory
|
|
91
|
+
* @returns Promise resolving to path of newest .jsonl file
|
|
92
|
+
* @throws {Error} If directory doesn't exist or contains no .jsonl files
|
|
93
|
+
*/
|
|
94
|
+
export async function findNewestSessionFile(sessionDir) {
|
|
95
|
+
try {
|
|
96
|
+
const files = await readdir(sessionDir);
|
|
97
|
+
const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
98
|
+
if (jsonlFiles.length === 0) {
|
|
99
|
+
throw new Error(`No session files found in ${sessionDir}`);
|
|
100
|
+
}
|
|
101
|
+
// Get stats for all .jsonl files
|
|
102
|
+
const fileStats = await Promise.all(jsonlFiles.map(async (file) => {
|
|
103
|
+
const filePath = path.join(sessionDir, file);
|
|
104
|
+
const stats = await stat(filePath);
|
|
105
|
+
return { path: filePath, mtime: stats.mtime };
|
|
106
|
+
}));
|
|
107
|
+
// Sort by modification time (newest first)
|
|
108
|
+
fileStats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
109
|
+
return fileStats[0].path;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (error.code === "ENOENT") {
|
|
113
|
+
throw new Error(`Session directory does not exist: ${sessionDir}`);
|
|
114
|
+
}
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Wait for a new session file to be created after a given timestamp
|
|
120
|
+
*
|
|
121
|
+
* Polls the session directory until a new .jsonl file appears that was
|
|
122
|
+
* created after the specified start time. This prevents picking up old
|
|
123
|
+
* session files when spawning a new CLI session.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* const startTime = Date.now();
|
|
128
|
+
* // ... spawn claude CLI ...
|
|
129
|
+
* const sessionFile = await waitForNewSessionFile(sessionDir, startTime);
|
|
130
|
+
* // => '/Users/ed/.claude/projects/-Users-ed-Code-myproject/new-session.jsonl'
|
|
131
|
+
* ```
|
|
132
|
+
*
|
|
133
|
+
* @param sessionDir - Absolute path to CLI session directory
|
|
134
|
+
* @param startTime - Timestamp (ms) before which files should be ignored
|
|
135
|
+
* @param options - Optional configuration
|
|
136
|
+
* @returns Promise resolving to path of newly created session file
|
|
137
|
+
* @throws {Error} If timeout exceeded or directory doesn't exist
|
|
138
|
+
*/
|
|
139
|
+
export async function waitForNewSessionFile(sessionDir, startTime, options = {}) {
|
|
140
|
+
const { timeoutMs = 5000, pollIntervalMs = 100 } = options;
|
|
141
|
+
const deadline = Date.now() + timeoutMs;
|
|
142
|
+
while (Date.now() < deadline) {
|
|
143
|
+
try {
|
|
144
|
+
const files = await readdir(sessionDir);
|
|
145
|
+
const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
146
|
+
// Find files created after startTime
|
|
147
|
+
const newFiles = [];
|
|
148
|
+
for (const file of jsonlFiles) {
|
|
149
|
+
const filePath = path.join(sessionDir, file);
|
|
150
|
+
const stats = await stat(filePath);
|
|
151
|
+
// Check if file was modified after startTime
|
|
152
|
+
if (stats.mtime.getTime() > startTime) {
|
|
153
|
+
newFiles.push({ path: filePath, mtime: stats.mtime });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Return the newest file created after startTime
|
|
157
|
+
if (newFiles.length > 0) {
|
|
158
|
+
newFiles.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
159
|
+
return newFiles[0].path;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
// Directory might not exist yet - keep polling
|
|
164
|
+
if (error.code !== "ENOENT") {
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Wait before next poll
|
|
169
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
170
|
+
}
|
|
171
|
+
throw new Error(`Timeout waiting for new session file in ${sessionDir} (waited ${timeoutMs}ms)`);
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=cli-session-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-session-path.js","sourceRoot":"","sources":["../../../src/runner/runtime/cli-session-path.ts"],"names":[],"mappings":"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"}
|