@ozyman42/ozy-cli 0.4.8-linux-x64.0 → 0.4.8
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/bin/ozy +191 -0
- package/bin/ozy-signing-agent +191 -0
- package/bin/ozy-ssh-keygen +191 -0
- package/bin/ozy-virtual-security-key +191 -0
- package/dist/commands/git/hosts.js +18 -0
- package/dist/commands/git/hosts.js.map +1 -0
- package/dist/commands/git/index.js +8 -0
- package/dist/commands/git/index.js.map +1 -0
- package/dist/commands/git/setup.js +145 -0
- package/dist/commands/git/setup.js.map +1 -0
- package/dist/commands/index.js +13 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/npm/index.js +7 -0
- package/dist/commands/npm/index.js.map +1 -0
- package/dist/commands/npm/setup/git-remote.js +66 -0
- package/dist/commands/npm/setup/git-remote.js.map +1 -0
- package/dist/commands/npm/setup/index.js +168 -0
- package/dist/commands/npm/setup/index.js.map +1 -0
- package/dist/commands/npm/setup/npm-auth.js +62 -0
- package/dist/commands/npm/setup/npm-auth.js.map +1 -0
- package/dist/commands/npm/setup/package-json-validate.js +47 -0
- package/dist/commands/npm/setup/package-json-validate.js.map +1 -0
- package/dist/commands/npm/setup/package-registry.js +20 -0
- package/dist/commands/npm/setup/package-registry.js.map +1 -0
- package/dist/commands/npm/setup/setup.test.js +95 -0
- package/dist/commands/npm/setup/setup.test.js.map +1 -0
- package/dist/commands/npm/setup/trusted-publishing.js +84 -0
- package/dist/commands/npm/setup/trusted-publishing.js.map +1 -0
- package/dist/commands/npm/setup/workflow.js +19 -0
- package/dist/commands/npm/setup/workflow.js.map +1 -0
- package/dist/commands/ssh/get-sk-credential.js +104 -0
- package/dist/commands/ssh/get-sk-credential.js.map +1 -0
- package/dist/commands/ssh/index.js +7 -0
- package/dist/commands/ssh/index.js.map +1 -0
- package/dist/commands/upgrade.js +199 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/common/command.js +15 -0
- package/dist/common/command.js.map +1 -0
- package/dist/common/constants.js +25 -0
- package/dist/common/constants.js.map +1 -0
- package/dist/common/effective-modules-extensions.js +13 -0
- package/dist/common/effective-modules-extensions.js.map +1 -0
- package/dist/common/log.js +4 -0
- package/dist/common/log.js.map +1 -0
- package/dist/common/render-caller-tree.js +20 -0
- package/dist/common/render-caller-tree.js.map +1 -0
- package/dist/entrypoints/ozy-signing-agent.js +48 -0
- package/dist/entrypoints/ozy-signing-agent.js.map +1 -0
- package/dist/entrypoints/ozy-ssh-keygen.js +9 -0
- package/dist/entrypoints/ozy-ssh-keygen.js.map +1 -0
- package/dist/entrypoints/ozy-virtual-security-key.js +18 -0
- package/dist/entrypoints/ozy-virtual-security-key.js.map +1 -0
- package/dist/entrypoints/ozy.js +3 -0
- package/dist/entrypoints/ozy.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/thing.js +4 -0
- package/dist/internal/thing.js.map +1 -0
- package/dist/modules/cli/agent-client/impl.js +109 -0
- package/dist/modules/cli/agent-client/impl.js.map +1 -0
- package/dist/modules/cli/agent-client/interface.js +11 -0
- package/dist/modules/cli/agent-client/interface.js.map +1 -0
- package/dist/modules/cli/git/impl.js +124 -0
- package/dist/modules/cli/git/impl.js.map +1 -0
- package/dist/modules/cli/git/interface.js +3 -0
- package/dist/modules/cli/git/interface.js.map +1 -0
- package/dist/modules/cli/git/submodule/fix.js +257 -0
- package/dist/modules/cli/git/submodule/fix.js.map +1 -0
- package/dist/modules/cli/git/submodule/git-state.js +154 -0
- package/dist/modules/cli/git/submodule/git-state.js.map +1 -0
- package/dist/modules/cli/git/submodule/gitmodules.js +113 -0
- package/dist/modules/cli/git/submodule/gitmodules.js.map +1 -0
- package/dist/modules/cli/git/submodule/printer.js +232 -0
- package/dist/modules/cli/git/submodule/printer.js.map +1 -0
- package/dist/modules/cli/git/submodule/resolve-git-dir.js +16 -0
- package/dist/modules/cli/git/submodule/resolve-git-dir.js.map +1 -0
- package/dist/modules/cli/github/impl.js +137 -0
- package/dist/modules/cli/github/impl.js.map +1 -0
- package/dist/modules/cli/github/interface.js +2 -0
- package/dist/modules/cli/github/interface.js.map +1 -0
- package/dist/modules/cli/index.js +9 -0
- package/dist/modules/cli/index.js.map +1 -0
- package/dist/modules/common/crypto/impl.js +133 -0
- package/dist/modules/common/crypto/impl.js.map +1 -0
- package/dist/modules/common/crypto/interface.js +2 -0
- package/dist/modules/common/crypto/interface.js.map +1 -0
- package/dist/modules/common/index.js +10 -0
- package/dist/modules/common/index.js.map +1 -0
- package/dist/modules/common/kep-map-store/impl.js +42 -0
- package/dist/modules/common/kep-map-store/impl.js.map +1 -0
- package/dist/modules/common/kep-map-store/interface.js +2 -0
- package/dist/modules/common/kep-map-store/interface.js.map +1 -0
- package/dist/modules/common/os-platform/caller-info.js +132 -0
- package/dist/modules/common/os-platform/caller-info.js.map +1 -0
- package/dist/modules/common/os-platform/impl.js +95 -0
- package/dist/modules/common/os-platform/impl.js.map +1 -0
- package/dist/modules/common/os-platform/interface.js +2 -0
- package/dist/modules/common/os-platform/interface.js.map +1 -0
- package/dist/modules/common/os-platform/virtual-hid/index.js +12 -0
- package/dist/modules/common/os-platform/virtual-hid/index.js.map +1 -0
- package/dist/modules/common/os-platform/virtual-hid/linux.js +5 -0
- package/dist/modules/common/os-platform/virtual-hid/linux.js.map +1 -0
- package/dist/modules/common/os-platform/virtual-hid/mac.js +119 -0
- package/dist/modules/common/os-platform/virtual-hid/mac.js.map +1 -0
- package/dist/modules/common/os-platform/virtual-hid/windows.js +5 -0
- package/dist/modules/common/os-platform/virtual-hid/windows.js.map +1 -0
- package/dist/modules/common/ssh-config/impl.js +116 -0
- package/dist/modules/common/ssh-config/impl.js.map +1 -0
- package/dist/modules/common/ssh-config/interface.js +10 -0
- package/dist/modules/common/ssh-config/interface.js.map +1 -0
- package/dist/modules/ssh-agent/index.js +8 -0
- package/dist/modules/ssh-agent/index.js.map +1 -0
- package/dist/modules/ssh-agent/session/impl.js +265 -0
- package/dist/modules/ssh-agent/session/impl.js.map +1 -0
- package/dist/modules/ssh-agent/session/interface-rpc.js +20 -0
- package/dist/modules/ssh-agent/session/interface-rpc.js.map +1 -0
- package/dist/modules/ssh-agent/session/interface.js +32 -0
- package/dist/modules/ssh-agent/session/interface.js.map +1 -0
- package/dist/modules/ssh-agent/session/passkey-prf-page.js +204 -0
- package/dist/modules/ssh-agent/session/passkey-prf-page.js.map +1 -0
- package/dist/modules/ssh-agent/session/prf-flow.js +103 -0
- package/dist/modules/ssh-agent/session/prf-flow.js.map +1 -0
- package/dist/modules/ssh-agent/ssh-agent/impl.js +117 -0
- package/dist/modules/ssh-agent/ssh-agent/impl.js.map +1 -0
- package/dist/modules/ssh-agent/ssh-agent/interface.js +2 -0
- package/dist/modules/ssh-agent/ssh-agent/interface.js.map +1 -0
- package/dist/scripts/build.js +410 -0
- package/dist/scripts/build.js.map +1 -0
- package/dist/scripts/check-npm-version.js +8 -0
- package/dist/scripts/check-npm-version.js.map +1 -0
- package/dist/scripts/kill-existing-agent.js +21 -0
- package/dist/scripts/kill-existing-agent.js.map +1 -0
- package/dist/scripts/publish.js +46 -0
- package/dist/scripts/publish.js.map +1 -0
- package/package.json +52 -11
- package/src/commands/git/hosts.ts +20 -0
- package/src/commands/git/index.ts +9 -0
- package/src/commands/git/setup.ts +182 -0
- package/src/commands/index.ts +15 -0
- package/src/commands/npm/index.ts +8 -0
- package/src/commands/npm/setup/git-remote.ts +78 -0
- package/src/commands/npm/setup/index.ts +189 -0
- package/src/commands/npm/setup/npm-auth.ts +58 -0
- package/src/commands/npm/setup/package-json-validate.ts +68 -0
- package/src/commands/npm/setup/package-registry.ts +24 -0
- package/src/commands/npm/setup/publish-workflow.yml +35 -0
- package/src/commands/npm/setup/setup.test.ts +117 -0
- package/src/commands/npm/setup/trusted-publishing.ts +106 -0
- package/src/commands/npm/setup/workflow.ts +20 -0
- package/src/commands/ssh/get-sk-credential.ts +110 -0
- package/src/commands/ssh/index.ts +8 -0
- package/src/commands/upgrade.ts +220 -0
- package/src/common/command.ts +16 -0
- package/src/common/constants.ts +26 -0
- package/src/common/effective-modules-extensions.ts +24 -0
- package/src/common/log.ts +3 -0
- package/src/common/render-caller-tree.ts +22 -0
- package/src/entrypoints/ozy-signing-agent.ts +48 -0
- package/src/entrypoints/ozy-ssh-keygen.ts +10 -0
- package/src/entrypoints/ozy-virtual-security-key.ts +21 -0
- package/src/entrypoints/ozy.ts +2 -0
- package/src/index.ts +1 -0
- package/src/internal/thing.ts +3 -0
- package/src/modules/cli/agent-client/impl.ts +152 -0
- package/src/modules/cli/agent-client/interface.ts +17 -0
- package/src/modules/cli/git/impl.ts +133 -0
- package/src/modules/cli/git/interface.ts +23 -0
- package/src/modules/cli/git/submodule/fix.ts +298 -0
- package/src/modules/cli/git/submodule/git-state.ts +217 -0
- package/src/modules/cli/git/submodule/gitmodules.ts +126 -0
- package/src/modules/cli/git/submodule/printer.ts +256 -0
- package/src/modules/cli/git/submodule/resolve-git-dir.ts +14 -0
- package/src/modules/cli/github/impl.ts +169 -0
- package/src/modules/cli/github/interface.ts +29 -0
- package/src/modules/cli/index.ts +16 -0
- package/src/modules/common/crypto/impl.ts +173 -0
- package/src/modules/common/crypto/interface.ts +20 -0
- package/src/modules/common/index.ts +19 -0
- package/src/modules/common/kep-map-store/impl.ts +47 -0
- package/src/modules/common/kep-map-store/interface.ts +9 -0
- package/src/modules/common/os-platform/caller-info.ts +152 -0
- package/src/modules/common/os-platform/impl.ts +91 -0
- package/src/modules/common/os-platform/interface.ts +16 -0
- package/src/modules/common/os-platform/virtual-hid/index.ts +12 -0
- package/src/modules/common/os-platform/virtual-hid/linux.ts +7 -0
- package/src/modules/common/os-platform/virtual-hid/mac.ts +150 -0
- package/src/modules/common/os-platform/virtual-hid/windows.ts +7 -0
- package/src/modules/common/ssh-config/impl.ts +130 -0
- package/src/modules/common/ssh-config/interface.ts +33 -0
- package/src/modules/ssh-agent/index.ts +13 -0
- package/src/modules/ssh-agent/session/agent-notes.md +210 -0
- package/src/modules/ssh-agent/session/design.md +2 -0
- package/src/modules/ssh-agent/session/html-refactor.md +23 -0
- package/src/modules/ssh-agent/session/impl.ts +328 -0
- package/src/modules/ssh-agent/session/interface-rpc.ts +24 -0
- package/src/modules/ssh-agent/session/interface.ts +62 -0
- package/src/modules/ssh-agent/session/passkey-prf-page.ts +224 -0
- package/src/modules/ssh-agent/session/prf-flow.ts +152 -0
- package/src/modules/ssh-agent/ssh-agent/impl.ts +136 -0
- package/src/modules/ssh-agent/ssh-agent/interface.ts +9 -0
- package/src/scripts/build.ts +504 -0
- package/src/scripts/check-npm-version.ts +7 -0
- package/src/scripts/command-launcher.js.ejs +168 -0
- package/src/scripts/kill-existing-agent.ts +22 -0
- package/src/scripts/publish.ts +47 -0
- package/src/scripts/windows-bin-shims.md +1104 -0
- package/multi-call-binary +0 -0
|
@@ -0,0 +1,1104 @@
|
|
|
1
|
+
# Windows bin shim audit
|
|
2
|
+
|
|
3
|
+
This document describes what the package managers do when a package exposes
|
|
4
|
+
commands through `package.json#bin`, with focus on the Windows first-run repair
|
|
5
|
+
problem for `ozy-cli`.
|
|
6
|
+
|
|
7
|
+
The goal is mechanical: list the files created by each installer, the target
|
|
8
|
+
they point at, the code that creates them, and what still needs real Windows
|
|
9
|
+
fixture verification. Do not implement a shim-replacement strategy from memory;
|
|
10
|
+
use this file as the checklist.
|
|
11
|
+
|
|
12
|
+
## Package shape under discussion
|
|
13
|
+
|
|
14
|
+
Base package manifest shape:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"name": "@scope/tool",
|
|
19
|
+
"bin": {
|
|
20
|
+
"ozy": "./bin/ozy",
|
|
21
|
+
"ozy-ssh-keygen": "./bin/ozy-ssh-keygen",
|
|
22
|
+
"ozy-signing-agent": "./bin/ozy-signing-agent"
|
|
23
|
+
},
|
|
24
|
+
"optionalDependencies": {
|
|
25
|
+
"@scope/tool-darwin-arm64": "1.2.3",
|
|
26
|
+
"@scope/tool-darwin-x64": "1.2.3",
|
|
27
|
+
"@scope/tool-linux-arm64": "1.2.3",
|
|
28
|
+
"@scope/tool-linux-x64": "1.2.3",
|
|
29
|
+
"@scope/tool-windows-x64": "1.2.3"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Base package file shape:
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
package/
|
|
38
|
+
package.json
|
|
39
|
+
bin/
|
|
40
|
+
ozy
|
|
41
|
+
ozy-ssh-keygen
|
|
42
|
+
ozy-signing-agent
|
|
43
|
+
src/
|
|
44
|
+
...
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Each file in `bin/` can be identical JS install/repair code. The useful property
|
|
48
|
+
is that each command has a distinct installer-owned bin target, which gives the
|
|
49
|
+
package manager a chance to create one shim set per command.
|
|
50
|
+
|
|
51
|
+
Platform package shape:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
package/
|
|
55
|
+
package.json
|
|
56
|
+
multi-call-binary # POSIX
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
package/
|
|
61
|
+
package.json
|
|
62
|
+
multi-call-binary.exe # Windows
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The platform package should not be relied on to expose final command links for
|
|
66
|
+
the base package. A package manager generally links the bins of the package
|
|
67
|
+
being installed as a direct dependency/global package. It does not make an
|
|
68
|
+
optional dependency's `bin` entries stand in for the parent package commands in
|
|
69
|
+
all local/global cases.
|
|
70
|
+
|
|
71
|
+
## Implemented package-local repair strategy
|
|
72
|
+
|
|
73
|
+
The base package owns every public command through package-owned files:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
package/
|
|
77
|
+
bin/
|
|
78
|
+
ozy
|
|
79
|
+
ozy-signing-agent
|
|
80
|
+
ozy-ssh-keygen
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Each file has identical JS launcher contents. The filename is the command name.
|
|
84
|
+
The generated base `package.json#bin` points each command at its matching file:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"bin": {
|
|
89
|
+
"ozy": "./bin/ozy",
|
|
90
|
+
"ozy-signing-agent": "./bin/ozy-signing-agent",
|
|
91
|
+
"ozy-ssh-keygen": "./bin/ozy-ssh-keygen"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The platform packages do not expose `bin` entries. They only contain the native
|
|
97
|
+
multi-call payload:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
multi-call-binary # POSIX
|
|
101
|
+
multi-call-binary.exe # Windows
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
At runtime, the JS launcher resolves the installed optional platform package and
|
|
105
|
+
materializes command-named executables next to the launchers.
|
|
106
|
+
|
|
107
|
+
POSIX first run:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
package/bin/ozy # JS launcher
|
|
111
|
+
package/bin/ozy-signing-agent # JS launcher
|
|
112
|
+
package/bin/ozy-ssh-keygen # JS launcher
|
|
113
|
+
platform/multi-call-binary # native payload
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The launcher materializes the first command path from the platform
|
|
117
|
+
`multi-call-binary`, using a hardlink first and falling back to one copy. It
|
|
118
|
+
then hardlinks every other command path to that first materialized command:
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
package/bin/ozy # native multi-call executable after repair
|
|
122
|
+
package/bin/ozy-signing-agent # hardlink to package/bin/ozy
|
|
123
|
+
package/bin/ozy-ssh-keygen # hardlink to package/bin/ozy
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Then it spawns `package/bin/ozy`. Later invocations through package-manager
|
|
127
|
+
`.bin` symlinks run the native command path directly.
|
|
128
|
+
|
|
129
|
+
Windows first run:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
package/bin/ozy # JS launcher; remains JS
|
|
133
|
+
package/bin/ozy-signing-agent # JS launcher; remains JS
|
|
134
|
+
package/bin/ozy-ssh-keygen # JS launcher; remains JS
|
|
135
|
+
platform/multi-call-binary.exe # native payload
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The launcher materializes all command `.exe` files together. It hardlinks the
|
|
139
|
+
first command `.exe` from `multi-call-binary.exe`, falling back to one copy when
|
|
140
|
+
that hardlink fails. It then hardlinks every other command `.exe` to the first
|
|
141
|
+
materialized `.exe`:
|
|
142
|
+
|
|
143
|
+
```text
|
|
144
|
+
package/bin/ozy # JS launcher
|
|
145
|
+
package/bin/ozy.exe # native multi-call executable
|
|
146
|
+
package/bin/ozy-signing-agent # JS launcher
|
|
147
|
+
package/bin/ozy-signing-agent.exe # hardlink to package/bin/ozy.exe
|
|
148
|
+
package/bin/ozy-ssh-keygen # JS launcher
|
|
149
|
+
package/bin/ozy-ssh-keygen.exe # hardlink to package/bin/ozy.exe
|
|
150
|
+
package/bin/installed.json # { "version": "<platform-package-version>" }
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Then it spawns `package/bin/ozy.exe`. Later invocations still pass through the
|
|
154
|
+
package-manager shim and JS launcher, but the final process image is the
|
|
155
|
+
command-specific `.exe`.
|
|
156
|
+
|
|
157
|
+
The Windows `installed.json` records only the platform package version. If it
|
|
158
|
+
does not match the currently resolved optional dependency version, or if any
|
|
159
|
+
command `.exe` is missing, the launcher recreates all command `.exe` files
|
|
160
|
+
together.
|
|
161
|
+
|
|
162
|
+
## Source links
|
|
163
|
+
|
|
164
|
+
### npm
|
|
165
|
+
|
|
166
|
+
- npm `bin-links` selects Windows shim generation here:
|
|
167
|
+
https://github.com/npm/bin-links/blob/main/lib/link-bins.js
|
|
168
|
+
- npm `bin-links` local/global target rules:
|
|
169
|
+
https://github.com/npm/bin-links/blob/main/lib/index.js
|
|
170
|
+
https://github.com/npm/bin-links/blob/main/lib/bin-target.js
|
|
171
|
+
- npm `bin-links` calls `cmd-shim` and tracks `name`, `name.cmd`, `name.ps1`:
|
|
172
|
+
https://github.com/npm/bin-links/blob/main/lib/shim-bin.js
|
|
173
|
+
- npm `cmd-shim` writes the actual `name`, `name.cmd`, and `name.ps1` files:
|
|
174
|
+
https://github.com/npm/cmd-shim/blob/main/lib/index.js
|
|
175
|
+
|
|
176
|
+
### pnpm
|
|
177
|
+
|
|
178
|
+
- pnpm `@pnpm/link-bins` gathers package bin entries and calls
|
|
179
|
+
`@zkochan/cmd-shim`:
|
|
180
|
+
https://github.com/pnpm/pnpm/blob/main/pkg-manager/link-bins/src/index.ts
|
|
181
|
+
- pnpm `@zkochan/cmd-shim` writes shell, CMD, and PowerShell shims:
|
|
182
|
+
https://github.com/pnpm/cmd-shim/blob/main/src/index.ts
|
|
183
|
+
|
|
184
|
+
### Bun package manager
|
|
185
|
+
|
|
186
|
+
- Bun chooses POSIX symlink vs Windows shim in `src/install/bin.rs`:
|
|
187
|
+
https://github.com/oven-sh/bun/blob/main/src/install/bin.rs#L936-L952
|
|
188
|
+
- Bun writes `name.bunx` metadata and `name.exe` PE shim:
|
|
189
|
+
https://github.com/oven-sh/bun/blob/main/src/install/bin.rs#L1138-L1254
|
|
190
|
+
- Bun's Windows shim metadata maps target extensions to launchers:
|
|
191
|
+
https://github.com/oven-sh/bun/blob/main/src/install/windows-shim/BinLinkingShim.rs#L174-L260
|
|
192
|
+
- Bun's PE shim implementation reads adjacent `.bunx` metadata:
|
|
193
|
+
https://github.com/oven-sh/bun/blob/main/src/install/windows-shim/bun_shim_impl.rs
|
|
194
|
+
|
|
195
|
+
### Yarn Classic
|
|
196
|
+
|
|
197
|
+
- Yarn Classic uses `@zkochan/cmd-shim` on Windows with
|
|
198
|
+
`createPwshFile: false`, and POSIX symlinks elsewhere:
|
|
199
|
+
https://github.com/yarnpkg/yarn/blob/master/src/package-linker.js#L22-L45
|
|
200
|
+
- Yarn Classic links package bins into `.bin` under the target install location:
|
|
201
|
+
https://github.com/yarnpkg/yarn/blob/master/src/package-linker.js#L74-L94
|
|
202
|
+
|
|
203
|
+
### Yarn Berry
|
|
204
|
+
|
|
205
|
+
- Yarn Berry `nodeLinker: node-modules` builds `.bin` link maps from package
|
|
206
|
+
manifests:
|
|
207
|
+
https://github.com/yarnpkg/berry/blob/master/packages/plugin-nm/sources/NodeModulesLinker.ts#L1014-L1072
|
|
208
|
+
- Yarn Berry `nodeLinker: node-modules` writes Windows shims with
|
|
209
|
+
`@zkochan/cmd-shim` and `createPwshFile: false`:
|
|
210
|
+
https://github.com/yarnpkg/berry/blob/master/packages/plugin-nm/sources/NodeModulesLinker.ts#L1399-L1436
|
|
211
|
+
- Yarn Berry script execution creates temporary wrappers through
|
|
212
|
+
`scriptUtils.makePathWrapper`:
|
|
213
|
+
https://github.com/yarnpkg/berry/blob/master/packages/yarnpkg-core/sources/scriptUtils.ts#L40-L50
|
|
214
|
+
- Yarn Berry script execution classifies native executables vs JS-like scripts:
|
|
215
|
+
https://github.com/yarnpkg/berry/blob/master/packages/yarnpkg-core/sources/scriptUtils.ts#L607-L632
|
|
216
|
+
- Yarn Berry script execution installs temporary accessible binaries:
|
|
217
|
+
https://github.com/yarnpkg/berry/blob/master/packages/yarnpkg-core/sources/scriptUtils.ts#L713-L739
|
|
218
|
+
- Yarn Berry `dlx` installs into a temporary project, then executes a workspace
|
|
219
|
+
accessible binary:
|
|
220
|
+
https://github.com/yarnpkg/berry/blob/master/packages/plugin-dlx/sources/commands/dlx.ts#L46-L132
|
|
221
|
+
|
|
222
|
+
## Generated files by package manager
|
|
223
|
+
|
|
224
|
+
The examples below assume this manifest:
|
|
225
|
+
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"name": "@scope/tool",
|
|
229
|
+
"bin": {
|
|
230
|
+
"ozy": "./bin/ozy"
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
and the package contains:
|
|
236
|
+
|
|
237
|
+
```text
|
|
238
|
+
package/
|
|
239
|
+
bin/
|
|
240
|
+
ozy
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### npm, local dependency install on Windows
|
|
244
|
+
|
|
245
|
+
Observed from source:
|
|
246
|
+
|
|
247
|
+
- `bin-links/lib/link-bins.js` selects `shim-bin.js` on Windows.
|
|
248
|
+
- `bin-links/lib/bin-target.js` targets `node_modules/.bin` for non-top
|
|
249
|
+
packages.
|
|
250
|
+
- `shim-bin.js` tracks and creates `ozy`, `ozy.cmd`, and `ozy.ps1`.
|
|
251
|
+
- `cmd-shim` writes all three files.
|
|
252
|
+
|
|
253
|
+
Expected directory structure:
|
|
254
|
+
|
|
255
|
+
```text
|
|
256
|
+
project/
|
|
257
|
+
node_modules/
|
|
258
|
+
.bin/
|
|
259
|
+
ozy
|
|
260
|
+
ozy.cmd
|
|
261
|
+
ozy.ps1
|
|
262
|
+
@scope/
|
|
263
|
+
tool/
|
|
264
|
+
package.json
|
|
265
|
+
bin/
|
|
266
|
+
ozy
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Important npm top-package detail: npm `bin-links` returns without linking bins
|
|
270
|
+
for a non-global top package. That is about the project root package itself, not
|
|
271
|
+
about a package installed into `node_modules` as a dependency.
|
|
272
|
+
|
|
273
|
+
### npm, global install on Windows
|
|
274
|
+
|
|
275
|
+
Observed from source:
|
|
276
|
+
|
|
277
|
+
- `bin-links/lib/index.js` states that global top packages on Windows get bins
|
|
278
|
+
installed in `{prefix}`.
|
|
279
|
+
- `bin-target.js` returns `getPrefix(path)` for Windows global top packages.
|
|
280
|
+
- The package payload lives under `{prefix}/node_modules`.
|
|
281
|
+
|
|
282
|
+
Expected directory structure:
|
|
283
|
+
|
|
284
|
+
```text
|
|
285
|
+
<npm-prefix>/
|
|
286
|
+
ozy
|
|
287
|
+
ozy.cmd
|
|
288
|
+
ozy.ps1
|
|
289
|
+
node_modules/
|
|
290
|
+
@scope/
|
|
291
|
+
tool/
|
|
292
|
+
package.json
|
|
293
|
+
bin/
|
|
294
|
+
ozy
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Fixture verification still needed: exact `<npm-prefix>` for current npm on
|
|
298
|
+
Windows with `npm prefix -g`, and shell precedence when `ozy.exe` is later added
|
|
299
|
+
beside these shims.
|
|
300
|
+
|
|
301
|
+
### pnpm, local dependency install on Windows
|
|
302
|
+
|
|
303
|
+
Observed from source:
|
|
304
|
+
|
|
305
|
+
- `@pnpm/link-bins` reads dependency manifests and links bins into the provided
|
|
306
|
+
bins directory.
|
|
307
|
+
- On Windows, before writing shims it removes an existing `ozy.exe` from the bin
|
|
308
|
+
directory.
|
|
309
|
+
- It calls `@zkochan/cmd-shim`.
|
|
310
|
+
- For normal packages, `makePowerShellShim` is true on Windows unless the
|
|
311
|
+
package being linked is named `pnpm`.
|
|
312
|
+
|
|
313
|
+
Expected directory structure:
|
|
314
|
+
|
|
315
|
+
```text
|
|
316
|
+
project/
|
|
317
|
+
node_modules/
|
|
318
|
+
.bin/
|
|
319
|
+
ozy
|
|
320
|
+
ozy.cmd
|
|
321
|
+
ozy.ps1
|
|
322
|
+
@scope/
|
|
323
|
+
tool -> .pnpm/@scope+tool@1.2.3/node_modules/@scope/tool
|
|
324
|
+
.pnpm/
|
|
325
|
+
@scope+tool@1.2.3/
|
|
326
|
+
node_modules/
|
|
327
|
+
@scope/
|
|
328
|
+
tool/
|
|
329
|
+
package.json
|
|
330
|
+
bin/
|
|
331
|
+
ozy
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
pnpm-specific warning: because `@pnpm/link-bins` removes `ozy.exe` when linking
|
|
335
|
+
on Windows, a self-repair strategy that creates `node_modules/.bin/ozy.exe`
|
|
336
|
+
could be undone by a later `pnpm install`.
|
|
337
|
+
|
|
338
|
+
### pnpm, global install on Windows
|
|
339
|
+
|
|
340
|
+
Expected shape, but must be verified with a fixture:
|
|
341
|
+
|
|
342
|
+
```text
|
|
343
|
+
<PNPM_HOME>/
|
|
344
|
+
ozy
|
|
345
|
+
ozy.cmd
|
|
346
|
+
ozy.ps1
|
|
347
|
+
<pnpm-global-store-or-virtual-store>/
|
|
348
|
+
...
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
The source inspected here proves the shim writer and local bin behavior. It does
|
|
352
|
+
not by itself prove the exact current global directory layout for every pnpm
|
|
353
|
+
configuration.
|
|
354
|
+
|
|
355
|
+
### Bun, local dependency install on Windows
|
|
356
|
+
|
|
357
|
+
Observed from source:
|
|
358
|
+
|
|
359
|
+
- Bun uses a POSIX symlink path on non-Windows and `create_windows_shim` on
|
|
360
|
+
Windows.
|
|
361
|
+
- Windows `create_windows_shim` writes adjacent `ozy.bunx` metadata and
|
|
362
|
+
`ozy.exe`.
|
|
363
|
+
- The `.bunx` metadata stores the relative target and launcher information.
|
|
364
|
+
- The PE `ozy.exe` reads its adjacent `.bunx` metadata.
|
|
365
|
+
|
|
366
|
+
Expected directory structure:
|
|
367
|
+
|
|
368
|
+
```text
|
|
369
|
+
project/
|
|
370
|
+
node_modules/
|
|
371
|
+
.bin/
|
|
372
|
+
ozy.bunx
|
|
373
|
+
ozy.exe
|
|
374
|
+
@scope/
|
|
375
|
+
tool/
|
|
376
|
+
package.json
|
|
377
|
+
bin/
|
|
378
|
+
ozy
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Bun does not use npm-style `ozy.cmd` and `ozy.ps1` shims for package bins in the
|
|
382
|
+
source inspected here.
|
|
383
|
+
|
|
384
|
+
### Bun, global install on Windows
|
|
385
|
+
|
|
386
|
+
Expected shape, but must be verified with a fixture:
|
|
387
|
+
|
|
388
|
+
```text
|
|
389
|
+
<bun-global-bin>/
|
|
390
|
+
ozy.bunx
|
|
391
|
+
ozy.exe
|
|
392
|
+
<bun-global-package-location>/
|
|
393
|
+
@scope/
|
|
394
|
+
tool/
|
|
395
|
+
package.json
|
|
396
|
+
bin/
|
|
397
|
+
ozy
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
The source proves the pair of files Bun writes for a Windows bin destination.
|
|
401
|
+
The exact global install root should be recorded from a real `bun add -g`
|
|
402
|
+
fixture.
|
|
403
|
+
|
|
404
|
+
### Yarn Classic, local dependency install on Windows
|
|
405
|
+
|
|
406
|
+
Observed from source:
|
|
407
|
+
|
|
408
|
+
- `src/package-linker.js` calls `@zkochan/cmd-shim` on Windows.
|
|
409
|
+
- It passes `createPwshFile: false`.
|
|
410
|
+
- It links into `targetBinLoc/.bin`.
|
|
411
|
+
|
|
412
|
+
Expected directory structure:
|
|
413
|
+
|
|
414
|
+
```text
|
|
415
|
+
project/
|
|
416
|
+
node_modules/
|
|
417
|
+
.bin/
|
|
418
|
+
ozy
|
|
419
|
+
ozy.cmd
|
|
420
|
+
@scope/
|
|
421
|
+
tool/
|
|
422
|
+
package.json
|
|
423
|
+
bin/
|
|
424
|
+
ozy
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
No `ozy.ps1` is expected from Yarn Classic because it disables PowerShell shim
|
|
428
|
+
creation.
|
|
429
|
+
|
|
430
|
+
### Yarn Classic, global install on Windows
|
|
431
|
+
|
|
432
|
+
Expected shape, but must be verified with a fixture:
|
|
433
|
+
|
|
434
|
+
```text
|
|
435
|
+
<yarn-classic-global-bin>/
|
|
436
|
+
ozy
|
|
437
|
+
ozy.cmd
|
|
438
|
+
<yarn-classic-global-package-location>/
|
|
439
|
+
...
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
The source proves shim type and local `.bin` behavior. The exact global path
|
|
443
|
+
depends on Yarn Classic configuration and needs Windows fixture capture.
|
|
444
|
+
|
|
445
|
+
### Yarn Berry with `nodeLinker: node-modules` on Windows
|
|
446
|
+
|
|
447
|
+
Observed from source:
|
|
448
|
+
|
|
449
|
+
- The node-modules linker reads manifests and builds a `binSymlinks` map.
|
|
450
|
+
- On Windows it calls `@zkochan/cmd-shim` with `createPwshFile: false`.
|
|
451
|
+
- It removes outdated `name` and `name.cmd`; it does not remove `name.ps1` in
|
|
452
|
+
the inspected code path because it does not create one.
|
|
453
|
+
|
|
454
|
+
Expected directory structure:
|
|
455
|
+
|
|
456
|
+
```text
|
|
457
|
+
project/
|
|
458
|
+
node_modules/
|
|
459
|
+
.bin/
|
|
460
|
+
ozy
|
|
461
|
+
ozy.cmd
|
|
462
|
+
@scope/
|
|
463
|
+
tool/
|
|
464
|
+
package.json
|
|
465
|
+
bin/
|
|
466
|
+
ozy
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
No persistent `ozy.ps1` is expected from this path.
|
|
470
|
+
|
|
471
|
+
### Yarn Berry with default PnP on Windows
|
|
472
|
+
|
|
473
|
+
Observed from source:
|
|
474
|
+
|
|
475
|
+
- PnP does not create the normal persistent `node_modules/.bin` tree.
|
|
476
|
+
- `scriptUtils.makePathWrapper` creates temporary wrappers in a Yarn-managed
|
|
477
|
+
`binFolder` for script execution.
|
|
478
|
+
- On Windows, `makePathWrapper` creates `name.cmd`.
|
|
479
|
+
- It also creates a no-extension POSIX shell wrapper.
|
|
480
|
+
- It does not create `name.ps1` in the inspected code.
|
|
481
|
+
- `installBinaries` decides whether to invoke a target through Node or directly
|
|
482
|
+
by calling `isNodeScript`.
|
|
483
|
+
|
|
484
|
+
Expected script-time shape:
|
|
485
|
+
|
|
486
|
+
```text
|
|
487
|
+
<temporary-yarn-bin-folder>/
|
|
488
|
+
ozy
|
|
489
|
+
ozy.cmd
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
There may be no stable file path for a self-repairing command to mutate when
|
|
493
|
+
the command is only available through `yarn run` in PnP mode. That is a major
|
|
494
|
+
constraint.
|
|
495
|
+
|
|
496
|
+
### Yarn Berry `dlx` on Windows
|
|
497
|
+
|
|
498
|
+
Observed from source:
|
|
499
|
+
|
|
500
|
+
- `plugin-dlx` creates a temporary project directory.
|
|
501
|
+
- It writes a temporary `package.json`, `yarn.lock`, and `.yarnrc.yml`.
|
|
502
|
+
- It runs `yarn add --fixed -- ...` in that temporary project.
|
|
503
|
+
- It loads the temporary project install state.
|
|
504
|
+
- It executes the command through `scriptUtils.executeWorkspaceAccessibleBinary`.
|
|
505
|
+
|
|
506
|
+
Expected shape:
|
|
507
|
+
|
|
508
|
+
```text
|
|
509
|
+
<temporary-base>/
|
|
510
|
+
dlx-<pid>/
|
|
511
|
+
package.json
|
|
512
|
+
yarn.lock
|
|
513
|
+
.yarnrc.yml
|
|
514
|
+
.pnp.cjs or node_modules/ # depends on effective Yarn config
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
Then command execution follows the same `scriptUtils` temporary wrapper behavior
|
|
518
|
+
described in the PnP section. `dlx` is not a durable global command install
|
|
519
|
+
surface; it is a temporary execution surface.
|
|
520
|
+
|
|
521
|
+
## Literal shim contents
|
|
522
|
+
|
|
523
|
+
These examples use this package layout:
|
|
524
|
+
|
|
525
|
+
```text
|
|
526
|
+
project/
|
|
527
|
+
node_modules/
|
|
528
|
+
.bin/
|
|
529
|
+
ozy
|
|
530
|
+
ozy.cmd
|
|
531
|
+
ozy.ps1
|
|
532
|
+
@scope/
|
|
533
|
+
tool/
|
|
534
|
+
bin/
|
|
535
|
+
ozy
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
and this bin target:
|
|
539
|
+
|
|
540
|
+
```javascript
|
|
541
|
+
#!/usr/bin/env node
|
|
542
|
+
console.log(process.argv)
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
The exact absolute paths vary by install location. The examples below keep the
|
|
546
|
+
same relative relationship the package managers generate.
|
|
547
|
+
|
|
548
|
+
Markdown renders the `.cmd` examples with normal line breaks. The generated
|
|
549
|
+
`.cmd` files are Windows command scripts and should be treated as CRLF-oriented
|
|
550
|
+
text files when comparing raw bytes.
|
|
551
|
+
|
|
552
|
+
### npm `cmd-shim`: `ozy`
|
|
553
|
+
|
|
554
|
+
npm's no-extension shim is a POSIX shell script for Git Bash/MSYS/Cygwin-style
|
|
555
|
+
execution.
|
|
556
|
+
|
|
557
|
+
```sh
|
|
558
|
+
#!/bin/sh
|
|
559
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
560
|
+
basedir_win="$basedir"
|
|
561
|
+
|
|
562
|
+
case `uname -a` in
|
|
563
|
+
*CYGWIN*|*MINGW*|*MSYS*)
|
|
564
|
+
if command -v cygpath > /dev/null 2>&1; then
|
|
565
|
+
basedir_win=`cygpath -w "$basedir"`
|
|
566
|
+
fi
|
|
567
|
+
;;
|
|
568
|
+
*WSL2*)
|
|
569
|
+
if command -v wslpath > /dev/null 2>&1; then
|
|
570
|
+
basedir_win="$(wslpath -w "$basedir" 2> /dev/null)"
|
|
571
|
+
if [ $? -ne 0 ] || [ -z "$basedir_win" ]; then
|
|
572
|
+
echo "Error: wslpath failed to convert path. WSL environment may be misconfigured." >&2
|
|
573
|
+
exit 1
|
|
574
|
+
fi
|
|
575
|
+
fi
|
|
576
|
+
;;
|
|
577
|
+
esac
|
|
578
|
+
|
|
579
|
+
PROG_EXE="$basedir/node.exe"
|
|
580
|
+
if ! [ -x "$PROG_EXE" ]; then
|
|
581
|
+
PROG_EXE="$basedir/node"
|
|
582
|
+
if ! [ -x "$PROG_EXE" ]; then
|
|
583
|
+
PROG_EXE=node
|
|
584
|
+
if ! [ -x "$PROG_EXE" ]; then
|
|
585
|
+
PROG_EXE=node.exe
|
|
586
|
+
fi
|
|
587
|
+
fi
|
|
588
|
+
fi
|
|
589
|
+
|
|
590
|
+
exec "$PROG_EXE" "$basedir_win/../@scope/tool/bin/ozy" "$@"
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
### npm `cmd-shim`: `ozy.cmd`
|
|
594
|
+
|
|
595
|
+
```bat
|
|
596
|
+
@ECHO off
|
|
597
|
+
GOTO start
|
|
598
|
+
:find_dp0
|
|
599
|
+
SET dp0=%~dp0
|
|
600
|
+
EXIT /b
|
|
601
|
+
:start
|
|
602
|
+
SETLOCAL
|
|
603
|
+
CALL :find_dp0
|
|
604
|
+
|
|
605
|
+
IF EXIST "%dp0%\node.exe" (
|
|
606
|
+
SET "_prog=%dp0%\node.exe"
|
|
607
|
+
) ELSE (
|
|
608
|
+
SET "_prog=node"
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & set PATHEXT=%PATHEXT:;.JS;=;% & "%_prog%" "%dp0%\..\@scope\tool\bin\ozy" %*
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
### npm `cmd-shim`: `ozy.ps1`
|
|
615
|
+
|
|
616
|
+
```powershell
|
|
617
|
+
#!/usr/bin/env pwsh
|
|
618
|
+
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
|
619
|
+
|
|
620
|
+
$exe=""
|
|
621
|
+
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
|
622
|
+
# Fix case when both the Windows and Linux builds of Node
|
|
623
|
+
# are installed in the same directory
|
|
624
|
+
$exe=".exe"
|
|
625
|
+
}
|
|
626
|
+
$ret=0
|
|
627
|
+
if (Test-Path "$basedir/node$exe") {
|
|
628
|
+
# Support pipeline input
|
|
629
|
+
if ($MyInvocation.ExpectingInput) {
|
|
630
|
+
$input | & "$basedir/node$exe" "$basedir/../@scope/tool/bin/ozy" $args
|
|
631
|
+
} else {
|
|
632
|
+
& "$basedir/node$exe" "$basedir/../@scope/tool/bin/ozy" $args
|
|
633
|
+
}
|
|
634
|
+
$ret=$LASTEXITCODE
|
|
635
|
+
} else {
|
|
636
|
+
# Support pipeline input
|
|
637
|
+
if ($MyInvocation.ExpectingInput) {
|
|
638
|
+
$input | & "node$exe" "$basedir/../@scope/tool/bin/ozy" $args
|
|
639
|
+
} else {
|
|
640
|
+
& "node$exe" "$basedir/../@scope/tool/bin/ozy" $args
|
|
641
|
+
}
|
|
642
|
+
$ret=$LASTEXITCODE
|
|
643
|
+
}
|
|
644
|
+
exit $ret
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### pnpm `@zkochan/cmd-shim`: `ozy`
|
|
648
|
+
|
|
649
|
+
pnpm uses `@zkochan/cmd-shim`. Yarn Classic and Yarn Berry node-modules use the
|
|
650
|
+
same shim generator, but pass `createPwshFile: false`.
|
|
651
|
+
|
|
652
|
+
```sh
|
|
653
|
+
#!/bin/sh
|
|
654
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
655
|
+
basedir_win="$basedir"
|
|
656
|
+
exe=""
|
|
657
|
+
msys=""
|
|
658
|
+
|
|
659
|
+
case `uname -a` in
|
|
660
|
+
*CYGWIN*|*MINGW*|*MSYS*)
|
|
661
|
+
if command -v cygpath > /dev/null 2>&1; then
|
|
662
|
+
basedir_win=`cygpath -w "$basedir"`
|
|
663
|
+
fi
|
|
664
|
+
exe=".exe"
|
|
665
|
+
msys="true"
|
|
666
|
+
;;
|
|
667
|
+
*WSL2*)
|
|
668
|
+
if command -v wslpath > /dev/null 2>&1; then
|
|
669
|
+
basedir_win="$(wslpath -w "$basedir" 2> /dev/null)"
|
|
670
|
+
if [ $? -ne 0 ] || [ -z "$basedir_win" ]; then
|
|
671
|
+
basedir_win="$basedir"
|
|
672
|
+
else
|
|
673
|
+
exe=".exe"
|
|
674
|
+
fi
|
|
675
|
+
fi
|
|
676
|
+
;;
|
|
677
|
+
esac
|
|
678
|
+
|
|
679
|
+
if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
|
|
680
|
+
exec "$basedir/node.exe" "$basedir_win/../@scope/tool/bin/ozy" "$@"
|
|
681
|
+
elif [ -x "$basedir/node" ]; then
|
|
682
|
+
exec "$basedir/node" "$basedir/../@scope/tool/bin/ozy" "$@"
|
|
683
|
+
elif command -v node >/dev/null 2>&1; then
|
|
684
|
+
exec node "$basedir/../@scope/tool/bin/ozy" "$@"
|
|
685
|
+
elif [ -n "$exe" ] && command -v node.exe >/dev/null 2>&1; then
|
|
686
|
+
exec node.exe "$basedir_win/../@scope/tool/bin/ozy" "$@"
|
|
687
|
+
else
|
|
688
|
+
exec node "$basedir/../@scope/tool/bin/ozy" "$@"
|
|
689
|
+
fi
|
|
690
|
+
# cmd-shim-target=<absolute path to project/node_modules/@scope/tool/bin/ozy>
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
### pnpm `@zkochan/cmd-shim`: `ozy.cmd`
|
|
694
|
+
|
|
695
|
+
```bat
|
|
696
|
+
@SETLOCAL
|
|
697
|
+
@IF EXIST "%~dp0\node.exe" (
|
|
698
|
+
"%~dp0\node.exe" "%~dp0\..\@scope\tool\bin\ozy" %*
|
|
699
|
+
) ELSE (
|
|
700
|
+
@SET PATHEXT=%PATHEXT:;.JS;=;%
|
|
701
|
+
node "%~dp0\..\@scope\tool\bin\ozy" %*
|
|
702
|
+
)
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
### pnpm `@zkochan/cmd-shim`: `ozy.ps1`
|
|
706
|
+
|
|
707
|
+
```powershell
|
|
708
|
+
#!/usr/bin/env pwsh
|
|
709
|
+
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
|
710
|
+
|
|
711
|
+
$exe=""
|
|
712
|
+
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
|
713
|
+
# Fix case when both the Windows and Linux builds of Node
|
|
714
|
+
# are installed in the same directory
|
|
715
|
+
$exe=".exe"
|
|
716
|
+
}
|
|
717
|
+
$ret=0
|
|
718
|
+
if (Test-Path "$basedir/node$exe") {
|
|
719
|
+
# Support pipeline input
|
|
720
|
+
if ($MyInvocation.ExpectingInput) {
|
|
721
|
+
$input | & "$basedir/node$exe" "$basedir/../@scope/tool/bin/ozy" $args
|
|
722
|
+
} else {
|
|
723
|
+
& "$basedir/node$exe" "$basedir/../@scope/tool/bin/ozy" $args
|
|
724
|
+
}
|
|
725
|
+
$ret=$LASTEXITCODE
|
|
726
|
+
} else {
|
|
727
|
+
# Support pipeline input
|
|
728
|
+
if ($MyInvocation.ExpectingInput) {
|
|
729
|
+
$input | & "node$exe" "$basedir/../@scope/tool/bin/ozy" $args
|
|
730
|
+
} else {
|
|
731
|
+
& "node$exe" "$basedir/../@scope/tool/bin/ozy" $args
|
|
732
|
+
}
|
|
733
|
+
$ret=$LASTEXITCODE
|
|
734
|
+
}
|
|
735
|
+
exit $ret
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
### Yarn Classic and Yarn Berry node-modules
|
|
739
|
+
|
|
740
|
+
Yarn Classic and Yarn Berry node-modules emit the `@zkochan/cmd-shim` `ozy` and
|
|
741
|
+
`ozy.cmd` contents shown above. They do not emit `ozy.ps1` in the inspected
|
|
742
|
+
source paths because both pass `createPwshFile: false`.
|
|
743
|
+
|
|
744
|
+
### Yarn Berry PnP/script wrappers: `ozy`
|
|
745
|
+
|
|
746
|
+
Yarn Berry PnP does not create persistent `node_modules/.bin` shims. For
|
|
747
|
+
script-time execution, `scriptUtils.makePathWrapper` creates a temporary
|
|
748
|
+
no-extension shell wrapper:
|
|
749
|
+
|
|
750
|
+
```sh
|
|
751
|
+
#!/bin/sh
|
|
752
|
+
exec "<argv0>" '<arg0>' '<arg1>' "$@"
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
For a JS package binary, Yarn passes Node as `argv0` and the package binary path
|
|
756
|
+
as an argument, so the shape is:
|
|
757
|
+
|
|
758
|
+
```sh
|
|
759
|
+
#!/bin/sh
|
|
760
|
+
exec "<path-to-node>" '<path-to-package-bin-ozy>' "$@"
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
For a native binary, Yarn passes the binary path as `argv0` with no package-bin
|
|
764
|
+
argument:
|
|
765
|
+
|
|
766
|
+
```sh
|
|
767
|
+
#!/bin/sh
|
|
768
|
+
exec "<path-to-native-binary>" "$@"
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
### Yarn Berry PnP/script wrappers: `ozy.cmd`
|
|
772
|
+
|
|
773
|
+
Yarn Berry's temporary CMD wrapper is one line:
|
|
774
|
+
|
|
775
|
+
```bat
|
|
776
|
+
@goto #_undefined_# 2>NUL || @title %COMSPEC% & @setlocal & @"<argv0>" "<arg0>" "<arg1>" %*
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
For a JS package binary:
|
|
780
|
+
|
|
781
|
+
```bat
|
|
782
|
+
@goto #_undefined_# 2>NUL || @title %COMSPEC% & @setlocal & @"<path-to-node>" "<path-to-package-bin-ozy>" %*
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
For a native binary:
|
|
786
|
+
|
|
787
|
+
```bat
|
|
788
|
+
@goto #_undefined_# 2>NUL || @title %COMSPEC% & @setlocal & @"<path-to-native-binary>" %*
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
Yarn Berry's `makePathWrapper` does not create a `.ps1` file.
|
|
792
|
+
|
|
793
|
+
### Bun package manager: `ozy.exe`
|
|
794
|
+
|
|
795
|
+
Bun's Windows command shim is not a text shim. It writes:
|
|
796
|
+
|
|
797
|
+
```text
|
|
798
|
+
ozy.exe # copied PE executable bytes from bun_shim_impl.exe
|
|
799
|
+
ozy.bunx # target-specific binary metadata read by ozy.exe
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
`ozy.exe` is target-independent PE content embedded in Bun. Its file starts with
|
|
803
|
+
the normal DOS/PE magic bytes:
|
|
804
|
+
|
|
805
|
+
```text
|
|
806
|
+
4d 5a ... # "MZ"
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
The target-specific content lives in `ozy.bunx`.
|
|
810
|
+
|
|
811
|
+
### Bun package manager: `ozy.bunx`
|
|
812
|
+
|
|
813
|
+
The `.bunx` file is binary metadata, not text. Bun's source documents and
|
|
814
|
+
encodes it as:
|
|
815
|
+
|
|
816
|
+
```text
|
|
817
|
+
[UTF-16LE bin_path][u16 quote][u16 zero](shebang?)[flags:u16]
|
|
818
|
+
|
|
819
|
+
if shebang:
|
|
820
|
+
[UTF-16LE launcher][u16 space][u32 bin_path_byte_len][u32 launcher_plus_space_byte_len]
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
The flags are a little-endian `u16` bitfield:
|
|
824
|
+
|
|
825
|
+
```text
|
|
826
|
+
bit 0: launcher is node or bun
|
|
827
|
+
bit 1: launcher is node
|
|
828
|
+
bit 2: has shebang
|
|
829
|
+
bits 3..15: version tag, currently 5478
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
For `node_modules/.bin/ozy` pointing at `node_modules/@scope/tool/bin/ozy`, Bun
|
|
833
|
+
stores the target path relative to `node_modules`, after stripping the leading
|
|
834
|
+
`..\` from the `.bin` relative path:
|
|
835
|
+
|
|
836
|
+
```text
|
|
837
|
+
@scope\tool\bin\ozy
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
For a target whose first line is `#!/usr/bin/env node`, the concrete `.bunx`
|
|
841
|
+
bytes are:
|
|
842
|
+
|
|
843
|
+
```text
|
|
844
|
+
00000000 40 00 73 00 63 00 6f 00 70 00 65 00 5c 00 74 00 @.s.c.o.p.e.\.t.
|
|
845
|
+
00000010 6f 00 6f 00 6c 00 5c 00 62 00 69 00 6e 00 5c 00 o.o.l.\.b.i.n.\.
|
|
846
|
+
00000020 6f 00 7a 00 79 00 22 00 00 00 6e 00 6f 00 64 00 o.z.y."...n.o.d.
|
|
847
|
+
00000030 65 00 20 00 26 00 00 00 0a 00 00 00 37 ab e. .&.......7.
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
Decoded:
|
|
851
|
+
|
|
852
|
+
```text
|
|
853
|
+
bin_path: @scope\tool\bin\ozy
|
|
854
|
+
quote terminator: u16 0x0022
|
|
855
|
+
nul terminator: u16 0x0000
|
|
856
|
+
launcher: node
|
|
857
|
+
space: u16 0x0020
|
|
858
|
+
bin_path_byte_len: 0x00000026
|
|
859
|
+
launcher_plus_space_byte_len: 0x0000000a
|
|
860
|
+
flags: 0xab37
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
`0xab37` means:
|
|
864
|
+
|
|
865
|
+
```text
|
|
866
|
+
version tag: 5478
|
|
867
|
+
has shebang: true
|
|
868
|
+
launcher is node: true
|
|
869
|
+
launcher is node or bun: true
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
## Target invocation rules
|
|
873
|
+
|
|
874
|
+
### npm `cmd-shim`
|
|
875
|
+
|
|
876
|
+
For a Windows bin target, npm's `cmd-shim`:
|
|
877
|
+
|
|
878
|
+
- Reads the target file.
|
|
879
|
+
- Parses a shebang from the first line when one exists.
|
|
880
|
+
- If a shebang exists, the generated shim runs that program and passes the
|
|
881
|
+
target path as an argument.
|
|
882
|
+
- If no shebang exists, the generated shim calls the target path directly.
|
|
883
|
+
- Writes `name`, `name.cmd`, and `name.ps1`.
|
|
884
|
+
|
|
885
|
+
Implication: if `./bin/ozy` starts with `#!/usr/bin/env node`, npm's `.cmd` and
|
|
886
|
+
`.ps1` shims invoke Node and run `bin/ozy`; they do not execute `bin/ozy` as a
|
|
887
|
+
PowerShell file just because a `.ps1` shim exists.
|
|
888
|
+
|
|
889
|
+
### pnpm and Yarn `@zkochan/cmd-shim`
|
|
890
|
+
|
|
891
|
+
For a Windows bin target, `@zkochan/cmd-shim`:
|
|
892
|
+
|
|
893
|
+
- Creates a no-extension shell shim.
|
|
894
|
+
- Creates `name.cmd` when `createCmdFile` is enabled.
|
|
895
|
+
- Creates `name.ps1` only when `createPwshFile` is enabled.
|
|
896
|
+
- Uses shebang parsing and extension fallback.
|
|
897
|
+
- Extension fallback maps `.js`, `.cjs`, `.mjs` to Node, `.cmd` and `.bat` to
|
|
898
|
+
CMD, `.ps1` to PowerShell, and `.sh` to sh in the inspected package version.
|
|
899
|
+
|
|
900
|
+
Implications:
|
|
901
|
+
|
|
902
|
+
- pnpm normally creates `name`, `name.cmd`, and `name.ps1`.
|
|
903
|
+
- Yarn Classic creates `name` and `name.cmd`.
|
|
904
|
+
- Yarn Berry node-modules creates `name` and `name.cmd`.
|
|
905
|
+
|
|
906
|
+
### Bun Windows PE shim
|
|
907
|
+
|
|
908
|
+
For a Windows bin target, Bun:
|
|
909
|
+
|
|
910
|
+
- Writes `name.exe`.
|
|
911
|
+
- Writes `name.bunx`.
|
|
912
|
+
- Encodes the target path and launcher into `.bunx`.
|
|
913
|
+
- Uses a shebang when it can parse one.
|
|
914
|
+
- If there is no shebang, extension fallback maps JS/TS-like extensions and
|
|
915
|
+
`.sh` to `bun run`, `.cmd`/`.bat` to `cmd /c`, and `.ps1` to
|
|
916
|
+
`powershell -ExecutionPolicy Bypass -File`.
|
|
917
|
+
- If the target has no recognized extension and no shebang, the PE shim runs the
|
|
918
|
+
target path directly.
|
|
919
|
+
|
|
920
|
+
## What happens if the target itself is `.cmd`, `.bat`, `.ps1`, or `.exe`
|
|
921
|
+
|
|
922
|
+
This section is about `package.json#bin` pointing at different target files:
|
|
923
|
+
|
|
924
|
+
```json
|
|
925
|
+
{
|
|
926
|
+
"bin": {
|
|
927
|
+
"ozy": "./bin/ozy.cmd"
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
or:
|
|
933
|
+
|
|
934
|
+
```json
|
|
935
|
+
{
|
|
936
|
+
"bin": {
|
|
937
|
+
"ozy": "./bin/ozy.exe"
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
Package manager behavior is not "link target as-is" on Windows, except for Bun's
|
|
943
|
+
PE shim strategy. npm, pnpm, and Yarn generally create manager-owned shims that
|
|
944
|
+
then invoke the target.
|
|
945
|
+
|
|
946
|
+
Expected effects from source:
|
|
947
|
+
|
|
948
|
+
| Target path | npm `cmd-shim` | pnpm/Yarn `@zkochan/cmd-shim` | Bun |
|
|
949
|
+
| --- | --- | --- | --- |
|
|
950
|
+
| `./bin/ozy` with shebang | Generated shim invokes shebang program | Generated shim invokes shebang program | `.exe` shim uses parsed shebang |
|
|
951
|
+
| `./bin/ozy` without shebang | Generated shim invokes target path directly | Generated shim likely invokes target path directly | `.exe` shim invokes target path directly |
|
|
952
|
+
| `./bin/ozy.js` | Shebang if present, otherwise direct target path | Node by extension fallback if no shebang | `bun run` by extension fallback if no shebang |
|
|
953
|
+
| `./bin/ozy.cmd` | Shebang if present, otherwise direct target path | `cmd` by extension fallback if no shebang | `cmd /c` by extension fallback if no shebang |
|
|
954
|
+
| `./bin/ozy.bat` | Shebang if present, otherwise direct target path | `cmd` by extension fallback if no shebang | `cmd /c` by extension fallback if no shebang |
|
|
955
|
+
| `./bin/ozy.ps1` | Shebang if present, otherwise direct target path | PowerShell by extension fallback if no shebang | PowerShell by extension fallback if no shebang |
|
|
956
|
+
| `./bin/ozy.exe` | Shebang read fails or no shebang, then direct target path | Direct target path when treated as native executable | Direct target path via PE shim metadata |
|
|
957
|
+
|
|
958
|
+
The `.ps1` case is easy to misunderstand. This file is valid PowerShell:
|
|
959
|
+
|
|
960
|
+
```powershell
|
|
961
|
+
#!/usr/bin/env node
|
|
962
|
+
node install.js
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
because `#` begins a comment in PowerShell. But if a package manager reads that
|
|
966
|
+
file as a bin target and honors the shebang, the generated shim may run it with
|
|
967
|
+
Node instead of PowerShell. That means a polyglot `.ps1` target does not
|
|
968
|
+
guarantee PowerShell execution.
|
|
969
|
+
|
|
970
|
+
Replacing a `.ps1` file with PE executable bytes is not a safe second-run
|
|
971
|
+
strategy. PowerShell treats `.ps1` as a script path. A PE file with a `.ps1`
|
|
972
|
+
extension will be parsed as script text and fail.
|
|
973
|
+
|
|
974
|
+
## Same-name `.exe` beside text shims
|
|
975
|
+
|
|
976
|
+
One possible Windows repair strategy is:
|
|
977
|
+
|
|
978
|
+
```text
|
|
979
|
+
node_modules/.bin/
|
|
980
|
+
ozy
|
|
981
|
+
ozy.cmd
|
|
982
|
+
ozy.ps1
|
|
983
|
+
ozy.exe # added by self-repair
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
This might make `ozy` resolve to `ozy.exe` in `cmd.exe` because common `PATHEXT`
|
|
987
|
+
orders `.EXE` before `.BAT` and `.CMD`. It is not yet proven for:
|
|
988
|
+
|
|
989
|
+
- PowerShell command resolution.
|
|
990
|
+
- Git Bash/MSYS.
|
|
991
|
+
- npm global installs.
|
|
992
|
+
- pnpm local/global installs, especially because pnpm removes existing
|
|
993
|
+
`name.exe` before relinking bins.
|
|
994
|
+
- Yarn Classic and Yarn Berry without a `.ps1` shim.
|
|
995
|
+
- Bun, which already creates `name.exe`.
|
|
996
|
+
- uninstall cleanup.
|
|
997
|
+
|
|
998
|
+
Do not rely on this strategy without the fixture matrix below.
|
|
999
|
+
|
|
1000
|
+
## Why shim discovery is hard
|
|
1001
|
+
|
|
1002
|
+
The installer JS can inspect:
|
|
1003
|
+
|
|
1004
|
+
- `process.argv[0]`
|
|
1005
|
+
- `process.argv[1]`
|
|
1006
|
+
- `process.execPath`
|
|
1007
|
+
- `process.env.npm_execpath`
|
|
1008
|
+
- `process.env.npm_config_user_agent`
|
|
1009
|
+
- parent process information, if available
|
|
1010
|
+
|
|
1011
|
+
Those values do not uniformly identify the package-manager-created shim file.
|
|
1012
|
+
Depending on manager and shell, the current process may be:
|
|
1013
|
+
|
|
1014
|
+
- `node.exe` running a target script selected by a text shim.
|
|
1015
|
+
- `powershell.exe` running a `.ps1` shim.
|
|
1016
|
+
- `cmd.exe` running a `.cmd` shim.
|
|
1017
|
+
- Bun's PE shim running through `.bunx` metadata.
|
|
1018
|
+
- Yarn Berry running through a temporary wrapper directory.
|
|
1019
|
+
|
|
1020
|
+
Caller-tree inspection can help debugging, but it is not a stable installer API.
|
|
1021
|
+
It also does not tell us what files the package manager considers owned for
|
|
1022
|
+
uninstall. For a repair strategy, file ownership matters as much as locating the
|
|
1023
|
+
file.
|
|
1024
|
+
|
|
1025
|
+
## Current conclusions
|
|
1026
|
+
|
|
1027
|
+
1. The base package needs to own the command `bin` entries. Platform-package
|
|
1028
|
+
`bin` entries alone are not enough for a generic local/global install story.
|
|
1029
|
+
2. POSIX can be clean: a first-run JS launcher replaces the package-owned
|
|
1030
|
+
command file with a hardlink or copy of the platform `multi-call-binary`,
|
|
1031
|
+
then executes that command path.
|
|
1032
|
+
3. Windows is not one case:
|
|
1033
|
+
- npm writes `name`, `name.cmd`, and `name.ps1`.
|
|
1034
|
+
- pnpm normally writes `name`, `name.cmd`, and `name.ps1`, and removes a
|
|
1035
|
+
pre-existing `name.exe` while linking.
|
|
1036
|
+
- Bun writes `name.exe` and `name.bunx`.
|
|
1037
|
+
- Yarn Classic writes `name` and `name.cmd`.
|
|
1038
|
+
- Yarn Berry node-modules writes `name` and `name.cmd`.
|
|
1039
|
+
- Yarn Berry PnP uses temporary script wrappers rather than persistent
|
|
1040
|
+
`node_modules/.bin` files.
|
|
1041
|
+
4. Replacing `.cmd` or `.ps1` with PE executable bytes is not a general
|
|
1042
|
+
solution. `.cmd` and `.ps1` are interpreted as text by their shells.
|
|
1043
|
+
5. Windows keeps the package-manager shim and JS launcher, but the launcher
|
|
1044
|
+
creates and runs a command-named `.exe` next to itself. This avoids locating
|
|
1045
|
+
or mutating package-manager-owned shims while still giving the multi-call
|
|
1046
|
+
executable a command-specific invocation name.
|
|
1047
|
+
|
|
1048
|
+
## Required Windows fixture matrix
|
|
1049
|
+
|
|
1050
|
+
Create a small package with one command and repeat with these target variants:
|
|
1051
|
+
|
|
1052
|
+
```text
|
|
1053
|
+
bin/ozy # JS with #!/usr/bin/env node
|
|
1054
|
+
bin/ozy-no-shebang # JS without shebang
|
|
1055
|
+
bin/ozy.cmd
|
|
1056
|
+
bin/ozy.bat
|
|
1057
|
+
bin/ozy.ps1
|
|
1058
|
+
bin/ozy.exe
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
For each package manager:
|
|
1062
|
+
|
|
1063
|
+
- npm
|
|
1064
|
+
- pnpm
|
|
1065
|
+
- bun
|
|
1066
|
+
- Yarn Classic
|
|
1067
|
+
- Yarn Berry with PnP
|
|
1068
|
+
- Yarn Berry with `nodeLinker: node-modules`
|
|
1069
|
+
|
|
1070
|
+
Test install modes:
|
|
1071
|
+
|
|
1072
|
+
- local dependency install
|
|
1073
|
+
- global install
|
|
1074
|
+
- Yarn Berry `dlx` temporary install
|
|
1075
|
+
|
|
1076
|
+
Test shells:
|
|
1077
|
+
|
|
1078
|
+
- `cmd.exe`
|
|
1079
|
+
- PowerShell 5 if available
|
|
1080
|
+
- PowerShell 7
|
|
1081
|
+
- Git Bash/MSYS if installed
|
|
1082
|
+
|
|
1083
|
+
Record:
|
|
1084
|
+
|
|
1085
|
+
```text
|
|
1086
|
+
manager:
|
|
1087
|
+
install mode:
|
|
1088
|
+
shell:
|
|
1089
|
+
target variant:
|
|
1090
|
+
created files:
|
|
1091
|
+
Get-Command ozy -All:
|
|
1092
|
+
where.exe ozy:
|
|
1093
|
+
command executed:
|
|
1094
|
+
process.argv:
|
|
1095
|
+
process.execPath:
|
|
1096
|
+
process.cwd:
|
|
1097
|
+
npm_execpath:
|
|
1098
|
+
npm_config_user_agent:
|
|
1099
|
+
after adding ozy.exe beside shims, command executed:
|
|
1100
|
+
after uninstall, leftover files:
|
|
1101
|
+
```
|
|
1102
|
+
|
|
1103
|
+
Do not implement the Windows shim mutation logic until this matrix is filled in
|
|
1104
|
+
for the package managers we intend to support.
|