riciplay 1.9.0 → 1.9.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
@@ -29,6 +29,18 @@ riciplay chat # start an agent session
29
29
 
30
30
  ## The npm wrapper
31
31
 
32
- `bin/riciplay` prefers the pip-installed console script on PATH and falls back to invoking `riciplay_cli.main:app` through `python3`. `scripts/postinstall.js` pip-installs `riciplay-cli` if the module isn't importable — and degrades to a printed manual instruction when it can't (no network, `--ignore-scripts`, managed Python).
32
+ `bin/riciplay` resolves the pip-installed console script via `PATH` and falls back to invoking `riciplay_cli.main:app` through `python3`. It explicitly excludes its own path as a candidate, so an npm global install (which symlinks this wrapper onto `PATH`) can never re-exec itself.
33
+
34
+ `scripts/postinstall.js` pip-installs `riciplay-cli` **pinned to this launcher's exact version** (`riciplay-cli==<package.json version>`), and compares the *installed* version rather than merely checking that the module imports — so an out-of-date install is upgraded instead of being silently kept. It degrades to a printed manual command when it can't (no network, `--ignore-scripts`, managed Python).
35
+
36
+ ### The two versions are locked together
37
+
38
+ The launcher and the Python package must be released at the same version, because postinstall pins the pair. That used to be an unenforced convention and it drifted (npm `1.8.95` against PyPI `1.8.96`). Three things now enforce it:
39
+
40
+ - `npm test` / `npm publish` run `scripts/check-version-sync.js` — `prepublishOnly` **refuses to publish** a mismatched pair
41
+ - `pytest tests/test_version_sync.py` fails the suite on drift, catching it at PR time
42
+ - `python scripts/bump_version.py <version>` moves both files together, so the correct action is also the easy one (`--check` to verify, `--show` to inspect)
43
+
44
+ Release order: **PyPI first, then npm** — the pinned postinstall needs the matching version to already exist on PyPI.
33
45
 
34
46
  Repository: [Zaidux/Riciplay](https://github.com/Zaidux/Riciplay)
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "riciplay",
3
- "version": "1.9.0",
3
+ "version": "1.9.1",
4
4
  "description": "AI-powered bug bounty reconnaissance and penetration testing agent \u2014 browser, proxy interception, recon, and exploit workflows",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "riciplay": "./bin/riciplay"
8
8
  },
9
9
  "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1",
11
- "prepublishOnly": "echo \"Ensure riciplay-cli is published to PyPI first\"",
10
+ "test": "node scripts/check-version-sync.js",
11
+ "prepublishOnly": "node scripts/check-version-sync.js || (echo 'Refusing to publish: npm and PyPI versions disagree.' && exit 1)",
12
12
  "postinstall": "node scripts/postinstall.js"
13
13
  },
