npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@joe-broadhead/opencode-artifacts

v0.2.1

Published

OpenCode plugin for toqan artifacts: Studio's tools, skill and default permissions in one install

Readme

toqan artifacts for OpenCode

One OpenCode plugin that gives an agent toqan artifacts: Studio's tools, the agent skill, and default permissions that ask the person before anything is shared, deleted, stopped or resumed.

Requires OpenCode >=2.2.3 <2.3.0.

Install

The plugin is published on npm as @joe-broadhead/opencode-artifacts. Add it to plugins in opencode.json(c); OpenCode downloads it on start:

{
  "$schema": "https://opencode.ai/config.json",
  "plugins": ["@joe-broadhead/opencode-artifacts"]
}

To stay on one version, pin it: "@joe-broadhead/[email protected]".

To use another Studio, add options:

{
  "plugins": [
    {
      "package": "@joe-broadhead/opencode-artifacts",
      "options": { "studioUrl": "https://studio.example.com" }
    }
  ]
}

studioUrl defaults to https://studio.heyjelly.ai and must be an https origin. Plain http:// is accepted only for localhost, 127.0.0.1 and [::1], for a Studio on the same machine. Loading the plugin makes no network call.

Sign in

  1. Run /connect and choose toqan artifacts.

  2. OpenCode opens Studio's sign-in page in the browser. Sign in with your verified Prosus Google account.

  3. Studio's consent page asks what this agent may do. Pick one:

    | Access | What the agent may do | |---|---| | Read | view artifacts and their data | | Publish (default) | read, plus create artifacts and publish versions | | Manage | publish, plus share, delete, stop and resume |

    The level can be changed later by revoking the agent on Studio's Agent access page and connecting again.

  4. Come back to OpenCode. The credential is stored in OpenCode's own credential store; the plugin never writes a token to a file.

