Skip to content

Add magpie-agent-console (page plugin) - #535

Merged
yetone merged 8 commits into
magpie-community:mainfrom
qingnian108:add-magpie-agent-console
Oct 31, 2026
Merged

yetone merged 8 commits into
magpie-community:mainfrom
qingnian108:add-magpie-agent-console

Conversation

@qingnian108

Copy link
Copy Markdown
Contributor

Summary

Test plan

  • magpie plugin add magpie-agent-console (after npm publish)
  • Page appears under Plugins → Pages
  • Icon loads

@yetone yetone left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. I reviewed the npm package the entry points to, magpie-agent-console@0.1.0: I unpacked the tarball and read index.mjs, page/ and daemon/dist/main.js. I can't add it to the market yet. One security hole has to be fixed first, and a few things need the maintainer under this repo's contributor rules.

1. Any web page can read the daemon's token and drive the user's agents with full access.

  • checkOrigin (daemon/dist/main.js:5036) accepts Origin: null:
    if (origin === void 0 || origin === "null" || origin === "") return { ok: true };
    For that origin, the preflight answers access-control-allow-origin: null (:5074).
  • GET / serves app/index.html with __TOKEN__ replaced by the real token (:5146).
  • A sandboxed iframe, or a data: or file: page, sends Origin: null. So any website the user visits can:
    1. fetch http://127.0.0.1:3488/;
    2. read the token out of the HTML;
    3. open the WebSocket, which runs the same checkOrigin, and send hello with the token.
  • From there it has the whole RPC: project.upsert, thread.setMode and thread.send. bypassPermissions maps to Codex danger-full-access with approval never (:2473), so that is code execution on the user's machine.

I checked this on a Mac mini in a sandbox HOME (npm install --ignore-scripts, daemon started on port 3591, no real accounts):

curl -H "Origin: null" http://127.0.0.1:3591/            -> 200, access-control-allow-origin: null, token in the page
curl -H "Origin: https://evil.example" http://127.0.0.1:3591/  -> 403
GET /api/ping with that token and Origin: null           -> ok

magpie had this same class of bug (an Origin: null page spending a subscription without a key) and fixed it. Please:

  • treat null as foreign;
  • never put the token in a page an unauthenticated GET can read: hand it to the frame through magpie's page bridge instead;
  • check Host too, to cover DNS rebinding (a rebound page's GET carries no Origin at all).

2. The daemon starts when the module loads, and outlives magpie. index.mjs spawns daemon/dist/main.js with detached: true and unref() (:147-152) as soon as the plugin is imported, before the user opens the page. Please start it when the page opens, and stop it when magpie exits.

3. Things the maintainer has to clear first (from docs/contributors.md, "Look for harm first"):

  • Runtime dependencies, including a native module (node-pty) and @anthropic-ai/claude-agent-sdk, all on caret ranges.
  • Minified or bundled code: app/app.js is a 977 KB bundle and daemon/dist/main.js is 252 KB, neither built in this repo. To review it, we need the source and a lockfile that rebuilds these bytes exactly.
  • npm 0.1.0 was published after this PR was opened. The entry points at an external package whose contents can change without a PR here, so the version would have to be pinned, or the source brought into packages/.

I'm requesting changes for item 1. Once it's fixed, please say which version to look at, and I'll check it again.

@qingnian108

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review. All three points are addressed in magpie-agent-console@1.0.0 (source: qingnian108/magpie-agent-console@0d3c19e, tag v1.0.0; tarball sha512-2KRz4PArGHVbUeHDIDosvbdJQpJ4GgctUYexrA0QAlOehE7ETcZz2y6i4JwTu8Ng5lEehup+fnMMzmh86IHqtA==).

1. Token / Origin

  • Origin: null is now foreign: static pages answer 403 to it, and token routes accept it only with a valid token.
  • No HTML contains the token anymore. index.mjs writes page/session.js with only a gate port and a one-run gate key. The page navigates to the gate's /open?k=<key>, which 302s to the daemon with the token in the # fragment only, so it never sits in a GET-readable response.
  • Host is checked on every HTTP and WS request (421 on mismatch), against DNS rebinding.
  • Your first curl now gets 403 with no token in any response, the second stays 403, and the token for the third can no longer be obtained by a foreign page. Covered in test/v1/pr535-http.test.ts and test/v1/gate.test.ts.

2. Lifecycle

  • Importing the module has no side effects.
  • The daemon starts only when the page opens. It runs as a non-detached child on a random port, with a stdin lifeline and --parent-pid, and exits when magpie's plugin host exits or after 60 idle minutes (test/v1/lifeline.test.ts).

3. Maintainer items

  • All dependencies are pinned to exact versions, and package-lock.json is committed.
  • npm ci && npm run build && npm run verify:build rebuilds app/ and daemon/dist/ byte-for-byte from source (sha256 list in scripts/verify-build.mjs; line endings fixed via .gitattributes).
  • On pinning: registry.json has no version field that I could find. Would you prefer a "version": "1.0.0" field, a package@1.0.0 spec, or moving the source into packages/? Happy to do whichever you prefer.

@yetone

yetone commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

Thanks for the quick turnaround, and for the detail. I haven't reviewed the fixes yet, because they aren't in this PR. Its head is still 38e9f19 from 10-30, and the diff is the same 10-line registry.json entry.

On pinning, which answers your question: registry.json has no version field. magpie doesn't read one either (Listing in magpie's internal/plugin/market.go takes package, name, icon, providers, kind, summary). magpie installs whatever npm marks latest and offers that package's later versions as updates. So "version": "1.0.0" would be ignored, and so would a package@1.0.0 spec, which isn't a package name. Merging this entry would approve every future version of magpie-agent-console, none of which would pass through a PR here. That's acceptable for a small plugin, but not for one that runs a daemon able to drive Claude Code and Codex with full access, together with a native module.

