@termix-ssh/plugin-sdk 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE +202 -0
  3. package/README.md +3 -2
  4. package/cli/commands/build.mjs +37 -12
  5. package/cli/commands/dev.mjs +1 -1
  6. package/cli/commands/openapi.mjs +39 -0
  7. package/cli/commands/validate.mjs +67 -0
  8. package/cli/index.mjs +3 -0
  9. package/cli/lib/css-classes.mjs +37 -0
  10. package/dist/docs.d.ts +28 -0
  11. package/dist/docs.js +39 -0
  12. package/dist/frontend.d.ts +17 -0
  13. package/dist/frontend.js +8 -0
  14. package/dist/host/{chunk-LKYAVM4T.js → chunk-F5RZH6HE.js} +13 -2
  15. package/dist/host/{chunk-XKMRKQFN.js → chunk-O2OHNVFX.js} +1 -1
  16. package/dist/host/{chunk-SS43S6VI.js → chunk-RU4E5XLY.js} +453 -287
  17. package/dist/host/core-classes.json +1 -0
  18. package/dist/host/{linked-server-DW5P3NLZ.js → linked-server-4VWYYABP.js} +1 -1
  19. package/dist/host/{rbac-api-JKISXZEP.js → rbac-api-WIJVZH3M.js} +1 -1
  20. package/dist/host/testing-host.js +3 -3
  21. package/dist/host/types/src/ui/api/plugins-api.d.ts +6 -0
  22. package/dist/host/types/src/ui/components/docs-link.d.ts +25 -0
  23. package/dist/host/types/src/ui/components/panel-layout.d.ts +3 -1
  24. package/dist/host/types/src/ui/lib/docs.d.ts +10 -0
  25. package/dist/host/types/src/ui/main-axios.d.ts +1 -0
  26. package/dist/host/types/src/ui/plugin-host/sdk-ui.d.ts +1 -0
  27. package/dist/host/ui.js +6 -3
  28. package/dist/host/{use-permissions-5CZEA3P4.js → use-permissions-KIQCJ35M.js} +1 -1
  29. package/dist/manifest.d.ts +7 -1
  30. package/dist/manifest.js +48 -1
  31. package/dist/openapi.d.ts +43 -0
  32. package/dist/openapi.js +60 -0
  33. package/dist/testing/setup.js +1 -0
  34. package/package.json +10 -1
  35. package/schema/manifest.schema.json +21 -0
