Skip to content

feat: bili CLI + debug in config + README rewrite - #7

Merged
ranxianglei merged 1 commit into
masterfrom
2026-08-08_bili-cli
Aug 8, 2026
Merged

ranxianglei merged 1 commit into
masterfrom
2026-08-08_bili-cli

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

What

  • ** CLI** — new primary command with subcommands and flags. bili-proxy kept as alias for backward compat.
  • debug configurable from config file — "debug": true in billion-context.json now works (was env-only). Three ways: flag > env > config.
  • README Usage rewritten — bili is the documented entry point; routing examples updated; Debugging section added.

CLI usage

bili                              # start (reads ~/.config/billion-context/billion-context.json)
bili --port 9000 --debug          # override port + verbose
bili --config ~/my-bili.json      # use a different config file
bili --help / --version

Files

  • src/cli.ts (new) — arg parser + command dispatcher
  • src/index.ts — delegates to cli (both bili and bili-proxy bins point here)
  • src/paths.ts — configFile() respects BILI_CONFIG_FILE env (--config flag)
  • src/server.ts — fix startup banner printing [object Object] for {url,models} routes
  • package.json — add bili bin, keep bili-proxy
  • README.md — Usage + Debugging sections rewritten

Verification

  • typecheck ✅ (src + full tsc --noEmit 0 errors)
  • 145 tests pass
  • build ✅
  • manual: bili --help / --version / start --port / --debug all verified

Follow-up to PR#6 (master after merge).

- Add src/cli.ts: bili command with subcommands (start default), flags
  (--port/--host/--config/--debug/--passthrough) that override the config
  file and env. bili --help / --version supported.
- package.json: add 'bili' bin (keep 'bili-proxy' as alias).
- paths.ts: configFile() now respects BILI_CONFIG_FILE env (--config flag).
- server.ts: fix startup banner printing [object Object] for {url,models}
  routes — now shows the real URL.
- README Usage rewritten: bili start is the primary entry, debug documented
  as flag/env/config (three ways), routing examples updated.
@ranxianglei
ranxianglei merged commit 328d68d into master Aug 8, 2026
2 checks passed
ranxianglei added a commit that referenced this pull request Aug 10, 2026
- loop #7 (B2): successful compress KEEPS active record in round 2
  (compressCallId threading prevents the re-compress loop class).
  Requires a 7-message corpus + >=5000-char messages + >=50-char summary
  to clear the kernel's protected-zone / minCompressRange / minSummaryLength gates.
- loop #8 (B3): textProtocol round-2 body has NO function_call items.
- loop #9 (S2): responses session.stats.lastInputTokens populated via usage.
- loop #10 (S3): upstream-500 mid-loop terminates cleanly (timer cleared).
- adapters S1: openai single finish chunk (no double emit).
- makeCtx now uses defaultConfig(200000) so applyCompression has full config.
- drain() parametrized with optional adapter arg for textProtocol tests.
237/237 pass.
ranxianglei pushed a commit to wangbl1010/billion-context-wang that referenced this pull request Sep 23, 2026
…ei#7)

Two concurrent claude sessions share one stable-port proxy, but the
parent-gone watchdog watched only the SPAWNING session's host: when it
exited, the shared proxy died under every other live session
(Connection refused mid-session).

- server: the watchdog is now a watcher SET. The spawning hook seeds it
  via BILI_PARENT_PID; attached sessions register their claude host via
  POST /__bili__/watcher. Shutdown fires only when every owner is gone.
  Single-owner proxies keep the historical 'parent-gone (pid N)' reason;
  multi-owner deaths log 'watchers-gone (pids: ...)'.
- server: daemon proxies (no BILI_PARENT_PID) refuse registrations with
  409 — a registration must never arm a lifetime watchdog on a daemon.
- hook: after attaching, register the resolved claude host pid (2s
  timeout, fire-and-forget: failure only degrades to the old
  single-owner watchdog, never blocks session start).
- tests: platform-neutral route contract (400/409/200 + armed proxy
  exits when its keeper dies) and a linux two-session e2e pinning
  'survives the first exit, dies after the last'.
ranxianglei pushed a commit to wangbl1010/billion-context-wang that referenced this pull request Sep 23, 2026
…anxianglei#7)

Live regression caught a race the watcher set alone does not close: the
spawner exited 1s after a second session launched, and the first sweep
after its death killed the proxy before the attacher's registration
landed — the second session still went down.

The watchdog now shuts down only after the watcher set has been EMPTY
for WATCHER_IDLE_GRACE_MS (5s). Any registration in the window (late
attacher, resumed session) re-populates the set and cancels the pending
shutdown; an abandoned proxy still disappears within ~7s of its last
owner.

Route test pins the semantics platform-neutrally: keeper1 dies, keeper2
registers 3s later, proxy survives past the grace window, then dies
within ticks+grace after keeper2.
ranxianglei pushed a commit to wangbl1010/billion-context-wang that referenced this pull request Sep 23, 2026
… review)

