* feat: add zh-TW locale and OpenAI-compatible provider support
Add Traditional Chinese (zh-TW) i18n locale and an OpenAI-compatible
provider so the app can connect to any OpenAI-compatible API endpoint.
Also includes KNOWN_PROVIDERS config and SettingsDialog model options
merge from upstream.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: address review — SSRF, credential leaks, zh-CN regression, model heuristic
P1: Block internal IPs and non-http protocols in proxy SSRF validation.
P1: Scrub Bearer tokens from upstream error messages sent to browser.
P1: Log only hostname + model instead of full URL to prevent key leakage.
P2: Restore zh-CN in the supported locale list alongside zh-TW.
P2: Check known model prefixes (gpt-, o1, o3, o4) before URL-based
fallback in isOpenAICompatible heuristic.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* test: add e2e ui automation suite
* fix review feedback for ui e2e suite
Resolved the FileWorkspace.tsx merge-marker issue and kept the intended combination of multiple, accept="image/*", and data-testid.
Updated the e2e port handling so the test config no longer relies on a single hardcoded app port. It now resolves an available port first and passes the same port selection through the dev server and Playwright base URL. Since main has moved to the Next.js dev stack, this was also adapted from the old Vite-based flow to NEXT_PORT.
Kept test:ui serialized so cleanup completes before Playwright starts.
Updated reset-e2e-artifacts.mjs so cleanup failures are surfaced with a warning instead of being silently swallowed, except for the expected ENOENT case.
* Refactor project name from "Open Claude Design" to "Open Design"
- Updated project name in package.json, package-lock.json, and README files.
- Changed CLI commands and references from "ocd" to "od".
- Adjusted file structure references in documentation and code to reflect new naming conventions.
- Enhanced .gitignore to include new runtime data files.
- Updated metadata in LICENSE file to match new project name.
* chore: migrate frontend toolchain from Vite to Next.js 16 App Router
Replace the Vite SPA scaffold with Next.js 16 App Router while keeping
the existing daemon as the API/SSE/sqlite backend. The whole client
tree now mounts under a single optional catch-all route
(app/[[...slug]]) loaded with ssr:false; static export emits one shell
HTML the daemon serves as the SPA fallback for deep links. Dev uses
next.config rewrites to proxy /api, /artifacts, /frames to the daemon,
matching the previous Vite setup.
Made-with: Cursor
* fix: address Next migration review feedback
* fix: serve static export in preview script
---------
Co-authored-by: mrcfps <mrc@powerformer.com>
* fix(daemon): mitigate Windows ENAMETOOLONG and fix daemon crash on temp-file cleanup
On Windows, child_process.spawn caps the command line at ~8,191 chars
(shell:true for .cmd/.bat npm shims via cmd.exe) and ~32,767 chars
(direct CreateProcess for .exe). When using Claude Code or Copilot CLI
(agents without promptViaStdin), the composed prompt (system prompt +
design system + skill body + user message) can exceed these limits,
causing spawn ENAMETOOLONG.
Mitigation: when the prompt exceeds the threshold, write it to a temp
file `.od-prompt.md` inside the project directory and pass a short
bootstrap message telling the agent to Read the file before responding.
- shell:true threshold: 4,000 chars (accounts for cmd.exe escaping overhead)
- Direct exe threshold: 30,000 chars
- Diagnostic logging only on Windows
- Temp file auto-cleaned on spawn error or child process exit
The temp-file cleanup used `fs.unlink(path).catch(() => {})`, but the
top-level `fs` module's unlink is callback-style and returns undefined,
not a Promise. Calling .catch() on undefined throws
ERR_INVALID_ARG_TYPE and crashes the daemon process.
Fixed with callback form `fs.unlink(path, () => {})` and extracted into
an idempotent `cleanPromptFile()` function to prevent double-delete when
both the spawn catch block and the child close handler fire.
- Eliminated duplicate `resolveAgentBin()` call within the same request
- Extracted constants: CMD_BAT_RE, PROMPT_TEMP_FILE, promptFileBootstrap()
- Reused resolvedBin variable instead of a second resolveAgentBin call
Close#52, Close#72
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(daemon): address PR review feedback on ENAMETOOLONG mitigation
- Use per-request unique temp filenames (timestamp + random suffix) to
prevent race conditions when concurrent requests target the same project
- Normalize backslashes to forward slashes in the bootstrap message path
for Windows compatibility with agents that shell out to POSIX tools
- Clean up temp prompt file on the missing-binary early-return path
- Raise shell:true threshold from 4000 to 6500 chars (cmd.exe escaping
overhead is ~1.1-1.3x, not 2x, so the old value was overly conservative)
- Log only the basename of the resolved agent binary to avoid leaking
user home-directory paths in diagnostic output
- Add comment documenting the idempotency guard ordering in
cleanPromptFile (flag set synchronously before async fs.unlink)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
The composed prompt (system instructions + skill body + cwd hint + file
listing + user message) can easily exceed Windows' CreateProcess limit of
~32 KB when passed as a CLI argument via -p <string>. This causes
spawn ENAMETOOLONG whenever Gemini CLI (or Codex, OpenCode, Cursor
Agent, Qwen) is selected on Windows — even for short user messages,
because the skill / design-system system prompt is folded in.
Fix: add promptViaStdin: true to every plain-text agent definition.
The daemon's /api/chat handler checks this flag, opens stdin as a pipe,
writes the composed prompt to it and closes the stream. Claude Code is
unaffected — it still uses the -p argv path and a separate stream-json
parser.
docs/agent-adapters.md: update §5.5 Gemini CLI to document the stdin
delivery strategy, and update the Windows open-question note to reflect
the fix.
Co-authored-by: KNIGHTABDO <abdessamad.aabida-etu@etu.univh2c.ma>
Co-authored-by: pftom <1043269994@qq.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: lefarcen <20859779+lefarcen@users.noreply.github.com>
Two coupled bugs surfaced when the daemon resolved an older `claude`
binary on PATH:
1. Spawn crashed with "unknown option '--include-partial-messages'"
because that flag landed in 1.0.86 and we passed it unconditionally.
2. Even after dropping the flag, the assistant bubble rendered empty —
the stream parser only extracted text from `stream_event` deltas
(only emitted under --include-partial-messages). Without those
events, the full text in the `assistant` wrapper went unread.
Fix:
- Probe `claude --help` once during agent detection and cache which
flags the installed CLI advertises (`agentCapabilities` map).
- `buildArgs` reads the cache and only emits `--include-partial-messages`
/ `--add-dir` when supported; safe baseline when probing fails.
- Warm the cache on server startup (`detectAgents()`) so the first
/api/chat doesn't race ahead of the frontend's /api/agents call.
- Parse text (and thinking) blocks out of the `assistant` wrapper as a
fallback, deduped via a per-message-id `textStreamed` set so newer
builds streaming deltas never double-emit.
Co-authored-by: lukebaze <lukebaze@users.noreply.github.com>
Co-authored-by: lefarcen <20859779+lefarcen@users.noreply.github.com>
* feat(agents): add GitHub Copilot CLI as a code-agent option
Wire `copilot -p "..." --allow-all-tools` into AGENT_DEFS so the daemon
can spawn it alongside the other CLIs. `--allow-all-tools` is required
in non-interactive mode (same tradeoff as Claude's `-p` and Codex's
`exec`). Add a matching GitHub-dark icon with the Copilot two-eye mark.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(copilot-stream): treat result.success as completed when exitCode missing
Strict `obj.exitCode === 0 ? 'completed' : 'error'` mis-flags turns
where Copilot emits a `result` event with `success: true` but no numeric
exitCode. Switch to `obj.success === true || obj.exitCode === 0`. The
asymmetry argument that applies to other defensive ideas in this file
(raw fallback, stringifyResult cap) doesn't apply here — Claude's
`result` event uses a string `stop_reason`, not exitCode, so this is a
Copilot-specific schema-variance fix rather than a one-sided hardening.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(agent-adapters): list Copilot CLI auth paths in §5.7
Adds a one-line note: detection assumes Copilot is already
authenticated, via either `copilot login` (subcommand, OAuth device
flow) or the interactive `/login` slash command inside `copilot` with
no args. Surfaces the assumption so anyone hitting an undetected /
unversioned Copilot install can trace it back.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Adds design-systems/xiaohongshu/DESIGN.md. Lands under Media & Consumer.
- Color, typography, layout, component, and dark-mode tokens sampled
from production CSS at https://www.xiaohongshu.com/explore — the
inline :root,.force-light and :root[dark],.force-dark blocks.
- Brand primary captured as two values: #FF2442 (the --primary token)
and #FF2E4D (hard-coded on .reds-button-new.primary and .active-bar).
Both ship in the live UI.
- Danger / error reuses --primary; no independent error token in source.
- Bookmark / collect star (#FDBC5F) sampled from the inline
<symbol id="collected"> SVG path fill. Digits use the custom RED
Number family (Regular / Medium / Bold).
- Narrative copy in English per CONTRIBUTING.md; real product strings
(brand name, slogan, font name, follow-button labels, profile tab
labels) kept verbatim with English glosses, mirroring
pinterest/DESIGN.md preserving its Japanese font fallback names.
- Upstream VoltAgent/awesome-design-md does not currently include any
Chinese-internet brand, so this lands directly in the OD bundled set
rather than going to upstream first.
* fix: spawn agents via resolved absolute path on Windows (#10)
Detection in `/api/agents` resolves each agent's full executable path,
but `/api/chat` was spawning the bare `def.bin` ("claude"). On Windows
the child process's PATH often doesn't include the user's npm-global
shim directory, so spawn() failed with ENOENT despite the picker
showing the agent as available.
Use the resolved path at spawn time, and pass `shell: true` when the
resolved bin is a `.cmd`/`.bat` shim so Node ≥21 doesn't refuse to
execute it (CVE-2024-27980).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: address PR #13 review — friendlier ENOENT, document shell:true caveats
- When `resolveAgentBin` returns null we now emit a friendly SSE `error`
pointing at /api/agents and end the stream, instead of silently falling
back to spawn(def.bin) — which would re-introduce the exact issue #10
symptom this PR is meant to prevent.
- Strengthen the comment around `shell: true` on Windows: call out that
the only thing keeping user-controlled prompt text safe today is Node's
CVE-2024-27980 escaper, that the proper fix is to route the composed
prompt through child stdin (not a new `-p $prompt`-style flag), and
that cmd.exe's ~8191-char command-line cap reintroduces an
ENAMETOOLONG-class failure for long prompts under shell:true.
- Document why the `.cmd`/`.bat` regex is the right gate: those are the
only PATHEXT extensions that strictly need cmd.exe; `.exe`/`.com`
launch directly and `.ps1`/`.vbs` would need a different host that
shell:true (=cmd.exe) wouldn't help with.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(guizang-ppt): sync host slide counter on transform-paginated decks
go() previously only mutated #deck.style.transform on the parent track,
leaving .slide attributes untouched — so the iframe-host bridge in
src/runtime/srcdoc.ts couldn't detect which slide was active and the
host counter froze at 1/N. Toggle .active on the current .slide so
findActiveByClass returns the right index and the existing
MutationObserver fires a re-report.
* docs(guizang-ppt): mark .slide.active toggle as load-bearing for OD host counter
* fix(daemon): non-interactive permissions for agent CLIs in web UI
Claude Code and other CLIs prompt for tool approval; OD has no terminal
in the browser, so prompts stall the stream. Align spawn args with cwd
sandboxing: Claude bypassPermissions, Codex --full-auto, Gemini/Qwen
--yolo, Cursor --force.
Fixes https://github.com/nexu-io/open-design/issues/25
* fix(daemon): pass --force before -p for cursor-agent
---------
Co-authored-by: zhengyuanqing.zyq <zhengyuanqing.zyq@alibaba-inc.com>
* feat: per-CLI model picker for local agents (closes#8)
Each agent CLI declares its selectable models (and reasoning effort, for
Codex) on the daemon side; the frontend renders a model dropdown in the
avatar menu and the Settings dialog scoped to the currently picked CLI,
persists the choice per-agent in the AppConfig, and threads it through
/api/chat to the spawn argv. "Default" leaves the flag off so the CLI's
own config wins.
* feat(models): fetch live model lists from CLIs, allow custom ids
Each agent definition now declares an optional `listModels` spec; the
daemon runs the CLI's own list-models command (e.g. `opencode models`,
`cursor-agent models`) during agent detection and uses the result as
the dropdown options. Hardcoded entries shrink to a `fallbackModels`
hint that only kicks in when the CLI has no listing command (Claude,
Codex, Gemini, Qwen) or when the listing fails (e.g. unauth'd
cursor-agent).
UI groups `provider/model` ids by provider via <optgroup> so opencode's
~175 live models stay navigable, and the Settings dialog gains a
"Custom…" entry that opens a free-text input for any model id the
listing didn't surface yet. Daemon validates picks against the live
cache + fallback, with a permissive sanitizer for custom ids.
* Refactor project name from "Open Claude Design" to "Open Design"
- Updated project name in package.json, package-lock.json, and README files.
- Changed CLI commands and references from "ocd" to "od".
- Adjusted file structure references in documentation and code to reflect new naming conventions.
- Enhanced .gitignore to include new runtime data files.
- Updated metadata in LICENSE file to match new project name.
* Add contributing guidelines in English and Chinese
- Introduced CONTRIBUTING.md and CONTRIBUTING.zh-CN.md to provide clear instructions for contributors.
- Outlined contribution types, local setup instructions, and merging criteria for skills and design systems.
- Enhanced README files to reference the new contributing guidelines.
* Update README and documentation for deck framework directives
- Clarified DECK_FRAMEWORK_DIRECTIVE description in both English and Chinese README files to specify conditions for deck kind without a skill seed.
- Added detailed workflow instructions in deck-framework.ts to emphasize the importance of copying the framework before adding content.
- Enhanced discovery.ts to reinforce the framework-first approach for deck projects.
- Updated system.ts to ensure proper handling of deck projects with and without bound skills, preventing re-authorship of scaling and navigation logic.
* Update README and documentation for deck framework directives
- Clarified DECK_FRAMEWORK_DIRECTIVE description in both English and Chinese README files to specify conditions for deck kind without a skill seed.
- Added detailed workflow instructions in deck-framework.ts to emphasize the importance of copying the framework before adding content.
- Enhanced discovery.ts to reinforce the framework-first approach for deck projects.
- Updated system.ts to ensure proper handling of deck projects with and without bound skills, preventing re-authorship of scaling and navigation logic.
* Enhance README and add star promotion assets
- Added a "Star us" section in both English and Chinese README files to encourage users to star the project on GitHub.
- Included a new image asset for the star promotion.
- Introduced a new HTML file for a dedicated star promotion page.
- Updated .gitignore to exclude new cursor-related files.
* feat(dev): auto-switch ports on dev:all when defaults are busy
Adds a small launcher (scripts/dev-all.mjs) that probes free ports
for the daemon (OD_PORT, default 7456) and Vite (VITE_PORT, default
5173) before invoking concurrently, so a stray process holding
either port no longer breaks the boot. The resolved ports are
exported into the child env; vite.config.ts now reads VITE_PORT to
keep its dev server and /api proxy aligned with the daemon's actual
port.
Made-with: Cursor
* Allow Claude Code to read skill seeds and design-system specs (#6)
The skill body's preamble points the agent at absolute paths like
`<repo>/skills/guizang-ppt/assets/template.html`, but the agent's cwd
is `.od/projects/<id>/`. Without an explicit allowlist Claude Code
blocks Read on those paths and the user sees a permission error
mid-conversation.
Pass `SKILLS_DIR` and `DESIGN_SYSTEMS_DIR` through `buildArgs` and emit
them as `--add-dir` for Claude so the seed template, references, and
design-system DESIGN.md are all readable. Other agents ignore the
extra dirs (no equivalent flag).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: add verification screenshot for issue #6 fix
Captures the agent successfully Read-ing skills/guizang-ppt/ side files
through the new --add-dir allowlist, confirming the permission error
from issue #6 is gone.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Refactor project name from "Open Claude Design" to "Open Design"
- Updated project name in package.json, package-lock.json, and README files.
- Changed CLI commands and references from "ocd" to "od".
- Adjusted file structure references in documentation and code to reflect new naming conventions.
- Enhanced .gitignore to include new runtime data files.
- Updated metadata in LICENSE file to match new project name.
* Add contributing guidelines in English and Chinese
- Introduced CONTRIBUTING.md and CONTRIBUTING.zh-CN.md to provide clear instructions for contributors.
- Outlined contribution types, local setup instructions, and merging criteria for skills and design systems.
- Enhanced README files to reference the new contributing guidelines.
* Update README and documentation for deck framework directives
- Clarified DECK_FRAMEWORK_DIRECTIVE description in both English and Chinese README files to specify conditions for deck kind without a skill seed.
- Added detailed workflow instructions in deck-framework.ts to emphasize the importance of copying the framework before adding content.
- Enhanced discovery.ts to reinforce the framework-first approach for deck projects.
- Updated system.ts to ensure proper handling of deck projects with and without bound skills, preventing re-authorship of scaling and navigation logic.
* Update README and documentation for deck framework directives
- Clarified DECK_FRAMEWORK_DIRECTIVE description in both English and Chinese README files to specify conditions for deck kind without a skill seed.
- Added detailed workflow instructions in deck-framework.ts to emphasize the importance of copying the framework before adding content.
- Enhanced discovery.ts to reinforce the framework-first approach for deck projects.
- Updated system.ts to ensure proper handling of deck projects with and without bound skills, preventing re-authorship of scaling and navigation logic.
* Enhance README and add star promotion assets
- Added a "Star us" section in both English and Chinese README files to encourage users to star the project on GitHub.
- Included a new image asset for the star promotion.
- Introduced a new HTML file for a dedicated star promotion page.
- Updated .gitignore to exclude new cursor-related files.
The Save button fired both onSave and onClose. onClose's closed-over
`config` still held the bootstrap default (agentId: 'claude'), so it
re-ran setConfig with the stale value right after onSave wrote the
user's choice — leaving the user with Claude Code even when they had
clicked Codex on first run.
Split the responsibilities: Save now owns close (handleConfigSave
calls setSettingsOpen(false)); onClose stays scoped to dismiss flows
(Skip / backdrop) where the stale-config write is harmless.
* Refactor project name from "Open Claude Design" to "Open Design"
- Updated project name in package.json, package-lock.json, and README files.
- Changed CLI commands and references from "ocd" to "od".
- Adjusted file structure references in documentation and code to reflect new naming conventions.
- Enhanced .gitignore to include new runtime data files.
- Updated metadata in LICENSE file to match new project name.
* Add contributing guidelines in English and Chinese
- Introduced CONTRIBUTING.md and CONTRIBUTING.zh-CN.md to provide clear instructions for contributors.
- Outlined contribution types, local setup instructions, and merging criteria for skills and design systems.
- Enhanced README files to reference the new contributing guidelines.
* Update README and documentation for deck framework directives
- Clarified DECK_FRAMEWORK_DIRECTIVE description in both English and Chinese README files to specify conditions for deck kind without a skill seed.
- Added detailed workflow instructions in deck-framework.ts to emphasize the importance of copying the framework before adding content.
- Enhanced discovery.ts to reinforce the framework-first approach for deck projects.
- Updated system.ts to ensure proper handling of deck projects with and without bound skills, preventing re-authorship of scaling and navigation logic.
* Update README and documentation for deck framework directives
- Clarified DECK_FRAMEWORK_DIRECTIVE description in both English and Chinese README files to specify conditions for deck kind without a skill seed.
- Added detailed workflow instructions in deck-framework.ts to emphasize the importance of copying the framework before adding content.
- Enhanced discovery.ts to reinforce the framework-first approach for deck projects.
- Updated system.ts to ensure proper handling of deck projects with and without bound skills, preventing re-authorship of scaling and navigation logic.
* Refactor project name from "Open Claude Design" to "Open Design"
- Updated project name in package.json, package-lock.json, and README files.
- Changed CLI commands and references from "ocd" to "od".
- Adjusted file structure references in documentation and code to reflect new naming conventions.
- Enhanced .gitignore to include new runtime data files.
- Updated metadata in LICENSE file to match new project name.
* Add contributing guidelines in English and Chinese
- Introduced CONTRIBUTING.md and CONTRIBUTING.zh-CN.md to provide clear instructions for contributors.
- Outlined contribution types, local setup instructions, and merging criteria for skills and design systems.
- Enhanced README files to reference the new contributing guidelines.
- Created .gitignore to exclude build artifacts and dependencies.
- Added index.html as the main entry point for the application.
- Included LICENSE file with Apache 2.0 terms.
- Initialized package.json and package-lock.json for project dependencies.
- Added pnpm-lock.yaml for package management.
- Created QUICKSTART.md for setup instructions.
- Added README.md and README.zh-CN.md for project documentation in English and Chinese.