@bli-cockpit/cli 0.2.54 → 0.2.56
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/attribution-core-fallbacks.js +247 -0
- package/dist/adapters/attribution-core-paths.js +182 -0
- package/dist/adapters/attribution-core-score.js +159 -0
- package/dist/adapters/attribution-core-types.js +13 -0
- package/dist/adapters/attribution-core.js +13 -565
- package/dist/adapters/claude-attribution-discovery.js +186 -0
- package/dist/adapters/claude-attribution-score.js +204 -0
- package/dist/adapters/claude-attribution-signals.js +180 -0
- package/dist/adapters/claude-attribution-types.js +25 -0
- package/dist/adapters/claude-attribution.js +14 -569
- package/dist/commands/doctor-access.js +129 -0
- package/dist/commands/doctor-pipeline.js +326 -0
- package/dist/commands/doctor-registration.js +105 -0
- package/dist/commands/doctor-report.js +111 -0
- package/dist/commands/doctor-update.js +120 -0
- package/dist/commands/doctor.js +8 -753
- package/dist/commands/heartbeat.js +8 -0
- package/dist/commands/jarvis-contracts.js +8 -0
- package/dist/commands/jarvis-render.js +413 -0
- package/dist/commands/jarvis-turn.js +305 -0
- package/dist/commands/jarvis.js +23 -698
- package/dist/commands/local-args-collector-setup.js +250 -0
- package/dist/commands/local-args-collector-status.js +227 -0
- package/dist/commands/local-args-collector-work.js +175 -0
- package/dist/commands/local-args-collector.js +19 -624
- package/dist/commands/local-args-tower-admin.js +456 -0
- package/dist/commands/local-args-tower-chat.js +194 -0
- package/dist/commands/local-args-tower-pages.js +314 -0
- package/dist/commands/local-args-tower.js +13 -880
- package/dist/commands/local-help.js +10 -2
- package/dist/commands/onboard-completion.js +136 -0
- package/dist/commands/onboard-flows.js +165 -0
- package/dist/commands/onboard-setup.js +102 -0
- package/dist/commands/onboard.js +5 -392
- package/dist/commands/public-root.js +1 -1
- package/dist/commands/session-sync-counters.js +55 -0
- package/dist/commands/session-sync-health.js +8 -1
- package/dist/commands/session-sync-plan.js +47 -7
- package/dist/commands/session-sync-scan.js +4 -4
- package/dist/commands/session-sync.js +6 -0
- package/dist/commands/settings-render.js +27 -0
- package/dist/commands/sync-followups.js +5 -1
- package/dist/commands/sync.js +5 -1
- package/dist/commands/team-device-reasons.js +16 -0
- package/dist/commands/team.js +87 -7
- package/dist/evidence-upload-client.js +14 -763
- package/dist/evidence-upload-object.js +181 -0
- package/dist/evidence-upload-plan.js +233 -0
- package/dist/evidence-upload-terminal.js +309 -0
- package/dist/evidence-upload-transport.js +104 -0
- package/dist/spool/local-spool-io.js +122 -0
- package/dist/spool/local-spool-mutations.js +174 -0
- package/dist/spool/local-spool-parse.js +143 -0
- package/dist/spool/local-spool-types.js +22 -0
- package/dist/spool/local-spool.js +20 -426
- package/dist/upload-evidence-delivery-offer.js +144 -0
- package/dist/upload-evidence-delivery-reconcile.js +134 -0
- package/dist/upload-evidence-delivery-summary.js +205 -0
- package/dist/upload-evidence-delivery.js +12 -482
- package/package.json +3 -3
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The six human "set my machine up" doors (`install`, `onboard`, `login`,
|
|
3
|
+
* `pair`, `update`, `upgrade`), `do-everything`/`fix`, `logout`, and the
|
|
4
|
+
* maintainer's `release`. Split out of local-args-collector.ts (BLI-3636).
|
|
5
|
+
*
|
|
6
|
+
* `OnboardLikeOptions` — the flag set the setup doors share — lives here with
|
|
7
|
+
* the parser that produces it and is imported back by `local-args.ts` for the
|
|
8
|
+
* `LocalCommand` union. It has never been part of that module's export
|
|
9
|
+
* surface and still is not.
|
|
10
|
+
*
|
|
11
|
+
* Every parser moved verbatim: same allowed flags, same value flags, same
|
|
12
|
+
* defaults, same refusal sentences.
|
|
13
|
+
*/
|
|
14
|
+
import { DEFAULT_DASHBOARD_URL } from "../local-state.js";
|
|
15
|
+
import { assertNoPositionals, normalizeUrl, optionalEmail, optionalNonEmpty, optionalNonEmptyList, optionalPositiveInteger, optionalUrl, parseNamedArgs, workRootFlagValue, workRootFlagValues, } from "./local-arg-values.js";
|
|
16
|
+
function parseOnboardLikeArgs(args, command) {
|
|
17
|
+
const values = parseNamedArgs(args, {
|
|
18
|
+
allowedFlags: [
|
|
19
|
+
"--home",
|
|
20
|
+
"--repo",
|
|
21
|
+
"--workspace",
|
|
22
|
+
"--dashboard-url",
|
|
23
|
+
"--email",
|
|
24
|
+
"--device-name",
|
|
25
|
+
"--ticket",
|
|
26
|
+
"--branch",
|
|
27
|
+
"--json",
|
|
28
|
+
"--no-auth",
|
|
29
|
+
"--poll-interval-ms",
|
|
30
|
+
"--timeout-ms",
|
|
31
|
+
"--max-depth",
|
|
32
|
+
"--max-repos",
|
|
33
|
+
"--allow-home-root",
|
|
34
|
+
],
|
|
35
|
+
valueFlags: [
|
|
36
|
+
"--home",
|
|
37
|
+
"--repo",
|
|
38
|
+
"--workspace",
|
|
39
|
+
"--dashboard-url",
|
|
40
|
+
"--email",
|
|
41
|
+
"--device-name",
|
|
42
|
+
"--ticket",
|
|
43
|
+
"--branch",
|
|
44
|
+
"--poll-interval-ms",
|
|
45
|
+
"--timeout-ms",
|
|
46
|
+
"--max-depth",
|
|
47
|
+
"--max-repos",
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
assertNoPositionals(values.positionals, command);
|
|
51
|
+
return {
|
|
52
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
53
|
+
repoRoot: optionalNonEmpty(workRootFlagValue(values)),
|
|
54
|
+
collectionRoots: optionalNonEmptyList(workRootFlagValues(values)),
|
|
55
|
+
dashboardUrl: normalizeUrl(values.flags.get("--dashboard-url") ?? DEFAULT_DASHBOARD_URL),
|
|
56
|
+
dashboardUrlExplicit: values.flags.has("--dashboard-url"),
|
|
57
|
+
claimedOwnerEmail: optionalEmail(values.flags.get("--email")),
|
|
58
|
+
deviceName: optionalNonEmpty(values.flags.get("--device-name")),
|
|
59
|
+
activeTicketId: optionalNonEmpty(values.flags.get("--ticket")),
|
|
60
|
+
branch: optionalNonEmpty(values.flags.get("--branch")),
|
|
61
|
+
noAuth: values.booleans.has("--no-auth"),
|
|
62
|
+
json: values.booleans.has("--json"),
|
|
63
|
+
pollIntervalMs: optionalPositiveInteger(values.flags.get("--poll-interval-ms"), "--poll-interval-ms"),
|
|
64
|
+
timeoutMs: optionalPositiveInteger(values.flags.get("--timeout-ms"), "--timeout-ms"),
|
|
65
|
+
maxDepth: optionalPositiveInteger(values.flags.get("--max-depth"), "--max-depth"),
|
|
66
|
+
maxRepos: optionalPositiveInteger(values.flags.get("--max-repos"), "--max-repos"),
|
|
67
|
+
allowHomeRoot: values.booleans.has("--allow-home-root"),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export function parseOnboardArgs(args) {
|
|
71
|
+
return { kind: "onboard", ...parseOnboardLikeArgs(args, "onboard") };
|
|
72
|
+
}
|
|
73
|
+
export function parseUpdateArgs(alias, args) {
|
|
74
|
+
return {
|
|
75
|
+
kind: "update",
|
|
76
|
+
alias,
|
|
77
|
+
...parseOnboardLikeArgs(args, alias),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function parseDoctorArgs(alias, args) {
|
|
81
|
+
// The allowed set is the UNION of what the six setup doors used to accept.
|
|
82
|
+
// An alias that silently rejected a flag its own docs told people to pass
|
|
83
|
+
// would be a worse dead end than the one we are removing.
|
|
84
|
+
const values = parseNamedArgs(args, {
|
|
85
|
+
allowedFlags: [
|
|
86
|
+
"--home",
|
|
87
|
+
"--repo",
|
|
88
|
+
"--workspace",
|
|
89
|
+
"--dashboard-url",
|
|
90
|
+
"--update-tag",
|
|
91
|
+
"--dry-run",
|
|
92
|
+
"--json",
|
|
93
|
+
"--allow-home-root",
|
|
94
|
+
"--max-depth",
|
|
95
|
+
"--max-repos",
|
|
96
|
+
// Accepted and ignored: the convergence run works these out itself.
|
|
97
|
+
// Rejecting them would break existing DMs and runbooks for no gain.
|
|
98
|
+
"--email",
|
|
99
|
+
"--device-name",
|
|
100
|
+
"--ticket",
|
|
101
|
+
"--branch",
|
|
102
|
+
"--no-auth",
|
|
103
|
+
"--poll-interval-ms",
|
|
104
|
+
"--timeout-ms",
|
|
105
|
+
],
|
|
106
|
+
valueFlags: [
|
|
107
|
+
"--home",
|
|
108
|
+
"--repo",
|
|
109
|
+
"--workspace",
|
|
110
|
+
"--dashboard-url",
|
|
111
|
+
"--update-tag",
|
|
112
|
+
"--max-depth",
|
|
113
|
+
"--max-repos",
|
|
114
|
+
"--email",
|
|
115
|
+
"--device-name",
|
|
116
|
+
"--ticket",
|
|
117
|
+
"--branch",
|
|
118
|
+
"--poll-interval-ms",
|
|
119
|
+
"--timeout-ms",
|
|
120
|
+
],
|
|
121
|
+
});
|
|
122
|
+
assertNoPositionals(values.positionals, alias);
|
|
123
|
+
const updateTag = optionalNonEmpty(values.flags.get("--update-tag"));
|
|
124
|
+
if (updateTag && !/^[a-z0-9][a-z0-9._-]*$/u.test(updateTag)) {
|
|
125
|
+
throw new Error("--update-tag must be a lowercase npm dist-tag such as 'next' or 'latest'.");
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
kind: "doctor",
|
|
129
|
+
alias,
|
|
130
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
131
|
+
repoRoot: optionalNonEmpty(workRootFlagValue(values)),
|
|
132
|
+
collectionRoots: optionalNonEmptyList(workRootFlagValues(values)),
|
|
133
|
+
dashboardUrl: normalizeUrl(values.flags.get("--dashboard-url") ?? DEFAULT_DASHBOARD_URL),
|
|
134
|
+
updateTag,
|
|
135
|
+
dryRun: values.booleans.has("--dry-run"),
|
|
136
|
+
json: values.booleans.has("--json"),
|
|
137
|
+
allowHomeRoot: values.booleans.has("--allow-home-root"),
|
|
138
|
+
maxDepth: optionalPositiveInteger(values.flags.get("--max-depth"), "--max-depth"),
|
|
139
|
+
maxRepos: optionalPositiveInteger(values.flags.get("--max-repos"), "--max-repos"),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export function parseInstallArgs(args) {
|
|
143
|
+
const values = parseNamedArgs(args, {
|
|
144
|
+
allowedFlags: [
|
|
145
|
+
"--home",
|
|
146
|
+
"--repo",
|
|
147
|
+
"--workspace",
|
|
148
|
+
"--dashboard-url",
|
|
149
|
+
"--supabase-url",
|
|
150
|
+
"--json",
|
|
151
|
+
"--allow-home-root",
|
|
152
|
+
],
|
|
153
|
+
valueFlags: [
|
|
154
|
+
"--home",
|
|
155
|
+
"--repo",
|
|
156
|
+
"--workspace",
|
|
157
|
+
"--dashboard-url",
|
|
158
|
+
"--supabase-url",
|
|
159
|
+
],
|
|
160
|
+
});
|
|
161
|
+
assertNoPositionals(values.positionals, "install");
|
|
162
|
+
return {
|
|
163
|
+
kind: "install",
|
|
164
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
165
|
+
repoRoot: optionalNonEmpty(workRootFlagValue(values)),
|
|
166
|
+
dashboardUrl: normalizeUrl(values.flags.get("--dashboard-url") ?? DEFAULT_DASHBOARD_URL),
|
|
167
|
+
supabaseUrl: optionalNonEmpty(values.flags.get("--supabase-url")),
|
|
168
|
+
json: values.booleans.has("--json"),
|
|
169
|
+
allowHomeRoot: values.booleans.has("--allow-home-root"),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export function parseReleaseArgs(args) {
|
|
173
|
+
const values = parseNamedArgs(args, {
|
|
174
|
+
allowedFlags: [
|
|
175
|
+
"--dry-run",
|
|
176
|
+
"--tag",
|
|
177
|
+
"--access",
|
|
178
|
+
"--otp",
|
|
179
|
+
"--skip-checks",
|
|
180
|
+
// BLI-3557: publish without raising the fleet forced-update floor. The
|
|
181
|
+
// maintainer path must be able to say that, or the staged rollout only
|
|
182
|
+
// exists for whoever runs the npm script directly.
|
|
183
|
+
"--no-floor",
|
|
184
|
+
],
|
|
185
|
+
valueFlags: ["--tag", "--access", "--otp"],
|
|
186
|
+
});
|
|
187
|
+
assertNoPositionals(values.positionals, "release");
|
|
188
|
+
const releaseArgs = [];
|
|
189
|
+
if (values.booleans.has("--dry-run"))
|
|
190
|
+
releaseArgs.push("--dry-run");
|
|
191
|
+
if (values.booleans.has("--skip-checks"))
|
|
192
|
+
releaseArgs.push("--skip-checks");
|
|
193
|
+
if (values.booleans.has("--no-floor"))
|
|
194
|
+
releaseArgs.push("--no-floor");
|
|
195
|
+
for (const flag of ["--tag", "--access", "--otp"]) {
|
|
196
|
+
const value = values.flags.get(flag);
|
|
197
|
+
if (value !== undefined)
|
|
198
|
+
releaseArgs.push(flag, value);
|
|
199
|
+
}
|
|
200
|
+
return { kind: "release", args: releaseArgs };
|
|
201
|
+
}
|
|
202
|
+
export function parseLoginArgs(args) {
|
|
203
|
+
const values = parseNamedArgs(args, {
|
|
204
|
+
allowedFlags: [
|
|
205
|
+
"--home",
|
|
206
|
+
"--dashboard-url",
|
|
207
|
+
"--email",
|
|
208
|
+
"--device-name",
|
|
209
|
+
"--json",
|
|
210
|
+
"--no-auth",
|
|
211
|
+
"--poll-interval-ms",
|
|
212
|
+
"--timeout-ms",
|
|
213
|
+
],
|
|
214
|
+
valueFlags: [
|
|
215
|
+
"--home",
|
|
216
|
+
"--dashboard-url",
|
|
217
|
+
"--email",
|
|
218
|
+
"--device-name",
|
|
219
|
+
"--poll-interval-ms",
|
|
220
|
+
"--timeout-ms",
|
|
221
|
+
],
|
|
222
|
+
});
|
|
223
|
+
assertNoPositionals(values.positionals, "login");
|
|
224
|
+
return {
|
|
225
|
+
kind: "login",
|
|
226
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
227
|
+
dashboardUrl: optionalUrl(values.flags.get("--dashboard-url")),
|
|
228
|
+
claimedOwnerEmail: optionalEmail(values.flags.get("--email")),
|
|
229
|
+
deviceName: optionalNonEmpty(values.flags.get("--device-name")),
|
|
230
|
+
json: values.booleans.has("--json"),
|
|
231
|
+
noAuth: values.booleans.has("--no-auth"),
|
|
232
|
+
pollIntervalMs: optionalPositiveInteger(values.flags.get("--poll-interval-ms"), "--poll-interval-ms"),
|
|
233
|
+
timeoutMs: optionalPositiveInteger(values.flags.get("--timeout-ms"), "--timeout-ms"),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
export function parseLogoutArgs(args) {
|
|
237
|
+
const values = parseNamedArgs(args, {
|
|
238
|
+
allowedFlags: ["--home", "--json", "--revoke", "--dashboard-url", "--yes"],
|
|
239
|
+
valueFlags: ["--home", "--dashboard-url"],
|
|
240
|
+
});
|
|
241
|
+
assertNoPositionals(values.positionals, "logout");
|
|
242
|
+
return {
|
|
243
|
+
kind: "logout",
|
|
244
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
245
|
+
json: values.booleans.has("--json"),
|
|
246
|
+
revoke: values.booleans.has("--revoke"),
|
|
247
|
+
dashboardUrl: optionalUrl(values.flags.get("--dashboard-url")),
|
|
248
|
+
yes: values.booleans.has("--yes"),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The commands that read or maintain an already-collecting machine rather
|
|
3
|
+
* than converge new data: `cockpit status`, `cockpit sessions`,
|
|
4
|
+
* `cockpit serve`, `cockpit autostart`, `cockpit agent-rules`,
|
|
5
|
+
* `cockpit memory`, and `cockpit clean`. Split out of local-args-collector.ts
|
|
6
|
+
* (BLI-3636).
|
|
7
|
+
*
|
|
8
|
+
* Every parser moved verbatim: same allowed flags, same value flags, same
|
|
9
|
+
* defaults, same refusal sentences.
|
|
10
|
+
*/
|
|
11
|
+
import { DEFAULT_AUTOSTART_INTERVAL_SECONDS } from "../autostart.js";
|
|
12
|
+
import { assertNoPositionals, normalizeUrl, optionalNonEmpty, optionalPositiveInteger, optionalUrl, parseNamedArgs, workRootFlagValue, } from "./local-arg-values.js";
|
|
13
|
+
import { DEFAULT_DASHBOARD_URL } from "../local-state.js";
|
|
14
|
+
export function parseStatusArgs(args) {
|
|
15
|
+
const values = parseNamedArgs(args, {
|
|
16
|
+
allowedFlags: [
|
|
17
|
+
"--home",
|
|
18
|
+
"--repo",
|
|
19
|
+
"--workspace",
|
|
20
|
+
"--json",
|
|
21
|
+
"--max-depth",
|
|
22
|
+
"--max-repos",
|
|
23
|
+
],
|
|
24
|
+
valueFlags: [
|
|
25
|
+
"--home",
|
|
26
|
+
"--repo",
|
|
27
|
+
"--workspace",
|
|
28
|
+
"--max-depth",
|
|
29
|
+
"--max-repos",
|
|
30
|
+
],
|
|
31
|
+
});
|
|
32
|
+
assertNoPositionals(values.positionals, "status");
|
|
33
|
+
return {
|
|
34
|
+
kind: "status",
|
|
35
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
36
|
+
repoRoot: optionalNonEmpty(workRootFlagValue(values)),
|
|
37
|
+
json: values.booleans.has("--json"),
|
|
38
|
+
maxDepth: optionalPositiveInteger(values.flags.get("--max-depth"), "--max-depth"),
|
|
39
|
+
maxRepos: optionalPositiveInteger(values.flags.get("--max-repos"), "--max-repos"),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function parseSessionsArgs(args) {
|
|
43
|
+
const values = parseNamedArgs(args, {
|
|
44
|
+
allowedFlags: [
|
|
45
|
+
"--home",
|
|
46
|
+
"--repo",
|
|
47
|
+
"--workspace",
|
|
48
|
+
"--source",
|
|
49
|
+
"--since-days",
|
|
50
|
+
"--all",
|
|
51
|
+
"--json",
|
|
52
|
+
"--max-depth",
|
|
53
|
+
"--max-repos",
|
|
54
|
+
],
|
|
55
|
+
valueFlags: [
|
|
56
|
+
"--home",
|
|
57
|
+
"--repo",
|
|
58
|
+
"--workspace",
|
|
59
|
+
"--source",
|
|
60
|
+
"--since-days",
|
|
61
|
+
"--max-depth",
|
|
62
|
+
"--max-repos",
|
|
63
|
+
],
|
|
64
|
+
});
|
|
65
|
+
assertNoPositionals(values.positionals, "sessions");
|
|
66
|
+
const source = values.flags.get("--source");
|
|
67
|
+
if (source !== undefined && source !== "codex" && source !== "claude") {
|
|
68
|
+
throw new Error("--source must be 'codex' or 'claude'. Omit it to scan both.");
|
|
69
|
+
}
|
|
70
|
+
const sinceDays = optionalPositiveInteger(values.flags.get("--since-days"), "--since-days");
|
|
71
|
+
const all = values.booleans.has("--all");
|
|
72
|
+
if (all && sinceDays !== undefined) {
|
|
73
|
+
throw new Error("--since-days and --all cannot be combined.");
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
kind: "sessions",
|
|
77
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
78
|
+
repoRoot: optionalNonEmpty(workRootFlagValue(values)),
|
|
79
|
+
source,
|
|
80
|
+
sinceDays,
|
|
81
|
+
all,
|
|
82
|
+
json: values.booleans.has("--json"),
|
|
83
|
+
maxDepth: optionalPositiveInteger(values.flags.get("--max-depth"), "--max-depth"),
|
|
84
|
+
maxRepos: optionalPositiveInteger(values.flags.get("--max-repos"), "--max-repos"),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export function parseServeArgs(args) {
|
|
88
|
+
const values = parseNamedArgs(args, {
|
|
89
|
+
allowedFlags: ["--home", "--repo", "--workspace", "--port"],
|
|
90
|
+
valueFlags: ["--home", "--repo", "--workspace", "--port"],
|
|
91
|
+
});
|
|
92
|
+
assertNoPositionals(values.positionals, "serve");
|
|
93
|
+
const port = Number(values.flags.get("--port") ?? "4174");
|
|
94
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
95
|
+
throw new Error("--port must be a TCP port between 1 and 65535.");
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
kind: "serve",
|
|
99
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
100
|
+
repoRoot: optionalNonEmpty(workRootFlagValue(values)),
|
|
101
|
+
port,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export function parseAutostartArgs(args) {
|
|
105
|
+
const values = parseNamedArgs(args, {
|
|
106
|
+
allowedFlags: [
|
|
107
|
+
"--home",
|
|
108
|
+
"--repo",
|
|
109
|
+
"--workspace",
|
|
110
|
+
"--dashboard-url",
|
|
111
|
+
"--interval-seconds",
|
|
112
|
+
"--parent-pid",
|
|
113
|
+
"--json",
|
|
114
|
+
],
|
|
115
|
+
valueFlags: [
|
|
116
|
+
"--home",
|
|
117
|
+
"--repo",
|
|
118
|
+
"--workspace",
|
|
119
|
+
"--dashboard-url",
|
|
120
|
+
"--interval-seconds",
|
|
121
|
+
"--parent-pid",
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
if (values.positionals.length > 1) {
|
|
125
|
+
throw new Error("autostart accepts at most one action (install|uninstall|status).");
|
|
126
|
+
}
|
|
127
|
+
const action = values.positionals[0] ?? "install";
|
|
128
|
+
if (action !== "install" &&
|
|
129
|
+
action !== "uninstall" &&
|
|
130
|
+
action !== "status" &&
|
|
131
|
+
action !== "heal-detached") {
|
|
132
|
+
throw new Error("autostart action must be install, uninstall, or status.");
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
kind: "autostart",
|
|
136
|
+
action,
|
|
137
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
138
|
+
repoRoot: optionalNonEmpty(workRootFlagValue(values)),
|
|
139
|
+
dashboardUrl: normalizeUrl(values.flags.get("--dashboard-url") ?? DEFAULT_DASHBOARD_URL),
|
|
140
|
+
intervalSeconds: optionalPositiveInteger(values.flags.get("--interval-seconds"), "--interval-seconds") ?? DEFAULT_AUTOSTART_INTERVAL_SECONDS,
|
|
141
|
+
parentPid: optionalPositiveInteger(values.flags.get("--parent-pid"), "--parent-pid"),
|
|
142
|
+
json: values.booleans.has("--json"),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
export function parseAgentRulesArgs(args) {
|
|
146
|
+
const values = parseNamedArgs(args, {
|
|
147
|
+
allowedFlags: ["--home", "--host", "--repo", "--workspace", "--json"],
|
|
148
|
+
valueFlags: ["--home", "--host", "--repo", "--workspace"],
|
|
149
|
+
});
|
|
150
|
+
if (values.positionals.length > 1) {
|
|
151
|
+
throw new Error("agent-rules accepts at most one action (install|uninstall|status).");
|
|
152
|
+
}
|
|
153
|
+
const action = values.positionals[0] ?? "install";
|
|
154
|
+
if (action !== "install" && action !== "uninstall" && action !== "status") {
|
|
155
|
+
throw new Error("agent-rules action must be install, uninstall, or status.");
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
kind: "agent-rules",
|
|
159
|
+
action,
|
|
160
|
+
host: parseAgentRulesHost(values.flags.get("--host")),
|
|
161
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
162
|
+
repoRoot: optionalNonEmpty(workRootFlagValue(values)),
|
|
163
|
+
json: values.booleans.has("--json"),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* BLI-3580. `install` is the default action on purpose: this command exists to
|
|
168
|
+
* be run unasked (by `do-everything` and by the sync tick), and a bare
|
|
169
|
+
* `cockpit memory` should do the thing rather than print usage.
|
|
170
|
+
*/
|
|
171
|
+
export function parseMemoryArgs(args) {
|
|
172
|
+
const values = parseNamedArgs(args, {
|
|
173
|
+
allowedFlags: ["--home", "--dashboard-url", "--dry-run", "--json"],
|
|
174
|
+
valueFlags: ["--home", "--dashboard-url"],
|
|
175
|
+
});
|
|
176
|
+
if (values.positionals.length > 1) {
|
|
177
|
+
throw new Error("memory accepts at most one action (install|status).");
|
|
178
|
+
}
|
|
179
|
+
const action = values.positionals[0] ?? "install";
|
|
180
|
+
if (action !== "install" && action !== "status") {
|
|
181
|
+
throw new Error("memory action must be install or status.");
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
kind: "memory",
|
|
185
|
+
action,
|
|
186
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
187
|
+
dashboardUrl: optionalUrl(values.flags.get("--dashboard-url")),
|
|
188
|
+
dryRun: values.booleans.has("--dry-run"),
|
|
189
|
+
json: values.booleans.has("--json"),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* BLI-3619. Deliberately takes no positional: `clean` is one verb, and the two
|
|
194
|
+
* flags say how far it goes. A path is never accepted — the only directory this
|
|
195
|
+
* command may touch is the collector's own state directory.
|
|
196
|
+
*/
|
|
197
|
+
export function parseCleanArgs(args) {
|
|
198
|
+
const values = parseNamedArgs(args, {
|
|
199
|
+
allowedFlags: [
|
|
200
|
+
"--home",
|
|
201
|
+
"--dashboard-url",
|
|
202
|
+
"--dry-run",
|
|
203
|
+
"--all-committed",
|
|
204
|
+
"--reconcile",
|
|
205
|
+
"--json",
|
|
206
|
+
],
|
|
207
|
+
valueFlags: ["--home", "--dashboard-url"],
|
|
208
|
+
});
|
|
209
|
+
if (values.positionals.length > 0) {
|
|
210
|
+
throw new Error("clean takes no arguments; use --dry-run, --all-committed, or --reconcile.");
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
kind: "clean",
|
|
214
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
215
|
+
dashboardUrl: optionalUrl(values.flags.get("--dashboard-url")),
|
|
216
|
+
dryRun: values.booleans.has("--dry-run"),
|
|
217
|
+
allCommitted: values.booleans.has("--all-committed"),
|
|
218
|
+
reconcile: values.booleans.has("--reconcile"),
|
|
219
|
+
json: values.booleans.has("--json"),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function parseAgentRulesHost(value) {
|
|
223
|
+
const host = value?.trim().toLowerCase() || "all";
|
|
224
|
+
if (host === "codex" || host === "claude" || host === "all")
|
|
225
|
+
return host;
|
|
226
|
+
throw new Error("agent-rules --host must be codex, claude, or all.");
|
|
227
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The commands that do ongoing collection work on an already-set-up machine:
|
|
3
|
+
* `cockpit start` (bind a ticket), `cockpit sync`/`cockpit analyze`
|
|
4
|
+
* (converge now), and `cockpit backfill` (catch up on history). Split out of
|
|
5
|
+
* local-args-collector.ts (BLI-3636).
|
|
6
|
+
*
|
|
7
|
+
* Every parser moved verbatim: same allowed flags, same value flags, same
|
|
8
|
+
* defaults, same refusal sentences.
|
|
9
|
+
*/
|
|
10
|
+
import { IntentSourceSchema, WorkIntentSchema, WorkPhaseSchema, } from "@bli-cockpit/telemetry-core";
|
|
11
|
+
import { assertNoPositionals, optionalConfidence, optionalNonEmpty, optionalPositiveInteger, optionalSchemaValue, optionalUrl, parseNamedArgs, workRootFlagValue, } from "./local-arg-values.js";
|
|
12
|
+
export function parseStartArgs(args) {
|
|
13
|
+
const values = parseNamedArgs(args, {
|
|
14
|
+
allowedFlags: [
|
|
15
|
+
"--home",
|
|
16
|
+
"--repo",
|
|
17
|
+
"--workspace",
|
|
18
|
+
"--branch",
|
|
19
|
+
"--ticket",
|
|
20
|
+
"--clear-ticket",
|
|
21
|
+
"--topic",
|
|
22
|
+
"--topic-summary",
|
|
23
|
+
"--intent",
|
|
24
|
+
"--phase",
|
|
25
|
+
"--intent-source",
|
|
26
|
+
"--intent-confidence",
|
|
27
|
+
"--operator-id",
|
|
28
|
+
"--session-id",
|
|
29
|
+
"--json",
|
|
30
|
+
"--max-depth",
|
|
31
|
+
"--max-repos",
|
|
32
|
+
],
|
|
33
|
+
valueFlags: [
|
|
34
|
+
"--home",
|
|
35
|
+
"--repo",
|
|
36
|
+
"--workspace",
|
|
37
|
+
"--branch",
|
|
38
|
+
"--ticket",
|
|
39
|
+
"--topic",
|
|
40
|
+
"--topic-summary",
|
|
41
|
+
"--intent",
|
|
42
|
+
"--phase",
|
|
43
|
+
"--intent-source",
|
|
44
|
+
"--intent-confidence",
|
|
45
|
+
"--operator-id",
|
|
46
|
+
"--session-id",
|
|
47
|
+
"--max-depth",
|
|
48
|
+
"--max-repos",
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
assertNoPositionals(values.positionals, "start");
|
|
52
|
+
const activeTicketId = optionalNonEmpty(values.flags.get("--ticket"));
|
|
53
|
+
const clearTicket = values.booleans.has("--clear-ticket");
|
|
54
|
+
if (activeTicketId && clearTicket) {
|
|
55
|
+
throw new Error("--ticket and --clear-ticket cannot be combined.");
|
|
56
|
+
}
|
|
57
|
+
const topicLabel = optionalNonEmpty(values.flags.get("--topic"));
|
|
58
|
+
const topicSummaryRedacted = optionalNonEmpty(values.flags.get("--topic-summary"));
|
|
59
|
+
const workIntent = optionalSchemaValue(WorkIntentSchema, values.flags.get("--intent"), "--intent");
|
|
60
|
+
const workPhase = optionalSchemaValue(WorkPhaseSchema, values.flags.get("--phase"), "--phase");
|
|
61
|
+
const intentConfidence = optionalConfidence(values.flags.get("--intent-confidence"), "--intent-confidence");
|
|
62
|
+
const explicitIntentMetadata = Boolean(topicLabel ||
|
|
63
|
+
topicSummaryRedacted ||
|
|
64
|
+
workIntent ||
|
|
65
|
+
workPhase ||
|
|
66
|
+
intentConfidence !== undefined);
|
|
67
|
+
return {
|
|
68
|
+
kind: "start",
|
|
69
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
70
|
+
repoRoot: optionalNonEmpty(workRootFlagValue(values)),
|
|
71
|
+
branch: optionalNonEmpty(values.flags.get("--branch")),
|
|
72
|
+
activeTicketId,
|
|
73
|
+
clearTicket,
|
|
74
|
+
topicLabel,
|
|
75
|
+
topicSummaryRedacted,
|
|
76
|
+
workIntent,
|
|
77
|
+
workPhase,
|
|
78
|
+
intentSource: optionalSchemaValue(IntentSourceSchema, values.flags.get("--intent-source"), "--intent-source") ?? (explicitIntentMetadata ? "explicit_user" : undefined),
|
|
79
|
+
intentConfidence,
|
|
80
|
+
operatorId: optionalNonEmpty(values.flags.get("--operator-id")),
|
|
81
|
+
sessionId: optionalNonEmpty(values.flags.get("--session-id")),
|
|
82
|
+
json: values.booleans.has("--json"),
|
|
83
|
+
maxDepth: optionalPositiveInteger(values.flags.get("--max-depth"), "--max-depth"),
|
|
84
|
+
maxRepos: optionalPositiveInteger(values.flags.get("--max-repos"), "--max-repos"),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export function parseSyncArgs(args) {
|
|
88
|
+
return parseSyncLikeArgs(args, "sync");
|
|
89
|
+
}
|
|
90
|
+
export function parseAnalyzeArgs(args) {
|
|
91
|
+
return parseSyncLikeArgs(args, "analyze");
|
|
92
|
+
}
|
|
93
|
+
function parseSyncLikeArgs(args, command) {
|
|
94
|
+
const values = parseNamedArgs(args, {
|
|
95
|
+
allowedFlags: [
|
|
96
|
+
"--home",
|
|
97
|
+
"--repo",
|
|
98
|
+
"--workspace",
|
|
99
|
+
"--dashboard-url",
|
|
100
|
+
"--json",
|
|
101
|
+
"--max-depth",
|
|
102
|
+
"--max-repos",
|
|
103
|
+
],
|
|
104
|
+
valueFlags: [
|
|
105
|
+
"--home",
|
|
106
|
+
"--repo",
|
|
107
|
+
"--workspace",
|
|
108
|
+
"--dashboard-url",
|
|
109
|
+
"--max-depth",
|
|
110
|
+
"--max-repos",
|
|
111
|
+
],
|
|
112
|
+
});
|
|
113
|
+
assertNoPositionals(values.positionals, command);
|
|
114
|
+
return {
|
|
115
|
+
kind: command,
|
|
116
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
117
|
+
repoRoot: optionalNonEmpty(workRootFlagValue(values)),
|
|
118
|
+
dashboardUrl: optionalUrl(values.flags.get("--dashboard-url")),
|
|
119
|
+
json: values.booleans.has("--json"),
|
|
120
|
+
maxDepth: optionalPositiveInteger(values.flags.get("--max-depth"), "--max-depth"),
|
|
121
|
+
maxRepos: optionalPositiveInteger(values.flags.get("--max-repos"), "--max-repos"),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export function parseBackfillArgs(args) {
|
|
125
|
+
const values = parseNamedArgs(args, {
|
|
126
|
+
allowedFlags: [
|
|
127
|
+
"--home",
|
|
128
|
+
"--repo",
|
|
129
|
+
"--workspace",
|
|
130
|
+
"--since-days",
|
|
131
|
+
"--all",
|
|
132
|
+
"--source",
|
|
133
|
+
"--dry-run",
|
|
134
|
+
"--max-files",
|
|
135
|
+
"--max-depth",
|
|
136
|
+
"--max-repos",
|
|
137
|
+
"--yes",
|
|
138
|
+
"--json",
|
|
139
|
+
],
|
|
140
|
+
valueFlags: [
|
|
141
|
+
"--home",
|
|
142
|
+
"--repo",
|
|
143
|
+
"--workspace",
|
|
144
|
+
"--since-days",
|
|
145
|
+
"--source",
|
|
146
|
+
"--max-files",
|
|
147
|
+
"--max-depth",
|
|
148
|
+
"--max-repos",
|
|
149
|
+
],
|
|
150
|
+
});
|
|
151
|
+
assertNoPositionals(values.positionals, "backfill");
|
|
152
|
+
const source = values.flags.get("--source");
|
|
153
|
+
if (source !== undefined && source !== "codex" && source !== "claude") {
|
|
154
|
+
throw new Error("--source must be 'codex' or 'claude'. Omit it to scan both.");
|
|
155
|
+
}
|
|
156
|
+
const sinceDays = optionalPositiveInteger(values.flags.get("--since-days"), "--since-days");
|
|
157
|
+
const all = values.booleans.has("--all");
|
|
158
|
+
if (all && sinceDays !== undefined) {
|
|
159
|
+
throw new Error("--since-days and --all cannot be combined.");
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
kind: "backfill",
|
|
163
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
164
|
+
repoRoot: optionalNonEmpty(workRootFlagValue(values)),
|
|
165
|
+
source,
|
|
166
|
+
sinceDays,
|
|
167
|
+
all,
|
|
168
|
+
dryRun: values.booleans.has("--dry-run"),
|
|
169
|
+
maxFiles: optionalPositiveInteger(values.flags.get("--max-files"), "--max-files"),
|
|
170
|
+
maxDepth: optionalPositiveInteger(values.flags.get("--max-depth"), "--max-depth"),
|
|
171
|
+
maxRepos: optionalPositiveInteger(values.flags.get("--max-repos"), "--max-repos"),
|
|
172
|
+
yes: values.booleans.has("--yes"),
|
|
173
|
+
json: values.booleans.has("--json"),
|
|
174
|
+
};
|
|
175
|
+
}
|