The watcher route was spelled /__bili__/watcher (double underscore) —
the only such URL in the file. isAdminPath matches startsWith("/__bili/"),
so the route silently skipped the tunnel-header, loopback and
trusted-origin gates. Rename to /__bili/watcher and pin the placement
with regression asserts (x-bili-tunnel and cross-origin POSTs must be
403-rejected by the gate, never reach the route).

Also stop warning on HTTP 409 from watcher registration: that is the
daemon refusal — a proxy with no watchdog already outlives every
session, nothing to register and nothing to warn about.
ranxianglei pushed a commit that referenced this pull request Sep 23, 2026
…ed proxy (#1190)

ensureProxyRunning now registers the attaching caller's owner pid (opts.parentPid ?? process.pid — the same expression the spawn path passes as BILI_PARENT_PID) with the shared proxy's parent-gone watchdog on every attach path, so the proxy dies only after the LAST owner exits (#7/#1183) instead of the first. Covers kimi/zcode MCP children, pi/omp/opencode/dsh in-host plugins, and bili <client> launchers. Registration is awaited before returning; 409 (daemon proxy) is silent, any other failure warns and degrades to the pre-fix single-owner behavior. Claude's now-redundant explicit registration block in claude-native-bootstrap.ts is removed (Set dedupes; same pid/endpoint/policy).
ranxianglei pushed a commit that referenced this pull request Sep 23, 2026
…ed proxy (#1190)

ensureProxyRunning now registers the attaching caller's owner pid (opts.parentPid ?? process.pid — the same expression the spawn path passes as BILI_PARENT_PID) with the shared proxy's parent-gone watchdog on every attach path, so the proxy dies only after the LAST owner exits (#7/#1183) instead of the first. Covers kimi/zcode MCP children, pi/omp/opencode/dsh in-host plugins, and bili <client> launchers. Registration is awaited before returning; 409 (daemon proxy) is silent, any other failure warns and degrades to the pre-fix single-owner behavior. Claude's now-redundant explicit registration block in claude-native-bootstrap.ts is removed (Set dedupes; same pid/endpoint/policy).
ranxianglei pushed a commit that referenced this pull request Sep 30, 2026
…still watch (#1753 shutdown side)

runLaunch's finally called stopProxy unconditionally for spawned
handles — POSIX process-group kill took down instances that ATTACHED
sessions were actively watching, burning their 3 client retries until
some later wrapper re-spawned an instance on the same port. The
server's watcher-set watchdog (#7, last-owner-exits semantics) never
got to run.

stopProxyGuarded consults /__bili/health before killing: if the live
watcher set still contains pids other than ours, the instance is
spared with a one-line notice and the server retires it after its last
watcher leaves (WATCHER_IDLE_GRACE_MS). Health-parse failures degrade
to the old kill behavior; attached handles and win32 are untouched.

- HealthInfo.watchdog gains optional watchers: number[] (parsed in
  fetchHealthInfoDefault)
- runLaunch + runTestPi finally paths now use the guard
- 6 new tests: spare-with-other-watchers, last-watcher kills, health
  failure degrades, absent watchers kills, attached no-op, runLaunch
  integration (fake client exit leaves shared proxy alive)
ranxianglei added a commit to yingzhongshuoqi/billion-context that referenced this pull request Oct 7, 2026
…to src/server/admin.ts (ranxianglei#1440 P2 cut 1)

Strict pure move (ranxianglei#1440 P2 cut 1): zero renames, zero logic edits, zero behavior
change. The SECURITY gate block at the top of handle() (self-probe url
normalization, isAdminPath computation, tunnel-header 403, loopback 403,
trusted-origin 403/404, the whole inline GET/PUT/POST dispatch for
/__bili/* + /__acp/* incl. config/plugin/watcher/upstream routes, and the
unknown-admin-path 404 fallback) moved verbatim into the single entry point
handleAdminRoute(req, res, ctx) in src/server/admin.ts. The admin-only helpers
(handleConfigReload, sendCacheReport, handleConflictsClear, sendStats,
currentAdvisoryPayload, sendStatus, sendResignStatus, sendOverview,
sendWebSessions, sendWebLogs, sendWebSessionDetail), the admin-origin trio
(normalizeAdminHostname/isTrustedAdminOrigin/adminTrustedHostnames, used ONLY
by the gate) and the splitWarnedBases canary set (used ONLY by sendStatus)
traveled with their doc comments intact; reapOrphansLogged (non-admin, called
from four request-pipeline sites) stayed behind. Byte-identity: every deleted
line from server.ts appears verbatim in admin.ts (multiset diff empty except
two blank-line normalizations where block removals had created double blanks).

Deliberately stays in server.ts: the top-of-file proxy-bypass /__bili URL
special case (~line 222), destAdminPath in forward() (~line 5254), the
__biliKeepaliveBoundary / __bili_inplace_sysdev wire markers, selfAdminProbePath
(test-imported export, now also consumed by admin.ts), headerValue (dual-use
with non-admin pipeline code → newly exported), readBody/BodyTooLargeError
(already exported). Log strings mentioning /__bili URLs are unchanged.

AdminCtx fields (all passed BY REFERENCE so mutations stay visible to
server.ts): opts (ProxyOptions — config PUT/reload mutate it in place), core
(CompressionCore — plugin status/tool handlers), config (Config — plugin
manifest resolution via applyCompressSettings), log (tee logger fn), instanceId
+ instanceStartedAt (health endpoint identity), proxyWatchers (Set<number>,
watcher POST mutates; health reads), initialWatcherPid (watchdog arming state).
adminCtx is assembled once per startServer() call because these are per-server
instance values (per-ranxianglei#7 watchdog sets are intentionally per-server, like
instanceId) and threaded through handle() as its final parameter — this keeps
admin.ts exporting exactly ONE symbol (handleAdminRoute; AdminCtx stays
module-private and server.ts types the param as Parameters<typeof
handleAdminRoute>[2]).

Call-site control flow: the original zone FELL THROUGH to the proxy pipeline
for every non-admin URL (it only short-circuits when isAdminPath matched,
after which every path ends the response), so the replacement is
`await handleAdminRoute(req, res, adminCtx); if (res.writableEnded) return;`
— response-ended ⟺ the admin surface answered; non-admin traffic never has a
byte written by the moved code, so fall-through is byte-for-byte the old path.

Verification: tsc --noEmit clean; tsup build OK (dist-import-annotations OK);
18-file behavioral matrix (web-routing, web-sessions, advisory-endpoint-parity,
admin-origin, admin-probe, web-conflict-banner, web-logs-view,
watcher-registration, apig-resign, watchdog-health, watchdog-health-daemon,
conflict-lifecycle, plugin-status-provenance, plugin-fork, runtime-info,
issue833-panel-effective-config, issue532-panel-system-tokens, wire-contract)
= 191 pass / 0 fail. reference/architecture.md: one line added for admin.ts
(docs-sync file map).

Closes part of ranxianglei#1440 (P2 cut 1 of the server.ts god-file decomposition).
ranxianglei pushed a commit that referenced this pull request Oct 12, 2026
…er note (#2837)

Review fix for the #2854 note. The CLIENTS.md / CLIENTS.zh-CN.md paragraph
added by this PR said "in plugin mode the proxy owns compression", which
contradicts the TECHNICAL-NOTES.md "Two compression modes" section it cites
(line 175/184), AGENTS.md KDD #7 and server.ts:1309 — all state the AGENT
executes compress in plugin mode (dsh stamps x-bili-plugin: dsh, CLIENTS.md:19).
Source issue #2854 never claimed ownership; its lesson is only that the host's
durable log stays lossless, so a local meter over-measures. Removed the false
clause (EN + zh mirror); the divergence lesson and the /acp +
/__bili/plugin/status contextTokens pointer are unchanged.
cskwork pushed a commit to cskwork/billion-context that referenced this pull request Oct 21, 2026
…anxianglei#2)

Review ranxianglei#2 findings (more comprehensive than ranxianglei#1):

ranxianglei#2 (compress-loop*.ts): fetch-timeout timer leak across compress-loop
rounds. activeClearTimer = clearTimer overwrote the previous round's
timer without clearing it, so rounds 1..N-1 each leaked a 10-min timer
that self-fired harmlessly but accumulated handles in the event loop
over a long session. Clear the previous timer before overwriting
(3 lines, both openai and responses variants).

ranxianglei#5 (orphan-gc.ts): orphanStreak was monkey-patched onto the kernel's
Block type via (block as unknown as {orphanStreak?:number}).orphanStreak
— fragile, no type safety, breaks if the kernel Block shape changes.
Replaced with a module-level WeakMap<object, number> keyed by the block
object. TS now type-checks the access; counts are GC'd when a block is
dropped.

ranxianglei#7 (README.md): stale test count (79 → 136) and missing env-var docs
(BILI_PERSIST, BILI_PERSIST_DEBOUNCE_MS, MAX_SESSIONS,
BILI_SESSIONS_DIR). Both fixed.

ranxianglei#8 (tests/*.test.ts): 7 type errors under tsc --noEmit (role: string
not narrowing to the union literal; Session literals missing new fields
after inFlight/persisted/blockContents were added). Fixed with 'as
const' on roles and the missing fields. Full tsc --noEmit now 0 errors.

ranxianglei#10 (persist.ts): comment referenced AGENTS.md which doesn't exist;
replaced with an inline note on the known concurrency limitation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant