Repository navigation
Commit e07b115
* loop(LOOP-01): route vault/vision auth through ApiClient, not raw fetch()
downloadSpacesFile/uploadFile/deleteSpacesFile (vault.ts) and
downloadMediaFile (vision.ts) issued their own raw fetch() calls via
private-member casts into ApiClient's tokens/baseUrl/authHeaders, so they
never got PR #47's refresh-on-401 retry or its distinct 401/402/403 hint
UX (they threw plain Error, not HttpError, so errorHint()/hintFor()
could never classify them).
Add two public authed methods to ApiClient — getBinary() (streaming GET,
accepts a relative path or an absolute media URL) and postForm() (authed
multipart POST) — both going through the same refresh-on-401 retry and
toHttpError() classification as request()/stream(). deleteSpacesFile now
uses the existing deleteJson(). Delete vault.ts's _bearerToken/_baseUrl/
_authHeaders and vision.ts's authHeaders() cast entirely — no file outside
transport.ts needs to know ApiClient's internal field names anymore.
Also fold downloadSpacesFile's res.arrayBuffer() into downloadFile() and
stream the response body straight to disk instead of buffering the whole
file in memory (mirrors vision.ts's existing pipeline() pattern).
Add test/vault_vision_401.test.ts mirroring auth_401.test.ts's
fetch-stubbing pattern: 401-then-refresh-then-retry for all four
functions, HttpError classification + hintFor 401 wording for
deleteSpacesFile, and a regression check that downloadMediaFile still
fails closed on an insecure (non-loopback http) media host.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* loop(LOOP-01,LOOP-06): stop masking device-poll network outages as authorization_pending
pollForToken's catch block folded ANY thrown error into authorization_pending,
including raw network failures (ECONNREFUSED, DNS, timeout) with no HTTP body
— an outage during `aether auth login` read as silent normal polling for the
whole expires_in window. Now only an HttpError carrying a real body (the
documented 400 pending/slow_down/expired case) maps to ordinary poll state.
Any other error counts as a network failure; after 3 in a row it prints a
distinct "can't reach the server" warning, and if the deadline is hit while
still failing it throws a distinct timeout message instead of the generic
"login timed out — run `aether auth login` again" (which reads as "too slow").
Adds device.test.ts coverage for: a transient blip that still succeeds with
no warning, a sustained outage that fails with the distinct message, and
genuine authorization_pending polling to deadline with the original message.
* loop(LOOP-06): distinguish a rejected session from a network outage in `aether auth status`
renderAuthBox's /models probe folded a 401/403 (server actively rejected the
stored token — expired or revoked session, the stale-AETHER_TOKEN case PR #47
fixed elsewhere) into the same bare catch used for a genuine network outage.
Either way the panel printed "Authenticated" with Token/API rows, telling the
user they're signed in when the server just said otherwise — `status` had no
error state at all for this case.
Now an HttpError with status 401 or 403 renders a distinct "Session expired"
panel (swapped header/icon, a "sign in again" hint instead of Tier/Default),
title included. Any other failure (no HttpError, or a different status,
including 5xx) keeps the original silent local-only fallback — an outage or
a plan-only 429/etc. is not proof the session itself is dead.
Deliberately unchanged: cmdAuth's `status` exit code stays 0 in the expired
case (the finding asked for a distinct rendered state, not an exit-code
contract — changing that would need renderAuthBox to leak expired-ness out
to its caller, which is out of scope here).
Extends test/auth_401.test.ts (the repo's fetch-stubbing pattern) with four
renderAuthBox cases: 401 -> Session expired, 403 -> Session expired, a real
network throw -> still Authenticated (no regression), and a normal 200 ->
Authenticated with tier/default.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* loop(LOOP-01,LOOP-06): bound ApiClient.request() by default, throw on malformed 2xx bodies
Root fix (transport.ts): ApiClient.request() (backing getJson/postJson/
deleteJson) had no timeout at all, unlike stream()'s 120s default — a
silently-dropped connection to /models, /auth/refresh, or the device-poll
endpoint just hung forever. Now bounded by AETHER_REQUEST_TIMEOUT_MS
(default 30s, mirrors AETHER_STREAM_TIMEOUT_MS; 0 disables it), using the
same raceAgainst/net-controller pattern stream() already uses so a timeout
releases the socket without ever being confused with a caller's own abort.
Also fixed a latent bug this surfaced: raceAgainst's early-return for an
already-aborted signal discarded its `promise` argument without a catch,
which could leak an unhandled rejection — now swallowed, closing the same
gap in stream()'s pre-existing use of raceAgainst too.
Also: a 2xx response whose body fails to parse as JSON used to silently
become `undefined as T`, so callers that trust T (slash.ts's getCatalog ->
`cat.models`, auth.ts's authRefresh -> `r.session_token`) crashed one layer
up with a raw property-access TypeError. request()/postForm() now throw a
new MalformedResponseError (extends HttpError) instead, wired into
errorHint()/hintFor() for a normal, hinted message. A genuinely EMPTY body
(204 No Content, etc.) is unaffected — classified by reading the text first,
not by sniffing JSON.parse's error message, so a body truncated mid-object
by a dropped connection is correctly treated as malformed, not empty.
loginWithPassword() (the headless --username/--password flow, a raw fetch
with no ApiClient behind it) gets its own bounded default via the same env
var, guarded so an explicit 0 doesn't become an instant AbortSignal.timeout(0).
Regression-prevention carve-outs (required by the new default, not scope
creep): a flat 30s would kill any non-streaming call that legitimately runs
long. postJson/getJson/deleteJson gained an optional per-call timeoutMs
override for exactly this. Applied it to: chat.ts's/brain_cloud.ts's/
client.ts's CHAT_PATH non-streaming chat fallback (a full LLM turn), and
bench.ts/test_drive.ts/workflow.ts's project-conversion calls (block
server-side on completed generation/iteration work, not a job handle) —
each now opts into stream()'s own defaultStreamTimeoutMs() instead. Left
orchestrator.ts's delegate/broadcast/gather and audit.ts's exportProof at
the 30s default — those return a job handle or a fast confirmation, not
completed heavy work.
Tests: test/transport_request.test.ts (new — default timeout, per-call
override in both directions, malformed-vs-empty-vs-truncated body
classification, errorHint/hintFor wiring), test/long_running_timeout_overrides.test.ts
(new — bench/test_drive/workflow survive a slow-but-healthy response),
test/chat.test.ts (fallback survives past the metadata default),
test/device.test.ts (a single stalled poll request times out instead of
blocking pollForToken's loop), test/auth.test.ts (loginWithPassword times
out / 0 disables it), test/abort.test.ts + test/slash_abort.test.ts (updated
for request()'s new net-controller wiring — signal identity is no longer
preserved end-to-end, same tradeoff stream() already made; behavioral
equivalence asserted instead).
Note for follow-up: startTestDrive is bounded at the 120s stream default,
but it's an iterate-until-green loop that could plausibly run longer in
practice; prior behavior was fully unbounded. Revisit if real TDD loops
exceed 120s.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* loop(LOOP-01): unify injected-token TokenStore selection between client.ts and auth.ts
AetherClient's constructor and tokenStoreFromEnv() each hand-maintained their
own copy of the "injected AETHER_TOKEN -> TokenStore" decision, untested
against each other and free to silently diverge. Factor the shared skeleton
into auth.ts's new tokenStoreForInjected(injected, {persistOnLogin}), with the
one axis that legitimately differs (whether an explicit login persists to
disk) passed explicitly at each call site: tokenStoreFromEnv keeps
persistOnLogin:true (PR #47's CLI-entry fix), AetherClient keeps
persistOnLogin:false (a library embed's login() must not clobber the
standalone CLI's on-disk session). Add regression tests pinning both
directions so the two paths can't drift again.
* loop(LOOP-06): unify printError and Renderer.error's terminal formatting
Extract a shared formatErrorLine helper (src/ui/error_line.ts) so a
client-caught error (chat.ts's printError) and a mid-stream server-emitted
error frame (render.ts's Renderer.error) render the identical "✗ msg" glyph,
dim " ⤷ hint" line, and trailing blank-line separator — the same "session
expired" moment no longer reads differently depending on which path caught
it.
* loop(LOOP-06): document intentional 5xx/network hint divergence, dedupe isAbortError
error_hints.ts's header comment implied hintFor and errors.errorHint were
unified beyond 401/402/403/429 (the only statuses httpStatusHint actually
owns). Their 5xx and network-failure wording was always intentionally
separate per-surface (errorHint knows the baseUrl; hintFor's public
signature doesn't), so make that explicit in comments instead of leaving
it looking like an unfixed bug.
Also stop maintaining a second, narrower isAbortError in error_hints.ts
(only checked err.name, unlike errors.isAbortError's cause-wrapped/regex
checks) — it was never imported anywhere in src/, only exercised by its
own test. Re-export errors.isAbortError instead so there's one
implementation to keep correct.
* loop(LOOP-01): gate getBinary's bearer on same-origin, bound getBinary/postForm by timeout, dedupe aek_ prefix check
- transport.ts: isCredentialSafeUrl() only checked scheme (any https host
passes), so getBinary() attached the live session bearer to ANY absolute
https URL a caller passed -- including a third-party media host completely
different from the configured Aether API baseUrl. Gate attachment on the
new isSameOrigin(target, baseUrl) instead; a cross-origin target is now
fetched unauthenticated rather than leaking the token or failing the whole
download closed (deliberate, security-relevant behavior change from the
prior fail-closed InsecureTransportError on non-loopback http targets --
see the updated tests in vault_vision_401.test.ts for the new contract).
- transport.ts: getBinary()/postForm() had no timeout/AbortSignal.timeout
bound at all, unlike request()/stream(), reintroducing the "stalled
connection hangs forever" bug a7b3621 fixed for request(). Both now accept
a timeoutMs override (default AETHER_REQUEST_TIMEOUT_MS), mirroring
postJson/getJson/deleteJson. getBinary additionally wraps its body with an
idle/quiet-period timeout (stream()'s withIdleTimeout, re-wrapped as a Web
ReadableStream) so a large-but-healthy download isn't killed by a flat
overall cap -- only a genuinely quiet connection times out.
- core/auth.ts: added the canonical isApiKeyToken() export; transport.ts's
refreshSession and commands/auth.ts's isApiToken both now delegate to it
instead of separately hand-checking the "aek_" prefix.
Regression tests: test/transport_getbinary_postform.test.ts (new -- same-
origin gating, cross-origin-no-auth-header, connect timeout, body idle
timeout, large-healthy-download-not-killed, caller-abort-still-wins,
postForm timeout); test/vault_vision_401.test.ts (updated for the new
same-origin contract, cross-origin http/https no-credential-sent cases, and
same-origin-insecure-baseUrl still fails closed); test/auth.test.ts (direct
isApiKeyToken coverage, not just transitively through isApiToken).
npm run build && npm test: 753/753 passing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* loop(LOOP-06): add StreamTimeoutError branch to errorHint — REPL now shows the retry/doctor hint for mid-turn stream timeouts, matching hintFor
* loop(LOOP-06): sanitize+cap server-supplied login text before it hits the terminal
loginWithPassword built its thrown Error directly from the server's raw
`reason` field, bypassing the control-char-strip-and-cap step toHttpError
applies to every other server-text path — login.ts's headless catch writes
that message straight to stderr with no sanitization of its own, so a
compromised/misconfigured backend could inject terminal escape sequences
(including OSC 52 clipboard-hijack payloads) via `aether auth login
--username/--password`. The success path had the same gap: `plan`/
`commitment_hash` reached login.ts's stdout write unsanitized too.
Extract toHttpError's strip-and-cap regex into a shared sanitizeServerText()
in transport.ts and apply it to reason/plan/commitment_hash in
loginWithPassword, so any server-supplied text loginWithPassword surfaces
gets the same treatment as every other HTTP-error path.
Adds regression tests in test/auth.test.ts (loginWithPassword-level) and a
new test/login.test.ts (full cmdLogin path, capturing process.stderr/stdout)
asserting a malicious reason/plan cannot survive into the terminal.
* loop(LOOP-01): route vault/media command failures through hintFor() — no more hardcoded "are you logged in?"
vault.ts's fail() and media.ts's fail() printed the SAME "are you logged
in?" hint for every failure, even though downloadFile/uploadFile/
deleteSpacesFile/dispatchGeneration etc. already throw a properly-classified
HttpError (401/402/403/5xx). media.ts's mediaGenerate() and all 9 catch
blocks in slash_media.ts printed only the raw err.message with no hint at
all. All of these now route through core/error_hints.ts's hintFor(), the
same fix already applied to workflow.ts's workflowNew, so a vault or media
402/403/5xx/network-outage shows the correct actionable hint instead of a
misleading or missing one.
* loop(LOOP-01): stream vault uploads via fs.openAsBlob() instead of readFileSync+Blob double-buffering
uploadFile() read the entire local file into a Buffer (fs.readFileSync)
and then copied it again into an in-memory Blob([data]) before any bytes
went over the wire — an unaddressed resource-consumption gap on the
upload side, even though downloadFile() was fixed to stream straight to
disk in 1d33357. Now hands FormData an fs.openAsBlob() Blob backed by
the open file handle itself, so a large vault upload no longer holds
the whole file in memory twice. Zero-dependency, matches this repo's
no-deps convention.
Deliberately skipped the finding's "at minimum, add a file-size guard"
fallback clause — that was only meant to apply if true streaming wasn't
feasible; since openAsBlob() removes the buffering entirely, an
arbitrary size cap would just reject legitimate large uploads for no
remaining safety reason.
Added a regression test that proves the fix bites: since fs.openAsBlob()
blobs re-validate the file on disk at read time (and a Buffer-backed
Blob copy does not), deleting the source file mid-upload now causes the
Blob read to fail — verified this test fails against the pre-fix code
and passes against the fix.
* loop(LOOP-06): print a loading line before aether auth status's /models call
renderAuthBox's first move is a network round-trip to /models with no
cache to fall back on, and cmdAuth wrote nothing to stdout until the
whole call resolved -- up to DEFAULT_REQUEST_TIMEOUT_MS (30s) of silence
on a slow connection, "the REPL just looks hung" (the exact class PR #47
fixed for slash.ts's catalog fetch). Print theme.dim("checking session...")
before awaiting renderAuthBox in both the "status" subcommand and bare
`aether auth` branches, matching the loading-line convention already used
by slash.ts/vault.ts/slash_media.ts.
Adds cmdAuth-level regression tests proving the loading line is written
while /models is still pending (not after), for both call sites.
* loop(LOOP-06): distinguish a picker key-handler fault from a deliberate Escape cancel
pickModel's onKey catch silently resolved null on ANY thrown error (render/
decode bug), exactly like a user pressing Escape. showPicker then printed
the same "kept current session." for both, hiding a real internal fault
from the user and from anyone debugging a picker regression.
Now the catch writes a distinct dim diagnostic ("picker error — kept
current session.") before resolving null, so the two paths are visibly
different in the transcript even though both still resolve null and
restore the REPL listeners the same way.
* loop(LOOP-06): style confirmSwitch's tier-lock message, add /tier hint
The "<id> is locked on tier <tier>" rejection in confirmSwitch was plain
unstyled text with no next step, unlike every other auth-adjacent surface
where a plan/tier restriction (HTTP 403 via httpStatusHint) gets a dim hint
pointing at /tier or `aether models`. It was also the only line in this
function not wrapped in theme.dim (the restart warning two lines down
already is).
Now the locked-item line is dim and appends the same actionable pointer
httpStatusHint(403) uses, so a tier lock reached via the picker reads the
same as one reached over the wire.
confirmSwitch is exported (alongside the existing resolveSelection) so this
can be unit-tested directly — the module-level catalog cache the /model
tests above populate isn't force-refreshable in-band, so a differently-
available item can't reach this branch through handleSlash alone.
* loop(LOOP-06): reject a stream with no terminal frame — throw StreamIncompleteError
runCloudTurn and CloudBrain's pump both let decodeSse's for-await loop exit
cleanly (no throw) when the underlying byte stream just ends without ever
sending a done or error frame — e.g. a proxy/load-balancer that time-boxes
the SSE response and closes the socket inside the idle window. That rendered
whatever partial delta output arrived as a fully successful turn. Both sites
now track whether a terminal frame was actually observed and throw/report
StreamIncompleteError when it wasn't, routed through errorHint/hintFor like
any other failure.
* loop(LOOP-01): narrow pollForToken's HttpError guard to the RFC 8628 400 pending shape — a 5xx no longer masquerades as authorization_pending
* loop(LOOP-01): stop workflow.ts's vault CRUD wrappers from swallowing every failure
listWorkflows/getWorkflow/deleteWorkflow caught ANY error from listSpaces/
getSpacesContent/deleteSpacesFile -- an expired-session 401, a network
outage, or a 5xx -- and turned it into an empty list / null / false. The
command layer then reported that as legitimate "no data": "No workflows
found in vault.", "workflow not found: <name>", "delete failed -- workflow
may not exist", with zero indication to run `aether auth login`.
vault.ts's own functions never throw for a genuinely empty vault or a
content-less file (200 with `files: []` / `content: null`), so a thrown
error here is always a real failure -- let it propagate to the existing
per-command try/catch that already calls fail().
workflowSave's getWorkflow call sat outside its own try block (the try only
wrapped the later saveWorkflow call), so removing the swallow would have let
a real failure there escape cmdWorkflow entirely and hit main.ts's bare
top-level catch with no hint at all -- gave it its own try/catch too.
Adds test/workflow_vault_error_propagation.test.ts covering list/view/
delete/save against a 401 and a 500, plus a control case confirming a
genuinely empty vault still prints "No workflows found in vault."
* loop(LOOP-06): check err.cause.code in hintFor — no more silent null for cause-shaped undici network errors
* loop(LOOP-06): route login.ts + auth.ts's authRefresh through formatErrorLine
Every error path in cmdLogin (headless --username/--password, device-code
request, and the poll-for-token wait) and authRefresh's failure paths wrote a
raw, unstyled `✗ <message>` straight to stderr — no errTheme color, no
errorHint-derived next step, no /doctor pointer, and none of formatErrorLine's
trailing blank-line separator. Every other auth-adjacent surface (chat.ts's
printError, render.ts's Renderer.error) had already been unified onto that
convention; login/refresh, the surfaces a user hits first, were left bare.
Route every catch in cmdLogin, plus authRefresh's catch AND its 200-with-no-
session_token branch (same bare ✗ line, no err object but the same fix
applies), through formatErrorLine(msg, { hint: errorHint(err, baseUrl) }) —
same convention chat.ts's printError uses, so a network failure during login
or refresh now gets the same /doctor pointer and a rejected session gets the
same re-login pointer as everywhere else.
Adds regression tests in test/login.test.ts (all three cmdLogin catch paths)
and test/auth_401.test.ts (authRefresh's catch + the no-session_token branch)
asserting the printed line carries the ✗ glyph, the errorHint-derived hint
where one applies, and the separator — and confirms a plain error with no
mapped hint (denied-in-browser) still gets styled output with no fabricated
hint.
Deliberately left login.ts's "No token on stdin" (--with-token, empty stdin)
unconverted: it's an exit-2 input-validation message with no underlying err
to derive a hint from, matching the unstyled "usage:"-style convention used
by other exit-2 paths elsewhere in the CLI (config.ts, vault.ts, workflow.ts),
and it wasn't in the finding's cited evidence.
* loop(LOOP-01): route workflow.ts's fail() through hintFor() — no more hardcoded auth hint for 402/403/5xx
* loop(LOOP-01): uploadFile() opts into the 120s stream-class timeout — no more flat 30s cap on large vault uploads
postForm() bounds the ENTIRE request (connect + send-body + wait-for-response)
with one flat timeoutMs, unlike getBinary()/stream() which split into a
connect-phase timeout plus a separate idle-timeout on the body. fetch() gives
no hook to observe outgoing multipart-body progress, so that split isn't
achievable for the upload path. Instead uploadFile() now explicitly passes
defaultStreamTimeoutMs() (120s) to postForm(), the same pattern client.ts/
workflow.ts already use for their long-running CHAT_PATH fallback, instead of
silently inheriting postForm()'s 30s metadata-call default — so a large-but-
healthy vault upload is no longer always killed by RequestTimeoutError just
for taking longer than 30s.
* loop(LOOP-06): wrap pickModel's empty-state lines in theme.dim — matches the rest of the picker's styling
* loop(META): simplify — consolidate duplicated baseUrl-independent error hints
errorHint (errors.ts) and hintFor (error_hints.ts) each hand-duplicated four
identical branches (MalformedResponseError, StreamTimeoutError,
StreamIncompleteError, RequestTimeoutError) that don't depend on baseUrl —
different loop rounds added these to both files without reconciling them,
and the RequestTimeoutError wording had already drifted between the two
copies. Extracted the shared branches into errors.ts's new
nonHttpErrorHint(), called first by both errorHint and hintFor; only the
genuinely baseUrl-dependent 5xx/network branches remain separate per file.
812 tests still pass; no test pinned the drifted exact wording.
* loop(META): close terminal-escape-injection gap in getBinary + media error paths
Independent security review of the meta-loop diff found that a hostile
server-controlled media_url can make fetch()'s own URL parser throw a raw
TypeError whose message echoes the target string verbatim — the one path
left unsanitized after this same loop closed the equivalent gap everywhere
else (sanitizeServerText/sanitizeTerm/formatErrorLine).
- transport.ts: getBinary() now sanitizes any non-typed error (a raw
fetch()-level throw) before it leaves the function, mirroring
sanitizeServerText's existing treatment of every other server string.
- media.ts: sanitize resp.media_url, resp.text/response, and the catch
block's err.message before writing them to the terminal.
- slash_media.ts: sanitize writeErr's err.message (shared by every media
slash command's catch block).
* loop(META): bound LLM-generation calls by the 120s stream timeout, not the new 30s default
Independent review found a real regression: commit a7b3621 gave
ApiClient.request() a 30s default timeout, and the loop was careful to
carve out defaultStreamTimeoutMs() (120s) overrides for every long-running
generation call it touched (chat.ts, brain_cloud.ts, client.ts, workflow.ts)
— except three sibling LLM-generation calls it missed: vision.ts's
dispatchGeneration/parseStoryboard (image/video/storyboard generation —
aether image, aether video, /photogen, /videogen, /animate, /reframe,
/recut, /storyboard) and the same class in scaffold.ts/port.ts (LLM-backed
code generation and cross-language translation). Any of these that
legitimately ran past 30s now failed with RequestTimeoutError where they
previously succeeded.
* loop(META): fix duplicate "kept current session." message on a picker fault
Round 3's fix (9ee863e) had pickModel() print a distinct diagnostic on an
internal key-handler fault, but its caller (slash.ts's showPicker) still
unconditionally printed its own generic "kept current session." right
after — so a real fault showed two back-to-back, redundant lines instead
of one clear message, while a plain Escape correctly showed just one.
pickModel() now resolves undefined (not null) on a fault, distinguishing
it from a deliberate cancel; showPicker checks for undefined and skips its
own message in that case. Added an end-to-end regression test exercising
the full handleSlash -> showPicker -> pickModel path (the prior tests only
covered pickModel in isolation, which is why this composition bug wasn't
caught).
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent cacb6c2 commit e07b115
47 files changed
Lines changed: 3892 additions & 289 deletions
File tree
- src
- commands
- core
- ui
- test
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
12 | 14 | | |
13 | 15 | | |
14 | 16 | | |
| 17 | + | |
15 | 18 | | |
16 | | - | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
17 | 23 | | |
18 | | - | |
| 24 | + | |
19 | 25 | | |
20 | 26 | | |
21 | 27 | | |
| |||
35 | 41 | | |
36 | 42 | | |
37 | 43 | | |
38 | | - | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
39 | 47 | | |
40 | 48 | | |
41 | 49 | | |
42 | 50 | | |
43 | 51 | | |
44 | 52 | | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
45 | 61 | | |
46 | 62 | | |
47 | 63 | | |
48 | 64 | | |
49 | | - | |
50 | | - | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
51 | 70 | | |
52 | 71 | | |
53 | 72 | | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
54 | 76 | | |
55 | 77 | | |
56 | | - | |
| 78 | + | |
57 | 79 | | |
58 | 80 | | |
59 | | - | |
| 81 | + | |
60 | 82 | | |
61 | 83 | | |
62 | 84 | | |
63 | 85 | | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
69 | 99 | | |
70 | 100 | | |
71 | 101 | | |
| |||
78 | 108 | | |
79 | 109 | | |
80 | 110 | | |
81 | | - | |
| 111 | + | |
| 112 | + | |
82 | 113 | | |
83 | 114 | | |
84 | 115 | | |
| |||
140 | 171 | | |
141 | 172 | | |
142 | 173 | | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
143 | 181 | | |
144 | 182 | | |
145 | 183 | | |
| |||
152 | 190 | | |
153 | 191 | | |
154 | 192 | | |
155 | | - | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
156 | 196 | | |
| 197 | + | |
157 | 198 | | |
158 | 199 | | |
159 | 200 | | |
| |||
208 | 249 | | |
209 | 250 | | |
210 | 251 | | |
211 | | - | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
212 | 257 | | |
213 | 258 | | |
214 | | - | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
215 | 263 | | |
216 | 264 | | |
217 | 265 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
| 10 | + | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
| 13 | + | |
| 14 | + | |
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
| |||
88 | 89 | | |
89 | 90 | | |
90 | 91 | | |
91 | | - | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
92 | 96 | | |
93 | 97 | | |
94 | 98 | | |
| |||
143 | 147 | | |
144 | 148 | | |
145 | 149 | | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
146 | 157 | | |
147 | 158 | | |
148 | 159 | | |
| |||
154 | 165 | | |
155 | 166 | | |
156 | 167 | | |
| 168 | + | |
157 | 169 | | |
158 | 170 | | |
159 | 171 | | |
160 | 172 | | |
| 173 | + | |
161 | 174 | | |
162 | 175 | | |
163 | 176 | | |
164 | | - | |
165 | | - | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
166 | 183 | | |
167 | 184 | | |
168 | 185 | | |
| |||
957 | 974 | | |
958 | 975 | | |
959 | 976 | | |
960 | | - | |
961 | | - | |
962 | | - | |
963 | | - | |
964 | | - | |
965 | | - | |
966 | | - | |
| 977 | + | |
| 978 | + | |
| 979 | + | |
| 980 | + | |
967 | 981 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
| 17 | + | |
16 | 18 | | |
17 | 19 | | |
18 | 20 | | |
| |||
75 | 77 | | |
76 | 78 | | |
77 | 79 | | |
78 | | - | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
79 | 84 | | |
80 | 85 | | |
81 | 86 | | |
| |||
86 | 91 | | |
87 | 92 | | |
88 | 93 | | |
89 | | - | |
| 94 | + | |
90 | 95 | | |
91 | 96 | | |
92 | 97 | | |
| |||
103 | 108 | | |
104 | 109 | | |
105 | 110 | | |
106 | | - | |
| 111 | + | |
107 | 112 | | |
108 | 113 | | |
109 | 114 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
| |||
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
| 20 | + | |
19 | 21 | | |
20 | 22 | | |
21 | 23 | | |
| |||
196 | 198 | | |
197 | 199 | | |
198 | 200 | | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
199 | 205 | | |
200 | 206 | | |
201 | | - | |
| 207 | + | |
202 | 208 | | |
203 | 209 | | |
204 | 210 | | |
205 | 211 | | |
206 | | - | |
| 212 | + | |
207 | 213 | | |
208 | 214 | | |
209 | | - | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
210 | 224 | | |
211 | 225 | | |
212 | 226 | | |
| |||
230 | 244 | | |
231 | 245 | | |
232 | 246 | | |
| 247 | + | |
233 | 248 | | |
234 | | - | |
| 249 | + | |
235 | 250 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
374 | 374 | | |
375 | 375 | | |
376 | 376 | | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
377 | 383 | | |
378 | 384 | | |
379 | 385 | | |
| |||
420 | 426 | | |
421 | 427 | | |
422 | 428 | | |
423 | | - | |
| 429 | + | |
424 | 430 | | |
425 | 431 | | |
426 | 432 | | |
427 | 433 | | |
428 | 434 | | |
429 | 435 | | |
430 | 436 | | |
431 | | - | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
432 | 442 | | |
433 | 443 | | |
434 | 444 | | |
| |||
0 commit comments