@cnwenf/occ 2.1.268 → 2.1.270
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 +5 -5
- package/README.zh-CN.md +3 -3
- package/bin/occ.cjs +300 -0
- package/dist/cli.js +48270 -45804
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/@cnwenf/occ)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
[](https://bun.sh/)
|
|
8
|
-
[](https://docs.anthropic.com/en/docs/claude-code)
|
|
9
9
|
|
|
10
10
|
[简体中文](./README.zh-CN.md) · **English**
|
|
11
11
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
## What is OCC
|
|
15
15
|
|
|
16
|
-
**Open C Code (OCC)** is an open-source coding agent. Its capabilities are aligned with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) (currently tracking `2.1.
|
|
16
|
+
**Open C Code (OCC)** is an open-source coding agent. Its capabilities are aligned with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) (currently tracking `2.1.211`). The code is fully open, auditable, backdoor-free, and your data stays under your control.
|
|
17
17
|
|
|
18
18
|
If you worry that a closed-source CLI might hide backdoors, or that your code and credentials are uploaded to unauditable services, OCC is for you: all source is open and unobfuscated, the build is reproducible from source, and API credentials are sent only to endpoints you configure.
|
|
19
19
|
|
|
@@ -62,7 +62,7 @@ Requires a valid Anthropic API Key (or AWS Bedrock / Google Vertex / Azure Found
|
|
|
62
62
|
| **Auditability** | Line-by-line reviewable | No |
|
|
63
63
|
| **Telemetry** | Minimal (analytics stubbed) | Standard |
|
|
64
64
|
| **Data sovereignty** | Credentials stay on your machine; requests only to endpoints you configure | Anthropic endpoints |
|
|
65
|
-
| **Capability parity** | Tracks CC `2.1.
|
|
65
|
+
| **Capability parity** | Tracks CC `2.1.211` | Reference implementation |
|
|
66
66
|
| **Providers** | Anthropic Direct, Bedrock, Vertex, Azure | Anthropic, Bedrock, Vertex |
|
|
67
67
|
| **Cost** | Free & open-source (MIT) | Subscription |
|
|
68
68
|
| **Build** | Reproducible from source | N/A |
|
|
@@ -143,7 +143,7 @@ Requires [Bun](https://bun.sh/) >= 1.3.11 (use `bun upgrade` — older Bun cause
|
|
|
143
143
|
|
|
144
144
|
```bash
|
|
145
145
|
bun install
|
|
146
|
-
bun run dev # run from source; version prints 2.1.
|
|
146
|
+
bun run dev # run from source; version prints 2.1.270 (dev polyfill; build overrides with pkg.version) when working
|
|
147
147
|
bun run build # output: dist/cli.js (~26 MB, single-file bundle)
|
|
148
148
|
bun test # test suite (Bun test runner)
|
|
149
149
|
bun run lint # Biome lint (formatter disabled to avoid large diffs)
|
|
@@ -157,7 +157,7 @@ For architecture, entry/bootstrap, tool system, UI layer, and module-status deta
|
|
|
157
157
|
|
|
158
158
|
## Status
|
|
159
159
|
|
|
160
|
-
- Tracks Claude Code **`2.1.
|
|
160
|
+
- Tracks Claude Code **`2.1.211`**.
|
|
161
161
|
- Published to npm as [`@cnwenf/occ`](https://www.npmjs.com/package/@cnwenf/occ).
|
|
162
162
|
- Many modules are intentionally stubbed or feature-flagged off — see "Disabled / stubbed" above.
|
|
163
163
|
|
package/README.zh-CN.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
## 这是什么
|
|
10
10
|
|
|
11
|
-
**Open C Code(OCC)** 是一个开源的编码智能体。能力与 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) 对齐(当前跟踪 `2.1.
|
|
11
|
+
**Open C Code(OCC)** 是一个开源的编码智能体。能力与 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) 对齐(当前跟踪 `2.1.211`)。代码全开放、可审计、无暗门,数据由你掌控。
|
|
12
12
|
|
|
13
13
|
如果你担心闭源 CLI 可能植入后门、担心代码与凭据被上传到不可审计的服务,OCC 就是为你准备的:全部源码开放、无混淆,构建产物可由源码复现,API 凭据只发往你自己配置的端点。
|
|
14
14
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
## 现状
|
|
23
23
|
|
|
24
|
-
- 跟踪 Claude Code **`2.1.
|
|
24
|
+
- 跟踪 Claude Code **`2.1.211`**。
|
|
25
25
|
- 代码库有约 1300 个不阻塞的 `tsc` 类型错误(大量松散的 `unknown`/`never`/`{}` 类型),**不影响 Bun 运行时执行**。门槛是 Biome lint,不是 `tsc`。
|
|
26
26
|
- 所有内部 feature flag(`feature(...)`)已被 polyfill 为 `false` —— 内部功能(COORDINATOR_MODE、KAIROS、PROACTIVE 等)全部关闭。
|
|
27
27
|
- 已发布到 npm:[`@cnwenf/occ`](https://www.npmjs.com/package/@cnwenf/occ)。
|
|
@@ -80,7 +80,7 @@ Glob、Grep(默认启用);TaskCreate/Get/Update/List(Todo v2)、EnterW
|
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
82
|
bun install
|
|
83
|
-
bun run dev # 从源码运行;版本号显示 2.1.
|
|
83
|
+
bun run dev # 从源码运行;版本号显示 2.1.270(dev polyfill;build 用 pkg.version 覆盖)即正常
|
|
84
84
|
bun run build # 产物:dist/cli.js(~26MB,5300+ 模块,单文件 bundle)
|
|
85
85
|
bun test # 测试套件
|
|
86
86
|
bun run lint # Biome lint(禁用格式化以避免大 diff)
|
package/bin/occ.cjs
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* OCC launcher shim.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS EXISTS
|
|
6
|
+
* OCC ships a Bun-target bundle at dist/cli.js (built with
|
|
7
|
+
* `bun build --target bun`, shebang `#!/usr/bin/env bun`). When a user
|
|
8
|
+
* installs OCC via `npm i -g @cnwenf/occ` on a machine that has npm but
|
|
9
|
+
* NOT Bun, the kernel's shebang resolution fails with
|
|
10
|
+
* `/usr/bin/env: 'bun': No such file or directory` — so the `occ`
|
|
11
|
+
* command never even starts.
|
|
12
|
+
*
|
|
13
|
+
* This shim is a Node script (npm guarantees Node is present), so it
|
|
14
|
+
* ALWAYS runs. It locates a Bun binary, then spawns
|
|
15
|
+
* `bun <package>/dist/cli.js <args…>`. If no Bun is available it prints
|
|
16
|
+
* a clear install instruction instead of a cryptic env error.
|
|
17
|
+
*
|
|
18
|
+
* WHY PROBE-RUN + LIBC DETECTION
|
|
19
|
+
* `bun` is an OCC *optional* dependency. npm does NOT link an optional
|
|
20
|
+
* dependency's `bin` into the global bin dir, so `bun` is NOT on PATH
|
|
21
|
+
* after `npm i -g @cnwenf/occ` — the shim must rely on the bundled
|
|
22
|
+
* `@oven/bun-*` platform binaries (and the `bun` meta-package's
|
|
23
|
+
* postinstall-selected `bin/bun.exe`).
|
|
24
|
+
*
|
|
25
|
+
* The `bun` meta-package ships platform binaries for BOTH glibc and musl
|
|
26
|
+
* variants without an `os.libc` filter, so npm installs ALL of them on
|
|
27
|
+
* every Linux host. A musl ELF's interpreter is `/lib/ld-musl-x86_64.so.1`,
|
|
28
|
+
* which does NOT exist on a glibc host — spawning it fails with ENOENT.
|
|
29
|
+
* Older versions of this shim picked the first `existsSync`-true candidate
|
|
30
|
+
* in a fixed order (glibc, musl, …); when the glibc tarball's `bin/bun`
|
|
31
|
+
* was absent (the `bun` postinstall moves it) the musl binary was picked
|
|
32
|
+
* next, `existsSync`'d true, then ENOENT'd at spawn — `occ` died with
|
|
33
|
+
* `failed to launch bun`.
|
|
34
|
+
*
|
|
35
|
+
* The fix mirrors the official `@anthropic-ai/claude-code` cli-wrapper:
|
|
36
|
+
* detect the host's libc (`process.report` glibcVersionRuntime), restrict
|
|
37
|
+
* candidates to the MATCHING libc only, and PROBE-RUN each candidate
|
|
38
|
+
* (`<bin> --version`) before committing — a present-but-unrunnable binary
|
|
39
|
+
* is skipped, not fatal.
|
|
40
|
+
*
|
|
41
|
+
* BUN RESOLUTION ORDER (each existing candidate is probe-run; first success wins)
|
|
42
|
+
* 1. `$BUN_PATH` env (power-user override)
|
|
43
|
+
* 2. `bun` on PATH (explicit PATH walk — npm does not link it for optional deps)
|
|
44
|
+
* 3. `~/.bun/bin/bun` (user installed Bun via bun.sh installer)
|
|
45
|
+
* 4. The `bun` meta-package's `bin/bun.exe` — the meta postinstall already
|
|
46
|
+
* detected the host and placed the correct (arch + libc) binary here.
|
|
47
|
+
* 5. The `@oven/bun-<platform>-<arch>[-musl][-baseline]` platform binary
|
|
48
|
+
* matching the host's libc, non-baseline (AVX2) before baseline. This
|
|
49
|
+
* path works EVEN WHEN npm was run with `--ignore-scripts` (so the
|
|
50
|
+
* `bun` postinstall never ran and #4 is absent), because the `@oven-*`
|
|
51
|
+
* tarballs ship the real ELF/Mach-O/PE binary — no postinstall needed.
|
|
52
|
+
*
|
|
53
|
+
* If none probe-run successfully, print install instructions and exit 1.
|
|
54
|
+
*/
|
|
55
|
+
'use strict'
|
|
56
|
+
|
|
57
|
+
const { existsSync } = require('node:fs')
|
|
58
|
+
const { spawn, spawnSync } = require('node:child_process')
|
|
59
|
+
const path = require('node:path')
|
|
60
|
+
const os = require('node:os')
|
|
61
|
+
|
|
62
|
+
const pkgRoot = path.join(__dirname, '..')
|
|
63
|
+
const cliJs = path.join(pkgRoot, 'dist', 'cli.js')
|
|
64
|
+
const isWin = process.platform === 'win32'
|
|
65
|
+
const bunExeName = isWin ? 'bun.exe' : 'bun'
|
|
66
|
+
|
|
67
|
+
if (!existsSync(cliJs)) {
|
|
68
|
+
console.error(
|
|
69
|
+
'occ: dist/cli.js not found — the package install is incomplete or corrupt.',
|
|
70
|
+
)
|
|
71
|
+
console.error('Reinstall with: npm i -g @cnwenf/occ')
|
|
72
|
+
process.exit(1)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Detect a musl-libc Linux host the same way the official
|
|
77
|
+
* `@anthropic-ai/claude-code` cli-wrapper does: Node's runtime report sets
|
|
78
|
+
* `header.glibcVersionRuntime` only when running against glibc. Its absence
|
|
79
|
+
* (on Alpine/musl, or when the report is unavailable) means musl.
|
|
80
|
+
*/
|
|
81
|
+
function detectMusl() {
|
|
82
|
+
if (process.platform !== 'linux') return false
|
|
83
|
+
try {
|
|
84
|
+
const report =
|
|
85
|
+
typeof process.report?.getReport === 'function'
|
|
86
|
+
? process.report.getReport()
|
|
87
|
+
: null
|
|
88
|
+
if (!report) return false
|
|
89
|
+
return report.header?.glibcVersionRuntime === undefined
|
|
90
|
+
} catch (_) {
|
|
91
|
+
return false
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The @oven/bun-<platform>-<arch> packages that match the CURRENT process,
|
|
97
|
+
* restricted to the host's libc (glibc OR musl, never both) and ordered
|
|
98
|
+
* non-baseline (AVX2, faster) before baseline (compatible fallback). Each
|
|
99
|
+
* ships the real Bun binary at `bin/bun` (or `bin/bun.exe` on Windows).
|
|
100
|
+
* List gleaned from the `bun` npm meta-package's optionalDependencies
|
|
101
|
+
* (@oven 1.3.14).
|
|
102
|
+
*
|
|
103
|
+
* Restricting to one libc is the whole point: a glibc host must never
|
|
104
|
+
* consider a musl ELF (its `/lib/ld-musl-x86_64.so.1` interpreter is absent
|
|
105
|
+
* → ENOENT at spawn), and vice versa.
|
|
106
|
+
*/
|
|
107
|
+
function platformPackages() {
|
|
108
|
+
const p = process.platform
|
|
109
|
+
const a = process.arch
|
|
110
|
+
const musl = detectMusl()
|
|
111
|
+
if (p === 'darwin' && a === 'x64') return ['@oven/bun-darwin-x64']
|
|
112
|
+
if (p === 'darwin' && a === 'arm64') return ['@oven/bun-darwin-aarch64']
|
|
113
|
+
if (p === 'linux' && a === 'x64') {
|
|
114
|
+
if (musl)
|
|
115
|
+
return ['@oven/bun-linux-x64-musl', '@oven/bun-linux-x64-musl-baseline']
|
|
116
|
+
return ['@oven/bun-linux-x64', '@oven/bun-linux-x64-baseline']
|
|
117
|
+
}
|
|
118
|
+
if (p === 'linux' && a === 'arm64') {
|
|
119
|
+
if (musl) return ['@oven/bun-linux-aarch64-musl']
|
|
120
|
+
return ['@oven/bun-linux-aarch64']
|
|
121
|
+
}
|
|
122
|
+
if (p === 'win32' && a === 'x64') return ['@oven/bun-windows-x64']
|
|
123
|
+
if (p === 'win32' && a === 'arm64') return ['@oven/bun-windows-aarch64']
|
|
124
|
+
return []
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Resolve a dependency PACKAGE DIRECTORY via its package.json (always
|
|
129
|
+
* resolvable — `package.json` is exempt from `exports` restrictions), then
|
|
130
|
+
* join the in-package relative path. This is the reliable way to locate a
|
|
131
|
+
* bundled binary: `require.resolve('pkg/bin/bun')` fails when the binary
|
|
132
|
+
* file is absent (postinstall hasn't created/moved it yet) or when the
|
|
133
|
+
* package restricts subpath access — both false negatives that broke the
|
|
134
|
+
* old shim. Returns null if the package or file is not present.
|
|
135
|
+
*/
|
|
136
|
+
function resolveDepFile(spec, relPath) {
|
|
137
|
+
try {
|
|
138
|
+
// Resolve the package DIRECTORY via its package.json (always resolvable
|
|
139
|
+
// — `package.json` is exempt from `exports` restrictions). Resolving the
|
|
140
|
+
// bare package name would fail for packages with no `main`/`exports`
|
|
141
|
+
// (e.g. the `bun` meta-package and the `@oven/bun-*` binaries).
|
|
142
|
+
const pkgJson = require.resolve(spec + '/package.json')
|
|
143
|
+
const file = path.join(path.dirname(pkgJson), relPath)
|
|
144
|
+
return existsSync(file) ? file : null
|
|
145
|
+
} catch (_) {
|
|
146
|
+
return null
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Walk PATH for an executable named `name` (with Windows PATHEXT extensions
|
|
152
|
+
* on win32). Returns the absolute path if found, else null.
|
|
153
|
+
*/
|
|
154
|
+
function findOnPath(name) {
|
|
155
|
+
const pathVar = process.env.PATH || process.env.Path
|
|
156
|
+
if (!pathVar) return null
|
|
157
|
+
const sep = isWin ? ';' : ':'
|
|
158
|
+
const exts = isWin
|
|
159
|
+
? (process.env.PATHEXT ? process.env.PATHEXT.split(';') : ['.exe', '.cmd', '.bat', ''])
|
|
160
|
+
: ['']
|
|
161
|
+
for (const dir of pathVar.split(sep)) {
|
|
162
|
+
if (!dir) continue
|
|
163
|
+
for (const ext of exts) {
|
|
164
|
+
const candidate = path.join(dir, name + ext)
|
|
165
|
+
if (existsSync(candidate)) return candidate
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return null
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Ordered candidate Bun binaries. Each entry is an absolute path. Existence
|
|
173
|
+
* is checked at build time; runnability is probe-verified later in selectBun().
|
|
174
|
+
*/
|
|
175
|
+
function bunCandidates() {
|
|
176
|
+
const cands = []
|
|
177
|
+
|
|
178
|
+
// 1. Power-user override.
|
|
179
|
+
if (process.env.BUN_PATH && existsSync(process.env.BUN_PATH)) {
|
|
180
|
+
cands.push(process.env.BUN_PATH)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 2. `bun` on PATH — verified via an explicit PATH walk (npm does not
|
|
184
|
+
// link an optional dependency's bin globally, so this only matches when
|
|
185
|
+
// the user installed Bun separately).
|
|
186
|
+
const pathBun = findOnPath(bunExeName)
|
|
187
|
+
if (pathBun) cands.push(pathBun)
|
|
188
|
+
|
|
189
|
+
// 3. ~/.bun/bin/bun (bun.sh installer default location).
|
|
190
|
+
const homeBun = path.join(os.homedir(), '.bun', 'bin', bunExeName)
|
|
191
|
+
if (existsSync(homeBun)) cands.push(homeBun)
|
|
192
|
+
|
|
193
|
+
// 4. `bun` meta-package bin — postinstall-selected for this host. The
|
|
194
|
+
// meta package always names it `bin/bun.exe` regardless of platform.
|
|
195
|
+
const metaBin = resolveDepFile('bun', path.posix.join('bin', 'bun.exe'))
|
|
196
|
+
if (metaBin) cands.push(metaBin)
|
|
197
|
+
|
|
198
|
+
// 5. Optional-dep platform binary (robust under --ignore-scripts); one
|
|
199
|
+
// libc only, non-baseline before baseline.
|
|
200
|
+
for (const pkg of platformPackages()) {
|
|
201
|
+
const resolved = resolveDepFile(pkg, path.posix.join('bin', bunExeName))
|
|
202
|
+
if (resolved) cands.push(resolved)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return cands
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Probe-run a candidate Bun binary: `spawnSync(<bin>, ['--version'])` with a
|
|
210
|
+
* short timeout. Returns true only if the binary actually launched AND
|
|
211
|
+
* exited 0 — i.e. it is executable on THIS host. This is the signal that
|
|
212
|
+
* `existsSync` cannot give: a present musl ELF on a glibc host `existsSync`'s
|
|
213
|
+
* true but ENOENTs at spawn (missing `/lib/ld-musl-x86_64.so.1`); a
|
|
214
|
+
* non-baseline binary needing a newer glibc ditto. Probing lets the shim
|
|
215
|
+
* fall through to the next candidate instead of dying.
|
|
216
|
+
*/
|
|
217
|
+
function probeRuns(bin) {
|
|
218
|
+
try {
|
|
219
|
+
const r = spawnSync(bin, ['--version'], {
|
|
220
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
221
|
+
timeout: 5000,
|
|
222
|
+
windowsHide: true,
|
|
223
|
+
shell: false,
|
|
224
|
+
})
|
|
225
|
+
if (r.error) return false
|
|
226
|
+
return r.status === 0
|
|
227
|
+
} catch (_) {
|
|
228
|
+
return false
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function selectBun() {
|
|
233
|
+
const tried = []
|
|
234
|
+
for (const c of bunCandidates()) {
|
|
235
|
+
if (!c) continue
|
|
236
|
+
tried.push(c)
|
|
237
|
+
if (probeRuns(c)) return { bin: c, viaShell: false, tried }
|
|
238
|
+
}
|
|
239
|
+
return { bin: null, viaShell: false, tried }
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Locate a working Bun binary and spawn `bun dist/cli.js <args…>`, inheriting
|
|
244
|
+
* stdio so the child REPL/pipe owns the terminal. Exits the process with the
|
|
245
|
+
* child's code/signal, or 1 on a launch error / no-binary-found.
|
|
246
|
+
*
|
|
247
|
+
* Hoisted into a function (and only invoked when this file is the entry point)
|
|
248
|
+
* so the pure helpers above can be unit-tested via `require('./bin/occ.cjs')`
|
|
249
|
+
* without the shim auto-spawning a child on import.
|
|
250
|
+
*/
|
|
251
|
+
function launch() {
|
|
252
|
+
const { bin: bunBin, viaShell, tried } = selectBun()
|
|
253
|
+
|
|
254
|
+
if (!bunBin) {
|
|
255
|
+
console.error('')
|
|
256
|
+
console.error('occ: Bun runtime not found.')
|
|
257
|
+
console.error('')
|
|
258
|
+
console.error('OCC requires Bun (>= 1.3.11) to run. Install it with one of:')
|
|
259
|
+
console.error(' npm i -g bun')
|
|
260
|
+
console.error(' curl -fsSL https://bun.sh/install | bash')
|
|
261
|
+
console.error('Then run `occ` again.')
|
|
262
|
+
console.error('')
|
|
263
|
+
console.error('Tried: ' + (tried.length ? tried.join(', ') : '(no bundled bun found)'))
|
|
264
|
+
process.exit(1)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const child = spawn(bunBin, [cliJs, ...process.argv.slice(2)], {
|
|
268
|
+
stdio: 'inherit',
|
|
269
|
+
env: process.env,
|
|
270
|
+
shell: viaShell,
|
|
271
|
+
windowsHide: false,
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
child.on('error', (err) => {
|
|
275
|
+
console.error('occ: failed to launch bun:', err ? err.message : 'unknown error')
|
|
276
|
+
console.error(' binary: ' + bunBin)
|
|
277
|
+
process.exit(1)
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
child.on('exit', (code, signal) => {
|
|
281
|
+
if (signal) {
|
|
282
|
+
try {
|
|
283
|
+
process.kill(process.pid, signal)
|
|
284
|
+
} catch (_) {
|
|
285
|
+
process.exit(1)
|
|
286
|
+
}
|
|
287
|
+
return
|
|
288
|
+
}
|
|
289
|
+
process.exit(code ?? 0)
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Pure helpers exported for unit tests (see test/launcher.test.ts). When this
|
|
294
|
+
// file is the entry point (`occ` invoked on the CLI), run the launcher; when
|
|
295
|
+
// it is merely required, do nothing — no spawn side effect on import.
|
|
296
|
+
module.exports = { detectMusl, platformPackages, resolveDepFile, findOnPath, bunCandidates, probeRuns, selectBun }
|
|
297
|
+
|
|
298
|
+
if (require.main === module) {
|
|
299
|
+
launch()
|
|
300
|
+
}
|