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

@planuze/pack-publisher

v0.4.9

Published

CLI de autoria, build, inspeção e publicação de packs .plnzpack.

Readme

@planuze/pack-publisher

CLI de autoria, build, inspeção e publicação de packs Planuze (.plnzpack). Expõe o binário planuze e é a ferramenta que um autor (first-party ou de comunidade) usa localmente e no CI para chegar do diretório de pack ao registry. Pacote público (SDK de packs, ADR-0429); orquestra @planuze/pack-format (formato/assinatura/cripto) e @planuze/pack-runtime (tar/generator), lendo env tipada via @planuze/platform-core (getEnv, nunca process.env direto — §4.10).

Guias para autores: docs/DEVELOPING_PACKS.md e docs/PUBLISHING_PACKS.md.

Comandos

planuze pack init <pack-id> [--yes] [--force]
                            [--kind template|extension] [--extends <parent>] [--extends-min-version 1.0.0]
                            [--modules <id1,id2,id3>]
planuze pack lint    [pack-dir]
planuze pack build   [pack-dir] --key=<pem> [--escrow-public-key=<base64>] [--out=<file>]
planuze pack publish <pack.plnzpack> [--token=<token>] [--endpoint=<url>] [--ci-mode]
planuze pack release [pack-dir] --key=<pem>
                            [--token=<token>] [--endpoint=<url>] [--out=<file>] [--ci-mode]
                            [--legacy-direct-oidc-attestation]
planuze pack inspect <pack.plnzpack> [--json] [--with-files] [--public-key=<pem>]
planuze pack run     <pack-dir> [project-dir] [--steps=a,b] [--models=a,b] [--force] [--debug]
planuze pack publisher:keygen       [--output=<dir>] [--ci-mode]
planuze pack publisher:register-key [--key=<pem>] [--endpoint=<url>] [--ci-mode]

O parser é minimalista (cli.ts); cada comando devolve um exit code (0 ok, 1 erro de conteúdo, 2 erro de uso/publish) e loga erros via describeError. Cada comando é também exportado como função pura de src/index.ts (packBuild, packPublish, …), devolvendo Result<_, PackPublisherError> — testável sem shell nem rede (fetch injetável).

Fluxo de autoria

flowchart LR
  I["init<br/>scaffold"] --> L["lint<br/>manifest/locales/docs"]
  L --> B["build<br/>sign + encrypt + escrow"]
  B --> P["publish<br/>upload-init → PUT → finalize"]
  L --> A["release<br/>snapshot → scan local → build → PUT"]
  A --> C["GitHub GitLab Bitbucket<br/>scan central OIDC → finalize"]
  P --> R["registry<br/>scan + review"]
  C --> R

init

Scaffolds um pack novo (commands/pack-init.ts): manifest.json (com publicKeyFingerprint placeholder sha256:0…0), locales/, base/, generator/. --kind extension exige --extends <parent>; --modules gera stubs em manifest.modules[]. Rejeita IDs reservados (planuze, core, auth, …). --yes pula o wizard interativo (nome/descrição/linguagem/…).

lint

