@skrr-ai/cli 0.1.107 → 0.1.109
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/README.md +10 -0
- package/dist/base-command.d.ts +59 -0
- package/dist/base-command.js +118 -7
- package/dist/commands/agents/update.d.ts +14 -0
- package/dist/commands/agents/update.js +40 -1
- package/dist/commands/commitments/accounting.js +7 -2
- package/dist/commands/commitments/cost.d.ts +11 -0
- package/dist/commands/commitments/cost.js +26 -3
- package/dist/commands/commitments/create.js +2 -1
- package/dist/commands/commitments/operating-review.d.ts +15 -0
- package/dist/commands/commitments/operating-review.js +103 -50
- package/dist/commands/commitments/update.js +12 -5
- package/dist/commands/doctor.js +9 -0
- package/dist/commands/goals/list.js +6 -1
- package/dist/commands/grants/rotate.js +4 -0
- package/dist/commands/login.js +42 -14
- package/dist/commands/machines/list.js +28 -6
- package/dist/commands/projects/show.js +1 -1
- package/dist/commands/revoke-daemon.js +2 -1
- package/dist/commands/spaces/workflows/archive.d.ts +1 -0
- package/dist/commands/spaces/workflows/archive.js +1 -0
- package/dist/commands/spaces/workflows/artifact.d.ts +1 -0
- package/dist/commands/spaces/workflows/artifact.js +1 -0
- package/dist/commands/spaces/workflows/cancel-node.d.ts +1 -0
- package/dist/commands/spaces/workflows/cancel-node.js +1 -0
- package/dist/commands/spaces/workflows/cancel.d.ts +1 -0
- package/dist/commands/spaces/workflows/cancel.js +6 -2
- package/dist/commands/spaces/workflows/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/create.js +7 -1
- package/dist/commands/spaces/workflows/events.d.ts +1 -0
- package/dist/commands/spaces/workflows/events.js +1 -0
- package/dist/commands/spaces/workflows/fixtures/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/fixtures/create.js +1 -0
- package/dist/commands/spaces/workflows/fixtures/list.d.ts +1 -0
- package/dist/commands/spaces/workflows/fixtures/list.js +1 -0
- package/dist/commands/spaces/workflows/lifecycle-diagnostics.d.ts +1 -0
- package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +1 -0
- package/dist/commands/spaces/workflows/list.d.ts +1 -0
- package/dist/commands/spaces/workflows/list.js +1 -0
- package/dist/commands/spaces/workflows/node-types.d.ts +2 -0
- package/dist/commands/spaces/workflows/node-types.js +24 -6
- package/dist/commands/spaces/workflows/pause.d.ts +1 -0
- package/dist/commands/spaces/workflows/pause.js +1 -0
- package/dist/commands/spaces/workflows/plan/show.d.ts +1 -0
- package/dist/commands/spaces/workflows/plan/show.js +1 -0
- package/dist/commands/spaces/workflows/plan/update.d.ts +1 -0
- package/dist/commands/spaces/workflows/plan/update.js +1 -0
- package/dist/commands/spaces/workflows/proposals/apply.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/apply.js +1 -0
- package/dist/commands/spaces/workflows/proposals/confirm.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/confirm.js +1 -0
- package/dist/commands/spaces/workflows/proposals/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/create.js +1 -0
- package/dist/commands/spaces/workflows/proposals/dismiss.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/dismiss.js +1 -0
- package/dist/commands/spaces/workflows/proposals/list.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/list.js +1 -0
- package/dist/commands/spaces/workflows/publish.d.ts +1 -0
- package/dist/commands/spaces/workflows/publish.js +9 -0
- package/dist/commands/spaces/workflows/reconcile.d.ts +1 -0
- package/dist/commands/spaces/workflows/reconcile.js +1 -0
- package/dist/commands/spaces/workflows/resume.d.ts +1 -0
- package/dist/commands/spaces/workflows/resume.js +1 -0
- package/dist/commands/spaces/workflows/retry.d.ts +1 -0
- package/dist/commands/spaces/workflows/retry.js +1 -0
- package/dist/commands/spaces/workflows/run.d.ts +1 -0
- package/dist/commands/spaces/workflows/run.js +14 -7
- package/dist/commands/spaces/workflows/runs.d.ts +1 -0
- package/dist/commands/spaces/workflows/runs.js +1 -0
- package/dist/commands/spaces/workflows/show.d.ts +25 -0
- package/dist/commands/spaces/workflows/show.js +42 -4
- package/dist/commands/spaces/workflows/signal.d.ts +12 -0
- package/dist/commands/spaces/workflows/signal.js +40 -2
- package/dist/commands/spaces/workflows/start.d.ts +1 -0
- package/dist/commands/spaces/workflows/start.js +1 -0
- package/dist/commands/spaces/workflows/task-execution/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/task-execution/create.js +1 -0
- package/dist/commands/spaces/workflows/test.d.ts +1 -0
- package/dist/commands/spaces/workflows/test.js +1 -0
- package/dist/commands/spaces/workflows/triggers/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/triggers/create.js +1 -0
- package/dist/commands/spaces/workflows/triggers/fire.d.ts +1 -0
- package/dist/commands/spaces/workflows/triggers/fire.js +1 -0
- package/dist/commands/spaces/workflows/triggers/list.d.ts +1 -0
- package/dist/commands/spaces/workflows/triggers/list.js +1 -0
- package/dist/commands/spaces/workflows/triggers/update.d.ts +1 -0
- package/dist/commands/spaces/workflows/triggers/update.js +1 -0
- package/dist/commands/spaces/workflows/update.d.ts +1 -0
- package/dist/commands/spaces/workflows/update.js +4 -0
- package/dist/commands/spaces/workflows/validate.d.ts +1 -0
- package/dist/commands/spaces/workflows/validate.js +15 -0
- package/dist/commands/tasks/activity.js +6 -2
- package/dist/commands/tasks/bulk-update.d.ts +1 -0
- package/dist/commands/tasks/bulk-update.js +7 -0
- package/dist/commands/tasks/complete.js +2 -1
- package/dist/commands/tasks/create-tree.d.ts +69 -0
- package/dist/commands/tasks/create-tree.js +413 -47
- package/dist/commands/tasks/list.d.ts +2 -0
- package/dist/commands/tasks/list.js +19 -1
- package/dist/commands/tasks/rearm-recurrence.d.ts +22 -0
- package/dist/commands/tasks/rearm-recurrence.js +68 -0
- package/dist/commands/tasks/show.d.ts +1 -0
- package/dist/commands/tasks/show.js +17 -3
- package/dist/commands/tasks/timeline.js +4 -0
- package/dist/commands/tasks/update.d.ts +1 -0
- package/dist/commands/tasks/update.js +6 -1
- package/dist/commands/triggers/create.js +8 -2
- package/dist/commands/triggers/parse-intent.d.ts +7 -0
- package/dist/commands/triggers/parse-intent.js +31 -9
- package/dist/commands/workspaces/grants.js +23 -3
- package/dist/commands/workspaces/members.d.ts +20 -0
- package/dist/commands/workspaces/members.js +64 -0
- package/dist/commands/workspaces/stewardship.js +14 -2
- package/dist/help.js +12 -0
- package/dist/hooks/command-not-found.js +27 -1
- package/dist/lib/command-miss.d.ts +52 -2
- package/dist/lib/command-miss.js +187 -18
- package/dist/lib/commitment-loop-status.d.ts +8 -0
- package/dist/lib/commitment-loop-status.js +45 -0
- package/dist/lib/commitment-product.js +13 -1
- package/dist/lib/commitments.d.ts +16 -2
- package/dist/lib/commitments.js +48 -8
- package/dist/lib/daemon-command-hint.d.ts +15 -0
- package/dist/lib/daemon-command-hint.js +21 -0
- package/dist/lib/daemon-setup.js +10 -1
- package/dist/lib/daemonBrokerRefusal.js +3 -2
- package/dist/lib/daemonHandoff.js +2 -1
- package/dist/lib/deployed-build.d.ts +43 -0
- package/dist/lib/deployed-build.js +66 -0
- package/dist/lib/grants.d.ts +27 -1
- package/dist/lib/grants.js +39 -21
- package/dist/lib/json-mode.d.ts +11 -0
- package/dist/lib/json-mode.js +19 -0
- package/dist/lib/json-output.d.ts +25 -0
- package/dist/lib/json-output.js +30 -0
- package/dist/lib/list-output.d.ts +9 -0
- package/dist/lib/list-output.js +20 -1
- package/dist/lib/local-daemon-readiness.d.ts +90 -0
- package/dist/lib/local-daemon-readiness.js +143 -0
- package/dist/lib/machines.d.ts +117 -0
- package/dist/lib/machines.js +139 -0
- package/dist/lib/managed-instruction-local.d.ts +2 -0
- package/dist/lib/managed-instruction-local.js +2 -0
- package/dist/lib/profile-machine-identity-guard.d.ts +18 -0
- package/dist/lib/profile-machine-identity-guard.js +29 -0
- package/dist/lib/request-field-flags.d.ts +20 -0
- package/dist/lib/request-field-flags.js +34 -0
- package/dist/lib/stewardship-output.d.ts +1 -1
- package/dist/lib/stewardship-output.js +26 -30
- package/dist/lib/task-batch.d.ts +61 -4
- package/dist/lib/task-batch.js +316 -80
- package/dist/lib/task-economics.d.ts +5 -0
- package/dist/lib/task-economics.js +15 -0
- package/dist/lib/task-recurrence-outcome.d.ts +6 -6
- package/dist/lib/task-recurrence-outcome.js +27 -3
- package/dist/lib/tasks.d.ts +39 -0
- package/dist/lib/tasks.js +132 -0
- package/dist/lib/triggers.d.ts +31 -1
- package/dist/lib/triggers.js +55 -1
- package/dist/lib/workflow-issues.d.ts +14 -0
- package/dist/lib/workflow-issues.js +32 -0
- package/dist/lib/workflow-run-view.d.ts +55 -0
- package/dist/lib/workflow-run-view.js +105 -0
- package/dist/lib/workspaces.d.ts +31 -4
- package/dist/lib/workspaces.js +43 -0
- package/dist/lib/write-target.d.ts +29 -0
- package/dist/lib/write-target.js +72 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/workspaceIsolationProbeWire.d.ts +51 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/workspaceIsolationProbeWire.js +50 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/workspaceIsolationProbeWire.d.ts +51 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/workspaceIsolationProbeWire.js +46 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +7896 -7524
- package/oclif.manifest.json +376 -52
- package/package.json +19 -1
|
@@ -57,7 +57,20 @@ export declare function nearestCommands(id: string, all: string[], limit?: numbe
|
|
|
57
57
|
*/
|
|
58
58
|
export declare function helpSubjectId(tokens: readonly string[], config: CommandLookup): string;
|
|
59
59
|
/**
|
|
60
|
-
*
|
|
60
|
+
* A command miss, in parts: what is wrong, what to do, and the commands that
|
|
61
|
+
* would do it. The text a person reads is `message` and `fix` on separate lines;
|
|
62
|
+
* a script reads `suggestions` without parsing either (OSK-16434).
|
|
63
|
+
*/
|
|
64
|
+
export type CommandMiss = {
|
|
65
|
+
message: string;
|
|
66
|
+
fix?: string;
|
|
67
|
+
/** Full commands, `skrr tasks show` — never prose. */
|
|
68
|
+
suggestions: string[];
|
|
69
|
+
};
|
|
70
|
+
/** The text form: exactly what a miss has always printed. */
|
|
71
|
+
export declare function describeCommandMiss(config: CommandLookup, id: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* The miss for a colon-joined id that did not resolve.
|
|
61
74
|
*
|
|
62
75
|
* Two shapes, in order:
|
|
63
76
|
*
|
|
@@ -68,7 +81,7 @@ export declare function helpSubjectId(tokens: readonly string[], config: Command
|
|
|
68
81
|
* suggest the nearest siblings under the same topic before falling back to
|
|
69
82
|
* the whole command list.
|
|
70
83
|
*/
|
|
71
|
-
export declare function
|
|
84
|
+
export declare function analyzeCommandMiss(config: CommandLookup, id: string): CommandMiss;
|
|
72
85
|
/** Adapt an oclif `Config` to the lookup shape above. */
|
|
73
86
|
export declare function lookupFromConfig(config: {
|
|
74
87
|
bin: string;
|
|
@@ -80,3 +93,40 @@ export declare function lookupFromConfig(config: {
|
|
|
80
93
|
name?: string;
|
|
81
94
|
}>;
|
|
82
95
|
}): CommandLookup;
|
|
96
|
+
export type VerbSynonym = {
|
|
97
|
+
/** The command that really exists, colon-joined: `tasks:show`. */
|
|
98
|
+
id: string;
|
|
99
|
+
/** What the reader typed after the verb: the arguments of the real command. */
|
|
100
|
+
rest: string[];
|
|
101
|
+
/** The two spellings, spoken: `skrr tasks get` and `skrr tasks show`. */
|
|
102
|
+
typed: string;
|
|
103
|
+
resolved: string;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* The real command a mis-spelled READ verb means, when a synonym of it exists
|
|
107
|
+
* under the same topic path.
|
|
108
|
+
*
|
|
109
|
+
* `words` are the leading, non-flag tokens as the reader typed them. The scan
|
|
110
|
+
* walks down the real topic path; at the first word that is not part of it, that
|
|
111
|
+
* word is the verb, and only a synonym that is itself a command qualifies. A
|
|
112
|
+
* word that is a real command, or a prefix of one, is never reinterpreted.
|
|
113
|
+
*/
|
|
114
|
+
export declare function resolveVerbSynonym(config: CommandLookup, words: readonly string[]): VerbSynonym | null;
|
|
115
|
+
/**
|
|
116
|
+
* The words a person typed for the command, recovered from the process argv.
|
|
117
|
+
*
|
|
118
|
+
* oclif joins the words it takes for a command with `:`, so a positional that
|
|
119
|
+
* itself contains a colon cannot be told apart from two words in the id. The
|
|
120
|
+
* raw argv can: its leading tokens, joined, are the id.
|
|
121
|
+
*/
|
|
122
|
+
export declare function typedCommandWords(id: string, rawArgv: readonly string[]): string[];
|
|
123
|
+
/**
|
|
124
|
+
* What `--json` prints for a miss: the same `{ok:false, error}` every other
|
|
125
|
+
* refusal prints, so a script that parses refusals parses this one — instead of
|
|
126
|
+
* `jq` reading prose and failing with "Invalid numeric literal", which is how
|
|
127
|
+
* the report found it.
|
|
128
|
+
*/
|
|
129
|
+
export declare function commandMissEnvelope(config: CommandLookup, id: string, words: readonly string[]): {
|
|
130
|
+
ok: false;
|
|
131
|
+
error: Record<string, unknown>;
|
|
132
|
+
};
|
package/dist/lib/command-miss.js
CHANGED
|
@@ -24,7 +24,11 @@ exports.editDistance = editDistance;
|
|
|
24
24
|
exports.nearestCommands = nearestCommands;
|
|
25
25
|
exports.helpSubjectId = helpSubjectId;
|
|
26
26
|
exports.describeCommandMiss = describeCommandMiss;
|
|
27
|
+
exports.analyzeCommandMiss = analyzeCommandMiss;
|
|
27
28
|
exports.lookupFromConfig = lookupFromConfig;
|
|
29
|
+
exports.resolveVerbSynonym = resolveVerbSynonym;
|
|
30
|
+
exports.typedCommandWords = typedCommandWords;
|
|
31
|
+
exports.commandMissEnvelope = commandMissEnvelope;
|
|
28
32
|
/** A uuid-ish token, which is what a stray space id looks like. */
|
|
29
33
|
const LOOKS_LIKE_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
30
34
|
const DAEMON_READINESS = [
|
|
@@ -57,8 +61,49 @@ function describeAnsweredElsewhere(config, resolved, extras) {
|
|
|
57
61
|
if (targets.length === 0)
|
|
58
62
|
return null;
|
|
59
63
|
const spoken = `${config.bin} ${resolved.split(':').join(' ')}`;
|
|
60
|
-
return
|
|
61
|
-
|
|
64
|
+
return {
|
|
65
|
+
message: `\`${spoken}\` has no \`${extras[0]}\`. Depending on what you want to know:`,
|
|
66
|
+
fix: targets.map((t) => ` \`${config.bin} ${t.usage}\` — ${t.answers}`).join('\n'),
|
|
67
|
+
suggestions: targets.map((t) => `${config.bin} ${t.usage}`),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const WHOAMI = {
|
|
71
|
+
id: 'whoami',
|
|
72
|
+
answers: 'who you are signed in as, which server, and where the credential came from',
|
|
73
|
+
};
|
|
74
|
+
const LOGIN = { id: 'login', answers: 'sign in' };
|
|
75
|
+
const LOGOUT = { id: 'logout', answers: 'sign out' };
|
|
76
|
+
const FLAT_COMMAND_FOR_TOPIC = {
|
|
77
|
+
auth: {
|
|
78
|
+
'*': [WHOAMI, LOGIN, LOGOUT],
|
|
79
|
+
status: [WHOAMI],
|
|
80
|
+
whoami: [WHOAMI],
|
|
81
|
+
me: [WHOAMI],
|
|
82
|
+
user: [WHOAMI],
|
|
83
|
+
account: [WHOAMI],
|
|
84
|
+
info: [WHOAMI],
|
|
85
|
+
show: [WHOAMI],
|
|
86
|
+
login: [LOGIN],
|
|
87
|
+
signin: [LOGIN],
|
|
88
|
+
'sign-in': [LOGIN],
|
|
89
|
+
logout: [LOGOUT],
|
|
90
|
+
signout: [LOGOUT],
|
|
91
|
+
'sign-out': [LOGOUT],
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
function describeFlatRedirect(config, parts, spokenId) {
|
|
95
|
+
const topic = FLAT_COMMAND_FOR_TOPIC[parts[0]?.toLowerCase() ?? ''];
|
|
96
|
+
// A real topic or command of that name answers for itself.
|
|
97
|
+
if (!topic || config.topicNames.includes(parts[0]) || config.findCommand(parts[0]))
|
|
98
|
+
return null;
|
|
99
|
+
const targets = (topic[(parts[1] ?? '*').toLowerCase()] ?? []).filter((target) => config.findCommand(target.id) !== undefined);
|
|
100
|
+
if (targets.length === 0)
|
|
101
|
+
return null;
|
|
102
|
+
return {
|
|
103
|
+
message: `\`${spokenId}\` is not a command; this CLI has no \`${parts[0]}\` topic.`,
|
|
104
|
+
fix: targets.map((t) => ` \`${config.bin} ${t.id}\` — ${t.answers}`).join('\n'),
|
|
105
|
+
suggestions: targets.map((t) => `${config.bin} ${t.id}`),
|
|
106
|
+
};
|
|
62
107
|
}
|
|
63
108
|
/** Cheap edit distance, bounded — this runs only on a miss. */
|
|
64
109
|
function editDistance(a, b) {
|
|
@@ -135,8 +180,13 @@ function helpSubjectId(tokens, config) {
|
|
|
135
180
|
}
|
|
136
181
|
return taken.join(':');
|
|
137
182
|
}
|
|
183
|
+
/** The text form: exactly what a miss has always printed. */
|
|
184
|
+
function describeCommandMiss(config, id) {
|
|
185
|
+
const miss = analyzeCommandMiss(config, id);
|
|
186
|
+
return miss.fix ? `${miss.message}\n${miss.fix}` : miss.message;
|
|
187
|
+
}
|
|
138
188
|
/**
|
|
139
|
-
* The
|
|
189
|
+
* The miss for a colon-joined id that did not resolve.
|
|
140
190
|
*
|
|
141
191
|
* Two shapes, in order:
|
|
142
192
|
*
|
|
@@ -147,7 +197,7 @@ function helpSubjectId(tokens, config) {
|
|
|
147
197
|
* suggest the nearest siblings under the same topic before falling back to
|
|
148
198
|
* the whole command list.
|
|
149
199
|
*/
|
|
150
|
-
function
|
|
200
|
+
function analyzeCommandMiss(config, id) {
|
|
151
201
|
const parts = String(id ?? '')
|
|
152
202
|
.split(':')
|
|
153
203
|
.filter(Boolean);
|
|
@@ -169,9 +219,12 @@ function describeCommandMiss(config, id) {
|
|
|
169
219
|
? ' It takes `--space <id>`.'
|
|
170
220
|
: '';
|
|
171
221
|
const plural = extras.length === 1 ? 'argument' : 'arguments';
|
|
172
|
-
return
|
|
173
|
-
|
|
174
|
-
|
|
222
|
+
return {
|
|
223
|
+
message: `\`${spoken}\` exists, but does not take the positional ${plural} ` +
|
|
224
|
+
`${extras.map((e) => `\`${e}\``).join(' ')}.${hint}`,
|
|
225
|
+
fix: `See \`${spoken} --help\`.`,
|
|
226
|
+
suggestions: [spoken],
|
|
227
|
+
};
|
|
175
228
|
}
|
|
176
229
|
// Topic names are candidates too, so a near-miss on a bare topic gets routed
|
|
177
230
|
// to the topic rather than nothing (OSK-4869): `skrr harness` (singular) is
|
|
@@ -200,10 +253,16 @@ function describeCommandMiss(config, id) {
|
|
|
200
253
|
// the honest thing to report is the disagreement, which has a known cause and
|
|
201
254
|
// a next step.
|
|
202
255
|
if (config.commandIDs.includes(typedId) || config.topicNames.includes(typedId)) {
|
|
203
|
-
return
|
|
204
|
-
|
|
205
|
-
`
|
|
256
|
+
return {
|
|
257
|
+
message: `\`${spokenId}\` is registered but could not be loaded from this build.`,
|
|
258
|
+
fix: `Its compiled output is missing or stale — run \`npm --prefix cli run build\` in a ` +
|
|
259
|
+
`development checkout, or reinstall the CLI.`,
|
|
260
|
+
suggestions: [],
|
|
261
|
+
};
|
|
206
262
|
}
|
|
263
|
+
const flat = describeFlatRedirect(config, parts, spokenId);
|
|
264
|
+
if (flat)
|
|
265
|
+
return flat;
|
|
207
266
|
const siblings = config.commandIDs.filter((c) => c.startsWith(`${parts[0]}:`));
|
|
208
267
|
const suggestions = (siblings.length
|
|
209
268
|
? nearestCommands(parts.slice(0, 2).join(':'), siblings)
|
|
@@ -213,10 +272,12 @@ function describeCommandMiss(config, id) {
|
|
|
213
272
|
// than the id, so it costs a suggestion slot as well as the reader's trust.
|
|
214
273
|
.filter((candidate) => candidate !== typedId);
|
|
215
274
|
if (suggestions.length) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
275
|
+
const spoken = suggestions.map((s) => `${config.bin} ${s.split(':').join(' ')}`);
|
|
276
|
+
return {
|
|
277
|
+
message: `\`${spokenId}\` is not a command.`,
|
|
278
|
+
fix: `Did you mean: ${spoken.map((s) => `\`${s}\``).join(', ')}?`,
|
|
279
|
+
suggestions: spoken,
|
|
280
|
+
};
|
|
220
281
|
}
|
|
221
282
|
// The topic the reader typed is real, and nothing in it is spelled like what
|
|
222
283
|
// they asked for — a different word for it, not a typo. "See the topics"
|
|
@@ -233,11 +294,18 @@ function describeCommandMiss(config, id) {
|
|
|
233
294
|
.sort()
|
|
234
295
|
.slice(0, MAX_TOPIC_LISTING);
|
|
235
296
|
const more = siblings.length > shown.length ? ', …' : '';
|
|
236
|
-
return
|
|
237
|
-
`\`${
|
|
238
|
-
|
|
297
|
+
return {
|
|
298
|
+
message: `\`${spokenId}\` is not a command.`,
|
|
299
|
+
fix: `\`${config.bin} ${topic}\` has: ${shown.map((c) => `\`${c}\``).join(', ')}${more}.\n` +
|
|
300
|
+
`See \`${config.bin} ${topic} --help\`.`,
|
|
301
|
+
suggestions: shown.map((c) => `${config.bin} ${topic} ${c}`),
|
|
302
|
+
};
|
|
239
303
|
}
|
|
240
|
-
return
|
|
304
|
+
return {
|
|
305
|
+
message: `\`${spokenId}\` is not a command.`,
|
|
306
|
+
fix: `Run \`${config.bin} --help\` to see the topics.`,
|
|
307
|
+
suggestions: [],
|
|
308
|
+
};
|
|
241
309
|
}
|
|
242
310
|
/** Enough to scan at a glance; the topic's own `--help` has the rest. */
|
|
243
311
|
const MAX_TOPIC_LISTING = 10;
|
|
@@ -252,3 +320,104 @@ function lookupFromConfig(config) {
|
|
|
252
320
|
: [],
|
|
253
321
|
};
|
|
254
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Verbs this CLI spells two ways across its topics (OSK-16434).
|
|
325
|
+
*
|
|
326
|
+
* `skrr tasks` and `skrr machines` say `list` and `show`; `skrr wiki` and
|
|
327
|
+
* `skrr machines dedicated` say `ls`, and `get` is what most CLIs call `show`.
|
|
328
|
+
* Which a topic uses is history, not meaning, so a reader who guessed the other
|
|
329
|
+
* spelling was refused with a suggestion list that did not contain the command
|
|
330
|
+
* they meant (`tasks get` answered "did you mean tasks chat, tasks list, tasks
|
|
331
|
+
* open" — no `show`).
|
|
332
|
+
*
|
|
333
|
+
* Read-only verbs only, and only as a fallback: a spelling a topic really has
|
|
334
|
+
* always wins, and nothing here can run a command that changes anything.
|
|
335
|
+
*/
|
|
336
|
+
const VERB_SYNONYMS = [
|
|
337
|
+
['list', 'ls'],
|
|
338
|
+
['show', 'get'],
|
|
339
|
+
];
|
|
340
|
+
function synonymsOf(verb) {
|
|
341
|
+
const lower = verb.toLowerCase();
|
|
342
|
+
const group = VERB_SYNONYMS.find((g) => g.includes(lower));
|
|
343
|
+
return group ? group.filter((v) => v !== lower) : [];
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* The real command a mis-spelled READ verb means, when a synonym of it exists
|
|
347
|
+
* under the same topic path.
|
|
348
|
+
*
|
|
349
|
+
* `words` are the leading, non-flag tokens as the reader typed them. The scan
|
|
350
|
+
* walks down the real topic path; at the first word that is not part of it, that
|
|
351
|
+
* word is the verb, and only a synonym that is itself a command qualifies. A
|
|
352
|
+
* word that is a real command, or a prefix of one, is never reinterpreted.
|
|
353
|
+
*/
|
|
354
|
+
function resolveVerbSynonym(config, words) {
|
|
355
|
+
const usable = new Set();
|
|
356
|
+
for (const id of [...config.commandIDs, ...config.topicNames]) {
|
|
357
|
+
const parts = id.split(':');
|
|
358
|
+
for (let i = 1; i <= parts.length; i += 1)
|
|
359
|
+
usable.add(parts.slice(0, i).join(':'));
|
|
360
|
+
}
|
|
361
|
+
for (let i = 1; i < words.length; i += 1) {
|
|
362
|
+
const prefix = words.slice(0, i).join(':');
|
|
363
|
+
if (!usable.has(prefix))
|
|
364
|
+
return null;
|
|
365
|
+
if (usable.has(`${prefix}:${words[i]}`))
|
|
366
|
+
continue;
|
|
367
|
+
for (const synonym of synonymsOf(words[i])) {
|
|
368
|
+
const candidate = `${prefix}:${synonym}`;
|
|
369
|
+
if (config.findCommand(candidate) === undefined)
|
|
370
|
+
continue;
|
|
371
|
+
const spoken = (verb) => `${config.bin} ${[...words.slice(0, i), verb].join(' ')}`;
|
|
372
|
+
return {
|
|
373
|
+
id: candidate,
|
|
374
|
+
rest: words.slice(i + 1),
|
|
375
|
+
typed: spoken(words[i]),
|
|
376
|
+
resolved: spoken(synonym),
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* The words a person typed for the command, recovered from the process argv.
|
|
385
|
+
*
|
|
386
|
+
* oclif joins the words it takes for a command with `:`, so a positional that
|
|
387
|
+
* itself contains a colon cannot be told apart from two words in the id. The
|
|
388
|
+
* raw argv can: its leading tokens, joined, are the id.
|
|
389
|
+
*/
|
|
390
|
+
function typedCommandWords(id, rawArgv) {
|
|
391
|
+
const words = [];
|
|
392
|
+
for (const token of rawArgv) {
|
|
393
|
+
if (token.startsWith('-'))
|
|
394
|
+
break;
|
|
395
|
+
words.push(token);
|
|
396
|
+
if (words.join(':') === id)
|
|
397
|
+
return words;
|
|
398
|
+
}
|
|
399
|
+
return String(id ?? '')
|
|
400
|
+
.split(':')
|
|
401
|
+
.filter(Boolean);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* What `--json` prints for a miss: the same `{ok:false, error}` every other
|
|
405
|
+
* refusal prints, so a script that parses refusals parses this one — instead of
|
|
406
|
+
* `jq` reading prose and failing with "Invalid numeric literal", which is how
|
|
407
|
+
* the report found it.
|
|
408
|
+
*/
|
|
409
|
+
function commandMissEnvelope(config, id, words) {
|
|
410
|
+
const miss = analyzeCommandMiss(config, id);
|
|
411
|
+
return {
|
|
412
|
+
ok: false,
|
|
413
|
+
error: {
|
|
414
|
+
code: 'COMMAND_NOT_FOUND',
|
|
415
|
+
message: miss.message,
|
|
416
|
+
retryable: false,
|
|
417
|
+
command: null,
|
|
418
|
+
typed: `${config.bin} ${words.join(' ')}`.trim(),
|
|
419
|
+
...(miss.fix ? { fix: miss.fix } : {}),
|
|
420
|
+
...(miss.suggestions.length > 0 ? { suggestions: miss.suggestions } : {}),
|
|
421
|
+
},
|
|
422
|
+
};
|
|
423
|
+
}
|
|
@@ -12,5 +12,13 @@ import { type CommitmentLoopStatus, type CommitmentRoster } from '@skrr-ai/data-
|
|
|
12
12
|
export declare function loopStateLabel(state: unknown): string;
|
|
13
13
|
/** Roster table: one row per Commitment. */
|
|
14
14
|
export declare function formatCommitmentRoster(roster: Partial<CommitmentRoster>): string[];
|
|
15
|
+
/**
|
|
16
|
+
* One loop in a few lines, for a surface that lists many (`commitments operating-review`). It carries
|
|
17
|
+
* the same facts as `formatCommitmentLoopStatus` but in the order an owner asks them: is it armed,
|
|
18
|
+
* when does it next wake, why did it last stop, can it read what it watches, what did it run today.
|
|
19
|
+
* Every line is present when its fact is unknown, because "cannot see" must not read as "nothing to
|
|
20
|
+
* report" (OSK-15438, OSK-15412). Lines are returned unindented; the caller indents them.
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatLoopSummary(status: CommitmentLoopStatus): string[];
|
|
15
23
|
/** One Commitment's loop, in full. */
|
|
16
24
|
export declare function formatCommitmentLoopStatus(status: CommitmentLoopStatus): string[];
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.loopStateLabel = loopStateLabel;
|
|
4
4
|
exports.formatCommitmentRoster = formatCommitmentRoster;
|
|
5
|
+
exports.formatLoopSummary = formatLoopSummary;
|
|
5
6
|
exports.formatCommitmentLoopStatus = formatCommitmentLoopStatus;
|
|
6
7
|
/**
|
|
7
8
|
* Text rendering for `skrr commitments roster` / `skrr commitments status`.
|
|
@@ -111,6 +112,50 @@ function formatCommitmentRoster(roster) {
|
|
|
111
112
|
}
|
|
112
113
|
return lines;
|
|
113
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* One loop in a few lines, for a surface that lists many (`commitments operating-review`). It carries
|
|
117
|
+
* the same facts as `formatCommitmentLoopStatus` but in the order an owner asks them: is it armed,
|
|
118
|
+
* when does it next wake, why did it last stop, can it read what it watches, what did it run today.
|
|
119
|
+
* Every line is present when its fact is unknown, because "cannot see" must not read as "nothing to
|
|
120
|
+
* report" (OSK-15438, OSK-15412). Lines are returned unindented; the caller indents them.
|
|
121
|
+
*/
|
|
122
|
+
function formatLoopSummary(status) {
|
|
123
|
+
const lines = [];
|
|
124
|
+
const detail = status.loopDetail || (0, data_provider_1.describeCommitmentLoopState)(status.loopState);
|
|
125
|
+
lines.push(`loop: ${loopStateLabel(status.loopState)} — ${detail}${status.loopReason ? ` (${status.loopReason})` : ''}`);
|
|
126
|
+
const wake = status.nextWakes?.[0];
|
|
127
|
+
lines.push(`next wake: ${wake
|
|
128
|
+
? `${when(wake.at)} ${wake.kind}`
|
|
129
|
+
: status.loopState === 'unknown'
|
|
130
|
+
? 'could not be read'
|
|
131
|
+
: 'none — nothing is scheduled to wake it'}`);
|
|
132
|
+
if (status.lastStop) {
|
|
133
|
+
const stop = status.lastStop;
|
|
134
|
+
lines.push(`last stop: ${stop.question}${stop.reason ? ` — ${stop.reason}` : ''}${stop.detail ? `: ${stop.detail}` : ''}`);
|
|
135
|
+
if (stop.fix)
|
|
136
|
+
lines.push(`fix: ${stop.fix}`);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
lines.push('last stop: none recorded');
|
|
140
|
+
}
|
|
141
|
+
const watching = Array.isArray(status.watching) ? status.watching : [];
|
|
142
|
+
const trouble = watching
|
|
143
|
+
.filter((row) => row.access !== 'readable' || row.completeness === 'partial')
|
|
144
|
+
.map((row) => {
|
|
145
|
+
const access = row.access === 'blocked'
|
|
146
|
+
? 'CANNOT READ'
|
|
147
|
+
: row.access === 'unknown'
|
|
148
|
+
? 'not known'
|
|
149
|
+
: 'PARTIAL read';
|
|
150
|
+
return `${row.source} ${access}${row.reason ? ` (${row.reason})` : ''}`;
|
|
151
|
+
});
|
|
152
|
+
lines.push(`sources: ${!watching.length ? 'none declared' : trouble.length ? trouble.join('; ') : 'all readable'}`);
|
|
153
|
+
lines.push(`today: ${runs(status)} runs · spend ${spend(status)}`);
|
|
154
|
+
if (status.readErrors?.length) {
|
|
155
|
+
lines.push(`unread: ${status.readErrors.join(', ')} could not be read; treat the above as unknown`);
|
|
156
|
+
}
|
|
157
|
+
return lines;
|
|
158
|
+
}
|
|
114
159
|
/** One Commitment's loop, in full. */
|
|
115
160
|
function formatCommitmentLoopStatus(status) {
|
|
116
161
|
const lines = [];
|
|
@@ -58,10 +58,22 @@ function renderExecutionReceipt(receipt, log, verbose = false) {
|
|
|
58
58
|
log(` ${evidence.label}: ${location}`);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
// "Next" names an action only when the receipt has one. A reason that only
|
|
62
|
+
// restates what happened (a typed blocker's summary, which servers before
|
|
63
|
+
// OSK-16429 sent as the reason) is narrative, not something to do: the actor
|
|
64
|
+
// is printed without it and the narrative stays with the blocker.
|
|
65
|
+
const narrative = [receipt.summary, receipt.blocker?.summary].filter(Boolean);
|
|
66
|
+
const action = receipt.nextActor?.reason && !narrative.includes(receipt.nextActor.reason)
|
|
67
|
+
? receipt.nextActor.reason
|
|
68
|
+
: '';
|
|
61
69
|
if (receipt.nextActor)
|
|
62
|
-
log(`Next: ${receipt.nextActor.label || receipt.nextActor.kind}${
|
|
70
|
+
log(`Next: ${receipt.nextActor.label || receipt.nextActor.kind}${action ? ` — ${action}` : ''}`);
|
|
63
71
|
if (receipt.blocker) {
|
|
64
72
|
log(`Blocker: ${receipt.blocker.code}`);
|
|
73
|
+
// The headline already carries a typed blocker's summary; say it again only
|
|
74
|
+
// when it differs.
|
|
75
|
+
if (receipt.blocker.summary && receipt.blocker.summary !== receipt.summary)
|
|
76
|
+
log(` Detail: ${receipt.blocker.summary}`);
|
|
65
77
|
for (const evidenceRef of receipt.blocker.evidenceRefs || [])
|
|
66
78
|
log(` Evidence: ${evidenceRef}`);
|
|
67
79
|
}
|
|
@@ -18,7 +18,7 @@ import { type CommitmentObservationsQuery, type CommitmentObservationsResponse }
|
|
|
18
18
|
* "hold this state", derived from a Compass invariant — not authored here).
|
|
19
19
|
*/
|
|
20
20
|
import { COMMITMENT_WATCHED_SOURCES, COMMITMENT_TEMPLATE_IDS, COMMITMENT_CADENCE_MODES, COMMITMENT_CADENCE_TIME_PATTERN, COMMITMENT_CADENCE_MAX_TIMES, COMMITMENT_EXECUTION_ISOLATION_MODES, COMMITMENT_STATUSES, COMMITMENT_KIND_VALUES } from '@skrr-ai/data-provider';
|
|
21
|
-
import type { CommitmentCheck, CommitmentChecksResponse, Commitment, CommitmentActionProposal, CommitmentListResponse, CommitmentPlanResponse, CommitmentScheduleIssue, CommitmentStatusResponse, CommitmentWorkLink, CommitmentExecutionReceiptListResponse, CommitmentExecutionReceiptQuarterlyMetricsResponse, CommitmentCapabilitySource, CommitmentDeliveryPatchResponse, CommitmentDeliveryBundleResponse, CommitmentCyclesResponse, CommitmentExplanationResponse, CommitmentRunPermissions, CommitmentLoopStatus, CommitmentRoster } from '@skrr-ai/data-provider';
|
|
21
|
+
import type { CommitmentCheck, CommitmentChecksResponse, Commitment, CommitmentRevisionReceipt, CommitmentUpdateResponse, CommitmentActionProposal, CommitmentListResponse, CommitmentPlanResponse, CommitmentScheduleIssue, CommitmentStatusResponse, CommitmentWorkLink, CommitmentExecutionReceiptListResponse, CommitmentExecutionReceiptQuarterlyMetricsResponse, CommitmentCapabilitySource, CommitmentDeliveryPatchResponse, CommitmentDeliveryBundleResponse, CommitmentCyclesResponse, CommitmentExplanationResponse, CommitmentRunPermissions, CommitmentLoopStatus, CommitmentRoster } from '@skrr-ai/data-provider';
|
|
22
22
|
import { withQuery } from './triggers';
|
|
23
23
|
export { COMMITMENT_WATCHED_SOURCES, COMMITMENT_TEMPLATE_IDS, COMMITMENT_CADENCE_MODES, COMMITMENT_CADENCE_TIME_PATTERN, COMMITMENT_CADENCE_MAX_TIMES, COMMITMENT_STATUSES, COMMITMENT_KIND_VALUES, };
|
|
24
24
|
export type { CommitmentCheck, CommitmentChecksResponse, Commitment, CommitmentActionProposal, CommitmentListResponse, CommitmentPlanResponse, CommitmentStatusResponse, CommitmentWorkLink, };
|
|
@@ -276,7 +276,7 @@ export declare const commitmentApi: {
|
|
|
276
276
|
}) => Promise<Record<string, unknown>>;
|
|
277
277
|
readinessReceipt: (id: string, receiptId: string) => Promise<Record<string, unknown>>;
|
|
278
278
|
create: (body: Record<string, unknown>) => Promise<Commitment>;
|
|
279
|
-
update: (id: string, body: Record<string, unknown>) => Promise<
|
|
279
|
+
update: (id: string, body: Record<string, unknown>) => Promise<CommitmentUpdateResponse>;
|
|
280
280
|
draftPreflight: (body: Record<string, unknown>) => Promise<CommitmentPreflightReport>;
|
|
281
281
|
workLinks: (id: string, includeInactive?: boolean) => Promise<{
|
|
282
282
|
object: "commitment_work_link_list";
|
|
@@ -553,6 +553,10 @@ export interface CommitmentAccountingReadout {
|
|
|
553
553
|
costRunCount?: number;
|
|
554
554
|
costReportedRunCount?: number;
|
|
555
555
|
costUnavailableRunCount?: number;
|
|
556
|
+
/** Runs refused before any model turn: they cost nothing (OSK-15500). */
|
|
557
|
+
costNoSpendRunCount?: number;
|
|
558
|
+
/** …of which the verdict rests on inference, not a recorded refusal. */
|
|
559
|
+
costNoSpendInferredRunCount?: number;
|
|
556
560
|
costStatus?: 'reported' | 'partial' | 'unavailable' | 'not_applicable';
|
|
557
561
|
recommendedAttemptMultiplier?: number | null;
|
|
558
562
|
multiplierDerivation?: string;
|
|
@@ -573,6 +577,10 @@ export interface CommitmentCostWindow {
|
|
|
573
577
|
runCount?: number;
|
|
574
578
|
reportedRunCount?: number;
|
|
575
579
|
unavailableRunCount?: number;
|
|
580
|
+
/** Runs refused before any model turn: they cost nothing (OSK-15500). */
|
|
581
|
+
noSpendRunCount?: number;
|
|
582
|
+
/** …of which the verdict rests on inference, not a recorded refusal. */
|
|
583
|
+
noSpendInferredRunCount?: number;
|
|
576
584
|
costStatus?: 'reported' | 'partial' | 'unavailable' | 'not_applicable';
|
|
577
585
|
}
|
|
578
586
|
/**
|
|
@@ -846,6 +854,12 @@ export declare function withLiveAssessment(commitment: CommitmentView, operating
|
|
|
846
854
|
executorBlocked?: unknown;
|
|
847
855
|
firstActionableBlocker?: unknown;
|
|
848
856
|
} | null): CommitmentView;
|
|
857
|
+
/**
|
|
858
|
+
* The first line of `commitments update` text output. An update that changed
|
|
859
|
+
* nothing says so and names the version it left alone (OSK-16199); a server
|
|
860
|
+
* that predates the receipt is reported as updated, exactly as before.
|
|
861
|
+
*/
|
|
862
|
+
export declare function describeCommitmentRevision(revision: CommitmentRevisionReceipt | undefined): string;
|
|
849
863
|
export declare function renderCommitmentSummary(commitment: CommitmentView, log: (line: string) => void,
|
|
850
864
|
/**
|
|
851
865
|
* The most recent check, when the caller has one. Optional because pause /
|
package/dist/lib/commitments.js
CHANGED
|
@@ -25,6 +25,7 @@ exports.describeOperation = describeOperation;
|
|
|
25
25
|
exports.describeOperationFix = describeOperationFix;
|
|
26
26
|
exports.describeStoppedBy = describeStoppedBy;
|
|
27
27
|
exports.withLiveAssessment = withLiveAssessment;
|
|
28
|
+
exports.describeCommitmentRevision = describeCommitmentRevision;
|
|
28
29
|
exports.renderCommitmentSummary = renderCommitmentSummary;
|
|
29
30
|
exports.describeWakeDecision = describeWakeDecision;
|
|
30
31
|
exports.renderCheckList = renderCheckList;
|
|
@@ -661,6 +662,17 @@ function buildWatchedSources(inputs) {
|
|
|
661
662
|
}
|
|
662
663
|
return normalizeWatchedSourceDeclarations(rows);
|
|
663
664
|
}
|
|
665
|
+
/**
|
|
666
|
+
* The assessment's evidence state as one line. `unconfigured` read as if a source were broken (OSK-15412); it
|
|
667
|
+
* means no required success criterion is set, so nothing is being measured, which is a different thing to fix.
|
|
668
|
+
* The other states keep their one word.
|
|
669
|
+
*/
|
|
670
|
+
function describeAssessmentEvidence(evidence) {
|
|
671
|
+
if (evidence === 'unconfigured') {
|
|
672
|
+
return 'unconfigured — no required success criterion is set, so nothing is being measured (not a failing source)';
|
|
673
|
+
}
|
|
674
|
+
return evidence ?? 'unavailable';
|
|
675
|
+
}
|
|
664
676
|
/**
|
|
665
677
|
* One line an author can act on, or nothing.
|
|
666
678
|
*
|
|
@@ -1036,6 +1048,19 @@ function withLiveAssessment(commitment, operatingState) {
|
|
|
1036
1048
|
assessment: live,
|
|
1037
1049
|
};
|
|
1038
1050
|
}
|
|
1051
|
+
/**
|
|
1052
|
+
* The first line of `commitments update` text output. An update that changed
|
|
1053
|
+
* nothing says so and names the version it left alone (OSK-16199); a server
|
|
1054
|
+
* that predates the receipt is reported as updated, exactly as before.
|
|
1055
|
+
*/
|
|
1056
|
+
function describeCommitmentRevision(revision) {
|
|
1057
|
+
if (!revision || typeof revision.changed !== 'boolean')
|
|
1058
|
+
return 'Updated commitment:';
|
|
1059
|
+
if (!revision.changed) {
|
|
1060
|
+
return `No change: the commitment already matches; it stays at version ${revision.version}.`;
|
|
1061
|
+
}
|
|
1062
|
+
return `Updated commitment (version ${revision.previousVersion} -> ${revision.version}):`;
|
|
1063
|
+
}
|
|
1039
1064
|
function renderCommitmentSummary(commitment, log,
|
|
1040
1065
|
/**
|
|
1041
1066
|
* The most recent check, when the caller has one. Optional because pause /
|
|
@@ -1092,7 +1117,7 @@ scheduleIssues) {
|
|
|
1092
1117
|
? (0, data_provider_2.readCommitmentAssessment)(commitment.assessment)
|
|
1093
1118
|
: undefined;
|
|
1094
1119
|
log(`Assessment: ${assessment?.label ?? 'Unknown'}`);
|
|
1095
|
-
log(`Evidence: ${assessment?.evidence
|
|
1120
|
+
log(`Evidence: ${describeAssessmentEvidence(assessment?.evidence)}`);
|
|
1096
1121
|
log(`Operation: ${describeOperation(assessment?.operation, assessment?.operationReason)}`);
|
|
1097
1122
|
for (const line of describeOperationFix(commitment, assessment?.operation))
|
|
1098
1123
|
log(` ${line.label}: ${line.text}`);
|
|
@@ -1237,15 +1262,22 @@ function renderCheckList(checks, log) {
|
|
|
1237
1262
|
log('No checks recorded.');
|
|
1238
1263
|
return;
|
|
1239
1264
|
}
|
|
1265
|
+
// A compacted Check's open loops and action plan were stripped, so an empty
|
|
1266
|
+
// list there is "not retained", never "none" (OSK-15148). Its retained
|
|
1267
|
+
// explanation stands in for the stripped `whyNow`.
|
|
1268
|
+
const retainedCount = (c, list) => c.historyDetail && !list?.length ? '-' : String(list?.length ?? 0);
|
|
1240
1269
|
(0, format_1.renderTable)(checks.map((c) => ({
|
|
1241
1270
|
started: c.startedAt ?? '-',
|
|
1242
1271
|
kind: c.checkKind ?? '-',
|
|
1243
1272
|
status: c.status ?? '-',
|
|
1244
1273
|
posture: c.decision?.posture ?? '-',
|
|
1245
|
-
loops:
|
|
1246
|
-
actions:
|
|
1274
|
+
loops: retainedCount(c, c.openLoops),
|
|
1275
|
+
actions: retainedCount(c, c.actionPlan),
|
|
1247
1276
|
woke: describeWakeDecision(c),
|
|
1248
|
-
why: c.decision?.whyNow ??
|
|
1277
|
+
why: c.decision?.whyNow ??
|
|
1278
|
+
c.decision?.summary ??
|
|
1279
|
+
c.reason ??
|
|
1280
|
+
(c.historyDetail ? '(detail not retained)' : ''),
|
|
1249
1281
|
id: c.id ?? '-',
|
|
1250
1282
|
})), [
|
|
1251
1283
|
{ key: 'started', header: 'STARTED', maxWidth: 24 },
|
|
@@ -1261,6 +1293,9 @@ function renderCheckList(checks, log) {
|
|
|
1261
1293
|
// meant re-running the command with `--json` (OSK-6175).
|
|
1262
1294
|
{ key: 'id', header: 'ID', verbatim: true },
|
|
1263
1295
|
], log);
|
|
1296
|
+
if (checks.some((c) => c.historyDetail)) {
|
|
1297
|
+
log("'-' = not retained: an older check's detailed history was compacted; its stop and retained reason remain.");
|
|
1298
|
+
}
|
|
1264
1299
|
}
|
|
1265
1300
|
function records(value) {
|
|
1266
1301
|
return Array.isArray(value)
|
|
@@ -1636,10 +1671,15 @@ function formatCycleLatency(cycle) {
|
|
|
1636
1671
|
: history?.state === 'unavailable'
|
|
1637
1672
|
? [' Detailed history is unavailable; why it is missing was not recorded.']
|
|
1638
1673
|
: [];
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1674
|
+
// A hop whose inputs were compacted away without a retained projection is
|
|
1675
|
+
// "not retained": it may have been measured and shown before (OSK-15148).
|
|
1676
|
+
const unretained = new Set(latency?.unretainedStages || []);
|
|
1677
|
+
const missingFor = (stage) => unretained.has(stage)
|
|
1678
|
+
? 'not retained'
|
|
1679
|
+
: (latency?.pendingStages ? latency.pendingStages.includes(stage) : latency?.pending)
|
|
1680
|
+
? 'pending'
|
|
1681
|
+
: 'unmeasurable';
|
|
1682
|
+
if (latency?.measured === false && unretained.size === 0) {
|
|
1643
1683
|
return [...disclosure, ` latency: ${latency.pending ? 'pending' : 'unmeasurable'}`];
|
|
1644
1684
|
}
|
|
1645
1685
|
return [
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `skrr daemon <verb> [args]` as a person can paste it for THIS profile.
|
|
3
|
+
*
|
|
4
|
+
* `skrr daemon login` with no `--profile` acts on whichever profile the shell it
|
|
5
|
+
* is typed in resolves, which is the default one unless something says
|
|
6
|
+
* otherwise. So a hint printed after `skrr login --profile expd` that said only
|
|
7
|
+
* `skrr daemon login` sent people to sign a different daemon in and leave the
|
|
8
|
+
* one they had just set up exactly as it was (OSK-16435). The profile is named
|
|
9
|
+
* only when it is not the default, the way every other hint here spells it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function daemonCommandHint(verb: string, options?: {
|
|
12
|
+
bin?: string;
|
|
13
|
+
extraArgs?: string;
|
|
14
|
+
profile?: string;
|
|
15
|
+
}): string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.daemonCommandHint = daemonCommandHint;
|
|
4
|
+
const keychain_1 = require("./keychain");
|
|
5
|
+
/**
|
|
6
|
+
* `skrr daemon <verb> [args]` as a person can paste it for THIS profile.
|
|
7
|
+
*
|
|
8
|
+
* `skrr daemon login` with no `--profile` acts on whichever profile the shell it
|
|
9
|
+
* is typed in resolves, which is the default one unless something says
|
|
10
|
+
* otherwise. So a hint printed after `skrr login --profile expd` that said only
|
|
11
|
+
* `skrr daemon login` sent people to sign a different daemon in and leave the
|
|
12
|
+
* one they had just set up exactly as it was (OSK-16435). The profile is named
|
|
13
|
+
* only when it is not the default, the way every other hint here spells it.
|
|
14
|
+
*/
|
|
15
|
+
function daemonCommandHint(verb, options = {}) {
|
|
16
|
+
const bin = options.bin ?? 'skrr';
|
|
17
|
+
const profile = options.profile ?? (0, keychain_1.getActiveProfile)();
|
|
18
|
+
const scoped = profile && profile !== keychain_1.DEFAULT_PROFILE ? ` --profile ${profile}` : '';
|
|
19
|
+
const extra = options.extraArgs ? ` ${options.extraArgs}` : '';
|
|
20
|
+
return `${bin} daemon ${verb}${scoped}${extra}`;
|
|
21
|
+
}
|