@skrr-ai/cli 0.1.105 → 0.1.107
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/commands/agents/operating-plan.js +1 -1
- package/dist/commands/commitments/doctor.js +2 -1
- package/dist/commands/daemon/logs.d.ts +32 -0
- package/dist/commands/daemon/logs.js +117 -15
- package/dist/commands/proposals/list.d.ts +7 -0
- package/dist/commands/proposals/list.js +11 -0
- package/dist/commands/tasks/retry-schedule.js +11 -1
- package/dist/lib/commitment-product.js +6 -0
- package/dist/lib/commitments.d.ts +10 -2
- package/dist/lib/commitments.js +21 -10
- package/dist/node_modules/@skrr-ai/data-provider/index.js +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
|
@@ -44,7 +44,7 @@ class AgentOperatingPlan extends base_command_1.BaseCommand {
|
|
|
44
44
|
this.log(` Evaluation: ${w.status} — ${w.reason}${w.nextAt ? `; next ${w.nextAt}` : ''}`);
|
|
45
45
|
this.log(` Next intervention: ${r.lifecycle !== 'active' ? 'inactive; requires activation' : r.nextIntervention?.nextAt || r.nextIntervention?.status || 'not recorded'}`);
|
|
46
46
|
if (r.blocker)
|
|
47
|
-
this.log(` Blocker: ${r.blocker.code} — ${r.blocker.remediation || ''}`);
|
|
47
|
+
this.log(` Blocker: ${r.blocker.code} — ${r.blocker.remediation || r.blocker.detail || ''}`);
|
|
48
48
|
}
|
|
49
49
|
for (const line of (0, operating_plan_loops_1.planningLoopLines)(plan.planning.loops, plan.planning.todos.length))
|
|
50
50
|
this.log(line);
|
|
@@ -16,7 +16,8 @@ function doctorVerdict(input) {
|
|
|
16
16
|
const lifecycle = /^status_([a-z_]+)$/.exec(String(blocker.code));
|
|
17
17
|
return {
|
|
18
18
|
verdict: lifecycle ? lifecycle[1] : blocker.code === 'deleted' ? 'deleted' : 'blocked',
|
|
19
|
-
|
|
19
|
+
// A typed blocker carries no remedy, only what happened (OSK-16230).
|
|
20
|
+
next: String(blocker.remediation || blocker.detail || 'Inspect the Commitment'),
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
if (operatingState.status === 'waiting_approval') {
|
|
@@ -124,6 +124,13 @@ export declare const LIVE_LOG_RESOLUTION_RUNTIME_VERSION = "0.8.119";
|
|
|
124
124
|
* that file and its rotated siblings itself.
|
|
125
125
|
*/
|
|
126
126
|
export declare const ROTATED_LOGS_RUNTIME_VERSION = "0.8.120";
|
|
127
|
+
/**
|
|
128
|
+
* The first runtime whose `logs --rotated --lines N` says where a window that
|
|
129
|
+
* cut older lines starts, and whose `logs --path` names the rotated siblings
|
|
130
|
+
* on stderr (OSK-16228). 0.8.120 knows `--rotated` but says neither, so for it
|
|
131
|
+
* the CLI reads the files itself, exactly as it does for an older runtime.
|
|
132
|
+
*/
|
|
133
|
+
export declare const ROTATED_WINDOW_NOTE_RUNTIME_VERSION = "0.8.121";
|
|
127
134
|
/**
|
|
128
135
|
* Rotated siblings of a log, OLDEST FIRST (OSK-16214). The daemon's logger
|
|
129
136
|
* renames `daemon.app.log` to `daemon.app.1.log`, `.1` to `.2` and so on at
|
|
@@ -146,6 +153,31 @@ export declare function readLastLines(filePath: string, maxLines: number): strin
|
|
|
146
153
|
* file. A file renamed away between the listing and the read is skipped.
|
|
147
154
|
*/
|
|
148
155
|
export declare function readLastLinesAcross(files: readonly string[], maxLines: number): string[];
|
|
156
|
+
/**
|
|
157
|
+
* Where a `--rotated --lines N` window began when it left older lines out
|
|
158
|
+
* (OSK-16228). `startLine` is 1-based within `file`; `totalLines` counts the
|
|
159
|
+
* whole set. A copy of `daemon/src/rotated-log-files.ts`, like the helpers above.
|
|
160
|
+
*/
|
|
161
|
+
export interface LogWindowCut {
|
|
162
|
+
file: string;
|
|
163
|
+
startLine: number;
|
|
164
|
+
totalLines: number;
|
|
165
|
+
}
|
|
166
|
+
/** Lines in a file, split as `readLastLines` splits them; read in chunks. */
|
|
167
|
+
export declare function countLines(filePath: string): number;
|
|
168
|
+
/**
|
|
169
|
+
* `readLastLinesAcross`, plus where the window began when it cut older lines.
|
|
170
|
+
* Without it, `--rotated --lines 20000 | grep <id>` for an id 90k lines back
|
|
171
|
+
* printed nothing and said nothing, which reads as "never logged". Lines are
|
|
172
|
+
* counted only once the window is full, and only from the file it stopped in
|
|
173
|
+
* back.
|
|
174
|
+
*/
|
|
175
|
+
export declare function readLogWindow(files: readonly string[], maxLines: number): {
|
|
176
|
+
lines: string[];
|
|
177
|
+
cut: LogWindowCut | null;
|
|
178
|
+
};
|
|
179
|
+
/** The stderr sentence for a window that left older lines out. */
|
|
180
|
+
export declare function describeLogWindowCut(cut: LogWindowCut, shown: number): string;
|
|
149
181
|
/** Every byte of `files`, in order, to `out` — streamed, honouring back-pressure. */
|
|
150
182
|
export declare function streamLogFiles(files: readonly string[], out: NodeJS.WritableStream): Promise<void>;
|
|
151
183
|
/** `x.y.z` from a version string, or null. A prerelease suffix is ignored. */
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.STDERR_FLAG_DESCRIPTION = exports.ROTATED_LOGS_RUNTIME_VERSION = exports.LIVE_LOG_RESOLUTION_RUNTIME_VERSION = void 0;
|
|
6
|
+
exports.STDERR_FLAG_DESCRIPTION = exports.ROTATED_WINDOW_NOTE_RUNTIME_VERSION = exports.ROTATED_LOGS_RUNTIME_VERSION = exports.LIVE_LOG_RESOLUTION_RUNTIME_VERSION = void 0;
|
|
7
7
|
exports.describeLogAge = describeLogAge;
|
|
8
8
|
exports.resolveLegacyDaemonLogPath = resolveLegacyDaemonLogPath;
|
|
9
9
|
exports.resolveDaemonLogsProfile = resolveDaemonLogsProfile;
|
|
@@ -15,6 +15,9 @@ exports.rotatedLogSiblings = rotatedLogSiblings;
|
|
|
15
15
|
exports.describeRotatedSiblings = describeRotatedSiblings;
|
|
16
16
|
exports.readLastLines = readLastLines;
|
|
17
17
|
exports.readLastLinesAcross = readLastLinesAcross;
|
|
18
|
+
exports.countLines = countLines;
|
|
19
|
+
exports.readLogWindow = readLogWindow;
|
|
20
|
+
exports.describeLogWindowCut = describeLogWindowCut;
|
|
18
21
|
exports.streamLogFiles = streamLogFiles;
|
|
19
22
|
exports.parseRuntimeVersion = parseRuntimeVersion;
|
|
20
23
|
exports.runtimeVersionAtLeast = runtimeVersionAtLeast;
|
|
@@ -228,6 +231,13 @@ exports.LIVE_LOG_RESOLUTION_RUNTIME_VERSION = '0.8.119';
|
|
|
228
231
|
* that file and its rotated siblings itself.
|
|
229
232
|
*/
|
|
230
233
|
exports.ROTATED_LOGS_RUNTIME_VERSION = '0.8.120';
|
|
234
|
+
/**
|
|
235
|
+
* The first runtime whose `logs --rotated --lines N` says where a window that
|
|
236
|
+
* cut older lines starts, and whose `logs --path` names the rotated siblings
|
|
237
|
+
* on stderr (OSK-16228). 0.8.120 knows `--rotated` but says neither, so for it
|
|
238
|
+
* the CLI reads the files itself, exactly as it does for an older runtime.
|
|
239
|
+
*/
|
|
240
|
+
exports.ROTATED_WINDOW_NOTE_RUNTIME_VERSION = '0.8.121';
|
|
231
241
|
/**
|
|
232
242
|
* Rotated siblings of a log, OLDEST FIRST (OSK-16214). The daemon's logger
|
|
233
243
|
* renames `daemon.app.log` to `daemon.app.1.log`, `.1` to `.2` and so on at
|
|
@@ -304,19 +314,90 @@ function readLastLines(filePath, maxLines) {
|
|
|
304
314
|
* file. A file renamed away between the listing and the read is skipped.
|
|
305
315
|
*/
|
|
306
316
|
function readLastLinesAcross(files, maxLines) {
|
|
317
|
+
return readLogWindow(files, maxLines).lines;
|
|
318
|
+
}
|
|
319
|
+
/** Lines in a file, split as `readLastLines` splits them; read in chunks. */
|
|
320
|
+
function countLines(filePath) {
|
|
321
|
+
const descriptor = (0, node_fs_1.openSync)(filePath, 'r');
|
|
322
|
+
try {
|
|
323
|
+
const buffer = Buffer.alloc(64 * 1024);
|
|
324
|
+
let count = 0;
|
|
325
|
+
let last = -1;
|
|
326
|
+
let position = 0;
|
|
327
|
+
for (;;) {
|
|
328
|
+
const read = (0, node_fs_1.readSync)(descriptor, buffer, 0, buffer.length, position);
|
|
329
|
+
if (read === 0)
|
|
330
|
+
break;
|
|
331
|
+
position += read;
|
|
332
|
+
for (let i = 0; i < read; i += 1)
|
|
333
|
+
if (buffer[i] === 0x0a)
|
|
334
|
+
count += 1;
|
|
335
|
+
last = buffer[read - 1];
|
|
336
|
+
}
|
|
337
|
+
return last === -1 || last === 0x0a ? count : count + 1;
|
|
338
|
+
}
|
|
339
|
+
finally {
|
|
340
|
+
(0, node_fs_1.closeSync)(descriptor);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* `readLastLinesAcross`, plus where the window began when it cut older lines.
|
|
345
|
+
* Without it, `--rotated --lines 20000 | grep <id>` for an id 90k lines back
|
|
346
|
+
* printed nothing and said nothing, which reads as "never logged". Lines are
|
|
347
|
+
* counted only once the window is full, and only from the file it stopped in
|
|
348
|
+
* back.
|
|
349
|
+
*/
|
|
350
|
+
function readLogWindow(files, maxLines) {
|
|
307
351
|
const collected = [];
|
|
308
352
|
let remaining = maxLines;
|
|
353
|
+
let boundary = null;
|
|
309
354
|
for (let i = files.length - 1; i >= 0 && remaining > 0; i -= 1) {
|
|
310
355
|
try {
|
|
311
356
|
const lines = readLastLines(files[i], remaining);
|
|
312
357
|
collected.unshift(lines);
|
|
313
358
|
remaining -= lines.length;
|
|
359
|
+
if (remaining <= 0)
|
|
360
|
+
boundary = { index: i, taken: lines.length };
|
|
314
361
|
}
|
|
315
362
|
catch {
|
|
316
363
|
// Rotated away mid-read; the rest are still worth showing.
|
|
317
364
|
}
|
|
318
365
|
}
|
|
319
|
-
|
|
366
|
+
const lines = collected.flat();
|
|
367
|
+
if (boundary === null)
|
|
368
|
+
return { lines, cut: null };
|
|
369
|
+
let boundaryLines;
|
|
370
|
+
try {
|
|
371
|
+
boundaryLines = countLines(files[boundary.index]);
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
return { lines, cut: null };
|
|
375
|
+
}
|
|
376
|
+
let olderLines = 0;
|
|
377
|
+
for (const file of files.slice(0, boundary.index)) {
|
|
378
|
+
try {
|
|
379
|
+
olderLines += countLines(file);
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
// Rotated away since the read; it is not in the window either way.
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const startLine = Math.max(1, boundaryLines - boundary.taken + 1);
|
|
386
|
+
if (startLine === 1 && olderLines === 0)
|
|
387
|
+
return { lines, cut: null };
|
|
388
|
+
return {
|
|
389
|
+
lines,
|
|
390
|
+
cut: {
|
|
391
|
+
file: files[boundary.index],
|
|
392
|
+
startLine,
|
|
393
|
+
totalLines: olderLines + (startLine - 1) + lines.length,
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
/** The stderr sentence for a window that left older lines out. */
|
|
398
|
+
function describeLogWindowCut(cut, shown) {
|
|
399
|
+
return (`Showing the last ${shown} of ${cut.totalLines} lines; the window starts at line ` +
|
|
400
|
+
`${cut.startLine} of ${node_path_1.default.basename(cut.file)}. Drop --lines to read the full history.`);
|
|
320
401
|
}
|
|
321
402
|
/** Every byte of `files`, in order, to `out` — streamed, honouring back-pressure. */
|
|
322
403
|
async function streamLogFiles(files, out) {
|
|
@@ -623,30 +704,46 @@ class DaemonLogs extends core_1.Command {
|
|
|
623
704
|
const runtimeKnowsRotated = plan.kind === 'forward' &&
|
|
624
705
|
plan.runtimeVersion !== null &&
|
|
625
706
|
runtimeVersionAtLeast(plan.runtimeVersion, exports.ROTATED_LOGS_RUNTIME_VERSION) === true;
|
|
626
|
-
|
|
707
|
+
// OSK-16228: a `--rotated --lines N` window that cuts older lines, and
|
|
708
|
+
// `--path` with rotated siblings beside it, are said on stderr only from
|
|
709
|
+
// ROTATED_WINDOW_NOTE_RUNTIME_VERSION. Below it, read the files here.
|
|
710
|
+
const runtimeSaysWindow = plan.kind === 'forward' &&
|
|
711
|
+
plan.runtimeVersion !== null &&
|
|
712
|
+
runtimeVersionAtLeast(plan.runtimeVersion, exports.ROTATED_WINDOW_NOTE_RUNTIME_VERSION) === true;
|
|
713
|
+
if (plan.kind === 'forward' && !runtimeSaysWindow && (flags.rotated || flags.path)) {
|
|
627
714
|
const resolved = await resolveForwardedLogPath(flags);
|
|
628
|
-
if (
|
|
715
|
+
if (resolved) {
|
|
716
|
+
plan = {
|
|
717
|
+
kind: 'read',
|
|
718
|
+
path: resolved,
|
|
719
|
+
note: null,
|
|
720
|
+
missingHint: 'The live daemon has not written it yet; `skrr daemon status` shows whether one is running.',
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
else if (flags.rotated && !runtimeKnowsRotated) {
|
|
629
724
|
this.error(`The installed runtime${plan.runtimeVersion ? ` (${plan.runtimeVersion})` : ''} predates ` +
|
|
630
725
|
`--rotated (${exports.ROTATED_LOGS_RUNTIME_VERSION}) and did not name its log file, so its ` +
|
|
631
726
|
'rotated files cannot be found. Upgrade it (`skrr daemon update`), or list the ' +
|
|
632
727
|
'directory `skrr daemon logs --path` prints.', { exit: 1 });
|
|
633
728
|
}
|
|
634
|
-
|
|
635
|
-
kind: 'read',
|
|
636
|
-
path: resolved,
|
|
637
|
-
note: null,
|
|
638
|
-
missingHint: 'The live daemon has not written it yet; `skrr daemon status` shows whether one is running.',
|
|
639
|
-
};
|
|
729
|
+
// Otherwise forward: the runtime still answers, just without the note.
|
|
640
730
|
}
|
|
641
731
|
if (plan.kind === 'read') {
|
|
642
732
|
const logPath = plan.path;
|
|
643
733
|
const siblings = rotatedLogSiblings(logPath);
|
|
644
|
-
// `--path` prints the one path and nothing else
|
|
645
|
-
//
|
|
734
|
+
// `--path` prints the one path and nothing else on stdout — under
|
|
735
|
+
// --rotated, every file oldest first, the current one last. Without
|
|
736
|
+
// --rotated, stderr names the siblings a grep of that path would miss
|
|
737
|
+
// (OSK-16228).
|
|
646
738
|
if (flags.path) {
|
|
647
739
|
for (const file of flags.rotated ? siblings : [])
|
|
648
740
|
this.log(file);
|
|
649
741
|
this.log(logPath);
|
|
742
|
+
if (!flags.rotated) {
|
|
743
|
+
const hint = describeRotatedSiblings(logPath, siblings);
|
|
744
|
+
if (hint)
|
|
745
|
+
this.logToStderr(hint);
|
|
746
|
+
}
|
|
650
747
|
return;
|
|
651
748
|
}
|
|
652
749
|
if (plan.note)
|
|
@@ -668,11 +765,16 @@ class DaemonLogs extends core_1.Command {
|
|
|
668
765
|
await streamLogFiles([...siblings, logPath], process.stdout);
|
|
669
766
|
}
|
|
670
767
|
else {
|
|
671
|
-
const
|
|
672
|
-
?
|
|
673
|
-
:
|
|
768
|
+
const window = flags.rotated
|
|
769
|
+
? readLogWindow([...siblings, logPath], flags.lines ?? 200)
|
|
770
|
+
: null;
|
|
771
|
+
const lines = window ? window.lines : readDaemonLogTail(logPath, flags.lines ?? 200);
|
|
674
772
|
if (lines.length > 0)
|
|
675
773
|
this.log(lines.join('\n'));
|
|
774
|
+
// OSK-16228: after the content, so it is the last thing a terminal
|
|
775
|
+
// shows, and on stderr, so a grep over stdout is unchanged.
|
|
776
|
+
if (window?.cut)
|
|
777
|
+
this.logToStderr(describeLogWindowCut(window.cut, lines.length));
|
|
676
778
|
}
|
|
677
779
|
if (flags.follow) {
|
|
678
780
|
let position = (0, node_fs_1.statSync)(logPath).size;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ProposalView } from '@skrr-ai/data-provider';
|
|
1
2
|
import { BaseCommand } from '../../base-command';
|
|
2
3
|
/**
|
|
3
4
|
* What an empty page means. With a filter AND a next cursor it does NOT mean "none": the inbox reads a
|
|
@@ -6,6 +7,12 @@ import { BaseCommand } from '../../base-command';
|
|
|
6
7
|
* Without a cursor the inbox is exhausted and "none" is true.
|
|
7
8
|
*/
|
|
8
9
|
export declare function emptyPageLines(narrowed: boolean, nextCursor: string | null | undefined): string[];
|
|
10
|
+
/**
|
|
11
|
+
* What the row is ABOUT: the Commitment and the Agent behind it, in the order the web inbox shows them. 51 distinct titles covered 747 decidable
|
|
12
|
+
* initiatives, so the title alone could not tell two rows apart (OSK-15378). A dash is "not named" (an id the
|
|
13
|
+
* server could not read for this viewer is in `--json`), never "none".
|
|
14
|
+
*/
|
|
15
|
+
export declare function aboutFor(row: ProposalView): string;
|
|
9
16
|
export default class ProposalsList extends BaseCommand {
|
|
10
17
|
static description: string;
|
|
11
18
|
static flags: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.emptyPageLines = emptyPageLines;
|
|
4
|
+
exports.aboutFor = aboutFor;
|
|
4
5
|
const core_1 = require("@oclif/core");
|
|
5
6
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
6
7
|
const base_command_1 = require("../../base-command");
|
|
@@ -23,6 +24,14 @@ function emptyPageLines(narrowed, nextCursor) {
|
|
|
23
24
|
]
|
|
24
25
|
: ['No proposals on this page; the cursor below continues.'];
|
|
25
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* What the row is ABOUT: the Commitment and the Agent behind it, in the order the web inbox shows them. 51 distinct titles covered 747 decidable
|
|
29
|
+
* initiatives, so the title alone could not tell two rows apart (OSK-15378). A dash is "not named" (an id the
|
|
30
|
+
* server could not read for this viewer is in `--json`), never "none".
|
|
31
|
+
*/
|
|
32
|
+
function aboutFor(row) {
|
|
33
|
+
return [row.commitmentTitle, row.agent?.name].filter(Boolean).join(' · ') || '—';
|
|
34
|
+
}
|
|
26
35
|
/** A snoozed proposal reads `proposed` in its disposition; the wake time is what tells them apart. */
|
|
27
36
|
function noteFor(row) {
|
|
28
37
|
if (row.sourceStatus === 'snoozed' && row.snoozedUntil)
|
|
@@ -77,6 +86,7 @@ class ProposalsList extends base_command_1.BaseCommand {
|
|
|
77
86
|
(0, format_1.renderTable)(result.data.map((row) => ({
|
|
78
87
|
id: row.id,
|
|
79
88
|
title: row.title,
|
|
89
|
+
about: aboutFor(row),
|
|
80
90
|
disposition: row.disposition,
|
|
81
91
|
// Most proposals carry no estimate; "unknown" read as a failure to determine one (OSK-15377).
|
|
82
92
|
basis: row.economics?.basis || '—',
|
|
@@ -85,6 +95,7 @@ class ProposalsList extends base_command_1.BaseCommand {
|
|
|
85
95
|
})), [
|
|
86
96
|
{ key: 'id', header: 'PROPOSAL', verbatim: true },
|
|
87
97
|
{ key: 'title', header: 'TITLE' },
|
|
98
|
+
{ key: 'about', header: 'ABOUT', maxWidth: 48 },
|
|
88
99
|
{ key: 'disposition', header: 'DISPOSITION' },
|
|
89
100
|
{ key: 'basis', header: 'BASIS' },
|
|
90
101
|
{ key: 'deadline', header: 'DEADLINE' },
|
|
@@ -40,7 +40,17 @@ class TasksRetrySchedule extends base_command_1.BaseCommand {
|
|
|
40
40
|
const t = result;
|
|
41
41
|
const ref = t.identifier ?? (t.taskNumber ? `#${t.taskNumber}` : '');
|
|
42
42
|
const when = flags.at ?? t.scheduledFor ?? '(server default)';
|
|
43
|
-
|
|
43
|
+
const retry = t.scheduleRetry;
|
|
44
|
+
const subject = `task ${t.id ?? t._id ?? taskRef}${ref ? ` (${ref})` : ''}`;
|
|
45
|
+
if (retry?.changed === false) {
|
|
46
|
+
this.log(`Schedule for ${subject} is already armed for ${when}; nothing changed.`);
|
|
47
|
+
}
|
|
48
|
+
else if (retry?.armedFirstSchedule) {
|
|
49
|
+
this.log(`Armed a first schedule for ${subject} → ${when} (it had none; \`tasks schedule\` is the command for that).`);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
this.log(`Re-armed schedule for ${subject} → ${when}`);
|
|
53
|
+
}
|
|
44
54
|
}
|
|
45
55
|
}
|
|
46
56
|
exports.default = TasksRetrySchedule;
|
|
@@ -65,6 +65,12 @@ function renderExecutionReceipt(receipt, log, verbose = false) {
|
|
|
65
65
|
for (const evidenceRef of receipt.blocker.evidenceRefs || [])
|
|
66
66
|
log(` Evidence: ${evidenceRef}`);
|
|
67
67
|
}
|
|
68
|
+
// A source gap beside this episode, not the reason it stopped (OSK-16230).
|
|
69
|
+
if (receipt.standingBlocker) {
|
|
70
|
+
log(`Standing: ${receipt.standingBlocker.code} — ${receipt.standingBlocker.summary} (${receipt.standingBlocker.owner.label})`);
|
|
71
|
+
for (const evidenceRef of receipt.standingBlocker.evidenceRefs || [])
|
|
72
|
+
log(` Evidence: ${evidenceRef}`);
|
|
73
|
+
}
|
|
68
74
|
if (verbose)
|
|
69
75
|
log(`Diagnostics:\n${JSON.stringify({ id: receipt.id, revision: receipt.revision, evidence: receipt.evidence, diagnostics: receipt.diagnostics }, null, 2)}`);
|
|
70
76
|
}
|
|
@@ -818,8 +818,16 @@ export declare function describeOperation(operation: string | null | undefined,
|
|
|
818
818
|
* already composed sat only in `--json` and `doctor` (OSK-16212). Nothing is
|
|
819
819
|
* printed while the operation is ready or working, where a fix would contradict
|
|
820
820
|
* the line above it.
|
|
821
|
-
|
|
822
|
-
|
|
821
|
+
*
|
|
822
|
+
* Returns one or two lines. `Fix:` only when the blocker carries an action; a
|
|
823
|
+
* typed blocker an agent returned carries only what happened, which printed
|
|
824
|
+
* under "Fix:" read as a remedy nobody wrote (OSK-16230). That narrative is
|
|
825
|
+
* labelled `Blocker:` instead, and no remedy is made up for it.
|
|
826
|
+
*/
|
|
827
|
+
export declare function describeOperationFix(commitment: CommitmentView, operation: string | null | undefined): Array<{
|
|
828
|
+
label: 'Fix' | 'Blocker';
|
|
829
|
+
text: string;
|
|
830
|
+
}>;
|
|
823
831
|
/**
|
|
824
832
|
* Who stopped a Commitment, and when and why, in one line. The server stores it as `stoppedBy`;
|
|
825
833
|
* `show` printed neither (OSK-15423), so a Commitment you paused did not say you had.
|
package/dist/lib/commitments.js
CHANGED
|
@@ -955,20 +955,32 @@ function describeOperation(operation, reason) {
|
|
|
955
955
|
* already composed sat only in `--json` and `doctor` (OSK-16212). Nothing is
|
|
956
956
|
* printed while the operation is ready or working, where a fix would contradict
|
|
957
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.
|
|
958
963
|
*/
|
|
959
964
|
function describeOperationFix(commitment, operation) {
|
|
960
965
|
if (operation === 'ready' || operation === 'working')
|
|
961
|
-
return
|
|
966
|
+
return [];
|
|
962
967
|
const blocker = commitment.firstActionableBlocker;
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
const
|
|
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
|
|
967
973
|
? blocker.ownerLabel
|
|
968
|
-
: typeof blocker
|
|
974
|
+
: typeof blocker.owner === 'string' && blocker.owner
|
|
969
975
|
? blocker.owner
|
|
970
976
|
: '';
|
|
971
|
-
|
|
977
|
+
const lines = [];
|
|
978
|
+
// The detail is shown only when the fix does not already say it.
|
|
979
|
+
if (detail && detail !== remediation)
|
|
980
|
+
lines.push({ label: 'Blocker', text: `${detail}${!remediation && who ? ` (${who})` : ''}` });
|
|
981
|
+
if (remediation)
|
|
982
|
+
lines.push({ label: 'Fix', text: `${remediation}${who ? ` (${who})` : ''}` });
|
|
983
|
+
return lines;
|
|
972
984
|
}
|
|
973
985
|
/**
|
|
974
986
|
* Who stopped a Commitment, and when and why, in one line. The server stores it as `stoppedBy`;
|
|
@@ -1082,9 +1094,8 @@ scheduleIssues) {
|
|
|
1082
1094
|
log(`Assessment: ${assessment?.label ?? 'Unknown'}`);
|
|
1083
1095
|
log(`Evidence: ${assessment?.evidence ?? 'unavailable'}`);
|
|
1084
1096
|
log(`Operation: ${describeOperation(assessment?.operation, assessment?.operationReason)}`);
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
log(` Fix: ${fix}`);
|
|
1097
|
+
for (const line of describeOperationFix(commitment, assessment?.operation))
|
|
1098
|
+
log(` ${line.label}: ${line.text}`);
|
|
1088
1099
|
if (commitment.state?.lastCheckedAt)
|
|
1089
1100
|
log(`Checked: ${commitment.state.lastCheckedAt}`);
|
|
1090
1101
|
if (typeof commitment.state?.consecutiveUnresolvedChecks === 'number') {
|
|
@@ -42386,7 +42386,7 @@ var RA = tA.map(function(e3) {
|
|
|
42386
42386
|
return e3.kind;
|
|
42387
42387
|
}).join(", ");
|
|
42388
42388
|
var CA = "ui_widget";
|
|
42389
|
-
var OA = { titleMax: 120, fallbackTextMax: 600, cssMax: 4e4, htmlMax: 12e4, jsMax: 12e4, totalMax: 2e5, idMax: 64, heightMin: 80, heightInlineMax:
|
|
42389
|
+
var OA = { titleMax: 120, fallbackTextMax: 600, cssMax: 4e4, htmlMax: 12e4, jsMax: 12e4, totalMax: 2e5, idMax: 64, heightMin: 80, heightInlineMax: 4e3, heightHardMax: 4e3, perMessageMax: 6 };
|
|
42390
42390
|
var PA = e.z.string().min(1).max(OA.idMax).regex(/^[A-Za-z0-9_-]+$/, 'id may contain letters, digits, "_" and "-" only');
|
|
42391
42391
|
var NA = e.z.object({ transport: e.z.enum(["fence", "tool", "cli"]), harness: e.z.string().max(64).optional() }).strict();
|
|
42392
42392
|
var MA = { v: e.z.literal(1), id: PA, title: e.z.string().trim().min(1).max(OA.titleMax), initialHeight: e.z.number().int().min(OA.heightMin).max(OA.heightHardMax).optional(), css: e.z.string().max(OA.cssMax), html: e.z.string().max(OA.htmlMax), js: e.z.string().max(OA.jsMax), source: NA.optional() };
|
package/oclif.manifest.json
CHANGED
|
@@ -132497,5 +132497,5 @@
|
|
|
132497
132497
|
]
|
|
132498
132498
|
}
|
|
132499
132499
|
},
|
|
132500
|
-
"version": "0.1.
|
|
132500
|
+
"version": "0.1.107"
|
|
132501
132501
|
}
|