@opvs-ai/cli 0.16.0 → 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.
Files changed (60) hide show
  1. package/README.md +88 -8
  2. package/dist/.build-stamp.json +5 -5
  3. package/dist/commands/agent.js +6 -5
  4. package/dist/commands/agent.js.map +1 -1
  5. package/dist/commands/auth.js +246 -76
  6. package/dist/commands/auth.js.map +1 -1
  7. package/dist/commands/board-links.js +5 -2
  8. package/dist/commands/board-links.js.map +1 -1
  9. package/dist/commands/boards.js +26 -16
  10. package/dist/commands/boards.js.map +1 -1
  11. package/dist/commands/code.d.ts +23 -0
  12. package/dist/commands/code.js +90 -0
  13. package/dist/commands/code.js.map +1 -0
  14. package/dist/commands/columns.d.ts +1 -0
  15. package/dist/commands/columns.js +53 -4
  16. package/dist/commands/columns.js.map +1 -1
  17. package/dist/commands/config.js +5 -2
  18. package/dist/commands/config.js.map +1 -1
  19. package/dist/commands/docs.js +17 -8
  20. package/dist/commands/docs.js.map +1 -1
  21. package/dist/commands/employees.js +4 -2
  22. package/dist/commands/employees.js.map +1 -1
  23. package/dist/commands/files.js +5 -2
  24. package/dist/commands/files.js.map +1 -1
  25. package/dist/commands/links.js +9 -4
  26. package/dist/commands/links.js.map +1 -1
  27. package/dist/commands/mail.js +5 -2
  28. package/dist/commands/mail.js.map +1 -1
  29. package/dist/commands/markers.js +18 -5
  30. package/dist/commands/markers.js.map +1 -1
  31. package/dist/commands/members.js +4 -1
  32. package/dist/commands/members.js.map +1 -1
  33. package/dist/commands/projects.js +57 -12
  34. package/dist/commands/projects.js.map +1 -1
  35. package/dist/commands/scheduled-runs.d.ts +26 -0
  36. package/dist/commands/scheduled-runs.js +182 -0
  37. package/dist/commands/scheduled-runs.js.map +1 -0
  38. package/dist/commands/stages.js +5 -2
  39. package/dist/commands/stages.js.map +1 -1
  40. package/dist/commands/tasks.js +8 -5
  41. package/dist/commands/tasks.js.map +1 -1
  42. package/dist/commands/tools.js +34 -5
  43. package/dist/commands/tools.js.map +1 -1
  44. package/dist/commands/workspace.js +5 -2
  45. package/dist/commands/workspace.js.map +1 -1
  46. package/dist/index.js +4 -0
  47. package/dist/index.js.map +1 -1
  48. package/dist/lib/auth-poll.d.ts +25 -3
  49. package/dist/lib/auth-poll.js +39 -6
  50. package/dist/lib/auth-poll.js.map +1 -1
  51. package/dist/lib/confirm.d.ts +14 -0
  52. package/dist/lib/confirm.js +17 -0
  53. package/dist/lib/confirm.js.map +1 -1
  54. package/dist/lib/health.d.ts +131 -1
  55. package/dist/lib/health.js +192 -3
  56. package/dist/lib/health.js.map +1 -1
  57. package/dist/lib/pending-auth.d.ts +41 -0
  58. package/dist/lib/pending-auth.js +69 -0
  59. package/dist/lib/pending-auth.js.map +1 -0
  60. package/package.json +2 -2
@@ -2,6 +2,7 @@
2
2
  * opvs workspace — multi-workspace management
3
3
  */
4
4
  import { listWorkspaces, setCurrentWorkspace, removeWorkspace, getEffectiveConfig, } from "@opvs-ai/core";
5
+ import { confirmOrExit, YES_HELP } from "../lib/confirm.js";
5
6
  export function registerWorkspaceCommands(program) {
6
7
  const ws = program.command("workspace").description("Manage workspaces");
7
8
  ws.command("list")
@@ -49,8 +50,10 @@ export function registerWorkspaceCommands(program) {
49
50
  }
50
51
  });
51
52
  ws.command("remove <slug>")
