browser4-cli 0.1.16 → 0.1.17

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.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser4-cli",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Browser automation CLI for AI agents",
5
5
  "type": "module",
6
6
  "files": [
@@ -154,12 +154,60 @@ function Get-DefaultInstallDir {
154
154
  throw "Unsupported OS"
155
155
  }
156
156
 
157
+ # ──────────────────────────────────────────────
158
+ # China mainland locale detection (zero-network)
159
+ # ──────────────────────────────────────────────
160
+
161
+ <#
162
+ .SYNOPSIS
163
+ Detect whether the current system is likely in China mainland.
164
+ Uses only local env vars and .NET APIs — no network calls.
165
+ #>
166
+ function Test-ChinaLocale {
167
+ # 1 — Locale env vars
168
+ $lang = $env:LC_ALL, $env:LANG, $env:LC_CTYPE, $env:LC_MESSAGES | Where-Object { $_ } | Select-Object -First 1
169
+ if ($lang -and ($lang -match '^zh_CN' -or $lang -match '^zh-CN' -or $lang -match '^Chinese \(Simplified\)_China')) {
170
+ return $true
171
+ }
172
+
173
+ # 2 — TZ env var
174
+ $tzEnv = $env:TZ
175
+ if ($tzEnv -and ($tzEnv -match '^Asia/(Shanghai|Chongqing|Urumqi|Harbin)$')) {
176
+ return $true
177
+ }
178
+
179
+ # 3 — .NET TimeZoneInfo (works on Windows and Unix PowerShell 7+)
180
+ try {
181
+ $tzId = [System.TimeZoneInfo]::Local.Id
182
+ if ($tzId -match '^Asia/(Shanghai|Chongqing|Urumqi|Harbin)$') {
183
+ return $true
184
+ }
185
+ } catch {
186
+ # TimeZoneInfo not available (unlikely on PS 5.1+ but guard anyway)
187
+ }
188
+
189
+ # 4 — /etc/timezone (PowerShell on Linux/macOS)
190
+ if (-not $script:OSWin -and (Test-Path '/etc/timezone')) {
191
+ try {
192
+ $tz = Get-Content '/etc/timezone' -Raw -ErrorAction Stop
193
+ if ($tz -match '^Asia/(Shanghai|Chongqing|Urumqi|Harbin)$') {
194
+ return $true
195
+ }
196
+ } catch {
197
+ # Permission or read error — skip
198
+ }
199
+ }
200
+
201
+ return $false
202
+ }
203
+
157
204
  # ──────────────────────────────────────────────
158
205
  # Download URLs
159
206
  # ──────────────────────────────────────────────
160
207
 
161
208
  $GITHUB_REPO = "platonai/Browser4"
162
209
  $OSS_BASE = "https://browser4.oss-cn-beijing.aliyuncs.com"
210
+ $script:ChinaDetected = $false
163
211
 
164
212
  function Get-DownloadUrls {
165
213
  param([string]$BinaryName, [string]$VersionTag)
@@ -181,8 +229,13 @@ function Get-DownloadUrls {
181
229
  } elseif ($Source -eq "oss") {
182
230
  $urls += @{ Url = $ossUrl; Label = "Aliyun OSS" }
183
231
  } else {
184
- $urls += @{ Url = $ghUrl; Label = "GitHub Releases" }
185
- $urls += @{ Url = $ossUrl; Label = "Aliyun OSS" }
232
+ if ($script:ChinaDetected) {
233
+ $urls += @{ Url = $ossUrl; Label = "Aliyun OSS" }
234
+ $urls += @{ Url = $ghUrl; Label = "GitHub Releases" }
235
+ } else {
236
+ $urls += @{ Url = $ghUrl; Label = "GitHub Releases" }
237
+ $urls += @{ Url = $ossUrl; Label = "Aliyun OSS" }
238
+ }
186
239
  }
187
240
 
188
241
  return $urls
@@ -359,6 +412,14 @@ function Main {
359
412
  Write-Summary "╚════════════════════════════════════════╝" -Color Cyan
360
413
  Write-Summary ""
361
414
 
415
+ # Auto-detect China mainland locale when no explicit source is given
416
+ if (-not $Source) {
417
+ $script:ChinaDetected = Test-ChinaLocale
418
+ if ($script:ChinaDetected) {
419
+ Write-Step "China mainland locale detected: preferring Aliyun OSS mirror."
420
+ }
421
+ }
422
+
362
423
  # Detect platform
363
424
  $platformKey = Get-PlatformKey
364
425
  $binaryName = Get-BinaryName -PlatformKey $platformKey
@@ -36,6 +36,7 @@ SOURCE=""
36
36
  ADD_TO_PATH=true
37
37
  SILENT=false
38
38
  DRY_RUN=false
39
+ CHINA_DETECTED=false
39
40
 
40
41
  # ──────────────────────────────────────────────
41
42
  # Helpers
@@ -110,6 +111,35 @@ while [[ $# -gt 0 ]]; do
110
111
  esac
111
112
  done
112
113
 
114
+ # ──────────────────────────────────────────────
115
+ # China mainland locale detection (zero-network)
116
+ # ──────────────────────────────────────────────
117
+
118
+ detect_china_locale() {
119
+ # 1 — Locale env vars
120
+ local lang
121
+ lang="${LC_ALL:-${LANG:-${LC_CTYPE:-${LC_MESSAGES:-}}}}"
122
+ case "$lang" in
123
+ zh_CN*|zh-CN*|"Chinese (Simplified)_China"*) return 0 ;;
124
+ esac
125
+
126
+ # 2 — TZ env var
127
+ case "${TZ:-}" in
128
+ Asia/Shanghai|Asia/Chongqing|Asia/Urumqi|Asia/Harbin) return 0 ;;
129
+ esac
130
+
131
+ # 3 — /etc/timezone
132
+ if [[ -f /etc/timezone ]]; then
133
+ local tz
134
+ tz=$(cat /etc/timezone 2>/dev/null || true)
135
+ case "$tz" in
136
+ Asia/Shanghai|Asia/Chongqing|Asia/Urumqi|Asia/Harbin) return 0 ;;
137
+ esac
138
+ fi
139
+
140
+ return 1
141
+ }
142
+
113
143
  # ──────────────────────────────────────────────
114
144
  # Platform detection
115
145
  # ──────────────────────────────────────────────
@@ -245,8 +275,13 @@ get_download_urls() {
245
275
  github) urls+=("GitHub Releases|${gh_url}") ;;
246
276
  oss) urls+=("Aliyun OSS|${oss_url}") ;;
247
277
  *)
