@the-librarian/cli 1.0.0-rc.21
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/LICENSE +201 -0
- package/README.md +94 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +15 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands/install.d.ts +28 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +204 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/shared.d.ts +16 -0
- package/dist/commands/shared.d.ts.map +1 -0
- package/dist/commands/shared.js +30 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/uninstall.d.ts +18 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +67 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.d.ts +21 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +75 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/config.d.ts +40 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +65 -0
- package/dist/config.js.map +1 -0
- package/dist/doctor.d.ts +23 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +81 -0
- package/dist/doctor.js.map +1 -0
- package/dist/env.d.ts +39 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +208 -0
- package/dist/env.js.map +1 -0
- package/dist/exec.d.ts +37 -0
- package/dist/exec.d.ts.map +1 -0
- package/dist/exec.js +81 -0
- package/dist/exec.js.map +1 -0
- package/dist/harnesses/claude.d.ts +3 -0
- package/dist/harnesses/claude.d.ts.map +1 -0
- package/dist/harnesses/claude.js +88 -0
- package/dist/harnesses/claude.js.map +1 -0
- package/dist/harnesses/cli.d.ts +4 -0
- package/dist/harnesses/cli.d.ts.map +1 -0
- package/dist/harnesses/cli.js +16 -0
- package/dist/harnesses/cli.js.map +1 -0
- package/dist/harnesses/codex.d.ts +3 -0
- package/dist/harnesses/codex.d.ts.map +1 -0
- package/dist/harnesses/codex.js +183 -0
- package/dist/harnesses/codex.js.map +1 -0
- package/dist/harnesses/hermes.d.ts +16 -0
- package/dist/harnesses/hermes.d.ts.map +1 -0
- package/dist/harnesses/hermes.js +158 -0
- package/dist/harnesses/hermes.js.map +1 -0
- package/dist/harnesses/index.d.ts +12 -0
- package/dist/harnesses/index.d.ts.map +1 -0
- package/dist/harnesses/index.js +29 -0
- package/dist/harnesses/index.js.map +1 -0
- package/dist/harnesses/opencode.d.ts +3 -0
- package/dist/harnesses/opencode.d.ts.map +1 -0
- package/dist/harnesses/opencode.js +141 -0
- package/dist/harnesses/opencode.js.map +1 -0
- package/dist/harnesses/pi.d.ts +3 -0
- package/dist/harnesses/pi.d.ts.map +1 -0
- package/dist/harnesses/pi.js +72 -0
- package/dist/harnesses/pi.js.map +1 -0
- package/dist/harnesses/types.d.ts +48 -0
- package/dist/harnesses/types.d.ts.map +1 -0
- package/dist/harnesses/types.js +22 -0
- package/dist/harnesses/types.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/machine.d.ts +10 -0
- package/dist/machine.d.ts.map +1 -0
- package/dist/machine.js +39 -0
- package/dist/machine.js.map +1 -0
- package/dist/parse-args.d.ts +12 -0
- package/dist/parse-args.d.ts.map +1 -0
- package/dist/parse-args.js +54 -0
- package/dist/parse-args.js.map +1 -0
- package/dist/paths.d.ts +32 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +75 -0
- package/dist/paths.js.map +1 -0
- package/dist/prompt.d.ts +53 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +165 -0
- package/dist/prompt.js.map +1 -0
- package/dist/runtime.d.ts +45 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +370 -0
- package/dist/runtime.js.map +1 -0
- package/dist/semver.d.ts +13 -0
- package/dist/semver.d.ts.map +1 -0
- package/dist/semver.js +98 -0
- package/dist/semver.js.map +1 -0
- package/dist/server/admin.d.ts +31 -0
- package/dist/server/admin.d.ts.map +1 -0
- package/dist/server/admin.js +110 -0
- package/dist/server/admin.js.map +1 -0
- package/dist/server/boot.d.ts +51 -0
- package/dist/server/boot.d.ts.map +1 -0
- package/dist/server/boot.js +233 -0
- package/dist/server/boot.js.map +1 -0
- package/dist/server/deploy-state.d.ts +33 -0
- package/dist/server/deploy-state.d.ts.map +1 -0
- package/dist/server/deploy-state.js +79 -0
- package/dist/server/deploy-state.js.map +1 -0
- package/dist/server/docker.d.ts +37 -0
- package/dist/server/docker.d.ts.map +1 -0
- package/dist/server/docker.js +138 -0
- package/dist/server/docker.js.map +1 -0
- package/dist/server/down.d.ts +19 -0
- package/dist/server/down.d.ts.map +1 -0
- package/dist/server/down.js +62 -0
- package/dist/server/down.js.map +1 -0
- package/dist/server/index.d.ts +8 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +48 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/logs.d.ts +50 -0
- package/dist/server/logs.d.ts.map +1 -0
- package/dist/server/logs.js +162 -0
- package/dist/server/logs.js.map +1 -0
- package/dist/server/preflight.d.ts +24 -0
- package/dist/server/preflight.d.ts.map +1 -0
- package/dist/server/preflight.js +58 -0
- package/dist/server/preflight.js.map +1 -0
- package/dist/server/redact.d.ts +2 -0
- package/dist/server/redact.d.ts.map +1 -0
- package/dist/server/redact.js +26 -0
- package/dist/server/redact.js.map +1 -0
- package/dist/server/status.d.ts +20 -0
- package/dist/server/status.d.ts.map +1 -0
- package/dist/server/status.js +103 -0
- package/dist/server/status.js.map +1 -0
- package/dist/server/up.d.ts +211 -0
- package/dist/server/up.d.ts.map +1 -0
- package/dist/server/up.js +625 -0
- package/dist/server/up.js.map +1 -0
- package/dist/server/update.d.ts +35 -0
- package/dist/server/update.d.ts.map +1 -0
- package/dist/server/update.js +308 -0
- package/dist/server/update.js.map +1 -0
- package/dist/status.d.ts +18 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +113 -0
- package/dist/status.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +20 -0
- package/dist/version.js.map +1 -0
- package/package.json +36 -0
package/dist/env.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// Env-var plumbing: `~/.librarian/env` + the managed shell-rc block.
|
|
2
|
+
//
|
|
3
|
+
// The token and MCP URL never land in a committed or world-readable rc.
|
|
4
|
+
// We write them to `~/.librarian/env` (chmod 600) and add ONE idempotent
|
|
5
|
+
// managed block to the user's shell rc that sources it:
|
|
6
|
+
//
|
|
7
|
+
// # >>> librarian >>>
|
|
8
|
+
// [ -f "$HOME/.librarian/env" ] && . "$HOME/.librarian/env"
|
|
9
|
+
// # <<< librarian <<<
|
|
10
|
+
//
|
|
11
|
+
// Re-running REPLACES the block (matched on the sentinels) — never
|
|
12
|
+
// duplicates. Fish can't source a POSIX env file, so it gets a native
|
|
13
|
+
// `~/.config/fish/conf.d/librarian.fish` with `set -gx` reading the same
|
|
14
|
+
// values. The token is never logged.
|
|
15
|
+
//
|
|
16
|
+
// Every function takes an injectable `home` so tests run against a temp
|
|
17
|
+
// dir; nothing here touches the real `~`.
|
|
18
|
+
import fs from "node:fs";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import { bashRcPath, envFilePath, fishConfPath, librarianDir, zshRcPath } from "./paths.js";
|
|
21
|
+
// The exact sentinels the spec pins. The block is matched on these so a
|
|
22
|
+
// re-run replaces it rather than appending a duplicate. Do not reword.
|
|
23
|
+
const BLOCK_OPEN = "# >>> librarian >>>";
|
|
24
|
+
const BLOCK_CLOSE = "# <<< librarian <<<";
|
|
25
|
+
const POSIX_SOURCE_LINE = '[ -f "$HOME/.librarian/env" ] && . "$HOME/.librarian/env"';
|
|
26
|
+
const FISH_OPEN = "# >>> librarian >>>";
|
|
27
|
+
const FISH_CLOSE = "# <<< librarian <<<";
|
|
28
|
+
/**
|
|
29
|
+
* Detect the user's shell from `$SHELL`, defaulting to bash.
|
|
30
|
+
*
|
|
31
|
+
* `override` (e.g. from a `--shell` flag) wins when it names a supported
|
|
32
|
+
* shell. An unrecognised `$SHELL` falls back to bash — the POSIX block
|
|
33
|
+
* is the safe default.
|
|
34
|
+
*/
|
|
35
|
+
export function detectShell(override, env = process.env) {
|
|
36
|
+
const pick = (value) => {
|
|
37
|
+
if (!value)
|
|
38
|
+
return undefined;
|
|
39
|
+
const base = value.toLowerCase();
|
|
40
|
+
if (base.includes("fish"))
|
|
41
|
+
return "fish";
|
|
42
|
+
if (base.includes("zsh"))
|
|
43
|
+
return "zsh";
|
|
44
|
+
if (base.includes("bash"))
|
|
45
|
+
return "bash";
|
|
46
|
+
return undefined;
|
|
47
|
+
};
|
|
48
|
+
return pick(override) ?? pick(env.SHELL) ?? "bash";
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Write `~/.librarian/env` with the two exports, chmod 600.
|
|
52
|
+
*
|
|
53
|
+
* The directory is created if missing; the file is (re)written wholesale
|
|
54
|
+
* so stale values can't linger. Never logs `values.token`.
|
|
55
|
+
*/
|
|
56
|
+
export function writeEnvFile(values, home) {
|
|
57
|
+
const dir = librarianDir(home);
|
|
58
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
59
|
+
const body = [
|
|
60
|
+
`export LIBRARIAN_MCP_URL=${shellQuote(values.mcpUrl)}`,
|
|
61
|
+
`export LIBRARIAN_AGENT_TOKEN=${shellQuote(values.token)}`,
|
|
62
|
+
"",
|
|
63
|
+
].join("\n");
|
|
64
|
+
const file = envFilePath(home);
|
|
65
|
+
fs.writeFileSync(file, body, { encoding: "utf8", mode: 0o600 });
|
|
66
|
+
// writeFileSync only applies `mode` on create; chmod unconditionally so a
|
|
67
|
+
// pre-existing looser file is tightened.
|
|
68
|
+
fs.chmodSync(file, 0o600);
|
|
69
|
+
}
|
|
70
|
+
/** Read the env file back into structured values, or null if absent. */
|
|
71
|
+
export function readEnvFile(home) {
|
|
72
|
+
let raw;
|
|
73
|
+
try {
|
|
74
|
+
raw = fs.readFileSync(envFilePath(home), "utf8");
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const mcpUrl = parseExport(raw, "LIBRARIAN_MCP_URL");
|
|
80
|
+
const token = parseExport(raw, "LIBRARIAN_AGENT_TOKEN");
|
|
81
|
+
if (mcpUrl === undefined && token === undefined)
|
|
82
|
+
return null;
|
|
83
|
+
return { mcpUrl: mcpUrl ?? "", token: token ?? "" };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Apply (or replace) the managed block in the shell rc.
|
|
87
|
+
*
|
|
88
|
+
* For bash/zsh this writes the POSIX source-block into `~/.bashrc` or
|
|
89
|
+
* `~/.zshrc`. For fish it writes `~/.config/fish/conf.d/librarian.fish`
|
|
90
|
+
* with `set -gx` reading the values directly (fish can't source POSIX).
|
|
91
|
+
*
|
|
92
|
+
* Idempotent: a second call with the rc already containing a block
|
|
93
|
+
* replaces it in place — never appends a duplicate.
|
|
94
|
+
*
|
|
95
|
+
* Returns the path that was written.
|
|
96
|
+
*/
|
|
97
|
+
export function applyShellBlock(shell, values, home) {
|
|
98
|
+
if (shell === "fish")
|
|
99
|
+
return writeFishConf(values, home);
|
|
100
|
+
const rcPath = shell === "zsh" ? zshRcPath(home) : bashRcPath(home);
|
|
101
|
+
const block = [BLOCK_OPEN, POSIX_SOURCE_LINE, BLOCK_CLOSE].join("\n");
|
|
102
|
+
const next = upsertBlock(readFileOrEmpty(rcPath), block);
|
|
103
|
+
fs.mkdirSync(path.dirname(rcPath), { recursive: true });
|
|
104
|
+
fs.writeFileSync(rcPath, next, { encoding: "utf8" });
|
|
105
|
+
return rcPath;
|
|
106
|
+
}
|
|
107
|
+
/** Remove the managed block from the shell rc (or delete the fish conf). */
|
|
108
|
+
export function removeShellBlock(shell, home) {
|
|
109
|
+
if (shell === "fish") {
|
|
110
|
+
try {
|
|
111
|
+
fs.rmSync(fishConfPath(home));
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// already gone
|
|
115
|
+
}
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const rcPath = shell === "zsh" ? zshRcPath(home) : bashRcPath(home);
|
|
119
|
+
const current = readFileOrEmpty(rcPath);
|
|
120
|
+
if (!current)
|
|
121
|
+
return;
|
|
122
|
+
const stripped = stripBlock(current);
|
|
123
|
+
if (stripped !== current)
|
|
124
|
+
fs.writeFileSync(rcPath, stripped, { encoding: "utf8" });
|
|
125
|
+
}
|
|
126
|
+
function writeFishConf(values, home) {
|
|
127
|
+
const confPath = fishConfPath(home);
|
|
128
|
+
const body = [
|
|
129
|
+
FISH_OPEN,
|
|
130
|
+
`set -gx LIBRARIAN_MCP_URL ${fishQuote(values.mcpUrl)}`,
|
|
131
|
+
`set -gx LIBRARIAN_AGENT_TOKEN ${fishQuote(values.token)}`,
|
|
132
|
+
FISH_CLOSE,
|
|
133
|
+
"",
|
|
134
|
+
].join("\n");
|
|
135
|
+
fs.mkdirSync(path.dirname(confPath), { recursive: true });
|
|
136
|
+
// The fish conf carries the token directly, so tighten it to 600 too.
|
|
137
|
+
fs.writeFileSync(confPath, body, { encoding: "utf8", mode: 0o600 });
|
|
138
|
+
fs.chmodSync(confPath, 0o600);
|
|
139
|
+
return confPath;
|
|
140
|
+
}
|
|
141
|
+
// --- block manipulation (sentinel-matched, idempotent) -------------------
|
|
142
|
+
/** Replace an existing sentinel block, or append a fresh one. */
|
|
143
|
+
function upsertBlock(content, block) {
|
|
144
|
+
const stripped = stripBlock(content);
|
|
145
|
+
const base = stripped.length === 0 || stripped.endsWith("\n") ? stripped : `${stripped}\n`;
|
|
146
|
+
return `${base}${block}\n`;
|
|
147
|
+
}
|
|
148
|
+
/** Remove a sentinel-delimited block (and its surrounding blank lines). */
|
|
149
|
+
function stripBlock(content) {
|
|
150
|
+
const lines = content.split("\n");
|
|
151
|
+
const start = lines.findIndex((line) => line.trim() === BLOCK_OPEN);
|
|
152
|
+
if (start === -1)
|
|
153
|
+
return content;
|
|
154
|
+
let end = -1;
|
|
155
|
+
for (let i = start + 1; i < lines.length; i += 1) {
|
|
156
|
+
if (lines[i]?.trim() === BLOCK_CLOSE) {
|
|
157
|
+
end = i;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (end === -1)
|
|
162
|
+
return content; // unterminated — leave it alone
|
|
163
|
+
const before = lines.slice(0, start);
|
|
164
|
+
const after = lines.slice(end + 1);
|
|
165
|
+
// Drop a single trailing blank line left by the block so repeated
|
|
166
|
+
// apply/strip cycles don't accumulate whitespace.
|
|
167
|
+
while (before.length > 0 && before[before.length - 1] === "")
|
|
168
|
+
before.pop();
|
|
169
|
+
while (after.length > 0 && after[0] === "")
|
|
170
|
+
after.shift();
|
|
171
|
+
const joined = [...before, ...after].join("\n");
|
|
172
|
+
return joined;
|
|
173
|
+
}
|
|
174
|
+
// --- small helpers -------------------------------------------------------
|
|
175
|
+
function readFileOrEmpty(file) {
|
|
176
|
+
try {
|
|
177
|
+
return fs.readFileSync(file, "utf8");
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return "";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function parseExport(raw, name) {
|
|
184
|
+
// Matches `export NAME=value` (value optionally single/double quoted).
|
|
185
|
+
const re = new RegExp(`^\\s*export\\s+${name}=(.*)$`, "m");
|
|
186
|
+
const match = re.exec(raw);
|
|
187
|
+
if (!match)
|
|
188
|
+
return undefined;
|
|
189
|
+
return unquote((match[1] ?? "").trim());
|
|
190
|
+
}
|
|
191
|
+
/** POSIX single-quote a value so the env file is robust to special chars. */
|
|
192
|
+
function shellQuote(value) {
|
|
193
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
194
|
+
}
|
|
195
|
+
/** Fish single-quote a value (fish escapes `\` and `'` inside single quotes). */
|
|
196
|
+
function fishQuote(value) {
|
|
197
|
+
return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
198
|
+
}
|
|
199
|
+
function unquote(value) {
|
|
200
|
+
if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
|
|
201
|
+
return value.slice(1, -1).replace(/'\\''/g, "'");
|
|
202
|
+
}
|
|
203
|
+
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
204
|
+
return value.slice(1, -1);
|
|
205
|
+
}
|
|
206
|
+
return value;
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=env.js.map
|
package/dist/env.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"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"}
|
package/dist/exec.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** The result of running a command: captured streams + exit code. */
|
|
2
|
+
export interface RunResult {
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
/** The process exit code; `null` only if the process was signalled. */
|
|
6
|
+
code: number | null;
|
|
7
|
+
}
|
|
8
|
+
/** Options for a single command run. */
|
|
9
|
+
export interface RunOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Extra environment for the child. Merged over `process.env`. The token
|
|
12
|
+
* rides here (per a harness's native env-var mechanism) — it is passed
|
|
13
|
+
* to the child but never logged by this module.
|
|
14
|
+
*/
|
|
15
|
+
env?: Record<string, string | undefined> | undefined;
|
|
16
|
+
/** Working directory for the child. */
|
|
17
|
+
cwd?: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The runner surface every harness depends on. The default implementation
|
|
21
|
+
* spawns real processes; tests inject a fake that records calls.
|
|
22
|
+
*/
|
|
23
|
+
export interface Runner {
|
|
24
|
+
/** Run `cmd args…`, resolving with captured streams + exit code. */
|
|
25
|
+
run(cmd: string, args: readonly string[], opts?: RunOptions): Promise<RunResult>;
|
|
26
|
+
/** Resolve `cmd` to its absolute path on PATH, or `null` if absent. */
|
|
27
|
+
which(cmd: string): Promise<string | null>;
|
|
28
|
+
}
|
|
29
|
+
/** Run a command through the current runner. */
|
|
30
|
+
export declare function run(cmd: string, args: readonly string[], opts?: RunOptions): Promise<RunResult>;
|
|
31
|
+
/** Resolve a command to its PATH location through the current runner. */
|
|
32
|
+
export declare function which(cmd: string): Promise<string | null>;
|
|
33
|
+
/** Swap in a fake runner (tests). Returns the previous runner. */
|
|
34
|
+
export declare function setRunner(runner: Runner): Runner;
|
|
35
|
+
/** Restore the real, process-spawning runner (tests). */
|
|
36
|
+
export declare function resetRunner(): void;
|
|
37
|
+
//# sourceMappingURL=exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../src/exec.ts"],"names":[],"mappings":"AAkBA,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IACrD,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,oEAAoE;IACpE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACjF,uEAAuE;IACvE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC5C;AAmDD,gDAAgD;AAChD,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAE/F;AAED,yEAAyE;AACzE,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEzD;AAED,kEAAkE;AAClE,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED,yDAAyD;AACzD,wBAAgB,WAAW,IAAI,IAAI,CAElC"}
|
package/dist/exec.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Injectable child-process runner.
|
|
2
|
+
//
|
|
3
|
+
// Every harness that drives a native CLI (`claude`, `codex`, `pi`) goes
|
|
4
|
+
// through this module rather than calling `node:child_process` directly,
|
|
5
|
+
// so tests can stub the runner and assert the exact command + args a
|
|
6
|
+
// harness invokes — WITHOUT spawning real processes or touching PATH.
|
|
7
|
+
//
|
|
8
|
+
// Security (spec §9): a command may *carry* the agent token (e.g. via an
|
|
9
|
+
// env var its native mechanism reads), but the token is NEVER logged,
|
|
10
|
+
// echoed, or placed where this module would print it. The runner returns
|
|
11
|
+
// stdout/stderr to the caller; it deliberately does not log them.
|
|
12
|
+
//
|
|
13
|
+
// Stubbing: `setRunner()` swaps the module-level default for a fake in
|
|
14
|
+
// tests; `resetRunner()` restores the real one. `run`/`which` always go
|
|
15
|
+
// through the current runner, so a single override covers both.
|
|
16
|
+
import { spawn } from "node:child_process";
|
|
17
|
+
// --- the real, process-spawning runner -----------------------------------
|
|
18
|
+
const realRunner = {
|
|
19
|
+
run(cmd, args, opts = {}) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const child = spawn(cmd, [...args], {
|
|
22
|
+
cwd: opts.cwd,
|
|
23
|
+
env: opts.env ? { ...process.env, ...opts.env } : process.env,
|
|
24
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
25
|
+
});
|
|
26
|
+
let stdout = "";
|
|
27
|
+
let stderr = "";
|
|
28
|
+
child.stdout?.on("data", (chunk) => {
|
|
29
|
+
stdout += chunk.toString("utf8");
|
|
30
|
+
});
|
|
31
|
+
child.stderr?.on("data", (chunk) => {
|
|
32
|
+
stderr += chunk.toString("utf8");
|
|
33
|
+
});
|
|
34
|
+
// ENOENT (binary not on PATH) surfaces here, not as a non-zero exit.
|
|
35
|
+
child.on("error", reject);
|
|
36
|
+
child.on("close", (code) => {
|
|
37
|
+
resolve({ stdout, stderr, code });
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
async which(cmd) {
|
|
42
|
+
// Use the platform's own resolver so PATHEXT etc. are honoured. A
|
|
43
|
+
// non-zero exit (not found) resolves to null; we never throw.
|
|
44
|
+
const probe = process.platform === "win32" ? "where" : "which";
|
|
45
|
+
try {
|
|
46
|
+
const { stdout, code } = await realRunner.run(probe, [cmd]);
|
|
47
|
+
if (code !== 0)
|
|
48
|
+
return null;
|
|
49
|
+
const first = stdout
|
|
50
|
+
.split("\n")
|
|
51
|
+
.map((l) => l.trim())
|
|
52
|
+
.find((l) => l.length > 0);
|
|
53
|
+
return first ?? null;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// `which`/`where` itself missing, or some other spawn failure.
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
// --- the swappable module-level runner -----------------------------------
|
|
62
|
+
let current = realRunner;
|
|
63
|
+
/** Run a command through the current runner. */
|
|
64
|
+
export function run(cmd, args, opts) {
|
|
65
|
+
return current.run(cmd, args, opts);
|
|
66
|
+
}
|
|
67
|
+
/** Resolve a command to its PATH location through the current runner. */
|
|
68
|
+
export function which(cmd) {
|
|
69
|
+
return current.which(cmd);
|
|
70
|
+
}
|
|
71
|
+
/** Swap in a fake runner (tests). Returns the previous runner. */
|
|
72
|
+
export function setRunner(runner) {
|
|
73
|
+
const prev = current;
|
|
74
|
+
current = runner;
|
|
75
|
+
return prev;
|
|
76
|
+
}
|
|
77
|
+
/** Restore the real, process-spawning runner (tests). */
|
|
78
|
+
export function resetRunner() {
|
|
79
|
+
current = realRunner;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=exec.js.map
|
package/dist/exec.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../src/exec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/harnesses/claude.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,YAAY,CAAC;AAgC/D,eAAO,MAAM,MAAM,EAAE,aAsCpB,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Claude Code harness.
|
|
2
|
+
//
|
|
3
|
+
// Drives the `claude` CLI's native plugin/marketplace commands — we never
|
|
4
|
+
// hand-edit Claude config. The marketplace is `JimJafar/the-librarian`;
|
|
5
|
+
// the plugin is `the-librarian@the-librarian` (name@marketplace).
|
|
6
|
+
//
|
|
7
|
+
// detect `claude` on PATH AND `claude plugin list` shows our plugin
|
|
8
|
+
// install `claude plugin marketplace add JimJafar/the-librarian`
|
|
9
|
+
// then `claude plugin install the-librarian@the-librarian`
|
|
10
|
+
// uninstall `claude plugin remove the-librarian@the-librarian`
|
|
11
|
+
// then `claude plugin marketplace remove the-librarian`
|
|
12
|
+
// update re-run install (idempotent; pulls the current version)
|
|
13
|
+
//
|
|
14
|
+
// The token plays no part here — Claude's MCP auth is configured by the
|
|
15
|
+
// plugin itself — so nothing token-shaped is ever passed or logged.
|
|
16
|
+
import { run, which } from "../exec.js";
|
|
17
|
+
const CLI = "claude";
|
|
18
|
+
const MARKETPLACE = "JimJafar/the-librarian";
|
|
19
|
+
const MARKETPLACE_ID = "the-librarian";
|
|
20
|
+
const PLUGIN = "the-librarian@the-librarian";
|
|
21
|
+
const PLUGIN_NAME = "the-librarian";
|
|
22
|
+
/** Friendly error when the harness CLI isn't installed. */
|
|
23
|
+
function notFound() {
|
|
24
|
+
return new Error("Claude Code CLI not found on PATH (expected `claude`).");
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Pull our plugin's version out of `claude plugin list` output, which
|
|
28
|
+
* formats loosely across versions. We look for a line mentioning the
|
|
29
|
+
* plugin and a trailing semver-ish token; absence is `undefined`.
|
|
30
|
+
*/
|
|
31
|
+
function parsePluginVersion(listOutput) {
|
|
32
|
+
for (const line of listOutput.split("\n")) {
|
|
33
|
+
if (!line.includes(PLUGIN_NAME))
|
|
34
|
+
continue;
|
|
35
|
+
const m = /\bv?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)\b/.exec(line);
|
|
36
|
+
if (m?.[1])
|
|
37
|
+
return m[1];
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
/** True iff `claude plugin list` output shows our plugin as present. */
|
|
42
|
+
function pluginListed(listOutput) {
|
|
43
|
+
return listOutput.split("\n").some((line) => line.includes(PLUGIN_NAME));
|
|
44
|
+
}
|
|
45
|
+
export const claude = {
|
|
46
|
+
id: "claude",
|
|
47
|
+
displayName: "Claude Code",
|
|
48
|
+
async detect() {
|
|
49
|
+
if (!(await which(CLI)))
|
|
50
|
+
return { installed: false };
|
|
51
|
+
const { stdout, code } = await run(CLI, ["plugin", "list"]);
|
|
52
|
+
if (code !== 0 || !pluginListed(stdout))
|
|
53
|
+
return { installed: false };
|
|
54
|
+
const version = parsePluginVersion(stdout);
|
|
55
|
+
return version === undefined ? { installed: true } : { installed: true, version };
|
|
56
|
+
},
|
|
57
|
+
async install(_cfg) {
|
|
58
|
+
if (!(await which(CLI)))
|
|
59
|
+
throw notFound();
|
|
60
|
+
// marketplace add is idempotent in the CLI; installing again is a no-op
|
|
61
|
+
// re-resolve, so a second `install` neither errors nor duplicates.
|
|
62
|
+
const add = await run(CLI, ["plugin", "marketplace", "add", MARKETPLACE]);
|
|
63
|
+
if (add.code !== 0) {
|
|
64
|
+
throw new Error(`claude plugin marketplace add failed: ${oneLine(add.stderr || add.stdout)}`);
|
|
65
|
+
}
|
|
66
|
+
const install = await run(CLI, ["plugin", "install", PLUGIN]);
|
|
67
|
+
if (install.code !== 0) {
|
|
68
|
+
throw new Error(`claude plugin install failed: ${oneLine(install.stderr || install.stdout)}`);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
async uninstall() {
|
|
72
|
+
if (!(await which(CLI)))
|
|
73
|
+
return; // nothing to remove if the CLI is gone
|
|
74
|
+
// Removing an absent plugin / marketplace is a no-op; we don't fail the
|
|
75
|
+
// uninstall on a "not installed" exit, only surface genuine errors.
|
|
76
|
+
await run(CLI, ["plugin", "remove", PLUGIN]);
|
|
77
|
+
await run(CLI, ["plugin", "marketplace", "remove", MARKETPLACE_ID]);
|
|
78
|
+
},
|
|
79
|
+
async update(cfg) {
|
|
80
|
+
// Re-running install pulls the marketplace's current version.
|
|
81
|
+
await this.install(cfg);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
/** Collapse multi-line CLI error text to a single tidy line. */
|
|
85
|
+
function oneLine(text) {
|
|
86
|
+
return text.replace(/\s+/g, " ").trim();
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/harnesses/claude.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/harnesses/cli.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,6EAA6E;AAC7E,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAMxD,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Which native CLI binary (if any) each harness drives.
|
|
2
|
+
//
|
|
3
|
+
// The `HarnessModule` interface deliberately doesn't expose this — the
|
|
4
|
+
// modules are thin and uniform. But the orchestration layer needs it for two
|
|
5
|
+
// things: choosing the default install set (harnesses whose CLI is on PATH)
|
|
6
|
+
// and the `doctor` report ("which harness CLIs are present?"). File-based
|
|
7
|
+
// harnesses (opencode, hermes) have no CLI gate, so they map to `null`.
|
|
8
|
+
/** Harness id → the native CLI binary it drives, or `null` if file-based. */
|
|
9
|
+
export const HARNESS_CLI = {
|
|
10
|
+
claude: "claude",
|
|
11
|
+
codex: "codex",
|
|
12
|
+
opencode: null,
|
|
13
|
+
hermes: null,
|
|
14
|
+
pi: "pi",
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/harnesses/cli.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,EAAE;AACF,uEAAuE;AACvE,6EAA6E;AAC7E,4EAA4E;AAC5E,0EAA0E;AAC1E,wEAAwE;AAIxE,6EAA6E;AAC7E,MAAM,CAAC,MAAM,WAAW,GAAqC;IAC3D,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,IAAI;IACd,MAAM,EAAE,IAAI;IACZ,EAAE,EAAE,IAAI;CACT,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/harnesses/codex.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,YAAY,CAAC;AA6F/D,eAAO,MAAM,KAAK,EAAE,aAuDnB,CAAC"}
|