mirror of
https://github.com/KeygraphHQ/shannon.git
synced 2026-09-20 08:52:27 +02:00
feat(cli)!: default the scan target and add a JSON error contract
List local scans, resolve the active or most recent workspace automatically, and make logs, status, and stop use one canonical scan identity. Add stable machine-readable failures, richer status output, explicit help errors, and seven-day Temporal retention. Treat absent Temporal pending-activity failures as absent whether the decoder represents them as `null` or missing. BREAKING CHANGE: `status --json` now returns a fixed `failureMessage`. Read `partialReasons`, `agenticSast`, and `workflow.log` for diagnostic detail.
This commit is contained in:
@@ -91,7 +91,14 @@ export async function describeScan(workflowId: string): Promise<ScanDescription
|
||||
if (!progress) {
|
||||
throw new ActivityMirrorError(activityType || 'unknown activity');
|
||||
}
|
||||
const lastFailure = pending.lastFailure?.message;
|
||||
// Temporal's own failure message is never forwarded verbatim: it can carry raw
|
||||
// exception text from inside the activity, which this client has no way to vet
|
||||
// before painting it into a terminal. Only its presence is kept; the boolean feeds
|
||||
// a fixed sentence downstream (see safeFailureDetail), and the real detail stays
|
||||
// one `shannon logs` away.
|
||||
// NOTE: the proto decoder writes an absent lastFailure as null, not undefined, so a
|
||||
// loose check is what distinguishes a healthy attempt from a failed one.
|
||||
const lastFailure = pending.lastFailure == null ? undefined : 'This activity attempt failed.';
|
||||
const startedAt = timestampMs(pending.scheduledTime ?? pending.lastStartedTime ?? null);
|
||||
runningAgents.push({
|
||||
agent: progress.key,
|
||||
@@ -179,8 +186,9 @@ export async function waitForWorkflowClose(workflowId: string, opts: WatchOption
|
||||
|
||||
while (!signal?.aborted) {
|
||||
try {
|
||||
const desc = await describeScan(workflowId);
|
||||
if (desc === null || TERMINAL_STATUSES.has(desc.status)) {
|
||||
const client = await getClient();
|
||||
const desc = await client.workflow.getHandle(workflowId).describe();
|
||||
if (TERMINAL_STATUSES.has(desc.status.name)) {
|
||||
return { reason: 'closed' };
|
||||
}
|
||||
// Reachable and still RUNNING — reset the failure streak and note any recovery.
|
||||
@@ -190,6 +198,9 @@ export async function waitForWorkflowClose(workflowId: string, opts: WatchOption
|
||||
}
|
||||
connectFailures = 0;
|
||||
} catch (err) {
|
||||
if (err instanceof WorkflowNotFoundError) {
|
||||
return { reason: 'closed' };
|
||||
}
|
||||
connectFailures++;
|
||||
lastError = err instanceof Error ? err.message : String(err);
|
||||
if (!warned && connectFailures >= warnAfterFailures) {
|
||||
|
||||
Reference in New Issue
Block a user