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

@octonodes/ui-extensions

v0.2.38

Published

Portable contracts and React primitives for Octonode plugin UI extensions.

Readme

Octonode UI extensions

Use @octonodes/ui-extensions/react to arrange a plugin node's existing inputs without taking ownership of values, expressions, connections, or saving.

npm install @octonodes/ui-extensions
import { defineExtension, InputField, NodeForm, Section } from "@octonodes/ui-extensions/react";

function Inputs() {
  return (
    <NodeForm>
      <Section title="Message">
        <InputField name="channel" />
        <InputField name="text" appearance="multiline" />
      </Section>
    </NodeForm>
  );
}

export default defineExtension("node.inspector.inputs", Inputs);

Declare the entry on the node passed to defineNode:

ui: {
  apiVersion: "1",
  renderers: {
    focused: {
      label: "Focused form",
      targets: { "node.inspector.inputs": "src/slack-inputs.tsx" },
    },
  },
}

octonodes plugin build bundles the entry into the immutable plugin artifact. Octonode loads it only for the exact installed plugin version and renders real host-owned form controls inside an isolated, network-disabled sandbox.

The first public target is node.inspector.inputs. Undeclared or omitted inputs fall back to the standard inspector form.

Native app contributions (v3)

Installed apps can add navbar/navigation/search launchers, panels, Octonode tabs, workflow node bodies and context-menu actions, task badges/details, document and editor operations, Git/table/execution views, and settings sections. A hosted app page or backend is optional. These changes require the SDK and CLI built from the accompanying branch; they are not published yet.

Views use defineExtension(target, Component) and useExtension(target) from @octonodes/ui-extensions/react. Actions use defineAction(target, handler) from @octonodes/ui-extensions. The CLI bundles and hashes each immutable browser entry. The generated runtime checks the definition's target and action/view kind at startup, then calls startExtension or startAction.

import { defineExtension, useExtension, Section, Button } from "@octonodes/ui-extensions/react";
export default defineExtension("shell.panel", function Assistant() {
  const api = useExtension("shell.panel");
  return (
    <Section title="Claude Code">
      <Button onPress={() => void api.ui.openAssistant("Help with this resource.")}>Open assistant</Button>
    </Section>
  );
});
import { defineAction } from "@octonodes/ui-extensions";
export default defineAction("node.action", async (api) => {
  if (!api.signal.aborted) await api.ui.openAssistant(`Explain node ${api.context.nodeId}`);
});

The target context contains resource IDs, revisions, selection ranges, and bounded metadata batches, never automatic document/file/task bodies. ui.openPanel opens an own declared view; ui.openAssistant hands off to the existing native assistant and its authorized tools. tabs.list/update/undo operate on registered personal layout groups with revision checks. edits.propose opens native review for a captured file/document draft; native Save owns persistence. Mutating host commands require recent native user interaction. No raw filesystem/process or private Studio API access is granted.

Node views and task badges return display-only sections named for captured IDs, using one sandbox per canvas/task view and at most 100 resources. Other views can use host-owned controls. An action has a captured invocation ID and abort signal; it completes automatically and has a sixty-second deadline. Legacy node input extensions still use protocol v1; native app contributions use protocol v2.

The full target catalog, runnable example, permissions, limits, and actual native screenshots are in the engine's native extensions guide (docs/app-extensions/README.md in the engine checkout). When using a local engine checkout, open docs/app-extensions/README.md for the branch version. The older examples below describe the separate v1/v2 contracts.

Extension-only apps

Apps can contribute only a workspace block; a page and backend are optional. Use the same defineExtension / startExtension SDK with workspace.block or app.page and bundle as a browser IIFE. Select Extension only in Partner and upload the JavaScript. Octonode stores immutable bytes and checks the digest at publication and launch. The static sandbox has no direct network access; the scoped host bridge below provides approved project actions.

Declare shared string settings in the v2 manifest and read them through getAppSession().configuration. Customers configure or disable the app under its installation settings. Blocks appear in workspace home; an app.page or self-hosted web app can have an Open app link. See examples/workspace-notice-app and docs/plugins.md in the engine repository.

Self-hosted apps

This is also the SDK selected for Octonode's developer-authored app extensions. The engine owns its source in packages/ui-extensions; scripts/sync/plugin/sdk.mjs synchronizes the public package into octonode-devtools. Do not create a second app UI SDK.

Legacy self-hosted app targets are app.page and workspace.block. Register an app in Partner → Apps, publish its HTTPS application URL and extension bundle digests, then install it from Studio → App marketplace. Plugin marketplace remains dedicated to executable node plugins.

App controls use the same host design system: NodeForm, Section, Button, and TextField. InputField is only for node inspectors. Bundle your entry as a browser IIFE, including React and this SDK, and call startExtension:

import { useState } from "react";
import { getAppSession } from "@octonodes/ui-extensions";
import { defineExtension, startExtension, NodeForm, Section, Button, TextField } from "@octonodes/ui-extensions/react";

function App() {
  const [note, setNote] = useState("");
  const [status, setStatus] = useState("");
  async function save() {
    try {
      const session = getAppSession();
      const response = await fetch("https://your-app.example/notes", {
        method: "POST",
        headers: { authorization: `Bearer ${session.token}`, "content-type": "application/json" },
        body: JSON.stringify({ note }),
      });
      if (!response.ok) throw new Error("Save failed");
      setStatus("Saved");
    } catch {
      setStatus("Could not save. Reopen the app and try again.");
    }
  }
  return (
    <NodeForm>
      <Section title="Project notes">
        <TextField label="Note" value={note} onChange={setNote} />
        <Button onPress={() => void save()}>Save note</Button>
        {status}
      </Section>
    </NodeForm>
  );
}
startExtension(defineExtension("app.page", App));

