Vai al contenuto
CADCAMFUN
Documentazione

Documentazione

Plugin

Manifesti delle estensioni, API lavoratore-host, strumenti di assistenza e contratto di installazione separato del plug-in del browser.

Tradotto automaticamente dall'inglese. Vedi l'originale in inglese

Scegli la superficie di estensione

@cadcamfun/plugin-sdk definisce il contratto del plugin di lavoro con a plugin.json manifest e versione API 1. Il suo host espone servizi controllati dalle capacità ed esegue funzioni di plugin nei lavoratori Bun. Il caricatore dell'assistente di produzione attualmente accetta solo plugin che lo richiedono ai:tool.

Il /api/plugins La famiglia HTTP utilizza un diverso contratto di plug-in del browser: pacchetti ZIP con manifest.json, a main voce, autorizzazioni del browser, configurazione e bootstrap JavaScript. L'installazione di un pacchetto tramite tale API non lo rende un plug-in SDK di lavoro. Utilizza il manifest e il ciclo di vita corrispondenti all'integrazione prevista.

Manifesto dell'SDK di lavoro

plugin.json per un'estensione dell'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"
    }]
  }
}

Gli identificatori utilizzano un modulo DNS inverso, la versione è semantica major.minor.patch con un prerelease facoltativo e apiVersion deve essere "1". I percorsi di ingresso sono relativi alla directory del plugin. I contributi possono dichiarare comandi, pannelli, post-processori, operazioni e strumenti di assistente; dichiarare un contributo non collega tale funzionalità a ogni host.

Lo schema consente facoltativo signature e dev campi. L'host di lavoro corrente registra questi valori ma non esegue la verifica della firma crittografica durante l'installazione. Il solo campo della firma non costituisce la prova di un editore verificato.

Inserimento del modulo e funzioni AI

definePlugin è un helper di identità tipizzato per un modulo con un manifest e activate(ctx). L'attivazione potrebbe restituire una funzione di pulizia e il modulo potrebbe esporre deactivate. L'SDK descrive questi hook del ciclo di vita; la loro disponibilità dipende dal bootstrap del lavoratore concreto, come descritto di seguito.

Le funzioni dell'assistente vengono esportate tramite un file aiTools documentazione; il contributo entry nomina la funzione in quel record. Il contributo di uno strumento include la sua descrizione, lo schema dei parametri JSON e read o write tipo. Uno strumento di scrittura viene instradato attraverso una richiesta di approvazione umana nell'assistente incorporato; un bridge esterno riceve solo gli strumenti di plug-in di lettura consentiti.

Raggruppa la voce lavoratore come modulo autonomo. L'host lavoratore legge l'origine della voce e la importa tramite un modulo BLOB. Trattare le importazioni relative di pacchetti o file come un problema di integrazione separato piuttosto che presupporre un caricatore di filesystem in stile Node.

API controllate dalle funzionalità

L'SDK riconosce drawing:read, drawing:write, toolpath:read, storage, ui:panel, e ai:tool. La scrittura del disegno implica la lettura del disegno. L'host generale ricava le funzionalità effettive dal manifest installato e le controlla su RPC; l'applicazione di incorporamento decide quali manifest e superfici è disposta a caricare.

Il contesto SDK può esporre letture di disegni e patch Aggiungi/Aggiorna/RimuoviEntità, letture di programmi/percorsi strumenti, archiviazione di valori-chiave di stringhe private e registrazione dichiarativa di comandi/pannelli. I servizi mancanti sono assenti dal contesto. Utilizzo delle chiavi di archiviazione plugin:<id>: spazi dei nomi e rifiutare chiavi assolute o parent-traversal.

Il caricatore dell'assistente di produzione supporta solo ai:tool. Un manifest che richiede funzionalità di disegno, percorso utensile, archiviazione o interfaccia utente viene ignorato con un avviso. Non concede silenziosamente tali superfici né carica le loro capacità più ampie nell'host dell'assistente.

Caricamento e ciclo di vita del lavoratore

Per abilitare un interno dell'assistente, posizionalo plugin.json e voce raggruppata sotto <dataDir>/plugins/<id>/ ed elencare l'identificatore in <dataDir>/plugins/enabled.json, un array JSON. L'identificatore della cartella deve corrispondere al manifest. Il caricamento avviene mentre viene creato il servizio plugin dell'assistente; un plug-in non riuscito registra un avviso senza arrestare l'applicazione.

