oonacode 0.3.13

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 ADDED
@@ -0,0 +1,68 @@
1
+ # oonacode (npm wrapper)
2
+
3
+ OonaCode is a Windows-first, multi-provider coding agent: the genuine Claude Code engine (driven
4
+ through the official Claude Agent SDK) in an isolated profile, sessions in SQLite, any model
5
+ provider through your own gateway, and personal remote access to your own machines.
6
+
7
+ This package is the **npm/pnpm/bun/yarn entry point**. It contains no binaries itself; the native
8
+ build is pulled in through a per-platform optional dependency and linked into `bin/` by a
9
+ postinstall step (the same model as esbuild / opencode).
10
+
11
+ ```
12
+ npm install -g oonacode
13
+ pnpm add -g oonacode # then: pnpm approve-builds (allow the trusted postinstall)
14
+ bun add -g oonacode # then: bun pm trust oonacode
15
+ yarn global add oonacode
16
+ ```
17
+
18
+ Then:
19
+
20
+ ```
21
+ oona --help # CLI (mirrors Claude Code flags: -p, --model, --output-format text|json|stream-json, --resume, ...)
22
+ oona serve # start the host service (headless) + local web UI
23
+ oona web # open the local web UI
24
+ ```
25
+
26
+ ## Platform packages
27
+
28
+ | Platform | Package | Status |
29
+ | ------------- | --------------------- | ---------------------------------------- |
30
+ | Windows x64 | `oonacode-win32-x64` | shipped |
31
+ | Windows arm64 | `oonacode-win32-arm64` | later (x64 build runs under emulation) |
32
+ | Linux x64 | `oonacode-linux-x64` | later |
33
+ | Linux arm64 | `oonacode-linux-arm64` | later |
34
+ | macOS x64 | `oonacode-darwin-x64` | later |
35
+ | macOS arm64 | `oonacode-darwin-arm64` | later |
36
+
37
+ Each platform package carries `oona.exe` (the single-binary host + CLI), `oonaclaude.exe` (the pinned
38
+ Claude Code engine sidecar — it must live on real disk next to `oona.exe`), the local web UI assets
39
+ (`web-ui/`) and native addons (`build/`).
40
+
41
+ `oonaclaude.exe` is a byte-for-byte copy of the pinned, **unmodified** engine binary from the Claude
42
+ Agent SDK platform package. OonaCode does not fork or patch Claude Code; the file is renamed only so
43
+ it can never be confused with, or collide with, your own `claude` on PATH.
44
+
45
+ ## How `oona` resolves the binary
46
+
47
+ `bin/oona.js` looks for, in order: `bin/oona.exe` (linked by postinstall) → `oonacode-win32-x64/oona.exe`
48
+ in place → the JS CLI `@oonacode/cli/dist/main.js` when that package is a dependency (developer
49
+ installs; slower start, needs an engine reachable via `OONACODE_CLAUDE_BIN`). The binary runs with
50
+ inherited stdio and its exit code is forwarded verbatim. If nothing is found it prints how to fix
51
+ it, including the Windows one-liner:
52
+
53
+ ```
54
+ irm https://oonacode.oonak.ai/install.ps1 | iex
55
+ ```
56
+
57
+ ## Notes
58
+
59
+ - **pnpm / bun block lifecycle scripts by default.** Without the postinstall the wrapper still
60
+ works (it resolves the platform package in place); linking only removes one resolution step.
61
+ - Set `OONACODE_SKIP_POSTINSTALL=1` to skip the link step entirely.
62
+ - Your own Claude subscription always runs direct to Anthropic from your PC; API-key providers go
63
+ through your own OonaCode gateway. No credential is ever shared between users.
64
+ - `private: true` is set on this package until the first publish (TODO: flip in `package.json`
65
+ once the npm org is ready; the release workflow refuses to publish while it is private).
66
+ - In the monorepo `optionalDependencies` use `workspace:<version>` so pnpm links the sibling
67
+ platform package; `packaging/scripts/prepare-npm.mjs` rewrites it to the concrete version right
68
+ before `npm publish` (npm does not understand the workspace protocol).
package/bin/oona.js ADDED
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * `oona` / `oonacode` — npm wrapper shim.
5
+ *
6
+ * Resolution order (first hit wins):
7
+ * 1. <this package>/bin/oona.exe — hard-linked/copied there by postinstall.mjs
8
+ * 2. <platform package>/oona.exe — e.g. node_modules/oonacode-win32-x64/oona.exe
9
+ * (used when the package manager skipped postinstall;
10
+ * pnpm/bun block lifecycle scripts unless allowed)
11
+ * 3. @oonacode/cli/dist/main.js via Node — JS fallback when the monorepo CLI package is a
12
+ * dependency (developer installs); slower to start and
13
+ * requires an engine reachable via OONACODE_CLAUDE_BIN.
14
+ *
15
+ * The native binary is spawned with inherited stdio and its exit code is forwarded verbatim, so
16
+ * `oona -p ... --output-format json` works in pipes exactly like the bare executable.
17
+ *
18
+ * Windows and Linux x64 are published; the remaining platforms (arm64, darwin) are deferred —
19
+ * see README.md.
20
+ */
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+ const { spawn } = require('child_process');
24
+
25
+ const PLATFORM_PACKAGES = {
26
+ 'win32-x64': '@oonacode/win32-x64',
27
+ 'linux-x64': '@oonacode/linux-x64',
28
+ // 'win32-arm64': 'oonacode-win32-arm64', // deferred
29
+ // 'linux-arm64': 'oonacode-linux-arm64', // deferred — install.sh refuses aarch64 for the
30
+ // same reason: nothing is published for it
31
+ // 'darwin-x64': 'oonacode-darwin-x64', // deferred
32
+ // 'darwin-arm64': 'oonacode-darwin-arm64', // deferred
33
+ };
34
+
35
+ const IS_WIN = process.platform === 'win32';
36
+ const EXE_NAME = IS_WIN ? 'oona.exe' : 'oona';
37
+ // OonaCode ships the engine as `oonaclaude(.exe)`: a byte-for-byte copy of the pinned, UNMODIFIED
38
+ // Claude Code binary from the Claude Agent SDK. We do not fork or patch it — the filename differs
39
+ // only so it can never be confused with, or collide with, the user's own `claude` on PATH.
40
+ // The pre-rename name stays in the list so packages published before the rename still resolve.
41
+ const ENGINE_NAMES = IS_WIN ? ['oonaclaude.exe', 'claude.exe'] : ['oonaclaude', 'claude'];
42
+ const PLATFORM_KEY = `${process.platform}-${process.arch}`;
43
+
44
+ const INSTALL_HINT = [
45
+ `oonacode: no native binary found for ${PLATFORM_KEY}.`,
46
+ '',
47
+ PLATFORM_PACKAGES[PLATFORM_KEY]
48
+ ? ` The platform package "${PLATFORM_PACKAGES[PLATFORM_KEY]}" is missing or was installed without its postinstall step.`
49
+ : ` Prebuilt binaries for ${PLATFORM_KEY} are not published yet (Windows and Linux x64 first).`,
50
+ '',
51
+ ' Fixes:',
52
+ ' - npm: reinstall with optional dependencies enabled (do not use --no-optional / --omit=optional)',
53
+ ' - pnpm: allow the trusted postinstall: pnpm approve-builds (or add "oonacode" to pnpm.onlyBuiltDependencies)',
54
+ ' - bun: bun pm trust oonacode',
55
+ ' - or install without npm (recommended on Windows):',
56
+ ' irm https://oonacode.oonak.ai/install.ps1 | iex',
57
+ '',
58
+ ].join('\n');
59
+
60
+ function exists(p) {
61
+ try {
62
+ return fs.existsSync(p);
63
+ } catch {
64
+ return false;
65
+ }
66
+ }
67
+
68
+ function resolveNative() {
69
+ const local = path.join(__dirname, EXE_NAME);
70
+ if (exists(local)) return { bin: local, dir: __dirname };
71
+
72
+ const pkg = PLATFORM_PACKAGES[PLATFORM_KEY];
73
+ if (pkg) {
74
+ try {
75
+ const dir = path.dirname(require.resolve(`${pkg}/package.json`));
76
+ const bin = path.join(dir, EXE_NAME);
77
+ if (exists(bin)) return { bin, dir };
78
+ } catch {
79
+ /* platform package not installed */
80
+ }
81
+ }
82
+ return null;
83
+ }
84
+
85
+ function resolveBundledJs() {
86
+ // The documented "node + JS" fallback build ships lib/oona.cjs instead of a compiled oona.exe;
87
+ // run it with the user's Node (>= 22). Same lookup order as the native binary.
88
+ const local = path.join(__dirname, 'lib', 'oona.cjs');
89
+ if (exists(local)) return { script: local, dir: __dirname };
90
+ const pkg = PLATFORM_PACKAGES[PLATFORM_KEY];
91
+ if (pkg) {
92
+ try {
93
+ const dir = path.dirname(require.resolve(`${pkg}/package.json`));
94
+ const script = path.join(dir, 'lib', 'oona.cjs');
95
+ if (exists(script)) return { script, dir };
96
+ } catch {
97
+ /* platform package not installed */
98
+ }
99
+ }
100
+ return null;
101
+ }
102
+
103
+ function resolveJsFallback() {
104
+ for (const candidate of ['@oonacode/cli/dist/main.js', '@oonacode/cli/bin/oona.js']) {
105
+ try {
106
+ return require.resolve(candidate);
107
+ } catch {
108
+ /* not a dependency */
109
+ }
110
+ }
111
+ return null;
112
+ }
113
+
114
+ function engineEnv(env, dirs) {
115
+ // Make the sidecar engine discoverable even when only oona.exe was linked into ./bin.
116
+ // OONACODE_CLAUDE_BIN is the documented override read by the host service (packages/shared constants).
117
+ if (env.OONACODE_CLAUDE_BIN) return env;
118
+ for (const dir of dirs) {
119
+ for (const name of ENGINE_NAMES) {
120
+ const candidate = path.join(dir, name);
121
+ if (exists(candidate)) {
122
+ env.OONACODE_CLAUDE_BIN = candidate;
123
+ return env;
124
+ }
125
+ }
126
+ }
127
+ return env;
128
+ }
129
+
130
+ function run() {
131
+ const args = process.argv.slice(2);
132
+ const env = { ...process.env };
133
+ let child;
134
+
135
+ const native = resolveNative();
136
+ const bundled = native ? null : resolveBundledJs();
137
+ if (native) {
138
+ engineEnv(env, [native.dir, __dirname]);
139
+ child = spawn(native.bin, args, { stdio: 'inherit', env, windowsHide: false });
140
+ } else if (bundled) {
141
+ engineEnv(env, [bundled.dir, __dirname]);
142
+ child = spawn(process.execPath, [bundled.script, ...args], {
143
+ stdio: 'inherit',
144
+ env,
145
+ windowsHide: false,
146
+ });
147
+ } else {
148
+ const js = resolveJsFallback();
149
+ if (!js) {
150
+ process.stderr.write(INSTALL_HINT);
151
+ process.exit(1);
152
+ }
153
+ engineEnv(env, [__dirname]);
154
+ child = spawn(process.execPath, [js, ...args], { stdio: 'inherit', env, windowsHide: false });
155
+ }
156
+
157
+ const forward = (signal) => {
158
+ try {
159
+ child.kill(signal);
160
+ } catch {
161
+ /* already gone */
162
+ }
163
+ };
164
+ for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
165
+ process.on(signal, () => forward(signal));
166
+ }
167
+
168
+ child.on('error', (err) => {
169
+ process.stderr.write(
170
+ `oonacode: failed to start ${native ? native.bin : 'the JS CLI'}: ${err.message}\n`,
171
+ );
172
+ process.exit(1);
173
+ });
174
+ child.on('exit', (code, signal) => {
175
+ if (signal) {
176
+ // Mirror a signal death as the conventional 128+n exit code (130 for SIGINT).
177
+ const n = require('os').constants.signals[signal] || 0;
178
+ process.exit(128 + n);
179
+ }
180
+ process.exit(code == null ? 1 : code);
181
+ });
182
+ }
183
+
184
+ run();
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "oonacode",
3
+ "version": "0.3.13",
4
+ "description": "OonaCode \u2014 Windows-first multi-provider coding agent: the `oona` CLI, headless host service and local web UI. npm wrapper; the native binary is installed through the per-platform optional dependency.",
5
+ "license": "UNLICENSED",
6
+ "type": "commonjs",
7
+ "homepage": "https://oonacode.oonak.ai",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/oonak-ai/oonacode.git",
11
+ "directory": "packaging/npm/oonacode"
12
+ },
13
+ "keywords": [
14
+ "oonacode",
15
+ "oona",
16
+ "coding-agent",
17
+ "claude",
18
+ "cli",
19
+ "ai"
20
+ ],
21
+ "engines": {
22
+ "node": ">=22"
23
+ },
24
+ "bin": {
25
+ "oona": "bin/oona.js",
26
+ "oonacode": "bin/oona.js"
27
+ },
28
+ "files": [
29
+ "bin/oona.js",
30
+ "postinstall.mjs",
31
+ "README.md"
32
+ ],
33
+ "scripts": {
34
+ "postinstall": "node postinstall.mjs",
35
+ "test": "node --test test/wrapper.test.js test/packaging-scripts.test.js test/release-feed.test.js test/manifests.test.js test/set-version.test.js"
36
+ },
37
+ "optionalDependencies": {
38
+ "@oonacode/win32-x64": "0.3.13",
39
+ "@oonacode/linux-x64": "0.3.13"
40
+ }
41
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * oonacode postinstall — link the platform-specific native binaries into ./bin so that
3
+ * `oona` starts without a resolution step.
4
+ *
5
+ * node_modules/oonacode-win32-x64/oona.exe -> ./bin/oona.exe (hard link, falls back to copy)
6
+ * node_modules/oonacode-win32-x64/oonaclaude.exe -> ./bin/oonaclaude.exe (engine sidecar — must sit next to oona.exe)
7
+ * node_modules/oonacode-win32-x64/web-ui/ -> ./bin/web-ui/ (directory junction, falls back to copy)
8
+ * node_modules/oonacode-win32-x64/build/ -> ./bin/build/ (native addons, same treatment)
9
+ *
10
+ * `oonaclaude(.exe)` is a byte-for-byte copy of the pinned, UNMODIFIED Claude Code binary from the
11
+ * Claude Agent SDK. OonaCode does not fork or patch it — the filename differs only so it can never be
12
+ * confused with, or collide with, the user's own `claude` on PATH.
13
+ *
14
+ * This script NEVER fails the install: when the platform package is missing (unsupported platform,
15
+ * --no-optional, blocked lifecycle scripts) it prints a hint and exits 0. bin/oona.js resolves the
16
+ * platform package in place at runtime anyway and prints the same hint if nothing is available.
17
+ *
18
+ * Skipped silently when OONACODE_SKIP_POSTINSTALL=1 or when running inside the oonacode monorepo
19
+ * (the platform package there is a skeleton without binaries until the release workflow injects them).
20
+ */
21
+ import { createRequire } from 'node:module';
22
+ import fs from 'node:fs';
23
+ import path from 'node:path';
24
+ import { fileURLToPath } from 'node:url';
25
+
26
+ const here = path.dirname(fileURLToPath(import.meta.url));
27
+ const require = createRequire(import.meta.url);
28
+
29
+ const PLATFORM_PACKAGES = {
30
+ 'win32-x64': '@oonacode/win32-x64',
31
+ // Linux joined on 2026-09-07, when the npm channel was opened: `bin/oona.js` already resolved
32
+ // it at runtime, but without an entry here the postinstall linked nothing, so a Linux install
33
+ // had to pay the resolve on every invocation instead of once.
34
+ 'linux-x64': '@oonacode/linux-x64',
35
+ };
36
+ const IS_WIN = process.platform === 'win32';
37
+ const EXE_NAME = IS_WIN ? 'oona.exe' : 'oona';
38
+ const ENGINE_NAME = IS_WIN ? 'oonaclaude.exe' : 'oonaclaude';
39
+ /** Pre-rename filename, still linked when a package published before the rename is installed. */
40
+ const LEGACY_ENGINE_NAME = IS_WIN ? 'claude.exe' : 'claude';
41
+ const LINKED_DIRS = ['web-ui', 'build', 'prebuilds', 'lib'];
42
+
43
+ const log = (msg) => console.log(`[oonacode] ${msg}`);
44
+ const INSTALL_HINT =
45
+ 'Install the native build with: irm https://oonacode.oonak.ai/install.ps1 | iex (Windows)\n' +
46
+ '[oonacode] or re-run the install with optional dependencies + postinstall scripts enabled ' +
47
+ '(pnpm: `pnpm approve-builds`, bun: `bun pm trust oonacode`).';
48
+
49
+ function insideMonorepo() {
50
+ const root = path.resolve(here, '..', '..', '..');
51
+ return (
52
+ fs.existsSync(path.join(root, 'pnpm-workspace.yaml')) &&
53
+ fs.existsSync(path.join(root, 'packages', 'shared'))
54
+ );
55
+ }
56
+
57
+ function linkFile(src, dst) {
58
+ fs.rmSync(dst, { force: true });
59
+ try {
60
+ fs.linkSync(src, dst);
61
+ return 'hard-linked';
62
+ } catch {
63
+ fs.copyFileSync(src, dst);
64
+ return 'copied';
65
+ }
66
+ }
67
+
68
+ function linkDir(src, dst) {
69
+ fs.rmSync(dst, { recursive: true, force: true });
70
+ try {
71
+ fs.symlinkSync(src, dst, 'junction'); // junction: no admin rights needed on Windows
72
+ return 'linked';
73
+ } catch {
74
+ fs.cpSync(src, dst, { recursive: true });
75
+ return 'copied';
76
+ }
77
+ }
78
+
79
+ function main() {
80
+ if (process.env.OONACODE_SKIP_POSTINSTALL === '1') return;
81
+ if (insideMonorepo()) return;
82
+
83
+ const key = `${process.platform}-${process.arch}`;
84
+ const pkgName = PLATFORM_PACKAGES[key];
85
+ if (!pkgName) {
86
+ log(
87
+ `no prebuilt binary is published for ${key} yet (Windows x64 first; macOS/Linux deferred).`,
88
+ );
89
+ log(INSTALL_HINT);
90
+ return;
91
+ }
92
+
93
+ let pkgDir;
94
+ try {
95
+ pkgDir = path.dirname(require.resolve(`${pkgName}/package.json`));
96
+ } catch {
97
+ log(`platform package "${pkgName}" is not installed (optional dependencies disabled?).`);
98
+ log(INSTALL_HINT);
99
+ return;
100
+ }
101
+
102
+ const exeSrc = path.join(pkgDir, EXE_NAME);
103
+ const bundledJs = path.join(pkgDir, 'lib', 'oona.cjs');
104
+ if (!fs.existsSync(exeSrc) && !fs.existsSync(bundledJs)) {
105
+ log(`"${pkgName}" is installed but contains no ${EXE_NAME} (pre-release skeleton?).`);
106
+ log(INSTALL_HINT);
107
+ return;
108
+ }
109
+
110
+ const binDir = path.join(here, 'bin');
111
+ fs.mkdirSync(binDir, { recursive: true });
112
+
113
+ const parts = [];
114
+ if (fs.existsSync(exeSrc))
115
+ parts.push(`${EXE_NAME} ${linkFile(exeSrc, path.join(binDir, EXE_NAME))}`);
116
+ // Link whichever engine filename the platform package carries; ours first, the pre-rename one
117
+ // second so an older published platform package still yields a working sidecar.
118
+ const engineName = [ENGINE_NAME, LEGACY_ENGINE_NAME].find((name) =>
119
+ fs.existsSync(path.join(pkgDir, name)),
120
+ );
121
+ if (engineName) {
122
+ parts.push(
123
+ `${engineName} ${linkFile(path.join(pkgDir, engineName), path.join(binDir, engineName))}`,
124
+ );
125
+ } else {
126
+ log(
127
+ `warning: ${ENGINE_NAME} engine sidecar not found in ${pkgName}; \`oona doctor\` will flag it.`,
128
+ );
129
+ }
130
+ for (const dir of LINKED_DIRS) {
131
+ const src = path.join(pkgDir, dir);
132
+ if (fs.existsSync(src)) parts.push(`${dir}/ ${linkDir(src, path.join(binDir, dir))}`);
133
+ }
134
+ log(`ready: ${parts.join(', ')} -> ${binDir}`);
135
+ }
136
+
137
+ try {
138
+ main();
139
+ } catch (err) {
140
+ log(`postinstall could not link the native binary: ${err && err.message ? err.message : err}`);
141
+ log(INSTALL_HINT);
142
+ }
143
+ process.exitCode = 0;