248
- urls+=("GitHub Releases|${gh_url}")
249
- urls+=("Aliyun OSS|${oss_url}")
278
+ if [[ "$CHINA_DETECTED" == true ]]; then
279
+ urls+=("Aliyun OSS|${oss_url}")
280
+ urls+=("GitHub Releases|${gh_url}")
281
+ else
282
+ urls+=("GitHub Releases|${gh_url}")
283
+ urls+=("Aliyun OSS|${oss_url}")
284
+ fi
250
285
  ;;
251
286
  esac
252
287
 
@@ -430,6 +465,14 @@ main() {
430
465
  check_commands
431
466
  header
432
467
 
468
+ # Auto-detect China mainland locale when no explicit source is given
469
+ if [[ -z "$SOURCE" ]]; then
470
+ if detect_china_locale; then
471
+ CHINA_DETECTED=true
472
+ step "China mainland locale detected: preferring Aliyun OSS mirror."
473
+ fi
474
+ fi
475
+
433
476
  # Detect platform
434
477
  local platform_key binary_name
435
478
  platform_key=$(get_platform_key)
@@ -45,9 +45,65 @@ const packageJson = JSON.parse(
45
45
  const version = packageJson.version;
46
46
  const commandNames = Object.keys(packageJson.bin || { 'browser4-cli': './bin/browser4-cli.js' });
47
47
 
48
- // GitHub release URL
48
+ // ---------------------------------------------------------------------------
49
+ // China mainland locale detection (zero-network — uses only local env / Intl)
50
+ // ---------------------------------------------------------------------------
51
+
52
+ function isChinaLocale() {
53
+ // 1 — Locale env vars (Unix, Git Bash, WSL, Docker)
54
+ const lang = process.env.LC_ALL
55
+ || process.env.LANG
56
+ || process.env.LC_CTYPE
57
+ || process.env.LC_MESSAGES
58
+ || '';
59
+ if (/^(?:zh_CN|zh-CN|Chinese \(Simplified\)_China)/i.test(lang)) {
60
+ return true;
61
+ }
62
+
63
+ // 2 — TZ env var (Docker, CI)
64
+ const tz = process.env.TZ || '';
65
+ if (/^Asia\/(Shanghai|Chongqing|Urumqi|Harbin)$/.test(tz)) {
66
+ return true;
67
+ }
68
+
69
+ // 3 — Intl timezone API (V8/Node.js, no filesystem access needed)
70
+ try {
71
+ const resolvedTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
72
+ if (/^Asia\/(Shanghai|Chongqing|Urumqi|Harbin)$/.test(resolvedTz)) {
73
+ return true;
74
+ }
75
+ } catch {
76
+ // Intl not available (very old Node) — skip
77
+ }
78
+
79
+ return false;
80
+ }
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // Download URLs (locale-aware ordering)
84
+ // ---------------------------------------------------------------------------
85
+
49
86
  const GITHUB_REPO = 'platonai/Browser4';
50
- const DOWNLOAD_URL = `https://github.com/${GITHUB_REPO}/releases/download/v${version}/${binaryName}`;
87
+ const DOWNLOAD_URL_GITHUB = `https://github.com/${GITHUB_REPO}/releases/download/v${version}/${binaryName}`;
88
+ const DOWNLOAD_URL_OSS = `https://browser4.oss-cn-beijing.aliyuncs.com/releases/download/v${version}/${binaryName}`;
89
+
90
+ /**
91
+ * Return download URLs ordered by locale preference. `BROWSER4_RELEASES_BASE_URL`
92
+ * takes precedence; otherwise China mainland users get OSS-first ordering.
93
+ */
94
+ function getDownloadUrls() {
95
+ // Single-source override
96
+ const envOverride = (process.env.BROWSER4_RELEASES_BASE_URL || '').trim();
97
+ if (envOverride) {
98
+ const base = envOverride.replace(/\/+$/, '');
99
+ return [`${base}/download/v${version}/${binaryName}`];
100
+ }
101
+
102
+ if (isChinaLocale()) {
103
+ return [DOWNLOAD_URL_OSS, DOWNLOAD_URL_GITHUB];
104
+ }
105
+ return [DOWNLOAD_URL_GITHUB, DOWNLOAD_URL_OSS];
106
+ }
51
107
 
52
108
  async function downloadFile(url, dest) {
53
109
  return new Promise((resolve, reject) => {
@@ -130,19 +186,31 @@ async function main() {
130
186
  }
131
187
 
132
188
  console.log(`Downloading native binary for ${platformKey}...`);
133
- console.log(`URL: ${DOWNLOAD_URL}`);
134
189
 
135
- try {
136
- await downloadFile(DOWNLOAD_URL, binaryPath);
190
+ const urls = getDownloadUrls();
137
191
 
138
- // Make executable on Unix
139
- if (platform() !== 'win32') {
140
- chmodSync(binaryPath, 0o755);
192
+ let lastError = null;
193
+ for (const url of urls) {
194
+ console.log(`URL: ${url}`);
195
+ try {
196
+ await downloadFile(url, binaryPath);
197
+
198
+ // Make executable on Unix
199
+ if (platform() !== 'win32') {
200
+ chmodSync(binaryPath, 0o755);
201
+ }
202
+
203
+ console.log(`✓ Downloaded native binary: ${binaryName}`);
204
+ lastError = null;
205
+ break;
206
+ } catch (err) {
207
+ console.log(`Download failed: ${err.message}`);
208
+ lastError = err;
141
209
  }
210
+ }
142
211
 
143
- console.log(`✓ Downloaded native binary: ${binaryName}`);
144
- } catch (err) {
145
- console.log(`Could not download native binary: ${err.message}`);
212
+ if (lastError) {
213
+ console.log(`Could not download native binary from any mirror: ${lastError.message}`);
146
214
  console.log('');
147
215
  console.log('To build the native binary locally:');
148
216
  console.log(' 1. Install Rust: https://rustup.rs');