L'host generale supporta gli stati installato, abilitato, disabilitato e in errore, con operazioni di installazione, abilitazione, disabilitazione, disinstallazione, chiamata di comando, chiamata dello strumento AI e configurazione. Enable crea un lavoratore e completa l'handshake di bootstrap; disabilitarlo lo smonta. La disinstallazione rimuove lo stato del registro e lo spazio dei nomi di archiviazione tramite le dipendenze inserite dell'host.

I lavoratori iniziano con un ambiente vuoto e il bootstrap rimuove le API globali selezionate. Si tratta di una restrizione a livello di applicazione, non di una sandbox del sistema operativo: i Bun globali rimangono raggiungibili. Abilita solo il codice di estensione appropriato per l'esecuzione all'interno dei limiti del processo dell'applicazione.

Limiti di tempo di esecuzione

L'host definisce un'impostazione di memoria di lavoro di 256 MB, un limite di velocità di 2,000 chiamate al minuto e al massimo 64 chiamate in sospeso per plug-in. Il comportamento del timeout del comando viene fornito dall'host di incorporamento; l'assistente caricatore utilizza 10 secondi. Le chiamate agli strumenti AI hanno una scadenza separata di 30 secondi. Timeout, strumenti assenti, arresti anomali del lavoratore, errori di funzionalità ed esaurimento della coda restituiscono errori host digitati o risultati leggibili dello strumento.

Il monitoraggio degli arresti anomali blocca un plug-in dopo tre arresti anomali in dieci minuti. Questi controlli host vincolano il comportamento cooperativo dell'RPC e del ciclo di vita; l'impostazione della memoria passata a Bun non sostituisce un eseguibile isolato separatamente.

I limiti sono definiti nell'art apps/sidecar/src/plugins/host.ts: CALL_TIMEOUT_MS (5,000 ms per chiamata ordinaria), AI_TOOL_TIMEOUT_MS (30,000 ms per chiamata allo strumento dell'assistente), MEMORY_LIMIT_MB (256), CALL_RATE_LIMIT (2,000 chiamate per plug-in al minuto), MAX_PENDING_CALLS (64) e CRASH_LIMIT (3 si arresta in modo anomalo entro 10 minuto CRASH_WINDOW_MS, dopodiché il plugin verrà disabilitato automaticamente).

Installazione ZIP e API plug-in del browser

POST /api/plugins/install accetta un archivio multiparte. L'installatore legge manifest.json dalla radice ZIP o da una singola directory di livello superiore, convalida il manifest del plug-in del browser e memorizza i suoi file in <dataDir>/storage/plugins/<id>/. Il browser manifesta l'utilizzo main, engines.cadcame un elenco di autorizzazioni del browser, anziché l'SDK entry, apiVersione capacità.

GET /api/plugins elenca le voci installate; dettaglio, abilitazione/disabilitazione, disinstallazione, aggiornamento e configurazione degli endpoint operano sul relativo registro. /api/plugins/bootstrap.js?id=… serve JavaScript; /api/plugins/serve?id=…&file=… serve file plugin. Si tratta di operazioni di testo/binarie con i tipi di contenuto dichiarati.

Il browser del registro locale legge i metadati del pacchetto in <dataDir>/plugins-registry. È un catalogo manifest locale. L'installazione dell'archivio, l'elenco del registro e il caricamento dell'estensione dell'assistente lavoratore sono operazioni separate.

Aiutanti dell'SDK

effectiveCapabilities, hasCapability, e missingCapabilities ispezione della capacità di supporto. requireCapability solleva un errore di capacità quando una superficie di contesto richiesta è assente. storageNamespace e storageKey creare chiavi con spazi dei nomi; parseManifest convalida JSON; manifestKey formatta una coppia ID/versione.

Punti di ingresso dell'implementazione

packages/plugin-sdk/src possiede il manifesto pubblico, la funzionalità, il contesto, il plug-in e le API test-host. apps/sidecar/src/plugins/host.ts possiede il ciclo di vita del lavoratore e l'RPC; services/ai-plugins.ts possiede il caricamento del file di consenso e la registrazione dell'assistente. L'installazione del browser è implementata in http/plugins.ts e repos/plugin-registry-repo.ts.

Revisione dell'estensione

Mantieni accurati gli schemi di contributo, restituisci piccoli risultati strutturati e dichiara onestamente il comportamento di lettura/scrittura. Test rispetto all'effettivo host di incorporamento: la disponibilità dell'SDK non stabilisce l'accesso in produzione a disegni, archiviazione, interfaccia utente o registri di post. IL plugin nikcli autonomo è un client di azioni HTTP e ha uno scopo diverso dal codice caricato in un lavoratore.

