@skrr-ai/cli 0.1.108 → 0.1.110
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 +74 -0
- package/dist/base-command.js +182 -24
- package/dist/commands/agents/triggers/create.js +1 -1
- package/dist/commands/agents/triggers/update.js +1 -1
- package/dist/commands/agents/update.d.ts +25 -0
- package/dist/commands/agents/update.js +98 -3
- 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/daemon/install.js +17 -1
- package/dist/commands/daemon/login.js +14 -0
- 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/harnesses/prune.d.ts +26 -0
- package/dist/commands/harnesses/prune.js +172 -0
- package/dist/commands/login.js +53 -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 +4 -1
- 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 +27 -2
- package/dist/commands/tasks/activity.js +17 -3
- 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 +25 -3
- package/dist/commands/tasks/rearm-recurrence.d.ts +22 -0
- package/dist/commands/tasks/rearm-recurrence.js +87 -0
- package/dist/commands/tasks/show.d.ts +9 -0
- package/dist/commands/tasks/show.js +79 -33
- package/dist/commands/tasks/timeline.js +4 -0
- package/dist/commands/tasks/unschedule.js +5 -1
- package/dist/commands/tasks/update.d.ts +1 -0
- package/dist/commands/tasks/update.js +17 -2
- package/dist/commands/triggers/audit.js +12 -3
- package/dist/commands/triggers/create.js +9 -3
- package/dist/commands/triggers/fire-detail.d.ts +7 -0
- package/dist/commands/triggers/fire-detail.js +30 -12
- package/dist/commands/triggers/fire.js +13 -3
- package/dist/commands/triggers/parse-intent.d.ts +7 -0
- package/dist/commands/triggers/parse-intent.js +31 -9
- package/dist/commands/triggers/update.js +1 -1
- package/dist/commands/workspaces/grants.js +20 -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 +5 -2
- package/dist/lib/commitments.d.ts +18 -3
- package/dist/lib/commitments.js +85 -45
- package/dist/lib/daemon-command-hint.d.ts +15 -0
- package/dist/lib/daemon-command-hint.js +21 -0
- package/dist/lib/daemon-machine-identity-copy.d.ts +14 -0
- package/dist/lib/daemon-machine-identity-copy.js +27 -0
- package/dist/lib/daemon-machine-identity.d.ts +27 -0
- package/dist/lib/daemon-machine-identity.js +61 -0
- package/dist/lib/daemon-setup.js +10 -1
- package/dist/lib/daemonBrokerRefusal.js +3 -2
- package/dist/lib/daemonHandoff.d.ts +19 -1
- package/dist/lib/daemonHandoff.js +61 -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/harnesses.d.ts +71 -0
- package/dist/lib/harnesses.js +70 -0
- 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 +10 -1
- package/dist/lib/list-output.js +22 -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 +39 -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 +15 -0
- package/dist/lib/task-economics.js +39 -2
- 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 +42 -1
- package/dist/lib/triggers.js +98 -2
- 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 +59 -0
- package/dist/lib/workflow-run-view.js +110 -0
- package/dist/lib/workspaces.d.ts +48 -4
- package/dist/lib/workspaces.js +55 -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/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +5 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.d.ts +60 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +130 -6
- 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/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.d.ts +60 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +128 -6
- 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 +7523 -7140
- package/oclif.manifest.json +473 -59
- package/package.json +28 -1
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 = [];
|
|
@@ -62,9 +62,12 @@ function renderExecutionReceipt(receipt, log, verbose = false) {
|
|
|
62
62
|
// restates what happened (a typed blocker's summary, which servers before
|
|
63
63
|
// OSK-16429 sent as the reason) is narrative, not something to do: the actor
|
|
64
64
|
// is printed without it and the narrative stays with the blocker.
|
|
65
|
+
// A bare reason code (servers before OSK-16451 sent `runtime_connection_unverifiable`
|
|
66
|
+
// there) is not an action either: the actor is printed alone.
|
|
65
67
|
const narrative = [receipt.summary, receipt.blocker?.summary].filter(Boolean);
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
+
const reason = receipt.nextActor?.reason;
|
|
69
|
+
const action = reason && !narrative.includes(reason) && !/^[A-Za-z0-9_.:-]+$/.test(reason.trim())
|
|
70
|
+
? reason
|
|
68
71
|
: '';
|
|
69
72
|
if (receipt.nextActor)
|
|
70
73
|
log(`Next: ${receipt.nextActor.label || receipt.nextActor.kind}${action ? ` — ${action}` : ''}`);
|
|
@@ -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
|
/**
|
|
@@ -824,8 +832,9 @@ export declare function describeOperation(operation: string | null | undefined,
|
|
|
824
832
|
* under "Fix:" read as a remedy nobody wrote (OSK-16230). That narrative is
|
|
825
833
|
* labelled `Blocker:` instead, and no remedy is made up for it.
|
|
826
834
|
*/
|
|
835
|
+
type OperationFixLabel = 'Fix' | 'Blocker' | 'Last run';
|
|
827
836
|
export declare function describeOperationFix(commitment: CommitmentView, operation: string | null | undefined): Array<{
|
|
828
|
-
label:
|
|
837
|
+
label: OperationFixLabel;
|
|
829
838
|
text: string;
|
|
830
839
|
}>;
|
|
831
840
|
/**
|
|
@@ -846,6 +855,12 @@ export declare function withLiveAssessment(commitment: CommitmentView, operating
|
|
|
846
855
|
executorBlocked?: unknown;
|
|
847
856
|
firstActionableBlocker?: unknown;
|
|
848
857
|
} | null): CommitmentView;
|
|
858
|
+
/**
|
|
859
|
+
* The first line of `commitments update` text output. An update that changed
|
|
860
|
+
* nothing says so and names the version it left alone (OSK-16199); a server
|
|
861
|
+
* that predates the receipt is reported as updated, exactly as before.
|
|
862
|
+
*/
|
|
863
|
+
export declare function describeCommitmentRevision(revision: CommitmentRevisionReceipt | undefined): string;
|
|
849
864
|
export declare function renderCommitmentSummary(commitment: CommitmentView, log: (line: string) => void,
|
|
850
865
|
/**
|
|
851
866
|
* 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
|
*
|
|
@@ -946,40 +958,38 @@ function describeOperation(operation, reason) {
|
|
|
946
958
|
};
|
|
947
959
|
return `not running — ${meaning[status] ?? status}`;
|
|
948
960
|
}
|
|
949
|
-
/**
|
|
950
|
-
* What to do about an operation that is not running, from the operating state's
|
|
951
|
-
* first actionable blocker (`withLiveAssessment` carries it onto the view).
|
|
952
|
-
*
|
|
953
|
-
* `show` printed `Operation: blocked — typed_external_blocker` and nothing else,
|
|
954
|
-
* so the one line a reader acts on was a code; the remediation the server
|
|
955
|
-
* already composed sat only in `--json` and `doctor` (OSK-16212). Nothing is
|
|
956
|
-
* printed while the operation is ready or working, where a fix would contradict
|
|
957
|
-
* the line above it.
|
|
958
|
-
*
|
|
959
|
-
* Returns one or two lines. `Fix:` only when the blocker carries an action; a
|
|
960
|
-
* typed blocker an agent returned carries only what happened, which printed
|
|
961
|
-
* under "Fix:" read as a remedy nobody wrote (OSK-16230). That narrative is
|
|
962
|
-
* labelled `Blocker:` instead, and no remedy is made up for it.
|
|
963
|
-
*/
|
|
964
961
|
function describeOperationFix(commitment, operation) {
|
|
965
962
|
if (operation === 'ready' || operation === 'working')
|
|
966
963
|
return [];
|
|
967
964
|
const blocker = commitment.firstActionableBlocker;
|
|
968
|
-
if (!blocker)
|
|
969
|
-
return [];
|
|
970
|
-
const remediation = typeof blocker.remediation === 'string' ? blocker.remediation.trim() : '';
|
|
971
|
-
const detail = typeof blocker.detail === 'string' ? blocker.detail.trim() : '';
|
|
972
|
-
const who = typeof blocker.ownerLabel === 'string' && blocker.ownerLabel
|
|
973
|
-
? blocker.ownerLabel
|
|
974
|
-
: typeof blocker.owner === 'string' && blocker.owner
|
|
975
|
-
? blocker.owner
|
|
976
|
-
: '';
|
|
977
965
|
const lines = [];
|
|
978
|
-
|
|
979
|
-
if (
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
966
|
+
let remediation = '';
|
|
967
|
+
if (blocker) {
|
|
968
|
+
remediation = typeof blocker.remediation === 'string' ? blocker.remediation.trim() : '';
|
|
969
|
+
const detail = typeof blocker.detail === 'string' ? blocker.detail.trim() : '';
|
|
970
|
+
const who = typeof blocker.ownerLabel === 'string' && blocker.ownerLabel
|
|
971
|
+
? blocker.ownerLabel
|
|
972
|
+
: typeof blocker.owner === 'string' && blocker.owner
|
|
973
|
+
? blocker.owner
|
|
974
|
+
: '';
|
|
975
|
+
// The detail is shown only when the fix does not already say it.
|
|
976
|
+
if (detail && detail !== remediation)
|
|
977
|
+
lines.push({ label: 'Blocker', text: `${detail}${!remediation && who ? ` (${who})` : ''}` });
|
|
978
|
+
if (remediation)
|
|
979
|
+
lines.push({ label: 'Fix', text: `${remediation}${who ? ` (${who})` : ''}` });
|
|
980
|
+
}
|
|
981
|
+
// What refused the last Run is a second fact, not an alternative to the
|
|
982
|
+
// blocker: a paused Commitment whose Run was refused needs both a resume and
|
|
983
|
+
// the fix, or resuming only fails again (OSK-15389). The sentence is the
|
|
984
|
+
// server's, derived on read from the failure code; it is printed whenever it
|
|
985
|
+
// exists and says something the blocker's fix does not.
|
|
986
|
+
const liveness = commitment.state?.liveness;
|
|
987
|
+
const lastRunFix = liveness && liveness.current !== false && typeof liveness.diagnosisFix === 'string'
|
|
988
|
+
? liveness.diagnosisFix.trim()
|
|
989
|
+
: '';
|
|
990
|
+
if (lastRunFix && lastRunFix !== remediation) {
|
|
991
|
+
lines.push({ label: lines.length ? 'Last run' : 'Fix', text: lastRunFix });
|
|
992
|
+
}
|
|
983
993
|
return lines;
|
|
984
994
|
}
|
|
985
995
|
/**
|
|
@@ -1036,6 +1046,19 @@ function withLiveAssessment(commitment, operatingState) {
|
|
|
1036
1046
|
assessment: live,
|
|
1037
1047
|
};
|
|
1038
1048
|
}
|
|
1049
|
+
/**
|
|
1050
|
+
* The first line of `commitments update` text output. An update that changed
|
|
1051
|
+
* nothing says so and names the version it left alone (OSK-16199); a server
|
|
1052
|
+
* that predates the receipt is reported as updated, exactly as before.
|
|
1053
|
+
*/
|
|
1054
|
+
function describeCommitmentRevision(revision) {
|
|
1055
|
+
if (!revision || typeof revision.changed !== 'boolean')
|
|
1056
|
+
return 'Updated commitment:';
|
|
1057
|
+
if (!revision.changed) {
|
|
1058
|
+
return `No change: the commitment already matches; it stays at version ${revision.version}.`;
|
|
1059
|
+
}
|
|
1060
|
+
return `Updated commitment (version ${revision.previousVersion} -> ${revision.version}):`;
|
|
1061
|
+
}
|
|
1039
1062
|
function renderCommitmentSummary(commitment, log,
|
|
1040
1063
|
/**
|
|
1041
1064
|
* The most recent check, when the caller has one. Optional because pause /
|
|
@@ -1092,7 +1115,7 @@ scheduleIssues) {
|
|
|
1092
1115
|
? (0, data_provider_2.readCommitmentAssessment)(commitment.assessment)
|
|
1093
1116
|
: undefined;
|
|
1094
1117
|
log(`Assessment: ${assessment?.label ?? 'Unknown'}`);
|
|
1095
|
-
log(`Evidence: ${assessment?.evidence
|
|
1118
|
+
log(`Evidence: ${describeAssessmentEvidence(assessment?.evidence)}`);
|
|
1096
1119
|
log(`Operation: ${describeOperation(assessment?.operation, assessment?.operationReason)}`);
|
|
1097
1120
|
for (const line of describeOperationFix(commitment, assessment?.operation))
|
|
1098
1121
|
log(` ${line.label}: ${line.text}`);
|
|
@@ -1237,15 +1260,22 @@ function renderCheckList(checks, log) {
|
|
|
1237
1260
|
log('No checks recorded.');
|
|
1238
1261
|
return;
|
|
1239
1262
|
}
|
|
1263
|
+
// A compacted Check's open loops and action plan were stripped, so an empty
|
|
1264
|
+
// list there is "not retained", never "none" (OSK-15148). Its retained
|
|
1265
|
+
// explanation stands in for the stripped `whyNow`.
|
|
1266
|
+
const retainedCount = (c, list) => c.historyDetail && !list?.length ? '-' : String(list?.length ?? 0);
|
|
1240
1267
|
(0, format_1.renderTable)(checks.map((c) => ({
|
|
1241
1268
|
started: c.startedAt ?? '-',
|
|
1242
1269
|
kind: c.checkKind ?? '-',
|
|
1243
1270
|
status: c.status ?? '-',
|
|
1244
1271
|
posture: c.decision?.posture ?? '-',
|
|
1245
|
-
loops:
|
|
1246
|
-
actions:
|
|
1272
|
+
loops: retainedCount(c, c.openLoops),
|
|
1273
|
+
actions: retainedCount(c, c.actionPlan),
|
|
1247
1274
|
woke: describeWakeDecision(c),
|
|
1248
|
-
why: c.decision?.whyNow ??
|
|
1275
|
+
why: c.decision?.whyNow ??
|
|
1276
|
+
c.decision?.summary ??
|
|
1277
|
+
c.reason ??
|
|
1278
|
+
(c.historyDetail ? '(detail not retained)' : ''),
|
|
1249
1279
|
id: c.id ?? '-',
|
|
1250
1280
|
})), [
|
|
1251
1281
|
{ key: 'started', header: 'STARTED', maxWidth: 24 },
|
|
@@ -1261,6 +1291,16 @@ function renderCheckList(checks, log) {
|
|
|
1261
1291
|
// meant re-running the command with `--json` (OSK-6175).
|
|
1262
1292
|
{ key: 'id', header: 'ID', verbatim: true },
|
|
1263
1293
|
], log);
|
|
1294
|
+
// The legend states each distinct reason the server recorded, in the same
|
|
1295
|
+
// words explain and cycles use; it never guesses "compacted" for a Check
|
|
1296
|
+
// whose history is merely missing (OSK-15148).
|
|
1297
|
+
const reasons = [
|
|
1298
|
+
...new Set(checks
|
|
1299
|
+
.map((c) => (0, data_provider_2.describeCommitmentHistoryDetail)(c.historyDetail))
|
|
1300
|
+
.filter((sentence) => Boolean(sentence))),
|
|
1301
|
+
];
|
|
1302
|
+
if (reasons.length > 0)
|
|
1303
|
+
log(`'-' = not retained. ${reasons.join(' ')}`);
|
|
1264
1304
|
}
|
|
1265
1305
|
function records(value) {
|
|
1266
1306
|
return Array.isArray(value)
|
|
@@ -1628,18 +1668,18 @@ function formatCommitmentCycles(page) {
|
|
|
1628
1668
|
*/
|
|
1629
1669
|
function formatCycleLatency(cycle) {
|
|
1630
1670
|
const latency = cycle.latency;
|
|
1631
|
-
|
|
1632
|
-
const
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
if (latency?.measured === false) {
|
|
1671
|
+
// One sentence for every surface, from the server's own record (OSK-15148).
|
|
1672
|
+
const historySentence = (0, data_provider_2.describeCommitmentHistoryDetail)(cycle.historyDetail);
|
|
1673
|
+
const disclosure = historySentence ? [` ${historySentence}`] : [];
|
|
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
|
+
}
|