@@ -0,0 +1 @@
1
+ ["!max-h-[60px]","!mt-0","!opacity-100","!running","**:data-[slot=kbd]:isolate","**:data-[slot=kbd]:relative","**:data-[slot=kbd]:rounded-none","**:data-[slot=kbd]:z-50","*:[a]:hover:text-foreground","*:[a]:underline","*:[a]:underline-offset-3","*:[img:first-child]:rounded-none","*:[img:last-child]:rounded-none","*:[span]:last:flex","*:[span]:last:gap-2","*:[span]:last:items-center","*:data-[slot=alert-description]:text-destructive/90","*:data-[slot=select-value]:flex","*:data-[slot=select-value]:gap-2","*:data-[slot=select-value]:items-center","*:data-[slot=select-value]:line-clamp-1","-bottom-1.5","-left-1.5","-left-px","-mt-0.5","-mt-1","-mt-[3.5px]","-mx-1","-mx-3","-my-1","-right-1.5","-right-2","-right-2.5","-right-px","-top-1.5","-top-[3px]","-translate-x-1/2","-translate-x-px","-translate-y-1/2","2xl:flex","2xl:grid-cols-4","3","@container","@container/card-header","@md:grid-cols-2","[&::-webkit-inner-spin-button]:appearance-none","[&::-webkit-outer-spin-button]:appearance-none","[&::-webkit-scrollbar]:hidden","[&>*:last-child]:border-b-0","[&>*]:h-full","[&>*]:shrink-0","[&>button]:hidden","[&>span:last-child]:truncate","[&>svg]:pointer-events-none","[&>svg]:shrink-0","[&>svg]:size-3","[&>svg]:size-4","[&>svg]:text-current","[&>svg]:text-sidebar-accent-foreground","[&>svg]:translate-y-0.5","[&_[cmdk-group-heading]]:font-medium","[&_[cmdk-group-heading]]:px-2","[&_[cmdk-group-heading]]:py-1.5","[&_[cmdk-group-heading]]:text-muted-foreground","[&_[cmdk-group-heading]]:text-xs","[&_[cmdk-list-sizer]]:flex","[&_[cmdk-list-sizer]]:flex-col","[&_p]:leading-relaxed","[&_svg:not([class*='size-'])]:size-3","[&_svg:not([class*='size-'])]:size-3.5","[&_svg:not([class*='size-'])]:size-4","[&_svg:not([class*='text-'])]:text-muted-foreground","[&_svg]:pointer-events-none","[&_svg]:shrink-0","[&_svg]:size-2.5","[&_svg]:size-3","[&_svg]:size-3.5","[&_svg]:size-4","[.border-b]:pb-4","[.gap-4>&]:border-0","[.gap-4>&]:py-0","[@media(hover:none)]:flex","[[data-side=left][data-collapsible=offcanvas]_&]:-right-2","[[data-side=left][data-state=collapsed]_&]:cursor-e-resize","[[data-side=right][data-collapsible=offcanvas]_&]:-left-2","[[data-side=right][data-state=collapsed]_&]:cursor-w-resize","[a&]:hover:bg-accent","[a&]:hover:bg-destructive/90","[a&]:hover:bg-primary/90","[a&]:hover:bg-secondary/90","[a&]:hover:text-accent-foreground","[a]:hover:bg-primary/80","[animation-duration:900ms]","[appearance:textfield]","[backend:stderr]","[clip-path:inset(0px_-4px_-4px_-4px)]","[contain:content]","[op:http_server_port_conflict]","[scrollbar-width:none]","absolute","accent-[var(--accent-brand)]","active:bg-muted","active:bg-muted/60","active:bg-sidebar-accent","active:cursor-grabbing","active:not-aria-[haspopup]:translate-y-px","active:text-sidebar-accent-foreground","after:-inset-2","after:absolute","after:inset-y-0","after:start-1/2","after:w-[2px]","animate-pulse","animate-spin","appearance-none","aria-disabled:opacity-50","aria-disabled:pointer-events-none","aria-expanded:bg-muted","aria-expanded:bg-secondary","aria-expanded:opacity-100","aria-expanded:text-foreground","aria-expanded:text-secondary-foreground","aria-invalid:border-destructive","aria-invalid:ring-1","aria-invalid:ring-destructive/20","aspect-square","aspect-video","auto-cols-fr","auto-rows-fr","auto-rows-min","before:-top-0.5","before:-top-px","before:absolute","before:bg-accent-brand","before:h-0.5","before:inset-x-0","before:inset-x-1","before:z-20","bg-accent-brand","bg-accent-brand/10","bg-accent-brand/15","bg-accent-brand/20","bg-accent-brand/25","bg-accent-brand/40","bg-accent-brand/5","bg-accent-brand/60","bg-accent-brand/70","bg-accent-brand/[0.07]","bg-accent/30","bg-accent/60","bg-background","bg-background/50","bg-background/60","bg-background/70","bg-background/85","bg-background/90","bg-black","bg-black/10","bg-blue-500/20","bg-border","bg-border/60","bg-canvas","bg-card","bg-clip-padding","bg-destructive","bg-destructive/10","bg-destructive/15","bg-destructive/5","bg-destructive/70","bg-emerald-500","bg-emerald-500/10","bg-foreground","bg-green-500","bg-green-500/10","bg-green-500/70","bg-input","bg-muted","bg-muted-foreground","bg-muted-foreground/15","bg-muted-foreground/20","bg-muted-foreground/25","bg-muted-foreground/30","bg-muted-foreground/40","bg-muted-foreground/50","bg-muted/10","bg-muted/15","bg-muted/20","bg-muted/30","bg-muted/40","bg-muted/50","bg-muted/60","bg-muted/70","bg-muted/[0.08]","bg-overlay","bg-popover","bg-primary","bg-red-400","bg-red-500","bg-red-500/10","bg-red-500/40","bg-secondary","bg-sidebar","bg-sidebar-border","bg-surface","bg-surface-dim/40","bg-surface-dim/60","bg-surface/40","bg-transparent","bg-warning","bg-warning/10","bg-warning/20","bg-warning/40","bg-warning/5","bg-warning/70","block","blur","border","border-0","border-2","border-accent-brand","border-accent-brand/20","border-accent-brand/30","border-accent-brand/40","border-accent-brand/50","border-accent-brand/60","border-accent-brand/70","border-b","border-b-2","border-b-accent-brand","border-border","border-border/30","border-border/40","border-border/50","border-border/60","border-current","border-dashed","border-destructive","border-destructive/20","border-destructive/30","border-destructive/40","border-destructive/50","border-emerald-500/40","border-foreground","border-foreground/10","border-green-500/30","border-green-500/40","border-input","border-l","border-l-2","border-l-transparent","border-muted-foreground/30","border-primary","border-r","border-red-500/40","border-sidebar-border","border-t","border-t-0","border-t-border/40","border-t-muted-foreground/70","border-t-transparent","border-transparent","border-warning/30","border-warning/40","border-x","border-y","bottom-0","bottom-4","break-all","break-words","capitalize","catppuccin","col-span-2","col-start-2","collapse","columns-2","columns-3","com","container","contents","cursor-col-resize","cursor-default","cursor-grab","cursor-not-allowed","cursor-ns-resize","cursor-pointer","cursor-row-resize","dark","dark:aria-invalid:border-destructive/50","dark:aria-invalid:ring-destructive/40","dark:bg-destructive/20","dark:bg-destructive/60","dark:bg-input/30","dark:bg-transparent","dark:border-accent-brand/40","dark:border-input","dark:data-[state=active]:bg-input/30","dark:data-[state=active]:border-input","dark:data-[state=active]:text-foreground","dark:data-[state=checked]:bg-primary","dark:data-[state=checked]:bg-primary-foreground","dark:data-[state=unchecked]:bg-foreground","dark:data-[state=unchecked]:bg-input/80","dark:data-[variant=destructive]:focus:bg-destructive/20","dark:disabled:bg-input/80","dark:focus-visible:ring-destructive/40","dark:hover:bg-accent-brand/10","dark:hover:bg-destructive/10","dark:hover:bg-destructive/30","dark:hover:bg-input/50","dark:hover:bg-muted/50","dark:text-muted-foreground","data-[disabled=true]:opacity-50","data-[disabled=true]:pointer-events-none","data-[disabled]:opacity-50","data-[disabled]:pointer-events-none","data-[orientation=horizontal]:h-1.5","data-[orientation=horizontal]:h-full","data-[orientation=horizontal]:h-px","data-[orientation=horizontal]:w-full","data-[orientation=vertical]:flex-col","data-[orientation=vertical]:h-full","data-[orientation=vertical]:min-h-44","data-[orientation=vertical]:self-center","data-[orientation=vertical]:self-stretch","data-[orientation=vertical]:w-1.5","data-[orientation=vertical]:w-auto","data-[orientation=vertical]:w-full","data-[orientation=vertical]:w-px","data-[placeholder]:text-muted-foreground","data-[selected=true]:bg-muted","data-[selected=true]:text-accent-foreground","data-[side=bottom]:border-t","data-[side=bottom]:bottom-0","data-[side=bottom]:data-closed:slide-out-to-bottom-10","data-[side=bottom]:data-open:slide-in-from-bottom-10","data-[side=bottom]:h-auto","data-[side=bottom]:inset-x-0","data-[side=bottom]:slide-in-from-top-2","data-[side=bottom]:translate-y-1","data-[side=left]:-translate-x-1","data-[side=left]:border-r","data-[side=left]:data-closed:slide-out-to-left-10","data-[side=left]:data-open:slide-in-from-left-10","data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]","data-[side=left]:h-full","data-[side=left]:inset-y-0","data-[side=left]:left-0","data-[side=left]:slide-in-from-right-2","data-[side=left]:sm:max-w-sm","data-[side=left]:w-3/4","data-[side=right]:border-l","data-[side=right]:data-closed:slide-out-to-right-10","data-[side=right]:data-open:slide-in-from-right-10","data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]","data-[side=right]:h-full","data-[side=right]:inset-y-0","data-[side=right]:right-0","data-[side=right]:slide-in-from-left-2","data-[side=right]:sm:max-w-sm","data-[side=right]:translate-x-1","data-[side=right]:w-3/4","data-[side=top]:-translate-y-1","data-[side=top]:border-b","data-[side=top]:data-closed:slide-out-to-top-10","data-[side=top]:data-open:slide-in-from-top-10","data-[side=top]:h-auto","data-[side=top]:inset-x-0","data-[side=top]:slide-in-from-bottom-2","data-[side=top]:top-0","data-[size=default]:h-9","data-[size=md]:text-xs","data-[size=sm]:gap-2","data-[size=sm]:h-8","data-[size=sm]:has-data-[slot=card-footer]:pb-0","data-[size=sm]:py-3","data-[size=sm]:text-xs","data-[state=active]:animate-in","data-[state=active]:bg-background","data-[state=active]:fade-in-0","data-[state=active]:shadow-sm","data-[state=checked]:bg-primary","data-[state=checked]:border-primary","data-[state=checked]:text-primary-foreground","data-[state=checked]:translate-x-[calc(100%-2px)]","data-[state=closed]:animate-out","data-[state=closed]:fade-out-0","data-[state=closed]:overflow-hidden","data-[state=closed]:zoom-out-95","data-[state=delayed-open]:animate-in","data-[state=delayed-open]:fade-in-0","data-[state=delayed-open]:zoom-in-95","data-[state=inactive]:animate-out","data-[state=inactive]:fade-out-0","data-[state=open]:animate-in","data-[state=open]:fade-in-0","data-[state=open]:zoom-in-95","data-[state=unchecked]:bg-input","data-[state=unchecked]:translate-x-0","data-[variant=destructive]:*:[svg]:text-destructive","data-[variant=destructive]:focus:bg-destructive/10","data-[variant=destructive]:focus:text-destructive","data-[variant=destructive]:text-destructive","data-active:bg-sidebar-accent","data-active:font-medium","data-active:text-sidebar-accent-foreground","data-closed:animate-out","data-closed:fade-out-0","data-closed:zoom-out-95","data-disabled:opacity-50","data-disabled:pointer-events-none","data-inset:pl-7","data-open:animate-in","data-open:bg-accent","data-open:fade-in-0","data-open:hover:bg-sidebar-accent","data-open:hover:text-sidebar-accent-foreground","data-open:text-accent-foreground","data-open:zoom-in-95","delay-75","disabled:bg-input/50","disabled:cursor-not-allowed","disabled:opacity-30","disabled:opacity-40","disabled:opacity-50","disabled:opacity-60","disabled:pointer-events-none","divide-border","divide-x","divide-y","dracula","drop-shadow","duration-100","duration-150","duration-200","duration-500","duration-[450ms]","ease-in-out","ease-linear","ease-out","enabled:hover:text-accent-brand","fade-in","file:bg-transparent","file:border-0","file:font-medium","file:h-6","file:inline-flex","file:text-foreground","file:text-xs","fill-foreground","filter","fixed","flex","flex-1","flex-col","flex-col-reverse","flex-row","flex-wrap","focus-visible:border-destructive/40","focus-visible:border-ring","focus-visible:outline-1","focus-visible:outline-hidden","focus-visible:outline-none","focus-visible:outline-ring","focus-visible:relative","focus-visible:ring-0","focus-visible:ring-1","focus-visible:ring-2","focus-visible:ring-4","focus-visible:ring-[#f59145]/40","focus-visible:ring-[3px]","focus-visible:ring-destructive/20","focus-visible:ring-inset","focus-visible:ring-ring","focus-visible:ring-ring/50","focus-visible:z-10","focus-within:border-ring","focus-within:opacity-100","focus-within:ring-1","focus-within:ring-ring","focus-within:ring-ring/50","focus:**:text-accent-foreground","focus:bg-accent","focus:ring-1","focus:ring-ring","focus:text-accent-brand","focus:text-accent-foreground","focus:text-destructive","font-bold","font-heading","font-medium","font-mono","font-normal","font-semibold","fs-lg","fs-md","fs-sm","fs-xl","fs-xs","gap-0","gap-0.5","gap-1","gap-1.5","gap-2","gap-2.5","gap-3","gap-3.5","gap-4","gap-5","gap-6","gap-[1.75px]","gap-[10.5px]","gap-[2px]","gap-[3.5px]","gap-[3px]","gap-px","gap-x-2","gap-x-3","gap-x-4","gap-x-6","gap-y-0","gap-y-0.5","gap-y-1","gap-y-1.5","gap-y-2","gap-y-3","grid","grid-cols-1","grid-cols-2","grid-cols-3","grid-cols-4","grid-cols-6","grid-cols-8","grid-cols-[0_1fr]","grid-cols-[1fr_5rem]","grid-flow-col","group","group-[.toast]:!border-accent-brand/30","group-[.toast]:!border-destructive/30","group-[.toast]:!text-accent-brand","group-[.toast]:!text-destructive","group-[.toast]:bg-accent-brand","group-[.toast]:bg-muted","group-[.toast]:font-semibold","group-[.toast]:text-muted-foreground","group-[.toast]:text-white","group-[.toaster]:bg-card","group-[.toaster]:border-border","group-[.toaster]:font-mono","group-[.toaster]:rounded-none!","group-[.toaster]:shadow-lg","group-[.toaster]:text-card-foreground","group-data-[collapsible=icon]:-mt-8","group-data-[collapsible=icon]:hidden","group-data-[collapsible=icon]:opacity-0","group-data-[collapsible=icon]:overflow-hidden","group-data-[collapsible=icon]:p-0!","group-data-[collapsible=icon]:p-2!","group-data-[collapsible=icon]:size-8!","group-data-[collapsible=icon]:w-(--sidebar-width-icon)","group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]","group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]","group-data-[collapsible=offcanvas]:after:left-full","group-data-[collapsible=offcanvas]:translate-x-0","group-data-[collapsible=offcanvas]:w-0","group-data-[disabled=true]:opacity-50","group-data-[disabled=true]:pointer-events-none","group-data-[selected=true]:opacity-100","group-data-[side=left]:-right-4","group-data-[side=left]:border-r","group-data-[side=right]:border-l","group-data-[side=right]:left-0","group-data-[side=right]:rotate-180","group-data-[size=sm]/card:[.border-b]:pb-3","group-data-[size=sm]/card:p-3","group-data-[size=sm]/card:px-3","group-data-[size=sm]/card:text-sm","group-data-[variant=floating]:ring-1","group-data-[variant=floating]:ring-sidebar-border","group-data-[variant=floating]:rounded-none","group-data-[variant=floating]:shadow-sm","group-focus-within/folder:flex","group-focus-within/menu-item:opacity-100","group-focus-within/row:flex","group-focus/dropdown-menu-item:text-accent-foreground","group-has-data-[sidebar=menu-action]/menu-item:pr-8","group-hover/btn:bg-accent-brand/20","group-hover/btn:border-accent-brand/40","group-hover/folder:flex","group-hover/folder:opacity-100","group-hover/folder:text-muted-foreground/70","group-hover/menu-item:opacity-100","group-hover/open:text-accent-brand","group-hover/resize:bg-accent-brand/60","group-hover/row:flex","group-hover/row:text-muted-foreground/60","group-hover/tab:opacity-100","group-hover:bg-accent-brand/60","group-hover:hidden","group-hover:max-h-[60px]","group-hover:mt-0","group-hover:opacity-100","group-hover:text-muted-foreground/70","group/btn","group/card","group/dropdown-menu-item","group/folder","group/menu-item","group/open","group/resize","group/row","group/tab","grow","gruvbox","h-0","h-0.5","h-1","h-1.5","h-10","h-11","h-12","h-12.5","h-14","h-16","h-2.5","h-20","h-24","h-28","h-3","h-3.5","h-4","h-5","h-6","h-7","h-8","h-9","h-[1.15rem]","h-[136px]","h-[14px]","h-[2px]","h-[3px]","h-[46%]","h-[80vh]","h-[calc(100%-1px)]","h-[var(--radix-select-trigger-height)]","h-full","h-px","h-screen","h-svh","has-[>img:first-child]:pt-0","has-[>svg]:gap-x-3","has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr]","has-[[data-state=open]]:opacity-100","has-data-[icon=inline-end]:pr-1.5","has-data-[icon=inline-end]:pr-2","has-data-[icon=inline-start]:pl-1.5","has-data-[icon=inline-start]:pl-2","has-data-[slot=card-action]:grid-cols-[1fr_auto]","has-data-[slot=card-description]:grid-rows-[auto_auto]","has-data-[slot=card-footer]:pb-0","has-data-[slot=kbd]:pr-1.5","has-data-[variant=inset]:bg-sidebar","hidden","host-status-stripe","hover:after:bg-sidebar-border","hover:bg-accent","hover:bg-accent-brand","hover:bg-accent-brand/10","hover:bg-accent-brand/15","hover:bg-accent-brand/20","hover:bg-accent-brand/40","hover:bg-accent-brand/5","hover:bg-accent-brand/90","hover:bg-background/80","hover:bg-destructive/10","hover:bg-destructive/20","hover:bg-destructive/90","hover:bg-foreground/[0.03]","hover:bg-muted","hover:bg-muted-foreground/10","hover:bg-muted/20","hover:bg-muted/30","hover:bg-muted/40","hover:bg-muted/50","hover:bg-muted/60","hover:bg-secondary/80","hover:bg-sidebar-accent","hover:bg-surface","hover:border-accent-brand","hover:border-accent-brand/30","hover:border-accent-brand/40","hover:border-accent-brand/60","hover:border-destructive/40","hover:border-foreground/40","hover:border-muted-foreground","hover:border-ring/60","hover:group-data-[collapsible=offcanvas]:bg-sidebar","hover:opacity-100","hover:ring-4","hover:ring-[#f59145]/40","hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]","hover:text-accent-brand","hover:text-accent-brand/70","hover:text-accent-brand/80","hover:text-accent-foreground","hover:text-destructive","hover:text-destructive/80","hover:text-foreground","hover:text-muted-foreground","hover:text-primary","hover:text-sidebar-accent-foreground","hover:underline","in-data-[side=left]:cursor-w-resize","in-data-[side=right]:cursor-e-resize","inline","inline-block","inline-flex","inset-0","inset-x-0","inset-x-3","inset-y-0","invisible","isolate","italic","items-baseline","items-center","items-end","items-start","items-stretch","justify-between","justify-center","justify-end","justify-items-start","justify-self-end","justify-start","last:border-0","last:border-b-0","leading-[1.4]","leading-[1.7]","leading-[14px]","leading-none","leading-relaxed","leading-snug","leading-tight","left-0","left-1/2","left-2","left-2.5","left-[50%]","lg:block","lg:col-span-2","lg:flex","lg:grid-cols-1","lg:grid-cols-2","lg:grid-cols-3","lg:grid-cols-4","lg:grid-cols-5","lg:hidden","lg:inline","line-clamp-2","line-clamp-3","line-clamp-4","line-through","list-disc","list-inside","lowercase","ltr:-translate-x-1/2","lucide","m-2.5","m-auto","max-h-(--radix-dropdown-menu-content-available-height)","max-h-(--radix-popover-content-available-height)","max-h-(--radix-select-content-available-height)","max-h-0","max-h-24","max-h-40","max-h-56","max-h-64","max-h-96","max-h-[130px]","max-h-[300px]","max-h-[60px]","max-h-[60vh]","max-h-[80dvh]","max-w-(--skeleton-width)","max-w-0","max-w-2xl","max-w-32","max-w-3xl","max-w-40","max-w-48","max-w-5xl","max-w-72","max-w-96","max-w-[120px]","max-w-[160px]","max-w-[180px]","max-w-[200px]","max-w-[260px]","max-w-[380px]","max-w-[50%]","max-w-[560px]","max-w-[5rem]","max-w-[60%]","max-w-[calc(100%-2rem)]","max-w-full","max-w-lg","max-w-md","max-w-sm","max-w-xl","max-w-xs","mb-0.5","mb-1","mb-1.5","mb-2","mb-4","md:-mx-4","md:after:hidden","md:block","md:col-span-1","md:flex","md:flex-row","md:grid-cols-1","md:grid-cols-2","md:grid-cols-3","md:grid-cols-4","md:hidden","md:max-h-none","md:opacity-0","md:overflow-visible","md:overflow-y-auto","md:peer-data-[variant=inset]:m-2","md:peer-data-[variant=inset]:ml-0","md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2","md:peer-data-[variant=inset]:rounded-none","md:peer-data-[variant=inset]:shadow-sm","md:px-3.5","md:px-4","md:size-4","md:static","md:text-2xl","md:text-xs","md:w-44","md:w-48","md:w-56","md:w-60","md:w-72","min-[280px]:inline","min-h-0","min-h-11","min-h-16","min-h-24","min-h-48","min-h-8","min-h-9","min-h-[19px]","min-h-[220px]","min-h-[80px]","min-h-full","min-h-svh","min-w-(--radix-popover-trigger-width)","min-w-0","min-w-16","min-w-3","min-w-32","min-w-4","min-w-40","min-w-44","min-w-5","min-w-52","min-w-[14px]","min-w-[150px]","min-w-[160px]","min-w-[170px]","min-w-[180px]","min-w-[200px]","min-w-[8rem]","min-w-[96px]","min-w-[var(--radix-select-trigger-width)]","min-w-max","ml-0.5","ml-1","ml-1.5","ml-2","ml-3","ml-3.5","ml-4","ml-8","ml-[27px]","ml-auto","motion-context-enter","motion-fade-enter","motion-interactive","motion-meter","motion-panel-enter","motion-transfer-phase","motion-workspace-enter","motion-workspace-layout","mr-1","mr-1.5","mr-2","mt-0.5","mt-1","mt-1.5","mt-2","mt-2.5","mt-3","mt-4","mt-[1.75px]","mt-auto","mt-px","mx-0.5","mx-2","mx-3.5","mx-4","mx-auto","my-0.5","my-1","my-3","my-auto","nord","normal-case","not-data-[variant=destructive]:focus:**:text-accent-foreground","object-contain","one-dark","opacity-0","opacity-100","opacity-40","opacity-50","opacity-60","opacity-70","opacity-80","origin-(--radix-dropdown-menu-content-transform-origin)","origin-(--radix-popover-content-transform-origin)","origin-(--radix-select-content-transform-origin)","origin-(--radix-tooltip-content-transform-origin)","origin-center","outline","outline-hidden","outline-none","overflow-auto","overflow-hidden","overflow-x-auto","overflow-x-hidden","overflow-y-auto","overflow-y-hidden","overline","p-0","p-0.5","p-1","p-1.5","p-10","p-2","p-2.5","p-3","p-4","p-5","p-6","p-8","p-[3px]","p-px","paused","pb-0","pb-1","pb-1.5","pb-2","pb-3","pb-6","peer","peer-data-[size=default]/menu-button:top-1.5","peer-data-[size=lg]/menu-button:top-2.5","peer-data-[size=sm]/menu-button:top-1","peer-data-active/menu-button:text-sidebar-accent-foreground","peer-disabled:cursor-not-allowed","peer-disabled:opacity-50","peer-hover/menu-button:text-sidebar-accent-foreground","peer/menu-button","pl-1","pl-2","pl-2.5","pl-4","pl-5","pl-5.5","pl-6","pl-7","pl-8","pl-[7px]","pl-[8.75px]","pl-px","placeholder:text-muted-foreground","placeholder:text-muted-foreground/50","pointer-coarse:flex","pointer-coarse:opacity-100","pointer-events-none","pr-0.5","pr-1","pr-1.5","pr-12","pr-2","pr-2.5","pr-7","pr-8","pr-9","pr-[5.25px]","pr-[7px]","pt-0.5","pt-1","pt-1.5","pt-2","pt-2.5","pt-3","pt-4","pt-[15vh]","pt-[3.5px]","pt-[3px]","pt-[5.25px]","px-0","px-0.5","px-1","px-1.5","px-2","px-2.5","px-3","px-3.5","px-4","px-5","px-6","px-[3px]","py-0","py-0.5","py-1","py-1.5","py-10","py-12","py-2","py-2.5","py-3","py-4","py-6","py-8","py-[1px]","py-[2px]","py-[5px]","py-[7px]","py-px","relative","resize","resize-none","right-0","right-1","right-1.5","right-2","right-2.5","right-3","ring","ring-0","ring-1","ring-2","ring-accent-brand","ring-accent-brand/50","ring-border","ring-foreground/10","ring-inset","ring-offset-1","ring-offset-background","ring-sidebar-ring","ring-white/60","rotate-180","rotate-45","rotate-90","rounded","rounded-[inherit]","rounded-full","rounded-none","rounded-sm","row-span-2","row-start-1","rtl:-translate-x-1/2","running","safe-bottom","scale-110","scroll-my-1","scrollbar-none","select-none","self-end","self-start","self-stretch","settings-search-hit","shadow","shadow-2xl","shadow-[0_0_0_1px_hsl(var(--sidebar-border))]","shadow-lg","shadow-md","shadow-none","shadow-sm","shadow-xl","shadow-xs","shrink-0","size-1.5","size-10","size-12","size-2","size-2.5","size-3","size-3.5","size-4","size-5","size-6","size-6.5","size-7","size-8","size-9","size-[10.5px]","size-[17.5px]","size-[19px]","size-[22.75px]","size-full","size-px","sm:border-b-0","sm:border-r","sm:col-span-2","sm:flex","sm:flex-row","sm:grid-cols-12","sm:grid-cols-2","sm:grid-cols-3","sm:grid-cols-4","sm:items-center","sm:justify-end","sm:max-w-4xl","sm:max-w-lg","sm:max-w-sm","sm:mr-auto","sm:py-14","sm:text-left","solarized","space-y-0.5","space-y-1.5","space-y-2","sr-only","static","sticky","stroke-accent-brand","stroke-border","stroke-muted","supports-backdrop-filter:backdrop-blur-xs","tab-1","tab-2","table","tabular-nums","text-2xl","text-[10px]","text-[11px]","text-[12px]","text-[13px]","text-[9px]","text-accent-brand","text-accent-brand/50","text-accent-brand/80","text-accent-foreground","text-background","text-base","text-blue-400","text-blue-500","text-card-foreground","text-center","text-current","text-destructive","text-destructive/80","text-emerald-500","text-foreground","text-foreground-secondary","text-foreground/70","text-foreground/80","text-green-500","text-left","text-lg","text-muted-foreground","text-muted-foreground/0","text-muted-foreground/20","text-muted-foreground/30","text-muted-foreground/35","text-muted-foreground/40","text-muted-foreground/45","text-muted-foreground/50","text-muted-foreground/60","text-muted-foreground/70","text-muted-foreground/80","text-popover-foreground","text-primary","text-primary-foreground","text-red-500","text-right","text-secondary-foreground","text-sidebar-foreground","text-sidebar-foreground/70","text-sm","text-warning","text-white","text-xl","text-xs","text-xs/relaxed","thin-scrollbar","toast","toaster","tokyo-night","top-0","top-1","top-1.5","top-1/2","top-2","top-20","top-3","top-3.5","top-[50%]","touch-none","tracking-[0.35em]","tracking-[0.3em]","tracking-normal","tracking-tight","tracking-wide","tracking-wider","tracking-widest","transfer-progress-fill","transfer-progress-indeterminate","transfer-progress-stalled","transfer-progress-track","transform","transition","transition-[background-color,border-color,box-shadow,opacity]","transition-[color,background-color,border-color,opacity,box-shadow,transform]","transition-[color,box-shadow]","transition-[height,border-color]","transition-[height]","transition-[left,right,width]","transition-[left,top,width,height]","transition-[margin,opacity]","transition-[max-height,opacity,margin]","transition-[opacity,max-width]","transition-[opacity,width]","transition-[padding]","transition-[stroke-dasharray]","transition-[width,height,padding]","transition-[width]","transition-all","transition-colors","transition-none","transition-opacity","transition-shadow","transition-transform","translate-x-0.5","translate-x-4","translate-x-[-50%]","translate-x-px","translate-y-[-1px]","translate-y-[-50%]","translate-y-[calc(-50%_-_2px)]","truncate","underline","underline-offset-2","underline-offset-4","uppercase","visible","w-(--radix-dropdown-menu-trigger-width)","w-(--sidebar-width)","w-0","w-0.5","w-1","w-1.5","w-1/2","w-1/4","w-10","w-11","w-12","w-12.5","w-2","w-2.5","w-2/3","w-20","w-28","w-3","w-3/4","w-4","w-40","w-48","w-5","w-5/6","w-52","w-56","w-6","w-60","w-64","w-72","w-8","w-9","w-[120px]","w-[240px]","w-[2px]","w-[3px]","w-[46%]","w-[min(85vw,360px)]","w-[var(--radix-popover-trigger-width)]","w-auto","w-fit","w-full","w-max","w-px","w-screen","whitespace-normal","whitespace-nowrap","whitespace-pre","whitespace-pre-line","whitespace-pre-wrap","xl:block","xl:flex","xl:grid-cols-1","xl:grid-cols-2","xl:grid-cols-3","xl:grid-cols-4","z-0","z-10","z-20","z-30","z-40","z-50","z-500","z-[10000]","z-[100]","z-[200]","z-[60]","z-[9998]","z-[99999]"]
@@ -3,7 +3,7 @@ import {
3
3
  SYNC_CHANGED_EVENT,
4
4
  getLinkedSession,
5
5
  notifySyncChanged
6
- } from "./chunk-LKYAVM4T.js";
6
+ } from "./chunk-F5RZH6HE.js";
7
7
  import "./chunk-YC2MAKSP.js";
