@loucompanion/forge-bridge 0.1.3-dev.b6be5e7f9668 → 0.1.3-dev.bca7d0ccee53
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 +78 -0
- package/dist/bridge/bridge.base.d.ts +6 -1
- package/dist/bridge/bridge.service.d.ts +60 -3
- package/dist/bridge/bridge.service.js +970 -24
- package/dist/bridge/bridge.types.d.ts +172 -1
- package/dist/cleanup/cleanup.service.d.ts +13 -2
- package/dist/cleanup/cleanup.service.js +435 -3
- package/dist/cleanup/cleanup.types.d.ts +10 -0
- package/dist/cleanup/index.d.ts +1 -1
- package/dist/cleanup/index.js +1 -1
- package/dist/cleanup/internals.d.ts +2 -2
- package/dist/cleanup/internals.js +1 -1
- package/dist/cli/cli.help.d.ts +2 -0
- package/dist/cli/cli.help.js +217 -0
- package/dist/cli/cli.service.d.ts +42 -2
- package/dist/cli/cli.service.js +357 -206
- package/dist/cli/local-config.d.ts +6 -0
- package/dist/cli/local-config.js +47 -3
- package/dist/runtime/attempt-node-runtime-launcher.d.ts +17 -0
- package/dist/runtime/attempt-node-runtime-launcher.js +101 -0
- package/dist/runtime/attempt-process-supervisor-registry.d.ts +20 -0
- package/dist/runtime/attempt-process-supervisor-registry.js +64 -0
- package/dist/runtime/attempt-process-supervisor.d.ts +86 -0
- package/dist/runtime/attempt-process-supervisor.js +350 -0
- package/dist/runtime/generated-output-reset.d.ts +10 -0
- package/dist/runtime/generated-output-reset.js +103 -0
- package/dist/runtime/host-listener-inspector.d.ts +44 -0
- package/dist/runtime/host-listener-inspector.js +324 -0
- package/dist/runtime/host-port-broker.d.ts +76 -0
- package/dist/runtime/host-port-broker.js +333 -0
- package/dist/runtime/host-runtime-contract.d.ts +47 -0
- package/dist/runtime/host-runtime-contract.js +700 -0
- package/dist/runtime/host-runtime-lease.d.ts +133 -0
- package/dist/runtime/host-runtime-lease.js +1462 -0
- package/dist/runtime/host-runtime-promotion.d.ts +23 -0
- package/dist/runtime/host-runtime-promotion.js +159 -0
- package/dist/runtime/node-host-runtime-adapter.d.ts +51 -0
- package/dist/runtime/node-host-runtime-adapter.js +268 -0
- package/dist/runtime/playwright-host-runtime-adapter.d.ts +20 -0
- package/dist/runtime/playwright-host-runtime-adapter.js +220 -0
- package/dist/runtime/runtime-isolation-lease.d.ts +67 -0
- package/dist/runtime/runtime-isolation-lease.js +158 -0
- package/dist/sandbox/command-runner.d.ts +4 -0
- package/dist/sandbox/command-runner.js +47 -0
- package/dist/sandbox/contract.d.ts +5 -0
- package/dist/sandbox/contract.js +276 -0
- package/dist/sandbox/event-budget.d.ts +11 -0
- package/dist/sandbox/event-budget.js +27 -0
- package/dist/sandbox/image-policy.d.ts +35 -0
- package/dist/sandbox/image-policy.js +247 -0
- package/dist/sandbox/index.d.ts +5 -0
- package/dist/sandbox/index.js +5 -0
- package/dist/sandbox/lease-watchdog.d.ts +18 -0
- package/dist/sandbox/lease-watchdog.js +67 -0
- package/dist/sandbox/preflight.d.ts +54 -0
- package/dist/sandbox/preflight.js +335 -0
- package/dist/sandbox/process.d.ts +20 -0
- package/dist/sandbox/process.js +209 -0
- package/dist/sandbox/provider-auth/codex.d.ts +26 -0
- package/dist/sandbox/provider-auth/codex.js +212 -0
- package/dist/sandbox/repository-lock.d.ts +5 -0
- package/dist/sandbox/repository-lock.js +119 -0
- package/dist/sandbox/runtime.d.ts +297 -0
- package/dist/sandbox/runtime.js +2338 -0
- package/dist/sandbox/sandbox.types.d.ts +86 -0
- package/dist/sandbox/sandbox.types.js +1 -0
- package/dist/sandbox/smoke.d.ts +76 -0
- package/dist/sandbox/smoke.js +380 -0
- package/dist/sandbox/worker-supervisor.d.ts +13 -0
- package/dist/sandbox/worker-supervisor.js +36 -0
- package/dist/sandbox/worker.d.ts +1 -0
- package/dist/sandbox/worker.js +225 -0
- package/dist/sandbox/workspace.service.d.ts +13 -0
- package/dist/sandbox/workspace.service.js +72 -0
- package/dist/session/managed-attempt-tools.d.ts +17 -0
- package/dist/session/managed-attempt-tools.js +712 -0
- package/dist/session/phase-completion.artifacts.d.ts +10 -0
- package/dist/session/phase-completion.artifacts.js +627 -0
- package/dist/session/phase-completion.d.ts +43 -46
- package/dist/session/phase-completion.helpers.d.ts +117 -0
- package/dist/session/phase-completion.helpers.js +701 -0
- package/dist/session/phase-completion.js +40 -3488
- package/dist/session/phase-completion.prompts.d.ts +14 -0
- package/dist/session/phase-completion.prompts.js +235 -0
- package/dist/session/phase-completion.runtime.d.ts +48 -0
- package/dist/session/phase-completion.runtime.js +786 -0
- package/dist/session/phase-completion.schemas.d.ts +3 -0
- package/dist/session/phase-completion.schemas.js +812 -0
- package/dist/session/phase-completion.validation.d.ts +37 -0
- package/dist/session/phase-completion.validation.js +850 -0
- package/dist/session/provider-cli/ci-repair-readiness.d.ts +8 -0
- package/dist/session/provider-cli/ci-repair-readiness.js +95 -0
- package/dist/session/provider-cli/provider-cli.artifacts.d.ts +93 -0
- package/dist/session/provider-cli/provider-cli.artifacts.js +778 -0
- package/dist/session/provider-cli/provider-cli.base.d.ts +1 -0
- package/dist/session/provider-cli/provider-cli.deterministic-payloads.d.ts +12 -0
- package/dist/session/provider-cli/provider-cli.deterministic-payloads.js +279 -0
- package/dist/session/provider-cli/provider-cli.deterministic.d.ts +14 -0
- package/dist/session/provider-cli/provider-cli.deterministic.js +375 -0
- package/dist/session/provider-cli/provider-cli.process.d.ts +48 -0
- package/dist/session/provider-cli/provider-cli.process.js +474 -0
- package/dist/session/provider-cli/provider-cli.prompts.d.ts +47 -0
- package/dist/session/provider-cli/provider-cli.prompts.js +595 -0
- package/dist/session/provider-cli/provider-cli.qa-sidecars.d.ts +11 -0
- package/dist/session/provider-cli/provider-cli.qa-sidecars.js +66 -0
- package/dist/session/provider-cli/provider-cli.runtime.d.ts +9 -0
- package/dist/session/provider-cli/provider-cli.runtime.js +41 -0
- package/dist/session/provider-cli/provider-cli.service.d.ts +26 -53
- package/dist/session/provider-cli/provider-cli.service.js +524 -2571
- package/dist/session/provider-cli/provider-cli.types.d.ts +6 -0
- package/dist/session/provider-cli/provider-output-state.d.ts +22 -0
- package/dist/session/provider-cli/provider-output-state.js +473 -0
- package/dist/session/session-redaction.d.ts +3 -0
- package/dist/session/session-redaction.js +23 -0
- package/dist/session/session.base.d.ts +6 -1
- package/dist/session/session.service.d.ts +8 -2
- package/dist/session/session.service.js +480 -58
- package/dist/session/session.types.d.ts +161 -1
- package/dist/session/session.types.js +318 -3
- package/dist/shared/path/path.js +3 -1
- package/dist/shared/process/host-process-recovery.d.ts +12 -0
- package/dist/shared/process/host-process-recovery.js +78 -0
- package/dist/shared/process/host-process-supervisor.d.ts +57 -0
- package/dist/shared/process/host-process-supervisor.js +282 -0
- package/dist/shared/process/process.d.ts +1 -0
- package/dist/shared/process/process.js +1 -0
- package/dist/shared/process/windows-launch.d.ts +8 -0
- package/dist/shared/process/windows-launch.js +41 -0
- package/dist/shared/provider-capabilities.d.ts +1 -0
- package/dist/shared/provider-capabilities.js +3 -0
- package/dist/shared/redaction/redaction.js +10 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worktree/artifact-injector.d.ts +3 -2
- package/dist/worktree/artifact-injector.js +82 -10
- package/dist/worktree/dependency-warmer.d.ts +21 -1
- package/dist/worktree/dependency-warmer.js +138 -5
- package/dist/worktree/index.d.ts +1 -0
- package/dist/worktree/index.js +1 -0
- package/dist/worktree/prior-phase-artifact-hydrator.d.ts +2 -0
- package/dist/worktree/prior-phase-artifact-hydrator.js +136 -0
- package/dist/worktree/worktree-paths.d.ts +4 -0
- package/dist/worktree/worktree-paths.js +30 -0
- package/dist/worktree/worktree.base.d.ts +3 -0
- package/dist/worktree/worktree.service.d.ts +9 -3
- package/dist/worktree/worktree.service.js +100 -52
- package/dist/worktree/worktree.types.d.ts +3 -1
- package/package.json +6 -3
- package/runtime/windows-job-supervisor.ps1 +356 -0
- package/runtime/windows-listener-owner.ps1 +33 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import { execFile, spawn } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import readline from "node:readline";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
import { resolveCursorAgentDirectLaunch } from "../../shared/cursor-agent/cursor-agent.launch.js";
|
|
8
|
+
import { persistHostProcessSupervisorMetadata, resolveHostProcessSupervisedLaunch, terminateHostProcessTree, } from "../../shared/process/host-process-supervisor.js";
|
|
9
|
+
import { buildWindowsLaunchCommand } from "../../shared/process/windows-launch.js";
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
|
+
const allowedProviderParentEnvNames = [
|
|
12
|
+
"PATH",
|
|
13
|
+
"HOME",
|
|
14
|
+
"USERPROFILE",
|
|
15
|
+
"TMPDIR",
|
|
16
|
+
"TEMP",
|
|
17
|
+
"TMP",
|
|
18
|
+
"LANG"
|
|
19
|
+
];
|
|
20
|
+
const windowsAllowedProviderParentEnvNames = [
|
|
21
|
+
"SystemRoot",
|
|
22
|
+
"SystemDrive",
|
|
23
|
+
"ComSpec",
|
|
24
|
+
"PATHEXT",
|
|
25
|
+
"APPDATA",
|
|
26
|
+
"LOCALAPPDATA",
|
|
27
|
+
"USERNAME",
|
|
28
|
+
"USERDOMAIN",
|
|
29
|
+
"HOMEDRIVE",
|
|
30
|
+
"HOMEPATH",
|
|
31
|
+
"ProgramData",
|
|
32
|
+
"ProgramFiles",
|
|
33
|
+
"ProgramFiles(x86)",
|
|
34
|
+
"ProgramW6432",
|
|
35
|
+
"DOTNET_ROOT",
|
|
36
|
+
"DOTNET_ROOT(x86)",
|
|
37
|
+
"DOTNET_CLI_HOME",
|
|
38
|
+
"NUGET_PACKAGES",
|
|
39
|
+
"NUGET_HTTP_CACHE_PATH"
|
|
40
|
+
];
|
|
41
|
+
export function buildProviderChildEnvironment(requestEnv, options = {}) {
|
|
42
|
+
const parentEnv = options.parentEnv ?? process.env;
|
|
43
|
+
const platform = options.platform ?? process.platform;
|
|
44
|
+
const env = {};
|
|
45
|
+
for (const [key, value] of Object.entries(parentEnv)) {
|
|
46
|
+
if (typeof value === "string" && isAllowedProviderParentEnv(key, platform)) {
|
|
47
|
+
env[key] = value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (requestEnv) {
|
|
51
|
+
for (const [key, value] of Object.entries(requestEnv)) {
|
|
52
|
+
const trimmedKey = key.trim();
|
|
53
|
+
if (trimmedKey.length > 0) {
|
|
54
|
+
env[trimmedKey] = value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (platform === "win32") {
|
|
59
|
+
normalizeWindowsDotNetEnvironment(env, parentEnv, options.homeDir ?? os.homedir());
|
|
60
|
+
}
|
|
61
|
+
return env;
|
|
62
|
+
}
|
|
63
|
+
function isAllowedProviderParentEnv(key, platform = process.platform) {
|
|
64
|
+
return isAllowedProviderParentEnvName(key, allowedProviderParentEnvNames)
|
|
65
|
+
|| key.toUpperCase().startsWith("LC_")
|
|
66
|
+
|| (platform === "win32" && isAllowedProviderParentEnvName(key, windowsAllowedProviderParentEnvNames));
|
|
67
|
+
}
|
|
68
|
+
function isAllowedProviderParentEnvName(key, allowedNames) {
|
|
69
|
+
return allowedNames.some((allowed) => allowed.toLowerCase() === key.toLowerCase());
|
|
70
|
+
}
|
|
71
|
+
function normalizeWindowsDotNetEnvironment(env, parentEnv, homeDir) {
|
|
72
|
+
const systemRoot = readEnvironmentValue(env, "SystemRoot") ?? readEnvironmentValue(parentEnv, "SystemRoot") ?? "C:\\Windows";
|
|
73
|
+
const systemDrive = path.win32.parse(systemRoot).root.replace(/\\$/, "") || "C:";
|
|
74
|
+
setEnvironmentValueIfMissing(env, "SystemRoot", systemRoot);
|
|
75
|
+
setEnvironmentValueIfMissing(env, "SystemDrive", readEnvironmentValue(parentEnv, "SystemDrive") ?? systemDrive);
|
|
76
|
+
setEnvironmentValueIfMissing(env, "ComSpec", readEnvironmentValue(parentEnv, "ComSpec") ?? path.win32.join(systemRoot, "System32", "cmd.exe"));
|
|
77
|
+
setEnvironmentValueIfMissing(env, "PATHEXT", readEnvironmentValue(parentEnv, "PATHEXT") ?? ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL");
|
|
78
|
+
const userProfile = resolveWindowsUserProfile(env, parentEnv, homeDir);
|
|
79
|
+
if (userProfile) {
|
|
80
|
+
setEnvironmentValueIfMissing(env, "USERPROFILE", userProfile);
|
|
81
|
+
setEnvironmentValueIfMissing(env, "HOME", userProfile);
|
|
82
|
+
setEnvironmentValueIfMissing(env, "DOTNET_CLI_HOME", userProfile);
|
|
83
|
+
setEnvironmentValueIfMissing(env, "NUGET_PACKAGES", path.win32.join(userProfile, ".nuget", "packages"));
|
|
84
|
+
const parsed = path.win32.parse(userProfile);
|
|
85
|
+
if (parsed.root) {
|
|
86
|
+
setEnvironmentValueIfMissing(env, "HOMEDRIVE", parsed.root.replace(/\\$/, ""));
|
|
87
|
+
setEnvironmentValueIfMissing(env, "HOMEPATH", userProfile.slice(parsed.root.length - (parsed.root.endsWith("\\") ? 1 : 0)) || "\\");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const appData = readEnvironmentValue(env, "APPDATA")
|
|
91
|
+
?? readEnvironmentValue(parentEnv, "APPDATA")
|
|
92
|
+
?? (userProfile ? path.win32.join(userProfile, "AppData", "Roaming") : undefined);
|
|
93
|
+
const localAppData = readEnvironmentValue(env, "LOCALAPPDATA")
|
|
94
|
+
?? readEnvironmentValue(parentEnv, "LOCALAPPDATA")
|
|
95
|
+
?? (userProfile ? path.win32.join(userProfile, "AppData", "Local") : undefined);
|
|
96
|
+
if (appData) {
|
|
97
|
+
setEnvironmentValueIfMissing(env, "APPDATA", appData);
|
|
98
|
+
}
|
|
99
|
+
if (localAppData) {
|
|
100
|
+
setEnvironmentValueIfMissing(env, "LOCALAPPDATA", localAppData);
|
|
101
|
+
setEnvironmentValueIfMissing(env, "NUGET_HTTP_CACHE_PATH", path.win32.join(localAppData, "NuGet", "v3-cache"));
|
|
102
|
+
}
|
|
103
|
+
setEnvironmentValueIfMissing(env, "DOTNET_NOLOGO", "1");
|
|
104
|
+
setEnvironmentValueIfMissing(env, "DOTNET_CLI_TELEMETRY_OPTOUT", "1");
|
|
105
|
+
setEnvironmentValueIfMissing(env, "DOTNET_SKIP_FIRST_TIME_EXPERIENCE", "1");
|
|
106
|
+
setEnvironmentValueIfMissing(env, "NUGET_XMLDOC_MODE", "skip");
|
|
107
|
+
}
|
|
108
|
+
function resolveWindowsUserProfile(env, parentEnv, homeDir) {
|
|
109
|
+
const existing = readEnvironmentValue(env, "USERPROFILE") ?? readEnvironmentValue(parentEnv, "USERPROFILE");
|
|
110
|
+
if (existing) {
|
|
111
|
+
return existing;
|
|
112
|
+
}
|
|
113
|
+
const home = readEnvironmentValue(env, "HOME") ?? readEnvironmentValue(parentEnv, "HOME");
|
|
114
|
+
if (home && path.win32.isAbsolute(home)) {
|
|
115
|
+
return home;
|
|
116
|
+
}
|
|
117
|
+
const homeDrive = readEnvironmentValue(env, "HOMEDRIVE") ?? readEnvironmentValue(parentEnv, "HOMEDRIVE");
|
|
118
|
+
const homePath = readEnvironmentValue(env, "HOMEPATH") ?? readEnvironmentValue(parentEnv, "HOMEPATH");
|
|
119
|
+
if (homeDrive && homePath) {
|
|
120
|
+
return `${homeDrive}${homePath}`;
|
|
121
|
+
}
|
|
122
|
+
if (homeDir && path.win32.isAbsolute(homeDir)) {
|
|
123
|
+
return homeDir;
|
|
124
|
+
}
|
|
125
|
+
const systemDrive = readEnvironmentValue(env, "SystemDrive") ?? readEnvironmentValue(parentEnv, "SystemDrive") ?? "C:";
|
|
126
|
+
const username = readEnvironmentValue(env, "USERNAME") ?? readEnvironmentValue(parentEnv, "USERNAME");
|
|
127
|
+
return username ? path.win32.join(systemDrive, "Users", username) : undefined;
|
|
128
|
+
}
|
|
129
|
+
function readEnvironmentValue(env, name) {
|
|
130
|
+
const entry = Object.entries(env).find(([key]) => key.toLowerCase() === name.toLowerCase());
|
|
131
|
+
const value = entry?.[1];
|
|
132
|
+
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
|
133
|
+
}
|
|
134
|
+
function setEnvironmentValueIfMissing(env, name, value) {
|
|
135
|
+
if (readEnvironmentValue(env, name)) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
env[name] = value;
|
|
139
|
+
}
|
|
140
|
+
export class StreamingProviderProcessRunner {
|
|
141
|
+
async run(request, handlers = {}, signal) {
|
|
142
|
+
const providerLaunch = await resolveProviderProcessLaunch(request.command, request.args);
|
|
143
|
+
const supervisedLaunch = resolveHostProcessSupervisedLaunch(request, providerLaunch.command, providerLaunch.args);
|
|
144
|
+
const launch = supervisedLaunch ?? { ...providerLaunch, detached: false };
|
|
145
|
+
return await new Promise((resolve, reject) => {
|
|
146
|
+
const rootCreatedAt = new Date().toISOString();
|
|
147
|
+
const child = spawn(launch.command, launch.args, {
|
|
148
|
+
cwd: request.cwd,
|
|
149
|
+
env: buildProviderChildEnvironment(request.env),
|
|
150
|
+
stdio: [request.stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
|
|
151
|
+
windowsHide: true,
|
|
152
|
+
detached: launch.detached,
|
|
153
|
+
});
|
|
154
|
+
let termination = null;
|
|
155
|
+
const terminate = () => {
|
|
156
|
+
termination ??= terminateHostProcessTree(child, supervisedLaunch);
|
|
157
|
+
return termination;
|
|
158
|
+
};
|
|
159
|
+
const childStdout = child.stdout;
|
|
160
|
+
const childStderr = child.stderr;
|
|
161
|
+
if (!childStdout || !childStderr) {
|
|
162
|
+
void terminate();
|
|
163
|
+
reject(new Error("Provider process stdout/stderr streams were not piped."));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
let stdout = "";
|
|
167
|
+
let stderr = "";
|
|
168
|
+
let settled = false;
|
|
169
|
+
let closed = false;
|
|
170
|
+
let readiness = null;
|
|
171
|
+
const cleanup = () => {
|
|
172
|
+
signal?.removeEventListener("abort", abort);
|
|
173
|
+
};
|
|
174
|
+
const fail = (error) => {
|
|
175
|
+
if (settled) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
settled = true;
|
|
179
|
+
cleanup();
|
|
180
|
+
void terminate().catch(() => undefined);
|
|
181
|
+
reject(error);
|
|
182
|
+
};
|
|
183
|
+
const abort = () => {
|
|
184
|
+
// Cancellation is not a launch failure when the supervised tree can be
|
|
185
|
+
// proven stopped. Let the close handler settle the command after exact
|
|
186
|
+
// termination; only a failed termination proof rejects the run.
|
|
187
|
+
void terminate().catch(fail);
|
|
188
|
+
};
|
|
189
|
+
if (signal?.aborted) {
|
|
190
|
+
abort();
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
194
|
+
}
|
|
195
|
+
child.on("error", fail);
|
|
196
|
+
child.once("spawn", () => {
|
|
197
|
+
readiness = (async () => {
|
|
198
|
+
const providerReady = supervisedLaunch?.strategy === "windows_job_object"
|
|
199
|
+
? await waitForWindowsProviderReady(supervisedLaunch, signal, () => closed)
|
|
200
|
+
: undefined;
|
|
201
|
+
if (supervisedLaunch) {
|
|
202
|
+
persistHostProcessSupervisorMetadata(supervisedLaunch, request, child.pid ?? 0, rootCreatedAt, providerReady);
|
|
203
|
+
}
|
|
204
|
+
await handlers.onStarted?.();
|
|
205
|
+
if (supervisedLaunch?.strategy === "windows_job_object") {
|
|
206
|
+
grantWindowsProviderStart(supervisedLaunch);
|
|
207
|
+
}
|
|
208
|
+
})();
|
|
209
|
+
void readiness.catch(error => {
|
|
210
|
+
if (!closed && !signal?.aborted)
|
|
211
|
+
fail(error);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
const writeProviderInput = () => {
|
|
215
|
+
if (request.stdin === undefined)
|
|
216
|
+
return;
|
|
217
|
+
if (!child.stdin) {
|
|
218
|
+
void terminate();
|
|
219
|
+
fail(new Error("Provider process stdin stream was not piped."));
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
child.stdin.on("error", (error) => {
|
|
223
|
+
void terminate();
|
|
224
|
+
fail(error);
|
|
225
|
+
});
|
|
226
|
+
child.stdin.end(request.stdin, "utf8");
|
|
227
|
+
};
|
|
228
|
+
if (request.stdin !== undefined) {
|
|
229
|
+
if (supervisedLaunch?.strategy === "windows_job_object") {
|
|
230
|
+
// The outer supervisor must receive stdin before it can launch the
|
|
231
|
+
// inner provider. Its start-grant file prevents prompt delivery.
|
|
232
|
+
writeProviderInput();
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
child.once("spawn", () => {
|
|
236
|
+
void readiness?.then(writeProviderInput).catch(fail);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const readers = [
|
|
241
|
+
readProcessLines(childStdout, (line) => {
|
|
242
|
+
if (request.captureOutput !== false)
|
|
243
|
+
stdout += `${line}\n`;
|
|
244
|
+
return handlers.onStdoutLine?.(line);
|
|
245
|
+
}),
|
|
246
|
+
readProcessLines(childStderr, (line) => {
|
|
247
|
+
if (request.captureOutput !== false)
|
|
248
|
+
stderr += `${line}\n`;
|
|
249
|
+
return handlers.onStderrLine?.(line);
|
|
250
|
+
})
|
|
251
|
+
];
|
|
252
|
+
child.on("close", (code) => {
|
|
253
|
+
closed = true;
|
|
254
|
+
Promise.allSettled(readers).then(async (readerResults) => {
|
|
255
|
+
await readiness?.catch(() => undefined);
|
|
256
|
+
const rejected = readerResults.find((result) => result.status === "rejected");
|
|
257
|
+
if (rejected?.status === "rejected") {
|
|
258
|
+
fail(rejected.reason);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (settled) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
if (supervisedLaunch?.strategy === "unix_process_group") {
|
|
266
|
+
await terminate();
|
|
267
|
+
}
|
|
268
|
+
else if (termination) {
|
|
269
|
+
await termination;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
fail(error);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
settled = true;
|
|
277
|
+
cleanup();
|
|
278
|
+
resolve({
|
|
279
|
+
exitCode: code ?? 1,
|
|
280
|
+
stdout,
|
|
281
|
+
stderr
|
|
282
|
+
});
|
|
283
|
+
}).catch(fail);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
async function resolveProviderProcessLaunch(command, args) {
|
|
289
|
+
if (process.platform !== "win32") {
|
|
290
|
+
return { command, args };
|
|
291
|
+
}
|
|
292
|
+
const cursorDirect = await resolveCursorAgentDirectLaunch(command, args);
|
|
293
|
+
if (cursorDirect) {
|
|
294
|
+
return cursorDirect;
|
|
295
|
+
}
|
|
296
|
+
const resolved = await resolveWindowsCommand(command);
|
|
297
|
+
if (!resolved) {
|
|
298
|
+
return { command, args };
|
|
299
|
+
}
|
|
300
|
+
return resolveCodexNpmDirectLaunch(resolved, args)
|
|
301
|
+
?? resolvePackageManagerDirectLaunch(resolved, args)
|
|
302
|
+
?? buildWindowsLaunchCommand(resolved, args);
|
|
303
|
+
}
|
|
304
|
+
function grantWindowsProviderStart(launch) {
|
|
305
|
+
if (!launch.startGrantPath) {
|
|
306
|
+
throw new Error("host_process_supervisor_invalid: Windows provider start-grant path was not assigned.");
|
|
307
|
+
}
|
|
308
|
+
fs.writeFileSync(launch.startGrantPath, "granted\n", {
|
|
309
|
+
encoding: "utf8",
|
|
310
|
+
flag: "wx",
|
|
311
|
+
mode: 0o600,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
async function waitForWindowsProviderReady(launch, signal, isClosed) {
|
|
315
|
+
const readyPath = launch.readyPath;
|
|
316
|
+
if (!readyPath) {
|
|
317
|
+
throw new Error("host_process_supervisor_invalid: Windows provider-ready path was not assigned.");
|
|
318
|
+
}
|
|
319
|
+
while (true) {
|
|
320
|
+
try {
|
|
321
|
+
const parsed = JSON.parse(await fs.promises.readFile(readyPath, "utf8"));
|
|
322
|
+
if (parsed.schemaVersion !== 1
|
|
323
|
+
|| parsed.supervisorId !== launch.supervisorId
|
|
324
|
+
|| !Number.isSafeInteger(parsed.rootPid)
|
|
325
|
+
|| (parsed.rootPid ?? 0) <= 0
|
|
326
|
+
|| typeof parsed.rootIdentity !== "string"
|
|
327
|
+
|| !/^win32:\d+$/u.test(parsed.rootIdentity)
|
|
328
|
+
|| !Number.isSafeInteger(parsed.providerPid)
|
|
329
|
+
|| (parsed.providerPid ?? 0) <= 0
|
|
330
|
+
|| typeof parsed.providerCreatedAt !== "string"
|
|
331
|
+
|| Number.isNaN(Date.parse(parsed.providerCreatedAt))
|
|
332
|
+
|| (parsed.providerIdentity !== undefined
|
|
333
|
+
&& (typeof parsed.providerIdentity !== "string" || !/^win32:\d+$/u.test(parsed.providerIdentity)))
|
|
334
|
+
|| !Number.isSafeInteger(parsed.startAttempt)
|
|
335
|
+
|| (parsed.startAttempt ?? 0) < 1) {
|
|
336
|
+
throw new Error("host_process_supervisor_invalid: provider-ready receipt was invalid.");
|
|
337
|
+
}
|
|
338
|
+
return parsed;
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
if (error.code !== "ENOENT")
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
if (isClosed()) {
|
|
345
|
+
throw new Error("host_process_supervisor_start_failed: supervisor exited before the provider became ready.");
|
|
346
|
+
}
|
|
347
|
+
if (signal?.aborted) {
|
|
348
|
+
throw signal.reason instanceof Error ? signal.reason : new Error("Provider process launch was aborted.");
|
|
349
|
+
}
|
|
350
|
+
await new Promise(resolve => setTimeout(resolve, 25));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
export function resolveCodexNpmDirectLaunch(commandPath, args) {
|
|
354
|
+
const extension = path.extname(commandPath).toLowerCase();
|
|
355
|
+
if ((extension !== ".cmd" && extension !== ".ps1")
|
|
356
|
+
|| path.basename(commandPath, extension).toLowerCase() !== "codex") {
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
const entryPoint = path.join(path.dirname(commandPath), "node_modules", "@openai", "codex", "bin", "codex.js");
|
|
360
|
+
return fs.existsSync(entryPoint)
|
|
361
|
+
? { command: process.execPath, args: [entryPoint, ...args] }
|
|
362
|
+
: null;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Avoids cmd.exe's nested-quote handling for package-manager shims. That
|
|
366
|
+
* quoting loses executable paths containing spaces after the command is
|
|
367
|
+
* wrapped by the Windows Job Object supervisor.
|
|
368
|
+
*/
|
|
369
|
+
export function resolvePackageManagerDirectLaunch(commandPath, args) {
|
|
370
|
+
const extension = path.extname(commandPath).toLowerCase();
|
|
371
|
+
if (extension !== ".cmd" && extension !== ".ps1")
|
|
372
|
+
return null;
|
|
373
|
+
const packageManager = path.basename(commandPath, extension).toLowerCase();
|
|
374
|
+
if (!["npm", "npx", "pnpm", "pnpx", "yarn", "yarnpkg"].includes(packageManager))
|
|
375
|
+
return null;
|
|
376
|
+
const root = path.dirname(commandPath);
|
|
377
|
+
const candidates = [
|
|
378
|
+
path.join(root, "node_modules", "corepack", "dist", `${packageManager}.js`),
|
|
379
|
+
...(packageManager === "npm" || packageManager === "npx"
|
|
380
|
+
? [path.join(root, "node_modules", "npm", "bin", `${packageManager}-cli.js`)]
|
|
381
|
+
: []),
|
|
382
|
+
...(packageManager === "pnpm" || packageManager === "pnpx"
|
|
383
|
+
? [path.join(root, "node_modules", "pnpm", "bin", `${packageManager}.cjs`)]
|
|
384
|
+
: []),
|
|
385
|
+
...(packageManager === "yarn" || packageManager === "yarnpkg"
|
|
386
|
+
? [path.join(root, "node_modules", "yarn", "bin", "yarn.js")]
|
|
387
|
+
: []),
|
|
388
|
+
];
|
|
389
|
+
const entryPoint = candidates.find(candidate => fs.existsSync(candidate));
|
|
390
|
+
if (!entryPoint)
|
|
391
|
+
return null;
|
|
392
|
+
const adjacentNode = path.join(root, "node.exe");
|
|
393
|
+
return {
|
|
394
|
+
command: fs.existsSync(adjacentNode) ? adjacentNode : process.execPath,
|
|
395
|
+
args: [entryPoint, ...args],
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
async function resolveWindowsCommand(command) {
|
|
399
|
+
if (path.isAbsolute(command)) {
|
|
400
|
+
return command;
|
|
401
|
+
}
|
|
402
|
+
const resolvedFromPath = resolveWindowsCommandFromPath(command);
|
|
403
|
+
if (resolvedFromPath) {
|
|
404
|
+
return resolvedFromPath;
|
|
405
|
+
}
|
|
406
|
+
try {
|
|
407
|
+
const result = await execFileAsync("where.exe", [command], { timeout: 2000 });
|
|
408
|
+
return chooseWindowsCommandCandidate(result.stdout.split(/\r?\n/));
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Resolves Windows commands without spawning where.exe. Runtime launches can
|
|
416
|
+
* happen while the machine is under enough process pressure for where.exe to
|
|
417
|
+
* miss its timeout; returning the unresolved package-manager name in that case
|
|
418
|
+
* makes Node fall back to a shell shim that cannot survive Job Object wrapping.
|
|
419
|
+
*/
|
|
420
|
+
export function resolveWindowsCommandFromPath(command, pathValue = process.env.PATH ?? "", pathExtValue = process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD") {
|
|
421
|
+
if (!command || path.isAbsolute(command) || command.includes("/") || command.includes("\\")) {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
const hasExtension = path.extname(command).length > 0;
|
|
425
|
+
const extensions = hasExtension
|
|
426
|
+
? [""]
|
|
427
|
+
: [...new Set([
|
|
428
|
+
"",
|
|
429
|
+
".exe",
|
|
430
|
+
".ps1",
|
|
431
|
+
".cmd",
|
|
432
|
+
".bat",
|
|
433
|
+
...pathExtValue.split(";").map(value => value.trim().toLowerCase()).filter(Boolean),
|
|
434
|
+
])];
|
|
435
|
+
const candidates = [];
|
|
436
|
+
for (const rawDirectory of pathValue.split(path.delimiter)) {
|
|
437
|
+
const directory = rawDirectory.trim().replace(/^"|"$/gu, "");
|
|
438
|
+
if (!directory)
|
|
439
|
+
continue;
|
|
440
|
+
for (const extension of extensions) {
|
|
441
|
+
const candidate = path.join(directory, `${command}${extension}`);
|
|
442
|
+
if (fs.existsSync(candidate))
|
|
443
|
+
candidates.push(candidate);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return chooseWindowsCommandCandidate(candidates);
|
|
447
|
+
}
|
|
448
|
+
export function chooseWindowsCommandCandidate(candidates) {
|
|
449
|
+
const normalized = candidates
|
|
450
|
+
.map((candidate) => candidate.trim())
|
|
451
|
+
.filter((candidate) => candidate.length > 0);
|
|
452
|
+
if (normalized.length === 0) {
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
const preferredExtensions = [".exe", ".ps1", ".cmd", ".bat", ""];
|
|
456
|
+
for (const extension of preferredExtensions) {
|
|
457
|
+
const candidate = normalized.find((entry) => path.extname(entry).toLowerCase() === extension);
|
|
458
|
+
if (candidate) {
|
|
459
|
+
return candidate;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return normalized[0];
|
|
463
|
+
}
|
|
464
|
+
async function readProcessLines(stream, onLine) {
|
|
465
|
+
const reader = readline.createInterface({ input: stream });
|
|
466
|
+
try {
|
|
467
|
+
for await (const line of reader) {
|
|
468
|
+
await onLine(line);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
finally {
|
|
472
|
+
reader.close();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { completionPhaseFromDispatch, type CompletionToolRuntime } from "../phase-completion.js";
|
|
2
|
+
import type { RunOnceDispatchPacket } from "../session.types.js";
|
|
3
|
+
import type { ProviderCliCommand } from "./provider-cli.process.js";
|
|
4
|
+
export declare function buildDraftSpec(dispatch: RunOnceDispatchPacket): string;
|
|
5
|
+
export interface DraftAttachmentPromptReference {
|
|
6
|
+
readonly index: number;
|
|
7
|
+
readonly path: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function buildDraftPrompt(dispatch: RunOnceDispatchPacket, options?: {
|
|
10
|
+
readonly attachmentReferences?: readonly DraftAttachmentPromptReference[];
|
|
11
|
+
}): string;
|
|
12
|
+
export declare function durableDraftInput(dispatch: RunOnceDispatchPacket): RunOnceDispatchPacket["draft"];
|
|
13
|
+
export declare function isDecomposeDraftKind(kind: string | null | undefined): boolean;
|
|
14
|
+
export declare function isDecomposeDraftDispatch(dispatch: RunOnceDispatchPacket): boolean;
|
|
15
|
+
export declare function formatProviderCliCommand(command: ProviderCliCommand): string;
|
|
16
|
+
export declare function formatProviderCliCommandForCheckpoint(command: ProviderCliCommand): string;
|
|
17
|
+
export declare function singleLine(value: string): string;
|
|
18
|
+
export declare function buildProviderCliCommand(dispatch: RunOnceDispatchPacket, run?: {
|
|
19
|
+
readonly prompt?: string;
|
|
20
|
+
readonly promptFromStdin?: boolean;
|
|
21
|
+
readonly worktreePath?: string;
|
|
22
|
+
readonly completionTools?: CompletionToolRuntime;
|
|
23
|
+
readonly skipGitRepoCheck?: boolean;
|
|
24
|
+
readonly imagePaths?: readonly string[];
|
|
25
|
+
readonly runtimeEnvironment?: Readonly<Record<string, string>>;
|
|
26
|
+
}): ProviderCliCommand;
|
|
27
|
+
export declare function buildProviderCliPrompt(dispatch: RunOnceDispatchPacket, prompt: string, runtimeEnvironment?: Readonly<Record<string, string>>): string;
|
|
28
|
+
export declare function codexSandboxModeForPlatform(platform?: NodeJS.Platform, executionMode?: string): "workspace-write" | "danger-full-access";
|
|
29
|
+
export declare function buildArchitectPrompt(dispatch: RunOnceDispatchPacket): string;
|
|
30
|
+
export declare function architectDependencyInstruction(dispatch: RunOnceDispatchPacket): string;
|
|
31
|
+
export declare function designerRefInstruction(dispatch: RunOnceDispatchPacket): string;
|
|
32
|
+
export declare function architectRevisionInstruction(dispatch: RunOnceDispatchPacket): string;
|
|
33
|
+
export declare function buildImplementationPrompt(dispatch: RunOnceDispatchPacket, phase?: import("../phase-completion.js").CompletionPhase): string;
|
|
34
|
+
export declare function buildDevelopCyclePrompt(dispatch: RunOnceDispatchPacket, phase?: import("../phase-completion.js").CompletionPhase): string;
|
|
35
|
+
export declare function buildDebugCyclePrompt(dispatch: RunOnceDispatchPacket, phase?: import("../phase-completion.js").CompletionPhase): string;
|
|
36
|
+
export declare function qaReworkImplementationInstruction(dispatch: RunOnceDispatchPacket): string;
|
|
37
|
+
export declare function debugQaReworkInstruction(dispatch: RunOnceDispatchPacket): string;
|
|
38
|
+
export declare function qaReworkRetestInstruction(dispatch: RunOnceDispatchPacket): string;
|
|
39
|
+
export declare function phaseWorkInstruction(phase: ReturnType<typeof completionPhaseFromDispatch>): string;
|
|
40
|
+
export declare function qaBrowserInstruction(dispatch: RunOnceDispatchPacket): string;
|
|
41
|
+
export declare function storyQaInstruction(dispatch: RunOnceDispatchPacket): string;
|
|
42
|
+
export declare function buildQaPrompt(dispatch: RunOnceDispatchPacket, laneSpec: string): string;
|
|
43
|
+
export declare function buildConflictResolvePrompt(dispatch: RunOnceDispatchPacket, branchName: string): string;
|
|
44
|
+
export declare function buildPullRequestCheckRepairPrompt(dispatch: RunOnceDispatchPacket, branchName: string): string;
|
|
45
|
+
export declare function buildPullRequestCommentsPrompt(dispatch: RunOnceDispatchPacket, branchName: string): string;
|
|
46
|
+
export declare function isPullRequestCheckRepairDispatch(dispatch: RunOnceDispatchPacket): boolean;
|
|
47
|
+
export declare function isPullRequestCommentsDispatch(dispatch: RunOnceDispatchPacket): boolean;
|