Roda lintPack do pack-format (commands/pack-lint.ts): manifest válido pelo schema, locales/*.json cobrindo todas as labelKey, generator.entrypoint existente, placeholders de rota conhecidos, aviso para fingerprint placeholder e o gate de docs por locale (ADR-0303/0723 — toda doc declarada existe em docs/<locale>/ + fallback en-US, sem stub). Exit 1 se houver erro.

build

packBuild(dir, options) (commands/pack-build.ts) produz o .plnzpack assinado. Sempre roda lintPack() antes e aborta se houver erro de lint. Depois:

  1. Lê manifest.json e a chave de assinatura Ed25519 (--key, default ./planuze-pack-signing.pem). O manifest-fonte portátil mantém o placeholder canônico sha256: + 64 zeros; o build o substitui pelo fingerprint real apenas dentro do artefato assinado. Se o source declara um pin não-zero, ele precisa coincidir com a chave ou o build falha antes do archive. O fingerprint da antiga root Planuze não deve ser copiado para o source.
  2. Gera uma content key aleatória por pack (32 bytes, XChaCha20 — modelo único ADR-0439; sem master key global) e a lacra por escrow (ECIES X25519) para a pubkey de escrow do servidor, gravando contentKeyEscrow no manifest assinado. Só a privkey de escrow (custodiada no servidor) desembrulha — o CLI nunca conhece o segredo.
  3. Cifra base/ e o generator/ bundlado (ver abaixo) e o agent/ opcional com a content key (AAD ${id}@${version}), computa o pack.lock (SHA-256 de cada entrada), assina manifest.json (manifest.sig) e pack.lock (content.sig) e grava o ZIP no layout normativo de docs/specs/plnzpack-v1.md.
  4. Copia docs/<locale>/* em claro (pré-venda; ADR-0523). Forcing function (ADR-0572): se o manifest declara um doc sem o arquivo correspondente, o build falha — impede shippar pack que promete doc inexistente (drawer "Sem documentação" silencioso).

Bundle do generator (internal/bundle-generator.ts): um pack materializado não tem node_modules, então o build inlina com esbuild toda a árvore de deps do generator (handlebars/node-plop/prettier + @planuze/pack-runtime/generator-tools) — ESM, platform: node, preservando a estrutura de arquivos (multi-entry + code-splitting em chunks/), porque o generator resolve assets via import.meta.url relativo. Assets em runtime (.hbs, defaults.json) são copiados verbatim; testes ficam de fora. Um banner injeta createRequire para deps CJS que fazem require dinâmico (ADR-0516).

publish

packPublish(file, options) (commands/pack-publish.ts) envia o .plnzpack ao registry em três chamadas (ADR-0124/0384):

  1. POST /admin/packs/upload-init (Authorization: Bearer <token>) → devolve uploadUrl + uploadId + o header de upload esperado.
  2. PUT <uploadUrl> com os bytes (application/octet-stream); o token de upload vai por header, nunca na URL.
  3. POST /admin/packs/upload-finalize com o uploadId. O escrow da content key viaja no manifest assinado — o registry o lê de lá no finalize (o publish não manda header de escrow).

Quando chamado pelo pack release canônico, há uma etapa obrigatória entre PUT e finalize. A CLI cria um job central, aguarda o workflow Planuze escanear o mesmo blob recebido e só prossegue depois do atestado OIDC correlacionado. O PUT sempre carrega x-planuze-artifact-sha256; qualquer falha ou timeout impede o finalize. Sem attestation, o contrato histórico de packPublish permanece igual.

Retry (ADR-0546): 429/5xx são retryados com backoff exponencial (o registry pode responder 503 num blip/cold-start do licensing que verifica o token — um token válido não pode falhar por isso); 4xx (401/403/409/…) é determinístico, nunca retryado; erro de rede é surfaced imediatamente. Falhas de fetch são mapeadas para o erro de rede mais específico (timeout/offline/DNS) para diagnóstico útil. --ci-mode emite o resultado como JSON (sucesso em stdout, erro em stderr) para a GitHub Action parsear.

release

packRelease(dir, options) (commands/pack-release.ts) é o boundary atômico de publicação (ADR-0882/0890): coleta base/, agent/, docs e o generator já bundlado uma única vez; roda @planuze/pack-scanner localmente como fail-fast usando somente manifest.stack.requires; status fail não produz archive. Em sucesso, cifra/assina o snapshot, confere que o arquivo não mudou pelo SHA-256 esperado e faz PUT. Por padrão o release sempre aguarda o scan central independente (waitForCentralScan) — o registry NUNCA pula esse re-scan. O atestado direto legado só ativa com a flag --legacy-direct-oidc-attestation explícita nesta invocação: a mera presença de ACTIONS_ID_TOKEN_REQUEST_URL/ACTIONS_ID_TOKEN_REQUEST_TOKEN no ambiente nunca é suficiente sozinha (qualquer job GitHub Actions com permissions: id-token: write os expõe, inclusive de um publisher adversarial rodando este SDK público na própria CI — ADR-0890/ 0893). Sem os dois envs presentes, a flag falha cedo com usage error antes de build/upload. Os exemplos oficiais (Portal Publisher / pack-boilerplate) nunca passam essa flag e solicitam o scan central do blob exato em GitHub, GitLab e Bitbucket, aguardando seu atestado antes do finalize. Ausência de readiness, falha ou timeout fecham o release sem fallback inseguro. O checkout é tratado como dado: não roda npm install, lifecycle scripts, testes ou o generator do pack.

O subcomando interno pack scan-upload pertence somente ao reusable central e não integra a API pública do SDK nem o help da CLI. Como o runner recomendado vive num repositório público, sua saída é deliberadamente mínima: sucesso informa apenas completed; falhas viram central_scan_failed. Paths, pack id, manifest, findings, snippets, versão do scanner, content key e upload id nunca são serializados em stdout/stderr. O reusable executa apenas o comando estático planuze pack scan-upload --ci-mode. O Registry resolve upload e checksum pelo run_id do token OIDC já vinculado ao dispatch; nenhum UUID, checksum, endpoint ou audience passa por input, with:, env ou argv do runner público.

inspect

packInspect(file, options) (commands/pack-inspect.ts) lê apenas manifest, lock, entradas do ZIP e tamanhos — nunca decifra conteúdo. Com --public-key, valida a assinatura (via PackVerifier em modo direto) sem expor o payload cifrado, reportando valid/invalid/not_requested. --json e --with-files controlam a saída.

run

packRun(packDir, projectDir, options) (commands/pack-run.ts) executa o generator de um pack de fonte (via handleFromFilesystem + GeneratorEngine) contra um diretório de projeto — para desenvolver/testar steps localmente. --steps/--models filtram o que roda; --debug imprime cada evento NDJSON. Exit 1 se o generator emitir run.finished {ok:false}.

Chaves de publisher (chain-of-trust)

  • publisher:keygen (commands/publisher-keygen.ts) gera o keypair Ed25519 de assinatura localmente — a privkey nunca sai da máquina (§4.9), salva em ~/.planuze/publisher.key (modo 0600) + .pub (0644), e imprime o fingerprint. O comando recusa a operação se qualquer um desses caminhos já existir; nunca use a geração de chave para substituir uma identidade cadastrada. --help apenas exibe o uso e não acessa o filesystem.
  • publisher:register-key (commands/publisher-register-key.ts) registra a pubkey via challenge-response: pede um nonce, assina-o com a privkey local (provando posse sem expor a chave) e faz o register. A chave entra em awaiting-verification até o staff root-assiná-la (ADR-0179/0405) — fechando a cadeia pack ← publisher ← root que o consume verifica.

Segredos e configuração

Lidos por flag (precedência) ou env tipada via @planuze/platform-core.getEnv:

| Env / flag | Uso | |---|---| | --key / ./planuze-pack-signing.pem | privkey Ed25519 de assinatura do pack (build) | | --escrow-public-key / PLANUZE_ESCROW_PUBLIC_KEY | override técnico da pubkey X25519 de escrow (base64). O fluxo normal não configura esse valor: o build busca <registry>/escrow-public-key automaticamente (ADR-0445) | | --token / PLANUZE_PUBLISHER_TOKEN | token consumido pela CLI. Os snippets públicos recebem o secret PLANUZE_PUBLISH_TOKEN e o mapeiam para esta env sem logá-lo | | --endpoint / PLANUZE_REGISTRY_URL | base do registry; fallback canônico https://registry.planuze.com/v1 | | audiência OIDC | derivada internamente de <endpoint>/scan-attestations; não aceita input separado | | --legacy-direct-oidc-attestation (release) | opt-in EXPLÍCITO por invocação para o atestado direto legado; sem a flag, release sempre usa o scan central. Exige ACTIONS_ID_TOKEN_REQUEST_URL + ACTIONS_ID_TOKEN_REQUEST_TOKEN (falha cedo com usage error se ausentes) | | ACTIONS_ID_TOKEN_REQUEST_URL + ACTIONS_ID_TOKEN_REQUEST_TOKEN | envs que o GitHub injeta em jobs com permissions: id-token: write; só têm efeito quando combinados com --legacy-direct-oidc-attestation — a mera presença deles nunca seleciona o atestado direto sozinha (ADR-0890/0893). O fluxo público provider-neutral remove essa flag e usa o scan central | | PLANUZE_USER_SESSION_TOKEN | sessão para publisher:register-key | | PLANUZE_API_URL | base do api-gateway (registro de chave) |

A privkey de assinatura e o token de publish são secrets — nunca commitados. No caller GitHub, os nomes públicos são exatamente PLANUZE_SIGNING_KEY e PLANUZE_PUBLISH_TOKEN. PLANUZE_REGISTRY_URL não é necessária para produção. Não confundir a pubkey de escrow (pública por design — só lacra a content key, nunca a abre) com a privkey de assinatura.

CI/CD

GitHub Actions, GitLab CI e Bitbucket Pipelines executam planuze pack release pelo mesmo scan central. Os pins imutáveis do runtime e da CLI são rotacionados em conjunto e, por isso, não são duplicados neste README do pacote. Copie sempre o snippet exibido pelo Portal Publisher ou use o pack-boilerplate como fonte canônica. Em todos os casos, a CLI seleciona o scan central automaticamente e não recebe token GitHub, OIDC manual, SCAN_RUNNER_URL nem secrets de infraestrutura.

O Portal só exibe qualquer snippet depois que o runtime público confirma central_oidc/ready, incluindo o gate operacional pós-canário. Os três providers exigem no repositório apenas PLANUZE_PUBLISH_TOKEN e a chave de assinatura. GitHub/GitLab usam PLANUZE_SIGNING_KEY; Bitbucket usa a mesma PEM em Base64 numa única linha como PLANUZE_SIGNING_KEY_B64 e a decodifica/valida num arquivo temporário antes do release.

O exemplo completo está em docs/PUBLISHING_PACKS.md. O comando legado pack publish permanece uma API de transporte, não uma alternativa ao release atestado quando a autoridade OIDC está ativa.

Boundaries e build

  • Gate check:cruiser (regra pack-publisher-*): depende de @planuze/core-kit, @planuze/platform-core, @planuze/pack-format, @planuze/pack-runtime, @planuze/pack-scanner (+ esbuild, node-plop).
  • Toda operação com falha esperada devolve Result<_, PackPublisherError> (§4.6).
pnpm --filter @planuze/pack-publisher build       # tsup → dist/ (+ bin/planuze.js)
pnpm --filter @planuze/pack-publisher test        # vitest run
pnpm --filter @planuze/pack-publisher typecheck   # tsc --noEmit