52
- .description("Remove a saved workspace")
53
- .action((slug) => {
53
+ .description("Remove a saved workspace and its token from this machine (the token stays valid on the server). Asks first; needs --yes off a terminal.")
54
+ .option("-y, --yes", YES_HELP)
55
+ .action(async (slug, opts) => {
56
+ await confirmOrExit(`Remove workspace "${slug}" from this machine?`, opts.yes, "Its saved token is dropped, and getting one back needs a new `opvs auth request` and an approval");
54
57
  try {
55
58
  removeWorkspace(slug);
56
59
  console.log(`Workspace "${slug}" removed.`);
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/commands/workspace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/commands/workspace.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ import { registerAuthCommands } from "./commands/auth.js";
20
20
  import { registerSessionCommands } from "./commands/session.js";
21
21
  import { registerBoardCommands } from "./commands/boards.js";
22
22
  import { registerProjectCommands } from "./commands/projects.js";
23
+ import { registerScheduledRunCommands } from "./commands/scheduled-runs.js";
23
24
  import { registerTaskCommands } from "./commands/tasks.js";
24
25
  import { registerCommentCommands } from "./commands/comments.js";
25
26
  import { registerDocsCommands } from "./commands/docs.js";
@@ -43,6 +44,7 @@ import { registerMailCommands } from "./commands/mail.js";
43
44
  import { registerReportCommands } from "./commands/report.js";
44
45
  import { registerProfessionCommands } from "./commands/professions.js";
45
46
  import { registerEmployeeCommands } from "./commands/employees.js";
47
+ import { registerCodeCommands } from "./commands/code.js";
46
48
  const __dirname = dirname(fileURLToPath(import.meta.url));
47
49
  const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf8"));
48
50
  const program = new Command();
@@ -57,6 +59,7 @@ registerWorkspaceCommands(program);
57
59
  registerSessionCommands(program);
58
60
  registerBoardCommands(program);
59
61
  registerProjectCommands(program);
62
+ registerScheduledRunCommands(program);
60
63
  registerTaskCommands(program);
61
64
  registerCommentCommands(program);
62
65
  registerDocsCommands(program);
@@ -78,6 +81,7 @@ registerMailCommands(program);
78
81
  registerReportCommands(program);
79
82
  registerProfessionCommands(program);
80
83
  registerEmployeeCommands(program);
84
+ registerCodeCommands(program);
81
85
  registerConfigCommands(program);
82
86
  // A config error is a message, not a crash.
83
87
  //
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -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
- status: "active" | "denied" | "expired" | "timeout";
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 timed out.
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>;
@@ -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
- * Poll the PAT request status endpoint until resolved or timed out.
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 = DEFAULT_TIMEOUT_MS) {
12
- const deadline = Date.now() + timeoutMs;
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(" timed out.\n");
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,kBAAkB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;AAatD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAAe,EACf,SAAiB,EACjB,SAAiB,EACjB,SAAS,GAAG,kBAAkB;IAE9B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IAExC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAE9C,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAC5B,OAAO,EACP,4BAA4B,SAAS,SAAS,EAC9C,SAAS,CACV,CAAC;YACF,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAsB,CAAC;YAErD,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC;gBACxE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;gBACrC,OAAO;oBACL,MAAM,EAAE,QAAQ;oBAChB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;iBAC5B,CAAC;YACJ,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;gBACnC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;YAC9B,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;gBACpC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;YAC/B,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,+CAA+C;QACjD,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1B,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IACtC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC/B,CAAC"}
1
+ {"version":3,"file":"auth-poll.js","sourceRoot":"","sources":["../../src/lib/auth-poll.ts"],"names":[],"mappings":"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"}
@@ -26,3 +26,17 @@
26
26
  * safe rather than uninformative.
27
27
  */
28
28
  export declare function confirm(prompt: string, yes: boolean, consequence?: string): Promise<boolean>;
29
+ /**
30
+ * The help string every gated command uses for `-y, --yes` (AF.7b / ASP-144).
31
+ *
32
+ * One string, because the gate is one rule. `boards delete` used to delete at
33
+ * once while `tasks delete` asked, and nothing but reading every command could
34
+ * tell which was which.
35
+ */
36
+ export declare const YES_HELP = "Confirm without a prompt. Required when stdin is not a terminal (an agent loop, CI, a pipe)";
37
+ /**
38
+ * `confirm`, then exit 1 when the answer is no. A refusal is a non-zero exit so
39
+ * a script that forgot `--yes` stops, rather than carrying on as if the delete
40
+ * had happened.
41
+ */
42
+ export declare function confirmOrExit(prompt: string, yes: boolean | undefined, consequence?: string): Promise<void>;
@@ -38,4 +38,21 @@ export async function confirm(prompt, yes, consequence = "This cannot be undone
38
38
  rl.close();
39
39
  return /^y(es)?$/i.test(answer.trim());
40
40
  }
41
+ /**
42
+ * The help string every gated command uses for `-y, --yes` (AF.7b / ASP-144).
43
+ *
44
+ * One string, because the gate is one rule. `boards delete` used to delete at
45
+ * once while `tasks delete` asked, and nothing but reading every command could
46
+ * tell which was which.
47
+ */
48
+ export const YES_HELP = "Confirm without a prompt. Required when stdin is not a terminal (an agent loop, CI, a pipe)";
49
+ /**
50
+ * `confirm`, then exit 1 when the answer is no. A refusal is a non-zero exit so
51
+ * a script that forgot `--yes` stops, rather than carrying on as if the delete
52
+ * had happened.
53
+ */
54
+ export async function confirmOrExit(prompt, yes, consequence) {
55
+ if (!(await confirm(prompt, Boolean(yes), consequence)))
56
+ process.exit(1);
57
+ }
41
58
  //# sourceMappingURL=confirm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../src/lib/confirm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,MAAc,EACd,GAAY,EACZ,WAAW,GAAG,iCAAiC;IAE/C,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IACrB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CACX,YAAY,MAAM,KAAK,WAAW,8DAA8D,CACjG,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE,CACnD,EAAE,CAAC,QAAQ,CAAC,GAAG,MAAM,SAAS,EAAE,OAAO,CAAC,CACzC,CAAC;IACF,EAAE,CAAC,KAAK,EAAE,CAAC;IACX,OAAO,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../src/lib/confirm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,MAAc,EACd,GAAY,EACZ,WAAW,GAAG,iCAAiC;IAE/C,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IACrB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CACX,YAAY,MAAM,KAAK,WAAW,8DAA8D,CACjG,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE,CACnD,EAAE,CAAC,QAAQ,CAAC,GAAG,MAAM,SAAS,EAAE,OAAO,CAAC,CACzC,CAAC;IACF,EAAE,CAAC,KAAK,EAAE,CAAC;IACX,OAAO,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GACnB,6FAA6F,CAAC;AAEhG;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,GAAwB,EACxB,WAAoB;IAEpB,IAAI,CAAC,CAAC,MAAM,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,WAAW,CAAC,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC3E,CAAC"}
@@ -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): string;
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>;
@@ -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
- for (const chk of report.checks ?? []) {
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("--json", "Print the raw JSON report instead of the text rendering.");
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