fauxnix-cli 0.4.0 → 0.4.2
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 +29 -1
- package/dist/executor.js +9 -1
- package/dist/mcp.js +35 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# fauxnix
|
|
2
2
|
|
|
3
|
+
[](https://github.com/20000419/fauxnix/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/fauxnix-cli)
|
|
5
|
+
[](https://www.npmjs.com/package/fauxnix-cli)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://glama.ai/mcp/servers/20000419/fauxnix)
|
|
8
|
+
|
|
3
9
|
**Run Linux-style commands on Windows — natively, deterministically, with no VM and no WSL.**
|
|
4
10
|
|
|
5
11
|
fauxnix is a bash→PowerShell translation layer built for AI agents. Your agent keeps writing the
|
|
@@ -8,15 +14,37 @@ fauxnix deterministically translates each command into PowerShell, executes it n
|
|
|
8
14
|
back output that looks like GNU/Linux: `ls -l` columns, bash-style error messages, coreutils exit
|
|
9
15
|
codes, UTF-8/GBK handled automatically.
|
|
10
16
|
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g fauxnix-cli # then point any MCP harness at `fauxnix mcp`
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+

|
|
22
|
+
|
|
11
23
|
```
|
|
12
24
|
$ fauxnix "ls -la src | head -2"
|
|
13
25
|
-rw-r--r-- 1 me me 1204 Aug 16 09:12 ast.ts
|
|
14
|
-
-rw-r--r-- 1 me me
|
|
26
|
+
-rw-r--r-- 1 me me 8192 Aug 16 09:12 cli.ts
|
|
15
27
|
|
|
16
28
|
$ fauxnix "cat nope.txt"
|
|
17
29
|
cat: nope.txt: No such file or directory # not a PowerShell stack trace
|
|
18
30
|
```
|
|
19
31
|
|
|
32
|
+
## Measured: your model is probably worse at PowerShell than you think
|
|
33
|
+
|
|
34
|
+
Same model (DeepSeek-V4-Pro), same 5 tasks, three execution modes on one Windows machine —
|
|
35
|
+
full data in [`docs/benchmark-deepseek-v4-pro.md`](docs/benchmark-deepseek-v4-pro.md) and
|
|
36
|
+
[`docs/benchmark-ark-models.md](docs/benchmark-ark-models.md):
|
|
37
|
+
|
|
38
|
+
| | PowerShell | **fauxnix** | Git Bash |
|
|
39
|
+
|---|---|---|---|
|
|
40
|
+
| tool calls / unexpected errors | 14 / 9 | **7 / 0** | 4 / 0 |
|
|
41
|
+
| time (T1–T4) | 163s | **66s** | 57s |
|
|
42
|
+
|
|
43
|
+
Across 7 models on the Volcano Ark Coding Plan, the PowerShell-vs-fauxnix gap held for every
|
|
44
|
+
model tested — worst case (kimi-k2-thinking): **3.1× slower with 24 error events** writing
|
|
45
|
+
PowerShell vs zero errors through fauxnix. fauxnix lands within ~15% of the real-bash ceiling
|
|
46
|
+
with no bash toolchain installed.
|
|
47
|
+
|
|
20
48
|
## Why
|
|
21
49
|
|
|
22
50
|
LLM agents are dramatically better at bash than at PowerShell — bash dominates training data, so
|
package/dist/executor.js
CHANGED
|
@@ -350,7 +350,15 @@ async function runPlans(plans, session, opts, afterSegment, scriptFile) {
|
|
|
350
350
|
}, timeoutMs);
|
|
351
351
|
const code = await new Promise((resolve) => {
|
|
352
352
|
child.on('error', (e) => {
|
|
353
|
-
|
|
353
|
+
if (e.code === 'ENOENT') {
|
|
354
|
+
stderr +=
|
|
355
|
+
'fauxnix: powershell.exe not found — fauxnix executes bash via native Windows PowerShell 5.1+.\n' +
|
|
356
|
+
'This host has no PowerShell on PATH (typical for Linux containers/sandboxes).\n' +
|
|
357
|
+
'Run fauxnix on Windows, or install PowerShell and make powershell.exe reachable on PATH.\n';
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
stderr += 'fauxnix: failed to start powershell.exe: ' + e.message + '\n';
|
|
361
|
+
}
|
|
354
362
|
resolve(127);
|
|
355
363
|
});
|
|
356
364
|
child.on('close', (c) => resolve(running.killed ? 124 : (c ?? 0)));
|
package/dist/mcp.js
CHANGED
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
3
5
|
import { z } from 'zod';
|
|
4
6
|
import { FauxnixSession } from './executor.js';
|
|
5
7
|
import { parseCommand } from './parser.js';
|
|
6
8
|
import { translateCommandList, wrapScript, translatePipelineBody } from './translator.js';
|
|
7
9
|
import { registeredNames } from './registry.js';
|
|
8
10
|
import './commands/install-all.js';
|
|
11
|
+
// single source of truth: the npm package version in package.json
|
|
12
|
+
// (src/ and dist/ sit one level below the root, so the relative path holds in both)
|
|
13
|
+
const pkgVersion = JSON.parse(readFileSync(fileURLToPath(new URL('../package.json', import.meta.url)), 'utf8')).version;
|
|
9
14
|
const TOOL_NAME = process.env.FAUXNIX_TOOL_NAME || 'bash';
|
|
15
|
+
const EXEC_ANNOTATIONS = {
|
|
16
|
+
readOnlyHint: false,
|
|
17
|
+
destructiveHint: true,
|
|
18
|
+
idempotentHint: false,
|
|
19
|
+
openWorldHint: true,
|
|
20
|
+
};
|
|
21
|
+
const TRANSLATE_ANNOTATIONS = {
|
|
22
|
+
readOnlyHint: true,
|
|
23
|
+
destructiveHint: false,
|
|
24
|
+
idempotentHint: true,
|
|
25
|
+
openWorldHint: false,
|
|
26
|
+
};
|
|
27
|
+
const SESSION_ANNOTATIONS = {
|
|
28
|
+
readOnlyHint: false,
|
|
29
|
+
destructiveHint: false,
|
|
30
|
+
idempotentHint: true,
|
|
31
|
+
openWorldHint: false,
|
|
32
|
+
};
|
|
10
33
|
const TOOL_DESCRIPTION = `Execute a Linux/bash-style command on this Windows machine.
|
|
11
34
|
|
|
12
35
|
Commands are deterministically translated to PowerShell and executed natively — no WSL or VM.
|
|
@@ -17,9 +40,11 @@ Unknown commands (git, node, npm, python, cargo...) are passed through and execu
|
|
|
17
40
|
Not supported: heredocs, backticks, control flow (if/for/while), background jobs.
|
|
18
41
|
|
|
19
42
|
CWD, environment variables, export/unset and cd persist across calls within this session — but prefer COMBINING related commands in one call with ; or && (e.g. 'cd src && ls | wc -l'); each call is a fresh translation+process, so batching is faster than many tiny calls.
|
|
20
|
-
Exit codes follow bash conventions (0 ok, 1 fail, 2 usage/serious, 127 command not found, 124 timeout)
|
|
43
|
+
Exit codes follow bash conventions (0 ok, 1 fail, 2 usage/serious, 127 command not found, 124 timeout).
|
|
44
|
+
|
|
45
|
+
Platform requirement: the execution backend is native Windows PowerShell 5.1+. On hosts without PowerShell on PATH (e.g. Linux containers/sandboxes), the bash tool returns exit code 127 with an actionable error instead of running the command.`;
|
|
21
46
|
export async function startMcpServer() {
|
|
22
|
-
const server = new McpServer({ name: 'fauxnix', version:
|
|
47
|
+
const server = new McpServer({ name: 'fauxnix', version: pkgVersion }, { capabilities: { tools: {} } });
|
|
23
48
|
const session = new FauxnixSession();
|
|
24
49
|
server.tool(TOOL_NAME, TOOL_DESCRIPTION, {
|
|
25
50
|
command: z.string().describe('The bash-style command line to run'),
|
|
@@ -30,7 +55,7 @@ export async function startMcpServer() {
|
|
|
30
55
|
.max(600_000)
|
|
31
56
|
.optional()
|
|
32
57
|
.describe('Timeout in milliseconds (default 120000)'),
|
|
33
|
-
}, async ({ command, timeout_ms }) => {
|
|
58
|
+
}, EXEC_ANNOTATIONS, async ({ command, timeout_ms }) => {
|
|
34
59
|
try {
|
|
35
60
|
const plans = translateCommandList(parseCommand(command));
|
|
36
61
|
const result = await session.run(plans, { timeoutMs: timeout_ms });
|
|
@@ -49,7 +74,7 @@ export async function startMcpServer() {
|
|
|
49
74
|
return { content: [{ type: 'text', text: msg }], isError: true };
|
|
50
75
|
}
|
|
51
76
|
});
|
|
52
|
-
server.tool('fauxnix_translate', 'Translate a bash-style command into the equivalent PowerShell script WITHOUT executing it. Useful for learning/debugging what fauxnix does under the hood.', { command: z.string() }, async ({ command }) => {
|
|
77
|
+
server.tool('fauxnix_translate', 'Translate a bash-style command into the equivalent PowerShell script WITHOUT executing it. Useful for learning/debugging what fauxnix does under the hood.', { command: z.string().describe('The bash-style command line to translate (never executed)') }, TRANSLATE_ANNOTATIONS, async ({ command }) => {
|
|
53
78
|
try {
|
|
54
79
|
const list = parseCommand(command);
|
|
55
80
|
const plans = translateCommandList(list);
|
|
@@ -61,7 +86,12 @@ export async function startMcpServer() {
|
|
|
61
86
|
return { content: [{ type: 'text', text: msg }], isError: true };
|
|
62
87
|
}
|
|
63
88
|
});
|
|
64
|
-
server.tool('fauxnix_session', 'Inspect or reset the persistent fauxnix shell session (current directory, environment, session id). Actions: "status" (default) or "reset".', {
|
|
89
|
+
server.tool('fauxnix_session', 'Inspect or reset the persistent fauxnix shell session (current directory, environment, session id). Actions: "status" (default) or "reset".', {
|
|
90
|
+
action: z
|
|
91
|
+
.enum(['status', 'reset'])
|
|
92
|
+
.default('status')
|
|
93
|
+
.describe('"status" shows the session state (cwd, tracked env keys); "reset" clears it back to a fresh shell'),
|
|
94
|
+
}, SESSION_ANNOTATIONS, async ({ action }) => {
|
|
65
95
|
if (action === 'reset') {
|
|
66
96
|
await session.dispose();
|
|
67
97
|
return { content: [{ type: 'text', text: 'fauxnix: session reset' }] };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fauxnix-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Fauxnix — run Linux-style commands on Windows via deterministic PowerShell translation. No VM, no WSL. MCP server + CLI for AI agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -53,6 +53,6 @@
|
|
|
53
53
|
"@types/node": "^20.14.0",
|
|
54
54
|
"tsx": "^4.19.0",
|
|
55
55
|
"typescript": "^5.5.0",
|
|
56
|
-
"vitest": "
|
|
56
|
+
"vitest": "3.2.6"
|
|
57
57
|
}
|
|
58
58
|
}
|