@skrr-ai/cli 0.1.104 → 0.1.105
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/base-command.js +6 -2
- package/dist/commands/agenda/trigger/list.js +2 -0
- package/dist/commands/agents/chat.js +4 -1
- package/dist/commands/agents/operating-plan.js +5 -2
- package/dist/commands/agents/triggers/create.js +3 -1
- package/dist/commands/agents/triggers/fire.js +2 -0
- package/dist/commands/agents/triggers/replay.js +2 -0
- package/dist/commands/agents/triggers/update.js +12 -4
- package/dist/commands/commitments/action-history.js +7 -2
- package/dist/commands/commitments/cadence-decisions.js +6 -1
- package/dist/commands/commitments/cadence-requests/decide.d.ts +1 -1
- package/dist/commands/commitments/cadence-requests/decide.js +1 -1
- package/dist/commands/commitments/doctor.js +7 -1
- package/dist/commands/commitments/observations.js +7 -1
- package/dist/commands/commitments/operating-review.d.ts +15 -0
- package/dist/commands/commitments/operating-review.js +23 -2
- package/dist/commands/commitments/preflight.d.ts +12 -0
- package/dist/commands/commitments/preflight.js +23 -9
- package/dist/commands/commitments/reflection.js +6 -1
- package/dist/commands/commitments/show.js +3 -1
- package/dist/commands/commitments/trigger-status.js +2 -22
- package/dist/commands/commitments/wake-capabilities.d.ts +13 -0
- package/dist/commands/commitments/wake-capabilities.js +39 -3
- package/dist/commands/daemon/logs.d.ts +41 -3
- package/dist/commands/daemon/logs.js +202 -17
- package/dist/commands/followups/act.js +13 -1
- package/dist/commands/followups/checkin.js +11 -2
- package/dist/commands/followups/reschedule.js +13 -1
- package/dist/commands/proposals/discard.d.ts +1 -1
- package/dist/commands/proposals/keep.d.ts +1 -1
- package/dist/commands/proposals/list.d.ts +7 -0
- package/dist/commands/proposals/list.js +31 -5
- package/dist/commands/proposals/show.d.ts +2 -1
- package/dist/commands/proposals/show.js +31 -4
- package/dist/commands/proposals/snooze.d.ts +1 -1
- package/dist/commands/requests/create.js +8 -3
- package/dist/commands/spaces/workflows/create.js +3 -3
- package/dist/commands/tasks/complete.js +4 -0
- package/dist/commands/tasks/create.d.ts +2 -0
- package/dist/commands/tasks/create.js +17 -3
- package/dist/commands/tasks/deliverable/add.d.ts +2 -0
- package/dist/commands/tasks/deliverable/add.js +22 -7
- package/dist/commands/tasks/deliverable/list.d.ts +13 -0
- package/dist/commands/tasks/deliverable/list.js +34 -1
- package/dist/commands/tasks/economics-compare.js +7 -1
- package/dist/commands/tasks/expectations.d.ts +10 -0
- package/dist/commands/tasks/expectations.js +31 -3
- package/dist/commands/tasks/list.d.ts +9 -0
- package/dist/commands/tasks/list.js +40 -5
- package/dist/commands/tasks/report.js +1 -1
- package/dist/commands/tasks/result/show.d.ts +7 -0
- package/dist/commands/tasks/result/show.js +32 -1
- package/dist/commands/tasks/result/submit.js +1 -1
- package/dist/commands/tasks/show.d.ts +6 -0
- package/dist/commands/tasks/show.js +23 -0
- package/dist/commands/tasks/update.d.ts +3 -0
- package/dist/commands/tasks/update.js +23 -3
- package/dist/commands/triggers/audit.js +6 -2
- package/dist/commands/triggers/create.js +3 -1
- package/dist/commands/triggers/fire.d.ts +1 -0
- package/dist/commands/triggers/fire.js +16 -1
- package/dist/commands/triggers/preflight.js +6 -5
- package/dist/commands/triggers/replay.js +2 -0
- package/dist/commands/triggers/update.js +12 -4
- package/dist/commands/workspaces/stewardship.js +5 -1
- package/dist/lib/agentic-stream.d.ts +9 -0
- package/dist/lib/agentic-stream.js +38 -18
- package/dist/lib/commitment-event-wakes.d.ts +8 -0
- package/dist/lib/commitment-event-wakes.js +39 -0
- package/dist/lib/commitment-ledgers.d.ts +9 -0
- package/dist/lib/commitment-ledgers.js +126 -0
- package/dist/lib/commitments.d.ts +102 -2
- package/dist/lib/commitments.js +208 -8
- package/dist/lib/delegated-cli.js +13 -2
- package/dist/lib/followups.d.ts +6 -0
- package/dist/lib/followups.js +12 -0
- package/dist/lib/message-content.d.ts +6 -0
- package/dist/lib/message-content.js +24 -0
- package/dist/lib/operating-plan-loops.d.ts +8 -0
- package/dist/lib/operating-plan-loops.js +32 -0
- package/dist/lib/preflight-copy.d.ts +27 -0
- package/dist/lib/preflight-copy.js +89 -0
- package/dist/lib/proposal-decision-command.d.ts +1 -1
- package/dist/lib/proposal-decision-command.js +27 -3
- package/dist/lib/proposals.d.ts +17 -0
- package/dist/lib/proposals.js +41 -0
- package/dist/lib/stewardship-output.js +57 -12
- package/dist/lib/task-economics-line.d.ts +30 -0
- package/dist/lib/task-economics-line.js +49 -0
- package/dist/lib/task-economics.d.ts +15 -0
- package/dist/lib/task-economics.js +73 -8
- package/dist/lib/task-recurrence-outcome.d.ts +19 -0
- package/dist/lib/task-recurrence-outcome.js +30 -0
- package/dist/lib/triggers.d.ts +42 -0
- package/dist/lib/triggers.js +136 -2
- package/dist/lib/widgetTerminalText.d.ts +8 -0
- package/dist/lib/widgetTerminalText.js +116 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.d.ts +33 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.js +27 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/telemetryRedaction.d.ts +28 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/telemetryRedaction.js +50 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.d.ts +33 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.js +26 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/telemetryRedaction.d.ts +28 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/telemetryRedaction.js +50 -5
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +7261 -7056
- package/docs/v1/operations.md +1 -1
- package/oclif.manifest.json +116 -72
- package/package.json +1 -1
|
@@ -19,6 +19,7 @@ export interface DaemonLogsFlags {
|
|
|
19
19
|
stderr?: boolean;
|
|
20
20
|
path?: boolean;
|
|
21
21
|
profile?: string;
|
|
22
|
+
rotated?: boolean;
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Older daemons already kept logs in this profile-scoped location, even
|
|
@@ -116,6 +117,37 @@ export declare function resolveForwardedLogPath(flags: Pick<DaemonLogsFlags, 'st
|
|
|
116
117
|
* forwarding to the bug, so the CLI forwards only at or above this version.
|
|
117
118
|
*/
|
|
118
119
|
export declare const LIVE_LOG_RESOLUTION_RUNTIME_VERSION = "0.8.119";
|
|
120
|
+
/**
|
|
121
|
+
* The first runtime whose `logs` knows `--rotated` (OSK-16214). An older one
|
|
122
|
+
* rejects it as an unknown option, so the CLI never forwards the flag below
|
|
123
|
+
* this version: it asks the runtime which file it reads (`--path`) and reads
|
|
124
|
+
* that file and its rotated siblings itself.
|
|
125
|
+
*/
|
|
126
|
+
export declare const ROTATED_LOGS_RUNTIME_VERSION = "0.8.120";
|
|
127
|
+
/**
|
|
128
|
+
* Rotated siblings of a log, OLDEST FIRST (OSK-16214). The daemon's logger
|
|
129
|
+
* renames `daemon.app.log` to `daemon.app.1.log`, `.1` to `.2` and so on at
|
|
130
|
+
* 10 MB (`daemon/src/logger.ts` `maybeRotate`), and `logs` never reached them —
|
|
131
|
+
* `--lines 5000 | grep <run id>` found nothing while `daemon.app.1.log` held the
|
|
132
|
+
* run. A copy of `daemon/src/rotated-log-files.ts`, for runtimes that predate
|
|
133
|
+
* `--rotated`. Never throws.
|
|
134
|
+
*/
|
|
135
|
+
export declare function rotatedLogSiblings(filePath: string, readdir?: (dir: string) => string[]): string[];
|
|
136
|
+
/** The sentence shown when rotated siblings hold older lines the view leaves out. */
|
|
137
|
+
export declare function describeRotatedSiblings(filePath: string, siblings: readonly string[]): string | null;
|
|
138
|
+
/**
|
|
139
|
+
* The last `maxLines` lines of one file, read backwards in chunks so the cost
|
|
140
|
+
* follows the lines asked for, not the file's size. Bytes are decoded once, so
|
|
141
|
+
* a multi-byte character split across a chunk boundary survives.
|
|
142
|
+
*/
|
|
143
|
+
export declare function readLastLines(filePath: string, maxLines: number): string[];
|
|
144
|
+
/**
|
|
145
|
+
* The last `maxLines` lines across `files` (OLDEST FIRST) as if they were one
|
|
146
|
+
* file. A file renamed away between the listing and the read is skipped.
|
|
147
|
+
*/
|
|
148
|
+
export declare function readLastLinesAcross(files: readonly string[], maxLines: number): string[];
|
|
149
|
+
/** Every byte of `files`, in order, to `out` — streamed, honouring back-pressure. */
|
|
150
|
+
export declare function streamLogFiles(files: readonly string[], out: NodeJS.WritableStream): Promise<void>;
|
|
119
151
|
/** `x.y.z` from a version string, or null. A prerelease suffix is ignored. */
|
|
120
152
|
export declare function parseRuntimeVersion(text: string): [number, number, number] | null;
|
|
121
153
|
/** Whether `version` is at least `floor`; null when either is not a version. */
|
|
@@ -130,8 +162,11 @@ export declare function probeRuntimeVersion(capture?: typeof execRuntimeBinaryCa
|
|
|
130
162
|
*/
|
|
131
163
|
export declare function probeStatusLogPath(flags: Pick<DaemonLogsFlags, 'profile'>, capture?: typeof execRuntimeBinaryCapture): Promise<string | null>;
|
|
132
164
|
/** What `skrr daemon logs` does: forward to the runtime, or read a file itself. */
|
|
133
|
-
export type DaemonLogsPlan =
|
|
165
|
+
export type DaemonLogsPlan =
|
|
166
|
+
/** `runtimeVersion` decides whether a flag newer than 0.8.119 may be forwarded. */
|
|
167
|
+
{
|
|
134
168
|
kind: 'forward';
|
|
169
|
+
runtimeVersion: string | null;
|
|
135
170
|
}
|
|
136
171
|
/** `note` goes to stderr before any log content; never printed for `--path`. */
|
|
137
172
|
| {
|
|
@@ -171,13 +206,15 @@ export declare function readDaemonLogTail(filePath: string, maxLines: number): s
|
|
|
171
206
|
* or it parses successfully and is then silently dropped.
|
|
172
207
|
*/
|
|
173
208
|
export declare function buildDaemonLogsArgs(flags: DaemonLogsFlags): string[];
|
|
209
|
+
/** `--stderr` help, word for word the runtime's own (`daemon/src/index.ts`). */
|
|
210
|
+
export declare const STDERR_FLAG_DESCRIPTION = "Read the daemon's raw stderr instead of its log: the file its installed service redirects stderr to, or the profile's daemon.err.log. A daemon started with --log-file by anything else has no known stderr file, so its live log (which carries every WARN and ERROR) is shown instead, with a note";
|
|
174
211
|
export default class DaemonLogs extends Command {
|
|
175
212
|
static aliases: string[];
|
|
176
213
|
static description: string;
|
|
177
214
|
/**
|
|
178
|
-
* Mirrors the
|
|
215
|
+
* Mirrors the options `oversky logs` actually implements
|
|
179
216
|
* (`daemon/src/index.ts` — `-n/--lines`, `-f/--follow`, `--stderr`,
|
|
180
|
-
* `--path`). Typed flags are deliberate — they give `skrr daemon logs --help`
|
|
217
|
+
* `--path`, and since 0.8.120 `--rotated`). Typed flags are deliberate — they give `skrr daemon logs --help`
|
|
181
218
|
* something real to print, which raw `strict = false` passthrough never did.
|
|
182
219
|
* The trade is that anything NOT declared here becomes unreachable, so this
|
|
183
220
|
* list has to stay in step with the daemon command.
|
|
@@ -187,6 +224,7 @@ export default class DaemonLogs extends Command {
|
|
|
187
224
|
lines: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
188
225
|
stderr: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
189
226
|
path: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
227
|
+
rotated: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
190
228
|
profile: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
191
229
|
};
|
|
192
230
|
static examples: string[];
|
|
@@ -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.LIVE_LOG_RESOLUTION_RUNTIME_VERSION = void 0;
|
|
6
|
+
exports.STDERR_FLAG_DESCRIPTION = 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;
|
|
@@ -11,6 +11,11 @@ exports.installedServiceLogDestinations = installedServiceLogDestinations;
|
|
|
11
11
|
exports.installedServiceLogCandidates = installedServiceLogCandidates;
|
|
12
12
|
exports.chooseLegacyFallbackLog = chooseLegacyFallbackLog;
|
|
13
13
|
exports.resolveForwardedLogPath = resolveForwardedLogPath;
|
|
14
|
+
exports.rotatedLogSiblings = rotatedLogSiblings;
|
|
15
|
+
exports.describeRotatedSiblings = describeRotatedSiblings;
|
|
16
|
+
exports.readLastLines = readLastLines;
|
|
17
|
+
exports.readLastLinesAcross = readLastLinesAcross;
|
|
18
|
+
exports.streamLogFiles = streamLogFiles;
|
|
14
19
|
exports.parseRuntimeVersion = parseRuntimeVersion;
|
|
15
20
|
exports.runtimeVersionAtLeast = runtimeVersionAtLeast;
|
|
16
21
|
exports.probeRuntimeVersion = probeRuntimeVersion;
|
|
@@ -22,6 +27,7 @@ const core_1 = require("@oclif/core");
|
|
|
22
27
|
const node_fs_1 = require("node:fs");
|
|
23
28
|
const node_os_1 = require("node:os");
|
|
24
29
|
const node_path_1 = __importDefault(require("node:path"));
|
|
30
|
+
const node_events_1 = require("node:events");
|
|
25
31
|
const exec_runtime_binary_1 = require("../../lib/exec-runtime-binary");
|
|
26
32
|
const MAX_FALLBACK_LOG_BYTES = 1024 * 1024;
|
|
27
33
|
const PROFILE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
@@ -188,7 +194,9 @@ function chooseLegacyFallbackLog(args) {
|
|
|
188
194
|
*/
|
|
189
195
|
async function resolveForwardedLogPath(flags, capture = exec_runtime_binary_1.execRuntimeBinaryCapture) {
|
|
190
196
|
try {
|
|
191
|
-
|
|
197
|
+
// Only what decides the file: --rotated would list siblings first, and the
|
|
198
|
+
// last line is still the current file, but there is no reason to ask.
|
|
199
|
+
const out = await capture(buildDaemonLogsArgs({ stderr: flags.stderr, profile: flags.profile, path: true }), {
|
|
192
200
|
timeoutMs: 5_000,
|
|
193
201
|
});
|
|
194
202
|
if (out.code !== 0)
|
|
@@ -213,6 +221,119 @@ async function resolveForwardedLogPath(flags, capture = exec_runtime_binary_1.ex
|
|
|
213
221
|
* forwarding to the bug, so the CLI forwards only at or above this version.
|
|
214
222
|
*/
|
|
215
223
|
exports.LIVE_LOG_RESOLUTION_RUNTIME_VERSION = '0.8.119';
|
|
224
|
+
/**
|
|
225
|
+
* The first runtime whose `logs` knows `--rotated` (OSK-16214). An older one
|
|
226
|
+
* rejects it as an unknown option, so the CLI never forwards the flag below
|
|
227
|
+
* this version: it asks the runtime which file it reads (`--path`) and reads
|
|
228
|
+
* that file and its rotated siblings itself.
|
|
229
|
+
*/
|
|
230
|
+
exports.ROTATED_LOGS_RUNTIME_VERSION = '0.8.120';
|
|
231
|
+
/**
|
|
232
|
+
* Rotated siblings of a log, OLDEST FIRST (OSK-16214). The daemon's logger
|
|
233
|
+
* renames `daemon.app.log` to `daemon.app.1.log`, `.1` to `.2` and so on at
|
|
234
|
+
* 10 MB (`daemon/src/logger.ts` `maybeRotate`), and `logs` never reached them —
|
|
235
|
+
* `--lines 5000 | grep <run id>` found nothing while `daemon.app.1.log` held the
|
|
236
|
+
* run. A copy of `daemon/src/rotated-log-files.ts`, for runtimes that predate
|
|
237
|
+
* `--rotated`. Never throws.
|
|
238
|
+
*/
|
|
239
|
+
function rotatedLogSiblings(filePath, readdir = (dir) => (0, node_fs_1.readdirSync)(dir)) {
|
|
240
|
+
const dir = node_path_1.default.dirname(filePath);
|
|
241
|
+
const ext = node_path_1.default.extname(filePath);
|
|
242
|
+
const base = node_path_1.default.basename(filePath, ext);
|
|
243
|
+
const escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
244
|
+
const pattern = new RegExp(`^${escape(base)}\\.(\\d+)${escape(ext)}$`);
|
|
245
|
+
let names;
|
|
246
|
+
try {
|
|
247
|
+
names = readdir(dir);
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
return names
|
|
253
|
+
.map((name) => ({ name, index: Number(pattern.exec(name)?.[1] ?? NaN) }))
|
|
254
|
+
.filter(({ index }) => Number.isSafeInteger(index) && index >= 1)
|
|
255
|
+
.sort((a, b) => b.index - a.index)
|
|
256
|
+
.map(({ name }) => node_path_1.default.join(dir, name));
|
|
257
|
+
}
|
|
258
|
+
/** The sentence shown when rotated siblings hold older lines the view leaves out. */
|
|
259
|
+
function describeRotatedSiblings(filePath, siblings) {
|
|
260
|
+
if (siblings.length === 0)
|
|
261
|
+
return null;
|
|
262
|
+
const names = siblings.map((s) => node_path_1.default.basename(s));
|
|
263
|
+
const listed = names.length === 1 ? names[0] : `${names.at(-1)} … ${names[0]}`;
|
|
264
|
+
return (`Older lines are in ${siblings.length} rotated file${siblings.length === 1 ? '' : 's'} ` +
|
|
265
|
+
`beside ${filePath} (${listed}). Add --rotated to read them too, oldest first.`);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* The last `maxLines` lines of one file, read backwards in chunks so the cost
|
|
269
|
+
* follows the lines asked for, not the file's size. Bytes are decoded once, so
|
|
270
|
+
* a multi-byte character split across a chunk boundary survives.
|
|
271
|
+
*/
|
|
272
|
+
function readLastLines(filePath, maxLines) {
|
|
273
|
+
if (maxLines <= 0)
|
|
274
|
+
return [];
|
|
275
|
+
const { size } = (0, node_fs_1.statSync)(filePath);
|
|
276
|
+
const descriptor = (0, node_fs_1.openSync)(filePath, 'r');
|
|
277
|
+
try {
|
|
278
|
+
let start = size;
|
|
279
|
+
const chunks = [];
|
|
280
|
+
let newlines = 0;
|
|
281
|
+
while (start > 0 && newlines <= maxLines) {
|
|
282
|
+
const length = Math.min(64 * 1024, start);
|
|
283
|
+
start -= length;
|
|
284
|
+
const buffer = Buffer.alloc(length);
|
|
285
|
+
(0, node_fs_1.readSync)(descriptor, buffer, 0, length, start);
|
|
286
|
+
chunks.unshift(buffer);
|
|
287
|
+
for (const byte of buffer)
|
|
288
|
+
if (byte === 0x0a)
|
|
289
|
+
newlines += 1;
|
|
290
|
+
}
|
|
291
|
+
const lines = Buffer.concat(chunks).toString('utf8').split('\n');
|
|
292
|
+
if (lines.at(-1) === '')
|
|
293
|
+
lines.pop();
|
|
294
|
+
if (start > 0)
|
|
295
|
+
lines.shift();
|
|
296
|
+
return lines.slice(-maxLines);
|
|
297
|
+
}
|
|
298
|
+
finally {
|
|
299
|
+
(0, node_fs_1.closeSync)(descriptor);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* The last `maxLines` lines across `files` (OLDEST FIRST) as if they were one
|
|
304
|
+
* file. A file renamed away between the listing and the read is skipped.
|
|
305
|
+
*/
|
|
306
|
+
function readLastLinesAcross(files, maxLines) {
|
|
307
|
+
const collected = [];
|
|
308
|
+
let remaining = maxLines;
|
|
309
|
+
for (let i = files.length - 1; i >= 0 && remaining > 0; i -= 1) {
|
|
310
|
+
try {
|
|
311
|
+
const lines = readLastLines(files[i], remaining);
|
|
312
|
+
collected.unshift(lines);
|
|
313
|
+
remaining -= lines.length;
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
// Rotated away mid-read; the rest are still worth showing.
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return collected.flat();
|
|
320
|
+
}
|
|
321
|
+
/** Every byte of `files`, in order, to `out` — streamed, honouring back-pressure. */
|
|
322
|
+
async function streamLogFiles(files, out) {
|
|
323
|
+
for (const file of files) {
|
|
324
|
+
try {
|
|
325
|
+
for await (const chunk of (0, node_fs_1.createReadStream)(file)) {
|
|
326
|
+
if (!out.write(chunk))
|
|
327
|
+
await (0, node_events_1.once)(out, 'drain');
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
catch (err) {
|
|
331
|
+
if (err?.code === 'ENOENT')
|
|
332
|
+
continue;
|
|
333
|
+
throw err;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
216
337
|
/** `x.y.z` from a version string, or null. A prerelease suffix is ignored. */
|
|
217
338
|
function parseRuntimeVersion(text) {
|
|
218
339
|
const m = /(\d+)\.(\d+)\.(\d+)/.exec(text);
|
|
@@ -300,13 +421,13 @@ async function planDaemonLogs(flags, deps) {
|
|
|
300
421
|
}
|
|
301
422
|
// Help could not be read: nothing to go on, keep the old behaviour.
|
|
302
423
|
if (supports === null)
|
|
303
|
-
return { kind: 'forward' };
|
|
424
|
+
return { kind: 'forward', runtimeVersion: null };
|
|
304
425
|
const version = await deps.runtimeVersion();
|
|
305
426
|
const current = version
|
|
306
427
|
? runtimeVersionAtLeast(version, exports.LIVE_LOG_RESOLUTION_RUNTIME_VERSION)
|
|
307
428
|
: null;
|
|
308
429
|
if (current === true)
|
|
309
|
-
return { kind: 'forward' };
|
|
430
|
+
return { kind: 'forward', runtimeVersion: version };
|
|
310
431
|
const live = await deps.statusLogPath();
|
|
311
432
|
const predates = version !== null
|
|
312
433
|
? `The installed runtime (${version}) predates \`logs\` following the live daemon's ` +
|
|
@@ -315,12 +436,12 @@ async function planDaemonLogs(flags, deps) {
|
|
|
315
436
|
`${deps.profilePath} instead of the live log.`;
|
|
316
437
|
if (!live) {
|
|
317
438
|
if (current === null)
|
|
318
|
-
return { kind: 'forward' };
|
|
439
|
+
return { kind: 'forward', runtimeVersion: version };
|
|
319
440
|
return legacy(`${predates} Its \`status\` did not name the live log either.`);
|
|
320
441
|
}
|
|
321
442
|
// The live daemon writes the profile log itself: the runtime's answer is right.
|
|
322
443
|
if (sameLogFile(live, node_path_1.default.join(node_path_1.default.dirname(deps.profilePath), 'daemon.log'))) {
|
|
323
|
-
return { kind: 'forward' };
|
|
444
|
+
return { kind: 'forward', runtimeVersion: version };
|
|
324
445
|
}
|
|
325
446
|
const missingHint = 'The live daemon has not written it yet; `skrr daemon status` shows whether one is running.';
|
|
326
447
|
if (!flags.stderr) {
|
|
@@ -391,8 +512,13 @@ function buildDaemonLogsArgs(flags) {
|
|
|
391
512
|
...(flags.lines !== undefined ? ['--lines', String(flags.lines)] : []),
|
|
392
513
|
...(flags.stderr ? ['--stderr'] : []),
|
|
393
514
|
...(flags.path ? ['--path'] : []),
|
|
515
|
+
// Only ever reached for a runtime at ROTATED_LOGS_RUNTIME_VERSION or later;
|
|
516
|
+
// `run` reads the files itself for an older one.
|
|
517
|
+
...(flags.rotated ? ['--rotated'] : []),
|
|
394
518
|
];
|
|
395
519
|
}
|
|
520
|
+
/** `--stderr` help, word for word the runtime's own (`daemon/src/index.ts`). */
|
|
521
|
+
exports.STDERR_FLAG_DESCRIPTION = "Read the daemon's raw stderr instead of its log: the file its installed service redirects stderr to, or the profile's daemon.err.log. A daemon started with --log-file by anything else has no known stderr file, so its live log (which carries every WARN and ERROR) is shown instead, with a note";
|
|
396
522
|
class DaemonLogs extends core_1.Command {
|
|
397
523
|
// `skrr daemons <sub>` — the plural is advertised as an alias for the
|
|
398
524
|
// whole topic, and without a per-command alias only the bare topic
|
|
@@ -400,9 +526,9 @@ class DaemonLogs extends core_1.Command {
|
|
|
400
526
|
static aliases = ['daemons:logs'];
|
|
401
527
|
static description = 'Tail or print recent skrr daemon logs';
|
|
402
528
|
/**
|
|
403
|
-
* Mirrors the
|
|
529
|
+
* Mirrors the options `oversky logs` actually implements
|
|
404
530
|
* (`daemon/src/index.ts` — `-n/--lines`, `-f/--follow`, `--stderr`,
|
|
405
|
-
* `--path`). Typed flags are deliberate — they give `skrr daemon logs --help`
|
|
531
|
+
* `--path`, and since 0.8.120 `--rotated`). Typed flags are deliberate — they give `skrr daemon logs --help`
|
|
406
532
|
* something real to print, which raw `strict = false` passthrough never did.
|
|
407
533
|
* The trade is that anything NOT declared here becomes unreachable, so this
|
|
408
534
|
* list has to stay in step with the daemon command.
|
|
@@ -414,17 +540,26 @@ class DaemonLogs extends core_1.Command {
|
|
|
414
540
|
}),
|
|
415
541
|
lines: core_1.Flags.integer({
|
|
416
542
|
char: 'n',
|
|
417
|
-
description: 'Number of recent log lines to print',
|
|
543
|
+
description: 'Number of recent log lines to print (default 200; every line with --rotated)',
|
|
418
544
|
// `oversky logs` validates `>= 0`; keep the two in step so `-n 0` is not
|
|
419
545
|
// rejected here only to be accepted there.
|
|
420
546
|
min: 0,
|
|
421
547
|
}),
|
|
548
|
+
// OSK-16213: this said "daemon.err.log — every WARN and ERROR". Since
|
|
549
|
+
// OSK-15993 it is the process's raw stderr as the service redirects it
|
|
550
|
+
// (`planDaemonLogs` here, `resolveDaemonLogTarget` in the runtime), and a
|
|
551
|
+
// structured --log-file is what carries WARN and ERROR. The runtime's own
|
|
552
|
+
// `--stderr` help says the same words; logs.spec.ts compares the two.
|
|
422
553
|
stderr: core_1.Flags.boolean({
|
|
423
|
-
description:
|
|
554
|
+
description: exports.STDERR_FLAG_DESCRIPTION,
|
|
424
555
|
}),
|
|
425
556
|
path: core_1.Flags.boolean({
|
|
426
557
|
description: 'Print the resolved log file path and exit',
|
|
427
558
|
}),
|
|
559
|
+
// OSK-16214: the rotated files (`daemon.app.1.log` …) were unreachable.
|
|
560
|
+
rotated: core_1.Flags.boolean({
|
|
561
|
+
description: 'Include the rotated files (daemon.app.1.log …) before the current one, oldest first; with --path, list them all',
|
|
562
|
+
}),
|
|
428
563
|
// Every other daemon verb reaches a non-default profile with --profile; this
|
|
429
564
|
// one could only be steered by OVERSKY_PROFILE (OSK-10001).
|
|
430
565
|
profile: core_1.Flags.string({
|
|
@@ -438,6 +573,7 @@ class DaemonLogs extends core_1.Command {
|
|
|
438
573
|
'<%= config.bin %> daemon logs --lines 500',
|
|
439
574
|
'<%= config.bin %> daemon logs --stderr',
|
|
440
575
|
'<%= config.bin %> daemon logs --path',
|
|
576
|
+
'<%= config.bin %> daemon logs --rotated | grep arun_',
|
|
441
577
|
'<%= config.bin %> daemon logs --profile dogfood-laptop --lines 50',
|
|
442
578
|
];
|
|
443
579
|
async run() {
|
|
@@ -466,7 +602,7 @@ class DaemonLogs extends core_1.Command {
|
|
|
466
602
|
// with it — 0.8.112–0.8.118 read the profile log even when the live
|
|
467
603
|
// daemon writes elsewhere. `planDaemonLogs` forwards only to a runtime
|
|
468
604
|
// that resolves the live log itself.
|
|
469
|
-
|
|
605
|
+
let plan = await planDaemonLogs(flags, {
|
|
470
606
|
supportsLogs: () => (0, exec_runtime_binary_1.runtimeBinarySupportsSubcommand)('logs'),
|
|
471
607
|
runtimeVersion: () => probeRuntimeVersion(),
|
|
472
608
|
statusLogPath: () => probeStatusLogPath(flags),
|
|
@@ -481,22 +617,63 @@ class DaemonLogs extends core_1.Command {
|
|
|
481
617
|
}
|
|
482
618
|
},
|
|
483
619
|
});
|
|
620
|
+
// OSK-16214: `--rotated` is new in ROTATED_LOGS_RUNTIME_VERSION, and an
|
|
621
|
+
// older runtime rejects it as an unknown option. Its `--path` is still
|
|
622
|
+
// the live-aware answer, so read that file and its siblings here.
|
|
623
|
+
const runtimeKnowsRotated = plan.kind === 'forward' &&
|
|
624
|
+
plan.runtimeVersion !== null &&
|
|
625
|
+
runtimeVersionAtLeast(plan.runtimeVersion, exports.ROTATED_LOGS_RUNTIME_VERSION) === true;
|
|
626
|
+
if (plan.kind === 'forward' && flags.rotated && !runtimeKnowsRotated) {
|
|
627
|
+
const resolved = await resolveForwardedLogPath(flags);
|
|
628
|
+
if (!resolved) {
|
|
629
|
+
this.error(`The installed runtime${plan.runtimeVersion ? ` (${plan.runtimeVersion})` : ''} predates ` +
|
|
630
|
+
`--rotated (${exports.ROTATED_LOGS_RUNTIME_VERSION}) and did not name its log file, so its ` +
|
|
631
|
+
'rotated files cannot be found. Upgrade it (`skrr daemon update`), or list the ' +
|
|
632
|
+
'directory `skrr daemon logs --path` prints.', { exit: 1 });
|
|
633
|
+
}
|
|
634
|
+
plan = {
|
|
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
|
+
};
|
|
640
|
+
}
|
|
484
641
|
if (plan.kind === 'read') {
|
|
485
642
|
const logPath = plan.path;
|
|
486
|
-
|
|
643
|
+
const siblings = rotatedLogSiblings(logPath);
|
|
644
|
+
// `--path` prints the one path and nothing else, on either stream —
|
|
645
|
+
// under --rotated, every file oldest first, the current one last.
|
|
487
646
|
if (flags.path) {
|
|
647
|
+
for (const file of flags.rotated ? siblings : [])
|
|
648
|
+
this.log(file);
|
|
488
649
|
this.log(logPath);
|
|
489
650
|
return;
|
|
490
651
|
}
|
|
491
652
|
if (plan.note)
|
|
492
653
|
this.logToStderr(plan.note);
|
|
654
|
+
if (flags.rotated && siblings.length === 0) {
|
|
655
|
+
this.logToStderr(`No rotated files beside ${logPath}.`);
|
|
656
|
+
}
|
|
657
|
+
else if (!flags.rotated) {
|
|
658
|
+
const hint = describeRotatedSiblings(logPath, siblings);
|
|
659
|
+
if (hint)
|
|
660
|
+
this.logToStderr(hint);
|
|
661
|
+
}
|
|
493
662
|
showAge(logPath);
|
|
494
663
|
if (!(0, node_fs_1.existsSync)(logPath)) {
|
|
495
664
|
this.error(`No daemon log at ${logPath}. ${plan.missingHint}`, { exit: 1 });
|
|
496
665
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
666
|
+
if (flags.rotated && flags.lines === undefined) {
|
|
667
|
+
// Every line of every file, streamed rather than held whole.
|
|
668
|
+
await streamLogFiles([...siblings, logPath], process.stdout);
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
const lines = flags.rotated
|
|
672
|
+
? readLastLinesAcross([...siblings, logPath], flags.lines ?? 200)
|
|
673
|
+
: readDaemonLogTail(logPath, flags.lines ?? 200);
|
|
674
|
+
if (lines.length > 0)
|
|
675
|
+
this.log(lines.join('\n'));
|
|
676
|
+
}
|
|
500
677
|
if (flags.follow) {
|
|
501
678
|
let position = (0, node_fs_1.statSync)(logPath).size;
|
|
502
679
|
(0, node_fs_1.watchFile)(logPath, { interval: 500 }, () => {
|
|
@@ -528,8 +705,16 @@ class DaemonLogs extends core_1.Command {
|
|
|
528
705
|
}
|
|
529
706
|
// The banner must describe the file the runtime will actually print —
|
|
530
707
|
// the live daemon's log (OSK-15993) — not the profile default.
|
|
531
|
-
if (!flags.path)
|
|
532
|
-
|
|
708
|
+
if (!flags.path) {
|
|
709
|
+
const forwardedPath = await resolveForwardedLogPath(flags);
|
|
710
|
+
// A runtime that knows --rotated says this itself; an older one cannot.
|
|
711
|
+
if (!runtimeKnowsRotated && forwardedPath) {
|
|
712
|
+
const hint = describeRotatedSiblings(forwardedPath, rotatedLogSiblings(forwardedPath));
|
|
713
|
+
if (hint)
|
|
714
|
+
this.logToStderr(hint);
|
|
715
|
+
}
|
|
716
|
+
showAge(forwardedPath);
|
|
717
|
+
}
|
|
533
718
|
const code = await (0, exec_runtime_binary_1.execRuntimeBinary)(args);
|
|
534
719
|
this.exit(code);
|
|
535
720
|
}
|
|
@@ -23,7 +23,10 @@ class FollowupsAct extends base_command_1.BaseCommand {
|
|
|
23
23
|
required: true,
|
|
24
24
|
}),
|
|
25
25
|
at: core_1.Flags.string({ description: 'ISO-8601 instant to act at', exclusive: ['in'] }),
|
|
26
|
-
in: core_1.Flags.string({
|
|
26
|
+
in: core_1.Flags.string({
|
|
27
|
+
description: 'Duration from now (2h, 3d), between 1m and 30d',
|
|
28
|
+
exclusive: ['at'],
|
|
29
|
+
}),
|
|
27
30
|
agent: core_1.Flags.string({ description: 'Agent ID (default: bound session agent)' }),
|
|
28
31
|
endpoint: core_1.Flags.string({ description: 'Delivery endpoint id (EndpointRegistry)' }),
|
|
29
32
|
'endpoint-target': core_1.Flags.string({ description: 'Channel target for channel endpoints' }),
|
|
@@ -42,6 +45,15 @@ class FollowupsAct extends base_command_1.BaseCommand {
|
|
|
42
45
|
retryable: false,
|
|
43
46
|
});
|
|
44
47
|
}
|
|
48
|
+
const problem = (0, followups_1.whenFlagProblem)(flags.at ? 'at' : 'in', at);
|
|
49
|
+
if (problem) {
|
|
50
|
+
this.failWithCliError({
|
|
51
|
+
message: problem,
|
|
52
|
+
code: 'FOLLOWUP_INVALID_FIELD',
|
|
53
|
+
exit: 2,
|
|
54
|
+
retryable: false,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
45
57
|
const action = (0, triggers_1.parseJsonObject)(flags.action, '--action');
|
|
46
58
|
if (typeof action?.builtinType !== 'string' || !action.builtinType) {
|
|
47
59
|
this.failWithCliError({
|
|
@@ -23,7 +23,7 @@ class FollowupsCheckin extends base_command_1.BaseCommand {
|
|
|
23
23
|
subject: core_1.Flags.string({ description: followups_1.FOLLOWUP_SUBJECT_FLAG_HELP, required: true }),
|
|
24
24
|
why: core_1.Flags.string({ description: 'Why this wake exists, in one line', required: true }),
|
|
25
25
|
in: core_1.Flags.string({
|
|
26
|
-
description: 'Duration from now (90m, 2h) — the once form',
|
|
26
|
+
description: 'Duration from now (90m, 2h), between 1m and 30d — the once form',
|
|
27
27
|
exclusive: ['frequency'],
|
|
28
28
|
}),
|
|
29
29
|
frequency: core_1.Flags.string({
|
|
@@ -64,6 +64,15 @@ class FollowupsCheckin extends base_command_1.BaseCommand {
|
|
|
64
64
|
retryable: false,
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
|
+
const problem = (0, followups_1.whenFlagProblem)('in', flags.in);
|
|
68
|
+
if (problem) {
|
|
69
|
+
this.failWithCliError({
|
|
70
|
+
message: problem,
|
|
71
|
+
code: 'FOLLOWUP_INVALID_FIELD',
|
|
72
|
+
exit: 2,
|
|
73
|
+
retryable: false,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
67
76
|
let agent;
|
|
68
77
|
try {
|
|
69
78
|
agent = (0, followups_1.resolveFollowUpAgent)(flags.agent);
|
|
@@ -106,7 +115,7 @@ class FollowupsCheckin extends base_command_1.BaseCommand {
|
|
|
106
115
|
if (response.dropped && producer === 'user_explicit') {
|
|
107
116
|
this.failWithCliError({
|
|
108
117
|
message: budgetRefusalMessage(agent.id, response.budget),
|
|
109
|
-
code: '
|
|
118
|
+
code: 'FOLLOWUP_BUDGET_EXCEEDED',
|
|
110
119
|
exit: 1,
|
|
111
120
|
retryable: false,
|
|
112
121
|
details: { budget: response.budget ?? null, response },
|
|
@@ -15,7 +15,10 @@ class FollowupsReschedule extends base_command_1.BaseCommand {
|
|
|
15
15
|
};
|
|
16
16
|
static flags = {
|
|
17
17
|
at: core_1.Flags.string({ description: 'New instant (ISO-8601)', exclusive: ['in'] }),
|
|
18
|
-
in: core_1.Flags.string({
|
|
18
|
+
in: core_1.Flags.string({
|
|
19
|
+
description: 'New offset from now (90m, 2h); between 1m and 30d',
|
|
20
|
+
exclusive: ['at'],
|
|
21
|
+
}),
|
|
19
22
|
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
20
23
|
};
|
|
21
24
|
async run() {
|
|
@@ -30,6 +33,15 @@ class FollowupsReschedule extends base_command_1.BaseCommand {
|
|
|
30
33
|
retryable: false,
|
|
31
34
|
});
|
|
32
35
|
}
|
|
36
|
+
const problem = (0, followups_1.whenFlagProblem)(flags.at ? 'at' : 'in', at);
|
|
37
|
+
if (problem) {
|
|
38
|
+
this.failWithCliError({
|
|
39
|
+
message: problem,
|
|
40
|
+
code: 'FOLLOWUP_INVALID_FIELD',
|
|
41
|
+
exit: 2,
|
|
42
|
+
retryable: false,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
33
45
|
let result;
|
|
34
46
|
try {
|
|
35
47
|
result = (await followups_1.followupsApi.reschedule(args.id, { at }));
|
|
@@ -3,7 +3,7 @@ export default class ProposalsDiscard extends ProposalDecisionCommand {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static args: {
|
|
5
5
|
kind: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
-
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
id: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
7
|
};
|
|
8
8
|
static decision: "discard";
|
|
9
9
|
}
|
|
@@ -3,7 +3,7 @@ export default class ProposalsKeep extends ProposalDecisionCommand {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static args: {
|
|
5
5
|
kind: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
-
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
id: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
7
|
};
|
|
8
8
|
static decision: "keep";
|
|
9
9
|
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* What an empty page means. With a filter AND a next cursor it does NOT mean "none": the inbox reads a
|
|
4
|
+
* bounded page of each source and applies some filters (the Commitment, the kind of row) afterwards,
|
|
5
|
+
* so an unmatched id pages through every page of an 864-row inbox before the cursor ends (OSK-15369).
|
|
6
|
+
* Without a cursor the inbox is exhausted and "none" is true.
|
|
7
|
+
*/
|
|
8
|
+
export declare function emptyPageLines(narrowed: boolean, nextCursor: string | null | undefined): string[];
|
|
2
9
|
export default class ProposalsList extends BaseCommand {
|
|
3
10
|
static description: string;
|
|
4
11
|
static flags: {
|
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.emptyPageLines = emptyPageLines;
|
|
3
4
|
const core_1 = require("@oclif/core");
|
|
4
5
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
6
|
const base_command_1 = require("../../base-command");
|
|
6
7
|
const proposals_1 = require("../../lib/proposals");
|
|
7
8
|
const format_1 = require("../../lib/format");
|
|
9
|
+
/**
|
|
10
|
+
* What an empty page means. With a filter AND a next cursor it does NOT mean "none": the inbox reads a
|
|
11
|
+
* bounded page of each source and applies some filters (the Commitment, the kind of row) afterwards,
|
|
12
|
+
* so an unmatched id pages through every page of an 864-row inbox before the cursor ends (OSK-15369).
|
|
13
|
+
* Without a cursor the inbox is exhausted and "none" is true.
|
|
14
|
+
*/
|
|
15
|
+
function emptyPageLines(narrowed, nextCursor) {
|
|
16
|
+
if (!nextCursor)
|
|
17
|
+
return [narrowed ? 'No proposals match.' : 'No proposals.'];
|
|
18
|
+
return narrowed
|
|
19
|
+
? [
|
|
20
|
+
'No proposals on this page match your filter.',
|
|
21
|
+
'The inbox applies some filters after reading each page, so a later page can still match; ' +
|
|
22
|
+
'it may take many pages to be sure there is none. The cursor below continues.',
|
|
23
|
+
]
|
|
24
|
+
: ['No proposals on this page; the cursor below continues.'];
|
|
25
|
+
}
|
|
8
26
|
/** A snoozed proposal reads `proposed` in its disposition; the wake time is what tells them apart. */
|
|
9
27
|
function noteFor(row) {
|
|
10
28
|
if (row.sourceStatus === 'snoozed' && row.snoozedUntil)
|
|
@@ -14,7 +32,7 @@ function noteFor(row) {
|
|
|
14
32
|
return '—';
|
|
15
33
|
}
|
|
16
34
|
class ProposalsList extends base_command_1.BaseCommand {
|
|
17
|
-
static description =
|
|
35
|
+
static description = "List the bounded proposal inbox and source coverage. BASIS is how the proposal's own cost/benefit estimate was established (agent_estimate, human_confirmed or measured); — means it carries none.";
|
|
18
36
|
static flags = {
|
|
19
37
|
json: core_1.Flags.boolean(),
|
|
20
38
|
kind: core_1.Flags.string({ options: [...data_provider_1.PROPOSAL_KINDS] }),
|
|
@@ -23,7 +41,11 @@ class ProposalsList extends base_command_1.BaseCommand {
|
|
|
23
41
|
'agent-id': core_1.Flags.string(),
|
|
24
42
|
limit: core_1.Flags.integer({ min: 1, max: 100, default: 50 }),
|
|
25
43
|
cursor: core_1.Flags.string(),
|
|
26
|
-
sort: core_1.Flags.string({
|
|
44
|
+
sort: core_1.Flags.string({
|
|
45
|
+
options: ['recent', 'value'],
|
|
46
|
+
default: 'recent',
|
|
47
|
+
description: 'recent: newest first, page by page through every source. value: rows you can decide now first, then by estimated savings per effort hour (risk-discounted; a row with no estimate after every row that has one), then newest. value ranks one bounded window and is not exhaustive.',
|
|
48
|
+
}),
|
|
27
49
|
};
|
|
28
50
|
async run() {
|
|
29
51
|
this.requireAuth();
|
|
@@ -47,13 +69,17 @@ class ProposalsList extends base_command_1.BaseCommand {
|
|
|
47
69
|
this.log(JSON.stringify(result, null, 2));
|
|
48
70
|
return;
|
|
49
71
|
}
|
|
50
|
-
if (!result.data.length)
|
|
51
|
-
|
|
72
|
+
if (!result.data.length) {
|
|
73
|
+
const narrowed = Boolean(flags.kind || flags.disposition || flags['commitment-id'] || flags['agent-id']);
|
|
74
|
+
for (const line of emptyPageLines(narrowed, result.nextCursor))
|
|
75
|
+
this.log(line);
|
|
76
|
+
}
|
|
52
77
|
(0, format_1.renderTable)(result.data.map((row) => ({
|
|
53
78
|
id: row.id,
|
|
54
79
|
title: row.title,
|
|
55
80
|
disposition: row.disposition,
|
|
56
|
-
|
|
81
|
+
// Most proposals carry no estimate; "unknown" read as a failure to determine one (OSK-15377).
|
|
82
|
+
basis: row.economics?.basis || '—',
|
|
57
83
|
deadline: row.deadline || '—',
|
|
58
84
|
note: noteFor(row),
|
|
59
85
|
})), [
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command';
|
|
2
2
|
export default class ProposalsShow extends BaseCommand {
|
|
3
3
|
static description: string;
|
|
4
|
+
static examples: string[];
|
|
4
5
|
static args: {
|
|
5
6
|
kind: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
-
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
id: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
8
|
};
|
|
8
9
|
static flags: {
|
|
9
10
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|