8
8
  export {
9
9
  SYNC_CHANGED_EVENT,
@@ -23,7 +23,7 @@ import {
23
23
  shareHost,
24
24
  updateHostAccess,
25
25
  updateRole
26
- } from "./chunk-LKYAVM4T.js";
26
+ } from "./chunk-F5RZH6HE.js";
27
27
  import "./chunk-YC2MAKSP.js";
28
28
  export {
29
29
  assignRoleToUser,
@@ -40,16 +40,16 @@ import {
40
40
  setShellCallbacks,
41
41
  setShellHosts,
42
42
  setShellLayoutProvider
43
- } from "./chunk-SS43S6VI.js";
43
+ } from "./chunk-RU4E5XLY.js";
44
44
  import {
45
45
  setPermissionsForTesting
46
- } from "./chunk-XKMRKQFN.js";
46
+ } from "./chunk-O2OHNVFX.js";
47
47
  import {
48
48
  getTabType,
49
49
  listTabTypes,
50
50
  setFrontendState,
51
51
  setPluginSummaries
52
- } from "./chunk-LKYAVM4T.js";
52
+ } from "./chunk-F5RZH6HE.js";
53
53
  import "./chunk-YC2MAKSP.js";
54
54
 
55
55
  // src/ui/plugin-host/testing-host.tsx
