Skip to content

Commit 04d7635

Browse files
authored
chore: release/0.32.0.1 (#234)
* chore: update appcast.xml * chore: appcast.xml updates * feat: fixes to browseros-server to better handle restarts and health checks * feat: add chrome.BrowserOS.getBrowserosVersionNumber() API * chore: new browseros-server binaries * chore: bump PATCH and OFFSET * fix: minor
1 parent 3b00c3e commit 04d7635

File tree

15 files changed

+193
-118
lines changed

15 files changed

+193
-118
lines changed

docs/appcast-x86_64.xml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,36 @@
88
<language>en</language>
99

1010
<!-- Updates -->
11+
<!-- <item> -->
12+
<!-- <title>BrowserOS - 0.31.0.14</title> -->
13+
<!-- <description sparkle:format="plain-text"> -->
14+
<!-- </description> -->
15+
<!-- <sparkle:version>7535.49</sparkle:version> -->
16+
<!-- <sparkle:shortVersionString>0.31.0.14</sparkle:shortVersionString> -->
17+
<!-- <pubDate>Fri, 12 Dec 2025 02:34:49 +0000</pubDate> -->
18+
<!-- <link>https://browseros.com</link> -->
19+
<!-- <enclosure -->
20+
<!-- url="http://cdn.browseros.com/releases/0.31.0.14/macos/BrowserOS_v0.31.0.14_x64.dmg" -->
21+
<!-- sparkle:edSignature="2Wfs2WtzdTsLMe5g4wkgFtZ+CNwrWXyzQIQZf6F0jlZDrCEPJVzNsk8LQrClDui8nCXOYKlCvxbRVAwwu+EvAg==" -->
22+
<!-- length="170790283" -->
23+
<!-- type="application/octet-stream" /> -->
24+
<!-- <sparkle:minimumSystemVersion>10.15</sparkle:minimumSystemVersion> -->
25+
<!-- </item> -->
26+
<item>
27+
<title>BrowserOS - 0.31.0.7</title>
28+
<description sparkle:format="plain-text">
29+
</description>
30+
<sparkle:version>7535.49</sparkle:version>
31+
<sparkle:shortVersionString>0.31.0.7</sparkle:shortVersionString>
32+
<pubDate>Tue, 09 Dec 2025 04:51:00 +0000</pubDate>
33+
<link>https://browseros.com</link>
34+
<enclosure
35+
url="http://cdn.browseros.com/releases/0.31.0.7/macos/BrowserOS_v0.31.0.7_x64.dmg"
36+
sparkle:edSignature="sRPGGd6pr7A65+MC+2ls1866xClt1A8uctEO3Zv3dRW1DbwowpTWZK/r0OVrU03ZPhgLSis+MASbX2rdbth/DQ=="
37+
length="170836089"
38+
type="application/octet-stream" />
39+
<sparkle:minimumSystemVersion>10.15</sparkle:minimumSystemVersion>
40+
</item>
1141
<item>
1242
<title>Nxtscape - 0.29.0</title>
1343
<description sparkle:format="plain-text">

docs/appcast.xml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,36 @@
88
<language>en</language>
99

1010
<!-- Updates -->
11+
<!-- <item> -->
12+
<!-- <title>BrowserOS - 0.31.0.14</title> -->
13+
<!-- <description sparkle:format="plain-text"> -->
14+
<!-- </description> -->
15+
<!-- <sparkle:version>7535.49</sparkle:version> -->
16+
<!-- <sparkle:shortVersionString>0.31.0.14</sparkle:shortVersionString> -->
17+
<!-- <pubDate>Fri, 12 Dec 2025 02:34:49 +0000</pubDate> -->
18+
<!-- <link>https://browseros.com</link> -->
19+
<!-- <enclosure -->
20+
<!-- url="http://cdn.browseros.com/releases/0.31.0.14/macos/BrowserOS_v0.31.0.14_x64.dmg" -->
21+
<!-- sparkle:edSignature="2Wfs2WtzdTsLMe5g4wkgFtZ+CNwrWXyzQIQZf6F0jlZDrCEPJVzNsk8LQrClDui8nCXOYKlCvxbRVAwwu+EvAg==" -->
22+
<!-- length="170790283" -->
23+
<!-- type="application/octet-stream" /> -->
24+
<!-- <sparkle:minimumSystemVersion>10.15</sparkle:minimumSystemVersion> -->
25+
<!-- </item> -->
26+
<item>
27+
<title>BrowserOS - 0.31.0.7</title>
28+
<description sparkle:format="plain-text">
29+
</description>
30+
<sparkle:version>7535.49</sparkle:version>
31+
<sparkle:shortVersionString>0.31.0.7</sparkle:shortVersionString>
32+
<pubDate>Tue, 09 Dec 2025 04:51:00 +0000</pubDate>
33+
<link>https://browseros.com</link>
34+
<enclosure
35+
url="http://cdn.browseros.com/releases/0.31.0.7/macos/BrowserOS_v0.31.0.7_arm64.dmg"
36+
sparkle:edSignature="3Ogu5iNeyiKaQvkpSaSXkyVnbYSJPA6E0IPbj3xejGm1AN3e3PHYFrRjIyGTFMVPwsJFh7YfXlQus1FSQ0iXCw=="
37+
length="158944893"
38+
type="application/octet-stream" />
39+
<sparkle:minimumSystemVersion>10.15</sparkle:minimumSystemVersion>
40+
</item>
1141
<item>
1242
<title>Nxtscape - 0.29.0</title>
1343
<description sparkle:format="plain-text">
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
91
1+
92

packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_manager.cc

Lines changed: 78 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
diff --git a/chrome/browser/browseros_server/browseros_server_manager.cc b/chrome/browser/browseros_server/browseros_server_manager.cc
22
new file mode 100644
3-
index 0000000000000..43f1ee391f0a8
3+
index 0000000000000..e16b9181f8e3d
44
--- /dev/null
55
+++ b/chrome/browser/browseros_server/browseros_server_manager.cc
6-
@@ -0,0 +1,967 @@
6+
@@ -0,0 +1,952 @@
77
+// Copyright 2024 The Chromium Authors
88
+// Use of this source code is governed by a BSD-style license that can be
99
+// found in the LICENSE file.
@@ -55,10 +55,17 @@ index 0000000000000..43f1ee391f0a8
5555
+
5656
+namespace {
5757
+
58-
+const int kBackLog = 10;
58+
+constexpr int kBackLog = 10;
5959
+constexpr base::FilePath::CharType kConfigFileName[] =
6060
+ FILE_PATH_LITERAL("server_config.json");
6161
+
62+
+constexpr base::TimeDelta kHealthCheckInterval = base::Seconds(30);
63+
+constexpr base::TimeDelta kHealthCheckTimeout = base::Seconds(15);
64+
+constexpr base::TimeDelta kProcessCheckInterval = base::Seconds(10);
65+
+
66+
+constexpr int kMaxPortAttempts = 100;
67+
+constexpr int kMaxPort = 65535;
68+
+
6269
+// Holds configuration data gathered on UI thread, passed to background thread
6370
+struct ServerConfig {
6471
+ std::string install_id;
@@ -426,13 +433,10 @@ index 0000000000000..43f1ee391f0a8
426433
+
427434
+ LOG(INFO) << "browseros: Starting BrowserOS server";
428435
+
429-
+
430436
+ // Start servers and process
437+
+ // Note: monitoring timers are started in OnProcessLaunched() after successful launch
431438
+ StartCDPServer();
432439
+ LaunchBrowserOSProcess();
433-
+
434-
+ health_check_timer_.Start(FROM_HERE, base::Seconds(60), this,
435-
+ &BrowserOSServerManager::CheckServerHealth);
436440
+}
437441
+
438442
+void BrowserOSServerManager::Stop() {
@@ -444,8 +448,8 @@ index 0000000000000..43f1ee391f0a8
444448
+ health_check_timer_.Stop();
445449
+ process_check_timer_.Stop();
446450
+
447-
+ TerminateBrowserOSProcess();
448-
+ StopCDPServer();
451+
+ // Use wait=false for shutdown - just send kill signal, don't block UI thread
452+
+ TerminateBrowserOSProcess(/*wait=*/false);
449453
+
450454
+ // Release lock
451455
+ if (lock_file_.IsValid()) {
@@ -496,7 +500,6 @@ index 0000000000000..43f1ee391f0a8
496500
+ base::FilePath execution_dir = GetBrowserOSExecutionDir();
497501
+ if (execution_dir.empty()) {
498502
+ LOG(ERROR) << "browseros: Failed to resolve execution directory";
499-
+ StopCDPServer();
500503
+ return;
501504
+ }
502505
+
@@ -545,21 +548,26 @@ index 0000000000000..43f1ee391f0a8
545548
+void BrowserOSServerManager::OnProcessLaunched(base::Process process) {
546549
+ if (!process.IsValid()) {
547550
+ LOG(ERROR) << "browseros: Failed to launch BrowserOS server";
548-
+ StopCDPServer();
551+
+ // Don't stop CDP server - it's independent and may be used by other things
552+
+ // Leave system in degraded state (CDP up, no browseros_server) rather than
553+
+ // completely broken state (no CDP, no server)
549554
+ is_restarting_ = false;
550555
+ return;
551556
+ }
552557
+
553558
+ process_ = std::move(process);
554559
+ is_running_ = true;
555560
+
556-
+ LOG(INFO) << "browseros: BrowserOS server started";
561+
+ LOG(INFO) << "browseros: BrowserOS server started with PID: " << process_.Pid();
557562
+ LOG(INFO) << "browseros: CDP port: " << cdp_port_;
558563
+ LOG(INFO) << "browseros: MCP port: " << mcp_port_;
559564
+ LOG(INFO) << "browseros: Agent port: " << agent_port_;
560565
+ LOG(INFO) << "browseros: Extension port: " << extension_port_;
561566
+
562-
+ process_check_timer_.Start(FROM_HERE, base::Seconds(5), this,
567+
+ // Start/restart monitoring timers
568+
+ health_check_timer_.Start(FROM_HERE, kHealthCheckInterval, this,
569+
+ &BrowserOSServerManager::CheckServerHealth);
570+
+ process_check_timer_.Start(FROM_HERE, kProcessCheckInterval, this,
563571
+ &BrowserOSServerManager::CheckProcessStatus);
564572
+
565573
+ // Reset restart flag and pref after successful launch
@@ -573,36 +581,34 @@ index 0000000000000..43f1ee391f0a8
573581
+ }
574582
+}
575583
+
576-
+void BrowserOSServerManager::TerminateBrowserOSProcess() {
584+
+void BrowserOSServerManager::TerminateBrowserOSProcess(bool wait) {
577585
+ if (!process_.IsValid()) {
578586
+ return;
579587
+ }
580588
+
581-
+ LOG(INFO) << "browseros: Force killing BrowserOS server process (PID: "
582-
+ << process_.Pid() << ")";
583-
+
584-
+ // sync primitives is needed for process termination.
585-
+ // NOTE: only run on background threads
586-
+ base::ScopedAllowBaseSyncPrimitives allow_sync;
587-
+ base::ScopedAllowBlocking allow_blocking;
589+
+ LOG(INFO) << "browseros: Terminating BrowserOS server process (PID: "
590+
+ << process_.Pid() << ", wait: " << (wait ? "true" : "false") << ")";
588591
+
589592
+#if BUILDFLAG(IS_POSIX)
590-
+ // POSIX: Send SIGKILL for immediate termination (no graceful shutdown)
591-
+ // This matches Windows TerminateProcess behavior
592593
+ base::ProcessId pid = process_.Pid();
593-
+ if (kill(pid, SIGKILL) == 0) {
594+
+ if (kill(pid, SIGKILL) != 0) {
595+
+ PLOG(ERROR) << "browseros: Failed to send SIGKILL to PID " << pid;
596+
+ } else if (wait) {
597+
+ // Blocking wait - must be called from background thread
598+
+ base::ScopedAllowBaseSyncPrimitives allow_sync;
599+
+ base::ScopedAllowBlocking allow_blocking;
594600
+ int exit_code = 0;
595601
+ if (process_.WaitForExit(&exit_code)) {
596-
+ LOG(INFO) << "browseros: Process killed successfully with SIGKILL";
602+
+ LOG(INFO) << "browseros: Process killed successfully";
597603
+ } else {
598-
+ LOG(WARNING) << "browseros: SIGKILL sent but WaitForExit failed";
604+
+ LOG(WARNING) << "browseros: WaitForExit failed";
599605
+ }
600606
+ } else {
601-
+ PLOG(ERROR) << "browseros: Failed to send SIGKILL to PID " << pid;
607+
+ LOG(INFO) << "browseros: SIGKILL sent (not waiting for exit)";
602608
+ }
603609
+#else
604-
+ // Windows: TerminateProcess is already immediate force kill
605-
+ bool terminated = process_.Terminate(0, true);
610+
+ // Windows: Terminate with wait parameter
611+
+ bool terminated = process_.Terminate(0, wait);
606612
+ if (terminated) {
607613
+ LOG(INFO) << "browseros: Process terminated successfully";
608614
+ } else {
@@ -617,28 +623,21 @@ index 0000000000000..43f1ee391f0a8
617623
+ LOG(INFO) << "browseros: BrowserOS server exited with code: " << exit_code;
618624
+ is_running_ = false;
619625
+
620-
+ // Stop CDP server since BrowserOS process is gone
621-
+ StopCDPServer();
626+
+ // Stop timers during restart to prevent races
627+
+ health_check_timer_.Stop();
628+
+ process_check_timer_.Stop();
622629
+
623-
+ // Restart if it crashed unexpectedly
624-
+ if (exit_code != 0) {
625-
+ LOG(WARNING) << "browseros: BrowserOS server crashed, restarting...";
626-
+ Start();
627-
+ }
630+
+ // Always restart - we want the server running
631+
+ // Don't call Start() - we already hold the lock and CDP server is running
632+
+ LOG(WARNING) << "browseros: BrowserOS server exited, restarting process...";
633+
+ LaunchBrowserOSProcess();
628634
+}
629635
+
630636
+void BrowserOSServerManager::CheckServerHealth() {
631637
+ if (!is_running_) {
632638
+ return;
633639
+ }
634640
+
635-
+ // First check if process is still alive
636-
+ if (!process_.IsValid()) {
637-
+ LOG(WARNING) << "browseros: BrowserOS server process is invalid, restarting...";
638-
+ RestartBrowserOSProcess();
639-
+ return;
640-
+ }
641-
+
642641
+ // Build health check URL
643642
+ GURL health_url("http://127.0.0.1:" + base::NumberToString(mcp_port_) + "/health");
644643
+
@@ -667,10 +666,9 @@ index 0000000000000..43f1ee391f0a8
667666
+ resource_request->method = "GET";
668667
+ resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
669668
+
670-
+ // Create URL loader with 10 second timeout
671669
+ auto url_loader = network::SimpleURLLoader::Create(
672670
+ std::move(resource_request), traffic_annotation);
673-
+ url_loader->SetTimeoutDuration(base::Seconds(10));
671+
+ url_loader->SetTimeoutDuration(kHealthCheckTimeout);
674672
+
675673
+ // Get URL loader factory from default storage partition
676674
+ auto* url_loader_factory =
@@ -692,10 +690,13 @@ index 0000000000000..43f1ee391f0a8
692690
+ return;
693691
+ }
694692
+
695-
+ // Check if process has exited
696693
+ int exit_code = 0;
697-
+ if (process_.WaitForExitWithTimeout(base::TimeDelta(), &exit_code)) {
698-
+ // Process has exited
694+
+ bool exited = process_.WaitForExitWithTimeout(base::TimeDelta(), &exit_code);
695+
+ LOG(INFO) << "browseros: CheckProcessStatus PID: " << process_.Pid()
696+
+ << ", WaitForExitWithTimeout returned: " << exited
697+
+ << ", exit_code: " << exit_code;
698+
+
699+
+ if (exited) {
699700
+ OnProcessExited(exit_code);
700701
+ }
701702
+}
@@ -730,12 +731,36 @@ index 0000000000000..43f1ee391f0a8
730731
+void BrowserOSServerManager::RestartBrowserOSProcess() {
731732
+ LOG(INFO) << "browseros: Restarting BrowserOS server process";
732733
+
733-
+ // Stop the process and monitoring
734+
+ // Prevent multiple concurrent restarts
735+
+ if (is_restarting_) {
736+
+ LOG(INFO) << "browseros: Restart already in progress, ignoring";
737+
+ return;
738+
+ }
739+
+ is_restarting_ = true;
740+
+
741+
+ // Stop all timers during restart to prevent races
742+
+ health_check_timer_.Stop();
734743
+ process_check_timer_.Stop();
735-
+ TerminateBrowserOSProcess();
736744
+
737-
+ // Relaunch the process
738-
+ LaunchBrowserOSProcess();
745+
+ // Capture UI task runner to post back after background work
746+
+ auto ui_task_runner = base::SequencedTaskRunner::GetCurrentDefault();
747+
+
748+
+ // Kill process on background thread (wait=true requires background thread),
749+
+ // then relaunch on UI thread
750+
+ base::ThreadPool::PostTask(
751+
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
752+
+ base::BindOnce(
753+
+ [](BrowserOSServerManager* manager,
754+
+ scoped_refptr<base::SequencedTaskRunner> ui_runner) {
755+
+ manager->TerminateBrowserOSProcess(/*wait=*/true);
756+
+
757+
+ // Post back to UI thread to launch new process
758+
+ ui_runner->PostTask(
759+
+ FROM_HERE,
760+
+ base::BindOnce(&BrowserOSServerManager::LaunchBrowserOSProcess,
761+
+ base::Unretained(manager)));
762+
+ },
763+
+ base::Unretained(this), ui_task_runner));
739764
+}
740765
+
741766
+void BrowserOSServerManager::OnAllowRemoteInMCPChanged() {
@@ -776,51 +801,11 @@ index 0000000000000..43f1ee391f0a8
776801
+ return;
777802
+ }
778803
+
779-
+ // Ignore if already restarting (prevents thrashing from UI spam)
780-
+ if (is_restarting_) {
781-
+ LOG(INFO) << "browseros: Restart already in progress, ignoring duplicate request";
782-
+ return;
783-
+ }
784-
+
785-
+ // Ignore if not running
786-
+ if (!is_running_) {
787-
+ LOG(WARNING) << "browseros: Cannot restart - server is not running";
788-
+ // Reset pref anyway
789-
+ prefs->SetBoolean(browseros_server::kRestartServerRequested, false);
790-
+ return;
791-
+ }
792-
+
793804
+ LOG(INFO) << "browseros: Server restart requested via preference";
794-
+ is_restarting_ = true;
795-
+
796-
+ // Stop timer now (must be on UI thread)
797-
+ process_check_timer_.Stop();
798-
+
799-
+ // Capture UI task runner to post back after background work
800-
+ auto ui_task_runner = base::SequencedTaskRunner::GetCurrentDefault();
801-
+
802-
+ // Kill process on background thread, then relaunch on UI thread
803-
+ base::ThreadPool::PostTask(
804-
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
805-
+ base::BindOnce(
806-
+ [](BrowserOSServerManager* manager,
807-
+ scoped_refptr<base::SequencedTaskRunner> ui_runner) {
808-
+ // Kill old process and wait for exit (blocking, safe on background)
809-
+ manager->TerminateBrowserOSProcess();
810-
+
811-
+ // Post back to UI thread to launch new process
812-
+ ui_runner->PostTask(
813-
+ FROM_HERE,
814-
+ base::BindOnce(&BrowserOSServerManager::LaunchBrowserOSProcess,
815-
+ base::Unretained(manager)));
816-
+ },
817-
+ base::Unretained(this), ui_task_runner));
805+
+ RestartBrowserOSProcess();
818806
+}
819807
+
820808
+int BrowserOSServerManager::FindAvailablePort(int starting_port) {
821-
+ const int kMaxPortAttempts = 100;
822-
+ const int kMaxPort = 65535;
823-
+
824809
+ LOG(INFO) << "browseros: Finding port starting from "
825810
+ << starting_port;
826811
+

0 commit comments

Comments
 (0)