fix/scrollback-zero-cap
100 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6ef0a4ad6b | Fix settings localization gaps (#1465) | ||
|
|
9e31d53bdd |
Slim release package
Slim release package |
||
|
|
ea24841939 |
Fix Windows AI model selection
Some checks failed
build-packages / ${{ needs.dedupe.outputs.skip_heavy_ci == 'true' && 'deduped build-linux-x64' || 'build-linux-x64' }} (push) Has been cancelled
build-packages / ${{ needs.dedupe.outputs.skip_heavy_ci == 'true' && 'deduped build-linux-arm64' || 'build-linux-arm64' }} (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / dedupe push run (push) Has been cancelled
build-packages / dedupe result (push) Has been cancelled
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
Fix the Windows issue where AI models could not be selected. |
||
|
|
3408bba303 |
[codex] Add Cursor SDK agent support (#1399)
* feat(ai): add Cursor SDK agent support * fix(ai): harden Cursor SDK support * fix(ai): address Cursor SDK review findings * fix(ai): refresh Cursor environment handling * fix(ai): refresh Cursor discovery scans * fix(ai): enable Cursor recheck without path * Use official Cursor agent icon * Clarify Cursor SDK setup requirements * Split Cursor SDK setup status * Simplify Cursor settings copy * Improve Cursor API key error * Add safe Cursor auth diagnostics * Disable Cursor local sandbox by default * Show Cursor MCP tool names in tool cards * Add spacing inside tool call groups |
||
|
|
9013a7e312 | fix terminal popup release behavior (#1403) | ||
|
|
535b141b23 |
Merge pull request #1322 from lengyuqu/codebuddy
[Codebuddy] sdk |
||
|
|
36267717ac |
feat(terminal): add system manager side panel for processes, tmux, and Docker
Introduce workspace-aware System side panel with remote process/tmux/Docker management, terminal popup for interactive attach, capability warmup, review-hardened IPC, performance optimizations, toast action errors, and SSH channel recovery on reconnect. |
||
|
|
602ca92476 | merge: sync fork with upstream/main (v1.1.31+) | ||
|
|
733e19a6f6 |
perf(settings): reduce Mac settings window input lag (#1347) (#1368)
* perf(settings): reduce Mac settings window input lag (#1347) Debounce custom CSS commits, memoize heavy tabs, and replace Radix ScrollArea with native scrolling so typing and navigation stay responsive on macOS. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(settings): flush debounced textarea on unmount Avoid losing custom CSS edits when the settings window closes before the debounce timer fires. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
4b5993cad6 | fix host sidebar behavior for editor tabs (#1348) | ||
|
|
3203ed7a19 | merge: sync fork with upstream/main (latest) | ||
|
|
f74645e1a4 | chore: upgrade Electron to 42.3.3, @electron/asar to 4.2.0, electron-builder to 26.15.2 | ||
|
|
26a04b22d3 | merge: sync fork with upstream/main | ||
|
|
0792ce1415 | codebuddy sdk 接入 | ||
|
|
b1f930a995 |
feat(sftp): 侧栏 SFTP 增加追随终端目录模式 (#1266) (#1310)
* feat(sftp): add follow terminal directory mode for sidebar (#1266) Add a toolbar toggle that keeps the side-panel SFTP browser synced with the linked SSH terminal cwd, inspired by MobaXterm's follow-folder behavior. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(sftp): probe pwd after commands when follow mode lacks OSC 7 Add a deferred getSessionPwd fallback after terminal commands when follow-terminal-cwd is enabled and the shell did not report OSC 7, and fix settings sync hook dependencies. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(sftp): repair follow toggle UI and backend cwd sync fallback Fix SftpPaneView memo skipping follow prop updates, probe fresh pwd when OSC 7 is missing, and broaden linked terminal session resolution for sidebar follow mode. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(sftp): harden follow sync after review findings Reuse shouldFollowTerminalCwdNavigate in production path, re-read connection after async cwd probe, skip redundant navigate on toggle, and only probe pwd when the SFTP side panel is open. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
eca23a2691 | Merge remote-tracking branch 'origin/codebuddy' into codebuddy | ||
|
|
8181fe71cf |
fix(ai): make SDK deps optional, degrade gracefully when missing (#1242)
* fix(ai): make SDK deps optional, degrade gracefully when missing - Move @anthropic-ai/claude-agent-sdk and @github/copilot-sdk from dependencies to optionalDependencies so npm install does not fail when they are unavailable - claudeDriver.listClaudeModels: catch import error, return [] silently - copilotDriver.listCopilotModels: catch import error, return [] silently - sdkStreamHandlers: downgrade log from console.error to console.debug The renderer already falls back to curated model presets when list-models returns [], so no functional change. * fixup: honor queryFn before SDK import; regenerate lockfile with optional markers * fixup: guard runTurn against missing SDK modules runClaudeTurn and runCopilotTurn now catch dynamic import errors and emit a user-friendly error message instead of crashing with a raw module-not-found error. |
||
|
|
9d0f6a9cea | Merge origin/main into feat/et | ||
|
|
bb67aa77f5 |
[codex] compact long Catty agent context (#1237)
* feat: compact long Catty agent context * fix: tighten context compaction review issues * fix: preserve tool context during compaction * fix: clear stale model metadata on key edits |
||
|
|
3fc56df111 | refactor(ai): migrate agent backends from ACP to official SDKs (claude/codex/copilot) (#1229) | ||
|
|
c5ac85ae5b |
fix: OneDrive 同步 access token 自动续期与失效重连提示 (#1189) (#1208)
* Auto-refresh OneDrive sync token and prompt reconnect on dead refresh token OneDrive cloud sync (#1189) broke for users on 1.1.20 and only recovered after disconnecting and re-authorizing. Two gaps caused this: 1. Refreshed tokens were never persisted. When OneDriveAdapter silently refreshed the access token mid-session, the rotated tokens lived only in the adapter's in-memory state — CloudSyncManager never read them back or saved them. Microsoft consumer refresh tokens rotate on every refresh and invalidate the previous one, so the next app launch loaded a stale, rotated-out refresh token. Eventually that stored token was dead and sync failed, forcing a manual reconnect. Fix: OneDriveAdapter now exposes setOnTokensRefreshed(); the manager wires it so every silent refresh writes the rotated tokens back into provider state and encrypted storage (attachTokenRefreshPersistence / persistRefreshedProviderTokens), keeping the stored refresh token current. 2. A genuinely dead refresh token surfaced as a raw, generic error with no guidance. The bridge now detects invalid_grant / interaction_required / consent_required / login_required on refresh and tags the error with a stable marker. OneDriveAdapter normalizes these to OneDriveReauthRequiredError; the marker survives IPC and error re-wrapping so the condition stays detectable, and the UI strips it to show a clean "OneDrive session expired, please reconnect." message. Shared marker + detection/clean helpers live in domain/sync.ts so the bridge, adapter, and UI use one source of truth. Scope is limited to OneDrive; other providers (Gist/iCloud/Google/WebDAV/S3) are untouched. Tests: OneDriveAdapter refresh-persistence + reauth detection, manager token-persistence wiring, and bridge invalid_grant tagging. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Drop OneDrive to a reconnect state when its refresh token is dead codex review: detecting a dead refresh token was not enough. A provider in `error` state that still holds tokens stays "ready for sync" (isProviderReadyForSync), and syncAllProviders resets such providers back to `connected` and retries — so auto-sync kept hammering the dead refresh token and the user never got a stable reconnect prompt. Now, when a sync/download error indicates OneDrive reauth is required, the manager clears the stale tokens and tears down the cached adapter (handleProviderReauthRequired), leaving the provider in an error state with no credentials. With no tokens, isProviderReadyForSync returns false so auto-sync stops retrying, and the card shows a clean "please reconnect" message with a Connect button. The account is preserved for display; the error message is stripped of the internal marker. Wired into the syncToProvider / uploadToProvider / downloadFromProvider error paths. Added tests for the clear-on-reauth behavior and provider/error scoping. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Clear OneDrive reauth during inspection paths; include service tests in npm test codex review round 2: P2 — A dead OneDrive refresh token can also surface during startup remote inspection and the syncAllProviders preflight conflict check, both of which run through inspectProviderRemoteState. That path swallowed the error into an {error} tuple without clearing the stale tokens, so the provider stayed retryable. Wired the reauth handler into inspectProviderRemoteState's catch, covering sync preflight, syncAll preflight, and startup inspection in one place. Made the handler idempotent so the operation's own catch can also call it without re-saving. P3 — New tests live under infrastructure/services/, which npm test's globs did not cover (the pre-existing syncAllStorageMethods.test.ts was also uncovered). Added infrastructure/services/*.test.ts and infrastructure/services/*/*.test.ts to the test script. Full suite: 1400 tests, 0 failures. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test: drop unmatched services test glob from npm test The npm test script listed both infrastructure/services/*.test.ts and the nested infrastructure/services/*/*.test.ts. No .test.ts files live directly under infrastructure/services/ (the OneDrive adapter and cloudSync tests are in adapters/ and cloudSync/ subdirs), so the flat glob never matched. Under a default POSIX shell (sh/bash without nullglob), an unmatched glob is passed through literally, so node --test received the raw pattern. On Node versions without test-runner glob support this aborts with "Could not find '.../infrastructure/services/*.test.ts'" before any test runs, breaking npm test. Remove the redundant flat glob; the nested glob already covers the new OneDrive adapter and cloudSync tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
95208294b0 |
Bundle the prebuilt et client at pack time
Download and package the et binaries produced by build-et-binaries: - resolve-et-bin-release picks the latest et-bin-* release; fetch-et-binaries downloads the platform client into resources/et/, verifying SHA256SUMS. - et-extra-resources emits the electron-builder extraResources entry only when the binary is on disk, so pack still works without a bundled et. - electron-builder.config.cjs wires et into the mac/win/linux bundles; package.json adds the fetch:et scripts. |
||
|
|
456ddcfe68 |
chore(ai): upgrade ACP packages and unwrap Skill+CLI command in tool-call panel (#1128)
* chore(ai): upgrade ACP packages and unwrap Skill+CLI command in tool-call panel
Package bumps:
- @zed-industries/claude-agent-acp 0.22.2 → @agentclientprotocol/claude-agent-acp 0.37.0
(old npm package is deprecated; scope rename)
- @zed-industries/codex-acp 0.10.0 → 0.15.0
- @mcpc-tech/acp-ai-provider 0.2.8 → 0.3.3
- electron-builder asarUnpack glob + bridge require.resolve switched to the new scope
After the upgrade Codex tool-call cards started showing the local
worktree path for every step — "Run /Users/.../netcatty-tool-cli session
--session …" — instead of the remote command. Three things lined up:
1. The new acp-ai-provider maps ACP's `title` to `toolName`, and Codex's
title is the full shell invocation it's about to run.
2. Codex local_shell ships args as ["/bin/zsh","-lc","<full>"], so the
old `typeof args.command === 'string'` branch in ToolCall never fired
and we fell through to printing `name` (i.e. the title).
3. The bridge serializes tool args under `args`, but the ACP adapter
only read `event.input`, so even when args were available the
renderer received {}.
Fixes:
- acpAgentAdapter: read tool input from both `event.input` and
`event.args` so bridge-serialized chunks and direct AI SDK chunks
both work.
- ai-elements/tool-call: new extractDisplayCommand() unwraps the shell
array, then the netcatty-tool-cli wrapper (exec/job-start … -- <cmd>),
and renders the real remote command. session/env/job-poll/etc. fall
back to short labels ("netcatty: inspect session", …) instead of
exposing the binary path.
- shellUtils.cjs: defensive JSON-parse the ACP wrapper input in case
the AI SDK ever stops auto-parsing it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(build): exclude bundled Claude CLI binaries from the installer
@anthropic-ai/claude-agent-sdk@0.3.x bundles the native Claude Code CLI
(~211MB per arch) as optional sibling packages. Including them would
silently regress Netcatty's "bring your own Claude" design — the project
has always required users to install Claude Code locally, and the entire
path-discovery flow exists precisely to honor that contract:
- useAgentDiscovery.ts scans the user's PATH for `claude` and writes
the absolute path into the agent config's CLAUDE_CODE_EXECUTABLE env.
- aiBridge.cjs runs normalizeClaudeCodeExecutableEnvForAcp on every ACP
spawn, forwarding the env var to the child process.
- The @agentclientprotocol/claude-agent-acp wrapper's claudeCliPath()
(acp-agent.js) prefers process.env.CLAUDE_CODE_EXECUTABLE over the
bundled binary and only falls back to sibling-package resolution when
the env var is empty.
So the right place to enforce the design is electron-builder: exclude
node_modules/@anthropic-ai/claude-agent-sdk-* from `files`. Dev mode is
unaffected (optional deps still install for `npm run dev`); only the
packaged installer drops the binaries, saving ~150MB. Users without
Claude Code installed get the same SDK error they got pre-upgrade.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
e5d3d02b17 |
fix #1063: give each terminal its own WebGL texture atlas (disable cross-terminal sharing) (#1071)
Root cause of the persistent split-view 花屏: xterm's WebGL addon shares
ONE TextureAtlas across terminal instances with equal config (font / size
/ theme / DPR) — acquireTextureAtlas does `if (configEquals) { ownedBy.push;
return atlas }`. Two split panes then share an atlas, so the
clearTextureAtlas calls netcatty makes to recover from glyph corruption
(on resize / DPR / font change / tab show, from #1049 and #1066) clobber
the *other* pane's rendering. That's why the earlier redraw/clear-based
recovery attempts didn't help and only bounced the garble between panes.
Disable the sharing: remove the "reuse a matching atlas" loop so every
terminal creates its own atlas. The published bundle is minified, so this
is done with a small idempotent postinstall script (a patch-package patch
would be a ~550KB unreadable blob of the whole minified line). It
string-replaces the exact loop in the CJS + ESM builds, runs after
patch-package, and warns without failing if @xterm/addon-webgl changes.
Verified: split-view WebGL no longer garbles; script is idempotent
(patched=2 → already=2) and the production build is unaffected.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
92dd898eb4 |
Fix #931: let users pick a CJK font + per-font smart pairing (#940)
* feat(fonts): add CJK font pairing composition module Introduces composeFontFamilyStack() which builds the xterm fontFamily CSS string at runtime from: - the user's primary Latin font - an explicit CJK font (TerminalSettings.fallbackFont) if set - otherwise a per-Latin-font recommended CJK pairing - a hardcoded system CJK fallback stack - a Nerd Font icon fallback stack - the universal monospace generic 14 unit tests cover composition order, deduplication, OS defaults, quoting, and recommendation override behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(fonts): expose raw Latin families and add CJK-coverage entries - TERMINAL_FONTS[].family no longer bakes in the CJK fallback stack; composition is deferred to runtime via composeFontFamilyStack(). - Drops withCjkFallback helper from this module and its caller in lib/localFonts.ts. - Adds 6 CJK-coverage primary fonts to the dropdown: Sarasa Mono SC/TC, Maple Mono CN, LXGW WenKai Mono, Microsoft YaHei UI, PingFang SC. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(terminal): compose font-family stack with user-configurable CJK fallback resolvedFontFamily now passes through composeFontFamilyStack(), which prepends the user's TerminalSettings.fallbackFont (if set) ahead of the per-Latin-font recommended CJK pairing and the system fallback stack. The platform argument is derived from navigator.platform inside the useMemo, so the same Latin font may pair with PingFang SC on macOS and Microsoft YaHei UI on Windows out of the box. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(settings): add CJK font picker to terminal settings Adds a new "CJK font" select row right under the main font selector in the Terminal settings tab. Bound to TerminalSettings.fallbackFont (an already-existing-but-unused field), so this needs no schema or sync payload change. Default value "Auto" leaves fallbackFont empty, which lets the new per-Latin-font pairing in cjkFonts.ts pick a CJK font automatically. Selecting any explicit option (Sarasa Mono SC, PingFang SC, Microsoft YaHei UI, etc.) takes precedence over the per-font pairing. Includes en + zh-CN i18n strings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(sync): cover fallbackFont round-trip + legacy payload tolerance Four new test cases verify cloud-sync compatibility for the new CJK font setting: - buildSyncPayload includes fallbackFont when set - buildSyncPayload omits fallbackFont when unset - applySyncPayload writes incoming fallbackFont to TERM_SETTINGS - applySyncPayload from a legacy client (no fallbackFont) does NOT wipe the local value — critical for old-to-new upgrades Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(fonts): add font availability detection (canvas + document.fonts API) Three-layer detection used by isFontInstalled(family): 1. Known @fontsource-bundled families (e.g. JetBrains Mono) always count as installed. 2. document.fonts.check() — picks up @font-face and system-loaded fonts. 3. Canvas width measurement against serif / sans-serif / monospace fallbacks; only counts if the target font produces a width that differs from ALL three generics for a probe string. detectInstalledWithContext is a pure function taking an injected measurement context, which keeps the canvas / DOM behind a seam and lets the logic be unit-tested without a browser. 11 tests cover quoted-family parsing, the three-generic-fallback rule, bundled short-circuit, and document.fonts.check fast-path. Results are cached per process; clearFontAvailabilityCache() invalidates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(fonts): filter dropdowns to fonts actually installed on this machine Layer 3 of #931 added Sarasa Mono SC / Maple Mono CN / Microsoft YaHei UI / PingFang SC etc. to the terminal font dropdown, but users who don't have these installed would still see them and pick them — resulting in "I changed the font and nothing happened" confusion. This commit filters both dropdowns through isFontInstalled(): - TerminalFontSelect: drops any built-in or system-discovered font that detection can't render. If filtering would leave fewer than 4 fonts (detection misfire safety net), shows the full list. - TerminalCjkFontSelect: keeps the "Auto" sentinel always, drops concrete CJK choices that aren't present on this machine. Both selects always keep the currently-selected value visible — even when the underlying font is missing — so users can read and clear their setting without surprise. Also expands `npm test` globs to pick up infrastructure/config/*.test.ts and lib/*.test.ts, which previously matched no patterns and meant the new cjkFonts and fontAvailability suites were silently excluded from CI runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): never recommend proportional CJK fonts for terminal use The previous PingFang SC / Microsoft YaHei UI / Hiragino Sans GB choices were proportional sans-serif fonts whose CJK glyphs aren't designed to fit a terminal's 2x cell grid — the rendered Chinese ended up visibly wider than its allocated cells, breaking grid alignment (reported on macOS with PingFang SC selected as the CJK font). Changes: - TerminalCjkFontSelect: drops PingFang SC / Microsoft YaHei UI / Hiragino Sans GB from the dropdown. Legacy explicit selections still surface as a synthetic "not recommended" option so users can see and re-pick. - CJK_SYSTEM_FALLBACK_FONTS: monospace-only list. Sarasa Mono SC/TC, Maple Mono CN, LXGW WenKai Mono, Noto Sans Mono CJK SC, Source Han Mono SC, NSimSun, SimSun. Proportional fonts removed. - PER_FONT_CJK_PAIRING: every entry now points at a true monospace CJK font. Cascadia / Consolas / Menlo etc. all recommend Sarasa Mono SC, which the next commit bundles via @font-face. - getDefaultCjkFallback: Windows = SimSun (always installed, monospace); macOS = Sarasa Mono SC (will be bundled); Linux = Noto Sans Mono CJK SC. A regression test enforces that no per-OS default is a known proportional font. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(fonts): bundle Sarasa Mono SC as the universal CJK monospace Previous commit removed proportional CJK fonts (PingFang SC, etc.) from the picker and switched per-OS defaults to true monospace, but macOS ships NO system-installed monospace CJK font — leaving macOS users with a broken default unless they manually install Sarasa or similar. This commit closes that gap by bundling Sarasa Mono SC as an @font-face webfont, so the recommended pairings and macOS default "just work" out of the box. Details: - public/fonts/SarasaMonoSC-Regular.woff2 (~4.8 MB): subsetted from be5invis/Sarasa-Gothic v1.0.37 SarasaMonoSC-Regular.ttf (24 MB). Covers ASCII, Latin-1, common punctuation/symbols, CJK Unified Ideographs main block, Hiragana/Katakana, halfwidth/fullwidth, box-drawing — the everyday-Chinese coverage that matters for a terminal. Rare CJK Ext-A/B/historical chars fall through to the system fallback stack. - public/fonts/SarasaMono-LICENSE.txt: OFL-1.1 verbatim, required by the license. - index.css: @font-face declaration with font-display: swap so the user doesn't see a flash of nothing while the woff2 loads. - KNOWN_BUNDLED_FAMILIES: "Sarasa Mono SC" added so the dropdown availability filter doesn't hide it. Installer impact: ~+4.8 MB (vs current ~100-200 MB Electron baseline). The font replaces what would otherwise have been "Chinese chars look broken in the terminal" for every macOS user without a manually installed CJK monospace font. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): use Local Font Access API as the authoritative install check document.fonts.check() turned out to be unreliable as an installed-font signal in Chromium — it returns true for any syntactically-valid family name regardless of whether the font is actually installed, as a deliberate fingerprinting-mitigation. The previous detector took it as a positive signal and ended up keeping uninstalled fonts in the dropdown (reported by a macOS user seeing dozens of fonts they don't have). This commit pivots the detection chain: - lib/localFonts.ts: getAllSystemFontFamilies() exposes the unfiltered set of installed family names from queryLocalFonts(), reusing the same underlying call as getMonospaceFonts() via a shared cache. - lib/fontAvailability.ts: drops the document.fonts.check fast-path. Adds setSystemFamilies() / hasAuthoritativeData(). When the set has been populated, isFontInstalled answers from membership lookup directly — no canvas guessing. Canvas remains as a fallback for environments where the Local Font Access API is unavailable or permission is denied. - application/state/fontStore.ts: during initialize(), runs the monospace-only query and the full-system-families query together, then pipes the result into fontAvailability. - TerminalFontSelect: with authoritative data, drops the "if filtered list is suspiciously small, show all" safety net. Empty would now really mean empty (highly unlikely since Sarasa Mono SC is bundled). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): drop PingFang SC / Microsoft YaHei UI from primary dropdown Step 1 of this PR removed proportional CJK fonts from the CJK fallback picker but left them in BASE_TERMINAL_FONTS, so PingFang SC and Microsoft YaHei UI were still selectable as the *primary* terminal font. Picking PingFang SC as primary produced visibly bloated Latin character spacing (xterm.js samples cell width from the primary font; the wide proportional 'M' inflates every cell), reported by a macOS user in the same thread that opened #931. Both entries are removed from BASE_TERMINAL_FONTS. A new infrastructure/config/fonts.test.ts asserts that no known proportional CJK font name (including PingFang TC/HK, Microsoft YaHei variants, Hiragino Sans GB, Heiti SC/TC) is ever shipped in TERMINAL_FONTS as a primary choice. Migration for users already saved to one of the removed ids: useSettingsState rewrites STORAGE_KEY_TERM_FONT_FAMILY to the default (Menlo) on read when it sees a deprecated id, so the bad value also stops getting carried into cloud-sync uploads. Per-host fontFamily overrides are NOT migrated automatically — they still gracefully fall through to the dropdown's first entry via the existing getFontById fallback; users can re-pick from the host settings UI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): drop Comic Sans MS — it's a proportional handwriting font Same symptom as the PingFang SC / Microsoft YaHei UI removal: Comic Sans MS was historically in the primary font dropdown labeled "Casual, non-traditional terminal font", but Comic Sans is a handwriting-style proportional sans-serif. Picking it as the terminal primary inflates cell width and spaces every Latin character far apart (reported in the same #931 thread). - BASE_TERMINAL_FONTS: comic-sans-ms entry removed. - DEPRECATED_PRIMARY_FONT_IDS: gains comic-sans-ms so existing selections silently migrate to Menlo on read. - fonts.test.ts: the proportional-font ban list now also covers Latin proportional fonts (Comic Sans MS, Arial, Helvetica, Times New Roman, Georgia, Verdana, Trebuchet MS, Tahoma) so the test catches any future mislabeled body-text font from being added to the terminal dropdown. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): keep monospace ahead of CJK fallbacks in composed stack Addresses codex P1 review comment on PR #940 (https://github.com/binaricat/Netcatty/pull/940#discussion_r3216017737). The previous behavior of withCjkFallback() had monospace immediately after the primary family, before any CJK fallback. composeFontFamilyStack had moved monospace to the very end, which means: when the primary font isn't installed on the user's machine (common for Layer 3 CJK choices that aren't bundled and not present on a given OS, or for any built-in id like cascadia-code on a Linux system without it), CSS per-glyph fallback resolves Latin glyphs from a CJK font's full-width Latin variants before ever reaching monospace generic. That breaks xterm.js's fixed cell-grid alignment. The composed stack now reads: <primary>, monospace, <userFallback>, <recommended-cjk>, <system-cjk-stack>, <nerd-font-stack> Per-glyph CSS fallback behavior: - Latin → primary if installed → monospace generic. Cell width stays consistent. - CJK → primary (no) → monospace (no Chinese glyphs) → walks into CJK fallbacks. - Nerd PUA → falls past all of the above into the Nerd Font stack. Updates the position-invariant tests and adds a regression test that explicitly asserts monospace appears before every CJK family in the output stack. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): dedupe Local Font Access API calls under concurrent init Addresses codex P2 review on PR #940: https://github.com/binaricat/Netcatty/pull/940#discussion_r3216246xxx fontStore.initialize() runs getMonospaceFonts() and getAllSystemFontFamilies() in Promise.all; both internally called queryAllSystemFontsOnce(), whose cache check (`if (cache) return`) was only useful once the result had been written. Concurrent callers both passed the empty-cache check and fired their own queryLocalFonts() request — two real Local Font Access API invocations on cold start, with the risk of one succeeding while the other was denied (leaving the authoritative set unset). Fix: cache the *in-flight promise itself*, so subsequent callers await the same single invocation. The first await populates the family-set cache as a side effect, and the resolved promise keeps returning the same value to every subsequent caller. Adds lib/localFonts.test.ts with three regression tests: - concurrent getMonospaceFonts + getAllSystemFontFamilies = 1 API call - sequential repeats also reuse the resolved promise - missing API returns null authoritative set (canvas fallback signal) Exports __resetLocalFontsCacheForTesting() so each test gets a fresh module-level state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): retry LFA on transient failure + notify on availability changes Two follow-up fixes from codex P2 review on PR #940: 1) queryAllSystemFontsOnce() previously kept its in-flight promise even when queryLocalFonts threw. Subsequent callers reused the cached empty result for the rest of the session, so any transient failure at boot (permission state not ready, AbortError, etc.) permanently blinded the rest of the app to installed fonts. Catch now clears queryPromise so the next caller retries. Regression test added. 2) TerminalCjkFontSelect.visibleOptions and TerminalFontSelect .visibleFonts were memoized on [value] / [fonts, value] only, but the filter calls isFontInstalled() which reads module-level systemFamilies — a value that arrives asynchronously after the initial render. The memos never recomputed when authoritative availability data landed, so the dropdowns could continue showing stale "filtered" results until the user changed selection. fontAvailability now exposes subscribeFontAvailability() and getFontAvailabilityVersion() (monotonic counter bumped on setSystemFamilies / clearFontAvailabilityCache). Both selects subscribe via useSyncExternalStore and include the version in their memo deps; tests cover subscriber notification and version monotonicity. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): migrate host/group deprecated font ids + localize CJK labels Two follow-up fixes from codex review on PR #940: P2 — Host/group level font migration ==================================== The earlier deprecated-id migration only rewrote STORAGE_KEY_TERM_FONT_FAMILY, so hosts and group configs that had explicitly opted into a now-removed font id (e.g. pingfang-sc, microsoft-yahei, comic-sans-ms) kept `fontFamily` set with `fontFamilyOverride=true`. After the dropdown entries were dropped in 9f2bd282/c9b622d8, those records silently fell through to the first font in the registry (Menlo) while the override flag still read "true" — users saw a host claiming a custom font but rendering the global default with no way to tell what happened. Fix: - infrastructure/config/fonts.ts gains migrateDeprecatedFontOverride(), a structurally-shared helper that drops fontFamily and clears fontFamilyOverride when the id is deprecated. - sanitizeHost now runs it on every host load. - domain/groupConfig.ts grows sanitizeGroupConfig(); useVaultState applies it both on initial load and on cross-tab storage events. - Existing decrypt → sanitize → encrypt round-trip in useVaultState means the migrated values are persisted back to localStorage and propagate through cloud sync naturally. Tests: two each in domain/host.test.ts and domain/groupConfig.test.ts covering deprecated-id reset and untouched-valid-id preservation. P3 — Localize CJK font option labels ==================================== TerminalCjkFontSelect previously hardcoded Chinese option labels ("Auto · 按主字体智能搭配", "Sarasa Mono SC (更纱黑体 简)", etc.) and the synthetic "not recommended" warning. Non-Chinese locales saw a mixed-language UI despite the rest of the setting going through i18n. OPTIONS now references i18n keys; the component looks them up via useI18n(). Both en and zh-CN locales gain matching keys, including `...option.legacy` with `{font}` interpolation for the synthetic "not recommended" item that surfaces saved-but-removed values. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): also sanitize group configs on the write/import path Addresses codex P2 review on PR #940: https://github.com/binaricat/Netcatty/pull/940#discussion_r3216314xxx The previous commit (09c87820) added sanitizeGroupConfig() but only plumbed it into the decrypt paths (initial load + storage event). updateGroupConfigs() — which is also the write path used by applySyncPayload / importVaultData when ingesting a legacy payload — still set state from raw input. A sync from an older client carrying { fontFamily: "pingfang-sc", fontFamilyOverride: true } would land in memory unsanitized AND be re-persisted with the bad override active until the next reload re-ran the decrypt path. Fix mirrors updateHosts → sanitizeHost: map every incoming entry through sanitizeGroupConfig before both setGroupConfigs and the encrypt-and-persist step. Same call site now feeds the cleaned data to localStorage, so legacy values are scrubbed on first import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): migrate deprecated terminal font ids on every ingest path Addresses codex P2 review on PR #940: https://github.com/binaricat/Netcatty/pull/940#discussion_r3216517xxx The previous migration only ran in the initial useState() initializer for terminalFontFamilyId, so deprecated ids (pingfang-sc / microsoft-yahei / comic-sans-ms) could still re-enter state via: - rehydrateAllFromStorage() at line ~527 — runs on remote-import completion and re-reads STORAGE_KEY_TERM_FONT_FAMILY raw. - The notifySettingsChanged IPC handler at line ~663 — fires when a cloud sync or programmatic localStorage write announces a change. - The cross-window storage event handler at line ~873. Any of these paths could pull a deprecated id back into state after the initial migration ran, leaving the font selector with no matching option and silently rendering the global default while continuing to propagate the stale value through subsequent sync uploads. Centralizes the migration in migrateIncomingTerminalFontId(raw): - returns null when raw is empty - if raw is deprecated, writes DEFAULT_FONT_FAMILY back to localStorage AND returns it - otherwise returns raw unchanged All four ingest sites (initial init, rehydrate, IPC, storage event) now route through this helper. The rewrite-on-deprecated semantics also guarantee that the moment any path sees a bad value, the next sync upload carries the cleaned default — not the deprecated id. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): use bundled Latin-only fallback instead of monospace generic Resolves the tension between codex's two P1 reviews on PR #940: Round 1 (da1fe4cd): "monospace must come BEFORE CJK fallbacks" — otherwise Latin glyphs fall into a CJK font's full-width Latin when the primary font is missing. Round 2 (this commit): "monospace must come AFTER CJK fallbacks" — otherwise on macOS Chrome, the generic `monospace` pulls in PingFang via Chromium's CJK system fallback and silently masks the user's CJK picker. Both are right; using a single `monospace` token can't satisfy both roles because `monospace` is a generic family whose CJK-glyph coverage is platform-dependent. Fix mirrors Tabby's approach (their "monospace-fallback" SourceCodePro sitting before any CJK in the chain): insert a known Latin-only bundled font between the primary and CJK fallbacks. JetBrains Mono is already shipped via @fontsource/jetbrains-mono and carries no CJK glyphs, so it catches Latin without intercepting Chinese. New stack order: <primary>, "JetBrains Mono", <userFallback>, <recommended-cjk>, <system-cjk-stack>, <nerd-font-stack>, monospace Per-glyph CSS fallback now behaves as intended on every platform: - Latin: primary (if installed) → JetBrains Mono. Cells stay aligned. - CJK: primary (no) → JetBrains Mono (no CJK glyphs) → user CJK pick. - Nerd PUA: all of the above → Nerd Font stack. Replaces the two prior positional-invariant tests with one for each codex review concern: JetBrains Mono precedes every CJK family (Latin alignment), and user CJK precedes generic monospace (CJK picker effectiveness). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): use OR-of-fallbacks for canvas font detection Addresses codex P2 review on PR #940: https://github.com/binaricat/Netcatty/pull/940#discussion_r3216556xxx detectInstalledWithContext required the target font to produce a different rendered width from *all three* generic fallbacks (serif, sans-serif, monospace) to be counted as installed. That's too strict: on macOS the `monospace` generic resolves to Menlo itself, so measure(`"Menlo", monospace`) === measure(`monospace`), and the detector reported Menlo as missing even when it was clearly installed. The same false-negative trap exists for any font that happens to share metrics with one of the three generics on a given platform. Switches to OR-of-fallbacks: a font counts as installed if its rendered width differs from at least one generic baseline. A truly uninstalled font still falls through to each generic in turn and matches all three baselines, so this doesn't introduce false positives. Regression tests added for both directions: - Menlo with metrics identical to `monospace` generic → installed. - "Definitely Not Installed" font → still reported missing. The path only fires when the Local Font Access API is unavailable or denied — when LFA succeeds, `setSystemFamilies` short-circuits ahead of canvas — so this primarily improves the degraded-permission scenario. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): quote-aware tokenizer for font-family lists Addresses codex P2 review on PR #940: https://github.com/binaricat/Netcatty/pull/940#discussion_r3216559xxx composeFontFamilyStack and extractPrimaryFamily both tokenized their input with a raw String.split(',') — which corrupts any CSS family list whose quoted family name contains a comma (CSS allows that, e.g. `"Foo, Inc. Mono"` is a single family). A naive split would shred that into `"Foo` / `Inc. Mono"` and emit a malformed font-family back out. No current TERMINAL_FONTS entry hits this case, but lib/localFonts.ts builds family strings from arbitrary system fonts via the Local Font Access API — a user with a comma-bearing family name would have silently broken filtering until now. Adds splitFontFamilyList(css) in cjkFonts.ts: an exported quote-aware tokenizer that splits on commas only when outside quoted segments (handles both " and '). composeFontFamilyStack uses it instead of raw split; extractPrimaryFamily in lib/fontAvailability.ts imports it for symmetry so the two call sites can't drift. Tests cover the tokenizer directly (simple list, quoted-with-comma, single quotes, double commas) and end-to-end (a quoted primary with an internal comma survives composition intact). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(fonts): translate Layer 3 CJK font descriptions to English The 4 CJK-coverage entries added in earlier commits (Sarasa Mono SC, Sarasa Mono TC, Maple Mono CN, LXGW WenKai Mono) had hardcoded Chinese description strings, while every other TERMINAL_FONTS entry uses English ('Adobe's professional programming font', 'Iosevka variant mimicking Berkeley Mono style', etc.). The dropdown rendered a mixed-language list — flagged by the maintainer. Converted the 4 descriptions to English in the same style as the existing entries. No i18n scaffolding added; the existing convention is "English-only `description` field, not routed through t()", and the rest of the registry stays consistent with that. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d84c2cc902 |
Preserve local AI apiKeys when applying synced settings
`collectSyncableSettings` strips device-bound encrypted apiKeys from provider entries and webSearchConfig before upload, but `applySyncableSettings` was writing them back wholesale, silently wiping local credentials whenever any other setting changed on a second device. Merge by id (providers) and by providerId (web search) so a synced payload only overrides the apiKey when it explicitly carries one. Also include `application/*.test.ts` in the npm test glob so the syncPayload tests added in this PR actually run in CI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
72e305fb7a | Add reusable proxy profiles | ||
|
|
1b08e5ee88 |
[codex] Fix SFTP editor saved state (#887)
* Fix SFTP editor saved state * Restore window input focus after SFTP editor * Harden SFTP editor save flows |
||
|
|
8ccefc821c |
[codex] Use dedicated mosh binary repository (#881)
* Use dedicated mosh binary repository * Require bundled mosh client * Auto-fill saved password for mosh SSH handshake * Harden bundled mosh binary flow |
||
|
|
aa192c66c3 |
Wire bundled mosh release flow
* Wire bundled mosh release flow * Fix bundled mosh release flow review findings |
||
|
|
7dd25a55bb |
Bundle mosh-client + Node-side PTY handshake
* Bundle mosh-client via CI build pipeline Add a GitHub Actions workflow that builds a static, distro-portable mosh-client for linux-x64, linux-arm64, darwin-universal (arm64+x86_64) from upstream mobile-shell/mosh source, plus a pinned win32-x64 binary sourced from FluentTerminal (GPL-3.0). Releases attach SHA256SUMS so scripts/fetch-mosh-binaries.cjs can verify and pull the right binary into resources/mosh/<platform-arch>/ during npm run pack. electron-builder.config.cjs gains a moshExtraResources() helper that adds the binary to extraResources only when present on disk, keeping local dev packages working without bundled mosh. terminalBridge.cjs now exports bundledMoshClient() and prefers the bundled static client over whatever the system mosh wrapper would resolve via PATH (via the MOSH_CLIENT env var). The Windows branch throws a clear error pointing at Settings instead of silently falling back to a literal "mosh.exe" string when no wrapper is installed. This is Phase 1 — Phase 2 (follow-up) replaces the FluentTerminal Windows binary with an in-CI Cygwin static build and adds a Node-side mosh-server bootstrap so Mosh works out-of-the-box on Windows. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Phase 2: Node-side Mosh handshake (no Perl wrapper required) Reimplement what the upstream Mosh Perl wrapper does in pure Node: spawn `ssh [user@]host -- mosh-server new`, sniff the byte stream for `MOSH CONNECT <port> <key>`, then spawn `mosh-client` locally with MOSH_KEY in the environment. The new electron/bridges/moshHandshake.cjs module exposes the parser, sniffer, and command builders as pure functions so they can be unit tested without spawning real ssh. terminalBridge.startMoshSession now prefers this path whenever a bare mosh-client (bundled, explicit, or system) and ssh (in-box OpenSSH on Win10 1809+, system everywhere else) are both detectable. The legacy path through the system mosh Perl wrapper is preserved as a fallback so users with custom mosh setups don't regress. Auth is delegated to system ssh, so keys, agent, ssh_config, and known_hosts all keep working. Password / 2FA need a controlling TTY which the bootstrap doesn't provide; affected users keep the legacy wrapper path until interactive UI lands. Tests: - moshHandshake.test.cjs (20 tests) — parser corner cases, command builders, sniffer split-chunk handling, ring-buffer trim, exec resolver - terminalBridge.bareMoshClient.test.cjs (4 tests) — explicit-path basename gating 317 → 341 passing tests; lint clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Phase 3: in-CI Cygwin Windows build + visible PTY handshake Phase 3a — in-CI Cygwin Windows build - scripts/build-mosh/build-windows.sh builds mosh-client.exe from upstream mobile-shell/mosh source inside Cygwin, then walks the cygcheck import graph to bundle every required Cygwin DLL (cygwin1.dll, cygcrypto, cygprotobuf, cygncursesw, etc) into a tar.gz alongside the exe. - The `build-mosh-binaries` workflow swaps the FluentTerminal-pinned fetch job for a real Cygwin build (windows-latest + cygwin-install- action). fetch-windows.sh is preserved as an emergency fallback but no longer wired into the matrix. - fetch-mosh-binaries.cjs unpacks the tar.gz into resources/mosh/ win32-x64/ so mosh-client.exe sits next to its DLLs. - mosh-extra-resources.cjs ships the entire win32-x64/ dir (exe + DLL bundle) into Resources/mosh/, so the packaged installer runs on a stock Windows host with no Cygwin install. Phase 3b — visible PTY handshake (password / 2FA prompts) - terminalBridge.startMoshSession now spawns ssh inside node-pty so the user sees and can answer password / 2FA / known-hosts prompts in their terminal. When `MOSH CONNECT` is sniffed from the byte stream, session.proc is atomically swapped from the ssh PTY to a freshly-spawned mosh-client PTY. The MOSH CONNECT line itself is redacted from the visible output. - writeToSession / resizeSession read session.proc lazily, so input arriving after the swap goes to mosh-client without extra wiring. - The ZMODEM sentry is recreated for the new proc since its writeToRemote closure captured the previous handle. - Removes the earlier non-PTY child_process.spawn handshake — the PTY-based one supersedes it. Phase 3c — win32-arm64 deferred - Cygwin's arm64 port has no stable cygwin1.dll release yet, so we do not attempt an arm64 Windows build. arm64 Windows installs fall through to the legacy `mosh` wrapper path that the bridge already handles. Documented in the workflow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Allow branch/PR pushes to test the mosh-binaries workflow Mirrors the build-packages workflow change in #868: any push or PR that touches the mosh build pipeline triggers the matrix (artifacts only, no release), while only `mosh-bin-*` tag pushes (or an explicit workflow_dispatch with release_tag) publish a release. `paths` filter keeps unrelated commits from running this expensive workflow (~30min for the Cygwin leg). Concurrency group cancels superseded branch/PR builds; tag builds use a unique group so a follow-up commit can't kill an in-progress release. Release job's `if:` enforces the same rule independently — even if the trigger gets re-broadened, branches/PRs can't leak a release. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix mosh binary workflow runners * Fix Windows mosh workflow invocation * Keep shell scripts LF in workflow checkouts * Trigger mosh workflow on attributes changes * Fix mosh build tool dependencies * Fix Linux mosh static build * Fix macOS mosh build tool lookup * Skip macOS ncurses terminfo install * Fix mosh PR review findings * Allow Linux system mosh dependencies * Fix Windows mosh DLL bundling * Limit bundled Windows mosh DLLs * Honor configured PATH for mosh handshake --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
fb443541aa |
Optimize snippets shortcut behavior
Fixes #839 |
||
|
|
a4a5c703b1 | Fix terminal cursor preference handling | ||
|
|
4090483738 |
Fix Security Issues (#799)
* chore(deps): bump fast-xml-parser and @aws-sdk/xml-builder Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) and [@aws-sdk/xml-builder](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages-internal/xml-builder). These dependencies needed to be updated together. Updates `fast-xml-parser` from 5.3.4 to 5.5.8 - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.4...v5.5.8) Updates `@aws-sdk/xml-builder` from 3.972.4 to 3.972.18 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages-internal/xml-builder/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/HEAD/packages-internal/xml-builder) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-version: 5.5.8 dependency-type: indirect - dependency-name: "@aws-sdk/xml-builder" dependency-version: 3.972.18 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps-dev): bump follow-redirects from 1.15.11 to 1.16.0 Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.11 to 1.16.0. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.11...v1.16.0) --- updated-dependencies: - dependency-name: follow-redirects dependency-version: 1.16.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps): bump hono from 4.12.7 to 4.12.14 Bumps [hono](https://github.com/honojs/hono) from 4.12.7 to 4.12.14. - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.12.7...v4.12.14) --- updated-dependencies: - dependency-name: hono dependency-version: 4.12.14 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps-dev): bump vite from 7.3.1 to 7.3.2 Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.3.1 to 7.3.2. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v7.3.2/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v7.3.2/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 7.3.2 dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps-dev): bump flatted from 3.3.3 to 3.4.2 Bumps [flatted](https://github.com/WebReflection/flatted) from 3.3.3 to 3.4.2. - [Commits](https://github.com/WebReflection/flatted/compare/v3.3.3...v3.4.2) --- updated-dependencies: - dependency-name: flatted dependency-version: 3.4.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps-dev): bump minimatch from 3.1.2 to 3.1.5 Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.1.2 to 3.1.5. - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.5) --- updated-dependencies: - dependency-name: minimatch dependency-version: 3.1.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps-dev): bump lodash from 4.17.23 to 4.18.1 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.18.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps): bump @hono/node-server from 1.19.11 to 1.19.14 Bumps [@hono/node-server](https://github.com/honojs/node-server) from 1.19.11 to 1.19.14. - [Release notes](https://github.com/honojs/node-server/releases) - [Commits](https://github.com/honojs/node-server/compare/v1.19.11...v1.19.14) --- updated-dependencies: - dependency-name: "@hono/node-server" dependency-version: 1.19.14 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps): bump rollup from 4.57.1 to 4.60.2 Bumps [rollup](https://github.com/rollup/rollup) from 4.57.1 to 4.60.2. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v4.57.1...v4.60.2) --- updated-dependencies: - dependency-name: rollup dependency-version: 4.60.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps-dev): bump electron from 40.1.0 to 40.8.5 Bumps [electron](https://github.com/electron/electron) from 40.1.0 to 40.8.5. - [Release notes](https://github.com/electron/electron/releases) - [Commits](https://github.com/electron/electron/compare/v40.1.0...v40.8.5) --- updated-dependencies: - dependency-name: electron dependency-version: 40.8.5 dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps): bump path-to-regexp from 8.3.0 to 8.4.2 Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) from 8.3.0 to 8.4.2. - [Release notes](https://github.com/pillarjs/path-to-regexp/releases) - [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md) - [Commits](https://github.com/pillarjs/path-to-regexp/compare/v8.3.0...v8.4.2) --- updated-dependencies: - dependency-name: path-to-regexp dependency-version: 8.4.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps-dev): bump picomatch from 2.3.1 to 2.3.2 Bumps [picomatch](https://github.com/micromatch/picomatch) from 2.3.1 to 2.3.2. - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2) --- updated-dependencies: - dependency-name: picomatch dependency-version: 2.3.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps): bump yaml from 2.8.2 to 2.8.3 Bumps [yaml](https://github.com/eemeli/yaml) from 2.8.2 to 2.8.3. - [Release notes](https://github.com/eemeli/yaml/releases) - [Commits](https://github.com/eemeli/yaml/compare/v2.8.2...v2.8.3) --- updated-dependencies: - dependency-name: yaml dependency-version: 2.8.3 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps-dev): bump @xmldom/xmldom from 0.8.11 to 0.8.13 Bumps [@xmldom/xmldom](https://github.com/xmldom/xmldom) from 0.8.11 to 0.8.13. - [Release notes](https://github.com/xmldom/xmldom/releases) - [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md) - [Commits](https://github.com/xmldom/xmldom/compare/0.8.11...0.8.13) --- updated-dependencies: - dependency-name: "@xmldom/xmldom" dependency-version: 0.8.13 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps-dev): bump brace-expansion from 1.1.12 to 1.1.14 Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.12 to 1.1.14. - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.12...v1.1.14) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 1.1.14 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps-dev): bump tar from 7.5.7 to 7.5.13 Bumps [tar](https://github.com/isaacs/node-tar) from 7.5.7 to 7.5.13. - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v7.5.7...v7.5.13) --- updated-dependencies: - dependency-name: tar dependency-version: 7.5.13 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * Security Fixes Security fixes: Added input validation for uncontrolled command lines. Added Proper Shell Escaping for useTerminalAutocomplete Fixed 4 race condition alerts by atomic stat+read(s) without following symlinks. Misc: Use Crypto randomness instead of Math.random() (Not a security issue but convenient) * Fix OS quirk fallbacks * Review fix - use lstat before open to skip FIFO/devices early to prevent blocks - SFTP skip UUID tag could be dubiously long * allow symlinks alongside regular files. * Use acutal target size for reading * Fix Destructed import / fix to use full shellEscape charset - Destructed import - Guard now matches full shellEscape charset * Supress Codex complaints Replaced manual fd.read with fs.promises.readFile(fd) to ensure complete file reads to EOF. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
30f5346035 |
Classify AI proxy / size-limit errors instead of showing raw Zod output (#765) (#768)
Symptom: when an AI request is proxied through nginx (or any gateway)
and the request body exceeds client_max_body_size, the proxy returns a
413 HTML error page. The Vercel AI SDK then fails to parse the HTML
as a chat completion and surfaces a cryptic Zod validation error like
"Expected 'id' to be a string." through the UI — users have no idea
what's wrong.
Root cause: classifyError only did light sanitization and returned the
raw SDK message. It also string-coerced the error before inspection, so
the structured statusCode / responseBody fields that APICallError
attaches were thrown away.
Fix: classifyError now accepts `unknown` and inspects the full error
shape. Adds explicit branches for:
- HTTP 413 (from statusCode, cause.statusCode, or message text) →
"Request too large — exceeded proxy size limit. Try shorter
message, fewer attachments, or raise client_max_body_size."
- HTTP 502/503/504 → retryable upstream-gateway message
- HTML response body (starts with <!DOCTYPE/<html> or contains such
tags anywhere) → "Server returned HTML error page, likely a proxy
intercept."
- Zod/schema parse shapes ("Expected 'X' to be …", "Invalid JSON
response", "Type validation failed") → "Response could not be
parsed; proxy may have replaced/truncated the body."
In every classified case the raw SDK text is still appended ("Raw: …")
so users can report the underlying error verbatim.
useAIChatStreaming.ts callers now pass the raw error to classifyError
instead of `.message`, so the new structured branches actually fire.
Also wired infrastructure/ai/*.test.ts into the npm test glob.
Closes #765
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
0425841032 |
Fix ACP history replay and compaction (#754)
* Fix ACP history replay and compaction * Fix PR keyword importance matching Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address codex review on #754: preserve short constraints + cancel-clear Two recovery-path regressions flagged by codex review: 1. Compact ACP history dropped short load-bearing user constraints (acpHistory.ts:55). The blanket length<10 rule treated short non-trivial messages like "Use ssh2" or "中文输出" as filler, while longer generic follow-ups still ate the budget. After stale-session recovery the fresh ACP session would resume without constraints that were present in the original chat. Removed the length heuristic; the TRIVIAL_USER_MESSAGE_PATTERNS regex already filters actual filler ("ok", "yes", "继续", "thanks"). 2. historyReplayFallback was only cleared on non-aborted streams (aiBridge.cjs:2837). If the user stopped the first turn after stale-session recovery, the flag stayed set. The next turn would then trigger shouldResetProviderForHistoryReplay, discard the freshly recovered ACP session (resumeSessionId is forced to undefined in that path), and re-spend tokens on another compact replay — breaking the cancel-preserves-session contract. Now we also clear on abort; the empty-but-not-aborted retry path in the if-branch above is unchanged. Tests: - New test in acpHistory.test.ts asserts "Use ssh2" / "中文输出" survive when pushed outside the recent raw window - New test asserts "ok" / "继续" still drop (sanity check that the trivial regex still does its job without the length backstop) - Updated "does not treat pr inside ordinary words as important" to no longer assert that approach/improve/prepare are absent — the test's real intent (priority-2 line still wins) is preserved by the 不要提交 assertion - New test in aiBridge.test.cjs simulates a user cancelling the first turn after recovery and verifies the next turn reuses the recovered session (no extra provider creation, no re-replay) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address codex re-review: preserve replay flag across orthogonal recreation + keep tool output in raw window Two more P2 regressions flagged on the second review pass: 1. historyReplayFallback was only carried over in the reset-for-replay branch of the provider recreation path. An orthogonal change between an empty recovered turn and its retry — a permission-mode toggle, MCP scope/fingerprint flip, or auth rotation — would flip shouldReuseProvider to false, enter the !shouldReuseProvider branch, and drop the flag because preserveHistoryReplayFallback only covered the shouldResetProviderForHistoryReplay case. The next turn then sent only the latest prompt and lost the recovered conversation. Now the flag is preserved on any recreation where a replay is still pending. 2. Tool messages didn't flow through toRawHistoryMessage at all, so on stale-session recovery they only survived as the 500-char compact summary in summarizeToolMessage. Any follow-up referencing the last tool output ("use that output", "what did cat show?") lost the actual bytes when they exceeded the compact cap. Now tool results travel through the recent raw window up to MAX_RAW_MESSAGE_CHARS (2000), flattened to the "assistant" role since ACP only accepts user/assistant. Tests: - aiBridge.test.cjs: new "preserves history-replay across provider recreation caused by permission-mode / MCP / auth change" — exercises the gap via a permission-mode toggle between an empty recovered turn and its retry. Extends mock to support a dynamic getPermissionMode. - acpHistory.test.ts: new "preserves recent tool results verbatim" — pushes a ~1500-char tool output through the pipeline and asserts the replay still contains enough bytes to exceed the 500-char compact cap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address codex round 3: inline tool_call context + bound durable scan Two findings from the third codex review pass, both legitimate: 1. [P2] When the raw window starts mid-tool-interaction, the preceding assistant tool_call message can fall outside the 6-item slice while the tool_result stays in. Without the call's name+arguments, the result was opaque bytes and follow-ups like "use that output" had no provenance. The compact pass only preserved calls that matched IMPORTANT_PATTERNS, so read_file / grep / terminal_exec were silently dropped. Fix: build a toolCallId → { name, arguments } index from every assistant message and inline a `[from <name>(<args>)]` label next to each Tool result line in the raw window. Args are truncated to MAX_TOOL_CALL_LABEL_CHARS (200) so a verbose JSON payload can't eat the entire raw budget. 2. [P3] buildCompactContext scanned messages.entries() over the full transcript for durable-user/assistant candidates, even though MAX_MESSAGES_TO_SCAN (20) suggested the path was meant to be bounded. On a long ACP chat, every send did O(N) regex work plus an O(N log N) sort — the very chat-length-dependent latency the token-compaction PR was meant to address. Fix: introduce MAX_DURABLE_SCAN_MESSAGES (200) and restrict the durable scan to that tail. 200 is large enough to cover realistic sessions (99th-percentile chats are << 200 turns) while giving a constant-time worst case. Constraints older than the window age out of the compact replay; the live ACP provider's own persisted session still carries them when it can resume, which is the common path. Tests: - "inlines tool_call name+args so tool_result is interpretable without the preceding assistant turn" — pushes the tool_call out of the raw window and asserts the result line carries [from <tool>(<args>)]. - "bounds the durable-candidate scan to avoid O(N) work per send on long chats" — builds a 600+ message chat with an ancient priority-2 constraint outside the scan window and a recent one inside; asserts only the recent one survives. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address codex round 4: preserve short assistant decisions + provenance on older tool results Two P2 findings from the fourth codex pass, both mirror-images of earlier fixes on a different code path: 1. Short assistant decisions dropped from compact replay (acpHistory.ts:75-83). isSubstantiveAssistantMessage required length >= 40 OR a small English keyword match OR a numbered list. Short but load-bearing replies like "Use ssh2", "rebase instead", "中文输出" satisfied none of those and were silently dropped from the durable- assistant compact section. Once they fell outside the 6-item raw window, "do what you suggested earlier" would replay only the user question without the assistant's actual decision. Fix: mirror the user-side loosening — drop the length/keyword gate, rely on TRIVIAL_ASSISTANT_MESSAGE_PATTERNS to filter actual filler ("ok", "ack", "got it", "明白"). 2. Older tool results lost provenance (acpHistory.ts:108-114). The raw-window fix (round 3) only covered the last 6 items. Once a tool result fell into the compact section via summarizeToolMessage, the paired assistant tool_call was usually gone too, so multiple older outputs surfaced as indistinguishable "Tool result (callN): ...". Follow-ups like "use the resolv.conf output" had no way to map to the right call. Fix: plumb the toolCallIndex through summarizeMessage → summarizeToolMessage and inline `[from <name>(<args>)]` labels in the compact section too, the same shape the raw window uses. Tests: - New: preserves short non-trivial assistant decisions that miss the keyword heuristic (Use ssh2 / 中文输出 / rebase instead) - New: still drops trivial assistant filler like 'ack' / 'ok' / '明白' - New: inlines tool_call context on OLDER summarized tool results - Updated earlier raw-window tool regex tests to match the [from X(Y)] shape ([^)] was failing to cross the args JSON's closing paren) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address codex round 5: de-dup raw ∩ compact + wire userSkills test into npm test [P2] The scanned loop (last 20) overlaps with recentRaw (last 6), so without a raw-window skip in the summarizeMessage path the same last-6 turns were summarized into the compact section AND appended verbatim in the raw section. Important user turns and large tool output paid the budget twice — eating into the 3k compact cap and crowding out older durable context the replay is meant to preserve. Added the same recentRawSourceIds skip the durable-user / durable-assistant passes already use, and a regression test that asserts markers inside the raw window don't surface in compact while still appearing in raw. [P3] electron/bridges/ai/userSkills.test.cjs (added by this PR) sat in a subdirectory that the default "npm test" glob (electron/bridges/*.test.cjs) didn't pick up. The new routing / index-budget regressions would never run locally or in CI until someone noticed. Extended the glob to also match electron/bridges/*/*.test.cjs; the userSkills tests are now included in the 148-test run. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address codex round 6: cancel+immediate-send race + tool-call id collision Two P2 regressions in the recovery path: 1. If the user clicks Stop and immediately sends the next prompt, the new stream handler's existingRun path unconditionally called cleanupAcpProvider — destroying the fresh ACP session the cancel IPC had just promised to preserve. The round-2 clear-on-abort fix ran too late (in post-stream code) to help, because the new stream can arrive before the aborted stream fully unwinds. In that common timing window the follow-up still started from a bare provider and lost all recovered conversation state. Fix: (a) cancel IPC now synchronously clears historyReplayFallback on the preserved provider entry, so the next stream can't trigger shouldResetProviderForHistoryReplay and tear the session down via that path; (b) the existingRun path skips cleanupAcpProvider when the prior run was already cancelled via the cancel IPC (captured via existingRun.cancelRequested before we overwrite it). True interrupt-and-restart without an explicit cancel still falls back to the old clean-slate behavior. 2. The tool-call provenance index used raw toolCall.id as the key. Nothing in ChatMessage or the ACP event path enforces per-chat unique ids, so a provider reusing "call1" across turns would overwrite the older entry and mis-label older tool results (e.g., an /etc/hosts result annotated as /etc/resolv.conf in the compact summary). That makes stale-session recovery misleading whenever a follow-up refers back to an earlier tool output. Fix: key the index by `${toolResultMessageId}:${toolCallId}` and walk the message stream in order, resolving each tool_result to the most recent preceding assistant tool_call with matching id. Each result keeps its own historically-correct label regardless of later id reuse. Tests: - aiBridge: "preserves recovered ACP session when user cancels then immediately sends the next prompt" — fires the next stream request after cancel but BEFORE releasing the first stream's blocked read, asserts providerCreationArgs.length stays at 2 (no third creation) and the second turn sends only the latest prompt. - acpHistory: "resolves tool_call provenance correctly when tool ids are reused across turns" — two interactions sharing id "call1", asserts each tool_result carries its own call's args label. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address codex round 7: turn-based scan bound + single-pass history build Two P2 regressions in long-chat / tool-heavy recovery paths: 1. MAX_DURABLE_SCAN_MESSAGES (200) bounded the scan by raw message count. ACP tool interactions store the user turn, assistant tool_call turn, and each tool_result as separate messages, so a tool-heavy chat can produce 5+ messages per logical turn. 200 messages could be only 30-40 user turns — early constraints like "不要提交" from turn 5 fell out of the compact replay long before the turn count justified aging them out. Fix: bound by MAX_DURABLE_SCAN_TURNS (100 user turns) instead. Walk backwards from the end and stop after seeing 100 user messages. Realistic tool-heavy 30-turn chats now keep their early constraints alive, while true 100+ turn chats still benefit from the bound. 2. buildToolCallIndex(messages) and messages.flatMap(...).slice(-6) both walked the entire transcript on every send, even after the bounded compaction window landed. Compaction's stated purpose was to remove chat-length-dependent latency, but these per-send linear passes kept it. Fix: compute the scan start once via computeDurableScanStart, then do all subsequent work over messages.slice(durableScanStart). buildToolCallIndex walks only the window; the raw-6 flatMap also runs over the window. On a 1000-message chat with 100-turn window, send-time cost drops from O(1000) to O(~window_size). Acceptable trade: if a tool_call's matching tool_result straddles the window boundary (result inside, call outside), the single surviving result loses its [from X(Y)] label. Tool_calls and their results are almost always adjacent, so this affects at most the first 1-2 messages of the window. Tests: - "preserves an early constraint in a tool-heavy chat where message count balloons past the raw-count limit" — 35 turns × 6 msgs/turn = 212 messages. The old bound would have dropped the early EARLY_CONSTRAINT_MARKER; with turn-based bound it survives. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: bincxz <16399091+binaricat@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
54b26511a1 |
Cloud sync data-loss prevention (4-layer defense) (#742)
* feat(sync-guard): extend SyncState with BLOCKED + add shrink event variants * feat(sync-guard): add detectSuspiciousShrink pure function with 12 unit tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * polish(sync-guard): drop unnecessary cast, sharpen test naming, pin priority invariant Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(test): include domain/*.test.ts in npm test glob * feat(sync-guard): gate syncToProvider with shrink detection + force-push override Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(sync-guard): reset overrideShrinkOnce before early return for invariant strictness * fix(sync-guard): extend shrink guard to syncAllProviders (the actual sync entry point) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(sync-guard): apply empty-vault guard uniformly to auto and manual sync Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(sync-guard): preserve merge base on same-account re-auth Adds providerAccountId persistence; completePKCEAuth and completeGitHubAuth now only clear syncBase/anchor when the authenticated account id differs from the previously stored one, preventing zombie-entry resurrection on token refresh. disconnectProvider clears the stored id so a reconnect starts fresh. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(sync-guard): add i18n strings for sync-blocked banner + force-push modal Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(sync-guard): add SyncBlockedBanner showing shrink findings with restore/force-push actions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(sync-guard): stable subscribeToEvents reference + type-safe finding narrowing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(sync-guard): force-push confirmation modal + scroll restore button into view Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ux(local-backups): show version as title, demote reason+timestamp to meta line * feat(local-backups): record + display sync data version (v5/v6...) on each backup Each backup now captures the live CloudSyncManager.localVersion at creation time. UI shows it as title (v5, v6, ...) with timestamp + reason demoted to the meta line. Backups created before this field existed (or before any successful cloud sync) fall back to timestamp as title. Replaces the earlier app-version-transition title which conflated app version with sync data version. * fix(sync-guard): consume override flag at sync entry + restore provider status on block - Snapshot+clear overrideShrinkOnce at top of syncToProvider and syncAllProviders so an early-return cannot leak the flag to a later unrelated sync (Codex P1). - Restore provider status to 'connected' when shrink-block returns from syncToProvider; previously left provider stuck on 'syncing' in the UI (Codex P2). - Process pre-existing check errors before returning from the shouldBlockAll branch in syncAllProviders so a check-failed provider isn't dropped from results (Codex P2). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(sync-guard): refactor force-push to parameter passing + add credential-availability guard The previous design used a one-shot boolean flag on CloudSyncManager set by forcePushOverrideShrink(). Even with snapshot+clear at sync entry points, the renderer wrapper's await ensureUnlocked() could throw before the flag was consumed, leaving it armed for the next unrelated sync. Fix: pass overrideShrink as a call-time parameter through the chain. Eliminates the persistent flag and its leak surface. Also: force-push now runs the same ensureSyncablePayload(...) guard the other manual sync entry points use, so a vault with encrypted-credential placeholders won't be uploaded via the force path either. Addresses the latest two Codex P1/P2 findings on #742. * fix(sync-guard): backfill account id from in-memory state for upgrade-path re-auth Users upgrading to this PR have no netcatty.sync.accountId.* persisted yet. On their first re-auth the guard saw previousId=null and cleared the merge base anyway, defeating the point of the same-account preservation. Snapshot the in-memory account id BEFORE overwriting providers[provider] and use it as a fallback when the persisted id is missing. New users (no prior connection at all) still get the clear-on-first-auth path. Addresses Codex P1 on #742. * fix(sync-guard): inspect force-push results + mark blocked single-provider as error - Force-push handler now inspects syncNow result entries: applies any mergedPayload to local state, only clears the banner when all providers report success, surfaces a toast error otherwise. Previously the banner cleared unconditionally regardless of network/auth failures (Codex P1). - syncToProvider shrink-block branches now mark provider status as 'error' with a 'Sync blocked: would delete too much' message instead of 'connected'. Status aggregators treat 'connected' as healthy, so the blocked upload was surfacing as 'synced' in the UI (Codex P2). syncAllProviders already used this pattern; this brings the single-provider path in line. * fix(sync-guard): exempt USE_LOCAL conflict + clear post-merge BLOCKED + expose 'blocked' status - USE_LOCAL conflict resolution now passes { overrideShrink: true }: the conflict modal already served as user confirmation, and shrink-blocking it left users with a closed modal and an opaque banner (Review C-1). - Post-merge round-trip in useAutoSync now detects shrink-blocked results and resets syncState to IDLE via new manager.clearShrinkBlockedState(). The merged data is already applied locally; the next user-triggered sync will re-check, and we don't wedge the manager in BLOCKED with no visible banner outside the Settings tab (Review I-1). - overallSyncStatus now reports 'blocked' as a distinct value from 'error', so downstream UI (status icon, future badges) can offer shrink-block-specific affordances (Review I-2). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(sync-guard): stabilize banner subscription dep + map 'blocked' status to error indicator - The SyncBlockedBanner subscription useEffect depended on [sync] (the whole hook return object), which gets a new reference every render. This caused the listener to be unsubscribed+resubscribed on every render, opening a tiny race window where a SYNC_BLOCKED_SHRINK event could be missed and the banner would never appear. Destructure subscribeToEvents (already useCallback-stable) and depend on it directly, so the effect runs exactly once on mount. - SyncStatusButton's status mapping had no arm for the new 'blocked' value, falling through to 'none' (idle). The global status indicator said healthy while the in-page banner said paused. Map 'blocked' to the same error indicator used for 'conflict' so the UI is consistent. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(sync-guard): only clear banner on actual success + hydrate from manager state - Banner subscription now clears only on SYNC_COMPLETED with result.success. SYNC_STARTED (auto-sync timer ticks) and SYNC_FORCED (fires BEFORE upload) could clear the banner prematurely, removing the user's recovery affordance while the underlying issue was unresolved (Codex P2). - Manager now persists the last shrink finding in state.lastShrinkFinding alongside the SYNC_BLOCKED_SHRINK emission. New public getter getShrinkBlockedFinding() returns it when syncState is BLOCKED. Renderer hydrates the banner on mount so a block that happened off-screen (auto-sync while user was on another tab) is still visible when they open Sync Settings (Codex P2). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(sync-guard): unified BLOCKED-cleared event + USE_LOCAL inspects results - USE_LOCAL conflict resolution now inspects syncNow() results, applies any mergedPayload to local state, surfaces a toast error and KEEPS the modal open on failure (so user can switch to USE_REMOTE). Mirrors the force-push handler pattern. Without this, USE_LOCAL silently 'succeeded' even when providers failed (Codex CLI P1). - New SYNC_BLOCKED_CLEARED event emitted on every BLOCKED -> non-BLOCKED transition via a private exitBlockedState() helper. Banner subscribes to this single signal instead of guessing from per-provider SYNC_COMPLETED events. Fixes: - Multi-provider scenarios where first SYNC_COMPLETED clears the banner while a later provider was still going to fail (Codex CLI P1). - clearShrinkBlockedState() (post-merge self-heal) silently leaving the banner stuck because no event was emitted (Codex CLI P2). - disconnectProvider() now also exits BLOCKED state. Disconnecting implicitly resolves any pending shrink-block warning, otherwise the stale alert carried over to the next-account reconnect (Codex CLI P2). - All BLOCKED -> non-BLOCKED transitions consolidated through exitBlockedState() so lastShrinkFinding cleanup + event emission are always paired (Codex CLI P3 #6 covered). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(sync-guard): only clear BLOCKED on actual success, not on transient ERROR/SYNCING/CONFLICT Previous patch called exitBlockedState() at every BLOCKED -> non-BLOCKED transition, but this clears the banner on transitions that don't actually resolve the shrink concern: - SYNCING (sync just started — about to try, may fail) - ERROR (transient transport failure, shrink concern still real) - CONFLICT (separate concern; doesn't resolve the shrink) If a user was in BLOCKED then triggered a sync that failed for an unrelated reason (network, auth), the banner cleared and they lost the warning. Restrict exitBlockedState() to terminal-success transitions: - IDLE on successful upload (data made it to cloud — concern resolved) - explicit clears (disconnectProvider, clearShrinkBlockedState) - conflict resolution (USE_REMOTE/USE_LOCAL also end in IDLE) Found by Codex CLI review of commit 12d7fa7b. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
8ef91e1266 |
Ctrl+W close priority + local shell busy confirmation (#739)
* feat(ctrl-w): add ps-node + windows-process-tree + tsx deps for close-priority feature * fix(ctrl-w): drop ps-node dep and add windows-process-tree to asarUnpack Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ctrl-w): add ptyProcessTree bridge with per-platform child-process enumeration Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ctrl-w): ptyProcessTree uses args= for full command + warns on pid overwrite - Replace `comm=` with `args=` in defaultListPosix so the full command line is captured on both macOS (BSD ps) and Linux (GNU ps), avoiding the 15-char TASK_COMM_LEN truncation. - Add console.warn in registerPid when the same sessionId is overwritten with a different pid, making the race condition visible in logs. - Add test: registerPid warns exactly once on a pid change, not on a same-pid re-registration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ctrl-w): register local PTY pid with ptyProcessTree on spawn/exit Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ctrl-w): unregister pids in cleanupAllSessions to match per-delete invariant Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ctrl-w): add IPC handlers for pty child processes and confirm-close dialog Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ctrl-w): guard BrowserWindow.fromWebContents null and document dialog dismiss contract Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ctrl-w): expose ptyGetChildProcesses and confirmCloseBusy on window.netcatty Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ctrl-w): add i18n strings for close-busy-terminal dialog Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ctrl-w): add resolveCloseIntent pure function with 8 unit tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ctrl-w): expose handleCloseSidePanel via ref to App.tsx Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ctrl-w): wire resolveCloseIntent + local-shell busy confirmation into closeTab hotkey Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ctrl-w): add re-entrancy guard, aggregate busy count, sync sidebar ref, dedupe intent branches Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ctrl-w): auto-close workspace when its last session is closed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ctrl-w): sidebar close wins over focused terminal in priority chain Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ctrl-w): sidebar priority applies to single-session tabs too, not just workspaces Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ctrl-w): compute empty-workspace auto-close outside setSessions updater Addresses Codex P2 on #739: React 18+ does not guarantee updater execution timing under concurrent scheduling. Moving the decision outside the updater makes the microtask queue deterministic. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
c771979178 |
Add Skills + CLI mode for external agents (#599)
* Add Skills + CLI external agent workflow * feat: add Skills + CLI transport for ACP agents * chore: remove branch-local compatibility shims |
||
|
|
32ebc01552 |
feat: upgrade xterm.js to 6.0.0 with all addons
- @xterm/xterm: 5.5.0 → 6.0.0 - @xterm/addon-webgl: 0.18.0 → 0.19.0 - @xterm/addon-fit: 0.10.0 → 0.11.0 - @xterm/addon-search: 0.15.0 → 0.16.0 - @xterm/addon-serialize: 0.13.0 → 0.14.0 - @xterm/addon-web-links: 0.11.0 → 0.12.0 - Replace @xterm/addon-unicode11 with @xterm/addon-unicode-graphemes for more accurate CJK/emoji character width handling - Enable rescaleOverlappingGlyphs for CJK glyph rendering compliance - Replace 'canvas' renderer option with 'dom' (canvas removed in 6.0) - Migrate saved 'canvas' setting to 'dom' automatically - Fixes WebGL glyph atlas corruption causing garbled text (#5278) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
308d825db7 |
feat: ZMODEM (lrzsz) file transfer support (#579)
* feat: add ZMODEM (lrzsz) file transfer support for terminal sessions Adds ZMODEM protocol detection and file transfer capability to all terminal session types (Local, SSH, Telnet, Mosh, Serial). Uses zmodem.js library with main-process sentry pattern to intercept binary data before string decoding, avoiding IPC pipeline changes. - zmodemHelper.cjs: shared ZMODEM sentry with Electron dialog integration - terminalBridge.cjs: encoding:null for PTY + sentry wrappers for all session types - sshBridge.cjs: sentry wrapper for SSH stream data - preload.cjs + global.d.ts: ZMODEM event IPC bridge and TypeScript types - useZmodemTransfer.ts: React hook for ZMODEM transfer state Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: preserve charset decoding and add ZMODEM progress UI - zmodemHelper: pass raw Buffer to onData, let callers handle decoding - terminalBridge: use StringDecoder for telnet/serial, UTF-8 for local/mosh - sshBridge: restore iconv decoder for SSH session charset support - ZmodemProgressIndicator: floating progress bar with cancel button - Terminal.tsx: wire useZmodemTransfer hook + toast notifications Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: ZMODEM listener cleanup, stream leak, and toast dedup - preload: clean up zmodemListeners on session exit (memory leak) - zmodemHelper: add ws.on('error') handler to close write stream on failure - Terminal: use ref guard to prevent duplicate toast notifications Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address code review findings for ZMODEM - cancel/consume error now send IPC event to renderer (prevents stuck UI) - sanitize download filename with path.basename (path traversal prevention) - add on_detect concurrency guard (deny if transfer already active) - formatBytes: handle negative, zero, and TB+ values safely - closeSession: cancel active ZMODEM before destroying transport Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: prevent double-notification on cancel and stream error resilience - Guard .then()/.catch() in promise chain: skip if cancel() already handled - Download: add writeAborted flag to stop on_input after stream error - Upload: pre-compute file stats to avoid O(N²) statSync calls Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use zsession.abort() instead of close() on dialog cancel close() is only available on Send sessions. Calling it on a Receive session throws, leaving the sentry's internal _zsession dangling and causing subsequent terminal data to be consumed by the abandoned ZMODEM session (terminal freeze). abort() is defined on the base ZmodemSession class and properly fires session_end to reset the sentry. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: handle ZFIN/OO mismatch as successful transfer When sz exits over SSH, the shell prompt often arrives before the ZMODEM "OO" end marker, causing zmodem.js to throw a protocol error. Since ZFIN was already exchanged (= all file data transferred), treat this specific error as a successful completion and forward the shell prompt data back to the terminal via sentry re-consume. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: codex review — UTF-8 decoder, ZFIN abort, session exit cleanup - terminalBridge: use StringDecoder for local/mosh PTY to handle multi-byte UTF-8 split across buffer boundaries (prevents garbled CJK/emoji output) - zmodemHelper: on ZFIN/OO success path, use _on_session_end() instead of abort() to avoid sending CAN (Ctrl-X) bytes to the remote shell - useZmodemTransfer: listen to onSessionExit to reset state when the session dies mid-transfer (prevents stuck progress indicator) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: codex review — file collision handling and stream flush - Download: auto-rename with (1), (2), etc. if file already exists in the target directory, preventing silent overwrite - Download: wait for all write streams to finish flushing before resolving the session_end promise, ensuring data is on disk when the UI reports completion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: codex review — Windows PTY string compat and Telnet binary safety - Local/Mosh PTY: handle string data from Windows node-pty which ignores encoding: null; convert to Buffer before sentry.consume() - Telnet: bypass IAC negotiation during active ZMODEM transfer to preserve 0xFF bytes in binary data - Telnet writeToRemote: escape 0xFF as 0xFF 0xFF per Telnet spec so ZMODEM binary data is not treated as IAC commands Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: codex review — Windows PTY guard, Telnet IAC, stream cleanup - Local/Mosh: skip ZMODEM sentry on Windows where node-pty can't provide raw bytes; fall back to original string pipeline - Telnet: always run IAC negotiation (even during ZMODEM) since the Telnet layer still escapes 0xFF as IAC IAC; the existing handler already correctly collapses IAC IAC → single 0xFF - Download: destroy un-ended write streams on session_end to prevent hanging promises and leaked file descriptors on abort Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: codex review — early session start, progress throttle, no dup start - Download: call zsession.start() before showing folder picker dialog so lrzsz doesn't time out waiting for ZRINIT - Download: throttle progress IPC to ~10 updates/sec (100ms interval) to avoid overwhelming renderer on fast links - Download: remove duplicate zsession.start() at bottom of Promise Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: handle ZRPOS and prevent terminal flood after ZMODEM abort - Add 500ms cooldown after ZMODEM abort: suppress residual protocol bytes from remote rz/sz that would otherwise flood the terminal - Send 8x CAN (Ctrl-X) on abort/cancel/error to force remote end to stop transmitting even if the initial abort sequence was lost - Handles "Unhandled header: ZRPOS" gracefully (zmodem.js doesn't support error recovery, so abort is the correct response) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: send Ctrl+C after abort in all cancel/error paths Debian's rz stays attached to the TTY after receiving CAN sequences. The cancel() path already sent Ctrl+C via scheduleRemoteInterruptAfterCancel, but dialog-cancel and consume-error paths did not. Now all three abort paths (dialog cancel, consume error, explicit cancel) send Ctrl+C after 150ms to ensure the remote rz/sz process exits and the shell regains control. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add interruptRemote for SSH ZMODEM sentry Pass SSH stream.signal("INT") as interruptRemote callback so the ZMODEM helper can send SIGINT to the remote process when cancelling transfers, complementing the Ctrl+C byte sent via writeToRemote. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: dialog-cancel abort uses module-level helper to avoid ReferenceError sendExtraAbortBytes and writeToRemote are closure-scoped inside createZmodemSentry, not accessible from handleUpload/handleDownload. Extract abortRemoteProcess as a module-level function that takes writeToRemote as a parameter, used in both dialog-cancel paths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: dialog cancel throws instead of returning to avoid false complete When user dismisses the file/folder picker, handleUpload/handleDownload now throw "Transfer cancelled" instead of returning normally. This ensures the .catch() handler fires (sending error event) rather than .then() (which would incorrectly send complete event). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: codex review — preserve transferType in progress events - useZmodemTransfer: copy transferType from progress events so the transfer direction is preserved if renderer re-subscribes after the initial detect event was missed - zmodemHelper: clean up upload loop comments (backpressure handled via 64KB chunks + setImmediate yield per iteration) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: codex review — guard stale session cleanup, delete partial downloads - Promise chain .then/.catch/.finally now compare currentZSession identity (=== zsession) instead of truthiness, preventing a new transfer from being clobbered by the old promise settling - Aborted/incomplete downloads are deleted from disk on session_end so users don't end up with corrupt partial files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: unconditional cooldown suppression after ZMODEM abort The previous cooldown checked if data "looks like residual ZMODEM" which fails for sz's file content (arbitrary printable bytes). Now cooldown unconditionally drops ALL incoming data for 2 seconds after abort, with repeated CAN bursts to ensure the remote sz stops. This prevents the terminal flood seen when cancelling large sz downloads on fast connections. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
262bc57a21 |
feat: enable Unicode 11 for improved Nerd Fonts rendering (#545)
Load @xterm/addon-unicode11 and set activeVersion to '11' for better character width handling of Nerd Fonts, Powerline glyphs, and CJK characters. This matches the approach used by tabby terminal. Closes #543 (Nerd Fonts portion) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
9563ae9dcc |
Revert "feat: enable Unicode 11 for improved Nerd Fonts rendering"
This reverts commit
|
||
|
|
349b215d3d |
feat: enable Unicode 11 for improved Nerd Fonts rendering
Load @xterm/addon-unicode11 and set activeVersion to '11' for better character width handling of Nerd Fonts, Powerline glyphs, and CJK characters. This matches the approach used by tabby terminal. Closes #543 (Nerd Fonts portion) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
9a7d4decff |
feat: 终端命令自动补全系统 (#527)
* feat: 终端命令自动补全系统 实现类似 WindTerm/Fish 的终端命令自动补全功能,不依赖机器学习: - 历史命令持久化存储:按主机分组,频率+时间衰减排序,跨会话共享 - 前缀匹配引擎:支持精确前缀匹配和模糊匹配(首字符+连续字符+词边界加权) - Prompt 检测器:识别 bash/$、zsh/%、fish/> 等常见 prompt 模式,排除 vim/less 等程序 - Ghost Text 插件:xterm.js 自定义 addon,光标后灰色行内建议,→ 接受全部,Ctrl+→ 接受一词 - 弹出补全菜单:浮动列表 UI,↑↓ 导航,Tab/Enter 选中,Esc 关闭,来源标记(h/c/s/o/a) - @withfig/autocomplete 集成:600+ 命令规范的子命令、选项、参数补全 - 上下文感知:解析命令行 token,根据当前位置提供对应类型的补全 - 用户配置:启用/禁用、Ghost Text、弹出菜单、防抖延迟、最小字符数等 - 快速打字防误触:检测打字速度,快速输入时抑制建议 - 输入防抖 100ms,异步匹配不阻塞 UI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 补全菜单混合展示历史命令和 spec 子命令 - 输入已知命令名(如 docker)时即使没有空格也预览子命令 - 历史命令条数从 8 降为 5,留空间给 spec 建议 - 修复 wordIndex === 0 时 spec 补全被跳过的问题 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 补全菜单在终端底部时向上展开 当光标在终端下方、空间不足时,弹出菜单向上展开(底边对齐光标行), 避免溢出终端区域。列表顺序和选中逻辑不变——最可能的选项始终在顶部, 用户初始向下选择。参考 Termius 的做法。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 补全菜单跟随终端主题 + Enter 直接执行命令 1. 补全菜单颜色从终端主题动态派生(color-mix),不再硬编码色值, 确保与任何主题视觉一致 2. 在弹出菜单中按 Enter 选择命令时,直接插入并发送 \r 执行, 无需用户再按一次回车 3. Tab/鼠标点击仍然只插入不执行(保留选择后编辑的能力) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 修复 PR review 发现的全部 20 个问题 功能修复: - #1 修复 selectAndExecute 导致命令双重录入历史:用 suppressNextEnterRecordRef 标志位让 handleInput 的 Enter 分支跳过已经录入过的命令 - #2 修复 Prompt 末尾 $ 误判:重写 findPromptBoundary 为从左到右逐字符扫描, 排除 $HOME/$PATH 等变量引用(检查 $ 前是否有空格、是否在 token 内部) - #6 快速打字检测实际生效:快速打字时 debounce 延迟翻倍(200ms),等用户停顿 - #8 resolveSpecContext 处理带参数的 option(如 --name value): 识别 option 的 args 字段,自动跳过下一个 token - #9 Ghost text 位置随终端滚动/渲染更新:注册 term.onRender 回调 - #13 Escape 键不再拦截 vi-mode:仅在 popup 可见时消费 Escape, ghost text 显示时不拦截(ghost text 是被动的,不应阻止 shell 交互) - #14 所有 setState 统一使用 EMPTY_STATE 常量,不再遗漏 expandUpward 字段 架构修复: - #3 消除 CustomEvent 通信:改为 onAcceptText 回调注入, Terminal.tsx 直接传 writeToSession 回调给 hook, 删除 createXTermRuntime 中约 20 行 listener 代码和 cleanupAutocompleteListener 字段 - #7 xterm 私有 API 访问集中到 xtermUtils.ts:getCellDimensions 统一入口, 带缓存机制,仅在首次访问或 terminal 切换时触发 DOM 测量 - #16 删除 getCommandNameSuggestions 中多余的动态自导入 await import("./figSpecLoader") 性能修复: - #5 合并 ghost text 和 popup 的查询路径:删除独立的 getInlineSuggestion, fetchSuggestions 只调一次 getCompletions,ghost text 取 completions[0] - #10 preloadCommonSpecs 分批加载(每批 8 个,requestIdleCallback 间隔), 延迟 200ms 启动,且检查 enabled 才执行 - #11 scoreEntry 改为 scoreEntryAt(entry, now),now 在查询开始时缓存一次 - #15 scrollIntoView 从 smooth 改为 instant,消除快速导航动画排队 - #19 loadSpec 添加 in-flight 去重(inFlightLoads Map),同一 spec 并发加载只触发一次 import - #20 存储满时淘汰改为按 score 排序后保留前半,而非按插入顺序 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 修复二审发现的全部 10 个问题 功能修复: - #1(高) insertSuggestion 改用实时 detectPrompt 而非过时的 lastPromptRef, 修复用户继续打字后 Tab 选择建议导致字符重复插入的 bug - #2(中) handleInput Enter 录入历史优先用实时 detectPrompt, 修复快速打字场景下 recordCommand 记录不完整命令 - #9 suppressNextEnterRecordRef 添加 100ms 安全超时清除,防止 flag 残留 - #10 getNextWord 从 index 1 开始搜索分隔符,修复 ghost text 以 / 开头时 一次接受全部而非逐段的问题 性能修复: - #3(中) GhostTextAddon 注册 term.onResize 调用 invalidateCellDimensionCache, 确保 resize/字体变化后 cell 尺寸缓存正确失效 - #4 updatePosition 缓存 lastLeft/lastTop,位置无变化时跳过 DOM 写入; 字体属性移到 show() 中只设置一次,不再每帧写 6 个 style - #5 统一 clearState() 函数替代所有 setState({...EMPTY_STATE}), 带 popupVisible 守卫避免无效 re-render - #6 hasSpec 中 specs.includes() 改为 Set.has(),O(1) 查找 架构修复: - #7 Terminal.tsx 中 autocompleteAcceptTextRef 去掉多余的 useCallback 包装 - #8 删除 AutocompletePopup 的 onClose 死代码 prop Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: popup 默认不选中任何项,用户按 ↑/↓ 后才选中 修复输入 ls 等简单命令时回车误执行联想结果的问题: - selectedIndex 初始为 -1(无选中),Enter 直接执行用户输入的命令 - 用户按 ↑/↓ 导航后 selectedIndex >= 0,此时 Enter 才执行选中的建议 - Tab 仍然可以直接接受第一条建议(主动接受行为) - Enter 无选中时关闭 popup 并让按键透传到终端 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: fig spec 改为从静态资源 fetch 加载,修复生产构建中补全不工作 根因:@vite-ignore 动态 import 在 Electron 生产构建中无法解析 node_modules 路径(app:// 协议只能访问 dist/ 目录)。 修复方案(与 Monaco 编辑器相同的模式): - 新增 scripts/copy-fig-specs.cjs,prebuild 时将全部 739 个 fig spec 从 node_modules/@withfig/autocomplete/build/ 复制到 public/fig-specs/ - Vite 自动将 public/ 内容复制到 dist/,app:// 协议可以正常访问 - figSpecLoader.ts 改用 fetch + Blob URL + dynamic import 加载 spec, 同时保留 @vite-ignore import 作为 fallback(兼容 dev 模式) - public/fig-specs 加入 .gitignore(构建时生成,不进版本控制) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: ESLint 忽略 public/fig-specs 目录(第三方生成代码) 与 public/monaco 相同的处理方式——这些是从 node_modules 复制的 第三方构建产物,不应被项目 ESLint 规则检查。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 输入完整子命令名时展示其选项(如 git commit 显示 --message 等) 当 currentToken 完全匹配一个子命令时(如 "git commit" 中的 "commit"), 导航进入该子命令并展示其 options 和 sub-subcommands 作为预览。 之前的逻辑因为 name !== currentToken 过滤掉了完全匹配的项, 且 resolveSpecContext 的 consumedTokens 不包含当前 token, 导致停留在父级而看不到子级的选项。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 修复 fig spec index.js 解析失败导致补全不工作 根因:index.js 格式为 var e=[...],diffVersionedCompletions=[...]; 正则 /var\s+\w+\s*=\s*(\[[\s\S]*?\]);/ 要求 ] 后紧跟 ;, 但第一个数组后面是 , 不是 ;,导致非贪婪匹配跳到第二个 ];, 捕获了两个数组拼在一起,JSON.parse 失败,spec 列表为空。 修复:改用 indexOf 找第一个 [ 和对应的 ],直接截取子串解析。 fig spec 的 index 是简单的字符串平坦数组,无嵌套括号。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: fig spec 改用 URL 直接 dynamic import,移除 fetch+Blob 方案 fetch + Blob URL + import() 方案可能被 Electron CSP 策略阻止。 改为直接用完整 URL 做 dynamic import: - dev: import("http://localhost:5173/fig-specs/git.js") - prod: import("app://./fig-specs/git.js") 两种环境下动态 import 都能正常解析模块,无需 fetch 中间步骤。 同时简化 getAvailableSpecs 也用同样方式,移除 fetch+正则解析。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: fig spec 改为通过 Electron IPC 加载,彻底解决 dev/prod 加载问题 之前的方案(静态文件 + dynamic import / fetch + Blob URL)都因为 Vite dev server 对 .js 文件的模块转换和 Electron CSP 限制而失败。 新方案:通过 main process 的 Node.js require() 加载 fig spec, 通过 IPC 传给 renderer: - main.cjs: 添加 netcatty:figspec:list 和 netcatty:figspec:load handler - preload.cjs: 暴露 listFigSpecs() 和 loadFigSpec() API - figSpecLoader.ts: 通过 window.netcatty bridge 调用 IPC 优势: - main process 直接访问 node_modules,dev 和 production 都可靠 - 无需复制文件到 public/、无需 @vite-ignore hack - spec 数据通过 IPC 序列化传输,无 CSP 限制 - 删除了 scripts/copy-fig-specs.cjs 和 public/fig-specs/ 相关代码 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: main process fig spec 加载改用 import() 替代 require() @withfig/autocomplete 是 ESM 包("type": "module"), CommonJS 的 require() 无法加载 ESM 模块会抛 ERR_REQUIRE_ESM。 改用 dynamic import() 在 async handler 中加载。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: fig spec 加载用 pathToFileURL 绕过 package.json exports 限制 @withfig/autocomplete 的 exports 字段只允许 import "." 和 "./dynamic", Node.js 严格遵守 exports map 拒绝解析 build/git.js 等子路径。 改为手动拼接文件绝对路径 + pathToFileURL 转换为 file:// URL 后 import, 完全绕过 Node.js 的 package exports 限制。 同时修复 promptDetector 不再 trim 尾部空格(用 cursorX 确定实际输入长度), 确保 "git commit " 的尾部空格被保留,触发空 token 显示选项列表。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: 补全菜单添加详情面板 + 清理调试日志 - 选中或悬停补全项时,右侧显示详情面板(类似 VS Code IntelliSense) - 显示完整命令名、来源类型标签(Option/Subcommand/History 等) - 显示完整的描述文本(不再截断) - source 标记移到左侧,与描述分离,更易读 - 悬停和键盘选中都能触发详情面板 - 向上展开时详情面板也正确对齐 - 清理所有临时调试 console.log Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: 清理全部调试日志 移除 autocomplete 模块中所有临时 console.log 调试语句, 仅保留 figSpecLoader 中的 console.warn 用于真实错误报告。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 三审问题修复 — 移除多余 prop、过滤子路径 spec、防路径遍历 1. 移除 Terminal.tsx 传给 AutocompletePopup 的多余 onClose prop 2. getCommandNameSuggestions 过滤含 / 的 spec 名(aws/s3 等不是直接命令) 3. figspec:load IPC handler 添加 .. 路径遍历检查 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 5 个问题全部修复 1. [P1] fuzzy 匹配建议不以 userInput 开头时,用 Ctrl+U 清行再写入完整命令, 避免 substring 截断产生损坏的命令行 2. [P2] Ghost addon 初始化改用 polling 等待 termRef,解决首次挂载时 termRef.current 为 null 导致 ghost text 永远不激活的问题 3. [P2] popup overlay 改为 pointer-events-none 透传,仅 popup 自身设 pointer-events: auto,不再阻止终端区域的鼠标交互 4. [P2] getCompletions 异步返回后重新 detectPrompt 校验输入是否已变, 丢弃过时的补全结果避免覆盖新状态 5. [P2] prompt 检测支持折行:当 line.isWrapped 时向上回溯查找 prompt 行, 拼接多行内容作为完整 userInput Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第二轮 3 个问题修复 1. [P2] broadcast 模式下 autocomplete 插入也触发广播 — onAcceptText 回调中调用 onBroadcastInputRef 通知其他 session 2. [P2] 支持无尾随空格的 prompt(如 cmd.exe C:\path>)— prompt 字符后允许直接是行尾,boundary 为 i+1 3. [P2] 光标移动 escape 序列(Left/Home/End)清除过时建议 — 不再静默忽略,改为 clearState() 清除 popup 和 ghost text Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第三轮 3 个问题修复 1. [P2] commandBufferRef 处理 Ctrl+U 清行 — fuzzy 匹配发送 \x15 时 重置 buffer,避免 onCommandExecuted 记录错误的拼接命令 2. [P2] fetchVersionRef 递增计数器废弃过时异步结果 — clearState/Escape 关闭 popup 时 bump version,getCompletions 返回后检查 version 匹配, 防止已关闭的 popup 被旧请求重新打开 3. [P2] prompt scanLimit 从 80 提高到 200 — 支持包含 git branch、 kube context、长路径的 prompt,超过 80 列不再失效 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第四轮 3 个问题修复 1. [P1] 拒绝绝对路径 — figspec:load IPC handler 检查 commandName 不以 / 或 \ 开头,防止 path.join 丢弃前缀导致任意 JS 执行 2. [P1] cmd.exe prompt > 后不要求空格 — 对 > ❯ ➜ › 等 prompt 字符 不强制要求后跟空格,支持 C:\src>dir 格式 3. [P2] serial line mode 下 autocomplete 走 serialLineBufferRef — 在串口 lineMode 时不直接 writeToSession,而是缓冲到 line buffer 并处理 local echo,与正常按键输入行为一致 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第五轮 — translateToString(false) 保留尾部空格 translateToString(true) 会 trim 行尾空格,导致 cursorX 截取的 userInput 与实际行内容不一致。改为 translateToString(false) 保留 原始空格,确保 "git commit " 的尾部空格被正确保留用于触发选项补全。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: 设置页添加自动补全开关(启用/Ghost Text/弹出菜单) 在终端设置页末尾新增「自动补全」区域,包含三个开关: - 启用自动补全:总开关 - 行内建议(Ghost Text):光标后灰色建议文本 - 弹出菜单:浮动补全列表 子开关在总开关关闭时 disabled。中英文 i18n 翻译齐全。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第六轮 3 个问题修复 1. [P1] 光标不在行尾时禁止补全 — 检测 cursorX 后方是否有字符, 有则 clearState 不显示建议,避免 mid-line 插入导致文本重复 2. [P2] Enter 录入历史改为先尝试实时 detectPrompt,失败则 fallback 到 lastPromptRef 缓存,应对高延迟 SSH 下 buffer 未回显的情况 3. [P2] fuzzy 替换在 Windows host 上用退格清行而非 Ctrl+U — cmd.exe/PowerShell 不支持 Ctrl+U,改为发送 \b 退格序列 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第七轮 — commandBuffer 退格处理 + 接受后历史记录 1. [P2] commandBufferRef 处理 \b 退格 — Windows fuzzy 替换用退格 清行时正确移除 buffer 末尾字符,避免记录拼接错误的命令 2. [P3] lastAcceptedCommandRef 追踪接受的补全文本 — Tab/→ 接受后 立即 Enter 时用追踪值录入历史,不依赖可能未回显的 buffer Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第八轮 — 历史记录准确性 + 设置同步 1. [P2] 用户继续编辑后清除 lastAcceptedCommandRef — Tab 接受 "git status" 后追加 " --short" 再 Enter 时记录完整编辑后的命令 2. [P2] Ghost text →/Tab 接受路径也设置 lastAcceptedCommandRef — 确保所有接受路径在快速 Enter 时都能准确记录命令 3. [P2] autocomplete 设置加入 SYNCABLE_TERMINAL_KEYS — 跨设备同步时保留自动补全偏好 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第九轮 — REPL 误识别 + 本地终端 OS 检测 1. [P1] local terminal 的 hostOs 改用 navigator.platform 检测实际 OS, 避免 Windows 上 fallback 到 "linux" 导致 Ctrl+U 清行失败 2. [P2] 回退 > 无条件接受改动,恢复要求 > 后跟空格或行尾 — 避免 python >>>、mysql>、sqlite> 等 REPL 被误识别为 shell prompt 3. 新增 REPL NON_PROMPT_PATTERNS:>>>(python)和 word>(mysql/redis) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第十轮 4 个问题修复 1. [P1] cmd.exe prompt C:\path> — 对 > 特判:前面是 \ 或 / 时允许无空格, 避免误匹配 REPL(python>>>、mysql>)的同时支持 Windows cmd prompt 2. [P2] serial lineMode autocomplete 不再 early return — fall through 到 共享的 commandBuffer/broadcast 更新逻辑 3. [P2] serial 字符模式 + localEcho 时 autocomplete 插入文本也本地回显 4. [P3] 运行时关闭 autocomplete 时调用 clearState() 清除已显示的 popup Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第十一轮 — option args、PS2 误识别、bridge 缓存 1. [P2] resolveSpecContext 返回 option 的 args — 当光标在 option 参数 位置时(如 git archive --format |),返回该 option 的 args 而非 subcommand 的 args,使 tar/zip 等枚举值能正确补全 2. [P2] 排除 bare > 作为 shell prompt — bash PS2 续行提示 > 加入 NON_PROMPT_PATTERNS,避免在多行命令续行和 REPL 中误触发补全 3. [P3] bridge 不存在时不缓存 null — preload 时 bridge 可能未就绪, 缓存 null 会永久禁用该命令的 spec 补全 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第十二轮 — prompt 检测取最后一个分隔符 Starship/Powerlevel10k 等 prompt 包含多个 prompt 字符 (如 ➜ repo git:(main) $),之前在第一个 ➜ 就停了, 把后续 prompt 文本当成用户输入。 改为收集所有候选 prompt 边界,返回最后一个。确保 "➜ repo git:(main) $ ls" 中 userInput 正确为 "ls"。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第十三轮 — prompt 搜索范围限制 + cmd.exe 路径 1. [P2] prompt 扫描限制在行前 60% — 避免 "echo foo > bar" 中的 重定向符 > 被当作 prompt 结束(prompt 不会出现在行尾部分) 2. [P3] cmd.exe 路径检测扩展 — 除了 \ / 前缀,也检测行首是否有 驱动器号 (X:) 模式,支持 C:\Users\me> 等标准 Windows prompt P1 (高延迟 SSH buffer 滞后) 和 P2 (Enter 时 stale prompt) 属于 prompt 检测方案的固有局限,根本解决需要 OSC 133 Shell Integration, 不在本 PR 范围内。已有 lastAcceptedCommandRef fallback 缓解。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
b17775307f |
fix: bundle claude-code-acp to prevent crash when binary is missing (#404)
* fix: bundle claude-code-acp to prevent crash when binary is missing (#400) When users select Claude Code in the AI module, the app spawns `claude-code-acp` via ACP. Previously only the `claude` CLI was checked during agent discovery, so if `claude-code-acp` was not on PATH the spawn would fail with ENOENT and crash the Electron main process. - Add `@zed-industries/claude-code-acp` as a bundled dependency - Add `resolveClaudeAcpBinaryPath()` that checks PATH first, then falls back to the npm-bundled binary (mirrors Codex pattern) - Use the resolver in both the primary and fallback ACP provider paths - Update agent discovery to detect agents via bundled ACP binary when the standalone CLI is not installed Closes #400 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add claude-code-acp and its deps to asarUnpack In packaged Electron builds, files inside app.asar cannot be executed by child_process.spawn. Add claude-code-acp and its runtime dependencies to asarUnpack so the binary is accessible in production. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: migrate from deprecated claude-code-acp to claude-agent-acp The @zed-industries/claude-code-acp package has been renamed to @zed-industries/claude-agent-acp (bin: claude-agent-acp). Update all references across the codebase: - package.json: replace dep with @zed-industries/claude-agent-acp@0.22.2 - electron-builder.config.cjs: update asarUnpack entries, remove stale deps (diff, minimatch) no longer needed by the new package - shellUtils.cjs: update binary name and require.resolve path - aiBridge.cjs: update acpCommand, ALLOWED_AGENT_COMMANDS, isClaudeAgent - settings types, i18n locales: update command references Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
88760b763e |
fix: upgrade node-pty from 1.1.0-beta19 to 1.1.0 stable
The beta version had native module loading issues on Arch Linux AppImage builds (ERR_DLOPEN_FAILED). The stable release uses an improved module loading strategy with prebuild support for macOS/Windows and better build-from-source fallback for Linux. Related: #264 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
0b9cb86c4e |
fix: address security and correctness issues from code review
- Add command blocklist check to terminal_send_input (executor + SDK tools) - Add session scope validation to all tools in executor.ts - Fix abort handler to call aiChatCancel instead of aiChatStream - Enforce URL allowlist in fetch proxy to prevent SSRF - Wrap event.sender.send with safeSend for destroyed window check - Filter dangerous env vars (LD_PRELOAD, NODE_OPTIONS, etc.) from agent spawn - Fix stale debouncedPersistSessions closure using sessionsRef - Fix cleanupOrphanedSessions race when sessions load before workspaces - Fix limitConcurrency implementation using Set + finally pattern - Improve command blocklist regex patterns with word boundaries - Add regex validation with error feedback in SafetySettings - Add confirmation dialog for provider removal - Fix React key warning for tool-role messages in ChatMessageList - Remove debug console.warn in TerminalLayer - Remove unused nanoid dependency - Remove redundant platform-specific codex-acp binary from dependencies Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
bcd44f0177 |
refactor: remove Claude Agent SDK integration in favor of ACP
All external agents now use ACP protocol exclusively. The Claude Agent SDK flow was fully implemented but never wired into the UI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
8949394756 |
feat: add Claude Agent SDK streaming and Codex OAuth integration
Integrate Claude Agent SDK for direct streaming chat, add Codex login/logout flow with OAuth support in settings, improve AI chat panel UI and agent discovery, and update build config for new dependencies. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |