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

@gea-ai/extensions-sdk

v0.1.260715-alpha.0

Published

Public frontend SDK for GEA extensions.

Readme

@gea-ai/extensions-sdk

Public frontend SDK for GEA extensions.

Install

npm install @gea-ai/extensions-sdk

When testing unpublished SDK changes from this repository, build the SDK and link the workspace package from the standalone extension repo:

pnpm --filter @gea-ai/extensions-sdk build
npm install @gea-ai/extensions-sdk@file:/path/to/gea/packages/extensions-sdk

Minimal Shape

import {
  document,
  defineExtension,
  field,
  table,
  wiki,
} from "@gea-ai/extensions-sdk";

export const extensionDeclaration = defineExtension({
  frontend: {
    entry: "src/index.tsx",
  },
  name: "Customer Research",
  resources: {
    workspace: wiki({
      children: {
        notes: table({
          fields: {
            note: field.text({
              indexed: true,
              required: true,
              title: "Note",
            }),
          },
          title: "Notes",
        }),
        summary: document({
          initialContent: {
            format: "markdown",
            markdown: "# Customer Research Summary\n",
          },
          title: "Summary",
        }),
      },
      title: "Customer Research",
      visibility: "workspace",
    }),
  },
  slug: "customer-research",
});
// src/extension-runtime.ts
import { createExtensionRuntime } from "@gea-ai/extensions-sdk/react";
import extensionDeclaration from "../gea.extension";

export const { mount, useExtensionApp, useLocale, useTranslations } =
  createExtensionRuntime({
    declaration: extensionDeclaration,
  });
// src/index.tsx
import { useExtensionApp } from "./extension-runtime";

export default function CustomerResearch() {
  const app = useExtensionApp();

  async function submitNote(note: string) {
    await app.resources.notes.records.create({
      note,
    });
  }

  return <button onClick={() => void submitNote("Follow up")}>Add</button>;
}

Frontend code calls host APIs through SDK helpers. The SDK uses the GEA host postMessage bridge by default, so extension authors do not need to write message plumbing or know the host ORPC transport.

Actions And Backend Packages

Curated backend-capable extension packages can share one typed action contract between frontend and backend code. The frontend calls the host transport, while the backend exports handlers that the host loads through ORPC after install, membership, and runtime resource checks.

// src/contract.ts
import { defineActions } from "@gea-ai/extensions-sdk/actions";
import z from "zod";

export const actions = defineActions({
  ping: {
    inputSchema: z.object({ value: z.string() }),
    outputSchema: z.object({ echoed: z.string() }),
  },
});
// src/backend/index.ts
import { defineBackendActions } from "@gea-ai/extensions-sdk/backend";
import { actions } from "../contract";

export const backend = defineBackendActions(actions, {
  ping: async ({ input }) => ({ echoed: input.value }),
});
// src/frontend/index.tsx
import {
  createExtensionActionClient,
  type ExtensionActionTransport,
} from "@gea-ai/extensions-sdk/actions";
import { actions } from "../contract";

declare const callExtensionAction: ExtensionActionTransport;

const client = createExtensionActionClient({
  actions,
  extensionSlug: "example-extension",
  transport: callExtensionAction,
});

await client.call("ping", { value: "hello" });

Private organization extension packages and curated marketplace packages run backend actions through the trusted in-process executor. This is the current demo/development trust model; package authors and organization operators are responsible for the uploaded code. Remote backend files are checked against manifest sha256 and sizeBytes metadata before import, and the host dispatches only declaration-listed backend actions. The public backend SDK surface is intentionally narrow:

  • sdk.state.records.* for extension-private workflow state.
  • sdk.connectors.execute(...) for host-managed connector calls.
  • sdk.artifacts.path(...), writeText(...), and writeJson(...) for extension artifacts.
  • sdk.resources.records.create/update(...) and sdk.resources.records.findByIndexedText(...) for idempotent lookup on a declared indexed text field, plus sdk.resources.documents.update(...) for declared GEA resources.
  • sdk.agents.call(slug, input) and generated agent handles for declared package-internal agents.
  • sdk.chats.messages.list(...) for reading the scoped agent chat produced by an extension run.

Any broader host helpers are reserved for internal built-in workflows and are not a third-party SDK contract.

Nested-agent calls may include runtimeReminders: string[]. The host passes these reminders unchanged into the agent runtime, where they are assembled as system reminders outside the structured user input. Extension backends should derive reminder content from server-resolved action context such as context.extension.appResourceId and context.extension.resources; frontend input must not be trusted to identify extension-owned resources.

resources.records.findByIndexedText(...) accepts only a field declared by the current extension table as type: "text" and indexed: true. The lookup stays inside the current organization, workspace, declared table, and optional scope resource. It returns null for no match and rejects duplicate matches instead of selecting one silently.

state.records.update({ data }) merges the provided object into the existing record data. Use this for partial progress updates; model full replacement as a future explicit API if an extension needs it.