Host the compiled JavaScript with CORS enabled for the Studio origin. Hash the exact response bytes with SHA-256 and publish the prefixed digest (sha256:…). Redirects and bundles larger than 2 MiB are rejected. The hidden iframe has an opaque origin: your backend must accept CORS Origin: null for authenticated SDK requests; never use Origin as authorization. Its CSP allows connections only to the registered application origin. Octonode credentials and parent DOM access are unavailable.

The five-minute octo_app_ bearer is specific to the app installation, approved version, and opening user. Your backend must verify it on every request with GET https://octonodes.com/api/apps/runtime/session, then check that the returned appId equals your registered app ID. Use the verified workspace/user identity rather than values supplied by the browser. Never log the bearer or cache verification. Uninstall, consent changes, session expiry, and removed membership revoke subsequent access.

Hosted pages and routing

Use connectHostedApp() once when the page mounts, subscribe to its snapshot, and call dispose() on unmount. Studio displays the hosted page inline and delivers short-lived sessions through an origin-checked, window-bound handshake. Tokens stay in memory. app.fetch("/api/projects") sends the bearer only to your own backend; that backend must still verify it with connectAppServer on every request.

For React apps, wrap the workspace UI in OctonodeAppProvider from @octonodes/ui-extensions/app/react. It verifies /api/context before rendering children. Pass every page route in navigation, including the overview (/), to display them in Studio's app sidebar. Studio does not discover routes from your hosting provider or browser router. An app can render its own top navbar inside the provider; this does not replace sidebar registration. The generated React app keeps its navbar off by default; render <App showNavbar /> to enable it. The generated server verifies this route with connectAppServer. A direct visit without a session shows a translated access landing screen and no workspace UI. The provider also shows connecting, expired-session, and verification-error states. Customizing this UI does not replace backend session and installation checks.

In the Octonode source checkout, preview the screen with yarn workspace @octonode/ui-extensions dev on port 5185, then open /dev/ in your browser. Use ?state=expired, ?state=connecting, or ?state=error to preview the other states. With that preview running, run node --test packages/ui-extensions/tests/app/access.test.mjs to check mobile reflow, private-content protection, and recovery after a refreshed token.

import { connectHostedApp } from "@octonodes/ui-extensions/app";
const app = connectHostedApp();
const unsubscribe = app.subscribe(() => render(app.getSnapshot()));
app.navigate("/products?sort=name");
// On unmount: unsubscribe(); app.dispose();

Normal history.pushState, replaceState, and back/forward navigation work with browser routers. Embedded paths appear in Studio’s appPath query parameter; standalone pages use their own URL. Serve your app shell for nested page routes. Do not use Studio’s /studio/apps/... path as your app router basename.

Development starts with zero data grants. Explicit administrator consent selects actions and projects for one hour; heartbeats extend only the ten-minute idle session, not the consent. Source edits on the same host retain consent; host or action changes clear it. See the development guide.

The hosted page's HTML and static assets must be loadable before the iframe session handoff. Keep private workspace content behind the provider and private backend routes behind bearer verification. App backends can use POST /api/apps/runtime/data for consented project/table/row operations, and POST /api/apps/runtime/executions on the cloud gateway for workflow execution. Neither credential works as a general Octonode login. See the engine’s docs/plugins.md for request examples.

For legacy self-hosted v1 apps, developers host app backends and versioned extension assets themselves. Octonode-managed app hosting is reserved for a future paid service. App release metadata pins extension URLs and SHA-256 digests; publishing metadata does not deploy a backend.

Project and workflow access

Installed app.page and workspace.block extensions can use the host bridge without a developer backend. It verifies the live installation, actor, and project grant on every action. A project-specific host can supply its current project; workspace-wide surfaces select a project automatically only when exactly one project is granted.

import { connectApp } from "@octonodes/ui-extensions/app";

const app = await connectApp();
const project = app.project ?? app.forProject(chosenProjectId);
const products = await project.tables.rows.list("products");
const run = await project.wf.run("sync-products", { source: "app" });

app.projects lists the granted project IDs for a project picker. app.project is undefined when no project is granted or several are granted without a current project. forProject(id) rejects ungranted IDs locally; Octonode still checks current grants on every request. wf.run queues a workflow and returns its execution summary. Pass an idempotencyKey option when retrying the same action.

A developer-hosted backend can use the same project API after verifying the browser's app bearer. Configure the API URL and your own registered app ID on the server; never accept either from an incoming request.

The server helper supports Node 20.19+, 22.12+, or newer (^20.19.0 || >=22.12.0). Your backend code and dependencies must also support the selected runtime.

import { connectAppServer } from "@octonodes/ui-extensions/app/server";

const app = await connectAppServer(bearer, {
  appId: process.env.OCTONODE_APP_ID!,
  baseUrl: process.env.OCTONODE_API_URL!,
});
const project = app.project ?? app.forProject(chosenProjectId);
const products = await project.tables.rows.list("products");

The backend helper verifies the bearer on each connectAppServer call and uses that bearer only for the app runtime endpoints. Sessions expire after five minutes; background work requires a separately approved installation service identity.

End-to-end app guides

The public Playbook is the source for the complete authoring path: quickstart, project configuration, SDK imports and permissions, hosting, and publishing and updates. The CLI scaffold supplies a runnable extension-only or full app example.