Completa il modulo dell'assistente campione

Il plugin.json esempio sopra i nomi aiTools.lookup. Questo è il corrispondente autonomo index.js. Il codice del modulo di livello superiore viene eseguito quando viene importato; la funzione di ricerca dichiarata viene eseguita quando viene chiamata.

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."
    ]
  })
}

Queste sono istruzioni per il negozio di campioni, non uno standard di ispezione integrato. Sostituiscili con il contenuto effettivamente di proprietà della tua estensione. Per ammettere questo modulo alla successiva inizializzazione del servizio, il file abilitato è:

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

Il catalogo in esecuzione espone un identificatore derivato dagli ID del plug-in e dello strumento. Scoprilo attraverso GET /api/ai/actions; non dare per scontato che il registro ZIP del browser sia l'elenco abilitato dell'assistente. Ricaricare l'applicazione/servizio è il modo affidabile per rileggere il file di consenso alla produzione; nessun watcher del filesystem è configurato da questo caricatore.

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

Schemi argomentativi e limiti del ciclo di vita

L'ID locale di uno strumento deve iniziare con una lettera minuscola e contenere lettere minuscole, cifre o trattini bassi, con un massimo di 40 caratteri. Le descrizioni contengono 10–2,000 caratteri. Il contributo deve nominare una funzione di voce non vuota e uno schema di parametri JSON. L'adattatore dell'applicazione convalida gli argomenti prima di chiamare il lavoratore; un rifiuto può restituire un risultato strutturato ok: false risultato senza terminare il plugin.

L'SDK pubblico descrive activate, pulizia e deactivate punti di ingresso. I gestori RPC di attivazione/disattivazione del bootstrap del lavoratore Bun corrente riconoscono i messaggi del ciclo di vita ma non richiamano le funzioni del modulo esportato. Le estensioni dell'assistente dovrebbero quindi inserire il loro comportamento richiamabile aiTools ed evitare di dipendere dai callback di attivazione dell'SDK per la configurazione o la pulizia.

Il bootstrap del lavoratore generale contiene anche gestori di disegno/percorso utensile/interfaccia utente segnaposto. Non costituiscono disegni di produzione o integrazione dell'interfaccia utente. La restrizione dell'assistente caricatore a ai:tool impedisce che queste superfici non supportate vengano pubblicizzate come accesso ad applicazioni funzionanti.

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

Diagnosticare un'estensione mancante o guasta

  • Nessuno strumento nel catalogo: controlla il JSON abilitato valido, la corrispondenza esatta della cartella/ID manifest, la decodifica manifest e il file ai:tool capacità. Leggi l'avviso di avvio per un plugin saltato.
  • Manifest richiede superfici non supportate: rimuovere le funzionalità non supportate solo se il codice non ne ha veramente bisogno. Un plugin assistente non può acquisire l'accesso in scrittura e disegno semplicemente dichiarandolo.
  • NoSuchTool: fare contributes.aiTools[].entry corrisponde a una funzione esportata in aiTools. L'ID del contributo e il nome della voce hanno scopi diversi.
  • Errore di convalida dell'argomento: controllare lo schema JSON e i campi obbligatori. I valori errati vengono rifiutati prima dell'esecuzione, come dimostrato dai test di integrazione dei lavoratori reali e delle applicazioni.
  • Timeout o stato di errore: indagare su blocchi di lavoro, promesse non risolte o arresti anomali; arresti anomali ripetuti guastano il plugin. Riabilitare solo dopo aver corretto la causa.
  • Il programma di installazione ZIP rifiuta il manifest: utilizzare il plug-in del browser manifest.json contratto per quell'endpoint. Rinominare un lavoratore plugin.json non traduce i suoi campi.

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

Riferimento manifesto

Ogni plugin viene fornito con a plugin.json. parseManifest(json) lo analizza e lo convalida in un unico passaggio, trasformando un errore di sintassi JSON nello stesso canale di errore di un errore di schema, quindi una schermata di installazione deve gestire solo un tipo di errore.

