@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 CHANGED
@@ -5,7 +5,7 @@
5
5
  [![npm version](https://img.shields.io/npm/v/@cnwenf/occ.svg)](https://www.npmjs.com/package/@cnwenf/occ)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
7
7
  [![Runtime: Bun](https://img.shields.io/badge/Runtime-Bun-%23000000.svg)](https://bun.sh/)
8
- [![Tracks: Claude Code 2.1.204](https://img.shields.io/badge/Tracks-Claude%20Code%202.1.204-blueviolet.svg)](https://docs.anthropic.com/en/docs/claude-code)
8
+ [![Tracks: Claude Code 2.1.211](https://img.shields.io/badge/Tracks-Claude%20Code%202.1.211-blueviolet.svg)](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.204`). The code is fully open, auditable, backdoor-free, and your data stays under your control.
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.204` | Reference implementation |
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.204 when working
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.204`**.
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.204`)。代码全开放、可审计、无暗门,数据由你掌控。
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.204`**。
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.204 即正常
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
+ }