102 Commits

Author SHA1 Message Date
Alex Dunmow
7b15ce70d0 feat(captcha): in-process nonce store for single-use enforcement 2026-07-03 17:16:19 +08:00
Alex Dunmow
f5cbb56df7 feat(captcha): HMAC-signed stateless challenge + verification tokens 2026-07-03 17:13:24 +08:00
Alex Dunmow
84723991cd feat(captcha): FNV-1a + xorshift32 PRNG matching Cap.js widget
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 17:12:04 +08:00
Alex Dunmow
40ba4ee9de bnwasm: nil []string→NULL, fatten DbValue fixtures, doc text[] NULL-element limit
Close three WO-WZ-004 review gaps in the guest db driver:

- toDbValue: nil []string now marshals to DbValue_Null (matching []byte /
  json.RawMessage); empty-but-non-nil stays a non-NULL empty text[].
- DbValueFixtures: add edge entries (zero time.Time, negative + very-large
  numeric strings, empty text[], and text[] elements forcing encodePgTextArray
  quoting/escaping). Covered automatically by the table-driven round-trip and
  driver-value tests; new dbvalue_test.go covers the toDbValue nil convention.
- Document that TextArray cannot represent a NULL array element (repeated
  string has no per-element NULL) in the fixtures file and docs/wasm-abi.md,
  a contract limit the WO-WZ-007 host executor must also honor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v0.15.0
2026-07-03 14:49:49 +08:00
Alex Dunmow
081bacf2ab feat(wasmguest): wire bnwasm Pool into guest CoreServices (WO-WZ-004)
Bind the db.* driver over the same transport as the capability stubs so
deps.Pool keeps working for plugin sqlc code:

- dispatch.go: g.services.Pool = bnwasm.NewPool(capTransport) (nil on
  native/DESCRIBE → fails cleanly, matching the caps stubs).
- hostcalls.go (wasip1): also bind the "bnwasm" database/sql driver's
  process-global transport to CallHost.
- docs/wasm-abi.md: document the implemented guest driver + pgx-primary
  rationale under the Pool disposition.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:41:56 +08:00
Alex Dunmow
7881aeee05 feat(wasmguest): guest DB driver bnwasm (WO-WZ-004)
Guest-side database access over the db.* host calls (abiv1 db.proto),
two surfaces sharing one injected transport:

- database/sql driver registered as "bnwasm" (driver.go) — QueryContext/
  ExecContext/BeginTx over db.query/db.exec/db.tx_*; named args rejected.
- plugin.Pool (pool.go) handing out a pgx.Tx-shaped value (tx.go), so the
  pgx-flavored sqlc DBTX every current plugin generates against
  (sql_package: pgx/v5) is satisfied with no source edits. This is the
  primary path: their DBTX needs pgconn.CommandTag/pgx.Rows/pgx.Row, which
  database/sql cannot produce.

DbValue↔Go mapping (dbvalue.go) covers all 11 oneof arms both directions;
DbError surfaces as *pgconn.PgError (SQLSTATE preserved for errors.As);
nested tx/savepoints rejected with a clear error (no fleet plugin uses
them). The scan contract is pinned in the exported DbValueFixtures table
(dbvalue_fixtures.go) that the WO-WZ-007 host executor mirrors.

Tests: driver_test.go (fake host — every DbValue variant round-trips with
correct scan types, exec rows-affected, ordered host-call assertions for
tx commit/rollback sequences, post-rollback autocommit carries no handle)
and sqlcgen_test.go (vendored sqlc-style Queries + WithTx run against the
fake host). Native + wasip1 builds green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:41:45 +08:00
Alex Dunmow
bec3a43f55 feat(wasmguest): guest capability stubs over host calls (WO-WZ-003)
Every CoreServices interface (core/plugin/deps.go) now has a guest-side stub
that marshals to the WO-WZ-001 capability messages and dispatches through the
generic host_call transport, so plugin service code compiles and runs
unchanged against content.Content, settings.Settings, plugin.PluginBridge, etc.

