Pular para o conteúdo
CADCAMFUN
Docs

Documentação

Plug-ins

Manifestos de extensão, APIs de host de trabalho, ferramentas de assistente e contrato separado de instalação de plug-in de navegador.

Traduzido automaticamente do inglês. Ver o original em inglês

Escolha a superfície de extensão

@cadcamfun/plugin-sdk define o contrato do plugin de trabalho com um plugin.json manifesto e versão da API 1. Seu host expõe serviços controlados por capacidade e executa funções de plug-in em trabalhadores Bun. O carregador do assistente de produção atualmente aceita plug-ins que solicitam apenas ai:tool.

O /api/plugins A família HTTP usa um contrato de plug-in de navegador diferente: pacotes ZIP com manifest.json, a main entrada, permissões do navegador, configuração e inicialização de JavaScript. Instalar um pacote por meio dessa API não o torna um plugin de SDK de trabalho. Use o manifesto e o ciclo de vida correspondentes à integração pretendida.

Manifesto do SDK de trabalho

plugin.json para uma extensão assistente
{
  "id": "com.example.shop-helper",
  "name": "Shop helper",
  "version": "1.0.0",
  "apiVersion": "1",
  "entry": "index.js",
  "capabilities": ["ai:tool"],
  "contributes": {
    "aiTools": [{
      "id": "lookup",
      "description": "Look up the shop's documented inspection checklist.",
      "kind": "read",
      "parameters": { "type": "object", "properties": {} },
      "entry": "lookup"
    }]
  }
}

Os identificadores usam um formato DNS reverso, a versão é semântica major.minor.patch com um pré-lançamento opcional e apiVersion deve ser "1". Os caminhos de entrada são relativos ao diretório do plugin. As contribuições podem declarar comandos, painéis, pós-processadores, operações e ferramentas assistentes; declarar uma contribuição não conecta esse recurso a todos os hosts.

O esquema permite opcional signature e dev campos. O host de trabalho atual registra esses valores, mas não executa verificação de assinatura criptográfica durante a instalação. Um campo de assinatura por si só não é evidência de um editor verificado.

Entrada de módulo e funções de IA

definePlugin é um auxiliar de identidade digitado para um módulo com um manifesto e activate(ctx). A ativação pode retornar uma função de limpeza e o módulo pode expor deactivate. O SDK descreve esses ganchos de ciclo de vida; sua disponibilidade depende do bootstrap concreto do trabalhador, conforme detalhado abaixo.

As funções do assistente são exportadas através de um aiTools registro; a contribuição entry nomeia a função nesse registro. Uma contribuição de ferramenta inclui sua descrição, esquema de parâmetro JSON e read ou write tipo. Uma ferramenta de gravação é encaminhada por meio de uma solicitação de aprovação humana no assistente integrado; uma ponte externa recebe apenas ferramentas de plug-in de leitura permitidas.

Agrupe a entrada do trabalhador como um módulo independente. O host do trabalhador lê a fonte de entrada e a importa por meio de um módulo blob. Trate as importações relativas de pacotes ou arquivos como uma preocupação de integração separada, em vez de assumir um carregador de sistema de arquivos no estilo Node.

APIs controladas por capacidade

O SDK reconhece drawing:read, drawing:write, toolpath:read, storage, ui:panel, e ai:tool. A escrita do desenho implica a leitura do desenho. O host geral obtém recursos efetivos do manifesto instalado e os verifica no RPC; o aplicativo de incorporação decide quais manifestos e superfícies deseja carregar.

O contexto do SDK pode expor leituras de desenho e patches de adição/atualização/remoção de entidade, leituras de caminho de ferramenta/programa, armazenamento de valor-chave de string privada e registro de comando/painel declarativo. Os serviços ausentes estão ausentes do contexto. Uso de chaves de armazenamento plugin:<id>: namespaces e rejeitar chaves absolutas ou de passagem pai.

O carregador assistente de produção suporta apenas ai:tool. Um manifesto solicitando recursos de desenho, percurso, armazenamento ou UI é ignorado com um aviso. Ele não concede silenciosamente essas superfícies nem carrega seus recursos mais amplos no host assistente.