So please move the source into this PR as packages/agent-console/, the way the other page plugins are done (packages/chat has a host part like yours; packages/agent-office is another example):

  • name it @magpie-community/page-agent-console, with the magpie.page and permissions fields as in the README's "Pages" section;
  • include the TypeScript source, package-lock.json and the build script, so the repo's own checkout rebuilds app/ and daemon/dist/ (no committed bundles, or bundles that verify:build checks byte for byte at the PR head);
  • include the tests (test/v1/pr535-http.test.ts, gate.test.ts, lifeline.test.ts), runnable from the package folder;
  • point the registry entry at the new name. After that, versions ship from this repo through publish.yml and the dist branch, so every change is a PR.

What I looked at in 1.0.0 meanwhile (tarball sha512 matches yours; I read it and ran nothing): the gate design looks like the right shape. session.js holds only the gate key, the gate refuses any Origin, the token travels only in the #t= fragment, Host is checked on HTTP and WS (checkHost), and the daemon is a non-detached child with a stdin lifeline. I'll review every line, and run your tests and my Origin: null / Host probes again, once the code is in the PR.

One more thing to fix while moving it: the daemon imports only ws, zod, ignore, @agentclientprotocol/sdk, @anthropic-ai/claude-agent-sdk and node-pty. The other 11 dependencies (react, react-dom, @xterm/*, lucide-react, marked, dompurify, highlight.js, zustand, @tanstack/react-virtual, clsx) are only bundled into app/app.js, but every user's install still downloads them. Please make them devDependencies. Also say why node-pty has to be a beta (1.2.0-beta.15), and what happens on a machine where its native build or prebuild fails.

qingnian108 and others added 7 commits October 31, 2026 10:12
A Magpie window page that drives local Agent CLIs (Claude Code, Codex, Cursor, and others) via a persistent Node daemon.

Co-authored-by: Cursor <cursoragent@cursor.com>
The source of magpie-agent-console 1.0.0 (qingnian108/magpie-agent-console@0d3c19e,
tag v1.0.0), moved here as @magpie-community/page-agent-console so every
version ships from this repository through a pull request (review of magpie-community#535).

- TypeScript source (daemon/src, web/src), index.mjs, lib/, page/, assets/,
  build and verify scripts, tsconfigs, vitest config, package-lock.json,
  LICENSE, README, THIRD-PARTY-NOTICES and the vitest suite (test/v1,
  including pr535-http, gate, lifeline and the new terminal-fallback).
- app/ and daemon/dist/ are committed: publish.yml packs and publishes each
  package as checked in (npm pack --ignore-scripts, no build step), so a PR's
  bundles are what users get. `npm ci --workspaces=false && npm run
  verify:build` in the folder rebuilds them byte for byte.
- package.json: magpie.page and permissions: [] (the page makes no
  magpie.read), magpie.icon as a data URI, repository/bugs/homepage here.
- dependencies are what the daemon and index.mjs load (ACP SDK, Claude Agent
  SDK, ignore, ws, zod); node-pty is optional; the 11 UI packages bundled
  into app/app.js are devDependencies. Versions and lockfile integrities
  unchanged.
- README: why node-pty is 1.2.0-beta.15 and what happens when it can't load.
- agent-console.test.mjs: what the root `bun test` can check with no install
  (package shape, dependency split, the page reads nothing and reaches no
  network, the gate's Origin/Host/key rules).
- bunfig.toml: the root `bun test` skips the package's vitest files, which
  need its npm dependencies.
- README: the package in the Pages table.
…mmunity page

Points the entry at the package now in packages/agent-console, marks it community like the other entries, and drops the icon URL into the author's GitHub repository (the package carries its own magpie.icon).
Same change as qingnian108/magpie-agent-console community-move fbcd437.
Cursor, Copilot, OpenCode, Pi, Devin CLI and dsh (AcpSession) used to drop
attached images, and an image-only message ended without a prompt.

- Images go as ACP image blocks when the Agent's initialize advertises
  promptCapabilities.image, else as the file's path (text + resource_link)
  with a timeline notice; audio and text files likewise by capability.
  Inline media is capped at 10 MB a file, 16 MB a message.
- An image-only message starts a turn.
- capabilities.images for ACP providers follows what the probe or a session's
  initialize reported (imagesKnown: false until then); the composer notes when
  the Agent will get a path instead.
- test/v1/acp-images.test.ts with a fake ACP agent fixture.
- app/ and daemon/dist/ rebuilt; npm run verify:build matches.
…nput

Mirrors magpie-agent-console 54cba6d: PNG/JPEG/GIF/WebP go as images; Claude
Code gets text files as plain-text documents; Codex gets text by path; other
files go by path with a notice. Adds test/v1/attachment-content.test.ts.
Mirrors magpie-agent-console 17e1a29: dsh answers promptCapabilities.image for
the route its acp row starts on and refuses image blocks otherwise, so dsh is
started with a --patch naming the conversation's route. Adds
test/v1/dsh-images.test.ts and an opt-in npm run test:real (real CLIs, a
throwaway HOME and a local stand-in for the model API).
Mirrors magpie-agent-console 077b627: pick session/load or session/resume from
the Agent's initialize; dsh uses resume. Adds test/v1/acp-resume.test.ts and
an opt-in real dsh check.
@qingnian108
qingnian108 force-pushed the add-magpie-agent-console branch from 38e9f19 to 41c7a94 Compare October 31, 2026 03:33
@qingnian108

Copy link
Copy Markdown
Contributor Author

@yetone Thanks again for the review. I've reworked this PR to match what you asked for and pushed an update (force-push of add-magpie-agent-console).

Layout / package

  • Source now lives under packages/agent-console.
  • Package name is @magpie-community/page-agent-console (kind page), with community: true in registry.json.

Dependencies / shipping

  • Runtime dependencies are only what the daemon needs at run time; UI packages are devDependencies.
  • node-pty is an exact-pinned optional dependency, with a graceful non-PTY terminal fallback when the prebuild can't load (documented in the README).
  • app/ and daemon/dist/ stay committed; npm run verify:build checks they match a fresh build so Magpie's npm pack --ignore-scripts path stays reproducible.

Security (from the earlier pass; still in place)

  • Origin: null is treated as foreign / untrusted (token is the only authority).
  • Host is checked on every HTTP request and WebSocket upgrade (127.0.0.1 / localhost + bound port).
  • The daemon token is handed off only in the URL fragment (#t=…), never embedded in HTML served by the daemon.

Extra fixes in this update

  • All eight Agents can attach images (Claude Code / Codex natively; ACP Agents via promptCapabilities.image, with a path fallback when they don't advertise it). For dsh specifically, we start its ACP on the conversation's model so it advertises and accepts image blocks when that model supports vision.
  • Reopening an old dsh conversation no longer calls session/load (which dsh doesn't implement). We pick session/load or session/resume from what initialize advertises; dsh uses session/resume.

Verification

  • Tested on Linux (including real Magpie via magpie web) and on Windows.
  • Magpie's macOS desktop window (wails:// → http://127.0.0.1) has not been verified on real hardware yet — happy to take feedback there.

Could you take another look when you have a moment? Thanks.

@yetone yetone left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the rework. I reviewed 41c7a94. I read index.mjs, lib/gate.mjs, lib/pathscan.mjs, page/ and the daemon source behind every route: security.ts, http.ts, ws/gateway.ts, main.ts/config.ts, the providers' spawn and permission modes, magpieGateway.ts, and the history import. I also ran it on a Mac mini in a throwaway HOME, with no real accounts.

What I ran

  • npm ci --ignore-scripts, then node scripts/verify-build.mjs. All five bundles rebuild byte for byte (app/app.js 9a6911f3…, daemon/dist/main.js 324ab13b…).
  • vitest run: 44 files, 240 passed, 17 skipped (the test/real ones). That includes the gate, the HTTP/WS policy (Origin null, a foreign Origin, a bad Host, a WS without hello), the lifeline, and the server() → session.js → /open → daemon → stop() run.
  • The repo root: HOME=$(mktemp -d) bun test gives 3997 pass, 0 fail. bun run check is clean.
  • Installed the way magpie installs a plugin: npm pack --ignore-scripts, then bun add --ignore-scripts <tgz> into an empty folder. It installs, index.mjs imports with no side effects, and node-pty loads from its prebuild.

The October 30 review: met

  1. Token. No HTML has the token any more, and an unauthenticated GET / is clean. Origin null is refused on static routes. CORS null comes back only after a valid token. Foreign Origins are always refused. Host is checked on HTTP and on the WS upgrade. The gate key / fragment handoff holds up: a leaked session.js can't reach the API.
  2. Daemon lifetime. It's no longer detached. It starts on the first /open, holds a stdin lifeline, polls its parent pid, and exits after 60 idle minutes. The 0.1.0 daemon is shut down after a token ping.
  3. Maintainer items. The source is in packages/, every direct dependency is pinned exactly, the UI packages are devDependencies, and there are no install scripts. I looked for harm: the only addresses it calls are local (/v1/models on a 127.0.0.1/localhost base). It reads ~/.claude/settings.json and magpie's applied.json read-only. I found no telemetry, no eval/new Function, and no credential reads. I've cleared the dependency list and the bundles.

One thing to change before it goes in the market: say what it downloads

A cold install through magpie's path is 367 MB in node_modules:

272M  @anthropic-ai/claude-agent-sdk-darwin-arm64   (the SDK's native Claude Code binary, an optionalDependency of the SDK)
 26M  node-pty
 23M  @anthropic-ai/sdk
  4.7M @anthropic-ai/claude-agent-sdk

The chat page, the closest page plugin, has no runtime dependencies. This one is a click in the market that downloads about 370 MB into magpie's shared plugins/ folder. magpie gives an install 5 minutes, and a bun add cut off halfway leaves that folder half-written for every plugin. The README and the registry summary never mention the size. The 272 MB binary is only used when claudeExecutable() returns null: a Windows .cmd/.ps1 shim, or no claude found. Everyone else runs their own claude.

Please:

  • add one line to the README and to the registry summary, in en and zh, giving the install size and that it includes a Claude Code binary and node-pty's native terminal module. Then the user decides before clicking;
  • if you can find a way to drive the user's own claude everywhere without installing the SDK's platform binary, that is better, but it isn't required.

Notes, not blocking

  • The root bunfig.toml is needed (root bun test can't run vitest files), but it also means nothing on CI runs this package's suite. We'll add a step for it to pr.yml on our side. You don't need to touch .github.
  • Your README says a leaked gate key lets a page "at most open the console UI in a frame". The daemon's pages send no frame-ancestors/X-Frame-Options, so that frame can be clickjacked into a UI that can run agents in bypassPermissions. Reaching the key takes magpie's per-run plugin path, so the risk is low. If you find a policy that still works inside magpie's own frame, it's worth adding.
  • bun add doesn't read package-lock.json, so the dependencies of your dependencies resolve at install time. That's how every plugin installs, so it isn't something to fix here.

Once the size line is in, say which head to look at and I'll merge it.

@qingnian108

Copy link
Copy Markdown
Contributor Author

@yetone Thanks — head is now de011d1.

Install size (your required change)

  • Cold install the Magpie way (npm pack --ignore-scripts → bun add --ignore-scripts) now measures ~49 MB of node_modules (mostly node-pty ~26 MB).
  • Claude Code is not bundled anymore. We vendor @anthropic-ai/claude-agent-sdk without its platform optionalDependencies (the ~270 MB native Claude Code binaries) and bundle that SDK JS into daemon/dist/main.js. Sessions always use the user's own claude via pathToClaudeCodeExecutable.
  • One sentence in en + zh is in the package README and in the registry summary.

Optional notes

  • frame-ancestors / clickjacking: skipped for now. Magpie web's parent origin is effectively any host:port, and WebKit/wails:// matching is easy to get wrong; I did not want to risk breaking Magpie's iframe. Happy to take a policy you prefer.
  • Mac desktop Magpie (wails:// → http://127.0.0.1) is still unverified on real hardware (Linux Magpie web + Windows are covered).

Please re-check and merge when you are happy. Thanks again.

@yetone yetone left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. Getting rid of the 272 MB binary was the better fix I mentioned, and the build side holds up. I reviewed de011d1 and read every line of the diff since 41c7a94. One thing blocks the merge: the license of the code that is now vendored and bundled.

What I ran at de011d1 (Mac mini, throwaway HOME, no real accounts)

  • npm ci --ignore-scripts --workspaces=false, then node scripts/verify-build.mjs. All five bundles match byte for byte (daemon/dist/main.js b4c856df…, app/app.js 9a6911f3…).
  • I replaced vendor/claude-agent-sdk with the untouched npm tarball of @anthropic-ai/claude-agent-sdk@0.3.312 and rebuilt. It gives the same main.js. The vendored copy differs from npm only in package.json (the optionalDependencies dropped, and the description) and in CRLF line endings in sdk.d.ts. So the bundle hides no code of its own.
  • vitest run: 44 files, 240 passed, 17 skipped. npm run check is clean. The repo root's HOME=$(mktemp -d) bun test gives 3997 pass, 0 fail, and bun run check is clean.
  • Installed the way magpie installs a plugin: npm pack --ignore-scripts, then bun add --ignore-scripts <tgz>. That gives 48 MB on macOS, 49 MB on Linux (WSL2 Ubuntu 24.04) and 45.6 MB on Windows 11. No @anthropic-ai/* is installed. index.mjs imports on all three. node-pty loads from its prebuild with no scripts and runs a command in a pty on all three.

1. Blocker: the Claude Agent SDK can't be redistributed in this package

vendor/claude-agent-sdk/LICENSE.md says:

© Anthropic PBC. All rights reserved. Use is subject to the Legal Agreements outlined here: https://code.claude.com/docs/en/legal-and-compliance.

package.json has "license": "SEE LICENSE IN README.md", and that README points to Anthropic's Commercial Terms. Neither grants a right to redistribute. Before, a user's install downloaded the SDK from Anthropic's own npm package. Now this PR puts a copy of Anthropic's code in our repository (vendor/), and publish.yml would publish it inside daemon/dist/main.js (about 1.4 MB of the 1.67 MB file) as @magpie-community/page-agent-console, under that package's MIT LICENSE. We can't host or publish that. The package's own MIT license would also seem to cover code that isn't MIT.

Either of these fixes it:

  • (a) Depend on the SDK from npm again. Make it an exact-pinned dependency and drop the alias in build-v1.mjs, so packages: "external" leaves it as an import. Then put the real size in the README and the registry summary, in en and zh: about 370 MB, including a Claude Code binary for the user's platform. That was all the last review asked for, and it's the smaller change.
  • (b) Drop the SDK. Drive the user's own claude over its stream-json protocol in your own code (claude -p --input-format stream-json --output-format stream-json --verbose, with permission prompts over the control messages), the way you already drive codex app-server. That keeps the ~49 MB install, and no Anthropic code ships in the package. It is more work and needs its own tests against a real claude.

Please don't download the SDK at run time instead (npm install from the daemon on first use). That is code that never passes through a PR here.

Remove vendor/claude-agent-sdk/ from the branch whichever way you go. A squash merge would otherwise still carry it into main's history.

2. Smaller things in this diff (fix them with 1; they only matter if you take (b))

  • ClaudeSession now throws when claude resolves to a .cmd/.ps1 wrapper. Until now, the SDK's own binary covered those Windows users. If you keep (b), please give the error in English too: the throw is Chinese only, while the rest of the UI follows the user's language.
  • THIRD-PARTY-NOTICES.md still lists @anthropic-ai/claude-agent-sdk under "Installed with the plugin (dependencies)". That's no longer true at this head.
  • package.json's Chinese description and titles were rewritten as \uXXXX escapes. The JSON is the same, but it makes the file harder to read. Probably a side effect of a tool. Please write them back as plain text.

Notes, not blocking

  • agent-console.test.mjs now looks only at ^import … from lines. That's fine for this esbuild output, since its require("ajv/…") lines are ajv's code-generation strings, not real imports. If you take (a), the SDK goes back on the RUNTIME list and the test's "no claude-agent-sdk import" line comes out.
  • Not tried: a real Claude Code turn through the bundled SDK, and magpie's macOS desktop window (you said the same).

Once 1 is fixed, tell me the head to look at. I'll rerun the same checks and merge it.

…MB install

The Claude Code provider no longer uses any Anthropic package. It starts the
user's own `claude` per conversation:
  claude -p --input-format stream-json --output-format stream-json --verbose
         --include-partial-messages --permission-prompt-tool stdio
         [--model] [--permission-mode] [--effort] [--resume]
and speaks its line-delimited JSON (daemon/src/providers/claude/cli.ts):
initialize (slash commands), can_use_tool asks answered allow/deny,
control_cancel_request, interrupt, set_model, set_permission_mode.
bypassPermissions and effort relaunch with --resume. A .cmd shim runs
through cmd.exe with escaped args, a .ps1 through PowerShell -File, an npm
shim as node + cli.js; argv values are restricted to plain tokens. Errors
for a missing / dead claude are English with zh i18n.

Removed the SDK from dependencies, the lockfile, THIRD-PARTY-NOTICES and
docs. A cold install (npm pack + bun add --ignore-scripts) is ~46 MB of
node_modules, mostly node-pty; README and the registry summary say so in
en and zh, and that users install claude themselves.

Tests: a stand-in stream-json claude (streaming, approve/deny, withdrawn
ask, interrupt, model/mode switch, resume, images, commands, crash, broken
shim, missing claude); opt-in real tests against claude 2.1.316 with a
local fake Messages API.
@qingnian108
qingnian108 force-pushed the add-magpie-agent-console branch from de011d1 to 8e8f910 Compare October 31, 2026 05:16
@qingnian108

Copy link
Copy Markdown
Contributor Author

@yetone Thanks for catching the license problem. The new head is 8e8f910.

The SDK is gone, including from history

  • I rebuilt the branch as clean commits on the earlier head 41c7a94. No commit in upstream/main..8e8f910 contains vendor/claude-agent-sdk. I checked with git log -- '*claude-agent-sdk*' 'packages/agent-console/vendor' and with git ls-tree on every commit.
  • @anthropic-ai/claude-agent-sdk no longer appears in dependencies, devDependencies, package-lock.json, daemon/dist/main.js, THIRD-PARTY-NOTICES or the docs. Nothing is downloaded at runtime.

Claude Code now runs through the user's own claude CLI, over stream-json

  • It works like we drive codex app-server. Each conversation starts claude -p --input-format stream-json --output-format stream-json --verbose --include-partial-messages --permission-prompt-tool stdio, plus --model, --permission-mode, --effort and --resume as needed. The code is in daemon/src/providers/claude/cli.ts and was written from scratch against the CLI's protocol.
  • It speaks the control messages: initialize (slash commands), can_use_tool (answered allow/deny from the permission card), control_cancel_request, interrupt, set_model and set_permission_mode. Switching into bypassPermissions or changing effort restarts the CLI with --resume. Images still go as base64 PNG/JPEG/GIF/WebP blocks.
  • On Windows, an npm .cmd shim runs as node + cli.js. Any other .cmd runs through cmd.exe with escaped arguments, and a .ps1 through powershell -File. Argument values are limited to plain tokens. If claude is missing or exits, the user gets an English error (with zh i18n) that says Claude Code is not bundled and how to install it.

Small items

  • THIRD-PARTY-NOTICES no longer lists the SDK.
  • The Chinese description in package.json is readable UTF-8 again.
  • The build alias and the vendor directory are gone.

Size: a cold install (npm pack --ignore-scripts, then bun add --ignore-scripts) is now about 46 MB of node_modules, mostly node-pty (~26 MB). The tarball is 0.55 MB. The README and the registry summary say this in English and Chinese, and tell users to install claude themselves.

Tests

  • Package: 45 test files, 254 passed, 17 skipped. A stand-in stream-json claude covers streaming, approve, deny, a withdrawn permission request, interrupt, model and mode switch, resume, images, slash commands, a crash, a broken shim and a missing claude.
  • Opt-in real tests ran against Claude Code 2.1.316 with a local fake Messages API, with no sign-in: initialize, streaming, a Bash permission approved and denied, interrupt, resume with a model switch, and the image reaching the model request. All passed.
  • check and verify:build pass. At the repo root, bun test gives 4003 pass, 0 fail, and bun scripts/check.mjs passes.
  • I haven't tested Magpie desktop on a real Mac yet.

Could you re-run the checks and merge if it looks good? Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants