@introspection-ai/cli 0.18.1 → 0.20.0
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 +4 -2
- package/bin/introspection.js +189 -14
- package/package.json +12 -7
- package/scripts/postinstall.js +14 -0
package/README.md
CHANGED
|
@@ -6,7 +6,8 @@ operating agents across local development and the Introspection platform.
|
|
|
6
6
|
```bash
|
|
7
7
|
npm install -g @introspection-ai/cli
|
|
8
8
|
|
|
9
|
-
#
|
|
9
|
+
# install Pi when missing, then align Recipes and coding-agent plugins
|
|
10
|
+
introspection setup
|
|
10
11
|
introspection login
|
|
11
12
|
introspection --help
|
|
12
13
|
```
|
|
@@ -19,6 +20,7 @@ npx @introspection-ai/cli --help
|
|
|
19
20
|
|
|
20
21
|
The platform-specific CLI and its Rust judge engine are delivered via an
|
|
21
22
|
optional dependency (`@introspection-ai/cli-<os>-<cpu>`) selected automatically
|
|
22
|
-
by npm.
|
|
23
|
+
by npm. The CLI requires Node.js 24 or newer. On Node 18–23 the npm launcher
|
|
24
|
+
installs but every command exits with upgrade instructions.
|
|
23
25
|
|
|
24
26
|
Full documentation: https://github.com/introspection-org/introspection-cli
|
package/bin/introspection.js
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
"use strict";
|
|
7
7
|
|
|
8
8
|
const { spawnSync } = require("node:child_process");
|
|
9
|
+
const fs = require("node:fs");
|
|
10
|
+
const path = require("node:path");
|
|
11
|
+
|
|
12
|
+
const MIN_NODE_MAJOR = 24;
|
|
9
13
|
|
|
10
14
|
const PACKAGES = {
|
|
11
15
|
"darwin-arm64": "@introspection-ai/cli-darwin-arm64",
|
|
@@ -15,6 +19,137 @@ const PACKAGES = {
|
|
|
15
19
|
"win32-x64": "@introspection-ai/cli-win32-x64",
|
|
16
20
|
};
|
|
17
21
|
|
|
22
|
+
const PACKAGE_MANAGER_ENV = "INTROSPECTION_CLI_PACKAGE_MANAGER";
|
|
23
|
+
const PACKAGE_MANAGER_PATH_ENV = "INTROSPECTION_CLI_PACKAGE_MANAGER_PATH";
|
|
24
|
+
const LOCKFILES = [
|
|
25
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
26
|
+
["yarn.lock", "yarn"],
|
|
27
|
+
["bun.lock", "bun"],
|
|
28
|
+
["bun.lockb", "bun"],
|
|
29
|
+
["package-lock.json", "npm"],
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Identify the package manager that owns this launcher. This mirrors Vercel's
|
|
34
|
+
* update-command approach: use install metadata while it is available, then
|
|
35
|
+
* walk the installed package tree for a lockfile or a distinctive global-store
|
|
36
|
+
* path. An uncertain answer stays uncertain; the native CLI will show generic
|
|
37
|
+
* reinstall guidance rather than silently falling back to npm.
|
|
38
|
+
*/
|
|
39
|
+
function detectPackageManager({
|
|
40
|
+
entrypoint = __filename,
|
|
41
|
+
existsSync = fs.existsSync,
|
|
42
|
+
realpathSync = fs.realpathSync,
|
|
43
|
+
} = {}) {
|
|
44
|
+
const logical = path.resolve(entrypoint);
|
|
45
|
+
let physical;
|
|
46
|
+
try {
|
|
47
|
+
physical = realpathSync(entrypoint);
|
|
48
|
+
} catch {
|
|
49
|
+
physical = logical;
|
|
50
|
+
}
|
|
51
|
+
const locations = [...new Set([logical, physical])];
|
|
52
|
+
const normalized = locations.map(location =>
|
|
53
|
+
location.replaceAll("\\", "/").toLowerCase()
|
|
54
|
+
);
|
|
55
|
+
const globalLocations = normalized.filter(location =>
|
|
56
|
+
location.includes("/pnpm/global/") ||
|
|
57
|
+
location.includes("/yarn/global/") ||
|
|
58
|
+
location.includes("/.bun/install/global/") ||
|
|
59
|
+
location.includes("/lib/node_modules/") ||
|
|
60
|
+
location.includes("/appdata/roaming/npm/node_modules/")
|
|
61
|
+
);
|
|
62
|
+
if (globalLocations.length === 0) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (globalLocations.some(location =>
|
|
67
|
+
location.includes("/.pnpm/") || location.includes("/pnpm/global/")
|
|
68
|
+
)) {
|
|
69
|
+
return "pnpm";
|
|
70
|
+
}
|
|
71
|
+
if (globalLocations.some(location => location.includes("/yarn/global/"))) {
|
|
72
|
+
return "yarn";
|
|
73
|
+
}
|
|
74
|
+
if (globalLocations.some(location => location.includes("/.bun/install/global/"))) {
|
|
75
|
+
return "bun";
|
|
76
|
+
}
|
|
77
|
+
if (
|
|
78
|
+
globalLocations.some(location =>
|
|
79
|
+
location.includes("/lib/node_modules/") ||
|
|
80
|
+
location.includes("/appdata/roaming/npm/node_modules/")
|
|
81
|
+
)
|
|
82
|
+
) {
|
|
83
|
+
return "npm";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for (const location of locations) {
|
|
87
|
+
for (let dir = path.dirname(location); ; dir = path.dirname(dir)) {
|
|
88
|
+
for (const [lockfile, manager] of LOCKFILES) {
|
|
89
|
+
if (existsSync(path.join(dir, lockfile))) {
|
|
90
|
+
return manager;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const parent = path.dirname(dir);
|
|
94
|
+
if (parent === dir) break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Resolve the package-manager executable from the launcher's own installation
|
|
103
|
+
* prefix. Never consult PATH: it may now point at another NVM/Corepack prefix.
|
|
104
|
+
*/
|
|
105
|
+
function findOwningPackageManager(
|
|
106
|
+
manager,
|
|
107
|
+
{
|
|
108
|
+
entrypoint = __filename,
|
|
109
|
+
invokedAs = process.argv[1],
|
|
110
|
+
platform = process.platform,
|
|
111
|
+
existsSync = fs.existsSync,
|
|
112
|
+
realpathSync = fs.realpathSync,
|
|
113
|
+
} = {}
|
|
114
|
+
) {
|
|
115
|
+
if (!manager) return null;
|
|
116
|
+
const executable = platform === "win32" ? `${manager}.cmd` : manager;
|
|
117
|
+
const candidates = [];
|
|
118
|
+
const invokedSibling = invokedAs
|
|
119
|
+
? path.join(path.dirname(path.resolve(invokedAs)), executable)
|
|
120
|
+
: null;
|
|
121
|
+
|
|
122
|
+
const logical = path.resolve(entrypoint);
|
|
123
|
+
let physical;
|
|
124
|
+
try {
|
|
125
|
+
physical = realpathSync(entrypoint);
|
|
126
|
+
} catch {
|
|
127
|
+
physical = logical;
|
|
128
|
+
}
|
|
129
|
+
for (const location of [...new Set([logical, physical])]) {
|
|
130
|
+
const normalized = location.replaceAll("\\", "/");
|
|
131
|
+
const lower = normalized.toLowerCase();
|
|
132
|
+
const addFromMarker = (marker, suffix) => {
|
|
133
|
+
const offset = lower.indexOf(marker);
|
|
134
|
+
if (offset !== -1) {
|
|
135
|
+
candidates.push(path.join(normalized.slice(0, offset), ...suffix));
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
if (manager === "npm") {
|
|
140
|
+
addFromMarker("/lib/node_modules/", ["bin", executable]);
|
|
141
|
+
addFromMarker("/node_modules/", [executable]);
|
|
142
|
+
} else if (manager === "pnpm") {
|
|
143
|
+
addFromMarker("/global/", [executable]);
|
|
144
|
+
} else if (manager === "bun") {
|
|
145
|
+
addFromMarker("/install/global/", ["bin", executable]);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (invokedSibling) candidates.push(invokedSibling);
|
|
149
|
+
|
|
150
|
+
return candidates.find(candidate => existsSync(candidate)) || null;
|
|
151
|
+
}
|
|
152
|
+
|
|
18
153
|
function resolveBinary() {
|
|
19
154
|
const key = `${process.platform}-${process.arch}`;
|
|
20
155
|
const pkg = PACKAGES[key];
|
|
@@ -36,21 +171,61 @@ function resolveBinary() {
|
|
|
36
171
|
}
|
|
37
172
|
}
|
|
38
173
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
174
|
+
function requireSupportedNode(version = process.versions.node) {
|
|
175
|
+
const major = Number.parseInt(version.split(".")[0], 10);
|
|
176
|
+
if (!Number.isInteger(major) || major < MIN_NODE_MAJOR) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
`Introspection CLI requires Node.js ${MIN_NODE_MAJOR} or newer; ` +
|
|
179
|
+
`this command is running on Node.js ${version}.\n\n` +
|
|
180
|
+
`Upgrade the Node runtime that launches \`introspection\`, then reinstall:\n` +
|
|
181
|
+
` npm install -g @introspection-ai/cli@latest`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
45
184
|
}
|
|
46
185
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
186
|
+
function main() {
|
|
187
|
+
let binary;
|
|
188
|
+
try {
|
|
189
|
+
requireSupportedNode();
|
|
190
|
+
binary = resolveBinary();
|
|
191
|
+
} catch (err) {
|
|
192
|
+
console.error(err.message);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const manager = detectPackageManager();
|
|
197
|
+
const managerPath = findOwningPackageManager(manager);
|
|
198
|
+
const env = manager && managerPath
|
|
199
|
+
? {
|
|
200
|
+
...process.env,
|
|
201
|
+
[PACKAGE_MANAGER_ENV]: manager,
|
|
202
|
+
[PACKAGE_MANAGER_PATH_ENV]: managerPath,
|
|
203
|
+
}
|
|
204
|
+
: process.env;
|
|
205
|
+
const result = spawnSync(binary, process.argv.slice(2), {
|
|
206
|
+
stdio: "inherit",
|
|
207
|
+
env,
|
|
208
|
+
});
|
|
209
|
+
if (result.error) {
|
|
210
|
+
console.error(result.error.message);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
// Re-raise a terminating signal as our own exit; otherwise pass the code.
|
|
214
|
+
if (result.signal) {
|
|
215
|
+
process.kill(process.pid, result.signal);
|
|
216
|
+
}
|
|
217
|
+
process.exit(result.status ?? 0);
|
|
51
218
|
}
|
|
52
|
-
|
|
53
|
-
if (
|
|
54
|
-
|
|
219
|
+
|
|
220
|
+
if (require.main === module) {
|
|
221
|
+
main();
|
|
55
222
|
}
|
|
56
|
-
|
|
223
|
+
|
|
224
|
+
module.exports = {
|
|
225
|
+
MIN_NODE_MAJOR,
|
|
226
|
+
PACKAGE_MANAGER_ENV,
|
|
227
|
+
PACKAGE_MANAGER_PATH_ENV,
|
|
228
|
+
detectPackageManager,
|
|
229
|
+
findOwningPackageManager,
|
|
230
|
+
requireSupportedNode,
|
|
231
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@introspection-ai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "CLI for operating agents on the Introspection platform",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"introspection",
|
|
@@ -18,18 +18,23 @@
|
|
|
18
18
|
"bin": {
|
|
19
19
|
"introspection": "bin/introspection.js"
|
|
20
20
|
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"postinstall": "node scripts/postinstall.js",
|
|
23
|
+
"test": "node --test test/*.test.js"
|
|
24
|
+
},
|
|
21
25
|
"files": [
|
|
22
26
|
"bin/introspection.js",
|
|
27
|
+
"scripts/postinstall.js",
|
|
23
28
|
"README.md"
|
|
24
29
|
],
|
|
25
30
|
"engines": {
|
|
26
|
-
"node": ">=
|
|
31
|
+
"node": ">=18"
|
|
27
32
|
},
|
|
28
33
|
"optionalDependencies": {
|
|
29
|
-
"@introspection-ai/cli-darwin-arm64": "0.
|
|
30
|
-
"@introspection-ai/cli-darwin-x64": "0.
|
|
31
|
-
"@introspection-ai/cli-linux-x64": "0.
|
|
32
|
-
"@introspection-ai/cli-linux-arm64": "0.
|
|
33
|
-
"@introspection-ai/cli-win32-x64": "0.
|
|
34
|
+
"@introspection-ai/cli-darwin-arm64": "0.20.0",
|
|
35
|
+
"@introspection-ai/cli-darwin-x64": "0.20.0",
|
|
36
|
+
"@introspection-ai/cli-linux-x64": "0.20.0",
|
|
37
|
+
"@introspection-ai/cli-linux-arm64": "0.20.0",
|
|
38
|
+
"@introspection-ai/cli-win32-x64": "0.20.0"
|
|
34
39
|
}
|
|
35
40
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// Installation hooks are advisory only: package managers may suppress or skip
|
|
5
|
+
// them, and installing this package does not authorize changing Pi or coding
|
|
6
|
+
// agent hosts. The idempotent setup command owns those machine-level changes.
|
|
7
|
+
// Human-only output follows the CLI's stderr + TTY contract.
|
|
8
|
+
if (process.stderr.isTTY) {
|
|
9
|
+
process.stderr.write(`
|
|
10
|
+
Introspection CLI installed. To install or align Pi, Recipes, and plugins, run:
|
|
11
|
+
|
|
12
|
+
introspection setup
|
|
13
|
+
`);
|
|
14
|
+
}
|