Carregamento e ciclo de vida do trabalhador

Para habilitar um ramal assistente, coloque seu plugin.json e entrada agrupada em <dataDir>/plugins/<id>/ e liste o identificador em <dataDir>/plugins/enabled.json, uma matriz JSON. O identificador da pasta deve corresponder ao manifesto. O carregamento ocorre enquanto o serviço de plug-in assistente é criado; um plug-in com falha registra um aviso sem interromper o aplicativo.

O host geral suporta estados instalados, habilitados, desabilitados e com falha, com instalação, habilitação, desabilitação, desinstalação, chamada de comando, chamada de ferramenta de IA e operações de configuração. Enable cria um trabalhador e completa seu handshake de inicialização; desabilitar destrói tudo. A desinstalação remove o estado do registro e o namespace de armazenamento por meio das dependências injetadas do host.

Os trabalhadores começam com um ambiente vazio e o bootstrap remove as APIs globais selecionadas. Esta é uma restrição no nível do aplicativo, não uma sandbox do sistema operacional: os Bun globais permanecem acessíveis. Ative apenas o código de extensão apropriado para execução dentro do limite do processo de aplicativo.

Limites de tempo de execução

O host define uma configuração de memória de trabalho de 256 MB, um limite de taxa de 2,000 chamadas por minuto e no máximo 64 chamadas pendentes por plug-in. O comportamento do tempo limite do comando é fornecido pelo host de incorporação; o carregador assistente usa 10 segundos. As chamadas de ferramentas de IA têm um prazo separado de 30 segundos. Tempos limite, ferramentas ausentes, falhas de trabalho, falhas de capacidade e esgotamento da fila retornam falhas de host digitadas ou resultados de ferramentas legíveis.

O rastreamento de falhas causa falhas em um plug-in após três falhas em dez minutos. Esses controles de host vinculam o RPC cooperativo e o comportamento do ciclo de vida; a configuração de memória passada para Bun não substitui um executável isolado separadamente.

Os limites são definidos em apps/sidecar/src/plugins/host.ts: CALL_TIMEOUT_MS (5,000 ms por chamada normal), AI_TOOL_TIMEOUT_MS (30,000 ms por chamada da ferramenta assistente), MEMORY_LIMIT_MB (256), CALL_RATE_LIMIT (2,000 chamadas por plug-in por minuto), MAX_PENDING_CALLS (64) e CRASH_LIMIT (3 falha em 10 minutos CRASH_WINDOW_MS, após o qual o plugin é desabilitado automaticamente).

Instalação ZIP e API de plugin do navegador

POST /api/plugins/install aceita um arquivo multipart. O instalador lê manifest.json da raiz ZIP ou de um único diretório de nível superior, valida o manifesto do plug-in do navegador e armazena seus arquivos em <dataDir>/storage/plugins/<id>/. Uso de manifestos do navegador main, engines.cadcame uma lista de permissões do navegador, em vez do SDK entry, apiVersione capacidades.

GET /api/plugins lista as entradas instaladas; detalhes, ativar/desativar, desinstalar, atualizar e configurar endpoints operam em seu registro. /api/plugins/bootstrap.js?id=… serve JavaScript; /api/plugins/serve?id=…&file=… serve arquivos de plugin. Estas são operações de texto/binárias com seus tipos de conteúdo declarados.

O navegador de registro local lê os metadados do pacote em <dataDir>/plugins-registry. É um catálogo de manifesto local. A instalação do arquivo, a listagem do registro e o carregamento da extensão do assistente de trabalho são operações separadas.

Ajudantes do SDK

effectiveCapabilities, hasCapability, e missingCapabilities inspeção de capacidade de suporte. requireCapability gera um erro de capacidade quando uma superfície de contexto solicitada está ausente. storageNamespace e storageKey construir chaves com namespace; parseManifest valida JSON; manifestKey formata um par ID/versão.

Pontos de entrada de implementação

packages/plugin-sdk/src possui as APIs de manifesto público, capacidade, contexto, plug-in e host de teste. apps/sidecar/src/plugins/host.ts possui ciclo de vida do trabalhador e RPC; services/ai-plugins.ts possui carregamento de arquivo de consentimento e registro de assistente. A instalação do navegador é implementada em http/plugins.ts e repos/plugin-registry-repo.ts.

Revisão de extensão

Mantenha os esquemas de contribuição precisos, retorne pequenos resultados estruturados e declare honestamente o comportamento de leitura/gravação. Teste no host de incorporação real: a disponibilidade do SDK não estabelece acesso de produção ao desenho, armazenamento, interface do usuário ou registros posteriores. O plugin nikcli independente é um cliente de ação HTTP e tem uma finalidade diferente do código carregado em um trabalhador.

Conclua o módulo do assistente de amostra

O plugin.json exemplo acima de nomes aiTools.lookup. Este é o correspondente independente index.js. O código do módulo de nível superior é executado quando importado; a função de pesquisa declarada é executada quando chamada.

index.js
export const aiTools = {
  lookup: async () => ({
    ok: true,
    checklist: [
      "Compare the drawing revision with the job traveller.",
      "Record the measuring instrument and its calibration status.",
      "Record each inspected dimension and the measured value."
    ]
  })
}

Estes são exemplos de instruções de oficina, não um padrão de inspeção integrado. Substitua-os pelo conteúdo que sua extensão realmente possui. Para admitir este módulo na próxima inicialização do serviço, o arquivo habilitado é:

<dataDir>/plugins/enabled.json
["com.example.shop-helper"]

O catálogo em execução expõe um identificador derivado de IDs de plug-ins e ferramentas. Descubra através GET /api/ai/actions; não presuma que o registro ZIP do navegador seja a lista habilitada do assistente. Recarregar o aplicativo/serviço é a maneira confiável de reler o arquivo de consentimento de produção; nenhum observador de sistema de arquivos é configurado por este carregador.

Fonte: apps/sidecar/src/services/ai-plugins.ts, packages/ai/src/tools/plugin-tool.ts, e apps/sidecar/test/ai-plugin-tools.test.ts.

Esquemas de argumento e limites do ciclo de vida

O ID local de uma ferramenta deve começar com uma letra minúscula e conter letras minúsculas, dígitos ou sublinhados, com no máximo 40 caracteres. As descrições contêm 10–2,000 caracteres. A contribuição deve nomear uma função de entrada não vazia e um esquema de parâmetro JSON. O adaptador de aplicativo valida os argumentos antes de chamar o trabalhador; uma recusa pode retornar um estruturado ok: false resultado sem encerrar o plugin.

O SDK público descreve activate, limpeza e deactivate pontos de entrada. Os manipuladores RPC de ativação/desativação do atual bun trabalhador reconhecem mensagens de ciclo de vida, mas não invocam essas funções de módulo exportadas. As extensões assistentes devem, portanto, colocar seu comportamento de chamada em aiTools e evite depender de retornos de chamada de ativação do SDK para configuração ou limpeza.

O bootstrap do trabalhador geral também contém manipuladores de desenho/percurso/UI de espaço reservado. Eles não constituem desenho de produção ou integração de UI. A restrição do carregador assistente de ai:tool evita que essas superfícies não suportadas sejam anunciadas como acesso a aplicativos funcionais.

Fonte: packages/plugin-sdk/src/manifest.ts, plugin.ts, apps/sidecar/src/plugins/host.ts (PLUGIN_BOOTSTRAP), e services/ai-plugins.ts.

Diagnosticar uma extensão ausente ou com falha

  • Nenhuma ferramenta no catálogo: verifique JSON habilitado válido, correspondência exata de ID de pasta/manifesto, decodificação de manifesto e o ai:tool capacidade. Leia o aviso de inicialização para um plugin ignorado.
  • O manifesto solicita superfícies não suportadas: remova recursos não suportados somente se o código realmente não precisar deles. Um plugin assistente não pode adquirir acesso de desenho e gravação simplesmente declarando-o.
  • Nenhuma ferramenta: fazer contributes.aiTools[].entry corresponder a uma função exportada em aiTools. O ID da contribuição e o nome da entrada têm finalidades diferentes.
  • Falha na validação do argumento: inspecione o esquema JSON e os campos obrigatórios. Valores errados são recusados antes da execução, conforme demonstrado pelos testes de trabalho real e de integração de aplicativos.
  • Tempo limite ou estado de falha: investigue bloqueios de trabalho, promessas não resolvidas ou travamentos; travamentos repetidos culpam o plugin. Reative somente após corrigir a causa.
  • O instalador ZIP rejeita o manifesto: use o plugin do navegador manifest.json contrato para esse endpoint. Renomeando um trabalhador plugin.json não traduz seus campos.

Fonte: apps/sidecar/test/plugins-ai-tools.test.ts, ai-plugin-tools.test.ts, plugins-http.test.ts, e src/plugins/host.ts.

Referência de manifesto

Cada plugin vem com um plugin.json. parseManifest(json) analisa e valida em uma única etapa, transformando um erro de sintaxe JSON no mesmo canal de erro que um erro de esquema, portanto, uma tela de instalação só precisa lidar com um tipo de falha.

CampoRegra
idIdentificador DNS reverso: 3–128 caracteres, letras minúsculas, dígitos e hifens em pelo menos dois rótulos separados por pontos, por exemplo com.example.gear-generator.
nameNome de exibição não vazio.
versionmajor.minor.patch, opcionalmente seguido por um sufixo de pré-lançamento, como -beta.1.
apiVersionDeve ser a string "1"; é a única versão da API do plugin.
entryCaminho do módulo ES relativo à pasta do plugin.
capabilitiesMatriz de strings de capacidade (veja abaixo). Qualquer coisa fora da lista fechada é um erro manifesto.
contributesOpcional commands, panels, postProcessors, operations, e aiTools.
signatureAssinatura Ed25519 sobre o hash do arquivo; necessário para instalações empacotadas.
devtrue marca um plugin de desenvolvimento local: as verificações de assinatura são ignoradas e a IU mostra um emblema.
ContribuiçãoCampos
commands[]id, title, opcional keybinding como Mod+Shift+G.
panels[]id, title, entry: uma função exportada que retorna a árvore declarativa do painel. Nunca há acesso ao DOM.
postProcessors[]id, title, entry: um exportado (program, options) => string função.
operations[]id, title, entry: um registro exportado com um parâmetro schema e um puro generate(params, geometry, tool, stock) que retorna um percurso.
aiTools[]id correspondência ^[a-z][a-z0-9_]{0,39}$, description (10–2,000 caracteres; o modelo lê), kind read ou write, parameters (Esquema JSON dos argumentos) e entry.

Ajudantes: manifestKey(m) retorna id@version; requestedCapabilities(m) retorna a lista de recursos classificada para exibição; isDevPlugin(m) e isSigned(m) responda às perguntas sobre política de instalação.

Capacidades e o contexto do plugin

Capacidades são a única coisa através da qual um plugin pode atuar. O manifesto declara o que o plugin precisa, o usuário concede um subconjunto e o host constrói um contexto que contém exatamente as superfícies concedidas. Cada superfície é opcional no tipo, portanto, um recurso ausente é visível tanto em tempo de compilação quanto em tempo de execução, em vez de se tornar um ambiente autônomo silencioso.

CapacidadeSuperfície de contextoPermiteImplica
drawing:readctx.drawing.read()Leia o desenho aberto (ou undefined quando nenhum estiver aberto).—
drawing:writectx.drawing.patch(patch)Aplicar AddEntity, UpdateEntity, ou RemoveEntity manchas; retorna a nova revisão.drawing:read
toolpath:readctx.toolpath.list(), .program(id)Leia percursos da ferramenta de ferramenta e programas de código G.—
storagectx.storageStrings de valores-chave com namespace: get, set, delete, keys.—
ui:panelctx.uiregisterCommand e registerPanel (somente declarativo).—
ai:toolnenhuma (contribuições manifestas)Ofereça ferramentas ao assistente e, somente leitura, a um nikcli independente.—
effectiveCapabilities(granted) resolve implicações, então um plugin que declara drawing:write não precisa declarar drawing:read. Todo contexto também tem pluginId, o resolvido capabilities, e um log com debug, info, warn, e error; o host prefixa as linhas de log com o ID do plugin. Usar requireCapability(ctx, capability, surface) obter uma superfície ou falhar com um CapabilityError que nomeia a concessão ausente.