@@ -1,5 +1,6 @@
1
1
  import type { ChoiceAdjustment, PluginChoice } from "../../types/plugin-onboarding";
2
2
  import type { ChangelogRelease, ReleaseNotes } from "@termix-ssh/plugin-sdk/changelog";
3
+ import type { PluginEnvVar } from "@termix-ssh/plugin-sdk/docs";
3
4
  export type { PluginChoice };
4
5
  export interface PluginTabContribution {
5
6
  id: string;
@@ -138,6 +139,10 @@ export interface PluginSummary {
138
139
  videoId?: string;
139
140
  /** What the plugin does, from the manifest. Admins only. */
140
141
  features?: string[];
142
+ /** https docs link from the manifest. */
143
+ docs?: string;
144
+ /** Environment variables the plugin reads. Admins only. */
145
+ env?: PluginEnvVar[];
141
146
  signedBy?: string | null;
142
147
  }
143
148
  export declare function getPlugins(): Promise<PluginSummary[]>;
@@ -165,6 +170,7 @@ export interface RegistryPluginEntry {
165
170
  icon?: string;
166
171
  videoId?: string;
167
172
  features?: string[];
173
+ docs?: string;
168
174
  versions: RegistryPluginVersion[];
169
175
  /** Newest release on the plugin's channel. */
170
176
  latestVersion: string | null;
@@ -0,0 +1,25 @@
1
+ import type { ReactNode } from "react";
2
+ import { type DocsPage } from "../lib/docs";
3
+ /** A plugin's docs link from its manifest, or null when it has none. */
4
+ export declare function usePluginDocsUrl(pluginId: string | null | undefined, page?: string, anchor?: string): string | null;
5
+ export interface DocsLinkProps {
6
+ /** A core docs page. */
7
+ core?: DocsPage;
8
+ /**
9
+ * A page of the current plugin's docs, like "setup". Inside a plugin this
10
+ * resolves against its manifest docs link; "" is its main page.
11
+ */
12
+ page?: string;
13
+ anchor?: string;
14
+ /** A full URL, used as is. */
15
+ href?: string | null;
16
+ /** "text" is a small inline link, "icon" a square header button. */
17
+ variant?: "text" | "icon";
18
+ children?: ReactNode;
19
+ className?: string;
20
+ }
21
+ /**
22
+ * The one way the app links to docs. Renders nothing when there is nowhere
23
+ * to go, like a community plugin with no docs link.
24
+ */
25
+ export declare function DocsLink({ core, page, anchor, href, variant, children, className, }: DocsLinkProps): import("react").JSX.Element;
@@ -10,7 +10,7 @@ export declare const PANEL: {
10
10
  export type PanelViewMode = "grid" | "list";
11
11
  export type PanelDensity = "comfortable" | "compact";
12
12
  /** The header, toolbar band and body every tab and panel draws. */
13
- export declare function PanelShell({ chrome, icon, title, status, leading, actions, toolbar, tabs, footer, scroll, className, children, }: {
13
+ export declare function PanelShell({ chrome, icon, title, status, leading, actions, toolbar, tabs, footer, scroll, docs, className, children, }: {
14
14
  /** False inside a sidebar, which draws its own header. */
15
15
  chrome?: boolean;
16
16
  icon?: React.ReactNode;
@@ -27,6 +27,8 @@ export declare function PanelShell({ chrome, icon, title, status, leading, actio
27
27
  footer?: React.ReactNode;
28
28
  /** False when the body scrolls itself. */
29
29
  scroll?: boolean;
30
+ /** Docs URL for this screen, shown as a book icon in the header. */
31
+ docs?: string | null;
30
32
  className?: string;
31
33
  children: React.ReactNode;
32
34
  }): React.JSX.Element;
@@ -0,0 +1,10 @@
1
+ import DOCS_PAGES from "./docs-pages.json";
2
+ export declare const DOCS_URL = "https://docs.termix.site";
3
+ /**
4
+ * Every core docs page the app links to. The docs site checks each one
5
+ * exists, so add new links here rather than writing URLs inline.
6
+ */
7
+ export type DocsPage = keyof typeof DOCS_PAGES;
8
+ /** Full URL of a core docs page. */
9
+ export declare function docsUrl(page?: DocsPage): string;
10
+ export { DOCS_PAGES };
@@ -83,6 +83,7 @@ interface UserCount {
83
83
  count: number;
84
84
  }
85
85
  export { isElectron };
86
+ export declare const TRUST_TOKEN_KEY = "termixTrustToken";
86
87
  export declare function setCookie(name: string, value: string, days?: number): void | Promise<void>;
87
88
  export declare function getCookie(name: string): string | undefined;
88
89
  export declare function markUserAuthenticated(): void;
@@ -87,6 +87,7 @@ export { getAdaptiveResourceBudget, markAdaptiveResourceUsed, runAdaptiveBackgro
87
87
  export { getDeviceId } from "../lib/device-id";
88
88
  export { createQuickConnectHost } from "../sidebar/quick-connect-host";
89
89
  export { BackButton, Facts, GroupHeading, PANEL, PanelSearch, PanelShell, Segmented, ViewToggle, type PanelDensity, type PanelViewMode, } from "../components/panel-layout";
90
+ export { DocsLink, type DocsLinkProps } from "../components/docs-link";
90
91
  export { AddButton, FormFooter, ListBadge, ListRow, ListRowAction, ListRowFolder, PanelList, type ListRowTone, } from "../components/list-kit";
91
92
  export { DataView, type DataColumn, type GridColumns, } from "../components/data-view";
92
93
  export { TabStrip, type TabStripItem } from "../components/tab-strip";
package/dist/host/ui.js CHANGED
@@ -23,6 +23,7 @@ import {
23
23
  Copy,
24
24
  Corner,
25
25
  Description2,
26
+ DocsLink,
26
27
  ExternalLink,
27
28
  Eye,
28
29
  EyeOff,
@@ -137,8 +138,8 @@ import {
137
138
  useSurfaceKind,
138
139
  useTheme,
139
140
  verifySecondFactor
140
- } from "./chunk-SS43S6VI.js";
141
- import "./chunk-XKMRKQFN.js";
141
+ } from "./chunk-RU4E5XLY.js";
142
+ import "./chunk-O2OHNVFX.js";
142
143
  import {
143
144
  TabProvider,
144
145
  buildOriginWsUrl,
@@ -177,7 +178,7 @@ import {
177
178
  useTabs,
178
179
  useTabsSafe,
179
180
  verifyPasswordResetCode
180
- } from "./chunk-LKYAVM4T.js";
181
+ } from "./chunk-F5RZH6HE.js";
181
182
  import "./chunk-YC2MAKSP.js";
182
183
 
183
184
  // src/ui/components/alert.tsx
@@ -3838,6 +3839,7 @@ function HostPluginSection({
3838
3839
  {
3839
3840
  title: plugin.name,
3840
3841
  icon: /* @__PURE__ */ jsx36(PluginIcon, { name: plugin.icon, className: "size-3.5" }),
3842
+ action: /* @__PURE__ */ jsx36(DocsLink, { href: plugin.docs }),
3841
3843
  children: [
3842
3844
  !running && /* @__PURE__ */ jsx36("div", { className: "my-3 border border-warning/40 bg-warning/10 px-3 py-2 text-xs text-warning", children: t2("settings.featureUnavailable", { name: plugin.name }) }),
3843
3845
  enableKey && /* @__PURE__ */ jsx36(
@@ -31908,6 +31910,7 @@ export {
31908
31910
  DialogPortal,
31909
31911
  DialogTitle,
31910
31912
  DialogTrigger,
31913
+ DocsLink,
31911
31914
  DropdownMenu,
31912
31915
  DropdownMenuCheckboxItem,
31913
31916
  DropdownMenuContent,
@@ -7,7 +7,7 @@ import {
7
7
  resetPermissionsCache,
8
8
  setPermissionsForTesting,
9
9
  usePermissions
10
- } from "./chunk-XKMRKQFN.js";
10
+ } from "./chunk-O2OHNVFX.js";
11
11
  import "./chunk-YC2MAKSP.js";
12
12
  export {
13
13
  hasPermission,
@@ -9,6 +9,8 @@
9
9
  * "permissionGroups" used to validate clean and be silently dropped, which
10
10
  * made a manifest look like it declared something it did not.
11
11
  */
12
+ import { type PluginEnvVar } from "./docs.js";
13
+ export { ENV_NAME_PATTERN, MAX_ENV_DESCRIPTION_LENGTH, pluginDocsPage, pluginDocsUrl, pluginEnvVars, type PluginEnvVar, } from "./docs.js";
12
14
  /**
13
15
  * The plugin API this build implements. A minor bump adds to the API and
14
16
  * never removes; a major bump may break plugins.
@@ -17,7 +19,7 @@
17
19
  * "^1.2" needs 1.2 or later). engine.termix is checked against the core
18
20
  * version by the server.
19
21
  */
20
- export declare const PLUGIN_API_VERSION = "1.1.0";
22
+ export declare const PLUGIN_API_VERSION = "1.2.0";
21
23
  /** The API major, the default engine.api for a new plugin. */
22
24
  export declare const SUPPORTED_PLUGIN_API_VERSION: string;
23
25
  /** Whether this build's plugin API satisfies a manifest's engine.api. */
@@ -425,6 +427,10 @@ export interface PluginManifest {
425
427
  video?: string;
426
428
  /** Short lines listing what the plugin does, shown on its page. */
427
429
  features?: string[];
430
+ /** Where the plugin's docs live. https only. */
431
+ docs?: string;
432
+ /** Environment variables the plugin reads. */
433
+ env?: PluginEnvVar[];
428
434
  engine: PluginEngine;
429
435
  /** Catalog capability ids. */
430
436
  capabilities: string[];
package/dist/manifest.js CHANGED
@@ -11,6 +11,8 @@
11
11
  */
12
12
  import semver from "semver";
13
13
  import { isKnownCapability } from "./capabilities.js";
14
+ import { ENV_NAME_PATTERN, MAX_ENV_DESCRIPTION_LENGTH, pluginDocsUrl, } from "./docs.js";
15
+ export { ENV_NAME_PATTERN, MAX_ENV_DESCRIPTION_LENGTH, pluginDocsPage, pluginDocsUrl, pluginEnvVars, } from "./docs.js";
14
16
  /**
15
17
  * The plugin API this build implements. A minor bump adds to the API and
16
18
  * never removes; a major bump may break plugins.
@@ -19,7 +21,7 @@ import { isKnownCapability } from "./capabilities.js";
19
21
  * "^1.2" needs 1.2 or later). engine.termix is checked against the core
20
22
  * version by the server.
21
23
  */
22
- export const PLUGIN_API_VERSION = "1.1.0";
24
+ export const PLUGIN_API_VERSION = "1.2.0";
23
25
  /** The API major, the default engine.api for a new plugin. */
24
26
  export const SUPPORTED_PLUGIN_API_VERSION = String(semver.major(PLUGIN_API_VERSION));
25
27
  /** Whether this build's plugin API satisfies a manifest's engine.api. */
@@ -207,6 +209,8 @@ const ALLOWED_TOP_LEVEL = new Set([
207
209
  "icon",
208
210
  "video",
209
211
  "features",
212
+ "docs",
213
+ "env",
210
214
  "dependencies",
211
215
  "optionalDependencies",
212
216
  "provides",
@@ -337,6 +341,10 @@ export function validateManifest(manifest) {
337
341
  errors.push(`Field "video" must be a YouTube link such as https://www.youtube.com/watch?v=..., got: ${JSON.stringify(m.video)}`);
338
342
  }
339
343
  validateFeatures(m.features, errors);
344
+ if ("docs" in m && pluginDocsUrl(m.docs) === null) {
345
+ errors.push(`Field "docs" must be an https URL, got: ${JSON.stringify(m.docs)}`);
346
+ }
347
+ validateEnv(m.env, errors);
340
348
  for (const field of ["backend", "frontend", "locales"]) {
341
349
  if (field in m && requireString(m[field], field, errors)) {
342
350
  requireRelativePath(m[field], field, errors);
@@ -379,6 +387,45 @@ function validateFeatures(features, errors) {
379
387
  }
380
388
  });
381
389
  }
390
+ function validateEnv(env, errors) {
391
+ if (env === undefined)
392
+ return;
393
+ if (!Array.isArray(env)) {
394
+ errors.push('Field "env" must be an array');
395
+ return;
396
+ }
397
+ const seen = new Set();
398
+ env.forEach((entry, i) => {
399
+ const where = `env[${i}]`;
400
+ if (!isPlainObject(entry)) {
401
+ errors.push(`Field "${where}" must be an object`);
402
+ return;
403
+ }
404
+ rejectUnknown(entry, ["name", "description", "default", "required", "secret"], where, errors);
405
+ if (typeof entry.name !== "string" || !ENV_NAME_PATTERN.test(entry.name)) {
406
+ errors.push(`Field "${where}.name" must be an uppercase name like MY_PLUGIN_URL, got: ${JSON.stringify(entry.name)}`);
407
+ }
408
+ else if (seen.has(entry.name)) {
409
+ errors.push(`Field "${where}.name" repeats ${entry.name}`);
410
+ }
411
+ else {
412
+ seen.add(entry.name);
413
+ }
414
+ if (typeof entry.description !== "string" ||
415
+ entry.description.trim().length === 0 ||
416
+ entry.description.length > MAX_ENV_DESCRIPTION_LENGTH) {
417
+ errors.push(`Field "${where}.description" must be a non-empty string of at most ${MAX_ENV_DESCRIPTION_LENGTH} characters`);
418
+ }
419
+ if ("default" in entry && typeof entry.default !== "string") {
420
+ errors.push(`Field "${where}.default" must be a string`);
421
+ }
422
+ for (const flag of ["required", "secret"]) {
423
+ if (flag in entry && typeof entry[flag] !== "boolean") {
424
+ errors.push(`Field "${where}.${flag}" must be true or false`);
425
+ }
426
+ }
427
+ });
428
+ }
382
429
  function validateAuthor(author, errors) {
383
430
  if (author === undefined)
384
431
  return;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Builds an OpenAPI spec from the @openapi JSDoc blocks above routes. Core and
3
+ * every plugin use this, so the docs site renders them all the same way.
4
+ */
5
+ export interface OpenApiTag {
6
+ name: string;
7
+ description?: string;
8
+ }
9
+ export interface BuildOpenApiOptions {
10
+ title: string;
11
+ version: string;
12
+ description?: string;
13
+ /** Source files or globs holding @openapi blocks. */
14
+ files: string[];
15
+ tags?: OpenApiTag[];
16
+ }
17
+ export type OpenApiSpec = Record<string, unknown> & {
18
+ paths?: Record<string, unknown>;
19
+ };
20
+ /** How every Termix route is signed in to. */
21
+ export declare const TERMIX_SECURITY_SCHEMES: {
22
+ readonly bearerAuth: {
23
+ readonly type: "http";
24
+ readonly scheme: "bearer";
25
+ readonly bearerFormat: "JWT";
26
+ readonly description: "Session token sent in the Authorization header as Bearer and the token.";
27
+ };
28
+ readonly cookieAuth: {
29
+ readonly type: "apiKey";
30
+ readonly in: "cookie";
31
+ readonly name: "jwt";
32
+ readonly description: "Session JWT cookie set at sign-in. When present, it takes precedence over the Authorization header.";
33
+ };
34
+ readonly apiKeyAuth: {
35
+ readonly type: "http";
36
+ readonly scheme: "bearer";
37
+ readonly bearerFormat: "API key starting with tmx_";
38
+ readonly description: "API key sent in the Authorization header as Bearer and the key, which starts with tmx_. It acts as the key owner and cannot act as anyone else.";
39
+ };
40
+ };
41
+ export declare function buildOpenApi(options: BuildOpenApiOptions): Promise<OpenApiSpec>;
42
+ /** Whether a spec documents any route. */
43
+ export declare function hasOpenApiPaths(spec: OpenApiSpec): boolean;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Builds an OpenAPI spec from the @openapi JSDoc blocks above routes. Core and
3
+ * every plugin use this, so the docs site renders them all the same way.
4
+ */
5
+ import swaggerJSDoc from "@deadendjs/swagger-jsdoc";
6
+ /** How every Termix route is signed in to. */
7
+ export const TERMIX_SECURITY_SCHEMES = {
8
+ bearerAuth: {
9
+ type: "http",
10
+ scheme: "bearer",
11
+ bearerFormat: "JWT",
12
+ description: "Session token sent in the Authorization header as Bearer and the token.",
13
+ },
14
+ cookieAuth: {
15
+ type: "apiKey",
16
+ in: "cookie",
17
+ name: "jwt",
18
+ description: "Session JWT cookie set at sign-in. When present, it takes precedence over the Authorization header.",
19
+ },
20
+ apiKeyAuth: {
21
+ type: "http",
22
+ scheme: "bearer",
23
+ bearerFormat: "API key starting with tmx_",
24
+ description: "API key sent in the Authorization header as Bearer and the key, which starts with tmx_. It acts as the key owner and cannot act as anyone else.",
25
+ },
26
+ };
27
+ export async function buildOpenApi(options) {
28
+ const spec = (await swaggerJSDoc({
29
+ definition: {
30
+ openapi: "3.0.3",
31
+ info: {
32
+ title: options.title,
33
+ version: options.version,
34
+ ...(options.description ? { description: options.description } : {}),
35
+ },
36
+ servers: [{ url: "/", description: "Your Termix server" }],
37
+ components: {
38
+ securitySchemes: TERMIX_SECURITY_SCHEMES,
39
+ schemas: {
40
+ Error: {
41
+ type: "object",
42
+ properties: {
43
+ error: { type: "string" },
44
+ details: { type: "string" },
45
+ },
46
+ },
47
+ },
48
+ },
49
+ security: [{ bearerAuth: [] }, { cookieAuth: [] }, { apiKeyAuth: [] }],
50
+ ...(options.tags?.length ? { tags: options.tags } : {}),
51
+ },
52
+ apis: options.files.map((f) => f.replace(/\\/g, "/")),
53
+ failOnErrors: true,
54
+ }));
55
+ return spec;
56
+ }
57
+ /** Whether a spec documents any route. */
58
+ export function hasOpenApiPaths(spec) {
59
+ return Object.keys(spec.paths ?? {}).length > 0;
60
+ }
@@ -77,6 +77,7 @@ const noop = () => { };
77
77
  const offline = () => Promise.reject(new Error("No plugin API in a unit test; mock it"));
78
78
  globalThis.__termixTestPluginHost = {
79
79
  usePluginId: () => "test-plugin",
80
+ useDocsUrl: () => null,
80
81
  useTranslation: () => ({
81
82
  t: (key) => key,
82
83
  language: "en",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@termix-ssh/plugin-sdk",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "The contract between Termix core and a plugin: types, the capability catalog and the manifest schema.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -53,6 +53,14 @@
53
53
  "types": "./dist/changelog.d.ts",
54
54
  "default": "./dist/changelog.js"
55
55
  },
56
+ "./docs": {
57
+ "types": "./dist/docs.d.ts",
58
+ "default": "./dist/docs.js"
59
+ },
60
+ "./openapi": {
61
+ "types": "./dist/openapi.d.ts",
62
+ "default": "./dist/openapi.js"
63
+ },
56
64
  "./settings": {
57
65
  "types": "./dist/settings.d.ts",
58
66
  "default": "./dist/settings.js"
@@ -102,6 +110,7 @@
102
110
  "prepublishOnly": "npm run build"
103
111
  },
104
112
  "dependencies": {
113
+ "@deadendjs/swagger-jsdoc": "^8.1.2",
105
114
  "@tailwindcss/node": "^4.2.4",
106
115
  "@tailwindcss/oxide": "^4.2.4",
107
116
  "esbuild": "^0.28.1",
@@ -85,6 +85,27 @@
85
85
  "items": { "type": "string", "minLength": 1, "maxLength": 160 },
86
86
  "description": "Short lines listing what the plugin does, shown on its page."
87
87
  },
88
+ "docs": {
89
+ "type": "string",
90
+ "pattern": "^https://",
91
+ "description": "Where the plugin's docs live. Shown as a Documentation link in Termix."
92
+ },
93
+ "env": {
94
+ "type": "array",
95
+ "description": "Environment variables the plugin reads, listed in its docs.",
96
+ "items": {
97
+ "type": "object",
98
+ "additionalProperties": false,
99
+ "required": ["name", "description"],
100
+ "properties": {
101
+ "name": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]{0,63}$" },
102
+ "description": { "type": "string", "minLength": 1, "maxLength": 300 },
103
+ "default": { "type": "string" },
104
+ "required": { "type": "boolean" },
105
+ "secret": { "type": "boolean" }
106
+ }
107
+ }
108
+ },
88
109
  "engine": {
89
110
  "type": "object",
90
111
  "additionalProperties": false,