- core/plugin/wasmguest/caps/: one file per family (17 families, 38 methods),
  a var _ <iface> = (*stub)(nil) compile proof each, and NewCoreServices(call)
  assembling them. The transport is injected (CallFunc) so marshaling is
  natively testable; the wasm shim binds it to CallHost, DESCRIBE probes pass
  nil (capability calls fail cleanly instead of nil-panicking).
- Error mapping wraps AbiError with <family>.<method> context and maps
  DEADLINE_EXCEEDED onto context.DeadlineExceeded.
- RAGService.RegisterContentFetcher stays guest-side (RAGStub) for
  HOOK_RAG_FETCH dispatch; Query/OnContentChanged marshal out. dispatch.go and
  describe.go now source fetchers from the caps RAG stub.
- caps_roundtrip_test.go: fake transport + 76 deterministic golden payloads
  (family_method_{req,resp}.pb) covering 100% of families, plus error-mapping,
  deadline, nil-transport, and guest-side-fetcher tests. WO-WZ-006 replays the
  same goldens to prevent host/guest drift.
- Acceptance: testdata/fixture Load hook calls deps.Content/Settings/Bridge
  unchanged (compiles for wasip1); caps_wasmhost_test.go drives it end-to-end
  through a real wazero module + fake host_call table.
- Disposition table in docs/wasm-abi.md: every member stub | host-side
  (Pool, Interceptors, AppURL/MediaPath, CoreServiceBindings host-side).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:23:58 +08:00
Alex Dunmow
5f4fa9db0f fix(wasmguest): frame empty InvokeResponse as (ptr,0), not packed 0
A successful hook whose response message has no set fields (LoadResponse,
UnloadResponse) proto-marshals to zero bytes. bn_invoke's `len==0 → return 0`
shortcut collided with the "callee could not produce an envelope" sentinel, so
every successful empty-response hook — notably HOOK_LOAD — looked like an
INTERNAL failure and got the instance discarded. Frame the empty case as
(ptr, 0) with a real 1-byte-backed pointer instead; the host reads zero bytes
into a valid empty InvokeResponse.

Surfaced by the WO-WZ-003 end-to-end capability test (first exercise of LOAD).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:23:36 +08:00
Alex Dunmow
dc621e6d96 chore(ninja): check fmt.Fprintln returns in publish warnings (errcheck)
Pre-existing strict-lint failures in the publish warning helpers were
the only thing keeping check-safety's Go lint lane red for the module;
discard the writer errors explicitly (warnings are best-effort output).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 13:53:46 +08:00
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
704b046727 feat(abi): wasm plugin ABI protobuf schema v1 (WO-WZ-001)
Defines the host<->guest wire contract for the .so -> wazero migration as a
repo-local buf module (abi/proto/v1, generated Go in abi/v1):

- manifest.proto: PluginManifest mirroring every static field of
  plugin.PluginRegistration (abi_version, deps, block/template registrations,
  admin pages, settings schema, theme presets, fonts, master pages, AI
  actions, RBAC method roles, CSS manifest, icon packs, directory
  extensions, job types, RAG fetcher types, settings panel, hook flags,
  core service bindings)
- invoke.proto: bn_invoke envelope, Hook catalog (RENDER_BLOCK,
  RENDER_TEMPLATE, HANDLE_HTTP, JOB, LOAD, UNLOAD, RAG_FETCH, MEDIA_HOOK,
  DESCRIBE), AbiError, and job/lifecycle/rag/media/describe payloads
- render.proto: RenderBlock/RenderTemplate payloads + RenderContext
  enumerating every ctx value from blocks/context.go (incl. BlockContext 1:1)
- http.proto: buffered HttpRequest/HttpResponse for HANDLE_HTTP
- db.proto: guest sql-driver messages (DbValue oneof over pgx-mappable
  types, query/exec/tx with host-side tx handles, SQLSTATE-carrying DbError)
