30 Commits

Author SHA1 Message Date
Alex Dunmow
9bbc793563 test(wasmguest): reactor fixture plugin + throwaway wazero host (WO-WZ-002)
testdata/fixture is the acceptance fixture: one block, one template, one
admin page, plus the two-line reactor boilerplate main. wasmhost_test.go
compiles it with GOOS=wasip1 GOARCH=wasm -buildmode=c-shared (15.7 MiB,
the WO-WZ-012 memory-budget baseline) and drives it through wazero
exactly per wasm-abi.md: _initialize as the start function, request
bytes through bn_alloc, DESCRIBE returning a decodable manifest,
same-instance block+template renders, a panicking BlockFunc surfacing as
ABI_ERROR_CODE_INTERNAL with the instance still callable, and a
non-bn_alloc request pointer rejected as DECODE.

wazero v1.12.0 joins go.mod as a test-only dependency (approved).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 13:53:39 +08:00
Alex Dunmow
c35199f0bc feat(wasmguest): guest runtime shim for the wasm plugin ABI (WO-WZ-002)
The wasip1 half of the ABI: bn_alloc/bn_invoke/bn_free exports with a
live-pin map so the GC never frees host-visible buffers, the generic
`blockninja.host_call` import (single import decided over per-family
symbols; recorded in wasm-abi.md), and a dispatch table adapting an
unmodified plugin.PluginRegistration to all nine v1 hooks. Panics inside
plugin hooks come back as ABI_ERROR_CODE_INTERNAL — the instance stays
callable; traps stay reserved for runtime corruption.

DESCRIBE builds the PluginManifest from the registration's static funcs
plus a capture-only Register pass (block metas via the same
PluginBlockRegistry prefixing the .so loader applies, template/system/
page-template/email-wrapper keys), probes JobHandlers/ServiceHandlers/
Load with capture-only services for job types, RBAC roles, core-service
bindings, and RAG fetcher types. RenderContext values are rehydrated
through the exact core/blocks context keys, so existing block code
reading from ctx works unchanged.

Plugins build in REACTOR mode (go build -buildmode=c-shared): init()
calls wasmguest.Serve (non-blocking), main is never called, and the host
runs _initialize before any bn_invoke. Command mode deadlocks or exits
(verified against wazero v1.12.0) — documented prominently in
wasm-abi.md, which also now reconciles the import module namespace to
`blockninja` and requires bn_alloc'd buffers on both directions.

Dispatch/describe/context logic is buildable on every GOOS; only
exports.go and hostcalls.go carry the wasip1 tag. dispatch_test.go
covers describe, hook routing, envelope mismatch, decode failures,
template-override resolution, panic recovery, and lifecycle hooks
natively.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 13:53:24 +08:00
Alex Dunmow
d9788abe4c feat(plugin): media deposit SDK surface — Media, MediaDeposit, EnsureMedia
Add the SDK surface for plugins to deposit images into the CMS media library.
MediaDeposit / MediaResult types and a Media interface on CoreServices for
runtime deposits; EnsureMedia on the Provisioner interface for idempotent
seed-time deposits under a plugin-chosen, template-referable UUID. The CMS
implements both against one internal depositor; seeded templates reference
the chosen UUID directly via {% img "<id>" %}.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 13:14:31 +08:00
Alex Dunmow
d53c3d8325 feat(plugin): add Tags field to ModPlugin
TDD approach: added failing tests in mod_test.go that check parsing and
null-coalescing of tags, then added the []string Tags field to ModPlugin
struct with TOML tag "tags,omitempty".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 15:23:05 +08:00
Alex Dunmow
ed365f9030 feat(plugin): add NormalizeTags helper and slug rules
Add NormalizeTags, TagMinLen, TagMaxLen, TagMaxCount constants and
tagSlugRe to plugin/mod.go. Full TDD: 8 tests covering happy path,
trim/lowercase, case-insensitive dedupe, empty-drop, bad-slug rejection,
boundary acceptance, cap enforcement, and nil input.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 15:20:24 +08:00
Alex Dunmow
ba87684696 feat(plugin): add RequiredIconPacks to PluginRegistration and ModPlugin
Lets plugins declare icon-pack dependencies (e.g. "tabler", "phosphor")
in plugin.mod and PluginRegistration. The CMS loader auto-installs
declared packs from the bundled registry before the plugin loads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-07 15:14:15 +08:00
Alex Dunmow
af7f44c34d fix(plugin): drop VisibilityLabel and its proto import
When core/plugin imported core/internal/api/orchestrator/v1 for the
PluginVisibility enum, every consumer of core/plugin (including the
orchestrator) transitively pulled in core's generated bindings — and
those bindings register the same proto descriptors as the orchestrator's
own bindings, panicking at startup.

