Commit 3da8dcd
release: 0.0.39 — Windows runs the Linux build in WSL, no installer to sign (#59)
* feat(windows): WSL keepalive supervisor for the browser-GUI architecture
Windows is moving off Electron/Squirrel to run the ordinary Linux build
inside WSL 2, with the browser as the GUI. The one thing the Electron app
was doing that WSL cannot do for itself is stay alive: WSL tears down an
idle distro roughly 15 seconds after its last session closes, killing the
server even though 1helm.service is enabled and would auto-start on boot.
This adds a per-user Scheduled Task and a two-layer supervisor. On the
Windows side keepalive-run.ps1 holds exactly one wsl.exe anchor session,
respawns it if it dies, and periodically confirms both that the distro is
running and that the HTTP endpoint answers. Inside the distro
keepalive-hold.sh exists to pin the distro up and restarts the unit if it
stops.
It runs as the signed-in user with an interactive token, never as Local
System, because WSL state is user-scoped - the same invariant the rest of
the Windows support already honours. Crash recovery comes from a
one-minute repeating trigger with IgnoreNew rather than RestartOnFailure,
and a per-session mutex enforces a single holder.
Two Windows-specific traps are handled explicitly. wsl.exe does not strip
quotes from its own option values, so a quoted distro name fails with
WSL_E_DISTRO_NOT_FOUND; arguments are now quoted only when whitespace
requires it. wsl.exe also needs valid standard handles or it exits
instantly with no output, so all three anchor streams are redirected to
files - which is both the fix and how the quoting bug was found.
Every terminate is targeted at one distro and refuses any name matching
-runtime$; `wsl --shutdown` is never invoked, as it would kill every
distro for every user on the machine.
Verified on a live distro: holds indefinitely with no visible window,
recovers from killing the holder with zero HTTP downtime, refuses to
stack a second supervisor, removes cleanly, and costs 0.01% CPU.
Start-at-logon and survive-reboot are NOT yet proven - the acceptance
host's autologon credential is invalid and its Task Scheduler cannot
create any process, so both are pending a rebuilt VM.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(windows): make keepalive install fail loudly and stop racing cold starts
Validation on a healthy Windows 11 host proved both outstanding
requirements - start at logon (5/5, isolated from the crash-recovery
trigger) and survive a real reboot (4/4, reachable 89-104s after boot) -
and surfaced one dangerous defect plus one timing risk.
A non-elevated re-install reported success while doing nothing. When the
task folder was created by an elevated process, Register-ScheduledTask
fails with Access denied (0x80070005), but because the cmdlet is
CIM-backed $ErrorActionPreference = 'Stop' does not make it terminating,
so execution continued, printed "Registered scheduled task", inferred
success from port 8123 answering - which the stale task satisfied - and
exited 0. A failed upgrade was indistinguishable from a clean one.
Registration now passes -ErrorAction Stop, is confirmed by reading the
task back, and explains the elevation mismatch when it is the cause.
The supervisor also restarted 1helm.service while systemd was still
starting it. It now checks for the "activating" state and waits instead,
and the budget before a distro recycle goes from 180s to 360s: measured
cold starts were 33-80s on an 8-core box, which leaves too little margin
on modest hardware.
Defaults move off the spike names to the shipping distro '1helm' and
C:\1helm\keepalive.
All three scripts re-parsed clean with the PowerShell parser on Windows.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* build(linux): ship a ready-to-run Linux release archive
The Linux host installer built the release on the end user's machine: a
full `npm ci` (devDependencies included) plus `npm run build`, which is
why the install path needed build-essential and npm registry access, and
why node-pty compiled from source on every install.
Package that work into the artifact instead. `npm run package:linux` now
runs the client/sidecar build on the release builder and injects the
gitignored outputs (public/bundle.js, public/bundle.css, public/app.css,
the stamped public/index.html, public/excalidraw, and the photon sidecar
bundle) into the staging tree, the same way the sealed channel image is
already injected. Production dependencies are installed once inside
docker.io/library/node:22 with --network=host, so the vendored native
addons link against Debian bookworm's glibc 2.36 and stay forward
compatible with every supported target.
Every shipped `build/Release/*.node` is fingerprinted into
resources/linux-native-modules.json with its digest and its maximum
GLIBC/GLIBCXX symbol, alongside the builder's architecture and Node ABI,
so an installer can refuse a release whose native addons cannot load.
The archive digest is written to dist/<archive>.sha256 for pinning.
All existing release guards are untouched: packaging still refuses to run
outside the exact Git checkout whose HEAD version matches package.json,
still requires the sealed channel image, and still verifies the staged
package version.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(linux): install and update from the ready-to-run release archive
The packaging change in 23e852c made the Linux artifact ship its own
production node_modules, prebuilt client assets, and a native-addon
manifest, but the tracked user-facing scripts still ran `npm ci` and
`npm run build` on the end user's machine, so no user benefited.
install.sh, update-host.sh, and apply-linux-release.sh now consume that
archive instead of building it:
- No compiler is probed or installed. build-essential leaves the apt
list and make/c++ leave the dependency probe, because nothing on the
user's machine compiles 1Helm any more. python3/python3-venv stay.
- Every staged or retained release must prove it is runnable before it
is promoted: vendored node_modules, the shipped manifest, built client
assets, and a dlopen of every listed addon against the installed
Node's exact ABI, including a node-pty that still exposes spawn().
A source-only archive is refused with an actionable message rather
than silently falling back to a build the host can no longer do.
- The verification always runs outside the armed transaction window, so
a release that cannot run leaves the host untouched instead of
half-promoted; apply-linux-release.sh reports the refusal as an error
status rather than leaving the UI on "installing".
- Readiness now requires 1helm.service to be `active`, not merely that
something answered on 8123, and prints the unit journal on failure.
- Installs refuse to start behind a foreign listener on 8123. WSL 2 puts
every distribution in one shared network namespace, so a listener in
another distribution or on Windows itself would otherwise answer the
readiness probe while 1helm.service crash-looped on EADDRINUSE. The
probe binds the port the way the service will instead of parsing `ss`,
which is not guaranteed to be installed and would skip in silence.
Measured cold on WSL2: 3m39.9s versus 8m49s, with no compiler present.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(windows): install 1Helm on Windows with a PowerShell one-liner
Windows no longer ships an application. `irm https://1helm.com/install.ps1
| iex` enables WSL 2, imports a digest-pinned Ubuntu root filesystem, runs
the ordinary Linux installer inside it, and registers the keepalive. The
browser is the interface at http://localhost:8123. Nothing is code-signed
because nothing is an executable, so SmartScreen never appears - which
matters because 1Helm has no Authenticode identity and was never going to
obtain one.
Only enabling the Windows optional features and installing Microsoft's WSL
package cross the UAC boundary. Importing the distribution, running the
Linux installer and registering the keepalive all stay in the signed-in
user's session on purpose: WSL state is per-user, so a distribution
imported by an elevated session started with over-the-shoulder credentials
would belong to that administrator rather than the person at the machine.
The restart Windows requires to activate those features is reported as a
restart with numbered steps, never as a failure, and every step is
idempotent so re-running the same command continues where it stopped.
`-Wait` on an elevated ShellExecute is unreliable, so the parent blocks on
the real process handle before reading the exit code.
Microsoft's WSL package is checked against a pinned digest AND required to
carry a valid Microsoft Corporation Authenticode signature; a digest alone
only proves we fetched what we expected, not who built it.
A port pre-flight refuses to continue when something already holds 8123.
Every WSL distribution shares one network namespace with Windows, so a
foreign listener would both prevent 1Helm binding and answer its health
probe in its place.
The keepalive payload moves to site/public/keepalive/ and is fetched from
the same origin as the installer, so the two can never be mismatched.
Obtaining it is fail-closed: without the keepalive, WSL tears the
distribution down when idle and 1Helm silently stops answering, so a
missing component aborts the install rather than warning.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(windows): make the Windows installer able to report success
The first end-to-end run of install.ps1 on a pristine Windows 11 machine
failed at the point where it should have succeeded, and three separate
defects were behind it.
Invoke-InDistro returned wsl.exe's standard output as well as its exit
code. A PowerShell function returns everything it emits, so
`$code = Invoke-InDistro ...` collected every line the in-distro command
printed and the exit code into one array; `$code -ne 0` is then an array
filter that is truthy for any non-empty output. The Linux install
completed perfectly and the installer still stopped with the entire apt
and install transcript interpolated into "the Linux installer failed
(exit ...)". Because the long install step always prints something, this
made success unreachable rather than merely unlikely. wsl.exe's output
now goes to the host, and only the exit code is returned.
install.sh had no local-archive path at all: it always resolved the
published release from 1helm.com and downloaded it from GitHub, ignoring
the argument install.ps1 was already passing it. -LocalArchive therefore
silently installed something other than the archive it was given. It now
accepts an archive path, takes the version from that archive's own
package.json, and verifies the digest when HELM_RELEASE_SHA256 is pinned
(install.ps1 forwards it via the new -LocalArchiveSha256). Resolving the
published release stays the default and is unchanged.
The Start Menu entry was a .lnk whose TargetPath was set to a URL.
WScript.Shell accepts that and saves a shortcut with an empty target, so
the entry appeared and did nothing. It is now an Internet Shortcut.
Also stop the two readiness loops printing curl's connection errors on
every attempt; "connection refused" is the expected state while the unit
comes up, and it buried the real outcome under hundreds of lines.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(windows): stop the unelevated installer calling DISM
Get-WindowsOptionalFeature -Online requires elevation, and the main pass
runs as the signed-in user on purpose. So the very first thing the
installer did was something it was not permitted to do:
Get-WindowsOptionalFeature : The requested operation requires elevation.
At install.ps1:113
The main pass now uses Test-WslReady, which establishes the same fact from
signals any user can read: Microsoft's pinned WSL responds, the vmcompute
service exists - it does not until VirtualMachinePlatform is active - and
Windows has no servicing restart pending. Feature enumeration is left to
the elevated host-setup pass, where it is both permitted and
authoritative, and Test-RestartPending only attempts it when elevated.
This was invisible from an SSH session: Windows OpenSSH grants an
administrator a full elevated token, so DISM succeeded during automated
testing and failed immediately for a real user in an ordinary PowerShell
window.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(site): stop requiring Windows release artifacts that no longer exist
Windows publishes no release artifacts now: it installs the Linux archive
inside WSL via https://1helm.com/install.ps1. Three places still encoded
the old six-file matrix, and each would have failed the day 0.0.39 was
published rather than at merge time.
latestLinuxRelease() threw unless a Setup executable, full .nupkg and
RELEASES were all present with digests. It backs /api/releases/linux/latest,
which install.sh resolves to find the archive - so publishing a release
without those files would have broken the public Linux AND Windows
installers simultaneously, with a "does not contain the complete
digest-qualified desktop matrix" error pointing at the wrong thing. It now
expects the three artifacts that are actually shipped.
RELEASE_FALLBACK advertised the same six files, so an API outage would have
handed visitors download links for a Setup executable that does not exist.
/download/windows redirected to that executable; it now leads to the
install instructions.
The Linux installer's port-collision message explained Windows and WSL
sharing one network namespace regardless of platform, which reads as
nonsense to an operator on a native Debian host. It is now conditional on
systemd-detect-virt reporting wsl.
test/site.mjs asserted the old contract in two places and is updated to the
new one, including explicit assertions that the fallback does NOT advertise
a Setup executable, .nupkg or RELEASES.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(windows): add uninstall.ps1 and rewrite Windows documentation
Windows no longer ships an application: the ordinary Linux build runs inside
a WSL 2 distribution named "1helm" and the browser is the interface at
http://localhost:8123. Nothing is code-signed because nothing is shipped, so
SmartScreen never appears. The documentation still described a Setup
executable, a Squirrel feed, an Authenticode disclosure and %APPDATA% data
roots, none of which exist.
Add site/public/uninstall.ps1, the only supported way off Windows. In order:
stop the keepalive (via its own keepalive-remove.ps1 when present, otherwise
unregistering \1Helm\ directly), run /opt/1helm/uninstall-host.sh inside the
distribution so 1Helm removes its own ownership-checked containers and units
while it still can, then terminate and unregister the distribution, then
delete C:\1helm and the Start Menu entry.
Because `wsl --unregister` deletes the virtual disk holding every channel's
files, the database and the provider credentials, it requires typed
confirmation stating exactly what is lost; -Force exists for scripted use.
It refuses to run as SYSTEM, whose per-user WSL state is not the user's. It
never calls `wsl --shutdown`, which would stop every distribution for every
user on the machine, and both the targeted terminate and the unregister sit
behind the keepalive's protective pattern plus an exact, case-sensitive match
against a currently registered distribution. Every step is idempotent and the
run ends by reporting what was removed and what was not.
Documentation now matches the verified flow at the altitude a first-time user
needs: one command, one permission pop-up, a deliberate "Restart required"
stop, the same command again, then the browser. It also names the things that
look like failures and are not - Microsoft's own "Welcome to WSL" window, the
~40s the channel-computer runtime needs after install, the ExecutionPolicy
wall that only appears when the script is downloaded rather than piped - and
records that #main's terminal is now bash inside WSL, not cmd.exe.
test/site.mjs asserted the retired contract: that getting-started tells
Windows users to download a Setup executable and discloses `NotSigned`. Both
now assert the opposite, because SmartScreen and Authenticode cannot apply to
a product that ships no .exe and repeating them would send people looking for
a file that does not exist. New assertions cover /uninstall.ps1 being served,
the Windows guide carrying the whole two-phase flow, and the uninstaller's
safety guards.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* build(site): add a reproducible 1helm.com deploy
The site has been deployed by hand: git archive a commit into
/opt/1helm-site/releases/<sha>, repoint current, restart the unit. That is
fragile for something that gates a release - install.ps1 and install.sh are
served from here, and install.sh resolves /api/releases/linux/latest to find
the Linux archive, so publishing a release without deploying the site leaves
Windows with no installer and Linux unable to resolve a version.
Refuses any commit that is not an ancestor of origin/main, so the live
surface always traces back to reviewed source. Verifies the archive actually
contains install.sh, install.ps1 and the keepalive payload before going
live, and rolls back to the previous release if the new one fails to answer
its health endpoint.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(windows): retire the Electron/Squirrel Windows lane
Windows is now the ordinary Linux build running inside a per-user WSL 2
distribution, installed by the site-served install.ps1 and removed by
uninstall.ps1. Nothing Electron, Squirrel, or signed ships for Windows.
- delete scripts/package-windows.cjs, scripts/install-wsl-runtime.ps1,
scripts/windows-removal.cjs and test/windows-wsl-status.ps1
- drop the package:windows and package:windows:release npm scripts and the
electron-winstaller/png-to-ico devDependencies they were the only users of
- remove Squirrel event handling and the com.squirrel app user model id from
desktop/main.cjs; unpackedPath stays (still used for the asset root)
- remove the Windows branch of desktop/updater.cjs; the macOS Developer ID
feed, states and quiesce-before-install path are untouched
- remove the in-app WSL runtime installer that drove install-wsl-runtime.ps1
(server launcher, status plumbing, runtime/install win32 branch, and the
onboarding/settings cards that could only ever paint for it)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(release): three release artifacts, Windows accepted by behaviour
The governance documents still encoded a six-artifact desktop matrix with a
Windows Setup executable, Squirrel package/manifest and Authenticode
disclosure. None of that exists: Windows installs the ordinary Linux build
into WSL 2 from the site-served install.ps1.
- release-checklist, release-lifecycle, GOVERNANCE and release-notes-template
now name exactly 1Helm-<version>-arm64.dmg, 1Helm-<version>-mac-arm64.zip
and 1Helm-<version>-linux-node.tgz, and state that Windows publishes nothing
- the Windows acceptance section is replaced with behavioural requirements:
non-elevated one-liner install with a single UAC prompt, mid-install restart
and resume, keepalive surviving a reboot, browser reaching localhost:8123,
prior-version update with /var/lib/1helm-oci-v1 retained, uninstall.ps1
- a Linux artifact that fails acceptance now explicitly blocks Windows too,
and install.ps1/uninstall.ps1/keepalive are documented as site-served
- test/release-governance.mjs asserts the new contract: all four documents must
name the three artifacts and say Windows publishes nothing, must carry every
behavioural requirement, and must not reintroduce Squirrel, .nupkg, RELEASES,
Authenticode, a Setup executable, package:windows or a Windows update feed
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* release: 0.0.39
Version, changelog and the local/1helm-channel-machine:0.0.39 pins.
Points the website's offline release fallback at 0.0.39. Its digests are
the digests of this commit's own artifacts, so they cannot exist yet; they
are filled in at publish and the site is redeployed. Until then the
placeholder is deliberately not 64 hex characters, so latestLinuxRelease()
rejects it and /api/releases/linux/latest answers 503 - an installer is
told no release is available rather than handed a digest that cannot match
what it downloads. The macOS download link is unaffected because it
resolves by asset name.
test/site.mjs now covers both states and refuses a half-filled fallback,
which would serve one real digest and two wrong ones.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore: restamp index.html for the 0.0.39 bundle
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Joseph Yaksich <gitcommit90@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>1 parent f926473 commit 3da8dcd
42 files changed
Lines changed: 2637 additions & 1882 deletions
File tree
- desktop
- docs
- public
- scripts
- site
- public
- keepalive
- src
- client
- server
- 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 | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
10 | 71 | | |
11 | 72 | | |
12 | 73 | | |
| |||
1038 | 1099 | | |
1039 | 1100 | | |
1040 | 1101 | | |
| 1102 | + | |
1041 | 1103 | | |
1042 | 1104 | | |
1043 | 1105 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
| 17 | + | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| |||
130 | 130 | | |
131 | 131 | | |
132 | 132 | | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
138 | | - | |
139 | | - | |
140 | | - | |
141 | | - | |
142 | | - | |
143 | | - | |
144 | | - | |
145 | | - | |
146 | | - | |
147 | | - | |
148 | | - | |
149 | | - | |
150 | | - | |
151 | | - | |
152 | | - | |
153 | | - | |
154 | | - | |
155 | | - | |
156 | | - | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
157 | 201 | | |
158 | 202 | | |
159 | 203 | | |
| |||
178 | 222 | | |
179 | 223 | | |
180 | 224 | | |
181 | | - | |
182 | | - | |
183 | | - | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
184 | 232 | | |
185 | 233 | | |
186 | 234 | | |
187 | 235 | | |
188 | 236 | | |
189 | 237 | | |
190 | | - | |
191 | | - | |
192 | | - | |
193 | | - | |
194 | | - | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
195 | 245 | | |
196 | 246 | | |
197 | 247 | | |
| |||
324 | 374 | | |
325 | 375 | | |
326 | 376 | | |
327 | | - | |
328 | | - | |
329 | | - | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
330 | 380 | | |
331 | 381 | | |
332 | 382 | | |
| |||
337 | 387 | | |
338 | 388 | | |
339 | 389 | | |
340 | | - | |
| 390 | + | |
341 | 391 | | |
342 | 392 | | |
343 | 393 | | |
| |||
365 | 415 | | |
366 | 416 | | |
367 | 417 | | |
368 | | - | |
| 418 | + | |
369 | 419 | | |
370 | 420 | | |
371 | 421 | | |
| |||
383 | 433 | | |
384 | 434 | | |
385 | 435 | | |
386 | | - | |
387 | | - | |
388 | | - | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
389 | 440 | | |
390 | 441 | | |
391 | 442 | | |
392 | 443 | | |
393 | 444 | | |
394 | 445 | | |
395 | 446 | | |
396 | | - | |
| 447 | + | |
397 | 448 | | |
398 | 449 | | |
399 | 450 | | |
| |||
430 | 481 | | |
431 | 482 | | |
432 | 483 | | |
433 | | - | |
434 | | - | |
435 | | - | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
436 | 487 | | |
437 | 488 | | |
438 | 489 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | 52 | | |
73 | 53 | | |
74 | 54 | | |
| |||
310 | 290 | | |
311 | 291 | | |
312 | 292 | | |
313 | | - | |
314 | | - | |
315 | | - | |
316 | | - | |
| 293 | + | |
317 | 294 | | |
318 | 295 | | |
319 | 296 | | |
| |||
325 | 302 | | |
326 | 303 | | |
327 | 304 | | |
328 | | - | |
329 | 305 | | |
330 | 306 | | |
331 | 307 | | |
| |||
0 commit comments