browser-cookie-bridge 1.3.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,6 +16,7 @@
16
16
  <img src="https://img.shields.io/badge/local--first-cloud%20optional-C68B3C" alt="Local-first, cloud optional" />
17
17
  <img src="https://img.shields.io/github/stars/apoorvdarshan/browser-cookie-bridge?logo=github&color=C68B3C" alt="GitHub stars" />
18
18
  <a href="https://www.producthunt.com/products/browser-cookie-bridge"><img src="https://img.shields.io/badge/Product%20Hunt-View%20launch-DA552F?logo=producthunt&logoColor=white" alt="Browser Cookie Bridge on Product Hunt" /></a>
19
+ <a href="https://tally.so/r/J9j0D4"><img src="https://img.shields.io/badge/Windows-Join%20waitlist-8E2735?logo=windows&logoColor=white" alt="Join the Browser Cookie Bridge Windows waitlist" /></a>
19
20
  <img src="https://img.shields.io/badge/license-MIT-3DA639" alt="MIT License" />
20
21
  </p>
21
22
 
@@ -28,6 +29,7 @@
28
29
  <a href="CONTRIBUTING.md">Contribute</a> ·
29
30
  <a href="https://github.com/apoorvdarshan/browser-cookie-bridge/issues/new?template=bug_report.yml">Report a bug</a> ·
30
31
  <a href="https://www.producthunt.com/products/browser-cookie-bridge">Product Hunt</a> ·
32
+ <a href="https://tally.so/r/J9j0D4">Windows waitlist</a> ·
31
33
  <a href="#support">Support</a>
32
34
  </p>
33
35
 
@@ -44,7 +46,7 @@
44
46
 
45
47
  ---
46
48
 
47
- > **Version 1.3.0 makes large Browserless uploads observable and recoverable.** The app measures the local profile and IndexedDB without reading their contents, streams capture progress and elapsed time, supports cancellation with deterministic temporary-data cleanup, reports auto-fit omissions, and verifies the cloud profile after upload.
49
+ > **Version 1.4.0 adds an optional one-click Codex restart flow.** When enabled for a manual sync, Browser Cookie Bridge force quits Codex, waits for its database to close, performs the verified local transfer, preserves the imported/skipped/failed counts, and reopens Codex only after success. The option is off by default.
48
50
 
49
51
  ## Why Browser Cookie Bridge
50
52
 
@@ -52,11 +54,16 @@ Signing into the same sites across several browsers is repetitive. Export files
52
54
 
53
55
  Browser Cookie Bridge gives those browser profiles a small, native control panel. Local browser and Codex transfers stay on the Mac. If you explicitly select Browserless Cloud and click Upload, the app can instead send authenticated profile state to your own Browserless account.
54
56
 