CampoRegola
idIdentificatore DNS inverso: 3–128 caratteri, lettere minuscole, cifre e trattini in almeno due etichette separate da punti, ad esempio com.example.gear-generator.
nameNome visualizzato non vuoto.
versionmajor.minor.patch, facoltativamente seguito da un suffisso pre-release come -beta.1.
apiVersionDeve essere la stringa "1"; è l'unica versione API del plugin.
entryPercorso del modulo ES relativo alla cartella del plugin.
capabilitiesMatrice di stringhe di capacità (vedi sotto). Qualunque cosa al di fuori dell'elenco chiuso è un errore manifesto.
contributesOpzionale commands, panels, postProcessors, operations, e aiTools.
signatureFirma Ed25519 sull'hash dell'archivio; richiesto per le installazioni in pacchetto.
devtrue segnala un plugin di sviluppo locale: i controlli della firma vengono saltati e l'interfaccia utente mostra un badge.
ContributoCampi
commands[]id, title, facoltativo keybinding ad esempio Mod+Shift+G.
panels[]id, title, entry: una funzione esportata che restituisce l'albero dichiarativo del pannello. Non c'è mai accesso DOM.
postProcessors[]id, title, entry: un esportato (program, options) => string funzione.
operations[]id, title, entry: un record esportato con un parametro schema e un puro generate(params, geometry, tool, stock) che restituisce un percorso utensile.
aiTools[]id corrispondenza ^[a-z][a-z0-9_]{0,39}$, description (10–2,000 caratteri; il modello lo legge), kind read o write, parameters (Schema JSON degli argomenti), e entry.

Aiutanti: manifestKey(m) ritorna id@version; requestedCapabilities(m) restituisce l'elenco delle funzionalità ordinato per la visualizzazione; isDevPlugin(m) e isSigned(m) rispondere alle domande sulla politica di installazione.

Funzionalità e contesto del plugin

Le funzionalità sono l'unica cosa attraverso cui un plugin può agire. Il manifest dichiara ciò di cui ha bisogno il plugin, l'utente concede un sottoinsieme e l'host crea un contesto che contiene esattamente le superfici concesse. Ogni superficie è facoltativa nel tipo, quindi una funzionalità mancante è visibile sia in fase di compilazione che in fase di esecuzione invece di diventare un'operazione silenziosa.

CapacitàSuperficie del contestoConsenteImplica
drawing:readctx.drawing.read()Leggi il disegno aperto (o undefined quando nessuno è aperto).—
drawing:writectx.drawing.patch(patch)Fare domanda a AddEntity, UpdateEntity, o RemoveEntity toppe; restituisce la nuova revisione.drawing:read
toolpath:readctx.toolpath.list(), .program(id)Leggi percorsi utensile e programmi G-code.—
storagectx.storageStringhe di valori-chiave con spazio dei nomi: get, set, delete, keys.—
ui:panelctx.uiregisterCommand e registerPanel (solo dichiarativo).—
ai:toolnessuno (contributi manifesti)Offri strumenti all'assistente e, in sola lettura, a un nikcli autonomo.—
effectiveCapabilities(granted) risolve le implicazioni, quindi un plugin che dichiara drawing:write non ha bisogno di dichiararsi drawing:read. Anche ogni contesto ha pluginId, il risolto capabilities, e a log con debug, info, warn, e error; l'host prefissa le righe di registro con l'ID del plug-in. Utilizzo requireCapability(ctx, capability, surface) ottenere una superficie o fallire con a CapabilityError che nomina la sovvenzione mancante.

Testare un plugin con l'host di test dell'SDK

createTestHost crea un host in memoria con le stesse regole di funzionalità del sidecar. Nel contesto vengono visualizzate solo le funzionalità concesse ed è possibile fornire un disegno, percorsi utensile e programmi. L'esempio seguente è completo e funziona con bun nello spazio di lavoro.
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) inizia da un manifest minimo valido (com.example.test-plugin, versione 1.0.0, API 1, voce index.js, nessuna capacità).
  • granted per impostazione predefinita è tutto ciò che il manifest richiede. Passa un elenco più piccolo per testare i percorsi negati.
  • host.storage è un'istantanea scattata al momento della creazione dell'host. Leggi i valori in tempo reale host.context.storage.
  • host.on(commandId, handler) e host.invoke(commandId, args) esercitare i comandi registrati.
  • runPostProcessor esegue un post contribuito contro un programma e keyOf(pluginId, key) mostra la chiave di archiviazione con spazio dei nomi in modo che i test possano affermare l'isolamento.

