browser4-cli 0.1.6 → 0.1.7

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
@@ -1,4 +1,4 @@
1
- # Browser4 CLI
1
+ # Browser4
2
2
 
3
3
  Make websites accessible for AI agents. Automate tasks online with ease.
4
4
 
@@ -53,16 +53,6 @@ pnpm link --global # Makes browser4-cli available globally
53
53
  - **Java 17+** - Required to run the Browser4 backend (`Browser4.jar`).
54
54
  - **Rust** - Only needed when building from source (see From Source above).
55
55
 
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
56
  ## Usage
67
57
 
68
58
  ```
@@ -365,6 +355,41 @@ cargo test --test e2e -- --nocapture
365
355
  cargo test --test e2e -- --nocapture --scenario=test_e2e_batch_form_submission
366
356
  ```
367
357
 
358
+ ## Publishing the CLI package
359
+
360
+ For maintainers, the CLI package now uses an npm version guard before publish.
361
+
362
+ The GitHub release workflow publishes the npm package via npm trusted publishing
363
+ (GitHub Actions OIDC) instead of `NODE_AUTH_TOKEN`. This avoids CI failures caused
364
+ by npm one-time-password challenges (`EOTP`).
365
+
366
+ - Local release entrypoint: `npm run release`
367
+ - Direct guarded publish entrypoint: `npm run publish:if-needed`
368
+ - GitHub release workflow: re-checks npm immediately before the publish step
369
+
370
+ If the local version in `cli/package.json` already matches the version currently
371
+ published on npm, the publish step is skipped automatically.
372
+
373
+ Examples:
374
+
375
+ ```bash
376
+ # Check whether npm publish is needed
377
+ node scripts/check-npm-publish-needed.js --json
378
+
379
+ # Publish only when the local version differs from npm
380
+ npm run publish:if-needed
381
+
382
+ # Standard maintainer release command (also guarded)
383
+ npm run release
384
+ ```
385
+
386
+ For local testing, you can override the detected remote version:
387
+
388
+ ```bash
389
+ BROWSER4_CLI_NPM_REMOTE_VERSION=0.1.7 node scripts/check-npm-publish-needed.js --json
390
+ BROWSER4_CLI_NPM_REMOTE_VERSION=0.1.7 node scripts/publish-if-needed.js --dry-run
391
+ ```
392
+
368
393
  ## License
369
394
 
370
395
  Apache-2.0
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.6",
3
+ "version": "0.1.7",
4
4
  "description": "Browser automation CLI for AI agents",
5
5
  "type": "module",
6
6
  "files": [
@@ -22,7 +22,8 @@
22
22
  "build:windows": "npm run version:sync && docker compose -f docker/docker-compose.yml run --rm build-windows",
23
23
  "build:all-platforms": "npm run version:sync && (npm run build:linux & npm run build:windows & wait)",
24
24
  "build:docker": "docker build -t browser4-builder -f docker/Dockerfile.build .",
25
- "release": "npm run version:sync && npm run build:all-platforms && npm publish",
25
+ "publish:if-needed": "node scripts/publish-if-needed.js",
26
+ "release": "npm run publish:if-needed",
26
27
  "postinstall": "node scripts/postinstall.js"
27
28
  },
28
29
  "keywords": [
@@ -45,8 +46,5 @@
45
46
  "url": "https://github.com/platonai/Browser4/issues"
46
47
  },
47
48
  "homepage": "https://browser4.io",
