Allow SnowLuma to discover and restore accounts that were already active before startup.
Avoid reporting sessions ready before they can serve requests, preserve account changes, and keep discovery failures observable.
Add regression coverage for delayed startup, recovery, and process isolation.
Refs #305
Refs #307
QQ's silent hot-update churns the process table; the watcher's synchronous
native getAllMainProcess() (/proc walk reading cmdline/maps) blocks while a
process is mid-exec(). Running on the main event loop, that froze the whole
bot — OneBot stopped responding, the watcher stopped ticking, the supervisor
saw it 'alive' so never restarted it (silent death until manual restart).
- run the native enumeration in a worker_threads worker, raced against a
deadline on the main thread; a blocked call no longer stalls the loop
- on timeout/failure return an UNKNOWN sentinel (null); PipeWatcher keeps the
prior process+pipe snapshot instead of treating missing data as 'all QQ
gone' (which would dispose live sessions and tear down a healthy hook)
- graceful fallback to in-loop enumeration when the addon can't load in a
worker or there's no addon (macOS); custom listers (tests) bypass the worker
- bounded orphan workers across a sustained hang
Tests: PipeWatcher null-sentinel (no false process-gone, recovers); enumerator
sync-fallback + broken-isolation paths. Native /proc-walk hardening rides in
the nnphook repo separately.
Split the QQ NT client lifecycle / hook injection layer out of
@snowluma/core into its own self-contained package. The new bridge
package owns:
- native hook addon loader (injector)
- HookManager + per-PID HookSession orchestration
- PipeWatcher for QQ process discovery
- QqHookClient pipe protocol
- qq-port-probe for early UIN sniffing
The hook layer used to type its main collaborator as
`BridgeManager` directly. To keep the new package free of any
dependency on @snowluma/core, the cross-package coupling is now
expressed via a 3-method `BridgeManagerSink` interface that lives
in the bridge package. BridgeManager (in core) satisfies the
interface structurally with no source changes; the top-level
app/webui still inject it the same way.
Pure file relocation otherwise: git mv preserves history at 100%
on every file, the only logic edit is the interface introduction
in hook-manager.ts and an updated `nativeSearchDirs()` offset
(one fewer `..` since hook now lives at packages/bridge/src
instead of packages/core/src/hook).