dsh-win-multi-bash 0.1.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.
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Internal sandbox-result classification helpers.
3
+ *
4
+ * @module @deepseek-ai/dsh-bash-sandbox/helpers
5
+ */
6
+ import { accessSync, constants, statSync } from 'node:fs';
7
+ /** Node-local spawn codes proven to identify executable resolution or permission failure. */
8
+ const EXECUTABLE_SPAWN_CODES = new Set(['EACCES', 'ENOENT']);
9
+ /** Whether the caller-owned spawn cwd can be entered. */
10
+ function isUsableWorkdir(path) {
11
+ try {
12
+ if (!statSync(path).isDirectory())
13
+ return false;
14
+ accessSync(path, constants.X_OK);
15
+ return true;
16
+ }
17
+ catch {
18
+ return false;
19
+ }
20
+ }
21
+ /**
22
+ * Attribute only Node ENOENT/EACCES failures whose error path equals argv[0]
23
+ * after independently ruling out the caller-owned cwd. A supplied error path
24
+ * must exactly identify the runner; without one, the syscall must. With a
25
+ * usable cwd, these codes describe resolution or execute permission for that
26
+ * argv[0] or its shebang interpreter.
27
+ * The workdir is checked at classification time, not atomically with spawn;
28
+ * concurrent path replacement may change attribution but cannot permit an
29
+ * unconfined execution.
30
+ * @param error - the original spawn rejection.
31
+ * @param runnerProgram - provider argv[0], the executable that establishes confinement.
32
+ * @param workdir - the caller-owned spawn cwd, checked independently for usability.
33
+ * @returns whether the rejection has executable-specific runner evidence.
34
+ */
35
+ export function isRunnerSpawnFailure(error, runnerProgram, workdir) {
36
+ if (runnerProgram === undefined || !isUsableWorkdir(workdir))
37
+ return false;
38
+ if (typeof error !== 'object' || error === null)
39
+ return false;
40
+ const { code, path, syscall } = error;
41
+ if (typeof code !== 'string' || !EXECUTABLE_SPAWN_CODES.has(code))
42
+ return false;
43
+ if (typeof syscall !== 'string')
44
+ return false;
45
+ const exactSyscall = `spawn ${runnerProgram}`;
46
+ if (path === undefined)
47
+ return syscall === exactSyscall;
48
+ if (typeof path !== 'string' || path.length === 0 || path !== runnerProgram)
49
+ return false;
50
+ return syscall === 'spawn' || syscall === exactSyscall;
51
+ }
52
+ /**
53
+ * Classify a failed run against the selected backend's denial dialect.
54
+ * @param result - settled foreground run.
55
+ * @param signatures - case-insensitive denial substrings from the active wrap.
56
+ * @returns whether the failed run matches that denial dialect.
57
+ */
58
+ export function classifyDenial(result, signatures) {
59
+ return matchesSignature(result.exitCode, result.stderr.text, signatures);
60
+ }
61
+ /**
62
+ * Classify one settled process against the selected backend's structured
63
+ * runner-failure rules. Each rule requires a nonzero exit, its optional
64
+ * exit-code gate, and a fatal signature on one stderr line after exact
65
+ * informational lines are excluded. A rule may set `anchored: true` to match
66
+ * the signature only at the line start (bwrap's own diagnostics always begin
67
+ * with its prefix; a wrapped command echoing the same text mid-line is then
68
+ * not misread as a runner failure).
69
+ * @param exitCode - process exit code; null means signal termination.
70
+ * @param stderr - collected stderr text, left unchanged.
71
+ * @param rules - structured runner-failure rules from the active wrap.
72
+ * @returns the first matching fatal line, or undefined when evidence is insufficient.
73
+ */
74
+ export function classifyRunnerFailure(exitCode, stderr, rules) {
75
+ if (exitCode === null || exitCode === 0)
76
+ return undefined;
77
+ const lines = stderr.split(/\r?\n/);
78
+ for (const rule of rules) {
79
+ if (rule.allowedExitCodes !== undefined && !rule.allowedExitCodes.includes(exitCode))
80
+ continue;
81
+ const informationalLines = new Set((rule.informationalLines ?? []).map(line => line.toLowerCase()));
82
+ // An empty or whitespace-only substring is not meaningful runner evidence.
83
+ // Ignore it while keeping any valid signatures beside it active.
84
+ const fatalSignatures = rule.fatalSignatures
85
+ .filter(signature => signature.trim().length > 0)
86
+ .map(signature => signature.toLowerCase());
87
+ const anchored = rule.anchored === true;
88
+ for (const line of lines) {
89
+ const lowered = line.toLowerCase();
90
+ if (informationalLines.has(lowered))
91
+ continue;
92
+ if (fatalSignatures.some(signature => anchored ? lowered.startsWith(signature) : lowered.includes(signature)))
93
+ return { detail: line };
94
+ }
95
+ }
96
+ return undefined;
97
+ }
98
+ /**
99
+ * Match a non-zero exit against case-insensitive stderr signatures.
100
+ * @param exitCode - process exit code; null means signal termination.
101
+ * @param stderr - collected stderr text.
102
+ * @param signatures - substrings identifying the selected backend's dialect.
103
+ * @returns whether this is a non-zero exit whose stderr matches a signature.
104
+ */
105
+ export function matchesSignature(exitCode, stderr, signatures) {
106
+ if (exitCode === null || exitCode === 0)
107
+ return false;
108
+ const lowered = stderr.toLowerCase();
109
+ return signatures.some(signature => lowered.includes(signature.toLowerCase()));
110
+ }
111
+ //# sourceMappingURL=helpers.js.map
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "dsh-win-multi-bash",
3
+ "version": "0.1.0",
4
+ "author": "Dinosaur_MC",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/Dinosaur-MC/dsh-win-multi-bash.git"
8
+ },
9
+ "description": "Windows multi-bash plugin for dsh: git_bash / wsl_bash model tools plus a shell-select executor routing the single ctx.shell seat across Git Bash, WSL and pwsh (pwsh stays the default).",
10
+ "keywords": [
11
+ "dsh-plugin",
12
+ "windows",
13
+ "bash",
14
+ "git-bash",
15
+ "wsl",
16
+ "shell-select"
17
+ ],
18
+ "license": "MIT",
19
+ "engines": {
20
+ "node": "^22.19.0 || >=24.0.0"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "type": "module",
26
+ "main": "lib/index.js",
27
+ "exports": {
28
+ ".": "./lib/index.js",
29
+ "./shell-select": "./lib/shell-select/index.js",
30
+ "./tool-git-bash": "./lib/tool-bash/types/git-bash.js",
31
+ "./tool-wsl-bash": "./lib/tool-bash/types/wsl-bash.js",
32
+ "./package.json": "./package.json"
33
+ },
34
+ "files": [
35
+ "lib/**/*.js",
36
+ "cordis.patch.yml",
37
+ "README.md",
38
+ "README.zh.md",
39
+ "README.i18n.yaml",
40
+ "LICENSE",
41
+ "THIRD_PARTY_NOTICES"
42
+ ],
43
+ "scripts": {
44
+ "test": "powershell -NoProfile -ExecutionPolicy Bypass -File smoke/run.ps1"
45
+ },
46
+ "dsh": {
47
+ "bundle": {
48
+ "patch": "./cordis.patch.yml"
49
+ }
50
+ },
51
+ "peerDependencies": {
52
+ "@deepseek-ai/cordis": ">=4.0.1",
53
+ "@deepseek-ai/dsh-bash-local": ">=0.1.0-rc.7",
54
+ "@deepseek-ai/dsh-llm": ">=0.1.0-rc.7",
55
+ "@deepseek-ai/dsh-pwsh-local": ">=0.1.0-rc.7",
56
+ "@deepseek-ai/dsh-pwsh-sandbox": ">=0.1.0-rc.7",
57
+ "@deepseek-ai/dsh-sandbox": ">=0.1.0-rc.7",
58
+ "@deepseek-ai/dsh-settings": ">=0.1.0-rc.7",
59
+ "@deepseek-ai/dsh-shell": ">=0.1.0-rc.7",
60
+ "@deepseek-ai/dsh-tools": ">=0.1.0-rc.7",
61
+ "@deepseek-ai/schemastery": ">=3.18.0"
62
+ },
63
+ "peerDependenciesMeta": {
64
+ "@deepseek-ai/cordis": {
65
+ "optional": true
66
+ },
67
+ "@deepseek-ai/dsh-bash-local": {
68
+ "optional": true
69
+ },
70
+ "@deepseek-ai/dsh-llm": {
71
+ "optional": true
72
+ },
73
+ "@deepseek-ai/dsh-pwsh-local": {
74
+ "optional": true
75
+ },
76
+ "@deepseek-ai/dsh-pwsh-sandbox": {
77
+ "optional": true
78
+ },
79
+ "@deepseek-ai/dsh-sandbox": {
80
+ "optional": true
81
+ },
82
+ "@deepseek-ai/dsh-settings": {
83
+ "optional": true
84
+ },
85
+ "@deepseek-ai/dsh-shell": {
86
+ "optional": true
87
+ },
88
+ "@deepseek-ai/dsh-tools": {
89
+ "optional": true
90
+ },
91
+ "@deepseek-ai/schemastery": {
92
+ "optional": true
93
+ }
94
+ }
95
+ }