48
- "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be",
49
- "dependencies": {
50
- "browser4-cli": "^0.1.5"
51
- }
49
+ "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be"
52
50
  }
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { appendFileSync } from 'fs';
4
+ import { getPublishDecision, logPublishDecision } from './npm-publish-check.js';
5
+
6
+ const args = new Set(process.argv.slice(2));
7
+ const shouldWriteGithubOutput = args.has('--github-output');
8
+ const shouldPrintJson = args.has('--json');
9
+ const shouldPrintJsonOnly = args.has('--json-only');
10
+ const shouldPrintShell = args.has('--shell');
11
+ const decision = getPublishDecision();
12
+
13
+ if (!shouldPrintJsonOnly && !shouldPrintShell) {
14
+ logPublishDecision('check', decision);
15
+ }
16
+
17
+ if (shouldPrintJson) {
18
+ console.log(JSON.stringify(decision));
19
+ }
20
+
21
+ if (shouldPrintJsonOnly) {
22
+ console.log(JSON.stringify(decision));
23
+ }
24
+
25
+ if (shouldPrintShell) {
26
+ console.log(`PACKAGE_NAME='${decision.packageName}'`);
27
+ console.log(`CLI_VERSION='${decision.cliVersion}'`);
28
+ console.log(`REMOTE_VERSION='${decision.remoteVersion}'`);
29
+ console.log(`LOOKUP_STATUS='${decision.lookupStatus}'`);
30
+ console.log(`SHOULD_PUBLISH='${decision.shouldPublish ? 'true' : 'false'}'`);
31
+ }
32
+
33
+ if (shouldWriteGithubOutput) {
34
+ const githubOutputPath = process.env.GITHUB_OUTPUT;
35
+ if (!githubOutputPath) {
36
+ console.error('GITHUB_OUTPUT is required when using --github-output');
37
+ process.exit(1);
38
+ }
39
+
40
+ appendFileSync(
41
+ githubOutputPath,
42
+ [
43
+ `package_name=${decision.packageName}`,
44
+ `cli_version=${decision.cliVersion}`,
45
+ `remote_version=${decision.remoteVersion}`,
46
+ `lookup_status=${decision.lookupStatus}`,
47
+ `should_publish=${decision.shouldPublish ? 'true' : 'false'}`,
48
+ '',
49
+ ].join('\n')
50
+ );
51
+ }
52
+
53
+ if (!decision.shouldPublish && !shouldPrintJsonOnly && !shouldPrintShell) {
54
+ console.log(`Skipping publish because ${decision.packageName}@${decision.cliVersion} already exists on npm.`);
55
+ }
56
+
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync } from 'child_process';
4
+ import { readFileSync } from 'fs';
5
+ import { dirname, join } from 'path';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ export const cliRootDir = join(__dirname, '..');
10
+ const packageJsonPath = join(cliRootDir, 'package.json');
11
+
12
+ /**
13
+ * Reads browser4-cli package metadata.
14
+ *
15
+ * @returns {{name: string, version: string}}
16
+ */
17
+ export function readPackageMetadata() {
18
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
19
+ return {
20
+ name: packageJson.name,
21
+ version: packageJson.version,
22
+ };
23
+ }
24
+
25
+ /**
26
+ * Queries npm for the published version of the package.
27
+ *
28
+ * @param {string} packageName Package name to inspect.
29
+ * @returns {{status: string, version: string}}
30
+ */
31
+ export function getRemoteVersion(packageName) {
32
+ const overriddenVersion = process.env.BROWSER4_CLI_NPM_REMOTE_VERSION;
33
+ if (overriddenVersion) {
34
+ console.log(`Using overridden npm version from BROWSER4_CLI_NPM_REMOTE_VERSION=${overriddenVersion}`);
35
+ return {
36
+ status: 'overridden',
37
+ version: overriddenVersion,
38
+ };
39
+ }
40
+
41
+ try {
42
+ const version = execSync(`npm view "${packageName}" version`, {
43
+ cwd: cliRootDir,
44
+ stdio: ['ignore', 'pipe', 'pipe'],
45
+ encoding: 'utf-8',
46
+ }).trim();
47
+
48
+ return {
49
+ status: 'success',
50
+ version,
51
+ };
52
+ } catch (error) {
53
+ const stderr = error.stderr?.toString().trim();
54
+ const stdout = error.stdout?.toString().trim();
55
+ const message = stderr || stdout || error.message;
56
+
57
+ console.warn(`Warning: unable to query npm version for ${packageName}: ${message}`);
58
+ return {
59
+ status: 'failed',
60
+ version: 'unknown',
61
+ };
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Computes whether npm publish should proceed.
67
+ *
68
+ * @returns {{packageName: string, cliVersion: string, remoteVersion: string, lookupStatus: string, shouldPublish: boolean}}
69
+ */
70
+ export function getPublishDecision() {
71
+ const metadata = readPackageMetadata();
72
+ const remoteInfo = getRemoteVersion(metadata.name);
73
+
74
+ return {
75
+ packageName: metadata.name,
76
+ cliVersion: metadata.version,
77
+ remoteVersion: remoteInfo.version,
78
+ lookupStatus: remoteInfo.status,
79
+ shouldPublish: remoteInfo.version === 'unknown' || metadata.version !== remoteInfo.version,
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Logs a human-readable publish decision.
85
+ *
86
+ * @param {string} phase Current phase label.
87
+ * @param {{packageName: string, cliVersion: string, remoteVersion: string, lookupStatus: string, shouldPublish: boolean}} decision Publish decision.
88
+ */
89
+ export function logPublishDecision(phase, decision) {
90
+ console.log(
91
+ `[${phase}] package=${decision.packageName} local=${decision.cliVersion} remote=${decision.remoteVersion} status=${decision.lookupStatus} shouldPublish=${decision.shouldPublish}`
92
+ );
93
+ }
94
+
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Publishes browser4-cli only when the local package version differs from npm.
5
+ *
6
+ * Usage:
7
+ * node scripts/publish-if-needed.js
8
+ * node scripts/publish-if-needed.js --dry-run
9
+ *
10
+ * Optional env for testing:
11
+ * BROWSER4_CLI_NPM_REMOTE_VERSION=<version>
12
+ */
13
+
14
+ import { execSync } from 'child_process';
15
+ import { cliRootDir, getPublishDecision, logPublishDecision } from './npm-publish-check.js';
16
+
17
+ const args = new Set(process.argv.slice(2));
18
+ const isDryRun = args.has('--dry-run');
19
+
20
+ function runCommand(command) {
21
+ console.log(`> ${command}`);
22
+ execSync(command, {
23
+ cwd: cliRootDir,
24
+ stdio: 'inherit',
25
+ });
26
+ }
27
+
28
+ function main() {
29
+ const initialDecision = getPublishDecision();
30
+ logPublishDecision('pre-check', initialDecision);
31
+
32
+ if (!initialDecision.shouldPublish) {
33
+ console.log(`Skipping publish because ${initialDecision.packageName}@${initialDecision.cliVersion} already exists on npm.`);
34
+ return;
35
+ }
36
+
37
+ if (isDryRun) {
38
+ console.log('Dry run enabled; version differs, so publish would proceed.');
39
+ return;
40
+ }
41
+
42
+ runCommand('npm run version:sync');
43
+
44
+ const postSyncDecision = getPublishDecision();
45
+ logPublishDecision('post-sync-check', postSyncDecision);
46
+
47
+ if (!postSyncDecision.shouldPublish) {
48
+ console.log(`Skipping publish after sync because ${postSyncDecision.packageName}@${postSyncDecision.cliVersion} already exists on npm.`);
49
+ return;
50
+ }
51
+
52
+ runCommand('npm run build:all-platforms');
53
+
54
+ const prePublishDecision = getPublishDecision();
55
+ logPublishDecision('pre-publish-check', prePublishDecision);
56
+
57
+ if (!prePublishDecision.shouldPublish) {
58
+ console.log(`Skipping publish before npm publish because ${prePublishDecision.packageName}@${prePublishDecision.cliVersion} already exists on npm.`);
59
+ return;
60
+ }
61
+
62
+ runCommand('npm publish');
63
+ }
64
+
65
+ main();
66
+