57
+ ## Windows waitlist
58
+
59
+ Browser Cookie Bridge currently ships for macOS. A separate Windows app is being explored, with demand guiding whether it moves into active development. If you use Windows, [join the early-access waitlist](https://tally.so/r/J9j0D4) and tell us which browsers and transfer paths matter to you.
60
+
55
61
  ## Features
56
62
 
57
63
  - 🍪 **Cookie and session transfer** — cookies are enabled by default, including supported domain, path, expiry, security, `SameSite`, and partition attributes.
58
64
  - 🌐 **Seven Chromium browsers** — Brave, Chrome, Edge, Arc, Vivaldi, Opera, and Perplexity Comet can be sources or destinations.
59
65
  - ✨ **ChatGPT Codex import** — merge selected local browser data into Codex's built-in browser; Codex is destination-only.
66
+ - 🔁 **Optional Codex restart** — manual Sync can force quit a running Codex instance and reopen it only after a successful local transfer; off by default.
60
67
  - ☁️ **Optional Browserless upload** — create or refresh a Browserless authenticated profile with cookies, local storage, and IndexedDB; see a local size preflight, live progress, cancellation, and post-upload verification.
61
68
  - 🕘 **Background automation** — sync when you sign in, at a fixed daily time, or whenever you choose.
62
69
  - ◉ **Native menu-bar app** — closing the window removes the Dock icon while the helper continues running.
@@ -91,7 +98,7 @@ The DMG is self-contained. Node.js 24+ and Xcode Command Line Tools are required
91
98
  - **[Apple silicon DMG](https://github.com/apoorvdarshan/browser-cookie-bridge/releases/latest/download/Browser-Cookie-Bridge-arm64.dmg)** — M1, M2, M3, M4, and newer
92
99
  - **[Intel DMG](https://github.com/apoorvdarshan/browser-cookie-bridge/releases/latest/download/Browser-Cookie-Bridge-x64.dmg)** — Intel-based Macs
93
100
 
94
- Open the DMG, drag **Browser Cookie Bridge** onto **Applications**, then open it from Applications. The current public build is checksum-verified and ad-hoc signed but not Apple-notarized, so macOS may require **Control-click Open** on first launch. It does not require Node.js, Xcode, Terminal, or an administrator password.
101
+ Open the DMG, drag **Browser Cookie Bridge** onto **Applications**, then open it from Applications. Current DMGs are checksum-verified, Developer ID-signed, and Apple-notarized for a standard macOS first-launch experience. The app does not require Node.js, Xcode, Terminal, or an administrator password.
95
102
 
96
103
  ### Via npm
97
104
 
@@ -125,11 +132,11 @@ The final command compiles the native SwiftUI app, enables **Open at login**, **
125
132
  No browser extension is needed for this path.
126
133
 
127
134
  1. Select a source browser and **ChatGPT Codex** as the destination.
128
- 2. Quit Codex completely. Closing only its browser panel is not enough.
135
+ 2. Quit Codex completely, or enable **Restart Codex automatically**. Closing only its browser panel is not enough.
129
136
  3. Choose **Cookies** and, optionally, **History URLs**.
130
- 4. Press **Sync now** and reopen Codex after the success message.
137
+ 4. Press **Sync now** and reopen Codex after the success message if automatic restart is off.
131
138
 
132
- If Codex is open, the app blocks the transfer and tells you what to close. It never force-quits Codex.
139
+ By default, an open Codex blocks the transfer. The optional **Restart Codex automatically** setting applies only to a manual Sync: it force quits Codex, waits for its database to close, and reopens Codex only after the transfer succeeds. Scheduled and login syncs never force quit it.
133
140
 
134
141
  ### Browser → browser
135
142
 
@@ -171,6 +178,7 @@ The official CLI records the Browserless upload-disclaimer acceptance timestamp
171
178
  | **Sync at login** | Runs once whenever you sign in; on by default |
172
179
  | **Open at login** | Starts the background app after macOS login; on by default |
173
180
  | **Show in menu bar** | Keeps sync, status, updates, and support actions close at hand |
181
+ | **Restart Codex automatically** | Force quits Codex for a manual sync and reopens it only after success; off by default |
174
182
  | **Check for updates** | Finds a newer GitHub release, verifies its DMG, and offers install + relaunch |
175
183
 
176
184
  Automation uses the saved source, destination, and data choices for local transfers. A scheduled Codex sync safely exits without making changes when Codex is open. Browserless cloud uploads are always manual and require an explicit upload action.
@@ -180,7 +188,7 @@ Automation uses the saved source, destination, and data choices for local transf
180
188
  ```bash
181
189
  browser-cookie-bridge install-app [--no-open]
182
190
  browser-cookie-bridge setup [--hour 9] [--minute 0] [--no-schedule]
183
- browser-cookie-bridge preferences --source brave --target codex --cookies on --history off
191
+ browser-cookie-bridge preferences --source brave --target codex --cookies on --history off --auto-restart-codex off
184
192
  browser-cookie-bridge sync [--timeout 300] [--allow-cloud-upload]
185
193
  browser-cookie-bridge browserless-preflight
186
194
  browser-cookie-bridge doctor
@@ -255,6 +263,16 @@ The product landing page, documentation overview, Privacy Policy, and Terms are
255
263
  npm run web
256
264
  ```
257
265
 
266
+ ## Star History
267
+
268
+ <a href="https://github.com/apoorvdarshan/browser-cookie-bridge/stargazers">
269
+ <picture>
270
+ <source media="(prefers-color-scheme: dark)" srcset="https://cookiebridge.apoorvdarshan.com/api/star-history.svg?theme=dark&amp;v=1" />
271
+ <source media="(prefers-color-scheme: light)" srcset="https://cookiebridge.apoorvdarshan.com/api/star-history.svg?theme=light&amp;v=1" />
272
+ <img alt="Browser Cookie Bridge GitHub star history" src="https://cookiebridge.apoorvdarshan.com/api/star-history.svg?theme=light&amp;v=1" />
273
+ </picture>
274
+ </a>
275
+
258
276
  ## License
259
277
 
260
278
  [MIT](LICENSE) © 2026 [Apoorv Darshan](https://github.com/apoorvdarshan)
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Browser Cookie Bridge",
4
- "version": "1.3.0",
4
+ "version": "1.4.1",
5
5
  "description": "Transfers selected browser data locally between supported browsers and into ChatGPT Codex.",
6
6
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5o6lDkXzU/23Jm2QRk4mfuxNnr3VlEIjRHcZr93Gdo7PI7asHJQhigaMGT6UOz0lkaTn1F+TGIPlsJo9GNBSsxbCfxWoBSXcFQFrPolY5LEfKaMtZQQ6eb4UAnp+bbUwb1den4On0piWU47Yzl6i0Jcdg4EG5H1kCSF/6nJsbKgF7OTaR/3drULu40yRvflzSXRhly3UDO7c5mgHJsaMwVV+VILsxbcPOoRINjuo3zDC737HFs67dpf38xF3tjSVL0HURAWKKobMvfABiDfVEm5O0Lvf+57d1Ib12QLT2qtVXNDy3iYwX+w2S0wH2vIMsfvoFPlRdmeqft70kZOqXwIDAQAB",
7
7
  "permissions": ["alarms", "cookies", "history"],
@@ -10,8 +10,8 @@
10
10
  <key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
11
11
  <key>CFBundleName</key><string>Browser Cookie Bridge</string>
12
12
  <key>CFBundlePackageType</key><string>APPL</string>
13
- <key>CFBundleShortVersionString</key><string>1.3.0</string>
14
- <key>CFBundleVersion</key><string>27</string>
13
+ <key>CFBundleShortVersionString</key><string>1.4.1</string>
14
+ <key>CFBundleVersion</key><string>29</string>
15
15
  <key>LSMinimumSystemVersion</key><string>13.5</string>
16
16
  <key>NSHighResolutionCapable</key><true/>
17
17
  </dict>
@@ -578,6 +578,18 @@ struct PreferencesPanel: View {
578
578
  PreferenceRow(icon: "key.slash", color: .secondary, title: "Passwords", detail: "Browser extensions cannot access passwords", muted: true) {
579
579
  FixedBadge("Unavailable")
580
580
  }
581
+ if model.selectedTargetID == "codex" {
582
+ RowDivider()
583
+ PreferenceRow(
584
+ icon: "arrow.clockwise.circle",
585
+ color: Theme.accent,
586
+ title: "Restart Codex automatically",
587
+ detail: "Force quit before manual sync; reopen only after success"
588
+ ) {
589
+ Toggle("", isOn: Binding(get: { model.autoRestartCodex }, set: { model.setAutoRestartCodex($0) }))
590
+ .labelsHidden().toggleStyle(.switch).tint(Theme.active).disabled(model.isWorking || model.isSyncing)
591
+ }
592
+ }
581
593
  }
582
594
 
583
595
  SectionLabel(title: "Automation", detail: "Runs in the background", separated: true)
@@ -77,6 +77,7 @@ final class SyncModel: ObservableObject {
77
77
  @Published var openAtLogin = false
78
78
  @Published var menuBarEnabled = true
79
79
  @Published var autoCheckUpdates = true
80
+ @Published var autoRestartCodex = false
80
81
  @Published var isCheckingForUpdates = false
81
82
  @Published var isInstallingUpdate = false
82
83
  @Published var availableUpdateVersion: String?
@@ -128,7 +129,7 @@ final class SyncModel: ObservableObject {
128
129
  var targetName: String {
129
130
  isBrowserlessTarget ? "Browserless Cloud" : selectedTargetBrowser?.name ?? "ChatGPT Codex"
130
131
  }
131
- var codexBlocked: Bool { selectedTargetID == "codex" && codexRunning }
132
+ var codexBlocked: Bool { selectedTargetID == "codex" && codexRunning && !autoRestartCodex }
132
133
  var browserlessBlocked: Bool {
133
134
  isBrowserlessTarget && (!browserlessConfigured || sourceBrowserRunning || selectedSourceID == "comet")
134
135
  }
@@ -261,6 +262,7 @@ final class SyncModel: ObservableObject {
261
262
  historyEnabled = config.imports?.history ?? false
262
263
  menuBarEnabled = config.ui?.menuBar ?? true
263
264
  autoCheckUpdates = config.ui?.autoCheckUpdates ?? true
265
+ autoRestartCodex = config.ui?.autoRestartCodex ?? false
264
266
  browserlessProfileName = config.browserless?.profileName ?? "browser-cookie-bridge"
265
267
  browserlessRegion = config.browserless?.region ?? "sfo"
266
268
  browserlessOnlyDomains = (config.browserless?.onlyDomains ?? []).joined(separator: ", ")
@@ -347,6 +349,11 @@ final class SyncModel: ObservableObject {
347
349
  if enabled { checkForUpdates() }
348
350
  }
349
351
 
352
+ func setAutoRestartCodex(_ enabled: Bool) {
353
+ autoRestartCodex = enabled
354
+ persistPreferences(successMessage: enabled ? "Automatic Codex restart enabled" : "Automatic Codex restart disabled")
355
+ }
356
+
350
357
  func checkForUpdates(showAlert: Bool = false) {
351
358
  guard !isCheckingForUpdates, !isInstallingUpdate else { return }
352
359
  guard let url = URL(string: "https://api.github.com/repos/apoorvdarshan/browser-cookie-bridge/releases/latest") else { return }
@@ -449,6 +456,68 @@ final class SyncModel: ObservableObject {
449
456
  }
450
457
  return
451
458
  }
459
+ if selectedTargetID == "codex" && codexRunning && autoRestartCodex {
460
+ forceQuitCodexThenSync(showMenuBarAlert: showMenuBarAlert)
461
+ return
462
+ }
463
+ startSync(showMenuBarAlert: showMenuBarAlert, reopenCodexOnSuccess: false)
464
+ }
465
+
466
+ private func forceQuitCodexThenSync(showMenuBarAlert: Bool) {
467
+ isSyncing = true
468
+ uploadCanceling = false
469
+ state = .syncing
470
+ primaryStatus = "Closing Codex for sync"
471
+ secondaryStatus = "Force quitting ChatGPT Codex and waiting for its browser database to close…"
472
+ let applications = NSWorkspace.shared.runningApplications.filter {
473
+ $0.bundleIdentifier == "com.openai.codex"
474
+ }
475
+ guard !applications.isEmpty, applications.allSatisfy({ $0.forceTerminate() }) else {
476
+ finishCodexPreparationFailure(
477
+ message: "macOS could not force quit ChatGPT Codex. Quit it manually, then try again.",
478
+ showMenuBarAlert: showMenuBarAlert
479
+ )
480
+ return
481
+ }
482
+ waitForCodexToQuit(attemptsRemaining: 50, showMenuBarAlert: showMenuBarAlert)
483
+ }
484
+
485
+ private func waitForCodexToQuit(attemptsRemaining: Int, showMenuBarAlert: Bool) {
486
+ let stillRunning = NSWorkspace.shared.runningApplications.contains {
487
+ $0.bundleIdentifier == "com.openai.codex"
488
+ }
489
+ if !stillRunning {
490
+ codexRunning = false
491
+ secondaryStatus = "Codex is closed — waiting briefly for its database to be released…"
492
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) { [weak self] in
493
+ self?.startSync(showMenuBarAlert: showMenuBarAlert, reopenCodexOnSuccess: true)
494
+ }
495
+ return
496
+ }
497
+ guard attemptsRemaining > 0 else {
498
+ finishCodexPreparationFailure(
499
+ message: "ChatGPT Codex did not close within 10 seconds. Quit it manually, then try again.",
500
+ showMenuBarAlert: showMenuBarAlert
501
+ )
502
+ return
503
+ }
504
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
505
+ self?.waitForCodexToQuit(attemptsRemaining: attemptsRemaining - 1, showMenuBarAlert: showMenuBarAlert)
506
+ }
507
+ }
508
+
509
+ private func finishCodexPreparationFailure(message: String, showMenuBarAlert: Bool) {
510
+ isSyncing = false
511
+ state = .error
512
+ primaryStatus = "Could not close Codex"
513
+ secondaryStatus = message
514
+ updateEndpointRunningStatus()
515
+ if showMenuBarAlert {
516
+ postNativeAlert(title: primaryStatus, message: secondaryStatus, kind: .error)
517
+ }
518
+ }
519
+
520
+ private func startSync(showMenuBarAlert: Bool, reopenCodexOnSuccess: Bool) {
452
521
  isSyncing = true
453
522
  uploadCanceling = false
454
523
  state = .syncing
@@ -507,7 +576,13 @@ final class SyncModel: ObservableObject {
507
576
  : "Keep both browsers open and check the extensions")
508
577
  }
509
578
  self.updateEndpointRunningStatus()
510
- if showMenuBarAlert {
579
+ if success && reopenCodexOnSuccess {
580
+ self.reopenCodexAfterSuccessfulSync(
581
+ partial: partial,
582
+ syncSummary: self.secondaryStatus,
583
+ showMenuBarAlert: showMenuBarAlert
584
+ )
585
+ } else if showMenuBarAlert {
511
586
  self.postNativeAlert(
512
587
  title: self.primaryStatus,
513
588
  message: self.secondaryStatus,
@@ -517,6 +592,44 @@ final class SyncModel: ObservableObject {
517
592
  }
518
593
  }
519
594
 
595
+ private func reopenCodexAfterSuccessfulSync(partial: Bool, syncSummary: String, showMenuBarAlert: Bool) {
596
+ let transferResult = syncSummary.replacingOccurrences(
597
+ of: "Reopen Codex to use the updated sessions. ",
598
+ with: ""
599
+ )
600
+ guard let codexURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.openai.codex") else {
601
+ state = .warning
602
+ primaryStatus = "Codex sessions updated, but Codex was not reopened"
603
+ secondaryStatus = "\(transferResult)\n\nCodex could not be found. Open it manually to use the updated sessions."
604
+ if showMenuBarAlert {
605
+ postNativeAlert(title: primaryStatus, message: secondaryStatus, kind: .warning)
606
+ }
607
+ return
608
+ }
609
+ NSWorkspace.shared.openApplication(at: codexURL, configuration: NSWorkspace.OpenConfiguration()) { [weak self] _, error in
610
+ Task { @MainActor in
611
+ guard let self else { return }
612
+ if let error {
613
+ self.state = .warning
614
+ self.primaryStatus = "Codex sessions updated, but Codex was not reopened"
615
+ self.secondaryStatus = "\(transferResult)\n\nCodex could not be reopened: \(error.localizedDescription)"
616
+ } else {
617
+ self.codexRunning = true
618
+ self.state = partial ? .warning : .success
619
+ self.primaryStatus = partial ? "Codex sync completed with warnings" : "Codex sessions updated"
620
+ self.secondaryStatus = "\(transferResult)\n\nChatGPT Codex reopened successfully."
621
+ }
622
+ if showMenuBarAlert {
623
+ self.postNativeAlert(
624
+ title: self.primaryStatus,
625
+ message: self.secondaryStatus,
626
+ kind: self.state == .success ? .information : .warning
627
+ )
628
+ }
629
+ }
630
+ }
631
+ }
632
+
520
633
  func cancelSync() {
521
634
  guard isBrowserlessTarget, isSyncing, !uploadCanceling else { return }
522
635
  uploadCanceling = true
@@ -627,6 +740,7 @@ final class SyncModel: ObservableObject {
627
740
  "--history", historyEnabled ? "on" : "off",
628
741
  "--menu-bar", menuBarEnabled ? "on" : "off",
629
742
  "--auto-check-updates", autoCheckUpdates ? "on" : "off",
743
+ "--auto-restart-codex", autoRestartCodex ? "on" : "off",
630
744
  "--browserless-profile", browserlessProfileName,
631
745
  "--browserless-region", browserlessRegion,
632
746
  "--browserless-domains", browserlessOnlyDomains,
@@ -827,7 +941,13 @@ final class SyncModel: ObservableObject {
827
941
  $0.bundleIdentifier == selectedBrowser.bundleIdentifier
828
942
  }
829
943
  guard !isSyncing else { return }
830
- if selectedTargetID == "codex" && codexRunning {
944
+ if selectedTargetID == "codex" && codexRunning && autoRestartCodex {
945
+ if state == .ready || primaryStatus == "Quit Codex before syncing" {
946
+ state = .ready
947
+ primaryStatus = "Ready to sync and restart Codex"
948
+ secondaryStatus = "Sync will force quit Codex and reopen it only after a successful transfer"
949
+ }
950
+ } else if selectedTargetID == "codex" && codexRunning {
831
951
  state = .warning
832
952
  primaryStatus = "Quit Codex before syncing"
833
953
  secondaryStatus = "Close ChatGPT Codex completely so its local cookie database can be updated safely"
@@ -948,6 +1068,7 @@ private struct AppConfig: Decodable {
948
1068
  let menuBar: Bool?
949
1069
  let openAtLogin: Bool?
950
1070
  let autoCheckUpdates: Bool?
1071
+ let autoRestartCodex: Bool?
951
1072
  }
952
1073
 
953
1074
  struct BrowserlessSettings: Decodable {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-cookie-bridge",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "Local-first cookie and session transfer for macOS with optional Browserless upload",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,6 +8,7 @@
8
8
  },
9
9
  "scripts": {
10
10
  "test": "node --test test/*.test.js",
11
+ "test:web-worker": "node --test web/worker/*.test.mjs",
11
12
  "check": "node --check bin/brave-codex-cookie-sync.js && node --check src/app-installer.js && node --check src/browserless.js && node --check src/browserless-preflight.js && node --check src/browserless-runner.js && node --check src/broker.js && node --check src/chromium-reader.js && node --check src/cli.js && node --check src/codex-direct-import.js && node --check src/config.js && node --check src/paths.js && node --check src/scheduler.js && node --check src/updater.js && node --check scripts/build-dmg.js && node --check extension-template/background.js && node --check web/server.js && node --check web/script.js",
12
13
  "release:check": "node scripts/check-release-version.js",
13
14
  "build:app": "node bin/brave-codex-cookie-sync.js install-app --no-open",
package/src/cli.js CHANGED
@@ -45,7 +45,7 @@ Commands:
45
45
  setup [--hour 9] [--minute 0] [--no-schedule]
46
46
  install-app [--no-open]
47
47
  bootstrap-bundled --app-path /Applications/Browser Cookie Bridge.app
48
- preferences --source brave --target codex --cookies on --history off --menu-bar on --auto-check-updates on
48
+ preferences --source brave --target codex --cookies on --history off --menu-bar on --auto-check-updates on --auto-restart-codex off
49
49
  sync [--timeout 300] [--allow-cloud-upload]
50
50
  browserless-preflight
51
51
  doctor
@@ -128,12 +128,13 @@ function preferences(args) {
128
128
  menuBar: booleanFlag(args, "--menu-bar", existing.ui?.menuBar === true),
129
129
  openAtLogin: booleanFlag(args, "--open-at-login", existing.ui?.openAtLogin !== false),
130
130
  autoCheckUpdates: booleanFlag(args, "--auto-check-updates", existing.ui?.autoCheckUpdates !== false),
131
+ autoRestartCodex: booleanFlag(args, "--auto-restart-codex", existing.ui?.autoRestartCodex === true),
131
132
  browserlessProfileName: stringFlag(args, "--browserless-profile", existing.browserless?.profileName || "browser-cookie-bridge"),
132
133
  browserlessRegion: stringFlag(args, "--browserless-region", existing.browserless?.region || "sfo"),
133
134
  browserlessOnlyDomains: optionalStringFlag(args, "--browserless-domains", (existing.browserless?.onlyDomains || []).join(",")),
134
135
  });
135
136
  console.log(
136
- `Saved: source=${config.sourceBrowser}, target=${config.targetBrowser}, cookies=${config.imports.cookies ? "on" : "off"}, history=${config.imports.history ? "on" : "off"}, menu-bar=${config.ui.menuBar ? "on" : "off"}, open-at-login=${config.ui.openAtLogin ? "on" : "off"}, auto-check-updates=${config.ui.autoCheckUpdates ? "on" : "off"}`,
137
+ `Saved: source=${config.sourceBrowser}, target=${config.targetBrowser}, cookies=${config.imports.cookies ? "on" : "off"}, history=${config.imports.history ? "on" : "off"}, menu-bar=${config.ui.menuBar ? "on" : "off"}, open-at-login=${config.ui.openAtLogin ? "on" : "off"}, auto-check-updates=${config.ui.autoCheckUpdates ? "on" : "off"}, auto-restart-codex=${config.ui.autoRestartCodex ? "on" : "off"}`,
137
138
  );
138
139
  }
139
140
 
@@ -435,6 +436,7 @@ function setAppLogin(enabled) {
435
436
  menuBar: existing.ui?.menuBar === true,
436
437
  openAtLogin: enabled,
437
438
  autoCheckUpdates: existing.ui?.autoCheckUpdates !== false,
439
+ autoRestartCodex: existing.ui?.autoRestartCodex === true,
438
440
  browserlessProfileName: existing.browserless?.profileName,
439
441
  browserlessRegion: existing.browserless?.region,
440
442
  browserlessOnlyDomains: existing.browserless?.onlyDomains,
package/src/config.js CHANGED
@@ -50,6 +50,7 @@ export function installConfig({ home, hour = 9, minute = 0, nodePath = process.e
50
50
  menuBar: existing.ui?.menuBar !== false,
51
51
  openAtLogin: existing.ui?.openAtLogin !== false,
52
52
  autoCheckUpdates: existing.ui?.autoCheckUpdates !== false,
53
+ autoRestartCodex: existing.ui?.autoRestartCodex === true,
53
54
  },
54
55
  browserless: {
55
56
  profileName: cleanProfileName(existing.browserless?.profileName) || "browser-cookie-bridge",
@@ -77,6 +78,7 @@ export function updatePreferences({
77
78
  menuBar,
78
79
  openAtLogin,
79
80
  autoCheckUpdates,
81
+ autoRestartCodex,
80
82
  browserlessProfileName,
81
83
  browserlessRegion,
82
84
  browserlessOnlyDomains,
@@ -98,6 +100,7 @@ export function updatePreferences({
98
100
  menuBar: Boolean(menuBar),
99
101
  openAtLogin: Boolean(openAtLogin),
100
102
  autoCheckUpdates: Boolean(autoCheckUpdates),
103
+ autoRestartCodex: Boolean(autoRestartCodex),
101
104
  };
102
105
  const region = browserlessRegion ?? config.browserless?.region ?? "sfo";
103
106
  if (!["sfo", "lon", "ams"].includes(region)) throw new Error("Browserless region must be sfo, lon, or ams");