codex-plugin-doctor 0.1.3 → 0.1.4
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 +19 -0
- package/dist/core/discover-installed-plugins.d.ts +12 -0
- package/dist/core/discover-installed-plugins.js +69 -0
- package/dist/run-cli.js +78 -9
- package/dist/version.d.ts +1 -0
- package/dist/version.js +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -71,11 +71,20 @@ Global install from npm:
|
|
|
71
71
|
|
|
72
72
|
```bash
|
|
73
73
|
npm install -g codex-plugin-doctor
|
|
74
|
+
codex-plugin-doctor --version
|
|
74
75
|
codex-plugin-doctor check path/to/plugin-package
|
|
75
76
|
```
|
|
76
77
|
|
|
77
78
|
Run `codex-plugin-doctor check .` from the root of a Codex plugin package that contains `.codex-plugin/plugin.json`. The Codex Plugin Doctor source repository is not itself a plugin package.
|
|
78
79
|
|
|
80
|
+
If you already have Codex installed locally and do not know plugin paths, discover the installed plugin cache:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
codex-plugin-doctor list --installed
|
|
84
|
+
codex-plugin-doctor check --installed
|
|
85
|
+
codex-plugin-doctor check --installed github
|
|
86
|
+
```
|
|
87
|
+
|
|
79
88
|
Run from source:
|
|
80
89
|
|
|
81
90
|
```bash
|
|
@@ -144,6 +153,7 @@ x plugin.security.hard_coded_secret
|
|
|
144
153
|
Run these from a Codex plugin package root:
|
|
145
154
|
|
|
146
155
|
```bash
|
|
156
|
+
codex-plugin-doctor --version
|
|
147
157
|
codex-plugin-doctor check .
|
|
148
158
|
codex-plugin-doctor check . --json
|
|
149
159
|
codex-plugin-doctor check . --json --output report.json
|
|
@@ -154,6 +164,15 @@ codex-plugin-doctor check . --runtime
|
|
|
154
164
|
codex-plugin-doctor check . --json --runtime --verbose-runtime
|
|
155
165
|
```
|
|
156
166
|
|
|
167
|
+
Run these when you want Codex Plugin Doctor to find plugins from the local Codex installation:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
codex-plugin-doctor list --installed
|
|
171
|
+
codex-plugin-doctor check --installed
|
|
172
|
+
codex-plugin-doctor check --installed github
|
|
173
|
+
codex-plugin-doctor check --installed github --runtime --no-animations
|
|
174
|
+
```
|
|
175
|
+
|
|
157
176
|
To self-test this repository after cloning it:
|
|
158
177
|
|
|
159
178
|
```bash
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface InstalledPlugin {
|
|
2
|
+
name: string;
|
|
3
|
+
version?: string;
|
|
4
|
+
rootPath: string;
|
|
5
|
+
manifestPath: string;
|
|
6
|
+
relativePath: string;
|
|
7
|
+
}
|
|
8
|
+
export interface InstalledPluginDiscoveryOptions {
|
|
9
|
+
env?: Record<string, string | undefined>;
|
|
10
|
+
}
|
|
11
|
+
export declare function discoverInstalledPlugins(options?: InstalledPluginDiscoveryOptions): Promise<InstalledPlugin[]>;
|
|
12
|
+
export declare function filterInstalledPlugins(plugins: InstalledPlugin[], query: string | null): InstalledPlugin[];
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
async function directoryExists(targetPath) {
|
|
5
|
+
try {
|
|
6
|
+
const details = await stat(targetPath);
|
|
7
|
+
return details.isDirectory();
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function getCodexHomeCandidates(env) {
|
|
14
|
+
const candidates = env.CODEX_HOME
|
|
15
|
+
? [env.CODEX_HOME]
|
|
16
|
+
: [path.join(os.homedir(), ".codex")];
|
|
17
|
+
return [...new Set(candidates.map((candidate) => path.resolve(candidate)))];
|
|
18
|
+
}
|
|
19
|
+
async function findManifestPaths(rootPath) {
|
|
20
|
+
const entries = await readdir(rootPath, { withFileTypes: true });
|
|
21
|
+
const manifestPaths = [];
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
const entryPath = path.join(rootPath, entry.name);
|
|
24
|
+
if (entry.isDirectory()) {
|
|
25
|
+
if (entry.name === ".codex-plugin") {
|
|
26
|
+
manifestPaths.push(path.join(entryPath, "plugin.json"));
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
manifestPaths.push(...(await findManifestPaths(entryPath)));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return manifestPaths;
|
|
33
|
+
}
|
|
34
|
+
export async function discoverInstalledPlugins(options = {}) {
|
|
35
|
+
const env = options.env ?? process.env;
|
|
36
|
+
const plugins = [];
|
|
37
|
+
for (const codexHome of getCodexHomeCandidates(env)) {
|
|
38
|
+
const cacheRoot = path.join(codexHome, "plugins", "cache");
|
|
39
|
+
if (!(await directoryExists(cacheRoot))) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
for (const manifestPath of await findManifestPaths(cacheRoot)) {
|
|
43
|
+
try {
|
|
44
|
+
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
45
|
+
const rootPath = path.dirname(path.dirname(manifestPath));
|
|
46
|
+
const relativePath = path.relative(cacheRoot, rootPath);
|
|
47
|
+
plugins.push({
|
|
48
|
+
name: typeof manifest.name === "string" ? manifest.name : path.basename(rootPath),
|
|
49
|
+
version: typeof manifest.version === "string" ? manifest.version : undefined,
|
|
50
|
+
rootPath,
|
|
51
|
+
manifestPath,
|
|
52
|
+
relativePath
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return plugins.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
61
|
+
}
|
|
62
|
+
export function filterInstalledPlugins(plugins, query) {
|
|
63
|
+
if (!query) {
|
|
64
|
+
return plugins;
|
|
65
|
+
}
|
|
66
|
+
const normalizedQuery = query.toLowerCase();
|
|
67
|
+
return plugins.filter((plugin) => [plugin.name, plugin.relativePath, plugin.rootPath]
|
|
68
|
+
.some((value) => value.toLowerCase().includes(normalizedQuery)));
|
|
69
|
+
}
|
package/dist/run-cli.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { discoverInstalledPlugins, filterInstalledPlugins } from "./core/discover-installed-plugins.js";
|
|
2
3
|
import { runCheck } from "./index.js";
|
|
3
4
|
import { renderJsonReport } from "./reporting/render-json-report.js";
|
|
4
5
|
import { buildMarkdownReport } from "./reporting/render-markdown-report.js";
|
|
@@ -6,6 +7,7 @@ import { renderTextReport } from "./reporting/render-text-report.js";
|
|
|
6
7
|
import { createLiveStatusRenderer } from "./terminal/live-status-renderer.js";
|
|
7
8
|
import { determineOutputPolicy } from "./terminal/output-policy.js";
|
|
8
9
|
import { getSpinner } from "./terminal/spinner-registry.js";
|
|
10
|
+
import { packageVersion } from "./version.js";
|
|
9
11
|
const defaultIo = {
|
|
10
12
|
writeStdout(message) {
|
|
11
13
|
process.stdout.write(`${message}\n`);
|
|
@@ -15,18 +17,60 @@ const defaultIo = {
|
|
|
15
17
|
}
|
|
16
18
|
};
|
|
17
19
|
function printUsage(io) {
|
|
18
|
-
io.writeStderr("Usage: codex-plugin-doctor check <path> [--json|--markdown] [--output <path>] [--runtime] [--verbose-runtime] [--no-animations] [--ascii]");
|
|
20
|
+
io.writeStderr("Usage: codex-plugin-doctor check <path|--installed> [filter] [--json|--markdown] [--output <path>] [--runtime] [--verbose-runtime] [--no-animations] [--ascii]\n codex-plugin-doctor list --installed\n codex-plugin-doctor --version");
|
|
21
|
+
}
|
|
22
|
+
function renderInstalledPlugins(plugins) {
|
|
23
|
+
const lines = [
|
|
24
|
+
"Installed Codex Plugins",
|
|
25
|
+
"======================="
|
|
26
|
+
];
|
|
27
|
+
if (plugins.length === 0) {
|
|
28
|
+
lines.push("", "No installed Codex plugins found.");
|
|
29
|
+
return lines.join("\n");
|
|
30
|
+
}
|
|
31
|
+
for (const plugin of plugins) {
|
|
32
|
+
const version = plugin.version ? `@${plugin.version}` : "";
|
|
33
|
+
lines.push("", `- ${plugin.name}${version}`);
|
|
34
|
+
lines.push(` Path: ${plugin.rootPath}`);
|
|
35
|
+
lines.push(` Cache: ${plugin.relativePath}`);
|
|
36
|
+
}
|
|
37
|
+
return lines.join("\n");
|
|
19
38
|
}
|
|
20
39
|
export async function runCli(args, io = defaultIo, options = {}) {
|
|
21
40
|
const [command, maybePath, ...remainingArgs] = args;
|
|
41
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
42
|
+
io.writeStdout(packageVersion);
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
const terminalContext = options.terminalContext ?? {
|
|
46
|
+
stdoutIsTTY: Boolean(process.stdout.isTTY),
|
|
47
|
+
stderrIsTTY: Boolean(process.stderr.isTTY),
|
|
48
|
+
env: process.env
|
|
49
|
+
};
|
|
50
|
+
if (command === "list" && maybePath === "--installed") {
|
|
51
|
+
const installedPlugins = await discoverInstalledPlugins({
|
|
52
|
+
env: terminalContext.env
|
|
53
|
+
});
|
|
54
|
+
io.writeStdout(renderInstalledPlugins(installedPlugins));
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
22
57
|
if (command !== "check") {
|
|
23
58
|
printUsage(io);
|
|
24
59
|
return 2;
|
|
25
60
|
}
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
? [
|
|
61
|
+
const checkInstalled = maybePath === "--installed";
|
|
62
|
+
const installedFilter = checkInstalled && remainingArgs[0] && !remainingArgs[0].startsWith("--")
|
|
63
|
+
? remainingArgs[0]
|
|
64
|
+
: null;
|
|
65
|
+
const flagsAfterInstalledFilter = checkInstalled && installedFilter
|
|
66
|
+
? remainingArgs.slice(1)
|
|
29
67
|
: remainingArgs;
|
|
68
|
+
const targetPath = maybePath && !maybePath.startsWith("--") ? maybePath : ".";
|
|
69
|
+
const normalizedFlags = checkInstalled
|
|
70
|
+
? [maybePath, ...flagsAfterInstalledFilter]
|
|
71
|
+
: maybePath && maybePath.startsWith("--")
|
|
72
|
+
? [maybePath, ...remainingArgs]
|
|
73
|
+
: remainingArgs;
|
|
30
74
|
const jsonOutput = normalizedFlags.includes("--json");
|
|
31
75
|
const markdownOutput = normalizedFlags.includes("--markdown");
|
|
32
76
|
const runtimeProbeEnabled = normalizedFlags.includes("--runtime");
|
|
@@ -39,11 +83,6 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
39
83
|
io.writeStderr("Missing path after --output.");
|
|
40
84
|
return 2;
|
|
41
85
|
}
|
|
42
|
-
const terminalContext = options.terminalContext ?? {
|
|
43
|
-
stdoutIsTTY: Boolean(process.stdout.isTTY),
|
|
44
|
-
stderrIsTTY: Boolean(process.stderr.isTTY),
|
|
45
|
-
env: process.env
|
|
46
|
-
};
|
|
47
86
|
const outputPolicy = determineOutputPolicy({
|
|
48
87
|
jsonOutput,
|
|
49
88
|
markdownOutput,
|
|
@@ -55,6 +94,36 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
55
94
|
env: terminalContext.env
|
|
56
95
|
});
|
|
57
96
|
const runCheckImpl = options.runCheckImpl ?? runCheck;
|
|
97
|
+
if (checkInstalled) {
|
|
98
|
+
const installedPlugins = filterInstalledPlugins(await discoverInstalledPlugins({ env: terminalContext.env }), installedFilter);
|
|
99
|
+
if (installedPlugins.length === 0) {
|
|
100
|
+
io.writeStderr(installedFilter
|
|
101
|
+
? `No installed Codex plugins matched '${installedFilter}'.`
|
|
102
|
+
: "No installed Codex plugins found.");
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
const results = [];
|
|
106
|
+
for (const plugin of installedPlugins) {
|
|
107
|
+
results.push(await runCheckImpl(plugin.rootPath, {
|
|
108
|
+
runtime: runtimeProbeEnabled,
|
|
109
|
+
runtimeTranscript: runtimeProbeEnabled && verboseRuntime
|
|
110
|
+
? (line) => io.writeStderr(line)
|
|
111
|
+
: undefined
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
const report = results
|
|
115
|
+
.map((result) => markdownOutput
|
|
116
|
+
? buildMarkdownReport(result, { runtimeProbeEnabled })
|
|
117
|
+
: jsonOutput
|
|
118
|
+
? renderJsonReport(result, { runtimeProbeEnabled })
|
|
119
|
+
: renderTextReport(result, { ascii: outputPolicy.style === "ascii" }))
|
|
120
|
+
.join("\n\n");
|
|
121
|
+
if (outputPath) {
|
|
122
|
+
await writeFile(outputPath, report, "utf8");
|
|
123
|
+
}
|
|
124
|
+
io.writeStdout(report);
|
|
125
|
+
return results.some((result) => result.exitCode === 1) ? 1 : 0;
|
|
126
|
+
}
|
|
58
127
|
const renderer = outputPolicy.interactive
|
|
59
128
|
&& !verboseRuntime
|
|
60
129
|
? createLiveStatusRenderer(io, getSpinner(outputPolicy.style === "ascii" ? "ascii" : "doctor"))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const packageVersion: string;
|
package/dist/version.js
ADDED
package/package.json
CHANGED