Make short TRACE captures independently explain OneBot action and event lifecycles from complete input through a truthful terminal conclusion. Preserve request correlation across asynchronous work while keeping disabled TRACE paths allocation-free and lazy.
Cover shutdown rejection, stream cancellation, event conversion and suppression, reporter invocation settlement, and HTTP delivery outcomes without changing established transport or observer behavior. Add focused regression coverage for lifecycle uniqueness, context reuse, disconnect handling, and quiesce ingress boundaries.
Serialize desired network plans, isolate per-adapter apply failures, restore prior bindings when possible, and report saved versus applied state. Quiesce Action and event ingress, await in-flight dispatches and transport closes, retain failed generations for retry, and propagate shutdown failures.
Foundation for NapCat-compatible Stream API actions (chunked file
upload/download): a single request answered by many response frames.
- streaming.ts: StreamSink + frame wrappers (wrapStreamFrame /
wrapStreamTerminal) matching NapCat's wire — each frame an OB11 envelope
with stream:'stream-action', data.type ∈ stream/response/error, and
message mirrored into wording.
- ApiHandler: registerStreamAction / isStreamAction; handle() threads a
sink; processStreamRequest drives WS multi-frame dispatch with optional
liveness-based abort; dead processRequest removed.
- action-kit: defineStreamAction (sink-aware run, registers as a stream
action) reusing defineAction's parse/describe.
- adapters: HTTP server streams chunked \r\n\r\n-delimited frames (no
Content-Length, no false application/json), aborting on client
disconnect; WS server/client use a backpressure-aware safeSendAsync and
abort a stream once the socket closes.
No stream actions are registered yet — those land in the next phases.
Verified by two adversarial review passes. 480 onebot tests green.
The four NetworkAdapters each carried a byte-identical reload() state machine (sigChanged/wasEnabled -> Reopened/Closed/Opened/Normal). Lift it into IOneBotNetworkAdapter as a template method; per-adapter variation is now expressed via three hooks: bindingSignature(), willEnable() (default enabled!==false; clients add && !!url), and onConfigReplaced() (post-swap side effects).
Also delete four redundant 'override get isActive()' (identical to the base getter), lift the duplicated uin-logger setup into the base ctor, and drop http-post's now-redundant signature_ cache.
NetworkReloadType is a test-observability seam -- the sole production caller (instance.applyConfigDiff) discards it -- so a new base-level test exercises all transitions directly. Behaviour-preserving: +94/-157 in src, full workspace typecheck clean, 332 onebot tests pass.
Phase 3 of the 4-package split (RoadMap #3). Moves the entire
`packages/core/src/onebot/` subtree (43 source files + 20 test files
spanning network adapters, action handlers, event converters, message
parser, message store, media store, instance + manager) into the new
`@snowluma/onebot` workspace package. The remaining `@snowluma/core`
now hosts only the api-layer (Bridge class + 13 Api classes), the
hook tooling, the WebUI server, and the runnable `index.ts`.
Why
---
- The OneBot adapter is the public-facing OneBot11 protocol surface;
it has its own version cadence and its own consumers (third-party
bots talking OneBot WS / HTTP). Splitting it lets that cadence move
independently of the Api-on-ctx layer.
- The two were sharing the `packages/core/` build pipeline only
because they happened to live in the same directory. Nothing in
the api-layer reaches into OneBot internals.
- Mirrors NapCat / Lagrange.OneBot's repo split (their OneBot adapter
is a separate assembly from the Core protocol library).
Layout
------
Moved via `git mv` (history preserved):
packages/core/src/onebot/* → @snowluma/onebot/src/<x>
packages/core/tests/onebot/* (6 files) → @snowluma/onebot/tests/
packages/core/tests/{message-parser,message-store,event-pipeline,
media-store,media-url-resolver,event-converter,
http-server-hot-reload,http-server-content-type,
api-handler-logging,message-id,group-msg-emoji-like,
config,media-indexer,network-manager}.test.ts (14 files)
→ @snowluma/onebot/tests/
What stays in @snowluma/core
----------------------------
- `bridge/{bridge,bridge-interface,bridge-context,manager}.ts`
- `bridge/apis/*` (the 13 typed Api classes)
- `hook/*` (debugger/injector — tooling for the core protocol layer)
- `webui/*` (admin UI server — application wiring, not the OneBot
adapter itself)
- `index.ts` (application entry that wires BridgeManager +
OneBotManager + HookManager + WebUI together)
Changes
-------
- New package `packages/onebot/` (package.json with subpath exports
mirroring legacy `core/src/onebot/<x>` 1:1, tsconfig.json,
vitest.config.ts with `SNOWLUMA_LOG_LEVEL=debug` env so the action-
dispatch logging tests see debug-level subscribers — copied verbatim
from core's vitest.config.ts).
- @snowluma/core grows new subpath exports for the api-layer files
OneBot's type-only imports need: `./bridge`, `./bridge-interface`,
`./bridge-context`, `./manager`, `./apis`.
- `@snowluma/onebot` depends on `@snowluma/bridge`, `@snowluma/common`,
`@snowluma/core`, `@snowluma/proto-defs`, `@snowluma/proton`,
`@snowluma/websocket`.
- `@snowluma/core` depends on `@snowluma/onebot` (for index.ts +
webui/server.ts wiring).
- 100+ import sites rewritten via sed:
* OneBot → core: `'../bridge/bridge'` → `'@snowluma/core/bridge'`,
`'../bridge/manager'` → `'@snowluma/core/manager'`,
`'../bridge/bridge-interface'` → `'@snowluma/core/bridge-interface'`,
`'@/bridge/bridge'` (vite path alias) → `'@snowluma/core/bridge'`.
* Core → OneBot: `'./onebot/manager'` → `'@snowluma/onebot/manager'`,
`'../onebot/config'` → `'@snowluma/onebot/config'`, etc.
* Test relative paths: `'../../src/onebot/<x>'` → `'../src/<x>'`
after the test file move (one level shallower).
- New `packages/onebot/src/env.d.ts` declaring `__APP_VERSION__` and
`__BUILD_WEBUI__` (the vite-injected globals `info.ts` reads).
Workspace cycle
---------------
core ↔ onebot is allowed by pnpm workspaces. The runtime direction
is one-way: core → onebot (index.ts constructs OneBotManager,
webui/server reads OneBot config). OneBot only reaches into core for
TYPES (Bridge, BridgeInterface, BridgeManager, ClientKeyInfo,
DownloadRKeyInfo); those imports use `import type` so they compile
out at runtime and don't introduce a real cycle.
Verification
------------
- Full workspace `pnpm -r typecheck`: all 11 projects clean.
- `@snowluma/core` test suite: 302/302 pass (33 test files).
- `@snowluma/onebot` test suite: 250/250 pass (20 test files;
pre-existing `network-manager.test.ts` native-addon failure
unchanged — same baseline as before the split).
- Combined: 552/552 — identical pass count to the pre-Phase-3
baseline.
- Vite build with stubbed native binary: clean, 2.84 MB bundle /
224 KB gzip (identical envelope).