Move the label helper into the CLI's cmd package where it belongs;
core/plugin no longer references the proto package at all.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 20:58:09 +08:00
Alex Dunmow
264116f44e feat(core): private-plugin SDK, PluginVisibility enum, and Go 1.26.4 bump
Add private-plugin RPCs (ListPrivatePlugins, DeletePrivatePlugin,
DeletePrivatePluginVersion, ListPrivatePluginInstallSites) and
ListMyAccounts to the proto/generated stubs; introduce PluginVisibility
enum replacing the loose string field; add ModPlugin.Private + Coords()
routing to @private/<name>@<version>; update ninja CLI to use
VisibilityLabel helper; bump go directive to 1.26.4 for ABI alignment.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 01:00:50 +08:00
Alex Dunmow
041a7c2e3f feat(core): plugin display_name + description; CLI prompts; SDK fields 2026-06-03 11:31:23 +08:00
Alex Dunmow
20a7b35e50 docs(sdk): document Coords scope normalisation and accept-both contract
Adds a doc comment to ModFile.Coords explaining the leading-@ trim and a
note on ModPlugin.Scope clarifying that consumers should trim "@" before
comparing. Locks in the contract with a test asserting both call shapes
produce the same display string.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 08:55:46 +08:00
Alex Dunmow
139d9b8543 fix(sdk): Coords trims leading @ from scope so callers can store either form 2026-06-03 07:03:19 +08:00
Alex Dunmow
08be22ec34 feat(sdk): add Kind and Categories to ModPlugin; writeMod emits them 2026-06-03 01:41:56 +08:00
Alex Dunmow
2a76b30c51 feat(cli): scope subcommand, interactive scope prompt, bump+version helpers
Pre-existing CLI improvements ahead of the tarball-publish refactor:
- New top-level `ninja scope` command (create, list, set-default).
- `init` accepts no --scope: prompts from ListMyScopes or uses creds default.
- Plugin name prompted if not provided.
- `plugin bump <major|minor|patch>` writes the bumped version into plugin.mod.
- `plugin version` prints the current plugin.mod version.
- `login` prints a URL with ?user_code= so the link is one click.
- creds: HostCreds gains optional default_scope.
- plugin/version: ParseBaseSemver + BumpVersion helpers, with tests.
2026-06-03 01:18:11 +08:00
Alex Dunmow
7ff326ef25 feat(sdk): ModFile struct, ParseModFull, and tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-01 22:45:26 +08:00
Alex Dunmow
7eb3e27053 feat: converge BlockNote renderer, add datasources bridge, rename ServiceDeps to CoreServices
SDK renderer now has full feature parity with the host: text alignment,
checkListItem, toggleListItem, video, audio, file, statement blocks,
and text/background color inline styles. New datasources.Datasources
interface lets plugins resolve buckets directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-03 10:18:32 +08:00
Alex Dunmow
9c62780246 feat: add context-aware BlockNote SDK bridge 2026-05-03 08:36:08 +08:00
Alex Dunmow
b2c968af41 feat: add SymposiumSeeder and MessengerSeeder bridge interfaces
Defines cross-plugin seeder interfaces in the SDK so template plugins
can seed Symposium/Messenger content via PluginBridge without importing
their database packages directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-03 00:16:46 +08:00
Alex Dunmow
601718a309 feat: add Load/Unload lifecycle hooks to PluginRegistration
Enables runtime plugin disable/enable without CMS restart.
Load is called after registration succeeds; Unload on disable
with a 30-second context deadline.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-02 22:28:49 +08:00
Alex Dunmow
334d79b4bf fix: EnsureSetting accepts any value type for JSON-serializable settings
Plugins store arrays and scalars as settings, not just maps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-02 12:13:39 +08:00
Alex Dunmow
a2a56f642c fix: UpdateDataTableRowField accepts any value type, not just maps
The provisioner just marshals the value to JSON regardless of type.
Restricting to map[string]any prevented plugins from setting scalar
field values (strings, numbers, booleans).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-02 12:10:36 +08:00
Alex Dunmow
62c25a7b3a feat: add settings.Updater interface to ServiceDeps 2026-05-02 11:03:40 +08:00
Alex Dunmow
2c89ce4d42 feat: add BadgeRefresher interface and badge types 2026-05-02 11:03:09 +08:00
Alex Dunmow
1ede7d50be feat: add ReviewSubmitter interface to ServiceDeps 2026-05-02 11:02:28 +08:00
Alex Dunmow
a174eb943d feat: add CoreServiceBindings interface to ServiceDeps 2026-05-02 11:02:08 +08:00
Alex Dunmow
d270bc8582 feat: extend EmbeddingService and RAGService interfaces for .so plugins
Add EmbedContent/IsAvailable to EmbeddingService and
RegisterContentFetcher/OnContentChanged to RAGService so .so plugins
can use embedding and RAG capabilities through SDK interfaces instead
of type-asserting CMS concrete types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-02 09:25:44 +08:00
Alex Dunmow
917eee13a2 feat: add menus, subscriptions, public users, and plugin bridge to SDK
New capability interfaces:
- menus.Menus: menu/nav access (GetMenuByName, GetMenuItems)
- subscriptions.Subscriptions: tier/plan access (GetUserTierLevel, GetTierBySlug, ListActivePlans)
- auth.PublicUsers: public user profiles (GetByUsername, GetByID)
- plugin.PluginBridge: inter-plugin service registry with typed GetServiceAs[T] helper

