@coderook/cli 0.38.2 → 0.40.0
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/.claude-plugin/plugin.json +1 -1
- package/README.md +24 -0
- package/dist/cli/src/api.js +48 -16
- package/dist/cli/src/cli.js +395 -124
- package/dist/cli/src/community_command.js +54 -1
- package/dist/cli/src/help.js +21 -6
- package/dist/cli/src/issue_command.js +171 -5
- package/dist/cli/src/machine.js +80 -0
- package/dist/cli/src/project_commands.js +29 -26
- package/dist/cli/src/registry.js +32 -8
- package/dist/cli/src/service_commands.js +154 -0
- package/dist/cli/src/settings_commands.js +532 -0
- package/dist/cli/src/workspace_command.js +3 -0
- package/dist/cli/src/workspace_machine.js +322 -0
- package/package.json +1 -1
- package/skills/coderook/SKILL.md +4 -0
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.commandFork = commandFork;
|
|
4
7
|
exports.commandSecurity = commandSecurity;
|
|
@@ -12,7 +15,10 @@ exports.commandSecurity = commandSecurity;
|
|
|
12
15
|
*
|
|
13
16
|
* cbx security report sam/editor --title "…" [--severity high] [--body "…"]
|
|
14
17
|
* cbx security list | view S-3 | reply S-3 -m "…" | mark S-3 fixed
|
|
18
|
+
* cbx security policy [--on | --off] [--text <file or text>] [--clear]
|
|
15
19
|
*/
|
|
20
|
+
const node_fs_1 = require("node:fs");
|
|
21
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
16
22
|
const api_js_1 = require("./api.js");
|
|
17
23
|
const colour_js_1 = require("./colour.js");
|
|
18
24
|
const project_commands_js_1 = require("./project_commands.js");
|
|
@@ -86,6 +92,8 @@ async function commandSecurity(parsed) {
|
|
|
86
92
|
const project = await (0, project_commands_js_1.resolveProject)(flagText(parsed, "project"));
|
|
87
93
|
if (!project)
|
|
88
94
|
return 1;
|
|
95
|
+
if (sub === "policy")
|
|
96
|
+
return securityPolicy(parsed, project);
|
|
89
97
|
const base = `/v1/repositories/${encodeURIComponent(project.id)}/security-reports`;
|
|
90
98
|
if (sub === "list") {
|
|
91
99
|
const { reports } = await (0, api_js_1.callApi)(base);
|
|
@@ -131,6 +139,51 @@ async function commandSecurity(parsed) {
|
|
|
131
139
|
console.log((0, colour_js_1.green)(`S-${number} is ${state}. Whoever reported it is told.`));
|
|
132
140
|
return 0;
|
|
133
141
|
}
|
|
134
|
-
console.error((0, colour_js_1.red)(`cbx security has no "${sub}". `) + "Try: report, list, view, reply, mark");
|
|
142
|
+
console.error((0, colour_js_1.red)(`cbx security has no "${sub}". `) + "Try: report, list, view, reply, mark, policy");
|
|
135
143
|
return 1;
|
|
136
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* `cbx security policy` — whether the project takes private reports, and what
|
|
147
|
+
* it tells people who want to send one. The website's Security section.
|
|
148
|
+
*
|
|
149
|
+
* --text takes a file when one by that name exists, because a policy is a
|
|
150
|
+
* page of prose and nobody wants to type it as an argument; anything else is
|
|
151
|
+
* used as the text itself.
|
|
152
|
+
*/
|
|
153
|
+
async function securityPolicy(parsed, project) {
|
|
154
|
+
const route = `/v1/repositories/${encodeURIComponent(project.id)}/security-policy`;
|
|
155
|
+
const on = parsed.flags.has("on");
|
|
156
|
+
const off = parsed.flags.has("off");
|
|
157
|
+
if (on && off) {
|
|
158
|
+
console.error((0, colour_js_1.red)("Pick one: --on or --off."));
|
|
159
|
+
return 1;
|
|
160
|
+
}
|
|
161
|
+
const change = {};
|
|
162
|
+
if (on || off)
|
|
163
|
+
change.enabled = on;
|
|
164
|
+
if (parsed.flags.has("clear"))
|
|
165
|
+
change.policy = null;
|
|
166
|
+
const given = parsed.flags.get("text");
|
|
167
|
+
if (given !== undefined) {
|
|
168
|
+
if (typeof given !== "string" || !given.trim()) {
|
|
169
|
+
console.error((0, colour_js_1.red)("What should it say? --text SECURITY.md, or --text \"Email security@…\""));
|
|
170
|
+
return 1;
|
|
171
|
+
}
|
|
172
|
+
const file = node_path_1.default.resolve(given);
|
|
173
|
+
change.policy = (0, node_fs_1.existsSync)(file) && (0, node_fs_1.statSync)(file).isFile() ? (0, node_fs_1.readFileSync)(file, "utf8") : given;
|
|
174
|
+
}
|
|
175
|
+
if (Object.keys(change).length) {
|
|
176
|
+
await (0, api_js_1.callApi)(route, { method: "PUT", body: change });
|
|
177
|
+
console.log((0, colour_js_1.green)(`Saved ${project.name}'s security policy.`));
|
|
178
|
+
}
|
|
179
|
+
const policy = await (0, api_js_1.callApi)(route);
|
|
180
|
+
console.log(`${(0, colour_js_1.bold)(project.name)} ${policy.enabled ? (0, colour_js_1.green)("takes private vulnerability reports") : (0, colour_js_1.dim)("does not take private vulnerability reports")}`);
|
|
181
|
+
if (policy.policy?.trim()) {
|
|
182
|
+
console.log("");
|
|
183
|
+
console.log(policy.policy.trim());
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
console.log((0, colour_js_1.dim)(" No reporting policy written. Add one: cbx security policy --text SECURITY.md"));
|
|
187
|
+
}
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
package/dist/cli/src/help.js
CHANGED
|
@@ -15,33 +15,48 @@
|
|
|
15
15
|
* afford whole sentences and examples.
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.listedUsage = exports.USAGE_COLUMN = void 0;
|
|
18
19
|
exports.renderHelp = renderHelp;
|
|
19
20
|
exports.renderCommandHelp = renderCommandHelp;
|
|
20
21
|
exports.renderUnknown = renderUnknown;
|
|
21
22
|
const registry_js_1 = require("./registry.js");
|
|
22
23
|
const colour_js_1 = require("./colour.js");
|
|
23
|
-
/**
|
|
24
|
+
/** The widest the usage column may be; a longer usage needs a `brief`. */
|
|
25
|
+
exports.USAGE_COLUMN = 30;
|
|
26
|
+
/** What the command list shows in the usage column. */
|
|
27
|
+
const listedUsage = (spec) => spec.brief ?? spec.usage;
|
|
28
|
+
exports.listedUsage = listedUsage;
|
|
29
|
+
/** Longest visible usage, so the summary column lines up across every group. */
|
|
24
30
|
function nameColumn(specs) {
|
|
25
|
-
return specs.reduce((widest, spec) => Math.max(widest, spec.
|
|
31
|
+
return specs.reduce((widest, spec) => Math.max(widest, (0, exports.listedUsage)(spec).length), 0);
|
|
32
|
+
}
|
|
33
|
+
/** Where a command sits inside its group: by LISTING_ORDER, then as declared. */
|
|
34
|
+
function rank(spec, declared) {
|
|
35
|
+
const at = registry_js_1.LISTING_ORDER.indexOf(spec.name);
|
|
36
|
+
return at === -1 ? registry_js_1.LISTING_ORDER.length + declared : at;
|
|
26
37
|
}
|
|
27
38
|
function renderHelp(registry, version) {
|
|
28
39
|
const listed = registry.specs.filter((spec) => !spec.deprecatedBy);
|
|
29
|
-
const column = Math.min(nameColumn(listed),
|
|
40
|
+
const column = Math.min(nameColumn(listed), exports.USAGE_COLUMN);
|
|
30
41
|
const lines = [
|
|
31
42
|
`${(0, colour_js_1.bold)("cbx")} ${(0, colour_js_1.dim)(version)} — the CodeBox engine, for CodeRook`,
|
|
32
43
|
"",
|
|
33
44
|
];
|
|
34
45
|
for (const group of registry_js_1.GROUP_ORDER) {
|
|
35
|
-
const inGroup = listed
|
|
46
|
+
const inGroup = listed
|
|
47
|
+
.map((spec, declared) => ({ spec, order: rank(spec, declared) }))
|
|
48
|
+
.filter(({ spec }) => spec.group === group)
|
|
49
|
+
.sort((a, b) => a.order - b.order)
|
|
50
|
+
.map(({ spec }) => spec);
|
|
36
51
|
if (!inGroup.length)
|
|
37
52
|
continue;
|
|
38
53
|
lines.push((0, colour_js_1.bold)(group));
|
|
39
54
|
for (const spec of inGroup) {
|
|
40
|
-
lines.push(` ${spec.
|
|
55
|
+
lines.push(` ${(0, exports.listedUsage)(spec).padEnd(column)} ${spec.summary}`);
|
|
41
56
|
}
|
|
42
57
|
lines.push("");
|
|
43
58
|
}
|
|
44
|
-
lines.push((0, colour_js_1.dim)("cbx help <command>
|
|
59
|
+
lines.push((0, colour_js_1.dim)("cbx help <command> the full usage, options and examples for one command"), (0, colour_js_1.dim)("CODEROOK_TOKEN used instead of the stored sign-in, for automated runs"), (0, colour_js_1.dim)("CODEROOK_API_URL point at another service"));
|
|
45
60
|
return lines.join("\n");
|
|
46
61
|
}
|
|
47
62
|
function renderCommandHelp(spec) {
|
|
@@ -11,7 +11,10 @@ exports.commandIssue = commandIssue;
|
|
|
11
11
|
* cbx issue label 12 bug,ui [--remove]
|
|
12
12
|
* cbx issue assign 12 sam | none
|
|
13
13
|
* cbx issue labels [--starter] the project's labels
|
|
14
|
-
* cbx issue
|
|
14
|
+
* cbx issue labels add|change|remove <name> [--colour #hex] [--description …] [--name …]
|
|
15
|
+
* cbx issue rules the project's issue rules, numbered
|
|
16
|
+
* cbx issue rules add <auto-label|auto-assign|welcome|stale> [settings]
|
|
17
|
+
* cbx issue rules on|off|change|remove <n>
|
|
15
18
|
*
|
|
16
19
|
* Listing and opening issues stays with `cbx issues` and `cbx issues --new`.
|
|
17
20
|
* Inside a run, CODEROOK_TOKEN and CODEROOK_PROJECT make these work with no
|
|
@@ -20,6 +23,155 @@ exports.commandIssue = commandIssue;
|
|
|
20
23
|
const api_js_1 = require("./api.js");
|
|
21
24
|
const colour_js_1 = require("./colour.js");
|
|
22
25
|
const project_commands_js_1 = require("./project_commands.js");
|
|
26
|
+
const RULE_KINDS = ["auto_label", "auto_assign", "welcome", "stale"];
|
|
27
|
+
function flagValue(parsed, ...names) {
|
|
28
|
+
for (const name of names) {
|
|
29
|
+
const value = parsed.flags.get(name);
|
|
30
|
+
if (typeof value === "string")
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* `cbx issue labels add|change|remove <name>`. Answers 0 when the list should
|
|
37
|
+
* follow, as it does after a change.
|
|
38
|
+
*/
|
|
39
|
+
async function changeLabel(parsed, base, action) {
|
|
40
|
+
const name = parsed.positional[2]?.trim();
|
|
41
|
+
if (!["add", "change", "remove"].includes(action)) {
|
|
42
|
+
console.error((0, colour_js_1.red)(`cbx issue labels has no "${action}". `) + "Try: add, change, remove");
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
if (!name) {
|
|
46
|
+
console.error((0, colour_js_1.red)(`Which label? cbx issue labels ${action} bug`));
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
const route = `${base}/issue-labels/${encodeURIComponent(name)}`;
|
|
50
|
+
if (action === "remove") {
|
|
51
|
+
await (0, api_js_1.callApi)(route, { method: "DELETE" });
|
|
52
|
+
console.log((0, colour_js_1.green)(`Removed the label ${name}. Issues that had it keep everything else.`));
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
const { labels } = await (0, api_js_1.callApi)(`${base}/issue-labels`);
|
|
56
|
+
const exists = labels.some((label) => label.name === name);
|
|
57
|
+
/*
|
|
58
|
+
The service creates a label it does not have and changes one it does,
|
|
59
|
+
through the same request. Asked first here so a typo in `change` is not
|
|
60
|
+
quietly a new label, and `add` of an existing one is not a quiet edit.
|
|
61
|
+
*/
|
|
62
|
+
if (action === "add" && exists) {
|
|
63
|
+
console.error((0, colour_js_1.red)(`There is already a label called ${name}. Change it: cbx issue labels change ${name} --colour d73a4a`));
|
|
64
|
+
return 1;
|
|
65
|
+
}
|
|
66
|
+
if (action === "change" && !exists) {
|
|
67
|
+
console.error((0, colour_js_1.red)(`No label called ${name}. Add it: cbx issue labels add ${name}`));
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
const change = {};
|
|
71
|
+
const rename = flagValue(parsed, "name");
|
|
72
|
+
const colour = flagValue(parsed, "colour", "color");
|
|
73
|
+
const description = flagValue(parsed, "description");
|
|
74
|
+
if (rename !== undefined)
|
|
75
|
+
change.name = rename.trim();
|
|
76
|
+
if (colour !== undefined)
|
|
77
|
+
change.colour = colour.trim().startsWith("#") ? colour.trim() : `#${colour.trim()}`;
|
|
78
|
+
if (description !== undefined)
|
|
79
|
+
change.description = description.trim() || null;
|
|
80
|
+
if (action === "change" && !Object.keys(change).length) {
|
|
81
|
+
console.error((0, colour_js_1.red)("Change what? --name, --colour or --description"));
|
|
82
|
+
return 1;
|
|
83
|
+
}
|
|
84
|
+
await (0, api_js_1.callApi)(route, { method: "PUT", body: change });
|
|
85
|
+
console.log((0, colour_js_1.green)(action === "add" ? `Added the label ${name}.` : `Changed the label ${name}${change.name && change.name !== name ? ` (now ${change.name})` : ""}.`));
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
/** A rule's settings from the flags, over what it had. */
|
|
89
|
+
function ruleConfig(parsed, kind, current) {
|
|
90
|
+
const config = { ...current };
|
|
91
|
+
const list = (value) => value.split(",").map((one) => one.trim()).filter(Boolean);
|
|
92
|
+
const words = flagValue(parsed, "words");
|
|
93
|
+
const label = flagValue(parsed, "label");
|
|
94
|
+
const where = flagValue(parsed, "in");
|
|
95
|
+
const assignees = flagValue(parsed, "assignees");
|
|
96
|
+
const message = flagValue(parsed, "m", "message");
|
|
97
|
+
const days = flagValue(parsed, "days");
|
|
98
|
+
const closeAfter = flagValue(parsed, "close-after");
|
|
99
|
+
const exempt = flagValue(parsed, "exempt");
|
|
100
|
+
if (kind === "auto_label") {
|
|
101
|
+
if (words !== undefined)
|
|
102
|
+
config.words = list(words);
|
|
103
|
+
if (label !== undefined)
|
|
104
|
+
config.label = label.trim();
|
|
105
|
+
if (where !== undefined)
|
|
106
|
+
config.in = where.trim();
|
|
107
|
+
}
|
|
108
|
+
else if (kind === "auto_assign") {
|
|
109
|
+
if (assignees !== undefined)
|
|
110
|
+
config.assignees = list(assignees).map((one) => one.replace(/^@/, ""));
|
|
111
|
+
if (label !== undefined)
|
|
112
|
+
config.label = label.trim() || null;
|
|
113
|
+
}
|
|
114
|
+
else if (kind === "welcome") {
|
|
115
|
+
if (message !== undefined)
|
|
116
|
+
config.message = message;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
if (days !== undefined)
|
|
120
|
+
config.days = Number(days);
|
|
121
|
+
if (closeAfter !== undefined)
|
|
122
|
+
config.closeAfterDays = Number(closeAfter);
|
|
123
|
+
if (label !== undefined)
|
|
124
|
+
config.label = label.trim();
|
|
125
|
+
if (message !== undefined)
|
|
126
|
+
config.message = message;
|
|
127
|
+
if (exempt !== undefined)
|
|
128
|
+
config.exemptLabels = list(exempt);
|
|
129
|
+
}
|
|
130
|
+
return config;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* `cbx issue rules add <kind> …`, and `on|off|change|remove <n>`, where n is
|
|
134
|
+
* the number the list shows (or the rule's id). Answers 0 when the list
|
|
135
|
+
* should follow.
|
|
136
|
+
*/
|
|
137
|
+
async function changeRule(parsed, base, action) {
|
|
138
|
+
if (action === "add") {
|
|
139
|
+
const kind = (parsed.positional[2] ?? "").replace("-", "_");
|
|
140
|
+
if (!RULE_KINDS.includes(kind)) {
|
|
141
|
+
console.error((0, colour_js_1.red)("Which kind? auto-label, auto-assign, welcome or stale."));
|
|
142
|
+
return 1;
|
|
143
|
+
}
|
|
144
|
+
await (0, api_js_1.callApi)(`${base}/issue-rules`, { method: "POST", body: { kind, config: ruleConfig(parsed, kind, {}) } });
|
|
145
|
+
console.log((0, colour_js_1.green)(`Added a ${kind.replace("_", "-")} rule.`));
|
|
146
|
+
return 0;
|
|
147
|
+
}
|
|
148
|
+
if (!["on", "off", "change", "remove"].includes(action)) {
|
|
149
|
+
console.error((0, colour_js_1.red)(`cbx issue rules has no "${action}". `) + "Try: add, on, off, change, remove");
|
|
150
|
+
return 1;
|
|
151
|
+
}
|
|
152
|
+
const wanted = (parsed.positional[2] ?? "").replace(/^#/, "").trim();
|
|
153
|
+
const { rules } = await (0, api_js_1.callApi)(`${base}/issue-rules`);
|
|
154
|
+
const index = /^\d+$/.test(wanted) ? Number(wanted) - 1 : -1;
|
|
155
|
+
const rule = rules[index] ?? rules.find((one) => one.id === wanted);
|
|
156
|
+
if (!rule) {
|
|
157
|
+
console.error((0, colour_js_1.red)(wanted ? `No rule ${wanted}. cbx issue rules lists them, numbered.` : `Which rule? cbx issue rules ${action} 2`));
|
|
158
|
+
return 1;
|
|
159
|
+
}
|
|
160
|
+
const route = `${base}/issue-rules/${encodeURIComponent(rule.id)}`;
|
|
161
|
+
if (action === "remove") {
|
|
162
|
+
await (0, api_js_1.callApi)(route, { method: "DELETE" });
|
|
163
|
+
console.log((0, colour_js_1.green)(`Removed the ${rule.kind.replace("_", "-")} rule.`));
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
if (action === "change") {
|
|
167
|
+
await (0, api_js_1.callApi)(route, { method: "PATCH", body: { config: ruleConfig(parsed, rule.kind, rule.config) } });
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
await (0, api_js_1.callApi)(route, { method: "PATCH", body: { enabled: action === "on" } });
|
|
171
|
+
}
|
|
172
|
+
console.log((0, colour_js_1.green)(action === "change" ? `Changed the ${rule.kind.replace("_", "-")} rule.` : `The ${rule.kind.replace("_", "-")} rule is ${action}.`));
|
|
173
|
+
return 0;
|
|
174
|
+
}
|
|
23
175
|
function text(parsed) {
|
|
24
176
|
const message = parsed.flags.get("m") ?? parsed.flags.get("message");
|
|
25
177
|
return typeof message === "string" && message.trim() ? message : null;
|
|
@@ -58,6 +210,12 @@ async function commandIssue(parsed) {
|
|
|
58
210
|
return 1;
|
|
59
211
|
const base = `/v1/repositories/${encodeURIComponent(project.id)}`;
|
|
60
212
|
if (sub === "labels") {
|
|
213
|
+
const action = parsed.positional[1];
|
|
214
|
+
if (action) {
|
|
215
|
+
const changed = await changeLabel(parsed, base, action);
|
|
216
|
+
if (changed !== 0)
|
|
217
|
+
return changed;
|
|
218
|
+
}
|
|
61
219
|
if (parsed.flags.has("starter")) {
|
|
62
220
|
const { added } = await (0, api_js_1.callApi)(`${base}/issue-labels/starter`, { method: "POST" });
|
|
63
221
|
console.log((0, colour_js_1.green)(added ? `Added ${added} labels.` : "The usual labels are already there."));
|
|
@@ -73,14 +231,22 @@ async function commandIssue(parsed) {
|
|
|
73
231
|
return 0;
|
|
74
232
|
}
|
|
75
233
|
if (sub === "rules") {
|
|
234
|
+
const action = parsed.positional[1];
|
|
235
|
+
if (action) {
|
|
236
|
+
const changed = await changeRule(parsed, base, action);
|
|
237
|
+
if (changed !== 0)
|
|
238
|
+
return changed;
|
|
239
|
+
}
|
|
76
240
|
const { rules } = await (0, api_js_1.callApi)(`${base}/issue-rules`);
|
|
77
241
|
if (!rules.length) {
|
|
78
|
-
console.log(`${(0, colour_js_1.bold)(project.slug)} has no issue rules. ${(0, colour_js_1.dim)(
|
|
242
|
+
console.log(`${(0, colour_js_1.bold)(project.slug)} has no issue rules. ${(0, colour_js_1.dim)('Add one: cbx issue rules add welcome -m "Thanks for the report!"')}`);
|
|
79
243
|
return 0;
|
|
80
244
|
}
|
|
81
|
-
|
|
82
|
-
console.log(` ${rule.enabled ? (0, colour_js_1.green)("on ") : (0, colour_js_1.dim)("off")} ${(0, colour_js_1.accent)(rule.kind.padEnd(12))} ${(0, colour_js_1.dim)(JSON.stringify(rule.config))}`);
|
|
83
|
-
}
|
|
245
|
+
rules.forEach((rule, index) => {
|
|
246
|
+
console.log(` ${(0, colour_js_1.accent)(String(index + 1).padStart(2))} ${rule.enabled ? (0, colour_js_1.green)("on ") : (0, colour_js_1.dim)("off")} ${(0, colour_js_1.accent)(rule.kind.padEnd(12))} ${(0, colour_js_1.dim)(JSON.stringify(rule.config))}`);
|
|
247
|
+
});
|
|
248
|
+
if (!action)
|
|
249
|
+
console.log((0, colour_js_1.dim)("\n Change one by its number: cbx issue rules off 2, cbx issue rules remove 2"));
|
|
84
250
|
return 0;
|
|
85
251
|
}
|
|
86
252
|
const number = Number((parsed.positional[1] ?? "").replace(/^#/, ""));
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.machineIdentity = machineIdentity;
|
|
7
|
+
exports.checkIn = checkIn;
|
|
8
|
+
/**
|
|
9
|
+
* This machine, as a project's Actions page shows it (backend machines.ts).
|
|
10
|
+
*
|
|
11
|
+
* Each machine gets a random id the first time cbx runs there, kept in its
|
|
12
|
+
* settings folder, and is named after the computer. When cbx works in a
|
|
13
|
+
* folder linked to a project, or on a draft handed to this machine, it says
|
|
14
|
+
* so: which version it holds, which line it saves to, how many files it has
|
|
15
|
+
* not saved. Only the folder's name goes, never its path.
|
|
16
|
+
*
|
|
17
|
+
* Saying so is best effort. It never delays a command by more than a moment,
|
|
18
|
+
* never fails one, and is skipped when CODEROOK_NO_PRESENCE is set.
|
|
19
|
+
*/
|
|
20
|
+
const node_crypto_1 = require("node:crypto");
|
|
21
|
+
const promises_1 = require("node:fs/promises");
|
|
22
|
+
const node_os_1 = require("node:os");
|
|
23
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
24
|
+
const identify_js_1 = require("../../cbx/src/core/identify.js");
|
|
25
|
+
const config_js_1 = require("./config.js");
|
|
26
|
+
let cached = null;
|
|
27
|
+
/** This machine's id and name, made once and kept. */
|
|
28
|
+
async function machineIdentity() {
|
|
29
|
+
if (cached)
|
|
30
|
+
return cached;
|
|
31
|
+
const file = node_path_1.default.join((0, config_js_1.configDirectory)(), "machine.json");
|
|
32
|
+
let id = "";
|
|
33
|
+
try {
|
|
34
|
+
const saved = JSON.parse(await (0, promises_1.readFile)(file, "utf8"));
|
|
35
|
+
if (typeof saved.id === "string" && /^[A-Za-z0-9_-]{8,64}$/.test(saved.id))
|
|
36
|
+
id = saved.id;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
/* First run on this machine, or the file was removed. */
|
|
40
|
+
}
|
|
41
|
+
if (!id) {
|
|
42
|
+
id = (0, node_crypto_1.randomBytes)(12).toString("base64url");
|
|
43
|
+
await (0, promises_1.mkdir)(node_path_1.default.dirname(file), { recursive: true }).catch(() => undefined);
|
|
44
|
+
await (0, promises_1.writeFile)(file, JSON.stringify({ id }, null, 2) + "\n").catch(() => undefined);
|
|
45
|
+
}
|
|
46
|
+
cached = { id, name: (process.env.CODEROOK_MACHINE_NAME || (0, node_os_1.hostname)() || "This machine").slice(0, 120) };
|
|
47
|
+
return cached;
|
|
48
|
+
}
|
|
49
|
+
/** Tell the project this machine is here. Never throws, and gives up after a moment. */
|
|
50
|
+
async function checkIn(repositoryId, state) {
|
|
51
|
+
if (!repositoryId || process.env.CODEROOK_NO_PRESENCE)
|
|
52
|
+
return;
|
|
53
|
+
try {
|
|
54
|
+
const token = await (0, config_js_1.loadToken)();
|
|
55
|
+
if (!token)
|
|
56
|
+
return;
|
|
57
|
+
const me = await machineIdentity();
|
|
58
|
+
const client = (0, identify_js_1.clientHeaders)()["x-coderook-client"] ?? "";
|
|
59
|
+
await fetch(`${(0, config_js_1.apiOrigin)()}/v1/repositories/${encodeURIComponent(repositoryId)}/machines`, {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/json", ...(0, identify_js_1.clientHeaders)() },
|
|
62
|
+
body: JSON.stringify({
|
|
63
|
+
machineId: me.id,
|
|
64
|
+
name: me.name,
|
|
65
|
+
kind: state.kind,
|
|
66
|
+
platform: `${(0, node_os_1.platform)()} ${(0, node_os_1.release)()}`.slice(0, 80),
|
|
67
|
+
clientVersion: client.startsWith("cli/") ? client.slice(4) : null,
|
|
68
|
+
folderName: state.folder ? node_path_1.default.basename(node_path_1.default.resolve(state.folder)) : null,
|
|
69
|
+
track: state.track ?? null,
|
|
70
|
+
baseVersionId: state.baseVersionId ?? null,
|
|
71
|
+
changedFiles: state.changedFiles ?? null,
|
|
72
|
+
workspaceId: state.workspaceId ?? null,
|
|
73
|
+
}),
|
|
74
|
+
signal: AbortSignal.timeout(2500),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
/* Offline, signed out or slow: the command's own work is what matters. */
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.resolveProject = resolveProject;
|
|
16
|
+
exports.aiChangeAttempted = aiChangeAttempted;
|
|
16
17
|
exports.commandAi = commandAi;
|
|
17
18
|
exports.readLevel = readLevel;
|
|
18
19
|
exports.splitVisibility = splitVisibility;
|
|
@@ -83,45 +84,47 @@ const AI_SWITCHES = [
|
|
|
83
84
|
],
|
|
84
85
|
];
|
|
85
86
|
/**
|
|
86
|
-
*
|
|
87
|
+
* The flags `cbx ai` used to take, named when somebody types one.
|
|
87
88
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
|
|
89
|
+
* Returned rather than printed so the refusal can be checked without a
|
|
90
|
+
* service: it must happen before anything is asked of one.
|
|
91
|
+
*/
|
|
92
|
+
function aiChangeAttempted(parsed) {
|
|
93
|
+
return AI_SWITCHES.map(([name]) => name).filter((name) => parsed.flags.has(name));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Show what a project says about machines.
|
|
97
|
+
*
|
|
98
|
+
* Read only. What AI may do with a project is changed by a person, on the
|
|
99
|
+
* website or in the desktop app, and the service refuses the change from any
|
|
100
|
+
* token — which is what this tool, the MCP servers, runs and assistants all
|
|
101
|
+
* hold. An assistant able to switch off the rule that keeps it out would make
|
|
102
|
+
* the rule decoration, so the old switches are refused here, plainly, before
|
|
103
|
+
* anything is sent.
|
|
91
104
|
*/
|
|
92
105
|
async function commandAi(parsed) {
|
|
106
|
+
const attempted = aiChangeAttempted(parsed);
|
|
107
|
+
if (attempted.length) {
|
|
108
|
+
console.error((0, colour_js_1.red)(`AI access cannot be changed from the command line (${attempted.map((name) => "--" + name).join(", ")}).`));
|
|
109
|
+
console.error("A person changes it on the website, in the project's Settings → Access & AI, or in the desktop app.");
|
|
110
|
+
console.error((0, colour_js_1.dim)(" cbx ai shows what it is set to now."));
|
|
111
|
+
return 1;
|
|
112
|
+
}
|
|
93
113
|
const project = await resolveProject(parsed.positional[0]);
|
|
94
114
|
if (!project)
|
|
95
115
|
return 1;
|
|
96
|
-
const change = {};
|
|
97
|
-
for (const [name, field] of AI_SWITCHES) {
|
|
98
|
-
const value = parsed.flags.get(name);
|
|
99
|
-
if (value === undefined)
|
|
100
|
-
continue;
|
|
101
|
-
/*
|
|
102
|
-
`--read off` and `--read=false` both read naturally, and a bare `--read`
|
|
103
|
-
means on, because that is what a flag without a value means everywhere
|
|
104
|
-
else in this tool.
|
|
105
|
-
*/
|
|
106
|
-
const text = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
107
|
-
change[field] =
|
|
108
|
-
value === true ? true : !["off", "false", "no", "n", "0"].includes(text);
|
|
109
|
-
}
|
|
110
|
-
if (Object.keys(change).length) {
|
|
111
|
-
await (0, api_js_1.setAiAccess)(project.id, change);
|
|
112
|
-
console.log((0, colour_js_1.green)(`Updated ${project.name}.`));
|
|
113
|
-
}
|
|
114
116
|
const access = await (0, api_js_1.aiAccess)(project.id);
|
|
115
117
|
console.log("");
|
|
116
118
|
console.log((0, colour_js_1.bold)(project.name));
|
|
117
|
-
for (const [
|
|
119
|
+
for (const [, field, question, meaning] of AI_SWITCHES) {
|
|
118
120
|
const mark = access[field] ? (0, colour_js_1.green)("allowed") : (0, colour_js_1.red)("refused");
|
|
119
|
-
console.log(` ${question.padEnd(30)} ${mark}
|
|
121
|
+
console.log(` ${question.padEnd(30)} ${mark}`);
|
|
120
122
|
console.log(` ${(0, colour_js_1.dim)(meaning)}`);
|
|
121
123
|
}
|
|
122
124
|
console.log("");
|
|
123
|
-
console.log((0, colour_js_1.dim)("Everything is allowed until
|
|
124
|
-
console.log((0, colour_js_1.dim)("
|
|
125
|
+
console.log((0, colour_js_1.dim)("Everything is allowed until somebody says otherwise. Reading off is the one"));
|
|
126
|
+
console.log((0, colour_js_1.dim)("that refuses model training."));
|
|
127
|
+
console.log((0, colour_js_1.dim)("Change these on the website (Settings → Access & AI) or in the desktop app."));
|
|
125
128
|
return 0;
|
|
126
129
|
}
|
|
127
130
|
/**
|
package/dist/cli/src/registry.js
CHANGED
|
@@ -12,20 +12,44 @@
|
|
|
12
12
|
* command added without a summary is a type error rather than an omission.
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.GROUP_ORDER = void 0;
|
|
15
|
+
exports.LISTING_ORDER = exports.GROUP_ORDER = void 0;
|
|
16
16
|
exports.buildRegistry = buildRegistry;
|
|
17
17
|
exports.nearestCommand = nearestCommand;
|
|
18
18
|
/** The order groups appear in help, which is the order somebody meets them. */
|
|
19
19
|
exports.GROUP_ORDER = [
|
|
20
20
|
"Getting started",
|
|
21
|
-
"
|
|
21
|
+
"Saving work",
|
|
22
22
|
"History on this machine",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"Actions",
|
|
28
|
-
"
|
|
23
|
+
"Lines, merges and pull requests",
|
|
24
|
+
"Versions and releases",
|
|
25
|
+
"Project settings",
|
|
26
|
+
"Issues",
|
|
27
|
+
"Actions and builds",
|
|
28
|
+
"Workspaces",
|
|
29
|
+
"Mods and bundles",
|
|
30
|
+
"For assistants",
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* The order commands appear inside their group in `cbx help`.
|
|
34
|
+
*
|
|
35
|
+
* Kept apart from where the commands are declared, which follows the history
|
|
36
|
+
* of the file rather than what somebody looks for first. A command missing
|
|
37
|
+
* from this list still appears, after the ones that are in it.
|
|
38
|
+
*/
|
|
39
|
+
exports.LISTING_ORDER = [
|
|
40
|
+
"sign-in", "whoami", "sign-out", "doctor", "projects", "clone", "link", "unlink", "import", "transfer",
|
|
41
|
+
"status", "submit", "diff", "get", "track", "ignore", "licence",
|
|
42
|
+
"init", "save", "log", "restore", "switch", "push", "pull",
|
|
43
|
+
"tracks", "line-rules", "merges", "merge", "propose", "proposals", "pr",
|
|
44
|
+
"versions", "mark", "notes", "labels", "promote", "release", "releases", "attach", "held", "review", "undo",
|
|
45
|
+
"take-down", "verify", "deps",
|
|
46
|
+
"about", "visibility", "ai", "private", "upload-rules", "people", "watch", "hooks", "secret", "assistant",
|
|
47
|
+
"security", "pages", "fork", "hand-over", "delete",
|
|
48
|
+
"issues", "issue",
|
|
49
|
+
"actions", "runs", "logs", "cancel", "rerun", "approve", "runner", "build", "machines",
|
|
50
|
+
"workspace",
|
|
51
|
+
"mods", "bundle", "unbundle", "inspect",
|
|
52
|
+
"mcp", "skill", "tokens",
|
|
29
53
|
];
|
|
30
54
|
function buildRegistry(specs) {
|
|
31
55
|
const lookup = new Map();
|