@bli-cockpit/cli 0.2.112 → 0.2.113
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/adapters/codex-attribution.js +5 -143
- package/dist/adapters/codex-session-signals.js +161 -0
- package/dist/agent-rules-unmanaged-block.js +97 -0
- package/dist/agent-rules.js +1 -82
- package/dist/commands/local-args-tower-admin.js +8 -151
- package/dist/commands/local-args-tower-settings.js +186 -0
- package/dist/commands/onboard-report-blockers.js +58 -0
- package/dist/commands/onboard-report.js +2 -57
- package/dist/commands/ops-render-ladder.js +25 -0
- package/dist/commands/ops-render.js +7 -0
- package/dist/commands/public-root.js +1 -1
- package/dist/commands/settings-overview.js +94 -0
- package/dist/commands/settings.js +2 -82
- package/dist/onboarding-root-guidance.js +60 -0
- package/dist/onboarding-roots.js +6 -59
- package/package.json +4 -4
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { isInteractiveStdin, readLine, writeLine, yesByDefault } from "./cli-io.js";
|
|
23
23
|
import { asList, asRecord, callTower, isForbidden, openTower, parseModelKey, readAllStdin, writeCommandFailure, } from "./tower-command.js";
|
|
24
|
-
import {
|
|
24
|
+
import { showOverview } from "./settings-overview.js";
|
|
25
|
+
import { renderCliFloor, renderEnvBlobs, renderModelRouting, renderPersonal, renderSwitches, } from "./settings-render.js";
|
|
25
26
|
export async function runSettings(command, io) {
|
|
26
27
|
const tower = await openTower("settings", command, io);
|
|
27
28
|
switch (command.section) {
|
|
@@ -51,87 +52,6 @@ export async function runSettings(command, io) {
|
|
|
51
52
|
return listEnvBlobs(command, tower, io);
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
|
-
// ── overview ──────────────────────────────────────────────────────────
|
|
55
|
-
/**
|
|
56
|
-
* Everything at once, with the sections this person may not see named as such.
|
|
57
|
-
*
|
|
58
|
-
* The five reads go out together: they are independent, and a person waiting on
|
|
59
|
-
* five sequential round trips would reasonably conclude the command had hung.
|
|
60
|
-
*/
|
|
61
|
-
async function showOverview(command, tower, io) {
|
|
62
|
-
const [personal, switches, models, env, team, cliFloor] = await Promise.all([
|
|
63
|
-
callTower(tower, { path: "/api/settings/personal", label: "settings personal" }),
|
|
64
|
-
callTower(tower, { path: "/api/settings/switches", label: "settings switches" }),
|
|
65
|
-
callTower(tower, { path: "/api/settings/model-routing", label: "settings models" }),
|
|
66
|
-
callTower(tower, { path: "/api/settings/env-blobs", label: "settings env" }),
|
|
67
|
-
callTower(tower, { path: "/api/team/members", label: "settings team" }),
|
|
68
|
-
// BLI-3557: the floor rides the overview because "which version is the
|
|
69
|
-
// fleet being pulled to?" is a question people ask about their own laptop,
|
|
70
|
-
// and it went unanswered for weeks precisely because nothing showed it.
|
|
71
|
-
callTower(tower, { path: "/api/settings/cli-floor", label: "settings cli-floor" }),
|
|
72
|
-
]);
|
|
73
|
-
// The one read every signed-in person is entitled to. If THAT is refused,
|
|
74
|
-
// nothing else is going to work either, so say so once and stop.
|
|
75
|
-
if (!personal.ok && !isForbidden(personal)) {
|
|
76
|
-
return writeCommandFailure(io, command.json, personal);
|
|
77
|
-
}
|
|
78
|
-
if (command.json) {
|
|
79
|
-
writeLine(io.stdout, JSON.stringify({
|
|
80
|
-
ok: true,
|
|
81
|
-
personal: sectionPayload(personal),
|
|
82
|
-
switches: sectionPayload(switches),
|
|
83
|
-
models: sectionPayload(models),
|
|
84
|
-
env: sectionPayload(env),
|
|
85
|
-
team: sectionPayload(team),
|
|
86
|
-
cliFloor: sectionPayload(cliFloor),
|
|
87
|
-
}));
|
|
88
|
-
return 0;
|
|
89
|
-
}
|
|
90
|
-
writeLine(io.stdout, "TOWER SETTINGS");
|
|
91
|
-
writeSection(io, "PERSONAL", personal, (body) => renderPersonal(body));
|
|
92
|
-
writeSection(io, "TEAM", team, (body) => renderTeamSummary(body));
|
|
93
|
-
writeSection(io, "SWITCHES", switches, (body) => renderSwitches(body));
|
|
94
|
-
writeSection(io, "MODELS", models, (body) => renderModelRouting(body));
|
|
95
|
-
writeSection(io, "ENV FILES", env, (body) => renderEnvBlobs(body));
|
|
96
|
-
writeSection(io, "FLEET CLI FLOOR", cliFloor, (body) => renderCliFloor(body));
|
|
97
|
-
writeLine(io.stderr, `[settings cli] overview ${JSON.stringify({
|
|
98
|
-
personal: outcome(personal),
|
|
99
|
-
team: outcome(team),
|
|
100
|
-
switches: outcome(switches),
|
|
101
|
-
models: outcome(models),
|
|
102
|
-
env: outcome(env),
|
|
103
|
-
cli_floor: outcome(cliFloor),
|
|
104
|
-
})}`);
|
|
105
|
-
return 0;
|
|
106
|
-
}
|
|
107
|
-
/** One section's heading and body — or the one line that says it is not yours. */
|
|
108
|
-
function writeSection(io, heading, result, render) {
|
|
109
|
-
writeLine(io.stdout, "");
|
|
110
|
-
writeLine(io.stdout, heading);
|
|
111
|
-
if (isForbidden(result)) {
|
|
112
|
-
writeLine(io.stdout, " admin only");
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
if (!result.ok) {
|
|
116
|
-
// Not "admin only" and not shown: a real failure, named where it happened.
|
|
117
|
-
writeLine(io.stdout, ` unavailable — ${result.detail}`);
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
for (const line of render(asRecord(result.body)))
|
|
121
|
-
writeLine(io.stdout, line);
|
|
122
|
-
}
|
|
123
|
-
function sectionPayload(result) {
|
|
124
|
-
if (result.ok)
|
|
125
|
-
return result.body;
|
|
126
|
-
if (isForbidden(result))
|
|
127
|
-
return { visible: false, reason: "admin_only" };
|
|
128
|
-
return { visible: false, reason: result.reason, detail: result.detail };
|
|
129
|
-
}
|
|
130
|
-
function outcome(result) {
|
|
131
|
-
if (result.ok)
|
|
132
|
-
return "ok";
|
|
133
|
-
return isForbidden(result) ? "admin_only" : result.reason;
|
|
134
|
-
}
|
|
135
55
|
// ── personal ──────────────────────────────────────────────────────────
|
|
136
56
|
async function showPersonal(command, tower, io) {
|
|
137
57
|
const result = await callTower(tower, {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a folder cannot be a collection root, and exactly what we say about it.
|
|
3
|
+
*
|
|
4
|
+
* Split out of onboarding-roots.ts so the file that RESOLVES a root reads as
|
|
5
|
+
* the conversation, and this one holds the sentences. Every string here is
|
|
6
|
+
* read by a person mid-install and matched verbatim by tests: change the
|
|
7
|
+
* wording only on purpose.
|
|
8
|
+
*/
|
|
9
|
+
import os from "node:os";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
export function homeRootConsentPrompt(homeDirInput) {
|
|
12
|
+
const homeDir = path.resolve(homeDirInput ?? os.homedir());
|
|
13
|
+
return `You're in your home folder (${homeDir}).\n Sync ALL projects on this machine? Every git repo under here gets captured now and in the future.\n This is a work machine — exclude personal projects yourself if needed.\n Press Enter to sync everything, or answer n to name one folder instead. [Y/n]: `;
|
|
14
|
+
}
|
|
15
|
+
export const HOME_ROOT_DECLINE_PATH_PROMPT = "Okay — which folder should I sync? Enter the full path to your work directory: ";
|
|
16
|
+
export const HOME_ROOT_NO_FOLDER_CHOSEN_MESSAGE = "No folder chosen. Re-run: cockpit onboard --workspace <path-to-your-work-folder>";
|
|
17
|
+
export function rootRejectionExplanation(rejection, options = {}) {
|
|
18
|
+
switch (rejection.reason) {
|
|
19
|
+
case "home_dir":
|
|
20
|
+
return homeRootTutorial(options.homeDir);
|
|
21
|
+
case "fs_root":
|
|
22
|
+
return filesystemRootTutorial(options.homeDir);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function homeRootTutorial(homeDirInput) {
|
|
26
|
+
const homeDir = path.resolve(homeDirInput ?? os.homedir());
|
|
27
|
+
const bliRoot = path.join(homeDir, "BLI");
|
|
28
|
+
const otherRoot = path.join(homeDir, "other");
|
|
29
|
+
return [
|
|
30
|
+
"Your home folder can't be a collection root by default:",
|
|
31
|
+
` scanning all of ${homeDir} would include personal folders and every repo you ever create.`,
|
|
32
|
+
"What you can do:",
|
|
33
|
+
` 1) Specific folders: cockpit onboard --workspace "${bliRoot},${otherRoot}"`,
|
|
34
|
+
" 2) One work parent: mkdir ~/BLI && move your repos in, then: cockpit onboard --workspace ~/BLI",
|
|
35
|
+
" 3) Collect everything anyway (company machine): rerun with --allow-home-root",
|
|
36
|
+
].join("\n");
|
|
37
|
+
}
|
|
38
|
+
function filesystemRootTutorial(homeDirInput) {
|
|
39
|
+
const homeDir = path.resolve(homeDirInput ?? os.homedir());
|
|
40
|
+
const bliRoot = path.join(homeDir, "BLI");
|
|
41
|
+
const otherRoot = path.join(homeDir, "other");
|
|
42
|
+
return [
|
|
43
|
+
"Your filesystem root can't be a collection root:",
|
|
44
|
+
` scanning all of ${path.parse(homeDir).root} would include system folders, private data, and every mounted repo.`,
|
|
45
|
+
"What you can do:",
|
|
46
|
+
` 1) Specific folders: cockpit onboard --workspace "${bliRoot},${otherRoot}"`,
|
|
47
|
+
" 2) One work parent: mkdir ~/BLI && move your repos in, then: cockpit onboard --workspace ~/BLI",
|
|
48
|
+
].join("\n");
|
|
49
|
+
}
|
|
50
|
+
export function missingCollectionRootMessage(options = {}) {
|
|
51
|
+
const homeDir = path.resolve(options.homeDir ?? os.homedir());
|
|
52
|
+
return [
|
|
53
|
+
"No collection root was confirmed:",
|
|
54
|
+
" Tower only scans folders you name or confirm.",
|
|
55
|
+
"What you can do:",
|
|
56
|
+
` 1) Paste one work parent: ${path.join(homeDir, "BLI")}`,
|
|
57
|
+
` 2) Paste specific folders: ${path.join(homeDir, "repo-a")},${path.join(homeDir, "repo-b")}`,
|
|
58
|
+
" 3) Stop and rerun with a flag: cockpit onboard --workspace ~/BLI",
|
|
59
|
+
].join("\n");
|
|
60
|
+
}
|
package/dist/onboarding-roots.js
CHANGED
|
@@ -2,7 +2,12 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { HOME_ROOT_DECLINE_PATH_PROMPT, HOME_ROOT_NO_FOLDER_CHOSEN_MESSAGE, homeRootConsentPrompt, homeRootTutorial, missingCollectionRootMessage, rootRejectionExplanation, } from "./onboarding-root-guidance.js";
|
|
5
6
|
import { isSamePath, normalizeCollectionRoots, } from "./root-normalization.js";
|
|
7
|
+
// Every sentence this file says to a person lives next door, with the reasons
|
|
8
|
+
// it says them. Re-exported here because this is the address callers and tests
|
|
9
|
+
// have always imported them from.
|
|
10
|
+
export { HOME_ROOT_DECLINE_PATH_PROMPT, HOME_ROOT_NO_FOLDER_CHOSEN_MESSAGE, homeRootConsentPrompt, missingCollectionRootMessage, rootRejectionExplanation, } from "./onboarding-root-guidance.js";
|
|
6
11
|
export const COLLECTION_ROOT_REQUIRED = "collection_root_required";
|
|
7
12
|
/**
|
|
8
13
|
* No approved collection root, so there is nothing this machine may look at.
|
|
@@ -21,12 +26,6 @@ export class CollectionRootRequiredError extends Error {
|
|
|
21
26
|
this.name = "CollectionRootRequiredError";
|
|
22
27
|
}
|
|
23
28
|
}
|
|
24
|
-
export function homeRootConsentPrompt(homeDirInput) {
|
|
25
|
-
const homeDir = path.resolve(homeDirInput ?? os.homedir());
|
|
26
|
-
return `You're in your home folder (${homeDir}).\n Sync ALL projects on this machine? Every git repo under here gets captured now and in the future.\n This is a work machine — exclude personal projects yourself if needed.\n Press Enter to sync everything, or answer n to name one folder instead. [Y/n]: `;
|
|
27
|
-
}
|
|
28
|
-
export const HOME_ROOT_DECLINE_PATH_PROMPT = "Okay — which folder should I sync? Enter the full path to your work directory: ";
|
|
29
|
-
export const HOME_ROOT_NO_FOLDER_CHOSEN_MESSAGE = "No folder chosen. Re-run: cockpit onboard --workspace <path-to-your-work-folder>";
|
|
30
29
|
export async function resolveOnboardingRoots(options) {
|
|
31
30
|
const explicitInput = options.explicitRoots ?? [];
|
|
32
31
|
const explicit = normalizeRootsDetailed(explicitInput, {
|
|
@@ -207,28 +206,12 @@ function explainRejectedRoots(options, rejections) {
|
|
|
207
206
|
return;
|
|
208
207
|
const prompt = requirePrompt(options);
|
|
209
208
|
for (const rejection of rejections) {
|
|
210
|
-
prompt.message?.(
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
export function rootRejectionExplanation(rejection, options = {}) {
|
|
214
|
-
switch (rejection.reason) {
|
|
215
|
-
case "home_dir":
|
|
216
|
-
return homeRootTutorial(options.homeDir);
|
|
217
|
-
case "fs_root":
|
|
218
|
-
return filesystemRootTutorial(options.homeDir);
|
|
209
|
+
prompt.message?.(rootRejectionExplanation(rejection, options));
|
|
219
210
|
}
|
|
220
211
|
}
|
|
221
212
|
function withoutHomeRejections(rejections) {
|
|
222
213
|
return rejections.filter((rejection) => rejection.reason !== "home_dir");
|
|
223
214
|
}
|
|
224
|
-
function rootRejectionPromptHint(rejection, options = {}) {
|
|
225
|
-
switch (rejection.reason) {
|
|
226
|
-
case "home_dir":
|
|
227
|
-
return homeRootTutorial(options.homeDir);
|
|
228
|
-
case "fs_root":
|
|
229
|
-
return filesystemRootTutorial(options.homeDir);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
215
|
async function promptForHomeRootOptIn(options, rejections) {
|
|
233
216
|
if (!rejections.some((rejection) => rejection.reason === "home_dir"))
|
|
234
217
|
return null;
|
|
@@ -324,42 +307,6 @@ function canonicalPath(input, pathApi = path, realpath = realpathSync) {
|
|
|
324
307
|
function isHomeRoot(root, homeDir) {
|
|
325
308
|
return isSamePath(canonicalPath(root), canonicalPath(homeDir ?? os.homedir()));
|
|
326
309
|
}
|
|
327
|
-
function homeRootTutorial(homeDirInput) {
|
|
328
|
-
const homeDir = path.resolve(homeDirInput ?? os.homedir());
|
|
329
|
-
const bliRoot = path.join(homeDir, "BLI");
|
|
330
|
-
const otherRoot = path.join(homeDir, "other");
|
|
331
|
-
return [
|
|
332
|
-
"Your home folder can't be a collection root by default:",
|
|
333
|
-
` scanning all of ${homeDir} would include personal folders and every repo you ever create.`,
|
|
334
|
-
"What you can do:",
|
|
335
|
-
` 1) Specific folders: cockpit onboard --workspace "${bliRoot},${otherRoot}"`,
|
|
336
|
-
" 2) One work parent: mkdir ~/BLI && move your repos in, then: cockpit onboard --workspace ~/BLI",
|
|
337
|
-
" 3) Collect everything anyway (company machine): rerun with --allow-home-root",
|
|
338
|
-
].join("\n");
|
|
339
|
-
}
|
|
340
|
-
function filesystemRootTutorial(homeDirInput) {
|
|
341
|
-
const homeDir = path.resolve(homeDirInput ?? os.homedir());
|
|
342
|
-
const bliRoot = path.join(homeDir, "BLI");
|
|
343
|
-
const otherRoot = path.join(homeDir, "other");
|
|
344
|
-
return [
|
|
345
|
-
"Your filesystem root can't be a collection root:",
|
|
346
|
-
` scanning all of ${path.parse(homeDir).root} would include system folders, private data, and every mounted repo.`,
|
|
347
|
-
"What you can do:",
|
|
348
|
-
` 1) Specific folders: cockpit onboard --workspace "${bliRoot},${otherRoot}"`,
|
|
349
|
-
" 2) One work parent: mkdir ~/BLI && move your repos in, then: cockpit onboard --workspace ~/BLI",
|
|
350
|
-
].join("\n");
|
|
351
|
-
}
|
|
352
|
-
export function missingCollectionRootMessage(options = {}) {
|
|
353
|
-
const homeDir = path.resolve(options.homeDir ?? os.homedir());
|
|
354
|
-
return [
|
|
355
|
-
"No collection root was confirmed:",
|
|
356
|
-
" Tower only scans folders you name or confirm.",
|
|
357
|
-
"What you can do:",
|
|
358
|
-
` 1) Paste one work parent: ${path.join(homeDir, "BLI")}`,
|
|
359
|
-
` 2) Paste specific folders: ${path.join(homeDir, "repo-a")},${path.join(homeDir, "repo-b")}`,
|
|
360
|
-
" 3) Stop and rerun with a flag: cockpit onboard --workspace ~/BLI",
|
|
361
|
-
].join("\n");
|
|
362
|
-
}
|
|
363
310
|
export function likelyBliRootFromCwd(cwd, pathApi = path) {
|
|
364
311
|
const root = pathApi.parse(cwd).root;
|
|
365
312
|
const parts = pathApi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.113",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"test": "node dist/cli.js --help && node ../../scripts/assert-public-cli-routing.mjs && node ../../scripts/assert-public-cli-verb-help.mjs && node ../../scripts/assert-public-cli-runtime-files.mjs && node ../../scripts/assert-public-cli-exit-contract.mjs && node ../../scripts/assert-public-cli-no-fleet-posts.mjs && node ../../scripts/assert-public-package-pack.mjs --workspace=@bli-cockpit/cli"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@bli-cockpit/memory-mcp": "0.1.
|
|
31
|
-
"@bli-cockpit/mcp": "0.1.
|
|
32
|
-
"@bli-cockpit/telemetry-core": "0.1.
|
|
30
|
+
"@bli-cockpit/memory-mcp": "0.1.31",
|
|
31
|
+
"@bli-cockpit/mcp": "0.1.41",
|
|
32
|
+
"@bli-cockpit/telemetry-core": "0.1.47"
|
|
33
33
|
}
|
|
34
34
|
}
|