balladeer 1.0.13 → 1.0.14
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/checkout-form.d.ts +45 -0
- package/dist/checkout-form.js +223 -0
- package/dist/cli.js +30 -2
- package/dist/commands/guidance.d.ts +1 -9
- package/dist/commands/guidance.js +5 -13
- package/dist/guidance-hook.mjs +3 -7
- package/dist/wire.d.ts +2 -2
- package/dist/wire.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Taking this product's own older, per-repository form out of a checkout once
|
|
3
|
+
* the user-scope form is in place, so one hook speaks per session.
|
|
4
|
+
*
|
|
5
|
+
* Releases before 1.0.8 registered Balladeer inside each repository: a hook in
|
|
6
|
+
* the checkout's `.claude/settings.json` (statusMessage "Balladeer current
|
|
7
|
+
* guidance (loader 1)"), a `balladeer` server in its `.mcp.json` that names the
|
|
8
|
+
* repository, and for Codex the fenced `# balladeer:guidance` and
|
|
9
|
+
* `# balladeer:mcp` blocks in `.codex/config.toml`. From 1.0.8 the same hook
|
|
10
|
+
* and server live at the host's user scope and run in every checkout. A laptop
|
|
11
|
+
* that moved from one to the other without cleaning up runs both: the session
|
|
12
|
+
* hears the guidance twice, and the server sees two hook forms fire for one
|
|
13
|
+
* person (found on Luke's laptop, 23 September 2026). Every removal here
|
|
14
|
+
* proves ownership from the entry itself; nothing else in those files moves.
|
|
15
|
+
* A file git tracks is edited without a copy (git holds the previous version)
|
|
16
|
+
* and the person is told to commit it; an untracked file is copied beside
|
|
17
|
+
* itself first.
|
|
18
|
+
*/
|
|
19
|
+
export type CheckoutFormItem = Readonly<{
|
|
20
|
+
path: string;
|
|
21
|
+
found: string;
|
|
22
|
+
tracked: boolean;
|
|
23
|
+
remove: () => string | undefined;
|
|
24
|
+
}>;
|
|
25
|
+
export type CheckoutFormReport = Readonly<{
|
|
26
|
+
removed: readonly {
|
|
27
|
+
path: string;
|
|
28
|
+
found: string;
|
|
29
|
+
copy?: string;
|
|
30
|
+
tracked: boolean;
|
|
31
|
+
}[];
|
|
32
|
+
failed: readonly {
|
|
33
|
+
path: string;
|
|
34
|
+
found: string;
|
|
35
|
+
reason: string;
|
|
36
|
+
}[];
|
|
37
|
+
}>;
|
|
38
|
+
/** The older per-repository form in the checkout that holds `cwd`, if any. */
|
|
39
|
+
export declare function findCheckoutForm(input: {
|
|
40
|
+
cwd: string;
|
|
41
|
+
controlPlane: string;
|
|
42
|
+
now?: () => Date;
|
|
43
|
+
}): CheckoutFormItem[];
|
|
44
|
+
export declare function removeCheckoutForm(items: readonly CheckoutFormItem[]): CheckoutFormReport;
|
|
45
|
+
export declare function describeCheckoutFormRemoval(report: CheckoutFormReport): string[];
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { copyFileSync, existsSync, lstatSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { isOurEntry, writeJsonAtomically } from "./mcp-config.js";
|
|
5
|
+
const PROJECT_OWNER = "Balladeer current guidance (loader 1)";
|
|
6
|
+
const CODEX_BLOCKS = [
|
|
7
|
+
["# balladeer:guidance:start", "# balladeer:guidance:end"],
|
|
8
|
+
["# balladeer:mcp:start", "# balladeer:mcp:end"],
|
|
9
|
+
];
|
|
10
|
+
function repositoryRoot(cwd) {
|
|
11
|
+
try {
|
|
12
|
+
const root = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
13
|
+
cwd,
|
|
14
|
+
encoding: "utf8",
|
|
15
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
16
|
+
timeout: 2_000,
|
|
17
|
+
}).trim();
|
|
18
|
+
return root.length > 0 ? root : undefined;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function tracked(root, path) {
|
|
25
|
+
try {
|
|
26
|
+
execFileSync("git", ["ls-files", "--error-unmatch", "--", path], {
|
|
27
|
+
cwd: root,
|
|
28
|
+
stdio: "ignore",
|
|
29
|
+
timeout: 2_000,
|
|
30
|
+
});
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function regularFile(path) {
|
|
38
|
+
try {
|
|
39
|
+
return lstatSync(path).isFile();
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function readJson(path) {
|
|
46
|
+
try {
|
|
47
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
48
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
49
|
+
? parsed
|
|
50
|
+
: undefined;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function backup(path, now) {
|
|
57
|
+
// A file name, not a sentence: the person reads the copy's path back, and the
|
|
58
|
+
// stamp only has to sort. Same form as the earlier-client backups.
|
|
59
|
+
const stamp = now().toISOString().replaceAll(/[:.]/g, "-");
|
|
60
|
+
const copy = `${path}.before-balladeer-${stamp}`;
|
|
61
|
+
copyFileSync(path, copy);
|
|
62
|
+
return copy;
|
|
63
|
+
}
|
|
64
|
+
const isProjectHook = (hook) => {
|
|
65
|
+
if (hook === null || typeof hook !== "object")
|
|
66
|
+
return false;
|
|
67
|
+
const { command, statusMessage } = hook;
|
|
68
|
+
if (statusMessage === PROJECT_OWNER)
|
|
69
|
+
return true;
|
|
70
|
+
return (typeof command === "string" &&
|
|
71
|
+
/balladeer/i.test(command) &&
|
|
72
|
+
/--hook (?:claude|codex)\b/.test(command) &&
|
|
73
|
+
/--repository\b/.test(command));
|
|
74
|
+
};
|
|
75
|
+
function claudeHooks(root, now) {
|
|
76
|
+
const path = join(root, ".claude", "settings.json");
|
|
77
|
+
if (!regularFile(path))
|
|
78
|
+
return [];
|
|
79
|
+
const settings = readJson(path);
|
|
80
|
+
const hooks = settings?.hooks;
|
|
81
|
+
if (!hooks || typeof hooks !== "object" || Array.isArray(hooks))
|
|
82
|
+
return [];
|
|
83
|
+
const events = Object.entries(hooks).filter(([, groups]) => Array.isArray(groups) &&
|
|
84
|
+
groups.some((group) => (group?.hooks ?? []).some((h) => isProjectHook(h))));
|
|
85
|
+
if (events.length === 0)
|
|
86
|
+
return [];
|
|
87
|
+
const isTracked = tracked(root, ".claude/settings.json");
|
|
88
|
+
return [
|
|
89
|
+
{
|
|
90
|
+
path,
|
|
91
|
+
tracked: isTracked,
|
|
92
|
+
found: `per-repository hook${events.length === 1 ? "" : "s"} (${events.map(([event]) => event).join(", ")})`,
|
|
93
|
+
remove: () => {
|
|
94
|
+
const copy = isTracked ? undefined : backup(path, now);
|
|
95
|
+
const current = readJson(path) ?? {};
|
|
96
|
+
const kept = {};
|
|
97
|
+
for (const [event, groups] of Object.entries((current.hooks ?? {}))) {
|
|
98
|
+
if (!Array.isArray(groups)) {
|
|
99
|
+
kept[event] = groups;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const left = groups.filter((group) => !(group?.hooks ?? []).some((h) => isProjectHook(h)));
|
|
103
|
+
if (left.length > 0)
|
|
104
|
+
kept[event] = left;
|
|
105
|
+
}
|
|
106
|
+
writeJsonAtomically(path, JSON.stringify({ ...current, hooks: kept }, null, 2) + "\n");
|
|
107
|
+
return copy;
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
function claudeServer(root, controlPlane, now) {
|
|
113
|
+
const path = join(root, ".mcp.json");
|
|
114
|
+
if (!regularFile(path))
|
|
115
|
+
return [];
|
|
116
|
+
const config = readJson(path);
|
|
117
|
+
const servers = config?.mcpServers;
|
|
118
|
+
if (!servers || typeof servers !== "object" || Array.isArray(servers))
|
|
119
|
+
return [];
|
|
120
|
+
const entry = servers.balladeer;
|
|
121
|
+
if (entry === undefined || !isOurEntry(entry, controlPlane))
|
|
122
|
+
return [];
|
|
123
|
+
const args = entry.args;
|
|
124
|
+
if (!Array.isArray(args) || !args.includes("--repository"))
|
|
125
|
+
return [];
|
|
126
|
+
const isTracked = tracked(root, ".mcp.json");
|
|
127
|
+
return [
|
|
128
|
+
{
|
|
129
|
+
path,
|
|
130
|
+
tracked: isTracked,
|
|
131
|
+
found: "per-repository server entry",
|
|
132
|
+
remove: () => {
|
|
133
|
+
const copy = isTracked ? undefined : backup(path, now);
|
|
134
|
+
const current = readJson(path) ?? {};
|
|
135
|
+
const remaining = { ...(current.mcpServers ?? {}) };
|
|
136
|
+
delete remaining.balladeer;
|
|
137
|
+
writeJsonAtomically(path, JSON.stringify({ ...current, mcpServers: remaining }, null, 2) + "\n");
|
|
138
|
+
return copy;
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
function codexBlocks(root, now) {
|
|
144
|
+
const path = join(root, ".codex", "config.toml");
|
|
145
|
+
if (!regularFile(path))
|
|
146
|
+
return [];
|
|
147
|
+
const text = readFileSync(path, "utf8");
|
|
148
|
+
const present = CODEX_BLOCKS.filter(([start, end]) => text.includes(`${start}\n`) && text.includes(end));
|
|
149
|
+
if (present.length === 0)
|
|
150
|
+
return [];
|
|
151
|
+
const isTracked = tracked(root, ".codex/config.toml");
|
|
152
|
+
return [
|
|
153
|
+
{
|
|
154
|
+
path,
|
|
155
|
+
tracked: isTracked,
|
|
156
|
+
found: `per-repository Codex block${present.length === 1 ? "" : "s"}`,
|
|
157
|
+
remove: () => {
|
|
158
|
+
const copy = isTracked ? undefined : backup(path, now);
|
|
159
|
+
let after = readFileSync(path, "utf8");
|
|
160
|
+
for (const [start, end] of CODEX_BLOCKS) {
|
|
161
|
+
const from = after.indexOf(`${start}\n`);
|
|
162
|
+
const to = after.indexOf(end, from);
|
|
163
|
+
if (from < 0 || to < 0)
|
|
164
|
+
continue;
|
|
165
|
+
const cut = to + end.length + (after[to + end.length] === "\n" ? 1 : 0);
|
|
166
|
+
after = after.slice(0, from) + after.slice(cut);
|
|
167
|
+
}
|
|
168
|
+
writeJsonAtomically(path, after.replace(/\n{3,}/g, "\n\n"));
|
|
169
|
+
return copy;
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
/** The older per-repository form in the checkout that holds `cwd`, if any. */
|
|
175
|
+
export function findCheckoutForm(input) {
|
|
176
|
+
const root = repositoryRoot(input.cwd);
|
|
177
|
+
if (root === undefined || !existsSync(root))
|
|
178
|
+
return [];
|
|
179
|
+
const now = input.now ?? (() => new Date());
|
|
180
|
+
return [
|
|
181
|
+
...claudeHooks(root, now),
|
|
182
|
+
...claudeServer(root, input.controlPlane, now),
|
|
183
|
+
...codexBlocks(root, now),
|
|
184
|
+
];
|
|
185
|
+
}
|
|
186
|
+
export function removeCheckoutForm(items) {
|
|
187
|
+
const removed = [];
|
|
188
|
+
const failed = [];
|
|
189
|
+
for (const item of items) {
|
|
190
|
+
try {
|
|
191
|
+
const copy = item.remove();
|
|
192
|
+
removed.push({
|
|
193
|
+
path: item.path,
|
|
194
|
+
found: item.found,
|
|
195
|
+
tracked: item.tracked,
|
|
196
|
+
...(copy === undefined ? {} : { copy }),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
failed.push({
|
|
201
|
+
path: item.path,
|
|
202
|
+
found: item.found,
|
|
203
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return { removed, failed };
|
|
208
|
+
}
|
|
209
|
+
export function describeCheckoutFormRemoval(report) {
|
|
210
|
+
const lines = [];
|
|
211
|
+
for (const entry of report.removed)
|
|
212
|
+
lines.push(`Removed the older ${entry.found} from ${entry.path}` +
|
|
213
|
+
(entry.copy
|
|
214
|
+
? ` (copy at ${entry.copy}).`
|
|
215
|
+
: entry.tracked
|
|
216
|
+
? "; git tracks this file, so commit the change."
|
|
217
|
+
: "."));
|
|
218
|
+
for (const entry of report.failed)
|
|
219
|
+
lines.push(`Could not remove the older ${entry.found} from ${entry.path}: ${entry.reason}`);
|
|
220
|
+
if (report.removed.length > 0)
|
|
221
|
+
lines.push("The user-scope hook now speaks alone in this checkout; it was saying the same thing twice.");
|
|
222
|
+
return lines;
|
|
223
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -24,6 +24,7 @@ import { PUBLISHED_SPECIFIER } from "./release.js";
|
|
|
24
24
|
import { installUserScope, runningFromCheckout, userHome, } from "./user-scope.js";
|
|
25
25
|
import { updateNotice } from "./currency.js";
|
|
26
26
|
import { describeEarlier, describeRemoval, findEarlier, removeEarlier } from "./remove-earlier.js";
|
|
27
|
+
import { describeCheckoutFormRemoval, findCheckoutForm, removeCheckoutForm, } from "./checkout-form.js";
|
|
27
28
|
import { StoreError, normalizeControlPlane } from "./store.js";
|
|
28
29
|
import { CLI_INVOCATION, CLI_VERSION, DEFAULT_CONTROL_PLANE } from "./wire.js";
|
|
29
30
|
const USAGE = `balladeer ${CLI_VERSION}
|
|
@@ -471,6 +472,23 @@ function installEverything(options) {
|
|
|
471
472
|
for (const w of writes ?? [])
|
|
472
473
|
if (!options.json)
|
|
473
474
|
options.write(`${w.status === "refused" ? "Not changed" : w.status === "written" ? "Registered" : "Already current"}: ${w.path}${w.reason ? ` (${w.reason})` : ""}\n`);
|
|
475
|
+
// With the user-scope form in place, the older per-repository form in this
|
|
476
|
+
// checkout only makes the session hear the guidance twice. Out it goes, with
|
|
477
|
+
// a copy beside any file git does not already keep.
|
|
478
|
+
if ((writes ?? []).length > 0 && !(writes ?? []).some((w) => w.status === "refused")) {
|
|
479
|
+
const checkoutForm = findCheckoutForm({
|
|
480
|
+
cwd: process.cwd(),
|
|
481
|
+
controlPlane: options.controlPlane,
|
|
482
|
+
});
|
|
483
|
+
if (checkoutForm.length > 0) {
|
|
484
|
+
const report = removeCheckoutForm(checkoutForm);
|
|
485
|
+
if (options.json)
|
|
486
|
+
options.write(`${JSON.stringify({ step: "checkout_form", removed: report.removed, failed: report.failed })}\n`);
|
|
487
|
+
else
|
|
488
|
+
for (const line of describeCheckoutFormRemoval(report))
|
|
489
|
+
options.write(`${line}\n`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
474
492
|
if (!options.json && (writes ?? []).some((w) => w.status === "written"))
|
|
475
493
|
options.write("Balladeer now runs in every Claude Code" +
|
|
476
494
|
((writes ?? []).some((w) => w.host === "codex") ? " and Codex" : "") +
|
|
@@ -522,11 +540,21 @@ async function dispatch(parsed, write) {
|
|
|
522
540
|
return report.failed.length === 0 ? 0 : 6;
|
|
523
541
|
}
|
|
524
542
|
case "install":
|
|
525
|
-
return installEverything({
|
|
543
|
+
return installEverything({
|
|
544
|
+
json: parsed.json,
|
|
545
|
+
write,
|
|
546
|
+
allowPartial: false,
|
|
547
|
+
controlPlane: parsed.controlPlane,
|
|
548
|
+
});
|
|
526
549
|
case "setup":
|
|
527
550
|
return runSetup({
|
|
528
551
|
installCommand: () => {
|
|
529
|
-
const result = installEverything({
|
|
552
|
+
const result = installEverything({
|
|
553
|
+
json: parsed.json,
|
|
554
|
+
write,
|
|
555
|
+
allowPartial: true,
|
|
556
|
+
controlPlane: parsed.controlPlane,
|
|
557
|
+
});
|
|
530
558
|
return process.platform === "win32" ? 0 : result;
|
|
531
559
|
},
|
|
532
560
|
controlPlane: parsed.controlPlane,
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import { type GuidanceEvent } from "../guidance.js";
|
|
2
|
-
/**
|
|
3
|
-
* What an agent, or `status`, is told about a folder Balladeer is not tracking.
|
|
4
|
-
*
|
|
5
|
-
* Robert, 22 September 2026: the session hook says nothing in such a folder.
|
|
6
|
-
* A customer with one repository has every other folder on the laptop
|
|
7
|
-
* untracked, and a line in each of those is noise in the wrong place. This
|
|
8
|
-
* sentence stays for the two readers who ask: `balladeer status` in the folder,
|
|
9
|
-
* and an agent that opens the empty catalog and wonders why.
|
|
10
|
-
*/
|
|
2
|
+
/** A repository-specific setup step for explicit status and empty-catalog responses. */
|
|
11
3
|
export declare function untrackedLine(remote: string): string;
|
|
12
4
|
export type GuidanceOptions = Readonly<{
|
|
13
5
|
controlPlane: string;
|
|
@@ -5,15 +5,7 @@ import { selectAgent } from "../agent.js";
|
|
|
5
5
|
import { repositoryHints } from "../repository.js";
|
|
6
6
|
import { newerVersionPublished } from "../currency.js";
|
|
7
7
|
import { startSelfUpdateIfDue } from "../self-update.js";
|
|
8
|
-
/**
|
|
9
|
-
* What an agent, or `status`, is told about a folder Balladeer is not tracking.
|
|
10
|
-
*
|
|
11
|
-
* Robert, 22 September 2026: the session hook says nothing in such a folder.
|
|
12
|
-
* A customer with one repository has every other folder on the laptop
|
|
13
|
-
* untracked, and a line in each of those is noise in the wrong place. This
|
|
14
|
-
* sentence stays for the two readers who ask: `balladeer status` in the folder,
|
|
15
|
-
* and an agent that opens the empty catalog and wonders why.
|
|
16
|
-
*/
|
|
8
|
+
/** A repository-specific setup step for explicit status and empty-catalog responses. */
|
|
17
9
|
export function untrackedLine(remote) {
|
|
18
10
|
const here = remote === "unknown/unknown" ? "This folder" : `This folder (${remote})`;
|
|
19
11
|
return (`${here} isn't connected to Balladeer on this laptop, so nothing is tracked from here. ` +
|
|
@@ -70,8 +62,7 @@ function identifier(input) {
|
|
|
70
62
|
/** Hook stdout is bounded context only. Host prompts and transcript paths never leave this process. */
|
|
71
63
|
export async function runGuidance(options) {
|
|
72
64
|
// A project hook names its repository and must match the checkout it sits
|
|
73
|
-
// in. A user-scope hook names none: the folder's
|
|
74
|
-
// with no connection hears that it is untracked rather than nothing at all.
|
|
65
|
+
// in. A user-scope hook names none: the folder's remotes decide.
|
|
75
66
|
const projectScoped = options.repositoryId !== undefined;
|
|
76
67
|
if (projectScoped &&
|
|
77
68
|
!validateProjectGuidanceScope({
|
|
@@ -93,17 +84,18 @@ export async function runGuidance(options) {
|
|
|
93
84
|
event = parsedEvent;
|
|
94
85
|
const record = input;
|
|
95
86
|
const credentials = readCredentials(options.environment).agents;
|
|
87
|
+
const repositories = projectScoped ? [] : repositoryHints(options.cwd);
|
|
96
88
|
const agents = projectScoped
|
|
97
89
|
? credentials.filter((agent) => agent.controlPlane === options.controlPlane &&
|
|
98
90
|
agent.repositoryId === options.repositoryId)
|
|
99
91
|
: (() => {
|
|
100
|
-
const selection = selectAgent(credentials, options.controlPlane, undefined,
|
|
92
|
+
const selection = selectAgent(credentials, options.controlPlane, undefined, repositories);
|
|
101
93
|
return selection.kind === "refused" ? [] : [selection.agent];
|
|
102
94
|
})();
|
|
103
95
|
if (agents.length !== 1 || !agents[0]) {
|
|
104
96
|
if (projectScoped)
|
|
105
97
|
throw new Error("guidance_connection_unavailable");
|
|
106
|
-
//
|
|
98
|
+
// Unconnected folders stay silent. An explicit status request explains setup.
|
|
107
99
|
return 0;
|
|
108
100
|
}
|
|
109
101
|
const loaded = await loadGuidance({
|
package/dist/guidance-hook.mjs
CHANGED
|
@@ -2027,7 +2027,7 @@ var require_toml = __commonJS({
|
|
|
2027
2027
|
});
|
|
2028
2028
|
|
|
2029
2029
|
// src/wire.ts
|
|
2030
|
-
var CLI_VERSION = "1.0.
|
|
2030
|
+
var CLI_VERSION = "1.0.14";
|
|
2031
2031
|
var CLIENT_HEADER = "x-balladeer-client";
|
|
2032
2032
|
var CLIENT_HEADER_VALUE = `balladeer/${CLI_VERSION}`;
|
|
2033
2033
|
var DEFAULT_CONTROL_PLANE = "https://envelopes.balladeer.ai";
|
|
@@ -3107,15 +3107,11 @@ async function runGuidance(options) {
|
|
|
3107
3107
|
event = parsedEvent;
|
|
3108
3108
|
const record = input;
|
|
3109
3109
|
const credentials = readCredentials(options.environment).agents;
|
|
3110
|
+
const repositories = projectScoped ? [] : repositoryHints(options.cwd);
|
|
3110
3111
|
const agents = projectScoped ? credentials.filter(
|
|
3111
3112
|
(agent) => agent.controlPlane === options.controlPlane && agent.repositoryId === options.repositoryId
|
|
3112
3113
|
) : (() => {
|
|
3113
|
-
const selection = selectAgent(
|
|
3114
|
-
credentials,
|
|
3115
|
-
options.controlPlane,
|
|
3116
|
-
void 0,
|
|
3117
|
-
repositoryHints(options.cwd)
|
|
3118
|
-
);
|
|
3114
|
+
const selection = selectAgent(credentials, options.controlPlane, void 0, repositories);
|
|
3119
3115
|
return selection.kind === "refused" ? [] : [selection.agent];
|
|
3120
3116
|
})();
|
|
3121
3117
|
if (agents.length !== 1 || !agents[0]) {
|
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.14";
|
|
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.14";
|
|
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[];
|
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.14";
|
|
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}`;
|