Testar um plug-in com o host de teste do SDK

createTestHost constrói um host na memória com as mesmas regras de capacidade do sidecar. Apenas os recursos concedidos aparecem no contexto e você pode fornecer um desenho, percursos da ferramenta de ferramenta e programas. O exemplo abaixo está completo e é executado com bun no espaço de trabalho.
frame-plugin.test.ts
import { cad } from "@cadcamfun/core"
import { createTestHost, definePlugin, requireCapability, testManifest } from "@cadcamfun/plugin-sdk"

const manifest = testManifest({
  id: "com.example.frame",
  capabilities: ["drawing:write", "storage", "ui:panel"],
})

const plugin = definePlugin({
  manifest,
  activate(ctx) {
    const drawing = requireCapability(ctx, "drawing:write", "drawing")
    ctx.ui?.registerCommand({ id: "frame.add", title: "Add frame line" })
    ctx.storage?.set("runs", "1")
    drawing.patch({
      _tag: "AddEntity",
      entity: { ...cad.baseEntity("frame-1"), _tag: "Line",
                start: { x: 0, y: 0 }, end: { x: 100, y: 0 } },
    })                                   // returns revision 1
    return () => ctx.log.info("teardown") // runs on deactivation
  },
})

const host = createTestHost({ manifest, drawing: cad.emptyDrawing("d1", "Demo") })
const stop = await host.activate(plugin)
host.drawing()?.entities.length     // 1
host.context.storage?.get("runs")   // "1"
host.ui.commands                    // [{ id: "frame.add", title: "Add frame line" }]
await stop()

// Without the grant, requireCapability throws:
const denied = createTestHost({ manifest, granted: ["storage"] })
await denied.activate(plugin)
// CapabilityError: Plugin com.example.frame may not use "drawing:write":
// it was not granted at install time
  • testManifest(overrides) começa a partir de um manifesto válido mínimo (com.example.test-plugin, versão 1.0.0, API 1, entrada index.js, sem capacidades).
  • granted o padrão é tudo o que o manifesto pede. Passe uma lista menor para testar os caminhos negados.
  • host.storage é um instantâneo tirado quando o host é criado. Leia os valores ao vivo através host.context.storage.
  • host.on(commandId, handler) e host.invoke(commandId, args) exercer comandos registrados.
  • runPostProcessor executa uma postagem contribuída em um programa e keyOf(pluginId, key) mostra a chave de armazenamento com namespace para que os testes possam afirmar o isolamento.

Como o aplicativo carrega plug-ins assistentes

  1. Na inicialização, o sidecar lê <data dir>/plugins/enabled.json, uma matriz JSON de IDs de plug-ins. Nada é carregado, a menos que esteja listado lá.
  2. Para cada ID que ele lê <data dir>/plugins/<id>/plugin.json e analisa-o. O nome da pasta deve ser igual ao manifesto id.
  3. Apenas ai:tool está conectado neste host. Um manifesto que solicita acesso a desenho, percurso, armazenamento ou UI é ignorado com um aviso em vez de carregado com superfícies que silenciosamente não fariam nada.
  4. O plugin é instalado em um host de trabalho e habilitado. Um plugin que não consegue carregar logs Plugin <id> was not loaded: ... e nunca interrompe a inicialização do aplicativo.
  5. Cada ferramenta contribuída é oferecida ao assistente como plugin_<slug of plugin id>_<tool id>, no máximo 80 caracteres, onde o slug coloca o ID em minúscula e substitui outros caracteres por sublinhados. com.example.bolt-circle com ferramenta bolt_circle torna-se plugin_com_example_bolt_circle_bolt_circle.
  6. Os argumentos são validados no esquema JSON da ferramenta antes que o trabalhador os veja. Se o esquema exceder 20,000 caracteres, não for um esquema de objeto ou não puder ser convertido, o host aceitará qualquer objeto e o plug-in deverá validar.
  7. Uma chamada que é lançada, expira ou descobre que o plugin desapareceu retorna { ok: false, error } (texto de erro de até 500 caracteres) para o assistente em vez de travar a curva. UM write ferramenta é confirmada com a pessoa antes de cada chamada, independentemente dos seus argumentos.

