feat(release): declare the RunicNPC API the bridge needs (runicnpc stage 4, D224)
All checks were successful
PR Checks / plugin-checks (pull_request) Successful in -1m36s

overlay.toml gains runicnpc_api = 3, the release folds it into manifest.json,
and checkPlugin.js holds it equal to RunicNpcApiNeeded in the code. The
installer's bundle carries the latest RunicNPC release that answers it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-09-30 04:48:51 -05:00
parent 742e18ba0a
commit 6347c25d57
3 changed files with 26 additions and 0 deletions

View File

@@ -338,6 +338,10 @@ jobs:
# requires_plugins = ["Kits", "ZoneManager"] is already a JSON array.
# One line, quoted strings only; anything else fails the check below.
REQUIRES="$(grep -m1 -E '^requires_plugins[[:space:]]*=' overlay.toml | sed -E 's/^[^=]*=[[:space:]]*//')"
# The RunicNPC API the bridge's npc.* commands need (runicnpc stage 4). 0 when
# absent, which a bundle reads as "carries no RunicNPC".
RUNICNPC_API="$(grep -m1 -E '^runicnpc_api[[:space:]]*=' overlay.toml | sed -E 's/[^0-9]//g')"
RUNICNPC_API="${RUNICNPC_API:-0}"
[ -n "$PROTOCOL" ] || { echo "::error::could not read protocol from overlay.toml"; exit 1; }
[ -n "$MIN_OXIDE" ] || { echo "::error::could not read min_oxide_version from overlay.toml"; exit 1; }
[ -n "$MIN_CARBON" ] || { echo "::error::could not read min_carbon_version from overlay.toml"; exit 1; }
@@ -354,6 +358,7 @@ jobs:
--arg min_oxide "${MIN_OXIDE}" \
--arg min_carbon "${MIN_CARBON}" \
--argjson requires "${REQUIRES}" \
--argjson runicnpc "${RUNICNPC_API}" \
--argjson files "${FILES}" \
'{
component: $component,
@@ -364,6 +369,7 @@ jobs:
min_oxide_version: $min_oxide,
min_carbon_version: $min_carbon,
requires_plugins: $requires,
runicnpc_api: $runicnpc,
files: $files
}' > "${STAGE}/manifest.json"
echo "----- manifest.json -----"

View File

@@ -77,3 +77,15 @@ min_carbon_version = "2.0.259"
# asked of a server without it is refused with a reason, and chat is the default.
# BetterChat is optional in the same way — titles and group styles wait for it.
requires_plugins = ["Kits", "ZoneManager"]
# ── The RunicNPC API the bridge needs ───────────────────────────────────────
#
# RunicNPC (docs/runicnpc/PLAN.md stage 4): the bridge's npc.* commands call
# RunicNPC's API and need at least this version of it. It MUST equal
# `RunicNpcApiNeeded` in RunicGateway.cs; `node scripts/checkPlugin.js` holds the
# two equal. The release copies it into manifest.json, and the installer's
# bundle carries the latest RunicNPC release that answers at least it (D224).
#
# RunicNPC is optional to the bridge until its stage 9: without it every npc.*
# command answers `runicnpc-missing`, and events place Rust's own scientists.
runicnpc_api = 3

View File

@@ -198,6 +198,14 @@ function check(source, toml) {
// 5. The two declaration sites this repository owns must agree.
const inCode = /ProtocolVersion\s*=\s*(\d+)\s*;/.exec(source)
// runicnpc stage 4: the RunicNPC API the bridge needs, declared twice like the protocol.
const npcToml = /^\s*runicnpc_api\s*=\s*(\d+)\s*$/m.exec(toml)
const npcCode = /const\s+int\s+RunicNpcApiNeeded\s*=\s*(\d+)\s*;/.exec(source)
if (npcCode && !npcToml) problems.push('the plugin needs a RunicNPC API (RunicNpcApiNeeded) and overlay.toml declares no runicnpc_api')
if (npcCode && npcToml && npcCode[1] !== npcToml[1]) {
problems.push(`the plugin needs RunicNPC API ${npcCode[1]} and overlay.toml declares runicnpc_api = ${npcToml[1]}. The bundle picks a RunicNPC by the second, so they must agree.`)
}
const inToml = /^\s*protocol\s*=\s*(\d+)\s*$/m.exec(toml)
if (!inCode) problems.push('could not read ProtocolVersion from the plugin source')