balladeer 1.0.8 → 1.0.11
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/dist/agent.d.ts +1 -1
- package/dist/agent.js +12 -0
- package/dist/cli.d.ts +3 -2
- package/dist/cli.js +117 -56
- package/dist/commands/discover.js +2 -2
- package/dist/commands/guidance.d.ts +11 -1
- package/dist/commands/guidance.js +24 -11
- package/dist/commands/mcp.js +2 -2
- package/dist/commands/prepare.js +2 -2
- package/dist/commands/propose.js +2 -2
- package/dist/commands/session.js +2 -2
- package/dist/commands/setup.d.ts +15 -0
- package/dist/commands/setup.js +51 -15
- package/dist/commands/status.js +2 -2
- package/dist/currency.d.ts +3 -0
- package/dist/currency.js +8 -0
- package/dist/guidance-hook.mjs +99 -46
- package/dist/guidance.js +4 -0
- package/dist/install.d.ts +2 -0
- package/dist/install.js +2 -0
- package/dist/open-browser.d.ts +13 -0
- package/dist/open-browser.js +30 -0
- package/dist/release.d.ts +6 -0
- package/dist/release.js +6 -0
- package/dist/remove-earlier.d.ts +68 -0
- package/dist/remove-earlier.js +378 -0
- package/dist/repository.d.ts +9 -0
- package/dist/repository.js +31 -5
- package/dist/self-update.d.ts +11 -0
- package/dist/self-update.js +59 -0
- package/dist/user-scope.d.ts +23 -7
- package/dist/user-scope.js +50 -17
- package/dist/wire.d.ts +15 -2
- package/dist/wire.js +1 -1
- package/package.json +1 -1
package/dist/user-scope.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync, lstatSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
4
|
import { parse } from "@iarna/toml";
|
|
5
|
-
import {
|
|
5
|
+
import { HOOK_SPECIFIER, checkoutEntryPath } from "./release.js";
|
|
6
6
|
import { writeJsonAtomically } from "./mcp-config.js";
|
|
7
7
|
/**
|
|
8
8
|
* Install Balladeer once per laptop.
|
|
@@ -22,19 +22,29 @@ import { writeJsonAtomically } from "./mcp-config.js";
|
|
|
22
22
|
*/
|
|
23
23
|
export const USER_SCOPE_OWNER = "Balladeer current guidance (user scope, loader 1)";
|
|
24
24
|
const CLAUDE_EVENTS = ["SessionStart", "UserPromptSubmit", "SubagentStart"];
|
|
25
|
-
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
|
|
25
|
+
/**
|
|
26
|
+
* The command a host runs to reach Balladeer, with no repository named: the
|
|
27
|
+
* directory decides.
|
|
28
|
+
*
|
|
29
|
+
* With a durable command installed, the entries name it by absolute path:
|
|
30
|
+
* no npx, so a session start costs node's startup and nothing else, and no
|
|
31
|
+
* PATH, so a host launched from the dock finds it. A laptop where the durable
|
|
32
|
+
* install was refused gets the `npx` form on the current major, which works
|
|
33
|
+
* everywhere and costs a registry lookup per session. The checkout form
|
|
34
|
+
* exists so this repository's own tests can point a host at source.
|
|
35
|
+
*/
|
|
36
|
+
export function userScopeCommand(published, installed) {
|
|
37
|
+
if (installed !== undefined)
|
|
38
|
+
return { command: installed, args: [] };
|
|
29
39
|
return published
|
|
30
|
-
? { command: "npx", args: ["-y",
|
|
40
|
+
? { command: "npx", args: ["-y", HOOK_SPECIFIER] }
|
|
31
41
|
: { command: "node", args: [checkoutEntryPath()] };
|
|
32
42
|
}
|
|
33
43
|
function ordinaryFile(path) {
|
|
34
44
|
return !existsSync(path) || (lstatSync(path).isFile() && !lstatSync(path).isSymbolicLink());
|
|
35
45
|
}
|
|
36
46
|
/** `~/.claude.json` carries the user-scope MCP list under `mcpServers`. */
|
|
37
|
-
export function mergeClaudeUserMcp(home, published) {
|
|
47
|
+
export function mergeClaudeUserMcp(home, published, installed) {
|
|
38
48
|
const path = join(home, ".claude.json");
|
|
39
49
|
const host = "claude";
|
|
40
50
|
if (!ordinaryFile(path))
|
|
@@ -54,7 +64,7 @@ export function mergeClaudeUserMcp(home, published) {
|
|
|
54
64
|
const servers = root.mcpServers && typeof root.mcpServers === "object" && !Array.isArray(root.mcpServers)
|
|
55
65
|
? root.mcpServers
|
|
56
66
|
: {};
|
|
57
|
-
const { command, args } = userScopeCommand(published);
|
|
67
|
+
const { command, args } = userScopeCommand(published, installed);
|
|
58
68
|
const expected = { type: "stdio", command, args: [...args, "mcp"] };
|
|
59
69
|
const current = servers.balladeer;
|
|
60
70
|
if (current !== undefined && !isOurUserEntry(current)) {
|
|
@@ -84,14 +94,37 @@ export function isOurUserEntry(entry) {
|
|
|
84
94
|
if (record.command === "npx")
|
|
85
95
|
return (args.length === 3 &&
|
|
86
96
|
args[0] === "-y" &&
|
|
87
|
-
/^balladeer@(?:latest|\d+\.\d+\.\d+)$/.test(args[1]) &&
|
|
97
|
+
/^balladeer@(?:latest|1|\d+\.\d+\.\d+)$/.test(args[1]) &&
|
|
88
98
|
args[2] === "mcp");
|
|
89
99
|
if (record.command === "node" || String(record.command).endsWith("/node"))
|
|
90
100
|
return args.length === 2 && args[0] === checkoutEntryPath() && args[1] === "mcp";
|
|
101
|
+
// The durable command by absolute path: ours when it sits where this
|
|
102
|
+
// product's installer puts it and is asked for `mcp` alone. The July client
|
|
103
|
+
// also registered an absolute `balladeer mcp`, from its own home, so the
|
|
104
|
+
// name is not enough; the place is what says whose it is.
|
|
105
|
+
if (typeof record.command === "string" && isAbsolute(record.command))
|
|
106
|
+
return isInstalledCommandPath(record.command) && args.length === 1 && args[0] === "mcp";
|
|
91
107
|
return false;
|
|
92
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Where `ensureInstalled` puts the durable command: Volta's shim directory, an
|
|
111
|
+
* npm global prefix's `bin` with the package beside it under `lib/node_modules`,
|
|
112
|
+
* or this product's own fallback prefix under the person's home.
|
|
113
|
+
*/
|
|
114
|
+
export function isInstalledCommandPath(command) {
|
|
115
|
+
if (basename(command) !== "balladeer")
|
|
116
|
+
return false;
|
|
117
|
+
const normalized = command.replaceAll("\\", "/");
|
|
118
|
+
if (/\/\.volta\/bin\/balladeer$/.test(normalized))
|
|
119
|
+
return true;
|
|
120
|
+
if (/\/\.local\/share\/balladeer\/npm\/bin\/balladeer$/.test(normalized))
|
|
121
|
+
return true;
|
|
122
|
+
const bin = dirname(command);
|
|
123
|
+
return (basename(bin) === "bin" &&
|
|
124
|
+
existsSync(join(dirname(bin), "lib", "node_modules", "balladeer", "package.json")));
|
|
125
|
+
}
|
|
93
126
|
/** `~/.claude/settings.json` carries user-scope hooks. */
|
|
94
|
-
export function mergeClaudeUserHooks(home, published) {
|
|
127
|
+
export function mergeClaudeUserHooks(home, published, installed) {
|
|
95
128
|
const path = join(home, ".claude", "settings.json");
|
|
96
129
|
const host = "claude";
|
|
97
130
|
if (!ordinaryFile(path))
|
|
@@ -111,7 +144,7 @@ export function mergeClaudeUserHooks(home, published) {
|
|
|
111
144
|
const hooks = root.hooks && typeof root.hooks === "object" && !Array.isArray(root.hooks)
|
|
112
145
|
? { ...root.hooks }
|
|
113
146
|
: {};
|
|
114
|
-
const { command, args } = userScopeCommand(published);
|
|
147
|
+
const { command, args } = userScopeCommand(published, installed);
|
|
115
148
|
const line = [command, ...args, "guidance", "--hook", "claude"].join(" ");
|
|
116
149
|
let changed = false;
|
|
117
150
|
for (const event of CLAUDE_EVENTS) {
|
|
@@ -149,7 +182,7 @@ const CODEX_START = "# balladeer:user:start";
|
|
|
149
182
|
const CODEX_END = "# balladeer:user:end";
|
|
150
183
|
/** `~/.codex/config.toml` carries both the server and the hooks for Codex, in
|
|
151
184
|
* one fenced block this command owns end to end. */
|
|
152
|
-
export function mergeCodexUserConfig(home, published) {
|
|
185
|
+
export function mergeCodexUserConfig(home, published, installed) {
|
|
153
186
|
const path = join(home, ".codex", "config.toml");
|
|
154
187
|
const host = "codex";
|
|
155
188
|
if (!ordinaryFile(path))
|
|
@@ -163,7 +196,7 @@ export function mergeCodexUserConfig(home, published) {
|
|
|
163
196
|
catch {
|
|
164
197
|
return { host, path, status: "refused", reason: "could not read valid TOML" };
|
|
165
198
|
}
|
|
166
|
-
const { command, args } = userScopeCommand(published);
|
|
199
|
+
const { command, args } = userScopeCommand(published, installed);
|
|
167
200
|
const line = [command, ...args, "guidance", "--hook", "codex"].join(" ");
|
|
168
201
|
const block = [
|
|
169
202
|
CODEX_START,
|
|
@@ -225,10 +258,10 @@ export function userHome(environment = process.env) {
|
|
|
225
258
|
export function installUserScope(options) {
|
|
226
259
|
const home = options.home ?? userHome(options.environment);
|
|
227
260
|
const writes = [
|
|
228
|
-
mergeClaudeUserMcp(home, options.published),
|
|
229
|
-
mergeClaudeUserHooks(home, options.published),
|
|
261
|
+
mergeClaudeUserMcp(home, options.published, options.installed),
|
|
262
|
+
mergeClaudeUserHooks(home, options.published, options.installed),
|
|
230
263
|
];
|
|
231
264
|
if (existsSync(join(home, ".codex")))
|
|
232
|
-
writes.push(mergeCodexUserConfig(home, options.published));
|
|
265
|
+
writes.push(mergeCodexUserConfig(home, options.published, options.installed));
|
|
233
266
|
return writes;
|
|
234
267
|
}
|
package/dist/wire.d.ts
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* runtime dependency at all: Node 22 builtins and global fetch, nothing else.
|
|
6
6
|
* A contract test compares the scope list below against the server's.
|
|
7
7
|
*/
|
|
8
|
-
export declare const CLI_VERSION = "1.0.
|
|
8
|
+
export declare const CLI_VERSION = "1.0.11";
|
|
9
9
|
export declare const CLI_INVOCATION = "npx -y balladeer@latest";
|
|
10
10
|
export declare const CLIENT_HEADER = "x-balladeer-client";
|
|
11
|
-
export declare const CLIENT_HEADER_VALUE = "balladeer/1.0.
|
|
11
|
+
export declare const CLIENT_HEADER_VALUE = "balladeer/1.0.11";
|
|
12
12
|
export declare const DEFAULT_CONTROL_PLANE = "https://envelopes.balladeer.ai";
|
|
13
13
|
export type DelegatedScope = "repository:enroll" | "agent:issue" | "ci:connect" | "workspace:invite" | "candidate:propose";
|
|
14
14
|
export declare const DELEGATED_SCOPES: readonly DelegatedScope[];
|
|
@@ -222,6 +222,19 @@ export type ClaudeDesktopStep = Readonly<{
|
|
|
222
222
|
reason?: string;
|
|
223
223
|
}>;
|
|
224
224
|
export type JsonStep = Readonly<{
|
|
225
|
+
/** The earlier Balladeer found on this machine and what setup did about it. */
|
|
226
|
+
step: "earlier";
|
|
227
|
+
removed: readonly {
|
|
228
|
+
path: string;
|
|
229
|
+
found: string;
|
|
230
|
+
backup?: string;
|
|
231
|
+
}[];
|
|
232
|
+
failed: readonly {
|
|
233
|
+
path: string;
|
|
234
|
+
found: string;
|
|
235
|
+
reason: string;
|
|
236
|
+
}[];
|
|
237
|
+
}> | Readonly<{
|
|
225
238
|
step: "guidance_loader";
|
|
226
239
|
status: "installed_needs_host_trust" | "installed_trust_unverified" | "unavailable" | "not_applicable";
|
|
227
240
|
changed: boolean;
|
package/dist/wire.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* runtime dependency at all: Node 22 builtins and global fetch, nothing else.
|
|
6
6
|
* A contract test compares the scope list below against the server's.
|
|
7
7
|
*/
|
|
8
|
-
export const CLI_VERSION = "1.0.
|
|
8
|
+
export const CLI_VERSION = "1.0.11";
|
|
9
9
|
export const CLI_INVOCATION = "npx -y balladeer@latest";
|
|
10
10
|
export const CLIENT_HEADER = "x-balladeer-client";
|
|
11
11
|
export const CLIENT_HEADER_VALUE = `balladeer/${CLI_VERSION}`;
|