@openduo/duoduo 0.4.6 → 0.5.0-rc.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openduo/duoduo",
3
- "version": "0.4.6",
3
+ "version": "0.5.0-rc.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -43,7 +43,7 @@
43
43
  "ws": "^8.19.0"
44
44
  },
45
45
  "dependencies": {
46
- "@anthropic-ai/claude-agent-sdk": "^0.2.92",
46
+ "@anthropic-ai/claude-agent-sdk": "^0.2.114",
47
47
  "@fastify/websocket": "^11.2.0",
48
48
  "@modelcontextprotocol/sdk": "^1.27.1",
49
49
  "chalk": "^4.1.2",
@@ -55,7 +55,7 @@
55
55
  "ink-text-input": "^5.0.1",
56
56
  "react": "^18.3.1",
57
57
  "zod": "^4.3.6",
58
- "@openduo/protocol": "0.2.7"
58
+ "@openduo/protocol": "0.5.0-rc.1"
59
59
  },
60
60
  "scripts": {
61
61
  "test": "vitest run",
@@ -1,24 +1,108 @@
1
1
  #!/usr/bin/env node
2
- // Restore execute permission on vendored ripgrep binaries.
3
- // npm pack strips all files to 0644, so after `npm install` the rg
4
- // binaries are not executable. This script runs as a postinstall hook
5
- // to fix that.
6
- import { chmod, readdir } from "node:fs/promises";
7
- import path from "node:path";
8
- import { fileURLToPath } from "node:url";
9
-
10
- const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
- const rgDir = path.join(rootDir, "dist", "release", "vendor", "ripgrep");
12
-
13
- try {
14
- const entries = await readdir(rgDir, { withFileTypes: true });
15
- for (const entry of entries) {
16
- if (!entry.isDirectory()) continue;
17
- for (const bin of ["rg", "rg.exe"]) {
18
- const p = path.join(rgDir, entry.name, bin);
19
- try { await chmod(p, 0o755); } catch { /* skip missing */ }
20
- }
2
+ /* global console, process */
3
+ // SDK 0.2.113+ ships the Claude Code runtime as a per-platform native binary
4
+ // via optional dependencies (`@anthropic-ai/claude-agent-sdk-<platform>-<arch>`).
5
+ // When users install duoduo with `--omit=optional` / `NPM_CONFIG_OPTIONAL=false`
6
+ // or on an unsupported platform/arch combo, the binary is missing and the
7
+ // failure only surfaces on the first `query()` call deep inside a live session.
8
+ //
9
+ // This preflight catches that case at install time: try to resolve the SDK,
10
+ // locate the platform binary it would spawn, and print a clear actionable
11
+ // warning if something is off. We warn rather than error-exit so that
12
+ // development installs (where the SDK may not yet be linked) don't break;
13
+ // the daemon runs the same check at boot and fails fast there.
14
+ import { createRequire } from "node:module";
15
+ import { stat } from "node:fs/promises";
16
+
17
+ const require = createRequire(import.meta.url);
18
+
19
+ function platformPackageName() {
20
+ const { platform, arch } = process;
21
+ const key = `${platform}-${arch}`;
22
+ const supported = new Set([
23
+ "darwin-arm64",
24
+ "darwin-x64",
25
+ "linux-x64",
26
+ "linux-arm64",
27
+ "win32-x64",
28
+ "win32-arm64"
29
+ ]);
30
+ if (!supported.has(key)) return null;
31
+ // musl detection: node reports `linux` but the SDK ships separate
32
+ // `-musl` packages. A quick heuristic via `process.report` would be
33
+ // nice but isn't reliable; instead we try the glibc variant first
34
+ // and fall back to musl if that package isn't resolvable.
35
+ return `@anthropic-ai/claude-agent-sdk-${platform}-${arch}`;
36
+ }
37
+
38
+ function tryResolveBinary(pkg) {
39
+ try {
40
+ return require.resolve(`${pkg}/claude`);
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+
46
+ async function binaryIsExecutable(path) {
47
+ try {
48
+ const s = await stat(path);
49
+ return s.isFile();
50
+ } catch {
51
+ return false;
21
52
  }
22
- } catch {
23
- // dist/release may not exist in dev-only installs — silently skip.
24
53
  }
54
+
55
+ async function main() {
56
+ let sdkResolved = false;
57
+ try {
58
+ require.resolve("@anthropic-ai/claude-agent-sdk");
59
+ sdkResolved = true;
60
+ } catch {
61
+ // Dev-only install (no runtime dep); silently skip preflight.
62
+ return;
63
+ }
64
+ if (!sdkResolved) return;
65
+
66
+ const pkg = platformPackageName();
67
+ if (!pkg) {
68
+ console.warn(
69
+ `[duoduo] WARN: platform ${process.platform}-${process.arch} is not in the ` +
70
+ "list of @anthropic-ai/claude-agent-sdk native binary packages " +
71
+ "(darwin-arm64, darwin-x64, linux-x64, linux-arm64, win32-x64, win32-arm64). " +
72
+ "The daemon will fail to start. If you are on linux-musl, install the " +
73
+ "matching -musl optional package manually."
74
+ );
75
+ return;
76
+ }
77
+
78
+ // Try primary (glibc) package; fall back to -musl on linux.
79
+ let binPath = tryResolveBinary(pkg);
80
+ if (!binPath && process.platform === "linux") {
81
+ binPath = tryResolveBinary(`${pkg}-musl`);
82
+ }
83
+
84
+ if (!binPath) {
85
+ console.warn(
86
+ `[duoduo] WARN: native Claude Code binary for ${process.platform}-${process.arch} ` +
87
+ "was not found in node_modules. This usually means the install ran with " +
88
+ "`--omit=optional` or `NPM_CONFIG_OPTIONAL=false`. The daemon will fail " +
89
+ "to spawn agent subprocesses. Reinstall without the optional-omit flag, " +
90
+ `or manually install the optional package \`${pkg}\` (or its \`-musl\` ` +
91
+ "variant on Alpine/musl systems)."
92
+ );
93
+ return;
94
+ }
95
+
96
+ if (!(await binaryIsExecutable(binPath))) {
97
+ console.warn(
98
+ `[duoduo] WARN: found platform binary at ${binPath} but it is not a regular ` +
99
+ "file. The daemon will likely fail to spawn the agent subprocess."
100
+ );
101
+ }
102
+ }
103
+
104
+ main().catch((err) => {
105
+ // Never fail the install — the daemon preflight repeats this check and
106
+ // can produce a richer error once the runtime knows the install context.
107
+ console.warn(`[duoduo] postinstall preflight skipped: ${err?.message ?? err}`);
108
+ });