Commit graph

164 commits

Author SHA1 Message Date
Fini b6688c8818 fix(renderer): measure chrome text in its painted font 2026-08-07 02:00:23 +08:00
Kayshen-X a636efd7ae ci(release): publish SHA256SUMS + provenance attestation for download verification
Some antivirus engines heuristically flag the self-signed, low-prevalence
Windows installer (issue #198). Give downloaders a way to confirm assets
are exactly what CI built: a checksum manifest plus signed SLSA provenance
verifiable via gh attestation verify, with triage + code-signing policy
docs linked from the README and release notes.
2026-08-05 22:15:09 +08:00
Kayshen-X 64d70d672d feat(extension): add OpenPencil web-capture Chrome extension
MV3 extension that captures the rendered active tab (via the shared
snapshot-extractor contract) and imports it into OpenPencil. Logic lives
in the new op-chrome-extension-core crate (wasm): endpoint rules,
chunked-transfer integrity, /mcp envelope + reply classification,
download-name sanitisation, SSO/account session parsing, and hub
snapshot-inbox delivery. JS is glue only (chrome.* APIs, fetch, popup
DOM, injected page functions).

Capture: full page + element pick. Delivery: local ingress
(POST /api/import/web-snapshot) with /mcp fallback, JSON download, and —
when signed in to OP Hub — the account snapshot inbox. Flat popup UI,
15-locale strings with an in-popup language switcher, store packaging.

Why a crate: keeps the security-sensitive logic in tested Rust rather
than glue JS, and the SW/popup split keeps dynamic import() out of the
service-worker graph (guarded by check-sw-imports).
2026-08-04 21:43:01 +08:00
Kayshen-X 26bdc112f6 feat(collab): ship built-in regional collaboration hubs
Public relay was reachable only by setting OPENPENCIL_COLLAB_BOOTSTRAP_URL,
so a released build could never collaborate: no end user sets an environment
variable. Release builds now carry a hub URL per region, injected at compile
time from the release pipeline's secrets — the source tree keeps no
production endpoint, and an uninjected build stays environment-configured.

A persisted preference picks the region, exposed as a switch on the create
screen; the environment variables remain overrides that win and fail closed.
Each endpoint gets its own bootstrap cache record so switching regions cannot
overwrite the other hub's anti-rollback floor.

Also surfaces an unreachable device key as its own message instead of a
misleading reconnect notice, wraps notice text inside its bubble, and centres
button labels against their own height (28px buttons painted low).
2026-08-03 21:05:36 +08:00
Kayshen-X 8654c04b00 feat(collab): replace public invites with 10-char region-tagged pairing codes
The ~500-char opc1_ fragment is retired from every production surface; a
relay session now shares one 10-char Crockford code (1 region char + 9
random, 45 bits). The full invite is sealed with a blake3 encrypt-then-MAC
under keys derived from the code and stored on the locator control plane
under an independent code_id; guests claim from exactly the region the
code names, so neither the id nor the bearer ticket reaches uninvolved
regions. The store tombstones exhausted claim budgets (an id can never be
re-published under a burned code), caps codes per device key, and keeps
per-route ingress body limits. Connect failures now distinguish invalid,
expired, and relay-not-configured across all 15 locales, and the threat
model documents the operator-grindable 45-bit residual risk.
2026-08-02 08:31:13 +08:00
Kayshen-X f9f9c8574a fix(collab): harden p2p collaboration against resource exhaustion
Addresses a security review of the collaboration subsystem. No auth
bypass, key leak, or document-plaintext exposure was found; every
finding below is availability or trust-boundary hardening.

Landed as one commit because the pieces are not separable: the
inbound-direction ceiling spans op-collab, op-collab-transport, and the
desktop host atomically, the guarded accept spans transport, smoke, and
the desktop host, and the boundary-gate rules only hold against the
final state. Splitting would produce commits that fail to build or fail
the gate.

Relay server (public, internet-facing):
- Charge pre-pairing capacity per source address. The auth-concurrency
  semaphore was taken before the WebSocket upgrade and the peer address
  was discarded, so one host could pin every permit by connecting and
  going silent.
- Give renewals their own budget. Reauthentication competed for the same
  semaphore, so an unauthenticated flood progressively closed live
  tunnels with a policy error.
- Release the pair registration when the ready status fails to send; the
  counterpart only reclaims it if it reads its pairing notice.
- Require the X25519 key file to be owned by the running user; mode bits
  alone do not establish trust.
- Summarise capacity rejections instead of logging one line each.

Locator service:
- Rate-limit publishes per client instead of process-wide. One
  unauthenticated caller could consume the whole budget and 429 every
  tenant's invite issuance.

Collaboration protocol:
- Size the inbound envelope ceiling from the authenticated remote role
  rather than sharing the 64 MiB snapshot ceiling in both directions, so
  an admitted guest cannot force a 64 MiB JSON parse per frame. The
  ceiling is applied before the discriminator and before the generic
  value decode; a peer-declared snapshot kind cannot raise it.
- Reject display names carrying Unicode format characters, which render
  identically to an existing participant's name.
- Reject avatar URLs pointing at non-globally-routable addresses.

Transport:
- Reclaim a pending-handshake seat from a peer that has not produced a
  valid first handshake message, and raise the global ceiling. Sixteen
  seats held for the full handshake window let four addresses deny every
  join.
- Put inbound reassembly under an aggregate budget; only the outbound
  aggregate was bounded.
- Stop heartbeats from refreshing the idle deadline in receive_transfer.
- Filter IPv4 link-local discovery advertisements, matching IPv6.

Relay client and trust roots:
- Bound server-initiated reauthentication per connection by count and
  minimum interval, sized from the protocol's own cadence.
- Close the policy-file TOCTOU window by identity-checking the opened
  file, and reject group/world-writable or foreign-owned policy files.
- Stop discarding bootstrap cache-write failures, which silently
  disabled the anti-rollback generation floor.
2026-08-01 09:48:23 +08:00
Kayshen-X 2b713635f5 feat(collab): ship public relay collaboration 2026-07-29 21:42:33 +08:00
Kayshen-X 2e9de6d2a2 fix(ci): update collaboration security gate 2026-07-29 00:06:58 +08:00
Kayshen-X 0bd9947310 feat(collab): add authenticated p2p collaboration 2026-07-29 00:06:58 +08:00
Kayshen-X 6a780c7425 fix(web): pre-open the sign-in popup in the click gesture and use popup wording
Opening the verification page from the async poll callback was popup-
blocked (the 'click sign-in twice' bug); a placeholder window opened
synchronously inside the press is navigated once the URL arrives, and
closed on cancel/failure. The wasm login modal now says 'popup window'
instead of 'your browser' — the web editor already runs in one.
2026-07-25 23:51:27 +08:00
Can H. Tartanoglu bb27a7458f
feat(integration): expose declarative harness surface (#185)
* chore(nix): add reproducible OpenPencil flake

* fix(nix): patch and smoke-test prebuilt binaries

* chore(ci): keep workflow validation in integration follow-up

* feat(integration): expose declarative harness surface

* ci: run Nix checks for integration surface

* test: verify all OpenPencil runtime tools

* feat(integration): expose web server package

* chore(nix): remove rebase conflict marker

* fix(nix): update OpenPencil CLI prebuilt hash

* fix(nix): validate source outputs and pin release assets

* fix(nix): refresh desktop release hash

* fix(nix): refresh Bun dependency hash

* fix(nix): normalize Bun hoisted aliases

* ci: make headless desktop smoke test deterministic
2026-07-25 23:44:01 +08:00
Kayshen-X 685c5dd17e docs(release): title v0.8.2 and promote note headings to release names
GitHub releases were named by bare tag because the action got no name.
The note's leading heading is the natural title, so publish it as the
release name and drop it from the body instead of repeating it.
2026-07-25 12:53:31 +08:00
Kayshen-X 9e083e164a fix(desktop): recover from unusable native GL on Windows startup
Some Windows 11 machines flash-exit on launch: their WGL OpenGL is the
GDI-generic 1.1 software renderer (no/old GPU driver, VMs, RDP), which
Skia's GL backend rejects. The old WglThenEgl preference never fell
through to EGL because WGL *did* create a (1.1) context; the failure
only surfaced downstream in make_gl, after which new_desktop merely
eprintln'd (invisible under windows_subsystem="windows") and exited —
a silent crash. Not all machines: ones with a real GPU driver get
GL 3.0+ and work, which is why only some users saw it.

- native: new_desktop retries once via forced EGL (ANGLE) when the
  native context can't drive Skia; machines that already work keep the
  WGL path byte-for-byte unchanged.
- desktop: pop a native error dialog on GL-init failure instead of a
  silent exit, so a GPU/driver problem is visible not mysterious.
- packaging/CI: ship arch-matched ANGLE runtime DLLs (libEGL /
  libGLESv2 / d3dcompiler_47) next to the exe, staged from an Electron
  release zip, so glutin's EGL fallback can load them.
2026-07-21 21:15:23 +08:00
Kayshen-X bed9af317e ci(vscode): make open-vsx publishing retryable after partial failure 2026-07-19 20:45:02 +08:00
Kayshen-X 473ec1dbc5 ci(vscode): publish platform vsix to open-vsx on release 2026-07-19 20:45:02 +08:00
Kayshen-X 1e6238e42a fix(ci): restore native installer globs clipped from the release upload 2026-07-19 20:41:37 +08:00
Kayshen-X 943ffaf624 feat(vscode): release vsix ships the platform daemon and web assets
The released extension was a shell without its runtime. The build matrix
now also produces op-host-web-server per target; the vsix release job
packages six platform vsix (vsce --target) embedding bin/<daemon> plus
web/{pkg,canvaskit}; the extension prefers the bundled runtime at spawn
(binary probe with exec-bit restore, env pointing the daemon at the
bundled web assets) with the workspace debug build as the dev fallback.
2026-07-19 20:41:37 +08:00
Kayshen-X 2acf32a41a ci(vscode): attach the extension vsix to release artifacts 2026-07-19 20:41:37 +08:00
Kayshen-X ac4e6acb92 ci(vscode): trigger on workspace-root dependency changes too 2026-07-19 20:41:37 +08:00
Kayshen-X cb8c2e97f1 ci(vscode): package the extension vsix with typecheck and test gates 2026-07-19 20:41:37 +08:00
Kayshen-X 52260c3155 fix(ci): retry ARM apt prerequisites 2026-07-15 23:22:22 +08:00
Kayshen-X 0635927c42 fix(ci): install ripgrep for version guard 2026-07-15 22:47:01 +08:00
Kayshen-X f82f4e4ea3 fix: enforce centralized version inputs 2026-07-15 21:17:21 +08:00
Kayshen-X e28506d0af fix: cover all version guard inputs 2026-07-15 21:17:21 +08:00
Kayshen-X e5b7425f3a build: add version synchronization gate 2026-07-15 21:17:21 +08:00
Kayshen-X 84993cadc9 fix(release): gate publishing on Cargo version 2026-07-15 21:17:21 +08:00
Kayshen-X d3fc4fed6d build: derive package versions from Cargo 2026-07-15 21:17:21 +08:00
Kayshen-X c001809e86 fix(release): stop publishing unsigned macOS desktop tarball 2026-07-09 23:23:55 +08:00
Kayshen-X e22feae95c fix(release): notarize final macOS DMG artifacts 2026-07-09 22:52:48 +08:00
Kayshen-X 6b2aa18db6 fix(release): exclude docker build metadata from release assets 2026-07-09 22:43:33 +08:00
Kayshen-X 4397db6171 fix(desktop): notarize macOS app before DMG packaging 2026-07-09 21:54:45 +08:00
Kayshen-X 5799d9cc9c fix(release): tolerate apple agreement notarization block 2026-07-09 00:10:19 +08:00
Kayshen-X 4abd5119e7 fix(release): skip existing npm packages 2026-07-08 23:50:39 +08:00
Kayshen-X d49087027e chore(release): include cli install scripts 2026-07-08 23:24:32 +08:00
Kayshen-X a98458e146 fix(release): publish sdk tarballs from local paths 2026-07-08 23:06:02 +08:00
Kayshen-X 10b9ad8bfc chore(release): update package manager publishing 2026-07-08 22:56:59 +08:00
Kayshen-X a71d97e7b9 chore(release): wire signing and npm publish secrets 2026-07-08 22:47:21 +08:00
Kayshen-X 5dc21b2ff1 chore(release): publish prerelease on tag 2026-07-08 22:21:24 +08:00
Kayshen-X 613878e18e chore(release): prepare v0.8.0 prerelease 2026-07-08 22:16:43 +08:00
Kayshen-X 3590663aec fix(ci): gate windows test text measurement 2026-07-08 02:00:47 +08:00
Kayshen-X 4dcfe0d675 fix(ci): serialize windows nextest runs 2026-07-08 00:01:02 +08:00
Kayshen-X 8cd03adacd fix(ci): repair three v0.8.0-new failures (canvaskit test, windows-aarch64 deps, windows nextest)
Three independent CI failures, none caused by the font-lock push:

1. CanvasKit web-host test variables_panel_open_does_not_paint_legacy_modal
   false-tripped: its '> half viewport' heuristic was a proxy for 'no
   full-viewport modal', but only passed because the property panel used
   to narrow the canvas and clamp the floating VariablesPanel below
   half-width. The nothing-selected-by-default change collapsed the
   property panel, widening the canvas so the legitimate 820x480 panel
   paints unclamped. There is no legacy modal. Retarget the assertion at
   a near full-viewport round fill and positively assert the panel paints.

2. windows-aarch64 cross-check failed: op-host-services + op-orchestrator
   pulled op-mcp's native-only 'script' feature unconditionally, dragging
   in rquickjs-sys which ships no bindings for aarch64-pc-windows-msvc.
   Enable 'script' per-target (off on that unshipped check-only arch).

3. Windows workspace tests segfaulted single-threaded (before the first
   test prints, under --test-threads=1) — an intra-test race a thread
   flag can't serialize. Switch the Windows runner to cargo-nextest (one
   process per test) so the crash is isolated + named; doctests run
   separately.
2026-07-06 22:04:56 +08:00
Kayshen-X 40c3094077 ci: run whole workspace single-threaded on Windows (skia/DirectWrite)
The STATUS_ACCESS_VIOLATION is not confined to op-host-native/-desktop: any
crate whose tests reach a skia FontMgr (DirectWrite) via NativeBackend or
SkiaMeasure crashes when that test binary runs across parallel worker threads
(op-host-services surfaced next). Excluding crates one-by-one doesn't
converge. Instead run the entire workspace with --test-threads=1 on Windows
only; each binary is serialized (cross-process concurrency is safe), covering
every skia-touching crate at once. macOS / Linux stay parallel.
2026-07-05 23:22:29 +08:00
Kayshen-X 247583a054 ci: serialize op-host-native tests too (Windows DirectWrite segfault)
The Windows STATUS_ACCESS_VIOLATION surfaced in the op_host_native test
binary once op-host-desktop was serialized: op-host-native's backend::skia
tests create a skia FontMgr (DirectWrite) and segfault when run across
parallel test-worker threads. Extend the single-threaded run to cover
op-host-native as well as op-host-desktop in both CI workflows.
2026-07-05 22:56:22 +08:00
Kayshen-X 6efae16f14 ci: split multiplatform host-test into two steps (avoid pwsh exit masking)
Windows runs multi-line run: blocks under PowerShell, which reports only the
last command's exit code. Keeping the workspace test and the serialized
op-host-desktop test in one run: block would let a workspace-test failure be
masked by the second command passing. Split into two steps so each fails the
job independently.
2026-07-05 22:52:40 +08:00
Kayshen-X ce3ba6d527 ci: serialize op-host-desktop tests in multiplatform Windows job
Mirror the rust-check.yml fix into the cross-platform host-test step: the
op-host-desktop test binary segfaults on the Windows runner when its native
(skia/DirectWrite) tests run across parallel test-worker threads. Run that
crate single-threaded here too.
2026-07-05 22:48:02 +08:00
Kayshen-X 7439e25f70 ci: run op-host-desktop tests single-threaded (Windows skia segfault)
op-host-desktop tests build native NativeBackends (skia/DirectWrite fonts);
across cargo's parallel test-worker threads this segfaults on the Windows
runner. Split them out of the parallel workspace run and serialize with
--test-threads=1.
2026-07-05 22:41:18 +08:00
Kayshen-X 7607f061ac ci: trigger rust workflows on vendored-dep changes
PRs touching vendor/casement (the winit fork) or vendor/agent rode
green with zero CI: rust-check's pull_request paths had no vendor
entry and rust-multiplatform only listed vendor/jian. Add them, and
refresh the Linux test-step comment now that the GPU smoke tests run
un-ignored via the provider GL loader.
2026-07-03 00:08:24 +08:00
Fini 6ed9548c0d merge: land the align-branch work onto the refreshed base
47 commits from the align branch merged onto the force-updated remote
base (which had replayed an earlier snapshot of the same work plus new
overlay/pointer features and CI fixes). Conflict resolution: kept the
newer align side for the generation pipeline (orchestrator, mcp, skills,
design tools), kept the base side for the chat-panel test semantics and
graceful overlay teardown, fused both in sub_agent_session (design-turn
thinking policy + graceful epoch finish), and dropped the files each
side had deleted (legacy concurrent/dashboard paths, retired TS skills).
Deduped two identical replayed hunks (export.rs, chat_session_tests.rs).

Known issue carried over: provider_probe_host::landed_connected_outcome_
without_models_is_failure fails on a host with a live provider config
(env-sensitive test, both sides byte-identical there; green on CI).
2026-07-03 00:08:17 +08:00
Kayshen-X a7d73ebb62 feat(ai): pencil-style agentic design tool-loop, multi-chat tabs, #27 panel restyle
Built-in design generation now runs as an agentic MCP tool-loop (reusing the
agent-rs BuiltInProvider), gated behind OPENPENCIL_DESIGN_AGENT_LOOP / the
Settings experimental toggle; the orchestrator stays the default.

- design-agent system prompt + in-process design toolset (parity-locked with
  the MCP surface) + flag-gated Intent::Design routing
- spawn_agents execution as sequential sub-loops + live creation-mode badges
  (per-agent glow + 'N/M designing...' header)
- new MCP tools: get_guidelines, ToolSearch, get_screenshot, get_editor_state,
  export_nodes, spawn_agents; style-guide local audit
- #27 AI panel restyle: rounded tool cards + green check-rings, gray user
  bubbles, model-pill bottom toolbar, header, empty-state pills, the
  PARALLEL AGENTS (agent_team_size) 1x-6x chip dropdown
- multi-chat tabs: ChatSessions model (Deref-to-active) + tab row UI
  (switch / close / + / Cmd+T) with each run bound to its tab

Large checkpoint commit spanning the working tree (Rust shell crates).
2026-07-02 21:21:06 +08:00