Skip to content

fix(logs): address the #595 review — cancel the body, surface auth errors, poll for --since - #612

Merged
davidsu merged 1 commit into
mainfrom
fix/logs-review-followups
Aug 31, 2026
Merged

fix(logs): address the #595 review — cancel the body, surface auth errors, poll for --since#612
davidsu merged 1 commit into
mainfrom
fix/logs-review-followups

Conversation

@davidsu

@davidsu davidsu commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Follows up #595, which merged with an unactioned review. Three findings from @guyofeck plus @netanelgilad's one-liner. Nothing here overlaps #611 in intent, but both touch logs.ts — whichever lands second will need a trivial rebase.

1. The response body was never cancelled

readLines' finally called releaseLock() and nothing else, so exiting the generator on a typed end frame left the body unconsumed. An unconsumed body keeps its socket alive in the fetch pool, so a long-lived tail's reconnects pile up connections. The finally now cancels; the silence path's own cancel() became redundant and just returns.

Scope check, because the first version of this PR body overstated it: this is client-side hygiene, not a server-side subscriber leak. apper's sse_log_stream returns right after it yields the end frame, and its own finally fires detach() and close_stream() — so on the ordinary 5-minute rollover the server has already let go before the CLI reconnects. The narrow case where an abandoned socket could hold a server-side subscriber is a consumer that walks away from a still-live stream, and the silence path already cancelled explicitly for that. The apper side assessed it independently: bounded to ≤300s by the generator's own lifetime cap, whose finally detaches.

2. Auth failures were classified as transient

buildStreamUrl() and buildStreamAuthHeaders() were evaluated inside the fetch() arguments, i.e. inside the try. A logged-out user's readAuth() throw was therefore caught as {kind: "transient"} and laddered 1+2+4+8s before the real error surfaced through the poll path. Both are now built before the try, so a missing or unrefreshable token propagates immediately.

Repro on main: base44 logs -f --function foo while logged out — a 15s pause before the login error.

3. --since + --follow polls instead of erroring

Before #595 this worked: the first poll passed since through, giving backfill-then-tail. #595 turned it into a hard error, and that shipped in v0.1.13.

This restores the old behaviour as a deliberate compatibility concession, not as a feature. Anyone whose scripts already pair the two flags keeps working. The design intent is unchanged and stream-only: a live tail carries what happens next, --since is not part of the streaming story, and the polling path it falls back to is transitional — it goes away once realtime covers every app. So the combination is accepted with the simplest semantics available and nothing more: a run that asks for the past never attempts the stream. No capability check, no backfill-then-stream handover, no silent gap between the two.

Warning: --since reads the past, so this run polls instead of streaming (lines may lag ~20-30s).

--until and --order stay rejected with --follow.

Docs note: base44-troubleshooter in the skills repo says --since cannot be combined with --follow. That is the right thing for the design and for where this is heading, so it is deliberately not being rewritten to advertise the pairing; at most it earns a one-line "accepted today, polls" aside. The dependency is recorded on the apper side's rollout file with the exact locations, so a future owner can decide.

4. delay is now native

setTimeout from node:timers/promises, replacing the hand-rolled promise wrapper.

Tests

The old spec asserted the rejection. It is replaced with a live-process spec that runs --follow --since, waits for the warning on stderr, and asserts the message is the --since one — the two stream-failure warnings say "not available for this app" or "Could not reach" instead, so that line is proof the stream was skipped rather than attempted and refused.

Full suite 736 passed / 17 skipped; typecheck and lint clean.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

🚀 Package Preview Available!


Install this PR's preview build with npm:

npm i @base44-preview/cli@0.1.13-pr.612.385bd7a

Prefer not to change any import paths? Install using npm alias so your code still imports base44:

npm i "base44@npm:@base44-preview/cli@0.1.13-pr.612.385bd7a"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "base44": "npm:@base44-preview/cli@0.1.13-pr.612.385bd7a"
  }
}

Preview published to npm registry — try new features instantly!

…rors, poll for --since

Three findings from the review of #595 that were never actioned before it
merged, plus the review's one-line suggestion.

- Cancel the response, don't just unlock the reader. readLines' finally
  called releaseLock() only, so exiting on a typed `end` left the body
  unconsumed, and an unconsumed body keeps its socket alive in the fetch
  pool -- a long tail's reconnects pile them up. The silence path's own
  cancel() is now redundant, so it just returns and lets the finally do
  it. This is client-side hygiene: the server ends its own generator
  after the end frame and detaches there, so a rollover does not leave a
  subscriber behind.

- Build the URL and auth headers before the try. They were evaluated
  inside the fetch() arguments, so a logged-out user's readAuth() throw
  was caught as a transient failure and laddered 1+2+4+8s before the
  real error surfaced. A missing token is not something to retry.

- `--since` with `--follow` now warns and polls instead of erroring. It
  worked before #595 (the first poll passed `since` through) and #595
  turned it into a hard error, which is a break on a shipped flag pair.
  A stream only carries what happens next, so a run that asked for the
  past skips the stream entirely rather than opening one and backfilling
  around it.

Also: `delay` is now `setTimeout` from node:timers/promises, per review.
@davidsu
davidsu force-pushed the fix/logs-review-followups branch from 9203691 to 656f72d Compare August 31, 2026 11:39
@davidsu
davidsu merged commit 0a032dc into main Aug 31, 2026
16 checks passed
@davidsu
davidsu deleted the fix/logs-review-followups branch August 31, 2026 13:09
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.

2 participants