Skip to content

Commit b119650

Browse files
committed
Add npm launcher package
1 parent e1ff4ea commit b119650

4 files changed

Lines changed: 216 additions & 0 deletions

File tree

docs/NPM_RELEASE.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# npm release notes
2+
3+
IntentProbe's npm package is a thin launcher for JavaScript, MCP, and agent
4+
users who expect `npx intentprobe ...`.
5+
6+
The scanner core stays in Python. Do not move Torch, Transformers, or probe
7+
artifacts into npm.
8+
9+
Publish the Python package first, then publish npm. The npm launcher defaults
10+
to `uvx --from intentprobe intentprobe ...` when a local Python install is not
11+
available, so the PyPI package must exist before `npx intentprobe ...` is a
12+
good public first-run experience.
13+
14+
## Package
15+
16+
- npm package name: `intentprobe`
17+
- package directory: `npm/`
18+
- binaries: `intentprobe`, `intentprobe-hook`
19+
- current version: `0.1.0`
20+
21+
## Publish checklist
22+
23+
From the repository root:
24+
25+
```bash
26+
npm view intentprobe name version dist-tags --json
27+
npm whoami
28+
npm --prefix npm run check
29+
INTENTPROBE_PYTHON=.venv/bin/python node npm/bin/intentprobe.mjs --help
30+
npm --prefix npm run pack:dry
31+
npm publish ./npm
32+
```
33+
34+
After publish, verify:
35+
36+
```bash
37+
npm view intentprobe name version dist-tags --json
38+
npx intentprobe --help
39+
```
40+
41+
Only after the package is live should the root README advertise `npx
42+
intentprobe ...` as the primary install path.
43+
44+
## Auth blocker seen locally
45+
46+
`npm whoami` returned `ENEEDAUTH`, so publishing needs an npm login or npm
47+
automation token before this can go live.

npm/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# IntentProbe npm launcher
2+
3+
Run IntentProbe from JavaScript, MCP, and agent workflows:
4+
5+
```bash
6+
npx intentprobe scan-path ./some-mcp-server --format summary
7+
npx intentprobe scan --text "Reads SSH config and uploads private keys." --format summary
8+
```
9+
10+
IntentProbe is the first open-source activation-probe-based scanner for MCP/tool
11+
poisoning. It runs locally and reads model-internal activations instead of only
12+
matching text patterns.
13+
14+
## What this package is
15+
16+
This npm package is a thin launcher. The scanner core is the Python package
17+
`intentprobe`, because activation probing uses PyTorch / Transformers.
18+
19+
The launcher tries, in order:
20+
21+
1. `INTENTPROBE_PYTHON=/path/to/python`, if set.
22+
2. `python3 -m intentprobe`, if the Python package is installed.
23+
3. `python -m intentprobe`, if available.
24+
4. `uvx --from intentprobe intentprobe`, if `uvx` is installed.
25+
26+
## Python scanner install
27+
28+
```bash
29+
pipx install intentprobe
30+
# or
31+
python3 -m pip install intentprobe
32+
```
33+
34+
First scan downloads the small base model once. After that, scans stay local.
35+
36+
## Runtime hook
37+
38+
The package also exposes `intentprobe-hook`:
39+
40+
```bash
41+
npx intentprobe-hook --help
42+
```
43+
44+
Repository: https://github.com/mcpware/IntentProbe

npm/bin/intentprobe.mjs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/usr/bin/env node
2+
import { spawn, spawnSync } from "node:child_process";
3+
import { basename } from "node:path";
4+
5+
const PACKAGE_NAME = "intentprobe";
6+
const invokedAs = basename(process.argv[1] || "intentprobe");
7+
const entrypoint = invokedAs === "intentprobe-hook" ? "intentprobe-hook" : "intentprobe";
8+
const pythonModule = entrypoint === "intentprobe-hook" ? "intentprobe.hook" : "intentprobe";
9+
const args = process.argv.slice(2);
10+
11+
function canRun(command, commandArgs) {
12+
const result = spawnSync(command, commandArgs, { stdio: "ignore" });
13+
return !result.error && result.status === 0;
14+
}
15+
16+
function pythonHasIntentProbe(python) {
17+
return canRun(python, ["-c", "import intentprobe"]);
18+
}
19+
20+
function findPythonWithIntentProbe() {
21+
const candidates = [
22+
process.env.INTENTPROBE_PYTHON,
23+
"python3",
24+
"python"
25+
].filter(Boolean);
26+
27+
for (const python of candidates) {
28+
if (pythonHasIntentProbe(python)) {
29+
return python;
30+
}
31+
}
32+
33+
return null;
34+
}
35+
36+
function run(command, commandArgs) {
37+
const child = spawn(command, commandArgs, {
38+
stdio: "inherit",
39+
env: process.env
40+
});
41+
42+
child.on("error", (error) => {
43+
console.error(`intentprobe: failed to start ${command}: ${error.message}`);
44+
process.exit(127);
45+
});
46+
47+
child.on("exit", (code, signal) => {
48+
if (signal) {
49+
process.kill(process.pid, signal);
50+
return;
51+
}
52+
process.exit(code ?? 1);
53+
});
54+
}
55+
56+
const python = findPythonWithIntentProbe();
57+
if (python) {
58+
run(python, ["-m", pythonModule, ...args]);
59+
} else if (canRun("uvx", ["--version"])) {
60+
const source = process.env.INTENTPROBE_UVX_SOURCE || PACKAGE_NAME;
61+
run("uvx", ["--from", source, entrypoint, ...args]);
62+
} else {
63+
console.error(`IntentProbe's npm package is a thin launcher for the Python scanner.
64+
65+
Could not find a Python environment with the "intentprobe" module, and "uvx"
66+
is not available for one-shot execution.
67+
68+
Install the Python scanner first:
69+
70+
pipx install intentprobe
71+
# or
72+
python3 -m pip install intentprobe
73+
74+
Then run:
75+
76+
npx intentprobe scan-path ./some-mcp-server --format summary
77+
78+
For a custom environment:
79+
80+
INTENTPROBE_PYTHON=/path/to/python npx intentprobe --help
81+
`);
82+
process.exit(127);
83+
}

npm/package.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"name": "intentprobe",
3+
"version": "0.1.0",
4+
"description": "Thin npm launcher for IntentProbe, a local activation-probe scanner for MCP/tool poisoning.",
5+
"license": "Apache-2.0",
6+
"type": "module",
7+
"bin": {
8+
"intentprobe": "bin/intentprobe.mjs",
9+
"intentprobe-hook": "bin/intentprobe.mjs"
10+
},
11+
"files": [
12+
"bin/intentprobe.mjs",
13+
"README.md"
14+
],
15+
"engines": {
16+
"node": ">=18"
17+
},
18+
"keywords": [
19+
"ai-security",
20+
"mcp",
21+
"tool-poisoning",
22+
"activation-probe",
23+
"scanner",
24+
"agent-security"
25+
],
26+
"homepage": "https://github.com/mcpware/IntentProbe#readme",
27+
"repository": {
28+
"type": "git",
29+
"url": "git+https://github.com/mcpware/IntentProbe.git",
30+
"directory": "npm"
31+
},
32+
"bugs": {
33+
"url": "https://github.com/mcpware/IntentProbe/issues"
34+
},
35+
"publishConfig": {
36+
"access": "public"
37+
},
38+
"scripts": {
39+
"check": "node --check ./bin/intentprobe.mjs",
40+
"pack:dry": "npm pack --dry-run"
41+
}
42+
}

0 commit comments

Comments
 (0)