@akira-tl/forgerelay 0.10.1 → 0.10.3
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/CHANGELOG.md +21 -0
- package/README.md +3 -2
- package/capabilities/shell-processes/GUIDE.md +1 -1
- package/dist/mcp/process/process-platform.js +60 -1
- package/dist/mcp/process/process-sessions.js +15 -21
- package/dist/runtime/shell/command-shell-runtime.js +55 -22
- package/dist/server.js +1 -1
- package/dist/subagents/sessions/execution.js +1 -1
- package/docs/gotchas.md +1 -4
- package/package.json +4 -2
- package/scripts/ci/cmd-acceptance.mjs +295 -0
- package/scripts/ci/powershell51-acceptance.mjs +483 -0
- package/scripts/ci/pwsh-acceptance.mjs +160 -13
- package/scripts/release/release-gate.test.mjs +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,27 @@ All notable ForgeRelay changes are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.10.3] - 2026-09-06
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added first-class `cmd.exe` execution for Agent commands and Hooks with explicit cmd shell identity, editable cmd Instructions, Windows-native quoting/escaping guidance, and package-level `.cmd` launcher acceptance.
|
|
12
|
+
- Added Windows cmd PTY/ConPTY lifecycle coverage for stdin, resize, background completion, timeout, interrupt, durable output, Unicode, exit codes, and child-process-tree cleanup without introducing a separate Windows Process Manager.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Preserved raw `cmd.exe /S /C` command-line quoting through node-pty by using the Windows pre-escaped CommandLine path for interactive cmd sessions, preventing ConPTY from rewriting quoted commands and exit semantics.
|
|
17
|
+
|
|
18
|
+
## [0.10.2] - 2026-09-06
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Added first-class Windows PowerShell 5.1 (`powershell.exe`) execution for Agent commands, Hooks, pipe/PTY sessions, shell identity guidance, and packaged Windows acceptance while preserving PowerShell 5.1-specific syntax and quoting semantics.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Hardened the Windows ConPTY lifecycle used by PowerShell runtimes: process-tree interruption no longer passes unsupported Windows signals, leaked ConPTY input/output resources are released after exit, and Windows PowerShell text I/O is normalized to UTF-8 so PTY completion, Unicode output, durable output, and child cleanup complete reliably.
|
|
27
|
+
|
|
7
28
|
## [0.10.1] - 2026-09-05
|
|
8
29
|
|
|
9
30
|
### Added
|
package/README.md
CHANGED
|
@@ -295,9 +295,10 @@ See [Security Model](docs/security.md) for the full boundary and threat model.
|
|
|
295
295
|
| --- | --- | --- |
|
|
296
296
|
| Linux | Supported | Requires Node, npm, Git, and Bash. |
|
|
297
297
|
| macOS | Supported | Requires Node, npm, Git, and Bash. |
|
|
298
|
-
| Windows with PowerShell 7 (`pwsh`) | Supported | Agent commands and
|
|
298
|
+
| Windows with PowerShell 7 (`pwsh`) | Supported | Agent commands, Hooks, and PTY execution use one native `pwsh` runtime without loading the user profile. |
|
|
299
|
+
| Windows with Windows PowerShell 5.1 (`powershell.exe`) | Supported | Agent commands, Hooks, and PTY execution use the selected 5.1 runtime with 5.1-specific syntax guidance. |
|
|
300
|
+
| Windows with `cmd.exe` | Supported for Agent commands and Hooks | ForgeRelay preserves cmd syntax and reports the selected runtime explicitly; PTY/packaged-launcher lifecycle coverage is completed separately. |
|
|
299
301
|
| Windows with Git Bash, WSL, MSYS2, or Cygwin Bash | Supported | Bash remains available as a compatibility path. |
|
|
300
|
-
| Windows PowerShell 5.1 (`powershell.exe`) or `cmd.exe` only | Not supported yet | These runtimes are separate follow-up stages. |
|
|
301
302
|
|
|
302
303
|
You can check the local runtime with:
|
|
303
304
|
|
|
@@ -48,6 +48,6 @@ PTY 依赖 optional `node-pty`。缺少该依赖时 ForgeRelay 会明确报错
|
|
|
48
48
|
|
|
49
49
|
## Platform notes
|
|
50
50
|
|
|
51
|
-
ForgeRelay 在 Windows
|
|
51
|
+
ForgeRelay 在 Windows 上原生支持 PowerShell 7 (`pwsh`) 与 Windows PowerShell 5.1 (`powershell.exe`) 的 Agent、Hook 和 PTY 执行,也支持 `cmd.exe` 作为 Agent 命令与 Hook 的一等 Command Shell Runtime。Agent 命令与 Hooks 始终复用已选择的 runtime,不会为了兼容性静默切换到 Bash 或 PowerShell;PowerShell runtime 不加载用户 profile。公共工具名仍保留为 `bash` 以兼容 Host contract,实际命令语言以 ForgeRelay 报告的 Command Shell Runtime 为准。`cmd.exe` 的 PTY/ConPTY 与 packaged `.cmd` launcher lifecycle 属于下一阶段验收,不应由本阶段的非 TTY 支持推断。Git Bash、WSL、MSYS2 或 Cygwin Bash 仍可作为 Bash 兼容路径。用 `forgerelay doctor` 检查当前 shell identity、version 与 executable。
|
|
52
52
|
|
|
53
53
|
Shell 可以作为用户开发任务的一部分修改普通项目文件,但始终受 ForgeRelay core mutation/safety contract 约束。涉及 privileged OS files、credentials、configuration 或外部硬件持久写入时,不要用本指南替代 core authorization/safety 规则。
|
|
@@ -27,6 +27,10 @@ export function resolveShellCommandForRuntime(command, runtime, options = {}) {
|
|
|
27
27
|
// cmd.exe /S applies special quote stripping, so the whole command must
|
|
28
28
|
// be wrapped even when the executable inside it is already quoted.
|
|
29
29
|
args: ["/d", "/s", "/c", `"${command}"`],
|
|
30
|
+
// node-pty accepts a pre-escaped Windows CommandLine string. Passing the
|
|
31
|
+
// cmd arguments as string[] makes node-pty escape embedded quotes using
|
|
32
|
+
// CommandLineToArgvW rules, which changes cmd.exe /S /C semantics.
|
|
33
|
+
...(options.interactive ? { ptyCommandLine: `/d /s /c "${command}"` } : {}),
|
|
30
34
|
windowsVerbatimArguments: true,
|
|
31
35
|
};
|
|
32
36
|
case "bash":
|
|
@@ -46,11 +50,29 @@ export function resolveShellCommandForRuntime(command, runtime, options = {}) {
|
|
|
46
50
|
command,
|
|
47
51
|
],
|
|
48
52
|
};
|
|
49
|
-
case "fish":
|
|
50
53
|
case "powershell":
|
|
54
|
+
return {
|
|
55
|
+
executable: runtime.executable,
|
|
56
|
+
args: [
|
|
57
|
+
"-NoLogo",
|
|
58
|
+
"-NoProfile",
|
|
59
|
+
...(options.interactive ? [] : ["-NonInteractive"]),
|
|
60
|
+
"-Command",
|
|
61
|
+
windowsPowerShellUtf8Command(command),
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
case "fish":
|
|
51
65
|
throw new Error(`Command shell runtime ${runtime.family} is identified but native execution support is not enabled in this release stage. ForgeRelay will not silently execute the command through another shell.`);
|
|
52
66
|
}
|
|
53
67
|
}
|
|
68
|
+
function windowsPowerShellUtf8Command(command) {
|
|
69
|
+
return [
|
|
70
|
+
"[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)",
|
|
71
|
+
"[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)",
|
|
72
|
+
"$OutputEncoding = [Console]::OutputEncoding",
|
|
73
|
+
command,
|
|
74
|
+
].join("; ");
|
|
75
|
+
}
|
|
54
76
|
export function terminateProcessTree(child, signal, detached, runtime = defaultProcessTreeRuntime) {
|
|
55
77
|
if (runtime.platform === "win32" && child.pid) {
|
|
56
78
|
if (runtime.killWindowsTree(child.pid))
|
|
@@ -68,3 +90,40 @@ export function terminateProcessTree(child, signal, detached, runtime = defaultP
|
|
|
68
90
|
}
|
|
69
91
|
child.kill(signal);
|
|
70
92
|
}
|
|
93
|
+
export function releasePtyProcessResources(pty, platform = process.platform) {
|
|
94
|
+
if (platform !== "win32")
|
|
95
|
+
return;
|
|
96
|
+
// node-pty 1.1.0 leaves ConPTY resources referenced after exit: the input
|
|
97
|
+
// PipeWrap is not destroyed (microsoft/node-pty#947), and the system-ConPTY
|
|
98
|
+
// natural-exit path does not dispose its conout worker. Keep both workarounds
|
|
99
|
+
// at the PTY boundary so the shared ProcessManager stays platform-neutral.
|
|
100
|
+
const agent = pty._agent;
|
|
101
|
+
try {
|
|
102
|
+
agent?._conoutSocketWorker?.dispose?.();
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// The worker may already be disposing after an explicit PTY kill.
|
|
106
|
+
}
|
|
107
|
+
const inputSocket = agent?.inSocket;
|
|
108
|
+
if (!inputSocket || inputSocket.destroyed || typeof inputSocket.destroy !== "function")
|
|
109
|
+
return;
|
|
110
|
+
try {
|
|
111
|
+
inputSocket.destroy();
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Completion must remain observable even if node-pty already released it.
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export function terminatePtyProcessTree(pty, signal, runtime = defaultProcessTreeRuntime) {
|
|
118
|
+
if (runtime.platform === "win32") {
|
|
119
|
+
runtime.killWindowsTree(pty.pid);
|
|
120
|
+
try {
|
|
121
|
+
pty.kill();
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// The PTY may already have completed after tree termination.
|
|
125
|
+
}
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
pty.kill(signal);
|
|
129
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { resolveShellCommandForRuntime, terminateProcessTree } from "./process-platform.js";
|
|
2
|
+
import { releasePtyProcessResources, resolveShellCommandForRuntime, terminateProcessTree, terminatePtyProcessTree } from "./process-platform.js";
|
|
3
3
|
import { resolveCompatibilityCommandShellRuntime, snapshotCommandShellRuntime } from "../../runtime/shell/command-shell-runtime.js";
|
|
4
4
|
const DEFAULT_EXEC_YIELD_MS = 10_000;
|
|
5
5
|
const DEFAULT_INTERACTIVE_YIELD_MS = 250;
|
|
@@ -240,7 +240,7 @@ export class ProcessManager {
|
|
|
240
240
|
const processEntry = this.createProcess(input);
|
|
241
241
|
this.processes.set(processEntry.id, processEntry);
|
|
242
242
|
try {
|
|
243
|
-
if (input.tty
|
|
243
|
+
if (input.tty)
|
|
244
244
|
await this.startPty(processEntry, input);
|
|
245
245
|
else
|
|
246
246
|
this.startPipe(processEntry, input);
|
|
@@ -455,7 +455,6 @@ export class ProcessManager {
|
|
|
455
455
|
processEntry.process = {
|
|
456
456
|
write: (data) => child.stdin.write(data),
|
|
457
457
|
kill: (signal = "SIGTERM") => terminateProcessTree(child, signal, detached),
|
|
458
|
-
resize: input.tty ? () => undefined : undefined,
|
|
459
458
|
};
|
|
460
459
|
child.stdout.on("data", (data) => this.append(processEntry, "stdout", data));
|
|
461
460
|
child.stderr.on("data", (data) => this.append(processEntry, "stderr", data));
|
|
@@ -471,30 +470,25 @@ export class ProcessManager {
|
|
|
471
470
|
throw new Error("PTY support requires the optional node-pty dependency.");
|
|
472
471
|
}
|
|
473
472
|
const shell = resolveShellCommandForRuntime(input.command, this.commandShellRuntime, { interactive: true });
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
rows: processEntry.rows,
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
catch (error) {
|
|
489
|
-
throw error;
|
|
490
|
-
}
|
|
473
|
+
const pty = nodePty.spawn(shell.executable, shell.ptyCommandLine ?? shell.args, {
|
|
474
|
+
cwd: input.cwd,
|
|
475
|
+
env: processEnvironment({
|
|
476
|
+
workspaceId: input.workspaceId,
|
|
477
|
+
workspaceRoot: input.workspaceRoot,
|
|
478
|
+
codexCi: input.codexCi,
|
|
479
|
+
}),
|
|
480
|
+
name: "xterm-256color",
|
|
481
|
+
cols: processEntry.columns,
|
|
482
|
+
rows: processEntry.rows,
|
|
483
|
+
});
|
|
491
484
|
processEntry.process = {
|
|
492
485
|
write: (data) => pty.write(data),
|
|
493
|
-
kill: (signal) => pty
|
|
486
|
+
kill: (signal = "SIGTERM") => terminatePtyProcessTree(pty, signal),
|
|
494
487
|
resize: (columns, rows) => pty.resize(columns, rows),
|
|
495
488
|
};
|
|
496
489
|
pty.onData((data) => this.append(processEntry, "pty", data));
|
|
497
490
|
pty.onExit(({ exitCode, signal }) => {
|
|
491
|
+
releasePtyProcessResources(pty);
|
|
498
492
|
this.finish(processEntry, exitCode, signal === 0 ? undefined : String(signal));
|
|
499
493
|
});
|
|
500
494
|
}
|
|
@@ -45,7 +45,7 @@ export function resolveConfiguredCommandShellRuntime(preference, platform = proc
|
|
|
45
45
|
runtime = resolveCommandShellRuntime({ recordedFallback });
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
return enrichConfiguredCommandShellRuntime(runtime, metadata.probePowerShell7Version ?? probePowerShell7Version);
|
|
48
|
+
return enrichConfiguredCommandShellRuntime(runtime, metadata.probePowerShell7Version ?? probePowerShell7Version, metadata.probeWindowsPowerShellVersion ?? probeWindowsPowerShellVersion);
|
|
49
49
|
}
|
|
50
50
|
export function resolveCompatibilityCommandShellRuntime(platform = process.platform, environment = process.env) {
|
|
51
51
|
if (platform === "win32") {
|
|
@@ -129,39 +129,67 @@ export function formatCommandShellRuntime(runtime) {
|
|
|
129
129
|
export function snapshotCommandShellRuntime(runtime) {
|
|
130
130
|
return { ...runtime, capabilities: [...runtime.capabilities] };
|
|
131
131
|
}
|
|
132
|
-
function enrichConfiguredCommandShellRuntime(runtime, powerShell7VersionProbe) {
|
|
133
|
-
if (runtime.family
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
function enrichConfiguredCommandShellRuntime(runtime, powerShell7VersionProbe, windowsPowerShellVersionProbe) {
|
|
133
|
+
if (runtime.family === "pwsh") {
|
|
134
|
+
const version = powerShell7VersionProbe(runtime.executable).trim();
|
|
135
|
+
const major = Number.parseInt(version.split(".", 1)[0] ?? "", 10);
|
|
136
|
+
if (!version || !Number.isInteger(major)) {
|
|
137
|
+
throw new Error(`Unable to determine PowerShell 7 version from ${runtime.executable}.`);
|
|
138
|
+
}
|
|
139
|
+
if (major < 7) {
|
|
140
|
+
throw new Error(`Configured pwsh runtime must be PowerShell 7 or newer; detected ${version} at ${runtime.executable}.`);
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
...runtime,
|
|
144
|
+
version,
|
|
145
|
+
capabilities: Array.from(new Set([
|
|
146
|
+
...runtime.capabilities,
|
|
147
|
+
"profile-isolation",
|
|
148
|
+
"pipeline-chain-operators",
|
|
149
|
+
])),
|
|
150
|
+
};
|
|
139
151
|
}
|
|
140
|
-
if (
|
|
141
|
-
|
|
152
|
+
if (runtime.family === "powershell") {
|
|
153
|
+
const version = windowsPowerShellVersionProbe(runtime.executable).trim();
|
|
154
|
+
const [majorText, minorText] = version.split(".", 3);
|
|
155
|
+
const major = Number.parseInt(majorText ?? "", 10);
|
|
156
|
+
const minor = Number.parseInt(minorText ?? "", 10);
|
|
157
|
+
if (!version || !Number.isInteger(major) || !Number.isInteger(minor)) {
|
|
158
|
+
throw new Error(`Unable to determine Windows PowerShell version from ${runtime.executable}.`);
|
|
159
|
+
}
|
|
160
|
+
if (major !== 5 || minor !== 1) {
|
|
161
|
+
throw new Error(`Configured powershell runtime must be Windows PowerShell 5.1; detected ${version} at ${runtime.executable}. ForgeRelay will not substitute pwsh for the selected runtime.`);
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
...runtime,
|
|
165
|
+
version,
|
|
166
|
+
capabilities: Array.from(new Set([
|
|
167
|
+
...runtime.capabilities,
|
|
168
|
+
"profile-isolation",
|
|
169
|
+
"powershell-5.1",
|
|
170
|
+
])),
|
|
171
|
+
};
|
|
142
172
|
}
|
|
143
|
-
return
|
|
144
|
-
...runtime,
|
|
145
|
-
version,
|
|
146
|
-
capabilities: Array.from(new Set([
|
|
147
|
-
...runtime.capabilities,
|
|
148
|
-
"profile-isolation",
|
|
149
|
-
"pipeline-chain-operators",
|
|
150
|
-
])),
|
|
151
|
-
};
|
|
173
|
+
return runtime;
|
|
152
174
|
}
|
|
153
175
|
function probePowerShell7Version(executable) {
|
|
176
|
+
return probePowerShellVersion(executable, "PowerShell 7");
|
|
177
|
+
}
|
|
178
|
+
function probeWindowsPowerShellVersion(executable) {
|
|
179
|
+
return probePowerShellVersion(executable, "Windows PowerShell");
|
|
180
|
+
}
|
|
181
|
+
function probePowerShellVersion(executable, label) {
|
|
154
182
|
const result = spawnSync(executable, ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", "$PSVersionTable.PSVersion.ToString()"], { encoding: "utf8", windowsHide: true, timeout: 5_000 });
|
|
155
183
|
if (result.error) {
|
|
156
|
-
throw new Error(`Unable to query
|
|
184
|
+
throw new Error(`Unable to query ${label} version from ${executable}: ${result.error.message}`);
|
|
157
185
|
}
|
|
158
186
|
if (result.status !== 0) {
|
|
159
187
|
const detail = result.stderr?.trim();
|
|
160
|
-
throw new Error(`Unable to query
|
|
188
|
+
throw new Error(`Unable to query ${label} version from ${executable}: exited with code ${result.status ?? "unknown"}${detail ? `: ${detail}` : ""}.`);
|
|
161
189
|
}
|
|
162
190
|
const version = result.stdout?.trim();
|
|
163
191
|
if (!version)
|
|
164
|
-
throw new Error(`Unable to query
|
|
192
|
+
throw new Error(`Unable to query ${label} version from ${executable}: no version was reported.`);
|
|
165
193
|
return version;
|
|
166
194
|
}
|
|
167
195
|
export function commandShellAgentInstruction(runtime) {
|
|
@@ -173,6 +201,11 @@ export function commandShellAgentInstruction(runtime) {
|
|
|
173
201
|
`Executable: ${runtime.executable}.`,
|
|
174
202
|
`Selection source: ${runtime.source}.`,
|
|
175
203
|
"Write shell commands for this runtime rather than assuming Bash syntax.",
|
|
204
|
+
...(runtime.family === "powershell"
|
|
205
|
+
? ["Windows PowerShell 5.1 does not support PowerShell 7 pipeline-chain operators && or ||."]
|
|
206
|
+
: runtime.family === "cmd"
|
|
207
|
+
? ["cmd.exe uses %NAME% environment expansion and %ERRORLEVEL% process status. Do not emit PowerShell or Bash syntax unless explicitly launching those shells as child processes."]
|
|
208
|
+
: []),
|
|
176
209
|
].join(" ");
|
|
177
210
|
}
|
|
178
211
|
function runtimeFromSelection(selection, source) {
|
package/dist/server.js
CHANGED
|
@@ -124,7 +124,7 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
124
124
|
};
|
|
125
125
|
};
|
|
126
126
|
const toolDescriptions = buildToolDescriptions(config);
|
|
127
|
-
const hooks = new HookRunner(config.hooks, config.logging, process.env, (workspaceId, result) => attachCompletedProcessNotices(processSessions, workspaceId, result, (snapshot) => recordBashCompletion(activityLifecycle, bashOutputStore, snapshot.outputId)));
|
|
127
|
+
const hooks = new HookRunner(config.hooks, config.logging, process.env, (workspaceId, result) => attachCompletedProcessNotices(processSessions, workspaceId, result, (snapshot) => recordBashCompletion(activityLifecycle, bashOutputStore, snapshot.outputId)), config.commandShellRuntime);
|
|
128
128
|
const incomingArtifactRegistry = new IncomingArtifactAdapterRegistry(incomingArtifactAdapters);
|
|
129
129
|
const artifactDownloadAvailable = config.artifactsEnabled && isArtifactDownloadSupportedPlatform();
|
|
130
130
|
const reviewChangesAvailable = config.widgets === "changes";
|
|
@@ -13,7 +13,7 @@ export async function executeSubagentRun(config, input, providerRunner = runSuba
|
|
|
13
13
|
if (record.activeRun?.id !== input.runId) {
|
|
14
14
|
throw new Error(`Subagent Run ${input.runId} is not active for Session ${record.id}.`);
|
|
15
15
|
}
|
|
16
|
-
const hooks = new HookRunner(config.hooks, config.logging);
|
|
16
|
+
const hooks = new HookRunner(config.hooks, config.logging, process.env, undefined, config.commandShellRuntime);
|
|
17
17
|
const hookInvocation = {
|
|
18
18
|
workspaceId: record.workspaceId,
|
|
19
19
|
workspaceRoot: record.workspaceRoot,
|
package/docs/gotchas.md
CHANGED
|
@@ -162,10 +162,7 @@ ForgeRelay does not intentionally leave the source checkout in a merge-conflict
|
|
|
162
162
|
|
|
163
163
|
## Windows shell commands fail
|
|
164
164
|
|
|
165
|
-
ForgeRelay supports PowerShell 7 (`pwsh`) as
|
|
166
|
-
Agent commands and Hooks share that runtime and do not load the user PowerShell profile.
|
|
167
|
-
Windows PowerShell 5.1 (`powershell.exe`) and `cmd.exe` remain separate follow-up runtimes.
|
|
168
|
-
Git Bash, WSL, MSYS2, and Cygwin Bash also remain supported compatibility paths.
|
|
165
|
+
ForgeRelay supports PowerShell 7 (`pwsh`) and Windows PowerShell 5.1 (`powershell.exe`) as native Windows Command Shell Runtimes. Agent commands and Hooks share the selected runtime and PowerShell execution does not load the user profile. `cmd.exe` is also a first-class Agent/Hook runtime: use `%NAME%`, `%ERRORLEVEL%`, cmd quoting/chaining/redirection, and `^` escaping rather than silently substituting PowerShell or Bash syntax. cmd PTY/ConPTY and packaged `.cmd` launcher lifecycle coverage is completed separately. Git Bash, WSL, MSYS2, and Cygwin Bash remain supported compatibility paths.
|
|
169
166
|
|
|
170
167
|
Check the selected runtime with:
|
|
171
168
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akira-tl/forgerelay",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "Local development control plane for MCP coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/Akira-TL/forgerelay#readme",
|
|
@@ -43,6 +43,8 @@
|
|
|
43
43
|
"traffic:audit": "node --import tsx scripts/debug/traffic/traffic-audit.mjs",
|
|
44
44
|
"lsp:interop": "node scripts/lsp/interop.mjs",
|
|
45
45
|
"pwsh:accept": "node scripts/ci/pwsh-acceptance.mjs",
|
|
46
|
+
"powershell51:accept": "node scripts/ci/powershell51-acceptance.mjs",
|
|
47
|
+
"cmd:accept": "node scripts/ci/cmd-acceptance.mjs",
|
|
46
48
|
"wiki:check": "node scripts/wiki/sync.mjs check",
|
|
47
49
|
"wiki:publish": "node scripts/wiki/sync.mjs publish",
|
|
48
50
|
"architecture:check": "node scripts/ci/architecture.mjs",
|
|
@@ -53,7 +55,7 @@
|
|
|
53
55
|
"release:push-ready": "node scripts/release/push-ready.mjs",
|
|
54
56
|
"postinstall": "node scripts/fix-node-pty-permissions.mjs",
|
|
55
57
|
"start": "node dist/cli.js serve",
|
|
56
|
-
"test": "node --test scripts/debug/runtime.test.mjs scripts/release-proof.test.mjs scripts/release/release-gate.test.mjs scripts/release/push-ready.test.mjs scripts/release/release-version.test.mjs && tsx src/mcp/oauth/router.test.ts && tsx src/workspaces/relay/auth/remote-auth-cli.test.ts && tsx src/workspaces/relay/auth/remote-ssh-auth-cli.test.ts && tsx src/workspaces/relay/tests/lifecycle.test.ts && tsx src/workspaces/relay/tests/routing.test.ts && tsx src/workspaces/relay/tests/ssh.test.ts && tsx src/workspaces/relay/tests/process.test.ts && tsx src/workspaces/relay/tests/recovery.test.ts && tsx src/workspaces/relay/tests/checkpoint.test.ts && tsx src/runtime/config/config.test.ts && tsx src/runtime/instructions/powershell-skill.test.ts && tsx src/lsp/language-server-config.test.ts && tsx src/lsp/runtime/managed-language-servers.test.ts && tsx src/lsp/normalization/hover.test.ts && tsx src/lsp/references.server.test.ts && tsx src/lsp/operations/document-symbols.server.test.ts && tsx src/lsp/operations/workspace-symbols.server.test.ts && tsx src/lsp/operations/diagnostics-push.server.test.ts && tsx src/lsp/operations/diagnostics-pull.server.test.ts && tsx src/lsp/operations/request-hardening.server.test.ts && tsx src/lsp/operations/recovery.server.test.ts && tsx src/lsp/operations/lifecycle.server.test.ts && tsx src/lsp/runtime/semantic-requests.test.ts && tsx src/runtime/logging/logger.test.ts && tsx src/runtime/logging/proxy-trust.test.ts && tsx src/runtime/state/lock/file-lock.test.ts && tsx src/runtime/state/runtime-lease.test.ts && tsx src/mcp/panel/mcp-app-template.test.ts && tsx src/mcp/hooks/hooks.test.ts && tsx src/mcp/server/core/capability-registry.test.ts && tsx src/mcp/server-instructions.test.ts && tsx src/mcp/request-meta.test.ts && tsx src/mcp/artifacts/incoming-artifacts.test.ts && tsx src/mcp/artifacts/artifact-download.test.ts && tsx src/ui/core/card-types.test.ts && tsx src/ui/activity/model.test.ts && tsx src/ui/activity/detail-card.test.ts && tsx src/ui/review/patch-display.test.ts && tsx src/ui/core/tool-display.test.ts && tsx src/mcp/filesystem/apply-patch.test.ts && tsx src/mcp/process/process-platform.test.ts && tsx src/mcp/process/process-sessions.test.ts && tsx src/mcp/server/transport/mcp-sessions.test.ts && tsx src/mcp/server/transport/server-shutdown.test.ts && tsx src/mcp/server/operations/mutation-diagnostics.test.ts && tsx src/subagents/providers/adapters/codex.test.ts && tsx src/subagents/providers/adapters/pi.test.ts && tsx src/subagents/providers/registry.test.ts && tsx src/subagents/providers/availability.test.ts && tsx src/subagents/profiles.test.ts && tsx src/subagents/cli-target.test.ts && tsx src/subagents/sessions/store.test.ts && tsx src/subagents/sessions/manager.test.ts && tsx src/subagents/sessions/mcp/capability.server.test.ts && tsx src/subagents/sessions/mcp/continuation.server.test.ts && tsx src/subagents/sessions/mcp/lifecycle.server.test.ts && tsx src/subagents/sessions/mcp/reconciliation.server.test.ts && tsx src/subagents/sessions/mcp/routing.server.test.ts && tsx src/mcp/filesystem/roots.test.ts && tsx src/mcp/filesystem/file-mutations.test.ts && tsx src/mcp/operations/edit-preflight.test.ts && tsx src/workspaces/resources/skills.test.ts && tsx src/runtime/state/db/migrations.test.ts && tsx src/workspaces/state/workspace-store.test.ts && tsx src/workspaces/tasks/workspace-tasks.test.ts && tsx src/workspaces/tasks/workspace-task-reminders.test.ts && tsx src/activity/history/audit-store.test.ts && tsx src/activity/history/bash-output-store.test.ts && tsx src/activity/runtime/lifecycle.test.ts && tsx src/activity/history/query-service.test.ts && tsx src/mcp/operations/core-operation-executor.test.ts && tsx src/mcp/operations/bulk-mutation.test.ts && tsx src/mcp/operations/batch/scheduler.test.ts && tsx src/mcp/operations/batch/executor-policy.test.ts && tsx src/workspaces.test.ts && tsx src/workspaces/conversation-checkout.test.ts && tsx src/workspaces/conversation-worktree.test.ts && tsx src/workspaces/git/worktree-recovery.test.ts && tsx src/mcp/server/workspace/workspace-inventory.test.ts && tsx src/mcp/server/workspace/workspace-recovery.test.ts && tsx src/mcp/server/workspace/workspace-checkpoint.test.ts && tsx src/workspaces/review/review-checkpoints.test.ts && tsx src/lsp/code-intelligence.server.test.ts && npm run build:app && tsx src/mcp/process/server.test.ts && tsx src/mcp/panel/server.test.ts && tsx src/mcp/server/server.test.ts && tsx src/mcp/oauth/oauth-store.test.ts && tsx src/cli/maintenance.test.ts && tsx src/cli/maintenance-prune.test.ts && tsx src/cli/cli.test.ts",
|
|
58
|
+
"test": "node --test scripts/debug/runtime.test.mjs scripts/release-proof.test.mjs scripts/release/release-gate.test.mjs scripts/release/push-ready.test.mjs scripts/release/release-version.test.mjs && tsx src/mcp/oauth/router.test.ts && tsx src/workspaces/relay/auth/remote-auth-cli.test.ts && tsx src/workspaces/relay/auth/remote-ssh-auth-cli.test.ts && tsx src/workspaces/relay/tests/lifecycle.test.ts && tsx src/workspaces/relay/tests/routing.test.ts && tsx src/workspaces/relay/tests/ssh.test.ts && tsx src/workspaces/relay/tests/process.test.ts && tsx src/workspaces/relay/tests/recovery.test.ts && tsx src/workspaces/relay/tests/checkpoint.test.ts && tsx src/runtime/config/config.test.ts && tsx src/runtime/shell/command-shell-runtime.test.ts && tsx src/runtime/instructions/shell-instructions.test.ts && tsx src/runtime/instructions/powershell-skill.test.ts && tsx src/cli/shell/setup.test.ts && tsx src/lsp/language-server-config.test.ts && tsx src/lsp/runtime/managed-language-servers.test.ts && tsx src/lsp/normalization/hover.test.ts && tsx src/lsp/references.server.test.ts && tsx src/lsp/operations/document-symbols.server.test.ts && tsx src/lsp/operations/workspace-symbols.server.test.ts && tsx src/lsp/operations/diagnostics-push.server.test.ts && tsx src/lsp/operations/diagnostics-pull.server.test.ts && tsx src/lsp/operations/request-hardening.server.test.ts && tsx src/lsp/operations/recovery.server.test.ts && tsx src/lsp/operations/lifecycle.server.test.ts && tsx src/lsp/runtime/semantic-requests.test.ts && tsx src/runtime/logging/logger.test.ts && tsx src/runtime/logging/proxy-trust.test.ts && tsx src/runtime/state/lock/file-lock.test.ts && tsx src/runtime/state/runtime-lease.test.ts && tsx src/mcp/panel/mcp-app-template.test.ts && tsx src/mcp/hooks/hooks.test.ts && tsx src/mcp/server/core/capability-registry.test.ts && tsx src/mcp/server-instructions.test.ts && tsx src/mcp/request-meta.test.ts && tsx src/mcp/artifacts/incoming-artifacts.test.ts && tsx src/mcp/artifacts/artifact-download.test.ts && tsx src/ui/core/card-types.test.ts && tsx src/ui/activity/model.test.ts && tsx src/ui/activity/detail-card.test.ts && tsx src/ui/review/patch-display.test.ts && tsx src/ui/core/tool-display.test.ts && tsx src/mcp/filesystem/apply-patch.test.ts && tsx src/mcp/process/process-platform.test.ts && tsx src/mcp/process/process-sessions.test.ts && tsx src/mcp/server/transport/mcp-sessions.test.ts && tsx src/mcp/server/transport/server-shutdown.test.ts && tsx src/mcp/server/operations/mutation-diagnostics.test.ts && tsx src/mcp/server/operations/hooks.test.ts && tsx src/subagents/providers/adapters/codex.test.ts && tsx src/subagents/providers/adapters/pi.test.ts && tsx src/subagents/providers/registry.test.ts && tsx src/subagents/providers/availability.test.ts && tsx src/subagents/profiles.test.ts && tsx src/subagents/cli-target.test.ts && tsx src/subagents/sessions/store.test.ts && tsx src/subagents/sessions/manager.test.ts && tsx src/subagents/sessions/mcp/capability.server.test.ts && tsx src/subagents/sessions/mcp/continuation.server.test.ts && tsx src/subagents/sessions/mcp/lifecycle.server.test.ts && tsx src/subagents/sessions/mcp/reconciliation.server.test.ts && tsx src/subagents/sessions/mcp/routing.server.test.ts && tsx src/mcp/filesystem/roots.test.ts && tsx src/mcp/filesystem/file-mutations.test.ts && tsx src/mcp/operations/edit-preflight.test.ts && tsx src/workspaces/resources/skills.test.ts && tsx src/runtime/state/db/migrations.test.ts && tsx src/workspaces/state/workspace-store.test.ts && tsx src/workspaces/tasks/workspace-tasks.test.ts && tsx src/workspaces/tasks/workspace-task-reminders.test.ts && tsx src/activity/history/audit-store.test.ts && tsx src/activity/history/bash-output-store.test.ts && tsx src/activity/runtime/lifecycle.test.ts && tsx src/activity/history/query-service.test.ts && tsx src/mcp/operations/core-operation-executor.test.ts && tsx src/mcp/operations/bulk-mutation.test.ts && tsx src/mcp/operations/batch/scheduler.test.ts && tsx src/mcp/operations/batch/executor-policy.test.ts && tsx src/workspaces.test.ts && tsx src/workspaces/conversation-checkout.test.ts && tsx src/workspaces/conversation-worktree.test.ts && tsx src/workspaces/git/worktree-recovery.test.ts && tsx src/mcp/server/workspace/workspace-inventory.test.ts && tsx src/mcp/server/workspace/workspace-recovery.test.ts && tsx src/mcp/server/workspace/workspace-checkpoint.test.ts && tsx src/workspaces/review/review-checkpoints.test.ts && tsx src/lsp/code-intelligence.server.test.ts && npm run build:app && tsx src/mcp/process/server.test.ts && tsx src/mcp/panel/server.test.ts && tsx src/mcp/server/server.test.ts && tsx src/mcp/oauth/oauth-store.test.ts && tsx src/cli/maintenance.test.ts && tsx src/cli/maintenance-prune.test.ts && tsx src/cli/cli.test.ts",
|
|
57
59
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
58
60
|
"release:check": "node scripts/release-version.mjs check",
|
|
59
61
|
"release:tag-check": "node scripts/release-version.mjs tag",
|