- capability.proto: HostCall envelope + one request/response pair per
  CoreServices interface method (content, settings+update, gating, crypto,
  menus, datasources, users, subscriptions, media.deposit, email.send,
  ai.text_call, ai.tools.register, bridge, jobs.submit, embeddings, rag,
  reviews, badges.refresh)

The abi/ buf module is deliberately separate from the repo-root config:
proto/ is the shared block/proto submodule (service API contracts); the ABI
is SDK-internal and versions in lockstep with the guest shim. New `make abi`
target runs buf lint + generate. docs/wasm-abi.md documents the ptr+len
calling convention (bn_alloc, bn_invoke, packed u64), hook catalog, error
semantics, abi_version evolution rules, and the full registration/
CoreServices coverage tables.

Verified: `cd abi && buf lint` exit 0; `go build ./...` exit 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 12:47:00 +08:00
Alex Dunmow
c6305d18c2 feat(ninja): theme screenshot harness (chromedp)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 17:21:43 +08:00
Alex Dunmow
5170d3adf4 fix(render): unwrap BlockNote tableCell objects so table cells render content
BlockNote >=0.15 (the editor ships 0.47) stores each table cell as a
{type:"tableCell", content:[...]} object. The renderer passed each cell
straight to inlineContentFromRaw, which only understands strings and bare
inline arrays, so object-form cells rendered an empty grid (correct rows
and columns, no content). Unwrap content[] from tableCell objects while
keeping the legacy bare-array/string cell formats working.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v0.14.1
2026-06-18 21:59:30 +08:00
Alex Dunmow
d5e9d7ecd3 Link CLAUDE.md to AGENTS.md 2026-06-18 16:46:53 +08:00
Alex Dunmow
d08466ec70 chore: fix SDK downstream paths for consolidated layout
SDK_DOWNSTREAM_DIRS pointed at the pre-consolidation layout
(~/src/blockninja/backend, ~/src/orchestrator/backend, ~/src/blockninja-themes,
~/src/{assumechaos,bidbuddy,...}) — none of which exist after all repos moved
under ~/src/blockninja/. Repoint at cms/backend + orchestrator/backend and
wildcard themes/ sites/ plugins/ so update-sdk/distribute-sdk reach them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v0.14.0
2026-06-13 13:18:12 +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
e309e3d80b chore: bump templ to v0.3.1020
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 13:14:31 +08:00
Alex Dunmow
d75bec2e03 docs: README ServiceDeps -> CoreServices
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 16:22:03 +08:00
Alex Dunmow
aa66c2f409 chore: update proto submodule to merged main
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-08 20:54:07 +08:00
Alex Dunmow
f4e579ad7a refactor: simplify pongo template context and engine, add private plugins spec
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-08 20:49:18 +08:00
Alex Dunmow
26b262ce73 feat(ninja): wire ListTags into popular-tag prompt and list helpers v0.13.1 2026-06-07 15:53:03 +08:00
Alex Dunmow
bb3ddfe1bd refactor(ninja): extract popular-tags stubs into named helpers v0.13.0 2026-06-07 15:38:24 +08:00
Alex Dunmow
5d368da839 test(ninja): plugin tags subcommand round-trip
Adds TestMutateTags_AddRmSetClear (covers add/rm/set/clear operations
including dedupe and normalisation) and TestMutateTags_RejectsInvalidNoWrite
(ensures validation failures don't mutate plugin.mod).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 15:34:36 +08:00
Alex Dunmow
e16655aed8 refactor(ninja): use slices.Equal in mutateTags 2026-06-07 15:33:38 +08:00
Alex Dunmow
03d32aba26 feat(ninja): add 'plugin tags' subcommand
Adds ninja plugin tags (show), add, rm, set, and clear subcommands that
read and mutate the local plugin.mod tag list via NormalizeTags. The bare
tags command stubs ListTags with a TODO(tags): marker for Task 12.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 15:31:36 +08:00
Alex Dunmow
533632a3bb feat(ninja): prompt for tags in plugin init/edit flow
Add promptTagsWithDefault helper (mirrors promptCategoriesWithDefault) and
wire it into the init flow so upsertPluginMod receives real user-entered tags
instead of nil. ListTags RPC stub left with TODO(tags) marker for Task 12.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 15:27:30 +08:00
Alex Dunmow
6bc0f98979 refactor(ninja): thread tags through upsertPluginMod
Extend upsertPluginMod signature to accept tags parameter (positional arg 7,
between categories and private). Update the single call site to pass nil.
Add tags serialization in writeMod, mirroring the categories pattern.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 15:25:00 +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>
v0.12.4
2026-06-07 15:14:15 +08:00
Alex Dunmow
c3c7b2d441 docs: follow CMS module rename
CMS Go module renamed from git.dev.alexdunmow.com/block/ninja to
git.dev.alexdunmow.com/block/cms (along with the git remote rename
on gitea). All import paths, string-literal rules, test fixtures,
and doc references updated; (historical 'ninja-orchestrator' refs
preserved). go mod tidy regenerated checksums.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 13:55:49 +08:00
Alex Dunmow
48c54814ec docs: update paths after 2026-06-06 repo consolidation
Repos consolidated under ~/src/blockninja/ parent (collection, not
monorepo). This repo moved from ~/src/core to ~/src/blockninja/core.
Updates historical plan/audit docs that referenced the old paths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 13:02:51 +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>
v0.12.3
2026-06-04 20:58:09 +08:00
Alex Dunmow
7fc20a990b fix(cli): use renamed ListMyAccountsForCLI RPC
Tracks the shared proto rename that resolves the message-name collision
between PluginAuthService and AccountService.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.12.2
2026-06-04 20:55:10 +08:00
Alex Dunmow
051253396c fix(proto): narrow core generation to plugin_registry.proto only
When the orchestrator imports `block/core/internal/api/orchestrator/v1`
transitively through other core packages and also generates its own
bindings for the same files (accounts.proto etc.), proto registration
panics at startup: "file ... is already registered". Tests in the
orchestrator confirmed this.

Fix:
- buf generate now uses --path to limit core's output to
  proto/orchestrator/v1/plugin_registry.proto (see new `make proto`).
- Adds a minimal MyAccount message and PluginAuthService.ListMyAccounts
  RPC to plugin_registry.proto (already pushed to block/proto) so the
  CLI's account picker no longer needs accounts.proto generated.
- CLI switches back to cli.Auth.ListMyAccounts; orchclient.Client drops
  the Account field.

Side effect: every previously-generated orchestrator/v1 binding besides
plugin_registry is removed from this module.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.12.1
2026-06-04 20:53:44 +08:00
Alex Dunmow
35436581b9 feat(proto): consume canonical block/proto as a submodule
Remove core's local proto/ fork and pull the canonical block/proto repo in
as a submodule at the same path. buf.yaml now sources from the submodule's
orchestrator/v1 namespace; everything outside that (blockninja, helpdesk)
is excluded from generation.

This brings the orchestrator's local-only RPCs (PluginScopeService.ListMyPlugins,
PluginRegistryService.SubmitForReview, the full PluginModerationService) into
core's bindings — harmless surface area for the CLI, prerequisite for the
orchestrator to also stop forking the proto.

Side effect: the CLI's account picker now uses the canonical
AccountService.ListMyAccounts in accounts.proto rather than the duplicate
PluginAuthService.ListMyAccounts that lived only in core's fork. The
existing Account message uses Name (no DisplayName / Role), so the picker
output collapses to "slug — Name".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.12.0
2026-06-04 20:45:47 +08:00
Alex Dunmow
c390e16b5c build(make): auto-bump release version + distribute-sdk subcommand
- `make release` (no args) infers the next version from the last vX.Y.Z
  tag using conventional-commits: BREAKING/`!:` → major, `feat` → minor,
  otherwise → patch. LEVEL=major|minor|patch forces; VERSION=vX.Y.Z is
  the explicit escape hatch.
- New `distribute-sdk` subcommand commits + pushes the pin bump in each
  downstream. Surgical (commits go.mod + go.sum only) so any unrelated
  WIP in a downstream is left alone instead of getting swept into the
  commit. Repos without an origin remote land the commit locally.
- `release` now chains tag → push → update-sdk → distribute-sdk so one
  command takes the ecosystem from new commit to fully-distributed.
v0.11.1
2026-06-04 20:18:26 +08:00
Alex Dunmow
87910e22ff fix(make): check-sdk-pins matches single-line require form
The previous regex only matched indented entries inside a `require (...)`
block, so single-line requires (like blockninja-themes/lcars uses) were
reported as unpinned even when the version was correct.
2026-06-04 08:53:17 +08:00
Alex Dunmow
c4d00a11d9 build(make): release target + expand SDK_DOWNSTREAM_DIRS
- `make release VERSION=vX.Y.Z` checks the tree is clean, pushes HEAD,
  tags, pushes the tag, then runs update-sdk so every downstream repo's
  go.mod gets bumped in one shot.