Come l'applicazione carica i plugin dell'assistente

  1. All'avvio il sidecar legge <data dir>/plugins/enabled.json, un array JSON di ID plug-in. Non viene caricato nulla a meno che non sia elencato lì.
  2. Per ogni ID si legge <data dir>/plugins/<id>/plugin.json e lo analizza. Il nome della cartella deve corrispondere al manifest id.
  3. Soltanto ai:tool è cablato in questo host. Un manifest che richiede l'accesso al disegno, al percorso utensile, all'archiviazione o all'interfaccia utente viene ignorato con un avviso anziché caricato con superfici che non farebbero nulla silenziosamente.
  4. Il plug-in viene installato in un host di lavoro e abilitato. Un plugin che non riesce a caricare i log Plugin <id> was not loaded: ... e non interrompe mai l'avvio dell'applicazione.
  5. Ogni strumento fornito viene offerto all'assistente come plugin_<slug of plugin id>_<tool id>, al massimo 80 caratteri, dove lo slug minuscolo l'ID e sostituisce gli altri caratteri con trattini bassi. com.example.bolt-circle con strumento bolt_circle diventa plugin_com_example_bolt_circle_bolt_circle.
  6. Gli argomenti vengono convalidati rispetto allo schema JSON dello strumento prima che il lavoratore li veda. Se lo schema supera i 20,000 caratteri, non è uno schema di oggetti o non può essere convertito, l'host accetta qualsiasi oggetto e il plug-in deve essere convalidato.
  7. Viene restituita una chiamata che viene interrotta, va in timeout o rileva che il plug-in non è più disponibile { ok: false, error } (testo di errore fino a 500 caratteri) all'assistente invece di interrompere la svolta. UN write lo strumento viene confermato con la persona prima di ogni chiamata, qualunque siano i suoi argomenti.

Non una sandbox del sistema operativo

Il lavoratore non ha processi, fetch o handle di file propri, ma i globali Bun rimangono raggiungibili al suo interno. Abilita solo i plugin di cui ti fidi del codice e mantieni gli strumenti di scrittura al minimo.

Identificatore e regole di versione, testati

ValoreValido?Regola
com.example.gear-generatorsÌReverse-DNS: segmenti minuscoli di lettere, cifre e -, almeno due, 3–128 caratteri
a.bsÌSono sufficienti due segmenti di una lettera
gearnoHa bisogno di almeno un punto
Com.Example.xnoNon è consentito utilizzare lettere maiuscole
com.example_x.ynoIl carattere di sottolineatura non è consentito
VersioneValido?
1.0.0sÌ
1.2.3-beta.1sÌ
1.0no: tre numeri richiesti
v1.0.0no: nessun prefisso
apiVersion deve essere la stringa "1", l'unica API plugin principale esistente. Gli ID degli strumenti AI devono corrispondere ^[a-z][a-z0-9_]{0,39}$ e vengono mostrati all'assistente come plugin_<plugin>_<id>; le descrizioni devono contenere 10–2,000 caratteri perché il modello le legge.

Tipi di contributo

ChiaveCampiScopo
commandsid, title, facoltativo keybinding (e.g. Mod+Shift+G)Comandi nella tavolozza
panelsid, title, entryUn pannello reso da un albero dichiarativo restituito dalla funzione denominata; nessun accesso DOM
postProcessorsid, title, entryA (program, options) => string post registrato in fase di esecuzione
operationsid, title, entryUn esportato { schema, generate } documentazione
aiToolsid, description, kind leggere|scrivere, parameters (Schema JSON), entryStrumenti offerti all'assistente; uno strumento di scrittura viene confermato prima di ogni chiamata

Altri campi manifest: signature (Ed25519 sull'hash dell'archivio, richiesto per le installazioni di pacchetti) e dev (plugin di sviluppo locale: salta i controlli di firma e mostra un badge).

Risoluzione delle capacità, testata

Implicazioni, sovvenzioni mancanti, chiavi di archiviazione
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
Superficie sul contestoPresente quando concessoMetodi
drawingdrawing:read o drawing:writeread() → disegno o undefined; patch(p) → nuova revisione, con AddEntity, UpdateEntity, RemoveEntity patch
toolpathtoolpath:readlist(), program(toolpathId)
storagestorageget, set, delete, keys; valori di stringa
uiui:panelregisterCommand, registerPanel
logsempredebug, info, warn, error

Nel 3D

Pacchetti di plugin nell'area di lavoro Trascina per orbitare · scorri per ingrandire · trascina con il tasto destro per eseguire la panoramica
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.