Não é uma sandbox de sistema operacional

O trabalhador não tem processo, busca ou identificador de arquivo próprio, mas os Bun globais permanecem acessíveis dentro dele. Habilite apenas plug-ins cujo código você confia e reduza ao mínimo as ferramentas de gravação.

Regras de identificador e versão, testadas

ValorVálido?Regra
com.example.gear-generatorsimDNS reverso: segmentos minúsculos de letras, dígitos e -, pelo menos dois 3–128 caracteres
a.bsimDois segmentos de uma letra são suficientes
gearnãoPrecisa de pelo menos um ponto
Com.Example.xnãoMaiúsculas não são permitidas
com.example_x.ynãoO sublinhado não é permitido
VersãoVálido?
1.0.0sim
1.2.3-beta.1sim
1.0não: são necessários três números
v1.0.0não: sem prefixo
apiVersion deve ser a string "1", o único plugin API principal que existe. Os IDs das ferramentas de IA devem corresponder ^[a-z][a-z0-9_]{0,39}$ e são mostrados ao assistente como plugin_<plugin>_<id>; as descrições devem ter 10–2,000 caracteres porque o modelo as lê.

Tipos de contribuição

ChaveCamposPropósito
commandsid, title, opcional keybinding (e.g. Mod+Shift+G)Comandos na paleta
panelsid, title, entryUm painel renderizado a partir de uma árvore declarativa que a função nomeada retorna; sem acesso DOM
postProcessorsid, title, entryA (program, options) => string postagem registrada em tempo de execução
operationsid, title, entryUm exportado { schema, generate } registro
aiToolsid, description, kind ler | escrever, parameters (Esquema JSON), entryFerramentas oferecidas ao auxiliar; uma ferramenta de gravação é confirmada antes de cada chamada

Outros campos de manifesto: signature (Ed25519 sobre o hash de arquivo, necessário para instalações empacotadas) e dev (plugin de desenvolvimento local: ignora verificações de assinatura e mostra um crachá).

Resolução de capacidade, testada

Implicação, concessões ausentes, chaves de armazenamento
import { effectiveCapabilities, missingCapabilities, storageKey, requireCapability } from "@cadcamfun/plugin-sdk"

effectiveCapabilities(["drawing:write", "storage"])
// ["drawing:read", "drawing:write", "storage"]   (write implies read; sorted)

missingCapabilities(["drawing:read", "toolpath:read", "ai:tool"], ["drawing:write"])
// ["toolpath:read", "ai:tool"]

storageKey("com.example.gear", "presets/m2")   // "plugin:com.example.gear:presets/m2"
storageKey("com.example.gear", "../x")
// RangeError: storage key "../x" may not escape the plugin namespace   (same for "/abs")

requireCapability(ctxWithoutDrawing, "drawing:read", "drawing")
// CapabilityError (_tag "CapabilityDenied"):
// Plugin com.example.gear may not use "drawing:read": it was not granted at install time
Superfície no contextoPresente quando concedidoMétodos
drawingdrawing:read ou drawing:writeread() → desenho ou undefined; patch(p) → nova revisão, com AddEntity, UpdateEntity, RemoveEntity patches
toolpathtoolpath:readlist(), program(toolpathId)
storagestorageget, set, delete, keys; valores de string
uiui:panelregisterCommand, registerPanel
logsempredebug, info, warn, error

Em 3D

Pacotes de plug-ins no espaço de trabalho Arraste para orbitar · role para ampliar · arraste com o botão direito para deslocar
All 13 workspace packages from their package.json files; each line is a workspace:* dependency (17 in total). Rows are dependency depth: packages with no workspace dependencies at the front, the sidecar, which depends on 8 of them, at the back. Orange boxes are apps. Highlighted: plugin-sdk (depends on core) and the nikcli plugin, both loaded by the sidecar.