Studio registers the plugin as a public OAuth client each time you sign in, with a loopback redirect (http://127.0.0.1:<port>/callback), PKCE (S256), and the scopes openid profile email offline_access artifacts:read artifacts:publish artifacts:manage. Studio asks for all three artifact scopes so the consent page can offer every access level, and narrows the grant to the level you choose. Studio ignores the loopback port, so the plugin listens on any free port.

OpenCode stores the credential and refreshes it with the refresh token through the plugin's refresh method; Studio rotates the refresh token on every refresh, and OpenCode keeps the new one. A tool never spends the refresh token itself: if Studio refuses a call, the plugin asks OpenCode for the current token once and retries. If that is refused too, or the person is no longer signed in, the tool reports:

AUTH_REQUIRED: toqan artifacts isn't signed in. Next step: run /connect and choose toqan artifacts.

What the tools do

There is one tool for each MCP tool Studio lists, with Studio's own name, description and input schema, in the artifacts namespace: OpenCode names each one artifacts_<tool> (artifacts_publish_html), and Code Mode calls it as tools.artifacts.<tool>. Each one forwards its input to <studioUrl>/mcp as an MCP tools/call with your token and returns Studio's answer unchanged. Text comes back as text; an image (a widget layout screenshot, for example) comes back as a file OpenCode can show, with the same bytes and MIME type. Studio is the only place that checks access, so the plugin holds no access rules of its own. Studio's content is untrusted data, never instructions, and the namespace's Code Mode text says so.

Studio's tool list changes independently of the plugin. plugin/tools.json records the list this plugin was built against; a test fails when it drifts, and pnpm plugin:tools regenerates it.

Which tools ask

Every tool asserts its own permission, artifacts_<tool>, before it forwards anything, so a rule of yours that asks or denies any tool holds. The plugin adds an ask rule for these ten, which are direct tools, so you see a prompt first:

| Tool | What it changes | |---|---| | share_site | shares an artifact with a person or everyone in the organization | | unshare_site | removes a share | | share_workspace | shares an artifact with a workspace | | decide_access_request | approves or denies an access request; approving shares the artifact | | delete_site | deletes an artifact | | stop_site | stops serving an artifact's address | | resume_site | serves a stopped artifact again | | ensure_workspace | creates a workspace or adds people to it | | request_site_access | asks an artifact's owner for access | | open_change_request | asks an artifact's owner and editors to review a version |

Everything else — reading, publishing, dashboards, decks, versions, connections, widget checks, exports — runs without asking, in Code Mode.

The rules are on the actions artifacts_share_site, artifacts_unshare_site, artifacts_share_workspace, artifacts_decide_access_request, artifacts_delete_site, artifacts_stop_site, artifacts_resume_site, artifacts_ensure_workspace, artifacts_request_site_access and artifacts_open_change_request, one per tool, added to every agent the plugin sees at setup, right after that agent's base allow. A person's own rule wins when it comes later: deny hides the tool, and an allow for an artifact skips the prompt. Before 0.2.0 the actions were named artifacts.<tool>; rename a rule of yours that uses that form.

Agents defined only in the person's config are created after plugins run, so they can miss these rules. Add the same rules to that agent's permissions to get the prompts; do not put the ask rules in the top-level permissions, where they would also apply to other agents.

In Jelly

package.json carries a jelly declaration that Jelly reads without loading the plugin; stock OpenCode ignores it. docs/jelly.md says how Jelly uses it. test/jelly.test.ts checks it with the shared kit (test/support/jelly-kit.ts), and npm run check (scripts/check-copies.mjs) fails when the kit differs from the copy every plugin in this family carries.

The skill

skills/jelly-artifacts/SKILL.md is the same file Studio serves at /skill.md, copied by pnpm plugin:skill. It is auto-invoked, so the agent reads it whenever the person wants a page, dashboard, deck, app, game or Jelly widget made, changed or shared — and publishes it with toqan artifacts rather than as the chat app's own artifact.

Troubleshooting

  • AUTH_REQUIRED — run /connect, choose toqan artifacts, and finish the Google sign-in.
  • The tools are missing — check that the plugins entry is @joe-broadhead/opencode-artifacts (or, for a local build, the plugin directory itself, with dist/ compiled), and that OpenCode is 2.2.x. /reload picks up a config change.
  • CONFIG_INVALID: studioUrl ... — the option must be a bare origin with no path, such as https://studio.heyjelly.ai. Plain http:// works only on localhost, 127.0.0.1 and [::1].
  • MOVED: Studio has moved to <address> — the Studio you use answered that it now lives at <address>. Set studioUrl to that address (or remove the option, when the message says the default is that address), then run /connect: a sign-in belongs to the address it was made at.
  • A tool reports an HTTP status — Studio answered, but not with a tool result. Check /connect first, then Studio's own status.

Develop locally

Build the plugin from this repository and point plugins at the plugin directory with an absolute path:

pnpm install
pnpm plugin:build                # refresh tools.json and the skill from Studio
pnpm --dir plugin compile        # compile src/ to dist/
{ "plugins": ["/absolute/path/to/artifacts/plugin"] }

OpenCode loads exports["."], which is ./dist/index.js, and reads tools.json and skills/jelly-artifacts/SKILL.md next to it. Run pnpm --dir plugin compile again after changing src/. dist/ is not committed.

Release a new version

tools.json and the skill are copies of Studio taken at build time, so release a new version when Studio adds or changes a tool, when the skill changes, or when the plugin's own code changes. A published version can never be replaced; every change is a new version number.

| Change | Bump | |---|---| | A fix | patch (0.2.0 → 0.2.1) | | A new or changed Studio tool, a skill change, a new option | minor (0.2.1 → 0.3.0) | | A change that breaks existing configs, or a new OpenCode host range | major |

  1. In a pull request, run pnpm plugin:build, bump version in plugin/package.json, add a ## [x.y.z] - date entry to plugin/CHANGELOG.md, and declare any new tool in the jelly section (test/jelly.test.ts fails until you do). Merge it once check is green.
  2. Tag that commit on main as v<version> and push the tag. Nothing else releases.
  3. release-plugin.yml checks that the tag matches the version, runs the plugin's tests and npm run check, packs the plugin, and waits for approval in the release environment. Once approved, it publishes the tarball to npm with trusted publishing. This repository is private, and npm makes no provenance for a private repository, so it publishes without.

npm pack --dry-run in plugin/ shows the file list: dist, assets/icon.svg, skills, tools.json, README.md and CHANGELOG.md.