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

@palettelab/cli

v0.3.37

Published

Developer CLI for building Palette platform plugins — no platform source access required.

Readme

@palettelab/cli

Developer CLI for building plugins for the Palette platform. Works without any access to the platform source — your plugin repo is the only thing you own.

The installed executable is pltt.

Requirements

  • Node.js 18+
  • Python 3.12+ for local backend simulation
  • No Docker is required for normal app development, testing, or publishing.
  • Docker Desktop is only for internal pltt dev --platform parity checks.

Install

You don't have to install globally — use npx:

npx @palettelab/cli <command>
# or after global install
pltt <command>

Quick Start: Build And Test A Palette App

Use this flow when a developer wants to create an app, run it locally, then test it inside the real Palette OS without Docker.

npx --yes @palettelab/cli@latest init simple-todo --template database
cd simple-todo
npm install
npx --yes @palettelab/cli@latest dev

pltt dev runs the local SDK simulator. It is the fastest loop for frontend, backend, manifest, SDK hooks, and local database checks. It does not require Docker and does not publish anything to the platform.

When the app is ready to test with real Palette OS services, configure a hosted sandbox environment and run:

npx --yes @palettelab/cli@latest login \
  --env staging \
  --url https://YOUR-PALETTE-STAGING-URL \
  --token pltt_xxxxx

npx --yes @palettelab/cli@latest dev --sandbox --env staging

The hosted sandbox flow packages the app, uploads it to the configured Palette environment, creates a preview publish, and prints a real OS preview URL. Use that URL to test routing, OS shell behavior, login context, organization context, Data Room APIs, storage, install/review behavior, logs, permissions, and platform APIs.

Staging URL

The staging URL is the base URL of the Palette platform backend/API environment. The CLI appends the API paths itself, for example:

  • /api/v1/appstore/sign-upload
  • /api/v1/appstore/publish
  • /api/v1/developer/publish-tokens
  • /api/superadmin/publish-tokens

Use the same public origin only if that origin proxies API routes to the backend. For example, https://apps.pltt.xyz is valid only when these paths are served by the backend, not by the frontend catch-all route:

/api/v1/*
/api/superadmin/*

Validate a staging URL before giving it to developers:

curl https://YOUR-PALETTE-STAGING-URL/api/v1/health

A valid staging URL returns a backend health JSON response. If it returns the Palette frontend HTML, it is a frontend-only URL and the CLI cannot publish to it. In that case either use the real backend domain, for example https://api.your-domain.example, or update the web server/reverse proxy so /api/v1/* and /api/superadmin/* go to the backend.

Publish Token

Every developer needs a publish token before they can use hosted sandbox or publish commands. Tokens start with pltt_.

Developers can create their own token after logging in to Palette:

  1. Open Palette OS in the browser.
  2. Open Settings.
  3. Go to Developer.
  4. Create a developer publish token.
  5. Copy the token immediately. The raw token is shown only once.

Superadmins can also allocate tokens from the superadmin publish-token section. Use superadmin allocation for service accounts, CI, or developers who should not create their own token.

Do not commit tokens to a repository. Store them through pltt login or an environment variable.

Configure A Hosted Sandbox

The recommended setup is pltt login, which writes ~/.palette/config.json with file mode 0600:

npx --yes @palettelab/cli@latest login \
  --env staging \
  --url https://YOUR-PALETTE-STAGING-URL \
  --token pltt_xxxxx

You can verify the saved environment with:

cat ~/.palette/config.json

You can also use environment variables instead of storing the token:

export PALETTE_STAGING_URL=https://YOUR-PALETTE-STAGING-URL
export PALETTE_STAGING_TOKEN=pltt_xxxxx
npx --yes @palettelab/cli@latest dev --sandbox --env staging

Environment variable precedence:

  1. PALETTE_<ENV>_URL and PALETTE_<ENV>_TOKEN
  2. PALETTE_PUBLISH_TOKEN as a token fallback
  3. ~/.palette/config.json
  4. ./palette.config.json for repo-local overrides

Test Inside The Real Palette OS Without Docker

Use hosted sandbox for real OS testing:

npx --yes @palettelab/cli@latest dev --sandbox --env staging

This is the correct flow for internal teams that need real platform features without pushing the app to production approval and without running Docker on the developer machine.

Expected behavior:

  1. The CLI runs local contract checks.
  2. The CLI bundles frontend and backend artifacts.
  3. The CLI uploads the package to the staging Palette environment.
  4. The platform creates a preview/review publish.
  5. The CLI prints the preview URL, status command, and logs command.
  6. The developer opens the preview URL inside the real Palette OS.

For developer sandboxes where manual review should not block testing, the backend environment should run with:

APPSTORE_AUTO_APPROVE_SANDBOX_PREVIEWS=true

With that backend setting enabled, passing sandbox preview publishes are marked active automatically, so developers can test OS shell, auth, Data Room, organization, install, permission, log, and platform API behavior immediately.

Useful follow-up commands:

npx --yes @palettelab/cli@latest status --env staging
npx --yes @palettelab/cli@latest logs simple-todo --env staging --follow

Local-Only Versus OS Testing

Use the right command for the kind of test you need:

| Goal | Command | Docker | Uses real OS services | |---|---|---:|---:| | Fast SDK/frontend/backend loop | pltt dev | No | No | | Real OS preview in hosted cloud sandbox | pltt dev --sandbox --env staging | No | Yes | | Alias for hosted cloud sandbox | pltt dev --cloud --env staging | No | Yes | | Internal full local platform parity | pltt dev --platform | Yes | Local platform container | | Production/review publish | pltt publish --env production | No | Yes |

For normal app developers, Docker is not required. Docker is only needed for internal platform parity checks with pltt dev --platform.

App-Owned Data, Migrations, And Python Backends

For a detailed Python backend SDK guide with route examples, app-owned data, migrations, Data Rooms, config, secrets, lifecycle hooks, and hosted sandbox testing, see Python Backend SDK Developer Guide.

Palette apps can own their own Python backend, database tables, migrations, and organization-scoped data. The generated database template uses this structure:

my-app/
  palette-plugin.json
  frontend/src/index.tsx
  backend/api/main.py
  backend/api/models.py
  backend/migrations/env.py
  backend/migrations/versions/001_init.py

Declare database ownership in palette-plugin.json:

{
  "capabilities": { "database": true },
  "database": {
    "schema": "app_my_app",
    "migrations": "./backend/migrations"
  }
}

Define org-scoped models with the backend SDK. Table names must start with the app prefix (my_app__ for my-app):

from sqlalchemy import String
from sqlalchemy.orm import Mapped, mapped_column
from palette_sdk import OrgScopedTable

class Invoice(OrgScopedTable):
    __tablename__ = "my_app__invoices"

    id: Mapped[int] = mapped_column(primary_key=True)
    customer_name: Mapped[str] = mapped_column(String(255))

Use the migration helper in Alembic migrations:

from palette_sdk.db import ensure_org_rls

def upgrade():
    op.create_table(...)
    ensure_org_rls(op, "my_app__invoices")

Use ctx.db for the full database feature set. It is the real async SQLAlchemy session, scoped by Palette to the app schema and current organization, so app code can use Core/ORM queries, joins, aggregates, transactions, bulk operations, and raw text() SQL. Use migrations for schema changes such as tables, indexes, constraints, and types.

Use ctx.repo(Model) when simple org-safe CRUD is enough:

from fastapi import Depends
from palette_sdk import PluginContext, get_plugin_context
from models import Invoice

@router.get("/invoices")
async def list_invoices(ctx: PluginContext = Depends(get_plugin_context)):
    rows = await ctx.repo(Invoice).list(order_by="-id")
    return [{"id": r.id, "customer": r.customer_name} for r in rows]

@router.post("/invoices")
async def create_invoice(body: InvoiceIn, ctx: PluginContext = Depends(get_plugin_context)):
    invoice = await ctx.repo(Invoice).create(**body.model_dump())
    return {"id": invoice.id}

Backend SDK features for app-owned data:

  • PluginRouter, PluginContext, and get_plugin_context provide the FastAPI route and request-context surface.
  • PluginContext exposes user_id, organization_id, org_role, plugin_id, permissions, storage, ctx.db, ctx.data_rooms, ctx.members, ctx.redis, ctx.vector, ctx.config, and ctx.logger.
  • ctx.db is the full scoped SQLAlchemy AsyncSession for app-owned database data.
  • ctx.repo(Model) gives org-safe CRUD helpers for app tables.
  • ctx.data_rooms gives backend access to Palette Data Rooms without importing platform internals.
  • ctx.members gives backend access to current organisation members; it exposes list/get/invite/update-role helpers, but no delete/remove helper.
  • ctx.has_permission("..."), ctx.has_any_permission([...]), and ctx.has_all_permissions([...]) check declared permissions.
  • ctx.config_value("key") and ctx.require_config("key") read app install/config values.
  • ctx.secret("KEY") reads app secrets from config or environment variables.
  • get_config(ctx, key) and require_config(ctx, key) are functional config helper forms.
  • require_permission(permission), KNOWN_PERMISSIONS, and is_known_permission(permission) support route and manifest permission checks.
  • ctx.redis gives a Redis-backed, plugin/org-scoped Redis API when "redis" is declared in platform_services.
  • ctx.vector gives a Qdrant-backed, plugin/org-scoped vector API when "vector" is declared in platform_services.
  • ctx.storage gives app/org-scoped file upload helpers when "storage" is declared in platform_services.
  • LifecycleHooks lets apps define install/update/enable/disable/uninstall hooks.
  • OrgScopedTable and PluginBase keep app data inside the plugin schema model set.
  • plugin_safe_id(...), plugin_schema(...), plugin_table_prefix(...), and ensure_org_rls(...) keep database names and row-level security consistent.
  • Event and subscribe_event(...) register in-process platform event handlers.
  • sign_webhook(...) and verify_webhook_signature(...) handle HMAC-SHA256 webhook signing checks.
  • ToolDefinition is the base class for custom agent tools.
  • PluginManifest and load_manifest(...) parse and validate palette-plugin.json.
  • SuccessResponse, ErrorResponse, and PaginatedResponse are reusable response schemas.
  • route_permission_issues(router, public_routes=None) is the test helper for detecting ungated backend routes.

Python backend Data Room example:

@router.post("/sync-invoices", dependencies=[require_permission("data_rooms:write")])
async def sync_invoices(ctx: PluginContext = Depends(get_plugin_context)):
    room = await ctx.data_rooms.ensure_room("Finance")
    folder = await ctx.data_rooms.resolve_folder_path(
        room["id"],
        "Clients/Acme/Invoices",
        create=True,
    )
    file = await ctx.data_rooms.find_file_by_name(
        room["id"],
        "jan.pdf",
        folder_id=folder["id"] if folder else None,
    )
    content = await ctx.data_rooms.read_file_bytes(file["id"]) if file else None
    if folder:
        await ctx.data_rooms.upload_file(
            room["id"],
            "summary.txt",
            b"Generated by my app",
            folder_id=folder["id"],
            content_type="text/plain",
        )
    return {"room": room, "folder": folder, "bytes": len(content or b"")}

Python backend app-storage example:

@router.post("/reports", dependencies=[require_permission("reports:write")])
async def save_report(ctx: PluginContext = Depends(get_plugin_context)):
    saved = await ctx.storage.upload_file(
        "summary.json",
        b'{"ok": true}',
        "application/json",
        key="reports/summary.json",
    )
    return saved

Frontend apps can use createPaletteClient(platform).storage.upload(file, { onProgress }). Palette writes every object under the app folder and current organisation folder, then uses GCS resumable uploads in hosted environments.

The npm @palettelab/sdk package is for frontend JavaScript/React apps. Python backend code uses palette_sdk, which is embedded in the CLI for local dev/tests and injected by the hosted Palette runtime.

Lifecycle example:

from palette_sdk import LifecycleHooks, PluginContext

lifecycle = LifecycleHooks()

@lifecycle.on_install
async def seed_defaults(ctx: PluginContext):
    await ctx.repo(DefaultSetting).create(name="currency", value="USD")

Run local checks before publishing:

npx --yes @palettelab/cli@latest test
npx --yes @palettelab/cli@latest package

The CLI validates manifest shape, SDK compatibility, frontend bundling, backend imports, backend route permission gates, declared permissions, migration safety, package dependency policy, and backend package size.

Commands

pltt init <name>

Scaffold a new plugin directory from the official template.

pltt init data-explorer
pltt init crm-dashboard --template dashboard
pltt init next-panel --template next
cd data-explorer

Creates data-explorer/ with a valid palette-plugin.json, a frontend React entry, and a FastAPI backend entry.

Templates:

  • dashboard
  • next
  • agent-tool
  • external-service
  • database
  • frontend-only

Next-Compatible Frontend Config

Palette native apps publish as a single React module loaded by the OS. They do not run a standalone Next server. When an app needs Next-style frontend config, set the manifest to Next-compatible native mode:

{
  "frontend": {
    "entry": "./frontend/src/index.tsx",
    "sandbox": true,
    "framework": "next",
    "config": "./frontend/next.config.ts"
  }
}

Put the config file at frontend/next.config.ts unless you set a custom frontend.config path. pltt dev, pltt test, pltt package, and pltt publish load next.config.ts/js/mjs/cjs, apply env values plus NEXT_PUBLIC_* environment variables to the native bundle, and honor path aliases from frontend/tsconfig.json.

Supported today: env, NEXT_PUBLIC_*, and TypeScript path aliases. Full Next server features such as API routes, server components, Next image optimization, middleware, and multi-file static export are outside this native module mode.

pltt dev

Run a no-Docker local SDK simulator with your plugin mounted live. Run this from inside your plugin directory.

pltt dev
pltt sandbox --env staging
pltt dev --sandbox --env staging
pltt dev --cloud --env staging
pltt dev --platform

By default this starts:

  • A small local app shell on the first available port starting at http://localhost:3000
  • A local FastAPI backend runner on the first available port starting at http://localhost:8000
  • A mock Palette platform context for usePlatform(), OS language, toasts, org/user data, and authenticated API calls
  • A plugin-local SQLite database under .palette/dev/ when capabilities.database or database is enabled

Your frontend entry is bundled and watched. Your backend entry is loaded under /api/v1/plugins/<your-id>/* with a dev PluginContext, so normal SDK calls work without Docker or platform source.

The simulator also provides the same language fields that Palette OS provides: language, fallbackLanguage, supportedLanguages, and setLanguage(). Generated frontend templates include app-owned frontend/src/translations.ts files wired through usePluginTranslations(), so apps can switch when the OS language changes without storing copy in the platform.

For Python apps with database tables, ctx.db is an async SQLAlchemy session in local dev. The simulator imports backend/api/models.py when present and creates tables from palette_sdk.db.PluginBase.metadata. Production installs still use the declared Alembic migrations and platform-managed Postgres/RLS.

3000 and 8000 are preferred defaults, not hard requirements. If either port is already in use, pltt dev automatically picks the next free port and prints the actual URLs.

pltt dev --sandbox skips Docker and publishes a reviewable preview to a configured Palette sandbox. This is the payment-gateway-style flow: your app uses local SDKs, while real platform services such as login, Data Room, storage, database policies, logs, review, and publish lifecycle run in the hosted sandbox. It defaults to --env staging unless --env or PALETTE_ENV is set, adds a 24-hour preview TTL by default, and prints the preview/status/log commands returned by the platform.

For developer sandboxes where manual approval should not block OS testing, run the sandbox backend with APPSTORE_AUTO_APPROVE_SANDBOX_PREVIEWS=true. Preview publishes are still checked by the automated review gates; passing preview publishes are marked active and synced immediately so backend routes, installs, logs, permissions, and platform APIs can be tested in the OS without Docker.

pltt dev --cloud is kept as an alias for --sandbox.

pltt sandbox --env staging is the same hosted preview flow as pltt dev --sandbox --env staging.

pltt preview --env staging is also supported as a developer-friendly alias for the same hosted preview flow.

pltt dev --platform runs the full Docker platform-dev image for internal platform parity testing. App developers should not need it. It pulls ghcr.io/palette-lab/platform-dev:latest and mounts your plugin at /plugins/<your-id>.

Environment variables:

| Name | Default | Purpose | |---|---|---| | PALETTE_DEV_IMAGE | ghcr.io/palette-lab/platform-dev:latest | Override the platform image for --platform | | PALETTE_FRONTEND_PORT | 3000 | Preferred starting host port for the frontend | | PALETTE_BACKEND_PORT | 8000 | Preferred starting host port for the backend | | PALETTE_DEV_DATABASE_URL | .palette/dev/<plugin-id>.sqlite3 | Override the local dev database URL | | APPSTORE_AUTO_APPROVE_SANDBOX_PREVIEWS | false | Backend setting for hosted sandboxes; auto-approve passing preview publishes so developers can test full OS behavior without manual review |

pltt secrets

Palette secrets are declared in palette-plugin.json and resolved through ctx.secret("NAME").

{
  "secrets": {
    "OPENAI_API_KEY": { "scope": "install", "required": true },
    "STRIPE_SECRET": { "scope": "plugin", "required": true },
    "DEBUG_PROBE_URL": { "scope": "dev", "required": false }
  },
  "platform_services": ["llm", "redis", "storage", "vector"]
}

Commands:

pltt secrets init
pltt secrets set STRIPE_SECRET --env staging --value sk_live_...
pltt secrets rotate STRIPE_SECRET --env staging --value sk_live_...
pltt secrets list --env staging
pltt publish --env staging --secrets-file plugin-secrets.env

dev secrets live in .palette/.env.local, are loaded by pltt dev, and are never uploaded. plugin secrets are encrypted by the platform and attached to the plugin/environment. install secrets are filled by the installing org. Frontend bundles may only receive public values such as NEXT_PUBLIC_*.

Managed platform services do not require developer Redis/Qdrant keys. Declare them and use scoped SDK clients:

await ctx.redis.set("cart:123", {"items": [1, 2]}, ttl=3600)
cart = await ctx.redis.get("cart:123")

await ctx.vector.upsert_texts(
    "products",
    [{"id": "p1", "text": "Red cotton shirt", "metadata": {"category": "clothing"}}],
)
matches = await ctx.vector.search("products", query="red shirt", top_k=5)

The platform rewrites Redis keys and vector filters with plugin_id and organization_id, so create/update/delete/list operations cannot reach another app or org's data.

For provider-level Redis features, use ctx.redis.execute(...). It forwards to Redis after rewriting key arguments into the plugin/org namespace, and blocks server/admin commands such as FLUSHDB, CONFIG, KEYS, and cluster management. For advanced Qdrant features, use ctx.vector.client() with collection_name(), scoped_filter(), merge_filter(), scoped_payload(), and scoped_point() so custom calls remain scoped.

Common managed-service commands:

# Redis strings, counters, and key discovery
await ctx.redis.get("key", default=None)
await ctx.redis.set("key", {"json": True}, ttl=600)
await ctx.redis.delete("key1", "key2")
await ctx.redis.exists("key")
await ctx.redis.expire("key", 300)
await ctx.redis.ttl("key")
await ctx.redis.incr("counter")
await ctx.redis.decr("counter")
await ctx.redis.scan(prefix="cache:", limit=100)

# Redis hashes, lists, sets, sorted sets, queues, locks
await ctx.redis.hset("hash", "field", {"value": 1})
await ctx.redis.hgetall("hash")
await ctx.redis.lpush("queue", {"job": 1})
await ctx.redis.lrange("queue", 0, -1)
await ctx.redis.sadd("tags", "red", "blue")
await ctx.redis.smembers("tags")
await ctx.redis.zadd("scores", {"alice": 10})
await ctx.redis.zrange("scores", 0, -1, with_scores=True)
await ctx.redis.enqueue("jobs", {"task": "sync"})
await ctx.redis.dequeue("jobs")
await ctx.redis.lock("invoice:1", token, ttl=30)
await ctx.redis.unlock("invoice:1", token)

# Redis provider-style data-plane calls
await ctx.redis.execute("MSET", "a", "1", "b", "2")
values = await ctx.redis.execute("MGET", "a", "b")

# Vector search
await ctx.vector.upsert_texts("knowledge", [{"id": "doc-1", "text": "Text"}])
await ctx.vector.upsert_vectors("knowledge", [{"id": "vec-1", "vector": [0.1, 0.2]}])
hits = await ctx.vector.search("knowledge", query="invoice policy", top_k=10)
record = await ctx.vector.get("knowledge", "doc-1")
await ctx.vector.delete("knowledge", ["doc-1"])
await ctx.vector.delete_index("knowledge")
stats = await ctx.vector.stats("knowledge")

Blocked Redis control-plane commands include FLUSHDB, FLUSHALL, CONFIG, KEYS, CLUSTER, SCRIPT, EVAL, FUNCTION, and SELECT.

pltt login

Save a Palette sandbox or production environment URL plus token in ~/.palette/config.json with file mode 0600. Environment variables still override the stored token when present.

pltt login --env staging --url https://sandbox.pltt.ai --token <publish-token>
pltt dev --sandbox --env staging

pltt doctor

Check local tooling and common setup problems.

pltt doctor

Checks include Node version, port availability, manifest validity, entry files, and frontend bundling.

Docker is intentionally skipped by default:

pltt doctor --platform

Use --platform only when validating the internal Docker parity harness.

pltt build

Validate palette-plugin.json and check that all declared entry files exist. Run this before pushing a release.

pltt build

Also lints plugin migrations for unsafe RLS patterns when database.migrations is declared.

pltt test

Run local contract checks before publishing.

pltt test
pltt test --json

Checks include manifest validity, SDK/platform compatibility, semver bump detection, forbidden platform imports, frontend bundling and size limits, sandbox bridge smoke, backend dependency installation/import, route permission gates, route permission declarations, migration linting, frontend sandbox policy, and dependency policy for package.json / pyproject.toml.

pltt package

Bundle the plugin into dist/<plugin-id>-<version>.tar.gz without uploading.

pltt package
pltt package --json

Use this to inspect the publishable artifact before sending it to a Palette environment.

pltt publish

Build and publish the plugin to a Palette appstore environment.

pltt publish --env local
pltt publish --env staging
pltt publish --env production
pltt publish --env production -y
pltt publish --env staging --json
pltt publish --env staging --ttl-hours 24

Publishing first runs the same contract checks as pltt test. If preflight passes, it bundles frontend/backend artifacts, uploads them, creates a pending_review publish record, and prints review/preview URLs when the platform returns them.

--ttl-hours sets an expiration on the preview URL. It is mainly used by pltt dev --sandbox, which defaults previews to 24 hours.

Environment config is read from ./palette.config.json, ~/.palette/config.json, or PALETTE_<ENV>_URL plus PALETTE_<ENV>_TOKEN / PALETTE_PUBLISH_TOKEN.

pltt status <publish-id>

Show review status and automated risk report details for a publish.

pltt status 123 --env staging
pltt status --json

If no publish ID is provided, the CLI uses .palette/last-publish.json when available.

pltt logs [plugin-id]

Fetch or stream telemetry events for a plugin.

pltt logs my-plugin --env staging
pltt logs --tail 100
pltt logs --follow
pltt logs --json

If no plugin ID is provided, the CLI uses the current palette-plugin.json or .palette/last-publish.json.

Global Flags

  • --json emits machine-readable output for package, publish, status, logs, and test.
  • --env <name> selects a configured publish/status/logs environment.
  • -y, --yes skips production publish confirmation.

What gets shipped

@palettelab/cli itself contains only:

  • bin/pltt.js — entry point
  • backend-sdk/ — backend SDK files used by local contract tests and simulator
  • lib/ — pure-Node command implementations (no runtime dependencies)
  • platform-dev/docker-compose.yml — compose file for internal pltt dev --platform parity checks
  • template-fallback/ — offline fallback for pltt init if git is unavailable

See also

  • @palettelab/sdk on npm — frontend hooks and types
  • palette-sdk GitHub Release wheel on sdk-backend-v* — backend PluginRouter + ToolDefinition
  • sdk/scripts/verify-release-registries.sh — npmjs, GHCR, and backend SDK release verification