Backend actions execute external systems through host-managed connectors:

const result = await sdk.connectors.execute({
  connector: "bocha-search",
  params: {
    method: "GET",
    operation: "request",
    path: "/search",
    query: { q: "consumer trend" },
  },
});

Connector credentials are resolved by normal GEA connector credential scope rules. Extensions should declare every connector dependency in connectorRequirements so install and setup flows can surface missing connectors or credentials before backend actions run. Provider-specific request details should live in API, MCP, or native connector implementations; backend actions should call those connectors instead of embedding provider credentials or direct network integration code.

A builtin_connector requirement activates the host-owned connector from the published extension package. It does not add a connector export or create a standalone connector app resource. The host still requires the connector to be enabled in the deployment environment and resolves any system or user credentials at execution time.

export const extensionDeclaration = defineExtension({
  // ...
  connectorRequirements: [
    {
      slug: "bocha-search",
      source: { type: "app_resource", slug: "bocha-search" },
    },
    {
      slug: "wechat-official-account",
      source: {
        type: "builtin_connector",
        slug: "wechat-official-account",
      },
    },
  ],
});

Installed extension summaries expose each declared connector as connected, missing_connector, or missing_credentials. Backend action dispatch preflights the same setup state before creating the connector runtime and fails with connector_setup_required when setup is incomplete.

Host Context, Theme, And Locale

The local-dev host sends a host-context message to the extension iframe on load and when the host document theme or locale attributes change. React extensions should normally use createExtensionRuntime from @gea-ai/extensions-sdk/react; it wires host-context sync into ExtensionRuntimeProvider and exposes:

  • useExtensionApp() for the typed host SDK client and resolved resource handles.
  • useLocale() for the current resolved locale.
  • useTranslations(namespace) for extension-owned local dictionaries.
  • mount(Component) for rendering the default no-props React component into #root.

The context includes locale, text direction, resolved color scheme, resolved theme, and CSS custom properties such as --background, --foreground, and --border. The CLI scaffold wires this into src/extension-runtime.ts, includes a small local i18n dictionary, and configures Tailwind CSS so utilities like bg-background, text-foreground, and border-border follow the host theme. Extension messages remain extension-owned; the host only supplies the active locale.

Upload And Publish

Use gea ext package for local package inspection, then gea ext deploy to upload the package to the hosted server as an extension app-resource draft. Deployment does not publish. Review and publish that app resource from the GEA web UI, using the same lifecycle as other app resources.

The generated artifact is a canonical gea.resource_package.v1 directory. Its root manifest.json exports the extension, while private agents and skills stay under agents/<slug> and skills/<slug> as non-exported components. The extension frontend and backend stay under extensions/<slug>. Deployment uploads this complete generated directory; source repositories, tests, and node_modules are not part of the runtime package. The CLI bundles backend runtime dependencies into the generated backend entrypoint, so hosted execution does not depend on packages installed in the GEA server image.

The app-resource publish version is the canonical hosted version. gea.extension.ts version metadata is stored in the normalized manifest as package metadata. Published extension frontend assets are served by the host through a sandboxed iframe asset route, and SDK calls still go through the same postMessage bridge.

Resource Handles

Extensions declare durable resources in defineExtension. Draft uploads store the normalized declaration only. Publishing and local-dev registration provision real GEA resources and bind them to the extension scope. The iframe URL carries only extension identity. The host sends lifecycle.stage = "ready" plus resolved resource handles through the host-context postMessage, and the React runtime exposes them as typed app.resources when the frontend uses useExtensionApp() from a runtime created with the declaration.

Use those handles for extension-owned durable data:

await app.resources.notes.records.create({
  note: "Follow up with the design team",
});

The full demo lives at examples/research-queue in the GEA repo. It declares a workspace-visible extension wiki, stores submitted URLs in a declared context table inside that wiki, declares one aggregate summary document next to the table, and starts a hosted agent task with only the resolved table and document locations in the prompt.

Third-party iframe extensions should not persist private UI state through production extension_record. Use browser-local or dev-runtime-local state for UI-only state. Durable user-visible data should be modeled as declared GEA resources so users and admins can inspect, recover, export, permission, and delete it through normal resource flows.

Current table APIs are not yet optimized for large database workloads. Treat 10k to 100k row scenarios, server-side filtering/sorting, relation joins, and benchmarks as a separate table performance track.

The root SDK avoids private monorepo imports so extension projects can type-check and build outside the GEA monorepo. Public context resource result types are exported from @gea-ai/extensions-sdk/context. The portable schemas and TypeScript types are owned by @gea-ai/contract and re-exported by the SDK.

Local-dev extensions are trusted developer code. They do not declare manifest permissions yet. The host bridge exposes only curated SDK methods, but it does not enforce extension-specific grants. SDK calls run as the current signed-in user and remain constrained by normal hosted ORPC user, workspace, and resource permissions.