Summary
Two independent defects make the source-checkout + dapi setup unusable on Windows. Both have to be fixed before dapi can talk to the app at all.
- OS: Windows 11
- Node: v22.21.0 · npm: 11.6.1
- dapi: 0.201.0, built from a source checkout (
dapi on PATH is a junction into apps/cli)
- Setup:
git clone → npm install → cp apps/web/.env.example apps/web/.env → npm run dev:desktop
Bug 1 — the CLI build script fails on Windows (chmod)
apps/cli/package.json ends its build with:
"build": "esbuild ... --outfile=dist/index.js && chmod +x dist/index.js"
On Windows that is:
'chmod' is not recognized as an internal or external command,
operable program or batch file.
npm error Lifecycle script `build` failed with error:
npm error code 1
npm run dev:desktop builds the CLI as its first step, so the whole dev stack aborts before Vite or Electron ever start.
chmod is only needed for the macOS/Homebrew symlink and should be a no-op where it does not apply:
"build": "esbuild ... --outfile=dist/index.js && node -e \"if(process.platform!=='win32')require('fs').chmodSync('dist/index.js',0o755)\""
Bug 2 — the CLI handshake never gets a reply on Windows named pipes
With the app running and the named pipe present (\\.\pipe\diffusion-studio), every command fails:
$ dapi context
Unexpected end of JSON input
apps/cli/src/cli-client.ts (requestConnection) does:
sock.on("connect", () => sock.end(JSON.stringify(handshake)));
and apps/desktop/src/cli-server.ts answers on the socket's end event.
On Windows named pipes a half-close does not survive: the pipe goes down as soon as the client ends its side, so the app's 'end' handler races a torn-down socket and sock.end(JSON.stringify(reply)) writes to a dead pipe. The CLI sees EOF with an empty buffer, hence JSON.parse("").
Minimal repro (no Diffusion Studio involved, Windows only)
import { createServer, connect } from "node:net";
const PIPE = "\\\\.\\pipe\\ds-halftest";
const srv = createServer({ allowHalfOpen: true }, (sock) => {
let buf = "";
sock.setEncoding("utf8");
sock.on("data", (c) => (buf += c));
sock.on("end", async () => {
await new Promise((r) => setTimeout(r, 50)); // any async work
if (!sock.destroyed) sock.end(JSON.stringify({ ok: true }));
});
sock.on("error", () => {});
});
srv.listen(PIPE, () => {
const c = connect(PIPE);
let got = "";
c.setEncoding("utf8");
c.on("data", (d) => (got += d));
c.on("end", () => { console.log("reply:", JSON.stringify(got)); process.exit(0); });
c.on("connect", () => c.end(JSON.stringify({ port: 1, token: "x" })));
});
Result on Windows: reply: "", and the server's 'end' handler never runs.
If the client writes without half-closing (c.write(...)) and the server processes the handshake on the first complete data chunk, the reply arrives correctly.
Suggested fix (works on all platforms)
cli-client.ts: sock.write(...) instead of sock.end(...), letting the app close the socket after replying.
cli-server.ts: parse the handshake from accumulated data as soon as it is complete, keeping the end handler as a fallback for peers that do half-close (macOS/Linux).
Also worth documenting
openProject only calls launchApp() when process.platform === "darwin", so on Windows dapi open cannot start the app — the user has to launch Diffusion Studio manually first. That is easy to miss when following the source-setup instructions.
Workaround for anyone hitting this today
npm run dev:desktop # after patching the chmod line
# then, with the app running:
dapi context
Summary
Two independent defects make the source-checkout +
dapisetup unusable on Windows. Both have to be fixed beforedapican talk to the app at all.dapion PATH is a junction intoapps/cli)git clone→npm install→cp apps/web/.env.example apps/web/.env→npm run dev:desktopBug 1 — the CLI build script fails on Windows (
chmod)apps/cli/package.jsonends its build with:On Windows that is:
npm run dev:desktopbuilds the CLI as its first step, so the whole dev stack aborts before Vite or Electron ever start.chmodis only needed for the macOS/Homebrew symlink and should be a no-op where it does not apply:Bug 2 — the CLI handshake never gets a reply on Windows named pipes
With the app running and the named pipe present (
\\.\pipe\diffusion-studio), every command fails:apps/cli/src/cli-client.ts(requestConnection) does:and
apps/desktop/src/cli-server.tsanswers on the socket'sendevent.On Windows named pipes a half-close does not survive: the pipe goes down as soon as the client ends its side, so the app's
'end'handler races a torn-down socket andsock.end(JSON.stringify(reply))writes to a dead pipe. The CLI sees EOF with an empty buffer, henceJSON.parse("").Minimal repro (no Diffusion Studio involved, Windows only)
Result on Windows:
reply: "", and the server's'end'handler never runs.If the client writes without half-closing (
c.write(...)) and the server processes the handshake on the first completedatachunk, the reply arrives correctly.Suggested fix (works on all platforms)
cli-client.ts:sock.write(...)instead ofsock.end(...), letting the app close the socket after replying.cli-server.ts: parse the handshake from accumulateddataas soon as it is complete, keeping theendhandler as a fallback for peers that do half-close (macOS/Linux).Also worth documenting
openProjectonly callslaunchApp()whenprocess.platform === "darwin", so on Windowsdapi opencannot start the app — the user has to launch Diffusion Studio manually first. That is easy to miss when following the source-setup instructions.Workaround for anyone hitting this today