@opvs-ai/cli 0.16.1 → 0.20.1
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 +61 -3
- package/dist/.build-stamp.json +5 -5
- package/dist/commands/agent.js +6 -5
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/auth.js +230 -74
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/boards.js +21 -13
- package/dist/commands/boards.js.map +1 -1
- package/dist/commands/code.d.ts +23 -0
- package/dist/commands/code.js +90 -0
- package/dist/commands/code.js.map +1 -0
- package/dist/commands/columns.d.ts +1 -0
- package/dist/commands/columns.js +48 -2
- package/dist/commands/columns.js.map +1 -1
- package/dist/commands/docs.js +4 -2
- package/dist/commands/docs.js.map +1 -1
- package/dist/commands/markers.js +13 -3
- package/dist/commands/markers.js.map +1 -1
- package/dist/commands/projects.js +57 -12
- package/dist/commands/projects.js.map +1 -1
- package/dist/commands/tasks.js +5 -2
- package/dist/commands/tasks.js.map +1 -1
- package/dist/commands/tools.js +29 -3
- package/dist/commands/tools.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/auth-poll.d.ts +25 -3
- package/dist/lib/auth-poll.js +39 -6
- package/dist/lib/auth-poll.js.map +1 -1
- package/dist/lib/health.d.ts +131 -1
- package/dist/lib/health.js +192 -3
- package/dist/lib/health.js.map +1 -1
- package/dist/lib/pending-auth.d.ts +41 -0
- package/dist/lib/pending-auth.js +69 -0
- package/dist/lib/pending-auth.js.map +1 -0
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/auth-poll.d.ts
CHANGED
|
@@ -2,8 +2,27 @@
|
|
|
2
2
|
* Polling loop for PAT approval — waits for human to click approve/deny.
|
|
3
3
|
*/
|
|
4
4
|
import type { PATWorkspaceToken } from "@opvs-ai/core";
|
|
5
|
+
/**
|
|
6
|
+
* How long the CLI waits in the foreground before handing off to
|
|
7
|
+
* `opvs auth resume`. NOT the request's lifetime: the server keeps a request
|
|
8
|
+
* approvable for 60 minutes (PC-252), and an agent must not sit polling for all
|
|
9
|
+
* of it — its shell call is killed long before, and that would be a hang.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEFAULT_WAIT_MS: number;
|
|
12
|
+
/**
|
|
13
|
+
* When to stop polling: after the local wait, or just past the request's own
|
|
14
|
+
* expiry — whichever comes first. Always leaves room for one poll, so a request
|
|
15
|
+
* that has already expired is reported as `expired` by the server rather than
|
|
16
|
+
* as a local `timeout`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function pollDeadline(now: number, waitMs: number, expiresAt?: string | Date): number;
|
|
5
19
|
export interface PollResult {
|
|
6
|
-
|
|
20
|
+
/**
|
|
21
|
+
* `timeout` = not decided yet, still approvable. `invalid` = the server does not
|
|
22
|
+
* recognise this request or its poll token (404/401) — e.g. a stale saved
|
|
23
|
+
* request; waiting longer cannot change that.
|
|
24
|
+
*/
|
|
25
|
+
status: "active" | "denied" | "expired" | "timeout" | "invalid";
|
|
7
26
|
token?: string;
|
|
8
27
|
scopes?: string[];
|
|
9
28
|
brand_id?: number;
|
|
@@ -11,7 +30,10 @@ export interface PollResult {
|
|
|
11
30
|
workspaces?: PATWorkspaceToken[];
|
|
12
31
|
}
|
|
13
32
|
/**
|
|
14
|
-
* Poll the PAT request status endpoint until resolved or
|
|
33
|
+
* Poll the PAT request status endpoint until resolved or the wait runs out.
|
|
15
34
|
* Prints dots to stderr to show progress.
|
|
35
|
+
*
|
|
36
|
+
* `timeout` means only "not decided YET" — the request may still be approvable
|
|
37
|
+
* on the server; the caller decides whether that is a handoff or a failure.
|
|
16
38
|
*/
|
|
17
|
-
export declare function pollForApproval(baseUrl: string, requestId: string, pollToken: string, timeoutMs?: number): Promise<PollResult>;
|
|
39
|
+
export declare function pollForApproval(baseUrl: string, requestId: string, pollToken: string, timeoutMs?: number, expiresAt?: string | Date): Promise<PollResult>;
|
package/dist/lib/auth-poll.js
CHANGED
|
@@ -3,13 +3,39 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { apiUnauthGet } from "@opvs-ai/core";
|
|
5
5
|
const POLL_INTERVAL_MS = 3000;
|
|
6
|
-
const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
7
6
|
/**
|
|
8
|
-
*
|
|
7
|
+
* How long the CLI waits in the foreground before handing off to
|
|
8
|
+
* `opvs auth resume`. NOT the request's lifetime: the server keeps a request
|
|
9
|
+
* approvable for 60 minutes (PC-252), and an agent must not sit polling for all
|
|
10
|
+
* of it — its shell call is killed long before, and that would be a hang.
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_WAIT_MS = 5 * 60 * 1000;
|
|
13
|
+
/** Past the server's expiry, one more poll lets the SERVER say "expired". */
|
|
14
|
+
const EXPIRY_GRACE_MS = POLL_INTERVAL_MS + 2000;
|
|
15
|
+
/**
|
|
16
|
+
* When to stop polling: after the local wait, or just past the request's own
|
|
17
|
+
* expiry — whichever comes first. Always leaves room for one poll, so a request
|
|
18
|
+
* that has already expired is reported as `expired` by the server rather than
|
|
19
|
+
* as a local `timeout`.
|
|
20
|
+
*/
|
|
21
|
+
export function pollDeadline(now, waitMs, expiresAt) {
|
|
22
|
+
let deadline = now + waitMs;
|
|
23
|
+
if (expiresAt !== undefined) {
|
|
24
|
+
const expiry = new Date(expiresAt).getTime();
|
|
25
|
+
if (Number.isFinite(expiry))
|
|
26
|
+
deadline = Math.min(deadline, expiry + EXPIRY_GRACE_MS);
|
|
27
|
+
}
|
|
28
|
+
return Math.max(deadline, now + 1);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Poll the PAT request status endpoint until resolved or the wait runs out.
|
|
9
32
|
* Prints dots to stderr to show progress.
|
|
33
|
+
*
|
|
34
|
+
* `timeout` means only "not decided YET" — the request may still be approvable
|
|
35
|
+
* on the server; the caller decides whether that is a handoff or a failure.
|
|
10
36
|
*/
|
|
11
|
-
export async function pollForApproval(baseUrl, requestId, pollToken, timeoutMs =
|
|
12
|
-
const deadline = Date.now()
|
|
37
|
+
export async function pollForApproval(baseUrl, requestId, pollToken, timeoutMs = DEFAULT_WAIT_MS, expiresAt) {
|
|
38
|
+
const deadline = pollDeadline(Date.now(), timeoutMs, expiresAt);
|
|
13
39
|
process.stderr.write("Waiting for approval ");
|
|
14
40
|
while (Date.now() < deadline) {
|
|
15
41
|
try {
|
|
@@ -35,13 +61,20 @@ export async function pollForApproval(baseUrl, requestId, pollToken, timeoutMs =
|
|
|
35
61
|
return { status: "expired" };
|
|
36
62
|
}
|
|
37
63
|
}
|
|
38
|
-
catch {
|
|
64
|
+
catch (err) {
|
|
65
|
+
// 404 / 401 are answers, not blips: the request (or its poll token) is not
|
|
66
|
+
// one the server knows. Polling on would only turn that into a timeout.
|
|
67
|
+
const httpStatus = err?.status;
|
|
68
|
+
if (httpStatus === 404 || httpStatus === 401) {
|
|
69
|
+
process.stderr.write(" not recognised by the server.\n");
|
|
70
|
+
return { status: "invalid" };
|
|
71
|
+
}
|
|
39
72
|
// Network error or server error — keep polling
|
|
40
73
|
}
|
|
41
74
|
process.stderr.write(".");
|
|
42
75
|
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
43
76
|
}
|
|
44
|
-
process.stderr.write("
|
|
77
|
+
process.stderr.write(" not approved yet.\n");
|
|
45
78
|
return { status: "timeout" };
|
|
46
79
|
}
|
|
47
80
|
//# sourceMappingURL=auth-poll.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-poll.js","sourceRoot":"","sources":["../../src/lib/auth-poll.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAG7C,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAC9B,MAAM,
|
|
1
|
+
{"version":3,"file":"auth-poll.js","sourceRoot":"","sources":["../../src/lib/auth-poll.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/health.d.ts
CHANGED
|
@@ -15,6 +15,27 @@
|
|
|
15
15
|
* The parameter a `not_checked` names is translated to the CLI FLAG that would
|
|
16
16
|
* enable it, because "stale_days" is the API's name and the reader is at a
|
|
17
17
|
* shell prompt.
|
|
18
|
+
*
|
|
19
|
+
* ── The exit code is the point (ASP-253) ───────────────────────────────────
|
|
20
|
+
*
|
|
21
|
+
* SpiderIQ's planner gates a session on these verbs from bash. Until ASP-253
|
|
22
|
+
* both commands exited 0 on EVERY verdict, so `opvs boards health X && deploy`
|
|
23
|
+
* ran the deploy on a board that had just reported a FAIL — a gate that never
|
|
24
|
+
* fires is worse than no gate, because someone believes it.
|
|
25
|
+
*
|
|
26
|
+
* The mapping below is deliberate in both directions:
|
|
27
|
+
*
|
|
28
|
+
* healthy -> 0 every check ran, none failed
|
|
29
|
+
* incomplete -> 0 YOU SUPPLIED NO THRESHOLD. That is a fact about the
|
|
30
|
+
* REQUEST, not about the board, and a caller who gates on
|
|
31
|
+
* it is gating on their own flags. `--require-complete`
|
|
32
|
+
* is there for a caller who wants it to be fatal.
|
|
33
|
+
* not_healthy -> 2 at least one check FAILED
|
|
34
|
+
* anything else-> 1 could not read a verdict -> FAIL CLOSED. Never 0.
|
|
35
|
+
*
|
|
36
|
+
* `2` rather than `1` for not_healthy so a script can tell "the board is bad"
|
|
37
|
+
* from "I could not ask" (`diff`/`grep` use the same split); the transport and
|
|
38
|
+
* auth failures the commands already had keep 1.
|
|
18
39
|
*/
|
|
19
40
|
export interface HealthCheck {
|
|
20
41
|
check: string;
|
|
@@ -37,9 +58,85 @@ export interface HealthReport {
|
|
|
37
58
|
}
|
|
38
59
|
/** API query parameter → the flag `opvs boards|projects health` spells it as. */
|
|
39
60
|
export declare const HEALTH_PARAM_FLAGS: Record<string, string>;
|
|
61
|
+
/** Exit code for a report that was read. See the contract in the file header. */
|
|
62
|
+
export declare const EXIT_OK = 0;
|
|
63
|
+
/** Transport, auth, usage — or a verdict we could not read. Fail closed. */
|
|
64
|
+
export declare const EXIT_ERROR = 1;
|
|
65
|
+
/** The report came back `not_healthy`: at least one check FAILED. */
|
|
66
|
+
export declare const EXIT_NOT_HEALTHY = 2;
|
|
67
|
+
/**
|
|
68
|
+
* Map a verdict to the process exit code.
|
|
69
|
+
*
|
|
70
|
+
* `requireComplete` promotes `incomplete` to a failure for a caller who wants
|
|
71
|
+
* "you did not supply thresholds" to be fatal. It is OFF by default, because
|
|
72
|
+
* the server refuses to invent a threshold and a CLI that failed by default on
|
|
73
|
+
* an unsupplied one would be holding the same opinion through a different door.
|
|
74
|
+
*/
|
|
75
|
+
export declare function healthExitCode(verdict: string | undefined, opts?: {
|
|
76
|
+
requireComplete?: boolean;
|
|
77
|
+
}): number;
|
|
40
78
|
export declare function levelWord(level: string): string;
|
|
79
|
+
/**
|
|
80
|
+
* Pick the check rows to print.
|
|
81
|
+
*
|
|
82
|
+
* On `not_healthy` the default is the FAIL rows ONLY. A planner runs this on a
|
|
83
|
+
* gate and reads the wall of PASS exactly once; every run after that, the three
|
|
84
|
+
* lines it needs are buried in thirty it does not. Every other verdict prints
|
|
85
|
+
* in full — on `incomplete` the `not_checked` rows ARE the finding, and on
|
|
86
|
+
* `healthy` there is nothing to bury.
|
|
87
|
+
*
|
|
88
|
+
* Nothing is ever dropped silently: the caller is told the count and the flag.
|
|
89
|
+
*/
|
|
90
|
+
export declare function visibleChecks(report: HealthReport, opts?: {
|
|
91
|
+
allChecks?: boolean;
|
|
92
|
+
}): {
|
|
93
|
+
shown: HealthCheck[];
|
|
94
|
+
hidden: number;
|
|
95
|
+
};
|
|
41
96
|
/** Render a board or project health report as plain text lines. */
|
|
42
|
-
export declare function renderHealthReport(report: HealthReport
|
|
97
|
+
export declare function renderHealthReport(report: HealthReport, opts?: {
|
|
98
|
+
allChecks?: boolean;
|
|
99
|
+
}): string;
|
|
100
|
+
/**
|
|
101
|
+
* Read the verdict back out of the server's Markdown rendering.
|
|
102
|
+
*
|
|
103
|
+
* 🔴 WHY THIS IS ALLOWED TO FAIL, AND WHY IT MUST FAIL CLOSED.
|
|
104
|
+
*
|
|
105
|
+
* `?format=md` returns `text/markdown` with no verdict header and no JSON, so
|
|
106
|
+
* the exit code has to come out of the prose. The server writes exactly one
|
|
107
|
+
* line for it (`render_health_md`):
|
|
108
|
+
*
|
|
109
|
+
* **Verdict:** `not_healthy` · profile `factory`
|
|
110
|
+
*
|
|
111
|
+
* That is a hand-written copy of a contract owned by another repo path, and
|
|
112
|
+
* the house rule for those is to gate them, not to trust them. We cannot run
|
|
113
|
+
* the Python renderer from a CLI unit test — so instead of trusting it, this
|
|
114
|
+
* returns `undefined` when it cannot find the line, and `healthExitCode`
|
|
115
|
+
* turns that into exit 1. If the server ever reworded the heading, `--format
|
|
116
|
+
* md` starts erroring and names `--format json`; it does NOT start exiting 0
|
|
117
|
+
* on an unhealthy board. A drifted gate that shouts is recoverable; one that
|
|
118
|
+
* silently passes is the defect this whole card exists to remove.
|
|
119
|
+
*/
|
|
120
|
+
export declare function verdictFromMarkdown(md: string): string | undefined;
|
|
121
|
+
/**
|
|
122
|
+
* Read the verdict back out of the server's YAML rendering.
|
|
123
|
+
*
|
|
124
|
+
* `render_health_yaml` is `yaml.dump(report, sort_keys=False)`, so `verdict`
|
|
125
|
+
* is a top-level scalar. Same fail-closed contract as the Markdown reader: a
|
|
126
|
+
* miss returns `undefined` and becomes exit 1, never exit 0.
|
|
127
|
+
*/
|
|
128
|
+
export declare function verdictFromYaml(text: string): string | undefined;
|
|
129
|
+
/** `--format` values. `text` is the local rendering and sends no `format=`. */
|
|
130
|
+
export declare const HEALTH_FORMATS: readonly ["text", "json", "md", "yaml"];
|
|
131
|
+
export type HealthFormat = (typeof HEALTH_FORMATS)[number];
|
|
132
|
+
/**
|
|
133
|
+
* The `format=` query value for a `--format` choice.
|
|
134
|
+
*
|
|
135
|
+
* `text` and `json` both want the JSON report (the text rendering is built
|
|
136
|
+
* from it), and `buildUrl` appends `format=` for anything that is not the
|
|
137
|
+
* string `json` — so both must arrive here as `json`, never as `text`.
|
|
138
|
+
*/
|
|
139
|
+
export declare function healthWireFormat(fmt: HealthFormat): string;
|
|
43
140
|
/** The shared threshold flags, as query params. Only flags given become params. */
|
|
44
141
|
export declare function healthParams(opts: Record<string, string | undefined>): Record<string, string>;
|
|
45
142
|
/**
|
|
@@ -52,3 +149,36 @@ export declare function healthParams(opts: Record<string, string | undefined>):
|
|
|
52
149
|
export declare function withHealthOptions<T extends {
|
|
53
150
|
option: (flags: string, desc: string) => T;
|
|
54
151
|
}>(cmd: T): T;
|
|
152
|
+
/**
|
|
153
|
+
* The `--help` epilogue every `health` command carries.
|
|
154
|
+
*
|
|
155
|
+
* 🔑 It is not decoration. A caller who guesses the mapping writes a gate that
|
|
156
|
+
* never fires, which is the defect ASP-253 fixed — so the mapping is stated
|
|
157
|
+
* where the caller is standing when they write the gate. Every line here is
|
|
158
|
+
* backed by `healthExitCode` in this same file (AgentCLI LEARNINGS, ASP-25: a
|
|
159
|
+
* help string that describes behaviour nothing implements).
|
|
160
|
+
*/
|
|
161
|
+
export declare const HEALTH_EXIT_HELP = "Exit codes (a planner gates on these):\n 0 healthy \u2014 every check ran and none failed\n 0 incomplete \u2014 a threshold you did not supply left a check NOT CHECKED.\n That is a fact about your REQUEST, not about the board, so\n it does not fail the gate. Pass --require-complete to make\n it exit 2 instead.\n 2 not_healthy \u2014 at least one check FAILED\n 1 error \u2014 transport, auth, a bad ref, or a verdict this CLI could not\n read. Never confused with 0: a gate that cannot ask must\n not report a pass.";
|
|
162
|
+
export interface HealthOutputOpts {
|
|
163
|
+
format?: string;
|
|
164
|
+
json?: boolean;
|
|
165
|
+
allChecks?: boolean;
|
|
166
|
+
requireComplete?: boolean;
|
|
167
|
+
}
|
|
168
|
+
/** Thrown for a `--format` this CLI does not know. Caught as a usage error. */
|
|
169
|
+
export declare class HealthFormatError extends Error {
|
|
170
|
+
}
|
|
171
|
+
/** `--format` (validated) with `--json` kept working as an alias for it. */
|
|
172
|
+
export declare function resolveHealthFormat(opts: HealthOutputOpts): HealthFormat;
|
|
173
|
+
/**
|
|
174
|
+
* Print a health response and return the process exit code.
|
|
175
|
+
*
|
|
176
|
+
* The exit code is decided by the VERDICT in every format, including the ones
|
|
177
|
+
* the server renders itself — a gate must not change meaning because someone
|
|
178
|
+
* wanted Markdown. `yaml`/`md` carry no structured verdict, so the verdict is
|
|
179
|
+
* read back out of the text and a miss is an error (exit 1), never a pass.
|
|
180
|
+
*/
|
|
181
|
+
export declare function emitHealthReport(res: {
|
|
182
|
+
json: () => Promise<unknown>;
|
|
183
|
+
text: () => Promise<string>;
|
|
184
|
+
}, opts: HealthOutputOpts, fmt: HealthFormat): Promise<number>;
|
package/dist/lib/health.js
CHANGED
|
@@ -15,6 +15,27 @@
|
|
|
15
15
|
* The parameter a `not_checked` names is translated to the CLI FLAG that would
|
|
16
16
|
* enable it, because "stale_days" is the API's name and the reader is at a
|
|
17
17
|
* shell prompt.
|
|
18
|
+
*
|
|
19
|
+
* ── The exit code is the point (ASP-253) ───────────────────────────────────
|
|
20
|
+
*
|
|
21
|
+
* SpiderIQ's planner gates a session on these verbs from bash. Until ASP-253
|
|
22
|
+
* both commands exited 0 on EVERY verdict, so `opvs boards health X && deploy`
|
|
23
|
+
* ran the deploy on a board that had just reported a FAIL — a gate that never
|
|
24
|
+
* fires is worse than no gate, because someone believes it.
|
|
25
|
+
*
|
|
26
|
+
* The mapping below is deliberate in both directions:
|
|
27
|
+
*
|
|
28
|
+
* healthy -> 0 every check ran, none failed
|
|
29
|
+
* incomplete -> 0 YOU SUPPLIED NO THRESHOLD. That is a fact about the
|
|
30
|
+
* REQUEST, not about the board, and a caller who gates on
|
|
31
|
+
* it is gating on their own flags. `--require-complete`
|
|
32
|
+
* is there for a caller who wants it to be fatal.
|
|
33
|
+
* not_healthy -> 2 at least one check FAILED
|
|
34
|
+
* anything else-> 1 could not read a verdict -> FAIL CLOSED. Never 0.
|
|
35
|
+
*
|
|
36
|
+
* `2` rather than `1` for not_healthy so a script can tell "the board is bad"
|
|
37
|
+
* from "I could not ask" (`diff`/`grep` use the same split); the transport and
|
|
38
|
+
* auth failures the commands already had keep 1.
|
|
18
39
|
*/
|
|
19
40
|
/** API query parameter → the flag `opvs boards|projects health` spells it as. */
|
|
20
41
|
export const HEALTH_PARAM_FLAGS = {
|
|
@@ -34,6 +55,36 @@ const LEVEL_WORD = {
|
|
|
34
55
|
INFO: "INFO",
|
|
35
56
|
not_checked: "NOT CHECKED",
|
|
36
57
|
};
|
|
58
|
+
import { printJson, printRaw } from "./format.js";
|
|
59
|
+
/** Exit code for a report that was read. See the contract in the file header. */
|
|
60
|
+
export const EXIT_OK = 0;
|
|
61
|
+
/** Transport, auth, usage — or a verdict we could not read. Fail closed. */
|
|
62
|
+
export const EXIT_ERROR = 1;
|
|
63
|
+
/** The report came back `not_healthy`: at least one check FAILED. */
|
|
64
|
+
export const EXIT_NOT_HEALTHY = 2;
|
|
65
|
+
/**
|
|
66
|
+
* Map a verdict to the process exit code.
|
|
67
|
+
*
|
|
68
|
+
* `requireComplete` promotes `incomplete` to a failure for a caller who wants
|
|
69
|
+
* "you did not supply thresholds" to be fatal. It is OFF by default, because
|
|
70
|
+
* the server refuses to invent a threshold and a CLI that failed by default on
|
|
71
|
+
* an unsupplied one would be holding the same opinion through a different door.
|
|
72
|
+
*/
|
|
73
|
+
export function healthExitCode(verdict, opts) {
|
|
74
|
+
switch (verdict) {
|
|
75
|
+
case "healthy":
|
|
76
|
+
return EXIT_OK;
|
|
77
|
+
case "incomplete":
|
|
78
|
+
return opts?.requireComplete ? EXIT_NOT_HEALTHY : EXIT_OK;
|
|
79
|
+
case "not_healthy":
|
|
80
|
+
return EXIT_NOT_HEALTHY;
|
|
81
|
+
default:
|
|
82
|
+
// No verdict, or one this CLI does not know: the server grew a fourth
|
|
83
|
+
// value, or the body is not a report at all. Either way we cannot say
|
|
84
|
+
// the board is fine, so we must not say it with an exit code.
|
|
85
|
+
return EXIT_ERROR;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
37
88
|
const VERDICT_LINE = {
|
|
38
89
|
healthy: "HEALTHY — every check ran and none failed",
|
|
39
90
|
incomplete: "INCOMPLETE — not every check ran, so this is NOT a pass",
|
|
@@ -51,8 +102,27 @@ function flagsFor(parameter) {
|
|
|
51
102
|
.map((p) => HEALTH_PARAM_FLAGS[p] ?? p)
|
|
52
103
|
.join(", ");
|
|
53
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Pick the check rows to print.
|
|
107
|
+
*
|
|
108
|
+
* On `not_healthy` the default is the FAIL rows ONLY. A planner runs this on a
|
|
109
|
+
* gate and reads the wall of PASS exactly once; every run after that, the three
|
|
110
|
+
* lines it needs are buried in thirty it does not. Every other verdict prints
|
|
111
|
+
* in full — on `incomplete` the `not_checked` rows ARE the finding, and on
|
|
112
|
+
* `healthy` there is nothing to bury.
|
|
113
|
+
*
|
|
114
|
+
* Nothing is ever dropped silently: the caller is told the count and the flag.
|
|
115
|
+
*/
|
|
116
|
+
export function visibleChecks(report, opts) {
|
|
117
|
+
const checks = report.checks ?? [];
|
|
118
|
+
if (opts?.allChecks || report.verdict !== "not_healthy") {
|
|
119
|
+
return { shown: checks, hidden: 0 };
|
|
120
|
+
}
|
|
121
|
+
const shown = checks.filter((c) => c.level === "FAIL");
|
|
122
|
+
return { shown, hidden: checks.length - shown.length };
|
|
123
|
+
}
|
|
54
124
|
/** Render a board or project health report as plain text lines. */
|
|
55
|
-
export function renderHealthReport(report) {
|
|
125
|
+
export function renderHealthReport(report, opts) {
|
|
56
126
|
const lines = [];
|
|
57
127
|
const subject = report.project_name
|
|
58
128
|
? `Project health — ${report.project_name}`
|
|
@@ -70,7 +140,12 @@ export function renderHealthReport(report) {
|
|
|
70
140
|
lines.push(`Not supplied (so their checks made no finding): ${flagsFor(missing.join(","))}`);
|
|
71
141
|
}
|
|
72
142
|
lines.push("");
|
|
73
|
-
|
|
143
|
+
const { shown, hidden } = visibleChecks(report, opts);
|
|
144
|
+
if (hidden > 0) {
|
|
145
|
+
lines.push(`Showing the ${shown.length} FAIL check(s). ${hidden} other row(s) hidden — see them all with --all-checks.`);
|
|
146
|
+
lines.push("");
|
|
147
|
+
}
|
|
148
|
+
for (const chk of shown) {
|
|
74
149
|
const word = levelWord(chk.level).padEnd(WIDTH);
|
|
75
150
|
lines.push(` ${word} ${chk.check}${chk.detail ? ` — ${chk.detail}` : ""}`);
|
|
76
151
|
if (chk.level === "not_checked") {
|
|
@@ -86,6 +161,53 @@ export function renderHealthReport(report) {
|
|
|
86
161
|
}
|
|
87
162
|
return lines.join("\n");
|
|
88
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Read the verdict back out of the server's Markdown rendering.
|
|
166
|
+
*
|
|
167
|
+
* 🔴 WHY THIS IS ALLOWED TO FAIL, AND WHY IT MUST FAIL CLOSED.
|
|
168
|
+
*
|
|
169
|
+
* `?format=md` returns `text/markdown` with no verdict header and no JSON, so
|
|
170
|
+
* the exit code has to come out of the prose. The server writes exactly one
|
|
171
|
+
* line for it (`render_health_md`):
|
|
172
|
+
*
|
|
173
|
+
* **Verdict:** `not_healthy` · profile `factory`
|
|
174
|
+
*
|
|
175
|
+
* That is a hand-written copy of a contract owned by another repo path, and
|
|
176
|
+
* the house rule for those is to gate them, not to trust them. We cannot run
|
|
177
|
+
* the Python renderer from a CLI unit test — so instead of trusting it, this
|
|
178
|
+
* returns `undefined` when it cannot find the line, and `healthExitCode`
|
|
179
|
+
* turns that into exit 1. If the server ever reworded the heading, `--format
|
|
180
|
+
* md` starts erroring and names `--format json`; it does NOT start exiting 0
|
|
181
|
+
* on an unhealthy board. A drifted gate that shouts is recoverable; one that
|
|
182
|
+
* silently passes is the defect this whole card exists to remove.
|
|
183
|
+
*/
|
|
184
|
+
export function verdictFromMarkdown(md) {
|
|
185
|
+
const m = md.match(/\*\*Verdict:\*\*\s*`([a-z_]+)`/);
|
|
186
|
+
return m?.[1];
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Read the verdict back out of the server's YAML rendering.
|
|
190
|
+
*
|
|
191
|
+
* `render_health_yaml` is `yaml.dump(report, sort_keys=False)`, so `verdict`
|
|
192
|
+
* is a top-level scalar. Same fail-closed contract as the Markdown reader: a
|
|
193
|
+
* miss returns `undefined` and becomes exit 1, never exit 0.
|
|
194
|
+
*/
|
|
195
|
+
export function verdictFromYaml(text) {
|
|
196
|
+
const m = text.match(/^verdict:[ \t]*["']?([a-z_]+)["']?[ \t]*$/m);
|
|
197
|
+
return m?.[1];
|
|
198
|
+
}
|
|
199
|
+
/** `--format` values. `text` is the local rendering and sends no `format=`. */
|
|
200
|
+
export const HEALTH_FORMATS = ["text", "json", "md", "yaml"];
|
|
201
|
+
/**
|
|
202
|
+
* The `format=` query value for a `--format` choice.
|
|
203
|
+
*
|
|
204
|
+
* `text` and `json` both want the JSON report (the text rendering is built
|
|
205
|
+
* from it), and `buildUrl` appends `format=` for anything that is not the
|
|
206
|
+
* string `json` — so both must arrive here as `json`, never as `text`.
|
|
207
|
+
*/
|
|
208
|
+
export function healthWireFormat(fmt) {
|
|
209
|
+
return fmt === "text" ? "json" : fmt;
|
|
210
|
+
}
|
|
89
211
|
/** The shared threshold flags, as query params. Only flags given become params. */
|
|
90
212
|
export function healthParams(opts) {
|
|
91
213
|
const params = {};
|
|
@@ -121,6 +243,73 @@ export function withHealthOptions(cmd) {
|
|
|
121
243
|
.option("--stale-days <n>", "Warn on an open card not updated in N days.")
|
|
122
244
|
.option("--column-age-days <n>", "Warn on an open card in the same column for N days.")
|
|
123
245
|
.option("--star-fanout <n>", "Warn on a prerequisite whose top-layer fan-out exceeds N.")
|
|
124
|
-
.option("--
|
|
246
|
+
.option("--format <fmt>", "text (default, the human rendering) | json (the report VERBATIM, for jq) | md | yaml (the server's own rendering).")
|
|
247
|
+
.option("--json", "Alias for --format json.")
|
|
248
|
+
.option("--all-checks", "On a not_healthy report, print every check row, not only the FAIL rows.")
|
|
249
|
+
.option("--require-complete", "Treat `incomplete` as a failure too (exit 2). Off by default: `incomplete` means YOU supplied no threshold.");
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* The `--help` epilogue every `health` command carries.
|
|
253
|
+
*
|
|
254
|
+
* 🔑 It is not decoration. A caller who guesses the mapping writes a gate that
|
|
255
|
+
* never fires, which is the defect ASP-253 fixed — so the mapping is stated
|
|
256
|
+
* where the caller is standing when they write the gate. Every line here is
|
|
257
|
+
* backed by `healthExitCode` in this same file (AgentCLI LEARNINGS, ASP-25: a
|
|
258
|
+
* help string that describes behaviour nothing implements).
|
|
259
|
+
*/
|
|
260
|
+
export const HEALTH_EXIT_HELP = `Exit codes (a planner gates on these):
|
|
261
|
+
0 healthy — every check ran and none failed
|
|
262
|
+
0 incomplete — a threshold you did not supply left a check NOT CHECKED.
|
|
263
|
+
That is a fact about your REQUEST, not about the board, so
|
|
264
|
+
it does not fail the gate. Pass --require-complete to make
|
|
265
|
+
it exit 2 instead.
|
|
266
|
+
2 not_healthy — at least one check FAILED
|
|
267
|
+
1 error — transport, auth, a bad ref, or a verdict this CLI could not
|
|
268
|
+
read. Never confused with 0: a gate that cannot ask must
|
|
269
|
+
not report a pass.`;
|
|
270
|
+
/** Thrown for a `--format` this CLI does not know. Caught as a usage error. */
|
|
271
|
+
export class HealthFormatError extends Error {
|
|
272
|
+
}
|
|
273
|
+
/** `--format` (validated) with `--json` kept working as an alias for it. */
|
|
274
|
+
export function resolveHealthFormat(opts) {
|
|
275
|
+
if (opts.format === undefined)
|
|
276
|
+
return opts.json ? "json" : "text";
|
|
277
|
+
const fmt = opts.format.toLowerCase();
|
|
278
|
+
if (!HEALTH_FORMATS.includes(fmt)) {
|
|
279
|
+
throw new HealthFormatError(`Unknown --format "${opts.format}". Use one of: ${HEALTH_FORMATS.join(", ")}.`);
|
|
280
|
+
}
|
|
281
|
+
return fmt;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Print a health response and return the process exit code.
|
|
285
|
+
*
|
|
286
|
+
* The exit code is decided by the VERDICT in every format, including the ones
|
|
287
|
+
* the server renders itself — a gate must not change meaning because someone
|
|
288
|
+
* wanted Markdown. `yaml`/`md` carry no structured verdict, so the verdict is
|
|
289
|
+
* read back out of the text and a miss is an error (exit 1), never a pass.
|
|
290
|
+
*/
|
|
291
|
+
export async function emitHealthReport(res, opts, fmt) {
|
|
292
|
+
const exitOpts = { requireComplete: Boolean(opts.requireComplete) };
|
|
293
|
+
if (fmt === "json") {
|
|
294
|
+
// Verbatim — the report as the server sent it, NOT a re-render. A caller
|
|
295
|
+
// piping this into jq must see the server's field names and nothing else.
|
|
296
|
+
const report = (await res.json());
|
|
297
|
+
printJson(report);
|
|
298
|
+
return healthExitCode(report?.verdict, exitOpts);
|
|
299
|
+
}
|
|
300
|
+
if (fmt === "md" || fmt === "yaml") {
|
|
301
|
+
const text = await res.text();
|
|
302
|
+
printRaw(text);
|
|
303
|
+
const verdict = fmt === "md" ? verdictFromMarkdown(text) : verdictFromYaml(text);
|
|
304
|
+
if (verdict === undefined) {
|
|
305
|
+
console.error(`Error: printed the ${fmt} report, but could not read a verdict out of it, ` +
|
|
306
|
+
`so this exit code is NOT a health signal. Gate on --format json instead.`);
|
|
307
|
+
return EXIT_ERROR;
|
|
308
|
+
}
|
|
309
|
+
return healthExitCode(verdict, exitOpts);
|
|
310
|
+
}
|
|
311
|
+
const report = (await res.json());
|
|
312
|
+
console.log(renderHealthReport(report, { allChecks: Boolean(opts.allChecks) }));
|
|
313
|
+
return healthExitCode(report?.verdict, exitOpts);
|
|
125
314
|
}
|
|
126
315
|
//# sourceMappingURL=health.js.map
|
package/dist/lib/health.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/lib/health.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/lib/health.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A PAT request that is still waiting for a human to approve it (PC-252).
|
|
3
|
+
*
|
|
4
|
+
* The server keeps a request approvable for an hour, because the person who
|
|
5
|
+
* approves is usually someone else, reading email on a phone. The CLI must not
|
|
6
|
+
* sit polling for that hour — an agent's shell call is killed long before, and
|
|
7
|
+
* an hour of silent polling is a hang, not a feature. So `auth request` waits a
|
|
8
|
+
* bounded time (`--wait`, default 5 min) and then hands off: this file keeps
|
|
9
|
+
* what is needed to collect the token later with `opvs auth resume`.
|
|
10
|
+
*
|
|
11
|
+
* It is written BEFORE the first poll, so a process killed mid-wait (a tool-call
|
|
12
|
+
* timeout) still leaves a request that can be resumed. It holds the poll token —
|
|
13
|
+
* a bearer secret that fetches the PAT once approved — so it is written 0600,
|
|
14
|
+
* like a key file, and removed the moment the request settles either way.
|
|
15
|
+
*
|
|
16
|
+
* One pending request at a time: a new `auth request` replaces the old one,
|
|
17
|
+
* which matches what the approver is told — "approve the newest email".
|
|
18
|
+
*/
|
|
19
|
+
export interface PendingAuth {
|
|
20
|
+
request_id: string;
|
|
21
|
+
poll_token: string;
|
|
22
|
+
api_url: string;
|
|
23
|
+
/** ISO timestamp from the server — when the request stops being approvable. */
|
|
24
|
+
expires_at: string;
|
|
25
|
+
/** Who was emailed, so a resume can say whose inbox to check. */
|
|
26
|
+
owner_email: string;
|
|
27
|
+
/** `-w <slug>` flow: the workspace the token is saved under. `null` for the email-only flow. */
|
|
28
|
+
workspace_slug: string | null;
|
|
29
|
+
/** Email-only flow: the workspaces the server matched (fallback slug for a single-token answer). */
|
|
30
|
+
brands: Array<{
|
|
31
|
+
brand_slug: string;
|
|
32
|
+
brand_name?: string;
|
|
33
|
+
brand_id?: number;
|
|
34
|
+
}>;
|
|
35
|
+
created_at: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function pendingAuthPath(): string;
|
|
38
|
+
export declare function savePendingAuth(pending: PendingAuth, path?: string): void;
|
|
39
|
+
/** The saved request, or `null` when there is none — or it is unreadable, which is the same thing to a caller. */
|
|
40
|
+
export declare function loadPendingAuth(path?: string): PendingAuth | null;
|
|
41
|
+
export declare function clearPendingAuth(path?: string): void;
|