openpencil/Cargo.toml

61 lines
2.7 KiB
TOML
Raw Normal View History

[workspace]
resolver = "2"
# 用 glob 而非显式列表:Step 0 增量创建 crate 时不需要每次改 members(per implementer
# Phase 1 batch 2 的 workspace masking 反馈:列出未存在的 crate 会让 `cargo build -p X`
# 在 resolve 阶段就挂掉)。glob 自动包含 crates/ 下所有 manifest,自然支持增量创建。
members = ["crates/*"]
feat(desktop/chat): Claude Code adapter via anthropic-agent-sdk First of the per-CLI ChatProvider adapters that replace the hand-rolled stream-JSON parser in chat_subprocess.rs. This one wires `anthropic_agent_sdk::query` (the in-workspace fork of bartolli/anthropic-agent-sdk) into the OP chat-panel plumbing. `crates/openpencil-desktop/src/chat_claude.rs`: - `ClaudeCodeProvider` impls `ChatProvider`. Constructs trivially via `new()` (SDK defaults) or `with_options(ClaudeAgentOptions)` when the settings modal has user overrides (system prompt, model pick, allowed-tools list, MCP servers, sandbox config — all 30+ SDK option fields). - `send()` spawns the shared tokio runtime task, calls `anthropic_agent_sdk::query(prompt, options)`, drains its async `Stream<Item = Result<Message>>`, and dispatches each Message through `handle_message`: - `Message::Assistant.content` Vec<ContentBlock> is unpacked per block: `Text { text }` → `ChatDelta::TextDelta`, `Thinking { thinking, .. }` → `Thinking`, `ToolUse { name, input, .. }` → `ToolUse { name, args = input.to_string() }`, `ToolResult` swallowed (already part of conversation history the CLI tracks). - `Message::Result { subtype, is_error, .. }` is the turn terminator. `is_error` → `StopReason::Aborted`; otherwise `map_result_subtype` maps "success" → EndTurn, "error_max_turns" → MaxTokens, error variants → Aborted, unknown → EndTurn. - `System` / `User` / `StreamEvent` swallowed (init / context / partial-stream payloads the chat widget doesn't surface yet). - Receiver-drop short-circuit: every iteration checks `tx.is_closed()` so chat-panel teardown stops the SDK stream promptly without waiting for the CLI to flush more output. - Always emits a terminal `Done` — `Result` message → mapped stop reason; stream EOF without a Result → `EndTurn` fallback. `crates/openpencil-desktop/Cargo.toml`: - Adds `anthropic-agent-sdk = { path = "../anthropic-agent-sdk" }` + `copilot-sdk = { path = "../copilot-sdk" }`. Copilot dep declared now even though `chat_copilot.rs` lands in a follow-up, so Cargo.lock resolves the whole graph in one pass. `crates/openpencil-desktop/src/main.rs`: - `mod chat_claude;` between `mod chat_runtime` and `mod chat_subprocess` so the alphabetical mod-list rule holds. Tests (3 added, all pass): - `map_result_subtype_table` covers the success / error_max_turns / error_during_execution / error / unknown table. - `provider_label_is_human_readable` asserts the chat widget gets "Claude Code" as the displayed label. - `provider_constructs_as_chat_provider_trait_object` is the compile-time type-check that `ClaudeCodeProvider` satisfies the `Send + Sync` bounds so it can live behind `Arc<dyn ChatProvider>` in the widget host. End-to-end smoke testing requires an actual `claude` binary on PATH. The 3 tests here verify the wiring + type contracts but not the live CLI interaction; that lands when the settings modal exposes the "connect" button + we have a real session to drive. 46 openpencil-desktop tests pass (was 43 before this commit). Next: chat_copilot.rs over `copilot_sdk::Client + Session`, then chat_http_server.rs for Codex / OpenCode `serve` mode per the user's "opencode 和 codex 我们调用 http server, 通过 ipc 启动本地的 server 模式".
2026-05-14 08:52:17 +00:00
exclude = ["vendor/agent", "vendor/jian", "vendor/skia-safe-op", "node_modules"]
[workspace.package]
version = "0.1.0"
edition = "2021"
rust-version = "1.85"
license = "MIT"
authors = ["ZSeven-W <fini.yang@gmail.com>"]
repository = "https://github.com/ZSeven-W/openpencil"
[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "1"
anyhow = "1"
tracing = "0.1"
# WASM 边界相关(per kickoff §1.2 黑名单 = 不出现在这里)
# 故意不在 workspace.dependencies 暴露 tokio / reqwest / agent / pen-agent-cli /
# pen-server / native-tls —— 这些只在 native-only crate 内部用 dev-deps 或 target-specific deps。
# Step 1b §3.2 P0.5B Run path (2026-05-09): wasm32-unknown-unknown skia
# build via `vendor/skia-safe-op` — fork of rust-skia 0.97.0 with a new
# build_support/platform/wasm_unknown.rs target that produces .o files
# free of emscripten runtime imports.
#
# IMPORTANT: `[patch.crates-io]` is workspace-global, NOT target-scoped.
# Every `skia-safe` / `skia-bindings` consumer in this workspace —
# including `openpencil-shell-native` (macOS / Linux / Windows desktop,
# `gl` feature) — now resolves through this fork on every target.
# Cargo does not natively support per-target patches, so this is the
# accepted blast radius.
#
# Compatibility surface vs upstream rust-skia 0.97.0:
# - every upstream platform module (macos / linux / windows /
# emscripten / android / ios / ohos / alpine) is byte-identical;
# only `wasm_unknown` is new
# - the dispatch table adds one new arm for
# `("wasm32", "unknown", "unknown", _)`; every existing target
# tuple still resolves to the same upstream module as before
# - the binary-cache feature URL + skia-binaries archive layout are
# untouched, so native desktop builds continue to download the
# prebuilt artifact instead of compiling Skia from source
#
# Trade-offs accepted:
# - upstream rust-skia patches will not flow until we re-vendor
# - `Cargo.lock` records `path` sources for skia-bindings/skia-safe
# instead of `registry+…`, surfacing the fork in audit tools
#
# When upstream rust-skia ships a wasm32-unknown-unknown platform
# module of its own, this whole [patch] block can go away.
[patch.crates-io]
skia-bindings = { path = "vendor/skia-safe-op/skia-bindings" }
skia-safe = { path = "vendor/skia-safe-op/skia-safe" }