Skip to content

API plugins using localthought/integration-proxy - #1387

Open
michielbdejong wants to merge 59 commits into
feat/plugin-modelfrom
feat/api-plugins
Open

API plugins using localthought/integration-proxy#1387
michielbdejong wants to merge 59 commits into
feat/plugin-modelfrom
feat/api-plugins

Conversation

@michielbdejong

@michielbdejong michielbdejong commented Sep 8, 2026

Copy link
Copy Markdown

Adds browser-owned integrations from a localthought/integration-proxy server alongside the existing bundled plugins.

Users can choose the server under Settings → Integration without rebuilding the frontend. The form follows the Ollama settings layout, validates HTTPS/loopback origins, and supports resetting to the deployment default (VITE_INTEGRATION_PROXY_URL, falling back to https://localthought.io). Saved connections are isolated by proxy origin; existing browser connections migrate only when their origin and owner match.

Proxy integrations have an accent border and a “Via integration proxy” label. The original GitHub, Notion, Clockify and MT940 cards remain available independently, including when the proxy is unavailable. Existing provider implementations, Notion OAuth protocol, certification metadata and shipped-bundle regression tests remain in the repository.

The proxy flow discovers a catalog, connects accounts with PKCE, imports paginated records through Syncables/WASM, and previews changes before applying them locally. Generic metadata-driven traversal and request-limit improvements remain included.

Validation:

  • LocalThought/settings tests, restored provider fixture suites, provider UI tests and certification checks pass.
  • Frontend typecheck, lint and formatting pass.
  • Browser verification covers settings search, save/persistence, URL validation, reset, proxy catalog selection, and the original GitHub setup alongside the proxy card.
  • Restored OAuth and provider sandbox tests run against the actual shipped bundles.

Live provider tests requiring credentials and the full browser E2E suite were not rerun for the scope-restoration change.

CI scope

General CI/cache/runtime hardening, workflow changes, and certification tooling improvements have been split into #1424, independently based on feat/plugin-model. This PR retains only CI additions required to build and test the integration feature. No checks were disabled. CI is being rerun on the narrowed branch.

First commit toward rebuilding the API-plugins direction from PR #1383 on
feat/plugin-model. Pets is a static, provider-free demo integration (five
pets, no OAuth, no secrets) that walks every touch point a real API plugin
needs: a code-first ontology, an import mapping into the shared sandbox, a
reproducible plugin.js bundle, an installable connection with a table and
view, integration discovery in the UI, and a server-side sandbox test.

See planning/api-plugins.md for the direction, including the next step:
discovering providers through a localthought proxy instead of hand-writing
integrations/<name>.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj
joepio and others added 9 commits September 8, 2026 18:37
…ries (#1384)

* docs(planning): security and code-quality audit, September 2026

Consolidated findings for server, lib, browser, desktop, CLI and CI:
unauthenticated /iroh-sync, SYNC_PUSH without per-entry authorization,
agent impersonation via /agents/{key} URLs on arbitrary hosts, committed
API keys, forwarded-host trust, plus dependency advisories and a list of
sloppy code with file:line references.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* fix(security): close the sync, authentication and rights gaps from the September audit

Findings from planning/security-audit-2026-09.md fixed on this branch:

- Remove the committed editor config carrying live API keys, plus the
  tracked pnpm-store and vitest cache files; ignore them.
- /iroh-sync requires a signed agent with write on the drive (or the
  policy's leave to bring a new drive here) and only answers POST. Dial-
  side owner trust is confined to the drive that was dialed for.
- SYNC_PUSH checks every entry against the admitted drive: an existing
  resource must carry it as its stored drive stamp, a new one must
  resolve to it via its parent or its own stamp.
- Legacy https://host/agents/{key} subjects bind the key in the path to
  the signing key in both header auth and commit validation; agent
  subjects on other hosts are refused instead of fetched.
- check_append no longer falls back to the new resource's own write
  array, and a parentless did:ad:agent genesis is accepted only from
  that key or the node's own agent. Regression tests for both.
- Request origin is taken from Host/X-Forwarded-* only for the
  configured domain, its tenants or loopback; the WebSocket handler uses
  the same origin. Unit tests.
- /forget-peer requires write on a drive the peer was paired for (known
  peers now record those) or the node's own agent.
- Desktop and Android bind the embedded server to 127.0.0.1 by default.
- The data-browser accepts a node-reported portalUrl only as https (or
  http on localhost), pins the portal a device token was issued by, and
  routes every portal navigation through safePortalUrl. URI values and
  downloadUrl reject javascript:/data:/vbscript: schemes.
- BLOB_RESPONSE bytes must hash to the requested key; downloadUrl is
  escaped in the SPA meta tags; the plugin-ui query string is attribute-
  escaped; multipart uploads and search limits are bounded; LanceDB
  filter literals are quoted; origin() no longer panics; image rendition
  parameters are quantized before caching; config.toml is written 0600;
  plugin RPC ignores foreign message sources; the session cookie is
  Secure on https.
- Sloppy code: ValueComp comma-case, unawaited resource.save() calls, the
  CLI agent race, a commented-out tool block, the duplicate EventManager,
  SIGNER set twice, bitwise & on bools, the empty authorization test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* fix(server): treat *.localhost names as loopback in the request-origin check

CI serves the app at atomic.localhost for a server whose domain is
`atomic`, so the browser signs its WebSocket AUTH and requests for that
origin. The host allowlist added for the forwarded-host fix rejected it,
fell back to the configured origin, and every signed request failed
("requestedSubject ... does not name this server"). Names under
.localhost resolve to loopback by definition (RFC 6761), so accept them
like localhost itself.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* test(browser): mock setManagedDeviceToken in the managed session test

logoutManagedSession now also drops the device token, so the api mock
from develop's new session test needs that export too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* fix(security): bind Iroh AUTH to the responder, close the remaining rights gaps, bump advisories

Second round of fixes from planning/security-audit-2026-09.md:

- Iroh AUTH: the dialer signs `drive#<responder node id>` and the accept
  side verifies its own node id as the connection's challenge, so a proof
  captured by one responder cannot open another. A proof without a node
  id is honoured only from a peer this node paired with.
- check_append: a parentless non-agent DID is allowed only when it has no
  parent at all, or when its drive stamp names a drive the agent may
  append to; a non-DID top-level Drive needs the node's own agent.
- Path-only auth signatures are no longer accepted; the signer's Agent
  resource is created only after the commit is accepted; genesis certs
  with a parent or drive must match the document; did: resources are
  indexed only into their own drive's watched queries; Agent and
  SharedConfig redact secrets in Debug.
- Plugin zips and bookmark bodies are fetched through the SSRF guard
  with size caps; EPHEMERAL Loro payloads are relayed only from a
  subscriber; the ACME flow returns errors instead of panicking and a
  daily task renews the certificate on disk; /plugin-list and plugin UI
  files are read as the calling agent (the data-browser signs the list
  request) and the plugin parameter is validated; default_service logs
  at debug.
- The Flutter bridge deletes the database only on a corruption error;
  the atomic-saas checkout no longer persists its PAT; release.yml has
  per-job permissions; notarization secrets go through env; the desktop
  devtools feature is opt-in.
- Dependencies: wasmtime 47.0.4, h2 0.4.19, quinn-proto, rustls-webpki
  0.103.13; @tiptap/* 3.30, @modelcontextprotocol/sdk 1.30 and the
  transitive build tools, after which pnpm audit reports nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* docs: changelog entry for the security audit fixes

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* test(lib): upload integration tests create the agent's private drive

The fixture fell back to the server's root URL as the upload parent when
the config carried no initialDrive. A fresh server has no resource at
that URL since the key-derived drive became the default, and the
tightened check_append now refuses a child whose parent is not on the
server, so the roundtrip test failed with "Parent ... not found".
Materialize the private drive through Store.ensurePrivateDrive, the
same path the data-browser takes at sign-in, and upload into that.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* Fix desktop workspace discovery and restore feedback

* style(browser): fix the lint errors develop gained in #1389 and #1390

`pnpm lint` failed on develop's own head after those merges: missing
blank lines in websockets.ts, LocalOllamaDiscovery.tsx and the two new
e2e specs, and three shadowed names in username-live.spec.ts. The CI
pipeline stops at the first failing package, which hid the rest.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* test(data-browser): give the whole-app compiler test a 60 second budget

It transforms every TSX file in the app: about two seconds on a
workstation and on the self-hosted runner, nine on a two-core hosted
runner, where vitest's default five-second budget failed it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* revert(lib): drop the drive-scoping of DID resources in watched queries

For a `did:` query, `QueryFilter.drive` is `drive_prefix_from_subject`
of the queried subject, which is that subject itself rather than the
drive root. Comparing it with a resource's `drive` stamp excluded every
legitimate row, and the query_aggregates tests failed on the first CI
run that reached them. Back to develop's index; C17 moves to the
report's not-fixed list with what a correct fix needs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* test(lib): drop the drive-scoping test that went with the reverted C17 change

It asserted the watched-query index behaviour the previous commit
withdrew, so it failed on the first CI run after the revert.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* fix(lib): do not steal our own OPFS lock during a slow cold start

Ported from #1386. When the leader election window (2 s) elapses while
this tab already holds the lock and its worker is still importing WASM
and opening OPFS, the election treated the silence as a ghost leader,
logged the "stealing OPFS lock" warning and re-requested the lock with
steal. On a loaded CI runner every fresh page hit that path, and the
browser-diagnostics gate from #1382 fails a spec on any warning, so the
light e2e suite was red on develop and on this branch. Skip the steal
when our own worker already exists; the settle wait below still covers
the genuine ghost case. Adds #1386's unit test for the cold start.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* fix(data-browser): skip the SaaS logout call on a FOSS server

Ported from #1386. Signing out posted /api/logout to whatever
getManagedApiBase resolved to, and on a self-hosted node that is the
node's own origin, which answers 405. The browser logs that as an
error, and the diagnostics gate from #1382 fails the sign-out smoke
spec on it (the only failure left on this branch's hosted run).
logoutManagedSession now returns early when no control plane is known:
no linked portal, no build-time API base, no remembered portal and no
compiled-in portal URL. The local device token is still dropped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* chore(browser): dedupe prosemirror-model and prosemirror-view

The @tiptap bumps in this PR pulled prosemirror-model 1.25.11 in next
to the 1.25.7 that @tiptap/pm still resolved, and the two copies made
`pnpm typecheck` in data-browser fail on incompatible Node types in the
RTE chunks. `pnpm dedupe prosemirror-model prosemirror-view` collapses
them (and, as dedupe does, other duplicates to versions the lockfile
already carried: vite 8.2.2, codemirror state and lint, picomatch).
Typecheck, lint, the data-browser unit tests and the vite build pass;
a frozen offline install is consistent.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* Fix formatting and lint checks for desktop restore PR

* Validate sync entries before persistence and bind discovery authentication

---------

Co-authored-by: Claude <noreply@anthropic.com>
* Cancel automatic Vault work when the account session ends

* Improve collaboration profiles and managed subscription UX

* Fix nodeless profile loading and cold database startup warnings

* Settle lint and formatting after develop integration

* Allow cold CI time for whole-app compiler regression check

* Scope Cloud Server hosting and sync status to the selected drive

* Preserve drive display metadata and scope managed billing requests
* Prepare 0.41.0-beta.6 versions and release gates

* Record full beta.6 E2E results and release blockers

* Fix release E2E regressions in sync and plugin loading

* Finalize beta.6 release notes after passing E2E
CI's fmt check failed on the multi-line assert! in pets_tests.rs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

Copy link
Copy Markdown
Author

CI red: Main (GitHub) / CI — not this PR's, pre-existing on feat/plugin-model

The failing step is the production frontend build (jsBuild()vite build in data-browser), which errors with [UNRESOLVED_IMPORT] for every ../../../../../integrations/<name>/* import in browser/data-browser/src/chunks/PluginRuns/*.tsx — now including ConnectPets.tsx's imports of integrations/pets/schema and integrations/pets/plugin.js?raw.

This reproduces identically on this PR's own base commit, feat/plugin-model@3563a7ee24 (before any of my changes), for the pre-existing mt940, clockify and github-issues integrations: run 34218651424 fails with the same Could not resolve '../../../../../integrations/mt940/schema' / .../clockify/atomic / .../github-issues/plugin.js?raw errors. Every push to feat/plugin-model back to late August has this job red for the same reason, so it isn't a flake and it isn't specific to pets — I didn't spend the one re-run on it since a fresh run would reproduce deterministically.

Root cause: .dagger/src/index.ts's jsBuild() (used by the production vite build / e2e / netlify-preview pipeline) mounts only browser/ into the container (.withDirectory('/app', browser), ~line 1048). It never mounts the repo-root integrations/ directory, so any relative import reaching outside browser/ into integrations/ can't resolve inside that container — even though it resolves fine locally (full checkout) and under tsc. The JS-certification stage (integrationCertification-ish, ~lines 452/472) already does mount it (.withDirectory('/integrations', this.source.directory('integrations'))), so this is just a gap in jsBuild(), not a design decision. The existing lib-defaults case at line 1049 (.withDirectory('/app/lib-defaults', ...), aliased in vite.config.ts as @repo-lib-defaults) is precedent for mounting a repo-root sibling into this exact container — the fix would be the same shape: mount this.source.directory('integrations') at /integrations (sibling of /app, matching the real repo layout) so the existing plain relative imports resolve unmodified.

I'm not making that change myself — it's shared CI infrastructure used by every branch/PR, not code this PR was asked to touch, and outside the "trivial demo plugin" scope I was given. Flagging it here since pets is now one more instance of it. Let me know if you'd like me to send that fix as its own PR.


Generated by Claude Code

Michiel de Jong and others added 2 commits September 9, 2026 10:03
Mounting /integrations (previous commit) fixed the UNRESOLVED_IMPORT
errors, but every integrations/*/tsconfig.json extends the repo-root-
relative ../../browser/tsconfig.build.json, which doesn't resolve
inside the container: browser mounts at /app, not /browser. jsTest()
and integrationCertificationReport() already hit this and fix it with
`ln -s /app /browser`; apply the same fix to jsBuild(), which backs the
production vite build, e2e and netlify-preview pipelines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

Copy link
Copy Markdown
Author

Update: the /browser alias fix (previous commit) resolved the UNRESOLVED_IMPORT/tsconfig failures — Main (GitHub) / CI now gets past the frontend build entirely.

It's now failing one step later, at pnpm -r lint in browser/e2e (oxlint ... && pnpm format-check, "Found 26 warnings and 5 errors"). This is also pre-existing and unrelated to this PR: every flagged line already exists verbatim on this PR's base commit (feat/plugin-model@3563a7ee24, before any commit here) — e.g. the typescript(no-explicit-any) hits on (window as any).store in plugins.spec.ts and installation-recovery.spec.ts:20, and the no-console hits in scripts/record-features.mjs, are all unchanged pre-existing code (verified with git show 3563a7ee2:browser/e2e/tests/plugins.spec.ts). None of it is in a file this PR touches beyond appending the new Pets test near the top of plugins.spec.ts, which shifted these pre-existing lines' numbers but not their content.

Not fixing the e2e package's existing lint debt here — that's a separate, unrelated cleanup outside a trivial demo plugin's scope. Flagging per the same "not this PR's" reasoning as the earlier comment.


Generated by Claude Code

claude and others added 5 commits September 9, 2026 08:51
The e2e run showed the Pets card rendering with a blank heading and
description text: wuchale renders an untranslated string as empty
rather than falling back to source text, and these new literal strings
(IntegrationDiscovery.tsx's bundledIntegrations() entry) were never
extracted into the catalogs. Settled by running the app, per AGENTS.md's
translation-catalog guidance (matches the dev server's fixed point,
unlike the standalone `wuchale --clean` extractor).

ConnectPets.tsx's own strings (only reachable once the setup dialog is
opened against a real drive) still need the same treatment in a
follow-up commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj
Same issue as the previous commit, for the strings only reachable once
the Pets setup dialog is actually opened (Install/Reinstall demo pets,
the description paragraph, Open Pets). Settled by running the app
against a real dev-drive session and opening the dialog, so this
matches the dev server's fixed point exactly (verified: purely
additive, 20 lines per locale, no reordering).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj
…-plugins

# Conflicts:
#	browser/data-browser/src/locales/de.po
#	browser/data-browser/src/locales/en.po
#	browser/data-browser/src/locales/es.po
#	browser/data-browser/src/locales/fr.po
claude and others added 5 commits September 9, 2026 10:01
…finish

Root-caused the CI failure at "Apply 5 changes": not a hang, not a
missing translation, and not an artifact of a locally-underbuilt
server (ruled that out by rebuilding atomic-server with the real
wasm32-wasip2 plugin runtime and rerunning). Installing genuinely
takes ~24s — pluginClassesFor's six sequential lookups, two
ensureSchema calls, three ensureInstallationResource calls, then the
sandboxed plugin run itself — measured locally on a fresh, otherwise
idle server via the real `playwright test`, not a synthetic script.
The dialog's default 10s expect timeout is tuned for interaction
latency, not this one-time setup cost. Same targeted-timeout pattern
already used elsewhere in this file (vault-backup-restore.spec.ts,
onboarding.spec.ts, etc.).

Verified: `playwright test -g "Pets installs its demo table"` passes
end to end (~47s) against a real dev-drive session with the actual
embedded plugin runtime.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj
The Pets E2E job consistently failed at "Apply 5 changes" never
enabling, even with a generous timeout. The server log in the job
output shows why: `/plugin-run` 500s with "this server was built
without the plugin runtime... Rebuild with the wasm32-wasip2 target
installed."

rustBuild()'s e2e path enables the `wasm-plugins` cargo feature, whose
build.rs compiles atomic-plugin-runtime for wasm32-wasip2 as a nested
cargo build. Without that target's std lib installed, the nested build
fails and build.rs treats it as an optional degradation — it embeds an
empty runtime instead of failing the outer build, so the gap is silent
until something actually calls /plugin-run. rustTest() already adds
this target for the identical reason; rustBuild() (which builds the
binary the e2e Playwright suite actually talks to) never did.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj
jsBuild()/jsTest() only mounted testdata/pairing-request.json as a
single file and never mounted lib/defaults/tasks.json outside the
Vite-only /app/lib-defaults alias. Newly added
browser/lib/src/plugin-manifest.test.ts,
plugin-plan.fixtures.test.ts, and task-schema.test.ts read these
repo-root fixtures directly via readFileSync/readdirSync (not
Vite-resolved), so `vitest run` failed with ENOENT for all three in
CI while passing locally against a full checkout.

plugin-plan.fixtures.test.ts also readdirSync()s the whole
testdata/plugin-plans/ directory, which a single-file mount can't
provide, so testdata/ is now mounted whole (it isn't an OS path, so
unlike /lib there's no collision risk). lib/defaults/tasks.json stays
a single-file mount, matching the existing genesis_test_vectors.json
precedent, to avoid touching /lib itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj
The wasm32-wasip2 fix (20368ff) didn't take: the Pets E2E run on
that exact commit still failed with the identical "this server was
built without the plugin runtime" 500. build.rs's build_plugin_runtime()
only declares `rerun-if-changed` on plugin-runtime/{src,wit} and the
ATOMICSERVER_SKIP_PLUGIN_RUNTIME env var — nothing tells cargo that
"the wasm32-wasip2 target just became installed" should invalidate its
build-script fingerprint. Every prior CI run had already cached an
empty embedded runtime (from before that target existed) in the
rust-target-v3 volume, so cargo kept trusting that stale fingerprint
and never re-ran the nested wasm32-wasip2 build to notice the target
was now there.

This is the same class of staleness bug TOUCH_WORKSPACE_SOURCES
already documents fixing twice before (the -v2 and -v3 renames on this
same volume, for the same "cached artifact looks newer than fresh
sources" reason) — same fix, one more rename to force a clean
re-evaluation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

Copy link
Copy Markdown
Author

CI red on 6acc3fa75 — not this PR's, looks like a resource-contention flake

Main (GitHub) / CI failed in data-browser test: src/oxc-react-compiler.test.ts › transforms every app TSX/JSX file without a fatal error hit vitest's default 5000ms timeout (818/819 other tests passed). That test synchronously walks and oxc-transform-react-compiles every .tsx/.jsx file under data-browser/src in one it() block with no custom timeout — CPU-bound, no I/O, so a shared/loaded CI runner is enough to tip it over 5s. It predates this PR (381a517dd, before any commit here) and nothing in this PR touches it or its inputs.

Not spending the one re-run on this stale commit — a fresh run is already in flight for bae16426c (the next commit, unrelated to this test) and will exercise the same test independently. If it recurs there I'll treat it as real and either fix the root cause or give the test its own timeout budget.


Generated by Claude Code

Michiel de Jong and others added 3 commits September 9, 2026 13:28
src/oxc-react-compiler.test.ts's "transforms every app TSX/JSX file
without a fatal error" test compiles every TSX/JSX file under src/ one
at a time in a single it() block, with vitest's default 5000ms budget.
That budget was already tight and now consistently times out in CI
(reproduced twice in a row on the same commit, ~38s total suite
duration but this one test alone exceeding 5s) — this is deterministic
under CI load, not a flake, and the file count only grows over time.
Give it its own 20s timeout; the assertions are unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj
My previous commit's manual reformatting for the it() timeout argument
didn't match oxfmt's expected style, which failed the format-check
lint step. Move the explanatory comment onto a named constant instead
of a trailing comment after the test body, which oxfmt formats cleanly
and reads better besides. Verified with oxfmt --check, oxlint, and a
local vitest run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj
Michiel de Jong and others added 8 commits September 9, 2026 14:40
)

* Add LocalThought catalog plugins with typed paginated imports

* Record successful live GitHub import verification

* Add bounded Calendar event imports through Syncables

* Record successful scoped Calendar live import

* Fix LocalThought lint and retain upstream preview timing checks

* Fix shared Cargo cache locking across CI containers

* Allow the full-app compiler test to finish on hosted CI

* Queue main CI runs instead of replacing pending validation

* Run LocalThought integrations entirely in the browser (#1401)

* Run LocalThought integrations entirely in the browser

* Run focused Pets E2E on the browser integration branch

* Add browser Devonian issue tracker sync demo (#1399)

* Add browser Devonian issue tracker sync demo

* Use browser tenant connections and test two-way issue sync

* Record live proxy OAuth verification and repository access blocker

* Run one-way Google Calendar sync entirely in the browser (#1398)

* Show one-way Google Calendar imports in a calendar view

* Verify browser-only Calendar sync through shared mock proxy

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>
@michielbdejong

Copy link
Copy Markdown
Author

This branch now contains the machinery for adding API imports into the an atomic-server that runs in the browser. It relies on https://localthought.io (which runs https://github.com/localthought/integration-proxy on Heroku) for API key management and CORS proxying. To use it, log in to https://localthought.io with a Google account to become a tenant and receive a tenant secret that you can paste into atomic-server and start syncing your github issues and google calendars.

The proxy exposes a catalog based on a config file that points at a subset of https://github.com/localthought/overlays and https://github.com/localthought/openapi-directory.

The imported data becomes available in its own schema using https://github.com/localthought/syncables-rs.

It is then translated using https://github.com/localthought/devonian so that there is two-way sync between a github issue tracker and an atomic issue tracker, and between a Google calendar and an atomic calendar.

We should talk about how we want to brand and market this proxy service and its surrounding ecosystem (openapi-directory, overlays, syncables, devonian), for now I branded it as Local Thought which was a domain name I still had lying around and that I can donate to Ontola if you want to use it.

@michielbdejong michielbdejong changed the title API plugins: trivial demo Pets plugin API plugins: Sync your issue tracker with github and your calendar with Google Sep 9, 2026
@michielbdejong michielbdejong changed the title API plugins: Sync your issue tracker with github and your calendar with Google API plugins: issue tracker + calendar Sep 9, 2026
Michiel de Jong and others added 11 commits September 9, 2026 16:14
Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>
* Preserve and render exclusive date-only all-day event ranges

* Import and render recurring calendar series with exceptions and timezone support

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>
* Fix local integration reads reaching the server

* Fix integration formatting errors blocking CI

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>
* Connect LocalThought integrations without pasting a tenant secret

* Satisfy demo callback statement-spacing lint

* Apply Rust formatting required by CI

* Format calendar recurrence library for workspace CI

* Clarify automated coverage and live verification records

* Lint the plugin feature set used by CI tests and deployment

* Fix Node MCP loading and complete plugin CI feature coverage

* Declare certification compiler and report failed checks

* Align compiler dependency with workspace override

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>
@michielbdejong michielbdejong changed the title API plugins: issue tracker + calendar API plugins using localthought/integration-proxy Sep 10, 2026
michielbdejong and others added 6 commits September 10, 2026 15:08
…1419)

* Load Clockify and Notion integrations from Devonian

* Load OAuth token endpoint from trusted metadata

* Remove server tests for extracted integration bundles

* Restore integration regression tests during migration

* Follow OpenAPI links across sync collections

* Describe only root sync parameters

* Validate full Moneybird traversal fixture

* Consume platform lenses from Devonian

* Make integration OAuth provider-driven

* Keep import budgets in consumer and preserve mixed record fields

* Pin generated OAuth provider metadata

* Apply catalog import selections explicitly

* Handle declared absent objects and share link response context

* Document generic platform traversal regression coverage

* Extract GitHub and Calendar platform code

* Verify explicit selection removes inherited query defaults

* Validate per-item link selection and query removals

* Update Calendar import coverage ownership

* Load platform UI from Devonian registry

* Restore neutral external action test fixture

* Run external platform tests against pinned package

* Neutralize embedded platform host fixtures

* Document pinned external platform regression coverage

* Run external platform regressions in JS CI

* Render platform UI from external metadata

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants