Skip to content

Releases: emaxe/open-artifacts

v0.11.0 - redesigned share-page viewer panel

Choose a tag to compare

@emaxe emaxe released this 19 Sep 15:24

Changed

  • Redesigned the top panel of the shared-artifact viewer (/s/:token): the panel is now a single, always one-line row (logo, title, kind badge, version/date/author/team) with the actions on the right. Size, view count, expiry, the version note and the description moved into a details block that starts collapsed and is opened with a chevron (managers only; the choice is remembered). "Download source" and "Open in workspace" are grouped under a "⋯" menu when both are present. The version picker and the visibility control are now compact chips with icons, and the layout stays on one line down to phone width.
  • The visibility control applies on click. It is now a list of "Team only / Password protected / Public" rows with a one-line explanation each: picking one saves it immediately (a spinner, then a checkmark), updates the panel in place and no longer reloads the page, so the artifact keeps its scroll position and state. "Password protected" reveals a password field with a "Set password" button (Enter works too). A mode the team forbids is shown disabled with the reason.
  • Popovers close on Escape and on an outside click, return focus to their trigger, support arrow keys / Home / End in the visibility list, and announce their result to screen readers.
  • The panel's details block is collapsed by default, and its remembered state uses a new oa.viewer.details key (the old oa.viewer.panelCollapsed is ignored).

Fixed

  • Visibility control appeared to do nothing. The panel had max-height: 40vh; overflow-y: auto, which clipped the popover so the "Apply" button was out of reach, and picking an option in the <select> only revealed the password field without saving anything. The version picker was clipped the same way. Popovers are now anchored to their own trigger and are never clipped by the panel.

Full changelog: https://github.com/emaxe/open-artifacts/blob/main/CHANGELOG.md
npm: https://www.npmjs.com/package/@emaxe/oa

v0.8.0 - bilingual landing page and named share links

Choose a tag to compare

@emaxe emaxe released this 13 Sep 17:27

Added

  • A bilingual (RU/EN) marketing landing page at /, with an inline sign-in/sign-up card so a visitor can register or log in without leaving it. Signed-in users are redirected to their workspace as before; the same landing stays reachable at /welcome. A new unauthenticated GET /api/v1/instance/config endpoint tells the page (and, later, other pages) whether self-service registration is open, invite-only, or closed, so it never advertises a form that can only 403.
  • The "Sharing" table on an artifact's detail page can now name a link: an optional, creator-facing-only label (never shown to a visitor of /s/:token) on POST /artifacts/:id/shares (and the create_share MCP tool), so several links on the same artifact can be told apart at a glance.
  • Any active link in the "Sharing" table can be copied again at any time via a "Ссылка" button, not just right after creation — the list endpoint now returns each share's full URL.
  • The three separate "create link" buttons are now a single "Создать ссылку" dialog (access mode, optional label, password when applicable) whose submit button shows a loading spinner while the link is being created, instead of the button appearing to do nothing for a moment.

Fixed

  • incrementShareViewCount updated a share's view count via a read-then-write instead of an atomic SQL increment, silently losing counts under concurrent views. It's now a single atomic UPDATE ... SET view_count = view_count + 1.
  • The web dev server's proxy rule for share pages ("/s") was a bare prefix match, so it silently intercepted every request whose path merely started with "s" — including all of /src/*, which made pnpm dev:web serve a stale production bundle instead of live modules. Narrowed to "/s/".

Changed

  • A manager's (owner/team admin/superadmin) own view of their link is tracked again, in a separate managerViewCount — the "Sharing" table now shows both "Просмотры" (audience only, unchanged since 0.6.0) and "Всего" (audience + manager), instead of a manager's own check of the link looking like it wasn't counted at all.

Full changelog: https://github.com/emaxe/open-artifacts/blob/main/CHANGELOG.md#080---2026-09-13

v0.7.1 - fix attachment loading inside sandboxed preview/embed

Choose a tag to compare

@emaxe emaxe released this 13 Sep 15:52

Fixed

  • Attached files (images, text, and any other type) embedded inside an artifact's own content (<img src>, links, etc.) failed to load or open when viewed through the sandboxed preview/embed iframe (/api/v1/artifacts/:id/preview, /embed/:token), even though the exact same /af/:token URL worked fine opened directly in a browser tab. Two causes: the assetOrigin the artifact's Content-Security-Policy needs to allow media-src/font-src from the file-serving origin was never actually passed by either route, and the sandboxed iframes were missing the allow-downloads token, which browsers require before letting a sandboxed frame initiate any file download (every non-image/video/audio/font attachment is always served as Content-Disposition: attachment).

Full changelog: https://github.com/emaxe/open-artifacts/blob/main/CHANGELOG.md#071---2026-09-13

v0.7.0 - brand identity, S3 file storage, storage quotas

Choose a tag to compare

@emaxe emaxe released this 13 Sep 13:43

Added

  • Brand identity: the Open Artifacts logo now appears on the login/register pages (with a dark-theme wordmark variant), the sidebar rail and mobile header, and as the browser favicon/apple-touch-icon. Generated from a single source image via scripts/gen-brand-assets.sh (ImageMagick), checked in alongside it for reproducibility.
  • S3-compatible file storage for artifacts: images and other files can now be uploaded and attached to an artifact — POST /api/v1/artifacts/:id/files (multipart/form-data), GET .../files to list, DELETE .../files/:fileId to remove, served back through the app at GET /af/:token (never a public bucket, no presigned URLs). oa files upload|ls|rm on the CLI; list_artifact_files/delete_artifact_file as MCP tools (uploading itself is CLI/REST-only — MCP tool calls are JSON, not multipart). A file is always attached to exactly one artifact and is deleted with it (explicit delete, expiry, or an org being deleted all queue its object for removal); a file with no artifact cannot exist. docker-compose.yml now bundles a MinIO service as the default storage backend; point S3_ENDPOINT/S3_BUCKET/S3_ACCESS_KEY_ID/S3_SECRET_ACCESS_KEY at an external S3/R2/Spaces bucket instead, or leave S3_BUCKET unset to disable the feature entirely (upload endpoints answer 501, everything else is unaffected).
  • Storage quotas, instance- and team-wide: two new limits — total bytes per team and bytes per single artifact (both counting artifact source text plus uploaded files) — configurable instance-wide in /admin/settings and, per team, in its own team settings (the per-artifact one by an owner/admin, the team-wide one by a superadmin, matching the existing permission split for other org-level knobs). A team override can only ever be equal-or-stricter than the instance ceiling, never looser. Default: unlimited at both levels (see Changed below).
  • GET /api/v1/quota (and the get_storage_quota MCP tool / oa quota CLI command): lets an agent check remaining quota — team-wide, or narrowed to one artifact — before deciding whether to upload a file, rather than uploading blindly and handling the failure after the fact.
  • quota_exceeded is now a structured error (scope, limitBytes, usedBytes) instead of a flat message, on both the REST API and MCP tools, so a caller can act on it instead of just displaying it.

Changed

  • Breaking: a team's storage quota now defaults to unlimited instead of 1 GiB. Any team whose quota was never explicitly changed by a superadmin (i.e. still sitting at exactly the old 1 GiB default) is migrated to "inherit the instance quota" (itself unlimited by default); a team a superadmin deliberately set to a specific number keeps that number. orgs.storageQuotaBytes also changed from a required column to nullable (null = inherit) and from integer to bigint, since it now also counts uploaded file bytes, not just artifact source text.
  • Fixed a latent bug in the instance's own "maximum artifact size" setting (InstanceSettings.maxArtifactSizeBytes): it was editable in the admin UI and accepted by PATCH /admin/settings, but nothing ever read it back — artifact-size enforcement used a hardcoded 5 MiB constant regardless. It's now a required, resolved parameter on every artifact-creating/updating code path, the same pattern already used for artifact lifetime.

Fixed

  • Every "copy" button silently did nothing on insecure (plain HTTP) origins, or wherever the browser/embedder denied navigator.clipboard — that API is undefined or rejects in those cases without surfacing an error. Added a shared copyToClipboard() helper with an execCommand('copy') fallback, used by CopyButton and the public share page's own inline copy-link script.

v0.6.1 - Fix org slug resolution

Choose a tag to compare

@emaxe emaxe released this 13 Sep 10:54

Fixed

  • --org <team> (CLI) and orgId (REST/MCP) accept either a team's real id or its slug, per their own documentation — but passing the slug on an explicit request (anyone in more than one team, using --org/orgId to pick a non-default one) crashed with a 500: resolveOrgScope forwarded it straight into a UUID-typed database column instead of resolving it first. Affected POST/GET /artifacts and the org-scoped MCP tools; the single-team auto-selected case was never affected.

Found via an end-to-end check of the published npm package against a live server with more than one team.

v0.6.0 - Public Share Page Viewer Panel & Design Templates

Choose a tag to compare

@emaxe emaxe released this 13 Sep 10:31

Added

  • Viewer panel on the public share page (/s/:token): a header above the embedded artifact shows its title, type, displayed version number, and last-updated date to every visitor. A logged-in member of the artifact's team additionally sees the author and team name; the artifact's owner, a team owner/admin, or a superadmin gets the full panel — description, size, expiry, the share's view count, the version's commit message, and a version picker to browse older versions (view-only, via ?v=N; never writes to the database, and never affects what any other visitor of the same link sees). The panel is collapsible (state remembered per-browser) and hidden when printing. "Copy link" (always copies the canonical URL, without ?v=) and "Download source" are available to everyone, including anonymous visitors; "Open in workspace" is shown once the viewer has read access to the artifact.
  • New GET /s/:token/download endpoint — the artifact's raw source, served as text/plain (never as text/html or image/svg+xml, regardless of the artifact's own kind) with Content-Disposition: attachment, so an html/svg artifact's own script can never execute in this app's origin.
  • Design templates for html artifacts: four theme-specific design systems (DESIGN-data.md, DESIGN-document.md, DESIGN-promo.md, DESIGN-diagram.md) plus a shared DESIGN-core.md foundation (tokens, light/dark theming, responsive/print/accessibility rules, self-review checklist), shipped inside skills/open-artifacts/. SKILL.md now routes an agent to the matching template before it writes any markup.

Changed

  • Breaking: a view of /embed/:token by someone who can manage the artifact (its owner, a team owner/admin, or a superadmin) no longer increments the share's view count — browsing your own version history no longer inflates the metric that's supposed to measure your audience. Anonymous and other logged-in visitors are counted exactly as before.
  • /s/:token now sends Cache-Control: private, no-store and Vary: Cookie — its response now depends on who's asking (the viewer panel differs by audience), so it must never be served from a shared cache to the wrong visitor.
  • The public share page now discloses the artifact's title, type, currently-shown version number, and that version's date to anyone with the link — previously the page carried no metadata of any kind, just the embedded content itself.
  • Server-rendered markdown/mermaid/svg artifacts now use a real stylesheet instead of a five-line placeholder: full typography scale, tables, code blocks, blockquotes, light/dark theming via prefers-color-scheme, and print styles. mermaid diagrams now pick their theme (dark/default) from the same signal instead of always rendering light.

Docs

  • @emaxe/oa's README/CHANGELOG didn't document the --team/--public flags added in v0.5.0, and its README still listed the old --expires flag for oa push — both fixed.

v0.5.0 - Team-Only Share Links & Link Policy

Choose a tag to compare

@emaxe emaxe released this 13 Sep 10:27

Added

  • Team-Only Share Links (team mode): A third share mode alongside public and password — a /s/:token link that requires the viewer to be logged in and a member of the artifact's team (any role). No password needed; safe to paste into a team chat. An anonymous visitor is redirected to /login?next=...; a logged-in non-member gets a 403 page with a link to log in as someone else.
  • Team Link Policy: Team owners/admins (including for their personal main workspace) can now set, in /t/:orgId/settings, the default link mode (team or public) used whenever a share is created without naming one, and whether the team allows public links at all. Disabling public links only blocks new ones — existing public shares keep working until revoked, either one at a time from the artifact page or in bulk (with a confirmation dialog showing the affected count) from team settings.
  • Instance-Wide Link Policy: A superadmin sets the same two knobs instance-wide in /admin/settings. A team can only be equal-or-stricter than the instance, never looser: it cannot re-enable public links the instance disabled, nor default to public while public links are forbidden.
  • New endpoints GET /orgs/:id/share-policy (effective policy plus a count of active public shares) and POST /orgs/:id/shares/revoke-public (bulk-revoke, idempotent).
  • New CLI flags --team and --public on oa push and oa share, alongside the existing --password; both oa push/oa share now print the resolved share mode alongside the URL.

Changed

  • Breaking: oa push --share and oa share <id> with no mode flag now create a link in the team's configured default mode instead of always public. Use --public to get the old behavior explicitly.
  • Breaking: MCP create_share's mode argument no longer defaults to "public" — omitting it uses the team's configured default, same as the CLI. The tool result always echoes back the mode actually used.
  • Breaking: POST /artifacts/:id/shares's mode field is now optional (omitting it uses the team default) instead of required; requesting "public" when it's disallowed now returns 403 public_shares_forbidden with allowedModes in the body.
  • Breaking: POST /s/:token/unlock on a non-password share now returns 400 not_password_mode instead of silently {ok:true}.
  • The agent skill (skills/open-artifacts/SKILL.md) no longer instructs the agent to reason about link privacy (defaulting to no share, generating passwords, asking before going public) — that decision now belongs entirely to the server-side team/instance policy above; the skill only documents the three modes and the new error.

Fixed

  • A password-mode share whose passwordHash was NULL (a state that should never occur, but wasn't guarded against) was treated as fully public by the shared access-resolution matrix; it now correctly still requires a password.
  • An unvalidated ?next= parameter on the login page was an open-redirect risk.

Docs

  • @emaxe/oa's own README/CHANGELOG never documented the new --team/--public flags, and its README still listed --expires for oa push after that flag was renamed to --lifetime in v0.4.0 — both fixed.

v0.4.0 - Artifact Lifetime (TTL) & Retention

Choose a tag to compare

@emaxe emaxe released this 11 Sep 19:52

Added

  • Artifact Lifetime (TTL): Artifacts can now be set to expire. A superadmin sets an instance-wide maximum artifact lifetime in /admin/settings (minutes; unlimited by default) — it also serves as the default lifetime for new artifacts. Team owners/admins may set their own stricter maximum for their team, but never looser than the instance one. Callers may choose any lifetime up to the effective limit at creation time (CLI oa push --lifetime, the web UI, or the lifetime field in the REST/MCP API), and change it later via PATCH. On expiry the artifact's content is hard-deleted (all versions removed irreversibly) — only a tombstone row remains for audit/analytics. A background sweeper (ARTIFACT_PURGE_INTERVAL_MINUTES, default 5) performs the actual deletion; reads treat an expired artifact as gone immediately either way.

Changed

  • Lowering the instance or team maximum lifetime re-clamps every affected artifact's expiry from its own creation date (min(current deadline, created_at + new max)) and can make already-existing artifacts expire immediately; raising a maximum never extends anything already created.
  • GET /artifacts and GET /artifacts/:id now omit/404 an artifact once its lifetime has passed, even before the background sweeper has run. Artifact payloads gained an expiresAt field.

Full changelog: CHANGELOG.md · История изменений (RU)

v0.3.0 - Teams, Invites & Personal API Keys

Choose a tag to compare

@emaxe emaxe released this 11 Sep 13:32

Added

  • Personal (User-Scoped) API Keys: oa login now issues a personal key by default — one login, valid across every team you belong to, with your real role in each, instead of one agent key per team. oa orgs lists your teams and the currently selected one; oa use <team> sets a default for the current project (written to a secret-free .oa.json you can commit) or --global for the whole machine. The MCP server gained a matching orgId parameter and a list_orgs tool, and a request with no team selected and more than one candidate gets a structured org_required error listing them instead of guessing. Agent keys (oa login --agent) are unchanged and remain the right choice for CI and narrowly-scoped access. New Settings > Личные API-ключи page to issue and revoke personal keys from the web UI.
  • Auto-Provisioned Main Workspace: Every user now gets a personal "main" workspace on registration — including when registering through an invite link, alongside the inviting team — instead of landing with zero teams.
  • Full Invite Lifecycle: Invites are now a real state machine (pending / accepted / declined / revoked) with list, revoke, and reissue endpoints; a public link preview at /invite/:token; and an in-app "Приглашения" (/invites) inbox so an already-registered invitee can discover and accept or decline an invite without any email delivery.
  • Team Identification for Same-Named Teams: A single OrgIdentity component (id-derived color monogram, "Основное" badge, owner/slug secondary line) used everywhere a team is rendered, plus owner email and member/artifact counts in every team listing — so two teams (or two personal workspaces) sharing a display name stay distinguishable.
  • Searchable Team Switcher: The sidebar's team switcher is now a searchable popover grouped by "Основное" / "Мои команды" / (superadmin) "Все команды инстанса", backed by server-side search.
  • Restructured Admin Area: /admin is now real routed tabs (Overview, Users, Teams, Audit, Instance Settings) instead of one 387-line component; user search by name as well as email with real total-based pagination and team-membership chips; keyset-paginated audit log with resolved actor names/emails and Russian action labels; all five instance settings are editable (previously only registration mode had a UI).
  • Agent Instructions for Everyone: The agent-connection cheatsheet moved from /admin/instructions to /help/agents, available to any signed-in user — connecting an agent is a team task, not a superadmin one. Old links still redirect.
  • Tailwind v4 Design System: The whole web app moved off hand-rolled CSS classes onto Tailwind v4 and a small set of accessible primitives (Button, Dialog, ConfirmDialog, DropdownMenu, Toast, Table, EmptyState, and others), with a light/dark/system theme toggle and a responsive mobile nav drawer — with zero added runtime dependencies.
  • Team Management: Renaming a team, adding an existing user directly to a team (bypassing the invite round-trip), and a superadmin-only storage quota editor.

Changed

  • GET /auth/me No Longer Lists Every Org for Superadmins: It now returns only the caller's own memberships, plus mainOrgId and a pending-invite count — a superadmin reaches teams they don't belong to via search instead. (Breaking API change for any external client relying on the old shape.)
  • Native confirm() / alert() / prompt() dialogs — the last ones left in the app — replaced with proper modal components.

Fixed

  • Invite Email Spoofing: Registering through an invite link no longer trusts a caller-supplied email that doesn't match the invite — closing a hole where anyone holding a link could join under an arbitrary address.
  • A user could previously be removed as the last remaining owner of the org they created via DELETE /orgs/:id/members/:userId (only PATCH had the guard); both now refuse it, and a team's creator can never leave or be removed from their own main workspace.
  • Login and registration no longer redirect to a nonexistent /artifacts route.

v0.2.0 - MCP Server, Agent Skill & Multi-tenancy

Choose a tag to compare

@emaxe emaxe released this 11 Sep 08:47

Open Artifacts v0.2.0

See CHANGELOG.md for full release details.

  • MCP Server: Native Model Context Protocol support at /mcp
  • AI Agent Skill: Ready-to-use skill via npx skills add emaxe/open-artifacts
  • CLI @emaxe/oa v0.2.0: OAuth Device Flow login, password/expiring shares, auto-format detection
  • Multi-tenancy & Teams: Full organization management and role permissions
  • Bilingual Documentation: English and Russian READMEs & Changelogs