14
14
  "repository": {
@@ -1,40 +1,101 @@
1
1
  #!/usr/bin/env node
2
- // Riciplay npm postinstall — installs the Python package from PyPI.
2
+ // Riciplay npm postinstall — installs the matching Python package from PyPI.
3
3
  //
4
- // This npm package is a launcher; the implementation is the Python
5
- // package `riciplay-cli` (PyPI). If it is not importable, pip install
6
- // it. Failures are warnings, not errors: the user may be installing
7
- // with --ignore-scripts, without network, or managing Python manually —
8
- // print the manual command instead of failing the install.
4
+ // This npm package is a launcher; the implementation is the Python package
5
+ // `riciplay-cli` (PyPI). The two versions are meant to be identical, and this
6
+ // script enforces that:
7
+ //
8
+ // * it installs the EXACT version matching this npm release
9
+ // (`riciplay-cli==<package.json version>`), never just "latest", so a
10
+ // launcher can never end up driving a different implementation than the
11
+ // one it was released with;
12
+ // * it compares the INSTALLED version, not merely whether the module
13
+ // imports. The previous check was `python -c "import riciplay_cli"`, which
14
+ // passed for any old version already present — so a fresh npm install
15
+ // silently kept a stale CLI and reported "already importable".
16
+ //
17
+ // Failures are warnings, not errors: the user may be installing with
18
+ // --ignore-scripts, without network, or managing Python manually — print the
19
+ // manual command instead of failing the install.
9
20
 
10
21
  const { execSync, spawnSync } = require('child_process');
11
22
  const os = require('os');
23
+ const path = require('path');
12
24
 
13
- console.log('\n🕷️ Installing Riciplay (bug bounty AI agent)...\n');
25
+ const npmVersion = require(path.join(__dirname, '..', 'package.json')).version;
14
26
 
15
27
  const isWin = os.platform() === 'win32';
16
28
  const python = isWin ? 'python' : 'python3';
17
29
  const pip = isWin ? 'pip' : 'pip3';
18
30
 
19
- function probe(module) {
20
- const r = spawnSync(python, ['-c', `import ${module}`], { stdio: 'ignore' });
21
- return r.status === 0;
31
+ /**
32
+ * The version of `riciplay_cli` importable right now, or null if it is not
33
+ * installed / not importable.
34
+ *
35
+ * Read via `riciplay_cli.__version__`, which falls back to parsing
36
+ * pyproject.toml when dist metadata is unavailable, so this works for both a
37
+ * pip install and a source checkout.
38
+ */
39
+ function installedVersion(py = python, run = spawnSync) {
40
+ const r = run(py, ['-c', 'import riciplay_cli; print(riciplay_cli.__version__)'], {
41
+ encoding: 'utf8',
42
+ });
43
+ if (r.status !== 0 || !r.stdout) return null;
44
+ const v = String(r.stdout).trim();
45
+ return v && v !== '0.0.0' ? v : null;
46
+ }
47
+
48
+ /** True when the installed CLI is not the version this launcher ships with. */
49
+ function needsInstall(installed, target = npmVersion) {
50
+ return installed !== target;
51
+ }
52
+
53
+ /** The exact pip requirement pinning the Python package to this npm release. */
54
+ function pipSpec(target = npmVersion) {
55
+ return `riciplay-cli==${target}`;
22
56
  }
23
57
 
24
- try {
25
- if (probe('riciplay_cli')) {
26
- console.log('✓ Python package `riciplay_cli` already importable.');
58
+ function main() {
59
+ console.log('\n🕷️ Installing Riciplay (bug bounty AI agent)...\n');
60
+
61
+ let current = null;
62
+ try {
63
+ current = installedVersion();
64
+ } catch {
65
+ current = null; // treated as "not installed" below
66
+ }
67
+
68
+ if (!needsInstall(current)) {
69
+ console.log(`✓ riciplay-cli ${npmVersion} already installed.`);
27
70
  } else {
28
- console.log('Installing riciplay-cli from PyPI (this also pulls Chrome/mitmproxy deps)...');
29
- execSync(`${pip} install --upgrade riciplay-cli`, { stdio: 'inherit' });
30
- console.log('✓ Installed riciplay-cli.');
71
+ if (current) {
72
+ console.log(
73
+ `Updating riciplay-cli ${current} → ${npmVersion} ` +
74
+ '(the launcher and the Python package are version-locked)...'
75
+ );
76
+ } else {
77
+ console.log(
78
+ `Installing riciplay-cli ${npmVersion} from PyPI ` +
79
+ '(this also pulls Chrome/mitmproxy deps)...'
80
+ );
81
+ }
82
+ try {
83
+ execSync(`${pip} install --upgrade ${pipSpec()}`, { stdio: 'inherit' });
84
+ console.log(`✓ Installed riciplay-cli ${npmVersion}.`);
85
+ } catch (err) {
86
+ console.warn(
87
+ `\n⚠ Could not install the Python package automatically (${err.message}).\n` +
88
+ ' Riciplay needs Python 3.10+. Install the matching version manually with:\n' +
89
+ ` pip install ${pipSpec()}\n`
90
+ );
91
+ }
31
92
  }
32
- } catch (err) {
33
- console.warn(
34
- `\n⚠ Could not install the Python package automatically (${err.message}).\n` +
35
- ' Riciplay needs Python 3.10+. Install manually with:\n' +
36
- ' pip install riciplay-cli\n'
37
- );
93
+
94
+ console.log('\nUsage: riciplay chat # start an agent session\n');
95
+ }
96
+
97
+ if (require.main === module) {
98
+ main();
38
99
  }
39
100
 
40
- console.log('\nUsage: riciplay chat # start an agent session\n');
101
+ module.exports = { npmVersion, installedVersion, needsInstall, pipSpec, main };