23 Commits

Author SHA1 Message Date
Alonso Torres
adda9ad908
✨ Restrict global plugins to MCP and localhost (#12949)
Plugins with global scope or a management permission (allow:global,
manage:projects, manage:teams, manage:delete) keep running outside a file
and can drive the management API. That feature is still in progress, so
only the bundled MCP plugin and plugins served from localhost, 127.0.0.1
or [::1] get it. The host check ignores letter case and a trailing dot.

The check runs at three points:

- parse-manifest throws global-not-allowed, so the plugin manager and the
  dashboard install link refuse remote global plugins with a specific
  message. Opening an installed plugin fetches its manifest again, so
  plugins stored before this change are refused too.
- start-plugin! and load-plugin! refuse to run such a plugin and warn the
  user; this also covers bundled plugins without a URL.
- has-permission? withholds the global permissions from stored remote
  plugins, so the management API rejects them.

AI-assisted-by: claude-opus-5-5
2026-11-11 10:28:11 +01:00
Dominik Jain
be17fabc6a 🎉 Introduce global plugins and move MCP to application scope
Global plugin lifecycle:
- Add optional manifest scope "global" for plugins that remain active
  across dashboard and workspace navigation within one browser tab.
  Existing plugins retain workspace scope by default.
- Preserve global sandboxes when a workspace ends or another plugin
  loads, including when that other plugin fails to initialize.
- Avoid duplicate instances of an already-running global plugin.
- Close global plugins on explicit closure or logout, with idempotent
  cleanup of listeners, timers and UI resources. Global scope does not
  imply automatic startup or persistence across a browser reload.

Plugin API and navigation:
- Keep a permanently live penpot facade whose getters follow the current
  workspace, while user, theme and UI facilities remain available on
  the dashboard.
- Expose penpotMgmt to global plugins with workspace metadata,
  none/loading/ready status and workspacechange subscriptions.
- Add awaitable openFile(fileId, {teamId}) navigation in the current tab,
  defaulting to the current team and resolving only once the requested
  file and its active page are ready.
- Reject invalid identifiers, failed or superseded navigation and waits
  exceeding 30 seconds; release navigation watchers on completion.
- Guard workspace-bound operations while no workspace is ready and
  return null from root, currentFile and currentPage in that state.
- Require content:read for the initial management API. Callers must
  reacquire file, page and shape references after changing files.

MCP lifecycle and connection controls:
- Make MCP a global plugin and initialize its integrated controller per
  authenticated application session instead of per workspace.
- Load the enabled integration with a valid token on the dashboard as
  well as in a file, retaining its existing hidden-iframe implementation.
- Keep enabling and connecting separate: an enabled plugin starts idle,
  and each tab connects explicitly through dashboard or workspace
  controls.
- Preserve the iframe, WebSocket, heartbeat and reconnect machinery
  during navigation; disconnect on request and close on disable/logout.

MCP sessions and execution:
- Derive the short MCP session ID solely from currentUser.sessionId,
  excluding the file ID. Retain the SHA-256-based, 50-bit Base32 encoding
  to produce a stable, copyable 10-character identifier.
- Allow sessions without an open file and update workspace metadata over
  the existing connection without replacing its routing identity.
- Refresh context after the connection handshake so initialization does
  not leave stale file metadata.
- Expose penpotMgmt to execute_code, preserve execution storage across
  navigation and skip layout waits when no workspace is ready.
- Update API types, tool guidance, documentation and the changelog.

Validation:
- Cover authenticated startup, workspace survival, logout cleanup,
  readiness, navigation failure, duplicate loads and session routing.
- Verify that short IDs remain stable across files and reconnects and
  that disconnect cancels pending asynchronous ID initialization.
- Pass 16 focused frontend tests, 73 runtime tests, 30 MCP plugin tests,
  builds, TypeScript checks and frontend/runtime lint.
- Pass all 73 MCP server tests with --test-force-exit; the normal runner
  still stays alive after assertions finish.
- Verify a live dashboard-to-file-to-dashboard round trip without a
  document or iframe reload, preserving facade identity and storage.

AI-assisted-by: gpt-6
2026-10-07 14:58:32 +02:00
Andrey Antukh
9289e676e1 ✨ Require re-confirmation when plugin manifest differs on open
When the fetched manifest differs from the stored manifest
(code, name, etc.), show the permissions dialog instead of
silently updating — preventing execution of tampered/injected
plugins.

On fetch error, show a warning notification instead of loading
with the old manifest.

Bundled plugins (no URL) skip validation as they are trusted
with no remote source.

Completes the 3-layer defense for T3-N1-02: (1) closed
permission schema, (2) dedicated RPC methods, (3) integrity
validation on open.

AI-assisted-by: qwen3.7-plus
2026-09-07 09:30:01 +02:00
Andrey Antukh
81c3b3cd56
📎 Update copyright name on file header (#11346) 2026-08-25 11:55:10 +02:00
Andrey Antukh
e2d429d283
🐛 Add timeout to plugin manifest fetch (#11120)
The fetch-manifest function previously had no timeout, causing the
plugin installation flow to hang indefinitely if the server accepted
the connection but never completed the response.

Added a 15-second timeout using rx/timeout to abort the request
automatically.

Closes #11119

AI-assisted-by: qwen3.7-plus
2026-08-07 13:37:00 +02:00
Alonso Torres
8823f7ac4d
✨ Make v2 plugins default throw on error (#10433) 2026-06-30 14:50:50 +02:00
Andrey Antukh
a8d0c18c1b
🐛 Fix race condition between MCP initialization and plugin runtime (#10137)
* 🐛 Fix race condition between MCP init and plugin runtime

Add promise-based synchronization to ensure MCP initialization waits
for plugin runtime to be ready before calling global.ɵloadPlugin.

- Add runtime-ready-promise in app.plugins that resolves when
  init-plugins-runtime completes
- Add wait-for-runtime function for other modules to await readiness
- MCP init now waits for runtime via rx/from before starting plugin
- Add defensive guards in start-plugin!, load-plugin!, close-plugin!
  to check if plugin APIs exist before calling
- Rename init-plugins-runtime! to init-plugins-runtime

Fixes: global.ɵloadPlugin is not a function error when MCP plugin
starts before async plugin runtime initialization completes.

* 📎 Add 'create-pr' opencode skill
2026-06-12 11:40:02 +02:00
Yamila Moreno
ddba2ffa75
📎 Update Kaleidos Copyright (#9929) 2026-05-29 11:24:58 +02:00
Andrey Antukh
3a39676969 ⏪ Backport MCP from staging (part 1) 2026-04-20 19:37:02 +02:00
Andrey Antukh
b4c279ad7b 💄 Add minor cosmetic refactor on how plugin flags are stored
The main idea behind this, is move all plugin related stuff from
app.main.data.plugins into app.plugins.* and make them more consistent.
Also the intention that put all plugins related state under specific
prefix on the state.
2026-02-25 11:35:03 +01:00
alonso.torres
ef2bdf86d8 ✨ Add event to create shape in plugins 2026-02-03 13:09:58 +01:00
alonso.torres
512a31d375 ✨ Add naturalChildOrdering flag to Plugin's API 2026-02-03 13:09:58 +01:00
Andrey Antukh
0ab126748f
💄 Add format rule for code comments (#8211)
* 💄 Add format rule for code comments

* ⬆️ Update linter and formatter on devenv
2026-01-27 15:07:18 +01:00
Andrey Antukh
283eb0419c ♻️ Refactor time related namespaces
Mainly removes the custom app.util.time namespace
from frontend and backend and normalize all to use
the app.common.time namespace
2025-08-01 11:20:01 +02:00
María Valderrama
95f4a9bd29
✨ Add missing start-plugin event (#6809)
* ✨ Add missing start-plugin event

* 📎 Correct event origin
2025-07-03 10:29:43 +02:00
Miguel de Benito Delgado
71bb2556f9
♻️ Move page setup out of the data.workspace ns (#6502)
* ♻️ Split history workspace.cljs to workspace/pages.cljs - rename file to target-name

* ♻️ Split history workspace.cljs to workspace/pages.cljs - rename source-file to git-split-temp

* ♻️ Split history workspace.cljs to workspace/pages.cljs - restore name of source-file

* ♻️ Cleanup after adding new ns, add exports

* ♻️ Move set-plugin-data to main.data.plugins

* 🐛 Possible bugfix, cherry-picked from commit 8f7c63d6e2c (conflict during refactor)

---------

Co-authored-by: Eva Marco <eva.marco@kaleidos.net>
2025-05-20 22:11:05 +02:00
Pablo Alba
c89b6e2d6d ✨ Add error control on plugins permissions peek 2024-11-11 09:07:39 +01:00
Pablo Alba
d9eff00a71 ✨ Integrate viewer role with plugin menus and popup 2024-11-08 10:37:57 +01:00
alonso.torres
6a07e6ae01 ✨ Add update plugin permission dialog 2024-10-10 17:12:39 +02:00
alonso.torres
1e68d4ec87 ✨ Close plugin on esc button 2024-10-10 16:03:45 +02:00
alonso.torres
379770343a 🐛 Close plugin if open when installed 2024-10-09 10:50:56 +02:00
alonso.torres
c16a116707 ✨ Modifications after review 2024-10-01 11:57:52 +02:00
alonso.torres
d1277afee6 ✨ New plugin install workflow 2024-09-30 16:03:40 +02:00