browser4-cli 0.1.10 → 0.1.12

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.10",
3
+ "version": "0.1.12",
4
4
  "description": "Browser automation CLI for AI agents",
5
5
  "type": "module",
6
6
  "files": [
@@ -21,31 +21,24 @@ const cargoToml = readFileSync(join(rootDir, 'browser4-cli/Cargo.toml'), 'utf-8'
21
21
  const cargoVersionMatch = cargoToml.match(/^version\s*=\s*"([^"]*)"/m);
22
22
 
23
23
  if (!cargoVersionMatch) {
24
- console.error('Could not find version in browser4-cli/Cargo.toml');
25
- process.exit(1);
24
+ console.error('Could not find version in browser4-cli/Cargo.toml');
25
+ process.exit(1);
26
26
  }
27
27
 
28
28
  const cargoVersion = cargoVersionMatch[1];
29
29
 
30
- // Read dashboard package.json version
31
- const dashboardPkg = JSON.parse(readFileSync(join(rootDir, 'packages/dashboard/package.json'), 'utf-8'));
32
- const dashboardVersion = dashboardPkg.version;
33
-
34
30
  const mismatches = [];
35
31
  if (packageVersion !== cargoVersion) {
36
- mismatches.push(` browser4-cli/Cargo.toml: ${cargoVersion}`);
37
- }
38
- if (packageVersion !== dashboardVersion) {
39
- mismatches.push(` packages/dashboard: ${dashboardVersion}`);
32
+ mismatches.push(` browser4-cli/Cargo.toml: ${cargoVersion}`);
40
33
  }
41
34
 
42
35
  if (mismatches.length > 0) {
43
- console.error('Version mismatch detected!');
44
- console.error(` package.json: ${packageVersion}`);
45
- for (const m of mismatches) console.error(m);
46
- console.error('');
47
- console.error("Run 'pnpm run version:sync' to fix this.");
48
- process.exit(1);
36
+ console.error('Version mismatch detected!');
37
+ console.error(` package.json: ${packageVersion}`);
38
+ for (const m of mismatches) console.error(m);
39
+ console.error('');
40
+ console.error("Run 'pnpm run version:sync' to fix this.");
41
+ process.exit(1);
49
42
  }
50
43
 
51
44
  console.log(`Versions are in sync: ${packageVersion}`);
@@ -1,69 +1,240 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Syncs the version from package.json to all other config files.
5
- * Run this script before building or releasing.
4
+ * Syncs the CLI version from cli/package.json to the Cargo manifest.
5
+ *
6
+ * cli/package.json is the single source of truth for the CLI version. This
7
+ * allows the backend Maven project and the CLI to be published separately with
8
+ * different versions. This script:
9
+ * - Reads the version from cli/package.json
10
+ * - Strips the Maven-style "-SNAPSHOT" suffix
11
+ * - Fetches the latest published version from npm and compares it
12
+ * against the local version, warning when the bump is neither a
13
+ * patch nor a minor increment.
14
+ * - Writes the clean semver to cli/browser4-cli/Cargo.toml
15
+ * - Updates Cargo.lock to match
16
+ *
17
+ * Usage:
18
+ * node cli/scripts/sync-version.js # sync all files
19
+ * node cli/scripts/sync-version.js --check # exit 1 if out of sync (CI lint)
6
20
  */
7
21
 
8
22
  import {execSync} from "child_process";
9
23
  import {readFileSync, writeFileSync} from "fs";
10
- import {dirname, join} from "path";
24
+ import {dirname, join, resolve} from "path";
11
25
  import {fileURLToPath} from "url";
12
26
 
13
27
  const __dirname = dirname(fileURLToPath(import.meta.url));
14
- const rootDir = join(__dirname, "..");
15
- const cliDir = join(rootDir, "browser4-cli");
28
+ const scriptsDir = __dirname; // cli/scripts
29
+ const cliDir = resolve(scriptsDir, ".."); // cli
30
+ const cargoDir = join(cliDir, "browser4-cli"); // cli/browser4-cli
16
31
 
17
- // Read version from package.json (single source of truth)
18
- const packageJson = JSON.parse(
19
- readFileSync(join(rootDir, "package.json"), "utf-8")
20
- );
21
- const version = packageJson.version;
32
+ // ---------------------------------------------------------------------------
33
+ // Helpers
34
+ // ---------------------------------------------------------------------------
22
35
 
23
- console.log(`Syncing version ${version} to all config files...`);
36
+ /** Strip the "-SNAPSHOT" suffix if present. */
37
+ function stripSnapshot(version) {
38
+ if (version.endsWith("-SNAPSHOT")) {
39
+ return version.slice(0, -"-SNAPSHOT".length);
40
+ }
41
+ return version;
42
+ }
43
+
44
+ /**
45
+ * Parse a semver string into { major, minor, patch } integers.
46
+ * Returns null for non-semver strings (e.g. snapshots, prereleases).
47
+ */
48
+ function parseSemver(version) {
49
+ const m = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
50
+ if (!m) return null;
51
+ return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3]) };
52
+ }
53
+
54
+ /**
55
+ * Fetch the latest published version of a package from the npm registry.
56
+ * Returns the version string, or null if the package hasn't been published
57
+ * yet or the registry is unreachable.
58
+ */
59
+ function getLatestPublishedVersion(packageName) {
60
+ try {
61
+ const raw = execSync(`npm view "${packageName}" version --json`, {
62
+ stdio: ["ignore", "pipe", "ignore"],
63
+ timeout: 10_000,
64
+ });
65
+ return JSON.parse(raw.toString().trim());
66
+ } catch {
67
+ // Package may not be published yet, or network is unavailable.
68
+ return null;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Check whether `local` is an expected bump from `published`:
74
+ * - same version (already published)
75
+ * - next patch
76
+ * - next minor
77
+ *
78
+ * Returns { ok: true } or { ok: false, reason: "..." }.
79
+ */
80
+ function checkVersionBump(published, local) {
81
+ const pub = parseSemver(published);
82
+ const loc = parseSemver(local);
83
+
84
+ if (!pub || !loc) {
85
+ // Can't compare — pre-release / snapshot versions are fine.
86
+ return { ok: true };
87
+ }
88
+
89
+ // Same version — already published.
90
+ if (loc.major === pub.major && loc.minor === pub.minor && loc.patch === pub.patch) {
91
+ return { ok: true, note: `version ${local} is already published` };
92
+ }
93
+
94
+ // Next patch: X.Y.Z → X.Y.(Z+1)
95
+ if (loc.major === pub.major && loc.minor === pub.minor && loc.patch === pub.patch + 1) {
96
+ return { ok: true };
97
+ }
98
+
99
+ // Next minor: X.Y.Z → X.(Y+1).0
100
+ if (loc.major === pub.major && loc.minor === pub.minor + 1 && loc.patch === 0) {
101
+ return { ok: true };
102
+ }
103
+
104
+ // Local is behind published.
105
+ if (
106
+ loc.major < pub.major ||
107
+ (loc.major === pub.major && loc.minor < pub.minor) ||
108
+ (loc.major === pub.major && loc.minor === pub.minor && loc.patch < pub.patch)
109
+ ) {
110
+ return {
111
+ ok: false,
112
+ reason: `local version ${local} is behind the published version ${published}`,
113
+ };
114
+ }
24
115
 
25
- // Update Cargo.toml
26
- const cargoTomlPath = join(cliDir, "Cargo.toml");
116
+ // Anything else — skipping versions, major bump, etc.
117
+ return {
118
+ ok: false,
119
+ reason: `version bump from ${published} to ${local} is neither a patch nor a minor increment`,
120
+ };
121
+ }
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // Main
125
+ // ---------------------------------------------------------------------------
126
+
127
+ const checkOnly = process.argv.includes("--check");
128
+
129
+ // 1. Read the CLI version from cli/package.json (the source of truth)
130
+ const packageJsonPath = join(cliDir, "package.json");
131
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
132
+ const pkgName = packageJson.name;
133
+ const pkgVersion = packageJson.version;
134
+
135
+ if (!pkgVersion) {
136
+ console.error("ERROR: cli/package.json does not contain a version field.");
137
+ process.exit(1);
138
+ }
139
+
140
+ const version = stripSnapshot(pkgVersion);
141
+ if (checkOnly) console.log(`cli/package.json: ${version}`);
142
+
143
+ // 2. Compare against the latest published version on npm
144
+ const publishedVersion = getLatestPublishedVersion(pkgName);
145
+ if (publishedVersion) {
146
+ console.log(`Latest published: ${pkgName}@${publishedVersion}`);
147
+ const bump = checkVersionBump(publishedVersion, version);
148
+ if (!bump.ok) {
149
+ console.warn(`\x1b[33mWARNING: ${bump.reason}\x1b[0m`);
150
+ } else if (bump.note) {
151
+ console.log(` (${bump.note})`);
152
+ }
153
+ } else {
154
+ console.log(`Latest published: (not found — package may not be published yet)`);
155
+ }
156
+
157
+ // 3. Sync cli/browser4-cli/Cargo.toml
158
+ const cargoTomlPath = join(cargoDir, "Cargo.toml");
27
159
  let cargoToml = readFileSync(cargoTomlPath, "utf-8");
28
160
  const cargoVersionRegex = /^version\s*=\s*"[^"]*"/m;
29
- const newCargoVersion = `version = "${version}"`;
161
+ const cargoMatch = cargoToml.match(cargoVersionRegex);
30
162
 
31
- let cargoTomlUpdated = false;
32
- if (cargoVersionRegex.test(cargoToml)) {
33
- const oldMatch = cargoToml.match(cargoVersionRegex)?.[0];
34
- if (oldMatch !== newCargoVersion) {
35
- cargoToml = cargoToml.replace(cargoVersionRegex, newCargoVersion);
36
- writeFileSync(cargoTomlPath, cargoToml);
37
- console.log(` Updated browser4-cli/Cargo.toml: ${oldMatch} -> ${newCargoVersion}`);
38
- cargoTomlUpdated = true;
163
+ if (!cargoMatch) {
164
+ console.error("ERROR: Could not find version field in Cargo.toml");
165
+ process.exit(1);
166
+ }
167
+
168
+ const cargoVersion = cargoMatch[0].match(/"([^"]*)"/)[1];
169
+ if (cargoVersion !== version) {
170
+ if (checkOnly) {
171
+ console.error(`MISMATCH: ${cargoTomlPath} version is "${cargoVersion}", expected "${version}"`);
172
+ process.exitCode = 1;
39
173
  } else {
40
- console.log(` cli/Cargo.toml already up to date`);
174
+ cargoToml = cargoToml.replace(cargoVersionRegex, `version = "${version}"`);
175
+ writeFileSync(cargoTomlPath, cargoToml);
176
+ console.log(` Updated ${cargoTomlPath}: ${cargoVersion} -> ${version}`);
41
177
  }
42
178
  } else {
43
- console.error(" Could not find version field in browser4-cli/Cargo.toml");
44
- process.exit(1);
179
+ if (!checkOnly) console.log(` ${cargoTomlPath} already up to date`);
45
180
  }
46
181
 
47
- // Update Cargo.lock to match Cargo.toml
48
- if (cargoTomlUpdated) {
182
+ // 4. Update Cargo.lock (only in sync mode)
183
+ if (!checkOnly && cargoVersion !== version) {
184
+ let lockUpdated = false;
49
185
  try {
50
186
  execSync("cargo update -p browser4-cli --offline", {
51
- cwd: cliDir,
187
+ cwd: cargoDir,
52
188
  stdio: "pipe",
53
189
  });
54
- console.log(` Updated cli/Cargo.lock`);
190
+ console.log(" Updated Cargo.lock");
191
+ lockUpdated = true;
55
192
  } catch {
56
- // --offline may fail if package not in cache, try without it
57
193
  try {
58
194
  execSync("cargo update -p browser4-cli", {
59
- cwd: cliDir,
195
+ cwd: cargoDir,
60
196
  stdio: "pipe",
61
197
  });
62
- console.log(` Updated cli/Cargo.lock`);
198
+ console.log(" Updated Cargo.lock");
199
+ lockUpdated = true;
63
200
  } catch (e) {
64
- console.error(` Warning: Could not update Cargo.lock: ${e.message}`);
201
+ console.error(` Warning: Could not update Cargo.lock via cargo: ${e.message}`);
202
+ }
203
+ }
204
+
205
+ // Fallback: edit Cargo.lock directly when cargo update fails (e.g. due to
206
+ // lock file version mismatch between the installed cargo and the lock file).
207
+ if (!lockUpdated) {
208
+ const cargoLockPath = join(cargoDir, "Cargo.lock");
209
+ try {
210
+ let lockContent = readFileSync(cargoLockPath, "utf-8");
211
+ // Match the [[package]] block for browser4-cli and replace its version.
212
+ // Works for both v3 and v4 lock file formats.
213
+ const lockPkgRegex = new RegExp(
214
+ `(\\[\\[package\\]\\][\\r\\n]+\\s*name\\s*=\\s*"browser4-cli"[\\r\\n]+\\s*version\\s*=\\s*)"[^"]*"`,
215
+ "m"
216
+ );
217
+ if (lockPkgRegex.test(lockContent)) {
218
+ lockContent = lockContent.replace(lockPkgRegex, `$1"${version}"`);
219
+ writeFileSync(cargoLockPath, lockContent);
220
+ console.log(` Updated Cargo.lock directly: ${cargoVersion} -> ${version}`);
221
+ lockUpdated = true;
222
+ } else {
223
+ console.error(" Warning: Could not find browser4-cli entry in Cargo.lock");
224
+ }
225
+ } catch (e2) {
226
+ console.error(` Warning: Could not update Cargo.lock directly: ${e2.message}`);
65
227
  }
66
228
  }
67
229
  }
68
230
 
69
- console.log("Version sync complete.");
231
+ // 5. Report
232
+ if (checkOnly) {
233
+ if (process.exitCode === 1) {
234
+ console.error("\nVersion mismatch detected! Run 'node cli/scripts/sync-version.js' to fix.");
235
+ } else {
236
+ console.log(`\nAll versions in sync: ${version}`);
237
+ }
238
+ } else {
239
+ console.log(`\nVersion sync complete: ${pkgName}@${version}`);
240
+ }