fauxnix-cli 0.2.0 → 0.3.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/README.md +203 -199
- package/dist/ast.d.ts +7 -1
- package/dist/ast.js +10 -0
- package/dist/cli.js +13 -13
- package/dist/commands/sysinfo.js +1300 -48
- package/dist/encoding.d.ts +9 -0
- package/dist/encoding.js +19 -0
- package/dist/executor.js +230 -105
- package/dist/mcp.js +11 -11
- package/dist/parser.d.ts +2 -0
- package/dist/parser.js +420 -15
- package/dist/registry.js +10 -0
- package/dist/translator.d.ts +16 -1
- package/dist/translator.js +271 -11
- package/package.json +58 -58
package/dist/translator.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FauxnixParseError, } from './ast.js';
|
|
1
|
+
import { FauxnixParseError, isUnquotedLiteral, } from './ast.js';
|
|
2
2
|
import { parseCommand } from './parser.js';
|
|
3
3
|
import { lookup, psStr } from './registry.js';
|
|
4
4
|
/* ------------------------------------------------------------------ */
|
|
@@ -36,8 +36,13 @@ export function varExpr(name) {
|
|
|
36
36
|
/* Word → PowerShell expression */
|
|
37
37
|
/* ------------------------------------------------------------------ */
|
|
38
38
|
/** Escape text destined for the inside of a PS double-quoted string. */
|
|
39
|
-
function
|
|
40
|
-
return s
|
|
39
|
+
export function escapeDq(s) {
|
|
40
|
+
return s
|
|
41
|
+
.replace(/`/g, '``')
|
|
42
|
+
.replace(/"/g, '`"')
|
|
43
|
+
.replace(/\$/g, '`$')
|
|
44
|
+
.replace(/\r/g, '`r')
|
|
45
|
+
.replace(/\n/g, '`n');
|
|
41
46
|
}
|
|
42
47
|
/** Normalize a literal POSIX-ish path to its Windows equivalent. */
|
|
43
48
|
export function normalizeLiteralPath(s) {
|
|
@@ -107,10 +112,10 @@ export function exprOfWord(w) {
|
|
|
107
112
|
const emitPart = (p) => {
|
|
108
113
|
switch (p.kind) {
|
|
109
114
|
case 'Text':
|
|
110
|
-
out +=
|
|
115
|
+
out += escapeDq(p.text);
|
|
111
116
|
break;
|
|
112
117
|
case 'SingleQuoted':
|
|
113
|
-
out +=
|
|
118
|
+
out += escapeDq(p.text);
|
|
114
119
|
break;
|
|
115
120
|
case 'DoubleQuoted':
|
|
116
121
|
for (const q of p.parts)
|
|
@@ -164,11 +169,23 @@ export function translateCmdSub(cmdText) {
|
|
|
164
169
|
/* Simple command translation */
|
|
165
170
|
/* ------------------------------------------------------------------ */
|
|
166
171
|
export function translateSimple(cmd, position, hasStdin) {
|
|
172
|
+
// assignment-only segment (`X=1; cmd`): bash semantics are "set for the
|
|
173
|
+
// rest of the shell". Reuse the export code path — persist + env shadow —
|
|
174
|
+
// so empty values (`X=`) and `[[ -v X ]]` behave like bash (documented
|
|
175
|
+
// deviation: shell var vs exported var are indistinguishable here).
|
|
176
|
+
if (cmd.name === null) {
|
|
177
|
+
const exportHandler = lookup('export');
|
|
178
|
+
const words = cmd.assignments.map((a) => [
|
|
179
|
+
{ kind: 'Text', text: a.name + '=' },
|
|
180
|
+
...a.value,
|
|
181
|
+
]);
|
|
182
|
+
return exportHandler ? exportHandler(words, { position, hasStdin }) : '';
|
|
183
|
+
}
|
|
167
184
|
const nameLit = literalOfWord(cmd.name);
|
|
168
185
|
let body;
|
|
169
186
|
if (nameLit !== null) {
|
|
170
187
|
const handler = lookup(nameLit);
|
|
171
|
-
if (handler) {
|
|
188
|
+
if (handler && !(nameLit === '[[' && !isUnquotedLiteral(cmd.name, '[['))) {
|
|
172
189
|
body = handler(cmd.args, { position, hasStdin });
|
|
173
190
|
}
|
|
174
191
|
else {
|
|
@@ -197,15 +214,258 @@ export function translateSimple(cmd, position, hasStdin) {
|
|
|
197
214
|
'if ($LASTEXITCODE -gt 0) { $script:fx_exit = $LASTEXITCODE } elseif ($LASTEXITCODE -lt 0) { $script:fx_exit = 1 }',
|
|
198
215
|
].join('\n');
|
|
199
216
|
}
|
|
200
|
-
// `VAR=value cmd
|
|
217
|
+
// `VAR=value cmd` is command-scoped. Values are captured in the
|
|
218
|
+
// current environment, then applied, then restored — including when
|
|
219
|
+
// the command throws — so they never leak into later list segments
|
|
220
|
+
// or the persisted MCP session. `VAR=x export VAR` keeps VAR (bash).
|
|
201
221
|
if (cmd.assignments.length > 0) {
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
222
|
+
const persistNames = new Set();
|
|
223
|
+
const persistWords = [];
|
|
224
|
+
if (nameLit === 'export') {
|
|
225
|
+
for (const w of cmd.args) {
|
|
226
|
+
const lit = literalExportName(w);
|
|
227
|
+
if (lit === '')
|
|
228
|
+
continue; // flag
|
|
229
|
+
if (lit)
|
|
230
|
+
persistNames.add(lit);
|
|
231
|
+
else
|
|
232
|
+
persistWords.push(w);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
body = wrapTempEnv(cmd.assignments, body, { persistNames, persistWords });
|
|
206
236
|
}
|
|
207
237
|
return body;
|
|
208
238
|
}
|
|
239
|
+
/** Literal `NAME` / `NAME=...` from an export argument. `''` = flag. */
|
|
240
|
+
function literalExportName(w) {
|
|
241
|
+
if (w.length === 0)
|
|
242
|
+
return null;
|
|
243
|
+
let s = '';
|
|
244
|
+
for (const p of w) {
|
|
245
|
+
if (p.kind !== 'Text' && p.kind !== 'SingleQuoted')
|
|
246
|
+
return null;
|
|
247
|
+
const eq = p.text.indexOf('=');
|
|
248
|
+
if (eq >= 0) {
|
|
249
|
+
const name = s + p.text.slice(0, eq);
|
|
250
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? name : null;
|
|
251
|
+
}
|
|
252
|
+
s += p.text;
|
|
253
|
+
}
|
|
254
|
+
if (s.startsWith('-'))
|
|
255
|
+
return '';
|
|
256
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(s) ? s : null;
|
|
257
|
+
}
|
|
258
|
+
let tempEnvSeq = 0;
|
|
259
|
+
/** PS expr: encode a string so SETVALS records can stay newline-delimited. */
|
|
260
|
+
export function encodeSetValExpr(srcExpr) {
|
|
261
|
+
return ('([string](' +
|
|
262
|
+
srcExpr +
|
|
263
|
+
')).Replace([string][char]92, ([string][char]92 + [string][char]92)).Replace([string][char]13, ([string][char]92 + [char]114)).Replace([string][char]10, ([string][char]92 + [char]110))');
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Apply env assignments (and optional unsets) only for `body`, then restore.
|
|
267
|
+
* All assignment *values* are evaluated before any name is mutated.
|
|
268
|
+
* `persistWords` are evaluated after the prefix is applied (so
|
|
269
|
+
* `export "$NAME"` sees the current env) and those names are not restored.
|
|
270
|
+
*/
|
|
271
|
+
export function wrapTempEnv(sets, body, extra) {
|
|
272
|
+
const unsets = extra?.unsets ?? [];
|
|
273
|
+
const persistNames = extra?.persistNames ?? new Set();
|
|
274
|
+
const persistWords = extra?.persistWords;
|
|
275
|
+
const names = [];
|
|
276
|
+
const seen = new Set();
|
|
277
|
+
for (const s of sets) {
|
|
278
|
+
if (!seen.has(s.name)) {
|
|
279
|
+
seen.add(s.name);
|
|
280
|
+
names.push(s.name);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
for (const u of unsets) {
|
|
284
|
+
if (!seen.has(u)) {
|
|
285
|
+
seen.add(u);
|
|
286
|
+
names.push(u);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (names.length === 0)
|
|
290
|
+
return body;
|
|
291
|
+
const id = tempEnvSeq++;
|
|
292
|
+
const save = '$fx_es' + id;
|
|
293
|
+
const keep = persistWords && persistWords.length > 0 ? '$fx_ek' + id : '';
|
|
294
|
+
const lines = [
|
|
295
|
+
save + ' = @{}',
|
|
296
|
+
'$fx_sv0' + id + ' = $env:FAUXNIX_SETVARS',
|
|
297
|
+
'$fx_uv0' + id + ' = $env:FAUXNIX_UNSETVARS',
|
|
298
|
+
'$fx_xv0' + id + ' = $env:FAUXNIX_SETVALS',
|
|
299
|
+
];
|
|
300
|
+
for (const n of names) {
|
|
301
|
+
const p = psStr('Env:\\' + n);
|
|
302
|
+
lines.push(save +
|
|
303
|
+
'[' +
|
|
304
|
+
psStr(n) +
|
|
305
|
+
'] = $(if (Test-Path -LiteralPath ' +
|
|
306
|
+
p +
|
|
307
|
+
') { [string](Get-Item -LiteralPath ' +
|
|
308
|
+
p +
|
|
309
|
+
').Value } else { $null })');
|
|
310
|
+
}
|
|
311
|
+
const valVars = [];
|
|
312
|
+
for (let i = 0; i < sets.length; i++) {
|
|
313
|
+
const vn = '$fx_ev' + id + '_' + i;
|
|
314
|
+
valVars.push(vn);
|
|
315
|
+
lines.push(vn + ' = ' + exprOfWord(sets[i].value));
|
|
316
|
+
}
|
|
317
|
+
lines.push('try {');
|
|
318
|
+
for (const u of unsets) {
|
|
319
|
+
const uq = u.replace(/'/g, "''");
|
|
320
|
+
lines.push(' Remove-Item -LiteralPath ' + psStr('Env:\\' + u) + ' -ErrorAction SilentlyContinue');
|
|
321
|
+
// `env -u NAME` must hide NAME from fx-envget / fx-isset for the
|
|
322
|
+
// wrapped body. Removing Env:\NAME is not enough: an earlier
|
|
323
|
+
// `export NAME=x` still lives in SETVARS/SETVALS, and special
|
|
324
|
+
// names (PATH, HOME, …) have hardcoded fallbacks.
|
|
325
|
+
lines.push(" $env:FAUXNIX_SETVARS = (@($env:FAUXNIX_SETVARS -split ';' | Where-Object { $_ -ne '' -and $_ -cne '" +
|
|
326
|
+
uq +
|
|
327
|
+
"' }) -join ';')");
|
|
328
|
+
lines.push(" $env:FAUXNIX_UNSETVARS = ((@($env:FAUXNIX_UNSETVARS -split ';' | Where-Object { $_ -ne '' -and $_ -cne '" +
|
|
329
|
+
uq +
|
|
330
|
+
"' }) + '" +
|
|
331
|
+
uq +
|
|
332
|
+
"') -join ';')");
|
|
333
|
+
lines.push(' $fx_sm = @(); foreach ($fx_pair in @($env:FAUXNIX_SETVALS -split [string][char]10)) { $fx_eq = $fx_pair.IndexOf([char]61); if ($fx_eq -lt 1) { continue }; if ($fx_pair.Substring(0, $fx_eq) -cne \'' +
|
|
334
|
+
uq +
|
|
335
|
+
'\') { $fx_sm += $fx_pair } }; $env:FAUXNIX_SETVALS = ($fx_sm -join [string][char]10)');
|
|
336
|
+
}
|
|
337
|
+
for (let i = 0; i < sets.length; i++) {
|
|
338
|
+
const n = sets[i].name;
|
|
339
|
+
const nq = n.replace(/'/g, "''");
|
|
340
|
+
lines.push(' $env:' + n + ' = ' + valVars[i]);
|
|
341
|
+
lines.push(" $env:FAUXNIX_SETVARS = ((@($env:FAUXNIX_SETVARS -split ';' | Where-Object { $_ -ne '' -and $_ -cne '" +
|
|
342
|
+
nq +
|
|
343
|
+
"' }) + '" +
|
|
344
|
+
nq +
|
|
345
|
+
"') -join ';')");
|
|
346
|
+
lines.push(" $env:FAUXNIX_UNSETVARS = (@($env:FAUXNIX_UNSETVARS -split ';' | Where-Object { $_ -ne '' -and $_ -cne '" +
|
|
347
|
+
nq +
|
|
348
|
+
"' }) -join ';')");
|
|
349
|
+
lines.push(' $fx_sm = @(); foreach ($fx_pair in @($env:FAUXNIX_SETVALS -split [string][char]10)) { $fx_eq = $fx_pair.IndexOf([char]61); if ($fx_eq -lt 1) { continue }; if ($fx_pair.Substring(0, $fx_eq) -cne \'' +
|
|
350
|
+
nq +
|
|
351
|
+
'\') { $fx_sm += $fx_pair } }; $fx_sm += (\'' +
|
|
352
|
+
nq +
|
|
353
|
+
"' + [string][char]61 + " +
|
|
354
|
+
encodeSetValExpr(valVars[i]) +
|
|
355
|
+
'); $env:FAUXNIX_SETVALS = ($fx_sm -join [string][char]10)');
|
|
356
|
+
}
|
|
357
|
+
if (keep) {
|
|
358
|
+
lines.push(' ' + keep + ' = @{}');
|
|
359
|
+
for (const w of persistWords) {
|
|
360
|
+
const ev = '$fx_en' + id + '_' + lines.length;
|
|
361
|
+
lines.push(' ' + ev + ' = [string](' + exprOfWord(w) + ')');
|
|
362
|
+
lines.push(' if (' +
|
|
363
|
+
ev +
|
|
364
|
+
" -notmatch '^-') { $fx_nm = if (" +
|
|
365
|
+
ev +
|
|
366
|
+
".Contains([string][char]61)) { " +
|
|
367
|
+
ev +
|
|
368
|
+
'.Substring(0, ' +
|
|
369
|
+
ev +
|
|
370
|
+
".IndexOf([string][char]61)) } else { " +
|
|
371
|
+
ev +
|
|
372
|
+
" }; if ($fx_nm -match '^[A-Za-z_][A-Za-z0-9_]*$') { " +
|
|
373
|
+
keep +
|
|
374
|
+
'[$fx_nm] = $true } }');
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
for (const l of body.split('\n'))
|
|
378
|
+
lines.push(l ? ' ' + l : l);
|
|
379
|
+
lines.push('} finally {');
|
|
380
|
+
lines.push(' $env:FAUXNIX_SETVARS = $fx_sv0' + id);
|
|
381
|
+
lines.push(' $env:FAUXNIX_UNSETVARS = $fx_uv0' + id);
|
|
382
|
+
lines.push(' $env:FAUXNIX_SETVALS = $fx_xv0' + id);
|
|
383
|
+
for (const n of names) {
|
|
384
|
+
if (persistNames.has(n))
|
|
385
|
+
continue;
|
|
386
|
+
const p = psStr('Env:\\' + n);
|
|
387
|
+
if (keep) {
|
|
388
|
+
lines.push(' $fx_skip = if (' + keep + '[' + psStr(n) + ']) { $true } else { $false }');
|
|
389
|
+
lines.push(' if (-not $fx_skip) { if ($null -eq ' +
|
|
390
|
+
save +
|
|
391
|
+
'[' +
|
|
392
|
+
psStr(n) +
|
|
393
|
+
']) { Remove-Item -LiteralPath ' +
|
|
394
|
+
p +
|
|
395
|
+
' -ErrorAction SilentlyContinue } else { Set-Item -LiteralPath ' +
|
|
396
|
+
p +
|
|
397
|
+
' -Value ' +
|
|
398
|
+
save +
|
|
399
|
+
'[' +
|
|
400
|
+
psStr(n) +
|
|
401
|
+
'] } }');
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
lines.push(' if ($null -eq ' +
|
|
405
|
+
save +
|
|
406
|
+
'[' +
|
|
407
|
+
psStr(n) +
|
|
408
|
+
']) { Remove-Item -LiteralPath ' +
|
|
409
|
+
p +
|
|
410
|
+
' -ErrorAction SilentlyContinue } else { Set-Item -LiteralPath ' +
|
|
411
|
+
p +
|
|
412
|
+
' -Value ' +
|
|
413
|
+
save +
|
|
414
|
+
'[' +
|
|
415
|
+
psStr(n) +
|
|
416
|
+
'] }');
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const assigned = new Set(sets.map((s) => s.name));
|
|
420
|
+
for (const n of persistNames) {
|
|
421
|
+
const nq = n.replace(/'/g, "''");
|
|
422
|
+
const ep = psStr('Env:\\' + n);
|
|
423
|
+
// Bare `export UNSET` only marks the name for export; it must stay
|
|
424
|
+
// unset (`[[ -v UNSET ]]` is false). Persist a record only when the
|
|
425
|
+
// prefix assigned the name (including empty) or it already exists.
|
|
426
|
+
const cond = assigned.has(n) ? '$true' : '(Test-Path -LiteralPath ' + ep + ')';
|
|
427
|
+
lines.push(' if (' + cond + ') {');
|
|
428
|
+
lines.push(" $env:FAUXNIX_SETVARS = ((@($env:FAUXNIX_SETVARS -split ';' | Where-Object { $_ -ne '' -and $_ -cne '" +
|
|
429
|
+
nq +
|
|
430
|
+
"' }) + '" +
|
|
431
|
+
nq +
|
|
432
|
+
"') -join ';')");
|
|
433
|
+
lines.push(" $env:FAUXNIX_UNSETVARS = (@($env:FAUXNIX_UNSETVARS -split ';' | Where-Object { $_ -ne '' -and $_ -cne '" +
|
|
434
|
+
nq +
|
|
435
|
+
"' }) -join ';')");
|
|
436
|
+
lines.push(' $fx_pv = $(if (Test-Path -LiteralPath ' +
|
|
437
|
+
ep +
|
|
438
|
+
') { [string](Get-Item -LiteralPath ' +
|
|
439
|
+
ep +
|
|
440
|
+
").Value } else { '' })");
|
|
441
|
+
lines.push(' $fx_sm = @(); foreach ($fx_pair in @($env:FAUXNIX_SETVALS -split [string][char]10)) { $fx_eq = $fx_pair.IndexOf([char]61); if ($fx_eq -lt 1) { continue }; if ($fx_pair.Substring(0, $fx_eq) -cne \'' +
|
|
442
|
+
nq +
|
|
443
|
+
'\') { $fx_sm += $fx_pair } }; $fx_sm += (\'' +
|
|
444
|
+
nq +
|
|
445
|
+
"' + [string][char]61 + " +
|
|
446
|
+
encodeSetValExpr('$fx_pv') +
|
|
447
|
+
'); $env:FAUXNIX_SETVALS = ($fx_sm -join [string][char]10)');
|
|
448
|
+
lines.push(' }');
|
|
449
|
+
}
|
|
450
|
+
if (keep) {
|
|
451
|
+
const had = '@(' +
|
|
452
|
+
[...assigned].map((n) => "'" + n.replace(/'/g, "''") + "'").join(',') +
|
|
453
|
+
')';
|
|
454
|
+
lines.push(' foreach ($fx_pn in @(' + keep + '.Keys)) {');
|
|
455
|
+
lines.push(' if (-not ((Test-Path -LiteralPath (\'Env:\\\' + $fx_pn)) -or (' +
|
|
456
|
+
had +
|
|
457
|
+
' -ccontains $fx_pn))) { continue }');
|
|
458
|
+
lines.push(" $env:FAUXNIX_SETVARS = ((@($env:FAUXNIX_SETVARS -split ';' | Where-Object { $_ -ne '' -and $_ -cne $fx_pn }) + $fx_pn) -join ';')");
|
|
459
|
+
lines.push(" $env:FAUXNIX_UNSETVARS = (@($env:FAUXNIX_UNSETVARS -split ';' | Where-Object { $_ -ne '' -and $_ -cne $fx_pn }) -join ';')");
|
|
460
|
+
lines.push(" $fx_pv = $(if (Test-Path -LiteralPath ('Env:\\' + $fx_pn)) { [string](Get-Item -LiteralPath ('Env:\\' + $fx_pn)).Value } else { '' })");
|
|
461
|
+
lines.push(' $fx_sm = @(); foreach ($fx_pair in @($env:FAUXNIX_SETVALS -split [string][char]10)) { $fx_eq = $fx_pair.IndexOf([char]61); if ($fx_eq -lt 1) { continue }; if ($fx_pair.Substring(0, $fx_eq) -cne $fx_pn) { $fx_sm += $fx_pair } }; $fx_sm += ($fx_pn + [string][char]61 + ' +
|
|
462
|
+
encodeSetValExpr('$fx_pv') +
|
|
463
|
+
'); $env:FAUXNIX_SETVALS = ($fx_sm -join [string][char]10)');
|
|
464
|
+
lines.push(' }');
|
|
465
|
+
}
|
|
466
|
+
lines.push('}');
|
|
467
|
+
return lines.join('\n');
|
|
468
|
+
}
|
|
209
469
|
/** Unique suffix for generated stage functions (nested pipelines included). */
|
|
210
470
|
let stageSeq = 0;
|
|
211
471
|
/**
|
package/package.json
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "fauxnix-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Fauxnix — run Linux-style commands on Windows via deterministic PowerShell translation. No VM, no WSL. MCP server + CLI for AI agents.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"fauxnix": "dist/index.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"dist",
|
|
11
|
-
"README.md",
|
|
12
|
-
"LICENSE"
|
|
13
|
-
],
|
|
14
|
-
"scripts": {
|
|
15
|
-
"build": "tsc",
|
|
16
|
-
"test": "vitest run",
|
|
17
|
-
"test:watch": "vitest",
|
|
18
|
-
"typecheck": "tsc --noEmit",
|
|
19
|
-
"dev": "tsx src/index.ts"
|
|
20
|
-
},
|
|
21
|
-
"keywords": [
|
|
22
|
-
"bash",
|
|
23
|
-
"powershell",
|
|
24
|
-
"translate",
|
|
25
|
-
"translator",
|
|
26
|
-
"linux",
|
|
27
|
-
"windows",
|
|
28
|
-
"mcp",
|
|
29
|
-
"mcp-server",
|
|
30
|
-
"ai-agent",
|
|
31
|
-
"claude-code",
|
|
32
|
-
"codex",
|
|
33
|
-
"opencode",
|
|
34
|
-
"kimi-code",
|
|
35
|
-
"shell",
|
|
36
|
-
"gbk",
|
|
37
|
-
"utf8"
|
|
38
|
-
],
|
|
39
|
-
"engines": {
|
|
40
|
-
"node": ">=18"
|
|
41
|
-
},
|
|
42
|
-
"license": "MIT",
|
|
43
|
-
"repository": {
|
|
44
|
-
"type": "git",
|
|
45
|
-
"url": "git+https://github.com/20000419/fauxnix.git"
|
|
46
|
-
},
|
|
47
|
-
"dependencies": {
|
|
48
|
-
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
49
|
-
"iconv-lite": "^0.6.3",
|
|
50
|
-
"zod": "^3.24.0"
|
|
51
|
-
},
|
|
52
|
-
"devDependencies": {
|
|
53
|
-
"@types/node": "^20.14.0",
|
|
54
|
-
"tsx": "^4.19.0",
|
|
55
|
-
"typescript": "^5.5.0",
|
|
56
|
-
"vitest": "^2.1.0"
|
|
57
|
-
}
|
|
58
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "fauxnix-cli",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Fauxnix — run Linux-style commands on Windows via deterministic PowerShell translation. No VM, no WSL. MCP server + CLI for AI agents.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"fauxnix": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:watch": "vitest",
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"dev": "tsx src/index.ts"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"bash",
|
|
23
|
+
"powershell",
|
|
24
|
+
"translate",
|
|
25
|
+
"translator",
|
|
26
|
+
"linux",
|
|
27
|
+
"windows",
|
|
28
|
+
"mcp",
|
|
29
|
+
"mcp-server",
|
|
30
|
+
"ai-agent",
|
|
31
|
+
"claude-code",
|
|
32
|
+
"codex",
|
|
33
|
+
"opencode",
|
|
34
|
+
"kimi-code",
|
|
35
|
+
"shell",
|
|
36
|
+
"gbk",
|
|
37
|
+
"utf8"
|
|
38
|
+
],
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
},
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/20000419/fauxnix.git"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
49
|
+
"iconv-lite": "^0.6.3",
|
|
50
|
+
"zod": "^3.24.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^20.14.0",
|
|
54
|
+
"tsx": "^4.19.0",
|
|
55
|
+
"typescript": "^5.5.0",
|
|
56
|
+
"vitest": "^2.1.0"
|
|
57
|
+
}
|
|
58
|
+
}
|