- SDK_DOWNSTREAM_DIRS now includes orchestrator/backend and
  blockninja-themes/* (globbed), which were both missing previously.
v0.11.0
2026-06-04 08:49:28 +08:00
Alex Dunmow
7615bd92ca feat(cli): multi-account login, private-plugin SDK, publish dirty handling
- ninja login forces account selection (interactive when >1); creds now
  carry ActiveAccountID/Slug. New `ninja account` group.
- ninja plugin list / delete / delete-version split public vs active-account
  @private sections; `publish --private` is sticky in plugin.mod.
- GetPluginRequest gains active_account_id so @private resolution works
  alongside the public (scope, name) path.
- publish auto-commits a dirty plugin.mod (path-scoped, leaves other staged
  paths alone) so the bump→publish loop never trips the dirty check.
  --allow-dirty is replaced with --strict (default now ships dirty trees
  via stash-create).
- bump auto-commits its plugin.mod write with `bump to X.Y.Z`; --no-commit
  opts out.
- Design doc updated to match the new defaults.
2026-06-04 08:49:23 +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
06cabd6eb9 fix(cli): init prompts default to existing plugin.mod values 2026-06-03 12:01:56 +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
dae3aa918a feat(core): sync plugin_registry proto with canonical (DenyDevice, GetDeviceStatus) 2026-06-03 10:57:07 +08:00
Alex Dunmow
46e3389045 test(cli): cover emitPublishWarnings across all three warning paths
Proves the publish command's warning surface end-to-end: tracked-yet-
gitignored files, declared submodules, untracked files on --allow-dirty,
and that the dirty-tree abort suppresses the untracked warning.
2026-06-03 10:15:24 +08:00
Alex Dunmow
824d55a1fa refactor(cli): extract publish-time warnings into emitPublishWarnings
Pulls the three warning calls and the dirty-tree check out of the
publish RunE closure into a single helper so a refactor that drops one
warning can be caught by a fixture-based test.
2026-06-03 10:14:50 +08:00
Alex Dunmow
ea744888ae test(cli): lock in trailing newline on gitignored-tracked warning
Guards against a silent refactor from Fprintln to Fprint dropping the
terminating newline that downstream output relies on.
2026-06-03 10:14:06 +08:00
Alex Dunmow
3d62071f77 test(cli): cover autoCommitPluginMod detached HEAD and missing git
Adds coverage for two git-edge cases: commits land correctly on a
detached HEAD with the prior commit as parent, and an empty PATH
produces a git-mentioning error rather than a panic.
2026-06-03 10:13:53 +08:00
Alex Dunmow
e076a03c33 test(cli): cover publish-time warning helpers
Add fires/no-op pairs for gitignoredTrackedWarning, untrackedFilesWarning,
and submoduleWarning. The submodule case writes a hand-crafted .gitmodules
file rather than wiring real submodules — submodulePaths reads the file
directly so that's sufficient.
2026-06-03 09:13:00 +08:00