All added to ServiceDeps for plugin consumption.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-01 12:36:13 +08:00
Alex Dunmow
43deff21f7 feat: tighten types — MasterPageBlock JSON tags + *string, BlockNote []map[string]any
- Add JSON struct tags to MasterPageBlock and MasterPageDefinition
- Change MasterPageBlock.HtmlContent from string to *string (nullable)
- Change BlockNote renderer signatures from []any to []map[string]any
- Move type assertions to JSON boundary in blocksFromRaw/inlineContentFromRaw

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-01 11:37:41 +08:00
Alex Dunmow
13d741979a fix: rename module path from ninja/core to block/core
Matches the actual Gitea repo location at block/core.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-01 09:39:10 +08:00
Alex Dunmow
7c20538a4e feat: WO-PS-009 SDK plugin types
- plugin/types.go: Pool, EmailSender, JobHandlerFunc, Dependency,
  AdminPage, AIAction, MasterPageDefinition, status/source constants,
  MediaAnalyzedEvent, ModerationDecisionEvent, MediaHooksProvider,
  DirectoryExtensions
- plugin/deps.go: ServiceDeps with typed capability interfaces
  (Content, Settings, Gating, Crypto, ToolRegistry, JobRunner,
  EmbeddingService, RAGService)
- plugin/registration.go: PluginRegistration, RegisterFunc
- plugin/service.go: ConnectServiceBinding with generics, ServiceMount,
  ServiceRegistration
- plugin/provisioner.go: Provisioner interface with all config types
- plugin/css_manifest.go: CSSManifest, MergedCSSManifest, MergeCSSManifests
- plugin/version.go: ParseModVersion, CompareVersions
- plugin/topo_sort.go: TopologicalSort (Kahn's algorithm)
- plugin/block_registry.go: PluginBlockRegistry (auto-prefixing wrapper)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-30 22:41:52 +08:00
Alex Dunmow
0991b791b1 feat: WO-PS-001 initialize SDK repo
Initialize git.dev.alexdunmow.com/ninja/core with Go 1.26 module,
package directory structure, and README.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-30 22:25:52 +08:00